From 2eacf06b07be34237b40b6c02cdea270e414eb6b Mon Sep 17 00:00:00 2001 From: James Mead Date: Tue, 30 Jul 2024 10:31:40 +0100 Subject: [PATCH 1/4] Backtrace opening quote change in Ruby v3.4 See https://bugs.ruby-lang.org/issues/16495. --- lib/mocha/ruby_version.rb | 1 + test/acceptance/keyword_argument_matching_test.rb | 13 +++++++++---- .../positional_or_keyword_hash_test.rb | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/mocha/ruby_version.rb b/lib/mocha/ruby_version.rb index 124eb4729..8bcf79737 100644 --- a/lib/mocha/ruby_version.rb +++ b/lib/mocha/ruby_version.rb @@ -1,3 +1,4 @@ module Mocha RUBY_V27_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.7') + RUBY_V34_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('3.4') end diff --git a/test/acceptance/keyword_argument_matching_test.rb b/test/acceptance/keyword_argument_matching_test.rb index 8988d3905..e3372104a 100644 --- a/test/acceptance/keyword_argument_matching_test.rb +++ b/test/acceptance/keyword_argument_matching_test.rb @@ -3,6 +3,7 @@ require 'deprecation_disabler' require 'execution_point' require 'mocha/deprecation' +require 'mocha/ruby_version' class KeywordArgumentMatchingTest < Mocha::TestCase include AcceptanceTest @@ -24,7 +25,8 @@ def test_should_match_hash_parameter_with_keyword_args mock.method({ key: 42 }) # rubocop:disable Style/BracesAroundHashParameters end if Mocha::RUBY_V27_PLUS - location = "#{execution_point.file_name}:#{execution_point.line_number}:in `block in #{test_name}'" + opening_quote = Mocha::RUBY_V34_PLUS ? "'" : '`' + location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}block in #{test_name}'" assert_includes Mocha::Deprecation.messages.last, "Expectation defined at #{location} expected keyword arguments (key: 42)" assert_includes Mocha::Deprecation.messages.last, 'but received positional hash ({:key => 42})' end @@ -54,7 +56,8 @@ def test_should_match_hash_parameter_with_splatted_keyword_args mock.method({ key: 42 }) # rubocop:disable Style/BracesAroundHashParameters end if Mocha::RUBY_V27_PLUS - location = "#{execution_point.file_name}:#{execution_point.line_number}:in `block in #{test_name}'" + opening_quote = Mocha::RUBY_V34_PLUS ? "'" : '`' + location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}block in #{test_name}'" assert_includes Mocha::Deprecation.messages.last, "Expectation defined at #{location} expected keyword arguments (key: 42)" assert_includes Mocha::Deprecation.messages.last, 'but received positional hash ({:key => 42})' end @@ -102,7 +105,8 @@ def test_should_match_positional_and_keyword_args_with_last_positional_hash mock.method(1, key: 42) end if Mocha::RUBY_V27_PLUS - location = "#{execution_point.file_name}:#{execution_point.line_number}:in `block in #{test_name}'" + opening_quote = Mocha::RUBY_V34_PLUS ? "'" : '`' + location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}block in #{test_name}'" assert_includes Mocha::Deprecation.messages.last, "Expectation defined at #{location} expected positional hash ({:key => 42})" assert_includes Mocha::Deprecation.messages.last, 'but received keyword arguments (key: 42)' end @@ -132,7 +136,8 @@ def test_should_match_last_positional_hash_with_keyword_args mock.method(1, { key: 42 }) # rubocop:disable Style/BracesAroundHashParameters end if Mocha::RUBY_V27_PLUS - location = "#{execution_point.file_name}:#{execution_point.line_number}:in `block in #{test_name}'" + opening_quote = Mocha::RUBY_V34_PLUS ? "'" : '`' + location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}block in #{test_name}'" assert_includes Mocha::Deprecation.messages.last, "Expectation defined at #{location} expected keyword arguments (key: 42)" assert_includes Mocha::Deprecation.messages.last, 'but received positional hash ({:key => 42})' 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 591a01b9e..dbc874e5f 100644 --- a/test/unit/parameter_matchers/positional_or_keyword_hash_test.rb +++ b/test/unit/parameter_matchers/positional_or_keyword_hash_test.rb @@ -6,6 +6,7 @@ require 'mocha/parameter_matchers/instance_methods' require 'mocha/inspect' require 'mocha/expectation' +require 'mocha/ruby_version' class PositionalOrKeywordHashTest < Mocha::TestCase include Mocha::ParameterMatchers @@ -69,7 +70,8 @@ def test_should_match_hash_arg_with_keyword_args_but_display_deprecation_warning return unless Mocha::RUBY_V27_PLUS message = Mocha::Deprecation.messages.last - location = "#{execution_point.file_name}:#{execution_point.line_number}:in `new'" + opening_quote = Mocha::RUBY_V34_PLUS ? "'" : '`' + location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}new'" assert_includes message, "Expectation defined at #{location} expected keyword arguments (key_1: 1, key_2: 2)" assert_includes message, 'but received positional hash ({:key_1 => 1, :key_2 => 2})' assert_includes message, 'These will stop matching when strict keyword argument matching is enabled.' @@ -85,7 +87,8 @@ def test_should_match_keyword_args_with_hash_arg_but_display_deprecation_warning return unless Mocha::RUBY_V27_PLUS message = Mocha::Deprecation.messages.last - location = "#{execution_point.file_name}:#{execution_point.line_number}:in `new'" + opening_quote = Mocha::RUBY_V34_PLUS ? "'" : '`' + location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}new'" assert_includes message, "Expectation defined at #{location} expected positional hash ({:key_1 => 1, :key_2 => 2})" assert_includes message, 'but received keyword arguments (key_1: 1, key_2: 2)' assert_includes message, 'These will stop matching when strict keyword argument matching is enabled.' From c4fe850bc8f5ef2f44013ff54505b8b7ef6a9861 Mon Sep 17 00:00:00 2001 From: James Mead Date: Tue, 30 Jul 2024 10:31:40 +0100 Subject: [PATCH 2/4] Backtrace method description change in Ruby v3.4 See https://bugs.ruby-lang.org/issues/19117. --- .../parameter_matchers/positional_or_keyword_hash_test.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 dbc874e5f..41b709eef 100644 --- a/test/unit/parameter_matchers/positional_or_keyword_hash_test.rb +++ b/test/unit/parameter_matchers/positional_or_keyword_hash_test.rb @@ -71,7 +71,8 @@ def test_should_match_hash_arg_with_keyword_args_but_display_deprecation_warning message = Mocha::Deprecation.messages.last opening_quote = Mocha::RUBY_V34_PLUS ? "'" : '`' - location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}new'" + method_description = Mocha::RUBY_V34_PLUS ? 'Class#new' : 'new' + location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}#{method_description}'" assert_includes message, "Expectation defined at #{location} expected keyword arguments (key_1: 1, key_2: 2)" assert_includes message, 'but received positional hash ({:key_1 => 1, :key_2 => 2})' assert_includes message, 'These will stop matching when strict keyword argument matching is enabled.' @@ -88,7 +89,8 @@ def test_should_match_keyword_args_with_hash_arg_but_display_deprecation_warning message = Mocha::Deprecation.messages.last opening_quote = Mocha::RUBY_V34_PLUS ? "'" : '`' - location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}new'" + method_description = Mocha::RUBY_V34_PLUS ? 'Class#new' : 'new' + location = "#{execution_point.file_name}:#{execution_point.line_number}:in #{opening_quote}#{method_description}'" assert_includes message, "Expectation defined at #{location} expected positional hash ({:key_1 => 1, :key_2 => 2})" assert_includes message, 'but received keyword arguments (key_1: 1, key_2: 2)' assert_includes message, 'These will stop matching when strict keyword argument matching is enabled.' From 55dd0fca85a693d85b95e3fdbf07750988f8e48a Mon Sep 17 00:00:00 2001 From: James Mead Date: Tue, 30 Jul 2024 11:12:47 +0100 Subject: [PATCH 3/4] Fix frozen string literal warning in Mockery Thanks to @radville for pointing this out in #669. I've tackled it slightly differently by using an Array to build up the lines and then joining them into a String at the end rather than calling `String#dup` multiple times. Previously I was seeing a lot of the following warnings when running the tests under Ruby v3.4: lib/mocha/mockery.rb:118: warning: literal string will be frozen in the future --- lib/mocha/mockery.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/mocha/mockery.rb b/lib/mocha/mockery.rb index 26aae88b7..92b956ece 100644 --- a/lib/mocha/mockery.rb +++ b/lib/mocha/mockery.rb @@ -114,11 +114,11 @@ def sequences end def mocha_inspect - message = '' - message << "unsatisfied expectations:\n- #{unsatisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if unsatisfied_expectations.any? - message << "satisfied expectations:\n- #{satisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if satisfied_expectations.any? - message << "states:\n- #{state_machines.map(&:mocha_inspect).join("\n- ")}\n" if state_machines.any? - message + lines = [] + lines << "unsatisfied expectations:\n- #{unsatisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if unsatisfied_expectations.any? + lines << "satisfied expectations:\n- #{satisfied_expectations.map(&:mocha_inspect).join("\n- ")}\n" if satisfied_expectations.any? + lines << "states:\n- #{state_machines.map(&:mocha_inspect).join("\n- ")}\n" if state_machines.any? + lines.join end def on_stubbing(object, method) From ce2ae260bb6efb868bb3af703719db0f9ad891fb Mon Sep 17 00:00:00 2001 From: James Mead Date: Tue, 30 Jul 2024 11:12:47 +0100 Subject: [PATCH 4/4] Fix frozen string literal warning in InstanceMethod I've mostly fixed this by using `Object.new` instead of instances of `String` to act as partial mocks. I've also had to disable one test which is specifically stubbing a method on an instance of `String` when the Ruby version is v3.4 or higher. This is OK, because we already have behaviour in place to prevent stubbing of a method on a frozen object [1,2], so the behaviour under test would be moot at that point. Previously I was seeing a lot of the following warnings when running the tests under Ruby v3.4: lib/mocha/instance_method.rb:16: warning: literal string will be frozen in the future [1]: https://github.com/freerange/mocha/blob/5c7d14cb7d779bd22da3dbfe9d16ed1091ace7a1/lib/mocha/object_methods.rb#L75-L77 [2]: https://github.com/freerange/mocha/blob/5c7d14cb7d779bd22da3dbfe9d16ed1091ace7a1/lib/mocha/object_methods.rb#L121-L123 --- test/acceptance/failure_messages_test.rb | 10 ++++++---- test/acceptance/partial_mocks_test.rb | 8 ++++---- test/acceptance/sequence_block_test.rb | 8 ++++---- test/acceptance/sequence_test.rb | 8 ++++---- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/test/acceptance/failure_messages_test.rb b/test/acceptance/failure_messages_test.rb index 281c4cb76..38640d179 100644 --- a/test/acceptance/failure_messages_test.rb +++ b/test/acceptance/failure_messages_test.rb @@ -52,11 +52,13 @@ def test_should_display_mock_address_when_expectation_was_on_unnamed_mock assert_match Regexp.new("#"), test_result.failures[0].message end - def test_should_display_string_when_expectation_was_on_string - test_result = run_as_test do - 'Foo'.expects(:bar) + unless Mocha::RUBY_V34_PLUS + def test_should_display_string_when_expectation_was_on_string + test_result = run_as_test do + 'Foo'.expects(:bar) + end + assert_match Regexp.new(%("Foo")), test_result.failures[0].message end - assert_match Regexp.new(%("Foo")), test_result.failures[0].message end def test_should_display_that_block_was_expected diff --git a/test/acceptance/partial_mocks_test.rb b/test/acceptance/partial_mocks_test.rb index 5ccaccb3b..2c4c61ad8 100644 --- a/test/acceptance/partial_mocks_test.rb +++ b/test/acceptance/partial_mocks_test.rb @@ -13,8 +13,8 @@ def teardown def test_should_pass_if_all_expectations_are_satisfied test_result = run_as_test do - partial_mock_one = 'partial_mock_one' - partial_mock_two = 'partial_mock_two' + partial_mock_one = Object.new + partial_mock_two = Object.new partial_mock_one.expects(:first) partial_mock_one.expects(:second) @@ -29,8 +29,8 @@ def test_should_pass_if_all_expectations_are_satisfied def test_should_fail_if_all_expectations_are_not_satisfied test_result = run_as_test do - partial_mock_one = 'partial_mock_one' - partial_mock_two = 'partial_mock_two' + partial_mock_one = Object.new + partial_mock_two = Object.new partial_mock_one.expects(:first) partial_mock_one.expects(:second) diff --git a/test/acceptance/sequence_block_test.rb b/test/acceptance/sequence_block_test.rb index 86513cc9f..03bad0fa1 100644 --- a/test/acceptance/sequence_block_test.rb +++ b/test/acceptance/sequence_block_test.rb @@ -76,8 +76,8 @@ def test_should_allow_invocations_in_sequence_even_if_expected_on_different_mock def test_should_constrain_invocations_to_occur_in_expected_order_even_if_expected_on_partial_mocks test_result = run_as_test do - partial_mock_one = '1' - partial_mock_two = '2' + partial_mock_one = Object.new + partial_mock_two = Object.new sequence('one') do partial_mock_one.expects(:first) @@ -93,8 +93,8 @@ def test_should_constrain_invocations_to_occur_in_expected_order_even_if_expecte def test_should_allow_invocations_in_sequence_even_if_expected_on_partial_mocks test_result = run_as_test do - partial_mock_one = '1' - partial_mock_two = '2' + partial_mock_one = Object.new + partial_mock_two = Object.new sequence('one') do partial_mock_one.expects(:first) diff --git a/test/acceptance/sequence_test.rb b/test/acceptance/sequence_test.rb index 52bcd6145..7d915a616 100644 --- a/test/acceptance/sequence_test.rb +++ b/test/acceptance/sequence_test.rb @@ -72,8 +72,8 @@ def test_should_allow_invocations_in_sequence_even_if_expected_on_different_mock def test_should_constrain_invocations_to_occur_in_expected_order_even_if_expected_on_partial_mocks test_result = run_as_test do - partial_mock_one = '1' - partial_mock_two = '2' + partial_mock_one = Object.new + partial_mock_two = Object.new sequence = sequence('one') partial_mock_one.expects(:first).in_sequence(sequence) @@ -88,8 +88,8 @@ def test_should_constrain_invocations_to_occur_in_expected_order_even_if_expecte def test_should_allow_invocations_in_sequence_even_if_expected_on_partial_mocks test_result = run_as_test do - partial_mock_one = '1' - partial_mock_two = '2' + partial_mock_one = Object.new + partial_mock_two = Object.new sequence = sequence('one') partial_mock_one.expects(:first).in_sequence(sequence)