summaryrefslogtreecommitdiff
path: root/spec/ruby/core/thread/raise_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/thread/raise_spec.rb')
-rw-r--r--spec/ruby/core/thread/raise_spec.rb128
1 files changed, 107 insertions, 21 deletions
diff --git a/spec/ruby/core/thread/raise_spec.rb b/spec/ruby/core/thread/raise_spec.rb
index 8724d26202..efc09d4a35 100644
--- a/spec/ruby/core/thread/raise_spec.rb
+++ b/spec/ruby/core/thread/raise_spec.rb
@@ -1,13 +1,23 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../../../shared/kernel/raise', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/kernel/raise'
describe "Thread#raise" do
- it "ignores dead threads" do
+ it "is a public method" do
+ Thread.public_instance_methods.should.include?(:raise)
+ end
+
+ it_behaves_like :kernel_raise, :raise, ThreadSpecs::NewThreadToRaise
+ it_behaves_like :kernel_raise_across_contexts, :raise, ThreadSpecs::NewThreadToRaise
+ ruby_version_is "4.0" do
+ it_behaves_like :kernel_raise_with_cause, :raise, ThreadSpecs::NewThreadToRaise
+ end
+
+ it "ignores dead threads and returns nil" do
t = Thread.new { :dead }
Thread.pass while t.alive?
- lambda {t.raise("Kill the thread")}.should_not raise_error
- lambda {t.value}.should_not raise_error
+ t.raise("Kill the thread").should == nil
+ t.join
end
end
@@ -26,27 +36,27 @@ describe "Thread#raise on a sleeping thread" do
it "raises a RuntimeError if no exception class is given" do
@thr.raise
Thread.pass while @thr.status
- ScratchPad.recorded.should be_kind_of(RuntimeError)
+ ScratchPad.recorded.should.is_a?(RuntimeError)
end
it "raises the given exception" do
@thr.raise Exception
Thread.pass while @thr.status
- ScratchPad.recorded.should be_kind_of(Exception)
+ ScratchPad.recorded.should.is_a?(Exception)
end
it "raises the given exception with the given message" do
@thr.raise Exception, "get to work"
Thread.pass while @thr.status
- ScratchPad.recorded.should be_kind_of(Exception)
+ ScratchPad.recorded.should.is_a?(Exception)
ScratchPad.recorded.message.should == "get to work"
end
it "raises the given exception and the backtrace is the one of the interrupted thread" do
@thr.raise Exception
Thread.pass while @thr.status
- ScratchPad.recorded.should be_kind_of(Exception)
- ScratchPad.recorded.backtrace[0].should include("sleep")
+ ScratchPad.recorded.should.is_a?(Exception)
+ ScratchPad.recorded.backtrace[0].should.include?("sleep")
end
it "is captured and raised by Thread#value" do
@@ -58,7 +68,7 @@ describe "Thread#raise on a sleeping thread" do
ThreadSpecs.spin_until_sleeping(t)
t.raise
- lambda { t.value }.should raise_error(RuntimeError)
+ -> { t.value }.should.raise(RuntimeError)
end
it "raises a RuntimeError when called with no arguments inside rescue" do
@@ -76,7 +86,80 @@ describe "Thread#raise on a sleeping thread" do
ThreadSpecs.spin_until_sleeping(t)
t.raise
end
- lambda {t.value}.should raise_error(RuntimeError)
+ -> { t.value }.should.raise(RuntimeError)
+ end
+
+ it "re-raises a previously rescued exception without overwriting the backtrace" do
+ t = Thread.new do
+ -> { # To make sure there is at least one entry in the call stack
+ begin
+ sleep
+ rescue => e
+ e
+ end
+ }.call
+ end
+
+ ThreadSpecs.spin_until_sleeping(t)
+
+ begin
+ initial_raise_line = __LINE__; raise 'raised'
+ rescue => raised
+ raise_again_line = __LINE__; t.raise raised
+ raised_again = t.value
+
+ raised_again.backtrace.first.should.include?("#{__FILE__}:#{initial_raise_line}:")
+ raised_again.backtrace.first.should_not.include?("#{__FILE__}:#{raise_again_line}:")
+ end
+ end
+
+ it "calls #exception in both the caller and in the target thread" do
+ cls = Class.new(Exception) do
+ attr_accessor :log
+ def initialize(*args)
+ @log = [] # This is shared because the super #exception uses a shallow clone
+ super
+ end
+
+ def exception(*args)
+ @log << [self, Thread.current, args]
+ super
+ end
+ end
+ exc = cls.new
+
+ @thr.raise exc, "Thread#raise #exception spec"
+ @thr.join
+ ScratchPad.recorded.should.is_a?(cls)
+ exc.log.should == [
+ [exc, Thread.current, ["Thread#raise #exception spec"]],
+ [ScratchPad.recorded, @thr, []]
+ ]
+ end
+
+ it "calls #set_backtrace only in the caller thread" do
+ cls = Class.new(Exception) do
+ attr_accessor :log
+ def initialize(*args)
+ @log = [] # This is shared because the super #exception uses a shallow clone
+ super
+ end
+
+ def set_backtrace(backtrace)
+ @log << [Thread.current, backtrace]
+ super
+ end
+ end
+ exc = cls.new
+
+ backtrace = ["a.rb:1"]
+
+ @thr.raise exc, "Thread#raise #set_backtrace spec", backtrace
+ @thr.join
+ ScratchPad.recorded.should.is_a?(cls)
+ exc.log.should == [
+ [Thread.current, backtrace]
+ ]
end
end
@@ -97,30 +180,33 @@ describe "Thread#raise on a running thread" do
it "raises a RuntimeError if no exception class is given" do
@thr.raise
Thread.pass while @thr.status
- ScratchPad.recorded.should be_kind_of(RuntimeError)
+ ScratchPad.recorded.should.is_a?(RuntimeError)
end
it "raises the given exception" do
@thr.raise Exception
Thread.pass while @thr.status
- ScratchPad.recorded.should be_kind_of(Exception)
+ ScratchPad.recorded.should.is_a?(Exception)
end
it "raises the given exception with the given message" do
@thr.raise Exception, "get to work"
Thread.pass while @thr.status
- ScratchPad.recorded.should be_kind_of(Exception)
+ ScratchPad.recorded.should.is_a?(Exception)
ScratchPad.recorded.message.should == "get to work"
end
it "can go unhandled" do
+ q = Queue.new
t = Thread.new do
Thread.current.report_on_exception = false
+ q << true
loop { Thread.pass }
end
+ q.pop # wait for `report_on_exception = false`.
t.raise
- lambda {t.value}.should raise_error(RuntimeError)
+ -> { t.value }.should.raise(RuntimeError)
end
it "raises the given argument even when there is an active exception" do
@@ -139,7 +225,7 @@ describe "Thread#raise on a running thread" do
rescue
Thread.pass until raised
t.raise RangeError
- lambda {t.value}.should raise_error(RangeError)
+ -> { t.value }.should.raise(RangeError)
end
end
@@ -151,7 +237,7 @@ describe "Thread#raise on a running thread" do
1/0
rescue ZeroDivisionError
raised = true
- loop { }
+ loop { Thread.pass }
end
end
begin
@@ -160,7 +246,7 @@ describe "Thread#raise on a running thread" do
Thread.pass until raised
t.raise
end
- lambda {t.value}.should raise_error(RuntimeError)
+ -> { t.value }.should.raise(RuntimeError)
end
end
@@ -176,6 +262,6 @@ describe "Thread#raise on same thread" do
Thread.current.raise
end
end
- lambda {t.value}.should raise_error(RuntimeError)
+ -> { t.value }.should.raise(RuntimeError, '')
end
end