From 149c0e6e4b7e6ab4de0e16a84a0f45183131c634 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Sat, 17 Dec 2022 19:19:33 -0700 Subject: [PATCH] Don't use case-matching for proc arguments A proc on the left side of === calls itself passing in the right side. This causes typing issues and is easier to avoid for now. Procs arguments are compared with standard equality (==) instead of case-equality (===). --- CHANGELOG.md | 1 + src/spectator/mocks/abstract_arguments.cr | 32 ++++++++++++++++++++++- src/spectator/mocks/arguments.cr | 14 +++++++--- src/spectator/mocks/formal_arguments.cr | 4 +-- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 645a115..248f805 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Simplify string representation of mock-related types. - Remove unnecessary redefinitions of methods when adding stub functionality to a type. - Allow metadata to be stored as nil to reduce overhead when tracking nodes without tags. +- Use normal equality (==) instead of case-equality (===) with proc arguments in stubs. ## [0.11.4] - 2022-11-27 ### Added diff --git a/src/spectator/mocks/abstract_arguments.cr b/src/spectator/mocks/abstract_arguments.cr index b03f161..b06934c 100644 --- a/src/spectator/mocks/abstract_arguments.cr +++ b/src/spectator/mocks/abstract_arguments.cr @@ -6,11 +6,41 @@ module Spectator to_s(io) end + # Utility method for comparing two tuples considering special types. + private def compare_tuples(a : Tuple, b : Tuple) + return false if a.size != b.size + + a.zip(b) do |a_value, b_value| + if a_value.is_a?(Proc) + # Using procs as argument matchers isn't supported currently. + # Compare directly instead. + return false unless a_value == b_value + else + return false unless a_value === b_value + end + end + true + end + + # Utility method for comparing two tuples considering special types. + # Supports nilable tuples (ideal for splats). + private def compare_tuples(a : Tuple?, b : Tuple?) + return false if a.nil? ^ b.nil? + + compare_tuples(a.not_nil!, b.not_nil!) + end + # Utility method for comparing two named tuples ignoring order. private def compare_named_tuples(a : NamedTuple, b : NamedTuple) a.each do |k, v1| v2 = b.fetch(k) { return false } - return false unless v1 === v2 + if v1.is_a?(Proc) + # Using procs as argument matchers isn't supported currently. + # Compare directly instead. + return false unless v1 == v2 + else + return false unless v1 === v2 + end end true end diff --git a/src/spectator/mocks/arguments.cr b/src/spectator/mocks/arguments.cr index fcb74f4..2638e77 100644 --- a/src/spectator/mocks/arguments.cr +++ b/src/spectator/mocks/arguments.cr @@ -81,7 +81,7 @@ module Spectator # Checks if another set of arguments matches this set of arguments. def ===(other : Arguments) - positional === other.positional && compare_named_tuples(kwargs, other.kwargs) + compare_tuples(positional, other.positional) && compare_named_tuples(kwargs, other.kwargs) end # :ditto: @@ -95,13 +95,21 @@ module Spectator v1 = positional[i] i += 1 - return false unless v1 === v2 + if v1.is_a?(Proc) + return false unless v1 == v2 + else + return false unless v1 === v2 + end end other.splat.try &.each do |v2| v1 = positional.fetch(i) { return false } i += 1 - return false unless v1 === v2 + if v1.is_a?(Proc) + return false unless v1 == v2 + else + return false unless v1 === v2 + end end i == positional.size diff --git a/src/spectator/mocks/formal_arguments.cr b/src/spectator/mocks/formal_arguments.cr index e440214..1c0ca69 100644 --- a/src/spectator/mocks/formal_arguments.cr +++ b/src/spectator/mocks/formal_arguments.cr @@ -122,12 +122,12 @@ module Spectator # Checks if another set of arguments matches this set of arguments. def ===(other : Arguments) - positional === other.positional && compare_named_tuples(kwargs, other.kwargs) + compare_tuples(positional, other.positional) && compare_named_tuples(kwargs, other.kwargs) end # :ditto: def ===(other : FormalArguments) - compare_named_tuples(args, other.args) && splat === other.splat && compare_named_tuples(kwargs, other.kwargs) + compare_named_tuples(args, other.args) && compare_tuples(splat, other.splat) && compare_named_tuples(kwargs, other.kwargs) end end end