require 'test/unit'
require 'osx/cocoa'
require 'thread'
require 'rbconfig'
system 'make' || raise(RuntimeError, "'make' failed")
require 'objc_test.bundle'
class Barrier
def initialize
@signalled = nil
@mutex = Mutex.new
@cond = ConditionVariable.new
end
def signal(key)
@mutex.synchronize do
@signalled = key
@cond.broadcast
end
end
def wait(key)
@mutex.synchronize do
while @signalled != key
@cond.wait(@mutex)
end
end
end
end
class TestThreadNativeMethod < OSX::NSObject
def initWithTC(tc)
init
@tc = tc
return self
end
def threaded
@tc.assert_equal(@tc.mainThread, OSX::NSThread.currentThread)
if defined? OSX::CFRunLoopGetMain then
@tc.assert_equal(OSX::CFRunLoopGetCurrent(), OSX::CFRunLoopGetMain())
end
42
end
objc_method :threaded, ['id']
end
class TC_Thread < Test::Unit::TestCase
attr_reader :mainThread
def setup
@mainThread = OSX::NSThread.currentThread
@helper = OSX::RBThreadTest.alloc.init
@ruby_path = File.join(Config::CONFIG["bindir"], Config::CONFIG["RUBY_INSTALL_NAME"])
end
def threaded
assert_equal(@mainThread, OSX::NSThread.currentThread)
if defined? OSX::CFRunLoopGetMain then
assert_equal(OSX::CFRunLoopGetCurrent(), OSX::CFRunLoopGetMain())
end
42
end
def test_threaded_callback
OSX::TestThreadedCallback.callbackOnThreadRubyObject(self)
end
def test_threaded_closure
o = TestThreadNativeMethod.alloc.initWithTC(self)
OSX::TestThreadedCallback.callbackOnThreadRubyObject(o)
end
def assert_threads_supported
assert OSX::RBRuntime.isRubyThreadingSupported?, "no runtime support for Ruby threads" unless ENV['RUBYCOCOA_THREAD_HOOK_DISABLE']
end
def test_autorelease
assert_threads_supported
barrier = Barrier.new
t1 = Thread.new do
barrier.wait 0
@helper.callWithAutoreleasePool proc {
OSX::NSString.stringWithString "x"
barrier.signal 10
barrier.wait 20
OSX::NSString.stringWithString "y"
}
barrier.signal 30
end
t2 = Thread.new do
barrier.wait 10
@helper.callWithAutoreleasePool proc {
OSX::NSString.stringWithString "z"
barrier.signal 20
barrier.wait 30
}
end
barrier.signal 0
t1.join
t2.join
end
def test_exceptions
assert_threads_supported
barrier = Barrier.new
t1 = Thread.new do
barrier.wait 0
exception = @helper.callWithExceptionTryCatch proc {
barrier.signal 10
barrier.wait 20
OSX::NSException.exceptionWithName_reason_userInfo_("name1","reason1",nil).raise
}
assert_equal "name1", exception.name.to_s
barrier.signal 30
end
t2 = Thread.new do
barrier.wait 10
$RUBYCOCOA_SUPPRESS_EXCEPTION_LOGGING = true
exception = @helper.callWithExceptionTryCatch proc {
barrier.signal 20
barrier.wait 30
OSX::NSException.exceptionWithName_reason_userInfo_("name2","reason2",nil).raise
}
$RUBYCOCOA_SUPPRESS_EXCEPTION_LOGGING = false
assert_equal "name2", exception.name.to_s
end
barrier.signal 0
t1.join
t2.join
end
def __test_stress
assert_threads_supported
assert_nothing_raised do
10.times do
t = []
10.times do
t << Thread.new do
10.times do
('a'..'z').to_a.each { |c| OSX::NSString.stringWithString(c) }
end
end
end
t.each { |th| th.join; th.terminate }
t = nil
GC.start end
end
end
def test_existing_threads_before_rubycocoa
assert_threads_supported
code = <<EOS
t = Thread.new { sleep 0.1 }
require 'osx/foundation'
t.join
p 1
EOS
assert_equal('1', __spawn_line(code.gsub(/\n/, ';')))
end
def __spawn_line(line)
res = `DYLD_FRAMEWORK_PATH=../framework/build/Default #{@ruby_path} -I../lib -I../ext/rubycocoa -e \"#{line}\"`
raise "Can't spawn Ruby line: '#{line}'" unless $?.success?
return res.strip
end
end