# frozen_string_literal: true require 'test/unit' class TestBox < Test::Unit::TestCase EXPERIMENTAL_WARNINGS = [ "warning: Ruby::Box is experimental, and the behavior may change in the future!", "See doc/box.md for known issues, etc." ].join("\n") ENV_ENABLE_BOX = {'RUBY_BOX' => '1', 'TEST_DIR' => __dir__} def setup @n = nil @dir = __dir__ end def teardown @n = nil end def test_box_availability_in_default assert_separately([], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; assert_nil ENV['RUBY_BOX'] assert !Ruby::Box.enabled? end; end def test_box_availability_when_enabled assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; assert '1', ENV['RUBY_BOX'] assert Ruby::Box.enabled? end; end def test_current_box_in_main assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; assert_equal Ruby::Box.main, Ruby::Box.current assert Ruby::Box.main.main? end; end def test_require_rb_separately pend unless Ruby::Box.enabled? assert_raise(NameError) { NS_A } assert_raise(NameError) { NS_B } @n.require(File.join(__dir__, 'namespace', 'a.1_1_0')) assert_not_nil @n::NS_A assert_not_nil @n::NS_B assert_equal "1.1.0", @n::NS_A::VERSION assert_equal "yay 1.1.0", @n::NS_A.new.yay assert_equal "1.1.0", @n::NS_B::VERSION assert_equal "yay_b1", @n::NS_B.yay assert_raise(NameError) { NS_A } assert_raise(NameError) { NS_B } end def test_require_relative_rb_separately pend unless Ruby::Box.enabled? assert_raise(NameError) { NS_A } assert_raise(NameError) { NS_B } @n.require_relative('namespace/a.1_1_0') assert_not_nil @n::NS_A assert_not_nil @n::NS_B assert_equal "1.1.0", @n::NS_A::VERSION assert_equal "yay 1.1.0", @n::NS_A.new.yay assert_equal "1.1.0", @n::NS_B::VERSION assert_equal "yay_b1", @n::NS_B.yay assert_raise(NameError) { NS_A } assert_raise(NameError) { NS_B } end def test_load_separately pend unless Ruby::Box.enabled? assert_raise(NameError) { NS_A } assert_raise(NameError) { NS_B } @n.load(File.join(__dir__, 'namespace', 'a.1_1_0.rb')) assert_not_nil @n::NS_A assert_not_nil @n::NS_B assert_equal "1.1.0", @n::NS_A::VERSION assert_equal "yay 1.1.0", @n::NS_A.new.yay assert_equal "1.1.0", @n::NS_B::VERSION assert_equal "yay_b1", @n::NS_B.yay assert_raise(NameError) { NS_A } assert_raise(NameError) { NS_B } end def test_box_in_box pend unless Ruby::Box.enabled? assert_raise(NameError) { NS1 } assert_raise(NameError) { NS_A } assert_raise(NameError) { NS_B } @n.require_relative('namespace/ns') assert_not_nil @n::NS1 assert_not_nil @n::NS1::NS_A assert_not_nil @n::NS1::NS_B assert_equal "1.1.0", @n::NS1::NS_A::VERSION assert_equal "yay 1.1.0", @n::NS1::NS_A.new.yay assert_equal "1.1.0", @n::NS1::NS_B::VERSION assert_equal "yay_b1", @n::NS1::NS_B.yay assert_raise(NameError) { NS1 } assert_raise(NameError) { NS_A } assert_raise(NameError) { NS_B } end def test_require_rb_2versions pend unless Ruby::Box.enabled? assert_raise(NameError) { NS_A } @n.require(File.join(__dir__, 'namespace', 'a.1_2_0')) assert_equal "1.2.0", @n::NS_A::VERSION assert_equal "yay 1.2.0", @n::NS_A.new.yay n2 = Namespace.new n2.require(File.join(__dir__, 'namespace', 'a.1_1_0')) assert_equal "1.1.0", n2::NS_A::VERSION assert_equal "yay 1.1.0", n2::NS_A.new.yay # recheck @n is not affected by the following require assert_equal "1.2.0", @n::NS_A::VERSION assert_equal "yay 1.2.0", @n::NS_A.new.yay assert_raise(NameError) { NS_A } end def test_raising_errors_in_require pend unless Ruby::Box.enabled? assert_raise(RuntimeError, "Yay!") { @n.require(File.join(__dir__, 'namespace', 'raise')) } assert Namespace.current.inspect.include?("main") end def test_autoload_in_box pend unless Ruby::Box.enabled? assert_raise(NameError) { NS_A } @n.require_relative('namespace/autoloading') # autoloaded A is visible from global assert_equal '1.1.0', @n::NS_A::VERSION assert_raise(NameError) { NS_A } # autoload trigger NS_B::BAR is valid even from global assert_equal 'bar_b1', @n::NS_B::BAR assert_raise(NameError) { NS_A } assert_raise(NameError) { NS_B } end def test_continuous_top_level_method_in_a_box pend unless Ruby::Box.enabled? @n.require_relative('namespace/define_toplevel') @n.require_relative('namespace/call_toplevel') assert_raise(NameError) { foo } end def test_top_level_methods_in_box pend # TODO: fix loading/current box detection pend unless Ruby::Box.enabled? @n.require_relative('box/top_level') assert_equal "yay!", @n::Foo.foo assert_raise(NameError) { yaaay } assert_equal "foo", @n::Bar.bar assert_raise_with_message(RuntimeError, "boooo") { @n::Baz.baz } end def test_proc_defined_in_box_refers_module_in_box pend unless Ruby::Box.enabled? # require_relative dosn't work well in assert_separately even with __FILE__ and __LINE__ assert_separately([ENV_ENABLE_NAMESPACE], __FILE__, __LINE__, "here = '#{__dir__}'; #{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; ns1 = Namespace.new ns1.require(File.join("#{here}", 'namespace/proc_callee')) proc_v = ns1::Foo.callee assert_raise(NameError) { Target } assert ns1::Target assert_equal "fooooo", proc_v.call # refers Target in the namespace ns1 ns1.require(File.join("#{here}", 'namespace/proc_caller')) assert_equal "fooooo", ns1::Bar.caller(proc_v) ns2 = Namespace.new ns2.require(File.join("#{here}", 'namespace/proc_caller')) assert_raise(NameError) { ns2::Target } assert_equal "fooooo", ns2::Bar.caller(proc_v) # refers Target in the namespace ns1 end; end def test_proc_defined_globally_refers_global_module pend unless Ruby::Box.enabled? # require_relative dosn't work well in assert_separately even with __FILE__ and __LINE__ assert_separately([ENV_ENABLE_NAMESPACE], __FILE__, __LINE__, "here = '#{__dir__}'; #{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; require(File.join("#{here}", 'namespace/proc_callee')) def Target.foo "yay" end proc_v = Foo.callee assert Target assert_equal "yay", proc_v.call # refers global Foo ns1 = Namespace.new ns1.require(File.join("#{here}", 'namespace/proc_caller')) assert_equal "yay", ns1::Bar.caller(proc_v) ns2 = Namespace.new ns2.require(File.join("#{here}", 'namespace/proc_callee')) ns2.require(File.join("#{here}", 'namespace/proc_caller')) assert_equal "fooooo", ns2::Foo.callee.call assert_equal "yay", ns2::Bar.caller(proc_v) # should refer the global Target, not Foo in ns2 end; end def test_instance_variable pend unless Ruby::Box.enabled? @n.require_relative('namespace/instance_variables') assert_equal [], String.instance_variables assert_equal [:@str_ivar1, :@str_ivar2], @n::StringDelegatorObj.instance_variables assert_equal 111, @n::StringDelegatorObj.str_ivar1 assert_equal 222, @n::StringDelegatorObj.str_ivar2 assert_equal 222, @n::StringDelegatorObj.instance_variable_get(:@str_ivar2) @n::StringDelegatorObj.instance_variable_set(:@str_ivar3, 333) assert_equal 333, @n::StringDelegatorObj.instance_variable_get(:@str_ivar3) @n::StringDelegatorObj.remove_instance_variable(:@str_ivar1) assert_nil @n::StringDelegatorObj.str_ivar1 assert_equal [:@str_ivar2, :@str_ivar3], @n::StringDelegatorObj.instance_variables assert_equal [], String.instance_variables end def test_methods_added_in_box_are_invisible_globally pend unless Ruby::Box.enabled? @n.require_relative('namespace/string_ext') assert_equal "yay", @n::Bar.yay assert_raise(NoMethodError){ String.new.yay } end def test_continuous_method_definitions_in_a_box pend unless Ruby::Box.enabled? @n.require_relative('namespace/string_ext') assert_equal "yay", @n::Bar.yay @n.require_relative('namespace/string_ext_caller') assert_equal "yay", @n::Foo.yay @n.require_relative('namespace/string_ext_calling') end def test_methods_added_in_box_later_than_caller_code pend unless Ruby::Box.enabled? @n.require_relative('namespace/string_ext_caller') @n.require_relative('namespace/string_ext') assert_equal "yay", @n::Bar.yay assert_equal "yay", @n::Foo.yay end def test_method_added_in_box_are_available_on_eval pend unless Ruby::Box.enabled? @n.require_relative('namespace/string_ext') @n.require_relative('namespace/string_ext_eval_caller') assert_equal "yay", @n::Baz.yay end def test_method_added_in_box_are_available_on_eval_with_binding pend unless Ruby::Box.enabled? @n.require_relative('namespace/string_ext') @n.require_relative('namespace/string_ext_eval_caller') assert_equal "yay, yay!", @n::Baz.yay_with_binding end def test_methods_and_constants_added_by_include pend unless Ruby::Box.enabled? @n.require_relative('namespace/open_class_with_include') assert_equal "I'm saying foo 1", @n::OpenClassWithInclude.say assert_equal "I'm saying foo 1", @n::OpenClassWithInclude.say_foo assert_equal "I'm saying foo 1", @n::OpenClassWithInclude.say_with_obj("wow") assert_raise(NameError) { String::FOO } assert_equal "foo 1", @n::OpenClassWithInclude.refer_foo end end module ProcLookupTestA module B VALUE = 111 end end class TestBox < Test::Unit::TestCase def make_proc_from_block(&b) b end def test_proc_from_main_works_with_global_definitions pend unless Ruby::Box.enabled? @n.require_relative('namespace/procs') proc_and_labels = [ [Proc.new { String.new.yay }, "Proc.new"], [proc { String.new.yay }, "proc{}"], [lambda { String.new.yay }, "lambda{}"], [->(){ String.new.yay }, "->(){}"], [make_proc_from_block { String.new.yay }, "make_proc_from_block"], [@n::ProcInNS.make_proc_from_block { String.new.yay }, "make_proc_from_block in @n"], ] proc_and_labels.each do |str_pr| pr, pr_label = str_pr assert_raise(NoMethodError, "NoMethodError expected: #{pr_label}, called in main") { pr.call } assert_raise(NoMethodError, "NoMethodError expected: #{pr_label}, called in @n") { @n::ProcInNS.call_proc(pr) } end const_and_labels = [ [Proc.new { ProcLookupTestA::B::VALUE }, "Proc.new"], [proc { ProcLookupTestA::B::VALUE }, "proc{}"], [lambda { ProcLookupTestA::B::VALUE }, "lambda{}"], [->(){ ProcLookupTestA::B::VALUE }, "->(){}"], [make_proc_from_block { ProcLookupTestA::B::VALUE }, "make_proc_from_block"], [@n::ProcInNS.make_proc_from_block { ProcLookupTestA::B::VALUE }, "make_proc_from_block in @n"], ] const_and_labels.each do |const_pr| pr, pr_label = const_pr assert_equal 111, pr.call, "111 expected, #{pr_label} called in main" assert_equal 111, @n::ProcInNS.call_proc(pr), "111 expected, #{pr_label} called in @n" end end def test_proc_from_box_works_with_definitions_in_box pend unless Ruby::Box.enabled? @n.require_relative('namespace/procs') proc_types = [:proc_new, :proc_f, :lambda_f, :lambda_l, :block] proc_types.each do |proc_type| assert_equal 222, @n::ProcInNS.make_const_proc(proc_type).call, "ProcLookupTestA::B::VALUE should be 222 in @n" assert_equal "foo", @n::ProcInNS.make_str_const_proc(proc_type).call, "String::FOO should be \"foo\" in @n" assert_equal "yay", @n::ProcInNS.make_str_proc(proc_type).call, "String#yay should be callable in @n" # # TODO: method calls not-in-methods nor procs can't handle the current namespace correctly. # # assert_equal "yay,foo,222", # @n::ProcInNS.const_get(('CONST_' + proc_type.to_s.upcase).to_sym).call, # "Proc assigned to constants should refer constants correctly in @n" end end def test_class_module_singleton_methods pend unless Ruby::Box.enabled? @n.require_relative('namespace/singleton_methods') assert_equal "Good evening!", @n::SingletonMethods.string_greeing # def self.greeting assert_equal 42, @n::SingletonMethods.integer_answer # class << self; def answer assert_equal([], @n::SingletonMethods.array_blank) # def self.blank w/ instance methods assert_equal({status: 200, body: 'OK'}, @n::SingletonMethods.hash_http_200) # class << self; def ... w/ instance methods assert_equal([4, 4], @n::SingletonMethods.array_instance_methods_return_size([1, 2, 3, 4])) assert_equal([3, 3], @n::SingletonMethods.hash_instance_methods_return_size({a: 2, b: 4, c: 8})) assert_raise(NoMethodError) { String.greeting } assert_raise(NoMethodError) { Integer.answer } assert_raise(NoMethodError) { Array.blank } assert_raise(NoMethodError) { Hash.http_200 } end def test_add_constants_in_box pend unless Ruby::Box.enabled? String.const_set(:STR_CONST0, 999) assert_equal 999, String::STR_CONST0 assert_equal 999, String.const_get(:STR_CONST0) assert_raise(NameError) { String.const_get(:STR_CONST1) } assert_raise(NameError) { String::STR_CONST2 } assert_raise(NameError) { String::STR_CONST3 } assert_raise(NameError) { Integer.const_get(:INT_CONST1) } EnvUtil.suppress_warning do @n.require_relative('namespace/consts') end assert_equal 999, String::STR_CONST0 assert_raise(NameError) { String::STR_CONST1 } assert_raise(NameError) { String::STR_CONST2 } assert_raise(NameError) { Integer::INT_CONST1 } assert_not_nil @n::ForConsts.refer_all assert_equal 112, @n::ForConsts.refer1 assert_equal 112, @n::ForConsts.get1 assert_equal 112, @n::ForConsts::CONST1 assert_equal 222, @n::ForConsts.refer2 assert_equal 222, @n::ForConsts.get2 assert_equal 222, @n::ForConsts::CONST2 assert_equal 333, @n::ForConsts.refer3 assert_equal 333, @n::ForConsts.get3 assert_equal 333, @n::ForConsts::CONST3 EnvUtil.suppress_warning do @n::ForConsts.const_set(:CONST3, 334) end assert_equal 334, @n::ForConsts::CONST3 assert_equal 334, @n::ForConsts.refer3 assert_equal 334, @n::ForConsts.get3 assert_equal 10, @n::ForConsts.refer_top_const # use Proxy object to use usual methods instead of singleton methods proxy = @n::ForConsts::Proxy.new assert_raise(NameError){ proxy.call_str_refer0 } assert_raise(NameError){ proxy.call_str_get0 } proxy.call_str_set0(30) assert_equal 30, proxy.call_str_refer0 assert_equal 30, proxy.call_str_get0 assert_equal 999, String::STR_CONST0 proxy.call_str_remove0 assert_raise(NameError){ proxy.call_str_refer0 } assert_raise(NameError){ proxy.call_str_get0 } assert_equal 112, proxy.call_str_refer1 assert_equal 112, proxy.call_str_get1 assert_equal 223, proxy.call_str_refer2 assert_equal 223, proxy.call_str_get2 assert_equal 333, proxy.call_str_refer3 assert_equal 333, proxy.call_str_get3 EnvUtil.suppress_warning do proxy.call_str_set3 end assert_equal 334, proxy.call_str_refer3 assert_equal 334, proxy.call_str_get3 assert_equal 1, proxy.refer_int_const1 assert_equal 999, String::STR_CONST0 assert_raise(NameError) { String::STR_CONST1 } assert_raise(NameError) { String::STR_CONST2 } assert_raise(NameError) { String::STR_CONST3 } assert_raise(NameError) { Integer::INT_CONST1 } end def test_global_variables default_l = $-0 default_f = $, pend unless Ruby::Box.enabled? assert_equal "\n", $-0 # equal to $/, line splitter assert_equal nil, $, # field splitter @n.require_relative('namespace/global_vars') # read first assert_equal "\n", @n::LineSplitter.read @n::LineSplitter.write("\r\n") assert_equal "\r\n", @n::LineSplitter.read assert_equal "\n", $-0 # write first @n::FieldSplitter.write(",") assert_equal ",", @n::FieldSplitter.read assert_equal nil, $, # used only in ns assert !global_variables.include?(:$used_only_in_ns) @n::UniqueGvar.write(123) assert_equal 123, @n::UniqueGvar.read assert_nil $used_only_in_ns # Kernel#global_variables returns the sum of all gvars. global_gvars = global_variables.sort assert_equal global_gvars, @n::UniqueGvar.gvars_in_ns.sort @n::UniqueGvar.write_only(456) assert_equal (global_gvars + [:$write_only_var_in_ns]).sort, @n::UniqueGvar.gvars_in_ns.sort assert_equal (global_gvars + [:$write_only_var_in_ns]).sort, global_variables.sort ensure EnvUtil.suppress_warning do $-0 = default_l $, = default_f end end def test_load_path_and_loaded_features pend unless Ruby::Box.enabled? assert $LOAD_PATH.respond_to?(:resolve_feature_path) @n.require_relative('namespace/load_path') assert_not_equal $LOAD_PATH, @n::LoadPathCheck::FIRST_LOAD_PATH assert @n::LoadPathCheck::FIRST_LOAD_PATH_RESPOND_TO_RESOLVE namespace_dir = File.join(__dir__, 'namespace') # TODO: $LOADED_FEATURES in method calls should refer the current namespace in addition to the loading namespace. # assert @n::LoadPathCheck.current_loaded_features.include?(File.join(namespace_dir, 'blank1.rb')) # assert !@n::LoadPathCheck.current_loaded_features.include?(File.join(namespace_dir, 'blank2.rb')) # assert @n::LoadPathCheck.require_blank2 # assert @n::LoadPathCheck.current_loaded_features.include?(File.join(namespace_dir, 'blank2.rb')) assert !$LOADED_FEATURES.include?(File.join(namespace_dir, 'blank1.rb')) assert !$LOADED_FEATURES.include?(File.join(namespace_dir, 'blank2.rb')) end def test_eval_basic pend unless Ruby::Box.enabled? # Test basic evaluation result = @n.eval("1 + 1") assert_equal 2, result # Test string evaluation result = @n.eval("'hello ' + 'world'") assert_equal "hello world", result end def test_eval_with_constants pend unless Ruby::Box.enabled? # Define a constant in the namespace via eval @n.eval("TEST_CONST = 42") assert_equal 42, @n::TEST_CONST # Constant should not be visible in main namespace assert_raise(NameError) { TEST_CONST } end def test_eval_with_classes pend unless Ruby::Box.enabled? # Define a class in the namespace via eval @n.eval("class TestClass; def hello; 'from namespace'; end; end") # Class should be accessible in the namespace instance = @n::TestClass.new assert_equal "from namespace", instance.hello # Class should not be visible in main namespace assert_raise(NameError) { TestClass } end def test_eval_isolation pend unless Ruby::Box.enabled? # Create another namespace n2 = Namespace.new # Define different constants in each namespace @n.eval("ISOLATION_TEST = 'first'") n2.eval("ISOLATION_TEST = 'second'") # Each namespace should have its own constant assert_equal "first", @n::ISOLATION_TEST assert_equal "second", n2::ISOLATION_TEST # Constants should not interfere with each other assert_not_equal @n::ISOLATION_TEST, n2::ISOLATION_TEST end def test_eval_with_variables pend unless Ruby::Box.enabled? # Test local variable access (should work within the eval context) result = @n.eval("x = 10; y = 20; x + y") assert_equal 30, result end def test_eval_error_handling pend unless Ruby::Box.enabled? # Test syntax error assert_raise(SyntaxError) { @n.eval("1 +") } # Test name error assert_raise(NameError) { @n.eval("undefined_variable") } # Test that namespace is properly restored after error begin @n.eval("raise RuntimeError, 'test error'") rescue RuntimeError # Should be able to continue using the namespace result = @n.eval("2 + 2") assert_equal 4, result end end # Tests which run always (w/o RUBY_BOX=1 globally) def test_prelude_gems_and_loaded_features assert_in_out_err([ENV_ENABLE_BOX, "--enable=gems"], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error| begin; puts ["before:", $LOADED_FEATURES.select{ it.end_with?("/bundled_gems.rb") }&.first].join puts ["before:", $LOADED_FEATURES.select{ it.end_with?("/error_highlight.rb") }&.first].join require "error_highlight" puts ["after:", $LOADED_FEATURES.select{ it.end_with?("/bundled_gems.rb") }&.first].join puts ["after:", $LOADED_FEATURES.select{ it.end_with?("/error_highlight.rb") }&.first].join end; # No additional warnings except for experimental warnings assert_includes error.join("\n"), EXPERIMENTAL_WARNINGS assert_equal 2, error.size assert_includes output.grep(/^before:/).join("\n"), '/bundled_gems.rb' assert_includes output.grep(/^before:/).join("\n"), '/error_highlight.rb' assert_includes output.grep(/^after:/).join("\n"), '/bundled_gems.rb' assert_includes output.grep(/^after:/).join("\n"), '/error_highlight.rb' end end def test_prelude_gems_and_loaded_features_with_disable_gems assert_in_out_err([ENV_ENABLE_BOX, "--disable=gems"], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error| begin; puts ["before:", $LOADED_FEATURES.select{ it.end_with?("/bundled_gems.rb") }&.first].join puts ["before:", $LOADED_FEATURES.select{ it.end_with?("/error_highlight.rb") }&.first].join require "error_highlight" puts ["after:", $LOADED_FEATURES.select{ it.end_with?("/bundled_gems.rb") }&.first].join puts ["after:", $LOADED_FEATURES.select{ it.end_with?("/error_highlight.rb") }&.first].join end; assert_includes error.join("\n"), EXPERIMENTAL_WARNINGS assert_equal 2, error.size refute_includes output.grep(/^before:/).join("\n"), '/bundled_gems.rb' refute_includes output.grep(/^before:/).join("\n"), '/error_highlight.rb' refute_includes output.grep(/^after:/).join("\n"), '/bundled_gems.rb' assert_includes output.grep(/^after:/).join("\n"), '/error_highlight.rb' end end def test_root_and_main_methods assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; pend unless Ruby::Box.respond_to?(:root) and Ruby::Box.respond_to?(:main) # for RUBY_DEBUG > 0 assert Ruby::Box.root.respond_to?(:root?) assert Ruby::Box.main.respond_to?(:main?) assert Ruby::Box.root.root? assert Ruby::Box.main.main? assert_equal Ruby::Box.main, Ruby::Box.current $a = 1 $LOADED_FEATURES.push("/tmp/foobar") assert_equal 2, Ruby::Box.root.eval('$a = 2; $a') assert !Ruby::Box.root.eval('$LOADED_FEATURES.push("/tmp/barbaz"); $LOADED_FEATURES.include?("/tmp/foobar")') assert "FooClass", Ruby::Box.root.eval('class FooClass; end; Object.const_get(:FooClass).to_s') assert_equal 1, $a assert !$LOADED_FEATURES.include?("/tmp/barbaz") assert !Object.const_defined?(:FooClass) end; ensure tmp = ENV["TMPDIR"] || ENV["TMP"] || Etc.systmpdir || "/tmp" pat = "_ruby_ns_*."+RbConfig::CONFIG["DLEXT"] File.unlink(*Dir.glob(pat, base: tmp).map {|so| "#{tmp}/#{so}"}) end def test_basic_box_detections assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; ns = Ruby::Box.new $gvar1 = 'bar' code = <<~EOC NS1 = Ruby::Box.current $gvar1 = 'foo' def toplevel = $gvar1 class Foo NS2 = Ruby::Box.current NS2_proc = ->(){ NS2 } NS3_proc = ->(){ Ruby::Box.current } def ns4 = Ruby::Box.current def self.ns5 = NS2 def self.ns6 = Ruby::Box.current def self.ns6_proc = ->(){ Ruby::Box.current } def self.ns7 res = [] [1,2].chunk{ it.even? }.each do |bool, members| res << Ruby::Box.current.object_id.to_s + ":" + bool.to_s + ":" + members.map(&:to_s).join(",") end res end def self.yield_block = yield def self.call_block(&b) = b.call def self.gvar1 = $gvar1 def self.call_toplevel = toplevel end FOO_NAME = Foo.name module Kernel def foo_box = Ruby::Box.current module_function :foo_box end NS_X = Foo.new.ns4 NS_Y = foo_box EOC ns.eval(code) outer = Ruby::Box.current assert_equal ns, ns::NS1 # on TOP frame assert_equal ns, ns::Foo::NS2 # on CLASS frame assert_equal ns, ns::Foo::NS2_proc.call # proc -> a const on CLASS assert_equal ns, ns::Foo::NS3_proc.call # proc -> the current assert_equal ns, ns::Foo.new.ns4 # instance method -> the current assert_equal ns, ns::Foo.ns5 # singleton method -> a const on CLASS assert_equal ns, ns::Foo.ns6 # singleton method -> the current assert_equal ns, ns::Foo.ns6_proc.call # method returns a proc -> the current # a block after CFUNC/IFUNC in a method -> the current assert_equal ["#{ns.object_id}:false:1", "#{ns.object_id}:true:2"], ns::Foo.ns7 assert_equal outer, ns::Foo.yield_block{ Ruby::Box.current } # method yields assert_equal outer, ns::Foo.call_block{ Ruby::Box.current } # method calls a block assert_equal 'foo', ns::Foo.gvar1 # method refers gvar assert_equal 'bar', $gvar1 # gvar value out of the ns assert_equal 'foo', ns::Foo.call_toplevel # toplevel method referring gvar assert_equal ns, ns::NS_X # on TOP frame, referring a class in the current assert_equal ns, ns::NS_Y # on TOP frame, referring Kernel method defined by a CFUNC method assert_equal "Foo", ns::FOO_NAME assert_equal "Foo", ns::Foo.name end; end def test_loading_extension_libs_in_main_box pend if /mswin|mingw/ =~ RUBY_PLATFORM # timeout on windows environments assert_separately([ENV_ENABLE_BOX], __FILE__, __LINE__, "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true) begin; require "prism" require "optparse" require "date" require "time" require "delegate" require "singleton" require "pp" require "fileutils" require "tempfile" require "tmpdir" require "json" require "psych" require "yaml" require "zlib" require "open3" require "ipaddr" require "net/http" require "openssl" require "socket" require "uri" require "digest" require "erb" require "stringio" require "monitor" require "timeout" require "securerandom" expected = 1 assert_equal expected, 1 end; end end