diff --git a/core/fiber/fixtures/classes.rb b/core/fiber/fixtures/classes.rb new file mode 100644 index 0000000000..c00facd6e1 --- /dev/null +++ b/core/fiber/fixtures/classes.rb @@ -0,0 +1,12 @@ +module FiberSpecs + + class NewFiberToRaise + def self.raise(*args) + fiber = Fiber.new { Fiber.yield } + fiber.resume + fiber.raise(*args) + end + end + + class CustomError < StandardError; end +end diff --git a/core/fiber/raise_spec.rb b/core/fiber/raise_spec.rb new file mode 100644 index 0000000000..3c4859a3a5 --- /dev/null +++ b/core/fiber/raise_spec.rb @@ -0,0 +1,76 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative '../../shared/kernel/raise' + +ruby_version_is "2.7" do + describe "Fiber#raise" do + it_behaves_like :kernel_raise, :raise, FiberSpecs::NewFiberToRaise + end + + describe "Fiber#raise" do + it 'raises RuntimeError by default' do + -> { FiberSpecs::NewFiberToRaise.raise }.should raise_error(RuntimeError) + end + + it "raises FiberError if Fiber is not born" do + fiber = Fiber.new { true } + -> { fiber.raise }.should raise_error(FiberError, "cannot raise exception on unborn fiber") + end + + it "raises FiberError if Fiber is dead" do + fiber = Fiber.new { true } + fiber.resume + -> { fiber.raise }.should raise_error(FiberError, "dead fiber called") + end + + it 'accepts error class' do + -> { FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError }.should raise_error(FiberSpecs::CustomError) + end + + it 'accepts error message' do + -> { FiberSpecs::NewFiberToRaise.raise "error message" }.should raise_error(RuntimeError, "error message") + end + + it 'does not accept array of backtrace information only' do + -> { FiberSpecs::NewFiberToRaise.raise ['foo'] }.should raise_error(TypeError) + end + + it 'does not accept integer' do + -> { FiberSpecs::NewFiberToRaise.raise 100 }.should raise_error(TypeError) + end + + it 'accepts error class with error message' do + -> { FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError, 'test error' }.should raise_error(FiberSpecs::CustomError, 'test error') + end + + it 'accepts error class with with error message and backtrace information' do + -> { + FiberSpecs::NewFiberToRaise.raise FiberSpecs::CustomError, 'test error', ['foo', 'boo'] + }.should raise_error(FiberSpecs::CustomError) { |e| + e.message.should == 'test error' + e.backtrace.should == ['foo', 'boo'] + } + end + + it 'does not accept only error message and backtrace information' do + -> { FiberSpecs::NewFiberToRaise.raise 'test error', ['foo', 'boo'] }.should raise_error(TypeError) + end + + it "raises a FiberError if invoked from a different Thread" do + fiber = Fiber.new { Fiber.yield } + fiber.resume + Thread.new do + -> { + fiber.raise + }.should raise_error(FiberError, "fiber called across threads") + end.join + end + + it "kills Fiber" do + fiber = Fiber.new { Fiber.yield :first; :second } + fiber.resume + -> { fiber.raise }.should raise_error + -> { fiber.resume }.should raise_error(FiberError, "dead fiber called") + end + end +end diff --git a/shared/kernel/raise.rb b/shared/kernel/raise.rb index 7d9954e29a..f00a6ef294 100644 --- a/shared/kernel/raise.rb +++ b/shared/kernel/raise.rb @@ -50,20 +50,43 @@ def initialize end it "re-raises a previously rescued exception without overwriting the backtrace" do - begin - initial_raise_line = __LINE__; @object.raise 'raised' - rescue => raised - begin - raise_again_line = __LINE__; @object.raise raised - rescue => raised_again - # This spec is written using #backtrace and matching the line number - # from the string, as backtrace_locations is a more advanced - # method that is not always supported by implementations. + # This spec is written using #backtrace and matching the line number + # from the string, as backtrace_locations is a more advanced + # method that is not always supported by implementations. + # + initial_raise_line = nil + raise_again_line = nil + raised_again = nil - raised_again.backtrace.first.should include("#{__FILE__}:#{initial_raise_line}:") - raised_again.backtrace.first.should_not include("#{__FILE__}:#{raise_again_line}:") + if defined?(FiberSpecs::NewFiberToRaise) and @object == FiberSpecs::NewFiberToRaise + fiber = Fiber.new do + begin + initial_raise_line = __LINE__; Fiber.yield + rescue => raised + begin + raise_again_line = __LINE__; Fiber.yield raised + rescue => raised_again + raised_again + end + end + end + fiber.resume + raised = fiber.raise 'raised' + raised_again = fiber.raise raised + else + begin + initial_raise_line = __LINE__; @object.raise 'raised' + rescue => raised + begin + raise_again_line = __LINE__; @object.raise raised + rescue => raised_again + raised_again + end end end + + raised_again.backtrace.first.should include("#{__FILE__}:#{initial_raise_line}:") + raised_again.backtrace.first.should_not include("#{__FILE__}:#{raise_again_line}:") end it "allows Exception, message, and backtrace parameters" do