2018-03-04 10:09:32 -05:00
|
|
|
require_relative '../../spec_helper'
|
|
|
|
require_relative 'fixtures/classes'
|
|
|
|
require_relative '../../shared/kernel/raise'
|
2017-05-07 08:04:49 -04:00
|
|
|
|
|
|
|
describe "Thread#raise" do
|
2018-04-25 04:16:09 -04:00
|
|
|
it "ignores dead threads and returns nil" do
|
2017-05-07 08:04:49 -04:00
|
|
|
t = Thread.new { :dead }
|
|
|
|
Thread.pass while t.alive?
|
2018-04-25 04:16:09 -04:00
|
|
|
t.raise("Kill the thread").should == nil
|
|
|
|
t.join
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "Thread#raise on a sleeping thread" do
|
|
|
|
before :each do
|
|
|
|
ScratchPad.clear
|
|
|
|
@thr = ThreadSpecs.sleeping_thread
|
|
|
|
Thread.pass while @thr.status and @thr.status != "sleep"
|
|
|
|
end
|
|
|
|
|
|
|
|
after :each do
|
|
|
|
@thr.kill
|
|
|
|
@thr.join
|
|
|
|
end
|
|
|
|
|
|
|
|
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)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises the given exception" do
|
|
|
|
@thr.raise Exception
|
|
|
|
Thread.pass while @thr.status
|
|
|
|
ScratchPad.recorded.should be_kind_of(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.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")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "is captured and raised by Thread#value" do
|
|
|
|
t = Thread.new do
|
2017-10-28 11:15:48 -04:00
|
|
|
Thread.current.report_on_exception = false
|
2017-05-07 08:04:49 -04:00
|
|
|
sleep
|
|
|
|
end
|
|
|
|
|
|
|
|
ThreadSpecs.spin_until_sleeping(t)
|
|
|
|
|
|
|
|
t.raise
|
2018-04-25 04:15:58 -04:00
|
|
|
-> { t.value }.should raise_error(RuntimeError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "raises a RuntimeError when called with no arguments inside rescue" do
|
|
|
|
t = Thread.new do
|
2017-10-28 11:15:48 -04:00
|
|
|
Thread.current.report_on_exception = false
|
2017-05-07 08:04:49 -04:00
|
|
|
begin
|
|
|
|
1/0
|
|
|
|
rescue ZeroDivisionError
|
|
|
|
sleep
|
|
|
|
end
|
|
|
|
end
|
|
|
|
begin
|
|
|
|
raise RangeError
|
|
|
|
rescue
|
|
|
|
ThreadSpecs.spin_until_sleeping(t)
|
|
|
|
t.raise
|
|
|
|
end
|
2018-04-25 04:15:58 -04:00
|
|
|
-> { t.value }.should raise_error(RuntimeError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
2018-11-27 15:38:57 -05:00
|
|
|
|
|
|
|
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
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
describe "Thread#raise on a running thread" do
|
|
|
|
before :each do
|
|
|
|
ScratchPad.clear
|
|
|
|
ThreadSpecs.clear_state
|
|
|
|
|
|
|
|
@thr = ThreadSpecs.running_thread
|
|
|
|
Thread.pass until ThreadSpecs.state == :running
|
|
|
|
end
|
|
|
|
|
|
|
|
after :each do
|
|
|
|
@thr.kill
|
|
|
|
@thr.join
|
|
|
|
end
|
|
|
|
|
|
|
|
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)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises the given exception" do
|
|
|
|
@thr.raise Exception
|
|
|
|
Thread.pass while @thr.status
|
|
|
|
ScratchPad.recorded.should be_kind_of(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.message.should == "get to work"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "can go unhandled" do
|
2019-11-29 03:39:06 -05:00
|
|
|
q = Queue.new
|
2017-05-07 08:04:49 -04:00
|
|
|
t = Thread.new do
|
2017-10-28 11:15:48 -04:00
|
|
|
Thread.current.report_on_exception = false
|
2019-11-29 03:39:06 -05:00
|
|
|
q << true
|
2017-05-07 08:04:49 -04:00
|
|
|
loop { Thread.pass }
|
|
|
|
end
|
|
|
|
|
2019-11-29 03:39:06 -05:00
|
|
|
q.pop # wait for `report_on_exception = false`.
|
2017-05-07 08:04:49 -04:00
|
|
|
t.raise
|
2018-04-25 04:15:58 -04:00
|
|
|
-> { t.value }.should raise_error(RuntimeError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "raises the given argument even when there is an active exception" do
|
|
|
|
raised = false
|
|
|
|
t = Thread.new do
|
2017-10-28 11:15:48 -04:00
|
|
|
Thread.current.report_on_exception = false
|
2017-05-07 08:04:49 -04:00
|
|
|
begin
|
|
|
|
1/0
|
|
|
|
rescue ZeroDivisionError
|
|
|
|
raised = true
|
|
|
|
loop { Thread.pass }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
begin
|
|
|
|
raise "Create an active exception for the current thread too"
|
|
|
|
rescue
|
|
|
|
Thread.pass until raised
|
|
|
|
t.raise RangeError
|
2018-04-25 04:15:58 -04:00
|
|
|
-> { t.value }.should raise_error(RangeError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises a RuntimeError when called with no arguments inside rescue" do
|
|
|
|
raised = false
|
|
|
|
t = Thread.new do
|
2017-10-28 11:15:48 -04:00
|
|
|
Thread.current.report_on_exception = false
|
2017-05-07 08:04:49 -04:00
|
|
|
begin
|
|
|
|
1/0
|
|
|
|
rescue ZeroDivisionError
|
|
|
|
raised = true
|
2018-04-25 04:11:01 -04:00
|
|
|
loop { Thread.pass }
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
begin
|
|
|
|
raise RangeError
|
|
|
|
rescue
|
|
|
|
Thread.pass until raised
|
|
|
|
t.raise
|
|
|
|
end
|
2018-04-25 04:15:58 -04:00
|
|
|
-> { t.value }.should raise_error(RuntimeError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "Thread#raise on same thread" do
|
|
|
|
it_behaves_like :kernel_raise, :raise, Thread.current
|
|
|
|
|
|
|
|
it "raises a RuntimeError when called with no arguments inside rescue" do
|
|
|
|
t = Thread.new do
|
2017-10-28 11:15:48 -04:00
|
|
|
Thread.current.report_on_exception = false
|
2017-05-07 08:04:49 -04:00
|
|
|
begin
|
|
|
|
1/0
|
|
|
|
rescue ZeroDivisionError
|
|
|
|
Thread.current.raise
|
|
|
|
end
|
|
|
|
end
|
2018-04-25 04:15:58 -04:00
|
|
|
-> { t.value }.should raise_error(RuntimeError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
end
|