From 72d032e13edc4b9b17bd66ef69109d581641d5c1 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 8 May 2026 01:30:46 +0200 Subject: Update to ruby/mspec@dffcdf7 --- spec/mspec/lib/mspec/matchers/base.rb | 16 ++++++ spec/mspec/lib/mspec/matchers/raise_error.rb | 56 +++++++++++++++------ spec/mspec/spec/matchers/raise_error_spec.rb | 73 +++++++++++++++++++++++----- 3 files changed, 120 insertions(+), 25 deletions(-) diff --git a/spec/mspec/lib/mspec/matchers/base.rb b/spec/mspec/lib/mspec/matchers/base.rb index d9d7f6fec0..3534520d88 100644 --- a/spec/mspec/lib/mspec/matchers/base.rb +++ b/spec/mspec/lib/mspec/matchers/base.rb @@ -36,6 +36,14 @@ class SpecPositiveOperatorMatcher < BasicObject end end + def raise(exception = ::Exception, message = nil, options = nil, &block) + matcher = ::RaiseErrorMatcher.new(exception, message, options, &block) + unless matcher.matches? @actual + expected, actual = matcher.failure_message + ::SpecExpectation.fail_with(expected, actual) + end + end + def method_missing(name, *args, &block) result = @actual.__send__(name, *args, &block) unless result @@ -70,6 +78,14 @@ class SpecNegativeOperatorMatcher < BasicObject end end + def raise(exception = ::Exception, message = nil, options = nil, &block) + matcher = ::RaiseErrorMatcher.new(exception, message, options, &block) + if matcher.matches? @actual + expected, actual = matcher.negative_failure_message + ::SpecExpectation.fail_with(expected, actual) + end + end + def method_missing(name, *args, &block) result = @actual.__send__(name, *args, &block) if result diff --git a/spec/mspec/lib/mspec/matchers/raise_error.rb b/spec/mspec/lib/mspec/matchers/raise_error.rb index 2aa3038ed1..8cba842ce3 100644 --- a/spec/mspec/lib/mspec/matchers/raise_error.rb +++ b/spec/mspec/lib/mspec/matchers/raise_error.rb @@ -1,11 +1,18 @@ class RaiseErrorMatcher FAILURE_MESSAGE_FOR_EXCEPTION = {}.compare_by_identity + UNDEF_CAUSE = Object.new attr_writer :block - def initialize(exception, message, &block) + def initialize(exception, message = nil, options = nil, &block) + if message.is_a? Hash + @message = nil + options = message + else + @message = message + end + @cause = options ? options.fetch(:cause, UNDEF_CAUSE) : UNDEF_CAUSE @exception = exception - @message = message @block = block @actual = nil end @@ -45,24 +52,45 @@ class RaiseErrorMatcher end end + def matching_cause?(exc) + case @cause + when UNDEF_CAUSE + true + else + @cause == exc.cause + end + end + def matching_exception?(exc) - matching_class?(exc) and matching_message?(exc) + matching_class?(exc) and matching_message?(exc) and matching_cause?(exc) end - def exception_class_and_message(exception_class, message) - if message - "#{exception_class} (#{message})" - else - "#{exception_class}" + def exception_class_and_message_and_cause(exception_class, message, cause) + string = "#{exception_class}" + prefixed = false + prefix = -> { prefixed ? ", " : prefixed = "(" } + + if message != nil + string << "#{prefix.()}#{message.inspect}" + end + + if cause != UNDEF_CAUSE + string << "#{prefix.()}cause: #{cause.inspect}" end + + string << ")" if prefixed + + string end def format_expected_exception - exception_class_and_message(@exception, @message) + exception_class_and_message_and_cause(@exception, @message, @cause) end def format_exception(exception) - exception_class_and_message(exception.class, exception.message) + exception_class_and_message_and_cause(exception.class, + @message == nil ? nil : exception.message, + @cause == UNDEF_CAUSE ? UNDEF_CAUSE : exception.cause) end def failure_message @@ -87,18 +115,18 @@ class RaiseErrorMatcher end module MSpecMatchers - private def raise_error(exception = Exception, message = nil, &block) - RaiseErrorMatcher.new(exception, message, &block) + private def raise_error(exception = Exception, message = nil, options = nil, &block) + RaiseErrorMatcher.new(exception, message, options, &block) end # CRuby < 4.1 has inconsistent coercion errors: # https://bugs.ruby-lang.org/issues/21864 # This matcher ignores the message on CRuby < 4.1 # and checks the message for all other cases, including other Rubies - private def raise_consistent_error(exception = Exception, message = nil, &block) + private def raise_consistent_error(exception = Exception, message = nil, options = nil, &block) if RUBY_ENGINE == "ruby" and ruby_version_is ""..."4.1" message = nil end - RaiseErrorMatcher.new(exception, message, &block) + RaiseErrorMatcher.new(exception, message, options, &block) end end diff --git a/spec/mspec/spec/matchers/raise_error_spec.rb b/spec/mspec/spec/matchers/raise_error_spec.rb index 8613eee118..3849c7dd2a 100644 --- a/spec/mspec/spec/matchers/raise_error_spec.rb +++ b/spec/mspec/spec/matchers/raise_error_spec.rb @@ -84,10 +84,10 @@ RSpec.describe RaiseErrorMatcher do matcher.matches?(Proc.new { raise exc }) rescue UnexpectedException => e expect(matcher.failure_message).to eq( - ["Expected ExpectedException (message)", "but got: UnexpectedException (message)"] + ['Expected ExpectedException("message")', 'but got: UnexpectedException("message")'] ) expect(ExceptionState.new(nil, nil, e).message).to eq( - "Expected ExpectedException (message)\nbut got: UnexpectedException (message)" + "Expected ExpectedException(\"message\")\nbut got: UnexpectedException(\"message\")" ) else raise "no exception" @@ -103,10 +103,10 @@ RSpec.describe RaiseErrorMatcher do matcher.matches?(Proc.new { raise exc }) rescue ExpectedException => e expect(matcher.failure_message).to eq( - ["Expected ExpectedException (expected)", "but got: ExpectedException (unexpected)"] + ['Expected ExpectedException("expected")', 'but got: ExpectedException("unexpected")'] ) expect(ExceptionState.new(nil, nil, e).message).to eq( - "Expected ExpectedException (expected)\nbut got: ExpectedException (unexpected)" + "Expected ExpectedException(\"expected\")\nbut got: ExpectedException(\"unexpected\")" ) else raise "no exception" @@ -122,10 +122,10 @@ RSpec.describe RaiseErrorMatcher do matcher.matches?(Proc.new { raise exc }) rescue UnexpectedException => e expect(matcher.failure_message).to eq( - ["Expected ExpectedException (expected)", "but got: UnexpectedException (unexpected)"] + ['Expected ExpectedException("expected")', 'but got: UnexpectedException("unexpected")'] ) expect(ExceptionState.new(nil, nil, e).message).to eq( - "Expected ExpectedException (expected)\nbut got: UnexpectedException (unexpected)" + "Expected ExpectedException(\"expected\")\nbut got: UnexpectedException(\"unexpected\")" ) else raise "no exception" @@ -137,7 +137,7 @@ RSpec.describe RaiseErrorMatcher do matcher = RaiseErrorMatcher.new(ExpectedException, "expected") matcher.matches?(proc) expect(matcher.failure_message).to eq( - ["Expected ExpectedException (expected)", "but no exception was raised (120 was returned)"] + ['Expected ExpectedException("expected")', "but no exception was raised (120 was returned)"] ) end @@ -146,7 +146,7 @@ RSpec.describe RaiseErrorMatcher do matcher = RaiseErrorMatcher.new(ExpectedException, "expected") matcher.matches?(proc) expect(matcher.failure_message).to eq( - ["Expected ExpectedException (expected)", "but no exception was raised (nil was returned)"] + ['Expected ExpectedException("expected")', "but no exception was raised (nil was returned)"] ) end @@ -159,7 +159,7 @@ RSpec.describe RaiseErrorMatcher do matcher = RaiseErrorMatcher.new(ExpectedException, "expected") matcher.matches?(proc) expect(matcher.failure_message).to eq( - ["Expected ExpectedException (expected)", "but no exception was raised (#(#pretty_inspect raised #) was returned)"] + ['Expected ExpectedException("expected")', 'but no exception was raised (#(#pretty_inspect raised #) was returned)'] ) end @@ -168,7 +168,7 @@ RSpec.describe RaiseErrorMatcher do matcher = RaiseErrorMatcher.new(ExpectedException, "expected") matcher.matches?(proc) expect(matcher.negative_failure_message).to eq( - ["Expected to not get ExpectedException (expected)", ""] + ['Expected to not get ExpectedException("expected")', ""] ) end @@ -177,7 +177,58 @@ RSpec.describe RaiseErrorMatcher do matcher = RaiseErrorMatcher.new(Exception, nil) matcher.matches?(proc) expect(matcher.negative_failure_message).to eq( - ["Expected to not get Exception", "but got: UnexpectedException (unexpected)"] + ['Expected to not get Exception', 'but got: UnexpectedException'] ) end + + it "matches cause if given" do + cause = RuntimeError.new("foo") + proc = -> do + raise cause + rescue + raise "bar" + end + + matcher = RaiseErrorMatcher.new(RuntimeError, cause: cause) + expect(matcher.matches?(proc)).to eq(true) + end + + it "matches message and cause if given" do + cause = RuntimeError.new("foo") + proc = -> do + raise cause + rescue + raise "bar" + end + + matcher = RaiseErrorMatcher.new(RuntimeError, "bar", cause: cause) + expect(matcher.matches?(proc)).to eq(true) + end + + it "provides useful negative failure message when cause does not match" do + cause = RuntimeError.new("bar") + proc = -> do + raise "foo" + end + + matcher = RaiseErrorMatcher.new(RuntimeError, cause: cause) + + begin + matcher.matches?(proc) + rescue RuntimeError + expect(matcher.failure_message).to eq( + ['Expected RuntimeError(cause: #)', 'but got: RuntimeError(cause: nil)'] + ) + end + + matcher = RaiseErrorMatcher.new(RuntimeError, "foo", cause: cause) + + begin + matcher.matches?(proc) + rescue RuntimeError + expect(matcher.failure_message).to eq( + ['Expected RuntimeError("foo", cause: #)', 'but got: RuntimeError("foo", cause: nil)'] + ) + end + end end -- cgit v1.2.3