summaryrefslogtreecommitdiff
path: root/spec/ruby/core/marshal/shared/load.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/marshal/shared/load.rb')
-rw-r--r--spec/ruby/core/marshal/shared/load.rb697
1 files changed, 506 insertions, 191 deletions
diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb
index 98fae44296..02c8e7f0b1 100644
--- a/spec/ruby/core/marshal/shared/load.rb
+++ b/spec/ruby/core/marshal/shared/load.rb
@@ -1,4 +1,4 @@
-# -*- encoding: binary -*-
+# encoding: binary
require_relative '../fixtures/marshal_data'
describe :marshal_load, shared: true do
@@ -8,7 +8,11 @@ describe :marshal_load, shared: true do
it "raises an ArgumentError when the dumped data is truncated" do
obj = {first: 1, second: 2, third: 3}
- -> { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should.raise(ArgumentError, "marshal data too short")
+ end
+
+ it "raises an ArgumentError when the argument is empty String" do
+ -> { Marshal.send(@method, "") }.should.raise(ArgumentError, "marshal data too short")
end
it "raises an ArgumentError when the dumped class is missing" do
@@ -16,140 +20,165 @@ describe :marshal_load, shared: true do
kaboom = Marshal.dump(KaBoom.new)
Object.send(:remove_const, :KaBoom)
- -> { Marshal.send(@method, kaboom) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, kaboom) }.should.raise(ArgumentError)
end
- ruby_version_is "3.1" do
- describe "when called with freeze: true" do
- it "returns frozen strings" do
- string = Marshal.send(@method, Marshal.dump("foo"), freeze: true)
- string.should == "foo"
- string.should.frozen?
+ describe "when called with freeze: true" do
+ it "returns frozen strings" do
+ string = Marshal.send(@method, Marshal.dump("foo"), freeze: true)
+ string.should == "foo"
+ string.should.frozen?
- utf8_string = "foo".encode(Encoding::UTF_8)
- string = Marshal.send(@method, Marshal.dump(utf8_string), freeze: true)
- string.should == utf8_string
- string.should.frozen?
- end
+ utf8_string = "foo".encode(Encoding::UTF_8)
+ string = Marshal.send(@method, Marshal.dump(utf8_string), freeze: true)
+ string.should == utf8_string
+ string.should.frozen?
+ end
+
+ it "returns frozen arrays" do
+ array = Marshal.send(@method, Marshal.dump([1, 2, 3]), freeze: true)
+ array.should == [1, 2, 3]
+ array.should.frozen?
+ end
+
+ it "returns frozen hashes" do
+ hash = Marshal.send(@method, Marshal.dump({foo: 42}), freeze: true)
+ hash.should == {foo: 42}
+ hash.should.frozen?
+ end
+
+ it "returns frozen regexps" do
+ regexp = Marshal.send(@method, Marshal.dump(/foo/), freeze: true)
+ regexp.should == /foo/
+ regexp.should.frozen?
+ end
- it "returns frozen arrays" do
- array = Marshal.send(@method, Marshal.dump([1, 2, 3]), freeze: true)
- array.should == [1, 2, 3]
- array.should.frozen?
+ it "returns frozen structs" do
+ struct = Marshal.send(@method, Marshal.dump(MarshalSpec::StructToDump.new(1, 2)), freeze: true)
+ struct.should == MarshalSpec::StructToDump.new(1, 2)
+ struct.should.frozen?
+ end
+
+ it "returns frozen objects" do
+ source_object = Object.new
+
+ object = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ object.should.frozen?
+ end
+
+ describe "deep freezing" do
+ it "returns hashes with frozen keys and values" do
+ key = Object.new
+ value = Object.new
+ source_object = {key => value}
+
+ hash = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ hash.size.should == 1
+ hash.keys[0].should.frozen?
+ hash.values[0].should.frozen?
end
- it "returns frozen hashes" do
- hash = Marshal.send(@method, Marshal.dump({foo: 42}), freeze: true)
- hash.should == {foo: 42}
- hash.should.frozen?
+ it "returns arrays with frozen elements" do
+ object = Object.new
+ source_object = [object]
+
+ array = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ array.size.should == 1
+ array[0].should.frozen?
end
- it "returns frozen regexps" do
- regexp = Marshal.send(@method, Marshal.dump(/foo/), freeze: true)
- regexp.should == /foo/
- regexp.should.frozen?
+ it "returns structs with frozen members" do
+ object1 = Object.new
+ object2 = Object.new
+ source_object = MarshalSpec::StructToDump.new(object1, object2)
+
+ struct = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ struct.a.should.frozen?
+ struct.b.should.frozen?
end
- it "returns frozen objects" do
+ it "returns objects with frozen instance variables" do
source_object = Object.new
- source_object.instance_variable_set(:@foo, "bar")
+ instance_variable = Object.new
+ source_object.instance_variable_set(:@a, instance_variable)
object = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
- object.should.frozen?
- object.instance_variable_get(:@foo).should.frozen?
+ object.instance_variable_get(:@a).should != nil
+ object.instance_variable_get(:@a).should.frozen?
end
- it "does not freeze modules" do
- Marshal.send(@method, Marshal.dump(Kernel), freeze: true)
- Kernel.should_not.frozen?
- end
+ it "deduplicates frozen strings" do
+ source_object = ["foo" + "bar", "foobar"]
+ object = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
- it "does not freeze classes" do
- Marshal.send(@method, Marshal.dump(Object), freeze: true)
- Object.should_not.frozen?
+ object[0].should.equal?(object[1])
end
+ end
- describe "when called with a proc" do
- it "call the proc with frozen objects" do
- arr = []
- s = 'hi'
- s.instance_variable_set(:@foo, 5)
- st = Struct.new("Brittle", :a).new
- st.instance_variable_set(:@clue, 'none')
- st.a = 0.0
- h = Hash.new('def')
- h['nine'] = 9
- a = [:a, :b, :c]
- a.instance_variable_set(:@two, 2)
- obj = [s, 10, s, s, st, a]
- obj.instance_variable_set(:@zoo, 'ant')
- proc = Proc.new { |o| arr << o; o}
-
- Marshal.send(
- @method,
- "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F",
- proc,
- freeze: true,
- )
-
- arr.should == [
- false, 5, "hi", 10, "hi", "hi", 0.0, false, "none", st,
- :b, :c, 2, a, false, "ant", ["hi", 10, "hi", "hi", st, [:a, :b, :c]],
- ]
-
- arr.each do |v|
- v.should.frozen?
- end
-
- Struct.send(:remove_const, :Brittle)
- end
+ it "does not freeze modules" do
+ object = Marshal.send(@method, Marshal.dump(Kernel), freeze: true)
+ object.should_not.frozen?
+ Kernel.should_not.frozen?
+ end
- it "does not freeze the object returned by the proc" do
- string = Marshal.send(@method, Marshal.dump("foo"), proc { |o| o.upcase }, freeze: true)
- string.should == "FOO"
- string.should_not.frozen?
- end
- end
+ it "does not freeze classes" do
+ object = Marshal.send(@method, Marshal.dump(Object), freeze: true)
+ object.should_not.frozen?
+ Object.should_not.frozen?
end
- end
- describe "when called with a proc" do
- ruby_bug "#18141", ""..."3.1" do
- it "call the proc with fully initialized strings" do
- utf8_string = "foo".encode(Encoding::UTF_8)
- Marshal.send(@method, Marshal.dump(utf8_string), proc { |arg|
- if arg.is_a?(String)
- arg.should == utf8_string
- arg.encoding.should == Encoding::UTF_8
- end
- arg
- })
- end
+ it "does freeze extended objects" do
+ object = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x00", freeze: true)
+ object.should.frozen?
+ end
- it "no longer mutate the object after it was passed to the proc" do
- string = Marshal.load(Marshal.dump("foo"), :freeze.to_proc)
- string.should.frozen?
- end
+ it "does freeze extended objects with instance variables" do
+ object = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x06:\n@ivarT", freeze: true)
+ object.should.frozen?
end
- it "returns the value of the proc" do
- Marshal.send(@method, Marshal.dump([1,2]), proc { [3,4] }).should == [3,4]
+ it "returns frozen object having #_dump method" do
+ object = Marshal.send(@method, Marshal.dump(UserDefined.new), freeze: true)
+ object.should.frozen?
end
- ruby_bug "#18141", ""..."3.1" do
- it "calls the proc for recursively visited data" do
- a = [1]
- a << a
- ret = []
- Marshal.send(@method, Marshal.dump(a), proc { |arg| ret << arg.inspect; arg })
- ret[0].should == 1.inspect
- ret[1].should == a.inspect
- ret.size.should == 2
- end
+ it "returns frozen object responding to #marshal_dump and #marshal_load" do
+ object = Marshal.send(@method, Marshal.dump(UserMarshal.new), freeze: true)
+ object.should.frozen?
+ end
+
+ it "returns frozen object extended by a module" do
+ object = Object.new
+ object.extend(MarshalSpec::ModuleToExtendBy)
+
+ object = Marshal.send(@method, Marshal.dump(object), freeze: true)
+ object.should.frozen?
+ end
+
+ it "does not call freeze method" do
+ object = MarshalSpec::ObjectWithFreezeRaisingException.new
+ object = Marshal.send(@method, Marshal.dump(object), freeze: true)
+ object.should.frozen?
+ end
+
+ it "returns frozen object even if object does not respond to freeze method" do
+ object = MarshalSpec::ObjectWithoutFreeze.new
+ object = Marshal.send(@method, Marshal.dump(object), freeze: true)
+ object.should.frozen?
+ end
+
+ it "returns a frozen object when is an instance of String/Array/Regexp/Hash subclass and has instance variables" do
+ source_object = UserString.new
+ source_object.instance_variable_set(:@foo, "bar")
- it "loads an Array with proc" do
+ object = Marshal.send(@method, Marshal.dump(source_object), freeze: true)
+ object.should.frozen?
+ end
+
+ describe "when called with a proc" do
+ it "call the proc with frozen objects" do
arr = []
- s = 'hi'
+ s = +'hi'
s.instance_variable_set(:@foo, 5)
st = Struct.new("Brittle", :a).new
st.instance_variable_set(:@clue, 'none')
@@ -160,16 +189,94 @@ describe :marshal_load, shared: true do
a.instance_variable_set(:@two, 2)
obj = [s, 10, s, s, st, a]
obj.instance_variable_set(:@zoo, 'ant')
- proc = Proc.new { |o| arr << o.dup; o}
+ proc = Proc.new { |o| arr << o; o}
- Marshal.send(@method, "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", proc)
+ Marshal.send(
+ @method,
+ "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F",
+ proc,
+ freeze: true,
+ )
arr.should == [
false, 5, "hi", 10, "hi", "hi", 0.0, false, "none", st,
:b, :c, 2, a, false, "ant", ["hi", 10, "hi", "hi", st, [:a, :b, :c]],
]
+
+ arr.each do |v|
+ v.should.frozen?
+ end
+
Struct.send(:remove_const, :Brittle)
end
+
+ it "does not freeze the object returned by the proc" do
+ string = Marshal.send(@method, Marshal.dump("foo"), proc { |o| o.upcase }, freeze: true)
+ string.should == "FOO"
+ string.should_not.frozen?
+ end
+ end
+ end
+
+ describe "when called with a proc" do
+ it "call the proc with fully initialized strings" do
+ utf8_string = "foo".encode(Encoding::UTF_8)
+ Marshal.send(@method, Marshal.dump(utf8_string), proc { |arg|
+ if arg.is_a?(String)
+ arg.should == utf8_string
+ arg.encoding.should == Encoding::UTF_8
+ end
+ arg
+ })
+ end
+
+ it "no longer mutate the object after it was passed to the proc" do
+ string = Marshal.load(Marshal.dump("foo"), :freeze.to_proc)
+ string.should.frozen?
+ end
+
+ it "call the proc with extended objects" do
+ objs = []
+ obj = Marshal.load("\x04\be:\x0FEnumerableo:\vObject\x00", Proc.new { |o| objs << o; o })
+ objs.should == [obj]
+ end
+
+ it "returns the value of the proc" do
+ Marshal.send(@method, Marshal.dump([1,2]), proc { [3,4] }).should == [3,4]
+ end
+
+ it "calls the proc for recursively visited data" do
+ a = [1]
+ a << a
+ ret = []
+ Marshal.send(@method, Marshal.dump(a), proc { |arg| ret << arg.inspect; arg })
+ ret[0].should == 1.inspect
+ ret[1].should == a.inspect
+ ret.size.should == 2
+ end
+
+ it "loads an Array with proc" do
+ arr = []
+ s = +'hi'
+ s.instance_variable_set(:@foo, 5)
+ st = Struct.new("Brittle", :a).new
+ st.instance_variable_set(:@clue, 'none')
+ st.a = 0.0
+ h = Hash.new('def')
+ h['nine'] = 9
+ a = [:a, :b, :c]
+ a.instance_variable_set(:@two, 2)
+ obj = [s, 10, s, s, st, a]
+ obj.instance_variable_set(:@zoo, 'ant')
+ proc = Proc.new { |o| arr << o.dup; o}
+
+ Marshal.send(@method, "\x04\bI[\vI\"\ahi\a:\x06EF:\t@fooi\ni\x0F@\x06@\x06IS:\x14Struct::Brittle\x06:\x06af\x060\x06:\n@clueI\"\tnone\x06;\x00FI[\b;\b:\x06b:\x06c\x06:\t@twoi\a\x06:\t@zooI\"\bant\x06;\x00F", proc)
+
+ arr.should == [
+ false, 5, "hi", 10, "hi", "hi", 0.0, false, "none", st,
+ :b, :c, 2, a, false, "ant", ["hi", 10, "hi", "hi", st, [:a, :b, :c]],
+ ]
+ Struct.send(:remove_const, :Brittle)
end
end
@@ -190,12 +297,24 @@ describe :marshal_load, shared: true do
UserPreviouslyDefinedWithInitializedIvar.should_receive(:_load).and_return(UserPreviouslyDefinedWithInitializedIvar.new)
marshaled_obj = Marshal.send(@method, dump_str)
- marshaled_obj.should be_an_instance_of(UserPreviouslyDefinedWithInitializedIvar)
- marshaled_obj.field1.should be_nil
- marshaled_obj.field2.should be_nil
+ marshaled_obj.should.instance_of?(UserPreviouslyDefinedWithInitializedIvar)
+ marshaled_obj.field1.should == nil
+ marshaled_obj.field2.should == nil
end
- describe "that return an immediate value" do
+ it "loads the String in non US-ASCII and non UTF-8 encoding" do
+ source_object = UserDefinedString.new("a".encode("windows-1251"))
+ object = Marshal.send(@method, Marshal.dump(source_object))
+ object.string.should == "a".encode("windows-1251")
+ end
+
+ it "loads the String in multibyte encoding" do
+ source_object = UserDefinedString.new("a".encode("utf-32le"))
+ object = Marshal.send(@method, Marshal.dump(source_object))
+ object.string.should == "a".encode("utf-32le")
+ end
+
+ describe "that returns an immediate value" do
it "loads an array containing an instance of the object, followed by multiple instances of another object" do
str = "string"
@@ -233,39 +352,37 @@ describe :marshal_load, shared: true do
end
end
- ruby_bug "#18141", ""..."3.1" do
- it "loads an array containing objects having _dump method, and with proc" do
- arr = []
- myproc = Proc.new { |o| arr << o.dup; o }
- o1 = UserDefined.new;
- o2 = UserDefinedWithIvar.new
- obj = [o1, o2, o1, o2]
+ it "loads an array containing objects having _dump method, and with proc" do
+ arr = []
+ myproc = Proc.new { |o| arr << o.dup; o }
+ o1 = UserDefined.new;
+ o2 = UserDefinedWithIvar.new
+ obj = [o1, o2, o1, o2]
- Marshal.send(@method, "\x04\b[\tu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06u:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a@\x06@\a", myproc)
+ Marshal.send(@method, "\x04\b[\tu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06u:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a@\x06@\a", myproc)
- arr[0].should == o1
- arr[1].should == o2
- arr[2].should == obj
- arr.size.should == 3
- end
+ arr[0].should == o1
+ arr[1].should == o2
+ arr[2].should == obj
+ arr.size.should == 3
+ end
- it "loads an array containing objects having marshal_dump method, and with proc" do
- arr = []
- proc = Proc.new { |o| arr << o.dup; o }
- o1 = UserMarshal.new
- o2 = UserMarshalWithIvar.new
+ it "loads an array containing objects having marshal_dump method, and with proc" do
+ arr = []
+ proc = Proc.new { |o| arr << o.dup; o }
+ o1 = UserMarshal.new
+ o2 = UserMarshalWithIvar.new
- Marshal.send(@method, "\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc)
+ Marshal.send(@method, "\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc)
- arr[0].should == 'stuff'
- arr[1].should == o1
- arr[2].should == 'my data'
- arr[3].should == ['my data']
- arr[4].should == o2
- arr[5].should == [o1, o2, o1, o2]
+ arr[0].should == 'stuff'
+ arr[1].should == o1
+ arr[2].should == 'my data'
+ arr[3].should == ['my data']
+ arr[4].should == o2
+ arr[5].should == [o1, o2, o1, o2]
- arr.size.should == 6
- end
+ arr.size.should == 6
end
it "assigns classes to nested subclasses of Array correctly" do
@@ -282,24 +399,24 @@ describe :marshal_load, shared: true do
end
it "raises a TypeError with bad Marshal version" do
- marshal_data = '\xff\xff'
+ marshal_data = +'\xff\xff'
marshal_data[0] = (Marshal::MAJOR_VERSION).chr
marshal_data[1] = (Marshal::MINOR_VERSION + 1).chr
- -> { Marshal.send(@method, marshal_data) }.should raise_error(TypeError)
+ -> { Marshal.send(@method, marshal_data) }.should.raise(TypeError)
- marshal_data = '\xff\xff'
+ marshal_data = +'\xff\xff'
marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
marshal_data[1] = (Marshal::MINOR_VERSION).chr
- -> { Marshal.send(@method, marshal_data) }.should raise_error(TypeError)
+ -> { Marshal.send(@method, marshal_data) }.should.raise(TypeError)
end
it "raises EOFError on loading an empty file" do
temp_file = tmp("marshal.rubyspec.tmp.#{Process.pid}")
file = File.new(temp_file, "w+")
begin
- -> { Marshal.send(@method, file) }.should raise_error(EOFError)
+ -> { Marshal.send(@method, file) }.should.raise(EOFError)
ensure
file.close
rm_r temp_file
@@ -339,13 +456,13 @@ describe :marshal_load, shared: true do
end
it "loads an array having ivar" do
- s = 'well'
+ s = +'well'
s.instance_variable_set(:@foo, 10)
obj = ['5', s, 'hi'].extend(Meths, MethsMore)
obj.instance_variable_set(:@mix, s)
new_obj = Marshal.send(@method, "\004\bI[\b\"\0065I\"\twell\006:\t@fooi\017\"\ahi\006:\t@mix@\a")
new_obj.should == obj
- new_obj.instance_variable_get(:@mix).should equal new_obj[1]
+ new_obj.instance_variable_get(:@mix).should.equal? new_obj[1]
new_obj[1].instance_variable_get(:@foo).should == 10
end
@@ -385,7 +502,7 @@ describe :marshal_load, shared: true do
end
it "preserves hash ivars when hash contains a string having ivar" do
- s = 'string'
+ s = +'string'
s.instance_variable_set :@string_ivar, 'string ivar'
h = { key: s }
h.instance_variable_set :@hash_ivar, 'hash ivar'
@@ -394,6 +511,36 @@ describe :marshal_load, shared: true do
unmarshalled.instance_variable_get(:@hash_ivar).should == 'hash ivar'
unmarshalled[:key].instance_variable_get(:@string_ivar).should == 'string ivar'
end
+
+ it "preserves compare_by_identity behaviour" do
+ h = { a: 1 }
+ h.compare_by_identity
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should.compare_by_identity?
+
+ h = { a: 1 }
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should_not.compare_by_identity?
+ end
+
+ it "preserves compare_by_identity behaviour for a Hash subclass" do
+ h = UserHash.new({ a: 1 })
+ h.compare_by_identity
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should.compare_by_identity?
+
+ h = UserHash.new({ a: 1 })
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should_not.compare_by_identity?
+ end
+
+ it "allocates an instance of the proper class when Hash subclass with compare_by_identity behaviour" do
+ h = UserHash.new({ a: 1 })
+ h.compare_by_identity
+
+ unmarshalled = Marshal.send(@method, Marshal.dump(h))
+ unmarshalled.should.kind_of?(UserHash)
+ end
end
describe "for a Symbol" do
@@ -437,7 +584,7 @@ describe :marshal_load, shared: true do
end
it "loads a binary encoded Symbol" do
- s = "\u2192".force_encoding("binary").to_sym
+ s = "\u2192".dup.force_encoding("binary").to_sym
sym = Marshal.send(@method, "\x04\b:\b\xE2\x86\x92")
sym.should == s
sym.encoding.should == Encoding::BINARY
@@ -451,8 +598,8 @@ describe :marshal_load, shared: true do
value = Marshal.send(@method, dump)
value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8]
expected = [
- "€a".force_encoding(Encoding::UTF_8).to_sym,
- "€b".force_encoding(Encoding::UTF_8).to_sym
+ "€a".dup.force_encoding(Encoding::UTF_8).to_sym,
+ "€b".dup.force_encoding(Encoding::UTF_8).to_sym
]
value.should == expected
@@ -460,11 +607,19 @@ describe :marshal_load, shared: true do
value.map(&:encoding).should == [Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8]
value.should == [*expected, expected[0]]
end
+
+ it "raises ArgumentError when end of byte sequence reached before symbol characters end" do
+ Marshal.dump(:hello).should == "\x04\b:\nhello"
+
+ -> {
+ Marshal.send(@method, "\x04\b:\nhel")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
describe "for a String" do
it "loads a string having ivar with ref to self" do
- obj = 'hi'
+ obj = +'hi'
obj.instance_variable_set(:@self, obj)
Marshal.send(@method, "\004\bI\"\ahi\006:\n@self@\000").should == obj
end
@@ -475,6 +630,12 @@ describe :marshal_load, shared: true do
Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj
end
+ it "sets binmode if it is loading through StringIO stream" do
+ io = StringIO.new("\004\b:\vsymbol")
+ def io.binmode; raise "binmode"; end
+ -> { Marshal.load(io) }.should.raise(RuntimeError, "binmode")
+ end
+
it "loads a string with an ivar" do
str = Marshal.send(@method, "\x04\bI\"\x00\x06:\t@fooI\"\bbar\x06:\x06EF")
str.instance_variable_get("@foo").should == "bar"
@@ -482,40 +643,48 @@ describe :marshal_load, shared: true do
it "loads a String subclass with custom constructor" do
str = Marshal.send(@method, "\x04\bC: UserCustomConstructorString\"\x00")
- str.should be_an_instance_of(UserCustomConstructorString)
+ str.should.instance_of?(UserCustomConstructorString)
end
it "loads a US-ASCII String" do
- str = "abc".force_encoding("us-ascii")
+ str = "abc".dup.force_encoding("us-ascii")
data = "\x04\bI\"\babc\x06:\x06EF"
result = Marshal.send(@method, data)
result.should == str
- result.encoding.should equal(Encoding::US_ASCII)
+ result.encoding.should.equal?(Encoding::US_ASCII)
end
it "loads a UTF-8 String" do
- str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8")
+ str = "\x6d\xc3\xb6\x68\x72\x65".dup.force_encoding("utf-8")
data = "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET"
result = Marshal.send(@method, data)
result.should == str
- result.encoding.should equal(Encoding::UTF_8)
+ result.encoding.should.equal?(Encoding::UTF_8)
end
it "loads a String in another encoding" do
- str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le")
+ str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".dup.force_encoding("utf-16le")
data = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE"
result = Marshal.send(@method, data)
result.should == str
- result.encoding.should equal(Encoding::UTF_16LE)
+ result.encoding.should.equal?(Encoding::UTF_16LE)
end
it "loads a String as BINARY if no encoding is specified at the end" do
- str = "\xC3\xB8".force_encoding("BINARY")
- data = "\x04\b\"\a\xC3\xB8".force_encoding("UTF-8")
+ str = "\xC3\xB8".dup.force_encoding("BINARY")
+ data = "\x04\b\"\a\xC3\xB8".dup.force_encoding("UTF-8")
result = Marshal.send(@method, data)
result.encoding.should == Encoding::BINARY
result.should == str
end
+
+ it "raises ArgumentError when end of byte sequence reached before string characters end" do
+ Marshal.dump("hello").should == "\x04\b\"\nhello"
+
+ -> {
+ Marshal.send(@method, "\x04\b\"\nhel")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
describe "for a Struct" do
@@ -564,6 +733,32 @@ describe :marshal_load, shared: true do
end
end
+ describe "for a Data" do
+ it "loads a Data" do
+ obj = MarshalSpec::DataSpec::Measure.new(100, 'km')
+ dumped = "\x04\bS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm"
+ Marshal.dump(obj).should == dumped
+
+ Marshal.send(@method, dumped).should == obj
+ end
+
+ it "loads an extended Data" do
+ obj = MarshalSpec::DataSpec::MeasureExtended.new(100, "km")
+ dumped = "\x04\bS:+MarshalSpec::DataSpec::MeasureExtended\a:\vamountii:\tunit\"\akm"
+ Marshal.dump(obj).should == dumped
+
+ Marshal.send(@method, dumped).should == obj
+ end
+
+ it "returns a frozen object" do
+ obj = MarshalSpec::DataSpec::Measure.new(100, 'km')
+ dumped = "\x04\bS:#MarshalSpec::DataSpec::Measure\a:\vamountii:\tunit\"\akm"
+ Marshal.dump(obj).should == dumped
+
+ Marshal.send(@method, dumped).should.frozen?
+ end
+ end
+
describe "for an Exception" do
it "loads a marshalled exception with no message" do
obj = Exception.new
@@ -612,7 +807,7 @@ describe :marshal_load, shared: true do
describe "for an Object" do
it "loads an object" do
- Marshal.send(@method, "\004\bo:\vObject\000").should be_kind_of(Object)
+ Marshal.send(@method, "\004\bo:\vObject\000").should.is_a?(Object)
end
it "loads an extended Object" do
@@ -638,7 +833,7 @@ describe :marshal_load, shared: true do
end
it "loads an Object with a non-US-ASCII instance variable" do
- ivar = "@é".force_encoding(Encoding::UTF_8).to_sym
+ ivar = "@é".dup.force_encoding(Encoding::UTF_8).to_sym
obj = Marshal.send(@method, "\x04\bo:\vObject\x06I:\b@\xC3\xA9\x06:\x06ETi\x06")
obj.instance_variables.should == [ivar]
obj.instance_variables[0].encoding.should == Encoding::UTF_8
@@ -648,7 +843,15 @@ describe :marshal_load, shared: true do
it "raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class" do
-> do
Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd")
- end.should raise_error(ArgumentError)
+ end.should.raise(ArgumentError)
+ end
+
+ it "raises ArgumentError when end of byte sequence reached before class name end" do
+ Marshal.dump(Object.new).should == "\x04\bo:\vObject\x00"
+
+ -> {
+ Marshal.send(@method, "\x04\bo:\vObj")
+ }.should.raise(ArgumentError, "marshal data too short")
end
end
@@ -676,7 +879,7 @@ describe :marshal_load, shared: true do
end
it "loads a UserObject" do
- Marshal.send(@method, "\004\bo:\017UserObject\000").should be_kind_of(UserObject)
+ Marshal.send(@method, "\004\bo:\017UserObject\000").should.is_a?(UserObject)
end
describe "that extends a core type other than Object or BasicObject" do
@@ -689,26 +892,64 @@ describe :marshal_load, shared: true do
data = Marshal.dump(MarshalSpec::SwappedClass.new)
MarshalSpec.set_swapped_class(Class.new(Array))
- -> { Marshal.send(@method, data) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, data) }.should.raise(ArgumentError)
MarshalSpec.set_swapped_class(Class.new)
- -> { Marshal.send(@method, data) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, data) }.should.raise(ArgumentError)
end
end
end
describe "for a Regexp" do
- it "loads an extended Regexp" do
- obj = /[a-z]/.dup.extend(Meths, MethsMore)
- new_obj = Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000")
+ ruby_version_is "4.1" do
+ it "raises FrozenError for an extended Regexp" do
+ -> {
+ Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000")
+ }.should.raise(FrozenError)
+ end
+
+ it "raises FrozenError when regexp has instance variables" do
+ -> {
+ Marshal.send(@method, "\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/")
+ }.should.raise(FrozenError)
+ end
+ end
+
+ ruby_version_is ""..."4.1" do
+ it "loads an extended Regexp" do
+ obj = /[a-z]/.dup.extend(Meths, MethsMore)
+ new_obj = Marshal.send(@method, "\004\be:\nMethse:\016MethsMore/\n[a-z]\000")
+
+ new_obj.should == obj
+ new_obj_metaclass_ancestors = class << new_obj; ancestors; end
+ new_obj_metaclass_ancestors[@num_self_class, 3].should ==
+ [Meths, MethsMore, Regexp]
+ end
+
+ it "restore the regexp instance variables" do
+ obj = Regexp.new("hello")
+ obj.instance_variable_set(:@regexp_ivar, [42])
+
+ new_obj = Marshal.send(@method, "\x04\bI/\nhello\x00\a:\x06EF:\x11@regexp_ivar[\x06i/")
+ new_obj.instance_variables.should == [:@regexp_ivar]
+ new_obj.instance_variable_get(:@regexp_ivar).should == [42]
+ end
+ end
+
+ it "loads a Regexp subclass instance variables" do
+ obj = UserRegexp.new('abc')
+ obj.instance_variable_set(:@noise, 'much')
+
+ new_obj = Marshal.send(@method, Marshal.dump(obj))
new_obj.should == obj
+ new_obj.instance_variable_get(:@noise).should == 'much'
new_obj_metaclass_ancestors = class << new_obj; ancestors; end
- new_obj_metaclass_ancestors[@num_self_class, 3].should ==
- [Meths, MethsMore, Regexp]
+ new_obj_metaclass_ancestors[@num_self_class, 2].should ==
+ [UserRegexp, Regexp]
end
- it "loads a extended_user_regexp having ivar" do
+ it "loads a Regexp subclass instance variables when it is extended with a module" do
obj = UserRegexp.new('').extend(Meths)
obj.instance_variable_set(:@noise, 'much')
@@ -720,6 +961,22 @@ describe :marshal_load, shared: true do
new_obj_metaclass_ancestors[@num_self_class, 3].should ==
[Meths, UserRegexp, Regexp]
end
+
+ it "preserves Regexp encoding" do
+ source_object = Regexp.new("a".encode("utf-32le"))
+ regexp = Marshal.send(@method, Marshal.dump(source_object))
+
+ regexp.encoding.should == Encoding::UTF_32LE
+ regexp.source.should == "a".encode("utf-32le")
+ end
+
+ it "raises ArgumentError when end of byte sequence reached before source string end" do
+ Marshal.dump(/hello world/).should == "\x04\bI/\x10hello world\x00\x06:\x06EF"
+
+ -> {
+ Marshal.send(@method, "\x04\bI/\x10hel")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
describe "for a Float" do
@@ -741,6 +998,14 @@ describe :marshal_load, shared: true do
obj = 1.1867345e+22
Marshal.send(@method, "\004\bf\0361.1867344999999999e+22\000\344@").should == obj
end
+
+ it "raises ArgumentError when end of byte sequence reached before float string representation end" do
+ Marshal.dump(1.3).should == "\x04\bf\b1.3"
+
+ -> {
+ Marshal.send(@method, "\004\bf\v1")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
describe "for an Integer" do
@@ -793,7 +1058,7 @@ describe :marshal_load, shared: true do
"\004\bi\004\0",
"\004\bi\004\0\0",
"\004\bi\004\0\0\0"].each do |invalid|
- -> { Marshal.send(@method, invalid) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, invalid) }.should.raise(ArgumentError)
end
end
@@ -806,18 +1071,22 @@ describe :marshal_load, shared: true do
describe "for a Rational" do
it "loads" do
- Marshal.send(@method, Marshal.dump(Rational(1, 3))).should == Rational(1, 3)
+ r = Marshal.send(@method, Marshal.dump(Rational(1, 3)))
+ r.should == Rational(1, 3)
+ r.should.frozen?
end
end
describe "for a Complex" do
it "loads" do
- Marshal.send(@method, Marshal.dump(Complex(4, 3))).should == Complex(4, 3)
+ c = Marshal.send(@method, Marshal.dump(Complex(4, 3)))
+ c.should == Complex(4, 3)
+ c.should.frozen?
end
end
describe "for a Bignum" do
- platform_is wordsize: 64 do
+ platform_is c_long_size: 64 do
context "that is Bignum on 32-bit platforms but Fixnum on 64-bit" do
it "dumps a Fixnum" do
val = Marshal.send(@method, "\004\bl+\ab:wU")
@@ -850,37 +1119,67 @@ describe :marshal_load, shared: true do
t = Time.new
t1, t2 = Marshal.send(@method, Marshal.dump([t, t]))
- t1.should equal t2
+ t1.should.equal? t2
end
- it "loads the zone" do
+ it "keeps the local zone" do
with_timezone 'AST', 3 do
t = Time.local(2012, 1, 1)
Marshal.send(@method, Marshal.dump(t)).zone.should == t.zone
end
end
- it "loads nanoseconds" do
+ it "keeps UTC zone" do
+ t = Time.now.utc
+ t2 = Marshal.send(@method, Marshal.dump(t))
+ t2.should.utc?
+ end
+
+ it "keeps the zone" do
+ t = nil
+
+ with_timezone 'AST', 4 do
+ t = Time.local(2012, 1, 1)
+ end
+
+ with_timezone 'EET', -2 do
+ Marshal.send(@method, Marshal.dump(t)).zone.should == 'AST'
+ end
+ end
+
+ it "keeps utc offset" do
+ t = Time.new(2007,11,1,15,25,0, "+09:00")
+ t2 = Marshal.send(@method, Marshal.dump(t))
+ t2.utc_offset.should == 32400
+ end
+
+ it "keeps nanoseconds" do
t = Time.now
Marshal.send(@method, Marshal.dump(t)).nsec.should == t.nsec
end
+
+ it "does not add any additional instance variable" do
+ t = Time.now
+ t2 = Marshal.send(@method, Marshal.dump(t))
+ t2.instance_variables.should.empty?
+ end
end
describe "for nil" do
it "loads" do
- Marshal.send(@method, "\x04\b0").should be_nil
+ Marshal.send(@method, "\x04\b0").should == nil
end
end
describe "for true" do
it "loads" do
- Marshal.send(@method, "\x04\bT").should be_true
+ Marshal.send(@method, "\x04\bT").should == true
end
end
describe "for false" do
it "loads" do
- Marshal.send(@method, "\x04\bF").should be_false
+ Marshal.send(@method, "\x04\bF").should == false
end
end
@@ -890,11 +1189,19 @@ describe :marshal_load, shared: true do
end
it "raises ArgumentError if given the name of a non-Module" do
- -> { Marshal.send(@method, "\x04\bc\vKernel") }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, "\x04\bc\vKernel") }.should.raise(ArgumentError)
end
it "raises ArgumentError if given a nonexistent class" do
- -> { Marshal.send(@method, "\x04\bc\vStrung") }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, "\x04\bc\vStrung") }.should.raise(ArgumentError)
+ end
+
+ it "raises ArgumentError when end of byte sequence reached before class name end" do
+ Marshal.dump(String).should == "\x04\bc\vString"
+
+ -> {
+ Marshal.send(@method, "\x04\bc\vStr")
+ }.should.raise(ArgumentError, "marshal data too short")
end
end
@@ -904,12 +1211,20 @@ describe :marshal_load, shared: true do
end
it "raises ArgumentError if given the name of a non-Class" do
- -> { Marshal.send(@method, "\x04\bm\vString") }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, "\x04\bm\vString") }.should.raise(ArgumentError)
end
it "loads an old module" do
Marshal.send(@method, "\x04\bM\vKernel").should == Kernel
end
+
+ it "raises ArgumentError when end of byte sequence reached before module name end" do
+ Marshal.dump(Kernel).should == "\x04\bm\vKernel"
+
+ -> {
+ Marshal.send(@method, "\x04\bm\vKer")
+ }.should.raise(ArgumentError, "marshal data too short")
+ end
end
describe "for a wrapped C pointer" do
@@ -943,13 +1258,13 @@ describe :marshal_load, shared: true do
data = "\x04\bd:\x1AUnloadableDumpableDirI\"\x06.\x06:\x06ET"
- -> { Marshal.send(@method, data) }.should raise_error(TypeError)
+ -> { Marshal.send(@method, data) }.should.raise(TypeError)
end
it "raises ArgumentError when the local class is a regular object" do
data = "\004\bd:\020UserDefined\0"
- -> { Marshal.send(@method, data) }.should raise_error(ArgumentError)
+ -> { Marshal.send(@method, data) }.should.raise(ArgumentError)
end
end
@@ -962,7 +1277,7 @@ describe :marshal_load, shared: true do
it "raises an ArgumentError" do
message = "undefined class/module NamespaceTest::SameName"
- -> { Marshal.send(@method, @data) }.should raise_error(ArgumentError, message)
+ -> { Marshal.send(@method, @data) }.should.raise(ArgumentError, message)
end
end
@@ -971,6 +1286,6 @@ describe :marshal_load, shared: true do
@data = Marshal.dump(NamespaceTest::KaBoom.new)
NamespaceTest.send(:remove_const, :KaBoom)
- -> { Marshal.send(@method, @data) }.should raise_error(ArgumentError, /NamespaceTest::KaBoom/)
+ -> { Marshal.send(@method, @data) }.should.raise(ArgumentError, /NamespaceTest::KaBoom/)
end
end