diff options
Diffstat (limited to 'test/ruby/test_thread_queue.rb')
| -rw-r--r-- | test/ruby/test_thread_queue.rb | 205 |
1 files changed, 152 insertions, 53 deletions
diff --git a/test/ruby/test_thread_queue.rb b/test/ruby/test_thread_queue.rb index 13c839ab50..9a41be8b1a 100644 --- a/test/ruby/test_thread_queue.rb +++ b/test/ruby/test_thread_queue.rb @@ -8,17 +8,26 @@ class TestThreadQueue < Test::Unit::TestCase SizedQueue = Thread::SizedQueue def test_queue_initialized - assert_raise(TypeError) { + assert_raise_with_message(TypeError, /\bQueue.* not initialized/) { Queue.allocate.push(nil) } end def test_sized_queue_initialized - assert_raise(TypeError) { + assert_raise_with_message(TypeError, /\bSizedQueue.* not initialized/) { SizedQueue.allocate.push(nil) } end + def test_freeze + assert_raise(TypeError) { + Queue.new.freeze + } + assert_raise(TypeError) { + SizedQueue.new(5).freeze + } + end + def test_queue grind(5, 1000, 15, Queue) end @@ -54,15 +63,37 @@ class TestThreadQueue < Test::Unit::TestCase assert_equal 0, to_workers.size end + def test_queue_initialize + e = Class.new do + include Enumerable + def initialize(list) @list = list end + def each(&block) @list.each(&block) end + end + + all_assertions_foreach(nil, + [Array, "Array"], + [e, "Enumerable"], + [Struct.new(:to_a), "Array-like"], + ) do |a, type| + q = Thread::Queue.new(a.new([1,2,3])) + assert_equal(3, q.size, type) + assert_not_predicate(q, :empty?, type) + assert_equal(1, q.pop, type) + assert_equal(2, q.pop, type) + assert_equal(3, q.pop, type) + assert_predicate(q, :empty?, type) + end + end + def test_sized_queue_initialize - q = SizedQueue.new(1) + q = Thread::SizedQueue.new(1) assert_equal 1, q.max - assert_raise(ArgumentError) { SizedQueue.new(0) } - assert_raise(ArgumentError) { SizedQueue.new(-1) } + assert_raise(ArgumentError) { Thread::SizedQueue.new(0) } + assert_raise(ArgumentError) { Thread::SizedQueue.new(-1) } end def test_sized_queue_assign_max - q = SizedQueue.new(2) + q = Thread::SizedQueue.new(2) assert_equal(2, q.max) q.max = 1 assert_equal(1, q.max) @@ -82,37 +113,90 @@ class TestThreadQueue < Test::Unit::TestCase end def test_queue_pop_interrupt - q = Queue.new + q = Thread::Queue.new t1 = Thread.new { q.pop } sleep 0.01 until t1.stop? t1.kill.join assert_equal(0, q.num_waiting) end + def test_queue_pop_timeout + q = Thread::Queue.new + q << 1 + assert_equal 1, q.pop(timeout: 1) + + t1 = Thread.new { q.pop(timeout: 1) } + assert_equal t1, t1.join(2) + assert_nil t1.value + + t2 = Thread.new { q.pop(timeout: 0.1) } + assert_equal t2, t2.join(1) + assert_nil t2.value + ensure + t1&.kill&.join + t2&.kill&.join + end + def test_queue_pop_non_block - q = Queue.new + q = Thread::Queue.new assert_raise_with_message(ThreadError, /empty/) do q.pop(true) end end def test_sized_queue_pop_interrupt - q = SizedQueue.new(1) + q = Thread::SizedQueue.new(1) t1 = Thread.new { q.pop } sleep 0.01 until t1.stop? t1.kill.join assert_equal(0, q.num_waiting) end + def test_sized_queue_pop_timeout + q = Thread::SizedQueue.new(1) + + q << 1 + assert_equal 1, q.pop(timeout: 1) + + t1 = Thread.new { q.pop(timeout: 1) } + assert_equal t1, t1.join(2) + assert_nil t1.value + + t2 = Thread.new { q.pop(timeout: 0.1) } + assert_equal t2, t2.join(1) + assert_nil t2.value + ensure + t1&.kill&.join + t2&.kill&.join + end + def test_sized_queue_pop_non_block - q = SizedQueue.new(1) + q = Thread::SizedQueue.new(1) assert_raise_with_message(ThreadError, /empty/) do q.pop(true) end end + def test_sized_queue_push_timeout + q = Thread::SizedQueue.new(1) + + q << 1 + assert_equal 1, q.size + + t1 = Thread.new { q.push(2, timeout: 1) } + assert_equal t1, t1.join(2) + assert_nil t1.value + + t2 = Thread.new { q.push(2, timeout: 0.1) } + assert_equal t2, t2.join(1) + assert_nil t2.value + ensure + t1&.kill&.join + t2&.kill&.join + end + def test_sized_queue_push_interrupt - q = SizedQueue.new(1) + q = Thread::SizedQueue.new(1) q.push(1) assert_raise_with_message(ThreadError, /full/) do q.push(2, true) @@ -120,7 +204,7 @@ class TestThreadQueue < Test::Unit::TestCase end def test_sized_queue_push_non_block - q = SizedQueue.new(1) + q = Thread::SizedQueue.new(1) q.push(1) t1 = Thread.new { q.push(2) } sleep 0.01 until t1.stop? @@ -129,16 +213,18 @@ class TestThreadQueue < Test::Unit::TestCase end def test_thr_kill + omit "[Bug #18613]" if /freebsd/ =~ RUBY_PLATFORM + bug5343 = '[ruby-core:39634]' Dir.mktmpdir {|d| timeout = 60 total_count = 250 begin - assert_normal_exit(<<-"_eom", bug5343, **{:timeout => timeout, :chdir=>d}) + assert_normal_exit(<<-"_eom", bug5343, timeout: timeout, chdir: d) + r, w = IO.pipe #{total_count}.times do |i| - open("test_thr_kill_count", "w") {|f| f.puts i } - queue = Queue.new - r, w = IO.pipe + File.open("test_thr_kill_count", "w") {|f| f.puts i } + queue = Thread::Queue.new th = Thread.start { queue.push(nil) r.read 1 @@ -149,27 +235,33 @@ class TestThreadQueue < Test::Unit::TestCase end _eom rescue Timeout::Error + # record load average: + uptime = `uptime` rescue nil + if uptime && /(load average: [\d.]+),/ =~ uptime + la = " (#{$1})" + end + count = File.read("#{d}/test_thr_kill_count").to_i - flunk "only #{count}/#{total_count} done in #{timeout} seconds." + flunk "only #{count}/#{total_count} done in #{timeout} seconds.#{la}" end } end def test_queue_push_return_value - q = Queue.new + q = Thread::Queue.new retval = q.push(1) assert_same q, retval end def test_queue_clear_return_value - q = Queue.new + q = Thread::Queue.new retval = q.clear assert_same q, retval end def test_sized_queue_clear - # Fill queue, then test that SizedQueue#clear wakes up all waiting threads - sq = SizedQueue.new(2) + # Fill queue, then test that Thread::SizedQueue#clear wakes up all waiting threads + sq = Thread::SizedQueue.new(2) 2.times { sq << 1 } t1 = Thread.new do @@ -190,19 +282,19 @@ class TestThreadQueue < Test::Unit::TestCase end def test_sized_queue_push_return_value - q = SizedQueue.new(1) + q = Thread::SizedQueue.new(1) retval = q.push(1) assert_same q, retval end def test_sized_queue_clear_return_value - q = SizedQueue.new(1) + q = Thread::SizedQueue.new(1) retval = q.clear assert_same q, retval end def test_sized_queue_throttle - q = SizedQueue.new(1) + q = Thread::SizedQueue.new(1) i = 0 consumer = Thread.new do while q.pop @@ -225,7 +317,7 @@ class TestThreadQueue < Test::Unit::TestCase end def test_queue_thread_raise - q = Queue.new + q = Thread::Queue.new th1 = Thread.new do begin q.pop @@ -255,7 +347,7 @@ class TestThreadQueue < Test::Unit::TestCase def test_dup bug9440 = '[ruby-core:59961] [Bug #9440]' - q = Queue.new + q = Thread::Queue.new assert_raise(NoMethodError, bug9440) do q.dup end @@ -265,12 +357,12 @@ class TestThreadQueue < Test::Unit::TestCase def test_dump bug9674 = '[ruby-core:61677] [Bug #9674]' - q = Queue.new + q = Thread::Queue.new assert_raise_with_message(TypeError, /#{Queue}/, bug9674) do Marshal.dump(q) end - sq = SizedQueue.new(1) + sq = Thread::SizedQueue.new(1) assert_raise_with_message(TypeError, /#{SizedQueue}/, bug9674) do Marshal.dump(sq) end @@ -282,12 +374,12 @@ class TestThreadQueue < Test::Unit::TestCase end def test_close - [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate| + [->{Thread::Queue.new}, ->{Thread::SizedQueue.new 3}].each do |qcreate| q = qcreate.call assert_equal false, q.closed? q << :something assert_equal q, q.close - assert q.closed? + assert_predicate q, :closed? assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing} assert_equal q.pop, :something assert_nil q.pop @@ -321,15 +413,15 @@ class TestThreadQueue < Test::Unit::TestCase end def test_queue_close_wakeup - close_wakeup(15, 18){Queue.new} + close_wakeup(15, 18){Thread::Queue.new} end def test_size_queue_close_wakeup - close_wakeup(5, 8){SizedQueue.new 9} + close_wakeup(5, 8){Thread::SizedQueue.new 9} end def test_sized_queue_one_closed_interrupt - q = SizedQueue.new 1 + q = Thread::SizedQueue.new 1 q << :one t1 = Thread.new { Thread.current.report_on_exception = false @@ -341,12 +433,12 @@ class TestThreadQueue < Test::Unit::TestCase assert_equal 1, q.size assert_equal :one, q.pop - assert q.empty?, "queue not empty" + assert_empty q end # make sure that shutdown state is handled properly by empty? for the non-blocking case def test_empty_non_blocking - q = SizedQueue.new 3 + q = Thread::SizedQueue.new 3 3.times{|i| q << i} # these all block cos the queue is full @@ -372,13 +464,13 @@ class TestThreadQueue < Test::Unit::TestCase end def test_sized_queue_closed_push_non_blocking - q = SizedQueue.new 7 + q = Thread::SizedQueue.new 7 q.close assert_raise_with_message(ClosedQueueError, /queue closed/){q.push(non_block=true)} end def test_blocked_pushers - q = SizedQueue.new 3 + q = Thread::SizedQueue.new 3 prod_threads = 6.times.map do |i| thr = Thread.new{ Thread.current.report_on_exception = false @@ -424,9 +516,9 @@ class TestThreadQueue < Test::Unit::TestCase end def test_deny_pushers - [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate| + [->{Thread::Queue.new}, ->{Thread::SizedQueue.new 3}].each do |qcreate| q = qcreate[] - synq = Queue.new + synq = Thread::Queue.new prod_threads = 20.times.map do |i| Thread.new { synq.pop @@ -444,7 +536,7 @@ class TestThreadQueue < Test::Unit::TestCase # size should account for waiting pushers during shutdown def sized_queue_size_close - q = SizedQueue.new 4 + q = Thread::SizedQueue.new 4 4.times{|i| q << i} Thread.new{ q << 5 } Thread.new{ q << 6 } @@ -456,7 +548,7 @@ class TestThreadQueue < Test::Unit::TestCase end def test_blocked_pushers_empty - q = SizedQueue.new 3 + q = Thread::SizedQueue.new 3 prod_threads = 6.times.map do |i| Thread.new{ Thread.current.report_on_exception = false @@ -475,7 +567,7 @@ class TestThreadQueue < Test::Unit::TestCase assert_equal 0, q.size assert_equal 3, ary.size - ary.each{|e| assert [0,1,2,3,4,5].include?(e)} + ary.each{|e| assert_include [0,1,2,3,4,5], e} assert_nil q.pop prod_threads.each{|t| @@ -488,14 +580,14 @@ class TestThreadQueue < Test::Unit::TestCase # test thread wakeup on one-element SizedQueue with close def test_one_element_sized_queue - q = SizedQueue.new 1 + q = Thread::SizedQueue.new 1 t = Thread.new{ q.pop } q.close assert_nil t.value end def test_close_twice - [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate| + [->{Thread::Queue.new}, ->{Thread::SizedQueue.new 3}].each do |qcreate| q = qcreate[] q.close assert_nothing_raised(ClosedQueueError){q.close} @@ -503,14 +595,19 @@ class TestThreadQueue < Test::Unit::TestCase end def test_queue_close_multi_multi - q = SizedQueue.new rand(800..1200) + q = Thread::SizedQueue.new rand(800..1200) count_items = rand(3000..5000) count_producers = rand(10..20) + # ensure threads do not start running too soon and complete before we check status + mutex = Mutex.new + mutex.lock + producers = count_producers.times.map do Thread.new do - sleep(rand / 100) + mutex.lock + mutex.unlock count_items.times{|i| q << [i,"#{i} for #{Thread.current.inspect}"]} end end @@ -528,9 +625,11 @@ class TestThreadQueue < Test::Unit::TestCase # No dead or finished threads, give up to 10 seconds to start running t = Time.now - Thread.pass until Time.now - t > 10 || (consumers + producers).all?{|thr| thr.status =~ /\A(?:run|sleep)\z/} + Thread.pass until Time.now - t > 10 || (consumers + producers).all?{|thr| thr.status.to_s =~ /\A(?:run|sleep)\z/} + + assert (consumers + producers).all?{|thr| thr.status.to_s =~ /\A(?:run|sleep)\z/}, 'no threads running' - assert (consumers + producers).all?{|thr| thr.status =~ /\A(?:run|sleep)\z/}, 'no threads running' + mutex.unlock # just exercising the concurrency of the support methods. counter = Thread.new do @@ -558,14 +657,14 @@ class TestThreadQueue < Test::Unit::TestCase def test_queue_with_trap if ENV['APPVEYOR'] == 'True' && RUBY_PLATFORM.match?(/mswin/) - skip 'This test fails too often on AppVeyor vs140' + omit 'This test fails too often on AppVeyor vs140' end if RUBY_PLATFORM.match?(/mingw/) - skip 'This test fails too often on MinGW' + omit 'This test fails too often on MinGW' end assert_in_out_err([], <<-INPUT, %w(INT INT exit), []) - q = Queue.new + q = Thread::Queue.new trap(:INT){ q.push 'INT' } @@ -582,8 +681,8 @@ class TestThreadQueue < Test::Unit::TestCase end def test_fork_while_queue_waiting - q = Queue.new - sq = SizedQueue.new(1) + q = Thread::Queue.new + sq = Thread::SizedQueue.new(1) thq = Thread.new { q.pop } thsq = Thread.new { sq.pop } Thread.pass until thq.stop? && thsq.stop? |
