mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Move spec/rubyspec to spec/ruby for consistency
* Other ruby implementations use the spec/ruby directory. [Misc #13792] [ruby-core:82287] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59979 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
75bfc6440d
commit
1d15d5f080
4370 changed files with 0 additions and 0 deletions
582
spec/ruby/core/marshal/dump_spec.rb
Normal file
582
spec/ruby/core/marshal/dump_spec.rb
Normal file
|
@ -0,0 +1,582 @@
|
|||
# -*- encoding: binary -*-
|
||||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../fixtures/marshal_data', __FILE__)
|
||||
|
||||
describe "Marshal.dump" do
|
||||
it "dumps nil" do
|
||||
Marshal.dump(nil).should == "\004\b0"
|
||||
end
|
||||
|
||||
it "dumps true" do
|
||||
Marshal.dump(true).should == "\004\bT"
|
||||
end
|
||||
|
||||
it "dumps false" do
|
||||
Marshal.dump(false).should == "\004\bF"
|
||||
end
|
||||
|
||||
describe "with a Fixnum" do
|
||||
it "dumps a Fixnum" do
|
||||
[ [Marshal, 0, "\004\bi\000"],
|
||||
[Marshal, 5, "\004\bi\n"],
|
||||
[Marshal, 8, "\004\bi\r"],
|
||||
[Marshal, 122, "\004\bi\177"],
|
||||
[Marshal, 123, "\004\bi\001{"],
|
||||
[Marshal, 1234, "\004\bi\002\322\004"],
|
||||
[Marshal, -8, "\004\bi\363"],
|
||||
[Marshal, -123, "\004\bi\200"],
|
||||
[Marshal, -124, "\004\bi\377\204"],
|
||||
[Marshal, -1234, "\004\bi\376.\373"],
|
||||
[Marshal, -4516727, "\004\bi\375\211\024\273"],
|
||||
[Marshal, 2**8, "\004\bi\002\000\001"],
|
||||
[Marshal, 2**16, "\004\bi\003\000\000\001"],
|
||||
[Marshal, 2**24, "\004\bi\004\000\000\000\001"],
|
||||
[Marshal, -2**8, "\004\bi\377\000"],
|
||||
[Marshal, -2**16, "\004\bi\376\000\000"],
|
||||
[Marshal, -2**24, "\004\bi\375\000\000\000"],
|
||||
].should be_computed_by(:dump)
|
||||
end
|
||||
|
||||
platform_is wordsize: 64 do
|
||||
it "dumps a positive Fixnum > 31 bits as a Bignum" do
|
||||
Marshal.dump(2**31 + 1).should == "\x04\bl+\a\x01\x00\x00\x80"
|
||||
end
|
||||
|
||||
it "dumps a negative Fixnum > 31 bits as a Bignum" do
|
||||
Marshal.dump(-2**31 - 1).should == "\x04\bl-\a\x01\x00\x00\x80"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Symbol" do
|
||||
it "dumps a Symbol" do
|
||||
Marshal.dump(:symbol).should == "\004\b:\vsymbol"
|
||||
end
|
||||
|
||||
it "dumps a big Symbol" do
|
||||
Marshal.dump(('big' * 100).to_sym).should == "\004\b:\002,\001#{'big' * 100}"
|
||||
end
|
||||
|
||||
it "dumps an encoded Symbol" do
|
||||
s = "\u2192"
|
||||
[ [Marshal, s.encode("utf-8").to_sym,
|
||||
"\x04\bI:\b\xE2\x86\x92\x06:\x06ET"],
|
||||
[Marshal, s.encode("utf-16").to_sym,
|
||||
"\x04\bI:\t\xFE\xFF!\x92\x06:\rencoding\"\vUTF-16"],
|
||||
[Marshal, s.encode("euc-jp").to_sym,
|
||||
"\x04\bI:\a\xA2\xAA\x06:\rencoding\"\vEUC-JP"],
|
||||
[Marshal, s.encode("sjis").to_sym,
|
||||
"\x04\bI:\a\x81\xA8\x06:\rencoding\"\x10Windows-31J"]
|
||||
].should be_computed_by(:dump)
|
||||
end
|
||||
|
||||
it "dumps a binary encoded Symbol" do
|
||||
s = "\u2192".force_encoding("binary").to_sym
|
||||
Marshal.dump(s).should == "\x04\b:\b\xE2\x86\x92"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "dumps an extended_object" do
|
||||
Marshal.dump(Object.new.extend(Meths)).should == "\x04\be:\nMethso:\vObject\x00"
|
||||
end
|
||||
|
||||
it "dumps an object that has had an ivar added and removed as though the ivar never was set" do
|
||||
obj = Object.new
|
||||
initial = Marshal.dump(obj)
|
||||
obj.instance_variable_set(:@ivar, 1)
|
||||
Marshal.dump(obj).should == "\004\bo:\vObject\006:\n@ivari\006"
|
||||
obj.send :remove_instance_variable, :@ivar
|
||||
Marshal.dump(obj).should == initial
|
||||
end
|
||||
|
||||
describe "with an object responding to #marshal_dump" do
|
||||
it "dumps the object returned by #marshal_dump" do
|
||||
Marshal.dump(UserMarshal.new).should == "\x04\bU:\x10UserMarshal:\tdata"
|
||||
end
|
||||
|
||||
it "does not use Class#name" do
|
||||
UserMarshal.should_not_receive(:name)
|
||||
Marshal.dump(UserMarshal.new)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with an object responding to #_dump" do
|
||||
it "dumps the object returned by #marshal_dump" do
|
||||
Marshal.dump(UserDefined.new).should == "\004\bu:\020UserDefined\022\004\b[\a:\nstuff;\000"
|
||||
end
|
||||
|
||||
it "raises a TypeError if _dump returns a non-string" do
|
||||
m = mock("marshaled")
|
||||
m.should_receive(:_dump).and_return(0)
|
||||
lambda { Marshal.dump(m) }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "favors marshal_dump over _dump" do
|
||||
m = mock("marshaled")
|
||||
m.should_receive(:marshal_dump).and_return(0)
|
||||
m.should_not_receive(:_dump)
|
||||
Marshal.dump(m)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Class" do
|
||||
it "dumps a builtin Class" do
|
||||
Marshal.dump(String).should == "\004\bc\vString"
|
||||
end
|
||||
|
||||
it "dumps a user Class" do
|
||||
Marshal.dump(UserDefined).should == "\x04\bc\x10UserDefined"
|
||||
end
|
||||
|
||||
it "dumps a nested Class" do
|
||||
Marshal.dump(UserDefined::Nested).should == "\004\bc\030UserDefined::Nested"
|
||||
end
|
||||
|
||||
it "raises TypeError with an anonymous Class" do
|
||||
lambda { Marshal.dump(Class.new) }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises TypeError with a singleton Class" do
|
||||
lambda { Marshal.dump(class << self; self end) }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Module" do
|
||||
it "dumps a builtin Module" do
|
||||
Marshal.dump(Marshal).should == "\004\bm\fMarshal"
|
||||
end
|
||||
|
||||
it "raises TypeError with an anonymous Module" do
|
||||
lambda { Marshal.dump(Module.new) }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Float" do
|
||||
it "dumps a Float" do
|
||||
[ [Marshal, 0.0, "\004\bf\0060"],
|
||||
[Marshal, -0.0, "\004\bf\a-0"],
|
||||
[Marshal, 1.0, "\004\bf\0061"],
|
||||
[Marshal, 123.4567, "\004\bf\r123.4567"],
|
||||
[Marshal, -0.841, "\x04\bf\v-0.841"],
|
||||
[Marshal, -9876.345, "\x04\bf\x0E-9876.345"],
|
||||
[Marshal, infinity_value, "\004\bf\binf"],
|
||||
[Marshal, -infinity_value, "\004\bf\t-inf"],
|
||||
[Marshal, nan_value, "\004\bf\bnan"],
|
||||
].should be_computed_by(:dump)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Bignum" do
|
||||
it "dumps a Bignum" do
|
||||
[ [Marshal, -4611686018427387903, "\004\bl-\t\377\377\377\377\377\377\377?"],
|
||||
[Marshal, -2361183241434822606847, "\004\bl-\n\377\377\377\377\377\377\377\377\177\000"],
|
||||
].should be_computed_by(:dump)
|
||||
end
|
||||
|
||||
it "dumps a Bignum" do
|
||||
[ [Marshal, 2**64, "\004\bl+\n\000\000\000\000\000\000\000\000\001\000"],
|
||||
[Marshal, 2**90, "\004\bl+\v#{"\000" * 11}\004"],
|
||||
[Marshal, -2**63, "\004\bl-\t\000\000\000\000\000\000\000\200"],
|
||||
[Marshal, -2**64, "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
|
||||
].should be_computed_by(:dump)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a String" do
|
||||
it "dumps a blank String" do
|
||||
Marshal.dump("".force_encoding("binary")).should == "\004\b\"\000"
|
||||
end
|
||||
|
||||
it "dumps a short String" do
|
||||
Marshal.dump("short".force_encoding("binary")).should == "\004\b\"\012short"
|
||||
end
|
||||
|
||||
it "dumps a long String" do
|
||||
Marshal.dump(("big" * 100).force_encoding("binary")).should == "\004\b\"\002,\001#{"big" * 100}"
|
||||
end
|
||||
|
||||
it "dumps a String extended with a Module" do
|
||||
Marshal.dump("".extend(Meths).force_encoding("binary")).should == "\004\be:\nMeths\"\000"
|
||||
end
|
||||
|
||||
it "dumps a String subclass" do
|
||||
Marshal.dump(UserString.new.force_encoding("binary")).should == "\004\bC:\017UserString\"\000"
|
||||
end
|
||||
|
||||
it "dumps a String subclass extended with a Module" do
|
||||
Marshal.dump(UserString.new.extend(Meths).force_encoding("binary")).should == "\004\be:\nMethsC:\017UserString\"\000"
|
||||
end
|
||||
|
||||
it "dumps a String with instance variables" do
|
||||
str = ""
|
||||
str.instance_variable_set("@foo", "bar")
|
||||
Marshal.dump(str.force_encoding("binary")).should == "\x04\bI\"\x00\x06:\t@foo\"\bbar"
|
||||
end
|
||||
|
||||
with_feature :encoding do
|
||||
it "dumps a US-ASCII String" do
|
||||
str = "abc".force_encoding("us-ascii")
|
||||
Marshal.dump(str).should == "\x04\bI\"\babc\x06:\x06EF"
|
||||
end
|
||||
|
||||
it "dumps a UTF-8 String" do
|
||||
str = "\x6d\xc3\xb6\x68\x72\x65".force_encoding("utf-8")
|
||||
Marshal.dump(str).should == "\x04\bI\"\vm\xC3\xB6hre\x06:\x06ET"
|
||||
end
|
||||
|
||||
it "dumps a String in another encoding" do
|
||||
str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".force_encoding("utf-16le")
|
||||
result = "\x04\bI\"\x0Fm\x00\xF6\x00h\x00r\x00e\x00\x06:\rencoding\"\rUTF-16LE"
|
||||
Marshal.dump(str).should == result
|
||||
end
|
||||
|
||||
it "dumps multiple strings using symlinks for the :E (encoding) symbol" do
|
||||
Marshal.dump(["".encode("us-ascii"), "".encode("utf-8")]).should == "\x04\b[\aI\"\x00\x06:\x06EFI\"\x00\x06;\x00T"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Regexp" do
|
||||
it "dumps a Regexp" do
|
||||
Marshal.dump(/\A.\Z/).should == "\x04\bI/\n\\A.\\Z\x00\x06:\x06EF"
|
||||
end
|
||||
|
||||
it "dumps a Regexp with flags" do
|
||||
Marshal.dump(//im).should == "\x04\bI/\x00\x05\x06:\x06EF"
|
||||
end
|
||||
|
||||
it "dumps a Regexp with instance variables" do
|
||||
o = //
|
||||
o.instance_variable_set(:@ivar, :ivar)
|
||||
Marshal.dump(o).should == "\x04\bI/\x00\x00\a:\x06EF:\n@ivar:\tivar"
|
||||
end
|
||||
|
||||
it "dumps an extended Regexp" do
|
||||
Marshal.dump(//.extend(Meths)).should == "\x04\bIe:\nMeths/\x00\x00\x06:\x06EF"
|
||||
end
|
||||
|
||||
it "dumps a Regexp subclass" do
|
||||
Marshal.dump(UserRegexp.new("")).should == "\x04\bIC:\x0FUserRegexp/\x00\x00\x06:\x06EF"
|
||||
end
|
||||
|
||||
it "dumps a binary Regexp" do
|
||||
o = Regexp.new("".force_encoding("binary"), Regexp::FIXEDENCODING)
|
||||
Marshal.dump(o).should == "\x04\b/\x00\x10"
|
||||
end
|
||||
|
||||
it "dumps a UTF-8 Regexp" do
|
||||
o = Regexp.new("".force_encoding("utf-8"), Regexp::FIXEDENCODING)
|
||||
Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\x06ET"
|
||||
end
|
||||
|
||||
it "dumps a Regexp in another encoding" do
|
||||
o = Regexp.new("".force_encoding("utf-16le"), Regexp::FIXEDENCODING)
|
||||
Marshal.dump(o).should == "\x04\bI/\x00\x10\x06:\rencoding\"\rUTF-16LE"
|
||||
end
|
||||
end
|
||||
|
||||
describe "with an Array" do
|
||||
it "dumps an empty Array" do
|
||||
Marshal.dump([]).should == "\004\b[\000"
|
||||
end
|
||||
|
||||
it "dumps a non-empty Array" do
|
||||
Marshal.dump([:a, 1, 2]).should == "\004\b[\b:\006ai\006i\a"
|
||||
end
|
||||
|
||||
it "dumps an Array subclass" do
|
||||
Marshal.dump(UserArray.new).should == "\004\bC:\016UserArray[\000"
|
||||
end
|
||||
|
||||
it "dumps a recursive Array" do
|
||||
a = []
|
||||
a << a
|
||||
Marshal.dump(a).should == "\x04\b[\x06@\x00"
|
||||
end
|
||||
|
||||
it "dumps an Array with instance variables" do
|
||||
a = []
|
||||
a.instance_variable_set(:@ivar, 1)
|
||||
Marshal.dump(a).should == "\004\bI[\000\006:\n@ivari\006"
|
||||
end
|
||||
|
||||
it "dumps an extended Array" do
|
||||
Marshal.dump([].extend(Meths)).should == "\004\be:\nMeths[\000"
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Hash" do
|
||||
it "dumps a Hash" do
|
||||
Marshal.dump({}).should == "\004\b{\000"
|
||||
end
|
||||
|
||||
it "dumps a Hash subclass" do
|
||||
Marshal.dump(UserHash.new).should == "\004\bC:\rUserHash{\000"
|
||||
end
|
||||
|
||||
it "dumps a Hash with a default value" do
|
||||
Marshal.dump(Hash.new(1)).should == "\004\b}\000i\006"
|
||||
end
|
||||
|
||||
it "raises a TypeError with hash having default proc" do
|
||||
lambda { Marshal.dump(Hash.new {}) }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "dumps a Hash with instance variables" do
|
||||
a = {}
|
||||
a.instance_variable_set(:@ivar, 1)
|
||||
Marshal.dump(a).should == "\004\bI{\000\006:\n@ivari\006"
|
||||
end
|
||||
|
||||
it "dumps an extended Hash" do
|
||||
Marshal.dump({}.extend(Meths)).should == "\004\be:\nMeths{\000"
|
||||
end
|
||||
|
||||
it "dumps an Hash subclass with a parameter to initialize" do
|
||||
Marshal.dump(UserHashInitParams.new(1)).should == "\004\bIC:\027UserHashInitParams{\000\006:\a@ai\006"
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Struct" do
|
||||
it "dumps a Struct" do
|
||||
Marshal.dump(Struct::Pyramid.new).should == "\004\bS:\024Struct::Pyramid\000"
|
||||
end
|
||||
|
||||
it "dumps a Struct" do
|
||||
Marshal.dump(Struct::Useful.new(1, 2)).should == "\004\bS:\023Struct::Useful\a:\006ai\006:\006bi\a"
|
||||
end
|
||||
|
||||
it "dumps a Struct with instance variables" do
|
||||
st = Struct.new("Thick").new
|
||||
st.instance_variable_set(:@ivar, 1)
|
||||
Marshal.dump(st).should == "\004\bIS:\022Struct::Thick\000\006:\n@ivari\006"
|
||||
Struct.send(:remove_const, :Thick)
|
||||
end
|
||||
|
||||
it "dumps an extended Struct" do
|
||||
st = Struct.new("Extended", :a, :b).new
|
||||
Marshal.dump(st.extend(Meths)).should == "\004\be:\nMethsS:\025Struct::Extended\a:\006a0:\006b0"
|
||||
Struct.send(:remove_const, :Extended)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with an Object" do
|
||||
it "dumps an Object" do
|
||||
Marshal.dump(Object.new).should == "\004\bo:\x0BObject\x00"
|
||||
end
|
||||
|
||||
it "dumps an extended Object" do
|
||||
Marshal.dump(Object.new.extend(Meths)).should == "\004\be:\x0AMethso:\x0BObject\x00"
|
||||
end
|
||||
|
||||
it "dumps an Object with an instance variable" do
|
||||
obj = Object.new
|
||||
obj.instance_variable_set(:@ivar, 1)
|
||||
Marshal.dump(obj).should == "\004\bo:\vObject\006:\n@ivari\006"
|
||||
end
|
||||
|
||||
it "dumps an Object that has had an instance variable added and removed as though it was never set" do
|
||||
obj = Object.new
|
||||
obj.instance_variable_set(:@ivar, 1)
|
||||
obj.send(:remove_instance_variable, :@ivar)
|
||||
Marshal.dump(obj).should == "\004\bo:\x0BObject\x00"
|
||||
end
|
||||
|
||||
it "dumps an Object if it has a singleton class but no singleton methods" do
|
||||
obj = Object.new
|
||||
obj.singleton_class
|
||||
Marshal.dump(obj).should == "\004\bo:\x0BObject\x00"
|
||||
end
|
||||
|
||||
it "raises if an Object has a singleton class and singleton methods" do
|
||||
obj = Object.new
|
||||
def obj.foo; end
|
||||
lambda {
|
||||
Marshal.dump(obj)
|
||||
}.should raise_error(TypeError, "singleton can't be dumped")
|
||||
end
|
||||
|
||||
it "dumps a BasicObject subclass if it defines respond_to?" do
|
||||
obj = MarshalSpec::BasicObjectSubWithRespondToFalse.new
|
||||
Marshal.dump(obj).should == "\x04\bo:2MarshalSpec::BasicObjectSubWithRespondToFalse\x00"
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Range" do
|
||||
it "dumps a Range inclusive of end (with indeterminant order)" do
|
||||
dump = Marshal.dump(1..2)
|
||||
load = Marshal.load(dump)
|
||||
load.should == (1..2)
|
||||
end
|
||||
|
||||
it "dumps a Range exclusive of end (with indeterminant order)" do
|
||||
dump = Marshal.dump(1...2)
|
||||
load = Marshal.load(dump)
|
||||
load.should == (1...2)
|
||||
end
|
||||
|
||||
it "dumps a Range with extra instance variables" do
|
||||
range = (1...3)
|
||||
range.instance_variable_set :@foo, 42
|
||||
dump = Marshal.dump(range)
|
||||
load = Marshal.load(dump)
|
||||
load.should == range
|
||||
load.instance_variable_get(:@foo).should == 42
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Time" do
|
||||
before :each do
|
||||
@internal = Encoding.default_internal
|
||||
Encoding.default_internal = Encoding::UTF_8
|
||||
|
||||
@utc = Time.utc(2012, 1, 1)
|
||||
@utc_dump = @utc.send(:_dump)
|
||||
|
||||
with_timezone 'AST', 3 do
|
||||
@t = Time.local(2012, 1, 1)
|
||||
@fract = Time.local(2012, 1, 1, 1, 59, 56.2)
|
||||
@t_dump = @t.send(:_dump)
|
||||
@fract_dump = @fract.send(:_dump)
|
||||
end
|
||||
end
|
||||
|
||||
after :each do
|
||||
Encoding.default_internal = @internal
|
||||
end
|
||||
|
||||
it "dumps the zone and the offset" do
|
||||
with_timezone 'AST', 3 do
|
||||
dump = Marshal.dump(@t)
|
||||
base = "\x04\bIu:\tTime\r#{@t_dump}\a"
|
||||
offset = ":\voffseti\x020*"
|
||||
zone = ":\tzoneI\"\bAST\x06:\x06EF" # Last is 'F' (US-ASCII)
|
||||
[ "#{base}#{offset}#{zone}", "#{base}#{zone}#{offset}" ].should include(dump)
|
||||
end
|
||||
|
||||
it "dumps the zone, but not the offset if zone is UTC" do
|
||||
dump = Marshal.dump(@utc)
|
||||
zone = ":\tzoneI\"\bUTC\x06:\x06EF" # Last is 'F' (US-ASCII)
|
||||
dump.should == "\x04\bIu:\tTime\r#{@utc_dump}\x06#{zone}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "with an Exception" do
|
||||
it "dumps an empty Exception" do
|
||||
Marshal.dump(Exception.new).should == "\x04\bo:\x0EException\a:\tmesg0:\abt0"
|
||||
end
|
||||
|
||||
it "dumps the message for the exception" do
|
||||
Marshal.dump(Exception.new("foo")).should == "\x04\bo:\x0EException\a:\tmesg\"\bfoo:\abt0"
|
||||
end
|
||||
|
||||
it "contains the filename in the backtrace" do
|
||||
obj = Exception.new("foo")
|
||||
obj.set_backtrace(["foo/bar.rb:10"])
|
||||
Marshal.dump(obj).should == "\x04\bo:\x0EException\a:\tmesg\"\bfoo:\abt[\x06\"\x12foo/bar.rb:10"
|
||||
end
|
||||
end
|
||||
|
||||
it "dumps subsequent appearances of a symbol as a link" do
|
||||
Marshal.dump([:a, :a]).should == "\004\b[\a:\006a;\000"
|
||||
end
|
||||
|
||||
it "dumps subsequent appearances of an object as a link" do
|
||||
o = Object.new
|
||||
Marshal.dump([o, o]).should == "\004\b[\ao:\vObject\000@\006"
|
||||
end
|
||||
|
||||
MarshalSpec::DATA_19.each do |description, (object, marshal, attributes)|
|
||||
it "#{description} returns a binary string" do
|
||||
Marshal.dump(object).encoding.should == Encoding::BINARY
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an ArgumentError when the recursion limit is exceeded" do
|
||||
h = {'one' => {'two' => {'three' => 0}}}
|
||||
lambda { Marshal.dump(h, 3) }.should raise_error(ArgumentError)
|
||||
lambda { Marshal.dump([h], 4) }.should raise_error(ArgumentError)
|
||||
lambda { Marshal.dump([], 0) }.should raise_error(ArgumentError)
|
||||
lambda { Marshal.dump([[[]]], 1) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "ignores the recursion limit if the limit is negative" do
|
||||
Marshal.dump([], -1).should == "\004\b[\000"
|
||||
Marshal.dump([[]], -1).should == "\004\b[\006[\000"
|
||||
Marshal.dump([[[]]], -1).should == "\004\b[\006[\006[\000"
|
||||
end
|
||||
|
||||
describe "when passed an IO" do
|
||||
|
||||
it "writes the serialized data to the IO-Object" do
|
||||
(obj = mock('test')).should_receive(:write).at_least(1)
|
||||
Marshal.dump("test", obj)
|
||||
end
|
||||
|
||||
it "returns the IO-Object" do
|
||||
(obj = mock('test')).should_receive(:write).at_least(1)
|
||||
Marshal.dump("test", obj).should == obj
|
||||
end
|
||||
|
||||
it "raises an Error when the IO-Object does not respond to #write" do
|
||||
obj = mock('test')
|
||||
lambda { Marshal.dump("test", obj) }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
with_feature :encoding do
|
||||
|
||||
it "calls binmode when it's defined" do
|
||||
obj = mock('test')
|
||||
obj.should_receive(:write).at_least(1)
|
||||
obj.should_receive(:binmode).at_least(1)
|
||||
Marshal.dump("test", obj)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "raises a TypeError if marshalling a Method instance" do
|
||||
lambda { Marshal.dump(Marshal.method(:dump)) }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError if marshalling a Proc" do
|
||||
lambda { Marshal.dump(proc {}) }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError if dumping a IO/File instance" do
|
||||
lambda { Marshal.dump(STDIN) }.should raise_error(TypeError)
|
||||
lambda { File.open(__FILE__) { |f| Marshal.dump(f) } }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises a TypeError if dumping a MatchData instance" do
|
||||
lambda { Marshal.dump(/(.)/.match("foo")) }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "returns an untainted string if object is untainted" do
|
||||
Marshal.dump(Object.new).tainted?.should be_false
|
||||
end
|
||||
|
||||
it "returns a tainted string if object is tainted" do
|
||||
Marshal.dump(Object.new.taint).tainted?.should be_true
|
||||
end
|
||||
|
||||
it "returns a tainted string if nested object is tainted" do
|
||||
Marshal.dump([[Object.new.taint]]).tainted?.should be_true
|
||||
end
|
||||
|
||||
it "returns a trusted string if object is trusted" do
|
||||
Marshal.dump(Object.new).untrusted?.should be_false
|
||||
end
|
||||
|
||||
it "returns an untrusted string if object is untrusted" do
|
||||
Marshal.dump(Object.new.untrust).untrusted?.should be_true
|
||||
end
|
||||
|
||||
it "returns an untrusted string if nested object is untrusted" do
|
||||
Marshal.dump([[Object.new.untrust]]).untrusted?.should be_true
|
||||
end
|
||||
end
|
420
spec/ruby/core/marshal/fixtures/marshal_data.rb
Normal file
420
spec/ruby/core/marshal/fixtures/marshal_data.rb
Normal file
|
@ -0,0 +1,420 @@
|
|||
# -*- encoding: binary -*-
|
||||
class UserDefined
|
||||
class Nested
|
||||
def ==(other)
|
||||
other.kind_of? self.class
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :a, :b
|
||||
|
||||
def initialize
|
||||
@a = 'stuff'
|
||||
@b = @a
|
||||
end
|
||||
|
||||
def _dump(depth)
|
||||
Marshal.dump [:stuff, :stuff]
|
||||
end
|
||||
|
||||
def self._load(data)
|
||||
a, b = Marshal.load data
|
||||
|
||||
obj = allocate
|
||||
obj.instance_variable_set :@a, a
|
||||
obj.instance_variable_set :@b, b
|
||||
|
||||
obj
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
self.class === other and
|
||||
@a == other.a and
|
||||
@b == other.b
|
||||
end
|
||||
end
|
||||
|
||||
class UserDefinedWithIvar
|
||||
attr_reader :a, :b, :c
|
||||
|
||||
def initialize
|
||||
@a = 'stuff'
|
||||
@a.instance_variable_set :@foo, :UserDefinedWithIvar
|
||||
@b = 'more'
|
||||
@c = @b
|
||||
end
|
||||
|
||||
def _dump(depth)
|
||||
Marshal.dump [:stuff, :more, :more]
|
||||
end
|
||||
|
||||
def self._load(data)
|
||||
a, b, c = Marshal.load data
|
||||
|
||||
obj = allocate
|
||||
obj.instance_variable_set :@a, a
|
||||
obj.instance_variable_set :@b, b
|
||||
obj.instance_variable_set :@c, c
|
||||
|
||||
obj
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
self.class === other and
|
||||
@a == other.a and
|
||||
@b == other.b and
|
||||
@c == other.c and
|
||||
@a.instance_variable_get(:@foo) == other.a.instance_variable_get(:@foo)
|
||||
end
|
||||
end
|
||||
|
||||
class UserDefinedImmediate
|
||||
def _dump(depth)
|
||||
''
|
||||
end
|
||||
|
||||
def self._load(data)
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
class UserPreviouslyDefinedWithInitializedIvar
|
||||
attr_accessor :field1, :field2
|
||||
end
|
||||
|
||||
class UserMarshal
|
||||
attr_reader :data
|
||||
|
||||
def initialize
|
||||
@data = 'stuff'
|
||||
end
|
||||
def marshal_dump() :data end
|
||||
def marshal_load(data) @data = data end
|
||||
def ==(other) self.class === other and @data == other.data end
|
||||
end
|
||||
|
||||
class UserMarshalWithClassName < UserMarshal
|
||||
def self.name
|
||||
"Never::A::Real::Class"
|
||||
end
|
||||
end
|
||||
|
||||
class UserMarshalWithIvar
|
||||
attr_reader :data
|
||||
|
||||
def initialize
|
||||
@data = 'my data'
|
||||
end
|
||||
|
||||
def marshal_dump
|
||||
[:data]
|
||||
end
|
||||
|
||||
def marshal_load(o)
|
||||
@data = o.first
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
self.class === other and
|
||||
@data = other.data
|
||||
end
|
||||
end
|
||||
|
||||
class UserArray < Array
|
||||
end
|
||||
|
||||
class UserHash < Hash
|
||||
end
|
||||
|
||||
class UserHashInitParams < Hash
|
||||
def initialize(a)
|
||||
@a = a
|
||||
end
|
||||
end
|
||||
|
||||
class UserObject
|
||||
end
|
||||
|
||||
class UserRegexp < Regexp
|
||||
end
|
||||
|
||||
class UserString < String
|
||||
end
|
||||
|
||||
class UserCustomConstructorString < String
|
||||
def initialize(arg1, arg2)
|
||||
end
|
||||
end
|
||||
|
||||
module Meths
|
||||
def meths_method() end
|
||||
end
|
||||
|
||||
module MethsMore
|
||||
def meths_more_method() end
|
||||
end
|
||||
|
||||
Struct.new "Pyramid"
|
||||
Struct.new "Useful", :a, :b
|
||||
|
||||
module MarshalSpec
|
||||
class StructWithUserInitialize < Struct.new(:a)
|
||||
THREADLOCAL_KEY = :marshal_load_struct_args
|
||||
def initialize(*args)
|
||||
# using thread-local to avoid ivar marshaling
|
||||
Thread.current[THREADLOCAL_KEY] = args
|
||||
super(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class BasicObjectSubWithRespondToFalse < BasicObject
|
||||
def respond_to?(method_name, include_all=false)
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def self.random_data
|
||||
randomizer = Random.new(42)
|
||||
1000.times{randomizer.rand} # Make sure we exhaust his first state of 624 random words
|
||||
dump_data = File.binread(fixture(__FILE__, 'random.dump'))
|
||||
[randomizer, dump_data]
|
||||
rescue => e
|
||||
["Error when building Random marshal data #{e}", ""]
|
||||
end
|
||||
|
||||
SwappedClass = nil
|
||||
def self.set_swapped_class(cls)
|
||||
remove_const(:SwappedClass)
|
||||
const_set(:SwappedClass, cls)
|
||||
end
|
||||
|
||||
def self.reset_swapped_class
|
||||
set_swapped_class(nil)
|
||||
end
|
||||
|
||||
DATA = {
|
||||
"nil" => [nil, "\004\b0"],
|
||||
"1..2" => [(1..2),
|
||||
"\004\bo:\nRange\b:\nbegini\006:\texclF:\bendi\a",
|
||||
{ begin: 1, end: 2, :exclude_end? => false }],
|
||||
"1...2" => [(1...2),
|
||||
"\004\bo:\nRange\b:\nbegini\006:\texclT:\bendi\a",
|
||||
{ begin: 1, end: 2, :exclude_end? => true }],
|
||||
"'a'..'b'" => [('a'..'b'),
|
||||
"\004\bo:\nRange\b:\nbegin\"\006a:\texclF:\bend\"\006b",
|
||||
{ begin: 'a', end: 'b', :exclude_end? => false }],
|
||||
"Struct" => [Struct::Useful.new(1, 2),
|
||||
"\004\bS:\023Struct::Useful\a:\006ai\006:\006bi\a"],
|
||||
"Symbol" => [:symbol,
|
||||
"\004\b:\vsymbol"],
|
||||
"true" => [true,
|
||||
"\004\bT"],
|
||||
"false" => [false,
|
||||
"\004\bF"],
|
||||
"String empty" => ['',
|
||||
"\004\b\"\000"],
|
||||
"String small" => ['small',
|
||||
"\004\b\"\012small"],
|
||||
"String big" => ['big' * 100,
|
||||
"\004\b\"\002,\001#{'big' * 100}"],
|
||||
"String extended" => [''.extend(Meths), # TODO: check for module on load
|
||||
"\004\be:\nMeths\"\000"],
|
||||
"String subclass" => [UserString.new,
|
||||
"\004\bC:\017UserString\"\000"],
|
||||
"String subclass extended" => [UserString.new.extend(Meths),
|
||||
"\004\be:\nMethsC:\017UserString\"\000"],
|
||||
"Symbol small" => [:big,
|
||||
"\004\b:\010big"],
|
||||
"Symbol big" => [('big' * 100).to_sym,
|
||||
"\004\b:\002,\001#{'big' * 100}"],
|
||||
"Bignum -2**64" => [-2**64,
|
||||
"\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
|
||||
"Bignum -2**63" => [-2**63,
|
||||
"\004\bl-\t\000\000\000\000\000\000\000\200"],
|
||||
"Fixnum -2**24" => [-2**24,
|
||||
"\004\bi\375\000\000\000"],
|
||||
"Fixnum -4516727" => [-4516727,
|
||||
"\004\bi\375\211\024\273"],
|
||||
"Fixnum -2**16" => [-2**16,
|
||||
"\004\bi\376\000\000"],
|
||||
"Fixnum -2**8" => [-2**8,
|
||||
"\004\bi\377\000"],
|
||||
"Fixnum -123" => [-123,
|
||||
"\004\bi\200"],
|
||||
"Fixnum -124" => [-124, "\004\bi\377\204"],
|
||||
"Fixnum 0" => [0,
|
||||
"\004\bi\000"],
|
||||
"Fixnum 5" => [5,
|
||||
"\004\bi\n"],
|
||||
"Fixnum 122" => [122, "\004\bi\177"],
|
||||
"Fixnum 123" => [123, "\004\bi\001{"],
|
||||
"Fixnum 2**8" => [2**8,
|
||||
"\004\bi\002\000\001"],
|
||||
"Fixnum 2**16" => [2**16,
|
||||
"\004\bi\003\000\000\001"],
|
||||
"Fixnum 2**24" => [2**24,
|
||||
"\004\bi\004\000\000\000\001"],
|
||||
"Bignum 2**64" => [2**64,
|
||||
"\004\bl+\n\000\000\000\000\000\000\000\000\001\000"],
|
||||
"Bignum 2**90" => [2**90,
|
||||
"\004\bl+\v#{"\000" * 11}\004"],
|
||||
"Class String" => [String,
|
||||
"\004\bc\vString"],
|
||||
"Module Marshal" => [Marshal,
|
||||
"\004\bm\fMarshal"],
|
||||
"Module nested" => [UserDefined::Nested.new,
|
||||
"\004\bo:\030UserDefined::Nested\000"],
|
||||
"_dump object" => [UserDefinedWithIvar.new,
|
||||
"\004\bu:\030UserDefinedWithIvar5\004\b[\bI\"\nstuff\006:\t@foo:\030UserDefinedWithIvar\"\tmore@\a"],
|
||||
"_dump object extended" => [UserDefined.new.extend(Meths),
|
||||
"\004\bu:\020UserDefined\022\004\b[\a\"\nstuff@\006"],
|
||||
"marshal_dump object" => [UserMarshalWithIvar.new,
|
||||
"\004\bU:\030UserMarshalWithIvar[\006\"\fmy data"],
|
||||
"Regexp" => [/\A.\Z/,
|
||||
"\004\b/\n\\A.\\Z\000"],
|
||||
"Regexp subclass /i" => [UserRegexp.new('', Regexp::IGNORECASE),
|
||||
"\004\bC:\017UserRegexp/\000\001"],
|
||||
"Float 0.0" => [0.0,
|
||||
"\004\bf\0060"],
|
||||
"Float -0.0" => [-0.0,
|
||||
"\004\bf\a-0"],
|
||||
"Float Infinity" => [(1.0 / 0.0),
|
||||
"\004\bf\binf"],
|
||||
"Float -Infinity" => [(-1.0 / 0.0),
|
||||
"\004\bf\t-inf"],
|
||||
"Float 1.0" => [1.0,
|
||||
"\004\bf\0061"],
|
||||
"Float 8323434.342" => [8323434.342,
|
||||
"\004\bf\0328323434.3420000002\000S\370"],
|
||||
"Float 1.0799999999999912" => [1.0799999999999912,
|
||||
"\004\bf\0321.0799999999999912\000\341 "],
|
||||
"Hash" => [Hash.new,
|
||||
"\004\b{\000"],
|
||||
"Hash subclass" => [UserHash.new,
|
||||
"\004\bC:\rUserHash{\000"],
|
||||
"Array" => [Array.new,
|
||||
"\004\b[\000"],
|
||||
"Array subclass" => [UserArray.new,
|
||||
"\004\bC:\016UserArray[\000"],
|
||||
"Struct Pyramid" => [Struct::Pyramid.new,
|
||||
"\004\bS:\024Struct::Pyramid\000"],
|
||||
}
|
||||
DATA_19 = {
|
||||
"nil" => [nil, "\004\b0"],
|
||||
"1..2" => [(1..2),
|
||||
"\004\bo:\nRange\b:\nbegini\006:\texclF:\bendi\a",
|
||||
{ begin: 1, end: 2, :exclude_end? => false }],
|
||||
"1...2" => [(1...2),
|
||||
"\004\bo:\nRange\b:\nbegini\006:\texclT:\bendi\a",
|
||||
{ begin: 1, end: 2, :exclude_end? => true }],
|
||||
"'a'..'b'" => [('a'..'b'),
|
||||
"\004\bo:\nRange\b:\nbegin\"\006a:\texclF:\bend\"\006b",
|
||||
{ begin: 'a', end: 'b', :exclude_end? => false }],
|
||||
"Struct" => [Struct::Useful.new(1, 2),
|
||||
"\004\bS:\023Struct::Useful\a:\006ai\006:\006bi\a"],
|
||||
"Symbol" => [:symbol,
|
||||
"\004\b:\vsymbol"],
|
||||
"true" => [true,
|
||||
"\004\bT"],
|
||||
"false" => [false,
|
||||
"\004\bF"],
|
||||
"String empty" => ['',
|
||||
"\x04\bI\"\x00\x06:\x06EF"],
|
||||
"String small" => ['small',
|
||||
"\x04\bI\"\nsmall\x06:\x06EF"],
|
||||
"String big" => ['big' * 100,
|
||||
"\x04\bI\"\x02,\x01bigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbigbig\x06:\x06EF"],
|
||||
"String extended" => [''.extend(Meths), # TODO: check for module on load
|
||||
"\x04\bIe:\nMeths\"\x00\x06:\x06EF"],
|
||||
"String subclass" => [UserString.new,
|
||||
"\004\bC:\017UserString\"\000"],
|
||||
"String subclass extended" => [UserString.new.extend(Meths),
|
||||
"\004\be:\nMethsC:\017UserString\"\000"],
|
||||
"Symbol small" => [:big,
|
||||
"\004\b:\010big"],
|
||||
"Symbol big" => [('big' * 100).to_sym,
|
||||
"\004\b:\002,\001#{'big' * 100}"],
|
||||
"Bignum -2**64" => [-2**64,
|
||||
"\004\bl-\n\000\000\000\000\000\000\000\000\001\000"],
|
||||
"Bignum -2**63" => [-2**63,
|
||||
"\004\bl-\t\000\000\000\000\000\000\000\200"],
|
||||
"Fixnum -2**24" => [-2**24,
|
||||
"\004\bi\375\000\000\000"],
|
||||
"Fixnum -2**16" => [-2**16,
|
||||
"\004\bi\376\000\000"],
|
||||
"Fixnum -2**8" => [-2**8,
|
||||
"\004\bi\377\000"],
|
||||
"Fixnum -123" => [-123,
|
||||
"\004\bi\200"],
|
||||
"Fixnum 0" => [0,
|
||||
"\004\bi\000"],
|
||||
"Fixnum 5" => [5,
|
||||
"\004\bi\n"],
|
||||
"Fixnum 2**8" => [2**8,
|
||||
"\004\bi\002\000\001"],
|
||||
"Fixnum 2**16" => [2**16,
|
||||
"\004\bi\003\000\000\001"],
|
||||
"Fixnum 2**24" => [2**24,
|
||||
"\004\bi\004\000\000\000\001"],
|
||||
"Bignum 2**64" => [2**64,
|
||||
"\004\bl+\n\000\000\000\000\000\000\000\000\001\000"],
|
||||
"Bignum 2**90" => [2**90,
|
||||
"\004\bl+\v#{"\000" * 11}\004"],
|
||||
"Class String" => [String,
|
||||
"\004\bc\vString"],
|
||||
"Module Marshal" => [Marshal,
|
||||
"\004\bm\fMarshal"],
|
||||
"Module nested" => [UserDefined::Nested.new,
|
||||
"\004\bo:\030UserDefined::Nested\000"],
|
||||
"_dump object" => [UserDefinedWithIvar.new,
|
||||
"\x04\bu:\x18UserDefinedWithIvar>\x04\b[\bI\"\nstuff\a:\x06EF:\t@foo:\x18UserDefinedWithIvarI\"\tmore\x06;\x00F@\a"],
|
||||
"_dump object extended" => [UserDefined.new.extend(Meths),
|
||||
"\x04\bu:\x10UserDefined\x18\x04\b[\aI\"\nstuff\x06:\x06EF@\x06"],
|
||||
"marshal_dump object" => [UserMarshalWithIvar.new,
|
||||
"\x04\bU:\x18UserMarshalWithIvar[\x06I\"\fmy data\x06:\x06EF"],
|
||||
"Regexp" => [/\A.\Z/,
|
||||
"\x04\bI/\n\\A.\\Z\x00\x06:\x06EF"],
|
||||
"Regexp subclass /i" => [UserRegexp.new('', Regexp::IGNORECASE),
|
||||
"\x04\bIC:\x0FUserRegexp/\x00\x01\x06:\x06EF"],
|
||||
"Float 0.0" => [0.0,
|
||||
"\004\bf\0060"],
|
||||
"Float -0.0" => [-0.0,
|
||||
"\004\bf\a-0"],
|
||||
"Float Infinity" => [(1.0 / 0.0),
|
||||
"\004\bf\binf"],
|
||||
"Float -Infinity" => [(-1.0 / 0.0),
|
||||
"\004\bf\t-inf"],
|
||||
"Float 1.0" => [1.0,
|
||||
"\004\bf\0061"],
|
||||
"Hash" => [Hash.new,
|
||||
"\004\b{\000"],
|
||||
"Hash subclass" => [UserHash.new,
|
||||
"\004\bC:\rUserHash{\000"],
|
||||
"Array" => [Array.new,
|
||||
"\004\b[\000"],
|
||||
"Array subclass" => [UserArray.new,
|
||||
"\004\bC:\016UserArray[\000"],
|
||||
"Struct Pyramid" => [Struct::Pyramid.new,
|
||||
"\004\bS:\024Struct::Pyramid\000"],
|
||||
"Random" => random_data,
|
||||
}
|
||||
end
|
||||
|
||||
class ArraySub < Array
|
||||
def initialize(*args)
|
||||
super(args)
|
||||
end
|
||||
end
|
||||
|
||||
class ArraySubPush < Array
|
||||
def << value
|
||||
raise 'broken'
|
||||
end
|
||||
alias_method :push, :<<
|
||||
end
|
||||
|
||||
class SameName
|
||||
end
|
||||
|
||||
module NamespaceTest
|
||||
end
|
BIN
spec/ruby/core/marshal/fixtures/random.dump
Normal file
BIN
spec/ruby/core/marshal/fixtures/random.dump
Normal file
Binary file not shown.
77
spec/ruby/core/marshal/float_spec.rb
Normal file
77
spec/ruby/core/marshal/float_spec.rb
Normal file
|
@ -0,0 +1,77 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Marshal.dump with Float" do
|
||||
it "represents NaN" do
|
||||
Marshal.dump(nan_value).should == "\004\bf\bnan"
|
||||
end
|
||||
|
||||
it "represents +Infinity" do
|
||||
Marshal.dump(infinity_value).should == "\004\bf\binf"
|
||||
end
|
||||
|
||||
it "represents -Infinity" do
|
||||
Marshal.dump(-infinity_value).should == "\004\bf\t-inf"
|
||||
end
|
||||
|
||||
it "represents zero" do
|
||||
Marshal.dump(0.0).should == "\004\bf\0060"
|
||||
end
|
||||
|
||||
it "represents a Float less than 1" do
|
||||
Marshal.dump(0.666666667).should == "\x04\bf\x100.666666667"
|
||||
end
|
||||
|
||||
it "represents a Float much less than 1" do
|
||||
Marshal.dump(0.000000001234697).should == "\x04\bf\x101.234697e-9"
|
||||
end
|
||||
|
||||
it "represents a Float greater than 1" do
|
||||
Marshal.dump(42.666666667).should == "\x04\bf\x1142.666666667"
|
||||
end
|
||||
|
||||
it "represents a Float much greater than 1" do
|
||||
Marshal.dump(98743561239011.0).should == "\x04\bf\x1398743561239011"
|
||||
end
|
||||
|
||||
it "represents a Float much greater than 1 with a very small fractional part" do
|
||||
Marshal.dump(799346183459.0000000002999312541).should == "\x04\bf\x11799346183459"
|
||||
end
|
||||
end
|
||||
|
||||
describe "Marshal.load with Float" do
|
||||
it "loads NaN" do
|
||||
Marshal.load("\004\bf\bnan").should be_nan
|
||||
end
|
||||
|
||||
it "loads +Infinity" do
|
||||
Marshal.load("\004\bf\binf").should == infinity_value
|
||||
end
|
||||
|
||||
it "loads -Infinity" do
|
||||
Marshal.load("\004\bf\t-inf").should == -infinity_value
|
||||
end
|
||||
|
||||
it "loads zero" do
|
||||
Marshal.load("\004\bf\0060").should == 0.0
|
||||
end
|
||||
|
||||
it "loads a Float less than 1" do
|
||||
Marshal.load("\x04\bf\x100.666666667").should == 0.666666667
|
||||
end
|
||||
|
||||
it "loads a Float much less than 1" do
|
||||
Marshal.load("\x04\bf\x101.234697e-9").should == 0.000000001234697
|
||||
end
|
||||
|
||||
it "loads a Float greater than 1" do
|
||||
Marshal.load("\x04\bf\x1142.666666667").should == 42.666666667
|
||||
end
|
||||
|
||||
it "loads a Float much greater than 1" do
|
||||
Marshal.load("\x04\bf\x1398743561239011").should == 98743561239011.0
|
||||
end
|
||||
|
||||
it "loads a Float much greater than 1 with a very small fractional part" do
|
||||
Marshal.load("\x04\bf\x16793468359.0002999").should == 793468359.0002999
|
||||
end
|
||||
end
|
6
spec/ruby/core/marshal/load_spec.rb
Normal file
6
spec/ruby/core/marshal/load_spec.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../shared/load', __FILE__)
|
||||
|
||||
describe "Marshal.load" do
|
||||
it_behaves_like :marshal_load, :load
|
||||
end
|
7
spec/ruby/core/marshal/major_version_spec.rb
Normal file
7
spec/ruby/core/marshal/major_version_spec.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Marshal::MAJOR_VERSION" do
|
||||
it "is 4" do
|
||||
Marshal::MAJOR_VERSION.should == 4
|
||||
end
|
||||
end
|
7
spec/ruby/core/marshal/minor_version_spec.rb
Normal file
7
spec/ruby/core/marshal/minor_version_spec.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
|
||||
describe "Marshal::MINOR_VERSION" do
|
||||
it "is 8" do
|
||||
Marshal::MINOR_VERSION.should == 8
|
||||
end
|
||||
end
|
6
spec/ruby/core/marshal/restore_spec.rb
Normal file
6
spec/ruby/core/marshal/restore_spec.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
require File.expand_path('../../../spec_helper', __FILE__)
|
||||
require File.expand_path('../shared/load', __FILE__)
|
||||
|
||||
describe "Marshal.restore" do
|
||||
it_behaves_like :marshal_load, :restore
|
||||
end
|
830
spec/ruby/core/marshal/shared/load.rb
Normal file
830
spec/ruby/core/marshal/shared/load.rb
Normal file
|
@ -0,0 +1,830 @@
|
|||
# -*- encoding: binary -*-
|
||||
require File.expand_path('../../fixtures/marshal_data', __FILE__)
|
||||
require 'stringio'
|
||||
|
||||
describe :marshal_load, shared: true do
|
||||
before :all do
|
||||
@num_self_class = 1
|
||||
end
|
||||
|
||||
it "raises an ArgumentError when the dumped data is truncated" do
|
||||
obj = {first: 1, second: 2, third: 3}
|
||||
lambda { Marshal.send(@method, Marshal.dump(obj)[0, 5]) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "raises an ArgumentError when the dumped class is missing" do
|
||||
Object.send(:const_set, :KaBoom, Class.new)
|
||||
kaboom = Marshal.dump(KaBoom.new)
|
||||
Object.send(:remove_const, :KaBoom)
|
||||
|
||||
lambda { Marshal.send(@method, kaboom) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
describe "when called with a proc" do
|
||||
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; arg })
|
||||
ret.first.should == 1
|
||||
ret[1].should == [1,a]
|
||||
ret[2].should == a
|
||||
ret.size.should == 3
|
||||
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; 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 == ["hi", false, 5, 10, "hi", "hi", 0.0, st, "none", false,
|
||||
:b, :c, a, 2, ["hi", 10, "hi", "hi", st, [:a, :b, :c]], "ant", false]
|
||||
|
||||
Struct.send(:remove_const, :Brittle)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when called with nil for the proc argument" do
|
||||
it "behaves as if no proc argument was passed" do
|
||||
a = [1]
|
||||
a << a
|
||||
b = Marshal.send(@method, Marshal.dump(a), nil)
|
||||
b.should == a
|
||||
end
|
||||
end
|
||||
|
||||
describe "when called on objects with custom _dump methods" do
|
||||
it "does not set instance variables of an object with user-defined _dump/_load" do
|
||||
# this string represents: <#UserPreviouslyDefinedWithInitializedIvar @field2=7 @field1=6>
|
||||
dump_str = "\004\bu:-UserPreviouslyDefinedWithInitializedIvar\a:\f@field2i\f:\f@field1i\v"
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
describe "that return an immediate value" do
|
||||
it "loads an array containing an instance of the object, followed by multiple instances of another object" do
|
||||
str = "string"
|
||||
|
||||
# this string represents: [<#UserDefinedImmediate A>, <#String "string">, <#String "string">]
|
||||
marshaled_obj = Marshal.send(@method, "\004\b[\bu:\031UserDefinedImmediate\000\"\vstring@\a")
|
||||
|
||||
marshaled_obj.should == [nil, str, str]
|
||||
end
|
||||
|
||||
it "loads any structure with multiple references to the same object, followed by multiple instances of another object" do
|
||||
str = "string"
|
||||
|
||||
# this string represents: {a: <#UserDefinedImmediate A>, b: <#UserDefinedImmediate A>, c: <#String "string">, d: <#String "string">}
|
||||
hash_dump = "\x04\b{\t:\x06aIu:\x19UserDefinedImmediate\x00\x06:\x06ET:\x06b@\x06:\x06cI\"\vstring\x06;\aT:\x06d@\a"
|
||||
|
||||
marshaled_obj = Marshal.send(@method, hash_dump)
|
||||
marshaled_obj.should == {a: nil, b: nil, c: str, d: str}
|
||||
|
||||
# this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate A>, <#String "string">, <#String "string">]
|
||||
array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ET@\x06I\"\vstring\x06;\x06T@\a"
|
||||
|
||||
marshaled_obj = Marshal.send(@method, array_dump)
|
||||
marshaled_obj.should == [nil, nil, str, str]
|
||||
end
|
||||
|
||||
it "loads an array containing references to multiple instances of the object, followed by multiple instances of another object" do
|
||||
str = "string"
|
||||
|
||||
# this string represents: [<#UserDefinedImmediate A>, <#UserDefinedImmediate B>, <#String "string">, <#String "string">]
|
||||
array_dump = "\x04\b[\tIu:\x19UserDefinedImmediate\x00\x06:\x06ETIu;\x00\x00\x06;\x06TI\"\vstring\x06;\x06T@\b"
|
||||
|
||||
marshaled_obj = Marshal.send(@method, array_dump)
|
||||
marshaled_obj.should == [nil, nil, str, str]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "loads an array containing objects having _dump method, and with proc" do
|
||||
arr = []
|
||||
myproc = Proc.new { |o| arr << o; 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)
|
||||
|
||||
arr.should == [o1, o2, o1, o2, obj]
|
||||
end
|
||||
|
||||
it "loads an array containing objects having marshal_dump method, and with proc" do
|
||||
arr = []
|
||||
proc = Proc.new { |o| arr << o; o }
|
||||
o1 = UserMarshal.new
|
||||
o2 = UserMarshalWithIvar.new
|
||||
obj = [o1, o2, o1, o2]
|
||||
|
||||
Marshal.send(@method, "\004\b[\tU:\020UserMarshal\"\nstuffU:\030UserMarshalWithIvar[\006\"\fmy data@\006@\b", proc)
|
||||
|
||||
arr.should == ['stuff', o1, 'my data', ['my data'], o2, o1, o2, obj]
|
||||
end
|
||||
|
||||
it "assigns classes to nested subclasses of Array correctly" do
|
||||
arr = ArraySub.new(ArraySub.new)
|
||||
arr_dump = Marshal.dump(arr)
|
||||
Marshal.send(@method, arr_dump).class.should == ArraySub
|
||||
end
|
||||
|
||||
it "loads subclasses of Array with overridden << and push correctly" do
|
||||
arr = ArraySubPush.new
|
||||
arr[0] = '1'
|
||||
arr_dump = Marshal.dump(arr)
|
||||
Marshal.send(@method, arr_dump).should == arr
|
||||
end
|
||||
|
||||
it "raises a TypeError with bad Marshal version" do
|
||||
marshal_data = '\xff\xff'
|
||||
marshal_data[0] = (Marshal::MAJOR_VERSION).chr
|
||||
marshal_data[1] = (Marshal::MINOR_VERSION + 1).chr
|
||||
|
||||
lambda { Marshal.send(@method, marshal_data) }.should raise_error(TypeError)
|
||||
|
||||
marshal_data = '\xff\xff'
|
||||
marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
|
||||
marshal_data[1] = (Marshal::MINOR_VERSION).chr
|
||||
|
||||
lambda { Marshal.send(@method, marshal_data) }.should raise_error(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
|
||||
lambda { Marshal.send(@method, file) }.should raise_error(EOFError)
|
||||
ensure
|
||||
file.close
|
||||
rm_r temp_file
|
||||
end
|
||||
end
|
||||
|
||||
it "returns an untainted object if source is untainted" do
|
||||
x = Object.new
|
||||
y = Marshal.send(@method, Marshal.dump(x))
|
||||
y.tainted?.should be_false
|
||||
end
|
||||
|
||||
describe "when source is tainted" do
|
||||
it "returns a tainted object" do
|
||||
x = Object.new
|
||||
x.taint
|
||||
s = Marshal.dump(x)
|
||||
y = Marshal.send(@method, s)
|
||||
y.tainted?.should be_true
|
||||
|
||||
# note that round-trip via Marshal does not preserve
|
||||
# the taintedness at each level of the nested structure
|
||||
y = Marshal.send(@method, Marshal.dump([[x]]))
|
||||
y.tainted?.should be_true
|
||||
y.first.tainted?.should be_true
|
||||
y.first.first.tainted?.should be_true
|
||||
end
|
||||
|
||||
it "does not taint Symbols" do
|
||||
x = [:x]
|
||||
y = Marshal.send(@method, Marshal.dump(x).taint)
|
||||
y.tainted?.should be_true
|
||||
y.first.tainted?.should be_false
|
||||
end
|
||||
|
||||
it "does not taint Fixnums" do
|
||||
x = [1]
|
||||
y = Marshal.send(@method, Marshal.dump(x).taint)
|
||||
y.tainted?.should be_true
|
||||
y.first.tainted?.should be_false
|
||||
end
|
||||
|
||||
it "does not taint Bignums" do
|
||||
x = [bignum_value]
|
||||
y = Marshal.send(@method, Marshal.dump(x).taint)
|
||||
y.tainted?.should be_true
|
||||
y.first.tainted?.should be_false
|
||||
end
|
||||
|
||||
it "does not taint Floats" do
|
||||
x = [1.2]
|
||||
y = Marshal.send(@method, Marshal.dump(x).taint)
|
||||
y.tainted?.should be_true
|
||||
y.first.tainted?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "preserves taintedness of nested structure" do
|
||||
x = Object.new
|
||||
a = [[x]]
|
||||
x.taint
|
||||
y = Marshal.send(@method, Marshal.dump(a))
|
||||
y.tainted?.should be_true
|
||||
y.first.tainted?.should be_true
|
||||
y.first.first.tainted?.should be_true
|
||||
end
|
||||
|
||||
it "returns a trusted object if source is trusted" do
|
||||
x = Object.new
|
||||
y = Marshal.send(@method, Marshal.dump(x))
|
||||
y.untrusted?.should be_false
|
||||
end
|
||||
|
||||
it "returns an untrusted object if source is untrusted" do
|
||||
x = Object.new
|
||||
x.untrust
|
||||
y = Marshal.send(@method, Marshal.dump(x))
|
||||
y.untrusted?.should be_true
|
||||
|
||||
# note that round-trip via Marshal does not preserve
|
||||
# the untrustedness at each level of the nested structure
|
||||
y = Marshal.send(@method, Marshal.dump([[x]]))
|
||||
y.untrusted?.should be_true
|
||||
y.first.untrusted?.should be_true
|
||||
y.first.first.untrusted?.should be_true
|
||||
end
|
||||
|
||||
# Note: Ruby 1.9 should be compatible with older marshal format
|
||||
MarshalSpec::DATA.each do |description, (object, marshal, attributes)|
|
||||
it "loads a #{description}" do
|
||||
Marshal.send(@method, marshal).should == object
|
||||
end
|
||||
end
|
||||
|
||||
MarshalSpec::DATA_19.each do |description, (object, marshal, attributes)|
|
||||
it "loads a #{description}" do
|
||||
Marshal.send(@method, marshal).should == object
|
||||
end
|
||||
end
|
||||
|
||||
describe "for an Array" do
|
||||
it "loads an array containing the same objects" do
|
||||
s = 'oh'
|
||||
b = 'hi'
|
||||
r = //
|
||||
d = [b, :no, s, :go]
|
||||
c = String
|
||||
f = 1.0
|
||||
|
||||
o1 = UserMarshalWithIvar.new; o2 = UserMarshal.new
|
||||
|
||||
obj = [:so, 'hello', 100, :so, :so, d, :so, o2, :so, :no, o2,
|
||||
:go, c, nil, Struct::Pyramid.new, f, :go, :no, s, b, r,
|
||||
:so, 'huh', o1, true, b, b, 99, r, b, s, :so, f, c, :no, o1, d]
|
||||
|
||||
Marshal.send(@method, "\004\b[*:\aso\"\nhelloii;\000;\000[\t\"\ahi:\ano\"\aoh:\ago;\000U:\020UserMarshal\"\nstuff;\000;\006@\n;\ac\vString0S:\024Struct::Pyramid\000f\0061;\a;\006@\t@\b/\000\000;\000\"\bhuhU:\030UserMarshalWithIvar[\006\"\fmy dataT@\b@\bih@\017@\b@\t;\000@\016@\f;\006@\021@\a").should ==
|
||||
obj
|
||||
end
|
||||
|
||||
it "loads an array having ivar" do
|
||||
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[1].instance_variable_get(:@foo).should == 10
|
||||
end
|
||||
|
||||
it "loads an extended Array object containing a user-marshaled object" do
|
||||
obj = [UserMarshal.new, UserMarshal.new].extend(Meths)
|
||||
new_obj = Marshal.send(@method, "\x04\be:\nMeths[\ao:\x10UserMarshal\x06:\n@dataI\"\nstuff\x06:\x06ETo;\x06\x06;\aI\"\nstuff\x06;\bT")
|
||||
|
||||
new_obj.should == obj
|
||||
obj_ancestors = class << obj; ancestors[1..-1]; end
|
||||
new_obj_ancestors = class << new_obj; ancestors[1..-1]; end
|
||||
obj_ancestors.should == new_obj_ancestors
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Hash" do
|
||||
it "loads an extended_user_hash with a parameter to initialize" do
|
||||
obj = UserHashInitParams.new(:abc).extend(Meths)
|
||||
|
||||
new_obj = Marshal.send(@method, "\004\bIe:\nMethsC:\027UserHashInitParams{\000\006:\a@a:\babc")
|
||||
|
||||
new_obj.should == obj
|
||||
new_obj_metaclass_ancestors = class << new_obj; ancestors; end
|
||||
new_obj_metaclass_ancestors[@num_self_class].should == Meths
|
||||
new_obj_metaclass_ancestors[@num_self_class+1].should == UserHashInitParams
|
||||
end
|
||||
|
||||
it "loads an extended hash object containing a user-marshaled object" do
|
||||
obj = {a: UserMarshal.new}.extend(Meths)
|
||||
|
||||
new_obj = Marshal.send(@method, "\004\be:\nMeths{\006:\006aU:\020UserMarshal\"\nstuff")
|
||||
|
||||
new_obj.should == obj
|
||||
new_obj_metaclass_ancestors = class << new_obj; ancestors; end
|
||||
new_obj_metaclass_ancestors[@num_self_class].should == Meths
|
||||
new_obj_metaclass_ancestors[@num_self_class+1].should == Hash
|
||||
end
|
||||
|
||||
it "preserves hash ivars when hash contains a string having ivar" do
|
||||
s = 'string'
|
||||
s.instance_variable_set :@string_ivar, 'string ivar'
|
||||
h = { key: s }
|
||||
h.instance_variable_set :@hash_ivar, 'hash ivar'
|
||||
|
||||
unmarshalled = Marshal.send(@method, Marshal.dump(h))
|
||||
unmarshalled.instance_variable_get(:@hash_ivar).should == 'hash ivar'
|
||||
unmarshalled[:key].instance_variable_get(:@string_ivar).should == 'string ivar'
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a String" do
|
||||
it "loads a string having ivar with ref to self" do
|
||||
obj = 'hi'
|
||||
obj.instance_variable_set(:@self, obj)
|
||||
Marshal.send(@method, "\004\bI\"\ahi\006:\n@self@\000").should == obj
|
||||
end
|
||||
|
||||
it "loads a string through StringIO stream" do
|
||||
obj = "This is a string which should be unmarshalled through StringIO stream!"
|
||||
Marshal.send(@method, StringIO.new(Marshal.dump(obj))).should == obj
|
||||
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"
|
||||
end
|
||||
|
||||
it "loads a String subclass with custom constructor" do
|
||||
str = Marshal.send(@method, "\x04\bC: UserCustomConstructorString\"\x00")
|
||||
str.should be_an_instance_of(UserCustomConstructorString)
|
||||
end
|
||||
|
||||
with_feature :encoding do
|
||||
it "loads a US-ASCII String" do
|
||||
str = "abc".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)
|
||||
end
|
||||
|
||||
it "loads a UTF-8 String" do
|
||||
str = "\x6d\xc3\xb6\x68\x72\x65".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)
|
||||
end
|
||||
|
||||
it "loads a String in another encoding" do
|
||||
str = "\x6d\x00\xf6\x00\x68\x00\x72\x00\x65\x00".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)
|
||||
end
|
||||
|
||||
it "loads a String as ASCII-8BIT if no encoding is specified at the end" do
|
||||
str = "\xC3\xB8".force_encoding("ASCII-8BIT")
|
||||
data = "\x04\b\"\a\xC3\xB8".force_encoding("UTF-8")
|
||||
result = Marshal.send(@method, data)
|
||||
result.encoding.should == Encoding::ASCII_8BIT
|
||||
result.should == str
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Struct" do
|
||||
it "loads a extended_struct having fields with same objects" do
|
||||
s = 'hi'
|
||||
obj = Struct.new("Ure2", :a, :b).new.extend(Meths)
|
||||
obj.a = [:a, s]
|
||||
obj.b = [:Meths, s]
|
||||
|
||||
Marshal.send(@method,
|
||||
"\004\be:\nMethsS:\021Struct::Ure2\a:\006a[\a;\a\"\ahi:\006b[\a;\000@\a"
|
||||
).should == obj
|
||||
Struct.send(:remove_const, :Ure2)
|
||||
end
|
||||
|
||||
it "loads a struct having ivar" do
|
||||
obj = Struct.new("Thick").new
|
||||
obj.instance_variable_set(:@foo, 5)
|
||||
Marshal.send(@method, "\004\bIS:\022Struct::Thick\000\006:\t@fooi\n").should == obj
|
||||
Struct.send(:remove_const, :Thick)
|
||||
end
|
||||
|
||||
it "loads a struct having fields" do
|
||||
obj = Struct.new("Ure1", :a, :b).new
|
||||
Marshal.send(@method, "\004\bS:\021Struct::Ure1\a:\006a0:\006b0").should == obj
|
||||
Struct.send(:remove_const, :Ure1)
|
||||
end
|
||||
|
||||
it "does not call initialize on the unmarshaled struct" do
|
||||
threadlocal_key = MarshalSpec::StructWithUserInitialize::THREADLOCAL_KEY
|
||||
|
||||
s = MarshalSpec::StructWithUserInitialize.new('foo')
|
||||
Thread.current[threadlocal_key].should == ['foo']
|
||||
s.a.should == 'foo'
|
||||
|
||||
Thread.current[threadlocal_key] = nil
|
||||
|
||||
dumped = Marshal.dump(s)
|
||||
loaded = Marshal.send(@method, dumped)
|
||||
|
||||
Thread.current[threadlocal_key].should == nil
|
||||
loaded.a.should == 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
describe "for an Exception" do
|
||||
it "loads a marshalled exception with no message" do
|
||||
obj = Exception.new
|
||||
loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt0:\tmesg0")
|
||||
loaded.message.should == obj.message
|
||||
loaded.backtrace.should == obj.backtrace
|
||||
loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesg0:\abt0")
|
||||
loaded.message.should == obj.message
|
||||
loaded.backtrace.should == obj.backtrace
|
||||
end
|
||||
|
||||
it "loads a marshalled exception with a message" do
|
||||
obj = Exception.new("foo")
|
||||
loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt0:\tmesg\"\bfoo")
|
||||
loaded.message.should == obj.message
|
||||
loaded.backtrace.should == obj.backtrace
|
||||
loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt0")
|
||||
loaded.message.should == obj.message
|
||||
loaded.backtrace.should == obj.backtrace
|
||||
end
|
||||
|
||||
it "loads a marshalled exception with a backtrace" do
|
||||
obj = Exception.new("foo")
|
||||
obj.set_backtrace(["foo/bar.rb:10"])
|
||||
loaded = Marshal.send(@method, "\004\bo:\016Exception\a:\abt[\006\"\022foo/bar.rb:10:\tmesg\"\bfoo")
|
||||
loaded.message.should == obj.message
|
||||
loaded.backtrace.should == obj.backtrace
|
||||
loaded = Marshal.send(@method, "\x04\bo:\x0EException\a:\tmesgI\"\bfoo\x06:\x06EF:\abt[\x06I\"\x12foo/bar.rb:10\x06;\aF")
|
||||
loaded.message.should == obj.message
|
||||
loaded.backtrace.should == obj.backtrace
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a user Class" do
|
||||
it "loads a user-marshaled extended object" do
|
||||
obj = UserMarshal.new.extend(Meths)
|
||||
|
||||
new_obj = Marshal.send(@method, "\004\bU:\020UserMarshal\"\nstuff")
|
||||
|
||||
new_obj.should == obj
|
||||
new_obj_metaclass_ancestors = class << new_obj; ancestors; end
|
||||
new_obj_metaclass_ancestors[@num_self_class].should == UserMarshal
|
||||
end
|
||||
|
||||
it "loads a user_object" do
|
||||
UserObject.new
|
||||
Marshal.send(@method, "\004\bo:\017UserObject\000").should be_kind_of(UserObject)
|
||||
end
|
||||
|
||||
it "loads an object" do
|
||||
Marshal.send(@method, "\004\bo:\vObject\000").should be_kind_of(Object)
|
||||
end
|
||||
|
||||
it "raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class" do
|
||||
lambda do
|
||||
Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd")
|
||||
end.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "loads an extended Object" do
|
||||
obj = Object.new.extend(Meths)
|
||||
|
||||
new_obj = Marshal.send(@method, "\004\be:\nMethso:\vObject\000")
|
||||
|
||||
new_obj.class.should == obj.class
|
||||
new_obj_metaclass_ancestors = class << new_obj; ancestors; end
|
||||
new_obj_metaclass_ancestors[@num_self_class, 2].should == [Meths, Object]
|
||||
end
|
||||
|
||||
describe "that extends a core type other than Object or BasicObject" do
|
||||
after :each do
|
||||
MarshalSpec.reset_swapped_class
|
||||
end
|
||||
|
||||
it "raises ArgumentError if the resulting class does not extend the same type" do
|
||||
MarshalSpec.set_swapped_class(Class.new(Hash))
|
||||
data = Marshal.dump(MarshalSpec::SwappedClass.new)
|
||||
|
||||
MarshalSpec.set_swapped_class(Class.new(Array))
|
||||
lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError)
|
||||
|
||||
MarshalSpec.set_swapped_class(Class.new)
|
||||
lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
it "loads an object having ivar" do
|
||||
s = 'hi'
|
||||
arr = [:so, :so, s, s]
|
||||
obj = Object.new
|
||||
obj.instance_variable_set :@str, arr
|
||||
|
||||
new_obj = Marshal.send(@method, "\004\bo:\vObject\006:\t@str[\t:\aso;\a\"\ahi@\a")
|
||||
new_str = new_obj.instance_variable_get :@str
|
||||
|
||||
new_str.should == arr
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Regexp" do
|
||||
it "loads an extended Regexp" do
|
||||
obj = /[a-z]/.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 "loads a extended_user_regexp having ivar" do
|
||||
obj = UserRegexp.new('').extend(Meths)
|
||||
obj.instance_variable_set(:@noise, 'much')
|
||||
|
||||
new_obj = Marshal.send(@method, "\004\bIe:\nMethsC:\017UserRegexp/\000\000\006:\v@noise\"\tmuch")
|
||||
|
||||
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, UserRegexp, Regexp]
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Float" do
|
||||
it "loads a Float NaN" do
|
||||
obj = 0.0 / 0.0
|
||||
Marshal.send(@method, "\004\bf\bnan").to_s.should == obj.to_s
|
||||
end
|
||||
|
||||
it "loads a Float 1.3" do
|
||||
Marshal.send(@method, "\004\bf\v1.3\000\314\315").should == 1.3
|
||||
end
|
||||
|
||||
it "loads a Float -5.1867345e-22" do
|
||||
obj = -5.1867345e-22
|
||||
Marshal.send(@method, "\004\bf\037-5.1867345000000008e-22\000\203_").should be_close(obj, 1e-30)
|
||||
end
|
||||
|
||||
it "loads a Float 1.1867345e+22" do
|
||||
obj = 1.1867345e+22
|
||||
Marshal.send(@method, "\004\bf\0361.1867344999999999e+22\000\344@").should == obj
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Integer" do
|
||||
it "loads 0" do
|
||||
Marshal.send(@method, "\004\bi\000").should == 0
|
||||
Marshal.send(@method, "\004\bi\005").should == 0
|
||||
end
|
||||
|
||||
it "loads an Integer 8" do
|
||||
Marshal.send(@method, "\004\bi\r" ).should == 8
|
||||
end
|
||||
|
||||
it "loads and Integer -8" do
|
||||
Marshal.send(@method, "\004\bi\363" ).should == -8
|
||||
end
|
||||
|
||||
it "loads an Integer 1234" do
|
||||
Marshal.send(@method, "\004\bi\002\322\004").should == 1234
|
||||
end
|
||||
|
||||
it "loads an Integer -1234" do
|
||||
Marshal.send(@method, "\004\bi\376.\373").should == -1234
|
||||
end
|
||||
|
||||
it "loads an Integer 4611686018427387903" do
|
||||
Marshal.send(@method, "\004\bl+\t\377\377\377\377\377\377\377?").should == 4611686018427387903
|
||||
end
|
||||
|
||||
it "loads an Integer -4611686018427387903" do
|
||||
Marshal.send(@method, "\004\bl-\t\377\377\377\377\377\377\377?").should == -4611686018427387903
|
||||
end
|
||||
|
||||
it "loads an Integer 2361183241434822606847" do
|
||||
Marshal.send(@method, "\004\bl+\n\377\377\377\377\377\377\377\377\177\000").should == 2361183241434822606847
|
||||
end
|
||||
|
||||
it "loads an Integer -2361183241434822606847" do
|
||||
Marshal.send(@method, "\004\bl-\n\377\377\377\377\377\377\377\377\177\000").should == -2361183241434822606847
|
||||
end
|
||||
|
||||
it "raises ArgumentError if the input is too short" do
|
||||
["\004\bi",
|
||||
"\004\bi\001",
|
||||
"\004\bi\002",
|
||||
"\004\bi\002\0",
|
||||
"\004\bi\003",
|
||||
"\004\bi\003\0",
|
||||
"\004\bi\003\0\0",
|
||||
"\004\bi\004",
|
||||
"\004\bi\004\0",
|
||||
"\004\bi\004\0\0",
|
||||
"\004\bi\004\0\0\0"].each do |invalid|
|
||||
lambda { Marshal.send(@method, invalid) }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
if 0.size == 8 # for platforms like x86_64
|
||||
it "roundtrips 4611686018427387903 from dump/load correctly" do
|
||||
Marshal.send(@method, Marshal.dump(4611686018427387903)).should == 4611686018427387903
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Rational" do
|
||||
it "loads" do
|
||||
Marshal.send(@method, Marshal.dump(Rational(1, 3))).should == Rational(1, 3)
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Complex" do
|
||||
it "loads" do
|
||||
Marshal.send(@method, Marshal.dump(Complex(4, 3))).should == Complex(4, 3)
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Bignum" do
|
||||
platform_is wordsize: 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")
|
||||
val.should == 1433877090
|
||||
val.class.should == Fixnum
|
||||
end
|
||||
|
||||
it "dumps an array containing multiple references to the Bignum as an array of Fixnum" do
|
||||
arr = Marshal.send(@method, "\004\b[\al+\a\223BwU@\006")
|
||||
arr.should == [1433879187, 1433879187]
|
||||
arr.each { |v| v.class.should == Fixnum }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Time" do
|
||||
it "loads" do
|
||||
Marshal.send(@method, Marshal.dump(Time.at(1))).should == Time.at(1)
|
||||
end
|
||||
|
||||
it "loads serialized instance variables" do
|
||||
t = Time.new
|
||||
t.instance_variable_set(:@foo, 'bar')
|
||||
|
||||
Marshal.send(@method, Marshal.dump(t)).instance_variable_get(:@foo).should == 'bar'
|
||||
end
|
||||
|
||||
it "loads Time objects stored as links" do
|
||||
t = Time.new
|
||||
|
||||
t1, t2 = Marshal.send(@method, Marshal.dump([t, t]))
|
||||
t1.should equal t2
|
||||
end
|
||||
|
||||
it "loads the 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
|
||||
t = Time.now
|
||||
Marshal.send(@method, Marshal.dump(t)).nsec.should == t.nsec
|
||||
end
|
||||
end
|
||||
|
||||
describe "for nil" do
|
||||
it "loads" do
|
||||
Marshal.send(@method, "\x04\b0").should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "for true" do
|
||||
it "loads" do
|
||||
Marshal.send(@method, "\x04\bT").should be_true
|
||||
end
|
||||
end
|
||||
|
||||
describe "for false" do
|
||||
it "loads" do
|
||||
Marshal.send(@method, "\x04\bF").should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Class" do
|
||||
it "loads" do
|
||||
Marshal.send(@method, "\x04\bc\vString").should == String
|
||||
end
|
||||
|
||||
it "raises ArgumentError if given the name of a non-Module" do
|
||||
lambda { Marshal.send(@method, "\x04\bc\vKernel") }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "raises ArgumentError if given a nonexistent class" do
|
||||
lambda { Marshal.send(@method, "\x04\bc\vStrung") }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a Module" do
|
||||
it "loads a module" do
|
||||
Marshal.send(@method, "\x04\bm\vKernel").should == Kernel
|
||||
end
|
||||
|
||||
it "raises ArgumentError if given the name of a non-Class" do
|
||||
lambda { Marshal.send(@method, "\x04\bm\vString") }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "loads an old module" do
|
||||
Marshal.send(@method, "\x04\bM\vKernel").should == Kernel
|
||||
end
|
||||
end
|
||||
|
||||
describe "for a wrapped C pointer" do
|
||||
it "loads" do
|
||||
class DumpableDir < Dir
|
||||
def _dump_data
|
||||
path
|
||||
end
|
||||
def _load_data path
|
||||
initialize(path)
|
||||
end
|
||||
end
|
||||
|
||||
data = "\x04\bd:\x10DumpableDirI\"\x06.\x06:\x06ET"
|
||||
|
||||
dir = Marshal.send(@method, data)
|
||||
begin
|
||||
dir.path.should == '.'
|
||||
ensure
|
||||
dir.close
|
||||
end
|
||||
end
|
||||
|
||||
it "raises TypeError when the local class is missing _load_data" do
|
||||
class UnloadableDumpableDir < Dir
|
||||
def _dump_data
|
||||
path
|
||||
end
|
||||
# no _load_data
|
||||
end
|
||||
|
||||
data = "\x04\bd:\x1AUnloadableDumpableDirI\"\x06.\x06:\x06ET"
|
||||
|
||||
lambda { Marshal.send(@method, data) }.should raise_error(TypeError)
|
||||
end
|
||||
|
||||
it "raises ArgumentError when the local class is a regular object" do
|
||||
data = "\004\bd:\020UserDefined\0"
|
||||
|
||||
lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when a class does not exist in the namespace" do
|
||||
before :each do
|
||||
NamespaceTest.send(:const_set, :SameName, Class.new)
|
||||
@data = Marshal.dump(NamespaceTest::SameName.new)
|
||||
NamespaceTest.send(:remove_const, :SameName)
|
||||
end
|
||||
|
||||
it "raises an ArgumentError" do
|
||||
message = "undefined class/module NamespaceTest::SameName"
|
||||
lambda { Marshal.send(@method, @data) }.should raise_error(ArgumentError, message)
|
||||
end
|
||||
end
|
||||
|
||||
it "raises an ArgumentError with full constant name when the dumped constant is missing" do
|
||||
NamespaceTest.send(:const_set, :KaBoom, Class.new)
|
||||
@data = Marshal.dump(NamespaceTest::KaBoom.new)
|
||||
NamespaceTest.send(:remove_const, :KaBoom)
|
||||
|
||||
lambda { Marshal.send(@method, @data) }.should raise_error(ArgumentError, /NamespaceTest::KaBoom/)
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue