PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/test/ruby/test_marshal.rb

http://github.com/ruby/ruby
Ruby | 783 lines | 768 code | 14 blank | 1 comment | 3 complexity | 21174f6920e125b6e46c5d828c7f059a MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0
  1. # frozen_string_literal: false
  2. require 'test/unit'
  3. require 'tempfile'
  4. require_relative 'marshaltestlib'
  5. class TestMarshal < Test::Unit::TestCase
  6. include MarshalTestLib
  7. def setup
  8. @verbose = $VERBOSE
  9. $VERBOSE = nil
  10. end
  11. def teardown
  12. $VERBOSE = @verbose
  13. end
  14. def encode(o)
  15. Marshal.dump(o)
  16. end
  17. def decode(s)
  18. Marshal.load(s)
  19. end
  20. def fact(n)
  21. return 1 if n == 0
  22. f = 1
  23. while n>0
  24. f *= n
  25. n -= 1
  26. end
  27. return f
  28. end
  29. def test_marshal
  30. a = [1, 2, 3, [4,5,"foo"], {1=>"bar"}, 2.5, fact(30)]
  31. assert_equal a, Marshal.load(Marshal.dump(a))
  32. [[1,2,3,4], [81, 2, 118, 3146]].each { |w,x,y,z|
  33. obj = (x.to_f + y.to_f / z.to_f) * Math.exp(w.to_f / (x.to_f + y.to_f / z.to_f))
  34. assert_equal obj, Marshal.load(Marshal.dump(obj))
  35. }
  36. bug3659 = '[ruby-dev:41936]'
  37. [1.0, 10.0, 100.0, 110.0].each {|x|
  38. assert_equal(x, Marshal.load(Marshal.dump(x)), bug3659)
  39. }
  40. end
  41. StrClone = String.clone
  42. def test_marshal_cloned_class
  43. assert_instance_of(StrClone, Marshal.load(Marshal.dump(StrClone.new("abc"))))
  44. end
  45. def test_inconsistent_struct
  46. TestMarshal.const_set :StructOrNot, Struct.new(:a)
  47. s = Marshal.dump(StructOrNot.new(1))
  48. TestMarshal.instance_eval { remove_const :StructOrNot }
  49. TestMarshal.const_set :StructOrNot, Class.new
  50. assert_raise(TypeError, "[ruby-dev:31709]") { Marshal.load(s) }
  51. end
  52. def test_struct_invalid_members
  53. TestMarshal.const_set :StructInvalidMembers, Struct.new(:a)
  54. assert_raise(TypeError, "[ruby-dev:31759]") {
  55. Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo")
  56. TestMarshal::StructInvalidMembers.members
  57. }
  58. end
  59. class C
  60. def initialize(str)
  61. @str = str
  62. end
  63. attr_reader :str
  64. def _dump(limit)
  65. @str
  66. end
  67. def self._load(s)
  68. new(s)
  69. end
  70. end
  71. def test_too_long_string
  72. data = Marshal.dump(C.new("a".force_encoding("ascii-8bit")))
  73. data[-2, 1] = "\003\377\377\377"
  74. assert_raise_with_message(ArgumentError, "marshal data too short", "[ruby-dev:32054]") {
  75. Marshal.load(data)
  76. }
  77. end
  78. def test_userdef_encoding
  79. s1 = "\xa4\xa4".force_encoding("euc-jp")
  80. o1 = C.new(s1)
  81. m = Marshal.dump(o1)
  82. o2 = Marshal.load(m)
  83. s2 = o2.str
  84. assert_equal(s1, s2)
  85. end
  86. def test_pipe
  87. o1 = C.new("a" * 10000)
  88. IO.pipe do |r, w|
  89. th = Thread.new {Marshal.dump(o1, w)}
  90. o2 = Marshal.load(r)
  91. th.join
  92. assert_equal(o1.str, o2.str)
  93. end
  94. IO.pipe do |r, w|
  95. th = Thread.new {Marshal.dump(o1, w, 2)}
  96. o2 = Marshal.load(r)
  97. th.join
  98. assert_equal(o1.str, o2.str)
  99. end
  100. assert_raise(TypeError) { Marshal.dump("foo", Object.new) }
  101. assert_raise(TypeError) { Marshal.load(Object.new) }
  102. end
  103. def test_limit
  104. assert_equal([[[]]], Marshal.load(Marshal.dump([[[]]], 3)))
  105. assert_raise(ArgumentError) { Marshal.dump([[[]]], 2) }
  106. assert_nothing_raised(ArgumentError, '[ruby-core:24100]') { Marshal.dump("\u3042", 1) }
  107. end
  108. def test_userdef_invalid
  109. o = C.new(nil)
  110. assert_raise(TypeError) { Marshal.dump(o) }
  111. end
  112. def test_class
  113. o = class << Object.new; self; end
  114. assert_raise(TypeError) { Marshal.dump(o) }
  115. assert_equal(Object, Marshal.load(Marshal.dump(Object)))
  116. assert_equal(Enumerable, Marshal.load(Marshal.dump(Enumerable)))
  117. end
  118. class C2
  119. def initialize(ary)
  120. @ary = ary
  121. end
  122. def _dump(s)
  123. @ary.clear
  124. "foo"
  125. end
  126. end
  127. def test_modify_array_during_dump
  128. a = []
  129. o = C2.new(a)
  130. a << o << nil
  131. assert_raise(RuntimeError) { Marshal.dump(a) }
  132. end
  133. def test_change_class_name
  134. eval("class C3; def _dump(s); 'foo'; end; end")
  135. m = Marshal.dump(C3.new)
  136. assert_raise(TypeError) { Marshal.load(m) }
  137. eval("C3 = nil")
  138. assert_raise(TypeError) { Marshal.load(m) }
  139. end
  140. def test_change_struct
  141. eval("C3 = Struct.new(:foo, :bar)")
  142. m = Marshal.dump(C3.new("FOO", "BAR"))
  143. eval("C3 = Struct.new(:foo)")
  144. assert_raise(TypeError) { Marshal.load(m) }
  145. eval("C3 = Struct.new(:foo, :baz)")
  146. assert_raise(TypeError) { Marshal.load(m) }
  147. end
  148. class C4
  149. def initialize(gc)
  150. @gc = gc
  151. end
  152. def _dump(s)
  153. GC.start if @gc
  154. "foo"
  155. end
  156. end
  157. def test_gc
  158. assert_nothing_raised do
  159. Marshal.dump((0..1000).map {|x| C4.new(x % 50 == 25) })
  160. end
  161. end
  162. def test_symbol2
  163. [:ruby, :"\u{7d05}\u{7389}"].each do |sym|
  164. assert_equal(sym, Marshal.load(Marshal.dump(sym)), '[ruby-core:24788]')
  165. end
  166. bug2548 = '[ruby-core:27375]'
  167. ary = [:$1, nil]
  168. assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug2548)
  169. end
  170. def test_symlink
  171. assert_include(Marshal.dump([:a, :a]), ';')
  172. end
  173. def test_symlink_in_ivar
  174. bug10991 = '[ruby-core:68587] [Bug #10991]'
  175. sym = Marshal.load("\x04\x08" +
  176. "I" ":\x0bKernel" +
  177. ("\x06" +
  178. ("I" ":\x07@a" +
  179. ("\x06" ":\x07@b" "e;\x0""o:\x0bObject""\x0")) +
  180. "0"))
  181. assert_equal(:Kernel, sym, bug10991)
  182. end
  183. ClassUTF8 = eval("class R\u{e9}sum\u{e9}; self; end")
  184. iso_8859_1 = Encoding::ISO_8859_1
  185. structISO8859_1 = Struct.new("r\xe9sum\xe9".force_encoding(iso_8859_1).intern)
  186. const_set("R\xe9sum\xe9".force_encoding(iso_8859_1), structISO8859_1)
  187. structISO8859_1.name
  188. StructISO8859_1 = structISO8859_1
  189. classISO8859_1 = Class.new do
  190. attr_accessor "r\xe9sum\xe9".force_encoding(iso_8859_1)
  191. eval("def initialize(x) @r\xe9sum\xe9 = x; end".force_encoding(iso_8859_1))
  192. end
  193. const_set("R\xe9sum\xe92".force_encoding(iso_8859_1), classISO8859_1)
  194. classISO8859_1.name
  195. ClassISO8859_1 = classISO8859_1
  196. def test_class_nonascii
  197. a = ClassUTF8.new
  198. assert_instance_of(ClassUTF8, Marshal.load(Marshal.dump(a)), '[ruby-core:24790]')
  199. bug1932 = '[ruby-core:24882]'
  200. a = StructISO8859_1.new(10)
  201. assert_nothing_raised(bug1932) do
  202. assert_equal(a, Marshal.load(Marshal.dump(a)), bug1932)
  203. end
  204. a.__send__("#{StructISO8859_1.members[0]}=", a)
  205. assert_nothing_raised(bug1932) do
  206. assert_equal(a, Marshal.load(Marshal.dump(a)), bug1932)
  207. end
  208. a = ClassISO8859_1.new(10)
  209. assert_nothing_raised(bug1932) do
  210. b = Marshal.load(Marshal.dump(a))
  211. assert_equal(ClassISO8859_1, b.class, bug1932)
  212. assert_equal(a.instance_variables, b.instance_variables, bug1932)
  213. a.instance_variables.each do |i|
  214. assert_equal(a.instance_variable_get(i), b.instance_variable_get(i), bug1932)
  215. end
  216. end
  217. a.__send__(a.methods(true).grep(/=\z/)[0], a)
  218. assert_nothing_raised(bug1932) do
  219. b = Marshal.load(Marshal.dump(a))
  220. assert_equal(ClassISO8859_1, b.class, bug1932)
  221. assert_equal(a.instance_variables, b.instance_variables, bug1932)
  222. assert_equal(b, b.instance_variable_get(a.instance_variables[0]), bug1932)
  223. end
  224. end
  225. def test_regexp2
  226. assert_equal(/\\u/, Marshal.load("\004\b/\b\\\\u\000"))
  227. assert_equal(/u/, Marshal.load("\004\b/\a\\u\000"))
  228. assert_equal(/u/, Marshal.load("\004\bI/\a\\u\000\006:\016@encoding\"\vEUC-JP"))
  229. bug2109 = '[ruby-core:25625]'
  230. a = "\x82\xa0".force_encoding(Encoding::Windows_31J)
  231. b = "\x82\xa2".force_encoding(Encoding::Windows_31J)
  232. c = [/#{a}/, /#{b}/]
  233. assert_equal(c, Marshal.load(Marshal.dump(c)), bug2109)
  234. assert_nothing_raised(ArgumentError, '[ruby-dev:40386]') do
  235. re = Tempfile.create("marshal_regexp") do |f|
  236. f.binmode.write("\x04\bI/\x00\x00\x06:\rencoding\"\rUS-ASCII")
  237. f.rewind
  238. re2 = Marshal.load(f)
  239. re2
  240. end
  241. assert_equal(//, re)
  242. end
  243. end
  244. class DumpTest
  245. def marshal_dump
  246. @@block.call(:marshal_dump)
  247. end
  248. def dump_each(&block)
  249. @@block = block
  250. Marshal.dump(self)
  251. end
  252. end
  253. class LoadTest
  254. def marshal_dump
  255. nil
  256. end
  257. def marshal_load(obj)
  258. @@block.call(:marshal_load)
  259. end
  260. def self.load_each(m, &block)
  261. @@block = block
  262. Marshal.load(m)
  263. end
  264. end
  265. def test_context_switch
  266. o = DumpTest.new
  267. e = o.enum_for(:dump_each)
  268. assert_equal(:marshal_dump, e.next)
  269. GC.start
  270. assert(true, '[ruby-dev:39425]')
  271. assert_raise(StopIteration) {e.next}
  272. o = LoadTest.new
  273. m = Marshal.dump(o)
  274. e = LoadTest.enum_for(:load_each, m)
  275. assert_equal(:marshal_load, e.next)
  276. GC.start
  277. assert(true, '[ruby-dev:39425]')
  278. assert_raise(StopIteration) {e.next}
  279. end
  280. def test_dump_buffer
  281. bug2390 = '[ruby-dev:39744]'
  282. w = ""
  283. def w.write(str)
  284. self << str.to_s
  285. end
  286. Marshal.dump(Object.new, w)
  287. assert_not_empty(w, bug2390)
  288. end
  289. class C5
  290. def marshal_dump
  291. "foo"
  292. end
  293. def marshal_load(foo)
  294. @foo = foo
  295. end
  296. def initialize(x)
  297. @x = x
  298. end
  299. end
  300. def test_marshal_dump
  301. c = C5.new("bar")
  302. s = Marshal.dump(c)
  303. d = Marshal.load(s)
  304. assert_equal("foo", d.instance_variable_get(:@foo))
  305. assert_equal(false, d.instance_variable_defined?(:@x))
  306. end
  307. class C6
  308. def initialize
  309. @stdin = STDIN
  310. end
  311. attr_reader :stdin
  312. def marshal_dump
  313. 1
  314. end
  315. def marshal_load(x)
  316. @stdin = STDIN
  317. end
  318. end
  319. def test_marshal_dump_extra_iv
  320. o = C6.new
  321. m = nil
  322. assert_nothing_raised("[ruby-dev:21475] [ruby-dev:39845]") {
  323. m = Marshal.dump(o)
  324. }
  325. o2 = Marshal.load(m)
  326. assert_equal(STDIN, o2.stdin)
  327. end
  328. def test_marshal_string_encoding
  329. o1 = ["foo".force_encoding("EUC-JP")] + [ "bar" ] * 2
  330. m = Marshal.dump(o1)
  331. o2 = Marshal.load(m)
  332. assert_equal(o1, o2, "[ruby-dev:40388]")
  333. end
  334. def test_marshal_regexp_encoding
  335. o1 = [Regexp.new("r1".force_encoding("EUC-JP"))] + ["r2"] * 2
  336. m = Marshal.dump(o1)
  337. o2 = Marshal.load(m)
  338. assert_equal(o1, o2, "[ruby-dev:40416]")
  339. end
  340. def test_marshal_encoding_encoding
  341. o1 = [Encoding.find("EUC-JP")] + ["r2"] * 2
  342. m = Marshal.dump(o1)
  343. o2 = Marshal.load(m)
  344. assert_equal(o1, o2)
  345. end
  346. def test_marshal_symbol_ascii8bit
  347. bug6209 = '[ruby-core:43762]'
  348. o1 = "\xff".force_encoding("ASCII-8BIT").intern
  349. m = Marshal.dump(o1)
  350. o2 = nil
  351. assert_nothing_raised(EncodingError, bug6209) {o2 = Marshal.load(m)}
  352. assert_equal(o1, o2, bug6209)
  353. end
  354. class PrivateClass
  355. def initialize(foo)
  356. @foo = foo
  357. end
  358. attr_reader :foo
  359. end
  360. private_constant :PrivateClass
  361. def test_marshal_private_class
  362. o1 = PrivateClass.new("test")
  363. o2 = Marshal.load(Marshal.dump(o1))
  364. assert_equal(o1.class, o2.class)
  365. assert_equal(o1.foo, o2.foo)
  366. end
  367. def test_marshal_complex
  368. assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x05")}
  369. assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x06i\x00")}
  370. assert_equal(Complex(1, 2), Marshal.load("\x04\bU:\fComplex[\ai\x06i\a"))
  371. assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\bi\x00i\x00i\x00")}
  372. end
  373. def test_marshal_rational
  374. assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x05")}
  375. assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x06i\x00")}
  376. assert_equal(Rational(1, 2), Marshal.load("\x04\bU:\rRational[\ai\x06i\a"))
  377. assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\bi\x00i\x00i\x00")}
  378. end
  379. def test_marshal_flonum_reference
  380. bug7348 = '[ruby-core:49323]'
  381. e = []
  382. ary = [ [2.0, e], [e] ]
  383. assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug7348)
  384. end
  385. class TestClass
  386. end
  387. module TestModule
  388. end
  389. class Bug7627 < Struct.new(:bar)
  390. attr_accessor :foo
  391. def marshal_dump; 'dump'; end # fake dump data
  392. def marshal_load(*); end # do nothing
  393. end
  394. def test_marshal_dump_struct_ivar
  395. bug7627 = '[ruby-core:51163]'
  396. obj = Bug7627.new
  397. obj.foo = '[Bug #7627]'
  398. dump = Marshal.dump(obj)
  399. loaded = Marshal.load(dump)
  400. assert_equal(obj, loaded, bug7627)
  401. assert_nil(loaded.foo, bug7627)
  402. end
  403. class LoadData
  404. attr_reader :data
  405. def initialize(data)
  406. @data = data
  407. end
  408. alias marshal_dump data
  409. alias marshal_load initialize
  410. end
  411. class Bug8276 < LoadData
  412. def initialize(*)
  413. super
  414. freeze
  415. end
  416. alias marshal_load initialize
  417. end
  418. class FrozenData < LoadData
  419. def marshal_load(data)
  420. super
  421. data.instance_variables.each do |iv|
  422. instance_variable_set(iv, data.instance_variable_get(iv))
  423. end
  424. freeze
  425. end
  426. end
  427. def test_marshal_dump_excess_encoding
  428. bug8276 = '[ruby-core:54334] [Bug #8276]'
  429. t = Bug8276.new(bug8276)
  430. s = Marshal.dump(t)
  431. assert_nothing_raised(RuntimeError, bug8276) {s = Marshal.load(s)}
  432. assert_equal(t.data, s.data, bug8276)
  433. end
  434. def test_marshal_dump_ivar
  435. s = "data with ivar"
  436. s.instance_variable_set(:@t, 42)
  437. t = Bug8276.new(s)
  438. s = Marshal.dump(t)
  439. assert_raise(FrozenError) {Marshal.load(s)}
  440. end
  441. def test_marshal_load_ivar
  442. s = "data with ivar"
  443. s.instance_variable_set(:@t, 42)
  444. hook = ->(v) {
  445. if LoadData === v
  446. assert_send([v, :instance_variable_defined?, :@t], v.class.name)
  447. assert_equal(42, v.instance_variable_get(:@t), v.class.name)
  448. end
  449. v
  450. }
  451. [LoadData, FrozenData].each do |klass|
  452. t = klass.new(s)
  453. d = Marshal.dump(t)
  454. v = assert_nothing_raised(RuntimeError) {break Marshal.load(d, hook)}
  455. assert_send([v, :instance_variable_defined?, :@t], klass.name)
  456. assert_equal(42, v.instance_variable_get(:@t), klass.name)
  457. end
  458. end
  459. def test_class_ivar
  460. assert_raise(TypeError) {Marshal.load("\x04\x08Ic\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
  461. assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
  462. assert_not_operator(TestClass, :instance_variable_defined?, :@bug)
  463. end
  464. def test_module_ivar
  465. assert_raise(TypeError) {Marshal.load("\x04\x08Im\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
  466. assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
  467. assert_not_operator(TestModule, :instance_variable_defined?, :@bug)
  468. end
  469. class TestForRespondToFalse
  470. def respond_to?(a)
  471. false
  472. end
  473. end
  474. def test_marshal_respond_to_arity
  475. assert_nothing_raised(ArgumentError, '[Bug #7722]') do
  476. Marshal.dump(TestForRespondToFalse.new)
  477. end
  478. end
  479. def test_packed_string
  480. packed = ["foo"].pack("p")
  481. bare = "".force_encoding(Encoding::ASCII_8BIT) << packed
  482. assert_equal(Marshal.dump(bare), Marshal.dump(packed))
  483. end
  484. class Bug9523
  485. attr_reader :cc
  486. def marshal_dump
  487. callcc {|c| @cc = c }
  488. nil
  489. end
  490. def marshal_load(v)
  491. end
  492. end
  493. def test_continuation
  494. require "continuation"
  495. c = Bug9523.new
  496. assert_raise_with_message(RuntimeError, /Marshal\.dump reentered at marshal_dump/) do
  497. Marshal.dump(c)
  498. GC.start
  499. 1000.times {"x"*1000}
  500. GC.start
  501. c.cc.call
  502. end
  503. end
  504. def test_undumpable_message
  505. c = Module.new {break module_eval("class IO\u{26a1} < IO;self;end")}
  506. assert_raise_with_message(TypeError, /IO\u{26a1}/) {
  507. Marshal.dump(c.new(0, autoclose: false))
  508. }
  509. end
  510. def test_undumpable_data
  511. c = Module.new {break module_eval("class T\u{23F0 23F3}<Time;undef _dump;self;end")}
  512. assert_raise_with_message(TypeError, /T\u{23F0 23F3}/) {
  513. Marshal.dump(c.new)
  514. }
  515. end
  516. def test_unloadable_data
  517. name = "Unloadable\u{23F0 23F3}"
  518. c = eval("class #{name} < Time;;self;end")
  519. c.class_eval {
  520. alias _dump_data _dump
  521. undef _dump
  522. }
  523. d = Marshal.dump(c.new)
  524. assert_raise_with_message(TypeError, /Unloadable\u{23F0 23F3}/) {
  525. Marshal.load(d)
  526. }
  527. # cleanup
  528. self.class.class_eval do
  529. remove_const name
  530. end
  531. end
  532. def test_unloadable_userdef
  533. name = "Userdef\u{23F0 23F3}"
  534. c = eval("class #{name} < Time;self;end")
  535. class << c
  536. undef _load
  537. end
  538. d = Marshal.dump(c.new)
  539. assert_raise_with_message(TypeError, /Userdef\u{23F0 23F3}/) {
  540. Marshal.load(d)
  541. }
  542. # cleanup
  543. self.class.class_eval do
  544. remove_const name
  545. end
  546. end
  547. def test_unloadable_usrmarshal
  548. c = eval("class UsrMarshal\u{23F0 23F3}<Time;self;end")
  549. c.class_eval {
  550. alias marshal_dump _dump
  551. }
  552. d = Marshal.dump(c.new)
  553. assert_raise_with_message(TypeError, /UsrMarshal\u{23F0 23F3}/) {
  554. Marshal.load(d)
  555. }
  556. end
  557. def test_no_internal_ids
  558. opt = %w[--disable=gems]
  559. args = [opt, 'Marshal.dump("",STDOUT)', true, true]
  560. kw = {encoding: Encoding::ASCII_8BIT}
  561. out, err, status = EnvUtil.invoke_ruby(*args, **kw)
  562. assert_empty(err)
  563. assert_predicate(status, :success?)
  564. expected = out
  565. opt << "--enable=frozen-string-literal"
  566. opt << "--debug=frozen-string-literal"
  567. out, err, status = EnvUtil.invoke_ruby(*args, **kw)
  568. assert_empty(err)
  569. assert_predicate(status, :success?)
  570. assert_equal(expected, out)
  571. end
  572. def test_marshal_honor_post_proc_value_for_link
  573. str = 'x' # for link
  574. obj = [str, str]
  575. assert_equal(['X', 'X'], Marshal.load(Marshal.dump(obj), ->(v) { v == str ? v.upcase : v }))
  576. end
  577. def test_marshal_load_extended_class_crash
  578. assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
  579. begin;
  580. assert_raise_with_message(ArgumentError, /undefined/) do
  581. Marshal.load("\x04\be:\x0F\x00omparableo:\vObject\x00")
  582. end
  583. end;
  584. end
  585. def test_marshal_load_r_prepare_reference_crash
  586. crash = "\x04\bI/\x05\x00\x06:\x06E{\x06@\x05T"
  587. opt = %w[--disable=gems]
  588. assert_separately(opt, <<-RUBY)
  589. assert_raise_with_message(ArgumentError, /bad link/) do
  590. Marshal.load(#{crash.dump})
  591. end
  592. RUBY
  593. end
  594. MethodMissingWithoutRespondTo = Struct.new(:wrapped_object) do
  595. undef respond_to?
  596. def method_missing(*args, &block)
  597. wrapped_object.public_send(*args, &block)
  598. end
  599. def respond_to_missing?(name, private = false)
  600. wrapped_object.respond_to?(name, false)
  601. end
  602. end
  603. def test_method_missing_without_respond_to
  604. bug12353 = "[ruby-core:75377] [Bug #12353]: try method_missing if" \
  605. " respond_to? is undefined"
  606. obj = MethodMissingWithoutRespondTo.new("foo")
  607. dump = assert_nothing_raised(NoMethodError, bug12353) do
  608. Marshal.dump(obj)
  609. end
  610. assert_equal(obj, Marshal.load(dump))
  611. end
  612. class Bug12974
  613. def marshal_dump
  614. dup
  615. end
  616. end
  617. def test_marshal_dump_recursion
  618. assert_raise_with_message(RuntimeError, /same class instance/) do
  619. Marshal.dump(Bug12974.new)
  620. end
  621. end
  622. Bug14314 = Struct.new(:foo, keyword_init: true)
  623. def test_marshal_keyword_init_struct
  624. obj = Bug14314.new(foo: 42)
  625. assert_equal obj, Marshal.load(Marshal.dump(obj))
  626. end
  627. class Bug15968
  628. attr_accessor :bar, :baz
  629. def initialize
  630. self.bar = Bar.new(self)
  631. end
  632. class Bar
  633. attr_accessor :foo
  634. def initialize(foo)
  635. self.foo = foo
  636. end
  637. def marshal_dump
  638. if self.foo.baz
  639. self.foo.remove_instance_variable(:@baz)
  640. else
  641. self.foo.baz = :problem
  642. end
  643. {foo: self.foo}
  644. end
  645. def marshal_load(data)
  646. self.foo = data[:foo]
  647. end
  648. end
  649. end
  650. def test_marshal_dump_adding_instance_variable
  651. obj = Bug15968.new
  652. assert_raise_with_message(RuntimeError, /instance variable added/) do
  653. Marshal.dump(obj)
  654. end
  655. end
  656. def test_marshal_dump_removing_instance_variable
  657. obj = Bug15968.new
  658. obj.baz = :Bug15968
  659. assert_raise_with_message(RuntimeError, /instance variable removed/) do
  660. Marshal.dump(obj)
  661. end
  662. end
  663. ruby2_keywords def ruby2_keywords_hash(*a)
  664. a.last
  665. end
  666. def ruby2_keywords_test(key: 1)
  667. key
  668. end
  669. def test_marshal_with_ruby2_keywords_hash
  670. flagged_hash = ruby2_keywords_hash(key: 42)
  671. hash = Marshal.load(Marshal.dump(flagged_hash))
  672. assert_equal(42, ruby2_keywords_test(*[hash]))
  673. end
  674. end