diff options
Diffstat (limited to 'spec/ruby/core/thread/raise_spec.rb')
| -rw-r--r-- | spec/ruby/core/thread/raise_spec.rb | 128 |
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 |
