1
0
Fork 0
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:
eregon 2017-09-20 20:18:52 +00:00
parent 75bfc6440d
commit 1d15d5f080
4370 changed files with 0 additions and 0 deletions

View 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

View 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

View 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

View 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

View file

@ -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

View 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

View 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

View file

@ -0,0 +1,5 @@
def example
caller_locations[0].path
end
print example

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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