← Back to index

lib/BDD/Behave/Matcher.rakumod

69 / 72 lines covered (95.8%)

1unit module BDD::Behave::Matcher;23role Matcher is export {4  method matches($actual --> Bool) { ... }5  method failure-message($actual --> Str) { Str }6  method failure-message-negated($actual --> Str) { Str }7  method expected-value(--> Mu) { Nil }8  method description(--> Str) { self.^name }910  method and($other, *@rest) {11    my @others = ($other, |@rest);12    for @others -> $m {13      unless $m ~~ Matcher {14        die "Matcher.and requires Matcher arguments";15      }16    }17    ::('BDD::Behave::Matcher::AndMatcher').new(18      :matchers((self, |@others).flat.List),19    );20  }2122  method or($other, *@rest) {23    my @others = ($other, |@rest);24    for @others -> $m {25      unless $m ~~ Matcher {26        die "Matcher.or requires Matcher arguments";27      }28    }29    ::('BDD::Behave::Matcher::OrMatcher').new(30      :matchers((self, |@others).flat.List),31    );32  }33}3435class AndMatcher does Matcher is export {36  has @.matchers;37  has Int $.failing-index is rw;3839  method matches($actual --> Bool) {40    $!failing-index = Int;41    for @!matchers.kv -> $i, $m {42      unless ?$m.matches($actual) {43        $!failing-index = $i;44        return False;45      }46    }47    True;48  }4950  method failing-matcher() {51    return Nil unless $!failing-index.defined;52    @!matchers[$!failing-index];53  }5455  method failure-message($actual --> Str) {56    my $desc = self.description;57    return "expected {$actual.raku} to {$desc}" unless $!failing-index.defined;58    my $failing = @!matchers[$!failing-index];59    my $inner = $failing.failure-message($actual);60    if $inner.defined {61      "expected {$actual.raku} to {$desc}, but {$failing.description} failed: $inner";62    } else {63      "expected {$actual.raku} to {$desc}, but {$failing.description} did not match";64    }65  }6667  method failure-message-negated($actual --> Str) {68    "expected {$actual.raku} not to {self.description}";69  }7071  method expected-value(--> Mu) {72    @!matchers.map(*.expected-value).List;73  }7475  method description(--> Str) {76    @!matchers.map(*.description).join(' and ');77  }7879  method and($other, *@rest) {80    my @others = ($other, |@rest);81    for @others -> $m {82      unless $m ~~ Matcher {83        die "Matcher.and requires Matcher arguments";84      }85    }86    AndMatcher.new(:matchers((|@!matchers, |@others).flat.List));87  }88}8990class OrMatcher does Matcher is export {91  has @.matchers;92  has Int $.matched-index is rw;9394  method matches($actual --> Bool) {95    $!matched-index = Int;96    for @!matchers.kv -> $i, $m {97      if ?$m.matches($actual) {98        $!matched-index = $i;99        return True;100      }101    }102    False;103  }104105  method matched-matcher() {106    return Nil unless $!matched-index.defined;107    @!matchers[$!matched-index];108  }109110  method failure-message($actual --> Str) {111    "expected {$actual.raku} to {self.description}, but none matched";112  }113114  method failure-message-negated($actual --> Str) {115    my $desc = self.description;116    return "expected {$actual.raku} not to {$desc}" unless $!matched-index.defined;117    my $matched = @!matchers[$!matched-index];118    "expected {$actual.raku} not to {$desc}, but {$matched.description} matched";119  }120121  method expected-value(--> Mu) {122    @!matchers.map(*.expected-value).List;123  }124125  method description(--> Str) {126    @!matchers.map(*.description).join(' or ');127  }128129  method or($other, *@rest) {130    my @others = ($other, |@rest);131    for @others -> $m {132      unless $m ~~ Matcher {133        die "Matcher.or requires Matcher arguments";134      }135    }136    OrMatcher.new(:matchers((|@!matchers, |@others).flat.List));137  }138}