ruby/test/ruby/test_variable.rb
2025-09-12 13:44:39 +02:00

523 lines
12 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: false
require 'test/unit'
class TestVariable < Test::Unit::TestCase
class Gods
@@rule = "Uranus"
def ruler0
@@rule
end
def self.ruler1 # <= per method definition style
@@rule
end
class << self # <= multiple method definition style
def ruler2
@@rule
end
end
end
module Olympians
@@rule ="Zeus"
def ruler3
@@rule
end
end
class Titans < Gods
@@rule = "Cronus" # modifies @@rule in Gods
include Olympians
def ruler4
@@rule
end
end
Athena = Gods.clone
def test_cloned_classes_copy_cvar_cache
assert_equal "Cronus", Athena.new.ruler0
end
def test_setting_class_variable_on_module_through_inheritance
mod = Module.new
mod.class_variable_set(:@@foo, 1)
mod.freeze
c = Class.new { include(mod) }
assert_raise(FrozenError) { c.class_variable_set(:@@foo, 2) }
assert_raise(FrozenError) { c.class_eval("@@foo = 2") }
assert_equal(1, c.class_variable_get(:@@foo))
end
Zeus = Gods.clone
def test_cloned_allows_setting_cvar
Zeus.class_variable_set(:@@rule, "Athena")
god = Gods.new.ruler0
zeus = Zeus.new.ruler0
assert_equal "Cronus", god
assert_equal "Athena", zeus
assert_not_equal god.object_id, zeus.object_id
end
def test_singleton_class_included_class_variable
c = Class.new
c.extend(Olympians)
assert_empty(c.singleton_class.class_variables)
assert_raise(NameError){ c.singleton_class.class_variable_get(:@@rule) }
c.class_variable_set(:@@foo, 1)
assert_equal([:@@foo], c.singleton_class.class_variables)
assert_equal(1, c.singleton_class.class_variable_get(:@@foo))
c = Class.new
c.extend(Olympians)
sc = Class.new(c)
assert_empty(sc.singleton_class.class_variables)
assert_raise(NameError){ sc.singleton_class.class_variable_get(:@@rule) }
c.class_variable_set(:@@foo, 1)
assert_equal([:@@foo], sc.singleton_class.class_variables)
assert_equal(1, sc.singleton_class.class_variable_get(:@@foo))
c = Class.new
o = c.new
o.extend(Olympians)
assert_equal([:@@rule], o.singleton_class.class_variables)
assert_equal("Zeus", o.singleton_class.class_variable_get(:@@rule))
c.class_variable_set(:@@foo, 1)
assert_equal([:@@foo, :@@rule], o.singleton_class.class_variables.sort)
assert_equal(1, o.singleton_class.class_variable_get(:@@foo))
end
def test_cvar_overtaken_by_parent_class
error = eval <<~EORB
class Parent
end
class Child < Parent
@@cvar = 1
def self.cvar
@@cvar
end
end
assert_equal 1, Child.cvar
class Parent
@@cvar = 2
end
assert_raise RuntimeError do
Child.cvar
end
EORB
assert_equal "class variable @@cvar of TestVariable::Child is overtaken by TestVariable::Parent", error.message
ensure
TestVariable.send(:remove_const, :Child) rescue nil
TestVariable.send(:remove_const, :Parent) rescue nil
end
def test_cvar_overtaken_by_module
error = eval <<~EORB
class ParentForModule
@@cvar = 1
def self.cvar
@@cvar
end
end
assert_equal 1, ParentForModule.cvar
module Mixin
@@cvar = 2
end
class ParentForModule
include Mixin
end
assert_raise RuntimeError do
ParentForModule.cvar
end
EORB
assert_equal "class variable @@cvar of TestVariable::ParentForModule is overtaken by TestVariable::Mixin", error.message
ensure
TestVariable.send(:remove_const, :Mixin) rescue nil
TestVariable.send(:remove_const, :ParentForModule) rescue nil
end
class IncludeRefinedModuleClassVariableNoWarning
module Mod
@@_test_include_refined_module_class_variable = true
end
module Mod2
refine Mod do
end
end
include Mod
def t
@@_test_include_refined_module_class_variable
end
end
def test_include_refined_module_class_variable
assert_warning('') do
IncludeRefinedModuleClassVariableNoWarning.new.t
end
end
def test_set_class_variable_on_frozen_object
set_cvar = EnvUtil.labeled_class("SetCVar")
set_cvar.class_eval "#{<<~"begin;"}\n#{<<~'end;'}"
begin;
def self.set(val)
@@a = val # inline cache
end
end;
set_cvar.set(1) # fill write cache
set_cvar.freeze
assert_raise(FrozenError, "[Bug #19341]") do
set_cvar.set(2) # hit write cache, but should check frozen status
end
end
def test_variable
assert_instance_of(Integer, $$)
# read-only variable
assert_raise(NameError) do
$$ = 5
end
assert_normal_exit("$*=0; $*", "[ruby-dev:36698]")
foobar = "foobar"
$_ = foobar
assert_equal(foobar, $_)
assert_equal("Cronus", Gods.new.ruler0)
assert_equal("Cronus", Gods.ruler1)
assert_equal("Cronus", Gods.ruler2)
assert_equal("Cronus", Titans.ruler1)
assert_equal("Cronus", Titans.ruler2)
atlas = Titans.new
assert_equal("Cronus", atlas.ruler0)
assert_equal("Zeus", atlas.ruler3)
assert_raise(RuntimeError) { atlas.ruler4 }
assert_nothing_raised do
class << Gods
defined?(@@rule) && @@rule
end
end
end
def test_local_variables
lvar = 1
assert_instance_of(Symbol, local_variables[0], "[ruby-dev:34008]")
lvar
end
def test_local_variables2
x = 1
proc do |y|
assert_equal([:x, :y], local_variables.sort)
end.call
x
end
def test_local_variables3
x = 1
proc do |y|
1.times do |z|
assert_equal([:x, :y, :z], local_variables.sort)
end
end.call
x
end
def test_shadowing_local_variables
bug9486 = '[ruby-core:60501] [Bug #9486]'
assert_equal([:x, :bug9486], tap {|x| break local_variables}, bug9486)
end
def test_shadowing_block_local_variables
bug9486 = '[ruby-core:60501] [Bug #9486]'
assert_equal([:x, :bug9486], tap {|;x| x = x; break local_variables}, bug9486)
end
def test_global_variables
gv = global_variables
assert_empty(gv.grep(/\A(?!\$)/))
assert_nil($~)
assert_not_include(gv, :$1)
/(\w)(\d)?(.)(.)(.)(.)(.)(.)(.)(.)(\d)?(.)/ =~ "globalglobalglobal"
assert_not_nil($~)
gv = global_variables - gv
assert_include(gv, :$1)
assert_not_include(gv, :$2)
assert_not_include(gv, :$11)
assert_include(gv, :$12)
end
def prepare_klass_for_test_svar_with_ifunc
Class.new do
include Enumerable
def each(&b)
@b = b
end
def check1
check2.merge({check1: $1})
end
def check2
@b.call('foo')
{check2: $1}
end
end
end
def test_svar_with_ifunc
c = prepare_klass_for_test_svar_with_ifunc
expected_check1_result = {
check1: nil, check2: nil
}.freeze
obj = c.new
result = nil
obj.grep(/(f..)/){
result = $1
}
assert_equal nil, result
assert_equal nil, $1
assert_equal expected_check1_result, obj.check1
assert_equal 'foo', result
assert_equal 'foo', $1
# this frame was escaped so try it again
$~ = nil
obj = c.new
result = nil
obj.grep(/(f..)/){
result = $1
}
assert_equal nil, result
assert_equal nil, $1
assert_equal expected_check1_result, obj.check1
assert_equal 'foo', result
assert_equal 'foo', $1
# different context
result = nil
Fiber.new{
obj = c.new
obj.grep(/(f..)/){
result = $1
}
}.resume # obj is created in antoher Fiber
assert_equal nil, result
assert_equal expected_check1_result, obj.check1
assert_equal 'foo', result
assert_equal 'foo', $1
# different thread context
result = nil
Thread.new{
obj = c.new
obj.grep(/(f..)/){
result = $1
}
}.join # obj is created in another Thread
assert_equal nil, result
assert_equal expected_check1_result, obj.check1
assert_equal 'foo', result
assert_equal 'foo', $1
end
def test_global_variable_0
assert_in_out_err(["-e", "$0='t'*1000;print $0"], "", /\At+\z/, [])
end
def test_global_variable_popped
assert_nothing_raised {
EnvUtil.suppress_warning {
eval("$foo; 1")
}
}
end
def test_constant_popped
assert_nothing_raised {
EnvUtil.suppress_warning {
eval("TestVariable::Gods; 1")
}
}
end
def test_special_constant_ivars
[ true, false, :symbol, "dsym#{rand(9999)}".to_sym, 1, 1.0 ].each do |v|
assert_empty v.instance_variables
msg = "can't modify frozen #{v.class}: #{v.inspect}"
assert_raise_with_message(FrozenError, msg) do
v.instance_variable_set(:@foo, :bar)
end
assert_raise_with_message(FrozenError, msg, "[Bug #19339]") do
v.instance_eval do
@a = 1
end
end
assert_nil EnvUtil.suppress_warning {v.instance_variable_get(:@foo)}
assert_not_send([v, :instance_variable_defined?, :@foo])
assert_raise_with_message(FrozenError, msg) do
v.remove_instance_variable(:@foo)
end
end
end
class RemoveIvar
class << self
attr_reader :ivar
def add_ivar
@ivar = 1
end
end
attr_reader :ivar
def add_ivar
@ivar = 1
end
end
def add_and_remove_ivar(obj)
assert_nil obj.ivar
assert_equal 1, obj.add_ivar
assert_equal 1, obj.instance_variable_get(:@ivar)
assert_equal 1, obj.ivar
obj.remove_instance_variable(:@ivar)
assert_nil obj.ivar
assert_raise NameError do
obj.remove_instance_variable(:@ivar)
end
end
def test_remove_instance_variables_object
obj = RemoveIvar.new
add_and_remove_ivar(obj)
add_and_remove_ivar(obj)
end
def test_remove_instance_variables_class
add_and_remove_ivar(RemoveIvar)
add_and_remove_ivar(RemoveIvar)
end
class RemoveIvarGeneric < Array
attr_reader :ivar
def add_ivar
@ivar = 1
end
end
def test_remove_instance_variables_generic
obj = RemoveIvarGeneric.new
add_and_remove_ivar(obj)
add_and_remove_ivar(obj)
end
class ExIvar < Hash
def initialize
@a = 1
@b = 2
@c = 3
end
def ivars
[@a, @b, @c]
end
end
def test_external_ivars
3.times{
# check inline cache for external ivar access
assert_equal [1, 2, 3], ExIvar.new.ivars
}
end
def test_exivar_resize_with_compaction_stress
omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
objs = 10_000.times.map do
ExIvar.new
end
EnvUtil.under_gc_compact_stress do
10.times do
x = ExIvar.new
x.instance_variable_set(:@resize, 1)
x
end
end
objs or flunk
end
def test_local_variables_with_kwarg
bug11674 = '[ruby-core:71437] [Bug #11674]'
v = with_kwargs_11(v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8,v9:9,v10:10,v11:11)
assert_equal(%i(v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11), v, bug11674)
end
def test_many_instance_variables
objects = [Object.new, Hash.new, Module.new]
objects.each do |obj|
1000.times do |i|
obj.instance_variable_set("@var#{i}", i)
end
1000.times do |i|
assert_equal(i, obj.instance_variable_get("@var#{i}"))
end
end
end
def test_local_variables_encoding
α = 1 or flunk
b = binding
b.eval("".encode("us-ascii"))
assert_equal(%i[α b], b.local_variables)
end
def test_genivar_cache
bug21547 = '[Bug #21547]'
klass = Class.new(Array)
instance = klass.new
instance.instance_variable_set(:@a1, 1)
instance.instance_variable_set(:@a2, 2)
Fiber.new do
instance.instance_variable_set(:@a3, 3)
instance.instance_variable_set(:@a4, 4)
end.resume
assert_equal 4, instance.instance_variable_get(:@a4), bug21547
end
private
def with_kwargs_11(v1:, v2:, v3:, v4:, v5:, v6:, v7:, v8:, v9:, v10:, v11:)
local_variables
end
end