From c25554d34befb5e484924759d31b133b25f77803 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Mon, 22 Sep 2025 16:45:27 -0400 Subject: [PATCH 1/2] Change test-all suite to enable running each test in separate ractors. This tests for ractor safety issues as well as concurrency issues. You can enable these tests with RUBY_TESTS_WITH_RACTORS=X when running `make test-all TESTS=test/ruby`. Running tests with ractors is currently only working for tests under the "test/ruby" directory. You should also use the `.excludes-ractor` excludes directory. For example: ``` EXCLUDES=test/.excludes-ractor RUBY_TESTS_WITH_RACTORS=3 TESTS=test/ruby make test-all ``` This will create 3 ractors for each test method, run the test method inside each ractor and then call `join` on the ractors. Then, it's ready to process the next test. The reason we do this instead of taking all the tests and dividing them between the number of ractors is: * We want to run each test under concurrency load to test whether or not it is safe under ractors. If it isn't safe, we know it's something to do with this specific test. * If we get a segfault or error, we know the error is coming from this test alone, not interactions with other tests. The disadvantage of this approach is: * It's slower than dividing the tests between the number of ractors running. * This might not uncover ractor scheduling issues that would show up when you have long-running ractors. --- ext/-test-/file/fs.c | 3 + ext/-test-/integer/init.c | 3 + ext/-test-/iter/break.c | 4 + ext/-test-/iter/init.c | 5 + ext/-test-/iter/yield.c | 5 + .../rb_call_super_kw/rb_call_super_kw.c | 4 + ext/-test-/stack/stack.c | 3 + .../Prism/TestCompilePrism.rb | 27 ++++ test/.excludes-ractor/RubyVM.rb | 2 + test/.excludes-ractor/TestAlias.rb | 2 + test/.excludes-ractor/TestAllocation.rb | 1 + test/.excludes-ractor/TestArgf.rb | 1 + test/.excludes-ractor/TestArray.rb | 9 ++ test/.excludes-ractor/TestArraySubclass.rb | 2 + test/.excludes-ractor/TestAssignment.rb | 5 + test/.excludes-ractor/TestAssignmentGen.rb | 1 + test/.excludes-ractor/TestAst.rb | 2 + test/.excludes-ractor/TestAutoload.rb | 1 + test/.excludes-ractor/TestBacktrace.rb | 3 + .../.excludes-ractor/TestBasicInstructions.rb | 1 + test/.excludes-ractor/TestBeginEndBlock.rb | 2 + test/.excludes-ractor/TestBignum.rb | 2 + test/.excludes-ractor/TestCaseFold.rb | 1 + test/.excludes-ractor/TestClass.rb | 5 + test/.excludes-ractor/TestConst.rb | 1 + test/.excludes-ractor/TestDefaultGems.rb | 1 + test/.excludes-ractor/TestDefined.rb | 4 + test/.excludes-ractor/TestDir.rb | 1 + test/.excludes-ractor/TestDir_M17N.rb | 1 + test/.excludes-ractor/TestEnumerable.rb | 1 + test/.excludes-ractor/TestEnumerator.rb | 1 + test/.excludes-ractor/TestEnv.rb | 1 + test/.excludes-ractor/TestEval.rb | 16 ++ test/.excludes-ractor/TestException.rb | 11 ++ test/.excludes-ractor/TestFiber.rb | 1 + test/.excludes-ractor/TestFile.rb | 18 +++ test/.excludes-ractor/TestFileExhaustive.rb | 14 ++ test/.excludes-ractor/TestFlip.rb | 1 + test/.excludes-ractor/TestFloat.rb | 1 + test/.excludes-ractor/TestGCCompact.rb | 1 + test/.excludes-ractor/TestGc.rb | 1 + test/.excludes-ractor/TestHash.rb | 7 + test/.excludes-ractor/TestHash/TestSubHash.rb | 2 + test/.excludes-ractor/TestHashOnly.rb | 2 + test/.excludes-ractor/TestIO.rb | 27 ++++ test/.excludes-ractor/TestIOBuffer.rb | 13 ++ test/.excludes-ractor/TestIO_M17N.rb | 2 + test/.excludes-ractor/TestISeq.rb | 1 + test/.excludes-ractor/TestInteger.rb | 1 + test/.excludes-ractor/TestKeyword.rb | 1 + test/.excludes-ractor/TestM17N.rb | 2 + test/.excludes-ractor/TestMarshal.rb | 6 + .../TestMarshal/TestMarshalFreeze.rb | 2 + .../TestMarshal/TestMarshalFreezeProc.rb | 2 + test/.excludes-ractor/TestMath.rb | 3 + test/.excludes-ractor/TestMetaclass.rb | 1 + test/.excludes-ractor/TestMethod.rb | 5 + test/.excludes-ractor/TestModule.rb | 29 ++++ test/.excludes-ractor/TestNamespace.rb | 1 + test/.excludes-ractor/TestObjectSpace.rb | 1 + test/.excludes-ractor/TestParse.rb | 6 + test/.excludes-ractor/TestPatternMatching.rb | 4 + test/.excludes-ractor/TestProcess.rb | 26 +++ test/.excludes-ractor/TestRand.rb | 2 + test/.excludes-ractor/TestRange.rb | 3 + test/.excludes-ractor/TestReadPartial.rb | 2 + test/.excludes-ractor/TestRefinement.rb | 1 + test/.excludes-ractor/TestRegexp.rb | 2 + test/.excludes-ractor/TestRequire.rb | 39 +++++ test/.excludes-ractor/TestRequireLib.rb | 1 + test/.excludes-ractor/TestRubyOptimization.rb | 3 + test/.excludes-ractor/TestRubyOptions.rb | 1 + test/.excludes-ractor/TestRubyPrimitive.rb | 9 ++ test/.excludes-ractor/TestRubyVM.rb | 1 + test/.excludes-ractor/TestSetTraceFunc.rb | 1 + test/.excludes-ractor/TestShapes.rb | 7 + test/.excludes-ractor/TestSignal.rb | 1 + test/.excludes-ractor/TestSleep.rb | 1 + test/.excludes-ractor/TestSprintfComb.rb | 1 + test/.excludes-ractor/TestString.rb | 12 ++ test/.excludes-ractor/TestString2.rb | 2 + test/.excludes-ractor/TestStringMemory.rb | 1 + test/.excludes-ractor/TestStringchar.rb | 1 + test/.excludes-ractor/TestStruct.rb | 8 + test/.excludes-ractor/TestStruct/SubStruct.rb | 2 + test/.excludes-ractor/TestStruct/TopStruct.rb | 2 + test/.excludes-ractor/TestSuper.rb | 3 + test/.excludes-ractor/TestSymbol.rb | 3 + test/.excludes-ractor/TestSyntax.rb | 5 + test/.excludes-ractor/TestSystem.rb | 1 + test/.excludes-ractor/TestThread.rb | 13 ++ .../TestThreadConditionVariable.rb | 4 + test/.excludes-ractor/TestThreadQueue.rb | 2 + test/.excludes-ractor/TestTime.rb | 7 + test/.excludes-ractor/TestTrace.rb | 1 + test/.excludes-ractor/TestVariable.rb | 12 ++ test/fileutils/test_fileutils.rb | 9 +- test/json/json_fixtures_test.rb | 21 +-- test/ruby/enc/test_case_comprehensive.rb | 6 +- test/ruby/enc/test_emoji_breaks.rb | 5 +- test/ruby/enc/test_grapheme_breaks.rb | 14 +- test/ruby/enc/test_iso_8859.rb | 4 +- test/ruby/enc/test_koi8.rb | 4 +- test/ruby/enc/test_regex_casefold.rb | 21 ++- test/ruby/test_array.rb | 39 +++-- test/ruby/test_ast.rb | 74 +++++---- test/ruby/test_beginendblock.rb | 2 +- test/ruby/test_bignum.rb | 22 +-- test/ruby/test_class.rb | 31 ++-- test/ruby/test_compile_prism.rb | 103 +++++++----- test/ruby/test_condition.rb | 10 +- test/ruby/test_enum.rb | 3 + test/ruby/test_enumerator.rb | 10 +- test/ruby/test_env.rb | 12 +- test/ruby/test_eval.rb | 151 ++++++++++++------ test/ruby/test_exception.rb | 55 +++---- test/ruby/test_file.rb | 24 ++- test/ruby/test_file_exhaustive.rb | 122 ++++++++------ test/ruby/test_float.rb | 2 +- test/ruby/test_gc.rb | 10 +- test/ruby/test_hash.rb | 13 +- test/ruby/test_integer_comb.rb | 2 +- test/ruby/test_io.rb | 68 +++++--- test/ruby/test_io_buffer.rb | 4 +- test/ruby/test_io_m17n.rb | 71 ++++---- test/ruby/test_iseq.rb | 4 +- test/ruby/test_keyword.rb | 10 +- test/ruby/test_literal.rb | 2 +- test/ruby/test_m17n.rb | 75 +++++---- test/ruby/test_m17n_comb.rb | 12 +- test/ruby/test_marshal.rb | 57 +++---- test/ruby/test_method.rb | 13 +- test/ruby/test_module.rb | 59 +++---- test/ruby/test_object.rb | 11 +- test/ruby/test_object_id.rb | 9 ++ test/ruby/test_objectspace.rb | 6 +- test/ruby/test_parse.rb | 4 +- test/ruby/test_pattern_matching.rb | 2 + test/ruby/test_primitive.rb | 35 ++-- test/ruby/test_proc.rb | 22 +-- test/ruby/test_process.rb | 129 ++++++++------- test/ruby/test_refinement.rb | 62 +++---- test/ruby/test_regexp.rb | 12 +- test/ruby/test_require.rb | 25 +-- test/ruby/test_rubyvm.rb | 51 +++--- test/ruby/test_settracefunc.rb | 36 ++--- test/ruby/test_string.rb | 18 ++- test/ruby/test_stringchar.rb | 10 +- test/ruby/test_struct.rb | 12 +- test/ruby/test_super.rb | 6 +- test/ruby/test_symbol.rb | 4 +- test/ruby/test_syntax.rb | 21 ++- test/ruby/test_thread.rb | 28 ++-- test/ruby/test_thread_cv.rb | 4 +- test/ruby/test_time.rb | 4 +- test/ruby/test_time_tz.rb | 104 ++++++++---- test/ruby/test_variable.rb | 2 + test/ruby/test_whileuntil.rb | 16 +- test/ruby/test_yield.rb | 4 +- test/ruby/test_yjit.rb | 2 +- test/test_tmpdir.rb | 4 +- tool/lib/core_assertions.rb | 112 ++++++++----- tool/lib/envutil.rb | 78 +++++---- tool/lib/leakchecker.rb | 4 + tool/lib/test/unit.rb | 128 +++++++++++++-- tool/lib/test/unit/assertions.rb | 6 +- tool/lib/test/unit/testcase.rb | 31 +++- tool/lib/tracepointchecker.rb | 2 +- tool/lib/zombie_hunter.rb | 2 +- 169 files changed, 1785 insertions(+), 832 deletions(-) create mode 100644 test/.excludes-ractor/Prism/TestCompilePrism.rb create mode 100644 test/.excludes-ractor/RubyVM.rb create mode 100644 test/.excludes-ractor/TestAlias.rb create mode 100644 test/.excludes-ractor/TestAllocation.rb create mode 100644 test/.excludes-ractor/TestArgf.rb create mode 100644 test/.excludes-ractor/TestArray.rb create mode 100644 test/.excludes-ractor/TestArraySubclass.rb create mode 100644 test/.excludes-ractor/TestAssignment.rb create mode 100644 test/.excludes-ractor/TestAssignmentGen.rb create mode 100644 test/.excludes-ractor/TestAst.rb create mode 100644 test/.excludes-ractor/TestAutoload.rb create mode 100644 test/.excludes-ractor/TestBacktrace.rb create mode 100644 test/.excludes-ractor/TestBasicInstructions.rb create mode 100644 test/.excludes-ractor/TestBeginEndBlock.rb create mode 100644 test/.excludes-ractor/TestBignum.rb create mode 100644 test/.excludes-ractor/TestCaseFold.rb create mode 100644 test/.excludes-ractor/TestClass.rb create mode 100644 test/.excludes-ractor/TestConst.rb create mode 100644 test/.excludes-ractor/TestDefaultGems.rb create mode 100644 test/.excludes-ractor/TestDefined.rb create mode 100644 test/.excludes-ractor/TestDir.rb create mode 100644 test/.excludes-ractor/TestDir_M17N.rb create mode 100644 test/.excludes-ractor/TestEnumerable.rb create mode 100644 test/.excludes-ractor/TestEnumerator.rb create mode 100644 test/.excludes-ractor/TestEnv.rb create mode 100644 test/.excludes-ractor/TestEval.rb create mode 100644 test/.excludes-ractor/TestException.rb create mode 100644 test/.excludes-ractor/TestFiber.rb create mode 100644 test/.excludes-ractor/TestFile.rb create mode 100644 test/.excludes-ractor/TestFileExhaustive.rb create mode 100644 test/.excludes-ractor/TestFlip.rb create mode 100644 test/.excludes-ractor/TestFloat.rb create mode 100644 test/.excludes-ractor/TestGCCompact.rb create mode 100644 test/.excludes-ractor/TestGc.rb create mode 100644 test/.excludes-ractor/TestHash.rb create mode 100644 test/.excludes-ractor/TestHash/TestSubHash.rb create mode 100644 test/.excludes-ractor/TestHashOnly.rb create mode 100644 test/.excludes-ractor/TestIO.rb create mode 100644 test/.excludes-ractor/TestIOBuffer.rb create mode 100644 test/.excludes-ractor/TestIO_M17N.rb create mode 100644 test/.excludes-ractor/TestISeq.rb create mode 100644 test/.excludes-ractor/TestInteger.rb create mode 100644 test/.excludes-ractor/TestKeyword.rb create mode 100644 test/.excludes-ractor/TestM17N.rb create mode 100644 test/.excludes-ractor/TestMarshal.rb create mode 100644 test/.excludes-ractor/TestMarshal/TestMarshalFreeze.rb create mode 100644 test/.excludes-ractor/TestMarshal/TestMarshalFreezeProc.rb create mode 100644 test/.excludes-ractor/TestMath.rb create mode 100644 test/.excludes-ractor/TestMetaclass.rb create mode 100644 test/.excludes-ractor/TestMethod.rb create mode 100644 test/.excludes-ractor/TestModule.rb create mode 100644 test/.excludes-ractor/TestNamespace.rb create mode 100644 test/.excludes-ractor/TestObjectSpace.rb create mode 100644 test/.excludes-ractor/TestParse.rb create mode 100644 test/.excludes-ractor/TestPatternMatching.rb create mode 100644 test/.excludes-ractor/TestProcess.rb create mode 100644 test/.excludes-ractor/TestRand.rb create mode 100644 test/.excludes-ractor/TestRange.rb create mode 100644 test/.excludes-ractor/TestReadPartial.rb create mode 100644 test/.excludes-ractor/TestRefinement.rb create mode 100644 test/.excludes-ractor/TestRegexp.rb create mode 100644 test/.excludes-ractor/TestRequire.rb create mode 100644 test/.excludes-ractor/TestRequireLib.rb create mode 100644 test/.excludes-ractor/TestRubyOptimization.rb create mode 100644 test/.excludes-ractor/TestRubyOptions.rb create mode 100644 test/.excludes-ractor/TestRubyPrimitive.rb create mode 100644 test/.excludes-ractor/TestRubyVM.rb create mode 100644 test/.excludes-ractor/TestSetTraceFunc.rb create mode 100644 test/.excludes-ractor/TestShapes.rb create mode 100644 test/.excludes-ractor/TestSignal.rb create mode 100644 test/.excludes-ractor/TestSleep.rb create mode 100644 test/.excludes-ractor/TestSprintfComb.rb create mode 100644 test/.excludes-ractor/TestString.rb create mode 100644 test/.excludes-ractor/TestString2.rb create mode 100644 test/.excludes-ractor/TestStringMemory.rb create mode 100644 test/.excludes-ractor/TestStringchar.rb create mode 100644 test/.excludes-ractor/TestStruct.rb create mode 100644 test/.excludes-ractor/TestStruct/SubStruct.rb create mode 100644 test/.excludes-ractor/TestStruct/TopStruct.rb create mode 100644 test/.excludes-ractor/TestSuper.rb create mode 100644 test/.excludes-ractor/TestSymbol.rb create mode 100644 test/.excludes-ractor/TestSyntax.rb create mode 100644 test/.excludes-ractor/TestSystem.rb create mode 100644 test/.excludes-ractor/TestThread.rb create mode 100644 test/.excludes-ractor/TestThreadConditionVariable.rb create mode 100644 test/.excludes-ractor/TestThreadQueue.rb create mode 100644 test/.excludes-ractor/TestTime.rb create mode 100644 test/.excludes-ractor/TestTrace.rb create mode 100644 test/.excludes-ractor/TestVariable.rb diff --git a/ext/-test-/file/fs.c b/ext/-test-/file/fs.c index eb17e9768ef6f1..bc5bac407df4fb 100644 --- a/ext/-test-/file/fs.c +++ b/ext/-test-/file/fs.c @@ -105,6 +105,9 @@ get_noatime_p(VALUE self, VALUE str) void Init_fs(VALUE module) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif VALUE fs = rb_define_module_under(module, "Fs"); rb_define_module_function(fs, "fsname", get_fsname, 1); rb_define_module_function(fs, "noatime?", get_noatime_p, 1); diff --git a/ext/-test-/integer/init.c b/ext/-test-/integer/init.c index fc256ea16bf46e..483af73177eec3 100644 --- a/ext/-test-/integer/init.c +++ b/ext/-test-/integer/init.c @@ -5,6 +5,9 @@ void Init_integer(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif VALUE mBug = rb_define_module("Bug"); VALUE klass = rb_define_class_under(mBug, "Integer", rb_cObject); TEST_INIT_FUNCS(init); diff --git a/ext/-test-/iter/break.c b/ext/-test-/iter/break.c index 4d43c5d0cf4782..b0b240a47537a2 100644 --- a/ext/-test-/iter/break.c +++ b/ext/-test-/iter/break.c @@ -19,6 +19,10 @@ iter_break_value(VALUE self, VALUE val) void Init_break(VALUE klass) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + // Mark this extension as Ractor-safe. + rb_ext_ractor_safe(true); +#endif VALUE breakable = rb_define_module_under(klass, "Breakable"); rb_define_module_function(breakable, "iter_break", iter_break, 0); rb_define_module_function(breakable, "iter_break_value", iter_break_value, 1); diff --git a/ext/-test-/iter/init.c b/ext/-test-/iter/init.c index a074ec46a982d4..2eea461ddec71e 100644 --- a/ext/-test-/iter/init.c +++ b/ext/-test-/iter/init.c @@ -5,6 +5,11 @@ void Init_iter(void) { + +#ifdef HAVE_RB_EXT_RACTOR_SAFE + // Mark this extension as Ractor-safe. + rb_ext_ractor_safe(true); +#endif VALUE mBug = rb_define_module("Bug"); VALUE klass = rb_define_module_under(mBug, "Iter"); TEST_INIT_FUNCS(init); diff --git a/ext/-test-/iter/yield.c b/ext/-test-/iter/yield.c index 0f6f3e87eb7731..e47166be1db9b6 100644 --- a/ext/-test-/iter/yield.c +++ b/ext/-test-/iter/yield.c @@ -10,6 +10,11 @@ yield_block(int argc, VALUE *argv, VALUE self) void Init_yield(VALUE klass) { + +#ifdef HAVE_RB_EXT_RACTOR_SAFE + // Mark this extension as Ractor-safe. + rb_ext_ractor_safe(true); +#endif VALUE yield = rb_define_module_under(klass, "Yield"); rb_define_method(yield, "yield_block", yield_block, -1); diff --git a/ext/-test-/rb_call_super_kw/rb_call_super_kw.c b/ext/-test-/rb_call_super_kw/rb_call_super_kw.c index 61681ed7334498..3607e008c3dccf 100644 --- a/ext/-test-/rb_call_super_kw/rb_call_super_kw.c +++ b/ext/-test-/rb_call_super_kw/rb_call_super_kw.c @@ -9,6 +9,10 @@ rb_call_super_kw_m(int argc, VALUE *argv, VALUE self) void Init_rb_call_super_kw(void) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + // Mark this extension as Ractor-safe. + rb_ext_ractor_safe(true); +#endif VALUE module = rb_define_module("Bug"); module = rb_define_module_under(module, "RbCallSuperKw"); rb_define_method(module, "m", rb_call_super_kw_m, -1); diff --git a/ext/-test-/stack/stack.c b/ext/-test-/stack/stack.c index 8ff32f9737b218..30348cc22d47e0 100644 --- a/ext/-test-/stack/stack.c +++ b/ext/-test-/stack/stack.c @@ -30,6 +30,9 @@ asan_p(VALUE klass) void Init_stack(VALUE klass) { +#ifdef HAVE_RB_EXT_RACTOR_SAFE + RB_EXT_RACTOR_SAFE(true); +#endif rb_define_singleton_method(rb_cThread, "alloca_overflow", stack_alloca_overflow, 0); rb_define_singleton_method(rb_cThread, "asan?", asan_p, 0); } diff --git a/test/.excludes-ractor/Prism/TestCompilePrism.rb b/test/.excludes-ractor/Prism/TestCompilePrism.rb new file mode 100644 index 00000000000000..db5ba693cf8984 --- /dev/null +++ b/test/.excludes-ractor/Prism/TestCompilePrism.rb @@ -0,0 +1,27 @@ +# Prism::TestCompilePrism#test_repeated_proc_params../ruby/st.c:2621: Assertion Failed: set_rebuild_table_with:new_tab->num_entries == tab->num_entries +exclude(/^test_/, "FIXME: bug. Skip all tests for now") + +exclude(/^test_GlobalVariable/, "global variables") +exclude(:test_ClassVariableTargetNode, "class variables") +exclude(:test_ClassVariableAndWriteNode, "class variables") +exclude(:test_ClassVariableOperatorWriteNode, "class variables") +exclude(:test_ClassVariableOrWriteNode, "class variables") +exclude(:test_ClassVariableWriteNode, "class variables") +exclude(:test_ConstantWriteNode, "global side effects") +exclude(:test_InstanceVariableTargetNode, "class ivars") +exclude(:test_InstanceVariableReadNode, "class ivars") +exclude(:test_InstanceVariableWriteNode, "class ivars") +exclude(:test_EmbeddedVariableNode, "class ivars") +exclude(:test_InterpolatedMatchLastLineNode, "global variables") +exclude(:test_InterpolatedRegularExpressionNode, "global variables") +exclude(:test_InterpolatedStringNode, "global variables") +exclude(:test_InterpolatedSymbolNode, "global variables") +exclude(:test_AliasGlobalVariableNode, "global variables") +exclude(:test_ClassVariableReadNode, "class variables") +exclude(:test_ForNode_gvar, "gvars") +exclude(:test_PostExecutionNode_set_ivar, "set ivar of main") +exclude(:test_PinnedVariableNode_ractor_unsafe, "ractor incompatible") +exclude(:test_DefinedNode_ractor_unsafe, "ractor incompatible") +exclude(:test_ConstantPathOrWriteNode_ractor_unsafe, "ractor incompatible") +exclude(:test_ConstantPathOperatorWriteNode_ractor_unsafe, "ractor incompatible") +exclude(:test_IfNode_ractor_unsafe, "gvars") diff --git a/test/.excludes-ractor/RubyVM.rb b/test/.excludes-ractor/RubyVM.rb new file mode 100644 index 00000000000000..386a982dc79af6 --- /dev/null +++ b/test/.excludes-ractor/RubyVM.rb @@ -0,0 +1,2 @@ +exclude(:test_of_proc_and_method, "ractor incompatible") +exclude(:test_parse_file_raises_syntax_error, "ractor incompatible") diff --git a/test/.excludes-ractor/TestAlias.rb b/test/.excludes-ractor/TestAlias.rb new file mode 100644 index 00000000000000..95fe32e4d658bb --- /dev/null +++ b/test/.excludes-ractor/TestAlias.rb @@ -0,0 +1,2 @@ +exclude(:test_alias_class_method_added, "class ivars") +exclude(:test_alias_module_method_added, "module ivars") diff --git a/test/.excludes-ractor/TestAllocation.rb b/test/.excludes-ractor/TestAllocation.rb new file mode 100644 index 00000000000000..c909538bff7ad8 --- /dev/null +++ b/test/.excludes-ractor/TestAllocation.rb @@ -0,0 +1 @@ +exclude(/^test_/, "checks allocations in subprocess") diff --git a/test/.excludes-ractor/TestArgf.rb b/test/.excludes-ractor/TestArgf.rb new file mode 100644 index 00000000000000..fea05b4fd73c53 --- /dev/null +++ b/test/.excludes-ractor/TestArgf.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ARGF is not shareable") diff --git a/test/.excludes-ractor/TestArray.rb b/test/.excludes-ractor/TestArray.rb new file mode 100644 index 00000000000000..d33c7113e56976 --- /dev/null +++ b/test/.excludes-ractor/TestArray.rb @@ -0,0 +1,9 @@ +exclude(/_with_callcc$/, "ractor incompatible") +exclude(:test_join, "Uses $,") +exclude(:test_to_s, "Uses $,") +exclude(:test_equal_resize, "global variables") +exclude(:test_replace_wb_variable_width_alloc, "GC.verify_internal_consistency") +exclude(:test_concat_under_gc_stress, "EnvUtil.under_gc_stress") +exclude(:test_product_under_gc_stress, "EnvUtil.under_gc_stress") +exclude(:test_to_h_single_element_with_object, "global side effects") +exclude(:test_to_h_block_single_element_with_object, "global side effects") diff --git a/test/.excludes-ractor/TestArraySubclass.rb b/test/.excludes-ractor/TestArraySubclass.rb new file mode 100644 index 00000000000000..54dd8ae3c1d282 --- /dev/null +++ b/test/.excludes-ractor/TestArraySubclass.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../TestArray.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestAssignment.rb b/test/.excludes-ractor/TestAssignment.rb new file mode 100644 index 00000000000000..14d2c64ee264b6 --- /dev/null +++ b/test/.excludes-ractor/TestAssignment.rb @@ -0,0 +1,5 @@ +exclude(:test_assignment, "ractor incompatible") +exclude(:test_massign_const_order, "ractor incompatible") +exclude(:test_next, "ractor incompatible") +exclude(:test_return, "ractor incompatible") +exclude(:test_yield, "ractor incompatible") diff --git a/test/.excludes-ractor/TestAssignmentGen.rb b/test/.excludes-ractor/TestAssignmentGen.rb new file mode 100644 index 00000000000000..15e177545c7117 --- /dev/null +++ b/test/.excludes-ractor/TestAssignmentGen.rb @@ -0,0 +1 @@ +exclude(:test_assignment, "ractor incompatible") diff --git a/test/.excludes-ractor/TestAst.rb b/test/.excludes-ractor/TestAst.rb new file mode 100644 index 00000000000000..3c589ee653c966 --- /dev/null +++ b/test/.excludes-ractor/TestAst.rb @@ -0,0 +1,2 @@ +exclude(:test_parse_file_raises_syntax_error, "Tempfile") +exclude(:test_of_proc_and_method, "Tempfile") diff --git a/test/.excludes-ractor/TestAutoload.rb b/test/.excludes-ractor/TestAutoload.rb new file mode 100644 index 00000000000000..08db55ced48929 --- /dev/null +++ b/test/.excludes-ractor/TestAutoload.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ractor incompatible") diff --git a/test/.excludes-ractor/TestBacktrace.rb b/test/.excludes-ractor/TestBacktrace.rb new file mode 100644 index 00000000000000..421722c740035e --- /dev/null +++ b/test/.excludes-ractor/TestBacktrace.rb @@ -0,0 +1,3 @@ +exclude(:test_caller_locations_absolute_path, "ractor incompatible") +exclude(:test_caller_locations_lineno, "ractor incompatible") +exclude(:test_caller_locations_path, "ractor incompatible") diff --git a/test/.excludes-ractor/TestBasicInstructions.rb b/test/.excludes-ractor/TestBasicInstructions.rb new file mode 100644 index 00000000000000..cfa070aa5ab992 --- /dev/null +++ b/test/.excludes-ractor/TestBasicInstructions.rb @@ -0,0 +1 @@ +exclude(/^test_.*/, "Lots of unfrozen strings") diff --git a/test/.excludes-ractor/TestBeginEndBlock.rb b/test/.excludes-ractor/TestBeginEndBlock.rb new file mode 100644 index 00000000000000..852c72ca7ae5fd --- /dev/null +++ b/test/.excludes-ractor/TestBeginEndBlock.rb @@ -0,0 +1,2 @@ +exclude(:test_internal_errinfo_at_exit, "uses fork") +exclude(:test_rescue_at_exit, "uses subprocess") diff --git a/test/.excludes-ractor/TestBignum.rb b/test/.excludes-ractor/TestBignum.rb new file mode 100644 index 00000000000000..d76133ae0c4a77 --- /dev/null +++ b/test/.excludes-ractor/TestBignum.rb @@ -0,0 +1,2 @@ +exclude(:test_bignum, "global variables") +exclude(:test_interrupt_during_bigdivrem, "Signal.trap proc accesses outers") diff --git a/test/.excludes-ractor/TestCaseFold.rb b/test/.excludes-ractor/TestCaseFold.rb new file mode 100644 index 00000000000000..44d435c2afa12d --- /dev/null +++ b/test/.excludes-ractor/TestCaseFold.rb @@ -0,0 +1 @@ +exclude(/^test_/, "TODO: tests use define_method") diff --git a/test/.excludes-ractor/TestClass.rb b/test/.excludes-ractor/TestClass.rb new file mode 100644 index 00000000000000..429c0b86a5fa6c --- /dev/null +++ b/test/.excludes-ractor/TestClass.rb @@ -0,0 +1,5 @@ +exclude(:test_s_inherited, "class variables") +exclude(:test_singleton_class_should_has_own_namespace, "global variables") +exclude(:test_nonascii_name, "global side effects") +exclude(:test_check_inheritable_break_with_object, "global side effects") +exclude(/^test_subclass_gc/, "Takes long time") diff --git a/test/.excludes-ractor/TestConst.rb b/test/.excludes-ractor/TestConst.rb new file mode 100644 index 00000000000000..2de3826e167ffc --- /dev/null +++ b/test/.excludes-ractor/TestConst.rb @@ -0,0 +1 @@ +exclude(:test_const, "ractor incompatible") diff --git a/test/.excludes-ractor/TestDefaultGems.rb b/test/.excludes-ractor/TestDefaultGems.rb new file mode 100644 index 00000000000000..0f1407c7d4e4fb --- /dev/null +++ b/test/.excludes-ractor/TestDefaultGems.rb @@ -0,0 +1 @@ +exclude(:test_validate_gemspec, "loading gemspecs accesses load path") diff --git a/test/.excludes-ractor/TestDefined.rb b/test/.excludes-ractor/TestDefined.rb new file mode 100644 index 00000000000000..78596077ca2030 --- /dev/null +++ b/test/.excludes-ractor/TestDefined.rb @@ -0,0 +1,4 @@ +exclude(:test_autoloaded_noload, "accesses load path") +exclude(:test_defined_global_variable, "ractor incompatible") +exclude(:test_super_in_basic_object, "ractor incompatible") +exclude(:test_respond_to, "Uses Warning[]=") diff --git a/test/.excludes-ractor/TestDir.rb b/test/.excludes-ractor/TestDir.rb new file mode 100644 index 00000000000000..6ac61d494140b9 --- /dev/null +++ b/test/.excludes-ractor/TestDir.rb @@ -0,0 +1 @@ +exclude(/^test_/, "Lots of Dir.chdir") diff --git a/test/.excludes-ractor/TestDir_M17N.rb b/test/.excludes-ractor/TestDir_M17N.rb new file mode 100644 index 00000000000000..a37caed1195c44 --- /dev/null +++ b/test/.excludes-ractor/TestDir_M17N.rb @@ -0,0 +1 @@ +exclude(/^test_.*/, "Lots of Dir.chdir") diff --git a/test/.excludes-ractor/TestEnumerable.rb b/test/.excludes-ractor/TestEnumerable.rb new file mode 100644 index 00000000000000..b83f9c7a5f57f9 --- /dev/null +++ b/test/.excludes-ractor/TestEnumerable.rb @@ -0,0 +1 @@ +exclude(:test_zip_ractor_unsafe, "global side effects") diff --git a/test/.excludes-ractor/TestEnumerator.rb b/test/.excludes-ractor/TestEnumerator.rb new file mode 100644 index 00000000000000..d0d3e78c824db6 --- /dev/null +++ b/test/.excludes-ractor/TestEnumerator.rb @@ -0,0 +1 @@ +exclude(/under_gc_compact_stress/, "EnvUtil.under_gc_compact_stress") diff --git a/test/.excludes-ractor/TestEnv.rb b/test/.excludes-ractor/TestEnv.rb new file mode 100644 index 00000000000000..08db55ced48929 --- /dev/null +++ b/test/.excludes-ractor/TestEnv.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ractor incompatible") diff --git a/test/.excludes-ractor/TestEval.rb b/test/.excludes-ractor/TestEval.rb new file mode 100644 index 00000000000000..413e42f424a981 --- /dev/null +++ b/test/.excludes-ractor/TestEval.rb @@ -0,0 +1,16 @@ +exclude(:test_cvar_scope_with_instance_eval, "ractor incompatible") +exclude(:test_define_method_toplevel, "ractor incompatible") +exclude(:test_eval_with_toplevel_binding, "ractor incompatible") +exclude(:test_fixnum_instance_eval_cvar, "ractor incompatible") +exclude(:test_instance_eval_cvar, "ractor incompatible") +exclude(:test_instance_exec_cvar, "ractor incompatible") +exclude(:test_instance_eval_on_argf_singleton_class, "ractor incompatible") +exclude(:test_nil_instance_eval_cvar, "ractor incompatible") +exclude(:test_eval_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_eval_binding_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_module_eval_string_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_module_eval_block_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_instance_eval_string_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_instance_eval_block_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_instance_exec_block_basic_ractor_unsafe, "cvars and gvars") +exclude(:test_eval_orig_ractor_unsafe, "global side effects") diff --git a/test/.excludes-ractor/TestException.rb b/test/.excludes-ractor/TestException.rb new file mode 100644 index 00000000000000..ada2151ce60474 --- /dev/null +++ b/test/.excludes-ractor/TestException.rb @@ -0,0 +1,11 @@ +exclude(:test_kernel_warn_uplevel, "uses capture_warning_warn") +exclude(:test_thread_signal_location, "subprocess") +exclude(:test_warn_deprecated_backwards_compatibility_category, "ractor incompatible") +exclude(:test_warning_warn, "ractor incompatible") +exclude(:test_warning_warn_circular_require_backtrace, "ractor incompatible") +exclude(:test_warning_category, "global side effects") +exclude(:test_type_error_message_encoding, "global side effects") +exclude(:test_too_many_args_in_eval, "TODO: freezes process") +exclude(:test_catch_throw_in_require, "Tempfile") +exclude(:test_catch_throw_in_require_cant_be_rescued, "Tempfile") +exclude(:test_detailed_message_under_gc_compact_stress, "EnvUtil.under_gc_compact_stress") diff --git a/test/.excludes-ractor/TestFiber.rb b/test/.excludes-ractor/TestFiber.rb new file mode 100644 index 00000000000000..714aade300f67a --- /dev/null +++ b/test/.excludes-ractor/TestFiber.rb @@ -0,0 +1 @@ +exclude(:test_fork_from_fiber, "fork") diff --git a/test/.excludes-ractor/TestFile.rb b/test/.excludes-ractor/TestFile.rb new file mode 100644 index 00000000000000..9c2c67f8063eb2 --- /dev/null +++ b/test/.excludes-ractor/TestFile.rb @@ -0,0 +1,18 @@ +exclude(:test_chown, "Tempfile") +exclude(:test_each_byte_extended_file, "Tempfile") +exclude(:test_empty_file_bom, "Tempfile") +exclude(:test_getbyte_extended_file, "Tempfile") +exclude(:test_gets_extended_file, "Tempfile") +exclude(:test_gets_para_extended_file, "Tempfile") +exclude(:test_read_all_extended_file, "Tempfile") +exclude(:test_realdirpath_junction, "Dir.chdir") +exclude(:test_realpath_ractor_unsafe, "Dir.chdir") +exclude(:test_realdirpath_ractor_unsafe, "Dir.chdir") +exclude(:test_stat, "Tempfile") +exclude(:test_truncate_beyond_eof, "Tempfile") +exclude(:test_truncate_rbuf, "Tempfile") +exclude(:test_truncate_size, "Tempfile") +exclude(:test_truncate_wbuf, "Tempfile") +exclude(:test_utime, "Tempfile") +exclude(/^test_bom/, "Tempfile") +# done diff --git a/test/.excludes-ractor/TestFileExhaustive.rb b/test/.excludes-ractor/TestFileExhaustive.rb new file mode 100644 index 00000000000000..78a27ad584a60b --- /dev/null +++ b/test/.excludes-ractor/TestFileExhaustive.rb @@ -0,0 +1,14 @@ +exclude(:test_expand_path_compose, "Dir.chdir") +exclude(:test_expand_path_home, "global side effects") +exclude(:test_expand_path_home_dir_string, "global side effects") +exclude(:test_expand_path_memsize, "ObjectSpace.memsize_of") +exclude(:test_expand_path_encoding_filesystem, "global side effects") +exclude(:test_expand_path_error_for_non_absolute_home, "global side effects") +exclude(:test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_home_as_base, "global side effects") +exclude(:test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_unc_home, "global side effects") +exclude(:test_expand_path_does_not_modify_a_home_string_argument, "global side effects") +exclude(:test_flock_exclusive, "subprocess") +exclude(:test_flock_shared, "subprocess") +exclude(:test_stat_dotted_prefix_ractor_unsafe, "Dir.chdir") +exclude(:test_utime_ractor_unsafe, "Dir.chdir") +exclude(:test_umask, "global side effects") diff --git a/test/.excludes-ractor/TestFlip.rb b/test/.excludes-ractor/TestFlip.rb new file mode 100644 index 00000000000000..900d90453e2122 --- /dev/null +++ b/test/.excludes-ractor/TestFlip.rb @@ -0,0 +1 @@ +exclude(:test_input_line_number_range, "Accesses $.") diff --git a/test/.excludes-ractor/TestFloat.rb b/test/.excludes-ractor/TestFloat.rb new file mode 100644 index 00000000000000..cf86ac5c615558 --- /dev/null +++ b/test/.excludes-ractor/TestFloat.rb @@ -0,0 +1 @@ +exclude(:test_invalid_str, "EnvUtil.under_gc_stress") diff --git a/test/.excludes-ractor/TestGCCompact.rb b/test/.excludes-ractor/TestGCCompact.rb new file mode 100644 index 00000000000000..d52a1e84383af0 --- /dev/null +++ b/test/.excludes-ractor/TestGCCompact.rb @@ -0,0 +1 @@ +exclude(/^test_/, "GC.auto_compact = true then setting it back is racy") diff --git a/test/.excludes-ractor/TestGc.rb b/test/.excludes-ractor/TestGc.rb new file mode 100644 index 00000000000000..82f4e17304be07 --- /dev/null +++ b/test/.excludes-ractor/TestGc.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ractor incompatible") diff --git a/test/.excludes-ractor/TestHash.rb b/test/.excludes-ractor/TestHash.rb new file mode 100644 index 00000000000000..1fbc8a63de61cb --- /dev/null +++ b/test/.excludes-ractor/TestHash.rb @@ -0,0 +1,7 @@ +exclude(:test_AREF_fstring_key, "EnvUtil.without_gc") +exclude(:test_iterlevel_in_ivar_bug19589, "stack level too deep with ractors") +exclude(:test_memory_size_after_delete, "ObjectSpace.memsize_of not yet ractor safe") +exclude(:test_to_s, "global variables") +exclude(:test_inspect, "global side effects") +exclude(:test_broken_hash_value, "too many objects") +exclude(:test_replace_bug15358, "GC.start") diff --git a/test/.excludes-ractor/TestHash/TestSubHash.rb b/test/.excludes-ractor/TestHash/TestSubHash.rb new file mode 100644 index 00000000000000..61f25181f1f9e3 --- /dev/null +++ b/test/.excludes-ractor/TestHash/TestSubHash.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestHash.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestHashOnly.rb b/test/.excludes-ractor/TestHashOnly.rb new file mode 100644 index 00000000000000..814ac65d772773 --- /dev/null +++ b/test/.excludes-ractor/TestHashOnly.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../TestHash.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestIO.rb b/test/.excludes-ractor/TestIO.rb new file mode 100644 index 00000000000000..c489665ed681c5 --- /dev/null +++ b/test/.excludes-ractor/TestIO.rb @@ -0,0 +1,27 @@ +exclude(:test_binmode_pipe, "global side effects") +exclude(:test_advise_pipe, "ractor incompatible") +exclude(:test_autoclose_false_closed_by_finalizer, "Tempfile") +exclude(:test_autoclose_true_closed_by_finalizer, "Tempfile") +exclude(:test_autoclose, "TODO: assert_raise buggy") +exclude(:test_copy_stream_dup_buffer, "Tempfile") +exclude(:test_copy_stream_no_busy_wait, "unpredictable") +exclude(:test_copy_stream_socket7, "fork") +exclude(:test_copy_stream_strio_to_tempfile, "Tempfile") +exclude(:test_fcntl_dupfd, "Tempfile") +exclude(:test_fcntl_lock_freebsd, "Tempfile + fork") +exclude(:test_fcntl_lock_linux, "Tempfile + fork") +exclude(:test_flush_in_finalizer1, "Tempfile + ObjectSpace.each_object") +exclude(:test_flush_in_finalizer2, "Tempfile + ObjectSpace.each_object") +exclude(:test_pid_after_close_read, "TODO: buggy") +exclude(:test_print_separators, "global variable access") +exclude(:test_race_between_read, "Tempfile") +exclude(:test_readline_limit_without_separator, "Tempfile") +exclude(:test_readline_separators_limits, "Tempfile") +exclude(:test_set_lineno_gets, "global variable access") +exclude(:test_set_lineno_readline, "global variable access") +exclude(:test_threaded_flush, "subprocesses") +exclude(:test_try_convert, "uses STDOUT") +exclude(:test_close_read_write_separately, "TODO: buggy with ractors") +exclude(:test_exception_at_close, "TODO: assert_raise sometimes fails under multiple ractors") +exclude(:test_std_fileno_ractor_unsafe, "unshareable objects") +exclude(/buffer_not_raise_shared_string_error/, "Tempfile") diff --git a/test/.excludes-ractor/TestIOBuffer.rb b/test/.excludes-ractor/TestIOBuffer.rb new file mode 100644 index 00000000000000..885edb1fa50269 --- /dev/null +++ b/test/.excludes-ractor/TestIOBuffer.rb @@ -0,0 +1,13 @@ +exclude(:test_bug_21210, "GC.verify_compaction_references") +exclude(:test_read, "Tempfile") +exclude(:test_read_with_with_length, "Tempfile") +exclude(:test_read_with_with_offset, "Tempfile") +exclude(:test_read_with_length_and_offset, "Tempfile") +exclude(:test_pread, "Tempfile") +exclude(:test_pread_offset, "Tempfile") +exclude(:test_private, "Tempfile") +exclude(:test_pwrite, "Tempfile") +exclude(:test_pwrite_offset, "Tempfile") +exclude(:test_shared, "fork") +exclude(:test_write, "Tempfile") +exclude(:test_write_with_length_and_offset, "Tempfile") diff --git a/test/.excludes-ractor/TestIO_M17N.rb b/test/.excludes-ractor/TestIO_M17N.rb new file mode 100644 index 00000000000000..1c2af0646a8596 --- /dev/null +++ b/test/.excludes-ractor/TestIO_M17N.rb @@ -0,0 +1,2 @@ +exclude(:test_each_codepoint_need_more, "Tempfile") +exclude(:test_pipe_terminator_conversion, "Timeout.timeout") diff --git a/test/.excludes-ractor/TestISeq.rb b/test/.excludes-ractor/TestISeq.rb new file mode 100644 index 00000000000000..41407edfcc09ae --- /dev/null +++ b/test/.excludes-ractor/TestISeq.rb @@ -0,0 +1 @@ +exclude(/^test_/, "not ractor safe") diff --git a/test/.excludes-ractor/TestInteger.rb b/test/.excludes-ractor/TestInteger.rb new file mode 100644 index 00000000000000..5bf548f3773177 --- /dev/null +++ b/test/.excludes-ractor/TestInteger.rb @@ -0,0 +1 @@ +exclude(:test_aref, "Takes too long") diff --git a/test/.excludes-ractor/TestKeyword.rb b/test/.excludes-ractor/TestKeyword.rb new file mode 100644 index 00000000000000..3a7212149dd185 --- /dev/null +++ b/test/.excludes-ractor/TestKeyword.rb @@ -0,0 +1 @@ +exclude(:test_Thread_new_kwsplat, "global side effects") diff --git a/test/.excludes-ractor/TestM17N.rb b/test/.excludes-ractor/TestM17N.rb new file mode 100644 index 00000000000000..2b3109969e3b92 --- /dev/null +++ b/test/.excludes-ractor/TestM17N.rb @@ -0,0 +1,2 @@ +exclude(:test_string_inspect_encoding, "global side effects") +exclude(:test_object_utf16_32_inspect, "global side effects") diff --git a/test/.excludes-ractor/TestMarshal.rb b/test/.excludes-ractor/TestMarshal.rb new file mode 100644 index 00000000000000..bff69b03e9d0c8 --- /dev/null +++ b/test/.excludes-ractor/TestMarshal.rb @@ -0,0 +1,6 @@ +exclude(:test_context_switch, "class variables") +exclude(:test_struct_invalid_members, "global side effects") +exclude(:test_change_class_name, "global side effects") +exclude(:test_change_struct, "global side effects") +exclude(:test_continuation, "callcc") +exclude(:test_singleton, "class ivar") diff --git a/test/.excludes-ractor/TestMarshal/TestMarshalFreeze.rb b/test/.excludes-ractor/TestMarshal/TestMarshalFreeze.rb new file mode 100644 index 00000000000000..e69a996966caf6 --- /dev/null +++ b/test/.excludes-ractor/TestMarshal/TestMarshalFreeze.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestMarshal.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestMarshal/TestMarshalFreezeProc.rb b/test/.excludes-ractor/TestMarshal/TestMarshalFreezeProc.rb new file mode 100644 index 00000000000000..e69a996966caf6 --- /dev/null +++ b/test/.excludes-ractor/TestMarshal/TestMarshalFreezeProc.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestMarshal.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestMath.rb b/test/.excludes-ractor/TestMath.rb new file mode 100644 index 00000000000000..111e0a9521fc7f --- /dev/null +++ b/test/.excludes-ractor/TestMath.rb @@ -0,0 +1,3 @@ +exclude(:test_override_bignum_to_f, "global side effects") +exclude(:test_override_integer_to_f, "global side effects") +exclude(:test_override_rational_to_f, "global side effects") diff --git a/test/.excludes-ractor/TestMetaclass.rb b/test/.excludes-ractor/TestMetaclass.rb new file mode 100644 index 00000000000000..76901928c7ab50 --- /dev/null +++ b/test/.excludes-ractor/TestMetaclass.rb @@ -0,0 +1 @@ +exclude(/^test_/, "global side effects") diff --git a/test/.excludes-ractor/TestMethod.rb b/test/.excludes-ractor/TestMethod.rb new file mode 100644 index 00000000000000..2d1a1e18a071cd --- /dev/null +++ b/test/.excludes-ractor/TestMethod.rb @@ -0,0 +1,5 @@ +exclude(:test_unbind, "global side effects") +exclude(:test_define_method_in_private_scope, "TOPLEVEL_BINDING") +exclude(:test_method_list, "ObjectSpace.each_object") +exclude(:test_singleton_define_method_in_private_scope, "TOPLEVEL_BINDING") +exclude(:test_method_in_method_visibility_should_be_public, "global side effects") diff --git a/test/.excludes-ractor/TestModule.rb b/test/.excludes-ractor/TestModule.rb new file mode 100644 index 00000000000000..c6365784935f0e --- /dev/null +++ b/test/.excludes-ractor/TestModule.rb @@ -0,0 +1,29 @@ +exclude(:test_attr_obsoleted_flag, "class ivars") +exclude(:test_attr_public_at_toplevel, "TOPLEVEL_BINDING") +exclude(:test_class_eval, "class variable access") +exclude(:test_class_variable_defined, "class vars") +exclude(:test_class_variable_get, "class vars") +exclude(:test_class_variable_in_dup_class, "class vars") +exclude(:test_class_variable_set, "class vars") +exclude(:test_class_variables, "class vars") +exclude(:test_remove_class_variable, "class vars") +exclude(:test_const_added, "class ivars") +exclude(:test_const_defined_invalid_symbol_name, "EnvUtil.under_gc_stress") +exclude(:test_const_set, "class ivars") +exclude(:test_extend_module_with_protected_method, "class ivars") +exclude(:test_initialize_copy_empty, "class ivars") +exclude(:test_module_subclass_initialize, "class ivars") +exclude(:test_private_constant_const_missing, "class ivars") +exclude(:test_private_constant_reopen, "TOPLEVEL_BINDING") +exclude(:test_s_constants, "global side effects") +exclude(:test_s_nesting, "global variable access") +exclude(:test_uninitialized_attr_class, "class ivars") +exclude(:test_uninitialized_toplevel_constant, "TOPLEVEL_BINDING") +exclude(:test_nested_get, "global side effects") +exclude(:test_nested_get_symbol, "global side effects") +exclude(:test_nested_defined, "global side effects") +exclude(:test_nested_defined_symbol, "global side effects") +exclude(:test_deprecate_constant, "Warning[]=") +exclude(:test_dup, "global side effects") +exclude(:test_module_exec, "global side effects") +exclude(:test_module_eval, "global side effects") diff --git a/test/.excludes-ractor/TestNamespace.rb b/test/.excludes-ractor/TestNamespace.rb new file mode 100644 index 00000000000000..1656f5a70a0767 --- /dev/null +++ b/test/.excludes-ractor/TestNamespace.rb @@ -0,0 +1 @@ +exclude(:test_global_variables, "global variable access") diff --git a/test/.excludes-ractor/TestObjectSpace.rb b/test/.excludes-ractor/TestObjectSpace.rb new file mode 100644 index 00000000000000..e98428547f1928 --- /dev/null +++ b/test/.excludes-ractor/TestObjectSpace.rb @@ -0,0 +1 @@ +exclude(/^test_/, "TODO: should be safe when ractor-local GC lands") diff --git a/test/.excludes-ractor/TestParse.rb b/test/.excludes-ractor/TestParse.rb new file mode 100644 index 00000000000000..f9b0b7b979cd89 --- /dev/null +++ b/test/.excludes-ractor/TestParse.rb @@ -0,0 +1,6 @@ +exclude(:test_dstr, "class vars") +exclude(:test_global_variable, "global variable access") +exclude(:test_mlhs_node, "class ivars") +exclude(:test_shareable_constant_value_nested, "can't access unshareables") +exclude(:test_shareable_constant_value_simple, "can't access unshareables") +exclude(:test_parsing_begin_statement_inside_method_definition, "global side effects") diff --git a/test/.excludes-ractor/TestPatternMatching.rb b/test/.excludes-ractor/TestPatternMatching.rb new file mode 100644 index 00000000000000..5bff39721ce503 --- /dev/null +++ b/test/.excludes-ractor/TestPatternMatching.rb @@ -0,0 +1,4 @@ +exclude(:test_deconstruct_keys, "class ivars") +exclude(:test_hash_pattern, "class ivars") +exclude(:test_hash_pattern, "class ivars") +exclude(:test_pin_operator_value_pattern_ractor_unsafe, "cvars and gvars") diff --git a/test/.excludes-ractor/TestProcess.rb b/test/.excludes-ractor/TestProcess.rb new file mode 100644 index 00000000000000..3a0fa4db13858f --- /dev/null +++ b/test/.excludes-ractor/TestProcess.rb @@ -0,0 +1,26 @@ +exclude(:test__fork, "fork") +exclude(:test__fork_pid_cache, "fork") +exclude(:test_argv0_frozen, "gvars") +exclude(:test_concurrent_group_and_pid_wait, "Timeout.timeout") +exclude(:test_daemon_default, "fork") +exclude(:test_daemon_detached, "fork") +exclude(:test_daemon_no_threads, "fork") +exclude(:test_daemon_nochdir_noclose, "fork") +exclude(:test_daemon_noclose, "fork") +exclude(:test_daemon_pid, "fork") +exclude(:test_daemon_readwrite, "fork") +exclude(/test_execopts_env_popen/, "ENV changes") +exclude(:test_execopts_redirect_tempfile, "Tempfile") +exclude(:test_popen_fork, "fork") +exclude(:test_popen_fork_ensure, "fork") +exclude(:test_execopts_env_popen, "ENV changes") +exclude(:test_execopts_unsetenv_others, "ENV changes") +exclude(:test_process_detach, "fork") +exclude(:test_seteuid_name, "unsafe Etc method") +exclude(:test_signals_work_after_exec_fail, "Timeout.timeout") +exclude(:test_system_sigpipe, "Timeout.timeout") +exclude(:test_threading_works_after_exec_fail, "Timeout.timeout") +exclude(:test_to_hash_on_arguments, "separate processes") +exclude(:test_waitall, "global side effects") +exclude(:test_uid_from_name_ractor_unsafe, "Etc.getpwuid") +exclude(:test_gid_from_name_ractor_unsafe, "Etc.getgrgid") diff --git a/test/.excludes-ractor/TestRand.rb b/test/.excludes-ractor/TestRand.rb new file mode 100644 index 00000000000000..1f1255119f8e67 --- /dev/null +++ b/test/.excludes-ractor/TestRand.rb @@ -0,0 +1,2 @@ +exclude(:test_fork_shuffle, "fork") +exclude(:test_rand_reseed_on_fork, "fork") diff --git a/test/.excludes-ractor/TestRange.rb b/test/.excludes-ractor/TestRange.rb new file mode 100644 index 00000000000000..e79eac1fd2fc43 --- /dev/null +++ b/test/.excludes-ractor/TestRange.rb @@ -0,0 +1,3 @@ +exclude(:test_comparison_when_recursive, "Timeout.timeout") +exclude(:test_range_bsearch_for_floats, "Can take a long time") +exclude(:test_bsearch_typechecks_return_values, "global side effects") diff --git a/test/.excludes-ractor/TestReadPartial.rb b/test/.excludes-ractor/TestReadPartial.rb new file mode 100644 index 00000000000000..7eda414cc2ea18 --- /dev/null +++ b/test/.excludes-ractor/TestReadPartial.rb @@ -0,0 +1,2 @@ +exclude(:test_open_pipe, "Timeout.timeout") +exclude(:test_with_stdio, "Timeout.timeout") diff --git a/test/.excludes-ractor/TestRefinement.rb b/test/.excludes-ractor/TestRefinement.rb new file mode 100644 index 00000000000000..6f626a6d82e254 --- /dev/null +++ b/test/.excludes-ractor/TestRefinement.rb @@ -0,0 +1 @@ +exclude(/^test_/, "TODO: make it work reliably, create new sandbox binding for each test") diff --git a/test/.excludes-ractor/TestRegexp.rb b/test/.excludes-ractor/TestRegexp.rb new file mode 100644 index 00000000000000..e3dda20316700c --- /dev/null +++ b/test/.excludes-ractor/TestRegexp.rb @@ -0,0 +1,2 @@ +exclude(:test_getter, "gvars") +exclude(:test_ignorecase, "gvars") diff --git a/test/.excludes-ractor/TestRequire.rb b/test/.excludes-ractor/TestRequire.rb new file mode 100644 index 00000000000000..c48871b94572ba --- /dev/null +++ b/test/.excludes-ractor/TestRequire.rb @@ -0,0 +1,39 @@ +exclude(:test_load, "Tempfile") +exclude(:test_load_error_path, "Tempfile") +exclude(:test_load_into_module, "Tempfile") +exclude(:test_load_scope, "Tempfile") +exclude(:test_loaded_features_encoding, "gvars") +exclude(:test_loading_fifo_fd_leak, "Tempfile") +exclude(:test_loading_fifo_threading_raise, "Tempfile") +exclude(:test_loading_fifo_threading_success, "Tempfile") +exclude(:test_private_in_wrapped_load, "Tempfile") +exclude(:test_provide_in_required_file, "gvars") +exclude(:test_public_in_wrapped_load, "Tempfile") +exclude(:test_race_exception, "Tempfile") +exclude(:test_relative, "gvars") +exclude(:test_relative_symlink, "Dir.chdir") +exclude(:test_relative_symlink_realpath, "Dir.chdir") +exclude(:test_require_invalid_shared_object, "Tempfile") +exclude(:test_require_twice, "ractor incompatible") +exclude(:test_require_with_loaded_features_pop, "Tempfile") +exclude(:test_require_with_unc, "Tempfile") +exclude(:test_resolve_feature_path, "gvars") +exclude(:test_resolve_feature_path_with_missing_feature, "$LOAD_PATH") +exclude(:test_require_nonascii_path, "$LOAD_PATH") +exclude(:test_require_nonascii_path_utf8, "$LOAD_PATH") +exclude(:test_require_nonascii_path_shift_jis, "$LOAD_PATH") +exclude(:test_throw_while_loading, "Tempfile") +exclude(:test_require_path_home_1, "global side effects") +exclude(:test_require_path_home_2, "global side effects") +exclude(:test_require_path_home_3, "global side effects") +exclude(:test_require_syntax_error, "$LOADED_FEATURES") +exclude(:test_require_syntax_error_rescued, "$LOADED_FEATURES") +exclude(:test_load_syntax_error, "$LOADED_FEATURES") +exclude(:test_require_changed_current_dir, "Dir.chdir") +exclude(:test_require_changed_home, "Dir.chdir") +exclude(:test_require_to_path_redefined_in_load_path, "Dir.chdir") +exclude(:test_require_not_modified_load_path, "Dir.chdir") +exclude(:test_require_to_str_redefined_in_load_path, "Dir.chdir") +exclude(:test_require_with_array_pop, "Dir.chdir") +exclude(:test_require_with_array_shift, "Dir.chdir") +exclude(:test_require_local_var_on_toplevel, "Dir.chdir") diff --git a/test/.excludes-ractor/TestRequireLib.rb b/test/.excludes-ractor/TestRequireLib.rb new file mode 100644 index 00000000000000..9bf6ed2b493b25 --- /dev/null +++ b/test/.excludes-ractor/TestRequireLib.rb @@ -0,0 +1 @@ +exclude(/^test_/, "subprocesses") diff --git a/test/.excludes-ractor/TestRubyOptimization.rb b/test/.excludes-ractor/TestRubyOptimization.rb new file mode 100644 index 00000000000000..f712b76476ba54 --- /dev/null +++ b/test/.excludes-ractor/TestRubyOptimization.rb @@ -0,0 +1,3 @@ +exclude(:test_string_freeze_saves_memory, "ObjectSpace.memsize_of") +exclude(:test_opt_new, "RubyVM::Iseq.compile/eval not working across multiple ractors") +exclude(/tailcall/, "ractor incompatible") diff --git a/test/.excludes-ractor/TestRubyOptions.rb b/test/.excludes-ractor/TestRubyOptions.rb new file mode 100644 index 00000000000000..9bf6ed2b493b25 --- /dev/null +++ b/test/.excludes-ractor/TestRubyOptions.rb @@ -0,0 +1 @@ +exclude(/^test_/, "subprocesses") diff --git a/test/.excludes-ractor/TestRubyPrimitive.rb b/test/.excludes-ractor/TestRubyPrimitive.rb new file mode 100644 index 00000000000000..094c3bc016d635 --- /dev/null +++ b/test/.excludes-ractor/TestRubyPrimitive.rb @@ -0,0 +1,9 @@ +exclude(:test_constant, "global side effects") +exclude(:test_constant_cache3, "global variable access") +exclude(:test_constant_cache4, "global variable access") +exclude(:test_constant_cache5, "global variable access") +exclude(:test_cvar_from_instance_method, "class variables") +exclude(:test_cvar_from_singleton_method, "class variables") +exclude(:test_cvar_from_singleton_method2, "class variables") +exclude(:test_gvar, "global variable access") +exclude(:test_opassign_ractor_unsafe, "cvars and gvars") diff --git a/test/.excludes-ractor/TestRubyVM.rb b/test/.excludes-ractor/TestRubyVM.rb new file mode 100644 index 00000000000000..4e84d90f27c261 --- /dev/null +++ b/test/.excludes-ractor/TestRubyVM.rb @@ -0,0 +1 @@ +exclude(:test_keep_script_lines, "global side effects") diff --git a/test/.excludes-ractor/TestSetTraceFunc.rb b/test/.excludes-ractor/TestSetTraceFunc.rb new file mode 100644 index 00000000000000..374ba4c555f4c9 --- /dev/null +++ b/test/.excludes-ractor/TestSetTraceFunc.rb @@ -0,0 +1 @@ +exclude(/^test_/, "Tracepoint not working in ractors") diff --git a/test/.excludes-ractor/TestShapes.rb b/test/.excludes-ractor/TestShapes.rb new file mode 100644 index 00000000000000..6a97b591098c65 --- /dev/null +++ b/test/.excludes-ractor/TestShapes.rb @@ -0,0 +1,7 @@ +exclude(:test_class_too_complex_during_delete, "class ivars (anon)") +exclude(:test_generic_too_complex_during_delete, "class ivars (anon)") +exclude(:test_object_too_complex_during_delete, "class ivars (anon class)") +exclude(:test_ordered_alloc_is_not_complex, "ObjectSpace.dump") +exclude(:test_too_many_ivs_on_class, "class ivars") +exclude(:test_removing_when_too_many_ivs_on_class, "class ivars") +exclude(:test_removing_when_too_many_ivs_on_module, "class ivars") diff --git a/test/.excludes-ractor/TestSignal.rb b/test/.excludes-ractor/TestSignal.rb new file mode 100644 index 00000000000000..00ae0caf98b7e0 --- /dev/null +++ b/test/.excludes-ractor/TestSignal.rb @@ -0,0 +1 @@ +exclude(/^test_/, "racy") diff --git a/test/.excludes-ractor/TestSleep.rb b/test/.excludes-ractor/TestSleep.rb new file mode 100644 index 00000000000000..3f462d0271ab0a --- /dev/null +++ b/test/.excludes-ractor/TestSleep.rb @@ -0,0 +1 @@ +exclude(:test_sleep_5sec, "EnvUtil.without_gc") diff --git a/test/.excludes-ractor/TestSprintfComb.rb b/test/.excludes-ractor/TestSprintfComb.rb new file mode 100644 index 00000000000000..3b853bc04ebca1 --- /dev/null +++ b/test/.excludes-ractor/TestSprintfComb.rb @@ -0,0 +1 @@ +exclude(/^test_/, "unshareable procs") # FIXME diff --git a/test/.excludes-ractor/TestString.rb b/test/.excludes-ractor/TestString.rb new file mode 100644 index 00000000000000..684459bef08bab --- /dev/null +++ b/test/.excludes-ractor/TestString.rb @@ -0,0 +1,12 @@ +exclude(:test_chomp, "gvars") +exclude(:test_chomp!, "gvars") +exclude(:test_crypt_no_memory_leak, "assert_no_memory_leak, may as well skip") +exclude(:test_each, "gvars") +exclude(:test_each_line, "gvars") +exclude(:test_fs, "gvars") +exclude(:test_split, "gvars") +exclude(:test_split_with_block, "gvars") +exclude(:test_string_interpolations_across_heaps_get_embedded, "ObjectSpace.dump") +exclude(:test_uminus_frozen, "unpredictable") +exclude(:test_uplus_minus_ractor_unsafe, "ObjectSpace.dump") +exclude(:test_chilled_string_setivar, "global side effects") diff --git a/test/.excludes-ractor/TestString2.rb b/test/.excludes-ractor/TestString2.rb new file mode 100644 index 00000000000000..de9c96c11021e1 --- /dev/null +++ b/test/.excludes-ractor/TestString2.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../TestString.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestStringMemory.rb b/test/.excludes-ractor/TestStringMemory.rb new file mode 100644 index 00000000000000..d357478d3c24ca --- /dev/null +++ b/test/.excludes-ractor/TestStringMemory.rb @@ -0,0 +1 @@ +exclude(/^test_/, "ObjectSpace.trace_object_allocations") diff --git a/test/.excludes-ractor/TestStringchar.rb b/test/.excludes-ractor/TestStringchar.rb new file mode 100644 index 00000000000000..1f90501aa0687e --- /dev/null +++ b/test/.excludes-ractor/TestStringchar.rb @@ -0,0 +1 @@ +exclude(:test_string_ractor_unsafe, "gvars") diff --git a/test/.excludes-ractor/TestStruct.rb b/test/.excludes-ractor/TestStruct.rb new file mode 100644 index 00000000000000..8880a5701b93c6 --- /dev/null +++ b/test/.excludes-ractor/TestStruct.rb @@ -0,0 +1,8 @@ +exclude(:test_struct, "global side effects") +exclude(:test_struct_new, "global side effects") +exclude(:test_struct_new_with_keyword_init, "global side effects") +exclude(:test_redefinition_warning, "global side effects") +exclude(:test_nonascii, "global side effects") +exclude(:test_junk, "global side effects") +exclude(:test_comparison_when_recursive, "Timeout.timeout") +exclude(:test_inspect_ractor_unsafe, "global side effects") diff --git a/test/.excludes-ractor/TestStruct/SubStruct.rb b/test/.excludes-ractor/TestStruct/SubStruct.rb new file mode 100644 index 00000000000000..380af4fbc53494 --- /dev/null +++ b/test/.excludes-ractor/TestStruct/SubStruct.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestStruct.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestStruct/TopStruct.rb b/test/.excludes-ractor/TestStruct/TopStruct.rb new file mode 100644 index 00000000000000..380af4fbc53494 --- /dev/null +++ b/test/.excludes-ractor/TestStruct/TopStruct.rb @@ -0,0 +1,2 @@ +path = File.expand_path("../../TestStruct.rb", __FILE__) +instance_eval File.read(path), path diff --git a/test/.excludes-ractor/TestSuper.rb b/test/.excludes-ractor/TestSuper.rb new file mode 100644 index 00000000000000..f3b519252db24d --- /dev/null +++ b/test/.excludes-ractor/TestSuper.rb @@ -0,0 +1,3 @@ +exclude(:test_super_missing_prepended_module, "GC.stress=") +exclude(:test_public_zsuper_with_prepend, "Timeout.timeout") +exclude(:test_overlaid_ractor_unsafe, "unshareable proc") diff --git a/test/.excludes-ractor/TestSymbol.rb b/test/.excludes-ractor/TestSymbol.rb new file mode 100644 index 00000000000000..55066685f21320 --- /dev/null +++ b/test/.excludes-ractor/TestSymbol.rb @@ -0,0 +1,3 @@ +exclude(:test_to_proc_arg_with_refinements, "FIXME: ractor_confirm_belonging issue") +exclude(:test_to_proc_arity_with_refinements, "FIXME: ractor_confirm_belonging issue") +exclude(:test_to_proc_lambda_with_refinements, "FIXME: ractor_confirm_belonging issue") diff --git a/test/.excludes-ractor/TestSyntax.rb b/test/.excludes-ractor/TestSyntax.rb new file mode 100644 index 00000000000000..8b32bf9d642182 --- /dev/null +++ b/test/.excludes-ractor/TestSyntax.rb @@ -0,0 +1,5 @@ +exclude(:test_eval_return_toplevel, "Tempfile") +exclude(:test_must_ascii_compatible, "Tempfile") +exclude(:test_return_toplevel, "Tempfile") +exclude(:test_script_lines, "Tempfile") +exclude(:test_unassignable, "gvars") diff --git a/test/.excludes-ractor/TestSystem.rb b/test/.excludes-ractor/TestSystem.rb new file mode 100644 index 00000000000000..210ddddd44c36f --- /dev/null +++ b/test/.excludes-ractor/TestSystem.rb @@ -0,0 +1 @@ +exclude(:test_system_redirect_win, "Dir.chdir") diff --git a/test/.excludes-ractor/TestThread.rb b/test/.excludes-ractor/TestThread.rb new file mode 100644 index 00000000000000..1301526b407da2 --- /dev/null +++ b/test/.excludes-ractor/TestThread.rb @@ -0,0 +1,13 @@ +exclude(:test_blocking_mutex_unlocked_on_fork, "fork") +exclude(:test_fork_in_thread, "fork") +exclude(:test_fork_value, "fork") +exclude(:test_fork_while_locked, "fork") +exclude(:test_fork_while_parent_locked, "fork") +exclude(:test_fork_while_mutex_locked_by_forker, "fork") +exclude(:test_join_limit_, "ractor incompatible") +exclude(:test_local_barrier, "gvars") +exclude(:test_thread_interrupt_for_killed_thread, "Timeout.timeout") +exclude(:test_thread_native_thread_id_across_fork_on_linux, "fork") +exclude(:test_thread_timer_and_ensure, "ractor incompatible") +exclude(:test_thread_timer_and_interrupt, "Timeout.timeout") +exclude(/^test_join_limit_negative_/, "Timeout.timeout") diff --git a/test/.excludes-ractor/TestThreadConditionVariable.rb b/test/.excludes-ractor/TestThreadConditionVariable.rb new file mode 100644 index 00000000000000..03123308dd4016 --- /dev/null +++ b/test/.excludes-ractor/TestThreadConditionVariable.rb @@ -0,0 +1,4 @@ +exclude(:test_condvar_fork, "fork") +exclude(:test_condvar_wait_and_broadcast, "Timeout.timeout") +exclude(:test_condvar_wait_deadlock_2, "Timeout.timeout") +exclude(:test_condvar_timed_wait, "FIXME: unpredictable") diff --git a/test/.excludes-ractor/TestThreadQueue.rb b/test/.excludes-ractor/TestThreadQueue.rb new file mode 100644 index 00000000000000..23ed7cbb9739a3 --- /dev/null +++ b/test/.excludes-ractor/TestThreadQueue.rb @@ -0,0 +1,2 @@ +exclude(:test_fork_while_queue_waiting, "fork") +exclude(:test_queue_thread_raise, "Timeout.timeout") diff --git a/test/.excludes-ractor/TestTime.rb b/test/.excludes-ractor/TestTime.rb new file mode 100644 index 00000000000000..24d042063ceda6 --- /dev/null +++ b/test/.excludes-ractor/TestTime.rb @@ -0,0 +1,7 @@ +exclude(:test_2038, "class ivars") +exclude(:test_time_interval, "Timeout.timeout") +exclude(:test_timegm, "class ivars") +exclude(:test_marshal_broken_offset, "global side effects") +exclude(:test_marshal_broken_zone, "global side effects") +exclude(:test_marshal_zone_ractor_unsafe, "global side effects") +exclude(:test_deconstruct_keys, "global side effects") diff --git a/test/.excludes-ractor/TestTrace.rb b/test/.excludes-ractor/TestTrace.rb new file mode 100644 index 00000000000000..9f1869f0bc6ee8 --- /dev/null +++ b/test/.excludes-ractor/TestTrace.rb @@ -0,0 +1 @@ +exclude(/^test_/, "TODO: Tracepoint doesn't work well in ractors") diff --git a/test/.excludes-ractor/TestVariable.rb b/test/.excludes-ractor/TestVariable.rb new file mode 100644 index 00000000000000..b4d06c86414c06 --- /dev/null +++ b/test/.excludes-ractor/TestVariable.rb @@ -0,0 +1,12 @@ +exclude(:test_cloned_allows_setting_cvar, "cvars") +exclude(:test_cloned_classes_copy_cvar_cache, "cvars") +exclude(:test_cvar_overtaken_by_module, "cvars") +exclude(:test_cvar_overtaken_by_parent_class, "cvars") +exclude(:test_global_variable_popped, "gvars") +exclude(:test_global_variables, "gvars") +exclude(:test_include_refined_module_class_variable, "cvars") +exclude(:test_remove_instance_variables_class, "class ivars") +exclude(:test_set_class_variable_on_frozen_object, "cvars") +exclude(:test_setting_class_variable_on_module_through_inheritance, "cvars") +exclude(:test_singleton_class_included_class_variable, "cvars") +exclude(:test_variable, "cvars") diff --git a/test/fileutils/test_fileutils.rb b/test/fileutils/test_fileutils.rb index 92308d95573206..2e145b4ee93f83 100644 --- a/test/fileutils/test_fileutils.rb +++ b/test/fileutils/test_fileutils.rb @@ -84,13 +84,10 @@ def check_have_hardlink? return true end - @@no_broken_symlink = false - if /cygwin/ =~ RUBY_PLATFORM and /\bwinsymlinks:native(?:strict)?\b/ =~ ENV["CYGWIN"] - @@no_broken_symlink = true - end + NO_BROKEN_SYMLINK = /cygwin/ =~ RUBY_PLATFORM and /\bwinsymlinks:native(?:strict)?\b/ =~ ENV["CYGWIN"] def no_broken_symlink? - @@no_broken_symlink + NO_BROKEN_SYMLINK end def has_capsh? @@ -196,7 +193,7 @@ def teardown end - TARGETS = %w( data/a data/all data/random data/zero ) + TARGETS = %w( data/a data/all data/random data/zero ).freeze def prepare_data_file File.open('data/a', 'w') {|f| diff --git a/test/json/json_fixtures_test.rb b/test/json/json_fixtures_test.rb index c153ebef7cbed1..bdeeefff447ca7 100644 --- a/test/json/json_fixtures_test.rb +++ b/test/json/json_fixtures_test.rb @@ -7,20 +7,23 @@ class JSONFixturesTest < Test::Unit::TestCase passed.each do |f| name = File.basename(f).gsub(".", "_") - source = File.read(f) - define_method("test_#{name}") do - assert JSON.parse(source), "Did not pass for fixture '#{File.basename(f)}': #{source.inspect}" + class_eval <<-RUBY, __FILE__, __LINE__+1 + def test_#{name} + assert JSON.parse(File.read(#{f.inspect})), "Did not pass for fixture '#{File.basename(f)}': \#{File.read(#{f.inspect})}" end + RUBY end failed.each do |f| name = File.basename(f).gsub(".", "_") - source = File.read(f) - define_method("test_#{name}") do - assert_raise(JSON::ParserError, JSON::NestingError, - "Did not fail for fixture '#{name}': #{source.inspect}") do - JSON.parse(source) + class_eval <<-RUBY, __FILE__, __LINE__+1 + def test_#{name} + source = File.read(#{f.inspect}) + assert_raise(JSON::ParserError, JSON::NestingError, + "Did not fail for fixture '#{name}': \#{source.inspect}") do + JSON.parse(source) + end end - end + RUBY end end diff --git a/test/ruby/enc/test_case_comprehensive.rb b/test/ruby/enc/test_case_comprehensive.rb index b812b88b832be2..0692391ea89cf7 100644 --- a/test/ruby/enc/test_case_comprehensive.rb +++ b/test/ruby/enc/test_case_comprehensive.rb @@ -5,8 +5,8 @@ class TestComprehensiveCaseMapping < Test::Unit::TestCase UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION'] - path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__) - UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd" : path + path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__).freeze + UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd".freeze : path def self.hex2utf8(s) s.split(' ').map { |c| c.to_i(16) }.pack('U*') @@ -29,7 +29,7 @@ def test_data_files_available end end -TestComprehensiveCaseMapping.data_files_available? and class TestComprehensiveCaseMapping +TestComprehensiveCaseMapping.data_files_available? && will_run_in_main_ractor? && class TestComprehensiveCaseMapping (CaseTest = Struct.new(:method_name, :attributes, :first_data, :follow_data)).class_eval do def initialize(method_name, attributes, first_data, follow_data=first_data) super diff --git a/test/ruby/enc/test_emoji_breaks.rb b/test/ruby/enc/test_emoji_breaks.rb index bb5114680e686f..7789c8eed2bb4d 100644 --- a/test/ruby/enc/test_emoji_breaks.rb +++ b/test/ruby/enc/test_emoji_breaks.rb @@ -46,15 +46,16 @@ def self.files end UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION'] - UNICODE_DATA_PATH = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}/ucd/emoji", __dir__) + UNICODE_DATA_PATH = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}/ucd/emoji", __dir__).freeze EMOJI_VERSION = RbConfig::CONFIG['UNICODE_EMOJI_VERSION'] - EMOJI_DATA_PATH = File.expand_path("../../../enc/unicode/data/emoji/#{EMOJI_VERSION}", __dir__) + EMOJI_DATA_PATH = File.expand_path("../../../enc/unicode/data/emoji/#{EMOJI_VERSION}", __dir__).freeze EMOJI_DATA_FILES = %w[emoji-sequences emoji-test emoji-zwj-sequences].map do |basename| BreakFile.new(basename, EMOJI_DATA_PATH, EMOJI_VERSION) end UNICODE_DATA_FILE = BreakFile.new('emoji-variation-sequences', UNICODE_DATA_PATH, UNICODE_VERSION) EMOJI_DATA_FILES << UNICODE_DATA_FILE + Ractor.make_shareable(EMOJI_DATA_FILES) def self.data_files_available? EMOJI_DATA_FILES.all? do |f| diff --git a/test/ruby/enc/test_grapheme_breaks.rb b/test/ruby/enc/test_grapheme_breaks.rb index 7e6d722d4076e1..88eeaf6cf8ab89 100644 --- a/test/ruby/enc/test_grapheme_breaks.rb +++ b/test/ruby/enc/test_grapheme_breaks.rb @@ -27,9 +27,9 @@ def initialize(line_number, data, comment) end UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION'] - path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__) - UNICODE_DATA_PATH = File.directory?("#{path}/ucd/auxiliary") ? "#{path}/ucd/auxiliary" : path - GRAPHEME_BREAK_TEST_FILE = File.expand_path("#{UNICODE_DATA_PATH}/GraphemeBreakTest.txt", __dir__) + path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__).freeze + UNICODE_DATA_PATH = File.directory?("#{path}/ucd/auxiliary") ? "#{path}/ucd/auxiliary".freeze : path + GRAPHEME_BREAK_TEST_FILE = File.expand_path("#{UNICODE_DATA_PATH}/GraphemeBreakTest.txt", __dir__).freeze def self.file_available? File.exist? GRAPHEME_BREAK_TEST_FILE @@ -44,10 +44,12 @@ def test_data_files_available if file_available? def read_data tests = [] + lineno = 1 File.foreach(GRAPHEME_BREAK_TEST_FILE, encoding: Encoding::UTF_8) do |line| - if $. == 1 and not line.start_with?("# GraphemeBreakTest-#{UNICODE_VERSION}.txt") + if lineno == 1 and not line.start_with?("# GraphemeBreakTest-#{UNICODE_VERSION}.txt") raise "File Version Mismatch" end + lineno += 1 next if /\A#/.match? line tests << BreakTest.new($., *line.chomp.split('#')) rescue 'whatever' end @@ -55,9 +57,9 @@ def read_data end def all_tests - @@tests ||= read_data + @grapheme_breaks_tests ||= read_data rescue Errno::ENOENT - @@tests ||= [] + @grapheme_breaks_tests ||= [] end def test_each_grapheme_cluster diff --git a/test/ruby/enc/test_iso_8859.rb b/test/ruby/enc/test_iso_8859.rb index ed663be24307fb..3b0a830bafeba7 100644 --- a/test/ruby/enc/test_iso_8859.rb +++ b/test/ruby/enc/test_iso_8859.rb @@ -2,7 +2,7 @@ require 'test/unit' class TestISO8859 < Test::Unit::TestCase - ASSERTS = %q( + ASSERTS = Ractor.make_shareable(%q( assert_match(/^(\xdf)\1$/i, "\xdf\xdf") assert_match(/^(\xdf)\1$/i, "ssss") # assert_match(/^(\xdf)\1$/i, "\xdfss") # this must be bug... @@ -18,7 +18,7 @@ class TestISO8859 < Test::Unit::TestCase assert_match(/^[#{ c2 }]+$/i, c1 + c2) end assert_match(/^\xff$/i, "\xff") - ) + )) def test_iso_8859_1 eval("# encoding: iso8859-1\n" + ASSERTS.gsub(/ENCODING/m, "iso8859-1")) diff --git a/test/ruby/enc/test_koi8.rb b/test/ruby/enc/test_koi8.rb index 4a4d233e8d686d..8557ff29590912 100644 --- a/test/ruby/enc/test_koi8.rb +++ b/test/ruby/enc/test_koi8.rb @@ -2,7 +2,7 @@ require "test/unit" class TestKOI8 < Test::Unit::TestCase - ASSERTS = %q( + ASSERTS = Ractor.make_shareable(%q( (0xc0..0xdf).each do |c| c1 = c.chr("ENCODING") c2 = (c + 0x20).chr("ENCODING") @@ -11,7 +11,7 @@ class TestKOI8 < Test::Unit::TestCase assert_match(/^[#{ c1 }]+$/i, c2 + c1) assert_match(/^[#{ c2 }]+$/i, c1 + c2) end - ) + )) def test_koi8_r eval("# encoding: koi8-r\n" + ASSERTS.gsub("ENCODING", "koi8-r")) diff --git a/test/ruby/enc/test_regex_casefold.rb b/test/ruby/enc/test_regex_casefold.rb index b5d5c6e33740f5..b108d646ffa7ac 100644 --- a/test/ruby/enc/test_regex_casefold.rb +++ b/test/ruby/enc/test_regex_casefold.rb @@ -5,8 +5,8 @@ class TestCaseFold < Test::Unit::TestCase UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION'] - path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__) - UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd" : path + path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__).freeze + UNICODE_DATA_PATH = File.directory?("#{path}/ucd") ? "#{path}/ucd".freeze : path CaseTest = Struct.new :source, :target, :kind, :line def check_downcase_properties(expected, start, *flags) @@ -36,15 +36,20 @@ def to_codepoints(string) end def setup - @@tests ||= read_tests + @regex_casefold_tests ||= read_tests rescue Errno::ENOENT => e - @@tests ||= [] + @regex_casefold_tests ||= [] omit e.message end + def tests + setup + @regex_casefold_tests + end + def self.generate_test_casefold(encoding) define_method "test_mbc_case_fold_#{encoding}" do - @@tests.each do |test| + tests.each do |test| begin source = test.source.encode encoding target = test.target.encode encoding @@ -57,7 +62,7 @@ def self.generate_test_casefold(encoding) end define_method "test_get_case_fold_codes_by_str_#{encoding}" do - @@tests.each do |test| + tests.each do |test| begin source = test.source.encode encoding target = test.target.encode encoding @@ -71,7 +76,7 @@ def self.generate_test_casefold(encoding) end define_method "test_apply_all_case_fold_#{encoding}" do - @@tests.each do |test| + tests.each do |test| begin source = test.source.encode encoding target = test.target.encode encoding @@ -90,7 +95,7 @@ def self.generate_test_casefold(encoding) end def test_downcase_fold - @@tests.each do |test| + tests.each do |test| check_downcase_properties test.target, test.source, :fold end end diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index a3ac0a6a0b420d..9af96e6a3d1db3 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -659,7 +659,9 @@ def test_concat assert_raise(TypeError) { @cls[0].concat(:foo) } assert_raise(FrozenError) { @cls[0].freeze.concat(:foo) } + end + def test_concat_under_gc_stress a = @cls[nil] def (x = Object.new).to_ary ary = Array.new(2) @@ -1174,6 +1176,7 @@ def test_values_at def test_join assert_deprecated_warning {$, = ""} + a = @cls[] assert_equal("", assert_deprecated_warn(/non-nil value/) {a.join}) assert_equal("", a.join(',')) @@ -1975,14 +1978,14 @@ def test_to_s $, = nil end - StubToH = [ + StubToH = Ractor.make_shareable([ [:key, :value], Object.new.tap do |kvp| def kvp.to_ary [:obtained, :via_to_ary] end end, - ] + ]) def test_to_h array = StubToH @@ -1992,14 +1995,17 @@ def test_to_h [[:first_one, :ok], :not_ok].to_h } assert_equal "wrong element type Symbol at 1 (expected array)", e.message - array = [eval("class C\u{1f5ff}; self; end").new] - assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h} e = assert_raise(ArgumentError) { [[:first_one, :ok], [1, 2], [:not_ok]].to_h } assert_equal "wrong array length at 2 (expected 2, was 1)", e.message end + def test_to_h_single_element_with_object + array = [eval("class C\u{1f5ff}; self; end").new] + assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h} + end + def test_to_h_block array = StubToH assert_equal({"key" => "value", "obtained" => "via_to_ary"}, @@ -2012,15 +2018,18 @@ def test_to_h_block [[:first_one, :ok], :not_ok].to_h {|k, v| v ? [k, v] : k} } assert_equal "wrong element type Symbol at 1 (expected array)", e.message - array = [1] - k = eval("class C\u{1f5ff}; self; end").new - assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h {k}} e = assert_raise(ArgumentError) { [[:first_one, :ok], [1, 2], [:not_ok]].to_h {|kv| kv} } assert_equal "wrong array length at 2 (expected 2, was 1)", e.message end + def test_to_h_block_single_element_with_object + array = [1] + k = eval("class C\u{1f5ff}; self; end").new + assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h {k}} + end + def test_min assert_equal(3, [3].min) assert_equal(1, [1, 2, 3, 1, 2].min) @@ -2435,13 +2444,6 @@ def test_product assert_equal(@cls[[1],[2]], @cls[1,2].product) assert_equal(@cls[], @cls[1,2].product([])) - bug3394 = '[ruby-dev:41540]' - acc = [] - EnvUtil.under_gc_stress {[1,2].product([3,4,5],[6,8]){|array| acc << array}} - assert_equal([[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8], - [2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]], - acc, bug3394) - def (o = Object.new).to_ary; GC.start; [3,4] end acc = [1,2].product(*[o]*10) assert_equal([1,2].product([3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4]), @@ -2452,6 +2454,15 @@ def (o = Object.new).to_ary; GC.start; [3,4] end a.all? {|x| assert_not_include(x, 0)} end + def test_product_under_gc_stress + bug3394 = '[ruby-dev:41540]' + acc = [] + EnvUtil.under_gc_stress {[1,2].product([3,4,5],[6,8]){|array| acc << array}} + assert_equal([[1, 3, 6], [1, 3, 8], [1, 4, 6], [1, 4, 8], [1, 5, 6], [1, 5, 8], + [2, 3, 6], [2, 3, 8], [2, 4, 6], [2, 4, 8], [2, 5, 6], [2, 5, 8]], + acc, bug3394) + end + def test_permutation a = @cls[] assert_equal(1, a.permutation(0).size) diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index c7a946dec868bb..a55e4f74fd5f0f 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -116,49 +116,53 @@ def validate_not_cared0(node) SRCDIR = File.expand_path("../../..", __FILE__) Dir.glob("test/**/*.rb", base: SRCDIR).each do |path| - define_method("test_ranges:#{path}") do - helper = Helper.new("#{SRCDIR}/#{path}") - helper.validate_range - - assert_equal([], helper.errors) - end + class_eval <<-RUBY + def #{"test_ranges_#{path}".gsub(/[^\w]/, '_')} + helper = Helper.new("#{SRCDIR}/#{path}") + helper.validate_range + assert_equal([], helper.errors) + end + RUBY end Dir.glob("test/**/*.rb", base: SRCDIR).each do |path| - define_method("test_not_cared:#{path}") do - helper = Helper.new("#{SRCDIR}/#{path}") - helper.validate_not_cared - - assert_equal([], helper.errors) - end + class_eval <<-RUBY + def #{"test_not_cared:#{path}".gsub(/[^\w]/, '_')} + helper = Helper.new("#{SRCDIR}/#{path}") + helper.validate_not_cared + assert_equal([], helper.errors) + end + RUBY end Dir.glob("test/**/*.rb", base: SRCDIR).each do |path| - define_method("test_all_tokens:#{path}") do - node = EnvUtil.suppress_warning { RubyVM::AbstractSyntaxTree.parse_file("#{SRCDIR}/#{path}", keep_tokens: true) } - tokens = node.all_tokens.sort_by { [_1.last[0], _1.last[1]] } - tokens_bytes = tokens.map { _1[2]}.join.bytes - source_bytes = File.read("#{SRCDIR}/#{path}").bytes - - assert_equal(source_bytes, tokens_bytes) - - (tokens.count - 1).times do |i| - token_0 = tokens[i] - token_1 = tokens[i + 1] - end_pos = token_0.last[2..3] - beg_pos = token_1.last[0..1] - - if end_pos[0] == beg_pos[0] - # When both tokens are same line, column should be consecutives - assert_equal(beg_pos[1], end_pos[1], "#{token_0}. #{token_1}") - else - # Line should be next - assert_equal(beg_pos[0], end_pos[0] + 1, "#{token_0}. #{token_1}") - # It should be on the beginning of the line - assert_equal(0, beg_pos[1], "#{token_0}. #{token_1}") + class_eval <<-RUBY + def #{"test_all_tokens:#{path}".gsub(/[^\w]/, '_')} + node = RubyVM::AbstractSyntaxTree.parse_file("#{SRCDIR}/#{path}", keep_tokens: true) + tokens = node.all_tokens.sort_by { [_1.last[0], _1.last[1]] } + tokens_bytes = tokens.map { _1[2]}.join.bytes + source_bytes = File.read("#{SRCDIR}/#{path}").bytes + + assert_equal(source_bytes, tokens_bytes) + + (tokens.count - 1).times do |i| + token_0 = tokens[i] + token_1 = tokens[i + 1] + end_pos = token_0.last[2..3] + beg_pos = token_1.last[0..1] + + if end_pos[0] == beg_pos[0] + # When both tokens are same line, column should be consecutives + assert_equal(beg_pos[1], end_pos[1], "\#{token_0}. \#{token_1}") + else + # Line should be next + assert_equal(beg_pos[0], end_pos[0] + 1, "\#{token_0}. \#{token_1}") + # It should be on the beginning of the line + assert_equal(0, beg_pos[1], "\#{token_0}. \#{token_1}") + end end end - end + RUBY end private def parse(src) diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb index 3706efab52dd0b..2153dba236f6aa 100644 --- a/test/ruby/test_beginendblock.rb +++ b/test/ruby/test_beginendblock.rb @@ -3,7 +3,7 @@ EnvUtil.suppress_warning {require 'continuation'} class TestBeginEndBlock < Test::Unit::TestCase - DIR = File.dirname(File.expand_path(__FILE__)) + DIR = File.dirname(File.expand_path(__FILE__)).freeze def test_beginendblock target = File.join(DIR, 'beginmainend.rb') diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb index dd6f4baa4c292b..c944ccc21adffa 100644 --- a/test/ruby/test_bignum.rb +++ b/test/ruby/test_bignum.rb @@ -70,17 +70,17 @@ def test_prepare end def test_bignum - $x = fact(40) - assert_equal($x, $x) - assert_equal($x, fact(40)) - assert_operator($x, :<, $x+2) - assert_operator($x, :>, $x-2) - assert_equal(815915283247897734345611269596115894272000000000, $x) - assert_not_equal(815915283247897734345611269596115894272000000001, $x) - assert_equal(815915283247897734345611269596115894272000000001, $x+1) - assert_equal(335367096786357081410764800000, $x/fact(20)) - $x = -$x - assert_equal(-815915283247897734345611269596115894272000000000, $x) + x = fact(40) + assert_equal(x, x) + assert_equal(x, fact(40)) + assert_operator(x, :<, x+2) + assert_operator(x, :>, x-2) + assert_equal(815915283247897734345611269596115894272000000000, x) + assert_not_equal(815915283247897734345611269596115894272000000001, x) + assert_equal(815915283247897734345611269596115894272000000001, x+1) + assert_equal(335367096786357081410764800000, x/fact(20)) + x = -x + assert_equal(-815915283247897734345611269596115894272000000000, x) b = 2*BIGNUM_MIN assert_equal(2-b, -(b-2)) diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb index f40817e7a1ef54..ca6eb3eb6eafda 100644 --- a/test/ruby/test_class.rb +++ b/test/ruby/test_class.rb @@ -243,6 +243,9 @@ def test_check_inheritable assert_raise(TypeError) { Class.new(c) } assert_raise(TypeError) { Class.new(Class) } assert_raise(TypeError) { eval("class Foo < Class; end") } + end + + def test_check_inheritable_break_with_object m = "M\u{1f5ff}" o = Class.new {break eval("class #{m}; self; end.new")} assert_raise_with_message(TypeError, /#{m}/) {Class.new(o)} @@ -505,20 +508,26 @@ class C < c } end - define_method :test_invalid_reset_superclass do - class A; end - class SuperclassCannotBeReset < A - end + def test_invalid_reset_superclass + self.class.class_eval <<-RUBY + class A; end + class SuperclassCannotBeReset < A + end + RUBY assert_equal A, SuperclassCannotBeReset.superclass assert_raise_with_message(TypeError, /superclass mismatch/) { - class SuperclassCannotBeReset < String - end + self.class.class_eval <<-RUBY + class SuperclassCannotBeReset < String + end + RUBY } assert_raise_with_message(TypeError, /superclass mismatch/, "[ruby-core:75446]") { - class SuperclassCannotBeReset < Object - end + self.class.class_eval <<-RUBY + class SuperclassCannotBeReset < Object + end + RUBY } assert_equal A, SuperclassCannotBeReset.superclass @@ -805,7 +814,11 @@ def test_subclasses ssc = Class.new(sc) [c, sc, ssc].each do |k| k.include Module.new - k.new.define_singleton_method(:force_singleton_class){} + k.new.define_singleton_method(:force_singleton_class, &Ractor.make_shareable( + nil.instance_eval do + proc { } + end + )) end assert_equal([sc], c.subclasses) assert_equal([ssc], sc.subclasses) diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb index b95add5bd45fe4..2fde50e130cb58 100644 --- a/test/ruby/test_compile_prism.rb +++ b/test/ruby/test_compile_prism.rb @@ -134,9 +134,7 @@ def test_DefinedNode assert_prism_eval("defined? [a: [:b, :c]]") assert_prism_eval("defined? 1 in 1") - assert_prism_eval("defined? @a") - assert_prism_eval("defined? $a") - assert_prism_eval("defined? @@a") + assert_prism_eval("defined? @a", raw: true) assert_prism_eval("defined? A") assert_prism_eval("defined? ::A") assert_prism_eval("defined? A::B") @@ -151,29 +149,11 @@ def test_DefinedNode assert_prism_eval("defined? X &= 1") assert_prism_eval("defined? X ||= 1") - assert_prism_eval("defined? $1") - assert_prism_eval("defined? $2") - assert_prism_eval("defined? $`") - assert_prism_eval("defined? $'") - assert_prism_eval("defined? $+") - - assert_prism_eval("defined? $X = 1") - assert_prism_eval("defined? $X *= 1") - assert_prism_eval("defined? $X /= 1") - assert_prism_eval("defined? $X &= 1") - assert_prism_eval("defined? $X ||= 1") - - assert_prism_eval("defined? @@X = 1") - assert_prism_eval("defined? @@X *= 1") - assert_prism_eval("defined? @@X /= 1") - assert_prism_eval("defined? @@X &= 1") - assert_prism_eval("defined? @@X ||= 1") - - assert_prism_eval("defined? @X = 1") - assert_prism_eval("defined? @X *= 1") - assert_prism_eval("defined? @X /= 1") - assert_prism_eval("defined? @X &= 1") - assert_prism_eval("defined? @X ||= 1") + assert_prism_eval("defined? @X = 1", raw: true) + assert_prism_eval("defined? @X *= 1", raw: true) + assert_prism_eval("defined? @X /= 1", raw: true) + assert_prism_eval("defined? @X &= 1", raw: true) + assert_prism_eval("defined? @X ||= 1", raw: true) assert_prism_eval("x = 1; defined? x = 1") assert_prism_eval("x = 1; defined? x *= 1") @@ -274,6 +254,28 @@ def self.m1; defined?(return) end assert_prism_eval("defined?(unless true; 1; end)") end + def test_DefinedNode_ractor_unsafe + assert_prism_eval("defined? $a") + assert_prism_eval("defined? @@a") + assert_prism_eval("defined? $1") + assert_prism_eval("defined? $2") + assert_prism_eval("defined? $`") + assert_prism_eval("defined? $'") + assert_prism_eval("defined? $+") + + assert_prism_eval("defined? $X = 1") + assert_prism_eval("defined? $X *= 1") + assert_prism_eval("defined? $X /= 1") + assert_prism_eval("defined? $X &= 1") + assert_prism_eval("defined? $X ||= 1") + + assert_prism_eval("defined? @@X = 1") + assert_prism_eval("defined? @@X *= 1") + assert_prism_eval("defined? @@X /= 1") + assert_prism_eval("defined? @@X &= 1") + assert_prism_eval("defined? @@X ||= 1") + end + def test_GlobalVariableReadNode assert_prism_eval("$pit = 1; $pit") end @@ -350,11 +352,17 @@ def test_ConstantPathAndWriteNode def test_ConstantPathOrWriteNode assert_prism_eval("Prism::CPOrWN = nil; Prism::CPOrWN ||= 1") assert_prism_eval("Prism::CPOrWN ||= 1") + end + + def test_ConstantPathOrWriteNode_ractor_unsafe assert_prism_eval("::CPOrWN = nil; ::CPOrWN ||= 1") end def test_ConstantPathOperatorWriteNode assert_prism_eval("Prism::CPOWN = 0; Prism::CPOWN += 1") + end + + def test_ConstantPathOperatorWriteNode_ractor_unsafe assert_prism_eval("::CPOWN = 0; ::CPOWN += 1") end @@ -375,15 +383,15 @@ def test_GlobalVariableWriteNode end def test_InstanceVariableAndWriteNode - assert_prism_eval("@pit = 0; @pit &&= 1") + assert_prism_eval("@pit = 0; @pit &&= 1", raw: non_main_ractor?) end def test_InstanceVariableOperatorWriteNode - assert_prism_eval("@pit = 0; @pit += 1") + assert_prism_eval("@pit = 0; @pit += 1", raw: non_main_ractor?) end def test_InstanceVariableOrWriteNode - assert_prism_eval("@pit ||= 1") + assert_prism_eval("@pit ||= 1", raw: non_main_ractor?) end def test_InstanceVariableWriteNode @@ -992,11 +1000,14 @@ def test_IfNode assert_prism_eval('if "a"..; end') assert_prism_eval('if .."b"; end') assert_prism_eval('if ..1; end') - assert_prism_eval('if 1..; end') - assert_prism_eval('if 1..2; end') assert_prism_eval('if true or true; end'); end + def test_IfNode_ractor_unsafe + assert_prism_eval('if 1..; end') # accesses $. implicitly + assert_prism_eval('if 1..2; end') + end + def test_OrNode assert_prism_eval("true || 1") assert_prism_eval("false || 1") @@ -1047,9 +1058,7 @@ def o.bar = @ret.length < 3 def test_ForNode assert_prism_eval("for i in [1,2] do; i; end") - assert_prism_eval("for @i in [1,2] do; @i; end") - assert_prism_eval("for $i in [1,2] do; $i; end") - + assert_prism_eval("for @i in [1,2] do; @i; end", raw: non_main_ractor?) assert_prism_eval("for foo, in [1,2,3] do end") assert_prism_eval("for i, j in {a: 'b'} do; i; j; end") @@ -1058,6 +1067,10 @@ def test_ForNode assert_prism_eval("for *x in [[1,2], [3,4]] do; x; end") end + def test_ForNode_gvar + assert_prism_eval("for $i in [1,2] do; $i; end") + end + ############################################################################ # Throws # ############################################################################ @@ -1207,9 +1220,9 @@ def self.prism_test_ensure_node # Bug #21001 assert_prism_eval(<<~RUBY) - RUN_ARRAY = [1,2] + RUN_ARRAY = [1,2].freeze - MAP_PROC = Proc.new do |&blk| + MAP_PROC = Ractor.make_shareable(Proc.new do |&blk| block_results = [] RUN_ARRAY.each do |value| block_value = blk.call(value) @@ -1218,7 +1231,7 @@ def self.prism_test_ensure_node block_results ensure next block_results - end + end) MAP_PROC.call do |value| break if value > 1 @@ -1848,12 +1861,15 @@ def test_PreExecutionNode def test_PostExecutionNode assert_prism_eval("END { 1 }") - assert_prism_eval("END { @b }; @b = 1") - assert_prism_eval("END { @b; 0 }; @b = 1") assert_prism_eval("foo = 1; END { foo.nil? }") assert_prism_eval("foo = 1; END { END { foo.nil? }}") end + def test_PostExecutionNode_set_ivar + assert_prism_eval("END { @b }; @b = 1") + assert_prism_eval("END { @b; 0 }; @b = 1") + end + def test_ProgramNode assert_prism_eval("") assert_prism_eval("1") @@ -2612,11 +2628,14 @@ def test_PinnedExpressionNode end def test_PinnedVariableNode + assert_prism_eval("prism = 1; 1 in ^prism") + assert_prism_eval("[1].each { 1 => ^it }") + end + + def test_PinnedVariableNode_ractor_unsafe assert_prism_eval("module Prism; @@prism = 1; 1 in ^@@prism; end") assert_prism_eval("module Prism; @prism = 1; 1 in ^@prism; end") assert_prism_eval("$prism = 1; 1 in ^$prism") - assert_prism_eval("prism = 1; 1 in ^prism") - assert_prism_eval("[1].each { 1 => ^it }") end ############################################################################ @@ -2723,7 +2742,7 @@ def test_parse_file private def compare_eval(source, raw:, location:) - source = raw ? source : "class Prism::TestCompilePrism\n#{source}\nend" + source = raw ? source : "class Prism::TestCompilePrism#{Ractor.current.object_id}\n#{source}\nend" ruby_eval = RubyVM::InstructionSequence.compile_parsey(source).eval prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval diff --git a/test/ruby/test_condition.rb b/test/ruby/test_condition.rb index ab0ffc4b6a2559..a6b955fe592340 100644 --- a/test/ruby/test_condition.rb +++ b/test/ruby/test_condition.rb @@ -6,12 +6,12 @@ class TestCondition < Test::Unit::TestCase # [should] first test to see if we can run the tests. def test_condition - $x = '0'; + x = '0'; - $x == $x && assert(true) - $x != $x && assert(false) - $x == $x || assert(false) - $x != $x || assert(true) + x == x && assert(true) + x != x && assert(false) + x == x || assert(false) + x != x || assert(true) end end diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb index 237bdc8a4d0155..70f2a311d3a151 100644 --- a/test/ruby/test_enum.rb +++ b/test/ruby/test_enum.rb @@ -802,6 +802,9 @@ def ary.each; [3, 4].each{|e|yield e}; end assert_equal([[1, 3], [2, 4], [3, nil], [1, nil], [2, nil]], @obj.zip(ary)) def ary.to_ary; [5, 6]; end assert_equal([[1, 5], [2, 6], [3, nil], [1, nil], [2, nil]], @obj.zip(ary)) + end + + def test_zip_ractor_unsafe obj = eval("class C\u{1f5ff}; self; end").new assert_raise_with_message(TypeError, /C\u{1f5ff}/) {(1..1).zip(obj)} end diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb index cd62cd8acb7746..765ea57262084c 100644 --- a/test/ruby/test_enumerator.rb +++ b/test/ruby/test_enumerator.rb @@ -495,10 +495,12 @@ def test_generator assert_raise(LocalJumpError) {Enumerator::Generator.new} assert_raise(TypeError) {Enumerator::Generator.new(1)} - obj = eval("class C\u{1f5ff}; self; end").new - assert_raise_with_message(TypeError, /C\u{1f5ff}/) { - Enumerator::Generator.new(obj) - } + unless multiple_ractors? + obj = eval("class C\u{1f5ff}; self; end").new + assert_raise_with_message(TypeError, /C\u{1f5ff}/) { + Enumerator::Generator.new(obj) + } + end end def test_generator_args diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb index 2727620c198522..06a8b544add704 100644 --- a/test/ruby/test_env.rb +++ b/test/ruby/test_env.rb @@ -5,12 +5,12 @@ class TestEnv < Test::Unit::TestCase windows = /bccwin|mswin|mingw/ =~ RUBY_PLATFORM IGNORE_CASE = windows ENCODING = windows ? Encoding::UTF_8 : Encoding.find("locale") - PATH_ENV = "PATH" + PATH_ENV = "PATH".freeze INVALID_ENVVARS = [ - "foo\0bar", - "\xa1\xa1".force_encoding(Encoding::UTF_16LE), - "foo".force_encoding(Encoding::ISO_2022_JP), - ] + "foo\0bar".freeze, + "\xa1\xa1".force_encoding(Encoding::UTF_16LE).freeze, + "foo".force_encoding(Encoding::ISO_2022_JP).freeze, + ].freeze def assert_invalid_env(msg = nil) all_assertions(msg) do |a| @@ -645,7 +645,7 @@ def check(as, bs) end assert_equal(as.sort, bs.sort) end - } + }.freeze def test_bracket_in_ractor assert_ractor(<<-"end;") diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb index d2145bec5d9f13..6ffbd89b45c773 100644 --- a/test/ruby/test_eval.rb +++ b/test/ruby/test_eval.rb @@ -28,8 +28,6 @@ def test_eval_basic assert_equal 11, eval("11") @ivar = 12 assert_equal 12, eval("@ivar") - assert_equal 13, eval("@@cvar") - assert_equal 14, eval("$gvar__eval") assert_equal 15, eval("Const") assert_equal 16, eval("7 + 9") @@ -39,12 +37,19 @@ def test_eval_basic 1.times { assert_equal 12, eval("@ivar") - assert_equal 13, eval("@@cvar") - assert_equal 14, eval("$gvar__eval") assert_equal 15, eval("Const") } end + def test_eval_basic_ractor_unsafe + assert_equal 13, eval("@@cvar") + assert_equal 14, eval("$gvar__eval") + 1.times do + assert_equal 13, eval("@@cvar") + assert_equal 14, eval("$gvar__eval") + end + end + def test_eval_binding_basic assert_equal nil, eval("nil", binding()) assert_equal true, eval("true", binding()) @@ -56,8 +61,6 @@ def test_eval_binding_basic assert_equal 11, eval("11", binding()) @ivar = 12 assert_equal 12, eval("@ivar", binding()) - assert_equal 13, eval("@@cvar", binding()) - assert_equal 14, eval("$gvar__eval", binding()) assert_equal 15, eval("Const", binding()) assert_equal 16, eval("7 + 9", binding()) @@ -67,12 +70,19 @@ def test_eval_binding_basic 1.times { assert_equal 12, eval("@ivar") - assert_equal 13, eval("@@cvar") - assert_equal 14, eval("$gvar__eval") assert_equal 15, eval("Const") } end + def test_eval_binding_basic_ractor_unsafe + assert_equal 13, eval("@@cvar", binding()) + assert_equal 14, eval("$gvar__eval", binding()) + 1.times do + assert_equal 13, eval("@@cvar", binding()) + assert_equal 14, eval("$gvar__eval", binding()) + end + end + def test_module_eval_string_basic c = self.class assert_equal nil, c.module_eval("nil") @@ -83,8 +93,6 @@ def test_module_eval_string_basic assert_equal 11, c.module_eval("11") @ivar = 12 assert_equal 12, c.module_eval("@ivar") - assert_equal 13, c.module_eval("@@cvar") - assert_equal 14, c.module_eval("$gvar__eval") assert_equal 15, c.module_eval("Const") assert_equal 16, c.module_eval("7 + 9") assert_equal 17, c.module_eval("17.to_i") @@ -94,12 +102,20 @@ def test_module_eval_string_basic @ivar = 12 1.times { assert_equal 12, c.module_eval("@ivar") - assert_equal 13, c.module_eval("@@cvar") - assert_equal 14, c.module_eval("$gvar__eval") assert_equal 15, c.module_eval("Const") } end + def test_module_eval_string_basic_ractor_unsafe + c = self.class + assert_equal 13, c.module_eval("@@cvar") + assert_equal 14, c.module_eval("$gvar__eval") + 1.times do + assert_equal 13, c.module_eval("@@cvar") + assert_equal 14, c.module_eval("$gvar__eval") + end + end + def test_module_eval_block_basic c = self.class assert_equal nil, c.module_eval { nil } @@ -110,8 +126,6 @@ def test_module_eval_block_basic assert_equal 11, c.module_eval { 11 } @ivar = 12 assert_equal 12, c.module_eval { @ivar } - assert_equal 13, c.module_eval { @@cvar } - assert_equal 14, c.module_eval { $gvar__eval } assert_equal 15, c.module_eval { Const } assert_equal 16, c.module_eval { 7 + 9 } assert_equal 17, c.module_eval { "17".to_i } @@ -121,12 +135,20 @@ def test_module_eval_block_basic @ivar = 12 1.times { assert_equal 12, c.module_eval { @ivar } - assert_equal 13, c.module_eval { @@cvar } - assert_equal 14, c.module_eval { $gvar__eval } assert_equal 15, c.module_eval { Const } } end + def test_module_eval_block_basic_ractor_unsafe + c = self.class + assert_equal 13, c.module_eval { @@cvar } + assert_equal 14, c.module_eval { $gvar__eval } + 1.times do + assert_equal 13, c.module_eval { @@cvar } + assert_equal 14, c.module_eval { $gvar__eval } + end + end + def test_module_eval_block_symbol assert_equal "Math", Math.module_eval(&:to_s) end @@ -150,8 +172,6 @@ def test_instance_eval_string_basic assert_equal 11, o.instance_eval("11") assert_equal 12, o.instance_eval("@ivar") unless o.frozen? - assert_equal 13, o.instance_eval("@@cvar") - assert_equal 14, o.instance_eval("$gvar__eval") assert_equal 15, o.instance_eval("Const") assert_equal 16, o.instance_eval("7 + 9") assert_equal 17, o.instance_eval("17.to_i") @@ -160,9 +180,18 @@ def test_instance_eval_string_basic 1.times { assert_equal 12, o.instance_eval("@ivar") unless o.frozen? + assert_equal 15, o.instance_eval("Const") + } + end + end + + def test_instance_eval_string_basic_ractor_unsafe + forall_TYPE do |o| + assert_equal 13, o.instance_eval("@@cvar") + assert_equal 14, o.instance_eval("$gvar__eval") + 1.times { assert_equal 13, o.instance_eval("@@cvar") assert_equal 14, o.instance_eval("$gvar__eval") - assert_equal 15, o.instance_eval("Const") } end end @@ -178,8 +207,6 @@ def test_instance_eval_block_basic assert_equal 11, o.instance_eval { 11 } assert_equal 12, o.instance_eval { @ivar } unless o.frozen? - assert_equal 13, o.instance_eval { @@cvar } - assert_equal 14, o.instance_eval { $gvar__eval } assert_equal 15, o.instance_eval { Const } assert_equal 16, o.instance_eval { 7 + 9 } assert_equal 17, o.instance_eval { 17.to_i } @@ -188,9 +215,18 @@ def test_instance_eval_block_basic 1.times { assert_equal 12, o.instance_eval { @ivar } unless o.frozen? + assert_equal 15, o.instance_eval { Const } + } + end + end + + def test_instance_eval_block_basic_ractor_unsafe + forall_TYPE do |o| + assert_equal 13, o.instance_eval { @@cvar } + assert_equal 14, o.instance_eval { $gvar__eval } + 1.times { assert_equal 13, o.instance_eval { @@cvar } assert_equal 14, o.instance_eval { $gvar__eval } - assert_equal 15, o.instance_eval { Const } } end end @@ -270,8 +306,6 @@ def test_instance_exec_block_basic assert_equal 11, o.instance_exec { 11 } assert_equal 12, o.instance_exec { @ivar } unless o.frozen? - assert_equal 13, o.instance_exec { @@cvar } - assert_equal 14, o.instance_exec { $gvar__eval } assert_equal 15, o.instance_exec { Const } assert_equal 16, o.instance_exec { 7 + 9 } assert_equal 17, o.instance_exec { 17.to_i } @@ -280,9 +314,18 @@ def test_instance_exec_block_basic 1.times { assert_equal 12, o.instance_exec { @ivar } unless o.frozen? + assert_equal 15, o.instance_exec { Const } + } + end + end + + def test_instance_exec_block_basic_ractor_unsafe + forall_TYPE do |o| + assert_equal 13, o.instance_exec { @@cvar } + assert_equal 14, o.instance_exec { $gvar__eval } + 1.times { assert_equal 13, o.instance_exec { @@cvar } assert_equal 14, o.instance_exec { $gvar__eval } - assert_equal 15, o.instance_exec { Const } } end end @@ -346,14 +389,6 @@ def test_eval_orig assert(!eval('nil')) assert(!eval('false')) - $foo = 'assert(true)' - begin - eval $foo - rescue - assert(false) - end - - assert_equal('assert(true)', eval("$foo")) assert_equal(true, eval("true")) i = i = 5 assert(eval("i == 5")) @@ -371,24 +406,6 @@ def test_eval_orig end assert(!bad) - # !! use class_eval to avoid nested definition - x = self.class.class_eval %q( - module EvTest - EVTEST1 = 25 - evtest2 = 125 - evtest2 = evtest2 - binding - end - ) - assert_equal(25, eval("EVTEST1", x)) # constant in module - assert_equal(125, eval("evtest2", x)) # local var in module - bad = true - begin - eval("EVTEST1") - rescue NameError # must raise error - bad = false - end - assert(!bad) x = binding eval "i = 1", x @@ -418,9 +435,39 @@ module EvTest assert_equal(55, foo22) }.call + end + + def test_eval_orig_ractor_unsafe + $foo = 'assert(true)' + begin + eval $foo + rescue + assert(false) + end + assert_equal('assert(true)', eval("$foo")) + + # !! use class_eval to avoid nested definition + x = self.class.class_eval %q( + module EvTest + EVTEST1 = 25 + evtest2 = 125 + evtest2 = evtest2 + binding + end + ) + assert_equal(25, eval("EVTEST1", x)) # constant in module + assert_equal(125, eval("evtest2", x)) # local var in module + bad = true + begin + eval("EVTEST1") + rescue NameError # must raise error + bad = false + end + assert(!bad) + self.class.class_eval do remove_const :EvTest - end + end if x end def test_nil_instance_eval_cvar diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 17ff5a2e82996b..9ee506c6f5916b 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -665,7 +665,7 @@ def test_backtrace_by_exception assert_equal([__FILE__, line], [loc.path, loc.lineno]) end - Bug4438 = '[ruby-core:35364]' + Bug4438 = '[ruby-core:35364]'.freeze def test_rescue_single_argument assert_raise(TypeError, Bug4438) do @@ -1063,38 +1063,39 @@ def test_message_of_name_error end def capture_warning_warn(category: false) - verbose = $VERBOSE - categories = Warning.categories.to_h {|cat| [cat, Warning[cat]]} - warning = [] + begin + verbose = $VERBOSE + categories = Warning.categories.to_h {|cat| [cat, Warning[cat]]} + warning = [] - ::Warning.class_eval do - alias_method :warn2, :warn - remove_method :warn + ::Warning.class_eval do + alias_method :warn2, :warn + remove_method :warn - if category - define_method(:warn) do |str, category: nil| - warning << [str, category] - end - else - define_method(:warn) do |str, category: nil| - warning << str + if category + define_method(:warn) do |str, category: nil| + warning << [str, category] + end + else + define_method(:warn) do |str, category: nil| + warning << str + end end end - end - $VERBOSE = true - Warning.categories.each {|cat| Warning[cat] = true} - yield - - return warning - ensure - $VERBOSE = verbose - categories.each {|cat, flag| Warning[cat] = flag} + $VERBOSE = true + Warning.categories.each {|cat| Warning[cat] = true} + yield + return warning + ensure + $VERBOSE = verbose + categories.each {|cat, flag| Warning[cat] = flag} - ::Warning.class_eval do - remove_method :warn - alias_method :warn, :warn2 - remove_method :warn2 + ::Warning.class_eval do + remove_method :warn + alias_method :warn, :warn2 + remove_method :warn2 + end end end diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb index a3d6221c0f924b..636f4447256722 100644 --- a/test/ruby/test_file.rb +++ b/test/ruby/test_file.rb @@ -250,10 +250,6 @@ def test_realpath tst = realdir + (File::SEPARATOR*3 + ".") assert_equal(realdir, File.realpath(tst)) assert_equal(realdir, File.realpath(".", tst)) - assert_equal(realdir, Dir.chdir(realdir) {File.realpath(".")}) - realpath = File.join(realdir, "test") - File.write(realpath, "") - assert_equal(realpath, Dir.chdir(realdir) {File.realpath("test")}) if File::ALT_SEPARATOR bug2961 = '[ruby-core:28653]' assert_equal(realdir, File.realpath(realdir.tr(File::SEPARATOR, File::ALT_SEPARATOR)), bug2961) @@ -261,6 +257,16 @@ def test_realpath } end + def test_realpath_ractor_unsafe + Dir.mktmpdir('rubytest-realpath') {|tmpdir| + realdir = File.realpath(tmpdir) + assert_equal(realdir, Dir.chdir(realdir) {File.realpath(".")}) + realpath = File.join(realdir, "test") + File.write(realpath, "") + assert_equal(realpath, Dir.chdir(realdir) {File.realpath("test")}) + } + end + def test_realpath_encoding fsenc = Encoding.find("filesystem") nonascii = "\u{0391 0410 0531 10A0 05d0 2C00 3042}" @@ -300,8 +306,6 @@ def test_realdirpath assert_equal(realdir, File.realdirpath(tst)) assert_equal(realdir, File.realdirpath(".", tst)) assert_equal(File.join(realdir, "foo"), File.realdirpath("foo", tst)) - assert_equal(realdir, Dir.chdir(realdir) {File.realdirpath(".")}) - assert_equal(File.join(realdir, "foo"), Dir.chdir(realdir) {File.realdirpath("foo")}) } begin result = File.realdirpath("bar", "//:/foo") @@ -313,6 +317,14 @@ def test_realdirpath end end + def test_realdirpath_ractor_unsafe + Dir.mktmpdir('rubytest-realdirpath') {|tmpdir| + realdir = File.realpath(tmpdir) + assert_equal(realdir, Dir.chdir(realdir) {File.realdirpath(".")}) + assert_equal(File.join(realdir, "foo"), Dir.chdir(realdir) {File.realdirpath("foo")}) + } + end + def test_realdirpath_junction Dir.mktmpdir('rubytest-realpath') {|tmpdir| Dir.chdir(tmpdir) do diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb index 222578be269581..16bc5d9271f30f 100644 --- a/test/ruby/test_file_exhaustive.rb +++ b/test/ruby/test_file_exhaustive.rb @@ -7,7 +7,7 @@ class TestFileExhaustive < Test::Unit::TestCase ROOT_REGEXP = %r'\A(?:[a-z]:(?=(/))|//[^/]+/[^/]+)'i - DRIVE = Dir.pwd[ROOT_REGEXP] + DRIVE = Dir.pwd[ROOT_REGEXP].freeze POSIX = /cygwin|mswin|bccwin|mingw|emx/ !~ RUBY_PLATFORM NTFS = !(/mingw|mswin|bccwin/ !~ RUBY_PLATFORM) @@ -19,7 +19,7 @@ def assert_incompatible_encoding end def setup - @dir = Dir.mktmpdir("ruby-test") + @dir = Dir.mktmpdir("#{Ractor.current.object_id}") File.chown(-1, Process.gid, @dir) end @@ -279,13 +279,20 @@ def test_stat_drive_root end if DRIVE def test_stat_dotted_prefix - Dir.mktmpdir do |dir| + Dir.mktmpdir("#{Ractor.current.object_id}") do |dir| prefix = File.join(dir, "...a") Dir.mkdir(prefix) assert_file.exist?(prefix) assert_nothing_raised { File.stat(prefix) } + end + end if NTFS + def test_stat_dotted_prefix_ractor_unsafe + Dir.mktmpdir("#{Ractor.current.object_id}") do |dir| + prefix = File.join(dir, "...a") + Dir.mkdir(prefix) + assert_file.exist?(prefix) Dir.chdir(dir) do assert_nothing_raised { File.stat(File.basename(prefix)) } end @@ -711,6 +718,9 @@ def test_utime File.utime(t + 1, t + 2, zerofile) assert_equal(t + 1, File.atime(zerofile)) assert_equal(t + 2, File.mtime(zerofile)) + end + + def test_utime_ractor_unsafe Dir.mktmpdir do |dir| Dir.chdir(dir) do path = "foo\u{30b3 30d4 30fc}" @@ -925,17 +935,19 @@ def test_expand_path_encoding end def test_expand_path_encoding_filesystem - home = ENV["HOME"] - ENV["HOME"] = "#{DRIVE}/UserHome" + begin + home = ENV["HOME"] + ENV["HOME"] = "#{DRIVE}/UserHome" - path = "~".encode("US-ASCII") - dir = "C:/".encode("IBM437") - fs = Encoding.find("filesystem") + path = "~".encode("US-ASCII") + dir = "C:/".encode("IBM437") + fs = Encoding.find("filesystem") - assert_equal fs, File.expand_path(path).encoding - assert_equal fs, File.expand_path(path, dir).encoding - ensure - ENV["HOME"] = home + assert_equal fs, File.expand_path(path).encoding + assert_equal fs, File.expand_path(path, dir).encoding + ensure + ENV["HOME"] = home + end end UnknownUserHome = "~foo_bar_baz_unknown_user_wahaha".freeze @@ -967,20 +979,22 @@ def test_expand_path_home end def test_expand_path_home_dir_string - home = ENV["HOME"] - new_home = "#{DRIVE}/UserHome" - ENV["HOME"] = new_home - bug8034 = "[ruby-core:53168]" - - assert_equal File.join(new_home, "foo"), File.expand_path("foo", "~"), bug8034 - assert_equal File.join(new_home, "bar", "foo"), File.expand_path("foo", "~/bar"), bug8034 - - assert_raise(ArgumentError) { File.expand_path(".", UnknownUserHome) } - assert_nothing_raised(ArgumentError) { File.expand_path("#{DRIVE}/", UnknownUserHome) } - ENV["HOME"] = "#{DRIVE}UserHome" - assert_raise(ArgumentError) { File.expand_path("~") } - ensure - ENV["HOME"] = home + begin + home = ENV["HOME"] + new_home = "#{DRIVE}/UserHome" + ENV["HOME"] = new_home + bug8034 = "[ruby-core:53168]" + + assert_equal File.join(new_home, "foo"), File.expand_path("foo", "~"), bug8034 + assert_equal File.join(new_home, "bar", "foo"), File.expand_path("foo", "~/bar"), bug8034 + + assert_raise(ArgumentError) { File.expand_path(".", UnknownUserHome) } + assert_nothing_raised(ArgumentError) { File.expand_path("#{DRIVE}/", UnknownUserHome) } + ENV["HOME"] = "#{DRIVE}UserHome" + assert_raise(ArgumentError) { File.expand_path("~") } + ensure + ENV["HOME"] = home + end end if /mswin|mingw/ =~ RUBY_PLATFORM @@ -1102,31 +1116,37 @@ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_a_curre end def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_home_as_base - old_home = ENV["HOME"] - home = ENV["HOME"] = "#{DRIVE}/UserHome" - assert_equal(home, File.expand_path("~")) - assert_equal(home, File.expand_path("~", "C:/FooBar")) - assert_equal(File.join(home, "a"), File.expand_path("~/a", "C:/FooBar")) - ensure - ENV["HOME"] = old_home + begin + old_home = ENV["HOME"] + home = ENV["HOME"] = "#{DRIVE}/UserHome" + assert_equal(home, File.expand_path("~")) + assert_equal(home, File.expand_path("~", "C:/FooBar")) + assert_equal(File.join(home, "a"), File.expand_path("~/a", "C:/FooBar")) + ensure + ENV["HOME"] = old_home + end end def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_unc_home - old_home = ENV["HOME"] - unc_home = ENV["HOME"] = "//UserHome" - assert_equal(unc_home, File.expand_path("~")) - ensure - ENV["HOME"] = old_home + begin + old_home = ENV["HOME"] + unc_home = ENV["HOME"] = "//UserHome" + assert_equal(unc_home, File.expand_path("~")) + ensure + ENV["HOME"] = old_home + end end if DRIVE def test_expand_path_does_not_modify_a_home_string_argument - old_home = ENV["HOME"] - home = ENV["HOME"] = "#{DRIVE}/UserHome" - str = "~/a" - assert_equal("#{home}/a", File.expand_path(str)) - assert_equal("~/a", str) - ensure - ENV["HOME"] = old_home + begin + old_home = ENV["HOME"] + home = ENV["HOME"] = "#{DRIVE}/UserHome" + str = "~/a" + assert_equal("#{home}/a", File.expand_path(str)) + assert_equal("~/a", str) + ensure + ENV["HOME"] = old_home + end end def test_expand_path_raises_argument_error_for_any_supplied_username @@ -1146,11 +1166,13 @@ def test_expand_path_error_for_nonexistent_username end unless DRIVE def test_expand_path_error_for_non_absolute_home - old_home = ENV["HOME"] - ENV["HOME"] = "./UserHome" - assert_raise_with_message(ArgumentError, /non-absolute home/) {File.expand_path("~")} - ensure - ENV["HOME"] = old_home + begin + old_home = ENV["HOME"] + ENV["HOME"] = "./UserHome" + assert_raise_with_message(ArgumentError, /non-absolute home/) {File.expand_path("~")} + ensure + ENV["HOME"] = old_home + end end def test_expand_path_raises_a_type_error_if_not_passed_a_string_type diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index d0d180593ab272..f01a78f66e591d 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -657,7 +657,7 @@ def (prec = Object.new).to_int; 2; end -18446744073709551616.8, -18446744073709551617.0, -18446744073709551618.0, - ] + ].freeze def test_truncate VS.each {|f| diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index a8a937f078a9da..a2d2aca368346b 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -330,10 +330,12 @@ def test_latest_gc_info assert_equal true, h[:immediate_sweep] assert_equal true, h.key?(:need_major_by) - GC.stress = true - assert_equal :force, GC.latest_gc_info[:major_by] - ensure - GC.stress = false + begin + GC.stress = true + assert_equal :force, GC.latest_gc_info[:major_by] + ensure + GC.stress = false + end end def test_latest_gc_info_argument diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index 32384f5a5c1fd4..8894dc7dcaa92f 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -62,17 +62,18 @@ def test_hash x.default = 5 assert_equal(5, x[23]) + z = nil x = Hash.new - def x.default(k) - $z = k + x.singleton_class.define_method(:default) do |k| + z = k self[k] = k*2 end - $z = 0 + z = 0 assert_equal(44, x[22]) - assert_equal(22, $z) - $z = 0 + assert_equal(22, z) + z = 0 assert_equal(44, x[22]) - assert_equal(0, $z) + assert_equal(0, z) end # From rubicon diff --git a/test/ruby/test_integer_comb.rb b/test/ruby/test_integer_comb.rb index 150f45cfd7ddb7..e293fb3dc1f54f 100644 --- a/test/ruby/test_integer_comb.rb +++ b/test/ruby/test_integer_comb.rb @@ -104,7 +104,7 @@ class TestIntegerComb < Test::Unit::TestCase 0xffffffffffffffffffffffffffffffffffffffffffffffff, 0x1000000000000000000000000000000000000000000000000, 0x1000000000000000000000000000000000000000000000001 - ] + ].freeze #VS.map! {|v| 0x4000000000000000.coerce(v)[0] } #VS.concat VS.find_all {|v| Fixnum === v }.map {|v| 0x4000000000000000.coerce(v)[0] } diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 0025aa5a7dd502..11e42e6b7387ce 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -77,6 +77,7 @@ def with_read_pipe(content) end def mkcdtmpdir + omit "Dir.chdir" unless main_ractor? Dir.mktmpdir {|d| Dir.chdir(d) { yield @@ -1145,7 +1146,7 @@ def test_copy_stream_strio_to_tempfile assert_equal("abcd", dst.read) assert_equal(4, pos, bug11015) ensure - dst.close! + dst&.close! end def test_copy_stream_pathname_to_pathname @@ -1916,6 +1917,7 @@ def test_pid_after_close_read end def make_tempfile + pend "Tempfile" unless main_ractor? t = Tempfile.new("test_io") t.binmode t.puts "foo" @@ -2539,7 +2541,7 @@ def test_autoclose feature2250 = '[ruby-core:26222]' pre = 'ft2250' - Dir.mktmpdir {|d| + Dir.mktmpdir("#{Ractor.current.object_id}") {|d| t = open("#{d}/#{pre}", "w") f = IO.for_fd(t.fileno) assert_equal(true, f.autoclose?) @@ -2590,7 +2592,7 @@ def test_autoclose_false_closed_by_finalizer assert_nothing_raised(Errno::EBADF, feature2250) {t.close} end ensure - t.close! + t&.close! end def test_open_redirect @@ -2823,22 +2825,25 @@ def test_reopen_opt_encoding bug11320 = '[ruby-core:69780] [Bug #11320]' ["UTF-8", "EUC-JP", "Shift_JIS"].each do |enc| - define_method("test_reopen_nonascii(#{enc})") do - mkcdtmpdir do - fname = "\u{30eb 30d3 30fc}".encode(enc) - File.write(fname, '') - assert_file.exist?(fname) - stdin = $stdin.dup - begin - assert_nothing_raised(Errno::ENOENT, "#{bug11320}: #{enc}") { - $stdin.reopen(fname, 'r') - } - ensure - $stdin.reopen(stdin) - stdin.close + class_eval <<-RUBY + def test_reopen_nonascii_#{enc.sub('-', '_')} + mkcdtmpdir do + enc = #{enc.inspect} + fname = "#{'\u{30eb 30d3 30fc}'}".encode(enc) + File.write(fname, '') + assert_file.exist?(fname) + stdin = $stdin.dup + begin + assert_nothing_raised(Errno::ENOENT, "#{bug11320}: #{enc}") { + $stdin.reopen(fname, 'r') + } + ensure + $stdin.reopen(stdin) + stdin.close + end end end - end + RUBY end def test_reopen_ivar @@ -3629,7 +3634,7 @@ def test_ioctl_linux end if /^(?:i.?86|x86_64)-linux/ =~ RUBY_PLATFORM def test_ioctl_linux2 - return unless STDIN.tty? # stdin is not a terminal + return unless $stdin.tty? # stdin is not a terminal begin f = File.open('/dev/tty') rescue Errno::ENOENT, Errno::ENXIO => e @@ -3686,14 +3691,17 @@ def test_setpos end def test_std_fileno - assert_equal(0, STDIN.fileno) - assert_equal(1, STDOUT.fileno) - assert_equal(2, STDERR.fileno) assert_equal(0, $stdin.fileno) assert_equal(1, $stdout.fileno) assert_equal(2, $stderr.fileno) end + def test_std_fileno_ractor_unsafe + assert_equal(0, STDIN.fileno) + assert_equal(1, STDOUT.fileno) + assert_equal(2, STDERR.fileno) + end + def test_frozen_fileno bug9865 = '[ruby-dev:48241] [Bug #9865]' with_pipe do |r,w| @@ -3786,7 +3794,7 @@ def assert_buffer_not_raise_shared_string_error end assert_equal(data, w.join(""), bug9847) ensure - t.close! + t&.close! end def test_read_buffer_not_raise_shared_string_error @@ -4459,4 +4467,20 @@ def test_fork_close assert_predicate(status, :success?) RUBY end + + def test_no_fork_in_ractor + omit "fork is not supported" unless Process.respond_to?(:fork) + + assert_ractor(<<~'RUBY') + r = Ractor.new do + begin + fork { } + :ng + rescue Ractor::IsolationError + :ok + end + end + assert_equal :ok, r.value + RUBY + end end diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 62c46678882a10..88a9e902594a72 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -334,7 +334,7 @@ def test_zero_length_get_string end # We check that values are correctly round tripped. - RANGES = { + Ractor.make_shareable(RANGES = { :U8 => [0, 2**8-1], :S8 => [-2**7, 0, 2**7-1], @@ -355,7 +355,7 @@ def test_zero_length_get_string :F32 => [-1.0, 0.0, 0.5, 1.0, 128.0], :F64 => [-1.0, 0.0, 0.5, 1.0, 128.0], - } + }) def test_get_set_value buffer = IO::Buffer.new(128) diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb index b01d627d920d7e..465858fa67e218 100644 --- a/test/ruby/test_io_m17n.rb +++ b/test/ruby/test_io_m17n.rb @@ -11,9 +11,10 @@ class TestIO_M17N < Test::Unit::TestCase Encoding::EUC_JP, Encoding::Shift_JIS, Encoding::UTF_8 - ] + ].freeze def with_tmpdir + omit "Dir.chdir" unless main_ractor? Dir.mktmpdir {|dir| Dir.chdir(dir) { yield dir @@ -404,18 +405,18 @@ def test_dup_undef end def test_stdin - assert_equal(Encoding.default_external, STDIN.external_encoding) - assert_equal(nil, STDIN.internal_encoding) + assert_equal(Encoding.default_external, $stdin.external_encoding) + assert_equal(nil, $stdin.internal_encoding) end def test_stdout - assert_equal(nil, STDOUT.external_encoding) - assert_equal(nil, STDOUT.internal_encoding) + assert_equal(nil, $stdout.external_encoding) + assert_equal(nil, $stdout.internal_encoding) end def test_stderr - assert_equal(nil, STDERR.external_encoding) - assert_equal(nil, STDERR.internal_encoding) + assert_equal(nil, $stderr.external_encoding) + assert_equal(nil, $stderr.internal_encoding) end def test_terminator_conversion @@ -1657,18 +1658,17 @@ def test_textmode_decode_universal_newline_utf16 } end - SYSTEM_NEWLINE = [] def system_newline - return SYSTEM_NEWLINE.first if !SYSTEM_NEWLINE.empty? + return @_system_newline if defined?(@_system_newline) with_tmpdir { open("newline", "wt") {|f| f.print "\n" } open("newline", "rb") {|f| - SYSTEM_NEWLINE << f.read + @_system_newline = f.read } } - SYSTEM_NEWLINE.first + @_system_newline end def test_textmode_encode_newline @@ -2168,31 +2168,34 @@ def test_w_xml_attr end %w/UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE/.each do |name| - define_method("test_strip_bom:#{name}") do - path = "#{name}-bom.txt" - with_tmpdir { - text = "\uFEFF\u0100a" - stripped = "\u0100a" - content = text.encode(name) - generate_file(path, content) - result = File.read(path, mode: 'rb:BOM|UTF-8') - assert_equal(Encoding.find(name), result.encoding, name) - assert_equal(content[1..-1].b, result.b, name) - %w[rb rt r].each do |mode| - message = "#{name}, mode: #{mode.dump}" - result = File.read(path, mode: "#{mode}:BOM|UTF-8:UTF-8") - assert_equal(Encoding::UTF_8, result.encoding, message) - assert_equal(stripped, result, message) - end + class_eval <<-RUBY + def test_strip_bom_#{name.sub('-', '_')} + name = #{name.inspect} + path = "#{name}-bom.txt" + with_tmpdir { + text = "#{'\uFEFF\u0100a'}" + stripped = "#{'\u0100a'}" + content = text.encode(name) + generate_file(path, content) + result = File.read(path, mode: 'rb:BOM|UTF-8') + assert_equal(Encoding.find(name), result.encoding, name) + assert_equal(content[1..-1].b, result.b, name) + %w[rb rt r].each do |mode| + message = #{name.inspect} + ", mode: \#{mode.dump}" + result = File.read(path, mode: "\#{mode}:BOM|UTF-8:UTF-8") + assert_equal(Encoding::UTF_8, result.encoding, message) + assert_equal(stripped, result, message) + end - File.open(path, "rb") {|f| - assert_equal(Encoding.find(name), f.set_encoding_by_bom) - } - File.open(path, "rb", encoding: "iso-8859-1") {|f| - assert_raise(ArgumentError) {f.set_encoding_by_bom} + File.open(path, "rb") {|f| + assert_equal(Encoding.find(name), f.set_encoding_by_bom) + } + File.open(path, "rb", encoding: "iso-8859-1") {|f| + assert_raise(ArgumentError) {f.set_encoding_by_bom} + } } - } - end + end + RUBY end def test_strip_bom_no_conv diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index fa716787fe9841..1f1d320fd2e793 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -688,8 +688,8 @@ def test_to_binary_line_info class P def p; end def q; end - E = "" - N = "#{E}" + E = "".freeze + N = "#{E}".freeze attr_reader :i end end; diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index c836abd0c663b4..000d049a6e2e89 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -42,7 +42,7 @@ def test_f3 end - define_method(:f4) {|str: "foo", num: 424242| [str, num] } + define_method(:f4, &Ractor.make_shareable(proc {|str: "foo", num: 424242| [str, num] })) def test_f4 assert_equal(["foo", 424242], f4) @@ -54,7 +54,7 @@ def test_f4 end - define_method(:f5) {|str: "foo", num: 424242, **h| [str, num, h] } + define_method(:f5, &Ractor.make_shareable(proc {|str: "foo", num: 424242, **h| [str, num, h] })) def test_f5 assert_equal(["foo", 424242, {}], f5) @@ -98,9 +98,9 @@ def test_f7 # [ruby-core:41772] assert_equal([[1, 2, 3], "bar", 424242, {}], f7(1, 2, 3, str: "bar")) end - define_method(:f8) { |opt = :ion, *rest, key: :word| + define_method(:f8, &Ractor.make_shareable(proc { |opt = :ion, *rest, key: :word| [opt, rest, key] - } + })) def test_f8 assert_equal([:ion, [], :word], f8) @@ -966,7 +966,7 @@ def test_Thread_new_kwsplat assert_equal([1, h3], t.new(**h3, &f).value) assert_equal([1, h3], t.new(a: 1, **h2, &f).value) ensure - Thread.report_on_exception = true + Thread.report_on_exception = true if main_ractor? end def test_Fiber_resume_kwsplat diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb index dbff3c4734275d..15de867ea1d9ca 100644 --- a/test/ruby/test_literal.rb +++ b/test/ruby/test_literal.rb @@ -537,7 +537,7 @@ def test_hash_key_tampering assert_equal(100, h['a']) end - FOO = "foo" + FOO = "foo".freeze def test_hash_value_omission x = 1 diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb index 9f7a3c7f4b7ec4..026148e5f48a98 100644 --- a/test/ruby/test_m17n.rb +++ b/test/ruby/test_m17n.rb @@ -186,35 +186,33 @@ def test_string_inspect_invalid end def test_string_inspect_encoding - [ - Encoding::UTF_8, - Encoding::EUC_JP, - Encoding::Windows_31J, - Encoding::GB18030, - ].each do |e| - EnvUtil.with_default_external(e) do - str = "\x81\x30\x81\x30".force_encoding('GB18030') - assert_equal(Encoding::GB18030 == e ? %{"#{str}"} : '"\x{81308130}"', str.inspect) - str = e("\xa1\x8f\xa1\xa1") - expected = "\"\\xA1\x8F\xA1\xA1\"".force_encoding("EUC-JP") - assert_equal(Encoding::EUC_JP == e ? expected : "\"\\xA1\\x{8FA1A1}\"", str.inspect) - str = s("\x81@") - assert_equal(Encoding::Windows_31J == e ? %{"#{str}"} : '"\x{8140}"', str.inspect) - str = "\u3042\u{10FFFD}" - assert_equal(Encoding::UTF_8 == e ? %{"#{str}"} : '"\u3042\u{10FFFD}"', str.inspect) - end - end - - EnvUtil.with_default_external(Encoding::UTF_8) do - [ - Encoding::UTF_16BE, - Encoding::UTF_16LE, - Encoding::UTF_32BE, - Encoding::UTF_32LE, - Encoding::UTF8_SOFTBANK - ].each do |e| - str = "abc".encode(e) - assert_equal('"abc"', str.inspect) + EnvUtil.suppress_warning do + begin + orig_int = Encoding.default_internal + orig_ext = Encoding.default_external + Encoding.default_internal = nil + [Encoding::UTF_8, Encoding::EUC_JP, Encoding::Windows_31J, Encoding::GB18030]. + each do |e| + Encoding.default_external = e + str = "\x81\x30\x81\x30".force_encoding('GB18030') + assert_equal(Encoding::GB18030 == e ? %{"#{str}"} : '"\x{81308130}"', str.inspect) + str = e("\xa1\x8f\xa1\xa1") + expected = "\"\\xA1\x8F\xA1\xA1\"".force_encoding("EUC-JP") + assert_equal(Encoding::EUC_JP == e ? expected : "\"\\xA1\\x{8FA1A1}\"", str.inspect) + str = s("\x81@") + assert_equal(Encoding::Windows_31J == e ? %{"#{str}"} : '"\x{8140}"', str.inspect) + str = "\u3042\u{10FFFD}" + assert_equal(Encoding::UTF_8 == e ? %{"#{str}"} : '"\u3042\u{10FFFD}"', str.inspect) + end + Encoding.default_external = Encoding::UTF_8 + [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE, + Encoding::UTF8_SOFTBANK].each do |e| + str = "abc".encode(e) + assert_equal('"abc"', str.inspect) + end + ensure + Encoding.default_internal = orig_int + Encoding.default_external = orig_ext end end end @@ -248,11 +246,20 @@ def test_utf_without_bom_valid end def test_object_utf16_32_inspect - EnvUtil.with_default_external(Encoding::UTF_8) do - o = Object.new - [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE].each do |e| - o.instance_eval "undef inspect;def inspect;'abc'.encode('#{e}');end" - assert_equal '[abc]', [o].inspect + EnvUtil.suppress_warning do + begin + orig_int = Encoding.default_internal + orig_ext = Encoding.default_external + Encoding.default_internal = nil + Encoding.default_external = Encoding::UTF_8 + o = Object.new + [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE].each do |e| + o.instance_eval "undef inspect;def inspect;'abc'.encode('#{e}');end" + assert_equal '[abc]', [o].inspect + end + ensure + Encoding.default_internal = orig_int + Encoding.default_external = orig_ext end end end diff --git a/test/ruby/test_m17n_comb.rb b/test/ruby/test_m17n_comb.rb index e48a1948beaa1c..2a554457874b0f 100644 --- a/test/ruby/test_m17n_comb.rb +++ b/test/ruby/test_m17n_comb.rb @@ -25,7 +25,7 @@ def assert_strenc(bytes, enc, actual, message=nil) assert_equal(b(bytes), b(actual), message) end - STRINGS = [ + STRINGS = Ractor.make_shareable([ b(""), e(""), s(""), u(""), b("a"), e("a"), s("a"), u("a"), b("."), e("."), s("."), u("."), @@ -53,13 +53,13 @@ def assert_strenc(bytes, enc, actual, message=nil) # for transitivity test u("\xe0\xa0\xa1"), e("\xe0\xa0\xa1"), s("\xe0\xa0\xa1"), # [ruby-dev:32693] e("\xa1\xa1"), b("\xa1\xa1"), s("\xa1\xa1"), # [ruby-dev:36484] - ] + ]) - WSTRINGS = [ + WSTRINGS = Ractor.make_shareable([ "aa".force_encoding("utf-16be"), "aaaa".force_encoding("utf-32be"), "aaa".force_encoding("utf-32be"), - ] + ]) def combination(*args, &b) AllPairs.each(*args, &b) @@ -177,12 +177,12 @@ def each_slice_call } end - ASCII_INCOMPATIBLE_ENCODINGS = %w[ + ASCII_INCOMPATIBLE_ENCODINGS = Ractor.make_shareable(%w[ UTF-16BE UTF-16LE UTF-32BE UTF-32LE - ] + ]) def str_enc_compatible?(*strs) encs = [] ascii_incompatible_encodings = {} diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb index eb669948017d98..de3d73d48bf3b5 100644 --- a/test/ruby/test_marshal.rb +++ b/test/ruby/test_marshal.rb @@ -72,23 +72,26 @@ def test_marshal_cloned_class end def test_inconsistent_struct - TestMarshal.const_set :StructOrNot, Struct.new(:a) - s = Marshal.dump(StructOrNot.new(1)) - TestMarshal.instance_eval { remove_const :StructOrNot } - TestMarshal.const_set :StructOrNot, Class.new + const_name = "StructOrNot#{Ractor.current.object_id}".to_sym + TestMarshal.const_set const_name, Struct.new(:a) + s = Marshal.dump(TestMarshal.const_get(const_name).new(1)) + TestMarshal.instance_eval { remove_const const_name } + TestMarshal.const_set const_name, Class.new assert_raise(TypeError, "[ruby-dev:31709]") { Marshal.load(s) } ensure - TestMarshal.instance_eval { remove_const :StructOrNot } + TestMarshal.instance_eval { remove_const const_name } end def test_struct_invalid_members - TestMarshal.const_set :StructInvalidMembers, Struct.new(:a) - assert_raise(TypeError, "[ruby-dev:31759]") { - Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo") - TestMarshal::StructInvalidMembers.members - } - ensure - TestMarshal.instance_eval { remove_const :StructInvalidMembers } + begin + TestMarshal.const_set :StructInvalidMembers, Struct.new(:a) + assert_raise(TypeError, "[ruby-dev:31759]") { + Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo") + TestMarshal::StructInvalidMembers.members + } + ensure + TestMarshal.instance_eval { remove_const :StructInvalidMembers } + end end def test_load_range_as_struct @@ -334,11 +337,11 @@ def test_regexp2 class DumpTest def marshal_dump - @@block.call(:marshal_dump) + @block.call(:marshal_dump) end def dump_each(&block) - @@block = block + @block = block Marshal.dump(self) end end @@ -404,14 +407,14 @@ def test_marshal_dump class C6 def initialize - @stdin = STDIN + @stdin = $stdin end attr_reader :stdin def marshal_dump 1 end def marshal_load(x) - @stdin = STDIN + @stdin = $stdin end end def test_marshal_dump_extra_iv @@ -421,7 +424,7 @@ def test_marshal_dump_extra_iv m = Marshal.dump(o) } o2 = Marshal.load(m) - assert_equal(STDIN, o2.stdin) + assert_equal($stdin, o2.stdin) end def test_marshal_string_encoding @@ -662,28 +665,28 @@ def test_continuation end def test_undumpable_message - c = Module.new {break module_eval("class IO\u{26a1} < IO;self;end")} - assert_raise_with_message(TypeError, /IO\u{26a1}/) { + c = Module.new {break module_eval("class IO#{Ractor.current.object_id}\u{26a1} < IO;self;end")} + assert_raise_with_message(TypeError, /IO#{Ractor.current.object_id}\u{26a1}/) { Marshal.dump(c.new(0, autoclose: false)) } end def test_undumpable_data - c = Module.new {break module_eval("class T\u{23F0 23F3} do + MethodInMethodClass_Setup = Ractor.make_shareable(proc do remove_const :MethodInMethodClass if defined? MethodInMethodClass class MethodInMethodClass @@ -1573,7 +1574,7 @@ def m2 end private end - end + end) def test_method_in_method_visibility_should_be_public MethodInMethodClass_Setup.call diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 62b2a7164fb4ff..c8324b9de3a942 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -87,13 +87,13 @@ def user3 private :user3 end - OtherSetup = -> do + OtherSetup = Ractor.make_shareable(proc do remove_const :Other if defined? ::TestModule::Other module Other def other end end - end + end) class AClass def AClass.cm1 @@ -253,8 +253,9 @@ def test_const_defined? assert_operator(Math, :const_defined?, "PI") assert_not_operator(Math, :const_defined?, :IP) assert_not_operator(Math, :const_defined?, "IP") + end - # Test invalid symbol name + def test_const_defined_invalid_symbol_name # [Bug #20245] EnvUtil.under_gc_stress do assert_raise(EncodingError) do @@ -832,8 +833,9 @@ def test_method_defined_without_include_super assert User.method_defined?(:user, false) assert !User.method_defined?(:mixin, false) assert Mixin.method_defined?(:mixin, false) + const_name = "FOO#{Ractor.current.object_id}" - User.const_set(:FOO, c = Class.new) + User.const_set(const_name, c = Class.new) c.prepend(User) assert !c.method_defined?(:user, false) @@ -850,7 +852,7 @@ def test_method_defined_without_include_super # cleanup User.class_eval do - remove_const :FOO + remove_const const_name end end @@ -942,15 +944,16 @@ def test_classpath assert_match(/::N$/, m::N.name) assert_match(/\A#::O\z/, m::O.name) assert_match(/\A#::C\z/, m::C.name) - self.class.const_set(:M, m) - prefix = self.class.name + "::M::" + m_name = "M#{Ractor.current.object_id}" + self.class.const_set(m_name, m) + prefix = self.class.name + "::#{m_name}::" assert_equal(prefix+"N", m.const_get(:N).name) assert_equal(prefix+"O", m.const_get(:O).name) assert_equal(prefix+"C", m.const_get(:C).name) c = m.class_eval("Bug15891 = Class.new.freeze") assert_equal(prefix+"Bug15891", c.name) ensure - self.class.class_eval {remove_const(:M)} + self.class.class_eval {remove_const(m_name)} end def test_private_class_method @@ -1738,14 +1741,15 @@ def test_send def test_nonascii_name - c = eval("class ::C\u{df}; self; end") - assert_equal("C\u{df}", c.name, '[ruby-core:24600]') - c = eval("class C\u{df}; self; end") - assert_equal("TestModule::C\u{df}", c.name, '[ruby-core:24600]') + c_name = "C#{Ractor.current.object_id}" + c = eval("class ::#{c_name}\u{df}; self; end") + assert_equal("#{c_name}\u{df}", c.name, '[ruby-core:24600]') + c = eval("class #{c_name}\u{df}; self; end") + assert_equal("TestModule::#{c_name}\u{df}", c.name, '[ruby-core:24600]') c = Module.new.module_eval("class X\u{df} < Module; self; end") assert_match(/::X\u{df}:/, c.new.to_s) ensure - Object.send(:remove_const, "C\u{df}") + Object.send(:remove_const, "#{c_name}\u{df}") end @@ -2017,7 +2021,7 @@ def test_attr_writer_with_no_arguments def test_private_constant_in_class c = Class.new - c.const_set(:FOO, "foo") + c.const_set(:FOO, "foo".freeze) assert_equal("foo", c::FOO) c.private_constant(:FOO) e = assert_raise(NameError) {c::FOO} @@ -2026,7 +2030,7 @@ def test_private_constant_in_class assert_equal("foo", c.class_eval("FOO")) assert_equal("foo", c.const_get("FOO")) $VERBOSE, verbose = nil, $VERBOSE - c.const_set(:FOO, "foo") + c.const_set(:FOO, "foo".freeze) $VERBOSE = verbose e = assert_raise(NameError) {c::FOO} assert_equal(c, e.receiver) @@ -2040,7 +2044,7 @@ def test_private_constant_in_class def test_private_constant_in_module m = Module.new - m.const_set(:FOO, "foo") + m.const_set(:FOO, "foo".freeze) assert_equal("foo", m::FOO) m.private_constant(:FOO) e = assert_raise(NameError) {m::FOO} @@ -2049,7 +2053,7 @@ def test_private_constant_in_module assert_equal("foo", m.class_eval("FOO")) assert_equal("foo", m.const_get("FOO")) $VERBOSE, verbose = nil, $VERBOSE - m.const_set(:FOO, "foo") + m.const_set(:FOO, "foo".freeze) $VERBOSE = verbose e = assert_raise(NameError) {m::FOO} assert_equal(m, e.receiver) @@ -2068,8 +2072,8 @@ def test_private_constant_in_module def test_private_constant2 c = Class.new - c.const_set(:FOO, "foo") - c.const_set(:BAR, "bar") + c.const_set(:FOO, "foo".freeze) + c.const_set(:BAR, "bar".freeze) assert_equal("foo", c::FOO) assert_equal("bar", c::BAR) c.private_constant(:FOO, :BAR) @@ -2108,21 +2112,22 @@ class PrivateClass private_constant :PrivateClass def test_define_module_under_private_constant + const_name = "TestModule#{Ractor.current.object_id}" assert_raise(NameError) do eval %q{class TestModule::PrivateClass; end} end assert_raise(NameError) do - eval %q{module TestModule::PrivateClass::TestModule; end} + eval %Q{module TestModule::PrivateClass::#{const_name}; end} end eval %q{class PrivateClass; end} - eval %q{module PrivateClass::TestModule; end} - assert_instance_of(Module, PrivateClass::TestModule) - PrivateClass.class_eval { remove_const(:TestModule) } + eval %Q{module PrivateClass::#{const_name}; end} + assert_instance_of(Module, PrivateClass.const_get(const_name)) + PrivateClass.class_eval { remove_const(const_name) } end def test_public_constant c = Class.new - c.const_set(:FOO, "foo") + c.const_set(:FOO, "foo".freeze) assert_equal("foo", c::FOO) c.private_constant(:FOO) assert_raise(NameError) { c::FOO } @@ -2133,7 +2138,7 @@ def test_public_constant def test_deprecate_constant c = Class.new - c.const_set(:FOO, "foo") + c.const_set(:FOO, "foo".freeze) c.deprecate_constant(:FOO) assert_warn(/deprecated/) do Warning[:deprecated] = true @@ -2825,7 +2830,7 @@ def a assert_equal 'A', a.new.a, '[ruby-core:17019]' end - Bug6891 = '[ruby-core:47241]' + Bug6891 = '[ruby-core:47241]'.freeze def test_extend_module_with_protected_method list = [] @@ -3242,7 +3247,7 @@ def foo; bar; end } end - ConstLocation = [__FILE__, __LINE__] + ConstLocation = Ractor.make_shareable([__FILE__, __LINE__]) def test_const_source_location assert_equal(ConstLocation, self.class.const_source_location(:ConstLocation)) diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index cccd7359e1108e..b64b4eaf6e5ac1 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -339,6 +339,9 @@ def test_remove_instance_variable 'T_CLASS,T_MODULE' => Class.new(Object), 'generic ivar' => '', }.each do |desc, o| + if o.is_a?(Class) && !main_ractor? + next + end e = assert_raise(NameError, "#{desc} iv removal raises before set") do o.remove_instance_variable(:@foo) end @@ -373,14 +376,14 @@ def c = @c o1.instance_variable_set(:@a, 0) o1.instance_variable_set(:@b, 1) o1.instance_variable_set(:@c, 2) - refute_includes ObjectSpace.dump(o1), '"embedded":true' + refute_includes ObjectSpace.dump(o1), '"embedded":true' if main_ractor? o1.remove_instance_variable(:@foo) - assert_includes ObjectSpace.dump(o1), '"embedded":true' + assert_includes ObjectSpace.dump(o1), '"embedded":true' if main_ractor? o2.instance_variable_set(:@a, 0) o2.instance_variable_set(:@b, 1) o2.instance_variable_set(:@c, 2) - assert_includes ObjectSpace.dump(o2), '"embedded":true' + assert_includes ObjectSpace.dump(o2), '"embedded":true' if main_ractor? assert_equal(0, o1.a) assert_equal(1, o1.b) @@ -1056,7 +1059,7 @@ def test_bad_initialize_copy assert_not_initialize_copy {Enumerator::Yielder.new {}} assert_not_initialize_copy {File.stat(__FILE__)} assert_not_initialize_copy {open(__FILE__)}.each(&:close) - assert_not_initialize_copy {ARGF.class.new} + assert_not_initialize_copy {ARGF.class.new} if main_ractor? assert_not_initialize_copy {Random.new} assert_not_initialize_copy {//} assert_not_initialize_copy {/.*/.match("foo")} diff --git a/test/ruby/test_object_id.rb b/test/ruby/test_object_id.rb index adb819febce57c..368826480ca233 100644 --- a/test/ruby/test_object_id.rb +++ b/test/ruby/test_object_id.rb @@ -12,6 +12,7 @@ def test_dup_new_id end def test_dup_with_ivar_and_id + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? id = @obj.object_id @obj.instance_variable_set(:@foo, 42) @@ -21,6 +22,7 @@ def test_dup_with_ivar_and_id end def test_dup_with_id_and_ivar + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? @obj.instance_variable_set(:@foo, 42) id = @obj.object_id @@ -30,6 +32,7 @@ def test_dup_with_id_and_ivar end def test_dup_with_id_and_ivar_and_frozen + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? @obj.instance_variable_set(:@foo, 42) @obj.freeze id = @obj.object_id @@ -46,6 +49,7 @@ def test_clone_new_id end def test_clone_with_ivar_and_id + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? id = @obj.object_id @obj.instance_variable_set(:@foo, 42) @@ -55,6 +59,7 @@ def test_clone_with_ivar_and_id end def test_clone_with_id_and_ivar + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? @obj.instance_variable_set(:@foo, 42) id = @obj.object_id @@ -64,6 +69,7 @@ def test_clone_with_id_and_ivar end def test_clone_with_id_and_ivar_and_frozen + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? @obj.instance_variable_set(:@foo, 42) @obj.freeze id = @obj.object_id @@ -76,6 +82,7 @@ def test_clone_with_id_and_ivar_and_frozen def test_marshal_new_id return pass if @obj.is_a?(Module) + omit "class ivars" if @obj.is_a?(Module) && !main_ractor? id = @obj.object_id refute_equal id, Marshal.load(Marshal.dump(@obj)).object_id @@ -117,6 +124,7 @@ def test_marshal_with_id_and_ivar_and_frozen end def test_object_id_need_resize + omit "class ivars" if !main_ractor? && @obj.is_a?(Module) (3 - @obj.instance_variables.size).times do |i| @obj.instance_variable_set("@a_#{i}", "[Bug #21445]") end @@ -165,6 +173,7 @@ class TooComplex < Module end def setup + omit "class ivars" unless main_ractor? if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS) assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS end diff --git a/test/ruby/test_objectspace.rb b/test/ruby/test_objectspace.rb index a479547599a03a..86e3a532bb7bad 100644 --- a/test/ruby/test_objectspace.rb +++ b/test/ruby/test_objectspace.rb @@ -7,10 +7,10 @@ def self.deftest_id2ref(obj) file = $` line = $1.to_i code = <<"End" - define_method("test_id2ref_#{line}") {\ + define_method("test_id2ref_#{line}", &Ractor.make_shareable(proc {\ o = EnvUtil.suppress_warning { ObjectSpace._id2ref(obj.object_id) } assert_same(obj, o, "didn't round trip: \#{obj.inspect}");\ - } + })) End eval code, binding, file, line end @@ -29,7 +29,7 @@ def self.deftest_id2ref(obj) deftest_id2ref(:a) deftest_id2ref(:abcdefghijilkjl) deftest_id2ref(:==) - deftest_id2ref(Object.new) + deftest_id2ref(Object.new.freeze) deftest_id2ref(self) deftest_id2ref(true) deftest_id2ref(false) diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb index 98e95b98afaf77..36df50ed54b537 100644 --- a/test/ruby/test_parse.rb +++ b/test/ruby/test_parse.rb @@ -1449,11 +1449,11 @@ def test_command_def_cmdarg end; end - NONASCII_CONSTANTS = [ + NONASCII_CONSTANTS = Ractor.make_shareable([ *%W"\u{00de} \u{00C0}".flat_map {|c| [c, c.encode("iso-8859-15")]}, "\u{1c4}", "\u{1f2}", "\u{1f88}", "\u{370}", *%W"\u{391} \u{ff21}".flat_map {|c| [c, c.encode("cp932"), c.encode("euc-jp")]}, - ] + ]) def assert_nonascii_const assert_all_assertions_foreach("NONASCII_CONSTANTS", *NONASCII_CONSTANTS) do |n| diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb index 92a3244fc2ae88..9c04e352006390 100644 --- a/test/ruby/test_pattern_matching.rb +++ b/test/ruby/test_pattern_matching.rb @@ -429,7 +429,9 @@ def test_pin_operator_value_pattern true end end + end + def test_pin_operator_value_pattern_ractor_unsafe assert_block do @@TestPatternMatching = /a/ case 'abc' diff --git a/test/ruby/test_primitive.rb b/test/ruby/test_primitive.rb index f1db934000dc23..2116fd2d5a5924 100644 --- a/test/ruby/test_primitive.rb +++ b/test/ruby/test_primitive.rb @@ -26,7 +26,7 @@ def test_lvar assert_equal 4, c end - C_Setup = -> do + C_Setup = Ractor.make_shareable(proc do remove_const :C if defined? ::TestRubyPrimitive::C remove_const :A if defined? ::TestRubyPrimitive::A @@ -46,7 +46,7 @@ def const (1..2).map { A::B::C::Const } - end + end) def test_constant C_Setup.call @@ -119,7 +119,7 @@ class A5 } end - def test_constatant_cache4 + def test_constant_cache4 assert_equal 8, $test_ruby_primitive_constant_cache4 end @@ -224,19 +224,6 @@ def test_opassign @iv += 2 assert_equal 4, @iv - # init @@cv - @@cv = nil - - @@cv ||= 1 - assert_equal 1, @@cv - @@cv &&= 2 - assert_equal 2, @@cv - @@cv ||= 99 - assert_equal 2, @@cv - - $gv = 3 - $gv += 4 - assert_equal 7, $gv obj = A10.new obj.a = 9 @@ -265,6 +252,22 @@ def test_opassign assert_equal [0, :bar, 4], a end + def test_opassign_ractor_unsafe + # init @@cv + @@cv = nil + + @@cv ||= 1 + assert_equal 1, @@cv + @@cv &&= 2 + assert_equal 2, @@cv + @@cv ||= 99 + assert_equal 2, @@cv + + $gv = 3 + $gv += 4 + assert_equal 7, $gv + end + def test_opassign_and_or a = 1 a ||= 2 diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 2cd97ca32464ab..8b5fb57176af71 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -513,7 +513,7 @@ def test_binding_source_location file, lineno = method(:source_location_test).to_proc.binding.source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_source_location_test[0], lineno, 'Bug #2427') + assert_equal(LINE_OF_SOURCE_LOCATION_TEST[0], lineno, 'Bug #2427') end def test_binding_error_unless_ruby_frame @@ -1499,7 +1499,7 @@ def test_to_s assert_include(EnvUtil.labeled_class(name, Proc).new {}.to_s, name) end - @@line_of_source_location_test = [__LINE__ + 1, 2, __LINE__ + 3, 5] + LINE_OF_SOURCE_LOCATION_TEST = [__LINE__ + 1, 2, __LINE__ + 3, 5].freeze def source_location_test a=1, b=2 end @@ -1507,16 +1507,16 @@ def source_location_test a=1, def test_source_location file, *loc = method(:source_location_test).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_source_location_test, loc, 'Bug #2427') + assert_equal(LINE_OF_SOURCE_LOCATION_TEST, loc, 'Bug #2427') file, *loc = self.class.instance_method(:source_location_test).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_source_location_test, loc, 'Bug #2427') + assert_equal(LINE_OF_SOURCE_LOCATION_TEST, loc, 'Bug #2427') end - @@line_of_attr_reader_source_location_test = __LINE__ + 3 - @@line_of_attr_writer_source_location_test = __LINE__ + 3 - @@line_of_attr_accessor_source_location_test = __LINE__ + 3 + LINE_OF_ATTR_READER_SOURCE_LOCATION_TEST = __LINE__ + 3 + LINE_OF_ATTR_WRITER_SOURCE_LOCATION_TEST = __LINE__ + 3 + LINE_OF_ATTR_ACCESSOR_SOURCE_LOCATION_TEST = __LINE__ + 3 attr_reader :attr_reader_source_location_test attr_writer :attr_writer_source_location_test attr_accessor :attr_accessor_source_location_test @@ -1524,19 +1524,19 @@ def test_source_location def test_attr_source_location file, lineno = method(:attr_reader_source_location_test).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_attr_reader_source_location_test, lineno) + assert_equal(LINE_OF_ATTR_READER_SOURCE_LOCATION_TEST, lineno) file, lineno = method(:attr_writer_source_location_test=).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_attr_writer_source_location_test, lineno) + assert_equal(LINE_OF_ATTR_WRITER_SOURCE_LOCATION_TEST, lineno) file, lineno = method(:attr_accessor_source_location_test).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_attr_accessor_source_location_test, lineno) + assert_equal(LINE_OF_ATTR_ACCESSOR_SOURCE_LOCATION_TEST, lineno) file, lineno = method(:attr_accessor_source_location_test=).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_attr_accessor_source_location_test, lineno) + assert_equal(LINE_OF_ATTR_ACCESSOR_SOURCE_LOCATION_TEST, lineno) end def block_source_location_test(*args, &block) diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 221ff37c6b6946..82bc7d0e87c3eb 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -6,14 +6,14 @@ require 'rbconfig' class TestProcess < Test::Unit::TestCase - RUBY = EnvUtil.rubybin + RUBY = EnvUtil.rubybin.freeze def setup - Process.waitall + Process.waitall unless multiple_ractors? end def teardown - Process.waitall + Process.waitall unless multiple_ractors? end def windows? @@ -24,6 +24,7 @@ def self.windows? end def with_tmpchdir + omit "Dir.chdir" if multiple_ractors? Dir.mktmpdir {|d| d = File.realpath(d) Dir.chdir(d) { @@ -33,6 +34,7 @@ def with_tmpchdir end def run_in_child(str) # should be called in a temporary directory + omit "global side effects" unless main_ractor? File.write("test-script", str) Process.wait spawn(RUBY, "test-script") $? @@ -152,7 +154,7 @@ def test_rlimit_value end end - TRUECOMMAND = [RUBY, '-e', ''] + TRUECOMMAND = Ractor.make_shareable([RUBY, '-e', '']) def test_execopts_opts assert_nothing_raised { @@ -295,9 +297,10 @@ def test_overwrite_ENV if e = RbConfig::CONFIG['PRELOADENV'] and !e.empty? MANDATORY_ENVS << e end - PREENVARG = ['-e', "%w[#{MANDATORY_ENVS.join(' ')}].each{|e|ENV.delete(e)}"] - ENVARG = ['-e', 'ENV.each {|k,v| puts "#{k}=#{v}" }'] - ENVCOMMAND = [RUBY].concat(PREENVARG).concat(ENVARG) + Ractor.make_shareable(MANDATORY_ENVS) + PREENVARG = Ractor.make_shareable(['-e', "%w[#{MANDATORY_ENVS.join(' ')}].each{|e|ENV.delete(e)}"]) + ENVARG = Ractor.make_shareable(['-e', 'ENV.each {|k,v| puts "#{k}=#{v}" }']) + ENVCOMMAND = Ractor.make_shareable([RUBY].concat(PREENVARG).concat(ENVARG)) def test_execopts_env assert_raise(ArgumentError) { @@ -336,7 +339,7 @@ def test_execopts_env } with_tmpchdir {|d| - system({"fofo"=>"haha"}, *ENVCOMMAND, STDOUT=>"out") + system({"fofo"=>"haha"}, *ENVCOMMAND, $stdout=>"out") assert_match(/^fofo=haha$/, File.read("out").chomp) } @@ -455,7 +458,7 @@ def test_execopts_unsetenv_others } end - PWD = [RUBY, '-e', 'puts Dir.pwd'] + PWD = Ractor.make_shareable([RUBY, '-e', 'puts Dir.pwd']) def test_execopts_chdir with_tmpchdir {|d| @@ -511,7 +514,7 @@ def test_execopts_open_failure } end - UMASK = [RUBY, '-e', 'printf "%04o\n", File.umask'] + UMASK = Ractor.make_shareable([RUBY, '-e', 'printf "%04o\n", File.umask']) def test_execopts_umask omit "umask is not supported" if windows? @@ -548,49 +551,49 @@ def with_pipes(n) end end - ECHO = lambda {|arg| [RUBY, '-e', "puts #{arg.dump}; STDOUT.flush"] } - SORT = [RUBY, '-e', "puts ARGF.readlines.sort"] - CAT = [RUBY, '-e', "IO.copy_stream STDIN, STDOUT"] + ECHO = Ractor.make_shareable(lambda {|arg| [RUBY, '-e', "puts #{arg.dump}; $stdout.flush"] }) + SORT = Ractor.make_shareable([RUBY, '-e', "puts ARGF.readlines.sort"]) + CAT = Ractor.make_shareable([RUBY, '-e', "IO.copy_stream $stdin, $stdout"]) def test_execopts_redirect_fd with_tmpchdir {|d| - Process.wait Process.spawn(*ECHO["a"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + Process.wait Process.spawn(*ECHO["a"], $stdout=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) assert_equal("a", File.read("out").chomp) if windows? # currently telling to child the file modes is not supported. File.write("out", "0\n", mode: "a") else - Process.wait Process.spawn(*ECHO["0"], STDOUT=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644]) + Process.wait Process.spawn(*ECHO["0"], $stdout=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644]) assert_equal("a\n0\n", File.read("out")) end - Process.wait Process.spawn(*SORT, STDIN=>["out", File::RDONLY, 0644], - STDOUT=>["out2", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + Process.wait Process.spawn(*SORT, $stdin=>["out", File::RDONLY, 0644], + $stdout=>["out2", File::WRONLY|File::CREAT|File::TRUNC, 0644]) assert_equal("0\na\n", File.read("out2")) - Process.wait Process.spawn(*ECHO["b"], [STDOUT, STDERR]=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + Process.wait Process.spawn(*ECHO["b"], [$stdout, $stderr]=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) assert_equal("b", File.read("out").chomp) # problem occur with valgrind - #Process.wait Process.spawn(*ECHO["a"], STDOUT=>:close, STDERR=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + #Process.wait Process.spawn(*ECHO["a"], $stdout=>:close, $stderr=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) #p File.read("out") #assert_not_empty(File.read("out")) # error message such as "-e:1:in `flush': Bad file descriptor (Errno::EBADF)" - Process.wait Process.spawn(*ECHO["c"], STDERR=>STDOUT, STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) + Process.wait Process.spawn(*ECHO["c"], $stderr=>$stdout, $stdout=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]) assert_equal("c", File.read("out").chomp) File.open("out", "w") {|f| - Process.wait Process.spawn(*ECHO["d"], STDOUT=>f) + Process.wait Process.spawn(*ECHO["d"], $stdout=>f) assert_equal("d", File.read("out").chomp) } - opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} - opts.merge(3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT) unless windows? + opts = {$stdout=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} + opts.merge(3=>$stdout, 4=>$stdout, 5=>$stdout, 6=>$stdout, 7=>$stdout) unless windows? Process.wait Process.spawn(*ECHO["e"], opts) assert_equal("e", File.read("out").chomp) - opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} - opts.merge(3=>0, 4=>:in, 5=>STDIN, 6=>1, 7=>:out, 8=>STDOUT, 9=>2, 10=>:err, 11=>STDERR) unless windows? + opts = {$stdout=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]} + opts.merge(3=>0, 4=>:in, 5=>$stdin, 6=>1, 7=>:out, 8=>$stdout, 9=>2, 10=>:err, 11=>$stderr) unless windows? Process.wait Process.spawn(*ECHO["ee"], opts) assert_equal("ee", File.read("out").chomp) unless windows? # passing non-stdio fds is not supported on Windows File.open("out", "w") {|f| - h = {STDOUT=>f, f=>STDOUT} - 3.upto(30) {|i| h[i] = STDOUT if f.fileno != i } + h = {$stdout=>f, f=>$stdout} + 3.upto(30) {|i| h[i] = $stdout if f.fileno != i } Process.wait Process.spawn(*ECHO["f"], h) assert_equal("f", File.read("out").chomp) } @@ -602,14 +605,14 @@ def test_execopts_redirect_fd Process.wait Process.spawn(*ECHO["f"], [Process]=>1) } assert_raise(ArgumentError) { - Process.wait Process.spawn(*ECHO["f"], [1, STDOUT]=>2) + Process.wait Process.spawn(*ECHO["f"], [1, $stdout]=>2) } assert_raise(ArgumentError) { Process.wait Process.spawn(*ECHO["f"], -1=>2) } - Process.wait Process.spawn(*ECHO["hhh\nggg\n"], STDOUT=>"out") + Process.wait Process.spawn(*ECHO["hhh\nggg\n"], $stdout=>"out") assert_equal("hhh\nggg\n", File.read("out")) - Process.wait Process.spawn(*SORT, STDIN=>"out", STDOUT=>"out2") + Process.wait Process.spawn(*SORT, $stdin=>"out", $stdout=>"out2") assert_equal("ggg\nhhh\n", File.read("out2")) unless windows? @@ -620,9 +623,9 @@ def test_execopts_redirect_fd assert_equal("", File.read("err")) end - system(*ECHO["bb\naa\n"], STDOUT=>["out", "w"]) + system(*ECHO["bb\naa\n"], $stdout=>["out", "w"]) assert_equal("bb\naa\n", File.read("out")) - system(*SORT, STDIN=>["out"], STDOUT=>"out2") + system(*SORT, $stdin=>["out"], $stdout=>"out2") assert_equal("aa\nbb\n", File.read("out2")) } end @@ -738,7 +741,7 @@ def test_execopts_redirect_open_fifo_interrupt_print def test_execopts_redirect_pipe with_pipe {|r1, w1| with_pipe {|r2, w2| - opts = {STDIN=>r1, STDOUT=>w2} + opts = {$stdin=>r1, $stdout=>w2} opts.merge(w1=>:close, r2=>:close) unless windows? pid = spawn(*SORT, opts) r1.close @@ -851,30 +854,30 @@ def test_execopts_redirect_to_out_and_err def test_execopts_redirect_dup2_child with_tmpchdir {|d| Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", - STDOUT=>"out", STDERR=>[:child, STDOUT]) + $stdout=>"out", $stderr=>[:child, $stdout]) assert_equal("errout", File.read("out")) Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", - STDERR=>"out", STDOUT=>[:child, STDERR]) + $stderr=>"out", $stdout=>[:child, $stderr]) assert_equal("errout", File.read("out")) omit "inheritance of fd other than stdin,stdout and stderr is not supported" if windows? Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", - STDOUT=>"out", - STDERR=>[:child, 3], + $stdout=>"out", + $stderr=>[:child, 3], 3=>[:child, 4], - 4=>[:child, STDOUT] + 4=>[:child, $stdout] ) assert_equal("errout", File.read("out")) - IO.popen([RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", STDERR=>[:child, STDOUT]]) {|io| + IO.popen([RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'", $stderr=>[:child, $stdout]]) {|io| assert_equal("errout", io.read) } - assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, STDOUT]) } + assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, $stdout=>[:child, $stdout]) } assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 3]) } assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, 3=>[:child, 4], 4=>[:child, 5], 5=>[:child, 3]) } - assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, STDOUT=>[:child, 3]) } + assert_raise(ArgumentError) { Process.wait spawn(*TRUECOMMAND, $stdout=>[:child, 3]) } } end @@ -898,13 +901,13 @@ def test_execopts_popen def test_execopts_popen_stdio with_tmpchdir {|d| assert_raise(ArgumentError) { - IO.popen([*ECHO["qux"], STDOUT=>STDOUT]) {|io| } + IO.popen([*ECHO["qux"], $stdout=>$stdout]) {|io| } } - IO.popen([*ECHO["hoge"], STDERR=>STDOUT]) {|io| + IO.popen([*ECHO["hoge"], $stderr=>$stdout]) {|io| assert_equal("hoge\n", io.read) } assert_raise(ArgumentError) { - IO.popen([*ECHO["fuga"], STDOUT=>"out"]) {|io| } + IO.popen([*ECHO["fuga"], $stdout=>"out"]) {|io| } } } end @@ -941,7 +944,7 @@ def test_popen_fork def test_popen_fork_ensure IO.popen("-") do |io| if !io - STDERR.reopen(STDOUT) + $stderr.reopen($stdout) # issue is here raise "fooo" else assert_empty io.read @@ -1111,8 +1114,8 @@ def test_execopts_redirect_tempfile def test_execopts_duplex_io IO.popen("#{RUBY} -e ''", "r+") {|duplex| - assert_raise(ArgumentError) { system("#{RUBY} -e ''", duplex=>STDOUT) } - assert_raise(ArgumentError) { system("#{RUBY} -e ''", STDOUT=>duplex) } + assert_raise(ArgumentError) { system("#{RUBY} -e ''", duplex=>$stdout) } + assert_raise(ArgumentError) { system("#{RUBY} -e ''", $stdout=>duplex) } } end @@ -1414,12 +1417,12 @@ def test_argv0 def with_stdin(filename) File.open(filename) {|f| begin - old = STDIN.dup + old = $stdin.dup begin - STDIN.reopen(filename) + $stdin.reopen(filename) yield ensure - STDIN.reopen(old) + $stdin.reopen(old) end ensure old.close @@ -1684,26 +1687,32 @@ def test_setegid if Process::UID.respond_to?(:from_name) def test_uid_from_name - if u = Etc.getpwuid(Process.uid) - assert_equal(Process.uid, Process::UID.from_name(u.name), u.name) - end exc = assert_raise_kind_of(ArgumentError, SystemCallError) { Process::UID.from_name("\u{4e0d 5b58 5728}") } assert_match(/\u{4e0d 5b58 5728}/, exc.message) if exc.is_a?(ArgumentError) end + + def test_uid_from_name_ractor_unsafe + if u = Etc.getpwuid(Process.uid) + assert_equal(Process.uid, Process::UID.from_name(u.name), u.name) + end + end end if Process::GID.respond_to?(:from_name) && !RUBY_PLATFORM.include?("android") def test_gid_from_name - if g = Etc.getgrgid(Process.gid) - assert_equal(Process.gid, Process::GID.from_name(g.name), g.name) - end exc = assert_raise_kind_of(ArgumentError, SystemCallError) do Process::GID.from_name("\u{4e0d 5b58 5728}") # fu son zai ("absent" in Kanji) end assert_match(/\u{4e0d 5b58 5728}/, exc.message) if exc.is_a?(ArgumentError) end + + def test_gid_from_name_ractor_unsafe + if g = Etc.getgrgid(Process.gid) + assert_equal(Process.gid, Process::GID.from_name(g.name), g.name) + end + end end def test_uid_re_exchangeable_p @@ -1814,7 +1823,7 @@ def assert_fail_too_long_path((cmd, sep), mesg) exs = [Errno::ENOENT] exs << Errno::EINVAL if windows? exs << Errno::E2BIG if defined?(Errno::E2BIG) - opts = {[STDOUT, STDERR]=>File::NULL} + opts = {[$stdout, $stderr]=>File::NULL} if defined?(Process::RLIMIT_NPROC) opts[:rlimit_nproc] = /openbsd/i =~ RUBY_PLATFORM ? 64 : 128 end @@ -1894,7 +1903,7 @@ def test_daemon_readwrite break f.read end Process.daemon(true, true) - puts STDIN.gets + puts $stdin.gets end assert_equal("ok?\n", data) end @@ -2130,7 +2139,7 @@ def test_setsid assert_equal(Marshal.load(io), Process.getsid(io.pid)) ensure Process.kill(:KILL, io.pid) rescue nil - Process.wait(io.pid) + Process.wait(io.pid) rescue nil end end end @@ -2601,7 +2610,7 @@ def test_exec_failure_leaves_no_child assert_raise(Errno::ENOENT) do spawn('inexistent_command') end - assert_empty(Process.waitall) + assert_empty(Process.waitall) unless multiple_ractors? end def test__fork diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index 6ce434790be13b..5fc08c40adb6b0 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -4,6 +4,9 @@ class TestRefinement < Test::Unit::TestCase module Sandbox BINDING = binding + def self.create_binding # TODO: create new one each time for multiple ractors + BINDING + end end class Foo @@ -231,13 +234,14 @@ def test_method_should_use_refinements assert_raise(NameError) { foo.method(:z) } assert_equal("FooExt#z", FooExtClient.method_z(foo).call) assert_raise(NameError) { foo.method(:z) } - assert_equal(8, eval(<<~EOS, Sandbox::BINDING)) + b = Sandbox.create_binding + assert_equal(8, eval(<<~EOS, b)) meth = 2.method(:pow) using MethodIntegerPowEx meth.call(3) EOS - assert_equal(:refine_pow, eval_using(MethodIntegerPowEx, "2.pow(3)")) - assert_equal(:refine_pow, eval_using(MethodIntegerPowEx, "2.method(:pow).(3)")) + assert_equal(:refine_pow, eval_using(MethodIntegerPowEx, "2.pow(3)"), b) + assert_equal(:refine_pow, eval_using(MethodIntegerPowEx, "2.method(:pow).(3)"), b) end module InstanceMethodIntegerPowEx @@ -254,12 +258,13 @@ def test_instance_method_should_use_refinements assert_raise(NameError) { Foo.instance_method(:z) } assert_equal("FooExt#z", FooExtClient.instance_method_z(foo).bind(foo).call) assert_raise(NameError) { Foo.instance_method(:z) } - assert_equal(4, eval(<<~EOS, Sandbox::BINDING)) + b = Sandbox.create_binding + assert_equal(4, eval(<<~EOS, b)) meth = Integer.instance_method(:abs) using InstanceMethodIntegerPowEx meth.bind(-4).call EOS - assert_equal(:refine_abs, eval_using(InstanceMethodIntegerPowEx, "Integer.instance_method(:abs).bind(-4).call")) + assert_equal(:refine_abs, eval_using(InstanceMethodIntegerPowEx, "Integer.instance_method(:abs).bind(-4).call", b)) end def test_no_local_rebinding @@ -542,7 +547,7 @@ def foo def test_main_using_is_private assert_raise(NoMethodError) do - eval("recv = self; recv.using Module.new", Sandbox::BINDING) + eval("recv = self; recv.using Module.new", Sandbox.create_binding) end end @@ -557,7 +562,7 @@ class UsingClass def test_module_using_class assert_raise(TypeError) do - eval("using TestRefinement::UsingClass", Sandbox::BINDING) + eval("using TestRefinement::UsingClass", Sandbox.create_binding) end end @@ -720,12 +725,12 @@ def test_refine_with_proc def test_using_in_module assert_raise(RuntimeError) do - eval(<<-EOF, Sandbox::BINDING) - $main = self + eval(<<-EOF, Sandbox.create_binding) + main = self module M end - module M2 - $main.send(:using, M) + M2 = Module.new do + main.send(:using, M) end EOF end @@ -733,16 +738,16 @@ module M2 def test_using_in_method assert_raise(RuntimeError) do - eval(<<-EOF, Sandbox::BINDING) - $main = self + eval(<<-EOF, Sandbox.create_binding) + main = self module M end - class C - def call_using_in_method - $main.send(:using, M) + c = Class.new do + define_method(:call_using_in_method) do + main.send(:using, M) end end - C.new.call_using_in_method + c.new.call_using_in_method EOF end end @@ -861,8 +866,9 @@ def foo(*args) def test_super_in_block bug7925 = '[ruby-core:52750] [Bug #7925]' + b = Sandbox.create_binding x = eval_using(SuperInBlock::R, - "TestRefinement:: SuperInBlock::C.new.foo(#{bug7925.dump})") + "TestRefinement::SuperInBlock::C.new.foo(#{bug7925.dump})", b) assert_equal([:foo, :ref, bug7925], x, bug7925) end @@ -919,7 +925,7 @@ def test_module_using_in_method def test_module_using_invalid_self assert_raise(RuntimeError) do - eval <<-EOF, Sandbox::BINDING + eval <<-EOF, Sandbox.create_binding module TestRefinement::TestModuleUsingInvalidSelf Module.new.send(:using, TestRefinement::FooExt) end @@ -1766,21 +1772,21 @@ def in_ref_c module Foo using RefB - USED_MODS = Module.used_modules - USED_REFS = Module.used_refinements + USED_MODS = Ractor.make_shareable(Module.used_modules) + USED_REFS = Ractor.make_shareable(Module.used_refinements) end module Bar using RefC - USED_MODS = Module.used_modules - USED_REFS = Module.used_refinements + USED_MODS = Ractor.make_shareable(Module.used_modules) + USED_REFS = Ractor.make_shareable(Module.used_refinements) end module Combined using RefA using RefB - USED_MODS = Module.used_modules - USED_REFS = Module.used_refinements + USED_MODS = Ractor.make_shareable(Module.used_modules) + USED_REFS = Ractor.make_shareable(Module.used_refinements) end end @@ -2601,7 +2607,7 @@ def foo end module B - BAR = "bar" + BAR = "bar".freeze def bar "#{foo}:#{BAR}" @@ -2714,7 +2720,7 @@ def test private - def eval_using(mod, s) - eval("using #{mod}; #{s}", Sandbox::BINDING) + def eval_using(mod, s, b = nil) + eval("using #{mod}; #{s}", b || Sandbox.create_binding) end end diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index 9f1e03e6499782..b72e39fe3de6ec 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -1630,8 +1630,12 @@ def test_unicode_age_16_0 "WHITE CROSS MARK..TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE") end - UnicodeAgeRegexps = Hash.new do |h, age| - h[age] = [/\A\p{age=#{age}}+\z/u, /\A\P{age=#{age}}+\z/u].freeze + def unicode_age_regexps + @unicode_age_regexps ||= begin + Hash.new do |h, age| + h[age] = [/\A\p{age=#{age}}+\z/u, /\A\P{age=#{age}}+\z/u] + end + end end def assert_unicode_age(char, mesg = nil, matches: @matches, unmatches: @unmatches) @@ -1640,13 +1644,13 @@ def assert_unicode_age(char, mesg = nil, matches: @matches, unmatches: @unmatche end matches.each do |age| - pos, neg = UnicodeAgeRegexps[age] + pos, neg = unicode_age_regexps[age] assert_match(pos, char, mesg) assert_not_match(neg, char, mesg) end unmatches.each do |age| - pos, neg = UnicodeAgeRegexps[age] + pos, neg = unicode_age_regexps[age] assert_not_match(pos, char, mesg) assert_match(neg, char, mesg) end diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb index 4f6c62dc35c174..c8c63bacf2cc46 100644 --- a/test/ruby/test_require.rb +++ b/test/ruby/test_require.rb @@ -161,7 +161,6 @@ def test_require_path_home_2 ENV["RUBYPATH"] = "~" + "/foo" * 1024 ENV["HOME"] = "/foo" assert_in_out_err(%w(-S -w test_ruby_test_require), "", [], pathname_too_long) - ensure env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH") env_home ? ENV["HOME"] = env_home : ENV.delete("HOME") @@ -217,18 +216,20 @@ def test_require_twice end def assert_syntax_error_backtrace - loaded_features = $LOADED_FEATURES.dup - Dir.mktmpdir do |tmp| - req = File.join(tmp, "test.rb") - File.write(req, ",\n") - e = assert_raise_with_message(SyntaxError, /unexpected/) { - yield req - } - assert_not_nil(bt = e.backtrace, "no backtrace") - assert_not_empty(bt.find_all {|b| b.start_with? __FILE__}, proc {bt.inspect}) + begin + loaded_features = $LOADED_FEATURES.dup + Dir.mktmpdir do |tmp| + req = File.join(tmp, "test.rb") + File.write(req, ",\n") + e = assert_raise_with_message(SyntaxError, /unexpected/) { + yield req + } + assert_not_nil(bt = e.backtrace, "no backtrace") + assert_not_empty(bt.find_all {|b| b.start_with? __FILE__}, proc {bt.inspect}) + end + ensure + $LOADED_FEATURES.replace loaded_features end - ensure - $LOADED_FEATURES.replace loaded_features end def test_require_syntax_error diff --git a/test/ruby/test_rubyvm.rb b/test/ruby/test_rubyvm.rb index 225cb45f33769f..b7a9091b268658 100644 --- a/test/ruby/test_rubyvm.rb +++ b/test/ruby/test_rubyvm.rb @@ -35,39 +35,40 @@ def foo def test_keep_script_lines omit if ParserSupport.prism_enabled? pend if ENV['RUBY_ISEQ_DUMP_DEBUG'] # TODO + begin + prev_conf = RubyVM.keep_script_lines - prev_conf = RubyVM.keep_script_lines + # keep + RubyVM.keep_script_lines = true - # keep - RubyVM.keep_script_lines = true + ast, iseq = *parse_and_compile - ast, iseq = *parse_and_compile + lines = ast.script_lines + assert_equal Array, lines.class - lines = ast.script_lines - assert_equal Array, lines.class - - lines = iseq.script_lines - assert_equal Array, lines.class - iseq.each_child{|child| - assert_equal lines, child.script_lines - } - assert lines.frozen? + lines = iseq.script_lines + assert_equal Array, lines.class + iseq.each_child{|child| + assert_equal lines, child.script_lines + } + assert lines.frozen? - # don't keep - RubyVM.keep_script_lines = false + # don't keep + RubyVM.keep_script_lines = false - ast, iseq = *parse_and_compile + ast, iseq = *parse_and_compile - lines = ast.script_lines - assert_equal nil, lines + lines = ast.script_lines + assert_equal nil, lines - lines = iseq.script_lines - assert_equal nil, lines - iseq.each_child{|child| - assert_equal lines, child.script_lines - } + lines = iseq.script_lines + assert_equal nil, lines + iseq.each_child{|child| + assert_equal lines, child.script_lines + } - ensure - RubyVM.keep_script_lines = prev_conf + ensure + RubyVM.keep_script_lines = prev_conf + end end end diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index ccf24521694484..58a714b5c2225e 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -1563,12 +1563,12 @@ def test_define_method_on_exception end class C11492 - define_method(:foo_return){ + define_method(:foo_return, &Ractor.make_shareable(proc{ return true - } - define_method(:foo_break){ + })) + define_method(:foo_break, &Ractor.make_shareable(proc{ break true - } + })) end def test_define_method_on_return @@ -2103,29 +2103,29 @@ def test_return_value_with_rescue '[Bug #13369]' end - define_method(:f_last_defined) do + define_method(:f_last_defined, &Ractor.make_shareable(proc do :f_last_defined - end + end)) - define_method(:f_return_defined) do + define_method(:f_return_defined, &Ractor.make_shareable(proc do return :f_return_defined - end + end)) - define_method(:f_break_defined) do + define_method(:f_break_defined, &Ractor.make_shareable(proc do break :f_break_defined - end + end)) - define_method(:f_raise_defined) do + define_method(:f_raise_defined, &Ractor.make_shareable(proc do raise rescue return :f_raise_defined - end + end)) - define_method(:f_break_in_rescue_defined) do + define_method(:f_break_in_rescue_defined, &Ractor.make_shareable(proc do raise rescue break :f_break_in_rescue_defined - end + end)) def test_return_value_with_rescue_and_defined_methods assert_equal [[:b_return, :f_last_defined, :f_last_defined], @@ -2154,13 +2154,13 @@ def test_return_value_with_rescue_and_defined_methods '[Bug #13369]' end - define_method(:just_yield) do |&block| + define_method(:just_yield, &Ractor.make_shareable(proc do |&block| block.call - end + end)) - define_method(:unwind_multiple_bmethods) do + define_method(:unwind_multiple_bmethods, &Ractor.make_shareable(proc do just_yield { return :unwind_multiple_bmethods } - end + end)) def test_non_local_return_across_multiple_define_methods assert_equal [[:b_return, :unwind_multiple_bmethods, nil], diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 1e0f31ba7c540a..8915b16ba670be 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -5,7 +5,7 @@ class TestString < Test::Unit::TestCase WIDE_ENCODINGS = [ Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE, - ] + ].freeze def initialize(*args) @cls = String @@ -399,7 +399,7 @@ def test_capitalize! end - Bug2463 = '[ruby-dev:39856]' + Bug2463 = '[ruby-dev:39856]'.freeze def test_center assert_equal(S("hello"), S("hello").center(4)) assert_equal(S(" hello "), S("hello").center(11)) @@ -738,11 +738,14 @@ def test_crypt assert_raise(ArgumentError) {S("mypassword".encode(enc)).crypt(S("aa"))} end - @cls == String and - assert_no_memory_leak([], "s = ''; salt_proc = proc{#{(crypt_supports_des_crypt? ? '..' : good_salt).inspect}}", "#{<<~"begin;"}\n#{<<~'end;'}") + end + def test_crypt_no_memory_leak + good_salt = "$2a$04$0WVaz0pV3jzfZ5G5tpmHWu" + @cls == String and + assert_no_memory_leak([], "s = ''; salt_proc = proc{#{(crypt_supports_des_crypt? ? '..' : good_salt).inspect}}", "#{<<~"begin;"}\n#{<<~'end;'}") begin; - 1000.times { s.crypt(-salt_proc.call).clear } + 1000.times { s.crypt(-salt_proc.call).clear } end; end @@ -3385,11 +3388,12 @@ def test_uplus_minus assert_same(str, +str) assert_not_same(str, -str) - require 'objspace' + end + def test_uplus_minus_ractor_unsafe + require 'objspace' str = "test_uplus_minus_str".freeze assert_includes ObjectSpace.dump(str), '"fstring":true' - assert_predicate(str, :frozen?) assert_not_predicate(+str, :frozen?) assert_predicate(-str, :frozen?) diff --git a/test/ruby/test_stringchar.rb b/test/ruby/test_stringchar.rb index e13beef69c1883..647f74131849ba 100644 --- a/test/ruby/test_stringchar.rb +++ b/test/ruby/test_stringchar.rb @@ -41,10 +41,6 @@ def test_string assert_match(/foo(?=(bar)|(baz))/, "foobar") assert_match(/foo(?=(bar)|(baz))/, "foobaz") - $foo = "abc" - assert_equal("abc = abc", "#$foo = abc") - assert_equal("abc = abc", "#{$foo} = abc") - foo = "abc" assert_equal("abc = abc", "#{foo} = abc") @@ -65,6 +61,12 @@ def test_string assert_equal("", x.sub(/.*\.([^\.]+)$/, '<\&>')) end + def test_string_ractor_unsafe + $foo = "abc" + assert_equal("abc = abc", "#$foo = abc") + assert_equal("abc = abc", "#{$foo} = abc") + end + def test_char # character constants(assumes ASCII) assert_equal(?a, "a"[0]) diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb index 01e5cc68f6eadc..5e1e1271105443 100644 --- a/test/ruby/test_struct.rb +++ b/test/ruby/test_struct.rb @@ -203,11 +203,6 @@ def test_inspect o.a = o assert_match(/^#:...>>$/, o.inspect) - @Struct.new("Foo", :a) - o = @Struct::Foo.new(1) - assert_equal("#", o.inspect) - @Struct.instance_eval { remove_const(:Foo) } - klass = @Struct.new(:a, :b) o = klass.new(1, 2) assert_equal("#", o.inspect) @@ -229,6 +224,13 @@ def test_inspect assert_include(methods, :"@a=") end + def test_inspect_ractor_unsafe + @Struct.new("Foo", :a) + o = @Struct::Foo.new(1) + assert_equal("#", o.inspect) + @Struct.instance_eval { remove_const(:Foo) } + end + def test_init_copy klass = @Struct.new(:a) o = klass.new(1) diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb index 8e973b0f7f4a08..60ca4a3d078c15 100644 --- a/test/ruby/test_super.rb +++ b/test/ruby/test_super.rb @@ -213,7 +213,9 @@ def obj.reverse overlaid.call([1,2,3]) str.reverse end + end + def test_overlaid_ractor_unsafe assert_nothing_raised('[ruby-core:27230]') do mid=Indexed.new mid.instance_eval(&Overlaid) @@ -427,9 +429,9 @@ def foo(*args) end class Y < X - define_method(:foo) do |*args| + define_method(:foo, &Ractor.make_shareable(proc do |*args| super(*args) - end + end)) end def test_super_splat diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb index c50febf5d1c6fa..13e1f1fcfd3a1c 100644 --- a/test/ruby/test_symbol.rb +++ b/test/ruby/test_symbol.rb @@ -492,10 +492,10 @@ def test_singleton_method assert_raise(TypeError) { a = :foo; def a.foo; end } end - SymbolsForEval = [ + SymbolsForEval = Ractor.make_shareable([ :foo, "dynsym_#{Random.rand(10000)}_#{Time.now}".to_sym - ] + ]) def test_instance_eval bug11086 = '[ruby-core:68961] [Bug #11086]' diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 94a2e03940bf5b..d8b93d8cffe839 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -220,7 +220,11 @@ def test_newline_in_block_parameters m = m.tr_s('()', ' ').strip if n3 == 'do' name = "test_#{n3}_block_after_blockcall_#{n1}_#{n2}_arg" code = "#{blockcall}#{c}#{m} #{b}" - define_method(name) {assert_valid_syntax(code, bug6115)} + class_eval <<-RUBY + def #{name} + assert_valid_syntax(#{code.inspect}, #{bug6115.inspect}) + end + RUBY end end @@ -314,8 +318,8 @@ def o.kw(**a) a end assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989) EnvUtil.under_gc_stress do eval("def o.m(k: 0) k end") - end - assert_equal(42, o.m(k: 42), '[ruby-core:45744]') + assert_equal(42, o.m(k: 42), '[ruby-core:45744]') + end unless multiple_ractors? bug7922 = '[ruby-core:52744] [Bug #7922]' def o.bug7922(**) end assert_nothing_raised(ArgumentError, bug7922) {o.bug7922(foo: 42)} @@ -684,14 +688,17 @@ def test_duplicated_kw end def test_duplicated_rest_kw - assert_syntax_error("def foo(*a, a: 1) end", /duplicated argument name/) - assert_nothing_raised {def foo(*_, _: 1) end} + unless multiple_ractors? + assert_syntax_error("def foo(*a, a: 1) end", /duplicated argument name/) + assert_nothing_raised {def foo(*_, _: 1) end} + defined_foo = true + end (obj = Object.new).instance_eval("def foo(*_, x: 42, _: 1) x end") assert_equal(42, obj.foo(42)) assert_equal(42, obj.foo(2, _: 0)) assert_equal(2, obj.foo(x: 2, _: 0)) ensure - self.class.remove_method(:foo) + self.class.remove_method(:foo) if defined_foo end def test_duplicated_opt_kw @@ -837,7 +844,7 @@ def test_unassignable end end - Bug7559 = '[ruby-dev:46737]' + Bug7559 = '[ruby-dev:46737]'.freeze def test_lineno_command_call_quote expected = __LINE__ + 1 diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index 01a1e51025181c..9de43a778c0258 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -6,20 +6,22 @@ class TestThread < Test::Unit::TestCase class Thread < ::Thread - Threads = [] + def self.Threads + Ractor.current[:__THREADS] ||= [] + end def self.new(*) th = super - Threads << th + (Ractor.current[:__THREADS] ||= []) << th th end end def setup - Thread::Threads.clear + (Ractor.current[:__THREADS] ||= []).clear end def teardown - Thread::Threads.each do |t| + Thread.Threads.each do |t| t.kill if t.alive? begin t.join @@ -255,12 +257,14 @@ def test_join_argument_conversion { 'FIXNUM_MAX' => RbConfig::LIMITS['FIXNUM_MAX'], 'UINT64_MAX' => RbConfig::LIMITS['UINT64_MAX'], - 'INFINITY' => Float::INFINITY + 'INFINITY' => 'Float::INFINITY' }.each do |name, limit| - define_method("test_join_limit_#{name}") do - t = Thread.new {} - assert_same t, t.join(limit), "limit=#{limit.inspect}" - end + class_eval <<-RUBY + def test_join_limit_#{name} + t = Thread.new {} + assert_same t, t.join(#{limit}), %q(limit=#{limit.inspect}) + end + RUBY end { 'minus_1' => -1, @@ -269,7 +273,7 @@ def test_join_argument_conversion 'INT64_MIN' => RbConfig::LIMITS['INT64_MIN'], 'minus_INFINITY' => -Float::INFINITY }.each do |name, limit| - define_method("test_join_limit_negative_#{name}") do + define_method("test_join_limit_negative_#{name}", &Ractor.make_shareable(proc do t = Thread.new { sleep } begin assert_nothing_raised(Timeout::Error) do @@ -280,7 +284,7 @@ def test_join_argument_conversion ensure t.kill end - end + end)) end def test_kill_main_thread @@ -1239,7 +1243,7 @@ def test_fork_in_thread f = nil th = Thread.start do unless f = IO.popen("-") - STDERR.reopen(STDOUT) + $stderr.reopen($stdout) exit end Process.wait2(f.pid) diff --git a/test/ruby/test_thread_cv.rb b/test/ruby/test_thread_cv.rb index 81201f134f0c71..2d9a2ac2ede582 100644 --- a/test/ruby/test_thread_cv.rb +++ b/test/ruby/test_thread_cv.rb @@ -84,8 +84,8 @@ def test_condvar_wait_and_broadcast assert_equal ["C1", "C1", "C1", "P1", "P2", "C2", "C2", "C2"], result ensure - threads.each(&:kill) - threads.each(&:join) + threads&.each(&:kill) + threads&.each(&:join) end def test_condvar_wait_deadlock diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb index 333edb80218a64..7e7a2660e5fe00 100644 --- a/test/ruby/test_time.rb +++ b/test/ruby/test_time.rb @@ -440,7 +440,9 @@ def test_marshal_zone t = Time.utc(2013, 2, 24) assert_equal('UTC', t.zone) assert_equal('UTC', Marshal.load(Marshal.dump(t)).zone) + end + def test_marshal_zone_ractor_unsafe in_timezone('JST-9') do t = Time.local(2013, 2, 24) assert_equal('JST', Time.local(2013, 2, 24).zone) @@ -472,7 +474,7 @@ def test_marshal_to_s "[ruby-dev:44827] [Bug #5586]") end - Bug8795 = '[ruby-core:56648] [Bug #8795]' + Bug8795 = '[ruby-core:56648] [Bug #8795]'.freeze def test_marshal_broken_offset data = "\x04\bIu:\tTime\r\xEFF\x1C\x80\x00\x00\x00\x00\x06:\voffset" diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb index f66cd9bec2eedc..bbfbedeeacfdcd 100644 --- a/test/ruby/test_time_tz.rb +++ b/test/ruby/test_time_tz.rb @@ -17,6 +17,7 @@ class TestTimeTZ < Test::Unit::TestCase if force_tz_test module Util def with_tz(tz) + omit "global side effects" if multiple_ractors? && self.is_a?(TestTimeTZ) old = ENV["TZ"] begin ENV["TZ"] = tz @@ -29,6 +30,7 @@ def with_tz(tz) else module Util def with_tz(tz) + omit "global side effects" if multiple_ractors? && self.is_a?(TestTimeTZ) if ENV["TZ"] == tz yield end @@ -88,7 +90,7 @@ def group_by(e, &block) CORRECT_TOKYO_DST_1951 = with_tz("Asia/Tokyo") { if Time.local(1951, 5, 6, 12, 0, 0).dst? # noon, DST if Time.local(1951, 5, 6, 1, 0, 0).dst? # DST with fixed tzdata - Time.local(1951, 9, 8, 23, 0, 0).dst? ? "2018f" : "2018e" + Time.local(1951, 9, 8, 23, 0, 0).dst? ? "2018f".freeze : "2018e".freeze end end } @@ -96,7 +98,7 @@ def group_by(e, &block) Time.local(1994, 12, 31, 0, 0, 0).year == 1995 } CORRECT_SINGAPORE_1982 = with_tz("Asia/Singapore") { - "2022g" if Time.local(1981, 12, 31, 23, 59, 59).utc_offset == 8*3600 + "2022g".freeze if Time.local(1981, 12, 31, 23, 59, 59).utc_offset == 8*3600 } def time_to_s(t) @@ -379,20 +381,23 @@ def self.gen_zdump_test(data) expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)]) mesg_utc = "TZ=#{tz} Time.utc(#{u.map {|arg| arg.inspect }.join(', ')})" mesg = "#{mesg_utc}.localtime" - define_method(gen_test_name(tz)) { - with_tz(tz) { - t = nil - assert_nothing_raised(mesg) { t = Time.utc(*u) } - assert_equal(expected_utc, time_to_s(t), mesg_utc) - assert_nothing_raised(mesg) { t.localtime } - assert_equal(expected, time_to_s(t), mesg) - assert_equal(gmtoff, t.gmtoff) - assert_equal(format_gmtoff(gmtoff), t.strftime("%z")) - assert_equal(format_gmtoff(gmtoff, true), t.strftime("%:z")) - assert_equal(format_gmtoff2(gmtoff), t.strftime("%::z")) - assert_equal(Encoding::US_ASCII, t.zone.encoding) - } - } + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + tz = #{tz.inspect} + with_tz(tz) { + t = nil + assert_nothing_raised(#{mesg.inspect}) { t = Time.utc(*#{u.inspect}) } + assert_equal(#{expected_utc.inspect}, time_to_s(t), #{mesg_utc.inspect}) + assert_nothing_raised(#{mesg.inspect}) { t.localtime } + assert_equal(#{expected.inspect}, time_to_s(t), #{mesg.inspect}) + assert_equal(#{gmtoff.inspect}, t.gmtoff) + assert_equal(format_gmtoff(#{gmtoff.inspect}), t.strftime("%z")) + assert_equal(format_gmtoff(#{gmtoff.inspect}, true), t.strftime("%:z")) + assert_equal(format_gmtoff2(#{gmtoff.inspect}), t.strftime("%::z")) + assert_equal(Encoding::US_ASCII, t.zone.encoding) + } + end + RUBY } group_by(sample) {|tz, _, _, _| tz }.each {|tz, a| @@ -401,7 +406,11 @@ def self.gen_zdump_test(data) monotonic_to_past = i == 0 || (a[i-1][2] <=> l) < 0 monotonic_to_future = i == a.length-1 || (l <=> a[i+1][2]) < 0 if monotonic_to_past && monotonic_to_future - define_method(gen_test_name(tz)) { + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + expected = #{expected.inspect} + tz = #{tz.inspect} + l = #{l.inspect} with_tz(tz) { assert_time_constructor(tz, expected, :local, l) assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, false, nil]) @@ -410,25 +419,40 @@ def self.gen_zdump_test(data) assert_time_constructor(tz, expected, :new, l+[:std]) assert_time_constructor(tz, expected, :new, l+[:dst]) } - } + end + RUBY elsif monotonic_to_past && !monotonic_to_future - define_method(gen_test_name(tz)) { + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + expected = #{expected.inspect} + tz = #{tz.inspect} + l = #{l.inspect} with_tz(tz) { assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, true, nil]) assert_time_constructor(tz, expected, :new, l+[:dst]) } - } + end + RUBY elsif !monotonic_to_past && monotonic_to_future - define_method(gen_test_name(tz)) { + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + expected = #{expected.inspect} + tz = #{tz.inspect} + l = #{l.inspect} with_tz(tz) { assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, false, nil]) assert_time_constructor(tz, expected, :new, l+[:std]) } - } + end + RUBY else - define_method(gen_test_name(tz)) { - flunk("time in reverse order: TZ=#{tz} #{expected}") - } + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{gen_test_name(tz).gsub(/[@\/]/, '_')} + expected = #{expected.inspect} + tz = #{tz.inspect} + flunk("time in reverse order: TZ=\#{tz} \#{expected}") + end + RUBY end } } @@ -548,13 +572,15 @@ def self.gen_variational_zdump_test(hint, data) } end - # tzdata-2014g fixed the offset for lisbon from -0:36:32 to -0:36:45. - # [ruby-core:65058] [Bug #10245] - gen_variational_zdump_test "lisbon", <<'End' if has_lisbon_tz + if will_run_in_main_ractor? + # tzdata-2014g fixed the offset for lisbon from -0:36:32 to -0:36:45. + # [ruby-core:65058] [Bug #10245] + gen_variational_zdump_test "lisbon", <<'End' if has_lisbon_tz Europe/Lisbon Mon Jan 1 00:36:31 1912 UTC = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2192 Europe/Lisbon Mon Jan 1 00:36:44 1912 UT = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2205 Europe/Lisbon Sun Dec 31 23:59:59 1911 UT = Sun Dec 31 23:23:14 1911 LMT isdst=0 gmtoff=-2205 End + end class TZ attr_reader :name @@ -722,12 +748,12 @@ def nametest_marshal_compatibility(time_class, tzname, abbr, utc_offset) # t.zone may be a mere String or timezone object. end - ZONES = { + ZONES = Ractor.make_shareable({ "Asia/Tokyo" => ["JST", +9*3600], "America/Los_Angeles" => ["PST", -8*3600, "PDT", -7*3600], "Africa/Ndjamena" => ["WAT", +1*3600], "Etc/UTC" => ["UTC", 0], - } + }) def make_timezone(tzname, abbr, utc_offset, abbr2 = nil, utc_offset2 = nil) self.class::TIME_CLASS.find_timezone(tzname) @@ -744,22 +770,36 @@ def subtest_dst?(time_class, tz, tzarg, tzname, abbr, utc_offset) instance_methods(false).grep(/\Asub(?=test_)/) do |subtest| test = $' ZONES.each_pair do |tzname, (abbr, utc_offset, abbr2, utc_offset2)| - define_method("#{test}@#{tzname}") do + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{(test + '_' + tzname).gsub(/[^\w]/, '_')} + subtest = #{subtest.inspect} + tzname = #{tzname.inspect} + abbr = #{abbr.inspect} + utc_offset = #{utc_offset.inspect} + abbr2 = #{abbr2.inspect} + utc_offset2 = #{utc_offset2.inspect} tz = make_timezone(tzname, abbr, utc_offset, abbr2, utc_offset2) time_class = self.class::TIME_CLASS __send__(subtest, time_class, tz, tz, tzname, [abbr, abbr2], [utc_offset, utc_offset2]) __send__(subtest, time_class, tz, tzname, tzname, [abbr, abbr2], [utc_offset, utc_offset2]) end + RUBY end end instance_methods(false).grep(/\Aname(?=test_)/) do |subtest| test = $' ZONES.each_pair do |tzname, (abbr, utc_offset)| - define_method("#{test}@#{tzname}") do + class_eval <<-RUBY, __FILE__, __LINE__+1 + def #{(test + '_' + tzname).gsub(/[^\w]/, '_')} + subtest = #{subtest.inspect} + tzname = #{tzname.inspect} + abbr = #{abbr.inspect} + utc_offset = #{utc_offset.inspect} time_class = self.class::TIME_CLASS __send__(subtest, time_class, tzname, abbr, utc_offset) end + RUBY end end end diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb index 68434e0b6c479b..14ed7fcf76ccc0 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -487,9 +487,11 @@ def test_many_instance_variables objects = [Object.new, Hash.new, Module.new] objects.each do |obj| 1000.times do |i| + next if obj.is_a?(Module) && !main_ractor? obj.instance_variable_set("@var#{i}", i) end 1000.times do |i| + next if obj.is_a?(Module) && !main_ractor? assert_equal(i, obj.instance_variable_get("@var#{i}")) end end diff --git a/test/ruby/test_whileuntil.rb b/test/ruby/test_whileuntil.rb index ff6d29ac4a9f89..79a4b60ebcd845 100644 --- a/test/ruby/test_whileuntil.rb +++ b/test/ruby/test_whileuntil.rb @@ -59,14 +59,16 @@ def test_while end assert_equal(220, sum) - tmp = open(tmpfilename, "r") - while line = tmp.gets() - break if $. == 3 - assert_no_match(/vt100/, line) - assert_no_match(/Amiga/, line) - assert_no_match(/paper/, line) + if main_ractor? + tmp = open(tmpfilename, "r") + while line = tmp.gets() + break if $. == 3 + assert_no_match(/vt100/, line) + assert_no_match(/Amiga/, line) + assert_no_match(/paper/, line) + end + tmp.close end - tmp.close File.unlink tmpfilename or `/bin/rm -f "#{tmpfilename}"` assert_file.not_exist?(tmpfilename) diff --git a/test/ruby/test_yield.rb b/test/ruby/test_yield.rb index 9b2b2f37e06e04..f1658b1211c5b9 100644 --- a/test/ruby/test_yield.rb +++ b/test/ruby/test_yield.rb @@ -89,7 +89,7 @@ def test_block_args_unleashed require_relative 'sentence' class TestRubyYieldGen < Test::Unit::TestCase - Syntax = { + Syntax = Ractor.make_shareable({ :exp => [["0"], ["nil"], ["false"], @@ -178,7 +178,7 @@ class TestRubyYieldGen < Test::Unit::TestCase :test_proc => [['def m() yield', :command_args_noblock, ' end; r = m {', :block_param_def, 'vars', '}; undef m; r']], :test_lambda => [['def m() yield', :command_args_noblock, ' end; r = m(&lambda {', :block_param_def, 'vars', '}); undef m; r']], :test_enum => [['o = Object.new; def o.each() yield', :command_args_noblock, ' end; r1 = r2 = nil; o.each {|*x| r1 = x }; o.to_enum.each {|*x| r2 = x }; [r1, r2]']] - } + }) def rename_var(obj) vars = [] diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index ce9dfca3900e0e..ea8d8f49aa4586 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -1794,7 +1794,7 @@ def assert_no_exits(script) assert_compiles(script) end - ANY = Object.new + ANY = Object.new.freeze def assert_compiles( test_script, insns: [], call_threshold: 1, diff --git a/test/test_tmpdir.rb b/test/test_tmpdir.rb index c91fc334ed73db..9c09c66710f3a6 100644 --- a/test/test_tmpdir.rb +++ b/test/test_tmpdir.rb @@ -154,10 +154,10 @@ def test_ractor Ractor.receive end end - dir = r.take + dir = r.value assert_file.directory? dir r.send true - r.take + r.join assert_file.not_exist? dir end end; diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 47cc6574c878d1..f8aa334e7bc0f0 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -32,7 +32,7 @@ def filter bt end end - self.backtrace_filter = BacktraceFilter.new + self.backtrace_filter = BacktraceFilter.new.freeze def self.filter_backtrace bt # :nodoc: backtrace_filter.filter bt @@ -86,18 +86,21 @@ def mu_pp(obj) #:nodoc: end def assert_file - AssertFile + Ractor.current[:__AssertFile] ||= CoreAssertions.new_AssertFile end - FailDesc = proc do |status, message = "", out = ""| - now = Time.now - proc do - EnvUtil.failure_description(status, now, message, out) + FailDesc = Ractor.make_shareable(Ractor.current.instance_eval do + proc do |status, message = "", out = ""| + now = Time.now + proc do + EnvUtil.failure_description(status, now, message, out) + end end - end + end) def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, success: nil, failed: nil, **opt) + pend "#{__method__}" if respond_to?(:main_ractor?) && !main_ractor? args = Array(args).dup args.insert((Hash === args[0] ? 1 : 0), '--disable=gems') stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, **opt) @@ -154,6 +157,7 @@ def syntax_check(code, fname, line) end def assert_no_memory_leak(args, prepare, code, message=nil, limit: 2.0, rss: false, **opt) + omit "separate process" if respond_to?(:main_ractor?) && !main_ractor? # TODO: consider choosing some appropriate limit for RJIT and stop skipping this once it does not randomly fail pend 'assert_no_memory_leak may consider RJIT memory usage as leak' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # For previous versions which implemented MJIT @@ -270,6 +274,7 @@ def assert_valid_syntax(code, *args, **opt) end def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) + pend "#{__method__}" if respond_to?(:main_ractor?) && !main_ractor? assert_valid_syntax(testsrc, caller_locations(1, 1)[0]) if child_env child_env = [child_env] @@ -281,6 +286,7 @@ def assert_normal_exit(testsrc, message = '', child_env: nil, **opt) end def assert_ruby_status(args, test_stdin="", message=nil, **opt) + pend "#{__method__}" if respond_to?(:main_ractor?) && !main_ractor? out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt) desc = FailDesc[status, message, out] assert(!status.signaled?, desc) @@ -288,7 +294,7 @@ def assert_ruby_status(args, test_stdin="", message=nil, **opt) assert(status.success?, desc) end - ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM") + ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV TERM").freeze def separated_runner(token, out = nil) include(*Test::Unit::TestCase.ancestors.select {|c| !c.is_a?(Class) }) @@ -304,6 +310,7 @@ def separated_runner(token, out = nil) end def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt) + pend "#{__method__}" if respond_to?(:main_ractor?) && !main_ractor? unless file and line loc, = caller_locations(1,1) file ||= loc.path @@ -389,7 +396,7 @@ def assert_ractor(src, args: [], require: nil, require_relative: nil, file: nil, #{require} previous_verbose = $VERBOSE $VERBOSE = nil - Ractor.new {} # trigger initial warning + Ractor.new {}.join # trigger initial warning $VERBOSE = previous_verbose #{src} RUBY @@ -500,9 +507,19 @@ def assert_raise_with_message(exception, expected, msg = nil, &block) assert_respond_to(expected, :===) assert = :assert_match end - - ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do - yield + ex = m = nil + if respond_to?(:multiple_ractors?) && multiple_ractors? + ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do + yield + end + m = ex.message + else + EnvUtil.with_default_internal(of: expected) do + ex = assert_raise(exception, msg || proc {"Exception(#{exception}) with message matches to #{expected.inspect}"}) do + yield + end + m = ex.message + end end m = ex.message msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"} @@ -554,7 +571,7 @@ def assert_raise_kind_of(*exp, &b) e end - TEST_DIR = File.join(__dir__, "test/unit") #:nodoc: + TEST_DIR = File.join(__dir__, "test/unit").freeze #:nodoc: # :call-seq: # assert(test, [failure_message]) @@ -673,11 +690,17 @@ def assert_pattern_list(pattern_list, actual, message=nil) def assert_warning(pat, msg = nil) result = nil - stderr = EnvUtil.with_default_internal(of: pat) { - EnvUtil.verbose_warning { + if respond_to?(:multiple_ractors?) && multiple_ractors? + stderr = EnvUtil.verbose_warning { result = yield } - } + else + stderr = EnvUtil.with_default_internal(of: pat) { + EnvUtil.verbose_warning { + result = yield + } + } + end msg = message(msg) {diff pat, stderr} assert(pat === stderr, msg) result @@ -699,27 +722,32 @@ def assert_deprecated_warn(mesg = /deprecated/, &block) end end - class << (AssertFile = Struct.new(:failure_message).new) - include Assertions - include CoreAssertions - def assert_file_predicate(predicate, *args) - if /\Anot_/ =~ predicate - predicate = $' - neg = " not" + def self.new_AssertFile + struct = Struct.new(:failure_message).new + # Uses method_missing, so contain it within its own object + class << struct + include Assertions + include CoreAssertions + def assert_file_predicate(predicate, *args) + if /\Anot_/ =~ predicate + predicate = $' + neg = " not" + end + result = File.__send__(predicate, *args) + result = !result if neg + mesg = "Expected file ".dup << args.shift.inspect + mesg << "#{neg} to be #{predicate}" + mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty? + mesg << " #{failure_message}" if failure_message + assert(result, mesg) end - result = File.__send__(predicate, *args) - result = !result if neg - mesg = "Expected file ".dup << args.shift.inspect - mesg << "#{neg} to be #{predicate}" - mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty? - mesg << " #{failure_message}" if failure_message - assert(result, mesg) - end - alias method_missing assert_file_predicate + alias method_missing assert_file_predicate - def for(message) - clone.tap {|a| a.failure_message = message} + def for(message) + clone.tap {|a| a.failure_message = message} + end end + struct end class AllFailures @@ -845,6 +873,7 @@ def assert_all_assertions_foreach(msg = nil, *keys, &block) # :yield: each elements of +seq+. def assert_linear_performance(seq, rehearsal: nil, pre: ->(n) {n}) pend "No PERFORMANCE_CLOCK found" unless defined?(PERFORMANCE_CLOCK) + pend "Timeout" if respond_to?(:main_ractor?) && !main_ractor? # Timeout testing generally doesn't work when RJIT compilation happens. rjit_enabled = defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? @@ -967,11 +996,20 @@ def self.glibc?(*ver) end def self.macos?(*ver) - unless defined?(@macos) - @macos = RUBY_PLATFORM.include?('darwin') && `sw_vers -productVersion`.scan(/\d+/).map(&:to_i) + is_main_ractor = (!respond_to?(:main_ractor?)) || main_ractor? + # Don't cache @macos as class ivar if we're running in a ractor. That means we shell out + # each time if we're not in main ractor, but this guard isn't used often so it's fine for now. + if !is_main_ractor || !defined?(@macos) + macos = RUBY_PLATFORM.include?('darwin') && `sw_vers -productVersion`.scan(/\d+/).map(&:to_i) end - version_match? ver, @macos + if macos && is_main_ractor + @macos = macos + elsif is_main_ractor + macos = @macos + end + version_match? ver, macos end + private def macos?(*ver) CoreAssertions.macos?(*ver) end diff --git a/tool/lib/envutil.rb b/tool/lib/envutil.rb index ab5e8d84e9c371..6377e50ba12018 100644 --- a/tool/lib/envutil.rb +++ b/tool/lib/envutil.rb @@ -36,12 +36,14 @@ def rubybin end module_function :rubybin - LANG_ENVS = %w"LANG LC_ALL LC_CTYPE" + LANG_ENVS = %w"LANG LC_ALL LC_CTYPE".freeze DEFAULT_SIGNALS = Signal.list DEFAULT_SIGNALS.delete("TERM") if /mswin|mingw/ =~ RUBY_PLATFORM + DEFAULT_SIGNALS.freeze - RUBYLIB = ENV["RUBYLIB"] + RUBYLIB = ENV["RUBYLIB"].to_s.freeze + MULTIPLE_RACTORS = ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 1 class << self attr_accessor :timeout_scale @@ -54,7 +56,7 @@ def capture_global_values @original_verbose = $VERBOSE @original_warning = if defined?(Warning.categories) - Warning.categories.to_h {|i| [i, Warning[i]]} + Warning.categories.to_h {|i| [i, Warning[i]]}.freeze elsif defined?(Warning.[]) # 2.7+ %i[deprecated experimental performance].to_h do |i| [i, begin Warning[i]; rescue ArgumentError; end] @@ -293,7 +295,9 @@ def flush; end ensure stderr, $stderr = $stderr, stderr $VERBOSE = EnvUtil.original_verbose - EnvUtil.original_warning&.each {|i, v| Warning[i] = v} + unless MULTIPLE_RACTORS + EnvUtil.original_warning&.each {|i, v| Warning[i] = v} + end end module_function :verbose_warning @@ -329,50 +333,64 @@ def suppress_warning module_function :suppress_warning def under_gc_stress(stress = true) - stress, GC.stress = GC.stress, stress - yield - ensure - GC.stress = stress + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + begin + stress, GC.stress = GC.stress, stress + yield + ensure + GC.stress = stress + end end module_function :under_gc_stress def under_gc_compact_stress(val = :empty, &block) raise "compaction doesn't work well on s390x. Omit the test in the caller." if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077 + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + begin + if GC.respond_to?(:auto_compact) + auto_compact = GC.auto_compact + GC.auto_compact = val + end - if GC.respond_to?(:auto_compact) - auto_compact = GC.auto_compact - GC.auto_compact = val + under_gc_stress(&block) + ensure + GC.auto_compact = auto_compact if GC.respond_to?(:auto_compact) end - - under_gc_stress(&block) - ensure - GC.auto_compact = auto_compact if GC.respond_to?(:auto_compact) end module_function :under_gc_compact_stress def without_gc - prev_disabled = GC.disable - yield - ensure - GC.enable unless prev_disabled + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS + begin + prev_disabled = GC.disable + yield + ensure + GC.enable unless prev_disabled + end end module_function :without_gc def with_default_external(enc = nil, of: nil) + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS enc = of.encoding if defined?(of.encoding) suppress_warning { Encoding.default_external = enc } - yield - ensure - suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding } + begin + yield + ensure + suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding } + end end module_function :with_default_external def with_default_internal(enc = nil, of: nil) + raise Test::Unit::PendedError.new("not ractor safe (#{__method__})") if MULTIPLE_RACTORS enc = of.encoding if defined?(of.encoding) suppress_warning { Encoding.default_internal = enc } - yield - ensure - suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding } + begin + yield + ensure + suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding } + end end module_function :with_default_internal @@ -401,8 +419,8 @@ def labeled_class(name, superclass = Object, &block) module_function :labeled_class if /darwin/ =~ RUBY_PLATFORM - DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports") - DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S' + DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports").freeze + DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S'.freeze @ruby_install_name = RbConfig::CONFIG['RUBY_INSTALL_NAME'] def self.diagnostic_reports(signame, pid, now) @@ -481,10 +499,12 @@ def self.gc_stress_to_class? if defined?(RbConfig) module RbConfig - @ruby = EnvUtil.rubybin + RUBY__ = EnvUtil.rubybin.freeze class << self undef ruby if method_defined?(:ruby) - attr_reader :ruby + def ruby + RUBY__ + end end dir = File.dirname(ruby) CONFIG['bindir'] = dir diff --git a/tool/lib/leakchecker.rb b/tool/lib/leakchecker.rb index 69df9a64b87192..975a88b1d6529b 100644 --- a/tool/lib/leakchecker.rb +++ b/tool/lib/leakchecker.rb @@ -14,6 +14,7 @@ def initialize end def check(test_name) + return if ENV["RUBY_TESTS_WITH_RACTORS"] if /i386-solaris/ =~ RUBY_PLATFORM && /TestGem/ =~ test_name GC.verify_internal_consistency end @@ -130,6 +131,7 @@ def check_fd_leak(test_name) def extend_tempfile_counter return if defined? LeakChecker::TempfileCounter + return if ENV["RUBY_TESTS_WITH_RACTORS"] m = Module.new { @count = 0 class << self @@ -150,6 +152,7 @@ class << Tempfile def find_tempfiles(prev_count=-1) return [prev_count, []] unless defined? Tempfile + return [prev_count,[]] if ENV["RUBY_TESTS_WITH_RACTORS"] extend_tempfile_counter count = TempfileCounter.count if prev_count == count @@ -164,6 +167,7 @@ def find_tempfiles(prev_count=-1) def check_tempfile_leak(test_name) return false unless defined? Tempfile + return false if ENV["RUBY_TESTS_WITH_RACTORS"] count1, initial_tempfiles = @tempfile_info count2, current_tempfiles = find_tempfiles(count1) leaked = false diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index 7d43e825e179eb..ab587a6875f0be 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -171,8 +171,10 @@ def process_args(args = []) opts = option_parser setup_options(opts, options) opts.parse!(args) + @option_parser = nil if ractors_enabled? orig_args -= args args = @init_hook.call(args, options) if @init_hook + @init_hook = nil if ractors_enabled? non_options(args, options) @run_options = orig_args @@ -818,6 +820,10 @@ def _run_parallel suites, type, result end def _run_suites suites, type + if ENV["RUBY_TESTS_WITH_RACTORS"] + Ractor.make_shareable(RbConfig::CONFIG) + Ractor.make_shareable(RbConfig::MAKEFILE_CONFIG) + end _prepare_run(suites, type) @interrupt = nil result = [] @@ -1505,6 +1511,9 @@ class Runner # :nodoc: all attr_accessor :info_signal + TESTCASE_COPYABLE_IVARS = Ractor.make_shareable({:@_assertions => true, :@__passed__ => true, :@__name__ => true}) + RUNNER_COPYABLE_IVARS = Ractor.make_shareable({:@report => true, :@failures => true, :@errors => true, :@skips => true, :@assertion_count => true, :@test_count => true}) + ## # Lazy accessor for options. @@ -1513,10 +1522,15 @@ def options end @@installed_at_exit ||= false - @@out = $stdout + OUT = "$stdout" @@after_tests = [] @@current_repeat_count = 0 + def ractors_enabled? + ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 0 + end + private :ractors_enabled? + ## # A simple hook allowing you to run a block of code after _all_ of # the tests are done. Eg: @@ -1531,15 +1545,29 @@ def self.after_tests &block # Returns the stream to use for output. def self.output - @@out + if String === OUT + eval OUT # due to Ractors + else + OUT + end end ## # Sets Test::Unit::Runner to write output to +stream+. $stdout is the default - # output + # output. NOTE: if not $stdout or $stderr, may not be ractor safe! def self.output= stream - @@out = stream + old_verbose = $VERBOSE + $VERBOSE = nil + fd_num = stream.to_i + if [1,2].include?(fd_num) + stream = fd_num == 1 ? "$stdout" : "$stderr" # best guess + const_set(:OUT, stream) + else + const_set(:OUT, stream) + end + ensure + $VERBOSE = old_verbose end ## @@ -1667,9 +1695,15 @@ def _run_suite suite, type trace = true end + run_tests_inside_ractors_num = ENV["RUBY_TESTS_WITH_RACTORS"].to_i + if run_tests_inside_ractors_num > 0 + def GC.stress=(val) + raise "Cannot call GC.stress=(val) concurrently (it might not be set back properly after teardown)" + end + end + tests_run = 0 assertions = all_test_methods.map { |method| - - inst = suite.new method + inst = suite.new method.to_s _start_method(inst) inst._assertions = 0 @@ -1680,13 +1714,68 @@ def _run_suite suite, type if trace ObjectSpace.trace_object_allocations {inst.run self} else - inst.run self + if run_tests_inside_ractors_num > 0 + old_report = self.report + old_failures = self.failures + old_errors = self.errors + old_skips = self.skips + old_assertion_count = self.assertion_count + old_test_count = self.test_count + self.report = [] + self.failures = 0 + self.errors = 0 + self.skips = 0 + self.assertion_count = 0 + self.test_count = 0 + rs = run_tests_inside_ractors_num.times.map do + Ractor.new(inst, self) do |instance, runner| + res = instance.run runner + testcase_copyable_ivars = TESTCASE_COPYABLE_IVARS + runner_copyable_ivars = RUNNER_COPYABLE_IVARS + instance.instance_variables.each do |ivar| + unless testcase_copyable_ivars[ivar] + instance.remove_instance_variable(ivar) + end + end + runner.instance_variables.each do |ivar| + unless runner_copyable_ivars[ivar] + runner.remove_instance_variable(ivar) + end + end + [instance, runner, res] + end + end + ractor_results = [] + while rs.any? + r, obj = Ractor.select(*rs) + inst, runner, res = *obj + ractor_results << [res, inst, runner] + rs.delete(r) + end + # ractors done + self.report = old_report + self.failures = old_failures + self.errors = old_errors + self.skips = old_skips + self.assertion_count = old_assertion_count + self.test_count = old_test_count + res = +"" + ractor_results.each do |(res0, inst, runner)| + res << res0 + _merge_results_from_ractor(inst, runner) + end + inst._assertions = self.assertion_count - old_assertion_count + res + else + inst.run self + end end + tests_run += 1 + print "%.2f s = " % (Time.now - start_time) if @verbose print result puts if @verbose - $stdout.flush leakchecker.check("#{inst.class}\##{inst.__name__}") @@ -1697,6 +1786,18 @@ def _run_suite suite, type return assertions.size, assertions.inject(0) { |sum, n| sum + n } end + def __init_runner(runner) + end + + def _merge_results_from_ractor(inst, runner_cpy) + self.report += runner_cpy.report + self.failures += runner_cpy.failures + self.errors += runner_cpy.errors + self.skips += runner_cpy.skips + self.assertion_count += inst._assertions + self.test_count += runner_cpy.test_count + end + def _start_method(inst) end def _end_method(inst) @@ -1734,8 +1835,10 @@ def initialize # :nodoc: @report = [] @errors = @failures = @skips = 0 @verbose = false - @mutex = Thread::Mutex.new - @info_signal = Signal.list['INFO'] + unless ractors_enabled? + @mutex = Thread::Mutex.new + @info_signal = Signal.list['INFO'] + end @repeat_count = nil end @@ -1763,6 +1866,11 @@ def _run args = [] self.options.merge! args puts "Run options: #{help}" + ractors_num = ENV["RUBY_TESTS_WITH_RACTORS"].to_i + if ractors_num > 0 + puts "\nNOTE: Running tests inside ractors (each test method inside #{ractors_num} " \ + "ractor#{ractors_num > 1 ? 's' : ''})" + end self.class.plugins.each do |plugin| send plugin diff --git a/tool/lib/test/unit/assertions.rb b/tool/lib/test/unit/assertions.rb index 19581fc3ab3ec0..7bcf272b993c6d 100644 --- a/tool/lib/test/unit/assertions.rb +++ b/tool/lib/test/unit/assertions.rb @@ -544,8 +544,12 @@ def skipped? # Takes a block and wraps it with the runner's shared mutex. def synchronize - Test::Unit::Runner.runner.synchronize do + if respond_to?(:main_ractor?) && !main_ractor? yield + else + Test::Unit::Runner.runner.synchronize do + yield + end end end diff --git a/tool/lib/test/unit/testcase.rb b/tool/lib/test/unit/testcase.rb index 51ffff37ebc376..bb7acefcfa4d1e 100644 --- a/tool/lib/test/unit/testcase.rb +++ b/tool/lib/test/unit/testcase.rb @@ -50,6 +50,23 @@ def mingw? platform = RUBY_PLATFORM /mingw/ =~ platform end + def main_ractor? + return true if !defined?(Ractor) + Ractor.current == Ractor.main + end + + def non_main_ractor? + not main_ractor? + end + + def multiple_ractors? + ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 1 + end + + # In order to guard generating methods dynamically that will run inside another ractor + def will_run_in_main_ractor? + ENV["RUBY_TESTS_WITH_RACTORS"].to_i == 0 + end end ## @@ -141,7 +158,7 @@ class TestCase alias method_name __name__ PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, - Interrupt, SystemExit] # :nodoc: + Interrupt, SystemExit].freeze # :nodoc: ## # Runs the tests reporting the status to +runner+ @@ -198,22 +215,24 @@ def run runner RUN_TEST_TRACE = "#{__FILE__}:#{__LINE__+3}:in `run_test'".freeze def run_test(name) - progname, $0 = $0, "#{$0}: #{self.class}##{name}" + progname, $0 = $0, "#{$0}: #{self.class}##{name}" if main_ractor? self.__send__(name) ensure - $@.delete(RUN_TEST_TRACE) if $@ - $0 = progname + if main_ractor? + $@.delete(RUN_TEST_TRACE) if $@ + $0 = progname + end end def initialize name # :nodoc: @__name__ = name @__io__ = nil @__passed__ = nil - @@__current__ = self # FIX: make thread local + Ractor.current[:__test_current__] = self end def self.current # :nodoc: - @@__current__ # FIX: make thread local + Ractor.current[:__test_current__] end ## diff --git a/tool/lib/tracepointchecker.rb b/tool/lib/tracepointchecker.rb index 3254e59357d493..7ff155227a2cb3 100644 --- a/tool/lib/tracepointchecker.rb +++ b/tool/lib/tracepointchecker.rb @@ -120,7 +120,7 @@ def self.check end if defined?(TracePoint.stat) class ::Test::Unit::TestCase - include TracePointChecker::ZombieTraceHunter + include TracePointChecker::ZombieTraceHunter unless ENV["RUBY_TESTS_WITH_RACTORS"] end if defined?(TracePointChecker) # TracePointChecker.start verbose: false diff --git a/tool/lib/zombie_hunter.rb b/tool/lib/zombie_hunter.rb index 33bc46794127f6..b1d3c9eaa5841b 100644 --- a/tool/lib/zombie_hunter.rb +++ b/tool/lib/zombie_hunter.rb @@ -3,7 +3,7 @@ module ZombieHunter def after_teardown super - assert_empty(Process.waitall) + assert_empty(Process.waitall) unless multiple_ractors? end end From 24968f678ba8a78d3fcb7f8ee575f0dc396d4e92 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Mon, 29 Sep 2025 15:22:33 -0400 Subject: [PATCH 2/2] Try to fix failing ci --- tool/lib/test/unit.rb | 70 +++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/tool/lib/test/unit.rb b/tool/lib/test/unit.rb index ab587a6875f0be..5bb31c69f33d8e 100644 --- a/tool/lib/test/unit.rb +++ b/tool/lib/test/unit.rb @@ -1522,12 +1522,16 @@ def options end @@installed_at_exit ||= false - OUT = "$stdout" @@after_tests = [] @@current_repeat_count = 0 + class << self + def ractors_enabled? + ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 0 + end + end def ractors_enabled? - ENV["RUBY_TESTS_WITH_RACTORS"].to_i > 0 + self.class.ractors_enabled? end private :ractors_enabled? @@ -1541,34 +1545,50 @@ def self.after_tests &block @@after_tests << block end - ## - # Returns the stream to use for output. + if ractors_enabled? + OUT = 1 + def self.output + case OUT + when 1 + $stdout + when 2 + $stderr + else + raise "Not supported" + end + end - def self.output - if String === OUT - eval OUT # due to Ractors - else - OUT + ## + # Sets Test::Unit::Runner to write output to +stream+. $stdout is the default + # output. NOTE: if not $stdout or $stderr, may not be ractor safe! + + def self.output= stream + saved_verbose = $VERBOSE + begin + $VERBOSE = nil + fd_num = stream.to_i + if [1,2].include?(fd_num) + const_set(:OUT, fd_num) + else + raise ArgumentError, "Must give stdout or stderr (fd 1 or 2), got fd=#{fd_num}" + end + ensure + $VERBOSE = saved_verbose + end + end + else + @@out = $stdout + def self.output + @@out + end + def self.output=(out) + @@out = out end end ## - # Sets Test::Unit::Runner to write output to +stream+. $stdout is the default - # output. NOTE: if not $stdout or $stderr, may not be ractor safe! - - def self.output= stream - old_verbose = $VERBOSE - $VERBOSE = nil - fd_num = stream.to_i - if [1,2].include?(fd_num) - stream = fd_num == 1 ? "$stdout" : "$stderr" # best guess - const_set(:OUT, stream) - else - const_set(:OUT, stream) - end - ensure - $VERBOSE = old_verbose - end + # Returns the stream to use for output. + ## # Tells Test::Unit::Runner to delegate to +runner+, an instance of a