diff options
Diffstat (limited to 'test/-ext-')
29 files changed, 1000 insertions, 156 deletions
diff --git a/test/-ext-/box/test_load_ext.rb b/test/-ext-/box/test_load_ext.rb new file mode 100644 index 0000000000..ea3744375e --- /dev/null +++ b/test/-ext-/box/test_load_ext.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true +require 'test/unit' + +class Test_Load_Extensions < Test::Unit::TestCase + ENV_ENABLE_BOX = {'RUBY_BOX' => '1'} + + def test_load_extension + pend + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + require '-test-/box/yay1' + assert_equal "1.0.0", Yay.version + assert_equal "yay", Yay.yay + end; + end + + def test_extension_contamination_in_global + pend + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) + begin; + require '-test-/box/yay1' + yay1 = Yay + assert_equal "1.0.0", Yay.version + assert_equal "yay", Yay.yay + + require '-test-/box/yay2' + assert_equal "2.0.0", Yay.version + v = Yay.yay + assert(v == "yay" || v == "yaaay") # "yay" on Linux, "yaaay" on macOS, Win32 + end; + end + + def test_load_extension_in_box + pend + assert_separately([ENV_ENABLE_BOX], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + ns = Ruby::Box.new + ns.require '-test-/box/yay1' + assert_equal "1.0.0", ns::Yay.version + assert_raise(NameError) { Yay } + end; + end + + def test_different_version_extensions + pend + assert_separately([ENV_ENABLE_BOX], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + ns1 = Ruby::Box.new + ns2 = Ruby::Box.new + ns1.require('-test-/box/yay1') + ns2.require('-test-/box/yay2') + + assert_raise(NameError) { Yay } + assert_not_nil ns1::Yay + assert_not_nil ns2::Yay + assert_equal "1.0.0", ns1::Yay::VERSION + assert_equal "2.0.0", ns2::Yay::VERSION + assert_equal "1.0.0", ns1::Yay.version + assert_equal "2.0.0", ns2::Yay.version + end; + end + + def test_loading_extensions_from_global_to_local + pend + assert_separately([ENV_ENABLE_BOX], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + require '-test-/box/yay1' + assert_equal "1.0.0", Yay.version + assert_equal "yay", Yay.yay + + ns = Ruby::Box.new + ns.require '-test-/box/yay2' + assert_equal "2.0.0", ns::Yay.version + assert_equal "yaaay", ns::Yay.yay + + assert_equal "yay", Yay.yay + end; + end + + def test_loading_extensions_from_local_to_global + pend + assert_separately([ENV_ENABLE_BOX], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + ns = Ruby::Box.new + ns.require '-test-/box/yay1' + assert_equal "1.0.0", ns::Yay.version + assert_equal "yay", ns::Yay.yay + + + require '-test-/box/yay2' + assert_equal "2.0.0", Yay.version + assert_equal "yaaay", Yay.yay + + assert_equal "yay", ns::Yay.yay + end; + end +end diff --git a/test/-ext-/bug_reporter/test_bug_reporter.rb b/test/-ext-/bug_reporter/test_bug_reporter.rb index 81d2ca4674..83fdba2282 100644 --- a/test/-ext-/bug_reporter/test_bug_reporter.rb +++ b/test/-ext-/bug_reporter/test_bug_reporter.rb @@ -2,17 +2,12 @@ require 'test/unit' require 'tmpdir' require_relative '../../lib/jit_support' +require_relative '../../lib/parser_support' class TestBugReporter < Test::Unit::TestCase - def yjit_enabled? - defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled? - end - def test_bug_reporter_add - omit if ENV['RUBY_ON_BUG'] - description = RUBY_DESCRIPTION - description = description.sub(/\+MJIT /, '') unless JITSupport.mjit_force_enabled? + description = description.sub(/\+PRISM /, '') unless ParserSupport.prism_enabled_in_subprocess? expected_stderr = [ :*, /\[BUG\]\sSegmentation\sfault.*\n/, @@ -24,9 +19,11 @@ class TestBugReporter < Test::Unit::TestCase tmpdir = Dir.mktmpdir no_core = "Process.setrlimit(Process::RLIMIT_CORE, 0); " if defined?(Process.setrlimit) && defined?(Process::RLIMIT_CORE) - args = ["--disable-gems", "-r-test-/bug_reporter", - "-C", tmpdir] - args.push("--yjit") if yjit_enabled? # We want the printed description to match this process's RUBY_DESCRIPTION + args = ["-r-test-/bug_reporter", "-C", tmpdir] + # We want the printed description to match this process's RUBY_DESCRIPTION + args.push("--yjit") if JITSupport.yjit_enabled? + args.push("--zjit") if JITSupport.zjit_enabled? + args.unshift({"RUBY_ON_BUG" => nil, "RUBY_CRASH_REPORT" => nil}) stdin = "#{no_core}register_sample_bug_reporter(12345); Process.kill :SEGV, $$" assert_in_out_err(args, stdin, [], expected_stderr, encoding: "ASCII-8BIT") ensure diff --git a/test/-ext-/debug/test_debug.rb b/test/-ext-/debug/test_debug.rb index 8a351d74fa..c9263d76fa 100644 --- a/test/-ext-/debug/test_debug.rb +++ b/test/-ext-/debug/test_debug.rb @@ -26,11 +26,14 @@ class TestDebug < Test::Unit::TestCase count[:iseq] += 1 assert_instance_of(RubyVM::InstructionSequence, iseq, msg) - # check same location - assert_equal(loc.path, iseq.path, msg) - assert_equal(loc.absolute_path, iseq.absolute_path, msg) - assert_equal(loc.label, iseq.label, msg) - assert_operator(loc.lineno, :>=, iseq.first_lineno, msg) + # Backtraces and source locations don't match for :c_trace methods + unless iseq.disasm.include?('C_TRACE') + # check same location + assert_equal(loc.path, iseq.path, msg) + assert_equal(loc.absolute_path, iseq.absolute_path, msg) + #assert_equal(loc.label, iseq.label, msg) + assert_operator(loc.lineno, :>=, iseq.first_lineno, msg) + end end assert_instance_of(Thread::Backtrace::Location, loc, msg) @@ -73,3 +76,57 @@ class TestDebug < Test::Unit::TestCase assert_equal true, x, '[Bug #15105]' end end + +# This is a YJIT test, but we can't test this without a C extension that calls +# rb_debug_inspector_open(), so we're testing it using "-test-/debug" here. +class TestDebugWithYJIT < Test::Unit::TestCase + class LocalSetArray + def to_a + Bug::Debug.inspector.each do |_, binding,| + binding.local_variable_set(:local, :ok) if binding + end + [:ok] + end + end + + class DebugArray + def to_a + Bug::Debug.inspector + [:ok] + end + end + + def test_yjit_invalidates_getlocal_after_splatarray + val = getlocal_after_splatarray(LocalSetArray.new) + assert_equal [:ok, :ok], val + end + + def test_yjit_invalidates_setlocal_after_splatarray + val = setlocal_after_splatarray(DebugArray.new) + assert_equal [:ok], val + end + + def test_yjit_invalidates_setlocal_after_proc_call + val = setlocal_after_proc_call(proc { Bug::Debug.inspector; :ok }) + assert_equal :ok, val + end + + private + + def getlocal_after_splatarray(array) + local = 1 + [*array, local] + end + + def setlocal_after_splatarray(array) + local = *array # setlocal followed by splatarray + itself # split a block using a C call + local # getlocal + end + + def setlocal_after_proc_call(block) + local = block.call # setlocal followed by OPTIMIZED_METHOD_TYPE_CALL + itself # split a block using a C call + local # getlocal + end +end if defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled? diff --git a/test/-ext-/debug/test_profile_frames.rb b/test/-ext-/debug/test_profile_frames.rb index d6ae953dd2..d79c94c468 100644 --- a/test/-ext-/debug/test_profile_frames.rb +++ b/test/-ext-/debug/test_profile_frames.rb @@ -14,6 +14,8 @@ class SampleClassForTestProfileFrames end class Sample2 + EVAL_LINE = __LINE__ + 3 + def baz(block) instance_eval "def zab(block) block.call end" [self, zab(block)] @@ -37,6 +39,20 @@ class SampleClassForTestProfileFrames end end +class SampleClassForTestProfileThreadFrames + def initialize(mutex) + @mutex = mutex + end + + def foo(block) + bar(block) + end + + def bar(block) + block.call + end +end + class TestProfileFrames < Test::Unit::TestCase def test_profile_frames obj, frames = Fiber.new{ @@ -112,7 +128,7 @@ class TestProfileFrames < Test::Unit::TestCase "SampleClassForTestProfileFrames#foo", "TestProfileFrames#test_profile_frames", ] - paths = [ nil, file=__FILE__, "(eval)", file, file, file, file, file, file, nil ] + paths = [ nil, file=__FILE__, "(eval at #{__FILE__}:#{SampleClassForTestProfileFrames::Sample2::EVAL_LINE})", file, file, file, file, file, file, nil ] absolute_paths = [ "<cfunc>", file, nil, file, file, file, file, file, file, nil ] assert_equal(labels.size, frames.size) @@ -137,21 +153,65 @@ class TestProfileFrames < Test::Unit::TestCase } end + def test_profile_thread_frames + mutex = Mutex.new + th = Thread.new do + mutex.lock + Thread.stop + SampleClassForTestProfileThreadFrames.new(mutex).foo(lambda { mutex.unlock; loop { sleep(1) } } ) + end + + # ensure execution has reached SampleClassForTestProfileThreadFrames#bar before running profile_thread_frames + loop { break if th.status == "sleep"; sleep 0.1 } + th.run + mutex.lock # wait until SampleClassForTestProfileThreadFrames#bar has been called + + frames = Bug::Debug.profile_thread_frames(th, 0, 10) + + full_labels = [ + "Kernel#sleep", + "TestProfileFrames#test_profile_thread_frames", + "Kernel#loop", + "TestProfileFrames#test_profile_thread_frames", + "SampleClassForTestProfileThreadFrames#bar", + "SampleClassForTestProfileThreadFrames#foo", + "TestProfileFrames#test_profile_thread_frames", + ] + + frames.each.with_index do |frame, i| + assert_equal(full_labels[i], frame) + end + + ensure + th.kill + th.join + end + + def test_matches_backtrace_locations_main_thread assert_equal(Thread.current, Thread.main) # Keep these in the same line, so the backtraces match exactly backtrace_locations, profile_frames = [Thread.current.backtrace_locations, Bug::Debug.profile_frames(0, 100)] - assert_equal(backtrace_locations.size, profile_frames.size) + errmsg = "backtrace_locations:\n " + backtrace_locations.map.with_index{|loc, i| "#{i} #{loc}"}.join("\n ") + errmsg += "\n\nprofile_frames:\n " + profile_frames.map.with_index{|(path, absolute_path, _, base_label, _, _, _, _, _, full_label, lineno), i| + if lineno + "#{i} #{absolute_path}:#{lineno} // #{full_label}" + else + "#{i} #{absolute_path} #{full_label}" + end + }.join("\n ") + assert_equal(backtrace_locations.size, profile_frames.size, errmsg) # The first entries are not going to match, since one is #backtrace_locations and the other #profile_frames backtrace_locations.shift profile_frames.shift # The rest of the stack is expected to look the same... - backtrace_locations.zip(profile_frames).each.with_index do |(location, (path, absolute_path, _, base_label, _, _, _, _, _, _, lineno)), i| + backtrace_locations.zip(profile_frames).each.with_index do |(location, (path, absolute_path, _, base_label, label, _, _, _, _, _, lineno)), i| next if absolute_path == "<cfunc>" # ...except for cfunc frames + next if label in "Array#each" | "Array#map" # ...except for :c_trace method frames err_msg = "#{i}th frame" assert_equal(location.absolute_path, absolute_path, err_msg) @@ -177,4 +237,8 @@ class TestProfileFrames < Test::Unit::TestCase a end; end + + def test_start + assert_equal Bug::Debug.profile_frames(0, 10).tap(&:shift), Bug::Debug.profile_frames(1, 9) + end end diff --git a/test/-ext-/gvl/test_last_thread.rb b/test/-ext-/gvl/test_last_thread.rb index f1bebafeea..bcda0e3385 100644 --- a/test/-ext-/gvl/test_last_thread.rb +++ b/test/-ext-/gvl/test_last_thread.rb @@ -15,8 +15,7 @@ class TestLastThread < Test::Unit::TestCase t1 = Time.now t = t1 - t0 - assert_in_delta(1.0, t, 0.16) + assert_in_delta(1.0, t, 0.8) end; end end - diff --git a/test/-ext-/integer/test_my_integer.rb b/test/-ext-/integer/test_my_integer.rb index 1b6f8489f8..0dfa234921 100644 --- a/test/-ext-/integer/test_my_integer.rb +++ b/test/-ext-/integer/test_my_integer.rb @@ -8,19 +8,13 @@ class Test_MyInteger < Test::Unit::TestCase Bug::Integer::MyInteger.new.to_f end - begin - Bug::Integer::MyInteger.class_eval do - def to_f - end + int = Class.new(Bug::Integer::MyInteger) do + def to_f end + end - assert_nothing_raised do - Bug::Integer::MyInteger.new.to_f - end - ensure - Bug::Integer::MyInteger.class_eval do - remove_method :to_f - end + assert_nothing_raised do + int.new.to_f end end @@ -29,20 +23,14 @@ class Test_MyInteger < Test::Unit::TestCase Bug::Integer::MyInteger.new <=> 0 end - begin - Bug::Integer::MyInteger.class_eval do - def <=>(other) - 0 - end + int = Class.new(Bug::Integer::MyInteger) do + def <=>(other) + 0 end + end - assert_nothing_raised do - Bug::Integer::MyInteger.new <=> 0 - end - ensure - Bug::Integer::MyInteger.class_eval do - remove_method :<=> - end + assert_nothing_raised do + int.new <=> 0 end end end diff --git a/test/-ext-/iseq_load/test_iseq_load.rb b/test/-ext-/iseq_load/test_iseq_load.rb index 6e5bc8e811..864ce1afbb 100644 --- a/test/-ext-/iseq_load/test_iseq_load.rb +++ b/test/-ext-/iseq_load/test_iseq_load.rb @@ -123,6 +123,8 @@ class TestIseqLoad < Test::Unit::TestCase assert_equal false, test_break_ensure_def_method omit "failing due to exception entry sp mismatch" assert_iseq_roundtrip(src) + ensure + Object.undef_method(:test_break_ensure_def_method) rescue nil end def test_kwarg diff --git a/test/-ext-/load/test_resolve_symbol.rb b/test/-ext-/load/test_resolve_symbol.rb new file mode 100644 index 0000000000..471d3acebd --- /dev/null +++ b/test/-ext-/load/test_resolve_symbol.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true +require 'test/unit' + +class Test_Load_ResolveSymbol < Test::Unit::TestCase + def test_load_resolve_symbol_resolver + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + feature = "Feature #20005" + assert_raise(LoadError, "resolve_symbol_target is not loaded") { + require '-test-/load/resolve_symbol_resolver' + } + require '-test-/load/resolve_symbol_target' + assert_nothing_raised(LoadError, "#{feature} resolver can be loaded") { + require '-test-/load/resolve_symbol_resolver' + } + assert_not_nil ResolveSymbolResolver + assert_equal "from target", ResolveSymbolResolver.any_method + + assert_raise(LoadError, "tries to resolve missing feature name, and it should raise LoadError") { + ResolveSymbolResolver.try_resolve_fname + } + assert_raise(LoadError, "tries to resolve missing symbol name, and it should raise LoadError") { + ResolveSymbolResolver.try_resolve_sname + } + end; + end +end diff --git a/test/-ext-/load/test_stringify_symbols.rb b/test/-ext-/load/test_stringify_symbols.rb new file mode 100644 index 0000000000..0d9736b591 --- /dev/null +++ b/test/-ext-/load/test_stringify_symbols.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true +require 'test/unit' + +class Test_Load_stringify_symbols < Test::Unit::TestCase + def test_load_stringify_symbol_required_extensions + require '-test-/load/stringify_symbols' + require '-test-/load/stringify_target' + r1 = StringifySymbols.stringify_symbol("-test-/load/stringify_target", "stt_any_method") + assert_not_nil r1 + r2 = StringifySymbols.stringify_symbol("-test-/load/stringify_target.so", "stt_any_method") + assert_equal r1, r2, "resolved symbols should be equal even with or without .so suffix" + end + + def test_load_stringify_symbol_statically_linked + require '-test-/load/stringify_symbols' + # "complex.so" is actually not a statically linked extension. + # But it is registered in $LOADED_FEATURES, so it can be a target of this test. + r1 = StringifySymbols.stringify_symbol("complex", "rb_complex_minus") + assert_not_nil r1 + r2 = StringifySymbols.stringify_symbol("complex.so", "rb_complex_minus") + assert_equal r1, r2 + end + + def test_load_stringify_symbol_missing_target + require '-test-/load/stringify_symbols' + r1 = assert_nothing_raised { + StringifySymbols.stringify_symbol("something_missing", "unknown_method") + } + assert_nil r1 + r2 = assert_nothing_raised { + StringifySymbols.stringify_symbol("complex.so", "unknown_method") + } + assert_nil r2 + end +end diff --git a/test/-ext-/marshal/test_internal_ivar.rb b/test/-ext-/marshal/test_internal_ivar.rb index a32138f6e8..8b4667fdf9 100644 --- a/test/-ext-/marshal/test_internal_ivar.rb +++ b/test/-ext-/marshal/test_internal_ivar.rb @@ -7,11 +7,16 @@ module Bug end module Bug::Marshal class TestInternalIVar < Test::Unit::TestCase def test_marshal - v = InternalIVar.new("hello", "world", "bye") + v = InternalIVar.new("hello", "world", "bye", "hi") assert_equal("hello", v.normal) assert_equal("world", v.internal) assert_equal("bye", v.encoding_short) - dump = assert_warn(/instance variable `E' on class \S+ is not dumped/) { + assert_equal("hi", v.encoding_long) + warnings = ->(s) { + w = s.scan(/instance variable '(.+?)' on class \S+ is not dumped/) + assert_equal(%w[E K encoding], w.flatten.sort) + } + dump = assert_warn(warnings) { ::Marshal.dump(v) } v = assert_nothing_raised {break ::Marshal.load(dump)} @@ -19,6 +24,7 @@ module Bug::Marshal assert_equal("hello", v.normal) assert_nil(v.internal) assert_nil(v.encoding_short) + assert_nil(v.encoding_long) end end end diff --git a/test/-ext-/postponed_job/test_postponed_job.rb b/test/-ext-/postponed_job/test_postponed_job.rb index fee0172d11..8c2b3e95d1 100644 --- a/test/-ext-/postponed_job/test_postponed_job.rb +++ b/test/-ext-/postponed_job/test_postponed_job.rb @@ -2,34 +2,70 @@ require 'test/unit' require '-test-/postponed_job' -module Bug - def self.postponed_job_call_direct_wrapper(*args) - postponed_job_call_direct(*args) +class TestPostponed_job < Test::Unit::TestCase + def test_preregister_and_trigger + assert_separately([], __FILE__, __LINE__, <<-'RUBY') + require '-test-/postponed_job' + Bug.postponed_job_preregister_and_call_without_sleep(counters = []) + # i.e. rb_postponed_job_trigger performs coalescing + assert_equal([3], counters) + + # i.e. rb_postponed_job_trigger resets after interrupts are checked + Bug.postponed_job_preregister_and_call_with_sleep(counters = []) + assert_equal([1, 2, 3], counters) + RUBY end - def self.postponed_job_register_wrapper(*args) - postponed_job_register(*args) + def test_multiple_preregistration + assert_separately([], __FILE__, __LINE__, <<-'RUBY') + require '-test-/postponed_job' + handles = Bug.postponed_job_preregister_multiple_times + # i.e. rb_postponed_job_preregister returns the same handle if preregistered multiple times + assert_equal [handles[0]], handles.uniq + RUBY end -end -class TestPostponed_job < Test::Unit::TestCase - def test_register - direct, registered = [], [] + def test_multiple_preregistration_with_new_data + assert_separately([], __FILE__, __LINE__, <<-'RUBY') + require '-test-/postponed_job' + values = Bug.postponed_job_preregister_calls_with_last_argument + # i.e. the callback is called with the last argument it was preregistered with + assert_equal [3, 4], values + RUBY + end - Bug.postponed_job_call_direct_wrapper(direct) - Bug.postponed_job_register_wrapper(registered) + def test_legacy_register + assert_separately([], __FILE__, __LINE__, <<-'RUBY') + require '-test-/postponed_job' + direct, registered = [], [] - assert_equal([0], direct) - assert_equal([3], registered) + Bug.postponed_job_call_direct(direct) + Bug.postponed_job_register(registered) - Bug.postponed_job_register_one(ary = []) - assert_equal [1], ary + assert_equal([0], direct) + assert_equal([3], registered) + + Bug.postponed_job_register_one(ary = []) + assert_equal [1], ary + RUBY + end + + def test_legacy_register_one_same + assert_separately([], __FILE__, __LINE__, <<-'RUBY') + require '-test-/postponed_job' + # Registering the same job three times should result in three of the same handle + handles = Bug.postponed_job_register_one_same + assert_equal [handles[0]], handles.uniq + RUBY end if Bug.respond_to?(:postponed_job_register_in_c_thread) - def test_register_in_c_thread - assert Bug.postponed_job_register_in_c_thread(ary = []) - assert_equal [1], ary + def test_legacy_register_in_c_thread + assert_separately([], __FILE__, __LINE__, <<-'RUBY') + require '-test-/postponed_job' + assert Bug.postponed_job_register_in_c_thread(ary = []) + assert_equal [1], ary + RUBY end end end diff --git a/test/-ext-/required.rb b/test/-ext-/required.rb new file mode 100644 index 0000000000..70514355ff --- /dev/null +++ b/test/-ext-/required.rb @@ -0,0 +1,10 @@ +require 'continuation' +cont = nil +a = [*1..10].reject do |i| + callcc {|c| cont = c} if !cont and i == 10 + false +end +if a.size < 1000 + a.unshift(:x) + cont.call +end diff --git a/test/-ext-/scheduler/test_interrupt_with_scheduler.rb b/test/-ext-/scheduler/test_interrupt_with_scheduler.rb new file mode 100644 index 0000000000..eb7a0647e5 --- /dev/null +++ b/test/-ext-/scheduler/test_interrupt_with_scheduler.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true +require 'test/unit' +require 'timeout' +require_relative '../../fiber/scheduler' + +class TestSchedulerInterruptHandling < Test::Unit::TestCase + def setup + pend("No fork support") unless Process.respond_to?(:fork) + require '-test-/scheduler' + end + + # Test without Thread.handle_interrupt - should work regardless of fix + def test_without_handle_interrupt_signal_works + IO.pipe do |input, output| + pid = fork do + STDERR.reopen(output) + + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + + Signal.trap(:INT) do + ::Thread.current.raise(Interrupt) + end + + Fiber.schedule do + # Yield to the scheduler: + sleep(0) + + Bug::Scheduler.blocking_loop(output) + end + end + + output.close + assert_equal "x", input.read(1) + + Process.kill(:INT, pid) + + reaper = Thread.new do + Process.waitpid2(pid) + end + + unless reaper.join(10) + Process.kill(:KILL, pid) + end + + _, status = reaper.value + + # It should be interrupted (not killed): + assert_not_equal 0, status.exitstatus + assert_equal true, status.signaled? + assert_equal Signal.list["INT"], status.termsig + end + end +end diff --git a/test/-ext-/stack/test_stack_overflow.rb b/test/-ext-/stack/test_stack_overflow.rb new file mode 100644 index 0000000000..3d7f00331d --- /dev/null +++ b/test/-ext-/stack/test_stack_overflow.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true +require 'test/unit' + +class Test_StackOverflow < Test::Unit::TestCase + def setup + omit "Stack overflow tests are not supported on this platform: #{RUBY_PLATFORM.inspect}" unless RUBY_PLATFORM =~ /x86_64-linux|darwin/ + + require '-test-/stack' + + omit "Stack overflow tests are not supported with ASAN" if Thread.asan? + end + + def test_overflow + assert_separately([], <<~RUBY) + # GC may try to scan the top of the stack and cause a SEGV. + GC.disable + require '-test-/stack' + + assert_raise(SystemStackError) do + Thread.stack_overflow + end + RUBY + end + + def test_thread_stack_overflow + assert_separately([], <<~RUBY) + require '-test-/stack' + GC.disable + + thread = Thread.new do + Thread.current.report_on_exception = false + Thread.stack_overflow + end + + assert_raise(SystemStackError) do + thread.join + end + RUBY + end + + def test_fiber_stack_overflow + assert_separately([], <<~RUBY) + require '-test-/stack' + GC.disable + + fiber = Fiber.new do + Thread.stack_overflow + end + + assert_raise(SystemStackError) do + fiber.resume + end + RUBY + end +end diff --git a/test/-ext-/string/test_capacity.rb b/test/-ext-/string/test_capacity.rb index 0cb7c00761..df000f7cdb 100644 --- a/test/-ext-/string/test_capacity.rb +++ b/test/-ext-/string/test_capacity.rb @@ -23,7 +23,7 @@ class Test_StringCapacity < Test::Unit::TestCase def test_s_new_capacity assert_equal("", String.new(capacity: 1000)) assert_equal(String, String.new(capacity: 1000).class) - assert_equal(10000, capa(String.new(capacity: 10000))) + assert_equal(10_000, capa(String.new(capacity: 10_000))) assert_equal("", String.new(capacity: -1000)) assert_equal(capa(String.new(capacity: -10000)), capa(String.new(capacity: -1000))) @@ -66,11 +66,7 @@ class Test_StringCapacity < Test::Unit::TestCase end def embed_header_size - if GC.using_rvargc? - 2 * RbConfig::SIZEOF['void*'] + RbConfig::SIZEOF['long'] - else - 2 * RbConfig::SIZEOF['void*'] - end + GC::INTERNAL_CONSTANTS[:RBASIC_SIZE] + RbConfig::SIZEOF['void*'] end def max_embed_len diff --git a/test/-ext-/string/test_fstring.rb b/test/-ext-/string/test_fstring.rb index 5a3456c566..fcec6be543 100644 --- a/test/-ext-/string/test_fstring.rb +++ b/test/-ext-/string/test_fstring.rb @@ -15,19 +15,27 @@ class Test_String_Fstring < Test::Unit::TestCase def test_rb_enc_interned_str_autoloaded_encoding assert_separately([], <<~RUBY) require '-test-/string' - assert_include(Encoding::Windows_31J.inspect, 'autoload') - Bug::String.rb_enc_interned_str(Encoding::Windows_31J) + assert_include(Encoding::CESU_8.inspect, 'autoload') + Bug::String.rb_enc_interned_str(Encoding::CESU_8) RUBY end + def test_rb_enc_interned_str_null_encoding + assert_equal Encoding::ASCII_8BIT, Bug::String.rb_enc_interned_str(nil).encoding + end + def test_rb_enc_str_new_autoloaded_encoding assert_separately([], <<~RUBY) require '-test-/string' - assert_include(Encoding::Windows_31J.inspect, 'autoload') - Bug::String.rb_enc_str_new(Encoding::Windows_31J) + assert_include(Encoding::CESU_8.inspect, 'autoload') + Bug::String.rb_enc_str_new(Encoding::CESU_8) RUBY end + def test_rb_enc_str_new_null_encoding + assert_equal Encoding::ASCII_8BIT, Bug::String.rb_enc_str_new(nil).encoding + end + def test_instance_variable str = __method__.to_s * 3 str.instance_variable_set(:@test, 42) @@ -49,6 +57,10 @@ class Test_String_Fstring < Test::Unit::TestCase assert_raise(TypeError) {fstr.singleton_class} end + def test_fake_str + assert_equal([*"a".."z"].join(""), Bug::String.fstring_fake_str) + end + class S < String end diff --git a/test/-ext-/string/test_interned_str.rb b/test/-ext-/string/test_interned_str.rb index 340dba41e8..a81cb59aa5 100644 --- a/test/-ext-/string/test_interned_str.rb +++ b/test/-ext-/string/test_interned_str.rb @@ -9,4 +9,9 @@ class Test_RbInternedStr < Test::Unit::TestCase src << "b" * 20 assert_equal "a" * 20, interned_str end + + def test_interned_str_encoding + src = :ascii.name + assert_equal Encoding::US_ASCII, Bug::String.rb_interned_str_dup(src).encoding + end end diff --git a/test/-ext-/string/test_set_len.rb b/test/-ext-/string/test_set_len.rb index 67ba961194..1531d76167 100644 --- a/test/-ext-/string/test_set_len.rb +++ b/test/-ext-/string/test_set_len.rb @@ -34,4 +34,51 @@ class Test_StrSetLen < Test::Unit::TestCase assert_equal 128, Bug::String.capacity(str) assert_equal 127, str.set_len(127).bytesize, bug12757 end + + def test_coderange_after_append + u = -"\u3042" + str = Bug::String.new(encoding: Encoding::UTF_8) + bsize = u.bytesize + str.append(u) + assert_equal 0, str.bytesize + str.set_len(bsize) + assert_equal bsize, str.bytesize + assert_predicate str, :valid_encoding? + assert_not_predicate str, :ascii_only? + assert_equal u, str + end + + def test_coderange_after_trunc + u = -"\u3042" + bsize = u.bytesize + str = Bug::String.new(u) + str.set_len(bsize - 1) + assert_equal bsize - 1, str.bytesize + assert_not_predicate str, :valid_encoding? + assert_not_predicate str, :ascii_only? + str.append(u.byteslice(-1)) + str.set_len(bsize) + assert_equal bsize, str.bytesize + assert_predicate str, :valid_encoding? + assert_not_predicate str, :ascii_only? + assert_equal u, str + end + + def test_valid_encoding_after_resized + s = "\0\0".force_encoding(Encoding::UTF_16BE) + str = Bug::String.new(s) + assert_predicate str, :valid_encoding? + str.resize(1) + assert_not_predicate str, :valid_encoding? + str.resize(2) + assert_predicate str, :valid_encoding? + str.resize(3) + assert_not_predicate str, :valid_encoding? + + s = "\xDB\x00\xDC\x00".force_encoding(Encoding::UTF_16BE) + str = Bug::String.new(s) + assert_predicate str, :valid_encoding? + str.resize(2) + assert_not_predicate str, :valid_encoding? + end end diff --git a/test/-ext-/string/test_too_many_dummy_encodings.rb b/test/-ext-/string/test_too_many_dummy_encodings.rb index 4d71fd1d72..b96b40db7b 100644 --- a/test/-ext-/string/test_too_many_dummy_encodings.rb +++ b/test/-ext-/string/test_too_many_dummy_encodings.rb @@ -4,7 +4,7 @@ require "-test-/string" class Test_TooManyDummyEncodings < Test::Unit::TestCase def test_exceed_encoding_table_size - assert_separately(%w[--disable=gems], "#{<<~"begin;"}\n#{<<~'end;'}") + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; require "-test-/string" assert_raise_with_message(EncodingError, /too many encoding/) do diff --git a/test/-ext-/struct/test_data.rb b/test/-ext-/struct/test_data.rb new file mode 100644 index 0000000000..8dbc9113a5 --- /dev/null +++ b/test/-ext-/struct/test_data.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: false +require 'test/unit' +require "-test-/struct" + +class Bug::Struct::Test_Data < Test::Unit::TestCase + def test_data_new_default + klass = Bug::Struct.data_new(false) + assert_equal Data, klass.superclass + assert_equal %i[mem1 mem2], klass.members + end + + def test_data_new_superclass + superclass = Data.define + klass = Bug::Struct.data_new(superclass) + assert_equal superclass, klass.superclass + assert_equal %i[mem1 mem2], klass.members + end +end diff --git a/test/-ext-/symbol/test_type.rb b/test/-ext-/symbol/test_type.rb index fdee692fe4..ed019062fa 100644 --- a/test/-ext-/symbol/test_type.rb +++ b/test/-ext-/symbol/test_type.rb @@ -123,16 +123,25 @@ module Test_Symbol def test_check_id_invalid_type cx = EnvUtil.labeled_class("X\u{1f431}") - assert_raise_with_message(TypeError, /X\u{1F431}/) { - Bug::Symbol.pinneddown?(cx) - } + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{1F431}/) { + Bug::Symbol.pinneddown?(cx) + } + end end def test_check_symbol_invalid_type cx = EnvUtil.labeled_class("X\u{1f431}") - assert_raise_with_message(TypeError, /X\u{1F431}/) { - Bug::Symbol.find(cx) - } + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{1F431}/) { + Bug::Symbol.find(cx) + } + end + end + + def test_const_name_type + sym = "\xb5".force_encoding(Encoding::Windows_1253) + assert_not_operator Bug::Symbol, :const?, sym, sym.encode(Encoding::UTF_8) end end end diff --git a/test/-ext-/test_abi.rb b/test/-ext-/test_abi.rb index d3ea6bb9b1..7f30feb944 100644 --- a/test/-ext-/test_abi.rb +++ b/test/-ext-/test_abi.rb @@ -9,7 +9,11 @@ class TestABI < Test::Unit::TestCase assert_separately [], <<~RUBY err = assert_raise(LoadError) { require "-test-/abi" } assert_match(/incompatible ABI version/, err.message) - assert_include err.message, "/-test-/abi." + if Ruby::Box.enabled? + assert_include err.message, "_-test-+abi." + else + assert_include err.message, "/-test-/abi." + end RUBY end @@ -27,7 +31,11 @@ class TestABI < Test::Unit::TestCase assert_separately [{ "RUBY_ABI_CHECK" => "1" }], <<~RUBY err = assert_raise(LoadError) { require "-test-/abi" } assert_match(/incompatible ABI version/, err.message) - assert_include err.message, "/-test-/abi." + if Ruby::Box.enabled? + assert_include err.message, "_-test-+abi." + else + assert_include err.message, "/-test-/abi." + end RUBY end diff --git a/test/-ext-/test_bug-3571.rb b/test/-ext-/test_bug-3571.rb index c75d2e8523..5952ce2a33 100644 --- a/test/-ext-/test_bug-3571.rb +++ b/test/-ext-/test_bug-3571.rb @@ -13,8 +13,8 @@ end SRC out = [ "start() function is unimplemented on this machine", - "-:2:in `start'", - "-:2:in `<main>'", + "-:2:in 'Bug.start'", + "-:2:in '<main>'", ] assert_in_out_err(%w"-r-test-/bug_3571", src, [], out, bug3571) end diff --git a/test/-ext-/test_ensure_and_callcc.rb b/test/-ext-/test_ensure_and_callcc.rb new file mode 100644 index 0000000000..9303a094ea --- /dev/null +++ b/test/-ext-/test_ensure_and_callcc.rb @@ -0,0 +1,40 @@ +# -*- coding: us-ascii -*- +# frozen_string_literal: false +require 'test/unit' + +class TestEnsureAndCallcc < Test::Unit::TestCase + def test_bug20655_dir_chdir_using_rb_ensure + require 'tmpdir' + need_continuation + called = 0 + tmp = nil + Dir.mktmpdir do |tmpdir| + Dir.chdir(tmpdir) do + tmp = Dir.pwd + cont = nil + callcc{|c| cont = c} + assert_equal(tmp, Dir.pwd, "BUG #20655: ensure called and pwd was changed unexpectedly") + called += 1 + cont.call if called < 10 + end + end + end + + def test_bug20655_extension_using_rb_ensure + need_continuation + require '-test-/ensure_and_callcc' + EnsureAndCallcc.reset + assert_equal(0, EnsureAndCallcc.ensure_called) + EnsureAndCallcc.require_with_ensure(File.join(__dir__, 'required')) + assert_equal(1, EnsureAndCallcc.ensure_called, + "BUG #20655: ensure called unexpectedly in the required script even without exceptions") + end + + private + def need_continuation + unless respond_to?(:callcc, true) + EnvUtil.suppress_warning {require 'continuation'} + end + omit 'requires callcc support' unless respond_to?(:callcc, true) + end +end diff --git a/test/-ext-/thread/helper.rb b/test/-ext-/thread/helper.rb new file mode 100644 index 0000000000..3ea2057d15 --- /dev/null +++ b/test/-ext-/thread/helper.rb @@ -0,0 +1,51 @@ +module ThreadInstrumentation + module TestHelper + private + + def record + Bug::ThreadInstrumentation.register_callback(!ENV["GVL_DEBUG"]) + yield + ensure + timeline = Bug::ThreadInstrumentation.unregister_callback + if $! + raise + else + return timeline + end + end + + def timeline_for(thread, timeline) + timeline.select { |t, _| t == thread }.map(&:last) + end + + def assert_consistent_timeline(events) + refute_predicate events, :empty? + + previous_event = nil + events.each do |event| + refute_equal :exited, previous_event, "`exited` must be the final event: #{events.inspect}" + case event + when :started + assert_nil previous_event, "`started` must be the first event: #{events.inspect}" + when :ready + unless previous_event.nil? + assert %i(started suspended).include?(previous_event), "`ready` must be preceded by `started` or `suspended`: #{events.inspect}" + end + when :resumed + unless previous_event.nil? + assert_equal :ready, previous_event, "`resumed` must be preceded by `ready`: #{events.inspect}" + end + when :suspended + unless previous_event.nil? + assert_equal :resumed, previous_event, "`suspended` must be preceded by `resumed`: #{events.inspect}" + end + when :exited + unless previous_event.nil? + assert %i(resumed suspended).include?(previous_event), "`exited` must be preceded by `resumed` or `suspended`: #{events.inspect}" + end + end + previous_event = event + end + end + end +end diff --git a/test/-ext-/thread/test_instrumentation_api.rb b/test/-ext-/thread/test_instrumentation_api.rb index dd620e7380..ba41069304 100644 --- a/test/-ext-/thread/test_instrumentation_api.rb +++ b/test/-ext-/thread/test_instrumentation_api.rb @@ -1,91 +1,291 @@ # frozen_string_literal: false require 'envutil' +require_relative "helper" class TestThreadInstrumentation < Test::Unit::TestCase + include ThreadInstrumentation::TestHelper + def setup pend("No windows support") if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM require '-test-/thread/instrumentation' - Thread.list.each do |thread| - if thread != Thread.current - thread.kill - thread.join rescue nil - end - end - assert_equal [Thread.current], Thread.list - - Bug::ThreadInstrumentation.reset_counters - Bug::ThreadInstrumentation::register_callback + cleanup_threads end def teardown return if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM - Bug::ThreadInstrumentation::unregister_callback + Bug::ThreadInstrumentation.unregister_callback + cleanup_threads end THREADS_COUNT = 3 - def test_thread_instrumentation - threads = threaded_cpu_work - assert_equal [false] * THREADS_COUNT, threads.map(&:status) - counters = Bug::ThreadInstrumentation.counters - assert_join_counters(counters) - assert_global_join_counters(counters) + def test_single_thread_timeline + thread = nil + full_timeline = record do + thread = Thread.new { 1 + 1 } + thread.join + end + assert_equal %i(started ready resumed suspended exited), timeline_for(thread, full_timeline) + ensure + thread&.kill + end + + def test_thread_pass_single_thread + full_timeline = record do + Thread.pass + end + assert_equal [], timeline_for(Thread.current, full_timeline) + end + + def test_thread_pass_multi_thread + thread = Thread.new do + cpu_bound_work(0.5) + end + + full_timeline = record do + Thread.pass + end + + assert_equal %i(suspended ready resumed), timeline_for(Thread.current, full_timeline) + ensure + thread&.kill + thread&.join + end + + def test_multi_thread_timeline + threads = nil + full_timeline = record do + threads = threaded_cpu_bound_work(1.0) + results = threads.map(&:value) + results.each do |r| + refute_equal false, r + end + assert_equal [false] * THREADS_COUNT, threads.map(&:status) + end + + threads.each do |thread| + timeline = timeline_for(thread, full_timeline) + assert_consistent_timeline(timeline) + assert_operator timeline.count(:suspended), :>=, 1, "Expected threads to yield suspended at least once: #{timeline.inspect}" + end + + timeline = timeline_for(Thread.current, full_timeline) + assert_consistent_timeline(timeline) + ensure + threads&.each(&:kill) + end + + def test_join_suspends # Bug #18900 + thread = other_thread = nil + full_timeline = record do + other_thread = Thread.new { sleep 0.3 } + thread = Thread.new { other_thread.join } + thread.join + end + + timeline = timeline_for(thread, full_timeline) + assert_consistent_timeline(timeline) + assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline + ensure + other_thread&.kill + thread&.kill + end + + def test_io_release_gvl + r, w = IO.pipe + thread = nil + full_timeline = record do + thread = Thread.new do + w.write("Hello\n") + end + thread.join + end + + timeline = timeline_for(thread, full_timeline) + assert_consistent_timeline(timeline) + assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline + ensure + r&.close + w&.close + end + + def test_queue_releases_gvl + queue1 = Queue.new + queue2 = Queue.new + + thread = nil + + full_timeline = record do + thread = Thread.new do + queue1 << true + queue2.pop + end + + queue1.pop + queue2 << true + thread.join + end + + timeline = timeline_for(thread, full_timeline) + assert_consistent_timeline(timeline) + assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline + end + + def test_blocking_on_ractor + assert_ractor(<<-"RUBY", require_relative: "helper", require: "-test-/thread/instrumentation") + include ThreadInstrumentation::TestHelper + + ractor = Ractor.new { + Ractor.receive # wait until woke + Thread.current + } + + # Wait for the main thread to block, then wake the ractor + Thread.new do + while Thread.main.status != "sleep" + Thread.pass + end + ractor.send true + end + + full_timeline = record do + ractor.value + end + + timeline = timeline_for(Thread.current, full_timeline) + assert_consistent_timeline(timeline) + assert_equal %i(suspended ready resumed), timeline + RUBY + end + + def test_sleeping_inside_ractor + omit "This test is flaky and intermittently failing now on ModGC workflow" if ENV['GITHUB_WORKFLOW'] == 'ModGC' + + assert_ractor(<<-"RUBY", require_relative: "helper", require: "-test-/thread/instrumentation") + include ThreadInstrumentation::TestHelper + + thread = nil + + full_timeline = record do + thread = Ractor.new{ + sleep 0.1 + Thread.current + }.value + sleep 0.1 + end + + timeline = timeline_for(thread, full_timeline) + assert_consistent_timeline(timeline) + assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline + RUBY + end + + def test_thread_blocked_forever_on_mutex + mutex = Mutex.new + mutex.lock + thread = nil + + full_timeline = record do + thread = Thread.new do + mutex.lock + end + 10.times { Thread.pass } + sleep 0.1 + end + + mutex.unlock + thread.join + + timeline = timeline_for(thread, full_timeline) + assert_consistent_timeline(timeline) + assert_equal %i(started ready resumed suspended), timeline end - def test_join_counters # Bug #18900 - thr = Thread.new { fib(30) } - Bug::ThreadInstrumentation.reset_counters - thr.join - assert_join_counters(Bug::ThreadInstrumentation.local_counters) + def test_thread_blocked_temporarily_on_mutex + mutex = Mutex.new + mutex.lock + thread = nil + + full_timeline = record do + thread = Thread.new do + mutex.lock + end + 10.times { Thread.pass } + sleep 0.1 + mutex.unlock + 10.times { Thread.pass } + sleep 0.1 + end + + thread.join + + timeline = timeline_for(thread, full_timeline) + assert_consistent_timeline(timeline) + assert_equal %i(started ready resumed suspended ready resumed suspended exited), timeline end def test_thread_instrumentation_fork_safe skip "No fork()" unless Process.respond_to?(:fork) - thread_statuses = counters = nil + thread_statuses = full_timeline = nil IO.popen("-") do |read_pipe| if read_pipe thread_statuses = Marshal.load(read_pipe) - counters = Marshal.load(read_pipe) + full_timeline = Marshal.load(read_pipe) else - Bug::ThreadInstrumentation.reset_counters - threads = threaded_cpu_work + threads = threaded_cpu_bound_work.each(&:join) Marshal.dump(threads.map(&:status), STDOUT) - Marshal.dump(Bug::ThreadInstrumentation.counters, STDOUT) + full_timeline = Bug::ThreadInstrumentation.unregister_callback.map { |t, e| [t.to_s, e ] } + Marshal.dump(full_timeline, STDOUT) end end assert_predicate $?, :success? assert_equal [false] * THREADS_COUNT, thread_statuses - assert_join_counters(counters) - assert_global_join_counters(counters) + thread_names = full_timeline.map(&:first).uniq + thread_names.each do |thread_name| + assert_consistent_timeline(timeline_for(thread_name, full_timeline)) + end end def test_thread_instrumentation_unregister - Bug::ThreadInstrumentation::unregister_callback assert Bug::ThreadInstrumentation::register_and_unregister_callbacks end private - def fib(n = 20) + def fib(n = 30) return n if n <= 1 fib(n-1) + fib(n-2) end - def threaded_cpu_work(size = 20) - THREADS_COUNT.times.map { Thread.new { fib(size) } }.each(&:join) + def cpu_bound_work(duration) + deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + duration + i = 0 + while deadline > Process.clock_gettime(Process::CLOCK_MONOTONIC) + fib(25) + i += 1 + end + i > 0 ? i : false end - def assert_join_counters(counters) - counters.each_with_index do |c, i| - assert_operator c, :>, 0, "Call counters[#{i}]: #{counters.inspect}" + def threaded_cpu_bound_work(duration = 0.5) + THREADS_COUNT.times.map do + Thread.new do + cpu_bound_work(duration) + end end end - def assert_global_join_counters(counters) - assert_equal THREADS_COUNT, counters.first + def cleanup_threads + Thread.list.each do |thread| + if thread != Thread.current + thread.kill + thread.join rescue nil + end + end + assert_equal [Thread.current], Thread.list end end diff --git a/test/-ext-/thread/test_lock_native_thread.rb b/test/-ext-/thread/test_lock_native_thread.rb new file mode 100644 index 0000000000..b4044b2b93 --- /dev/null +++ b/test/-ext-/thread/test_lock_native_thread.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: false + +require 'envutil' + +mn_supported_p = -> do + out, *_ = EnvUtil.invoke_ruby([{'RUBY_MN_THREADS' => '1'}, '-v'], '', true) + return /\+MN/ =~ out +end + +if mn_supported_p.call + # test only on MN threads +else + return +end + +class TestThreadLockNativeThread < Test::Unit::TestCase + def test_lock_native_thread + omit "LSAN reports memory leak because NT is not freed for MN thread" if Test::Sanitizers.lsan_enabled? + + assert_separately([{'RUBY_MN_THREADS' => '1'}], <<-RUBY) + require '-test-/thread/lock_native_thread' + + Thread.new{ + assert_equal true, Thread.current.lock_native_thread + }.join + + # main thread already has DNT + assert_equal false, Thread.current.lock_native_thread + RUBY + end + + def test_lock_native_thread_tls + omit "LSAN reports memory leak because NT is not freed for MN thread" if Test::Sanitizers.lsan_enabled? + + assert_separately([{'RUBY_MN_THREADS' => '1'}], <<-RUBY) + require '-test-/thread/lock_native_thread' + tn = 10 + ln = 1_000 + + ts = tn.times.map{|i| + Thread.new(i){|i| + Thread.current.set_tls i + assert_equal true, Thread.current.lock_native_thread + + ln.times{ + assert_equal i, Thread.current.get_tls + Thread.pass + } + } + } + ts.each(&:join) + RUBY + end +end diff --git a/test/-ext-/thread_fd/test_thread_fd_close.rb b/test/-ext-/thread_fd/test_thread_fd_close.rb deleted file mode 100644 index 1d2ef63635..0000000000 --- a/test/-ext-/thread_fd/test_thread_fd_close.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true -require 'test/unit' -require '-test-/thread_fd' - -class TestThreadFdClose < Test::Unit::TestCase - - def test_thread_fd_close - IO.pipe do |r, w| - th = Thread.new do - begin - assert_raise(IOError) { - r.read(4) - } - ensure - w.syswrite('done') - end - end - Thread.pass until th.stop? - IO.thread_fd_close(r.fileno) - assert_equal 'done', r.read(4) - th.join - end - end -end diff --git a/test/-ext-/tracepoint/test_tracepoint.rb b/test/-ext-/tracepoint/test_tracepoint.rb index 9d1679602a..603fd01fd5 100644 --- a/test/-ext-/tracepoint/test_tracepoint.rb +++ b/test/-ext-/tracepoint/test_tracepoint.rb @@ -11,6 +11,7 @@ class TestTracepointObj < Test::Unit::TestCase def test_tracks_objspace_events result = EnvUtil.suppress_warning {eval(<<-EOS, nil, __FILE__, __LINE__+1)} + # frozen_string_literal: false Bug.tracepoint_track_objspace_events { 99 'abc' @@ -65,15 +66,15 @@ class TestTracepointObj < Test::Unit::TestCase count = 0 hook = proc {count += 1} def run(hook) - stress, GC.stress = GC.stress, false - Bug.after_gc_start_hook = hook - begin - GC.stress = true - 3.times {Object.new} - ensure - GC.stress = stress - Bug.after_gc_start_hook = nil - end + stress, GC.stress = GC.stress, false + Bug.after_gc_start_hook = hook + begin + GC.stress = true + 3.times {Object.new} + ensure + GC.stress = stress + Bug.after_gc_start_hook = nil + end end run(hook) puts count @@ -82,7 +83,7 @@ class TestTracepointObj < Test::Unit::TestCase end def test_teardown_with_active_GC_end_hook - assert_separately([], 'require("-test-/tracepoint"); Bug.after_gc_exit_hook = proc {}') + assert_ruby_status([], 'require("-test-/tracepoint"); Bug.after_gc_exit_hook = proc {}; GC.start') end end |
