Split part of RangeMatcher off as CollectionMatcher

Use CollectionMatcher for `be_within`.
The `#of` method creates a RangeMatcher.
This commit is contained in:
Michael Miller 2019-05-16 18:20:08 -06:00
parent fa5c13df0e
commit 77307f6eb1
5 changed files with 609 additions and 384 deletions

View file

@ -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

View file

@ -2,147 +2,117 @@ require "../spec_helper"
describe Spectator::Matchers::RangeMatcher do describe Spectator::Matchers::RangeMatcher do
describe "#match" 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 context "returned MatchData" do
describe "#matched?" do describe "#matched?" do
context "given a Range" do context "inclusive" do
context "inclusive" do it "is true for lower-bound" do
it "is true for lower-bound" do lower = 3
lower = 3 upper = 9
upper = 9 value = lower
value = lower range = Range.new(lower, upper, exclusive: false)
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
partial = new_partial(value) partial = new_partial(value)
matcher = Spectator::Matchers::RangeMatcher.new(array) matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial) match_data = matcher.match(partial)
match_data.matched?.should be_true match_data.matched?.should be_true
end end
it "is false for a non-existing item" do it "is false for lower-bound minus 1" do
array = %i[a b c] lower = 3
value = :z upper = 9
value = lower - 1
range = Range.new(lower, upper, exclusive: false)
partial = new_partial(value) 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 = matcher.match(partial)
match_data.matched?.should be_false match_data.matched?.should be_false
end end
@ -150,88 +120,62 @@ describe Spectator::Matchers::RangeMatcher do
end end
describe "#values" do describe "#values" do
context "given a Range" do context "lower" do
context "lower" do it "is #begin from the expected range" do
it "is #begin from the expected range" do range = Range.new(3, 9)
range = Range.new(3, 9) partial = new_partial(5)
partial = new_partial(5) matcher = Spectator::Matchers::RangeMatcher.new(range)
matcher = Spectator::Matchers::RangeMatcher.new(range) match_data = matcher.match(partial)
match_data = matcher.match(partial) match_data_value_sans_prefix(match_data.values, :lower)[:value].should eq(range.begin)
match_data_value_sans_prefix(match_data.values, :lower)[:value].should eq(range.begin) end
end
it "is prefixed with >=" do it "is prefixed with >=" do
range = Range.new(3, 9) 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) partial = new_partial(5)
matcher = Spectator::Matchers::RangeMatcher.new(range) matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial) 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
end end
context "upper" do context "when exclusive" do
it "is #end from the expected range" do it "is prefixed with <" do
range = Range.new(3, 9) range = Range.new(3, 9, exclusive: false)
partial = new_partial(5) partial = new_partial(5)
matcher = Spectator::Matchers::RangeMatcher.new(range) matcher = Spectator::Matchers::RangeMatcher.new(range)
match_data = matcher.match(partial) match_data = matcher.match(partial)
match_data_value_sans_prefix(match_data.values, :upper)[:value].should eq(range.end) match_data_value_sans_prefix(match_data.values, :upper)[:to_s].should start_with("<")
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)
end end
end end
end end
context "given an Enumerable" do context "actual" do
context "set" do it "is the actual value" do
it "is the expected value" do value = 5
array = %i[a b c] range = Range.new(3, 9)
value = :z partial = new_partial(value)
partial = new_partial(value) matcher = Spectator::Matchers::RangeMatcher.new(range)
matcher = Spectator::Matchers::RangeMatcher.new(array) match_data = matcher.match(partial)
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, :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
end end
end end
end end
@ -304,128 +248,6 @@ describe Spectator::Matchers::RangeMatcher do
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::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 describe "#inclusive" do
context "initially exclusive" do context "initially exclusive" do
it "is true for lower-bound" do it "is true for lower-bound" do

View file

@ -242,7 +242,7 @@ module Spectator::DSL
# ``` # ```
# #
# NOTE: The of suffix must be used # 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, # Additionally, for this second usage,
# an "inclusive" or "exclusive" suffix can be added. # 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. # NOTE: Do not attempt to mix the two use cases.
# It likely won't work and will result in a compilation error. # It likely won't work and will result in a compilation error.
macro be_within(expected) macro be_within(expected)
::Spectator::Matchers::RangeMatcher.new({{expected}}, {{expected.stringify}}) ::Spectator::Matchers::CollectionMatcher.new({{expected}}, {{expected.stringify}})
end end
# Indicates that some value should be between a lower and upper-bound. # Indicates that some value should be between a lower and upper-bound.

View file

@ -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

View file

@ -1,10 +1,8 @@
require "./value_matcher" require "./value_matcher"
module Spectator::Matchers module Spectator::Matchers
# Matcher that tests whether a value is in a given range or set of values. # Matcher that tests whether a value is in a given range.
# The `includes?` method is used for this check. # The `Range#includes?` method is used for this check.
# Typically this matcher uses a `Range`,
# but any type that implements the `includes?` method is supported.
struct RangeMatcher(ExpectedType) < ValueMatcher(ExpectedType) struct RangeMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it. # Determines whether the matcher is satisfied with the value given to it.
private def match?(actual) private def match?(actual)
@ -17,31 +15,7 @@ module Spectator::Matchers
actual = partial.actual actual = partial.actual
matched = match?(actual) matched = match?(actual)
expected_value = @expected expected_value = @expected
if expected_value.is_a?(Range) MatchData.new(matched, ExpectedActual.new(expected_value, label, actual, partial.label))
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}")
end end
# Returns a new matcher, with the same bounds, but uses an inclusive range. # 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. # Match data specific to this matcher.
# This is used when the expected type is a `Range`. # 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. # Creates the match data.
def initialize(matched, @values : ExpectedActual(Range(B, E), ActualType)) def initialize(matched, @values : ExpectedActual(Range(B, E), ActualType))
super(matched) super(matched)
@ -100,34 +74,5 @@ module Spectator::Matchers
exclusive? ? "exclusive" : "inclusive" exclusive? ? "exclusive" : "inclusive"
end end
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
end end