FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Ensure core exceptions get raised with $! as cause by headius · Pull Request #9409 · jruby/jruby · GitHub

/ jruby Public
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .java  (1) .rb  (7) All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
3 changes: 3 additions & 0 deletions core/src/main/java/org/jruby/Ruby.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -4243,6 +4243,9 @@ public RaiseException newNoMethodError(String message, IRubyObject recv, String
IRubyObject msg = new RubyNameError.RubyNameErrorMessage(this, message, recv, nameStr);
RubyException err = RubyNoMethodError.newNoMethodError(getNoMethodError(), msg, nameStr, args, privateCall);

// set cause since we are preparing to raise
err.setCause(getCurrentContext().getErrorInfo());

return err.toThrowable();
}

Expand Down
56 changes: 42 additions & 14 deletions spec/mspec/lib/mspec/matchers/raise_error.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -45,24 +52,45 @@ def matching_message?(exc)
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
Expand All @@ -87,18 +115,18 @@ def negative_failure_message
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
73 changes: 62 additions & 11 deletions spec/mspec/spec/matchers/raise_error_spec.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ class UnexpectedException < Exception; end
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"
Expand All @@ -103,10 +103,10 @@ class UnexpectedException < Exception; end
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"
Expand All @@ -122,10 +122,10 @@ class UnexpectedException < Exception; end
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"
Expand All @@ -137,7 +137,7 @@ class UnexpectedException < Exception; end
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

Expand All @@ -146,7 +146,7 @@ class UnexpectedException < Exception; end
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

Expand All @@ -159,7 +159,7 @@ def result.pretty_inspect
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc)
expect(matcher.failure_message).to eq(
["Expected ExpectedException (expected)", "but no exception was raised (#<Object>(#pretty_inspect raised #<ArgumentError: bad>) was returned)"]
['Expected ExpectedException("expected")', 'but no exception was raised (#<Object>(#pretty_inspect raised #<ArgumentError: bad>) was returned)']
)
end

Expand All @@ -168,7 +168,7 @@ def result.pretty_inspect
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

Expand All @@ -177,7 +177,58 @@ def result.pretty_inspect
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:)
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:)
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:)

begin
matcher.matches?(proc)
rescue RuntimeError
expect(matcher.failure_message).to eq(
['Expected RuntimeError(cause: #<RuntimeError: bar>)', 'but got: RuntimeError(cause: nil)']
)
end

matcher = RaiseErrorMatcher.new(RuntimeError, "foo", cause:)

begin
matcher.matches?(proc)
rescue RuntimeError
expect(matcher.failure_message).to eq(
['Expected RuntimeError("foo", cause: #<RuntimeError: bar>)', 'but got: RuntimeError("foo", cause: nil)']
)
end
Comment thread
headius marked this conversation as resolved.
end
end
54 changes: 18 additions & 36 deletions spec/ruby/core/exception/cause_spec.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,35 @@
it "returns the active exception when an exception is raised" do
begin
raise Exception, "the cause"
rescue Exception
begin
rescue Exception => cause
-> {
raise RuntimeError, "the consequence"
rescue RuntimeError => e
e.should be_an_instance_of(RuntimeError)
e.message.should == "the consequence"

e.cause.should be_an_instance_of(Exception)
e.cause.message.should == "the cause"
end
}.should raise_error(RuntimeError, "the consequence", cause:)
end
end

it "is set for user errors caused by internal errors" do
-> {
begin
1 / 0
rescue
raise "foo"
end
}.should raise_error(RuntimeError) { |e|
e.cause.should be_kind_of(ZeroDivisionError)
}
begin
1 / 0
rescue => cause
-> { raise "foo" }.should raise_error(RuntimeError, cause:)
end
end

it "is set for internal errors caused by user errors" do
cause = RuntimeError.new "cause"
-> {
begin
raise cause
rescue
1 / 0
end
}.should raise_error(ZeroDivisionError) { |e|
e.cause.should equal(cause)
}
begin
raise cause
rescue
-> { 1 / 0 }.should raise_error(ZeroDivisionError, cause:)
end
end

it "is not set to the exception itself when it is re-raised" do
-> {
begin
raise RuntimeError
rescue RuntimeError => e
raise e
end
}.should raise_error(RuntimeError) { |e|
e.cause.should == nil
}
begin
raise RuntimeError
rescue RuntimeError => e
-> { raise e }.should raise_error(RuntimeError, cause: nil)
end
end
end
4 changes: 2 additions & 2 deletions spec/ruby/core/exception/dup_spec.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ class << @obj

it "does copy the cause" do
begin
raise StandardError, "the cause"
raise StandardError
rescue StandardError => cause
begin
raise RuntimeError, "the consequence"
raise RuntimeError
rescue RuntimeError => e
e.cause.should equal(cause)
e.dup.cause.should equal(cause)
Expand Down
8 changes: 8 additions & 0 deletions spec/ruby/core/kernel/method_spec.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ def method_missing(m)
name = mock("method-name")
name.should_receive(:to_str).and_raise(NoMethodError)
-> { Object.method(name) }.should raise_error(NoMethodError)

name = mock("method-name")
name.should_receive(:to_str).and_raise(NoMethodError)
begin
raise RuntimeError.new
rescue => cause
-> { Object.method(name) }.should raise_error(NoMethodError, cause:)
end
end
end
end
31 changes: 26 additions & 5 deletions spec/ruby/language/send_spec.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -212,17 +212,38 @@ def foobar; 200; end
o.args.should == [1,2]
end

it "raises NameError if invoked as a vcall" do
-> { no_such_method }.should raise_error NameError
describe "if invoked as a vcall" do
it "raises NameError" do
-> { no_such_method }.should raise_error NameError
end

it "raises NameError with $! as a cause" do
begin
raise RuntimeError.new
rescue => cause
-> { no_such_method }.should raise_error(NameError, cause:)
end
end
end

it "should omit the method_missing call from the backtrace for NameError" do
-> { no_such_method }.should raise_error { |e| e.backtrace.first.should_not include("method_missing") }
end

it "raises NoMethodError if invoked as an unambiguous method call" do
-> { no_such_method() }.should raise_error NoMethodError
-> { no_such_method(1,2,3) }.should raise_error NoMethodError
describe "if invoked as an unambiguous method call" do
it "raises NoMethodError" do
-> { no_such_method() }.should raise_error NoMethodError
-> { no_such_method(1,2,3) }.should raise_error NoMethodError
end

it "raises NoMethodError with $! as a cause" do
begin
raise
rescue => cause
-> { no_such_method() }.should raise_error(NoMethodError, cause:)
-> { no_such_method(1,2,3) }.should raise_error(NoMethodError, cause:)
end
end
end

it "should omit the method_missing call from the backtrace for NoMethodError" do
Expand Down
Loading
Loading

Back | FazBrowse Home | New Git URL