121 / 122 lines covered (99.2%)
1unit module BDD::Behave::Matcher::Collection;23use BDD::Behave::Matcher;45class IncludeMatcher does Matcher is export {6 has $.expected;78 method matches($actual --> Bool) {9 return False unless $actual.defined;1011 given $actual {12 when Str {13 for $!expected.list -> $item {14 return False unless $actual.contains($item.Str);15 }16 return True;17 }18 when Setty | Baggy {19 for $!expected.list -> $item {20 return False unless $actual{$item};21 }22 return True;23 }24 when Associative {25 for $!expected.list -> $item {26 if $item ~~ Pair {27 return False unless $actual{$item.key}:exists;28 return False unless $actual{$item.key} eqv $item.value;29 } else {30 return False unless $actual{$item}:exists;31 }32 }33 return True;34 }35 when Positional | Iterable {36 my @arr = $actual.list;37 for $!expected.list -> $item {38 return False unless @arr.first({ $_ eqv $item }).defined;39 }40 return True;41 }42 default {43 return False;44 }45 }46 }4748 method format-expected(--> Str) {49 $!expected.list.map(*.raku).join(', ');50 }5152 method failure-message($actual --> Str) {53 "expected " ~ $actual.raku ~ " to include " ~ self.format-expected;54 }5556 method failure-message-negated($actual --> Str) {57 "expected " ~ $actual.raku ~ " not to include " ~ self.format-expected;58 }5960 method expected-value(--> Mu) { $!expected }6162 method description(--> Str) { 'include ' ~ self.format-expected }63}6465class ContainExactlyMatcher does Matcher is export {66 has $.expected;6768 method matches($actual --> Bool) {69 return False unless $actual.defined;70 return False unless $actual ~~ Positional | Iterable;7172 my @remaining = $actual.list;7374 for $!expected.list -> $item {75 my $idx = @remaining.first({ $_ eqv $item }, :k);76 if $idx.defined {77 @remaining.splice($idx, 1);78 } else {79 return False;80 }81 }8283 @remaining.elems == 0;84 }8586 method format-expected(--> Str) {87 $!expected.list.map(*.raku).join(', ');88 }8990 method failure-message($actual --> Str) {91 "expected " ~ $actual.raku ~ " to contain exactly " ~ self.format-expected;92 }9394 method failure-message-negated($actual --> Str) {95 "expected " ~ $actual.raku ~ " not to contain exactly " ~ self.format-expected;96 }9798 method expected-value(--> Mu) { $!expected }99100 method description(--> Str) { 'contain exactly ' ~ self.format-expected }101}102103class StartWithMatcher does Matcher is export {104 has $.expected;105106 method matches($actual --> Bool) {107 return False unless $actual.defined;108109 given $actual {110 when Str {111 for $!expected.list -> $item {112 return False unless $actual.starts-with($item.Str);113 }114 return True;115 }116 when Positional | Iterable {117 my @arr = $actual.list;118 my @items = $!expected.list;119 return False if @items.elems > @arr.elems;120 for @items.kv -> $i, $item {121 return False unless @arr[$i] eqv $item;122 }123 return True;124 }125 default {126 return False;127 }128 }129 }130131 method format-expected(--> Str) {132 $!expected.list.map(*.raku).join(', ');133 }134135 method failure-message($actual --> Str) {136 "expected " ~ $actual.raku ~ " to start with " ~ self.format-expected;137 }138139 method failure-message-negated($actual --> Str) {140 "expected " ~ $actual.raku ~ " not to start with " ~ self.format-expected;141 }142143 method expected-value(--> Mu) { $!expected }144145 method description(--> Str) { 'start with ' ~ self.format-expected }146}147148class EndWithMatcher does Matcher is export {149 has $.expected;150151 method matches($actual --> Bool) {152 return False unless $actual.defined;153154 given $actual {155 when Str {156 for $!expected.list -> $item {157 return False unless $actual.ends-with($item.Str);158 }159 return True;160 }161 when Positional | Iterable {162 my @arr = $actual.list;163 my @items = $!expected.list;164 return False if @items.elems > @arr.elems;165 my $offset = @arr.elems - @items.elems;166 for @items.kv -> $i, $item {167 return False unless @arr[$offset + $i] eqv $item;168 }169 return True;170 }171 default {172 return False;173 }174 }175 }176177 method format-expected(--> Str) {178 $!expected.list.map(*.raku).join(', ');179 }180181 method failure-message($actual --> Str) {182 "expected " ~ $actual.raku ~ " to end with " ~ self.format-expected;183 }184185 method failure-message-negated($actual --> Str) {186 "expected " ~ $actual.raku ~ " not to end with " ~ self.format-expected;187 }188189 method expected-value(--> Mu) { $!expected }190191 method description(--> Str) { 'end with ' ~ self.format-expected }192}193194class AllMatcher does Matcher is export {195 has Matcher $.inner;196197 method matches($actual --> Bool) {198 return False unless $actual.defined;199 return False unless $actual ~~ Positional | Iterable;200201 for $actual.list -> $item {202 return False unless $!inner.matches($item);203 }204 return True;205 }206207 method !find-failing-index($actual) {208 for $actual.list.kv -> $i, $item {209 return $i unless $!inner.matches($item);210 }211 return Int;212 }213214 method failure-message($actual --> Str) {215 unless $actual.defined && $actual ~~ Positional | Iterable {216 return "expected " ~ $actual.raku ~ " to be a collection that all "217 ~ $!inner.description;218 }219220 my $idx = self!find-failing-index($actual);221 if $idx.defined {222 my $item = $actual.list[$idx];223 return "expected " ~ $actual.raku ~ " to all " ~ $!inner.description224 ~ " (element at index " ~ $idx ~ ": " ~ $item.raku ~ " did not match)";225 }226 "expected " ~ $actual.raku ~ " to all " ~ $!inner.description;227 }228229 method failure-message-negated($actual --> Str) {230 "expected " ~ $actual.raku ~ " not to all " ~ $!inner.description;231 }232233 method expected-value(--> Mu) { $!inner }234235 method description(--> Str) { 'all ' ~ $!inner.description }236}