From 77307f6eb1554cbead2bec8948a6a2d73e5d0f1d Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Thu, 16 May 2019 18:20:08 -0600 Subject: [PATCH] Split part of RangeMatcher off as CollectionMatcher Use CollectionMatcher for `be_within`. The `#of` method creates a RangeMatcher. --- spec/matchers/collection_matcher_spec.cr | 391 ++++++++++++++++ spec/matchers/range_matcher_spec.cr | 468 ++++++------------- src/spectator/dsl/matcher_dsl.cr | 4 +- src/spectator/matchers/collection_matcher.cr | 67 +++ src/spectator/matchers/range_matcher.cr | 63 +-- 5 files changed, 609 insertions(+), 384 deletions(-) create mode 100644 spec/matchers/collection_matcher_spec.cr create mode 100644 src/spectator/matchers/collection_matcher.cr diff --git a/spec/matchers/collection_matcher_spec.cr b/spec/matchers/collection_matcher_spec.cr new file mode 100644 index 0000000..f848792 --- /dev/null +++ b/spec/matchers/collection_matcher_spec.cr @@ -0,0 +1,391 @@ +require "../spec_helper" + +describe Spectator::Matchers::CollectionMatcher do + describe "#match" do + it "compares using #includes?" do + spy = SpySUT.new + partial = new_partial(5) + matcher = Spectator::Matchers::CollectionMatcher.new(spy) + matcher.match(partial) + spy.includes_call_count.should be > 0 + end + + context "returned MatchData" do + describe "#matched?" do + context "given a Range" do + context "inclusive" do + it "is true for lower-bound" do + lower = 3 + upper = 9 + value = lower + range = Range.new(lower, upper, exclusive: false) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for lower-bound minus 1" do + lower = 3 + upper = 9 + value = lower - 1 + range = Range.new(lower, upper, exclusive: false) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + + it "is true for mid-range" do + lower = 3 + upper = 9 + value = 5 + range = Range.new(lower, upper, exclusive: false) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is true for upper-bound" do + lower = 3 + upper = 9 + value = upper + range = Range.new(lower, upper, exclusive: false) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for upper-bound plus 1" do + lower = 3 + upper = 9 + value = upper + 1 + range = Range.new(lower, upper, exclusive: false) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + end + + context "exclusive" do + it "is true for lower-bound" do + lower = 3 + upper = 9 + value = lower + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for lower-bound minus 1" do + lower = 3 + upper = 9 + value = lower - 1 + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + + it "is true for mid-range" do + lower = 3 + upper = 9 + value = 5 + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for upper-bound" do + lower = 3 + upper = 9 + value = upper + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + + it "is false for upper-bound plus 1" do + lower = 3 + upper = 9 + value = upper + 1 + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + end + end + + context "given an Enumerable" do + it "is true for an existing item" do + array = %i[a b c] + value = :b + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(array) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for a non-existing item" do + array = %i[a b c] + value = :z + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(array) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + end + end + + describe "#values" do + context "given a Range" do + context "collection" do + it "is the expected value" do + value = 5 + range = Range.new(3, 9) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data_value_sans_prefix(match_data.values, :collection)[:value].should eq(range) + end + end + + context "actual" do + it "is the actual value" do + value = 5 + range = Range.new(3, 9) + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value) + end + end + end + + context "given an Enumerable" do + context "collection" do + it "is the expected value" do + array = %i[a b c] + value = :z + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(array) + match_data = matcher.match(partial) + match_data_value_sans_prefix(match_data.values, :collection)[:value].should eq(array) + end + end + + context "actual" do + it "is the actual value" do + array = %i[a b c] + value = :z + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(array) + match_data = matcher.match(partial) + match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value) + end + end + end + end + + describe "#message" do + it "contains the actual label" do + range = 1..10 + value = 5 + label = "everything" + partial = new_partial(value, label) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.message.should contain(label) + end + + it "contains the expected label" do + range = 1..10 + value = 5 + label = "everything" + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range, label) + match_data = matcher.match(partial) + match_data.message.should contain(label) + end + + context "when expected label is omitted" do + it "contains stringified form of expected value" do + range = 1..10 + value = 5 + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.message.should contain(range.to_s) + end + end + end + + describe "#negated_message" do + it "contains the actual label" do + range = 1..10 + value = 5 + label = "everything" + partial = new_partial(value, label) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.negated_message.should contain(label) + end + + it "contains the expected label" do + range = 1..10 + value = 5 + label = "everything" + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range, label) + match_data = matcher.match(partial) + match_data.negated_message.should contain(label) + end + + context "when expected label is omitted" do + it "contains stringified form of expected value" do + range = 1..10 + value = 5 + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(range) + match_data = matcher.match(partial) + match_data.negated_message.should contain(range.to_s) + end + end + end + end + end + + describe "#of" do + it "is true for lower-bound" do + center = 5 + diff = 4 + lower = center - diff + value = lower + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for lower-bound minus 1" do + center = 5 + diff = 4 + lower = center - diff + value = lower - 1 + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + + it "is true for mid-range" do + center = 5 + diff = 4 + value = center + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is true for upper-bound" do + center = 5 + diff = 4 + upper = center + diff + value = upper + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for upper-bound plus 1" do + center = 5 + diff = 4 + upper = center + diff + value = upper + 1 + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + + describe "#message" do + it "contains the original label" do + center = 5 + diff = 4 + value = 3 + label = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff, label).of(center) + match_data = matcher.match(partial) + match_data.message.should contain(label) + end + + it "contains the center" do + center = 5 + diff = 4 + value = 3 + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center) + match_data = matcher.match(partial) + match_data.message.should contain(center.to_s) + end + + it "contains the diff" do + center = 5 + diff = 4 + value = 3 + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center) + match_data = matcher.match(partial) + match_data.message.should contain(diff.to_s) + end + end + + describe "#negated_message" do + it "contains the original label" do + center = 5 + diff = 4 + value = 3 + label = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff, label).of(center) + match_data = matcher.match(partial) + match_data.negated_message.should contain(label) + end + + it "contains the center" do + center = 5 + diff = 4 + value = 3 + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center) + match_data = matcher.match(partial) + match_data.negated_message.should contain(center.to_s) + end + + it "contains the diff" do + center = 5 + diff = 4 + value = 3 + partial = new_partial(value) + matcher = Spectator::Matchers::CollectionMatcher.new(diff).of(center) + match_data = matcher.match(partial) + match_data.negated_message.should contain(diff.to_s) + end + end + end +end diff --git a/spec/matchers/range_matcher_spec.cr b/spec/matchers/range_matcher_spec.cr index 71ffa15..5f473a5 100644 --- a/spec/matchers/range_matcher_spec.cr +++ b/spec/matchers/range_matcher_spec.cr @@ -2,147 +2,117 @@ require "../spec_helper" describe Spectator::Matchers::RangeMatcher do describe "#match" do - it "compares using #includes?" do - spy = SpySUT.new - partial = new_partial(5) - matcher = Spectator::Matchers::RangeMatcher.new(spy) - matcher.match(partial) - spy.includes_call_count.should be > 0 - end - context "returned MatchData" do describe "#matched?" do - context "given a Range" do - context "inclusive" do - it "is true for lower-bound" do - lower = 3 - upper = 9 - value = lower - range = Range.new(lower, upper, exclusive: false) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_true - end - - it "is false for lower-bound minus 1" do - lower = 3 - upper = 9 - value = lower - 1 - range = Range.new(lower, upper, exclusive: false) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_false - end - - it "is true for mid-range" do - lower = 3 - upper = 9 - value = 5 - range = Range.new(lower, upper, exclusive: false) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_true - end - - it "is true for upper-bound" do - lower = 3 - upper = 9 - value = upper - range = Range.new(lower, upper, exclusive: false) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_true - end - - it "is false for upper-bound plus 1" do - lower = 3 - upper = 9 - value = upper + 1 - range = Range.new(lower, upper, exclusive: false) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_false - end - end - - context "exclusive" do - it "is true for lower-bound" do - lower = 3 - upper = 9 - value = lower - range = Range.new(lower, upper, exclusive: true) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_true - end - - it "is false for lower-bound minus 1" do - lower = 3 - upper = 9 - value = lower - 1 - range = Range.new(lower, upper, exclusive: true) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_false - end - - it "is true for mid-range" do - lower = 3 - upper = 9 - value = 5 - range = Range.new(lower, upper, exclusive: true) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_true - end - - it "is false for upper-bound" do - lower = 3 - upper = 9 - value = upper - range = Range.new(lower, upper, exclusive: true) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_false - end - - it "is false for upper-bound plus 1" do - lower = 3 - upper = 9 - value = upper + 1 - range = Range.new(lower, upper, exclusive: true) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data.matched?.should be_false - end - end - end - - context "given an Enumerable" do - it "is true for an existing item" do - array = %i[a b c] - value = :b + context "inclusive" do + it "is true for lower-bound" do + lower = 3 + upper = 9 + value = lower + range = Range.new(lower, upper, exclusive: false) partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(array) + matcher = Spectator::Matchers::RangeMatcher.new(range) match_data = matcher.match(partial) match_data.matched?.should be_true end - it "is false for a non-existing item" do - array = %i[a b c] - value = :z + it "is false for lower-bound minus 1" do + lower = 3 + upper = 9 + value = lower - 1 + range = Range.new(lower, upper, exclusive: false) partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(array) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + + it "is true for mid-range" do + lower = 3 + upper = 9 + value = 5 + range = Range.new(lower, upper, exclusive: false) + partial = new_partial(value) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is true for upper-bound" do + lower = 3 + upper = 9 + value = upper + range = Range.new(lower, upper, exclusive: false) + partial = new_partial(value) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for upper-bound plus 1" do + lower = 3 + upper = 9 + value = upper + 1 + range = Range.new(lower, upper, exclusive: false) + partial = new_partial(value) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + end + + context "exclusive" do + it "is true for lower-bound" do + lower = 3 + upper = 9 + value = lower + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for lower-bound minus 1" do + lower = 3 + upper = 9 + value = lower - 1 + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + + it "is true for mid-range" do + lower = 3 + upper = 9 + value = 5 + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + + it "is false for upper-bound" do + lower = 3 + upper = 9 + value = upper + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + + it "is false for upper-bound plus 1" do + lower = 3 + upper = 9 + value = upper + 1 + range = Range.new(lower, upper, exclusive: true) + partial = new_partial(value) + matcher = Spectator::Matchers::RangeMatcher.new(range) match_data = matcher.match(partial) match_data.matched?.should be_false end @@ -150,88 +120,62 @@ describe Spectator::Matchers::RangeMatcher do end describe "#values" do - context "given a Range" do - context "lower" do - it "is #begin from the expected range" do - range = Range.new(3, 9) - partial = new_partial(5) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data_value_sans_prefix(match_data.values, :lower)[:value].should eq(range.begin) - end + context "lower" do + it "is #begin from the expected range" do + range = Range.new(3, 9) + partial = new_partial(5) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data_value_sans_prefix(match_data.values, :lower)[:value].should eq(range.begin) + end - it "is prefixed with >=" do - range = Range.new(3, 9) + it "is prefixed with >=" do + range = Range.new(3, 9) + partial = new_partial(5) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data_value_sans_prefix(match_data.values, :lower)[:to_s].should start_with(">=") + end + end + + context "upper" do + it "is #end from the expected range" do + range = Range.new(3, 9) + partial = new_partial(5) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data_value_sans_prefix(match_data.values, :upper)[:value].should eq(range.end) + end + + context "when inclusive" do + it "is prefixed with <=" do + range = Range.new(3, 9, exclusive: false) partial = new_partial(5) matcher = Spectator::Matchers::RangeMatcher.new(range) match_data = matcher.match(partial) - match_data_value_sans_prefix(match_data.values, :lower)[:to_s].should start_with(">=") + match_data_value_sans_prefix(match_data.values, :upper)[:to_s].should start_with("<=") end end - context "upper" do - it "is #end from the expected range" do - range = Range.new(3, 9) + context "when exclusive" do + it "is prefixed with <" do + range = Range.new(3, 9, exclusive: false) partial = new_partial(5) matcher = Spectator::Matchers::RangeMatcher.new(range) match_data = matcher.match(partial) - match_data_value_sans_prefix(match_data.values, :upper)[:value].should eq(range.end) - end - - context "when inclusive" do - it "is prefixed with <=" do - range = Range.new(3, 9, exclusive: false) - partial = new_partial(5) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data_value_sans_prefix(match_data.values, :upper)[:to_s].should start_with("<=") - end - end - - context "when exclusive" do - it "is prefixed with <" do - range = Range.new(3, 9, exclusive: false) - partial = new_partial(5) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data_value_sans_prefix(match_data.values, :upper)[:to_s].should start_with("<") - end - end - end - - context "actual" do - it "is the actual value" do - value = 5 - range = Range.new(3, 9) - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(range) - match_data = matcher.match(partial) - match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value) + match_data_value_sans_prefix(match_data.values, :upper)[:to_s].should start_with("<") end end end - context "given an Enumerable" do - context "set" do - it "is the expected value" do - array = %i[a b c] - value = :z - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(array) - match_data = matcher.match(partial) - match_data_value_sans_prefix(match_data.values, :set)[:value].should eq(array) - end - end - - context "actual" do - it "is the actual value" do - array = %i[a b c] - value = :z - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(array) - match_data = matcher.match(partial) - match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value) - end + context "actual" do + it "is the actual value" do + value = 5 + range = Range.new(3, 9) + partial = new_partial(value) + matcher = Spectator::Matchers::RangeMatcher.new(range) + match_data = matcher.match(partial) + match_data_value_sans_prefix(match_data.values, :actual)[:value].should eq(value) end end end @@ -304,128 +248,6 @@ describe Spectator::Matchers::RangeMatcher do end end - describe "#of" do - it "is true for lower-bound" do - center = 5 - diff = 4 - lower = center - diff - value = lower - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff).of(center) - match_data = matcher.match(partial) - match_data.matched?.should be_true - end - - it "is false for lower-bound minus 1" do - center = 5 - diff = 4 - lower = center - diff - value = lower - 1 - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff).of(center) - match_data = matcher.match(partial) - match_data.matched?.should be_false - end - - it "is true for mid-range" do - center = 5 - diff = 4 - value = center - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff).of(center) - match_data = matcher.match(partial) - match_data.matched?.should be_true - end - - it "is true for upper-bound" do - center = 5 - diff = 4 - upper = center + diff - value = upper - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff).of(center) - match_data = matcher.match(partial) - match_data.matched?.should be_true - end - - it "is false for upper-bound plus 1" do - center = 5 - diff = 4 - upper = center + diff - value = upper + 1 - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff).of(center) - match_data = matcher.match(partial) - match_data.matched?.should be_false - end - - describe "#message" do - it "contains the original label" do - center = 5 - diff = 4 - value = 3 - label = "foobar" - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff, label).of(center) - match_data = matcher.match(partial) - match_data.message.should contain(label) - end - - it "contains the center" do - center = 5 - diff = 4 - value = 3 - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff).of(center) - match_data = matcher.match(partial) - match_data.message.should contain(center.to_s) - end - - it "contains the diff" do - center = 5 - diff = 4 - value = 3 - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff).of(center) - match_data = matcher.match(partial) - match_data.message.should contain(diff.to_s) - end - end - - describe "#negated_message" do - it "contains the original label" do - center = 5 - diff = 4 - value = 3 - label = "foobar" - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff, label).of(center) - match_data = matcher.match(partial) - match_data.negated_message.should contain(label) - end - - it "contains the center" do - center = 5 - diff = 4 - value = 3 - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff).of(center) - match_data = matcher.match(partial) - match_data.negated_message.should contain(center.to_s) - end - - it "contains the diff" do - center = 5 - diff = 4 - value = 3 - partial = new_partial(value) - matcher = Spectator::Matchers::RangeMatcher.new(diff).of(center) - match_data = matcher.match(partial) - match_data.negated_message.should contain(diff.to_s) - end - end - end - describe "#inclusive" do context "initially exclusive" do it "is true for lower-bound" do diff --git a/src/spectator/dsl/matcher_dsl.cr b/src/spectator/dsl/matcher_dsl.cr index 5fdbe2c..7d14334 100644 --- a/src/spectator/dsl/matcher_dsl.cr +++ b/src/spectator/dsl/matcher_dsl.cr @@ -242,7 +242,7 @@ module Spectator::DSL # ``` # # NOTE: The of suffix must be used - # if the *expected* argument does not implement an includes? method. + # if the *expected* argument does not implement an `includes?` method. # # Additionally, for this second usage, # an "inclusive" or "exclusive" suffix can be added. @@ -258,7 +258,7 @@ module Spectator::DSL # NOTE: Do not attempt to mix the two use cases. # It likely won't work and will result in a compilation error. macro be_within(expected) - ::Spectator::Matchers::RangeMatcher.new({{expected}}, {{expected.stringify}}) + ::Spectator::Matchers::CollectionMatcher.new({{expected}}, {{expected.stringify}}) end # Indicates that some value should be between a lower and upper-bound. diff --git a/src/spectator/matchers/collection_matcher.cr b/src/spectator/matchers/collection_matcher.cr new file mode 100644 index 0000000..f57d46c --- /dev/null +++ b/src/spectator/matchers/collection_matcher.cr @@ -0,0 +1,67 @@ +require "./value_matcher" + +module Spectator::Matchers + # Matcher for checking that a value is in a collection of other values. + struct CollectionMatcher(ExpectedType) < ValueMatcher(ExpectedType) + # Determines whether the matcher is satisfied with the value given to it. + private def match?(actual) + expected.includes?(actual) + end + + # Determines whether the matcher is satisfied with the partial given to it. + # `MatchData` is returned that contains information about the match. + def match(partial) + actual = partial.actual + matched = match?(actual) + MatchData.new(matched, ExpectedActual.new(partial, self)) + end + + # Creates a new range matcher with bounds based off of *center*. + # + # This method expects that the original matcher was created with a "difference" value. + # That is: + # ``` + # RangeMatcher.new(diff).of(center) + # ``` + # This implies that the `#match` method would not work on the original matcher. + # + # The new range will be centered at *center* + # and have upper and lower bounds equal to *center* plus and minus diff. + # The range will be inclusive. + def of(center) + diff = @expected + lower = center - diff + upper = center + diff + range = Range.new(lower, upper) + RangeMatcher.new(range, "#{center} +/- #{label}") + end + + # Match data specific to this matcher. + private struct MatchData(ExpectedType, ActualType) < MatchData + # Creates the match data. + def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType)) + super(matched) + end + + # Information about the match. + def named_tuple + { + collection: NegatableMatchDataValue.new(@values.expected), + actual: @values.actual, + } + end + + # Describes the condition that satisfies the matcher. + # This is informational and displayed to the end-user. + def message + "#{@values.actual_label} is in #{@values.expected_label}" + end + + # Describes the condition that won't satsify the matcher. + # This is informational and displayed to the end-user. + def negated_message + "#{@values.actual_label} is not in #{@values.expected_label}" + end + end + end +end diff --git a/src/spectator/matchers/range_matcher.cr b/src/spectator/matchers/range_matcher.cr index 8b66497..ad05da3 100644 --- a/src/spectator/matchers/range_matcher.cr +++ b/src/spectator/matchers/range_matcher.cr @@ -1,10 +1,8 @@ require "./value_matcher" module Spectator::Matchers - # Matcher that tests whether a value is in a given range or set of values. - # The `includes?` method is used for this check. - # Typically this matcher uses a `Range`, - # but any type that implements the `includes?` method is supported. + # Matcher that tests whether a value is in a given range. + # The `Range#includes?` method is used for this check. struct RangeMatcher(ExpectedType) < ValueMatcher(ExpectedType) # Determines whether the matcher is satisfied with the value given to it. private def match?(actual) @@ -17,31 +15,7 @@ module Spectator::Matchers actual = partial.actual matched = match?(actual) expected_value = @expected - if expected_value.is_a?(Range) - RangeMatchData.new(matched, ExpectedActual.new(expected_value, label, actual, partial.label)) - else - SetMatchData.new(matched, ExpectedActual.new(partial, self)) - end - end - - # Creates a new range matcher with bounds based off of *center*. - # - # This method expects that the original matcher was created with a "difference" value. - # That is: - # ``` - # RangeMatcher.new(diff).of(center) - # ``` - # This implies that the `#match` method would not work on the original matcher. - # - # The new range will be centered at *center* - # and have upper and lower bounds equal to *center* plus and minus diff. - # The range will be inclusive. - def of(center) - diff = @expected - lower = center - diff - upper = center + diff - range = Range.new(lower, upper) - RangeMatcher.new(range, "#{center} +/- #{label}") + MatchData.new(matched, ExpectedActual.new(expected_value, label, actual, partial.label)) end # Returns a new matcher, with the same bounds, but uses an inclusive range. @@ -58,7 +32,7 @@ module Spectator::Matchers # Match data specific to this matcher. # This is used when the expected type is a `Range`. - private struct RangeMatchData(B, E, ActualType) < MatchData + private struct MatchData(B, E, ActualType) < MatchData # Creates the match data. def initialize(matched, @values : ExpectedActual(Range(B, E), ActualType)) super(matched) @@ -100,34 +74,5 @@ module Spectator::Matchers exclusive? ? "exclusive" : "inclusive" end end - - # Match data specific to this matcher. - # This is used when the expected type is not a `Range`. - private struct SetMatchData(ExpectedType, ActualType) < MatchData - # Creates the match data. - def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType)) - super(matched) - end - - # Information about the match. - def named_tuple - { - set: NegatableMatchDataValue.new(@values.expected), - actual: @values.actual, - } - end - - # Describes the condition that satisfies the matcher. - # This is informational and displayed to the end-user. - def message - "#{@values.actual_label} is in #{@values.expected_label}" - end - - # Describes the condition that won't satsify the matcher. - # This is informational and displayed to the end-user. - def negated_message - "#{@values.actual_label} is not in #{@values.expected_label}" - end - end end end