mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Refactor existing change matchers to use new format
This commit is contained in:
parent
db1118dac1
commit
214b2e171e
4 changed files with 153 additions and 216 deletions
|
@ -535,7 +535,8 @@ module Spectator::DSL
|
||||||
# expect { i += 42 }.to change { i }.by(42)
|
# expect { i += 42 }.to change { i }.by(42)
|
||||||
# ```
|
# ```
|
||||||
macro change(&expression)
|
macro change(&expression)
|
||||||
::Spectator::Matchers::ChangeMatcher.new("`" + {{expression.body.stringify}} + "`") {{expression}}
|
%proc = ->({{expression.args.splat}}) {{expression}}
|
||||||
|
::Spectator::Matchers::ChangeMatcher.new(::Spectator::TestBlock.new(%proc, "`" + {{expression.body.stringify}} + "`"))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Indicates that some block should raise an error.
|
# Indicates that some block should raise an error.
|
||||||
|
|
|
@ -3,100 +3,74 @@ require "./value_matcher"
|
||||||
module Spectator::Matchers
|
module Spectator::Matchers
|
||||||
# Matcher that tests whether an expression changed from a specific value.
|
# Matcher that tests whether an expression changed from a specific value.
|
||||||
struct ChangeFromMatcher(ExpressionType, FromType) < Matcher
|
struct ChangeFromMatcher(ExpressionType, FromType) < Matcher
|
||||||
# Textual representation of what the matcher expects.
|
# The expression that is expected to (not) change.
|
||||||
# This shouldn't be used in the conditional logic,
|
private getter expression
|
||||||
# but for verbose output to help the end-user.
|
|
||||||
getter label : String
|
|
||||||
|
|
||||||
# Determines whether the matcher is satisfied with the partial given to it.
|
# The expected value of the expression before the change.
|
||||||
# `MatchData` is returned that contains information about the match.
|
private getter expected
|
||||||
def match(partial)
|
|
||||||
before = @expression.call # Retrieve the expression's initial value.
|
|
||||||
partial.actual # Invoke action that might change the expression's value.
|
|
||||||
after = @expression.call # Retrieve the expression's value again.
|
|
||||||
if @expected_before != before
|
|
||||||
# Initial value isn't what was expected.
|
|
||||||
InitialMatchData.new(@expected_before, before, after, partial.label, label)
|
|
||||||
else
|
|
||||||
# Check if the expression's value changed.
|
|
||||||
same = before == after
|
|
||||||
ChangeMatchData.new(!same, @expected_before, before, after, partial.label, label)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new change matcher with a custom label.
|
|
||||||
def initialize(@label, @expected_before : FromType, &expression : -> ExpressionType)
|
|
||||||
@expression = expression
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new change matcher.
|
# Creates a new change matcher.
|
||||||
def initialize(@expected_before : FromType, &expression : -> ExpressionType)
|
def initialize(@expression : TestBlock(ExpressionType), @expected : FromType)
|
||||||
@label = expression.to_s
|
|
||||||
@expression = expression
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Match data for when the initial value isn't the expected value.
|
# Short text about the matcher's purpose.
|
||||||
private struct InitialMatchData(ExpressionType, FromType) < MatchData
|
# This explains what condition satisfies the matcher.
|
||||||
# Creates the match data.
|
# The description is used when the one-liner syntax is used.
|
||||||
def initialize(@expected_before : FromType, @actual_before : ExpressionType, @after : ExpressionType,
|
def description
|
||||||
@action_label : String, @expression_label : String)
|
"changes #{expression.label} from #{expected}"
|
||||||
super(false)
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Do not allow negation of this match data.
|
# Actually performs the test against the expression.
|
||||||
def override?
|
def match(actual : TestExpression(T)) : MatchData forall T
|
||||||
true
|
before, after = change(actual)
|
||||||
end
|
if before != expected
|
||||||
|
FailedMatchData.new("#{expression.label} was not initially #{expected}",
|
||||||
# Information about the match.
|
expected: expected.inspect,
|
||||||
def named_tuple
|
actual: before.inspect,
|
||||||
{
|
)
|
||||||
"expected before": @expected_before,
|
elsif before == after
|
||||||
"actual before": @actual_before,
|
FailedMatchData.new("#{actual.label} did not change #{expression.label} from #{expected}",
|
||||||
"expected after": NegatableMatchDataValue.new(@expected_before, true),
|
before: before.inspect,
|
||||||
"actual after": @after,
|
after: after.inspect,
|
||||||
}
|
expected: "Not #{expected.inspect}"
|
||||||
end
|
)
|
||||||
|
else
|
||||||
# This is informational and displayed to the end-user.
|
SuccessfulMatchData.new
|
||||||
def message
|
|
||||||
"#{@expression_label} is initially #{@expected_before}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# This is informational and displayed to the end-user.
|
|
||||||
def negated_message
|
|
||||||
"#{@expression_label} is not initially #{@expected_before}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private struct ChangeMatchData(ExpressionType, FromType) < MatchData
|
# Performs the test against the expression, but inverted.
|
||||||
# Creates the match data.
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
def initialize(matched, @expected_before : FromType, @actual_before : ExpressionType,
|
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
||||||
@after : ExpressionType, @action_label : String, @expression_label : String)
|
before, after = change(actual)
|
||||||
super(matched)
|
if before != expected
|
||||||
|
FailedMatchData.new("#{expression.label} was not initially #{expected}",
|
||||||
|
expected: expected.inspect,
|
||||||
|
actual: before.inspect
|
||||||
|
)
|
||||||
|
elsif before == after
|
||||||
|
SuccessfulMatchData.new
|
||||||
|
else
|
||||||
|
FailedMatchData.new("#{actual.label} changed #{expression.label} from #{expected}",
|
||||||
|
before: before.inspect,
|
||||||
|
after: after.inspect,
|
||||||
|
expected: expected.inspect
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Information about the match.
|
# Specifies what the resulting value of the expression must be.
|
||||||
def named_tuple
|
def to(value : T) forall T
|
||||||
{
|
raise NotImplementedError.new("ChangeFromMatcher#to")
|
||||||
"expected before": @expected_before,
|
end
|
||||||
"actual before": @actual_before,
|
|
||||||
"expected after": NegatableMatchDataValue.new(@expected_before, true),
|
|
||||||
"actual after": @after,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Describes the condition that satisfies the matcher.
|
# Performs the change and reports the before and after values.
|
||||||
# This is informational and displayed to the end-user.
|
private def change(actual)
|
||||||
def message
|
before = expression.value # Retrieve the expression's initial value.
|
||||||
"#{@action_label} changed #{@expression_label} from #{@expected_before}"
|
actual.value # Invoke action that might change the expression's value.
|
||||||
end
|
after = expression.value # Retrieve the expression's value again.
|
||||||
|
|
||||||
# Describes the condition that won't satsify the matcher.
|
{before, after}
|
||||||
# This is informational and displayed to the end-user.
|
|
||||||
def negated_message
|
|
||||||
"#{@action_label} did not change #{@expression_label} from #{@expected_before}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,73 +1,67 @@
|
||||||
require "./change_from_matcher"
|
require "./change_from_matcher"
|
||||||
require "./change_to_matcher"
|
require "./change_to_matcher"
|
||||||
require "./matcher"
|
require "./standard_matcher"
|
||||||
|
|
||||||
module Spectator::Matchers
|
module Spectator::Matchers
|
||||||
# Matcher that tests whether an expression changed.
|
# Matcher that tests whether an expression changed.
|
||||||
struct ChangeMatcher(ExpressionType) < Matcher
|
struct ChangeMatcher(ExpressionType) < Matcher
|
||||||
# Textual representation of what the matcher expects.
|
private getter expression
|
||||||
# This shouldn't be used in the conditional logic,
|
|
||||||
# but for verbose output to help the end-user.
|
|
||||||
getter label : String
|
|
||||||
|
|
||||||
# Determines whether the matcher is satisfied with the partial given to it.
|
|
||||||
# `MatchData` is returned that contains information about the match.
|
|
||||||
def match(partial)
|
|
||||||
before = @expression.call # Retrieve the expression's initial value.
|
|
||||||
partial.actual # Invoke action that might change the expression's value.
|
|
||||||
after = @expression.call # Retrieve the expression's value again.
|
|
||||||
same = before == after # Did the value change?
|
|
||||||
MatchData.new(!same, before, after, partial.label, label)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new change matcher with a custom label.
|
|
||||||
def initialize(@label, &expression : -> ExpressionType)
|
|
||||||
@expression = expression
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new change matcher.
|
# Creates a new change matcher.
|
||||||
def initialize(&expression : -> ExpressionType)
|
def initialize(@expression : TestBlock(ExpressionType))
|
||||||
@label = expression.to_s
|
end
|
||||||
@expression = expression
|
|
||||||
|
# Short text about the matcher's purpose.
|
||||||
|
# This explains what condition satisfies the matcher.
|
||||||
|
# The description is used when the one-liner syntax is used.
|
||||||
|
def description
|
||||||
|
"changes #{expression.label}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Actually performs the test against the expression.
|
||||||
|
def match(actual : TestExpression(T)) : MatchData forall T
|
||||||
|
before, after = change(actual)
|
||||||
|
if before == after
|
||||||
|
FailedMatchData.new("#{actual.label} did not change #{expression.label}",
|
||||||
|
before: before.inspect,
|
||||||
|
after: after.inspect
|
||||||
|
)
|
||||||
|
else
|
||||||
|
SuccessfulMatchData.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Performs the test against the expression, but inverted.
|
||||||
|
# A successful match with `#match` should normally fail for this method, and vice-versa.
|
||||||
|
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
||||||
|
before, after = change(actual)
|
||||||
|
if before == after
|
||||||
|
SuccessfulMatchData.new
|
||||||
|
else
|
||||||
|
FailedMatchData.new("#{actual.label} changed #{expression.label}",
|
||||||
|
before: before.inspect,
|
||||||
|
after: after.inspect
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Specifies what the initial value of the expression must be.
|
# Specifies what the initial value of the expression must be.
|
||||||
def from(value : T) forall T
|
def from(value : T) forall T
|
||||||
ChangeFromMatcher.new(label, value, &@expression)
|
ChangeFromMatcher.new(@expression, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Specifies what the resulting value of the expression must be.
|
# Specifies what the resulting value of the expression must be.
|
||||||
def to(value : T) forall T
|
def to(value : T) forall T
|
||||||
ChangeToMatcher.new(label, value, &@expression)
|
ChangeToMatcher.new(@expression, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Match data specific to this matcher.
|
# Performs the change and reports the before and after values.
|
||||||
private struct MatchData(ExpressionType) < MatchData
|
private def change(actual)
|
||||||
# Creates the match data.
|
before = expression.value # Retrieve the expression's initial value.
|
||||||
def initialize(matched, @before : ExpressionType, @after : ExpressionType,
|
actual.value # Invoke action that might change the expression's value.
|
||||||
@action_label : String, @expression_label : String)
|
after = expression.value # Retrieve the expression's value again.
|
||||||
super(matched)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Information about the match.
|
{before, after}
|
||||||
def named_tuple
|
|
||||||
{
|
|
||||||
before: @before,
|
|
||||||
after: @after,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Describes the condition that satisfies the matcher.
|
|
||||||
# This is informational and displayed to the end-user.
|
|
||||||
def message
|
|
||||||
"#{@action_label} changes #{@expression_label}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Describes the condition that won't satsify the matcher.
|
|
||||||
# This is informational and displayed to the end-user.
|
|
||||||
def negated_message
|
|
||||||
"#{@action_label} does not change #{@expression_label}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,102 +1,70 @@
|
||||||
require "./value_matcher"
|
require "./change_matcher"
|
||||||
|
|
||||||
module Spectator::Matchers
|
module Spectator::Matchers
|
||||||
# Matcher that tests whether an expression changed to a specific value.
|
# Matcher that tests whether an expression changed to a specific value.
|
||||||
struct ChangeToMatcher(ExpressionType, ToType) < Matcher
|
struct ChangeToMatcher(ExpressionType, ToType) < Matcher
|
||||||
# Textual representation of what the matcher expects.
|
# The expression that is expected to (not) change.
|
||||||
# This shouldn't be used in the conditional logic,
|
private getter expression
|
||||||
# but for verbose output to help the end-user.
|
|
||||||
getter label : String
|
|
||||||
|
|
||||||
# Determines whether the matcher is satisfied with the partial given to it.
|
# The expected value of the expression after the change.
|
||||||
# `MatchData` is returned that contains information about the match.
|
private getter expected
|
||||||
def match(partial)
|
|
||||||
before = @expression.call # Retrieve the expression's initial value.
|
|
||||||
partial.actual # Invoke action that might change the expression's value.
|
|
||||||
after = @expression.call # Retrieve the expression's value again.
|
|
||||||
if @expected_after != after
|
|
||||||
# Resulting value isn't what was expected.
|
|
||||||
ResultingMatchData.new(before, @expected_after, after, partial.label, label)
|
|
||||||
else
|
|
||||||
# Check if the expression's value changed.
|
|
||||||
same = before == after
|
|
||||||
ChangeMatchData.new(!same, before, @expected_after, after, partial.label, label)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new change matcher with a custom label.
|
|
||||||
def initialize(@label, @expected_after : ToType, &expression : -> ExpressionType)
|
|
||||||
@expression = expression
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new change matcher.
|
# Creates a new change matcher.
|
||||||
def initialize(@expected_after : ToType, &expression : -> ExpressionType)
|
def initialize(@expression : TestBlock(ExpressionType), @expected : ToType)
|
||||||
@label = expression.to_s
|
|
||||||
@expression = expression
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Match data for when the resulting value isn't the expected value.
|
# Short text about the matcher's purpose.
|
||||||
private struct ResultingMatchData(ExpressionType, ToType) < MatchData
|
# This explains what condition satisfies the matcher.
|
||||||
# Creates the match data.
|
# The description is used when the one-liner syntax is used.
|
||||||
def initialize(@before : ExpressionType, @expected_after : ToType, @actual_after : ExpressionType,
|
def description
|
||||||
@action_label : String, @expression_label : String)
|
"changes #{expression.label} to #{expected}"
|
||||||
super(false)
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Do not allow negation of this match data.
|
# Actually performs the test against the expression.
|
||||||
def override?
|
def match(actual : TestExpression(T)) : MatchData forall T
|
||||||
true
|
before, after = change(actual)
|
||||||
end
|
if before == after
|
||||||
|
FailedMatchData.new("#{actual.label} did not change #{expression.label}",
|
||||||
# Information about the match.
|
before: before.inspect,
|
||||||
def named_tuple
|
after: after.inspect,
|
||||||
{
|
expected: expected.inspect
|
||||||
"expected before": NegatableMatchDataValue.new(@expected_after, true),
|
)
|
||||||
"actual before": @before,
|
elsif expected == after
|
||||||
"expected after": @expected_after,
|
SuccessfulMatchData.new
|
||||||
"actual after": @actual_after,
|
else
|
||||||
}
|
FailedMatchData.new("#{actual.label} did not change #{expression.label} to #{expected}",
|
||||||
end
|
before: before.inspect,
|
||||||
|
after: after.inspect,
|
||||||
# This is informational and displayed to the end-user.
|
expected: expected.inspect
|
||||||
def message
|
)
|
||||||
"#{@expression_label} changes to #{@expected_after}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# This is informational and displayed to the end-user.
|
|
||||||
def negated_message
|
|
||||||
"#{@expression_label} did not change to #{@expected_after}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private struct ChangeMatchData(ExpressionType, ToType) < MatchData
|
# Negated matching for this matcher is not supported.
|
||||||
# Creates the match data.
|
# Attempting to call this method will result in a compilation error.
|
||||||
def initialize(matched, @before : ToType, @expected_after : ToType, @actual_after : ExpressionType,
|
#
|
||||||
@action_label : String, @expression_label : String)
|
# This syntax has a logical problem.
|
||||||
super(matched)
|
# "The action does not change the expression to some value."
|
||||||
end
|
# Is it a failure if the value is not changed,
|
||||||
|
# but it is the expected value?
|
||||||
|
#
|
||||||
|
# RSpec doesn't support this syntax either.
|
||||||
|
def negated_match(actual : TestExpression(T)) : MatchData forall T
|
||||||
|
{% raise "The `expect { }.to_not change { }.to()` syntax is not supported (ambiguous)." %}
|
||||||
|
end
|
||||||
|
|
||||||
# Information about the match.
|
# Specifies what the initial value of the expression must be.
|
||||||
def named_tuple
|
def from(value : T) forall T
|
||||||
{
|
raise NotImplementedError.new("ChangeToMatcher#from")
|
||||||
"expected before": NegatableMatchDataValue.new(@expected_after, true),
|
end
|
||||||
"actual before": @before,
|
|
||||||
"expected after": @expected_after,
|
|
||||||
"actual after": @actual_after,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Describes the condition that satisfies the matcher.
|
# Performs the change and reports the before and after values.
|
||||||
# This is informational and displayed to the end-user.
|
private def change(actual)
|
||||||
def message
|
before = expression.value # Retrieve the expression's initial value.
|
||||||
"#{@action_label} changed #{@expression_label} to #{@expected_after}"
|
actual.value # Invoke action that might change the expression's value.
|
||||||
end
|
after = expression.value # Retrieve the expression's value again.
|
||||||
|
|
||||||
# Describes the condition that won't satsify the matcher.
|
{before, after}
|
||||||
# This is informational and displayed to the end-user.
|
|
||||||
def negated_message
|
|
||||||
"#{@action_label} did not change #{@expression_label} to #{@expected_after}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue