Update RangeMatcher to use MatchData

There is a compilation error.
It appears that since the last Matcher#match method has been
implemented,
Crystal is finally grouping all of the NamedTuples together.
This commit is contained in:
Michael Miller 2019-03-06 15:01:00 -07:00
parent acf52e1553
commit 018e3232cd
2 changed files with 466 additions and 238 deletions

View file

@ -7,27 +7,21 @@ module Spectator::Matchers
# but any type that implements the `includes?` method is supported.
struct RangeMatcher(ExpectedType) < ValueMatcher(ExpectedType)
# Determines whether the matcher is satisfied with the value given to it.
# True is returned if the match was successful, false otherwise.
def match?(partial)
@expected.includes?(partial.actual)
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) : MatchData
raise NotImplementedError.new("#match")
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message(partial)
"Expected #{partial.label} to be in #{label}"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message(partial)
"Expected #{partial.label} to not be in #{label}"
def match(partial)
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*.
@ -37,10 +31,10 @@ module Spectator::Matchers
# ```
# RangeMatcher.new(diff).of(center)
# ```
# This implies that the `match?` method would not work on the original matcher.
# 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 minux diff.
# and have upper and lower bounds equal to *center* plus and minus diff.
# The range will be inclusive.
def of(center)
diff = @expected
@ -53,13 +47,87 @@ module Spectator::Matchers
# Returns a new matcher, with the same bounds, but uses an inclusive range.
def inclusive
range = Range.new(@expected.begin, @expected.end, exclusive: false)
RangeMatcher.new(range, label + " (inclusive)")
RangeMatcher.new(range, label)
end
# Returns a new matcher, with the same bounds, but uses an exclusive range.
def exclusive
range = Range.new(@expected.begin, @expected.end, exclusive: true)
RangeMatcher.new(range, label + " (exclusive)")
RangeMatcher.new(range, label)
end
# Match data specific to this matcher.
# This is used when the expected type is a `Range`.
private struct RangeMatchData(B, E, ActualType) < MatchData
# Creates the match data.
def initialize(matched, @values : ExpectedActual(Range(B, E), ActualType))
super(matched)
end
# Information about the match.
def values
{
lower: PrefixedValue.new(">=", range.begin),
upper: PrefixedValue.new(exclusive? ? "<" : "<=", range.end),
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} (#{exclusivity})"
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} (#{exclusivity})"
end
# Gets the expected range.
private def range
@values.expected
end
# Indicates whether the range is inclusive or exclusive.
private def exclusive?
range.exclusive?
end
# Produces a string "inclusive" or "exclusive" based on the range.
private def exclusivity
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 values
{
set: @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