diff options
Diffstat (limited to 'spec/ruby/core/thread')
44 files changed, 829 insertions, 498 deletions
diff --git a/spec/ruby/core/thread/abort_on_exception_spec.rb b/spec/ruby/core/thread/abort_on_exception_spec.rb index 49be84ea9f..aeca50e5c1 100644 --- a/spec/ruby/core/thread/abort_on_exception_spec.rb +++ b/spec/ruby/core/thread/abort_on_exception_spec.rb @@ -13,12 +13,12 @@ describe "Thread#abort_on_exception" do end it "is false by default" do - @thread.abort_on_exception.should be_false + @thread.abort_on_exception.should == false end it "returns true when #abort_on_exception= is passed true" do @thread.abort_on_exception = true - @thread.abort_on_exception.should be_true + @thread.abort_on_exception.should == true end end @@ -39,7 +39,7 @@ describe :thread_abort_on_exception, shared: true do ThreadSpecs.state = :run # Wait for the main thread to be interrupted sleep - end.should raise_error(RuntimeError, "Thread#abort_on_exception= specs") + end.should.raise(RuntimeError, "Thread#abort_on_exception= specs") ScratchPad << :after rescue Exception => e @@ -81,7 +81,7 @@ describe "Thread.abort_on_exception" do it "returns true when .abort_on_exception= is passed true" do Thread.abort_on_exception = true - Thread.abort_on_exception.should be_true + Thread.abort_on_exception.should == true end end diff --git a/spec/ruby/core/thread/allocate_spec.rb b/spec/ruby/core/thread/allocate_spec.rb index cfd556812f..0b4e4f1b1f 100644 --- a/spec/ruby/core/thread/allocate_spec.rb +++ b/spec/ruby/core/thread/allocate_spec.rb @@ -4,6 +4,6 @@ describe "Thread.allocate" do it "raises a TypeError" do -> { Thread.allocate - }.should raise_error(TypeError) + }.should.raise(TypeError) end end diff --git a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb index 68a69049d9..6d9482f2ae 100644 --- a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb +++ b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb @@ -42,7 +42,7 @@ describe 'Thread::Backtrace::Location#absolute_path' do locations = ScratchPad.recorded locations[0].absolute_path.should == path # Make sure it's from the class body, not from the file top-level - locations[0].label.should include 'MethodAddedAbsolutePath' + locations[0].label.should.include? 'MethodAddedAbsolutePath' end end diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb b/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb index e903c3e450..103c36b3a0 100644 --- a/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb +++ b/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb @@ -1,10 +1,26 @@ +# These are top-level def on purpose to test those cases + +def label_top_method = ThreadBacktraceLocationSpecs::LABEL.call + +def self.label_sdef_method_of_main = ThreadBacktraceLocationSpecs::LABEL.call + +class << self + def label_sclass_method_of_main = ThreadBacktraceLocationSpecs::LABEL.call +end + module ThreadBacktraceLocationSpecs MODULE_LOCATION = caller_locations(0) rescue nil + INSTANCE = Object.new.extend(self) + LABEL = -> { caller_locations(1, 1)[0].label } def self.locations caller_locations end + def instance_method_location + caller_locations(0) + end + def self.method_location caller_locations(0) end @@ -15,6 +31,12 @@ module ThreadBacktraceLocationSpecs end end + def instance_block_location + 1.times do + return caller_locations(0) + end + end + def self.locations_inside_nested_blocks first_level_location = nil second_level_location = nil @@ -32,4 +54,86 @@ module ThreadBacktraceLocationSpecs [first_level_location, second_level_location, third_level_location] end + + def instance_locations_inside_nested_block + loc = nil + 1.times do + 1.times do + loc = caller_locations(0) + end + end + loc + end + + def original_method = LABEL.call + alias_method :aliased_method, :original_method + + module M + class C + def regular_instance_method = LABEL.call + + def self.sdef_class_method = LABEL.call + + class << self + def sclass_method = LABEL.call + + def block_in_sclass_method + -> { + -> { LABEL.call }.call + }.call + end + end + block_in_sclass_method + end + end + + class M::D + def scoped_method = LABEL.call + + def self.sdef_scoped_method = LABEL.call + + class << self + def sclass_scoped_method = LABEL.call + end + + module ::ThreadBacktraceLocationSpecs + def top = LABEL.call + end + + class ::ThreadBacktraceLocationSpecs::Nested + def top_nested = LABEL.call + + class C + def top_nested_c = LABEL.call + end + end + end + + SOME_OBJECT = Object.new + SOME_OBJECT.instance_exec do + def unknown_def_singleton_method = LABEL.call + + def self.unknown_sdef_singleton_method = LABEL.call + end + + M.module_eval do + def module_eval_method = LABEL.call + + def self.sdef_module_eval_method = LABEL.call + end + + def ThreadBacktraceLocationSpecs.string_class_method = LABEL.call + + module M + def ThreadBacktraceLocationSpecs.nested_class_method = LABEL.call + end + + module M + module_function def mod_function = LABEL.call + end + + expr = self + def expr.sdef_expression = LABEL.call + + def expr.block_in_sdef_expression = -> { LABEL.call }.call end diff --git a/spec/ruby/core/thread/backtrace/location/inspect_spec.rb b/spec/ruby/core/thread/backtrace/location/inspect_spec.rb index 20e477a5a6..4df88a2f33 100644 --- a/spec/ruby/core/thread/backtrace/location/inspect_spec.rb +++ b/spec/ruby/core/thread/backtrace/location/inspect_spec.rb @@ -8,6 +8,6 @@ describe 'Thread::Backtrace::Location#inspect' do end it 'converts the call frame to a String' do - @frame.inspect.should include("#{__FILE__}:#{@line}:in ") + @frame.inspect.should.include?("#{__FILE__}:#{@line}:in ") end end diff --git a/spec/ruby/core/thread/backtrace/location/label_spec.rb b/spec/ruby/core/thread/backtrace/location/label_spec.rb index 85ddccc8e3..5f6a7b73df 100644 --- a/spec/ruby/core/thread/backtrace/location/label_spec.rb +++ b/spec/ruby/core/thread/backtrace/location/label_spec.rb @@ -3,7 +3,7 @@ require_relative 'fixtures/classes' describe 'Thread::Backtrace::Location#label' do it 'returns the base label of the call frame' do - ThreadBacktraceLocationSpecs.locations[0].label.should include('<top (required)>') + ThreadBacktraceLocationSpecs.locations[0].label.should.include?('<top (required)>') end it 'returns the method name for a method location' do @@ -15,7 +15,7 @@ describe 'Thread::Backtrace::Location#label' do end it 'returns the module name for a module location' do - ThreadBacktraceLocationSpecs::MODULE_LOCATION[0].label.should include "ThreadBacktraceLocationSpecs" + ThreadBacktraceLocationSpecs::MODULE_LOCATION[0].label.should == "<module:ThreadBacktraceLocationSpecs>" end it 'includes the nesting level of a block as part of the location label' do @@ -34,4 +34,194 @@ describe 'Thread::Backtrace::Location#label' do main_label.should == "block in <main>\n" required_label.should == "block in <top (required)>\n" end + + it "return the same name as the caller for eval" do + this = caller_locations(0)[0].label + eval("caller_locations(0)[0]").label.should == this + + b = binding + b.eval("caller_locations(0)[0]").label.should == this + + b.local_variable_set(:binding_var1, 1) + b.eval("caller_locations(0)[0]").label.should == this + + b.local_variable_set(:binding_var2, 2) + b.eval("caller_locations(0)[0]").label.should == this + + b.local_variable_set(:binding_var2, 2) + eval("caller_locations(0)[0]", b).label.should == this + end + + ruby_version_is "3.4" do + describe "is Module#method for" do + it "a core method defined natively" do + BasicObject.instance_method(:instance_exec).should_not.source_location + loc = nil + loc = instance_exec { caller_locations(1, 1)[0] } + loc.label.should == "BasicObject#instance_exec" + end + + it "a core method defined in Ruby" do + Kernel.instance_method(:tap).should.source_location + loc = nil + tap { loc = caller_locations(1, 1)[0] } + loc.label.should == "Kernel#tap" + end + + it "an instance method defined in Ruby" do + ThreadBacktraceLocationSpecs::INSTANCE.instance_method_location[0].label.should == "ThreadBacktraceLocationSpecs#instance_method_location" + end + + it "a block in an instance method defined in Ruby" do + ThreadBacktraceLocationSpecs::INSTANCE.instance_block_location[0].label.should == "block in ThreadBacktraceLocationSpecs#instance_block_location" + end + + it "a nested block in an instance method defined in Ruby" do + ThreadBacktraceLocationSpecs::INSTANCE.instance_locations_inside_nested_block[0].label.should == "block (2 levels) in ThreadBacktraceLocationSpecs#instance_locations_inside_nested_block" + end + + it "a method defined via module_exec" do + ThreadBacktraceLocationSpecs.module_exec do + def in_module_exec + caller_locations(0) + end + end + ThreadBacktraceLocationSpecs::INSTANCE.in_module_exec[0].label.should == "ThreadBacktraceLocationSpecs#in_module_exec" + end + + it "a method defined via module_eval" do + ThreadBacktraceLocationSpecs.module_eval <<~RUBY + def in_module_eval + caller_locations(0) + end + RUBY + ThreadBacktraceLocationSpecs::INSTANCE.in_module_eval[0].label.should == "ThreadBacktraceLocationSpecs#in_module_eval" + end + end + + describe "is Module.method for" do + it "a singleton method defined in Ruby" do + ThreadBacktraceLocationSpecs.method_location[0].label.should == "ThreadBacktraceLocationSpecs.method_location" + end + + it "a block in a singleton method defined in Ruby" do + ThreadBacktraceLocationSpecs.block_location[0].label.should == "block in ThreadBacktraceLocationSpecs.block_location" + end + + it "a nested block in a singleton method defined in Ruby" do + ThreadBacktraceLocationSpecs.locations_inside_nested_blocks[2].label.should == "block (3 levels) in ThreadBacktraceLocationSpecs.locations_inside_nested_blocks" + end + + it "a singleton method defined via def Const.method" do + def ThreadBacktraceLocationSpecs.def_singleton + caller_locations(0) + end + ThreadBacktraceLocationSpecs.def_singleton[0].label.should == "ThreadBacktraceLocationSpecs.def_singleton" + end + end + + it "shows the original method name for an aliased method" do + ThreadBacktraceLocationSpecs::INSTANCE.aliased_method.should == "ThreadBacktraceLocationSpecs#original_method" + end + + # A wide variety of cases. + # These show interesting cases when trying to determine the name statically/at parse time + describe "is correct for" do + base = ThreadBacktraceLocationSpecs + + it "M::C#regular_instance_method" do + base::M::C.new.regular_instance_method.should == "#{base}::M::C#regular_instance_method" + end + + it "M::C.sdef_class_method" do + base::M::C.sdef_class_method.should == "#{base}::M::C.sdef_class_method" + end + + it "M::C.sclass_method" do + base::M::C.sclass_method.should == "#{base}::M::C.sclass_method" + end + + it "M::C.block_in_sclass_method" do + base::M::C.block_in_sclass_method.should == "block (2 levels) in #{base}::M::C.block_in_sclass_method" + end + + it "M::D#scoped_method" do + base::M::D.new.scoped_method.should == "#{base}::M::D#scoped_method" + end + + it "M::D.sdef_scoped_method" do + base::M::D.sdef_scoped_method.should == "#{base}::M::D.sdef_scoped_method" + end + + it "M::D.sclass_scoped_method" do + base::M::D.sclass_scoped_method.should == "#{base}::M::D.sclass_scoped_method" + end + + it "ThreadBacktraceLocationSpecs#top" do + ThreadBacktraceLocationSpecs::INSTANCE.top.should == "ThreadBacktraceLocationSpecs#top" + end + + it "ThreadBacktraceLocationSpecs::Nested#top_nested" do + ThreadBacktraceLocationSpecs::Nested.new.top_nested.should == "ThreadBacktraceLocationSpecs::Nested#top_nested" + end + + it "ThreadBacktraceLocationSpecs::Nested::C#top_nested_c" do + ThreadBacktraceLocationSpecs::Nested::C.new.top_nested_c.should == "ThreadBacktraceLocationSpecs::Nested::C#top_nested_c" + end + + it "Object#label_top_method" do + label_top_method.should == "Object#label_top_method" + end + + it "main.label_sdef_method_of_main" do + main = TOPLEVEL_BINDING.receiver + main.label_sdef_method_of_main.should == "label_sdef_method_of_main" + end + + it "main.label_sclass_method_of_main" do + main = TOPLEVEL_BINDING.receiver + main.label_sclass_method_of_main.should == "label_sclass_method_of_main" + end + + it "unknown_def_singleton_method" do + base::SOME_OBJECT.unknown_def_singleton_method.should == "unknown_def_singleton_method" + end + + it "unknown_sdef_singleton_method" do + base::SOME_OBJECT.unknown_sdef_singleton_method.should == "unknown_sdef_singleton_method" + end + + it "M#module_eval_method" do + Object.new.extend(base::M).module_eval_method.should == "#{base}::M#module_eval_method" + end + + it "M.sdef_module_eval_method" do + base::M.sdef_module_eval_method.should == "#{base}::M.sdef_module_eval_method" + end + + it "ThreadBacktraceLocationSpecs.string_class_method" do + ThreadBacktraceLocationSpecs.string_class_method.should == "ThreadBacktraceLocationSpecs.string_class_method" + end + + it "ThreadBacktraceLocationSpecs.nested_class_method" do + ThreadBacktraceLocationSpecs.nested_class_method.should == "ThreadBacktraceLocationSpecs.nested_class_method" + end + + it "M#mod_function" do + Object.new.extend(base::M).send(:mod_function).should == "#{base}::M#mod_function" + end + + it "M.mod_function" do + base::M.mod_function.should == "#{base}::M.mod_function" + end + + it "sdef_expression" do + base.sdef_expression.should == "#{base}.sdef_expression" + end + + it "block_in_sdef_expression" do + base.block_in_sdef_expression.should == "block in #{base}.block_in_sdef_expression" + end + end + end end diff --git a/spec/ruby/core/thread/backtrace/location/to_s_spec.rb b/spec/ruby/core/thread/backtrace/location/to_s_spec.rb index 5911cdced0..983ce4c3f8 100644 --- a/spec/ruby/core/thread/backtrace/location/to_s_spec.rb +++ b/spec/ruby/core/thread/backtrace/location/to_s_spec.rb @@ -8,6 +8,6 @@ describe 'Thread::Backtrace::Location#to_s' do end it 'converts the call frame to a String' do - @frame.to_s.should include("#{__FILE__}:#{@line}:in ") + @frame.to_s.should.include?("#{__FILE__}:#{@line}:in ") end end diff --git a/spec/ruby/core/thread/backtrace_locations_spec.rb b/spec/ruby/core/thread/backtrace_locations_spec.rb index 09fe622e0d..28a488f311 100644 --- a/spec/ruby/core/thread/backtrace_locations_spec.rb +++ b/spec/ruby/core/thread/backtrace_locations_spec.rb @@ -3,20 +3,20 @@ require_relative '../../spec_helper' describe "Thread#backtrace_locations" do it "returns an Array" do locations = Thread.current.backtrace_locations - locations.should be_an_instance_of(Array) - locations.should_not be_empty + locations.should.instance_of?(Array) + locations.should_not.empty? end it "sets each element to a Thread::Backtrace::Location" do locations = Thread.current.backtrace_locations - locations.each { |loc| loc.should be_an_instance_of(Thread::Backtrace::Location) } + locations.each { |loc| loc.should.instance_of?(Thread::Backtrace::Location) } end it "can be called on any Thread" do locations = Thread.new { Thread.current.backtrace_locations }.value - locations.should be_an_instance_of(Array) - locations.should_not be_empty - locations.each { |loc| loc.should be_an_instance_of(Thread::Backtrace::Location) } + locations.should.instance_of?(Array) + locations.should_not.empty? + locations.each { |loc| loc.should.instance_of?(Thread::Backtrace::Location) } end it "can be called with a number of locations to omit" do diff --git a/spec/ruby/core/thread/backtrace_spec.rb b/spec/ruby/core/thread/backtrace_spec.rb index 15bb29a349..770c300f06 100644 --- a/spec/ruby/core/thread/backtrace_spec.rb +++ b/spec/ruby/core/thread/backtrace_spec.rb @@ -12,7 +12,7 @@ describe "Thread#backtrace" do Thread.pass while t.status && t.status != 'sleep' backtrace = t.backtrace - backtrace.should be_kind_of(Array) + backtrace.should.is_a?(Array) backtrace.first.should =~ /[`'](?:Kernel#)?sleep'/ t.raise 'finish the thread' @@ -30,7 +30,7 @@ describe "Thread#backtrace" do backtrace = t.backtrace t.kill t.join - backtrace.should be_kind_of(Array) + backtrace.should.is_a?(Array) end it "can be called with a number of locations to omit" do diff --git a/spec/ruby/core/thread/current_spec.rb b/spec/ruby/core/thread/current_spec.rb index f5ed1d95cd..f893f078ba 100644 --- a/spec/ruby/core/thread/current_spec.rb +++ b/spec/ruby/core/thread/current_spec.rb @@ -4,13 +4,13 @@ require_relative 'fixtures/classes' describe "Thread.current" do it "returns a thread" do current = Thread.current - current.should be_kind_of(Thread) + current.should.is_a?(Thread) end it "returns the current thread" do t = Thread.new { Thread.current } - t.value.should equal(t) - Thread.current.should_not equal(t.value) + t.value.should.equal?(t) + Thread.current.should_not.equal?(t.value) end it "returns the correct thread in a Fiber" do @@ -22,10 +22,10 @@ describe "Thread.current" do cur = Thread.current Fiber.new { Thread.current - }.resume.should equal cur + }.resume.should.equal? cur cur } - t.value.should equal t + t.value.should.equal? t end end end diff --git a/spec/ruby/core/thread/each_caller_location_spec.rb b/spec/ruby/core/thread/each_caller_location_spec.rb index 29c271789b..15fda1a37b 100644 --- a/spec/ruby/core/thread/each_caller_location_spec.rb +++ b/spec/ruby/core/thread/each_caller_location_spec.rb @@ -1,49 +1,47 @@ require_relative '../../spec_helper' describe "Thread.each_caller_location" do - ruby_version_is "3.2" do - it "iterates through the current execution stack and matches caller_locations content and type" do - ScratchPad.record [] - Thread.each_caller_location { |l| ScratchPad << l; } + it "iterates through the current execution stack and matches caller_locations content and type" do + ScratchPad.record [] + Thread.each_caller_location { |l| ScratchPad << l; } - ScratchPad.recorded.map(&:to_s).should == caller_locations.map(&:to_s) - ScratchPad.recorded[0].should be_kind_of(Thread::Backtrace::Location) - end + ScratchPad.recorded.map(&:to_s).should == caller_locations.map(&:to_s) + ScratchPad.recorded[0].should.is_a?(Thread::Backtrace::Location) + end - it "returns subset of 'Thread.to_enum(:each_caller_location)' locations" do - ar = [] - ecl = Thread.each_caller_location { |x| ar << x } + it "returns subset of 'Thread.to_enum(:each_caller_location)' locations" do + ar = [] + ecl = Thread.each_caller_location { |x| ar << x } - (ar.map(&:to_s) - Thread.to_enum(:each_caller_location).to_a.map(&:to_s)).should.empty? - end + (ar.map(&:to_s) - Thread.to_enum(:each_caller_location).to_a.map(&:to_s)).should.empty? + end - it "stops the backtrace iteration if 'break' occurs" do - i = 0 - ar = [] - ecl = Thread.each_caller_location do |x| - ar << x - i += 1 - break x if i == 2 - end - - ar.map(&:to_s).should == caller_locations(1, 2).map(&:to_s) - ecl.should be_kind_of(Thread::Backtrace::Location) + it "stops the backtrace iteration if 'break' occurs" do + i = 0 + ar = [] + ecl = Thread.each_caller_location do |x| + ar << x + i += 1 + break x if i == 2 end - it "returns nil" do - Thread.each_caller_location {}.should == nil - end + ar.map(&:to_s).should == caller_locations(1, 2).map(&:to_s) + ecl.should.is_a?(Thread::Backtrace::Location) + end - it "raises LocalJumpError when called without a block" do - -> { - Thread.each_caller_location - }.should raise_error(LocalJumpError, "no block given") - end + it "returns nil" do + Thread.each_caller_location {}.should == nil + end - it "doesn't accept keyword arguments" do - -> { - Thread.each_caller_location(12, foo: 10) {} - }.should raise_error(ArgumentError); - end + it "raises LocalJumpError when called without a block" do + -> { + Thread.each_caller_location + }.should.raise(LocalJumpError, "no block given") + end + + it "doesn't accept keyword arguments" do + -> { + Thread.each_caller_location(12, foo: 10) {} + }.should.raise(ArgumentError); end end diff --git a/spec/ruby/core/thread/element_reference_spec.rb b/spec/ruby/core/thread/element_reference_spec.rb index fde9d1f440..72892f6c50 100644 --- a/spec/ruby/core/thread/element_reference_spec.rb +++ b/spec/ruby/core/thread/element_reference_spec.rb @@ -49,7 +49,7 @@ describe "Thread#[]" do end it "raises exceptions on the wrong type of keys" do - -> { Thread.current[nil] }.should raise_error(TypeError) - -> { Thread.current[5] }.should raise_error(TypeError) + -> { Thread.current[nil] }.should.raise(TypeError) + -> { Thread.current[5] }.should.raise(TypeError) end end diff --git a/spec/ruby/core/thread/element_set_spec.rb b/spec/ruby/core/thread/element_set_spec.rb index f205177304..97d6c23980 100644 --- a/spec/ruby/core/thread/element_set_spec.rb +++ b/spec/ruby/core/thread/element_set_spec.rb @@ -12,7 +12,7 @@ describe "Thread#[]=" do th.freeze -> { th[:foo] = "bar" - }.should raise_error(FrozenError, "can't modify frozen thread locals") + }.should.raise(FrozenError, "can't modify frozen thread locals") end.join end @@ -40,8 +40,8 @@ describe "Thread#[]=" do end it "raises exceptions on the wrong type of keys" do - -> { Thread.current[nil] = true }.should raise_error(TypeError) - -> { Thread.current[5] = true }.should raise_error(TypeError) + -> { Thread.current[nil] = true }.should.raise(TypeError) + -> { Thread.current[5] = true }.should.raise(TypeError) end it "is not shared across fibers" do @@ -51,7 +51,7 @@ describe "Thread#[]=" do Thread.current[:value].should == 1 end fib.resume - Thread.current[:value].should be_nil + Thread.current[:value].should == nil Thread.current[:value] = 2 fib.resume Thread.current[:value] = 2 diff --git a/spec/ruby/core/thread/exit_spec.rb b/spec/ruby/core/thread/exit_spec.rb index c3f710920e..dbcd889898 100644 --- a/spec/ruby/core/thread/exit_spec.rb +++ b/spec/ruby/core/thread/exit_spec.rb @@ -1,6 +1,11 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/exit' + +describe "Thread#exit" do + it "is an alias of Thread#kill" do + Thread.instance_method(:exit).should == Thread.instance_method(:kill) + end +end describe "Thread#exit!" do it "needs to be reviewed for spec completeness" @@ -10,6 +15,6 @@ describe "Thread.exit" do it "causes the current thread to exit" do thread = Thread.new { Thread.exit; sleep } thread.join - thread.status.should be_false + thread.status.should == false end end diff --git a/spec/ruby/core/thread/fetch_spec.rb b/spec/ruby/core/thread/fetch_spec.rb index 85ffb71874..fe27dec4a2 100644 --- a/spec/ruby/core/thread/fetch_spec.rb +++ b/spec/ruby/core/thread/fetch_spec.rb @@ -19,7 +19,7 @@ describe 'Thread#fetch' do it 'raises a KeyError when the Thread does not have a fiber-local variable of the same name' do th = Thread.new {} th.join - -> { th.fetch(:cat) }.should raise_error(KeyError) + -> { th.fetch(:cat) }.should.raise(KeyError) end it 'returns the value of the fiber-local variable if value has been assigned' do @@ -60,7 +60,7 @@ describe 'Thread#fetch' do end it 'raises an ArgumentError when not passed one or two arguments' do - -> { Thread.current.fetch() }.should raise_error(ArgumentError) - -> { Thread.current.fetch(1, 2, 3) }.should raise_error(ArgumentError) + -> { Thread.current.fetch() }.should.raise(ArgumentError) + -> { Thread.current.fetch(1, 2, 3) }.should.raise(ArgumentError) end end diff --git a/spec/ruby/core/thread/fixtures/classes.rb b/spec/ruby/core/thread/fixtures/classes.rb index 23a090feb0..14d5d2f7bf 100644 --- a/spec/ruby/core/thread/fixtures/classes.rb +++ b/spec/ruby/core/thread/fixtures/classes.rb @@ -6,6 +6,31 @@ module ThreadSpecs end end + class NewThreadToRaise + def self.raise(*args, **kwargs, &block) + thread = Thread.new do + Thread.current.report_on_exception = false + + if block_given? + block.call do + sleep + end + else + sleep + end + end + + Thread.pass until thread.stop? + + thread.raise(*args, **kwargs) + + thread.join + ensure + thread.kill if thread.alive? + Thread.pass while thread.alive? # Thread#kill may not terminate a thread immediately so it may be detected as a leaked one + end + end + class Status attr_reader :thread, :inspect, :status, :to_s def initialize(thread) @@ -182,7 +207,7 @@ module ThreadSpecs def self.join_dying_thread_with_outer_ensure(kill_method_name=:kill) t = dying_thread_with_outer_ensure(kill_method_name) { yield } - -> { t.join }.should raise_error(RuntimeError, "In dying thread") + -> { t.join }.should.raise(RuntimeError, "In dying thread") return t end diff --git a/spec/ruby/core/thread/fork_spec.rb b/spec/ruby/core/thread/fork_spec.rb index a2f4181298..60d574a185 100644 --- a/spec/ruby/core/thread/fork_spec.rb +++ b/spec/ruby/core/thread/fork_spec.rb @@ -1,9 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/start' describe "Thread.fork" do - describe "Thread.start" do - it_behaves_like :thread_start, :fork + it "is an alias of Thread.start" do + Thread.method(:fork).should == Thread.method(:start) end end diff --git a/spec/ruby/core/thread/handle_interrupt_spec.rb b/spec/ruby/core/thread/handle_interrupt_spec.rb index ea7e81cb98..aa03d4c66d 100644 --- a/spec/ruby/core/thread/handle_interrupt_spec.rb +++ b/spec/ruby/core/thread/handle_interrupt_spec.rb @@ -75,7 +75,7 @@ describe "Thread.handle_interrupt" do Thread.handle_interrupt(RuntimeError => :immediate) { flunk "not reached" } - }.should raise_error(RuntimeError, "interrupt immediate") + }.should.raise(RuntimeError, "interrupt immediate") Thread.pending_interrupt?.should == false end end @@ -95,7 +95,7 @@ describe "Thread.handle_interrupt" do Thread.handle_interrupt(RuntimeError => :immediate) { flunk "not reached" } - }.should raise_error(RuntimeError, "interrupt with fibers") + }.should.raise(RuntimeError, "interrupt with fibers") Thread.pending_interrupt?.should == false end @@ -115,7 +115,7 @@ describe "Thread.handle_interrupt" do executed = true raise "regular exception" end - }.should raise_error(RuntimeError, "interrupt exception") + }.should.raise(RuntimeError, "interrupt exception") executed.should == true end diff --git a/spec/ruby/core/thread/initialize_spec.rb b/spec/ruby/core/thread/initialize_spec.rb index 4fca900cd8..b9a94560ee 100644 --- a/spec/ruby/core/thread/initialize_spec.rb +++ b/spec/ruby/core/thread/initialize_spec.rb @@ -19,7 +19,7 @@ describe "Thread#initialize" do @t.instance_eval do initialize {} end - }.should raise_error(ThreadError) + }.should.raise(ThreadError) end end diff --git a/spec/ruby/core/thread/inspect_spec.rb b/spec/ruby/core/thread/inspect_spec.rb index bd6e0c31fc..fee401830a 100644 --- a/spec/ruby/core/thread/inspect_spec.rb +++ b/spec/ruby/core/thread/inspect_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' -require_relative 'shared/to_s' describe "Thread#inspect" do - it_behaves_like :thread_to_s, :inspect + it "is an alias of Thread#to_s" do + Thread.instance_method(:inspect).should == Thread.instance_method(:to_s) + end end diff --git a/spec/ruby/core/thread/join_spec.rb b/spec/ruby/core/thread/join_spec.rb index 213fe2e505..f4332167f1 100644 --- a/spec/ruby/core/thread/join_spec.rb +++ b/spec/ruby/core/thread/join_spec.rb @@ -4,28 +4,28 @@ require_relative 'fixtures/classes' describe "Thread#join" do it "returns the thread when it is finished" do t = Thread.new {} - t.join.should equal(t) + t.join.should.equal?(t) end it "returns the thread when it is finished when given a timeout" do t = Thread.new {} t.join - t.join(0).should equal(t) + t.join(0).should.equal?(t) end it "coerces timeout to a Float if it is not nil" do t = Thread.new {} t.join - t.join(0).should equal(t) - t.join(0.0).should equal(t) - t.join(nil).should equal(t) + t.join(0).should.equal?(t) + t.join(0.0).should.equal?(t) + t.join(nil).should.equal?(t) end it "raises TypeError if the argument is not a valid timeout" do t = Thread.new { } t.join - -> { t.join(:foo) }.should raise_error TypeError - -> { t.join("bar") }.should raise_error TypeError + -> { t.join(:foo) }.should.raise TypeError + -> { t.join("bar") }.should.raise TypeError end it "returns nil if it is not finished when given a timeout" do @@ -55,16 +55,16 @@ describe "Thread#join" do Thread.current.report_on_exception = false raise NotImplementedError.new("Just kidding") } - -> { t.join }.should raise_error(NotImplementedError) + -> { t.join }.should.raise(NotImplementedError) end it "returns the dead thread" do t = Thread.new { Thread.current.kill } - t.join.should equal(t) + t.join.should.equal?(t) end it "raises any uncaught exception encountered in ensure block" do t = ThreadSpecs.dying_thread_ensures { raise NotImplementedError.new("Just kidding") } - -> { t.join }.should raise_error(NotImplementedError) + -> { t.join }.should.raise(NotImplementedError) end end diff --git a/spec/ruby/core/thread/key_spec.rb b/spec/ruby/core/thread/key_spec.rb index 339fa98f53..a14aeb8d31 100644 --- a/spec/ruby/core/thread/key_spec.rb +++ b/spec/ruby/core/thread/key_spec.rb @@ -24,30 +24,30 @@ describe "Thread#key?" do end it "raises exceptions on the wrong type of keys" do - -> { Thread.current.key? nil }.should raise_error(TypeError) - -> { Thread.current.key? 5 }.should raise_error(TypeError) + -> { Thread.current.key? nil }.should.raise(TypeError) + -> { Thread.current.key? 5 }.should.raise(TypeError) end it "is not shared across fibers" do fib = Fiber.new do Thread.current[:val1] = 1 Fiber.yield - Thread.current.key?(:val1).should be_true - Thread.current.key?(:val2).should be_false + Thread.current.key?(:val1).should == true + Thread.current.key?(:val2).should == false end - Thread.current.key?(:val1).should_not be_true + Thread.current.key?(:val1).should_not == true fib.resume Thread.current[:val2] = 2 fib.resume - Thread.current.key?(:val1).should be_false - Thread.current.key?(:val2).should be_true + Thread.current.key?(:val1).should == false + Thread.current.key?(:val2).should == true end it "stores a local in another thread when in a fiber" do fib = Fiber.new do t = Thread.new do sleep - Thread.current.key?(:value).should be_true + Thread.current.key?(:value).should == true end Thread.pass while t.status and t.status != "sleep" diff --git a/spec/ruby/core/thread/keys_spec.rb b/spec/ruby/core/thread/keys_spec.rb index 15efda51d6..3a2edd2456 100644 --- a/spec/ruby/core/thread/keys_spec.rb +++ b/spec/ruby/core/thread/keys_spec.rb @@ -16,22 +16,22 @@ describe "Thread#keys" do fib = Fiber.new do Thread.current[:val1] = 1 Fiber.yield - Thread.current.keys.should include(:val1) - Thread.current.keys.should_not include(:val2) + Thread.current.keys.should.include?(:val1) + Thread.current.keys.should_not.include?(:val2) end - Thread.current.keys.should_not include(:val1) + Thread.current.keys.should_not.include?(:val1) fib.resume Thread.current[:val2] = 2 fib.resume - Thread.current.keys.should include(:val2) - Thread.current.keys.should_not include(:val1) + Thread.current.keys.should.include?(:val2) + Thread.current.keys.should_not.include?(:val1) end it "stores a local in another thread when in a fiber" do fib = Fiber.new do t = Thread.new do sleep - Thread.current.keys.should include(:value) + Thread.current.keys.should.include?(:value) end Thread.pass while t.status and t.status != "sleep" diff --git a/spec/ruby/core/thread/kill_spec.rb b/spec/ruby/core/thread/kill_spec.rb index 4b62c686c7..907cc5c4d8 100644 --- a/spec/ruby/core/thread/kill_spec.rb +++ b/spec/ruby/core/thread/kill_spec.rb @@ -1,12 +1,221 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -require_relative 'shared/exit' # This spec randomly kills mspec worker like: https://ci.appveyor.com/project/ruby/ruby/builds/19473223/job/f69derxnlo09xhuj # TODO: Investigate the cause or at least print helpful logs, and remove this `platform_is_not` guard. platform_is_not :mingw do describe "Thread#kill" do - it_behaves_like :thread_exit, :kill + before :each do + ScratchPad.clear + end + + it "kills sleeping thread" do + sleeping_thread = Thread.new do + sleep + ScratchPad.record :after_sleep + end + Thread.pass while sleeping_thread.status and sleeping_thread.status != "sleep" + sleeping_thread.kill + sleeping_thread.join + ScratchPad.recorded.should == nil + end + + it "kills current thread" do + thread = Thread.new do + Thread.current.kill + ScratchPad.record :after_sleep + end + thread.join + ScratchPad.recorded.should == nil + end + + it "runs ensure clause" do + thread = ThreadSpecs.dying_thread_ensures(:kill) { ScratchPad.record :in_ensure_clause } + thread.join + ScratchPad.recorded.should == :in_ensure_clause + end + + it "runs nested ensure clauses" do + ScratchPad.record [] + @outer = Thread.new do + begin + @inner = Thread.new do + begin + sleep + ensure + ScratchPad << :inner_ensure_clause + end + end + sleep + ensure + ScratchPad << :outer_ensure_clause + @inner.kill + @inner.join + end + end + Thread.pass while @outer.status and @outer.status != "sleep" + Thread.pass until @inner + Thread.pass while @inner.status and @inner.status != "sleep" + @outer.kill + @outer.join + ScratchPad.recorded.should.include?(:inner_ensure_clause) + ScratchPad.recorded.should.include?(:outer_ensure_clause) + end + + it "does not set $!" do + thread = ThreadSpecs.dying_thread_ensures(:kill) { ScratchPad.record $! } + thread.join + ScratchPad.recorded.should == nil + end + + it "does not reset $!" do + ScratchPad.record [] + + exc = RuntimeError.new("foo") + thread = Thread.new do + begin + raise exc + ensure + ScratchPad << $! + begin + Thread.current.kill + ensure + ScratchPad << $! + end + end + end + thread.join + ScratchPad.recorded.should == [exc, exc] + end + + it "cannot be rescued" do + thread = Thread.new do + begin + Thread.current.kill + rescue Exception + ScratchPad.record :in_rescue + end + ScratchPad.record :end_of_thread_block + end + + thread.join + ScratchPad.recorded.should == nil + end + + it "kills the entire thread when a fiber is active" do + t = Thread.new do + Fiber.new do + sleep + end.resume + ScratchPad.record :fiber_resumed + end + Thread.pass while t.status and t.status != "sleep" + t.kill + t.join + ScratchPad.recorded.should == nil + end + + it "kills other fibers of that thread without running their ensure clauses" do + t = Thread.new do + f = Fiber.new do + ScratchPad.record :fiber_resumed + begin + Fiber.yield + ensure + ScratchPad.record :fiber_ensure + end + end + f.resume + sleep + end + Thread.pass until t.stop? + t.kill + t.join + ScratchPad.recorded.should == :fiber_resumed + end + + # This spec is a mess. It fails randomly, it hangs on MRI, it needs to be removed + quarantine! do + it "killing dying running does nothing" do + in_ensure_clause = false + exit_loop = true + t = ThreadSpecs.dying_thread_ensures do + in_ensure_clause = true + loop { if exit_loop then break end } + ScratchPad.record :after_stop + end + + Thread.pass until in_ensure_clause == true + 10.times { t.kill; Thread.pass } + exit_loop = true + t.join + ScratchPad.recorded.should == :after_stop + end + end + + quarantine! do + + it "propagates inner exception to Thread.join if there is an outer ensure clause" do + thread = ThreadSpecs.dying_thread_with_outer_ensure(:kill) { } + -> { thread.join }.should.raise(RuntimeError, "In dying thread") + end + + it "runs all outer ensure clauses even if inner ensure clause raises exception" do + ThreadSpecs.join_dying_thread_with_outer_ensure(:kill) { ScratchPad.record :in_outer_ensure_clause } + ScratchPad.recorded.should == :in_outer_ensure_clause + end + + it "sets $! in outer ensure clause if inner ensure clause raises exception" do + ThreadSpecs.join_dying_thread_with_outer_ensure(:kill) { ScratchPad.record $! } + ScratchPad.recorded.to_s.should == "In dying thread" + end + end + + it "can be rescued by outer rescue clause when inner ensure clause raises exception" do + thread = Thread.new do + begin + begin + Thread.current.kill + ensure + raise "In dying thread" + end + rescue Exception + ScratchPad.record $! + end + :end_of_thread_block + end + + thread.value.should == :end_of_thread_block + ScratchPad.recorded.to_s.should == "In dying thread" + end + + it "is deferred if ensure clause does Thread.stop" do + ThreadSpecs.wakeup_dying_sleeping_thread(:kill) { Thread.stop; ScratchPad.record :after_sleep } + ScratchPad.recorded.should == :after_sleep + end + + # Hangs on 1.8.6.114 OS X, possibly also on Linux + quarantine! do + it "is deferred if ensure clause sleeps" do + ThreadSpecs.wakeup_dying_sleeping_thread(:kill) { sleep; ScratchPad.record :after_sleep } + ScratchPad.recorded.should == :after_sleep + end + end + + # This case occurred in JRuby where native threads are used to provide + # the same behavior as MRI green threads. Key to this issue was the fact + # that the thread which called #exit in its block was also being explicitly + # sent #join from outside the thread. The 100.times provides a certain + # probability that the deadlock will occur. It was sufficient to reliably + # reproduce the deadlock in JRuby. + it "does not deadlock when called from within the thread while being joined from without" do + 100.times do + t = Thread.new { Thread.stop; Thread.current.kill } + Thread.pass while t.status and t.status != "sleep" + t.wakeup.should == t + t.join.should == t + end + end end describe "Thread.kill" do @@ -15,7 +224,7 @@ platform_is_not :mingw do Thread.pass while thread.status and thread.status != "sleep" Thread.kill(thread).should == thread thread.join - thread.status.should be_false + thread.status.should == false end end end diff --git a/spec/ruby/core/thread/list_spec.rb b/spec/ruby/core/thread/list_spec.rb index 3c6f70c13e..5036841d58 100644 --- a/spec/ruby/core/thread/list_spec.rb +++ b/spec/ruby/core/thread/list_spec.rb @@ -3,15 +3,15 @@ require_relative 'fixtures/classes' describe "Thread.list" do it "includes the current and main thread" do - Thread.list.should include(Thread.current) - Thread.list.should include(Thread.main) + Thread.list.should.include?(Thread.current) + Thread.list.should.include?(Thread.main) end it "includes threads of non-default thread groups" do t = Thread.new { sleep } begin ThreadGroup.new.add(t) - Thread.list.should include(t) + Thread.list.should.include?(t) ensure t.kill t.join @@ -21,7 +21,7 @@ describe "Thread.list" do it "does not include deceased threads" do t = Thread.new { 1; } t.join - Thread.list.should_not include(t) + Thread.list.should_not.include?(t) end it "includes waiting threads" do @@ -29,7 +29,7 @@ describe "Thread.list" do t = Thread.new { q.pop } begin Thread.pass while t.status and t.status != 'sleep' - Thread.list.should include(t) + Thread.list.should.include?(t) ensure q << nil t.join @@ -45,7 +45,7 @@ describe "Thread.list" do begin Thread.list.each { |th| - th.should be_kind_of(Thread) + th.should.is_a?(Thread) } end while spawner.alive? diff --git a/spec/ruby/core/thread/name_spec.rb b/spec/ruby/core/thread/name_spec.rb index 9b3d2f4b09..47d807be4d 100644 --- a/spec/ruby/core/thread/name_spec.rb +++ b/spec/ruby/core/thread/name_spec.rb @@ -36,7 +36,7 @@ describe "Thread#name=" do it "raises an ArgumentError if the name includes a null byte" do -> { @thread.name = "new thread\0name" - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end it "can be reset to nil" do diff --git a/spec/ruby/core/thread/native_thread_id_spec.rb b/spec/ruby/core/thread/native_thread_id_spec.rb index 374cc59279..cc72e0b853 100644 --- a/spec/ruby/core/thread/native_thread_id_spec.rb +++ b/spec/ruby/core/thread/native_thread_id_spec.rb @@ -3,7 +3,7 @@ require_relative '../../spec_helper' platform_is :linux, :darwin, :windows, :freebsd do describe "Thread#native_thread_id" do it "returns an integer when the thread is alive" do - Thread.current.native_thread_id.should be_kind_of(Integer) + Thread.current.native_thread_id.should.is_a?(Integer) end it "returns nil when the thread is not running" do @@ -18,12 +18,8 @@ platform_is :linux, :darwin, :windows, :freebsd do main_thread_id = Thread.current.native_thread_id t_thread_id = t.native_thread_id - if ruby_version_is "3.3" - # native_thread_id can be nil on a M:N scheduler - t_thread_id.should be_kind_of(Integer) if t_thread_id != nil - else - t_thread_id.should be_kind_of(Integer) - end + # native_thread_id can be nil on a M:N scheduler + t_thread_id.should.is_a?(Integer) if t_thread_id != nil main_thread_id.should_not == t_thread_id diff --git a/spec/ruby/core/thread/new_spec.rb b/spec/ruby/core/thread/new_spec.rb index 47a836201c..acb6cd4e30 100644 --- a/spec/ruby/core/thread/new_spec.rb +++ b/spec/ruby/core/thread/new_spec.rb @@ -18,7 +18,7 @@ describe "Thread.new" do end it "raises an exception when not given a block" do - -> { Thread.new }.should raise_error(ThreadError) + -> { Thread.new }.should.raise(ThreadError) end it "creates a subclass of thread calls super with a block in initialize" do @@ -36,7 +36,7 @@ describe "Thread.new" do -> { c.new - }.should raise_error(ThreadError) + }.should.raise(ThreadError) end it "calls and respects #initialize for the block to use" do diff --git a/spec/ruby/core/thread/pending_interrupt_spec.rb b/spec/ruby/core/thread/pending_interrupt_spec.rb index cd565d92a4..5fbe7422a9 100644 --- a/spec/ruby/core/thread/pending_interrupt_spec.rb +++ b/spec/ruby/core/thread/pending_interrupt_spec.rb @@ -19,7 +19,7 @@ describe "Thread.pending_interrupt?" do Thread.pending_interrupt?.should == true executed = true end - }.should raise_error(RuntimeError, "interrupt") + }.should.raise(RuntimeError, "interrupt") executed.should == true Thread.pending_interrupt?.should == false end diff --git a/spec/ruby/core/thread/priority_spec.rb b/spec/ruby/core/thread/priority_spec.rb index e13ad478b5..970f7f9971 100644 --- a/spec/ruby/core/thread/priority_spec.rb +++ b/spec/ruby/core/thread/priority_spec.rb @@ -15,19 +15,19 @@ describe "Thread#priority" do end it "inherits the priority of the current thread while running" do - @thread.alive?.should be_true + @thread.alive?.should == true @thread.priority.should == @current_priority end it "maintain the priority of the current thread after death" do ThreadSpecs.state = :exit @thread.join - @thread.alive?.should be_false + @thread.alive?.should == false @thread.priority.should == @current_priority end it "returns an integer" do - @thread.priority.should be_kind_of(Integer) + @thread.priority.should.is_a?(Integer) end end @@ -59,7 +59,7 @@ describe "Thread#priority=" do describe "when set with a non-integer" do it "raises a type error" do - ->{ @thread.priority = Object.new }.should raise_error(TypeError) + ->{ @thread.priority = Object.new }.should.raise(TypeError) end end diff --git a/spec/ruby/core/thread/raise_spec.rb b/spec/ruby/core/thread/raise_spec.rb index 49323cf270..efc09d4a35 100644 --- a/spec/ruby/core/thread/raise_spec.rb +++ b/spec/ruby/core/thread/raise_spec.rb @@ -3,6 +3,16 @@ require_relative 'fixtures/classes' require_relative '../../shared/kernel/raise' describe "Thread#raise" 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? @@ -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 - -> { 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,7 @@ describe "Thread#raise on a sleeping thread" do ThreadSpecs.spin_until_sleeping(t) t.raise end - -> { t.value }.should raise_error(RuntimeError) + -> { t.value }.should.raise(RuntimeError) end it "re-raises a previously rescued exception without overwriting the backtrace" do @@ -98,8 +108,8 @@ describe "Thread#raise on a sleeping thread" do 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}:") + raised_again.backtrace.first.should.include?("#{__FILE__}:#{initial_raise_line}:") + raised_again.backtrace.first.should_not.include?("#{__FILE__}:#{raise_again_line}:") end end @@ -126,6 +136,31 @@ describe "Thread#raise on a sleeping thread" do [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 describe "Thread#raise on a running thread" do @@ -145,19 +180,19 @@ 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 @@ -171,7 +206,7 @@ describe "Thread#raise on a running thread" do q.pop # wait for `report_on_exception = false`. t.raise - -> { 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 @@ -190,7 +225,7 @@ describe "Thread#raise on a running thread" do rescue Thread.pass until raised t.raise RangeError - -> { t.value }.should raise_error(RangeError) + -> { t.value }.should.raise(RangeError) end end @@ -211,7 +246,7 @@ describe "Thread#raise on a running thread" do Thread.pass until raised t.raise end - -> { t.value }.should raise_error(RuntimeError) + -> { t.value }.should.raise(RuntimeError) end end @@ -227,6 +262,6 @@ describe "Thread#raise on same thread" do Thread.current.raise end end - -> { t.value }.should raise_error(RuntimeError, '') + -> { t.value }.should.raise(RuntimeError, '') end end diff --git a/spec/ruby/core/thread/report_on_exception_spec.rb b/spec/ruby/core/thread/report_on_exception_spec.rb index d9daa041cd..9cf5260808 100644 --- a/spec/ruby/core/thread/report_on_exception_spec.rb +++ b/spec/ruby/core/thread/report_on_exception_spec.rb @@ -58,7 +58,7 @@ describe "Thread#report_on_exception=" do -> { t.join - }.should raise_error(RuntimeError, "Thread#report_on_exception specs") + }.should.raise(RuntimeError, "Thread#report_on_exception specs") end it "prints a backtrace on $stderr in the regular backtrace order" do @@ -86,7 +86,7 @@ describe "Thread#report_on_exception=" do -> { t.join - }.should raise_error(RuntimeError, "Thread#report_on_exception specs backtrace order") + }.should.raise(RuntimeError, "Thread#report_on_exception specs backtrace order") end it "prints the backtrace even if the thread was killed just after Thread#raise" do @@ -107,7 +107,7 @@ describe "Thread#report_on_exception=" do -> { t.join - }.should raise_error(RuntimeError, "Thread#report_on_exception before kill spec") + }.should.raise(RuntimeError, "Thread#report_on_exception before kill spec") end end @@ -124,7 +124,7 @@ describe "Thread#report_on_exception=" do -> { t.join - }.should raise_error(RuntimeError, "Thread#report_on_exception specs") + }.should.raise(RuntimeError, "Thread#report_on_exception specs") end end @@ -144,12 +144,12 @@ describe "Thread#report_on_exception=" do -> { mutex.sleep(5) - }.should raise_error(RuntimeError, "Thread#report_on_exception specs") + }.should.raise(RuntimeError, "Thread#report_on_exception specs") }.should output("", /Thread.+terminated with exception.+Thread#report_on_exception specs/m) -> { t.join - }.should raise_error(RuntimeError, "Thread#report_on_exception specs") + }.should.raise(RuntimeError, "Thread#report_on_exception specs") end end end diff --git a/spec/ruby/core/thread/shared/exit.rb b/spec/ruby/core/thread/shared/exit.rb deleted file mode 100644 index 13e8832684..0000000000 --- a/spec/ruby/core/thread/shared/exit.rb +++ /dev/null @@ -1,219 +0,0 @@ -describe :thread_exit, shared: true do - before :each do - ScratchPad.clear - end - - # This spec randomly kills mspec worker like: https://ci.appveyor.com/project/ruby/ruby/builds/19390874/job/wv1bsm8skd4e1pxl - # TODO: Investigate the cause or at least print helpful logs, and remove this `platform_is_not` guard. - platform_is_not :mingw do - - it "kills sleeping thread" do - sleeping_thread = Thread.new do - sleep - ScratchPad.record :after_sleep - end - Thread.pass while sleeping_thread.status and sleeping_thread.status != "sleep" - sleeping_thread.send(@method) - sleeping_thread.join - ScratchPad.recorded.should == nil - end - - it "kills current thread" do - thread = Thread.new do - Thread.current.send(@method) - ScratchPad.record :after_sleep - end - thread.join - ScratchPad.recorded.should == nil - end - - it "runs ensure clause" do - thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record :in_ensure_clause } - thread.join - ScratchPad.recorded.should == :in_ensure_clause - end - - it "runs nested ensure clauses" do - ScratchPad.record [] - @outer = Thread.new do - begin - @inner = Thread.new do - begin - sleep - ensure - ScratchPad << :inner_ensure_clause - end - end - sleep - ensure - ScratchPad << :outer_ensure_clause - @inner.send(@method) - @inner.join - end - end - Thread.pass while @outer.status and @outer.status != "sleep" - Thread.pass until @inner - Thread.pass while @inner.status and @inner.status != "sleep" - @outer.send(@method) - @outer.join - ScratchPad.recorded.should include(:inner_ensure_clause) - ScratchPad.recorded.should include(:outer_ensure_clause) - end - - it "does not set $!" do - thread = ThreadSpecs.dying_thread_ensures(@method) { ScratchPad.record $! } - thread.join - ScratchPad.recorded.should == nil - end - - it "does not reset $!" do - ScratchPad.record [] - - exc = RuntimeError.new("foo") - thread = Thread.new do - begin - raise exc - ensure - ScratchPad << $! - begin - Thread.current.send(@method) - ensure - ScratchPad << $! - end - end - end - thread.join - ScratchPad.recorded.should == [exc, exc] - end - - it "cannot be rescued" do - thread = Thread.new do - begin - Thread.current.send(@method) - rescue Exception - ScratchPad.record :in_rescue - end - ScratchPad.record :end_of_thread_block - end - - thread.join - ScratchPad.recorded.should == nil - end - - it "kills the entire thread when a fiber is active" do - t = Thread.new do - Fiber.new do - sleep - end.resume - ScratchPad.record :fiber_resumed - end - Thread.pass while t.status and t.status != "sleep" - t.send(@method) - t.join - ScratchPad.recorded.should == nil - end - - it "kills other fibers of that thread without running their ensure clauses" do - t = Thread.new do - f = Fiber.new do - ScratchPad.record :fiber_resumed - begin - Fiber.yield - ensure - ScratchPad.record :fiber_ensure - end - end - f.resume - sleep - end - Thread.pass until t.stop? - t.send(@method) - t.join - ScratchPad.recorded.should == :fiber_resumed - end - - # This spec is a mess. It fails randomly, it hangs on MRI, it needs to be removed - quarantine! do - it "killing dying running does nothing" do - in_ensure_clause = false - exit_loop = true - t = ThreadSpecs.dying_thread_ensures do - in_ensure_clause = true - loop { if exit_loop then break end } - ScratchPad.record :after_stop - end - - Thread.pass until in_ensure_clause == true - 10.times { t.send(@method); Thread.pass } - exit_loop = true - t.join - ScratchPad.recorded.should == :after_stop - end - end - - quarantine! do - - it "propagates inner exception to Thread.join if there is an outer ensure clause" do - thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { } - -> { thread.join }.should raise_error(RuntimeError, "In dying thread") - end - - it "runs all outer ensure clauses even if inner ensure clause raises exception" do - ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record :in_outer_ensure_clause } - ScratchPad.recorded.should == :in_outer_ensure_clause - end - - it "sets $! in outer ensure clause if inner ensure clause raises exception" do - ThreadSpecs.join_dying_thread_with_outer_ensure(@method) { ScratchPad.record $! } - ScratchPad.recorded.to_s.should == "In dying thread" - end - end - - it "can be rescued by outer rescue clause when inner ensure clause raises exception" do - thread = Thread.new do - begin - begin - Thread.current.send(@method) - ensure - raise "In dying thread" - end - rescue Exception - ScratchPad.record $! - end - :end_of_thread_block - end - - thread.value.should == :end_of_thread_block - ScratchPad.recorded.to_s.should == "In dying thread" - end - - it "is deferred if ensure clause does Thread.stop" do - ThreadSpecs.wakeup_dying_sleeping_thread(@method) { Thread.stop; ScratchPad.record :after_sleep } - ScratchPad.recorded.should == :after_sleep - end - - # Hangs on 1.8.6.114 OS X, possibly also on Linux - quarantine! do - it "is deferred if ensure clause sleeps" do - ThreadSpecs.wakeup_dying_sleeping_thread(@method) { sleep; ScratchPad.record :after_sleep } - ScratchPad.recorded.should == :after_sleep - end - end - - # This case occurred in JRuby where native threads are used to provide - # the same behavior as MRI green threads. Key to this issue was the fact - # that the thread which called #exit in its block was also being explicitly - # sent #join from outside the thread. The 100.times provides a certain - # probability that the deadlock will occur. It was sufficient to reliably - # reproduce the deadlock in JRuby. - it "does not deadlock when called from within the thread while being joined from without" do - 100.times do - t = Thread.new { Thread.stop; Thread.current.send(@method) } - Thread.pass while t.status and t.status != "sleep" - t.wakeup.should == t - t.join.should == t - end - end - - end # platform_is_not :mingw -end diff --git a/spec/ruby/core/thread/shared/start.rb b/spec/ruby/core/thread/shared/start.rb deleted file mode 100644 index 2ba926bf00..0000000000 --- a/spec/ruby/core/thread/shared/start.rb +++ /dev/null @@ -1,41 +0,0 @@ -describe :thread_start, shared: true do - before :each do - ScratchPad.clear - end - - it "raises an ArgumentError if not passed a block" do - -> { - Thread.send(@method) - }.should raise_error(ArgumentError) - end - - it "spawns a new Thread running the block" do - run = false - t = Thread.send(@method) { run = true } - t.should be_kind_of(Thread) - t.join - - run.should be_true - end - - it "respects Thread subclasses" do - c = Class.new(Thread) - t = c.send(@method) { } - t.should be_kind_of(c) - - t.join - end - - it "does not call #initialize" do - c = Class.new(Thread) do - def initialize - ScratchPad.record :bad - end - end - - t = c.send(@method) { } - t.join - - ScratchPad.recorded.should == nil - end -end diff --git a/spec/ruby/core/thread/shared/to_s.rb b/spec/ruby/core/thread/shared/to_s.rb deleted file mode 100644 index 43640deb33..0000000000 --- a/spec/ruby/core/thread/shared/to_s.rb +++ /dev/null @@ -1,53 +0,0 @@ -require_relative '../fixtures/classes' - -describe :thread_to_s, shared: true do - it "returns a description including file and line number" do - thread, line = Thread.new { "hello" }, __LINE__ - thread.join - thread.send(@method).should =~ /^#<Thread:([^ ]*?) #{Regexp.escape __FILE__}:#{line} \w+>$/ - end - - it "has a binary encoding" do - ThreadSpecs.status_of_current_thread.send(@method).encoding.should == Encoding::BINARY - end - - it "can check it's own status" do - ThreadSpecs.status_of_current_thread.send(@method).should include('run') - end - - it "describes a running thread" do - ThreadSpecs.status_of_running_thread.send(@method).should include('run') - end - - it "describes a sleeping thread" do - ThreadSpecs.status_of_sleeping_thread.send(@method).should include('sleep') - end - - it "describes a blocked thread" do - ThreadSpecs.status_of_blocked_thread.send(@method).should include('sleep') - end - - it "describes a completed thread" do - ThreadSpecs.status_of_completed_thread.send(@method).should include('dead') - end - - it "describes a killed thread" do - ThreadSpecs.status_of_killed_thread.send(@method).should include('dead') - end - - it "describes a thread with an uncaught exception" do - ThreadSpecs.status_of_thread_with_uncaught_exception.send(@method).should include('dead') - end - - it "describes a dying sleeping thread" do - ThreadSpecs.status_of_dying_sleeping_thread.send(@method).should include('sleep') - end - - it "reports aborting on a killed thread" do - ThreadSpecs.status_of_dying_running_thread.send(@method).should include('aborting') - end - - it "reports aborting on a killed thread after sleep" do - ThreadSpecs.status_of_dying_thread_after_sleep.send(@method).should include('aborting') - end -end diff --git a/spec/ruby/core/thread/shared/wakeup.rb b/spec/ruby/core/thread/shared/wakeup.rb index 6f010fea25..c89235ba60 100644 --- a/spec/ruby/core/thread/shared/wakeup.rb +++ b/spec/ruby/core/thread/shared/wakeup.rb @@ -57,6 +57,6 @@ describe :thread_wakeup, shared: true do it "raises a ThreadError when trying to wake up a dead thread" do t = Thread.new { 1 } t.join - -> { t.send @method }.should raise_error(ThreadError) + -> { t.send @method }.should.raise(ThreadError) end end diff --git a/spec/ruby/core/thread/start_spec.rb b/spec/ruby/core/thread/start_spec.rb index 3dd040f98b..a2ee52180b 100644 --- a/spec/ruby/core/thread/start_spec.rb +++ b/spec/ruby/core/thread/start_spec.rb @@ -1,9 +1,43 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/start' describe "Thread.start" do - describe "Thread.start" do - it_behaves_like :thread_start, :start + before :each do + ScratchPad.clear + end + + it "raises an ArgumentError if not passed a block" do + -> { + Thread.start + }.should.raise(ArgumentError) + end + + it "spawns a new Thread running the block" do + run = false + t = Thread.start { run = true } + t.should.is_a?(Thread) + t.join + + run.should == true + end + + it "respects Thread subclasses" do + c = Class.new(Thread) + t = c.start { } + t.should.is_a?(c) + + t.join + end + + it "does not call #initialize" do + c = Class.new(Thread) do + def initialize + ScratchPad.record :bad + end + end + + t = c.start { } + t.join + + ScratchPad.recorded.should == nil end end diff --git a/spec/ruby/core/thread/terminate_spec.rb b/spec/ruby/core/thread/terminate_spec.rb index cf6cab472b..68c431a0c0 100644 --- a/spec/ruby/core/thread/terminate_spec.rb +++ b/spec/ruby/core/thread/terminate_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' -require_relative 'shared/exit' describe "Thread#terminate" do - it_behaves_like :thread_exit, :terminate + it "is an alias of Thread#kill" do + Thread.instance_method(:terminate).should == Thread.instance_method(:kill) + end end diff --git a/spec/ruby/core/thread/thread_variable_get_spec.rb b/spec/ruby/core/thread/thread_variable_get_spec.rb index 1ea34cf2b3..3d92cd5479 100644 --- a/spec/ruby/core/thread/thread_variable_get_spec.rb +++ b/spec/ruby/core/thread/thread_variable_get_spec.rb @@ -10,7 +10,7 @@ describe "Thread#thread_variable_get" do end it "returns nil if the variable is not set" do - @t.thread_variable_get(:a).should be_nil + @t.thread_variable_get(:a).should == nil end it "returns the value previously set by #thread_variable_set" do @@ -20,7 +20,7 @@ describe "Thread#thread_variable_get" do it "returns a value private to self" do @t.thread_variable_set(:thread_variable_get_spec, 82) - Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil + Thread.current.thread_variable_get(:thread_variable_get_spec).should == nil end it "accepts String and Symbol keys interchangeably" do @@ -38,23 +38,23 @@ describe "Thread#thread_variable_get" do it "does not raise FrozenError if the thread is frozen" do @t.freeze - @t.thread_variable_get(:a).should be_nil + @t.thread_variable_get(:a).should == nil end it "raises a TypeError if the key is neither Symbol nor String when thread variables are already set" do @t.thread_variable_set(:a, 49) - -> { @t.thread_variable_get(123) }.should raise_error(TypeError, /123 is not a symbol/) + -> { @t.thread_variable_get(123) }.should.raise(TypeError, /123 is not a symbol/) end ruby_version_is '3.4' do it "raises a TypeError if the key is neither Symbol nor String when no thread variables are set" do - -> { @t.thread_variable_get(123) }.should raise_error(TypeError, /123 is not a symbol/) + -> { @t.thread_variable_get(123) }.should.raise(TypeError, /123 is not a symbol/) end it "raises a TypeError if the key is neither Symbol nor String without calling #to_sym" do key = mock('key') key.should_not_receive(:to_sym) - -> { @t.thread_variable_get(key) }.should raise_error(TypeError, /#{Regexp.escape(key.inspect)} is not a symbol/) + -> { @t.thread_variable_get(key) }.should.raise(TypeError, /#{Regexp.escape(key.inspect)} is not a symbol/) end end end diff --git a/spec/ruby/core/thread/thread_variable_set_spec.rb b/spec/ruby/core/thread/thread_variable_set_spec.rb index eadee76afb..f8d25364ae 100644 --- a/spec/ruby/core/thread/thread_variable_set_spec.rb +++ b/spec/ruby/core/thread/thread_variable_set_spec.rb @@ -21,7 +21,7 @@ describe "Thread#thread_variable_set" do it "sets a value private to self" do @t.thread_variable_set(:thread_variable_get_spec, 82) @t.thread_variable_get(:thread_variable_get_spec).should == 82 - Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil + Thread.current.thread_variable_get(:thread_variable_get_spec).should == nil end it "accepts String and Symbol keys interchangeably" do @@ -42,21 +42,21 @@ describe "Thread#thread_variable_set" do it "removes a key if the value is nil" do @t.thread_variable_set(:a, 52) @t.thread_variable_set(:a, nil) - @t.thread_variable?(:a).should be_false + @t.thread_variable?(:a).should == false end it "raises a FrozenError if the thread is frozen" do @t.freeze - -> { @t.thread_variable_set(:a, 1) }.should raise_error(FrozenError, "can't modify frozen thread locals") + -> { @t.thread_variable_set(:a, 1) }.should.raise(FrozenError, "can't modify frozen thread locals") end it "raises a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do - -> { @t.thread_variable_set(123, 1) }.should raise_error(TypeError, /123 is not a symbol/) + -> { @t.thread_variable_set(123, 1) }.should.raise(TypeError, /123 is not a symbol/) end it "does not try to convert the key with #to_sym" do key = mock('key') key.should_not_receive(:to_sym) - -> { @t.thread_variable_set(key, 42) }.should raise_error(TypeError, /#{Regexp.quote(key.inspect)} is not a symbol/) + -> { @t.thread_variable_set(key, 42) }.should.raise(TypeError, /#{Regexp.quote(key.inspect)} is not a symbol/) end end diff --git a/spec/ruby/core/thread/thread_variable_spec.rb b/spec/ruby/core/thread/thread_variable_spec.rb index 1b021e9404..ebafd4f3eb 100644 --- a/spec/ruby/core/thread/thread_variable_spec.rb +++ b/spec/ruby/core/thread/thread_variable_spec.rb @@ -11,50 +11,50 @@ describe "Thread#thread_variable?" do it "returns false if the thread variables do not contain 'key'" do @t.thread_variable_set(:a, 2) - @t.thread_variable?(:b).should be_false + @t.thread_variable?(:b).should == false end it "returns true if the thread variables contain 'key'" do @t.thread_variable_set(:a, 2) - @t.thread_variable?(:a).should be_true + @t.thread_variable?(:a).should == true end it "accepts String and Symbol keys interchangeably" do - @t.thread_variable?('a').should be_false - @t.thread_variable?(:a).should be_false + @t.thread_variable?('a').should == false + @t.thread_variable?(:a).should == false @t.thread_variable_set(:a, 49) - @t.thread_variable?('a').should be_true - @t.thread_variable?(:a).should be_true + @t.thread_variable?('a').should == true + @t.thread_variable?(:a).should == true end it "converts a key that is neither String nor Symbol with #to_str" do key = mock('key') key.should_receive(:to_str).and_return('a') @t.thread_variable_set(:a, 49) - @t.thread_variable?(key).should be_true + @t.thread_variable?(key).should == true end it "does not raise FrozenError if the thread is frozen" do @t.freeze - @t.thread_variable?(:a).should be_false + @t.thread_variable?(:a).should == false end it "raises a TypeError if the key is neither Symbol nor String when thread variables are already set" do @t.thread_variable_set(:a, 49) - -> { @t.thread_variable?(123) }.should raise_error(TypeError, /123 is not a symbol/) + -> { @t.thread_variable?(123) }.should.raise(TypeError, /123 is not a symbol/) end ruby_version_is '3.4' do it "raises a TypeError if the key is neither Symbol nor String when no thread variables are set" do - -> { @t.thread_variable?(123) }.should raise_error(TypeError, /123 is not a symbol/) + -> { @t.thread_variable?(123) }.should.raise(TypeError, /123 is not a symbol/) end it "raises a TypeError if the key is neither Symbol nor String without calling #to_sym" do key = mock('key') key.should_not_receive(:to_sym) - -> { @t.thread_variable?(key) }.should raise_error(TypeError, /#{Regexp.escape(key.inspect)} is not a symbol/) + -> { @t.thread_variable?(key) }.should.raise(TypeError, /#{Regexp.escape(key.inspect)} is not a symbol/) end end end diff --git a/spec/ruby/core/thread/thread_variables_spec.rb b/spec/ruby/core/thread/thread_variables_spec.rb index 51ceef3376..f15c681a8f 100644 --- a/spec/ruby/core/thread/thread_variables_spec.rb +++ b/spec/ruby/core/thread/thread_variables_spec.rb @@ -19,7 +19,8 @@ describe "Thread#thread_variables" do it "returns the keys private to self" do @t.thread_variable_set(:a, 82) @t.thread_variable_set(:b, 82) - Thread.current.thread_variables.should_not include(:a, :b) + Thread.current.thread_variables.should_not.include?(:a) + Thread.current.thread_variables.should_not.include?(:b) end it "only contains user thread variables and is empty initially" do diff --git a/spec/ruby/core/thread/to_s_spec.rb b/spec/ruby/core/thread/to_s_spec.rb index cb182a017f..2aef426de8 100644 --- a/spec/ruby/core/thread/to_s_spec.rb +++ b/spec/ruby/core/thread/to_s_spec.rb @@ -1,6 +1,54 @@ require_relative '../../spec_helper' -require_relative 'shared/to_s' +require_relative 'fixtures/classes' describe "Thread#to_s" do - it_behaves_like :thread_to_s, :to_s + it "returns a description including file and line number" do + thread, line = Thread.new { "hello" }, __LINE__ + thread.join + thread.to_s.should =~ /^#<Thread:([^ ]*?) #{Regexp.escape __FILE__}:#{line} \w+>$/ + end + + it "has a binary encoding" do + ThreadSpecs.status_of_current_thread.to_s.encoding.should == Encoding::BINARY + end + + it "can check it's own status" do + ThreadSpecs.status_of_current_thread.to_s.should.include?('run') + end + + it "describes a running thread" do + ThreadSpecs.status_of_running_thread.to_s.should.include?('run') + end + + it "describes a sleeping thread" do + ThreadSpecs.status_of_sleeping_thread.to_s.should.include?('sleep') + end + + it "describes a blocked thread" do + ThreadSpecs.status_of_blocked_thread.to_s.should.include?('sleep') + end + + it "describes a completed thread" do + ThreadSpecs.status_of_completed_thread.to_s.should.include?('dead') + end + + it "describes a killed thread" do + ThreadSpecs.status_of_killed_thread.to_s.should.include?('dead') + end + + it "describes a thread with an uncaught exception" do + ThreadSpecs.status_of_thread_with_uncaught_exception.to_s.should.include?('dead') + end + + it "describes a dying sleeping thread" do + ThreadSpecs.status_of_dying_sleeping_thread.to_s.should.include?('sleep') + end + + it "reports aborting on a killed thread" do + ThreadSpecs.status_of_dying_running_thread.to_s.should.include?('aborting') + end + + it "reports aborting on a killed thread after sleep" do + ThreadSpecs.status_of_dying_thread_after_sleep.to_s.should.include?('aborting') + end end diff --git a/spec/ruby/core/thread/value_spec.rb b/spec/ruby/core/thread/value_spec.rb index 30e43abd1a..50c823171d 100644 --- a/spec/ruby/core/thread/value_spec.rb +++ b/spec/ruby/core/thread/value_spec.rb @@ -11,7 +11,7 @@ describe "Thread#value" do Thread.current.report_on_exception = false raise "Hello" } - -> { t.value }.should raise_error(RuntimeError, "Hello") + -> { t.value }.should.raise(RuntimeError, "Hello") end it "is nil for a killed thread" do |
