summaryrefslogtreecommitdiff
path: root/test/ruby/test_struct.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby/test_struct.rb')
-rw-r--r--test/ruby/test_struct.rb154
1 files changed, 151 insertions, 3 deletions
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index db3d767edd..01e5cc68f6 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -23,6 +23,10 @@ module TestStruct
test.bar = 47
assert_equal(47, test.bar)
+
+ @Struct.class_eval do
+ remove_const :Test
+ end
end
# [ruby-dev:26247] more than 10 struct members causes segmentation fault
@@ -37,6 +41,14 @@ module TestStruct
end
end
+ def test_larger_than_largest_pool
+ count = (GC::INTERNAL_CONSTANTS[:RVARGC_MAX_ALLOCATE_SIZE] / RbConfig::SIZEOF["void*"]) + 1
+ list = Array(0..count)
+ klass = @Struct.new(*list.map { |i| :"a_#{i}"})
+ struct = klass.new(*list)
+ assert_equal 0, struct.a_0
+ end
+
def test_small_structs
names = [:a, :b, :c, :d]
1.upto(4) {|n|
@@ -60,6 +72,10 @@ module TestStruct
assert_equal(1, o.a)
end
+ def test_attrset_id
+ assert_raise(ArgumentError) { Struct.new(:x=) }
+ end
+
def test_members
klass = @Struct.new(:a)
o = klass.new(1)
@@ -92,11 +108,17 @@ module TestStruct
assert_equal([:utime, :stime, :cutime, :cstime], Process.times.members)
end
+ def test_struct_new_with_hash
+ assert_raise_with_message(TypeError, /not a symbol/) {Struct.new(:a, {})}
+ assert_raise_with_message(TypeError, /not a symbol/) {Struct.new(:a, {name: "b"})}
+ end
+
def test_struct_new_with_keyword_init
@Struct.new("KeywordInitTrue", :a, :b, keyword_init: true)
@Struct.new("KeywordInitFalse", :a, :b, keyword_init: false)
assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(1, 2) }
+ assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new({a: 100}, 2) }
assert_nothing_raised { @Struct::KeywordInitFalse.new(1, 2) }
assert_nothing_raised { @Struct::KeywordInitTrue.new(a: 1, b: 2) }
assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(1, b: 2) }
@@ -104,6 +126,10 @@ module TestStruct
assert_equal @Struct::KeywordInitTrue.new(a: 1, b: 2).values, @Struct::KeywordInitFalse.new(1, 2).values
assert_equal "#{@Struct}::KeywordInitFalse", @Struct::KeywordInitFalse.inspect
assert_equal "#{@Struct}::KeywordInitTrue(keyword_init: true)", @Struct::KeywordInitTrue.inspect
+ # eval is needed to prevent the warning duplication filter
+ k = Class.new(@Struct::KeywordInitTrue) {def initialize(b, options); super(a: options, b: b); end}
+ o = assert_warn('') { k.new(42, {foo: 1, bar: 2}) }
+ assert_equal(1, o.a[:foo])
@Struct.instance_eval do
remove_const(:KeywordInitTrue)
@@ -111,6 +137,27 @@ module TestStruct
end
end
+ def test_struct_new_with_keyword_init_and_block
+ struct = @Struct.new(:a, :b, keyword_init: true) do
+ def c
+ a + b
+ end
+ end
+
+ assert_equal(3, struct.new(a: 1, b: 2).c)
+ end
+
+ def test_struct_keyword_init_p
+ struct = @Struct.new(:a, :b, keyword_init: true)
+ assert_equal(true, struct.keyword_init?)
+
+ struct = @Struct.new(:a, :b, keyword_init: false)
+ assert_equal(false, struct.keyword_init?)
+
+ struct = @Struct.new(:a, :b)
+ assert_nil(struct.keyword_init?)
+ end
+
def test_initialize
klass = @Struct.new(:a)
assert_raise(ArgumentError) { klass.new(1, 2) }
@@ -122,6 +169,17 @@ module TestStruct
assert_equal 3, klass.new(1,2).total
end
+ def test_initialize_with_kw
+ klass = @Struct.new(:foo, :options) do
+ def initialize(foo, **options)
+ super(foo, options)
+ end
+ end
+ assert_equal({}, klass.new(42, **Hash.new).options)
+ x = assert_warn('') { klass.new(1, bar: 2) }
+ assert_equal 2, x.options[:bar]
+ end
+
def test_each
klass = @Struct.new(:a, :b)
o = klass.new(1, 2)
@@ -301,15 +359,30 @@ module TestStruct
end
def test_redefinition_warning
- @Struct.new("RedefinitionWarning")
+ @Struct.new(name = "RedefinitionWarning")
e = EnvUtil.verbose_warning do
@Struct.new("RedefinitionWarning")
end
assert_match(/redefining constant #@Struct::RedefinitionWarning/, e)
+
+ @Struct.class_eval do
+ remove_const name
+ end
+ end
+
+ def test_keyword_args_warning
+ assert_warn('') { assert_equal(1, @Struct.new(:a).new(a: 1).a) }
+ assert_warn('') { assert_equal(1, @Struct.new(:a, keyword_init: nil).new(a: 1).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a).new({a: 1}).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, :b).new(1, a: 1).b) }
+ assert_warn('') { assert_equal(1, @Struct.new(:a, keyword_init: true).new(a: 1).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, keyword_init: nil).new({a: 1}).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, keyword_init: false).new(a: 1).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, keyword_init: false).new({a: 1}).a) }
end
def test_nonascii
- struct_test = @Struct.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
+ struct_test = @Struct.new(name = "R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
assert_equal(@Struct.const_get("R\u{e9}sum\u{e9}"), struct_test, '[ruby-core:24849]')
a = struct_test.new(42)
assert_equal("#<struct #@Struct::R\u{e9}sum\u{e9} r\u{e9}sum\u{e9}=42>", a.inspect, '[ruby-core:24849]')
@@ -319,6 +392,10 @@ module TestStruct
assert_nothing_raised(Encoding::CompatibilityError) do
assert_match(/redefining constant #@Struct::R\u{e9}sum\u{e9}/, e)
end
+
+ @Struct.class_eval do
+ remove_const name
+ end
end
def test_junk
@@ -401,13 +478,84 @@ module TestStruct
assert_nil(o.dig(:b, 0))
end
- def test_new_dupilicate
+ def test_new_duplicate
bug12291 = '[ruby-core:74971] [Bug #12291]'
assert_raise_with_message(ArgumentError, /duplicate member/, bug12291) {
@Struct.new(:a, :a)
}
end
+ def test_deconstruct_keys
+ klass = @Struct.new(:a, :b)
+ o = klass.new(1, 2)
+ assert_equal({a: 1, b: 2}, o.deconstruct_keys(nil))
+ assert_equal({a: 1, b: 2}, o.deconstruct_keys([:b, :a]))
+ assert_equal({a: 1}, o.deconstruct_keys([:a]))
+ assert_not_send([o.deconstruct_keys([:a, :c]), :key?, :c])
+ assert_raise(TypeError) {
+ o.deconstruct_keys(0)
+ }
+ end
+
+ def test_public_send
+ klass = @Struct.new(:a)
+ x = klass.new(1)
+ assert_equal(1, x.public_send("a"))
+ assert_equal(42, x.public_send("a=", 42))
+ assert_equal(42, x.public_send("a"))
+ end
+
+ def test_arity
+ klass = @Struct.new(:a)
+ assert_equal 0, klass.instance_method(:a).arity
+ assert_equal 1, klass.instance_method(:a=).arity
+
+ klass.module_eval do
+ define_method(:b=, instance_method(:a=))
+ alias c= a=
+ end
+
+ assert_equal 1, klass.instance_method(:b=).arity
+ assert_equal 1, klass.instance_method(:c=).arity
+ end
+
+ def test_parameters
+ klass = @Struct.new(:a)
+ assert_equal [], klass.instance_method(:a).parameters
+ # NOTE: :_ may not be a spec.
+ assert_equal [[:req, :_]], klass.instance_method(:a=).parameters
+
+ klass.module_eval do
+ define_method(:b=, instance_method(:a=))
+ alias c= a=
+ end
+
+ assert_equal [[:req, :_]], klass.instance_method(:b=).parameters
+ assert_equal [[:req, :_]], klass.instance_method(:c=).parameters
+ end
+
+ def test_named_structs_are_not_rooted
+ omit 'skip on riscv64-linux CI machine. See https://github.com/ruby/ruby/pull/13422' if ENV['RUBY_DEBUG'] == 'ci' && /riscv64-linux/ =~ RUBY_DESCRIPTION
+
+ # [Bug #20311]
+ assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true)
+ code = proc do
+ Struct.new("A")
+ Struct.send(:remove_const, :A)
+ end
+
+ 10_000.times(&code)
+ PREP
+ 50_000.times(&code)
+ CODE
+ end
+
+ def test_frozen_subclass
+ test = Class.new(@Struct.new(:a)).freeze.new(a: 0)
+ assert_kind_of(@Struct, test)
+ assert_equal([:a], test.members)
+ end
+
class TopStruct < Test::Unit::TestCase
include TestStruct