Files
RubyMotion/test/TestUnit/test/ruby/test_thread.rb
2012-09-22 21:57:31 +09:00

664 lines
13 KiB
Ruby

require 'test/unit'
require 'thread'
require_relative 'envutil'
class TestThread < Test::Unit::TestCase
class Thread < ::Thread
Threads = []
def self.new(*)
th = super
th.abort_on_exception = true
Threads << th
th
end
end
def setup
Thread::Threads.clear
end
def teardown
Thread::Threads.each do |t|
t.kill if t.alive?
begin
t.join
rescue Exception
end
end
end
def test_mutex_synchronize
m = Mutex.new
r = 0
max = 10
(1..max).map{
Thread.new{
i=0
while i<max*max
i+=1
m.synchronize{
r += 1
}
end
}
}.each{|e|
e.join
}
assert_equal(max * max * max, r)
end
def test_condvar
mutex = Mutex.new
condvar = ConditionVariable.new
result = []
mutex.synchronize do
t = Thread.new do
mutex.synchronize do
result << 1
condvar.signal
end
end
result << 0
condvar.wait(mutex)
result << 2
t.join
end
assert_equal([0, 1, 2], result)
end
def test_condvar_wait_not_owner
mutex = Mutex.new
condvar = ConditionVariable.new
assert_raise(ThreadError) { condvar.wait(mutex) }
end
def test_condvar_wait_exception_handling
# Calling wait in the only thread running should raise a ThreadError of
# 'stopping only thread'
mutex = Mutex.new
condvar = ConditionVariable.new
locked = false
thread = Thread.new do
Thread.current.abort_on_exception = false
mutex.synchronize do
begin
condvar.wait(mutex)
rescue Exception
locked = mutex.locked?
raise
end
end
end
until thread.stop?
sleep(0.1)
end
thread.raise Interrupt, "interrupt a dead condition variable"
assert_raise(Interrupt) { thread.value }
assert(locked)
end
def test_condvar_wait_and_broadcast
nr_threads = 3
threads = Array.new
mutex = Mutex.new
condvar = ConditionVariable.new
result = []
nr_threads.times do |i|
threads[i] = Thread.new do
mutex.synchronize do
result << "C1"
condvar.wait mutex
result << "C2"
end
end
end
sleep 0.1
mutex.synchronize do
result << "P1"
condvar.broadcast
result << "P2"
end
nr_threads.times do |i|
threads[i].join
end
assert_equal ["C1", "C1", "C1", "P1", "P2", "C2", "C2", "C2"], result
end
# Hmm.. don't we have a way of catch fatal exception?
#
# def test_cv_wait_deadlock
# mutex = Mutex.new
# cv = ConditionVariable.new
#
# assert_raises(fatal) {
# mutex.lock
# cv.wait mutex
# mutex.unlock
# }
# end
def test_condvar_wait_deadlock_2
nr_threads = 3
threads = Array.new
mutex = Mutex.new
condvar = ConditionVariable.new
nr_threads.times do |i|
if (i != 0)
mutex.unlock
end
threads[i] = Thread.new do
mutex.synchronize do
condvar.wait mutex
end
end
mutex.lock
end
assert_raise(Timeout::Error) do
Timeout.timeout(0.1) { condvar.wait mutex }
end
mutex.unlock rescue
threads[i].each.join
end
def test_condvar_timed_wait
mutex = Mutex.new
condvar = ConditionVariable.new
timeout = 0.3
locked = false
t0 = Time.now
mutex.synchronize do
begin
condvar.wait(mutex, timeout)
ensure
locked = mutex.locked?
end
end
t1 = Time.now
t = t1-t0
assert_block { timeout*0.9 < t && t < timeout*1.1 }
assert(locked)
end
def test_condvar_nolock
mutex = Mutex.new
condvar = ConditionVariable.new
assert_raise(ThreadError) { condvar.wait(mutex) }
end
def test_condvar_nolock_2
mutex = Mutex.new
condvar = ConditionVariable.new
Thread.new do
assert_raise(ThreadError) {condvar.wait(mutex)}
end.join
end
def test_condvar_nolock_3
mutex = Mutex.new
condvar = ConditionVariable.new
Thread.new do
assert_raise(ThreadError) {condvar.wait(mutex, 0.1)}
end.join
end
# def test_local_barrier
# dir = File.dirname(__FILE__)
# lbtest = File.join(dir, "lbtest.rb")
# $:.unshift File.join(File.dirname(dir), 'ruby')
# require 'envutil'
# $:.shift
# 3.times {
# result = `#{EnvUtil.rubybin} #{lbtest}`
# assert(!$?.coredump?, '[ruby-dev:30653]')
# assert_equal("exit.", result[/.*\Z/], '[ruby-dev:30653]')
# }
# end
# def test_priority
# skip("[BUG : #1283] Segfault occurs when gets the busy loop with some threads.")
# c1 = c2 = 0
# t1 = Thread.new { loop { c1 += 1 } }
# t1.priority = -1
# t2 = Thread.new { loop { c2 += 1 } }
# t2.priority = -3
# assert_equal(-1, t1.priority)
# assert_equal(-3, t2.priority)
# sleep 0.5
# 5.times do
# break if c1 > c2
# sleep 0.1
# end
# t1.kill
# t2.kill
# # assert_operator(c1, :>, c2, "[ruby-dev:33124]") # not guaranteed
# 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
# assert_in_out_err([], <<-INPUT, %w(1), [])
# p 1
# Thread.kill Thread.current
# p 2
# INPUT
# end
def test_kill_wrong_argument
bug4367 = '[ruby-core:35086]'
assert_raise(TypeError, bug4367) {
Thread.kill(nil)
}
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
# assert_in_out_err([], <<-INPUT, %w(2), [])
# begin
# Thread.stop
# p 1
# rescue ThreadError
# p 2
# end
# INPUT
# end
# def test_list
# assert_in_out_err([], <<-INPUT) do |r, e|
# t1 = Thread.new { sleep }
# Thread.pass
# t2 = Thread.new { loop { } }
# t3 = Thread.new { }.join
# p [Thread.current, t1, t2].map{|t| t.object_id }.sort
# p Thread.list.map{|t| t.object_id }.sort
# INPUT
# assert_equal(r.first, r.last)
# assert_equal([], e)
# end
# end
# def test_main
# assert_in_out_err([], <<-INPUT, %w(true false), [])
# p Thread.main == Thread.current
# Thread.new { p Thread.main == Thread.current }.join
# INPUT
# end
# def test_abort_on_exception
# assert_in_out_err([], <<-INPUT, %w(false 1), [])
# p Thread.abort_on_exception
# begin
# Thread.new { raise }
# sleep 0.5
# p 1
# rescue
# p 2
# end
# INPUT
# assert_in_out_err([], <<-INPUT, %w(true 2), [])
# Thread.abort_on_exception = true
# p Thread.abort_on_exception
# begin
# Thread.new { raise }
# sleep 0.5
# p 1
# rescue
# p 2
# end
# INPUT
# assert_in_out_err(%w(-d), <<-INPUT, %w(false 2), /.+/)
# p Thread.abort_on_exception
# begin
# Thread.new { raise }
# sleep 0.5
# p 1
# rescue
# p 2
# end
# INPUT
# assert_in_out_err([], <<-INPUT, %w(false true 2), [])
# p Thread.abort_on_exception
# begin
# t = Thread.new { sleep 0.5; raise }
# t.abort_on_exception = true
# p t.abort_on_exception
# sleep 1
# p 1
# rescue
# p 2
# end
# INPUT
# 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
# assert_equal(nil, a.status)
# assert(a.stop?)
assert_equal("sleep", b.status)
assert(b.stop?)
assert_equal(false, c.status)
assert_match(/^#<TestThread::Thread:.* dead>$/, c.inspect)
assert(c.stop?)
d.kill
assert_equal(["aborting", false], [d.status, d.stop?])
assert_equal(["run", false], [e.status, 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
# skip("[BUG : #1233] Abort occurs when kill a thread waiting with IO.select")
# assert_nil(IO.select(nil, nil, nil, 1))
# t = Thread.new do
# 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_mutex_fifo_like_lock
m1 = Mutex.new
m2 = Mutex.new
m1.lock
m2.lock
m1.unlock
m2.unlock
assert_equal(false, m1.locked?)
assert_equal(false, m2.locked?)
m3 = Mutex.new
m1.lock
m2.lock
m3.lock
m1.unlock
m2.unlock
m3.unlock
assert_equal(false, m1.locked?)
assert_equal(false, m2.locked?)
assert_equal(false, m3.locked?)
end
def test_mutex_trylock
m = Mutex.new
assert_equal(true, m.try_lock)
assert_equal(false, m.try_lock, '[ruby-core:20943]')
Thread.new{
assert_equal(false, m.try_lock)
}.join
m.unlock
end
# def test_recursive_outer
# arr = []
# obj = Struct.new(:foo, :visited).new(arr, false)
# arr << obj
# def obj.hash
# self[:visited] = true
# super
# raise "recursive_outer should short circuit intermediate calls"
# end
# assert_nothing_raised {arr.hash}
# assert(obj[:visited])
# end
end
class TestThreadGroup < Test::Unit::TestCase
def test_thread_init
thgrp = ThreadGroup.new
Thread.new{
thgrp.add(Thread.current)
assert_equal(thgrp, Thread.new{sleep 1}.group)
}.join
end
# def test_frozen_thgroup
# skip("[BUG : #1085] Assertion failed: ((b->flags & flags) == flags)")
# 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
# skip("[BUG : #1085] Assertion failed: ((b->flags & flags) == flags)")
# thgrp = ThreadGroup.new
# assert_equal(false, thgrp.enclosed?)
# t = Thread.new{1}
# Thread.new{
# 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
# def test_uninitialized
# c = Class.new(Thread)
# c.class_eval { def initialize; end }
# assert_raise(ThreadError) { c.new.start }
# end
# def test_backtrace
# Thread.new{
# assert_equal(Array, Thread.main.backtrace.class)
# }.join
# t = Thread.new{}
# t.join
# assert_equal(nil, t.backtrace)
# end
end