mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* test/ruby/test_settracefunc.rb: add a test for set_trace_func.
* test/ruby/envutil.rb: move "rubyexec" method from test_rubyoptions.rb. * test/ruby/test_rubyoptions.rb: use rubyexec in envutil.rb. * test/ruby/test_thread.rb: add tests to achieve over 90% test coverage of thread.c. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@16175 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
c0d8e6cbf9
commit
f7ef694a71
6 changed files with 417 additions and 50 deletions
|
@ -1,7 +1,12 @@
|
|||
require 'test/unit'
|
||||
require 'thread'
|
||||
require_relative 'envutil'
|
||||
|
||||
class TestThread < Test::Unit::TestCase
|
||||
def ruby(*r, &b)
|
||||
EnvUtil.rubyexec(*r, &b)
|
||||
end
|
||||
|
||||
class Thread < ::Thread
|
||||
def self.new(*)
|
||||
th = super
|
||||
|
@ -97,6 +102,335 @@ class TestThread < Test::Unit::TestCase
|
|||
assert_equal("exit.", result[/.*\Z/], '[ruby-dev:30653]')
|
||||
}
|
||||
end
|
||||
|
||||
def test_priority
|
||||
c1 = c2 = 0
|
||||
t1 = Thread.new { loop { c1 += 1 } }
|
||||
t1.priority = -1
|
||||
t2 = Thread.new { loop { c2 += 1 } }
|
||||
t2.priority = -3
|
||||
assert(-1, t1.priority)
|
||||
assert(-3, t2.priority)
|
||||
sleep 0.5
|
||||
t1.kill
|
||||
t2.kill
|
||||
#assert(c1 > c2 * 2, "[ruby-dev:33124]")
|
||||
end
|
||||
|
||||
def test_new
|
||||
assert_raise(ThreadError) do
|
||||
Thread.new
|
||||
end
|
||||
|
||||
t1 = Thread.new { sleep }
|
||||
assert_raise(ThreadError) do
|
||||
t1.instance_eval { initialize { } }
|
||||
end
|
||||
|
||||
t2 = Thread.new(&method(:sleep).to_proc)
|
||||
assert_raise(ThreadError) do
|
||||
t2.instance_eval { initialize { } }
|
||||
end
|
||||
|
||||
ensure
|
||||
t1.kill if t1
|
||||
t2.kill if t2
|
||||
end
|
||||
|
||||
def test_join
|
||||
t = Thread.new { sleep }
|
||||
assert_nil(t.join(0.5))
|
||||
|
||||
ensure
|
||||
t.kill if t
|
||||
end
|
||||
|
||||
def test_join2
|
||||
t1 = Thread.new { sleep(1.5) }
|
||||
t2 = Thread.new do
|
||||
t1.join(1)
|
||||
end
|
||||
t3 = Thread.new do
|
||||
sleep 0.5
|
||||
t1.join
|
||||
end
|
||||
assert_nil(t2.value)
|
||||
assert_equal(t1, t3.value)
|
||||
|
||||
ensure
|
||||
t1.kill if t1
|
||||
t2.kill if t2
|
||||
t3.kill if t3
|
||||
end
|
||||
|
||||
def test_kill_main_thread
|
||||
ruby do |w, r, e|
|
||||
w.puts "p 1"
|
||||
w.puts "Thread.kill Thread.current"
|
||||
w.puts "p 2"
|
||||
w.close
|
||||
assert_equal("1", r.read.chomp)
|
||||
end
|
||||
end
|
||||
|
||||
def test_exit
|
||||
s = 0
|
||||
Thread.new do
|
||||
s += 1
|
||||
Thread.exit
|
||||
s += 2
|
||||
end.join
|
||||
assert_equal(1, s)
|
||||
end
|
||||
|
||||
def test_wakeup
|
||||
s = 0
|
||||
t = Thread.new do
|
||||
s += 1
|
||||
Thread.stop
|
||||
s += 1
|
||||
end
|
||||
sleep 0.5
|
||||
assert_equal(1, s)
|
||||
t.wakeup
|
||||
sleep 0.5
|
||||
assert_equal(2, s)
|
||||
assert_raise(ThreadError) { t.wakeup }
|
||||
|
||||
ensure
|
||||
t.kill if t
|
||||
end
|
||||
|
||||
def test_stop
|
||||
ruby do |w, r, e|
|
||||
w.puts "begin"
|
||||
w.puts " Thread.stop"
|
||||
w.puts " p 1"
|
||||
w.puts "rescue ThreadError"
|
||||
w.puts " p 2"
|
||||
w.puts "end"
|
||||
w.close
|
||||
assert_equal("2", r.read.chomp)
|
||||
end
|
||||
end
|
||||
|
||||
def test_list
|
||||
ruby do |w, r, e|
|
||||
w.puts "t1 = Thread.new { sleep }"
|
||||
w.puts "t2 = Thread.new { loop { } }"
|
||||
w.puts "t3 = Thread.new { }.join"
|
||||
w.puts "p [Thread.current, t1, t2].sort_by {|t| t.object_id }"
|
||||
w.puts "p Thread.list.sort_by {|t| t.object_id }"
|
||||
w.close
|
||||
assert_equal(r.gets, r.gets)
|
||||
end
|
||||
end
|
||||
|
||||
def test_main
|
||||
ruby do |w, r, e|
|
||||
w.puts "p Thread.main == Thread.current"
|
||||
w.puts "Thread.new { p Thread.main == Thread.current }.join"
|
||||
w.close
|
||||
assert_equal("true", r.gets.chomp)
|
||||
assert_equal("false", r.gets.chomp)
|
||||
end
|
||||
end
|
||||
|
||||
def test_abort_on_exception
|
||||
ruby do |w, r, e|
|
||||
w.puts "p Thread.abort_on_exception"
|
||||
w.puts "begin"
|
||||
w.puts " Thread.new { raise }"
|
||||
w.puts " sleep 0.5"
|
||||
w.puts " p 1"
|
||||
w.puts "rescue"
|
||||
w.puts " p 2"
|
||||
w.puts "end"
|
||||
w.close_write
|
||||
assert_equal("false", r.gets.chomp)
|
||||
assert_equal("1", r.gets.chomp)
|
||||
assert_equal("", e.read)
|
||||
end
|
||||
|
||||
ruby do |w, r, e|
|
||||
w.puts "Thread.abort_on_exception = true"
|
||||
w.puts "p Thread.abort_on_exception"
|
||||
w.puts "begin"
|
||||
w.puts " Thread.new { raise }"
|
||||
w.puts " sleep 0.5"
|
||||
w.puts " p 1"
|
||||
w.puts "rescue"
|
||||
w.puts " p 2"
|
||||
w.puts "end"
|
||||
w.close_write
|
||||
assert_equal("true", r.gets.chomp)
|
||||
assert_equal("2", r.gets.chomp)
|
||||
assert_equal("", e.read)
|
||||
end
|
||||
|
||||
ruby('-d') do |w, r, e|
|
||||
w.puts "p Thread.abort_on_exception"
|
||||
w.puts "begin"
|
||||
w.puts " Thread.new { raise }"
|
||||
w.puts " sleep 0.5"
|
||||
w.puts " p 1"
|
||||
w.puts "rescue"
|
||||
w.puts " p 2"
|
||||
w.puts "end"
|
||||
w.close_write
|
||||
assert_equal("false", r.gets.chomp)
|
||||
assert_equal("2", r.gets.chomp)
|
||||
assert_not_equal("", e.read)
|
||||
end
|
||||
|
||||
ruby do |w, r, e|
|
||||
w.puts "p Thread.abort_on_exception"
|
||||
w.puts "begin"
|
||||
w.puts " t = Thread.new { sleep 0.5; raise }"
|
||||
w.puts " t.abort_on_exception = true"
|
||||
w.puts " p t.abort_on_exception"
|
||||
w.puts " sleep 1"
|
||||
w.puts " p 1"
|
||||
w.puts "rescue"
|
||||
w.puts " p 2"
|
||||
w.puts "end"
|
||||
w.close_write
|
||||
assert_equal("false", r.gets.chomp)
|
||||
assert_equal("true", r.gets.chomp)
|
||||
assert_equal("2", r.gets.chomp)
|
||||
assert_equal("", e.read)
|
||||
end
|
||||
end
|
||||
|
||||
def test_status_and_stop_p
|
||||
a = ::Thread.new { raise("die now") }
|
||||
b = Thread.new { Thread.stop }
|
||||
c = Thread.new { Thread.exit }
|
||||
d = Thread.new { sleep }
|
||||
e = Thread.current
|
||||
sleep 0.5
|
||||
d.kill
|
||||
|
||||
assert_equal(nil, a.status)
|
||||
assert_equal("sleep", b.status)
|
||||
assert_equal(false, c.status)
|
||||
assert_match(/^#<TestThread::Thread:.* dead>$/, c.inspect)
|
||||
assert_equal("aborting", d.status)
|
||||
assert_equal("run", e.status)
|
||||
|
||||
assert(a.stop?)
|
||||
assert(b.stop?)
|
||||
assert(c.stop?)
|
||||
assert(!d.stop?)
|
||||
assert(!e.stop?)
|
||||
|
||||
ensure
|
||||
a.kill if a
|
||||
b.kill if b
|
||||
c.kill if c
|
||||
d.kill if d
|
||||
end
|
||||
|
||||
def test_safe_level
|
||||
t = Thread.new { $SAFE = 3; sleep }
|
||||
sleep 0.5
|
||||
assert_equal(0, Thread.current.safe_level)
|
||||
assert_equal(3, t.safe_level)
|
||||
|
||||
ensure
|
||||
t.kill if t
|
||||
end
|
||||
|
||||
def test_thread_local
|
||||
t = Thread.new { sleep }
|
||||
|
||||
assert_equal(false, t.key?(:foo))
|
||||
|
||||
t["foo"] = "foo"
|
||||
t["bar"] = "bar"
|
||||
t["baz"] = "baz"
|
||||
|
||||
assert_equal(true, t.key?(:foo))
|
||||
assert_equal(true, t.key?("foo"))
|
||||
assert_equal(false, t.key?(:qux))
|
||||
assert_equal(false, t.key?("qux"))
|
||||
|
||||
assert_equal([:foo, :bar, :baz], t.keys)
|
||||
|
||||
ensure
|
||||
t.kill if t
|
||||
end
|
||||
|
||||
def test_thread_local_security
|
||||
t = Thread.new { sleep }
|
||||
|
||||
assert_raise(SecurityError) do
|
||||
Thread.new { $SAFE = 4; t[:foo] }.join
|
||||
end
|
||||
|
||||
assert_raise(SecurityError) do
|
||||
Thread.new { $SAFE = 4; t[:foo] = :baz }.join
|
||||
end
|
||||
|
||||
assert_raise(RuntimeError) do
|
||||
Thread.new do
|
||||
Thread.current[:foo] = :bar
|
||||
Thread.current.freeze
|
||||
Thread.current[:foo] = :baz
|
||||
end.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_select_wait
|
||||
assert_nil(IO.select(nil, nil, nil, 1))
|
||||
t = Thread.new do
|
||||
assert_nil(IO.select(nil, nil, nil, nil))
|
||||
end
|
||||
sleep 0.5
|
||||
t.kill
|
||||
end
|
||||
|
||||
def test_mutex_deadlock
|
||||
m = Mutex.new
|
||||
m.synchronize do
|
||||
assert_raise(ThreadError) do
|
||||
m.synchronize do
|
||||
assert(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_mutex_interrupt
|
||||
m = Mutex.new
|
||||
m.lock
|
||||
t = Thread.new do
|
||||
m.lock
|
||||
:foo
|
||||
end
|
||||
sleep 0.5
|
||||
t.kill
|
||||
assert_nil(t.value)
|
||||
end
|
||||
|
||||
def test_mutex_illegal_unlock
|
||||
m = Mutex.new
|
||||
m.lock
|
||||
assert_raise(ThreadError) do
|
||||
Thread.new do
|
||||
m.unlock
|
||||
end.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_recursive_error
|
||||
o = Object.new
|
||||
def o.inspect
|
||||
Thread.current[:__recursive_key__][:inspect] = nil
|
||||
super
|
||||
end
|
||||
assert_raise(TypeError) { [o].inspect }
|
||||
end
|
||||
end
|
||||
|
||||
class TestThreadGroup < Test::Unit::TestCase
|
||||
|
@ -110,25 +444,43 @@ class TestThreadGroup < Test::Unit::TestCase
|
|||
|
||||
def test_frozen_thgroup
|
||||
thgrp = ThreadGroup.new
|
||||
|
||||
t = Thread.new{1}
|
||||
Thread.new{
|
||||
thgrp.add(Thread.current)
|
||||
thgrp.freeze
|
||||
assert_raise(ThreadError) do
|
||||
Thread.new{1}.join
|
||||
end
|
||||
assert_raise(ThreadError) do
|
||||
thgrp.add(t)
|
||||
end
|
||||
assert_raise(ThreadError) do
|
||||
ThreadGroup.new.add Thread.current
|
||||
end
|
||||
}.join
|
||||
t.join
|
||||
end
|
||||
|
||||
def test_enclosed_thgroup
|
||||
thgrp = ThreadGroup.new
|
||||
thgrp.enclose
|
||||
assert_equal(false, thgrp.enclosed?)
|
||||
|
||||
t = Thread.new{1}
|
||||
Thread.new{
|
||||
assert_raise(ThreadError) do
|
||||
thgrp.add(Thread.current)
|
||||
end
|
||||
thgrp.add(Thread.current)
|
||||
thgrp.enclose
|
||||
assert_equal(true, thgrp.enclosed?)
|
||||
assert_nothing_raised do
|
||||
Thread.new{1}.join
|
||||
end
|
||||
assert_raise(ThreadError) do
|
||||
thgrp.add t
|
||||
end
|
||||
assert_raise(ThreadError) do
|
||||
ThreadGroup.new.add Thread.current
|
||||
end
|
||||
}.join
|
||||
t.join
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue