← Back to index

lib/BDD/Behave/Matcher/Change.rakumod

66 / 75 lines covered (88.0%)

1unit module BDD::Behave::Matcher::Change;23use BDD::Behave::Matcher;45class ChangeMatcher does Matcher is export {6  has &.observable is required;7  has $.before-value             is rw;8  has $.after-value              is rw;9  has Bool $.callable-given      is rw = True;10  has Bool $.action-ran          is rw = False;11  has      $.expected-from       is rw;12  has Bool $.has-from            is rw = False;13  has      $.expected-to         is rw;14  has Bool $.has-to              is rw = False;15  has      $.expected-by         is rw;16  has Bool $.has-by              is rw = False;17  has      $.expected-by-at-least is rw;18  has Bool $.has-by-at-least     is rw = False;19  has      $.expected-by-at-most is rw;20  has Bool $.has-by-at-most      is rw = False;21  has Str  $.miss-reason         is rw;2223  method matches($actual --> Bool) {24    $!before-value   = Nil;25    $!after-value    = Nil;26    $!action-ran     = False;27    $!miss-reason    = Str;28    $!callable-given = ?($actual ~~ Callable);29    return self.check-captured unless $!callable-given;3031    $!before-value = &!observable.();32    $actual.();33    $!action-ran   = True;34    $!after-value  = &!observable.();3536    self.check-captured;37  }3839  method has-by-modifier(--> Bool) {40    ?($!has-by || $!has-by-at-least || $!has-by-at-most);41  }4243  method check-captured(--> Bool) {44    unless $!callable-given {45      $!miss-reason = 'non-callable';46      return False;47    }4849    if $!has-from && !($!before-value eqv $!expected-from) {50      $!miss-reason = 'from';51      return False;52    }5354    if $!has-to && !($!after-value eqv $!expected-to) {55      $!miss-reason = 'to';56      return False;57    }5859    if $!before-value eqv $!after-value {60      $!miss-reason = 'no-change';61      return False;62    }6364    if self.has-by-modifier {65      unless $!before-value ~~ Real && $!after-value ~~ Real {66        $!miss-reason = 'by-non-numeric';67        return False;68      }69      my $delta = $!after-value - $!before-value;70      if $!has-by && !($delta == $!expected-by) {71        $!miss-reason = 'by';72        return False;73      }74      if $!has-by-at-least && !($delta >= $!expected-by-at-least) {75        $!miss-reason = 'by-at-least';76        return False;77      }78      if $!has-by-at-most && !($delta <= $!expected-by-at-most) {79        $!miss-reason = 'by-at-most';80        return False;81      }82    }8384    $!miss-reason = Str;85    True;86  }8788  method change-clause(--> Str) {89    my $clause = '';90    $clause ~= ' from ' ~ $!expected-from.raku             if $!has-from;91    $clause ~= ' to '   ~ $!expected-to.raku               if $!has-to;92    $clause ~= ' by '   ~ $!expected-by.raku               if $!has-by;93    $clause ~= ' by at least ' ~ $!expected-by-at-least.raku if $!has-by-at-least;94    $clause ~= ' by at most '  ~ $!expected-by-at-most.raku  if $!has-by-at-most;95    $clause;96  }9798  method delta(--> Mu) {99    return Nil unless $!before-value ~~ Real && $!after-value ~~ Real;100    $!after-value - $!before-value;101  }102103  method failure-message($actual --> Str) {104    given $!miss-reason {105      when 'non-callable' {106        return "expected a Callable for change, but got " ~ $actual.raku;107      }108      when 'from' {109        return "expected block to change observable" ~ self.change-clause110          ~ ", but it started as " ~ $!before-value.raku;111      }112      when 'to' {113        return "expected block to change observable" ~ self.change-clause114          ~ ", but it ended as " ~ $!after-value.raku;115      }116      when 'by' | 'by-at-least' | 'by-at-most' {117        return "expected block to change observable" ~ self.change-clause118          ~ ", but it changed by " ~ self.delta.raku;119      }120      when 'by-non-numeric' {121        return "expected block to change observable" ~ self.change-clause122          ~ ", but values were not numeric (before: "123          ~ $!before-value.raku ~ ", after: " ~ $!after-value.raku ~ ")";124      }125    }126    "expected block to change observable" ~ self.change-clause127      ~ ", but it remained " ~ $!before-value.raku;128  }129130  method failure-message-negated($actual --> Str) {131    "expected block not to change observable" ~ self.change-clause132      ~ ", but it changed from " ~ $!before-value.raku133      ~ " to " ~ $!after-value.raku;134  }135136  method description(--> Str) { 'change observable' ~ self.change-clause }137138  method expected-value(--> Mu) {139    return [$!expected-from, $!expected-to] if $!has-from && $!has-to;140    return $!expected-from        if $!has-from;141    return $!expected-to          if $!has-to;142    return $!expected-by          if $!has-by;143    return $!expected-by-at-least if $!has-by-at-least;144    return $!expected-by-at-most  if $!has-by-at-most;145    Nil;146  }147}