summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorSatoshi Tagomori <s-tagomori@sakura.ad.jp>2026-05-08 22:37:22 +0900
committerSatoshi Tagomori <tagomoris@gmail.com>2026-05-10 09:38:45 +0900
commit276f0d9b3efbabe46e74510e5e3585924b37772c (patch)
tree2a7e19f7a1709d02078be569022be70757c27c86 /test
parent5ed1f93b0e87d961282a4487c81b48cf6f0b127f (diff)
[Bug #21881] Separate the master and root box
This change separates the master box from the root box, as the single master copy of boxes. Before this change, the root box is the source of copies, and also it runs builtin classes' code. Builtin code makes changes by requiring files, thus the source of copies is mutable, be different from the past one. This causes different internal states of boxes, depending on when it is created. After this change, all boxes are created from the master box, and the master box never runs code, so it realizes the immutable source of copies. This also makes is possible to separate RubyGems (and other default gems) from each boxes. All boxes will have their own copies of RubyGems. RubyGems has its internal state, but it'll be separated between boxes after this change.
Diffstat (limited to 'test')
-rw-r--r--test/ruby/test_box.rb89
1 files changed, 83 insertions, 6 deletions
diff --git a/test/ruby/test_box.rb b/test/ruby/test_box.rb
index 9d60941526..34b5d5fa96 100644
--- a/test/ruby/test_box.rb
+++ b/test/ruby/test_box.rb
@@ -2,6 +2,7 @@
require 'test/unit'
require 'rbconfig'
+require 'tempfile'
class TestBox < Test::Unit::TestCase
EXPERIMENTAL_WARNING_LINE_PATTERNS = [
@@ -155,8 +156,7 @@ class TestBox < Test::Unit::TestCase
assert_include Ruby::Box.current.inspect, "main"
end
- def test_class_variables
- # [Bug #21952]
+ def test_class_variables_in_root_are_invisible_in_other_boxes
assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "here = '#{__dir__}'; #{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
begin;
Ruby::Box.root.eval(<<~RUBY)
@@ -179,10 +179,9 @@ class TestBox < Test::Unit::TestCase
REPRO
b1 = Ruby::Box.new
- assert_equal 2, b1.eval(code)
-
- b2 = Ruby::Box.new
- assert_equal 2, b2.eval(code)
+ assert_raise(NameError, "uninitialized class variable @@x in B") {
+ b1.eval(code)
+ }
end;
end
@@ -899,6 +898,72 @@ class TestBox < Test::Unit::TestCase
end
end
+ def test_calling_root_box_methods_does_not_change_user_boxes_newly_created
+ assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
+ begin;
+ assert_not_include Object.constants.sort, :Find # required by Pathname#find
+ assert_not_include Ruby::Box.root.eval("Object.constants.sort"), :Find
+ b1 = Ruby::Box.new
+ assert_not_include b1.eval("Object.constants.sort"), :Find
+
+ require 'pathname'
+ Pathname.new('.').find{|path| path.directory?}
+ assert_include Object.constants.sort, :Find # required by Pathname#find
+
+ assert_not_include Ruby::Box.root.eval("Object.constants.sort"), :Find
+ assert_not_include b1.eval("Object.constants.sort"), :Find
+
+ Ruby::Box.root.eval("require 'pathname'; Pathname.new('.').find{|path| path.directory? }")
+ assert_include Ruby::Box.root.eval("Object.constants.sort"), :Find
+
+ assert_not_include b1.eval("Object.constants.sort"), :Find
+ b2 = Ruby::Box.new
+ assert_not_include b2.eval("Object.constants.sort"), :Find
+ end;
+ end
+
+ def test_boxes_have_different_rubygems
+ # assert_separately w/ ENV_ENABLE_BOX and --enable=gems causes timeouts on CI @ Windows
+ assert_in_out_err([ENV_ENABLE_BOX, "--enable=gems"], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
+ begin;
+ require "json"
+ h = {main: Gem.object_id, root: Ruby::Box.root.eval("Gem").object_id, box: Ruby::Box.new.eval("Gem").object_id}
+ puts h.to_json
+ end;
+ require "json"
+ result = JSON.parse(output.first, symbolize_names: true)
+ assert_not_equal result[:main], result[:root]
+ assert_not_equal result[:box], result[:root]
+ assert_not_equal result[:main], result[:box]
+ end
+ end
+
+ def test_require_list_loaded_only_in_main_box
+ Tempfile.create(["req_a", ".rb"]) do |t1|
+ Tempfile.create(["req_b", ".rb"]) do |t2|
+ t1.puts "module FooBarA; end"
+ t1.close
+ t2.puts "module FooBarB; end"
+ t2.close
+
+ opts = [ENV_ENABLE_BOX, "-r#{t1.path}", "-r#{t2.path}"]
+ assert_separately(opts, __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
+ begin;
+ main_constants = Object.constants
+ assert_include main_constants, :FooBarA
+ assert_include main_constants, :FooBarB
+
+ root_constants = Ruby::Box.root.eval("Object.constants.sort")
+ master_constants = Ruby::Box.master.eval("Object.constants.sort")
+ assert_not_include root_constants, :FooBarA
+ assert_not_include root_constants, :FooBarB
+ assert_not_include master_constants, :FooBarA
+ assert_not_include master_constants, :FooBarB
+ end;
+ end
+ end
+ end
+
def test_root_and_main_methods
assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
begin;
@@ -997,6 +1062,18 @@ class TestBox < Test::Unit::TestCase
end;
end
+ def test_very_basic_method_calls_and_constants
+ assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
+ begin;
+ code = <<~EOC
+ consts = Object.constants
+ [consts.include?(:String), consts.include?(:Array)]
+ EOC
+ assert_equal([true, true], Ruby::Box.current.eval(code))
+ assert_equal([true, true], Ruby::Box.root.eval(code))
+ end;
+ end
+
def test_loading_extension_libs_in_main_box_1
pend if /mswin|mingw/ =~ RUBY_PLATFORM # timeout on windows environments
assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)