diff options
Diffstat (limited to 'test/ruby/test_env.rb')
| -rw-r--r-- | test/ruby/test_env.rb | 1475 |
1 files changed, 1455 insertions, 20 deletions
diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb index 3a65f91fa4..87ccd5102b 100644 --- a/test/ruby/test_env.rb +++ b/test/ruby/test_env.rb @@ -1,16 +1,36 @@ +# frozen_string_literal: false require 'test/unit' class TestEnv < Test::Unit::TestCase - IGNORE_CASE = /djgpp|bccwin|mswin|mingw/ =~ RUBY_PLATFORM + IGNORE_CASE = /bccwin|mswin|mingw/ =~ RUBY_PLATFORM + PATH_ENV = "PATH" + INVALID_ENVVARS = [ + "foo\0bar", + "\xa1\xa1".force_encoding(Encoding::UTF_16LE), + "foo".force_encoding(Encoding::ISO_2022_JP), + ] + + def assert_invalid_env(msg = nil) + all_assertions(msg) do |a| + INVALID_ENVVARS.each do |v| + a.for(v) do + assert_raise(ArgumentError) {yield v} + end + end + end + end def setup - @backup = ENV.delete('test') - @BACKUP = ENV.delete('TEST') + @verbose = $VERBOSE + @backup = ENV.to_hash + ENV.delete('test') + ENV.delete('TEST') end def teardown - ENV['test'] = @backup if @backup - ENV['TEST'] = @BACKUP if @BACKUP + $VERBOSE = @verbose + ENV.clear + @backup.each {|k, v| ENV[k] = v } end def test_bracket @@ -31,20 +51,60 @@ class TestEnv < Test::Unit::TestCase assert_equal('foo', ENV['test']) end - assert_raises(TypeError) { - tmp = ENV[1] + assert_raise(TypeError) { + ENV[1] } - assert_raises(TypeError) { + assert_raise(TypeError) { ENV[1] = 'foo' } - assert_raises(TypeError) { + assert_raise(TypeError) { ENV['test'] = 0 } end + def test_dup + assert_raise(TypeError) { + ENV.dup + } + end + + def test_clone + warning = /ENV\.clone is deprecated; use ENV\.to_h instead/ + clone = assert_deprecated_warning(warning) { + ENV.clone + } + assert_same(ENV, clone) + + clone = assert_deprecated_warning(warning) { + ENV.clone(freeze: false) + } + assert_same(ENV, clone) + + clone = assert_deprecated_warning(warning) { + ENV.clone(freeze: nil) + } + assert_same(ENV, clone) + + assert_raise(TypeError) { + ENV.clone(freeze: true) + } + assert_raise(ArgumentError) { + ENV.clone(freeze: 1) + } + assert_raise(ArgumentError) { + ENV.clone(foo: false) + } + assert_raise(ArgumentError) { + ENV.clone(1) + } + assert_raise(ArgumentError) { + ENV.clone(1, foo: false) + } + end + def test_has_value val = 'a' - val.succ! while ENV.has_value?(val) && ENV.has_value?(val.upcase) + val.succ! while ENV.has_value?(val) || ENV.has_value?(val.upcase) ENV['test'] = val[0...-1] assert_equal(false, ENV.has_value?(val)) @@ -57,26 +117,1401 @@ class TestEnv < Test::Unit::TestCase assert_equal(true, ENV.has_value?(val.upcase)) end - def test_index + def test_key val = 'a' - val.succ! while ENV.has_value?(val) && ENV.has_value?(val.upcase) + val.succ! while ENV.has_value?(val) || ENV.has_value?(val.upcase) ENV['test'] = val[0...-1] - assert_nil(ENV.index(val)) - assert_nil(ENV.index(val.upcase)) + assert_nil(ENV.key(val)) + assert_nil(ENV.key(val.upcase)) ENV['test'] = val if IGNORE_CASE - assert_equal('TEST', ENV.index(val).upcase) + assert_equal('TEST', ENV.key(val).upcase) else - assert_equal('test', ENV.index(val)) + assert_equal('test', ENV.key(val)) end - assert_nil(ENV.index(val.upcase)) + assert_nil(ENV.key(val.upcase)) ENV['test'] = val.upcase - assert_nil(ENV.index(val)) + assert_nil(ENV.key(val)) + if IGNORE_CASE + assert_equal('TEST', ENV.key(val.upcase).upcase) + else + assert_equal('test', ENV.key(val.upcase)) + end + end + + def test_delete + assert_invalid_env {|v| ENV.delete(v)} + assert_nil(ENV.delete("TEST")) + assert_nothing_raised { ENV.delete(PATH_ENV) } + assert_equal("NO TEST", ENV.delete("TEST") {|name| "NO "+name}) + end + + def test_getenv + assert_invalid_env {|v| ENV[v]} + ENV[PATH_ENV] = "" + assert_equal("", ENV[PATH_ENV]) + assert_nil(ENV[""]) + end + + def test_fetch + ENV["test"] = "foo" + assert_equal("foo", ENV.fetch("test")) + ENV.delete("test") + feature8649 = '[ruby-core:56062] [Feature #8649]' + e = assert_raise_with_message(KeyError, /key not found: "test"/, feature8649) do + ENV.fetch("test") + end + assert_same(ENV, e.receiver) + assert_equal("test", e.key) + assert_equal("foo", ENV.fetch("test", "foo")) + assert_equal("bar", ENV.fetch("test") { "bar" }) + EnvUtil.suppress_warning do + assert_equal("bar", ENV.fetch("test", "foo") { "bar" }) + end + assert_invalid_env {|v| ENV.fetch(v)} + assert_nothing_raised { ENV.fetch(PATH_ENV, "foo") } + ENV[PATH_ENV] = "" + assert_equal("", ENV.fetch(PATH_ENV)) + end + + def test_aset + assert_nothing_raised { ENV["test"] = nil } + assert_equal(nil, ENV["test"]) + assert_invalid_env {|v| ENV[v] = "test"} + assert_invalid_env {|v| ENV["test"] = v} + + begin + # setenv(3) allowed the name includes '=', + # but POSIX.1-2001 says it should fail with EINVAL. + # see also http://togetter.com/li/22380 + ENV["foo=bar"] = "test" + assert_equal("test", ENV["foo=bar"]) + assert_equal("test", ENV["foo"]) + rescue Errno::EINVAL + end + end + + def test_keys + a = ENV.keys + assert_kind_of(Array, a) + a.each {|k| assert_kind_of(String, k) } + end + + def test_each_key + ENV.each_key {|k| assert_kind_of(String, k) } + end + + def test_values + a = ENV.values + assert_kind_of(Array, a) + a.each {|k| assert_kind_of(String, k) } + end + + def test_each_value + ENV.each_value {|k| assert_kind_of(String, k) } + end + + def test_each_pair + ENV.each_pair do |k, v| + assert_kind_of(String, k) + assert_kind_of(String, v) + end + end + + def test_reject_bang + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.reject! {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + assert_equal(h1, h2) + + assert_nil(ENV.reject! {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" }) + end + + def test_delete_if + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.delete_if {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + assert_equal(h1, h2) + + assert_equal(ENV, ENV.delete_if {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" }) + end + + def test_select_bang + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.select! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + assert_equal(h1, h2) + + assert_nil(ENV.select! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" }) + end + + def test_filter_bang + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.filter! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + assert_equal(h1, h2) + + assert_nil(ENV.filter! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" }) + end + + def test_keep_if + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.keep_if {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + assert_equal(h1, h2) + + assert_equal(ENV, ENV.keep_if {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" }) + end + + def test_values_at + ENV["test"] = "foo" + assert_equal(["foo", "foo"], ENV.values_at("test", "test")) + end + + def test_select + ENV["test"] = "foo" + h = ENV.select {|k| IGNORE_CASE ? k.upcase == "TEST" : k == "test" } + assert_equal(1, h.size) + k = h.keys.first + v = h.values.first + if IGNORE_CASE + assert_equal("TEST", k.upcase) + assert_equal("FOO", v.upcase) + else + assert_equal("test", k) + assert_equal("foo", v) + end + end + + def test_filter + ENV["test"] = "foo" + h = ENV.filter {|k| IGNORE_CASE ? k.upcase == "TEST" : k == "test" } + assert_equal(1, h.size) + k = h.keys.first + v = h.values.first if IGNORE_CASE - assert_equal('TEST', ENV.index(val.upcase).upcase) + assert_equal("TEST", k.upcase) + assert_equal("FOO", v.upcase) else - assert_equal('test', ENV.index(val.upcase)) + assert_equal("test", k) + assert_equal("foo", v) + end + end + + def test_slice + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + ENV["bar"] = "rab" + assert_equal({}, ENV.slice()) + assert_equal({}, ENV.slice("")) + assert_equal({}, ENV.slice("unknown")) + assert_equal({"foo"=>"bar", "baz"=>"qux"}, ENV.slice("foo", "baz")) + end + + def test_except + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + ENV["bar"] = "rab" + assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, ENV.except()) + assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, ENV.except("")) + assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, ENV.except("unknown")) + assert_equal({"bar"=>"rab"}, ENV.except("foo", "baz")) + end + + def test_clear + ENV.clear + assert_equal(0, ENV.size) + end + + def test_to_s + assert_equal("ENV", ENV.to_s) + end + + def test_inspect + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + s = ENV.inspect + if IGNORE_CASE + s = s.upcase + assert(s == '{"FOO"=>"BAR", "BAZ"=>"QUX"}' || s == '{"BAZ"=>"QUX", "FOO"=>"BAR"}') + else + assert(s == '{"foo"=>"bar", "baz"=>"qux"}' || s == '{"baz"=>"qux", "foo"=>"bar"}') + end + end + + def test_to_a + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + a = ENV.to_a + assert_equal(2, a.size) + if IGNORE_CASE + a = a.map {|x| x.map {|y| y.upcase } } + assert(a == [%w(FOO BAR), %w(BAZ QUX)] || a == [%w(BAZ QUX), %w(FOO BAR)]) + else + assert(a == [%w(foo bar), %w(baz qux)] || a == [%w(baz qux), %w(foo bar)]) + end + end + + def test_rehash + assert_nil(ENV.rehash) + end + + def test_size + s = ENV.size + ENV["test"] = "foo" + assert_equal(s + 1, ENV.size) + end + + def test_empty_p + ENV.clear + assert_predicate(ENV, :empty?) + ENV["test"] = "foo" + assert_not_predicate(ENV, :empty?) + end + + def test_has_key + assert_not_send([ENV, :has_key?, "test"]) + ENV["test"] = "foo" + assert_send([ENV, :has_key?, "test"]) + assert_invalid_env {|v| ENV.has_key?(v)} + end + + def test_assoc + assert_nil(ENV.assoc("test")) + ENV["test"] = "foo" + k, v = ENV.assoc("test") + if IGNORE_CASE + assert_equal("TEST", k.upcase) + assert_equal("FOO", v.upcase) + else + assert_equal("test", k) + assert_equal("foo", v) + end + assert_invalid_env {|var| ENV.assoc(var)} + encoding = /mswin|mingw/ =~ RUBY_PLATFORM ? Encoding::UTF_8 : Encoding.find("locale") + assert_equal(encoding, v.encoding) + end + + def test_has_value2 + ENV.clear + assert_not_send([ENV, :has_value?, "foo"]) + ENV["test"] = "foo" + assert_send([ENV, :has_value?, "foo"]) + end + + def test_rassoc + ENV.clear + assert_nil(ENV.rassoc("foo")) + ENV["foo"] = "bar" + ENV["test"] = "foo" + ENV["baz"] = "qux" + k, v = ENV.rassoc("foo") + if IGNORE_CASE + assert_equal("TEST", k.upcase) + assert_equal("FOO", v.upcase) + else + assert_equal("test", k) + assert_equal("foo", v) + end + end + + def test_to_hash + h = {} + ENV.each {|k, v| h[k] = v } + assert_equal(h, ENV.to_hash) + end + + def test_to_h + assert_equal(ENV.to_hash, ENV.to_h) + assert_equal(ENV.map {|k, v| ["$#{k}", v.size]}.to_h, + ENV.to_h {|k, v| ["$#{k}", v.size]}) + end + + def test_reject + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + h2 = ENV.reject {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" } + assert_equal(h1, h2) + end + + def check(as, bs) + if IGNORE_CASE + as = as.map {|k, v| [k.upcase, v] } + bs = bs.map {|k, v| [k.upcase, v] } + end + assert_equal(as.sort, bs.sort) + end + + def test_shift + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + a = ENV.shift + b = ENV.shift + check([a, b], [%w(foo bar), %w(baz qux)]) + assert_nil(ENV.shift) + end + + def test_invert + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + check(ENV.invert.to_a, [%w(bar foo), %w(qux baz)]) + end + + def test_replace + ENV["foo"] = "xxx" + ENV.replace({"foo"=>"bar", "baz"=>"qux"}) + check(ENV.to_hash.to_a, [%w(foo bar), %w(baz qux)]) + ENV.replace({"Foo"=>"Bar", "Baz"=>"Qux"}) + check(ENV.to_hash.to_a, [%w(Foo Bar), %w(Baz Qux)]) + end + + def test_update + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + ENV.update({"baz"=>"quux","a"=>"b"}) + check(ENV.to_hash.to_a, [%w(foo bar), %w(baz quux), %w(a b)]) + + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + ENV.update({"baz"=>"quux","a"=>"b"}) {|k, v1, v2| k + "_" + v1 + "_" + v2 } + check(ENV.to_hash.to_a, [%w(foo bar), %w(baz baz_qux_quux), %w(a b)]) + end + + def test_huge_value + if /mswin|ucrt/ =~ RUBY_PLATFORM + # On Windows >= Vista each environment variable can be max 32768 characters + huge_value = "bar" * 10900 + else + huge_value = "bar" * 40960 + end + ENV["foo"] = "overwritten" + assert_nothing_raised { ENV["foo"] = huge_value } + assert_equal(huge_value, ENV["foo"]) + end + + if /mswin|mingw/ =~ RUBY_PLATFORM + def windows_version + @windows_version ||= %x[ver][/Version (\d+)/, 1].to_i + end + + def test_win32_blocksize + keys = [] + len = 32767 - ENV.to_a.flatten.inject(1) {|r,e| r + e.bytesize + 1} + val = "bar" * 1000 + key = nil + while (len -= val.size + (key="foo#{len}").size + 2) > 0 + keys << key + ENV[key] = val + end + if windows_version < 6 + 1.upto(12) {|i| + assert_raise(Errno::EINVAL) { ENV[key] = val } + } + else + 1.upto(12) {|i| + assert_nothing_raised(Errno::EINVAL) { ENV[key] = val } + } + end + ensure + keys.each {|k| ENV.delete(k)} + end + end + + def test_frozen_env + assert_raise(TypeError) { ENV.freeze } + end + + def test_frozen + ENV[PATH_ENV] = "/" + ENV.each do |k, v| + assert_predicate(k, :frozen?) + assert_predicate(v, :frozen?) + end + ENV.each_key do |k| + assert_predicate(k, :frozen?) + end + ENV.each_value do |v| + assert_predicate(v, :frozen?) + end + ENV.each_key do |k| + assert_predicate(ENV[k], :frozen?, "[#{k.dump}]") + assert_predicate(ENV.fetch(k), :frozen?, "fetch(#{k.dump})") + end + end + + def test_shared_substring + bug12475 = '[ruby-dev:49655] [Bug #12475]' + n = [*"0".."9"].join("")*3 + e0 = ENV[n0 = "E#{n}"] + e1 = ENV[n1 = "E#{n}."] + ENV[n0] = nil + ENV[n1] = nil + ENV[n1.chop] = "T#{n}.".chop + ENV[n0], e0 = e0, ENV[n0] + ENV[n1], e1 = e1, ENV[n1] + assert_equal("T#{n}", e0, bug12475) + assert_nil(e1, bug12475) + end + + def ignore_case_str + IGNORE_CASE ? "true" : "false" + end + + def str_for_yielding_exception_class(code_str, exception_var: "raised_exception") + <<-"end;" + #{exception_var} = nil + begin + #{code_str} + rescue Exception => e + #{exception_var} = e + end + Ractor.yield #{exception_var}.class + end; + end + + def str_for_assert_raise_on_yielded_exception_class(expected_error_class, ractor_var) + <<-"end;" + error_class = #{ractor_var}.take + assert_raise(#{expected_error_class}) do + if error_class < Exception + raise error_class + end + end + end; + end + + def str_to_yield_invalid_envvar_errors(var_name, code_str) + <<-"end;" + envvars_to_check = [ + "foo\0bar", + "#{'\xa1\xa1'}".force_encoding(Encoding::UTF_16LE), + "foo".force_encoding(Encoding::ISO_2022_JP), + ] + envvars_to_check.each do |#{var_name}| + #{str_for_yielding_exception_class(code_str)} + end + end; + end + + def str_to_receive_invalid_envvar_errors(ractor_var) + <<-"end;" + 3.times do + #{str_for_assert_raise_on_yielded_exception_class(ArgumentError, ractor_var)} + end + end; + end + + STR_DEFINITION_FOR_CHECK = %Q{ + def check(as, bs) + if #{IGNORE_CASE ? "true" : "false"} + as = as.map {|k, v| [k.upcase, v] } + bs = bs.map {|k, v| [k.upcase, v] } + end + assert_equal(as.sort, bs.sort) + end + } + + def test_bracket_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + Ractor.yield ENV['test'] + Ractor.yield ENV['TEST'] + ENV['test'] = 'foo' + Ractor.yield ENV['test'] + Ractor.yield ENV['TEST'] + ENV['TEST'] = 'bar' + Ractor.yield ENV['TEST'] + Ractor.yield ENV['test'] + #{str_for_yielding_exception_class("ENV[1]")} + #{str_for_yielding_exception_class("ENV[1] = 'foo'")} + #{str_for_yielding_exception_class("ENV['test'] = 0")} + end + assert_nil(r.take) + assert_nil(r.take) + assert_equal('foo', r.take) + if #{ignore_case_str} + assert_equal('foo', r.take) + else + assert_nil(r.take) + end + assert_equal('bar', r.take) + if #{ignore_case_str} + assert_equal('bar', r.take) + else + assert_equal('foo', r.take) + end + 3.times do + #{str_for_assert_raise_on_yielded_exception_class(TypeError, "r")} + end + end; + end + + def test_dup_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + #{str_for_yielding_exception_class("ENV.dup")} + end + #{str_for_assert_raise_on_yielded_exception_class(TypeError, "r")} + end; + end + + def test_clone_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + original_warning_state = Warning[:deprecated] + Warning[:deprecated] = false + + begin + Ractor.yield ENV.clone.object_id + Ractor.yield ENV.clone(freeze: false).object_id + Ractor.yield ENV.clone(freeze: nil).object_id + + #{str_for_yielding_exception_class("ENV.clone(freeze: true)")} + #{str_for_yielding_exception_class("ENV.clone(freeze: 1)")} + #{str_for_yielding_exception_class("ENV.clone(foo: false)")} + #{str_for_yielding_exception_class("ENV.clone(1)")} + #{str_for_yielding_exception_class("ENV.clone(1, foo: false)")} + + ensure + Warning[:deprecated] = original_warning_state + end + end + assert_equal(ENV.object_id, r.take) + assert_equal(ENV.object_id, r.take) + assert_equal(ENV.object_id, r.take) + #{str_for_assert_raise_on_yielded_exception_class(TypeError, "r")} + 4.times do + #{str_for_assert_raise_on_yielded_exception_class(ArgumentError, "r")} + end + end; + end + + def test_has_value_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + val = 'a' + val.succ! while ENV.has_value?(val) || ENV.has_value?(val.upcase) + ENV['test'] = val[0...-1] + Ractor.yield(ENV.has_value?(val)) + Ractor.yield(ENV.has_value?(val.upcase)) + ENV['test'] = val + Ractor.yield(ENV.has_value?(val)) + Ractor.yield(ENV.has_value?(val.upcase)) + ENV['test'] = val.upcase + Ractor.yield ENV.has_value?(val) + Ractor.yield ENV.has_value?(val.upcase) + end + assert_equal(false, r.take) + assert_equal(false, r.take) + assert_equal(true, r.take) + assert_equal(false, r.take) + assert_equal(false, r.take) + assert_equal(true, r.take) + end; + end + + def test_key_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + val = 'a' + val.succ! while ENV.has_value?(val) || ENV.has_value?(val.upcase) + ENV['test'] = val[0...-1] + Ractor.yield ENV.key(val) + Ractor.yield ENV.key(val.upcase) + ENV['test'] = val + Ractor.yield ENV.key(val) + Ractor.yield ENV.key(val.upcase) + ENV['test'] = val.upcase + Ractor.yield ENV.key(val) + Ractor.yield ENV.key(val.upcase) + end + assert_nil(r.take) + assert_nil(r.take) + if #{ignore_case_str} + assert_equal('TEST', r.take.upcase) + else + assert_equal('test', r.take) + end + assert_nil(r.take) + assert_nil(r.take) + if #{ignore_case_str} + assert_equal('TEST', r.take.upcase) + else + assert_equal('test', r.take) + end + end; + + end + + def test_delete_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + #{str_to_yield_invalid_envvar_errors("v", "ENV.delete(v)")} + Ractor.yield ENV.delete("TEST") + #{str_for_yielding_exception_class("ENV.delete('#{PATH_ENV}')")} + Ractor.yield(ENV.delete("TEST"){|name| "NO "+name}) + end + #{str_to_receive_invalid_envvar_errors("r")} + assert_nil(r.take) + exception_class = r.take + assert_equal(NilClass, exception_class) + assert_equal("NO TEST", r.take) + end; + end + + def test_getenv_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + #{str_to_yield_invalid_envvar_errors("v", "ENV[v]")} + ENV["#{PATH_ENV}"] = "" + Ractor.yield ENV["#{PATH_ENV}"] + Ractor.yield ENV[""] + end + #{str_to_receive_invalid_envvar_errors("r")} + assert_equal("", r.take) + assert_nil(r.take) + end; + end + + def test_fetch_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV["test"] = "foo" + Ractor.yield ENV.fetch("test") + ENV.delete("test") + #{str_for_yielding_exception_class("ENV.fetch('test')", exception_var: "ex")} + Ractor.yield ex.receiver.object_id + Ractor.yield ex.key + Ractor.yield ENV.fetch("test", "foo") + Ractor.yield(ENV.fetch("test"){"bar"}) + #{str_to_yield_invalid_envvar_errors("v", "ENV.fetch(v)")} + #{str_for_yielding_exception_class("ENV.fetch('#{PATH_ENV}', 'foo')")} + ENV['#{PATH_ENV}'] = "" + Ractor.yield ENV.fetch('#{PATH_ENV}') + end + assert_equal("foo", r.take) + #{str_for_assert_raise_on_yielded_exception_class(KeyError, "r")} + assert_equal(ENV.object_id, r.take) + assert_equal("test", r.take) + assert_equal("foo", r.take) + assert_equal("bar", r.take) + #{str_to_receive_invalid_envvar_errors("r")} + exception_class = r.take + assert_equal(NilClass, exception_class) + assert_equal("", r.take) + end; + end + + def test_aset_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + #{str_for_yielding_exception_class("ENV['test'] = nil")} + ENV["test"] = nil + Ractor.yield ENV["test"] + #{str_to_yield_invalid_envvar_errors("v", "ENV[v] = 'test'")} + #{str_to_yield_invalid_envvar_errors("v", "ENV['test'] = v")} + end + exception_class = r.take + assert_equal(NilClass, exception_class) + assert_nil(r.take) + #{str_to_receive_invalid_envvar_errors("r")} + #{str_to_receive_invalid_envvar_errors("r")} + end; + end + + def test_keys_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + a = ENV.keys + Ractor.yield a + end + a = r.take + assert_kind_of(Array, a) + a.each {|k| assert_kind_of(String, k) } + end; + + end + + def test_each_key_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.each_key {|k| Ractor.yield(k)} + Ractor.yield "finished" + end + while((x=r.take) != "finished") + assert_kind_of(String, x) + end + end; + end + + def test_values_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + a = ENV.values + Ractor.yield a + end + a = r.take + assert_kind_of(Array, a) + a.each {|k| assert_kind_of(String, k) } + end; + end + + def test_each_value_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.each_value {|k| Ractor.yield(k)} + Ractor.yield "finished" + end + while((x=r.take) != "finished") + assert_kind_of(String, x) + end + end; + end + + def test_each_pair_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.each_pair {|k, v| Ractor.yield([k,v])} + Ractor.yield "finished" + end + while((k,v=r.take) != "finished") + assert_kind_of(String, k) + assert_kind_of(String, v) + end + end; + end + + def test_reject_bang_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.reject! {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + Ractor.yield [h1, h2] + Ractor.yield(ENV.reject! {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" }) + end + h1, h2 = r.take + assert_equal(h1, h2) + assert_nil(r.take) + end; + end + + def test_delete_if_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.delete_if {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + Ractor.yield [h1, h2] + Ractor.yield (ENV.delete_if {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" }).object_id + end + h1, h2 = r.take + assert_equal(h1, h2) + assert_equal(ENV.object_id, r.take) + end; + end + + def test_select_bang_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.select! {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + Ractor.yield [h1, h2] + Ractor.yield(ENV.select! {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" }) + end + h1, h2 = r.take + assert_equal(h1, h2) + assert_nil(r.take) + end; + end + + def test_filter_bang_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.filter! {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + Ractor.yield [h1, h2] + Ractor.yield(ENV.filter! {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" }) + end + h1, h2 = r.take + assert_equal(h1, h2) + assert_nil(r.take) + end; + end + + def test_keep_if_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + ENV.keep_if {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" } + h2 = {} + ENV.each_pair {|k, v| h2[k] = v } + Ractor.yield [h1, h2] + Ractor.yield (ENV.keep_if {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" }).object_id + end + h1, h2 = r.take + assert_equal(h1, h2) + assert_equal(ENV.object_id, r.take) + end; + end + + def test_values_at_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV["test"] = "foo" + Ractor.yield ENV.values_at("test", "test") + end + assert_equal(["foo", "foo"], r.take) + end; + end + + def test_select_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV["test"] = "foo" + h = ENV.select {|k| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" } + Ractor.yield h.size + k = h.keys.first + v = h.values.first + Ractor.yield [k, v] + end + assert_equal(1, r.take) + k, v = r.take + if #{ignore_case_str} + assert_equal("TEST", k.upcase) + assert_equal("FOO", v.upcase) + else + assert_equal("test", k) + assert_equal("foo", v) + end + end; + end + + def test_filter_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV["test"] = "foo" + h = ENV.filter {|k| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" } + Ractor.yield(h.size) + k = h.keys.first + v = h.values.first + Ractor.yield [k, v] + end + assert_equal(1, r.take) + k, v = r.take + if #{ignore_case_str} + assert_equal("TEST", k.upcase) + assert_equal("FOO", v.upcase) + else + assert_equal("test", k) + assert_equal("foo", v) + end + end; + end + + def test_slice_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + ENV["bar"] = "rab" + Ractor.yield(ENV.slice()) + Ractor.yield(ENV.slice("")) + Ractor.yield(ENV.slice("unknown")) + Ractor.yield(ENV.slice("foo", "baz")) + end + assert_equal({}, r.take) + assert_equal({}, r.take) + assert_equal({}, r.take) + assert_equal({"foo"=>"bar", "baz"=>"qux"}, r.take) + end; + end + + def test_except_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + ENV["bar"] = "rab" + Ractor.yield ENV.except() + Ractor.yield ENV.except("") + Ractor.yield ENV.except("unknown") + Ractor.yield ENV.except("foo", "baz") + end + assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, r.take) + assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, r.take) + assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, r.take) + assert_equal({"bar"=>"rab"}, r.take) + end; + end + + def test_clear_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.clear + Ractor.yield ENV.size + end + assert_equal(0, r.take) + end; + end + + def test_to_s_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.to_s + end + assert_equal("ENV", r.take) + end; + end + + def test_inspect_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + s = ENV.inspect + Ractor.yield s + end + s = r.take + if #{ignore_case_str} + s = s.upcase + assert(s == '{"FOO"=>"BAR", "BAZ"=>"QUX"}' || s == '{"BAZ"=>"QUX", "FOO"=>"BAR"}') + else + assert(s == '{"foo"=>"bar", "baz"=>"qux"}' || s == '{"baz"=>"qux", "foo"=>"bar"}') + end + end; + end + + def test_to_a_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + a = ENV.to_a + Ractor.yield a + end + a = r.take + assert_equal(2, a.size) + if #{ignore_case_str} + a = a.map {|x| x.map {|y| y.upcase } } + assert(a == [%w(FOO BAR), %w(BAZ QUX)] || a == [%w(BAZ QUX), %w(FOO BAR)]) + else + assert(a == [%w(foo bar), %w(baz qux)] || a == [%w(baz qux), %w(foo bar)]) + end + end; + end + + def test_rehash_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.rehash + end + assert_nil(r.take) + end; + end + + def test_size_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + s = ENV.size + ENV["test"] = "foo" + Ractor.yield [s, ENV.size] + end + s, s2 = r.take + assert_equal(s + 1, s2) + end; + end + + def test_empty_p_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.clear + Ractor.yield ENV.empty? + ENV["test"] = "foo" + Ractor.yield ENV.empty? + end + assert r.take + assert !r.take + end; + end + + def test_has_key_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + Ractor.yield ENV.has_key?("test") + ENV["test"] = "foo" + Ractor.yield ENV.has_key?("test") + #{str_to_yield_invalid_envvar_errors("v", "ENV.has_key?(v)")} + end + assert !r.take + assert r.take + #{str_to_receive_invalid_envvar_errors("r")} + end; + end + + def test_assoc_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + Ractor.yield ENV.assoc("test") + ENV["test"] = "foo" + Ractor.yield ENV.assoc("test") + #{str_to_yield_invalid_envvar_errors("v", "ENV.assoc(v)")} + end + assert_nil(r.take) + k, v = r.take + if #{ignore_case_str} + assert_equal("TEST", k.upcase) + assert_equal("FOO", v.upcase) + else + assert_equal("test", k) + assert_equal("foo", v) + end + #{str_to_receive_invalid_envvar_errors("r")} + encoding = /mswin|mingw/ =~ RUBY_PLATFORM ? Encoding::UTF_8 : Encoding.find("locale") + assert_equal(encoding, v.encoding) + end; + end + + def test_has_value2_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.clear + Ractor.yield ENV.has_value?("foo") + ENV["test"] = "foo" + Ractor.yield ENV.has_value?("foo") + end + assert !r.take + assert r.take + end; + end + + def test_rassoc_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV.clear + Ractor.yield ENV.rassoc("foo") + ENV["foo"] = "bar" + ENV["test"] = "foo" + ENV["baz"] = "qux" + Ractor.yield ENV.rassoc("foo") + end + assert_nil(r.take) + k, v = r.take + if #{ignore_case_str} + assert_equal("TEST", k.upcase) + assert_equal("FOO", v.upcase) + else + assert_equal("test", k) + assert_equal("foo", v) + end + end; + end + + def test_to_hash_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + h = {} + ENV.each {|k, v| h[k] = v } + Ractor.yield [h, ENV.to_hash] + end + h, h2 = r.take + assert_equal(h, h2) + end; + end + + def test_to_h_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + Ractor.yield [ENV.to_hash, ENV.to_h] + Ractor.yield [ENV.map {|k, v| ["$\#{k}", v.size]}.to_h, ENV.to_h {|k, v| ["$\#{k}", v.size]}] + end + a, b = r.take + assert_equal(a,b) + c, d = r.take + assert_equal(c,d) + end; + end + + def test_reject_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + h1 = {} + ENV.each_pair {|k, v| h1[k] = v } + ENV["test"] = "foo" + h2 = ENV.reject {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" } + Ractor.yield [h1, h2] + end + h1, h2 = r.take + assert_equal(h1, h2) + end; + end + + def test_shift_in_ractor + assert_ractor(<<-"end;") + #{STR_DEFINITION_FOR_CHECK} + r = Ractor.new do + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + a = ENV.shift + b = ENV.shift + Ractor.yield [a,b] + Ractor.yield ENV.shift + end + a,b = r.take + check([a, b], [%w(foo bar), %w(baz qux)]) + assert_nil(r.take) + end; + end + + def test_invert_in_ractor + assert_ractor(<<-"end;") + #{STR_DEFINITION_FOR_CHECK} + r = Ractor.new do + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + Ractor.yield(ENV.invert) + end + check(r.take.to_a, [%w(bar foo), %w(qux baz)]) + end; + end + + def test_replace_in_ractor + assert_ractor(<<-"end;") + #{STR_DEFINITION_FOR_CHECK} + r = Ractor.new do + ENV["foo"] = "xxx" + ENV.replace({"foo"=>"bar", "baz"=>"qux"}) + Ractor.yield ENV.to_hash + ENV.replace({"Foo"=>"Bar", "Baz"=>"Qux"}) + Ractor.yield ENV.to_hash + end + check(r.take.to_a, [%w(foo bar), %w(baz qux)]) + check(r.take.to_a, [%w(Foo Bar), %w(Baz Qux)]) + end; + end + + def test_update_in_ractor + assert_ractor(<<-"end;") + #{STR_DEFINITION_FOR_CHECK} + r = Ractor.new do + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + ENV.update({"baz"=>"quux","a"=>"b"}) + Ractor.yield ENV.to_hash + ENV.clear + ENV["foo"] = "bar" + ENV["baz"] = "qux" + ENV.update({"baz"=>"quux","a"=>"b"}) {|k, v1, v2| k + "_" + v1 + "_" + v2 } + Ractor.yield ENV.to_hash + end + check(r.take.to_a, [%w(foo bar), %w(baz quux), %w(a b)]) + check(r.take.to_a, [%w(foo bar), %w(baz baz_qux_quux), %w(a b)]) + end; + end + + def test_huge_value_in_ractor + assert_ractor(<<-"end;") + huge_value = "bar" * 40960 + r = Ractor.new huge_value do |v| + ENV["foo"] = "bar" + #{str_for_yielding_exception_class("ENV['foo'] = v ")} + Ractor.yield ENV["foo"] + end + + if /mswin|ucrt/ =~ RUBY_PLATFORM + #{str_for_assert_raise_on_yielded_exception_class(Errno::EINVAL, "r")} + result = r.take + assert_equal("bar", result) + else + exception_class = r.take + assert_equal(NilClass, exception_class) + result = r.take + assert_equal(huge_value, result) + end + end; + end + + def test_frozen_env_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + #{str_for_yielding_exception_class("ENV.freeze")} + end + #{str_for_assert_raise_on_yielded_exception_class(TypeError, "r")} + end; + end + + def test_frozen_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + ENV["#{PATH_ENV}"] = "/" + ENV.each do |k, v| + Ractor.yield [k.frozen?] + Ractor.yield [v.frozen?] + end + ENV.each_key do |k| + Ractor.yield [k.frozen?] + end + ENV.each_value do |v| + Ractor.yield [v.frozen?] + end + ENV.each_key do |k| + Ractor.yield [ENV[k].frozen?, "[\#{k.dump}]"] + Ractor.yield [ENV.fetch(k).frozen?, "fetch(\#{k.dump})"] + end + Ractor.yield "finished" + end + while((params=r.take) != "finished") + assert(*params) + end + end; + end + + def test_shared_substring_in_ractor + assert_ractor(<<-"end;") + r = Ractor.new do + bug12475 = '[ruby-dev:49655] [Bug #12475]' + n = [*"0".."9"].join("")*3 + e0 = ENV[n0 = "E\#{n}"] + e1 = ENV[n1 = "E\#{n}."] + ENV[n0] = nil + ENV[n1] = nil + ENV[n1.chop] = "T\#{n}.".chop + ENV[n0], e0 = e0, ENV[n0] + ENV[n1], e1 = e1, ENV[n1] + Ractor.yield [n, e0, e1, bug12475] + end + n, e0, e1, bug12475 = r.take + assert_equal("T\#{n}", e0, bug12475) + assert_nil(e1, bug12475) + end; + end + + def test_ivar_in_env_should_not_be_access_from_non_main_ractors + assert_ractor <<~RUBY + ENV.instance_eval{ @a = "hello" } + assert_equal "hello", ENV.instance_variable_get(:@a) + + r_get = Ractor.new do + ENV.instance_variable_get(:@a) + rescue Ractor::IsolationError => e + e + end + assert_equal Ractor::IsolationError, r_get.take.class + + r_get = Ractor.new do + ENV.instance_eval{ @a } + rescue Ractor::IsolationError => e + e + end + + assert_equal Ractor::IsolationError, r_get.take.class + + r_set = Ractor.new do + ENV.instance_eval{ @b = "hello" } + rescue Ractor::IsolationError => e + e + end + + assert_equal Ractor::IsolationError, r_set.take.class + RUBY + end + + if RUBY_PLATFORM =~ /bccwin|mswin|mingw/ + def test_memory_leak_aset + bug9977 = '[ruby-dev:48323] [Bug #9977]' + assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9977, limit: 2.0) + ENV.clear + k = 'FOO' + v = (ENV[k] = 'bar'*5000 rescue 'bar'*1500) + doit = proc {ENV[k] = v} + 500.times(&doit) + end; + end + + def test_memory_leak_select + bug9978 = '[ruby-dev:48325] [Bug #9978]' + assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9978, limit: 2.0) + ENV.clear + k = 'FOO' + (ENV[k] = 'bar'*5000 rescue 'bar'*1500) + doit = proc {ENV.select {break}} + 500.times(&doit) + end; + end + + def test_memory_crash_select + assert_normal_exit(<<-'end;') + 1000.times {ENV["FOO#{i}"] = 'bar'} + ENV.select {ENV.clear} + end; + end + + def test_memory_leak_shift + bug9983 = '[ruby-dev:48332] [Bug #9983]' + assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9983, limit: 2.0) + ENV.clear + k = 'FOO' + v = (ENV[k] = 'bar'*5000 rescue 'bar'*1500) + doit = proc {ENV[k] = v; ENV.shift} + 500.times(&doit) + end; + end + + def test_utf8 + text = "testing \u{e5 e1 e2 e4 e3 101 3042}" + test = ENV["test"] + ENV["test"] = text + assert_equal text, ENV["test"] + ensure + ENV["test"] = test end end end |
