From a8555d84637f561c27d56c520db610acc381f0c5 Mon Sep 17 00:00:00 2001 From: James Mead Date: Thu, 13 Oct 2022 14:43:13 +0100 Subject: [PATCH] Deprecate hash args that won't strictly match --- .../positional_or_keyword_hash.rb | 25 ++++++++++++++++--- .../keyword_argument_matching_test.rb | 18 ++++++++++--- .../positional_or_keyword_hash_test.rb | 25 ++++++++++++++++--- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb b/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb index f8e9f8fba..005dfb934 100644 --- a/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb +++ b/lib/mocha/parameter_matchers/positional_or_keyword_hash.rb @@ -1,4 +1,5 @@ require 'mocha/configuration' +require 'mocha/deprecation' require 'mocha/parameter_matchers/base' module Mocha @@ -9,14 +10,24 @@ def initialize(value) @value = value end - def matches?(available_parameters) + def matches?(available_parameters) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity parameter, is_last_parameter = extract_parameter(available_parameters) return false unless parameter.is_a?(Hash) + return false unless parameter == @value - if is_last_parameter && Mocha.configuration.strict_keyword_argument_matching? - return false unless ::Hash.ruby2_keywords_hash?(parameter) == ::Hash.ruby2_keywords_hash?(@value) + if is_last_parameter && (::Hash.ruby2_keywords_hash?(parameter) != ::Hash.ruby2_keywords_hash?(@value)) + return false if Mocha.configuration.strict_keyword_argument_matching? + + if Mocha::RUBY_V27_PLUS + if ::Hash.ruby2_keywords_hash?(@value) + deprecation_warning("Expected keyword arguments (#{mocha_inspect}), but received positional hash (#{parameter.mocha_inspect}).") + else + deprecation_warning("Expected positional hash (#{mocha_inspect}), but received keyword arguments (#{parameter.mocha_inspect}).") + end + end end - parameter == @value + + true end def mocha_inspect @@ -28,6 +39,12 @@ def mocha_inspect def extract_parameter(available_parameters) [available_parameters.shift, available_parameters.empty?] end + + def deprecation_warning(details) + sentence1 = 'These will stop matching when strict keyword argument matching is enabled.' + sentence2 = 'See the documentation for Mocha::Configuration#strict_keyword_argument_matching=.' + Deprecation.warning([details, sentence1, sentence2].join(' ')) + end end end end diff --git a/test/acceptance/keyword_argument_matching_test.rb b/test/acceptance/keyword_argument_matching_test.rb index 0adaf59e7..cf38370fb 100644 --- a/test/acceptance/keyword_argument_matching_test.rb +++ b/test/acceptance/keyword_argument_matching_test.rb @@ -1,5 +1,7 @@ require File.expand_path('../acceptance_test_helper', __FILE__) +require 'deprecation_disabler' + class KeywordArgumentMatchingTest < Mocha::TestCase include AcceptanceTest @@ -15,7 +17,9 @@ def test_should_match_hash_parameter_with_keyword_args test_result = run_as_test do mock = mock() mock.expects(:method).with(:key => 42) - mock.method({ :key => 42 }) # rubocop:disable Style/BracesAroundHashParameters + DeprecationDisabler.disable_deprecations do + mock.method({ :key => 42 }) # rubocop:disable Style/BracesAroundHashParameters + end end assert_passed(test_result) end @@ -37,7 +41,9 @@ def test_should_match_hash_parameter_with_splatted_keyword_args test_result = run_as_test do mock = mock() mock.expects(:method).with(**{ :key => 42 }) - mock.method({ :key => 42 }) # rubocop:disable Style/BracesAroundHashParameters + DeprecationDisabler.disable_deprecations do + mock.method({ :key => 42 }) # rubocop:disable Style/BracesAroundHashParameters + end end assert_passed(test_result) end @@ -77,7 +83,9 @@ def test_should_match_positional_and_keyword_args_with_last_positional_hash test_result = run_as_test do mock = mock() mock.expects(:method).with(1, { :key => 42 }) # rubocop:disable Style/BracesAroundHashParameters - mock.method(1, :key => 42) + DeprecationDisabler.disable_deprecations do + mock.method(1, :key => 42) + end end assert_passed(test_result) end @@ -99,7 +107,9 @@ def test_should_match_last_positional_hash_with_keyword_args test_result = run_as_test do mock = mock() mock.expects(:method).with(1, :key => 42) - mock.method(1, { :key => 42 }) # rubocop:disable Style/BracesAroundHashParameters + DeprecationDisabler.disable_deprecations do + mock.method(1, { :key => 42 }) # rubocop:disable Style/BracesAroundHashParameters + end end assert_passed(test_result) end diff --git a/test/unit/parameter_matchers/positional_or_keyword_hash_test.rb b/test/unit/parameter_matchers/positional_or_keyword_hash_test.rb index d36f8600b..6194115f0 100644 --- a/test/unit/parameter_matchers/positional_or_keyword_hash_test.rb +++ b/test/unit/parameter_matchers/positional_or_keyword_hash_test.rb @@ -1,5 +1,6 @@ require File.expand_path('../../../test_helper', __FILE__) +require 'deprecation_disabler' require 'mocha/parameter_matchers/positional_or_keyword_hash' require 'mocha/parameter_matchers/instance_methods' require 'mocha/inspect' @@ -32,14 +33,30 @@ def test_should_match_keyword_args_with_keyword_args assert matcher.matches?([Hash.ruby2_keywords_hash({ :key_1 => 1, :key_2 => 2 })]) # rubocop:disable Style/BracesAroundHashParameters end - def test_should_match_hash_arg_with_keyword_args + def test_should_match_hash_arg_with_keyword_args_but_display_deprecation_warning_if_appropriate matcher = Hash.ruby2_keywords_hash({ :key_1 => 1, :key_2 => 2 }).to_matcher # rubocop:disable Style/BracesAroundHashParameters - assert matcher.matches?([{ :key_1 => 1, :key_2 => 2 }]) + DeprecationDisabler.disable_deprecations do + assert matcher.matches?([{ :key_1 => 1, :key_2 => 2 }]) + end + return unless Mocha::RUBY_V27_PLUS + + message = Mocha::Deprecation.messages.last + assert_includes message, 'Expected keyword arguments (:key_1 => 1, :key_2 => 2), but received positional hash ({:key_1 => 1, :key_2 => 2}).' + assert_includes message, 'These will stop matching when strict keyword argument matching is enabled.' + assert_includes message, 'See the documentation for Mocha::Configuration#strict_keyword_argument_matching=.' end - def test_should_match_keyword_args_with_hash_arg + def test_should_match_keyword_args_with_hash_arg_but_display_deprecation_warning_if_appropriate matcher = { :key_1 => 1, :key_2 => 2 }.to_matcher - assert matcher.matches?([Hash.ruby2_keywords_hash({ :key_1 => 1, :key_2 => 2 })]) # rubocop:disable Style/BracesAroundHashParameters + DeprecationDisabler.disable_deprecations do + assert matcher.matches?([Hash.ruby2_keywords_hash({ :key_1 => 1, :key_2 => 2 })]) # rubocop:disable Style/BracesAroundHashParameters + end + return unless Mocha::RUBY_V27_PLUS + + message = Mocha::Deprecation.messages.last + assert_includes message, 'Expected positional hash ({:key_1 => 1, :key_2 => 2}), but received keyword arguments (:key_1 => 1, :key_2 => 2).' + assert_includes message, 'These will stop matching when strict keyword argument matching is enabled.' + assert_includes message, 'See the documentation for Mocha::Configuration#strict_keyword_argument_matching=.' end if Mocha::RUBY_V27_PLUS