mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Move spec/rubyspec to spec/ruby for consistency
* Other ruby implementations use the spec/ruby directory. [Misc #13792] [ruby-core:82287] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59979 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
75bfc6440d
commit
1d15d5f080
4370 changed files with 0 additions and 0 deletions
106
spec/ruby/core/thread/abort_on_exception_spec.rb
Normal file
106
spec/ruby/core/thread/abort_on_exception_spec.rb
Normal file
|
@ -0,0 +1,106 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#abort_on_exception" do
|
||||
before do
|
||||
ThreadSpecs.clear_state
|
||||
@thread = Thread.new { Thread.pass until ThreadSpecs.state == :exit }
|
||||
end
|
||||
|
||||
after do
|
||||
ThreadSpecs.state = :exit
|
||||
@thread.join
|
||||
end
|
||||
|
||||
it "is false by default" do
|
||||
@thread.abort_on_exception.should be_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
|
||||
end
|
||||
end
|
||||
|
||||
describe :thread_abort_on_exception, shared: true do
|
||||
before do
|
||||
@thread = Thread.new do
|
||||
Thread.pass until ThreadSpecs.state == :run
|
||||
raise RuntimeError, "Thread#abort_on_exception= specs"
|
||||
end
|
||||
end
|
||||
|
||||
it "causes the main thread to raise the exception raised in the thread" do
|
||||
begin
|
||||
ScratchPad << :before
|
||||
|
||||
@thread.abort_on_exception = true if @object
|
||||
lambda do
|
||||
ThreadSpecs.state = :run
|
||||
# Wait for the main thread to be interrupted
|
||||
sleep
|
||||
end.should raise_error(RuntimeError, "Thread#abort_on_exception= specs")
|
||||
|
||||
ScratchPad << :after
|
||||
rescue Exception => e
|
||||
ScratchPad << [:rescue, e]
|
||||
end
|
||||
|
||||
ScratchPad.recorded.should == [:before, :after]
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread#abort_on_exception=" do
|
||||
describe "when enabled and the thread dies due to an exception" do
|
||||
before do
|
||||
ScratchPad.record []
|
||||
ThreadSpecs.clear_state
|
||||
@stderr, $stderr = $stderr, IOStub.new
|
||||
end
|
||||
|
||||
after do
|
||||
$stderr = @stderr
|
||||
end
|
||||
|
||||
it_behaves_like :thread_abort_on_exception, nil, true
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread.abort_on_exception" do
|
||||
before do
|
||||
@abort_on_exception = Thread.abort_on_exception
|
||||
end
|
||||
|
||||
after do
|
||||
Thread.abort_on_exception = @abort_on_exception
|
||||
end
|
||||
|
||||
it "is false by default" do
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread.abort_on_exception=" do
|
||||
describe "when enabled and a non-main thread dies due to an exception" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
ThreadSpecs.clear_state
|
||||
@stderr, $stderr = $stderr, IOStub.new
|
||||
|
||||
@abort_on_exception = Thread.abort_on_exception
|
||||
Thread.abort_on_exception = true
|
||||
end
|
||||
|
||||
after :each do
|
||||
Thread.abort_on_exception = @abort_on_exception
|
||||
$stderr = @stderr
|
||||
end
|
||||
|
||||
it_behaves_like :thread_abort_on_exception, nil, false
|
||||
end
|
||||
end
|
5
spec/ruby/core/thread/add_trace_func_spec.rb
Normal file
5
spec/ruby/core/thread/add_trace_func_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Thread#add_trace_func" do
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
58
spec/ruby/core/thread/alive_spec.rb
Normal file
58
spec/ruby/core/thread/alive_spec.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#alive?" do
|
||||
it "can check it's own status" do
|
||||
ThreadSpecs.status_of_current_thread.alive?.should == true
|
||||
end
|
||||
|
||||
it "describes a running thread" do
|
||||
ThreadSpecs.status_of_running_thread.alive?.should == true
|
||||
end
|
||||
|
||||
it "describes a sleeping thread" do
|
||||
ThreadSpecs.status_of_sleeping_thread.alive?.should == true
|
||||
end
|
||||
|
||||
it "describes a blocked thread" do
|
||||
ThreadSpecs.status_of_blocked_thread.alive?.should == true
|
||||
end
|
||||
|
||||
it "describes a completed thread" do
|
||||
ThreadSpecs.status_of_completed_thread.alive?.should == false
|
||||
end
|
||||
|
||||
it "describes a killed thread" do
|
||||
ThreadSpecs.status_of_killed_thread.alive?.should == false
|
||||
end
|
||||
|
||||
it "describes a thread with an uncaught exception" do
|
||||
ThreadSpecs.status_of_thread_with_uncaught_exception.alive?.should == false
|
||||
end
|
||||
|
||||
it "describes a dying running thread" do
|
||||
ThreadSpecs.status_of_dying_running_thread.alive?.should == true
|
||||
end
|
||||
|
||||
it "describes a dying sleeping thread" do
|
||||
ThreadSpecs.status_of_dying_sleeping_thread.alive?.should == true
|
||||
end
|
||||
|
||||
it "returns true for a killed but still running thread" do
|
||||
exit = false
|
||||
t = Thread.new do
|
||||
begin
|
||||
sleep
|
||||
ensure
|
||||
Thread.pass until exit
|
||||
end
|
||||
end
|
||||
|
||||
ThreadSpecs.spin_until_sleeping(t)
|
||||
|
||||
t.kill
|
||||
t.alive?.should == true
|
||||
exit = true
|
||||
t.join
|
||||
end
|
||||
end
|
9
spec/ruby/core/thread/allocate_spec.rb
Normal file
9
spec/ruby/core/thread/allocate_spec.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Thread.allocate" do
|
||||
it "raises a TypeError" do
|
||||
lambda {
|
||||
Thread.allocate
|
||||
}.should raise_error(TypeError)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
require File.expand_path('../../../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe 'Thread::Backtrace::Location#absolute_path' do
|
||||
before :each do
|
||||
@frame = ThreadBacktraceLocationSpecs.locations[0]
|
||||
end
|
||||
|
||||
it 'returns the absolute path of the call frame' do
|
||||
@frame.absolute_path.should == File.realpath(__FILE__)
|
||||
end
|
||||
end
|
12
spec/ruby/core/thread/backtrace/location/base_label_spec.rb
Normal file
12
spec/ruby/core/thread/backtrace/location/base_label_spec.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
require File.expand_path('../../../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe 'Thread::Backtrace::Location#base_label' do
|
||||
before :each do
|
||||
@frame = ThreadBacktraceLocationSpecs.locations[0]
|
||||
end
|
||||
|
||||
it 'returns the base label of the call frame' do
|
||||
@frame.base_label.should == '<top (required)>'
|
||||
end
|
||||
end
|
17
spec/ruby/core/thread/backtrace/location/fixtures/classes.rb
Normal file
17
spec/ruby/core/thread/backtrace/location/fixtures/classes.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
module ThreadBacktraceLocationSpecs
|
||||
MODULE_LOCATION = caller_locations(0) rescue nil
|
||||
|
||||
def self.locations
|
||||
caller_locations
|
||||
end
|
||||
|
||||
def self.method_location
|
||||
caller_locations(0)
|
||||
end
|
||||
|
||||
def self.block_location
|
||||
1.times do
|
||||
return caller_locations(0)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
def example
|
||||
caller_locations[0].path
|
||||
end
|
||||
|
||||
print example
|
13
spec/ruby/core/thread/backtrace/location/inspect_spec.rb
Normal file
13
spec/ruby/core/thread/backtrace/location/inspect_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
require File.expand_path('../../../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe 'Thread::Backtrace::Location#inspect' do
|
||||
before :each do
|
||||
@frame = ThreadBacktraceLocationSpecs.locations[0]
|
||||
@line = __LINE__ - 1
|
||||
end
|
||||
|
||||
it 'converts the call frame to a String' do
|
||||
@frame.inspect.should include("#{__FILE__}:#{@line}:in ")
|
||||
end
|
||||
end
|
20
spec/ruby/core/thread/backtrace/location/label_spec.rb
Normal file
20
spec/ruby/core/thread/backtrace/location/label_spec.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
require File.expand_path('../../../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe 'Thread::Backtrace::Location#label' do
|
||||
it 'returns the base label of the call frame' do
|
||||
ThreadBacktraceLocationSpecs.locations[0].label.should include('<top (required)>')
|
||||
end
|
||||
|
||||
it 'returns the method name for a method location' do
|
||||
ThreadBacktraceLocationSpecs.method_location[0].label.should == "method_location"
|
||||
end
|
||||
|
||||
it 'returns the block name for a block location' do
|
||||
ThreadBacktraceLocationSpecs.block_location[0].label.should == "block in block_location"
|
||||
end
|
||||
|
||||
it 'returns the module name for a module location' do
|
||||
ThreadBacktraceLocationSpecs::MODULE_LOCATION[0].label.should include "ThreadBacktraceLocationSpecs"
|
||||
end
|
||||
end
|
13
spec/ruby/core/thread/backtrace/location/lineno_spec.rb
Normal file
13
spec/ruby/core/thread/backtrace/location/lineno_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
require File.expand_path('../../../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe 'Thread::Backtrace::Location#lineno' do
|
||||
before :each do
|
||||
@frame = ThreadBacktraceLocationSpecs.locations[0]
|
||||
@line = __LINE__ - 1
|
||||
end
|
||||
|
||||
it 'returns the absolute path of the call frame' do
|
||||
@frame.lineno.should == @line
|
||||
end
|
||||
end
|
91
spec/ruby/core/thread/backtrace/location/path_spec.rb
Normal file
91
spec/ruby/core/thread/backtrace/location/path_spec.rb
Normal file
|
@ -0,0 +1,91 @@
|
|||
require File.expand_path('../../../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe 'Thread::Backtrace::Location#path' do
|
||||
context 'outside a main script' do
|
||||
it 'returns an absolute path' do
|
||||
frame = ThreadBacktraceLocationSpecs.locations[0]
|
||||
|
||||
frame.path.should == __FILE__
|
||||
end
|
||||
end
|
||||
|
||||
context 'in a main script' do
|
||||
before do
|
||||
@script = fixture(__FILE__, 'main.rb')
|
||||
end
|
||||
|
||||
context 'when the script is in the working directory' do
|
||||
before do
|
||||
@directory = File.dirname(@script)
|
||||
end
|
||||
|
||||
context 'when using a relative script path' do
|
||||
it 'returns a path relative to the working directory' do
|
||||
Dir.chdir(@directory) {
|
||||
ruby_exe('main.rb')
|
||||
}.should == 'main.rb'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using an absolute script path' do
|
||||
it 'returns an absolute path' do
|
||||
Dir.chdir(@directory) {
|
||||
ruby_exe(@script)
|
||||
}.should == @script
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the script is in a sub directory of the working directory' do
|
||||
context 'when using a relative script path' do
|
||||
it 'returns a path relative to the working directory' do
|
||||
path = 'fixtures/main.rb'
|
||||
directory = File.dirname(__FILE__)
|
||||
Dir.chdir(directory) {
|
||||
ruby_exe(path)
|
||||
}.should == path
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using an absolute script path' do
|
||||
it 'returns an absolute path' do
|
||||
ruby_exe(@script).should == @script
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the script is outside of the working directory' do
|
||||
before do
|
||||
@parent_dir = tmp('path_outside_pwd')
|
||||
@sub_dir = File.join(@parent_dir, 'sub')
|
||||
@script = File.join(@parent_dir, 'main.rb')
|
||||
source = fixture(__FILE__, 'main.rb')
|
||||
|
||||
mkdir_p(@sub_dir)
|
||||
|
||||
cp(source, @script)
|
||||
end
|
||||
|
||||
after do
|
||||
rm_r(@script)
|
||||
rm_r(@sub_dir)
|
||||
rm_r(@parent_dir)
|
||||
end
|
||||
|
||||
context 'when using a relative script path' do
|
||||
it 'returns a path relative to the working directory' do
|
||||
Dir.chdir(@sub_dir) {
|
||||
ruby_exe('../main.rb')
|
||||
}.should == '../main.rb'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using an absolute path' do
|
||||
it 'returns an absolute path' do
|
||||
ruby_exe(@script).should == @script
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
13
spec/ruby/core/thread/backtrace/location/to_s_spec.rb
Normal file
13
spec/ruby/core/thread/backtrace/location/to_s_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
require File.expand_path('../../../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe 'Thread::Backtrace::Location#to_s' do
|
||||
before :each do
|
||||
@frame = ThreadBacktraceLocationSpecs.locations[0]
|
||||
@line = __LINE__ - 1
|
||||
end
|
||||
|
||||
it 'converts the call frame to a String' do
|
||||
@frame.to_s.should include("#{__FILE__}:#{@line}:in ")
|
||||
end
|
||||
end
|
27
spec/ruby/core/thread/backtrace_spec.rb
Normal file
27
spec/ruby/core/thread/backtrace_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Thread#backtrace" do
|
||||
it "returns the current backtrace of a thread" do
|
||||
t = Thread.new do
|
||||
begin
|
||||
sleep
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
Thread.pass while t.status && t.status != 'sleep'
|
||||
|
||||
backtrace = t.backtrace
|
||||
backtrace.should be_kind_of(Array)
|
||||
backtrace.first.should =~ /`sleep'/
|
||||
|
||||
t.raise 'finish the thread'
|
||||
t.join
|
||||
end
|
||||
|
||||
it "returns nil for dead thread" do
|
||||
t = Thread.new {}
|
||||
t.join
|
||||
t.backtrace.should == nil
|
||||
end
|
||||
end
|
15
spec/ruby/core/thread/current_spec.rb
Normal file
15
spec/ruby/core/thread/current_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread.current" do
|
||||
it "returns a thread" do
|
||||
current = Thread.current
|
||||
current.should be_kind_of(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)
|
||||
end
|
||||
end
|
44
spec/ruby/core/thread/element_reference_spec.rb
Normal file
44
spec/ruby/core/thread/element_reference_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#[]" do
|
||||
it "gives access to thread local values" do
|
||||
th = Thread.new do
|
||||
Thread.current[:value] = 5
|
||||
end
|
||||
th.join
|
||||
th[:value].should == 5
|
||||
Thread.current[:value].should == nil
|
||||
end
|
||||
|
||||
it "is not shared across threads" do
|
||||
t1 = Thread.new do
|
||||
Thread.current[:value] = 1
|
||||
end
|
||||
t2 = Thread.new do
|
||||
Thread.current[:value] = 2
|
||||
end
|
||||
[t1,t2].each {|x| x.join}
|
||||
t1[:value].should == 1
|
||||
t2[:value].should == 2
|
||||
end
|
||||
|
||||
it "is accessible using strings or symbols" do
|
||||
t1 = Thread.new do
|
||||
Thread.current[:value] = 1
|
||||
end
|
||||
t2 = Thread.new do
|
||||
Thread.current["value"] = 2
|
||||
end
|
||||
[t1,t2].each {|x| x.join}
|
||||
t1[:value].should == 1
|
||||
t1["value"].should == 1
|
||||
t2[:value].should == 2
|
||||
t2["value"].should == 2
|
||||
end
|
||||
|
||||
it "raises exceptions on the wrong type of keys" do
|
||||
lambda { Thread.current[nil] }.should raise_error(TypeError)
|
||||
lambda { Thread.current[5] }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
52
spec/ruby/core/thread/element_set_spec.rb
Normal file
52
spec/ruby/core/thread/element_set_spec.rb
Normal file
|
@ -0,0 +1,52 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#[]=" do
|
||||
after :each do
|
||||
Thread.current[:value] = nil
|
||||
end
|
||||
|
||||
it "raises a RuntimeError if the thread is frozen" do
|
||||
running = false
|
||||
t = Thread.new do
|
||||
Thread.pass until running
|
||||
t.freeze
|
||||
t[:foo] = "bar"
|
||||
end
|
||||
running = true
|
||||
lambda { t.join }.should raise_error(RuntimeError)
|
||||
end
|
||||
|
||||
it "raises exceptions on the wrong type of keys" do
|
||||
lambda { Thread.current[nil] = true }.should raise_error(TypeError)
|
||||
lambda { Thread.current[5] = true }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "is not shared across fibers" do
|
||||
fib = Fiber.new do
|
||||
Thread.current[:value] = 1
|
||||
Fiber.yield
|
||||
Thread.current[:value].should == 1
|
||||
end
|
||||
fib.resume
|
||||
Thread.current[:value].should be_nil
|
||||
Thread.current[:value] = 2
|
||||
fib.resume
|
||||
Thread.current[:value] = 2
|
||||
end
|
||||
|
||||
it "stores a local in another thread when in a fiber" do
|
||||
fib = Fiber.new do
|
||||
t = Thread.new do
|
||||
sleep
|
||||
Thread.current[:value].should == 1
|
||||
end
|
||||
|
||||
Thread.pass while t.status and t.status != "sleep"
|
||||
t[:value] = 1
|
||||
t.wakeup
|
||||
t.join
|
||||
end
|
||||
fib.resume
|
||||
end
|
||||
end
|
18
spec/ruby/core/thread/exclusive_spec.rb
Normal file
18
spec/ruby/core/thread/exclusive_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Thread.exclusive" do
|
||||
before :each do
|
||||
ScratchPad.clear
|
||||
end
|
||||
|
||||
it "yields to the block" do
|
||||
Thread.exclusive { ScratchPad.record true }
|
||||
ScratchPad.recorded.should == true
|
||||
end
|
||||
|
||||
it "returns the result of yielding" do
|
||||
Thread.exclusive { :result }.should == :result
|
||||
end
|
||||
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
15
spec/ruby/core/thread/exit_spec.rb
Normal file
15
spec/ruby/core/thread/exit_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
require File.expand_path('../shared/exit', __FILE__)
|
||||
|
||||
describe "Thread#exit!" do
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
end
|
298
spec/ruby/core/thread/fixtures/classes.rb
Normal file
298
spec/ruby/core/thread/fixtures/classes.rb
Normal file
|
@ -0,0 +1,298 @@
|
|||
unless defined? Channel
|
||||
require 'thread'
|
||||
class Channel < Queue
|
||||
alias receive shift
|
||||
end
|
||||
end
|
||||
|
||||
module ThreadSpecs
|
||||
|
||||
class SubThread < Thread
|
||||
def initialize(*args)
|
||||
super { args.first << 1 }
|
||||
end
|
||||
end
|
||||
|
||||
class Status
|
||||
attr_reader :thread, :inspect, :status
|
||||
def initialize(thread)
|
||||
@thread = thread
|
||||
@alive = thread.alive?
|
||||
@inspect = thread.inspect
|
||||
@status = thread.status
|
||||
@stop = thread.stop?
|
||||
end
|
||||
|
||||
def alive?
|
||||
@alive
|
||||
end
|
||||
|
||||
def stop?
|
||||
@stop
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: In the great Thread spec rewrite, abstract this
|
||||
class << self
|
||||
attr_accessor :state
|
||||
end
|
||||
|
||||
def self.clear_state
|
||||
@state = nil
|
||||
end
|
||||
|
||||
def self.spin_until_sleeping(t)
|
||||
Thread.pass while t.status and t.status != "sleep"
|
||||
end
|
||||
|
||||
def self.sleeping_thread
|
||||
Thread.new do
|
||||
begin
|
||||
sleep
|
||||
ScratchPad.record :woken
|
||||
rescue Object => e
|
||||
ScratchPad.record e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.running_thread
|
||||
Thread.new do
|
||||
begin
|
||||
ThreadSpecs.state = :running
|
||||
loop { Thread.pass }
|
||||
ScratchPad.record :woken
|
||||
rescue Object => e
|
||||
ScratchPad.record e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.completed_thread
|
||||
Thread.new {}
|
||||
end
|
||||
|
||||
def self.status_of_current_thread
|
||||
Thread.new { Status.new(Thread.current) }.value
|
||||
end
|
||||
|
||||
def self.status_of_running_thread
|
||||
t = running_thread
|
||||
Thread.pass while t.status and t.status != "run"
|
||||
status = Status.new t
|
||||
t.kill
|
||||
t.join
|
||||
status
|
||||
end
|
||||
|
||||
def self.status_of_completed_thread
|
||||
t = completed_thread
|
||||
t.join
|
||||
Status.new t
|
||||
end
|
||||
|
||||
def self.status_of_sleeping_thread
|
||||
t = sleeping_thread
|
||||
Thread.pass while t.status and t.status != 'sleep'
|
||||
status = Status.new t
|
||||
t.run
|
||||
t.join
|
||||
status
|
||||
end
|
||||
|
||||
def self.status_of_blocked_thread
|
||||
m = Mutex.new
|
||||
m.lock
|
||||
t = Thread.new { m.lock }
|
||||
Thread.pass while t.status and t.status != 'sleep'
|
||||
status = Status.new t
|
||||
m.unlock
|
||||
t.join
|
||||
status
|
||||
end
|
||||
|
||||
def self.status_of_killed_thread
|
||||
t = Thread.new { sleep }
|
||||
Thread.pass while t.status and t.status != 'sleep'
|
||||
t.kill
|
||||
t.join
|
||||
Status.new t
|
||||
end
|
||||
|
||||
def self.status_of_thread_with_uncaught_exception
|
||||
t = Thread.new { raise "error" }
|
||||
begin
|
||||
t.join
|
||||
rescue RuntimeError
|
||||
end
|
||||
Status.new t
|
||||
end
|
||||
|
||||
def self.status_of_dying_running_thread
|
||||
status = nil
|
||||
t = dying_thread_ensures { status = Status.new Thread.current }
|
||||
t.join
|
||||
status
|
||||
end
|
||||
|
||||
def self.status_of_dying_sleeping_thread
|
||||
t = dying_thread_ensures { Thread.stop; }
|
||||
Thread.pass while t.status and t.status != 'sleep'
|
||||
status = Status.new t
|
||||
t.wakeup
|
||||
t.join
|
||||
status
|
||||
end
|
||||
|
||||
def self.status_of_dying_thread_after_sleep
|
||||
status = nil
|
||||
t = dying_thread_ensures {
|
||||
Thread.stop
|
||||
status = Status.new(Thread.current)
|
||||
}
|
||||
Thread.pass while t.status and t.status != 'sleep'
|
||||
t.wakeup
|
||||
Thread.pass while t.status and t.status == 'sleep'
|
||||
t.join
|
||||
status
|
||||
end
|
||||
|
||||
def self.dying_thread_ensures(kill_method_name=:kill)
|
||||
Thread.new do
|
||||
begin
|
||||
Thread.current.send(kill_method_name)
|
||||
ensure
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.dying_thread_with_outer_ensure(kill_method_name=:kill)
|
||||
Thread.new do
|
||||
begin
|
||||
begin
|
||||
Thread.current.send(kill_method_name)
|
||||
ensure
|
||||
raise "In dying thread"
|
||||
end
|
||||
ensure
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.join_dying_thread_with_outer_ensure(kill_method_name=:kill)
|
||||
t = dying_thread_with_outer_ensure(kill_method_name) { yield }
|
||||
lambda { t.join }.should raise_error(RuntimeError, "In dying thread")
|
||||
return t
|
||||
end
|
||||
|
||||
def self.wakeup_dying_sleeping_thread(kill_method_name=:kill)
|
||||
t = ThreadSpecs.dying_thread_ensures(kill_method_name) { yield }
|
||||
Thread.pass while t.status and t.status != 'sleep'
|
||||
t.wakeup
|
||||
t.join
|
||||
end
|
||||
|
||||
def self.critical_is_reset
|
||||
# Create another thread to verify that it can call Thread.critical=
|
||||
t = Thread.new do
|
||||
initial_critical = Thread.critical
|
||||
Thread.critical = true
|
||||
Thread.critical = false
|
||||
initial_critical == false && Thread.critical == false
|
||||
end
|
||||
v = t.value
|
||||
t.join
|
||||
v
|
||||
end
|
||||
|
||||
def self.counter
|
||||
@@counter
|
||||
end
|
||||
|
||||
def self.counter= c
|
||||
@@counter = c
|
||||
end
|
||||
|
||||
def self.increment_counter(incr)
|
||||
incr.times do
|
||||
begin
|
||||
Thread.critical = true
|
||||
@@counter += 1
|
||||
ensure
|
||||
Thread.critical = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.critical_thread1
|
||||
Thread.critical = true
|
||||
Thread.current.key?(:thread_specs).should == false
|
||||
end
|
||||
|
||||
def self.critical_thread2(is_thread_stop)
|
||||
Thread.current[:thread_specs].should == 101
|
||||
Thread.critical.should == !is_thread_stop
|
||||
unless is_thread_stop
|
||||
Thread.critical = false
|
||||
end
|
||||
end
|
||||
|
||||
def self.main_thread1(critical_thread, is_thread_sleep, is_thread_stop)
|
||||
# Thread.stop resets Thread.critical. Also, with native threads, the Thread.Stop may not have executed yet
|
||||
# since the main thread will race with the critical thread
|
||||
unless is_thread_stop
|
||||
Thread.critical.should == true
|
||||
end
|
||||
critical_thread[:thread_specs] = 101
|
||||
if is_thread_sleep or is_thread_stop
|
||||
# Thread#wakeup calls are not queued up. So we need to ensure that the thread is sleeping before calling wakeup
|
||||
Thread.pass while critical_thread.status and critical_thread.status != "sleep"
|
||||
critical_thread.wakeup
|
||||
end
|
||||
end
|
||||
|
||||
def self.main_thread2(critical_thread)
|
||||
Thread.pass # The join below seems to cause a deadlock with CRuby unless Thread.pass is called first
|
||||
critical_thread.join
|
||||
Thread.critical.should == false
|
||||
end
|
||||
|
||||
def self.critical_thread_yields_to_main_thread(is_thread_sleep=false, is_thread_stop=false)
|
||||
@@after_first_sleep = false
|
||||
|
||||
critical_thread = Thread.new do
|
||||
Thread.pass while Thread.main.status and Thread.main.status != "sleep"
|
||||
critical_thread1()
|
||||
Thread.main.wakeup
|
||||
yield
|
||||
Thread.pass while @@after_first_sleep != true # Need to ensure that the next statement does not see the first sleep itself
|
||||
Thread.pass while Thread.main.status and Thread.main.status != "sleep"
|
||||
critical_thread2(is_thread_stop)
|
||||
Thread.main.wakeup
|
||||
end
|
||||
|
||||
sleep 5
|
||||
@@after_first_sleep = true
|
||||
main_thread1(critical_thread, is_thread_sleep, is_thread_stop)
|
||||
sleep 5
|
||||
main_thread2(critical_thread)
|
||||
end
|
||||
|
||||
def self.create_critical_thread
|
||||
Thread.new do
|
||||
Thread.critical = true
|
||||
yield
|
||||
Thread.critical = false
|
||||
end
|
||||
end
|
||||
|
||||
def self.create_and_kill_critical_thread(pass_after_kill=false)
|
||||
ThreadSpecs.create_critical_thread do
|
||||
Thread.current.kill
|
||||
Thread.pass if pass_after_kill
|
||||
ScratchPad.record("status=" + Thread.current.status)
|
||||
end
|
||||
end
|
||||
end
|
9
spec/ruby/core/thread/fork_spec.rb
Normal file
9
spec/ruby/core/thread/fork_spec.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
require File.expand_path('../shared/start', __FILE__)
|
||||
|
||||
describe "Thread.fork" do
|
||||
describe "Thread.start" do
|
||||
it_behaves_like :thread_start, :fork
|
||||
end
|
||||
end
|
5
spec/ruby/core/thread/group_spec.rb
Normal file
5
spec/ruby/core/thread/group_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
describe "Thread#group" do
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
27
spec/ruby/core/thread/initialize_spec.rb
Normal file
27
spec/ruby/core/thread/initialize_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#initialize" do
|
||||
|
||||
describe "already initialized" do
|
||||
|
||||
before do
|
||||
@t = Thread.new { sleep }
|
||||
end
|
||||
|
||||
after do
|
||||
@t.kill
|
||||
@t.join
|
||||
end
|
||||
|
||||
it "raises a ThreadError" do
|
||||
lambda {
|
||||
@t.instance_eval do
|
||||
initialize {}
|
||||
end
|
||||
}.should raise_error(ThreadError)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
44
spec/ruby/core/thread/inspect_spec.rb
Normal file
44
spec/ruby/core/thread/inspect_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#inspect" do
|
||||
it "can check it's own status" do
|
||||
ThreadSpecs.status_of_current_thread.inspect.should include('run')
|
||||
end
|
||||
|
||||
it "describes a running thread" do
|
||||
ThreadSpecs.status_of_running_thread.inspect.should include('run')
|
||||
end
|
||||
|
||||
it "describes a sleeping thread" do
|
||||
ThreadSpecs.status_of_sleeping_thread.inspect.should include('sleep')
|
||||
end
|
||||
|
||||
it "describes a blocked thread" do
|
||||
ThreadSpecs.status_of_blocked_thread.inspect.should include('sleep')
|
||||
end
|
||||
|
||||
it "describes a completed thread" do
|
||||
ThreadSpecs.status_of_completed_thread.inspect.should include('dead')
|
||||
end
|
||||
|
||||
it "describes a killed thread" do
|
||||
ThreadSpecs.status_of_killed_thread.inspect.should include('dead')
|
||||
end
|
||||
|
||||
it "describes a thread with an uncaught exception" do
|
||||
ThreadSpecs.status_of_thread_with_uncaught_exception.inspect.should include('dead')
|
||||
end
|
||||
|
||||
it "describes a dying sleeping thread" do
|
||||
ThreadSpecs.status_of_dying_sleeping_thread.inspect.should include('sleep')
|
||||
end
|
||||
|
||||
it "reports aborting on a killed thread" do
|
||||
ThreadSpecs.status_of_dying_running_thread.inspect.should include('aborting')
|
||||
end
|
||||
|
||||
it "reports aborting on a killed thread after sleep" do
|
||||
ThreadSpecs.status_of_dying_thread_after_sleep.inspect.should include('aborting')
|
||||
end
|
||||
end
|
62
spec/ruby/core/thread/join_spec.rb
Normal file
62
spec/ruby/core/thread/join_spec.rb
Normal file
|
@ -0,0 +1,62 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#join" do
|
||||
it "returns the thread when it is finished" do
|
||||
t = Thread.new {}
|
||||
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)
|
||||
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)
|
||||
lambda { t.join(:foo) }.should raise_error TypeError
|
||||
lambda { t.join("bar") }.should raise_error TypeError
|
||||
end
|
||||
|
||||
it "returns nil if it is not finished when given a timeout" do
|
||||
c = Channel.new
|
||||
t = Thread.new { c.receive }
|
||||
begin
|
||||
t.join(0).should == nil
|
||||
ensure
|
||||
c << true
|
||||
end
|
||||
t.join.should == t
|
||||
end
|
||||
|
||||
it "accepts a floating point timeout length" do
|
||||
c = Channel.new
|
||||
t = Thread.new { c.receive }
|
||||
begin
|
||||
t.join(0.01).should == nil
|
||||
ensure
|
||||
c << true
|
||||
end
|
||||
t.join.should == t
|
||||
end
|
||||
|
||||
it "raises any exceptions encountered in the thread body" do
|
||||
t = Thread.new { raise NotImplementedError.new("Just kidding") }
|
||||
lambda { t.join }.should raise_error(NotImplementedError)
|
||||
end
|
||||
|
||||
it "returns the dead thread" do
|
||||
t = Thread.new { Thread.current.kill }
|
||||
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") }
|
||||
lambda { t.join }.should raise_error(NotImplementedError)
|
||||
end
|
||||
end
|
53
spec/ruby/core/thread/key_spec.rb
Normal file
53
spec/ruby/core/thread/key_spec.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#key?" do
|
||||
before :each do
|
||||
@th = Thread.new do
|
||||
Thread.current[:oliver] = "a"
|
||||
end
|
||||
@th.join
|
||||
end
|
||||
|
||||
it "tests for existance of thread local variables using symbols or strings" do
|
||||
@th.key?(:oliver).should == true
|
||||
@th.key?("oliver").should == true
|
||||
@th.key?(:stanley).should == false
|
||||
@th.key?(:stanley.to_s).should == false
|
||||
end
|
||||
|
||||
it "raises exceptions on the wrong type of keys" do
|
||||
lambda { Thread.current.key? nil }.should raise_error(TypeError)
|
||||
lambda { Thread.current.key? 5 }.should raise_error(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
|
||||
end
|
||||
Thread.current.key?(:val1).should_not be_true
|
||||
fib.resume
|
||||
Thread.current[:val2] = 2
|
||||
fib.resume
|
||||
Thread.current.key?(:val1).should be_false
|
||||
Thread.current.key?(:val2).should be_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
|
||||
end
|
||||
|
||||
Thread.pass while t.status and t.status != "sleep"
|
||||
t[:value] = 1
|
||||
t.wakeup
|
||||
t.join
|
||||
end
|
||||
fib.resume
|
||||
end
|
||||
end
|
44
spec/ruby/core/thread/keys_spec.rb
Normal file
44
spec/ruby/core/thread/keys_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#keys" do
|
||||
it "returns an array of the names of the thread-local variables as symbols" do
|
||||
th = Thread.new do
|
||||
Thread.current["cat"] = 'woof'
|
||||
Thread.current[:cat] = 'meow'
|
||||
Thread.current[:dog] = 'woof'
|
||||
end
|
||||
th.join
|
||||
th.keys.sort_by {|x| x.to_s}.should == [:cat,:dog]
|
||||
end
|
||||
|
||||
it "is not shared across fibers" do
|
||||
fib = Fiber.new do
|
||||
Thread.current[:val1] = 1
|
||||
Fiber.yield
|
||||
Thread.current.keys.should include(:val1)
|
||||
Thread.current.keys.should_not include(:val2)
|
||||
end
|
||||
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)
|
||||
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)
|
||||
end
|
||||
|
||||
Thread.pass while t.status and t.status != "sleep"
|
||||
t[:value] = 1
|
||||
t.wakeup
|
||||
t.join
|
||||
end
|
||||
fib.resume
|
||||
end
|
||||
end
|
21
spec/ruby/core/thread/kill_spec.rb
Normal file
21
spec/ruby/core/thread/kill_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
require File.expand_path('../shared/exit', __FILE__)
|
||||
|
||||
describe "Thread#kill" do
|
||||
it_behaves_like :thread_exit, :kill
|
||||
end
|
||||
|
||||
describe "Thread#kill!" do
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
||||
|
||||
describe "Thread.kill" do
|
||||
it "causes the given thread to exit" do
|
||||
thread = Thread.new { sleep }
|
||||
Thread.pass while thread.status and thread.status != "sleep"
|
||||
Thread.kill(thread).should == thread
|
||||
thread.join
|
||||
thread.status.should be_false
|
||||
end
|
||||
end
|
42
spec/ruby/core/thread/list_spec.rb
Normal file
42
spec/ruby/core/thread/list_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread.list" do
|
||||
it "includes the current and main thread" do
|
||||
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)
|
||||
ensure
|
||||
t.kill
|
||||
t.join
|
||||
end
|
||||
end
|
||||
|
||||
it "does not include deceased threads" do
|
||||
t = Thread.new { 1; }
|
||||
t.join
|
||||
Thread.list.should_not include(t)
|
||||
end
|
||||
|
||||
it "includes waiting threads" do
|
||||
c = Channel.new
|
||||
t = Thread.new { c.receive }
|
||||
begin
|
||||
Thread.pass while t.status and t.status != 'sleep'
|
||||
Thread.list.should include(t)
|
||||
ensure
|
||||
c << nil
|
||||
t.join
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread.list" do
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
10
spec/ruby/core/thread/main_spec.rb
Normal file
10
spec/ruby/core/thread/main_spec.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread.main" do
|
||||
it "returns the main thread" do
|
||||
Thread.new { @main = Thread.main ; @current = Thread.current}.join
|
||||
@main.should_not == @current
|
||||
@main.should == Thread.current
|
||||
end
|
||||
end
|
56
spec/ruby/core/thread/name_spec.rb
Normal file
56
spec/ruby/core/thread/name_spec.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
ruby_version_is '2.3' do
|
||||
describe "Thread#name" do
|
||||
before :each do
|
||||
@thread = Thread.new {}
|
||||
end
|
||||
|
||||
after :each do
|
||||
@thread.join
|
||||
end
|
||||
|
||||
it "is nil initially" do
|
||||
@thread.name.should == nil
|
||||
end
|
||||
|
||||
it "returns the thread name" do
|
||||
@thread.name = "thread_name"
|
||||
@thread.name.should == "thread_name"
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread#name=" do
|
||||
before :each do
|
||||
@thread = Thread.new {}
|
||||
end
|
||||
|
||||
after :each do
|
||||
@thread.join
|
||||
end
|
||||
|
||||
it "can be set to a String" do
|
||||
@thread.name = "new thread name"
|
||||
@thread.name.should == "new thread name"
|
||||
end
|
||||
|
||||
it "raises an ArgumentError if the name includes a null byte" do
|
||||
lambda {
|
||||
@thread.name = "new thread\0name"
|
||||
}.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "can be reset to nil" do
|
||||
@thread.name = nil
|
||||
@thread.name.should == nil
|
||||
end
|
||||
|
||||
it "calls #to_str to convert name to String" do
|
||||
name = mock("Thread#name")
|
||||
name.should_receive(:to_str).and_return("a thread name")
|
||||
|
||||
@thread.name = name
|
||||
@thread.name.should == "a thread name"
|
||||
end
|
||||
end
|
||||
end
|
56
spec/ruby/core/thread/new_spec.rb
Normal file
56
spec/ruby/core/thread/new_spec.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread.new" do
|
||||
it "creates a thread executing the given block" do
|
||||
c = Channel.new
|
||||
Thread.new { c << true }.join
|
||||
c << false
|
||||
c.receive.should == true
|
||||
end
|
||||
|
||||
it "can pass arguments to the thread block" do
|
||||
arr = []
|
||||
a, b, c = 1, 2, 3
|
||||
t = Thread.new(a,b,c) {|d,e,f| arr << d << e << f }
|
||||
t.join
|
||||
arr.should == [a,b,c]
|
||||
end
|
||||
|
||||
it "raises an exception when not given a block" do
|
||||
lambda { Thread.new }.should raise_error(ThreadError)
|
||||
end
|
||||
|
||||
it "creates a subclass of thread calls super with a block in initialize" do
|
||||
arr = []
|
||||
t = ThreadSpecs::SubThread.new(arr)
|
||||
t.join
|
||||
arr.should == [1]
|
||||
end
|
||||
|
||||
it "calls #initialize and raises an error if super not used" do
|
||||
c = Class.new(Thread) do
|
||||
def initialize
|
||||
end
|
||||
end
|
||||
|
||||
lambda {
|
||||
c.new
|
||||
}.should raise_error(ThreadError)
|
||||
end
|
||||
|
||||
it "calls and respects #initialize for the block to use" do
|
||||
c = Class.new(Thread) do
|
||||
def initialize
|
||||
ScratchPad.record [:good]
|
||||
super { ScratchPad << :in_thread }
|
||||
end
|
||||
end
|
||||
|
||||
t = c.new
|
||||
t.join
|
||||
|
||||
ScratchPad.recorded.should == [:good, :in_thread]
|
||||
end
|
||||
|
||||
end
|
8
spec/ruby/core/thread/pass_spec.rb
Normal file
8
spec/ruby/core/thread/pass_spec.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread.pass" do
|
||||
it "returns nil" do
|
||||
Thread.pass.should == nil
|
||||
end
|
||||
end
|
68
spec/ruby/core/thread/priority_spec.rb
Normal file
68
spec/ruby/core/thread/priority_spec.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#priority" do
|
||||
before do
|
||||
@current_priority = Thread.current.priority
|
||||
ThreadSpecs.clear_state
|
||||
@thread = Thread.new { Thread.pass until ThreadSpecs.state == :exit }
|
||||
end
|
||||
|
||||
after do
|
||||
ThreadSpecs.state = :exit
|
||||
@thread.join
|
||||
end
|
||||
|
||||
it "inherits the priority of the current thread while running" do
|
||||
@thread.alive?.should be_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.priority.should == @current_priority
|
||||
end
|
||||
|
||||
it "returns an integer" do
|
||||
@thread.priority.should be_kind_of(Integer)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread#priority=" do
|
||||
before do
|
||||
ThreadSpecs.clear_state
|
||||
@thread = Thread.new {}
|
||||
end
|
||||
|
||||
after do
|
||||
@thread.join
|
||||
end
|
||||
|
||||
describe "when set with an integer" do
|
||||
it "returns an integer" do
|
||||
value = (@thread.priority = 3)
|
||||
value.should == 3
|
||||
end
|
||||
|
||||
it "clamps the priority to -3..3" do
|
||||
@thread.priority = 42
|
||||
@thread.priority.should == 3
|
||||
@thread.priority = -42
|
||||
@thread.priority.should == -3
|
||||
end
|
||||
end
|
||||
|
||||
describe "when set with a non-integer" do
|
||||
it "raises a type error" do
|
||||
lambda{ @thread.priority = Object.new }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
it "sets priority even when the thread has died" do
|
||||
@thread.join
|
||||
@thread.priority = 3
|
||||
@thread.priority.should == 3
|
||||
end
|
||||
end
|
175
spec/ruby/core/thread/raise_spec.rb
Normal file
175
spec/ruby/core/thread/raise_spec.rb
Normal file
|
@ -0,0 +1,175 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
require File.expand_path('../../../shared/kernel/raise', __FILE__)
|
||||
|
||||
describe "Thread#raise" do
|
||||
it "ignores dead threads" do
|
||||
t = Thread.new { :dead }
|
||||
Thread.pass while t.alive?
|
||||
lambda {t.raise("Kill the thread")}.should_not raise_error
|
||||
lambda {t.value}.should_not raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread#raise on a sleeping thread" do
|
||||
before :each do
|
||||
ScratchPad.clear
|
||||
@thr = ThreadSpecs.sleeping_thread
|
||||
Thread.pass while @thr.status and @thr.status != "sleep"
|
||||
end
|
||||
|
||||
after :each do
|
||||
@thr.kill
|
||||
@thr.join
|
||||
end
|
||||
|
||||
it "raises a RuntimeError if no exception class is given" do
|
||||
@thr.raise
|
||||
Thread.pass while @thr.status
|
||||
ScratchPad.recorded.should be_kind_of(RuntimeError)
|
||||
end
|
||||
|
||||
it "raises the given exception" do
|
||||
@thr.raise Exception
|
||||
Thread.pass while @thr.status
|
||||
ScratchPad.recorded.should be_kind_of(Exception)
|
||||
end
|
||||
|
||||
it "raises the given exception with the given message" do
|
||||
@thr.raise Exception, "get to work"
|
||||
Thread.pass while @thr.status
|
||||
ScratchPad.recorded.should be_kind_of(Exception)
|
||||
ScratchPad.recorded.message.should == "get to work"
|
||||
end
|
||||
|
||||
it "raises the given exception and the backtrace is the one of the interrupted thread" do
|
||||
@thr.raise Exception
|
||||
Thread.pass while @thr.status
|
||||
ScratchPad.recorded.should be_kind_of(Exception)
|
||||
ScratchPad.recorded.backtrace[0].should include("sleep")
|
||||
end
|
||||
|
||||
it "is captured and raised by Thread#value" do
|
||||
t = Thread.new do
|
||||
sleep
|
||||
end
|
||||
|
||||
ThreadSpecs.spin_until_sleeping(t)
|
||||
|
||||
t.raise
|
||||
lambda { t.value }.should raise_error(RuntimeError)
|
||||
end
|
||||
|
||||
it "raises a RuntimeError when called with no arguments inside rescue" do
|
||||
t = Thread.new do
|
||||
begin
|
||||
1/0
|
||||
rescue ZeroDivisionError
|
||||
sleep
|
||||
end
|
||||
end
|
||||
begin
|
||||
raise RangeError
|
||||
rescue
|
||||
ThreadSpecs.spin_until_sleeping(t)
|
||||
t.raise
|
||||
end
|
||||
lambda {t.value}.should raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread#raise on a running thread" do
|
||||
before :each do
|
||||
ScratchPad.clear
|
||||
ThreadSpecs.clear_state
|
||||
|
||||
@thr = ThreadSpecs.running_thread
|
||||
Thread.pass until ThreadSpecs.state == :running
|
||||
end
|
||||
|
||||
after :each do
|
||||
@thr.kill
|
||||
@thr.join
|
||||
end
|
||||
|
||||
it "raises a RuntimeError if no exception class is given" do
|
||||
@thr.raise
|
||||
Thread.pass while @thr.status
|
||||
ScratchPad.recorded.should be_kind_of(RuntimeError)
|
||||
end
|
||||
|
||||
it "raises the given exception" do
|
||||
@thr.raise Exception
|
||||
Thread.pass while @thr.status
|
||||
ScratchPad.recorded.should be_kind_of(Exception)
|
||||
end
|
||||
|
||||
it "raises the given exception with the given message" do
|
||||
@thr.raise Exception, "get to work"
|
||||
Thread.pass while @thr.status
|
||||
ScratchPad.recorded.should be_kind_of(Exception)
|
||||
ScratchPad.recorded.message.should == "get to work"
|
||||
end
|
||||
|
||||
it "can go unhandled" do
|
||||
t = Thread.new do
|
||||
loop { Thread.pass }
|
||||
end
|
||||
|
||||
t.raise
|
||||
lambda {t.value}.should raise_error(RuntimeError)
|
||||
end
|
||||
|
||||
it "raises the given argument even when there is an active exception" do
|
||||
raised = false
|
||||
t = Thread.new do
|
||||
begin
|
||||
1/0
|
||||
rescue ZeroDivisionError
|
||||
raised = true
|
||||
loop { Thread.pass }
|
||||
end
|
||||
end
|
||||
begin
|
||||
raise "Create an active exception for the current thread too"
|
||||
rescue
|
||||
Thread.pass until raised
|
||||
t.raise RangeError
|
||||
lambda {t.value}.should raise_error(RangeError)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises a RuntimeError when called with no arguments inside rescue" do
|
||||
raised = false
|
||||
t = Thread.new do
|
||||
begin
|
||||
1/0
|
||||
rescue ZeroDivisionError
|
||||
raised = true
|
||||
loop { }
|
||||
end
|
||||
end
|
||||
begin
|
||||
raise RangeError
|
||||
rescue
|
||||
Thread.pass until raised
|
||||
t.raise
|
||||
end
|
||||
lambda {t.value}.should raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread#raise on same thread" do
|
||||
it_behaves_like :kernel_raise, :raise, Thread.current
|
||||
|
||||
it "raises a RuntimeError when called with no arguments inside rescue" do
|
||||
t = Thread.new do
|
||||
begin
|
||||
1/0
|
||||
rescue ZeroDivisionError
|
||||
Thread.current.raise
|
||||
end
|
||||
end
|
||||
lambda {t.value}.should raise_error(RuntimeError)
|
||||
end
|
||||
end
|
9
spec/ruby/core/thread/run_spec.rb
Normal file
9
spec/ruby/core/thread/run_spec.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
require File.expand_path('../shared/wakeup', __FILE__)
|
||||
|
||||
describe "Thread#run" do
|
||||
it_behaves_like :thread_wakeup, :run
|
||||
end
|
||||
|
5
spec/ruby/core/thread/set_trace_func_spec.rb
Normal file
5
spec/ruby/core/thread/set_trace_func_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Thread#set_trace_func" do
|
||||
it "needs to be reviewed for spec completeness"
|
||||
end
|
176
spec/ruby/core/thread/shared/exit.rb
Normal file
176
spec/ruby/core/thread/shared/exit.rb
Normal file
|
@ -0,0 +1,176 @@
|
|||
describe :thread_exit, shared: true do
|
||||
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.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 "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
|
||||
|
||||
with_feature :fiber do
|
||||
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
|
||||
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 "propogates inner exception to Thread.join if there is an outer ensure clause" do
|
||||
thread = ThreadSpecs.dying_thread_with_outer_ensure(@method) { }
|
||||
lambda { 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
|
41
spec/ruby/core/thread/shared/start.rb
Normal file
41
spec/ruby/core/thread/shared/start.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
describe :thread_start, shared: true do
|
||||
before :each do
|
||||
ScratchPad.clear
|
||||
end
|
||||
|
||||
it "raises an ArgumentError if not passed a block" do
|
||||
lambda {
|
||||
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
|
61
spec/ruby/core/thread/shared/wakeup.rb
Normal file
61
spec/ruby/core/thread/shared/wakeup.rb
Normal file
|
@ -0,0 +1,61 @@
|
|||
describe :thread_wakeup, shared: true do
|
||||
it "can interrupt Kernel#sleep" do
|
||||
exit_loop = false
|
||||
after_sleep1 = false
|
||||
after_sleep2 = false
|
||||
|
||||
t = Thread.new do
|
||||
while true
|
||||
break if exit_loop == true
|
||||
Thread.pass
|
||||
end
|
||||
|
||||
sleep
|
||||
after_sleep1 = true
|
||||
|
||||
sleep
|
||||
after_sleep2 = true
|
||||
end
|
||||
|
||||
10.times { t.send(@method); Thread.pass }
|
||||
t.status.should_not == "sleep"
|
||||
|
||||
exit_loop = true
|
||||
|
||||
10.times { sleep 0.1 if t.status and t.status != "sleep" }
|
||||
after_sleep1.should == false # t should be blocked on the first sleep
|
||||
t.send(@method)
|
||||
|
||||
10.times { sleep 0.1 if after_sleep1 != true }
|
||||
10.times { sleep 0.1 if t.status and t.status != "sleep" }
|
||||
after_sleep2.should == false # t should be blocked on the second sleep
|
||||
t.send(@method)
|
||||
|
||||
t.join
|
||||
end
|
||||
|
||||
it "does not result in a deadlock" do
|
||||
t = Thread.new do
|
||||
100.times { Thread.stop }
|
||||
end
|
||||
|
||||
while t.status
|
||||
begin
|
||||
t.send(@method)
|
||||
rescue ThreadError
|
||||
# The thread might die right after.
|
||||
t.status.should == false
|
||||
end
|
||||
Thread.pass
|
||||
end
|
||||
|
||||
t.status.should == false
|
||||
t.join
|
||||
end
|
||||
|
||||
it "raises a ThreadError when trying to wake up a dead thread" do
|
||||
t = Thread.new { 1 }
|
||||
t.join
|
||||
lambda { t.send @method }.should raise_error(ThreadError)
|
||||
end
|
||||
end
|
9
spec/ruby/core/thread/start_spec.rb
Normal file
9
spec/ruby/core/thread/start_spec.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
require File.expand_path('../shared/start', __FILE__)
|
||||
|
||||
describe "Thread.start" do
|
||||
describe "Thread.start" do
|
||||
it_behaves_like :thread_start, :start
|
||||
end
|
||||
end
|
60
spec/ruby/core/thread/status_spec.rb
Normal file
60
spec/ruby/core/thread/status_spec.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#status" do
|
||||
it "can check it's own status" do
|
||||
ThreadSpecs.status_of_current_thread.status.should == 'run'
|
||||
end
|
||||
|
||||
it "describes a running thread" do
|
||||
ThreadSpecs.status_of_running_thread.status.should == 'run'
|
||||
end
|
||||
|
||||
it "describes a sleeping thread" do
|
||||
ThreadSpecs.status_of_sleeping_thread.status.should == 'sleep'
|
||||
end
|
||||
|
||||
it "describes a blocked thread" do
|
||||
ThreadSpecs.status_of_blocked_thread.status.should == 'sleep'
|
||||
end
|
||||
|
||||
it "describes a completed thread" do
|
||||
ThreadSpecs.status_of_completed_thread.status.should == false
|
||||
end
|
||||
|
||||
it "describes a killed thread" do
|
||||
ThreadSpecs.status_of_killed_thread.status.should == false
|
||||
end
|
||||
|
||||
it "describes a thread with an uncaught exception" do
|
||||
ThreadSpecs.status_of_thread_with_uncaught_exception.status.should == nil
|
||||
end
|
||||
|
||||
it "describes a dying sleeping thread" do
|
||||
ThreadSpecs.status_of_dying_sleeping_thread.status.should == 'sleep'
|
||||
end
|
||||
|
||||
it "reports aborting on a killed thread" do
|
||||
ThreadSpecs.status_of_dying_running_thread.status.should == 'aborting'
|
||||
end
|
||||
|
||||
it "reports aborting on a killed thread after sleep" do
|
||||
ThreadSpecs.status_of_dying_thread_after_sleep.status.should == 'aborting'
|
||||
end
|
||||
|
||||
it "reports aborting on an externally killed thread that sleeps" do
|
||||
q = Queue.new
|
||||
t = Thread.new do
|
||||
begin
|
||||
q.push nil
|
||||
sleep
|
||||
ensure
|
||||
q.push Thread.current.status
|
||||
end
|
||||
end
|
||||
q.pop
|
||||
t.kill
|
||||
t.join
|
||||
q.pop.should == 'aborting'
|
||||
end
|
||||
end
|
54
spec/ruby/core/thread/stop_spec.rb
Normal file
54
spec/ruby/core/thread/stop_spec.rb
Normal file
|
@ -0,0 +1,54 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread.stop" do
|
||||
it "causes the current thread to sleep indefinitely" do
|
||||
t = Thread.new { Thread.stop; 5 }
|
||||
Thread.pass while t.status and t.status != 'sleep'
|
||||
t.status.should == 'sleep'
|
||||
t.run
|
||||
t.value.should == 5
|
||||
end
|
||||
end
|
||||
|
||||
describe "Thread#stop?" do
|
||||
it "can check it's own status" do
|
||||
ThreadSpecs.status_of_current_thread.stop?.should == false
|
||||
end
|
||||
|
||||
it "describes a running thread" do
|
||||
ThreadSpecs.status_of_running_thread.stop?.should == false
|
||||
end
|
||||
|
||||
it "describes a sleeping thread" do
|
||||
ThreadSpecs.status_of_sleeping_thread.stop?.should == true
|
||||
end
|
||||
|
||||
it "describes a blocked thread" do
|
||||
ThreadSpecs.status_of_blocked_thread.stop?.should == true
|
||||
end
|
||||
|
||||
it "describes a completed thread" do
|
||||
ThreadSpecs.status_of_completed_thread.stop?.should == true
|
||||
end
|
||||
|
||||
it "describes a killed thread" do
|
||||
ThreadSpecs.status_of_killed_thread.stop?.should == true
|
||||
end
|
||||
|
||||
it "describes a thread with an uncaught exception" do
|
||||
ThreadSpecs.status_of_thread_with_uncaught_exception.stop?.should == true
|
||||
end
|
||||
|
||||
it "describes a dying running thread" do
|
||||
ThreadSpecs.status_of_dying_running_thread.stop?.should == false
|
||||
end
|
||||
|
||||
it "describes a dying sleeping thread" do
|
||||
ThreadSpecs.status_of_dying_sleeping_thread.stop?.should == true
|
||||
end
|
||||
|
||||
it "describes a dying thread after sleep" do
|
||||
ThreadSpecs.status_of_dying_thread_after_sleep.stop?.should == false
|
||||
end
|
||||
end
|
7
spec/ruby/core/thread/terminate_spec.rb
Normal file
7
spec/ruby/core/thread/terminate_spec.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
require File.expand_path('../shared/exit', __FILE__)
|
||||
|
||||
describe "Thread#terminate" do
|
||||
it_behaves_like :thread_exit, :terminate
|
||||
end
|
25
spec/ruby/core/thread/thread_variable_get_spec.rb
Normal file
25
spec/ruby/core/thread/thread_variable_get_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Thread#thread_variable_get" do
|
||||
before :each do
|
||||
@t = Thread.new { }
|
||||
end
|
||||
|
||||
after :each do
|
||||
@t.join
|
||||
end
|
||||
|
||||
it "returns nil if the variable is not set" do
|
||||
@t.thread_variable_get(:a).should be_nil
|
||||
end
|
||||
|
||||
it "returns the value previously set by #[]=" do
|
||||
@t.thread_variable_set :a, 49
|
||||
@t.thread_variable_get(:a).should == 49
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
end
|
26
spec/ruby/core/thread/thread_variable_set_spec.rb
Normal file
26
spec/ruby/core/thread/thread_variable_set_spec.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Thread#thread_variable_set" do
|
||||
before :each do
|
||||
@t = Thread.new { }
|
||||
end
|
||||
|
||||
after :each do
|
||||
@t.join
|
||||
end
|
||||
|
||||
it "returns the value set" do
|
||||
(@t.thread_variable_set :a, 2).should == 2
|
||||
end
|
||||
|
||||
it "sets a value that will be returned by #thread_variable_get" do
|
||||
@t.thread_variable_set :a, 49
|
||||
@t.thread_variable_get(:a).should == 49
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
end
|
21
spec/ruby/core/thread/thread_variable_spec.rb
Normal file
21
spec/ruby/core/thread/thread_variable_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Thread#thread_variable?" do
|
||||
before :each do
|
||||
@t = Thread.new { }
|
||||
end
|
||||
|
||||
after :each do
|
||||
@t.join
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
it "returns true if the thread variables contain 'key'" do
|
||||
@t.thread_variable_set :a, 2
|
||||
@t.thread_variable?(:a).should be_true
|
||||
end
|
||||
end
|
24
spec/ruby/core/thread/thread_variables_spec.rb
Normal file
24
spec/ruby/core/thread/thread_variables_spec.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Thread#thread_variables" do
|
||||
before :each do
|
||||
@t = Thread.new { }
|
||||
end
|
||||
|
||||
after :each do
|
||||
@t.join
|
||||
end
|
||||
|
||||
it "returns the keys of all the values set" do
|
||||
@t.thread_variable_set :a, 2
|
||||
@t.thread_variable_set :b, 4
|
||||
@t.thread_variable_set :c, 6
|
||||
@t.thread_variables.sort.should == [:a, :b, :c]
|
||||
end
|
||||
|
||||
it "sets a value private to self" do
|
||||
@t.thread_variable_set :thread_variables_spec_a, 82
|
||||
@t.thread_variable_set :thread_variables_spec_b, 82
|
||||
Thread.current.thread_variables.should == []
|
||||
end
|
||||
end
|
18
spec/ruby/core/thread/value_spec.rb
Normal file
18
spec/ruby/core/thread/value_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
|
||||
describe "Thread#value" do
|
||||
it "returns the result of the block" do
|
||||
Thread.new { 3 }.value.should == 3
|
||||
end
|
||||
|
||||
it "re-raises an error for an uncaught exception" do
|
||||
t = Thread.new { raise "Hello" }
|
||||
lambda { t.value }.should raise_error(RuntimeError, "Hello")
|
||||
end
|
||||
|
||||
it "is nil for a killed thread" do
|
||||
t = Thread.new { Thread.current.exit }
|
||||
t.value.should == nil
|
||||
end
|
||||
end
|
7
spec/ruby/core/thread/wakeup_spec.rb
Normal file
7
spec/ruby/core/thread/wakeup_spec.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/classes', __FILE__)
|
||||
require File.expand_path('../shared/wakeup', __FILE__)
|
||||
|
||||
describe "Thread#wakeup" do
|
||||
it_behaves_like :thread_wakeup, :wakeup
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue