spec/deserializer_spec.rb in python-pickle-0.1.1 vs spec/deserializer_spec.rb in python-pickle-0.2.0
- old
+ new
@@ -29,18 +29,26 @@
it "must contain '__builtin__.object' for Python 2.x support" do
expect(subject.constants['__builtin__']['object']).to be(described_class::OBJECT_CLASS)
end
+ it "must contain '__builtin__.set' for Python 2.x support" do
+ expect(subject.constants['__builtin__']['set']).to be(Set)
+ end
+
it "must contain '__builtin__.bytearray' for Python 2.x support" do
expect(subject.constants['__builtin__']['bytearray']).to be(Python::Pickle::ByteArray)
end
it "must contain 'builtins.object' for Python 3.x support" do
expect(subject.constants['builtins']['object']).to be(described_class::OBJECT_CLASS)
end
+ it "must contain 'builtins.set' for Python 2.x support" do
+ expect(subject.constants['builtins']['set']).to be(Set)
+ end
+
it "must contain 'builtins.bytearray' for Python 2.x support" do
expect(subject.constants['builtins']['bytearray']).to be(Python::Pickle::ByteArray)
end
end
@@ -82,25 +90,45 @@
'_reconstructor' => subject.method(:copyreg_reconstructor)
},
'__builtin__' => {
'object' => described_class::OBJECT_CLASS,
+ 'set' => Set,
'bytearray' => Python::Pickle::ByteArray
},
'builtins' => {
'object' => described_class::OBJECT_CLASS,
+ 'set' => Set,
'bytearray' => Python::Pickle::ByteArray
},
'__main__' => {
'MyClass' => TestDeserializer::MyClass
}
}
)
end
end
+
+ context "when initialized with the `buffers:` keyword argument" do
+ let(:buffer1) { "hello world" }
+ let(:buffer2) { "foo bar" }
+ let(:buffers) do
+ [
+ buffer1,
+ buffer2
+ ]
+ end
+
+ subject { described_class.new(buffers: buffers) }
+
+ it "must set #buffers to an Enumerator of the buffers" do
+ expect(subject.buffers).to be_kind_of(Enumerator)
+ expect(subject.buffers.to_a).to eq(buffers)
+ end
+ end
end
describe "#push_meta_stack" do
before do
subject.stack << 1 << 2 << 3
@@ -413,10 +441,41 @@
it "must push an empty Hash object onto the #stack" do
expect(subject.stack).to eq([ {} ])
end
end
+ context "when given a Python::Pickle::Instructions::EMPTY_SET" do
+ let(:instruction) { Python::Pickle::Instructions::EMPTY_SET }
+
+ before do
+ subject.execute(instruction)
+ end
+
+ it "must push an empty Set object onto the #stack" do
+ expect(subject.stack).to eq([ Set.new ])
+ end
+ end
+
+ context "when given a Python::Pickle::Instructions::FROZENSET" do
+ let(:instruction) { Python::Pickle::Instructions::FROZENSET }
+
+ before do
+ subject.meta_stack << []
+ subject.stack << 1 << 2 << 3
+ subject.execute(instruction)
+ end
+
+ it "must pop the #meta_stack and create a frozen Set from the previous #stack and push the frozen Set onto the new #stack" do
+ expect(subject.stack.length).to eq(1)
+
+ set = subject.stack[-1]
+
+ expect(set).to be_frozen
+ expect(set).to eq(Set[1,2,3])
+ end
+ end
+
context "when given a Python::Pickle::Instructions::APPEND" do
context "and when the previous element on the stack is an Array" do
let(:instruction) { Python::Pickle::Instructions::APPEND }
before do
@@ -427,10 +486,23 @@
it "must pop the last element from the #stack and push it onto the next list element" do
expect(subject.stack).to eq([ [2] ])
end
end
+ context "and when the previous element on the stack is a Set" do
+ let(:instruction) { Python::Pickle::Instructions::APPEND }
+
+ before do
+ subject.stack << Set.new << 2
+ subject.execute(instruction)
+ end
+
+ it "must pop the last element from the #stack and push it onto the next list element" do
+ expect(subject.stack).to eq([ Set[2] ])
+ end
+ end
+
context "but when the previous element on the stack is not an Array" do
let(:instruction) { Python::Pickle::Instructions::APPEND }
let(:item) { 2 }
let(:list) { "XXX" }
@@ -459,10 +531,24 @@
it "must pop the #meta_stack, store the #stack, and concat the previous #stack onto the last element of the new #stack" do
expect(subject.stack).to eq([ [1,2,3,4,5,6] ])
end
end
+ context "and when the previous element on the stack is a Set" do
+ let(:instruction) { Python::Pickle::Instructions::APPENDS }
+
+ before do
+ subject.meta_stack << [ Set[1,2,3] ]
+ subject.stack << 4 << 5 << 6
+ subject.execute(instruction)
+ end
+
+ it "must pop the #meta_stack, store the #stack, and concat the previous #stack onto the last element of the new #stack" do
+ expect(subject.stack).to eq([ Set[1,2,3,4,5,6] ])
+ end
+ end
+
context "but when the previous element on the stack is not an Array" do
let(:instruction) { Python::Pickle::Instructions::APPENDS }
let(:items) { [3,4,5] }
let(:list) { "XXX" }
@@ -477,10 +563,43 @@
}.to raise_error(Python::Pickle::DeserializationError,"cannot append elements #{items.inspect} onto a non-Array: #{list.inspect}")
end
end
end
+ context "when given a Python::Pickle::Instructions::ADDITEMS" do
+ context "and when the previous element on the stack is a Set" do
+ let(:instruction) { Python::Pickle::Instructions::ADDITEMS }
+
+ before do
+ subject.meta_stack << [ Set[1,2,3] ]
+ subject.stack << 4 << 5 << 6
+ subject.execute(instruction)
+ end
+
+ it "must pop the #meta_stack, store the #stack, and concat the previous #stack onto the last element of the new #stack" do
+ expect(subject.stack).to eq([ Set[1,2,3,4,5,6] ])
+ end
+ end
+
+ context "but when the previous element on the stack is not a Set" do
+ let(:instruction) { Python::Pickle::Instructions::ADDITEMS }
+ let(:items) { [3,4,5] }
+ let(:set) { [] }
+
+ before do
+ subject.meta_stack << [ set ]
+ subject.stack << items[0] << items[1] << items[2]
+ end
+
+ it do
+ expect {
+ subject.execute(instruction)
+ }.to raise_error(Python::Pickle::DeserializationError,"cannot add items #{items.inspect} to a non-Set object: #{set.inspect}")
+ end
+ end
+ end
+
context "when given a Python::Pickle::Instructions::LIST" do
let(:instruction) { Python::Pickle::Instructions::LIST }
before do
subject.meta_stack << [ [1,2,3] ]
@@ -627,10 +746,146 @@
expect(constant.name).to eq(name)
end
end
end
+ context "when given a Python::Pickle::Instructions::Inst object" do
+ let(:namespace) { '__main__' }
+ let(:name) { 'MyClass' }
+ let(:instruction) { Python::Pickle::Instructions::Inst.new(namespace,name) }
+
+ before do
+ subject.meta_stack << []
+ subject.stack << 1 << 2
+ subject.execute(instruction)
+ end
+
+ context "when the constant can be resolved" do
+ module TestInstInstruction
+ class MyClass
+ attr_reader :x, :y
+
+ def initialize(x,y)
+ @x = x
+ @y = y
+ end
+ end
+ end
+
+ subject do
+ described_class.new(
+ constants: {
+ '__main__' => {
+ 'MyClass' => TestInstInstruction::MyClass
+ }
+ }
+ )
+ end
+
+ it "must resolve the class from the INST instruction's #namespace and #name, pop the meta stack, use the previous sstack as the initialization arguments, initialize a new instance of the class, and push it onto the new #stack" do
+ expect(subject.stack.length).to eq(1)
+
+ object = subject.stack[-1]
+
+ expect(object).to be_kind_of(TestInstInstruction::MyClass)
+ expect(object.x).to eq(1)
+ expect(object.y).to eq(2)
+ end
+ end
+
+ context "but the constant cannot be resolved" do
+ it "must push a new Python::Pickle::PyClass object onto the #stack" do
+ py_object = subject.stack[-1]
+
+ expect(py_object).to be_kind_of(Python::Pickle::PyObject)
+ expect(py_object.py_class).to be_kind_of(Python::Pickle::PyClass)
+ expect(py_object.py_class.namespace).to eq(namespace)
+ expect(py_object.py_class.name).to eq(name)
+ expect(py_object.init_args).to eq([1,2])
+ end
+ end
+ end
+
+ context "when given a Python::Pickle::Instructions::OBJ" do
+ let(:instruction) { Python::Pickle::Instructions::OBJ }
+
+ context "and when the constant on the #stack is a Ruby class" do
+ module TestObjInstruction
+ class MyClass
+ end
+ end
+
+ context "but there are no additional arguments on the #stack after the class" do
+ before do
+ subject.stack << TestObjInstruction::MyClass
+ subject.execute(instruction)
+ end
+
+ it "must pop off first element, and initialize a new instance of the class, and push the new instance onto the #stack" do
+ expect(subject.stack.length).to eq(1)
+ expect(subject.stack[-1]).to be_kind_of(TestObjInstruction::MyClass)
+ end
+ end
+
+ context "but there are additional arguments on the #stack after the class" do
+ module TestObjInstruction
+ class MyClassWithArgs
+ attr_reader :x, :y
+
+ def initialize(x,y)
+ @x = x
+ @y = y
+ end
+ end
+ end
+
+ before do
+ subject.stack << TestObjInstruction::MyClassWithArgs << 1 << 2
+ subject.execute(instruction)
+ end
+
+ it "must call #initialize with the splatted tuple's arguments" do
+ object = subject.stack[-1]
+
+ expect(object.x).to eq(1)
+ expect(object.y).to eq(2)
+ end
+ end
+ end
+
+ context "and when the constant on the #stack is a PyClass" do
+ let(:namespace) { '__main__' }
+ let(:name) { 'MyClass' }
+ let(:py_class) { Python::Pickle::PyClass.new(namespace,name) }
+
+ context "but there are no additional arguments on the #stack after the class" do
+ before do
+ subject.stack << py_class
+ subject.execute(instruction)
+ end
+
+ it "must pop off the two last elements and push the new Python::Pickle::PyObject onto the #stack" do
+ expect(subject.stack.length).to eq(1)
+ expect(subject.stack[-1]).to be_kind_of(Python::Pickle::PyObject)
+ end
+ end
+
+ context "but there are additional arguments on the #stack after the class" do
+ before do
+ subject.stack << py_class << 1 << 2
+ subject.execute(instruction)
+ end
+
+ it "must set the object's #init_args to the tuple's elements" do
+ object = subject.stack[-1]
+
+ expect(object.init_args).to eq([1,2])
+ end
+ end
+ end
+ end
+
context "when given a Python::Pickle::Instructions::NEWOBJ" do
let(:instruction) { Python::Pickle::Instructions::NEWOBJ }
context "and when the constant on the #stack is a Ruby class" do
module TestNewObjInstruction
@@ -1197,9 +1452,62 @@
it do
expect {
subject.execute(instruction)
}.to raise_error(Python::Pickle::DeserializationError,"cannot set key value pairs (#{pairs.inspect}) into non-Hash: #{object.inspect}")
end
+ end
+ end
+
+ context "when given a Python::Pickle::Instructions::NEXT_BUFFER" do
+ let(:instruction) { Python::Pickle::Instructions::NEXT_BUFFER }
+
+ context "and the #{described_class} was initialized with the buffers: keyword argument" do
+ let(:buffer1) { String.new("hello world") }
+ let(:buffer2) { String.new("foo bar") }
+ let(:buffers) do
+ [
+ buffer1,
+ buffer2
+ ]
+ end
+
+ subject { described_class.new(buffers: buffers) }
+
+ before do
+ subject.execute(instruction)
+ end
+
+ it "must take the next element from #buffers and push it onto the #stack" do
+ expect(subject.stack).to eq([buffer1])
+ end
+
+ it "must not modify the underlying buffers Array" do
+ expect(buffers).to eq([buffer1, buffer2])
+ end
+ end
+
+ context "but the #{described_class} was not initialized with the buffers: keyword argument" do
+ it do
+ expect {
+ subject.execute(instruction)
+ }.to raise_error(Python::Pickle::DeserializationError,"pickle stream includes a NEXT_BUFFER instruction, but no buffers were provided")
+ end
+ end
+ end
+
+ context "when given a Python::Pickle::Instructions::READONLY_BUFFER" do
+ let(:instruction) { Python::Pickle::Instructions::READONLY_BUFFER }
+
+ let(:buffer1) { String.new("hello world") }
+ let(:buffer2) { String.new("foo bar") }
+
+ before do
+ subject.stack << buffer1 << buffer2
+ subject.execute(instruction)
+ end
+
+ it "must freeze the buffer at the top of the #stack" do
+ expect(subject.stack[-1]).to be_frozen
end
end
end
describe "#copyreg_reconstructor" do