summaryrefslogtreecommitdiff
path: root/spec/ruby/core/thread
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/thread')
-rw-r--r--spec/ruby/core/thread/abort_on_exception_spec.rb10
-rw-r--r--spec/ruby/core/thread/allocate_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace/limit_spec.rb18
-rw-r--r--spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb19
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/classes.rb104
-rw-r--r--spec/ruby/core/thread/backtrace/location/inspect_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace/location/label_spec.rb194
-rw-r--r--spec/ruby/core/thread/backtrace/location/to_s_spec.rb2
-rw-r--r--spec/ruby/core/thread/backtrace_locations_spec.rb12
-rw-r--r--spec/ruby/core/thread/backtrace_spec.rb4
-rw-r--r--spec/ruby/core/thread/current_spec.rb10
-rw-r--r--spec/ruby/core/thread/each_caller_location_spec.rb70
-rw-r--r--spec/ruby/core/thread/element_reference_spec.rb4
-rw-r--r--spec/ruby/core/thread/element_set_spec.rb8
-rw-r--r--spec/ruby/core/thread/exit_spec.rb9
-rw-r--r--spec/ruby/core/thread/fetch_spec.rb6
-rw-r--r--spec/ruby/core/thread/fixtures/classes.rb27
-rw-r--r--spec/ruby/core/thread/fork_spec.rb6
-rw-r--r--spec/ruby/core/thread/handle_interrupt_spec.rb6
-rw-r--r--spec/ruby/core/thread/initialize_spec.rb2
-rw-r--r--spec/ruby/core/thread/inspect_spec.rb5
-rw-r--r--spec/ruby/core/thread/join_spec.rb20
-rw-r--r--spec/ruby/core/thread/key_spec.rb16
-rw-r--r--spec/ruby/core/thread/keys_spec.rb12
-rw-r--r--spec/ruby/core/thread/kill_spec.rb215
-rw-r--r--spec/ruby/core/thread/list_spec.rb12
-rw-r--r--spec/ruby/core/thread/name_spec.rb2
-rw-r--r--spec/ruby/core/thread/native_thread_id_spec.rb48
-rw-r--r--spec/ruby/core/thread/new_spec.rb4
-rw-r--r--spec/ruby/core/thread/pending_interrupt_spec.rb2
-rw-r--r--spec/ruby/core/thread/priority_spec.rb8
-rw-r--r--spec/ruby/core/thread/raise_spec.rb67
-rw-r--r--spec/ruby/core/thread/report_on_exception_spec.rb12
-rw-r--r--spec/ruby/core/thread/shared/exit.rb219
-rw-r--r--spec/ruby/core/thread/shared/start.rb41
-rw-r--r--spec/ruby/core/thread/shared/to_s.rb53
-rw-r--r--spec/ruby/core/thread/shared/wakeup.rb2
-rw-r--r--spec/ruby/core/thread/start_spec.rb42
-rw-r--r--spec/ruby/core/thread/terminate_spec.rb6
-rw-r--r--spec/ruby/core/thread/thread_variable_get_spec.rb12
-rw-r--r--spec/ruby/core/thread/thread_variable_set_spec.rb10
-rw-r--r--spec/ruby/core/thread/thread_variable_spec.rb22
-rw-r--r--spec/ruby/core/thread/thread_variables_spec.rb3
-rw-r--r--spec/ruby/core/thread/to_s_spec.rb52
-rw-r--r--spec/ruby/core/thread/value_spec.rb2
45 files changed, 860 insertions, 542 deletions
diff --git a/spec/ruby/core/thread/abort_on_exception_spec.rb b/spec/ruby/core/thread/abort_on_exception_spec.rb
index 34b648ca0f..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
@@ -72,7 +72,7 @@ describe "Thread.abort_on_exception" do
end
after do
- Thread.abort_on_exception = @abort_on_exception
+ Thread.abort_on_exception = @abort_on_exception
end
it "is false by default" do
@@ -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/limit_spec.rb b/spec/ruby/core/thread/backtrace/limit_spec.rb
index 26a87a806c..b55ca67ea0 100644
--- a/spec/ruby/core/thread/backtrace/limit_spec.rb
+++ b/spec/ruby/core/thread/backtrace/limit_spec.rb
@@ -1,15 +1,13 @@
require_relative '../../../spec_helper'
-ruby_version_is "3.1" do
- describe "Thread::Backtrace.limit" do
- it "returns maximum backtrace length set by --backtrace-limit command-line option" do
- out = ruby_exe("print Thread::Backtrace.limit", options: "--backtrace-limit=2")
- out.should == "2"
- end
+describe "Thread::Backtrace.limit" do
+ it "returns maximum backtrace length set by --backtrace-limit command-line option" do
+ out = ruby_exe("print Thread::Backtrace.limit", options: "--backtrace-limit=2")
+ out.should == "2"
+ end
- it "returns -1 when --backtrace-limit command-line option is not set" do
- out = ruby_exe("print Thread::Backtrace.limit")
- out.should == "-1"
- end
+ it "returns -1 when --backtrace-limit command-line option is not set" do
+ out = ruby_exe("print Thread::Backtrace.limit")
+ out.should == "-1"
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 6e381e4868..6d9482f2ae 100644
--- a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
+++ b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
@@ -27,20 +27,11 @@ describe 'Thread::Backtrace::Location#absolute_path' do
end
context "when used in eval with a given filename" do
- code = "caller_locations(0)[0].absolute_path"
+ it "returns nil with absolute_path" do
+ code = "caller_locations(0)[0].absolute_path"
- ruby_version_is ""..."3.1" do
- it "returns filename with absolute_path" do
- eval(code, nil, "foo.rb").should == "foo.rb"
- eval(code, nil, "foo/bar.rb").should == "foo/bar.rb"
- end
- end
-
- ruby_version_is "3.1" do
- it "returns nil with absolute_path" do
- eval(code, nil, "foo.rb").should == nil
- eval(code, nil, "foo/bar.rb").should == nil
- end
+ eval(code, nil, "foo.rb").should == nil
+ eval(code, nil, "foo/bar.rb").should == nil
end
end
@@ -51,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 17a08c8a15..cc72e0b853 100644
--- a/spec/ruby/core/thread/native_thread_id_spec.rb
+++ b/spec/ruby/core/thread/native_thread_id_spec.rb
@@ -1,37 +1,31 @@
require_relative '../../spec_helper'
-ruby_version_is "3.1" do
- 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)
- end
+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.is_a?(Integer)
+ end
- it "returns nil when the thread is not running" do
- t = Thread.new {}
- t.join
- t.native_thread_id.should == nil
- end
+ it "returns nil when the thread is not running" do
+ t = Thread.new {}
+ t.join
+ t.native_thread_id.should == nil
+ end
- it "each thread has different native thread id" do
- t = Thread.new { sleep }
- Thread.pass until t.stop?
- main_thread_id = Thread.current.native_thread_id
- t_thread_id = t.native_thread_id
+ it "each thread has different native thread id" do
+ t = Thread.new { sleep }
+ Thread.pass until t.stop?
+ 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
+ main_thread_id.should_not == t_thread_id
- t.run
- t.join
- t.native_thread_id.should == nil
- end
+ t.run
+ t.join
+ t.native_thread_id.should == nil
end
end
end
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