1# -*- coding: us-ascii -*- 2# frozen_string_literal: false 3require 'test/unit' 4require 'timeout' 5 6module TestStruct 7 def test_struct 8 struct_test = @Struct.new("Test", :foo, :bar) 9 assert_equal(@Struct::Test, struct_test) 10 11 test = struct_test.new(1, 2) 12 assert_equal(1, test.foo) 13 assert_equal(2, test.bar) 14 assert_equal(1, test[0]) 15 assert_equal(2, test[1]) 16 17 a, b = test.to_a 18 assert_equal(1, a) 19 assert_equal(2, b) 20 21 test[0] = 22 22 assert_equal(22, test.foo) 23 24 test.bar = 47 25 assert_equal(47, test.bar) 26 end 27 28 # [ruby-dev:26247] more than 10 struct members causes segmentation fault 29 def test_morethan10members 30 list = %w( a b c d e f g h i j k l m n o p ) 31 until list.empty? 32 c = @Struct.new(* list.map {|ch| ch.intern }).new 33 list.each do |ch| 34 c.__send__(ch) 35 end 36 list.pop 37 end 38 end 39 40 def test_small_structs 41 names = [:a, :b, :c, :d] 42 1.upto(4) {|n| 43 fields = names[0, n] 44 klass = @Struct.new(*fields) 45 o = klass.new(*(0...n).to_a) 46 fields.each_with_index {|name, i| 47 assert_equal(i, o[name]) 48 } 49 o = klass.new(*(0...n).to_a.reverse) 50 fields.each_with_index {|name, i| 51 assert_equal(n-i-1, o[name]) 52 } 53 } 54 end 55 56 def test_inherit 57 klass = @Struct.new(:a) 58 klass2 = Class.new(klass) 59 o = klass2.new(1) 60 assert_equal(1, o.a) 61 end 62 63 def test_members 64 klass = @Struct.new(:a) 65 o = klass.new(1) 66 assert_equal([:a], klass.members) 67 assert_equal([:a], o.members) 68 end 69 70 def test_ref 71 klass = @Struct.new(:a) 72 o = klass.new(1) 73 assert_equal(1, o[:a]) 74 assert_raise(NameError) { o[:b] } 75 end 76 77 def test_set 78 klass = @Struct.new(:a) 79 o = klass.new(1) 80 o[:a] = 2 81 assert_equal(2, o[:a]) 82 assert_raise(NameError) { o[:b] = 3 } 83 end 84 85 def test_struct_new 86 assert_raise(NameError) { @Struct.new("foo") } 87 assert_nothing_raised { @Struct.new("Foo") } 88 @Struct.instance_eval { remove_const(:Foo) } 89 assert_nothing_raised { @Struct.new(:a) { } } 90 assert_raise(RuntimeError) { @Struct.new(:a) { raise } } 91 92 assert_equal([:utime, :stime, :cutime, :cstime], Process.times.members) 93 end 94 95 def test_struct_new_with_empty_hash 96 assert_equal({:a=>1}, Struct.new(:a, {}).new({:a=>1}).a) 97 end 98 99 def test_struct_new_with_keyword_init 100 @Struct.new("KeywordInitTrue", :a, :b, keyword_init: true) 101 @Struct.new("KeywordInitFalse", :a, :b, keyword_init: false) 102 103 assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(1, 2) } 104 assert_nothing_raised { @Struct::KeywordInitFalse.new(1, 2) } 105 assert_nothing_raised { @Struct::KeywordInitTrue.new(a: 1, b: 2) } 106 assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(1, b: 2) } 107 assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(a: 1, b: 2, c: 3) } 108 assert_equal @Struct::KeywordInitTrue.new(a: 1, b: 2).values, @Struct::KeywordInitFalse.new(1, 2).values 109 assert_equal "#{@Struct}::KeywordInitFalse", @Struct::KeywordInitFalse.inspect 110 assert_equal "#{@Struct}::KeywordInitTrue(keyword_init: true)", @Struct::KeywordInitTrue.inspect 111 112 @Struct.instance_eval do 113 remove_const(:KeywordInitTrue) 114 remove_const(:KeywordInitFalse) 115 end 116 end 117 118 def test_initialize 119 klass = @Struct.new(:a) 120 assert_raise(ArgumentError) { klass.new(1, 2) } 121 klass = @Struct.new(:total) do 122 def initialize(a, b) 123 super(a+b) 124 end 125 end 126 assert_equal 3, klass.new(1,2).total 127 end 128 129 def test_each 130 klass = @Struct.new(:a, :b) 131 o = klass.new(1, 2) 132 assert_equal([1, 2], o.each.to_a) 133 end 134 135 def test_each_pair 136 klass = @Struct.new(:a, :b) 137 o = klass.new(1, 2) 138 assert_equal([[:a, 1], [:b, 2]], o.each_pair.to_a) 139 bug7382 = '[ruby-dev:46533]' 140 a = [] 141 o.each_pair {|x| a << x} 142 assert_equal([[:a, 1], [:b, 2]], a, bug7382) 143 end 144 145 def test_inspect 146 klass = @Struct.new(:a) 147 o = klass.new(1) 148 assert_equal("#<struct a=1>", o.inspect) 149 o.a = o 150 assert_match(/^#<struct a=#<struct #<.*?>:...>>$/, o.inspect) 151 152 @Struct.new("Foo", :a) 153 o = @Struct::Foo.new(1) 154 assert_equal("#<struct #@Struct::Foo a=1>", o.inspect) 155 @Struct.instance_eval { remove_const(:Foo) } 156 157 klass = @Struct.new(:a, :b) 158 o = klass.new(1, 2) 159 assert_equal("#<struct a=1, b=2>", o.inspect) 160 161 klass = @Struct.new(:@a) 162 o = klass.new(1) 163 assert_equal(1, o.__send__(:@a)) 164 assert_equal("#<struct :@a=1>", o.inspect) 165 o.__send__(:"@a=", 2) 166 assert_equal(2, o.__send__(:@a)) 167 assert_equal("#<struct :@a=2>", o.inspect) 168 o.__send__("@a=", 3) 169 assert_equal(3, o.__send__(:@a)) 170 assert_equal("#<struct :@a=3>", o.inspect) 171 172 methods = klass.instance_methods(false) 173 assert_equal([:@a, :"@a="].sort.inspect, methods.sort.inspect, '[Bug #8756]') 174 assert_include(methods, :@a) 175 assert_include(methods, :"@a=") 176 end 177 178 def test_init_copy 179 klass = @Struct.new(:a) 180 o = klass.new(1) 181 assert_equal(o, o.dup) 182 end 183 184 def test_aref 185 klass = @Struct.new(:a) 186 o = klass.new(1) 187 assert_equal(1, o[0]) 188 assert_raise_with_message(IndexError, /offset -2\b/) {o[-2]} 189 assert_raise_with_message(IndexError, /offset 1\b/) {o[1]} 190 assert_raise_with_message(NameError, /foo/) {o["foo"]} 191 assert_raise_with_message(NameError, /foo/) {o[:foo]} 192 end 193 194 def test_aset 195 klass = @Struct.new(:a) 196 o = klass.new(1) 197 o[0] = 2 198 assert_equal(2, o[:a]) 199 assert_raise_with_message(IndexError, /offset -2\b/) {o[-2] = 3} 200 assert_raise_with_message(IndexError, /offset 1\b/) {o[1] = 3} 201 assert_raise_with_message(NameError, /foo/) {o["foo"] = 3} 202 assert_raise_with_message(NameError, /foo/) {o[:foo] = 3} 203 end 204 205 def test_values_at 206 klass = @Struct.new(:a, :b, :c, :d, :e, :f) 207 o = klass.new(1, 2, 3, 4, 5, 6) 208 assert_equal([2, 4, 6], o.values_at(1, 3, 5)) 209 assert_equal([2, 3, 4, 3, 4, 5], o.values_at(1..3, 2...5)) 210 end 211 212 def test_select 213 klass = @Struct.new(:a, :b, :c, :d, :e, :f) 214 o = klass.new(1, 2, 3, 4, 5, 6) 215 assert_equal([1, 3, 5], o.select {|v| v % 2 != 0 }) 216 assert_raise(ArgumentError) { o.select(1) } 217 end 218 219 def test_filter 220 klass = @Struct.new(:a, :b, :c, :d, :e, :f) 221 o = klass.new(1, 2, 3, 4, 5, 6) 222 assert_equal([1, 3, 5], o.filter {|v| v % 2 != 0 }) 223 assert_raise(ArgumentError) { o.filter(1) } 224 end 225 226 def test_big_struct 227 klass1 = @Struct.new(*('a'..'z').map(&:to_sym)) 228 o = klass1.new 229 assert_nil o.z 230 assert_equal(:foo, o.z = :foo) 231 assert_equal(:foo, o.z) 232 assert_equal(:foo, o[25]) 233 end 234 235 def test_overridden_aset 236 bug10601 = '[ruby-core:66846] [Bug #10601]: should not be affected by []= method' 237 238 struct = Class.new(Struct.new(*(:a..:z), :result)) do 239 def []=(*args) 240 raise args.inspect 241 end 242 end 243 244 obj = struct.new 245 assert_nothing_raised(RuntimeError, bug10601) do 246 obj.result = 42 247 end 248 assert_equal(42, obj.result, bug10601) 249 end 250 251 def test_overridden_aref 252 bug10601 = '[ruby-core:66846] [Bug #10601]: should not be affected by [] method' 253 254 struct = Class.new(Struct.new(*(:a..:z), :result)) do 255 def [](*args) 256 raise args.inspect 257 end 258 end 259 260 obj = struct.new 261 obj.result = 42 262 result = assert_nothing_raised(RuntimeError, bug10601) do 263 break obj.result 264 end 265 assert_equal(42, result, bug10601) 266 end 267 268 def test_equal 269 klass1 = @Struct.new(:a) 270 klass2 = @Struct.new(:a, :b) 271 o1 = klass1.new(1) 272 o2 = klass1.new(1) 273 o3 = klass2.new(1) 274 assert_equal(o1, o2) 275 assert_not_equal(o1, o3) 276 end 277 278 def test_hash 279 klass = @Struct.new(:a) 280 o = klass.new(1) 281 assert_kind_of(Integer, o.hash) 282 end 283 284 def test_eql 285 klass1 = @Struct.new(:a) 286 klass2 = @Struct.new(:a, :b) 287 o1 = klass1.new(1) 288 o2 = klass1.new(1) 289 o3 = klass2.new(1) 290 assert_operator(o1, :eql?, o2) 291 assert_not_operator(o1, :eql?, o3) 292 end 293 294 def test_size 295 klass = @Struct.new(:a) 296 o = klass.new(1) 297 assert_equal(1, o.size) 298 end 299 300 def test_error 301 assert_raise(TypeError){ 302 @Struct.new(0) 303 } 304 end 305 306 def test_redefinition_warning 307 @Struct.new("RedefinitionWarning") 308 e = EnvUtil.verbose_warning do 309 @Struct.new("RedefinitionWarning") 310 end 311 assert_match(/redefining constant #@Struct::RedefinitionWarning/, e) 312 end 313 314 def test_nonascii 315 struct_test = @Struct.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}") 316 assert_equal(@Struct.const_get("R\u{e9}sum\u{e9}"), struct_test, '[ruby-core:24849]') 317 a = struct_test.new(42) 318 assert_equal("#<struct #@Struct::R\u{e9}sum\u{e9} r\u{e9}sum\u{e9}=42>", a.inspect, '[ruby-core:24849]') 319 e = EnvUtil.verbose_warning do 320 @Struct.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}") 321 end 322 assert_nothing_raised(Encoding::CompatibilityError) do 323 assert_match(/redefining constant #@Struct::R\u{e9}sum\u{e9}/, e) 324 end 325 end 326 327 def test_junk 328 struct_test = @Struct.new("Foo", "a\000") 329 o = struct_test.new(1) 330 assert_equal(1, o.send("a\000")) 331 @Struct.instance_eval { remove_const(:Foo) } 332 end 333 334 def test_comparison_when_recursive 335 klass1 = @Struct.new(:a, :b, :c) 336 337 x = klass1.new(1, 2, nil); x.c = x 338 y = klass1.new(1, 2, nil); y.c = y 339 Timeout.timeout(1) { 340 assert_equal x, y 341 assert_operator x, :eql?, y 342 } 343 344 z = klass1.new(:something, :other, nil); z.c = z 345 Timeout.timeout(1) { 346 assert_not_equal x, z 347 assert_not_operator x, :eql?, z 348 } 349 350 x.c = y; y.c = x 351 Timeout.timeout(1) { 352 assert_equal x, y 353 assert_operator x, :eql?, y 354 } 355 356 x.c = z; z.c = x 357 Timeout.timeout(1) { 358 assert_not_equal x, z 359 assert_not_operator x, :eql?, z 360 } 361 end 362 363 def test_to_h 364 klass = @Struct.new(:a, :b, :c, :d, :e, :f) 365 o = klass.new(1, 2, 3, 4, 5, 6) 366 assert_equal({a:1, b:2, c:3, d:4, e:5, f:6}, o.to_h) 367 end 368 369 def test_to_h_block 370 klass = @Struct.new(:a, :b, :c, :d, :e, :f) 371 o = klass.new(1, 2, 3, 4, 5, 6) 372 assert_equal({"a" => 1, "b" => 4, "c" => 9, "d" => 16, "e" => 25, "f" => 36}, 373 o.to_h {|k, v| [k.to_s, v*v]}) 374 end 375 376 def test_question_mark_in_member 377 klass = @Struct.new(:a, :b?) 378 x = Object.new 379 o = klass.new("test", x) 380 assert_same(x, o.b?) 381 o.send("b?=", 42) 382 assert_equal(42, o.b?) 383 end 384 385 def test_bang_mark_in_member 386 klass = @Struct.new(:a, :b!) 387 x = Object.new 388 o = klass.new("test", x) 389 assert_same(x, o.b!) 390 o.send("b!=", 42) 391 assert_equal(42, o.b!) 392 end 393 394 def test_setter_method_returns_value 395 klass = @Struct.new(:a) 396 x = klass.new 397 assert_equal "[Bug #9353]", x.send(:a=, "[Bug #9353]") 398 end 399 400 def test_dig 401 klass = @Struct.new(:a) 402 o = klass.new(klass.new({b: [1, 2, 3]})) 403 assert_equal(1, o.dig(:a, :a, :b, 0)) 404 assert_nil(o.dig(:b, 0)) 405 end 406 407 def test_new_dupilicate 408 bug12291 = '[ruby-core:74971] [Bug #12291]' 409 assert_raise_with_message(ArgumentError, /duplicate member/, bug12291) { 410 @Struct.new(:a, :a) 411 } 412 end 413 414 class TopStruct < Test::Unit::TestCase 415 include TestStruct 416 417 def initialize(*) 418 super 419 @Struct = Struct 420 end 421 end 422 423 class SubStruct < Test::Unit::TestCase 424 include TestStruct 425 SubStruct = Class.new(Struct) 426 427 def initialize(*) 428 super 429 @Struct = SubStruct 430 end 431 end 432end 433