From f7ef694a712d8deccfba67a4860b0272e335f17a Mon Sep 17 00:00:00 2001 From: mame Date: Wed, 23 Apr 2008 15:22:13 +0000 Subject: [PATCH] * 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 --- ChangeLog | 11 + test/ruby/envutil.rb | 41 ++++ test/ruby/test_rubyoptions.rb | 45 +---- test/ruby/test_settracefunc.rb | 4 + test/ruby/test_thread.rb | 360 ++++++++++++++++++++++++++++++++- version.h | 6 +- 6 files changed, 417 insertions(+), 50 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9ef5d1f9cc..24097cba1a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Thu Apr 24 00:20:01 2008 Yusuke Endoh + + * 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. + Wed Apr 23 15:28:52 2008 Kazuhiro NISHIYAMA * test/gdbm/test_gdbm.rb (TestGDBM#test_s_open_no_create): failed diff --git a/test/ruby/envutil.rb b/test/ruby/envutil.rb index bbcab83d06..37efdde84e 100644 --- a/test/ruby/envutil.rb +++ b/test/ruby/envutil.rb @@ -1,3 +1,6 @@ +require "open3" +require "timeout" + module EnvUtil def rubybin unless ENV["RUBYOPT"] @@ -28,4 +31,42 @@ module EnvUtil end end module_function :rubybin + + LANG_ENVS = %w"LANG LC_ALL LC_CTYPE" + def rubyexec(*args) + if /(mswin|bccwin|mingw|emx)/ =~ RUBY_PLATFORM + flunk("cannot test in win32") + return + end + + ruby = EnvUtil.rubybin + c = "C" + env = {} + LANG_ENVS.each {|lc| env[lc], ENV[lc] = ENV[lc], c} + stdin, stdout, stderr = Open3.popen3(*([ruby] + args)) + env.each_pair {|lc, v| + if v + ENV[lc] = v + else + ENV.delete(lc) + end + } + env = nil + Timeout.timeout(10) do + yield(stdin, stdout, stderr) + end + + ensure + env.each_pair {|lc, v| + if v + ENV[lc] = v + else + ENV.delete(lc) + end + } if env + stdin .close unless !stdin || stdin .closed? + stdout.close unless !stdout || stdout.closed? + stderr.close unless !stderr || stderr.closed? + end + module_function :rubyexec end diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index 316f313a2c..3304a17ebf 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -1,44 +1,12 @@ require 'test/unit' -require 'timeout' require 'tmpdir' require 'tempfile' -require 'open3' require_relative 'envutil' class TestRubyOptions < Test::Unit::TestCase - -unless /(mswin|bccwin|mingw|emx)/ =~ RUBY_PLATFORM - - LANG_ENVS = %w"LANG LC_ALL LC_CTYPE" - def ruby(*args) - ruby = EnvUtil.rubybin - c = "C" - env = {} - LANG_ENVS.each {|lc| env[lc], ENV[lc] = ENV[lc], c} - stdin, stdout, stderr = Open3.popen3(*([ruby] + args)) - env.each_pair {|lc, v| - if v - ENV[lc] = v - else - ENV.delete(lc) - end - } - env = nil - Timeout.timeout(10) do - yield(stdin, stdout, stderr) - end - ensure - env.each_pair {|lc, v| - if v - ENV[lc] = v - else - ENV.delete(lc) - end - } if env - stdin .close unless !stdin || stdin .closed? - stdout.close unless !stdout || stdout.closed? - stderr.close unless !stderr || stderr.closed? + def ruby(*r, &b) + EnvUtil.rubyexec(*r, &b) end def test_source_file @@ -484,13 +452,4 @@ unless /(mswin|bccwin|mingw|emx)/ =~ RUBY_PLATFORM assert_equal('', r.read.chomp) end end - -else - - def test_win32 - flunk("cannot test in win32") - end - -end - end diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index 5ec450cb6d..1f260e003c 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -171,4 +171,8 @@ class TestSetTraceFunc < Test::Unit::TestCase events.shift) assert_equal([], events) end + + def test_invalid_proc + assert_raise(TypeError) { set_trace_func(1) } + end end diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index feebd99f13..b03b60b72a 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -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(/^#$/, 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 diff --git a/version.h b/version.h index bb614a75ba..f71847da23 100644 --- a/version.h +++ b/version.h @@ -1,7 +1,7 @@ #define RUBY_VERSION "1.9.0" -#define RUBY_RELEASE_DATE "2008-04-23" +#define RUBY_RELEASE_DATE "2008-04-24" #define RUBY_VERSION_CODE 190 -#define RUBY_RELEASE_CODE 20080423 +#define RUBY_RELEASE_CODE 20080424 #define RUBY_PATCHLEVEL 0 #define RUBY_VERSION_MAJOR 1 @@ -9,7 +9,7 @@ #define RUBY_VERSION_TEENY 0 #define RUBY_RELEASE_YEAR 2008 #define RUBY_RELEASE_MONTH 4 -#define RUBY_RELEASE_DAY 23 +#define RUBY_RELEASE_DAY 24 #ifdef RUBY_EXTERN RUBY_EXTERN const char ruby_version[];