Hello community, here is the log from the commit of package rubygem-rspec-mocks for openSUSE:Factory checked in at 2015-06-15 17:45:45 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-rspec-mocks (Old) and /work/SRC/openSUSE:Factory/.rubygem-rspec-mocks.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-rspec-mocks" Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-rspec-mocks/rubygem-rspec-mocks.changes 2015-03-01 14:57:59.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-rspec-mocks.new/rubygem-rspec-mocks.changes 2015-06-15 17:45:49.000000000 +0200 @@ -1,0 +2,53 @@ +Sat Jun 13 04:35:08 UTC 2015 - co...@suse.com + +- updated to version 3.3.0 + see installed Changelog.md + + ### 3.3.0 / 2015-06-12 + [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.1...v3.3.0) + + Enhancements: + + * When stubbing `new` on `MyClass` or `class_double(MyClass)`, use the + method signature from `MyClass#initialize` to verify arguments. + (Myron Marston, #886) + * Use matcher descriptions when generating description of received arguments + for mock expectation failures. (Tim Wade, #891) + * Avoid loading `stringio` unnecessarily. (Myron Marston, #894) + * Verifying doubles failure messages now distinguish between class and instance + level methods. (Tim Wade, #896, #908) + * Improve mock expectation failure messages so that it combines both + number of times and the received arguments in the output. (John Ceh, #918) + * Improve how test doubles are represented in failure messages. + (Siva Gollapalli, Myron Marston, #932) + * Rename `RSpec::Mocks::Configuration#when_declaring_verifying_double` to + `RSpec::Mocks::Configuration#before_verifying_doubles` and utilise when + verifying partial doubles. (Jon Rowe, #940) + * Use rspec-support's `ObjectFormatter` for improved formatting of + arguments in failure messages so that, for example, full time + precisions is displayed for time objects. (Gavin Miller, Myron Marston, #955) + + Bug Fixes: + + * Ensure expectations that raise eagerly also raise during RSpec verification. + This means that if exceptions are caught inside test execution the test will + still fail. (Sam Phippen, #884) + * Fix `have_received(msg).with(args).exactly(n).times` and + `receive(msg).with(args).exactly(n).times` failure messages + for when the message was received the wrong number of times with + the specified args, and also received additional times with other + arguments. Previously it confusingly listed the arguments as being + mis-matched (even when the double was allowed to receive with any + args) rather than listing the count. (John Ceh, #918) + * Fix `any_args`/`anything` support so that we avoid calling `obj == anything` + on user objects that may have improperly implemented `==` in a way that + raises errors. (Myron Marston, #924) + * Fix edge case involving stubbing the same method on a class and a subclass + which previously hit a `NoMethodError` internally in RSpec. (Myron Marston #954) + * Fix edge case where the message received count would be incremented multiple + times for one failure. (Myron Marston, #957) + * Fix failure messages for when spies received the expected message with + different arguments and also received another message. (MaurĂcio Linhares, #960) + * Silence whitespace-only diffs. (Myron Marston, #969) + +------------------------------------------------------------------- Old: ---- rspec-mocks-3.2.1.gem New: ---- rspec-mocks-3.3.0.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-rspec-mocks.spec ++++++ --- /var/tmp/diff_new_pack.3ZrT1g/_old 2015-06-15 17:45:50.000000000 +0200 +++ /var/tmp/diff_new_pack.3ZrT1g/_new 2015-06-15 17:45:50.000000000 +0200 @@ -24,7 +24,7 @@ # Name: rubygem-rspec-mocks -Version: 3.2.1 +Version: 3.3.0 Release: 0 %define mod_name rspec-mocks %define mod_full_name %{mod_name}-%{version} ++++++ rspec-mocks-3.2.1.gem -> rspec-mocks-3.3.0.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Changelog.md new/Changelog.md --- old/Changelog.md 2015-02-24 04:24:29.000000000 +0100 +++ new/Changelog.md 2015-06-12 17:05:16.000000000 +0200 @@ -1,3 +1,50 @@ +### 3.3.0 / 2015-06-12 +[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.1...v3.3.0) + +Enhancements: + +* When stubbing `new` on `MyClass` or `class_double(MyClass)`, use the + method signature from `MyClass#initialize` to verify arguments. + (Myron Marston, #886) +* Use matcher descriptions when generating description of received arguments + for mock expectation failures. (Tim Wade, #891) +* Avoid loading `stringio` unnecessarily. (Myron Marston, #894) +* Verifying doubles failure messages now distinguish between class and instance + level methods. (Tim Wade, #896, #908) +* Improve mock expectation failure messages so that it combines both + number of times and the received arguments in the output. (John Ceh, #918) +* Improve how test doubles are represented in failure messages. + (Siva Gollapalli, Myron Marston, #932) +* Rename `RSpec::Mocks::Configuration#when_declaring_verifying_double` to + `RSpec::Mocks::Configuration#before_verifying_doubles` and utilise when + verifying partial doubles. (Jon Rowe, #940) +* Use rspec-support's `ObjectFormatter` for improved formatting of + arguments in failure messages so that, for example, full time + precisions is displayed for time objects. (Gavin Miller, Myron Marston, #955) + +Bug Fixes: + +* Ensure expectations that raise eagerly also raise during RSpec verification. + This means that if exceptions are caught inside test execution the test will + still fail. (Sam Phippen, #884) +* Fix `have_received(msg).with(args).exactly(n).times` and + `receive(msg).with(args).exactly(n).times` failure messages + for when the message was received the wrong number of times with + the specified args, and also received additional times with other + arguments. Previously it confusingly listed the arguments as being + mis-matched (even when the double was allowed to receive with any + args) rather than listing the count. (John Ceh, #918) +* Fix `any_args`/`anything` support so that we avoid calling `obj == anything` + on user objects that may have improperly implemented `==` in a way that + raises errors. (Myron Marston, #924) +* Fix edge case involving stubbing the same method on a class and a subclass + which previously hit a `NoMethodError` internally in RSpec. (Myron Marston #954) +* Fix edge case where the message received count would be incremented multiple + times for one failure. (Myron Marston, #957) +* Fix failure messages for when spies received the expected message with + different arguments and also received another message. (MaurĂcio Linhares, #960) +* Silence whitespace-only diffs. (Myron Marston, #969) + ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2015-02-24 04:24:29.000000000 +0100 +++ new/README.md 2015-06-12 17:05:16.000000000 +0200 @@ -417,8 +417,8 @@ you are interested in learning more, here is some recommended reading: * Mock Objects: http://www.mockobjects.com/ -* Endo-Testing: http://stalatest.googlecode.com/svn/trunk/Literatur/mockobjects.pdf -* Mock Roles, Not Objects: http://jmock.org/oopsla2004.pdf +* Endo-Testing: http://www.ccs.neu.edu/research/demeter/related-work/extreme-programming/MockObjectsFinal.PDF +* Mock Roles, Not Objects: http://www.jmock.org/oopsla2004.pdf * Test Double: http://www.martinfowler.com/bliki/TestDouble.html * Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html * Mocks aren't stubs: http://www.martinfowler.com/articles/mocksArentStubs.html Files old/checksums.yaml.gz and new/checksums.yaml.gz differ Files old/checksums.yaml.gz.sig and new/checksums.yaml.gz.sig differ Files old/data.tar.gz.sig and new/data.tar.gz.sig differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/any_instance/chain.rb new/lib/rspec/mocks/any_instance/chain.rb --- old/lib/rspec/mocks/any_instance/chain.rb 2015-02-24 04:24:29.000000000 +0100 +++ new/lib/rspec/mocks/any_instance/chain.rb 2015-06-12 17:05:16.000000000 +0200 @@ -76,7 +76,7 @@ end def never - ErrorGenerator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated? + AnyInstance.error_generator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated? super end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/any_instance/error_generator.rb new/lib/rspec/mocks/any_instance/error_generator.rb --- old/lib/rspec/mocks/any_instance/error_generator.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/lib/rspec/mocks/any_instance/error_generator.rb 2015-06-12 17:05:16.000000000 +0200 @@ -0,0 +1,31 @@ +module RSpec + module Mocks + module AnyInstance + # @private + class ErrorGenerator < ::RSpec::Mocks::ErrorGenerator + def raise_second_instance_received_message_error(unfulfilled_expectations) + __raise "Exactly one instance should have received the following " \ + "message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}" + end + + def raise_does_not_implement_error(klass, method_name) + __raise "#{klass} does not implement ##{method_name}" + end + + def raise_message_already_received_by_other_instance_error(method_name, object_inspect, invoked_instance) + __raise "The message '#{method_name}' was received by #{object_inspect} " \ + "but has already been received by #{invoked_instance}" + end + + def raise_not_supported_with_prepend_error(method_name, problem_mod) + __raise "Using `any_instance` to stub a method (#{method_name}) that has been " \ + "defined on a prepended module (#{problem_mod}) is not supported." + end + end + + def self.error_generator + @error_generator ||= ErrorGenerator.new + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/any_instance/message_chains.rb new/lib/rspec/mocks/any_instance/message_chains.rb --- old/lib/rspec/mocks/any_instance/message_chains.rb 2015-02-24 04:24:29.000000000 +0100 +++ new/lib/rspec/mocks/any_instance/message_chains.rb 2015-06-12 17:05:16.000000000 +0200 @@ -75,9 +75,7 @@ return unless ExpectationChain === instance return if @instance_with_expectation.equal?(instance) - raise RSpec::Mocks::MockExpectationError, - "Exactly one instance should have received the following " \ - "message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}" + AnyInstance.error_generator.raise_second_instance_received_message_error(unfulfilled_expectations) end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/any_instance/recorder.rb new/lib/rspec/mocks/any_instance/recorder.rb --- old/lib/rspec/mocks/any_instance/recorder.rb 2015-02-24 04:24:29.000000000 +0100 +++ new/lib/rspec/mocks/any_instance/recorder.rb 2015-06-12 17:05:16.000000000 +0200 @@ -76,7 +76,7 @@ # @see Methods#unstub def unstub(method_name) unless @observed_methods.include?(method_name.to_sym) - raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" + AnyInstance.error_generator.raise_method_not_stubbed_error(method_name) end message_chains.remove_stub_chains_for!(method_name) stubs[method_name].clear @@ -91,9 +91,7 @@ return unless @expectation_set return if message_chains.all_expectations_fulfilled? - raise RSpec::Mocks::MockExpectationError, - "Exactly one instance should have received the following " \ - "message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}" + AnyInstance.error_generator.raise_second_instance_received_message_error(message_chains.unfulfilled_expectations) end # @private @@ -221,8 +219,7 @@ if RSpec::Mocks.configuration.verify_partial_doubles? unless public_protected_or_private_method_defined?(method_name) - raise MockExpectationError, - "#{@klass} does not implement ##{method_name}" + AnyInstance.error_generator.raise_does_not_implement_error(@klass, method_name) end end @@ -242,7 +239,9 @@ @klass.__send__(:define_method, method_name) do |*_args, &_blk| invoked_instance = recorder.instance_that_received(method_name) inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>" - raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{inspect} but has already been received by #{invoked_instance}" + AnyInstance.error_generator.raise_message_already_received_by_other_instance_error( + method_name, inspect, invoked_instance + ) end end @@ -252,9 +251,7 @@ problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) } return unless problem_mod - raise RSpec::Mocks::MockExpectationError, - "Using `any_instance` to stub a method (#{method_name}) that has been " \ - "defined on a prepended module (#{problem_mod}) is not supported." + AnyInstance.error_generator.raise_not_supported_with_prepend_error(method_name, problem_mod) end else def allow_no_prepended_module_definition_of(_method_name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/any_instance.rb new/lib/rspec/mocks/any_instance.rb --- old/lib/rspec/mocks/any_instance.rb 2015-02-24 04:24:29.000000000 +0100 +++ new/lib/rspec/mocks/any_instance.rb 2015-06-12 17:05:16.000000000 +0200 @@ -1,5 +1,6 @@ %w[ any_instance/chain + any_instance/error_generator any_instance/stub_chain any_instance/stub_chain_chain any_instance/expect_chain_chain diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/argument_list_matcher.rb new/lib/rspec/mocks/argument_list_matcher.rb --- old/lib/rspec/mocks/argument_list_matcher.rb 2015-02-24 04:24:29.000000000 +0100 +++ new/lib/rspec/mocks/argument_list_matcher.rb 2015-06-12 17:05:16.000000000 +0200 @@ -64,7 +64,7 @@ def resolve_expected_args_based_on(actual_args) return [] if [ArgumentMatchers::NoArgsMatcher::INSTANCE] == expected_args - any_args_index = expected_args.index(ArgumentMatchers::AnyArgsMatcher::INSTANCE) + any_args_index = expected_args.index { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a } return expected_args unless any_args_index replace_any_args_with_splat_of_anything(any_args_index, actual_args.count) @@ -81,10 +81,10 @@ end def ensure_expected_args_valid! - if expected_args.count(ArgumentMatchers::AnyArgsMatcher::INSTANCE) > 1 + if expected_args.count { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a } > 1 raise ArgumentError, "`any_args` can only be passed to " \ "`with` once but you have passed it multiple times." - elsif expected_args.count > 1 && expected_args.include?(ArgumentMatchers::NoArgsMatcher::INSTANCE) + elsif expected_args.count > 1 && expected_args.any? { |a| ArgumentMatchers::NoArgsMatcher::INSTANCE == a } raise ArgumentError, "`no_args` can only be passed as a " \ "singleton argument to `with` (i.e. `with(no_args)`), " \ "but you have passed additional arguments." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/configuration.rb new/lib/rspec/mocks/configuration.rb --- old/lib/rspec/mocks/configuration.rb 2015-02-24 04:24:29.000000000 +0100 +++ new/lib/rspec/mocks/configuration.rb 2015-06-12 17:05:16.000000000 +0200 @@ -103,17 +103,18 @@ # Provides a way to perform customisations when verifying doubles. # # @example - # RSpec::Mocks.configuration.when_declaring_verifying_double do |ref| + # RSpec::Mocks.configuration.before_verifying_doubles do |ref| # ref.some_method! # end - def when_declaring_verifying_double(&block) - verifying_double_declaration_callbacks << block + def before_verifying_doubles(&block) + verifying_double_callbacks << block end + alias :when_declaring_verifying_double :before_verifying_doubles # @api private # Returns an array of blocks to call when verifying doubles - def verifying_double_declaration_callbacks - @verifying_double_declaration_callbacks ||= [] + def verifying_double_callbacks + @verifying_double_callbacks ||= [] end def transfer_nested_constants? diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/error_generator.rb new/lib/rspec/mocks/error_generator.rb --- old/lib/rspec/mocks/error_generator.rb 2015-02-24 04:24:29.000000000 +0100 +++ new/lib/rspec/mocks/error_generator.rb 2015-06-12 17:05:16.000000000 +0200 @@ -1,4 +1,4 @@ -RSpec::Support.require_rspec_support 'differ' +RSpec::Support.require_rspec_support "object_formatter" module RSpec module Mocks @@ -36,9 +36,8 @@ class ErrorGenerator attr_writer :opts - def initialize(target, name) + def initialize(target=nil) @target = target - @name = name end # @private @@ -47,41 +46,26 @@ end # @private - def raise_unexpected_message_error(message, *args) - __raise "#{intro} received unexpected message :#{message}#{arg_message(*args)}" + def raise_unexpected_message_error(message, args) + __raise "#{intro} received unexpected message :#{message} with #{format_args(args)}" end # @private - def raise_unexpected_message_args_error(expectation, *args) - expected_args = format_args(*expectation.expected_args) - actual_args = format_received_args(*args) - diff = diff_message(expectation.expected_args, args) - - message = default_error_message(expectation, expected_args, actual_args) - message << "\nDiff:#{diff}" unless diff.empty? - - __raise message + def raise_unexpected_message_args_error(expectation, args_for_multiple_calls, source_id=nil) + __raise error_message(expectation, args_for_multiple_calls), nil, source_id end # @private - def raise_missing_default_stub_error(expectation, *args) - expected_args = format_args(*expectation.expected_args) - actual_args = format_received_args(*args) - diff = diff_message(expectation.expected_args, args) - - message = default_error_message(expectation, expected_args, actual_args) - message << "\nDiff:\n #{diff}" unless diff.empty? + def raise_missing_default_stub_error(expectation, args_for_multiple_calls) + message = error_message(expectation, args_for_multiple_calls) message << "\n Please stub a default value first if message might be received with other args as well. \n" __raise message end # @private - def raise_similar_message_args_error(expectation, *args_for_multiple_calls) - expected_args = format_args(*expectation.expected_args) - actual_args = args_for_multiple_calls.map { |a| format_received_args(*a) }.join(", ") - - __raise(default_error_message(expectation, expected_args, actual_args)) + def raise_similar_message_args_error(expectation, args_for_multiple_calls, backtrace_line=nil) + __raise error_message(expectation, args_for_multiple_calls), backtrace_line end def default_error_message(expectation, expected_args, actual_args) @@ -95,19 +79,37 @@ # rubocop:disable Style/ParameterLists # @private - def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, *args) + def raise_expectation_error(message, expected_received_count, argument_list_matcher, + actual_received_count, expectation_count_type, args, + backtrace_line=nil, source_id=nil) expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) - received_part = received_part_of_expectation_error(actual_received_count, *args) - __raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}" + received_part = received_part_of_expectation_error(actual_received_count, args) + __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}", backtrace_line, source_id end # rubocop:enable Style/ParameterLists # @private - def raise_unimplemented_error(doubled_module, method_name) - __raise "%s does not implement: %s" % [ - doubled_module.description, - method_name - ] + def raise_unimplemented_error(doubled_module, method_name, object) + message = case object + when InstanceVerifyingDouble + "the %s class does not implement the instance method: %s" << + if ObjectMethodReference.for(doubled_module, method_name).implemented? + ". Perhaps you meant to use `class_double` instead?" + else + "" + end + when ClassVerifyingDouble + "the %s class does not implement the class method: %s" << + if InstanceMethodReference.for(doubled_module, method_name).implemented? + ". Perhaps you meant to use `instance_double` instead?" + else + "" + end + else + "%s does not implement: %s" + end + + __raise message % [doubled_module.description, method_name] end # @private @@ -132,101 +134,131 @@ end # @private - def received_part_of_expectation_error(actual_received_count, *args) - "received: #{count_message(actual_received_count)}" + - actual_method_call_args_description(actual_received_count, args) + def describe_expectation(verb, message, expected_received_count, _actual_received_count, args) + "#{verb} #{message}#{format_args(args)} #{count_message(expected_received_count)}" end # @private - def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) - "expected: #{count_message(expected_received_count, expectation_count_type)}" + - expected_method_call_args_description(argument_list_matcher.expected_args) + def raise_out_of_order_error(message) + __raise "#{intro} received :#{message} out of order" end # @private - def actual_method_call_args_description(count, args) - method_call_args_description(args) || - if count > 0 && args.length > 0 - " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}" - else - "" - end + def raise_missing_block_error(args_to_yield) + __raise "#{intro} asked to yield |#{arg_list(args_to_yield)}| but no block was passed" end # @private - def expected_method_call_args_description(args) - method_call_args_description(args) || - if args.length > 0 - " with arguments: #{format_args(*args)}" - else - "" - end + def raise_wrong_arity_error(args_to_yield, signature) + __raise "#{intro} yielded |#{arg_list(args_to_yield)}| to block with #{signature.description}" end # @private - def method_call_args_description(args) - case args.first - when ArgumentMatchers::AnyArgsMatcher then " with any arguments" - when ArgumentMatchers::NoArgsMatcher then " with no arguments" - end + def raise_only_valid_on_a_partial_double(method) + __raise "#{intro} is a pure test double. `#{method}` is only " \ + "available on a partial double." end # @private - def describe_expectation(verb, message, expected_received_count, _actual_received_count, *args) - "#{verb} #{message}#{format_args(*args)} #{count_message(expected_received_count)}" + def raise_expectation_on_unstubbed_method(method) + __raise "#{intro} expected to have received #{method}, but that " \ + "object is not a spy or method has not been stubbed." end # @private - def raise_out_of_order_error(message) - __raise "#{intro} received :#{message} out of order" + def raise_expectation_on_mocked_method(method) + __raise "#{intro} expected to have received #{method}, but that " \ + "method has been mocked instead of stubbed or spied." end # @private - def raise_block_failed_error(message, detail) - __raise "#{intro} received :#{message} but passed block failed with: #{detail}" + def raise_double_negation_error(wrapped_expression) + __raise "Isn't life confusing enough? You've already set a " \ + "negative message expectation and now you are trying to " \ + "negate it again with `never`. What does an expression like " \ + "`#{wrapped_expression}.not_to receive(:msg).never` even mean?" end # @private - def raise_missing_block_error(args_to_yield) - __raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed" + def raise_verifying_double_not_defined_error(ref) + notify(VerifyingDoubleNotDefinedError.new( + "#{ref.description.inspect} is not a defined constant. " \ + "Perhaps you misspelt it? " \ + "Disable check with `verify_doubled_constant_names` configuration option." + )) end # @private - def raise_wrong_arity_error(args_to_yield, signature) - __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with #{signature.description}" + def raise_have_received_disallowed(type, reason) + __raise "Using #{type}(...) with the `have_received` " \ + "matcher is not supported#{reason}." end # @private - def raise_only_valid_on_a_partial_double(method) - __raise "#{intro} is a pure test double. `#{method}` is only " \ - "available on a partial double." + def raise_cant_constrain_count_for_negated_have_received_error(count_constraint) + __raise "can't use #{count_constraint} when negative" end # @private - def raise_expectation_on_unstubbed_method(method) - __raise "#{intro} expected to have received #{method}, but that " \ - "object is not a spy or method has not been stubbed." + def raise_method_not_stubbed_error(method_name) + __raise "The method `#{method_name}` was not stubbed or was already unstubbed" end # @private - def raise_expectation_on_mocked_method(method) - __raise "#{intro} expected to have received #{method}, but that " \ - "method has been mocked instead of stubbed or spied." - end + def raise_already_invoked_error(message, calling_customization) + error_message = "The message expectation for #{intro}.#{message} has already been invoked " \ + "and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \ + "customizations must be applied before it is used for the first time." - def self.raise_double_negation_error(wrapped_expression) - raise "Isn't life confusing enough? You've already set a " \ - "negative message expectation and now you are trying to " \ - "negate it again with `never`. What does an expression like " \ - "`#{wrapped_expression}.not_to receive(:msg).never` even mean?" + notify MockExpectationAlreadyInvokedError.new(error_message) end private + def received_part_of_expectation_error(actual_received_count, args) + "received: #{count_message(actual_received_count)}" + + method_call_args_description(args) do + actual_received_count > 0 && args.length > 0 + end + end + + def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher) + "expected: #{count_message(expected_received_count, expectation_count_type)}" + + method_call_args_description(argument_list_matcher.expected_args) do + argument_list_matcher.expected_args.length > 0 + end + end + + def method_call_args_description(args) + case args.first + when ArgumentMatchers::AnyArgsMatcher then " with any arguments" + when ArgumentMatchers::NoArgsMatcher then " with no arguments" + else + if yield + " with arguments: #{format_args(args)}" + else + "" + end + end + end + def unexpected_arguments_message(expected_args_string, actual_args_string) "with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}" end + def error_message(expectation, args_for_multiple_calls) + expected_args = format_args(expectation.expected_args) + actual_args = format_received_args(args_for_multiple_calls) + message = default_error_message(expectation, expected_args, actual_args) + + if args_for_multiple_calls.one? + diff = diff_message(expectation.expected_args, args_for_multiple_calls.first) + message << "\nDiff:#{diff}" unless diff.strip.empty? + end + + message + end + def diff_message(expected_args, actual_args) formatted_expected_args = expected_args.map do |x| RSpec::Support.rspec_description_for_object(x) @@ -253,47 +285,54 @@ RSpec::Support::Differ.new(:color => RSpec::Mocks.configuration.color?) end - def intro - if @name - "Double #{@name.inspect}" - elsif TestDouble === @target - "Double" - elsif Class === @target - "<#{@target.inspect} (class)>" - elsif @target - @target - else - "nil" + def intro(unwrapped=false) + case @target + when TestDouble then TestDoubleFormatter.format(@target, unwrapped) + when Class then + formatted = "#{@target.inspect} (class)" + return formatted if unwrapped + "#<#{formatted}>" + when NilClass then "nil" + else @target end end - def __raise(message) + def __raise(message, backtrace_line=nil, source_id=nil) message = opts[:message] unless opts[:message].nil? - Kernel.raise(RSpec::Mocks::MockExpectationError, message) + exception = RSpec::Mocks::MockExpectationError.new(message) + prepend_to_backtrace(exception, backtrace_line) if backtrace_line + notify exception, :source_id => source_id end - def arg_message(*args) - " with " + format_args(*args) - end - - def format_args(*args) - args.empty? ? "(no args)" : "(" + arg_list(*args) + ")" + if RSpec::Support::Ruby.jruby? + def prepend_to_backtrace(exception, line) + raise exception + rescue RSpec::Mocks::MockExpectationError => with_backtrace + with_backtrace.backtrace.unshift(line) + end + else + def prepend_to_backtrace(exception, line) + exception.set_backtrace(caller.unshift line) + end end - def arg_list(*args) - args.map { |arg| arg_has_valid_description?(arg) ? arg.description : arg.inspect }.join(", ") + def notify(*args) + RSpec::Support.notify_failure(*args) end - def arg_has_valid_description?(arg) - RSpec::Support.is_a_matcher?(arg) && arg.respond_to?(:description) + def format_args(args) + return "(no args)" if args.empty? + "(#{arg_list(args)})" end - def format_received_args(*args) - args.empty? ? "(no args)" : "(" + received_arg_list(*args) + ")" + def arg_list(args) + args.map { |arg| RSpec::Support::ObjectFormatter.format(arg) }.join(", ") end - def received_arg_list(*args) - args.map(&:inspect).join(", ") + def format_received_args(args_for_multiple_calls) + grouped_args(args_for_multiple_calls).map do |args_for_one_call, index| + "#{format_args(args_for_one_call)}#{group_count(index, args_for_multiple_calls)}" + end.join("\n ") end def count_message(count, expectation_count_type=nil) @@ -305,6 +344,19 @@ def times(count) "#{count} time#{count == 1 ? '' : 's'}" end + + def grouped_args(args) + Hash[args.group_by { |x| x }.map { |k, v| [k, v.count] }] + end + + def group_count(index, args) + " (#{times(index)})" if args.size > 1 || index > 1 + end + end + + # @private + def self.error_generator + @error_generator ||= ErrorGenerator.new end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/example_methods.rb new/lib/rspec/mocks/example_methods.rb --- old/lib/rspec/mocks/example_methods.rb 2015-02-24 04:24:29.000000000 +0100 +++ new/lib/rspec/mocks/example_methods.rb 2015-06-12 17:05:16.000000000 +0200 @@ -396,13 +396,10 @@ if RSpec::Mocks.configuration.verify_doubled_constant_names? && !ref.defined? - raise VerifyingDoubleNotDefinedError, - "#{ref.description.inspect} is not a defined constant. " \ - "Perhaps you misspelt it? " \ - "Disable check with `verify_doubled_constant_names` configuration option." + RSpec::Mocks.error_generator.raise_verifying_double_not_defined_error(ref) end - RSpec::Mocks.configuration.verifying_double_declaration_callbacks.each do |block| + RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| block.call(ref) end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/matchers/have_received.rb new/lib/rspec/mocks/matchers/have_received.rb --- old/lib/rspec/mocks/matchers/have_received.rb 2015-02-24 04:24:29.000000000 +0100 +++ new/lib/rspec/mocks/matchers/have_received.rb 2015-06-12 17:05:16.000000000 +0200 @@ -69,9 +69,7 @@ private def disallow(type, reason="") - raise RSpec::Mocks::MockExpectationError, - "Using #{type}(...) with the `have_received` " \ - "matcher is not supported#{reason}." + RSpec::Mocks.error_generator.raise_have_received_disallowed(type, reason) end def expect @@ -88,8 +86,7 @@ def ensure_count_unconstrained return unless count_constraint - raise RSpec::Mocks::MockExpectationError, - "can't use #{count_constraint} when negative" + RSpec::Mocks.error_generator.raise_cant_constrain_count_for_negated_have_received_error(count_constraint) end def count_constraint @@ -99,10 +96,10 @@ end def generate_failure_message - mock_proxy.check_for_unexpected_arguments(@expectation) - @expectation.generate_error - rescue RSpec::Mocks::MockExpectationError => error - error.message + RSpec::Support.with_failure_notifier(Proc.new { |err, _opt| return err.message }) do + mock_proxy.check_for_unexpected_arguments(@expectation) + @expectation.generate_error + end end def expected_messages_received_in_order? diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/message_chain.rb new/lib/rspec/mocks/message_chain.rb --- old/lib/rspec/mocks/message_chain.rb 2015-02-24 04:24:30.000000000 +0100 +++ new/lib/rspec/mocks/message_chain.rb 2015-06-12 17:05:16.000000000 +0200 @@ -30,10 +30,6 @@ private - def expectation(_object, _message, &_return_block) - raise NotImplementedError - end - def chain_on(object, *chain, &block) initialize(object, *chain, &block) setup_chain diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/message_expectation.rb new/lib/rspec/mocks/message_expectation.rb --- old/lib/rspec/mocks/message_expectation.rb 2015-02-24 04:24:30.000000000 +0100 +++ new/lib/rspec/mocks/message_expectation.rb 2015-06-12 17:05:16.000000000 +0200 @@ -26,11 +26,13 @@ end def verify_messages_received - InsertOntoBacktrace.line(@backtrace_line) do - unless @received - @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil) - end - end + return if @received + @error_generator.raise_expectation_error( + @message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, [], @backtrace_line + ) + end + + def unadvise(_) end end @@ -115,7 +117,7 @@ @error_generator.raise_only_valid_on_a_partial_double(:and_call_original) else warn_about_stub_override if implementation.inner_action - @implementation = AndWrapOriginalImplementation.new(@method_double.original_method, block) + @implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block) @yield_receiver_to_implementation_block = false end @@ -142,13 +144,9 @@ # allow(car).to receive(:go).and_raise(OutOfGas) # allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive") # allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz)) - def and_raise(exception=RuntimeError, message=nil) + def and_raise(*args) raise_already_invoked_error_if_necessary(__method__) - if exception.respond_to?(:exception) - exception = message ? exception.exception(message) : exception.exception - end - - self.terminal_implementation_action = Proc.new { raise exception } + self.terminal_implementation_action = Proc.new { raise(*args) } nil end @@ -177,6 +175,11 @@ def and_yield(*args, &block) raise_already_invoked_error_if_necessary(__method__) yield @eval_context = Object.new if block + + # Initialize args to yield now that it's being used, see also: comment + # in constructor. + @args_to_yield ||= [] + @args_to_yield << args self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator) self @@ -248,7 +251,7 @@ # @example # expect(car).to receive(:stop).never def never - ErrorGenerator.raise_double_negation_error("expect(obj)") if negative? + error_generator.raise_double_negation_error("expect(obj)") if negative? @expected_received_count = 0 self end @@ -371,8 +374,10 @@ @expectation_type = type @ordered = false @at_least = @at_most = @exactly = nil - @args_to_yield = [] - @failed_fast = nil + + # Initialized to nil so that we don't allocate an array for every + # mock or stub. See also comment in `and_yield`. + @args_to_yield = nil @eval_context = nil @yield_receiver_to_implementation_block = false @@ -426,9 +431,8 @@ end def verify_messages_received - InsertOntoBacktrace.line(@expected_from) do - generate_error unless expected_messages_received? || failed_fast? - end + return if expected_messages_received? + generate_error end def expected_messages_received? @@ -464,14 +468,28 @@ similar_messages << args end + def unadvise(args) + similar_messages.delete_if { |message| args.include?(message) } + end + def generate_error if similar_messages.empty? - @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args) + @error_generator.raise_expectation_error( + @message, @expected_received_count, @argument_list_matcher, + @actual_received_count, expectation_count_type, expected_args, + @expected_from, exception_source_id + ) else - @error_generator.raise_similar_message_args_error(self, *@similar_messages) + @error_generator.raise_similar_message_args_error( + self, @similar_messages, @expected_from + ) end end + def raise_unexpected_message_args_error(args_for_multiple_calls) + @error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls, exception_source_id) + end + def expectation_count_type return :at_least if @at_least return :at_most if @at_most @@ -481,7 +499,7 @@ def description_for(verb) @error_generator.describe_expectation( verb, @message, @expected_received_count, - @actual_received_count, *expected_args + @actual_received_count, expected_args ) end @@ -512,28 +530,33 @@ private + def exception_source_id + @exception_source_id ||= "#{self.class.name} #{__id__}" + end + def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block) args.unshift(orig_object) if yield_receiver_to_implementation_block? if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count)) - @actual_received_count += increment - @failed_fast = true # args are the args we actually received, @argument_list_matcher is the # list of args we were expecting - @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args) + @error_generator.raise_expectation_error( + @message, @expected_received_count, + @argument_list_matcher, + @actual_received_count + increment, + expectation_count_type, args, nil, exception_source_id + ) end @order_group.handle_order_constraint self - begin - if implementation.present? - implementation.call(*args, &block) - elsif parent_stub - parent_stub.invoke(nil, *args, &block) - end - ensure - @actual_received_count += increment + if implementation.present? + implementation.call(*args, &block) + elsif parent_stub + parent_stub.invoke(nil, *args, &block) end + ensure + @actual_received_count += increment end def has_been_invoked? @@ -543,15 +566,7 @@ def raise_already_invoked_error_if_necessary(calling_customization) return unless has_been_invoked? - error_message = "The message expectation for #{orig_object.inspect}.#{message} has already been invoked " \ - "and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \ - "customizations must be applied before it is used for the first time." - - raise MockExpectationAlreadyInvokedError, error_message - end - - def failed_fast? - @failed_fast + error_generator.raise_already_invoked_error(message, calling_customization) end def set_expected_received_count(relativity, n) @@ -698,17 +713,5 @@ "to call the original implementation, and cannot be modified further." end end - - # Insert original locations into stacktraces - # - # @private - class InsertOntoBacktrace - def self.line(location) - yield - rescue RSpec::Mocks::MockExpectationError => error - error.backtrace.insert(0, location) - Kernel.raise error - end - end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/method_double.rb new/lib/rspec/mocks/method_double.rb --- old/lib/rspec/mocks/method_double.rb 2015-02-24 04:24:30.000000000 +0100 +++ new/lib/rspec/mocks/method_double.rb 2015-06-12 17:05:16.000000000 +0200 @@ -18,20 +18,24 @@ @stubs = [] end - def original_method + def original_implementation_callable # If original method is not present, uses the `method_missing` # handler of the object. This accounts for cases where the user has not # correctly defined `respond_to?`, and also 1.8 which does not provide # method handles for missing methods even if `respond_to?` is correct. - @original_method ||= - @method_stasher.original_method || - @proxy.original_method_handle_for(method_name) || + @original_implementation_callable ||= original_method || Proc.new do |*args, &block| @object.__send__(:method_missing, @method_name, *args, &block) end end - alias_method :save_original_method!, :original_method + alias_method :save_original_implementation_callable!, :original_implementation_callable + + def original_method + @original_method ||= + @method_stasher.original_method || + @proxy.original_method_handle_for(method_name) + end # @private def visibility @@ -45,7 +49,7 @@ # @private def configure_method - @original_visibility = [visibility, method_name] + @original_visibility = visibility @method_stasher.stash unless @method_is_proxied define_proxy_method end @@ -54,7 +58,7 @@ def define_proxy_method return if @method_is_proxied - save_original_method! + save_original_implementation_callable! definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility| define_method(method_name) do |*args, &block| method_double.proxy_method_invoked(self, *args, &block) @@ -101,7 +105,7 @@ return unless @original_visibility && MethodReference.method_defined_at_any_visibility?(object_singleton_class, @method_name) - object_singleton_class.__send__(*@original_visibility) + object_singleton_class.__send__(@original_visibility, method_name) end # @private @@ -198,7 +202,7 @@ # @private def raise_method_not_stubbed_error - raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" + RSpec::Mocks.error_generator.raise_method_not_stubbed_error(method_name) end # In Ruby 2.0.0 and above prepend will alter the method lookup chain. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/method_reference.rb new/lib/rspec/mocks/method_reference.rb --- old/lib/rspec/mocks/method_reference.rb 2015-02-24 04:24:30.000000000 +0100 +++ new/lib/rspec/mocks/method_reference.rb 2015-06-12 17:05:16.000000000 +0200 @@ -6,6 +6,10 @@ # # @private class MethodReference + def self.for(object_reference, method_name) + new(object_reference, method_name) + end + def initialize(object_reference, method_name) @object_reference = object_reference @method_name = method_name @@ -133,6 +137,14 @@ # @private class ObjectMethodReference < MethodReference + def self.for(object_reference, method_name) + if ClassNewMethodReference.applies_to?(method_name) { object_reference.when_loaded { |o| o } } + ClassNewMethodReference.new(object_reference, method_name) + else + super + end + end + private def method_implemented?(object) @@ -151,5 +163,30 @@ MethodReference.method_visibility_for(object, @method_name) end end + + # When a class's `.new` method is stubbed, we want to use the method + # signature from `#initialize` because `.new`'s signature is a generic + # `def new(*args)` and it simply delegates to `#initialize` and forwards + # all args...so the method with the actually used signature is `#initialize`. + # + # This method reference implementation handles that specific case. + # @private + class ClassNewMethodReference < ObjectMethodReference + def self.applies_to?(method_name) + return false unless method_name == :new + klass = yield + return false unless klass.respond_to?(:new, true) + + # We only want to apply our special logic to normal `new` methods. + # Methods that the user has monkeyed with should be left as-is. + klass.method(:new).owner == ::Class + end + + def with_signature + @object_reference.when_loaded do |klass| + yield Support::MethodSignature.new(klass.instance_method(:initialize)) + end + end + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/proxy.rb new/lib/rspec/mocks/proxy.rb --- old/lib/rspec/mocks/proxy.rb 2015-02-24 04:24:30.000000000 +0100 +++ new/lib/rspec/mocks/proxy.rb 2015-06-12 17:05:16.000000000 +0200 @@ -14,11 +14,10 @@ end # @private - def initialize(object, order_group, name=nil, options={}) + def initialize(object, order_group, options={}) @object = object @order_group = order_group - @name = name - @error_generator = ErrorGenerator.new(object, name) + @error_generator = ErrorGenerator.new(object) @messages_received = [] @options = options @null_object = false @@ -46,8 +45,10 @@ nil end + DEFAULT_MESSAGE_EXPECTATION_OPTS = {}.freeze + # @private - def add_message_expectation(method_name, opts={}, &block) + def add_message_expectation(method_name, opts=DEFAULT_MESSAGE_EXPECTATION_OPTS, &block) location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line } meth_double = method_double_for(method_name) @@ -98,11 +99,17 @@ # @private def check_for_unexpected_arguments(expectation) - @messages_received.each do |(method_name, args, _)| - next unless expectation.matches_name_but_not_args(method_name, *args) + return if @messages_received.empty? + + return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) } - raise_unexpected_message_args_error(expectation, *args) + name_but_not_args, others = @messages_received.partition do |(method_name, args, _)| + expectation.matches_name_but_not_args(method_name, *args) end + + return if name_but_not_args.empty? && !others.empty? + + expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] }) end # @private @@ -142,6 +149,11 @@ end # @private + def messages_arg_list + @messages_received.map { |_, args, _| args } + end + + # @private def has_negative_expectation?(message) method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) } end @@ -166,16 +178,17 @@ end stub.invoke(nil, *args, &block) elsif expectation + expectation.unadvise(messages_arg_list) expectation.invoke(stub, *args, &block) elsif (expectation = find_almost_matching_expectation(message, *args)) expectation.advise(*args) if null_object? unless expectation.expected_messages_received? if null_object? || !has_negative_expectation?(message) - raise_unexpected_message_args_error(expectation, *args) + expectation.raise_unexpected_message_args_error([args]) end elsif (stub = find_almost_matching_stub(message, *args)) stub.advise(*args) - raise_missing_default_stub_error(stub, *args) + raise_missing_default_stub_error(stub, [args]) elsif Class === @object @object.superclass.__send__(message, *args, &block) else @@ -184,18 +197,13 @@ end # @private - def raise_unexpected_message_error(method_name, *args) - @error_generator.raise_unexpected_message_error method_name, *args - end - - # @private - def raise_unexpected_message_args_error(expectation, *args) - @error_generator.raise_unexpected_message_args_error(expectation, *args) + def raise_unexpected_message_error(method_name, args) + @error_generator.raise_unexpected_message_error method_name, args end # @private - def raise_missing_default_stub_error(expectation, *args) - @error_generator.raise_missing_default_stub_error(expectation, *args) + def raise_missing_default_stub_error(expectation, args_for_multiple_calls) + @error_generator.raise_missing_default_stub_error(expectation, args_for_multiple_calls) end # @private diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/test_double.rb new/lib/rspec/mocks/test_double.rb --- old/lib/rspec/mocks/test_double.rb 2015-02-24 04:24:30.000000000 +0100 +++ new/lib/rspec/mocks/test_double.rb 2015-06-12 17:05:16.000000000 +0200 @@ -39,7 +39,7 @@ # @private def inspect - "#<#{self.class}:#{'0x%x' % object_id} @name=#{@name.inspect}>" + TestDoubleFormatter.format(self) end # @private @@ -91,14 +91,14 @@ # https://github.com/jruby/jruby/issues/1398 visibility = proxy.visibility_for(message) if visibility == :private || visibility == :protected - ErrorGenerator.new(self, @name).raise_non_public_error( + ErrorGenerator.new(self).raise_non_public_error( message, visibility ) end # Required wrapping doubles in an Array on Ruby 1.9.2 raise NoMethodError if [:to_a, :to_ary].include? message - proxy.raise_unexpected_message_error(message, *args) + proxy.raise_unexpected_message_error(message, args) end def assign_stubs(stubs) @@ -112,12 +112,12 @@ end def __build_mock_proxy(order_group) - TestDoubleProxy.new(self, order_group, @name) + TestDoubleProxy.new(self, order_group) end def __raise_expired_error return false unless @__expired - ErrorGenerator.new(self, @name).raise_expired_test_double_error + ErrorGenerator.new(self).raise_expired_test_double_error end def initialize_copy(other) @@ -131,5 +131,40 @@ class Double include TestDouble end + + # @private + module TestDoubleFormatter + def self.format(dbl, unwrap=false) + format = "#{type_desc(dbl)}#{verified_module_desc(dbl)} #{name_desc(dbl)}" + return format if unwrap + "#<#{format}>" + end + + class << self + private + + def type_desc(dbl) + case dbl + when InstanceVerifyingDouble then "InstanceDouble" + when ClassVerifyingDouble then "ClassDouble" + when ObjectVerifyingDouble then "ObjectDouble" + else "Double" + end + end + + # @private + IVAR_GET = Object.instance_method(:instance_variable_get) + + def verified_module_desc(dbl) + return nil unless VerifyingDouble === dbl + "(#{IVAR_GET.bind(dbl).call(:@doubled_module).description})" + end + + def name_desc(dbl) + return "(anonymous)" unless (name = IVAR_GET.bind(dbl).call(:@name)) + name.inspect + end + end + end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/verifying_double.rb new/lib/rspec/mocks/verifying_double.rb --- old/lib/rspec/mocks/verifying_double.rb 2015-02-24 04:24:30.000000000 +0100 +++ new/lib/rspec/mocks/verifying_double.rb 2015-06-12 17:05:16.000000000 +0200 @@ -1,5 +1,4 @@ RSpec::Support.require_rspec_mocks 'verifying_proxy' -require 'stringio' module RSpec module Mocks @@ -35,8 +34,16 @@ super end + # @private + module SilentIO + def self.method_missing(*); end + def self.respond_to?(*) + true + end + end + # Redefining `__send__` causes ruby to issue a warning. - old, $stderr = $stderr, StringIO.new + old, $stderr = $stderr, SilentIO def __send__(name, *args, &block) @__sending_message = name super @@ -55,8 +62,6 @@ possible_name = args.first name = if String === possible_name || Symbol === possible_name args.shift - else - @description end super(name, *args) @@ -73,13 +78,8 @@ include TestDouble include VerifyingDouble - def initialize(doubled_module, *args) - @description = "#{doubled_module.description} (instance)" - super - end - def __build_mock_proxy(order_group) - VerifyingProxy.new(self, order_group, @name, + VerifyingProxy.new(self, order_group, @doubled_module, InstanceMethodReference ) @@ -101,13 +101,8 @@ private - def initialize(doubled_module, *args) - @description = doubled_module.description - super - end - def __build_mock_proxy(order_group) - VerifyingProxy.new(self, order_group, @name, + VerifyingProxy.new(self, order_group, @doubled_module, ObjectMethodReference ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/verifying_proxy.rb new/lib/rspec/mocks/verifying_proxy.rb --- old/lib/rspec/mocks/verifying_proxy.rb 2015-02-24 04:24:30.000000000 +0100 +++ new/lib/rspec/mocks/verifying_proxy.rb 2015-06-12 17:05:16.000000000 +0200 @@ -25,7 +25,8 @@ @error_generator.raise_unimplemented_error( @doubled_module, - method_name + method_name, + @object ) end @@ -55,8 +56,8 @@ class VerifyingProxy < TestDoubleProxy include VerifyingProxyMethods - def initialize(object, order_group, name, doubled_module, method_reference_class) - super(object, order_group, name) + def initialize(object, order_group, doubled_module, method_reference_class) + super(object, order_group) @object = object @doubled_module = doubled_module @method_reference_class = method_reference_class @@ -71,7 +72,7 @@ def method_reference @method_reference ||= Hash.new do |h, k| - h[k] = @method_reference_class.new(@doubled_module, k) + h[k] = @method_reference_class.for(@doubled_module, k) end end @@ -95,7 +96,11 @@ # A custom method double is required to pass through a way to lookup # methods to determine their parameters. @method_doubles = Hash.new do |h, k| - h[k] = VerifyingExistingMethodDouble.new(object, k, self) + h[k] = VerifyingExistingMethodDouble.for(object, k, self) + end + + RSpec::Mocks.configuration.verifying_double_callbacks.each do |block| + block.call @doubled_module end end @@ -158,16 +163,35 @@ # Trigger an eager find of the original method since if we find it any # later we end up getting a stubbed method with incorrect arity. - save_original_method! + save_original_implementation_callable! end def with_signature - yield Support::MethodSignature.new(original_method) + yield Support::MethodSignature.new(original_implementation_callable) end def unimplemented? !@valid_method end + + def self.for(object, method_name, proxy) + if ClassNewMethodReference.applies_to?(method_name) { object } + VerifyingExistingClassNewMethodDouble + else + self + end.new(object, method_name, proxy) + end + end + + # Used in place of a `VerifyingExistingMethodDouble` for the specific case + # of mocking or stubbing a `new` method on a class. In this case, we substitute + # the method signature from `#initialize` since new's signature is just `*args`. + # + # @private + class VerifyingExistingClassNewMethodDouble < VerifyingExistingMethodDouble + def with_signature + yield Support::MethodSignature.new(object.instance_method(:initialize)) + end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/rspec/mocks/version.rb new/lib/rspec/mocks/version.rb --- old/lib/rspec/mocks/version.rb 2015-02-24 04:24:30.000000000 +0100 +++ new/lib/rspec/mocks/version.rb 2015-06-12 17:05:16.000000000 +0200 @@ -3,7 +3,7 @@ # Version information for RSpec mocks. module Version # Version of RSpec mocks currently in use in SemVer format. - STRING = '3.2.1' + STRING = '3.3.0' end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2015-02-24 04:24:29.000000000 +0100 +++ new/metadata 2015-06-12 17:05:16.000000000 +0200 @@ -1,7 +1,7 @@ --- !ruby/object:Gem::Specification name: rspec-mocks version: !ruby/object:Gem::Version - version: 3.2.1 + version: 3.3.0 platform: ruby authors: - Steven Baker @@ -45,7 +45,7 @@ ZsVDj6a7lH3cNqtWXZxrb2wO38qV5AkYj8SQK7Hj3/Yui9myUX3crr+PdetazSqQ F3MdtaDehhjC -----END CERTIFICATE----- -date: 2015-02-24 00:00:00.000000000 Z +date: 2015-06-12 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rspec-support @@ -53,14 +53,14 @@ requirements: - - "~>" - !ruby/object:Gem::Version - version: 3.2.0 + version: 3.3.0 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version - version: 3.2.0 + version: 3.3.0 - !ruby/object:Gem::Dependency name: diff-lcs requirement: !ruby/object:Gem::Requirement @@ -151,6 +151,7 @@ - lib/rspec/mocks.rb - lib/rspec/mocks/any_instance.rb - lib/rspec/mocks/any_instance/chain.rb +- lib/rspec/mocks/any_instance/error_generator.rb - lib/rspec/mocks/any_instance/expect_chain_chain.rb - lib/rspec/mocks/any_instance/expectation_chain.rb - lib/rspec/mocks/any_instance/message_chains.rb @@ -207,10 +208,10 @@ - !ruby/object:Gem::Version version: '0' requirements: [] -rubyforge_project: rspec -rubygems_version: 2.4.5 +rubyforge_project: +rubygems_version: 2.2.2 signing_key: specification_version: 4 -summary: rspec-mocks-3.2.1 +summary: rspec-mocks-3.3.0 test_files: [] has_rdoc: Files old/metadata.gz.sig and new/metadata.gz.sig differ