spec/base_spec.rb in bindata-0.9.3 vs spec/base_spec.rb in bindata-0.10.0
- old
+ new
@@ -2,106 +2,176 @@
require File.expand_path(File.dirname(__FILE__)) + '/spec_common'
require 'bindata/base'
class BaseStub < BinData::Base
+ # Override to avoid NotImplemented errors
def clear; end
def clear?; end
- def single_value?; end
- def done_read; end
def _do_read(io) end
+ def _done_read; end
def _do_write(io) end
- def _do_num_bytes; end
+ def _do_num_bytes(x) end
+ def _assign(x); end
def _snapshot; end
+
+ expose_methods_for_testing
end
+class MockBaseStub < BaseStub
+ attr_accessor :mock
+ def clear; mock.clear; end
+ def clear?; mock.clear?; end
+ def _do_read(io) mock._do_read(io); end
+ def _done_read; mock._done_read; end
+ def _do_write(io) mock._do_write(io); end
+ def _do_num_bytes(x) mock._do_num_bytes(x) end
+ def _assign(x); mock._assign(x); end
+ def _snapshot; mock._snapshot; end
+end
+
+describe BinData::Base, "when subclassing" do
+ before(:all) do
+ eval <<-END
+ class SubClassOfBase < BinData::Base
+ expose_methods_for_testing
+ end
+ END
+ end
+
+ before(:each) do
+ @obj = SubClassOfBase.new
+ end
+
+ it "should raise errors on unimplemented methods" do
+ lambda { @obj.clear }.should raise_error(NotImplementedError)
+ lambda { @obj.clear? }.should raise_error(NotImplementedError)
+ lambda { @obj.assign(nil) }.should raise_error(NotImplementedError)
+ lambda { @obj.debug_name_of(nil) }.should raise_error(NotImplementedError)
+ lambda { @obj.offset_of(nil) }.should raise_error(NotImplementedError)
+ lambda { @obj._do_read(nil) }.should raise_error(NotImplementedError)
+ lambda { @obj._done_read }.should raise_error(NotImplementedError)
+ lambda { @obj._do_write(nil) }.should raise_error(NotImplementedError)
+ lambda { @obj._do_num_bytes(nil) }.should raise_error(NotImplementedError)
+ lambda { @obj._snapshot }.should raise_error(NotImplementedError)
+ end
+end
+
describe BinData::Base, "with mandatory parameters" do
before(:all) do
eval <<-END
- class MandatoryBase < BinData::Base
- bindata_mandatory_parameter :p1
+ class MandatoryBase < BaseStub
+ mandatory_parameter :p1
+ mandatory_parameter :p2
end
END
end
- it "should ensure that those parameters are present" do
- lambda { MandatoryBase.new(:p1 => "a") }.should_not raise_error
+ it "should ensure that all mandatory parameters are present" do
+ params = {:p1 => "a", :p2 => "b" }
+ lambda { MandatoryBase.new(params) }.should_not raise_error
end
- it "should fail when those parameters are not present" do
- lambda { MandatoryBase.new(:p2 => "a") }.should raise_error(ArgumentError)
+ it "should fail if not all mandatory parameters are present" do
+ params = {:p1 => "a", :xx => "b" }
+ lambda { MandatoryBase.new(params) }.should raise_error(ArgumentError)
end
+
+ it "should fail if no mandatory parameters are present" do
+ lambda { MandatoryBase.new() }.should raise_error(ArgumentError)
+ end
end
describe BinData::Base, "with default parameters" do
before(:all) do
eval <<-END
- class DefaultBase < BinData::Base
- bindata_default_parameter :p1 => "a"
- public :has_param?, :no_eval_param
+ class DefaultBase < BaseStub
+ default_parameter :p1 => "a"
end
END
end
- it "should set default parameters if they are not specified" do
+ it "should use default parameters when not specified" do
obj = DefaultBase.new
- obj.should have_param(:p1)
- obj.no_eval_param(:p1).should == "a"
+ obj.should have_parameter(:p1)
+ obj.eval_parameter(:p1).should == "a"
end
it "should be able to override default parameters" do
obj = DefaultBase.new(:p1 => "b")
- obj.should have_param(:p1)
- obj.no_eval_param(:p1).should == "b"
+ obj.should have_parameter(:p1)
+ obj.eval_parameter(:p1).should == "b"
end
end
describe BinData::Base, "with mutually exclusive parameters" do
before(:all) do
eval <<-END
- class MutexParamBase < BinData::Base
- bindata_optional_parameters :p1, :p2
- bindata_mutually_exclusive_parameters :p1, :p2
+ class MutexParamBase < BaseStub
+ optional_parameters :p1, :p2
+ mutually_exclusive_parameters :p1, :p2
end
END
end
- it "should not fail when neither of those parameters is present" do
+ it "should not fail when neither of those parameters are present" do
lambda { MutexParamBase.new }.should_not raise_error
end
it "should not fail when only one of those parameters is present" do
lambda { MutexParamBase.new(:p1 => "a") }.should_not raise_error
- lambda { MutexParamBase.new(:p2 => "a") }.should_not raise_error
+ lambda { MutexParamBase.new(:p2 => "b") }.should_not raise_error
end
it "should fail when both those parameters are present" do
lambda { MutexParamBase.new(:p1 => "a", :p2 => "b") }.should raise_error(ArgumentError)
end
end
describe BinData::Base, "with multiple parameters" do
before(:all) do
eval <<-END
- class WithParamBase < BinData::Base
- bindata_mandatory_parameter :p1
- bindata_optional_parameter :p2
- bindata_default_parameter :p3 => '3'
- public :has_param?, :eval_param, :no_eval_param
+ class WithParamBase < BaseStub
+ mandatory_parameter :p1
+ optional_parameter :p2
+ default_parameter :p3 => 3
end
END
end
- it "should not allow parameters with nil values" do
- lambda { WithParamBase.new(:p1 => 1, :p2 => nil) }.should raise_error(ArgumentError)
+ it "should identify internally accepted parameters" do
+ accepted = WithParamBase.accepted_internal_parameters
+ accepted.should include(:p1)
+ accepted.should include(:p2)
+ accepted.should include(:p3)
+ accepted.should_not include(:xx)
end
- it "should identify extra parameters" do
- obj = WithParamBase.new({:p1 => 1, :p3 => 3, :p4 => 4, :p5 => 5})
- obj.parameters.should == {:p4 => 4, :p5 => 5}
+ it "should evaluate parameters" do
+ params = {:p1 => 1, :p2 => 2, :p3 => 3, :p4 => lambda { 4 }}
+ obj = WithParamBase.new(params)
+ obj.eval_parameter(:p4).should == 4
end
+ it "should return parameters" do
+ params = {:p1 => 1, :p2 => 2, :p3 => 3, :p4 => :a}
+ obj = WithParamBase.new(params)
+ obj.get_parameter(:p4).should == :a
+ end
+
+ it "should have parameters" do
+ params = {:p1 => 1, :p2 => 2, :p3 => 3, :p4 => 4}
+ obj = WithParamBase.new(params)
+ obj.should have_parameter(:p4)
+ end
+
+ it "should not allow parameters with nil values" do
+ lambda { WithParamBase.new(:p1 => 1, :p2 => nil) }.should raise_error(ArgumentError)
+ end
+
+=begin
+# TODO: how should we evaluate internal parameters? think about this
it "should only recall mandatory, default and optional parameters" do
obj = WithParamBase.new(:p1 => 1, :p3 => 3, :p4 => 4, :p5 => 5)
obj.should have_param(:p1)
obj.should_not have_param(:p2)
obj.should have_param(:p3)
@@ -109,31 +179,24 @@
obj.should_not have_param(:p5)
end
it "should evaluate mandatory, default and optional parameters" do
obj = WithParamBase.new(:p1 => 1, :p3 => lambda {1 + 2}, :p4 => 4, :p5 => 5)
- obj.eval_param(:p1).should == 1
- obj.eval_param(:p2).should be_nil
- obj.eval_param(:p3).should == 3
- obj.eval_param(:p4).should be_nil
- obj.eval_param(:p5).should be_nil
+ obj.eval_parameter(:p1).should == 1
+ obj.eval_parameter(:p2).should be_nil
+ obj.eval_parameter(:p3).should == 3
+ obj.eval_parameter(:p4).should be_nil
+ obj.eval_parameter(:p5).should be_nil
end
+=end
it "should be able to access without evaluating" do
obj = WithParamBase.new(:p1 => :asym, :p3 => lambda {1 + 2})
- obj.no_eval_param(:p1).should == :asym
- obj.no_eval_param(:p2).should be_nil
- obj.no_eval_param(:p3).should respond_to(:arity)
+ obj.get_parameter(:p1).should == :asym
+ obj.get_parameter(:p2).should be_nil
+ obj.get_parameter(:p3).should respond_to(:arity)
end
-
- it "should identify accepted parameters" do
- internal_parameters = WithParamBase.internal_parameters
- internal_parameters.should include(:p1)
- internal_parameters.should include(:p2)
- internal_parameters.should include(:p3)
- internal_parameters.should_not include(:p4)
- end
end
describe BinData::Base, "with :check_offset" do
before(:all) do
eval <<-END
@@ -145,36 +208,36 @@
end
end
END
end
+ before(:each) do
+ @io = StringIO.new("12345678901234567890")
+ end
+
it "should fail if offset is incorrect" do
- io = StringIO.new("12345678901234567890")
- io.seek(2)
+ @io.seek(2)
obj = TenByteOffsetBase.new(:check_offset => 8)
- lambda { obj.read(io) }.should raise_error(BinData::ValidityError)
+ lambda { obj.read(@io) }.should raise_error(BinData::ValidityError)
end
it "should succeed if offset is correct" do
- io = StringIO.new("12345678901234567890")
- io.seek(3)
+ @io.seek(3)
obj = TenByteOffsetBase.new(:check_offset => 10)
- lambda { obj.read(io) }.should_not raise_error
+ lambda { obj.read(@io) }.should_not raise_error
end
it "should fail if :check_offset fails" do
- io = StringIO.new("12345678901234567890")
- io.seek(4)
+ @io.seek(4)
obj = TenByteOffsetBase.new(:check_offset => lambda { offset == 11 } )
- lambda { obj.read(io) }.should raise_error(BinData::ValidityError)
+ lambda { obj.read(@io) }.should raise_error(BinData::ValidityError)
end
it "should succeed if :check_offset succeeds" do
- io = StringIO.new("12345678901234567890")
- io.seek(5)
+ @io.seek(5)
obj = TenByteOffsetBase.new(:check_offset => lambda { offset == 10 } )
- lambda { obj.read(io) }.should_not raise_error
+ lambda { obj.read(@io) }.should_not raise_error
end
end
describe BinData::Base, "with :adjust_offset" do
before(:all) do
@@ -187,177 +250,119 @@
end
end
END
end
+ before(:each) do
+ @io = StringIO.new("12345678901234567890")
+ end
+
it "should be mutually exclusive with :check_offset" do
params = { :check_offset => 8, :adjust_offset => 8 }
lambda { TenByteAdjustingOffsetBase.new(params) }.should raise_error(ArgumentError)
end
it "should adjust if offset is incorrect" do
- io = StringIO.new("12345678901234567890")
- io.seek(2)
+ @io.seek(2)
obj = TenByteAdjustingOffsetBase.new(:adjust_offset => 13)
- obj.read(io)
- io.pos.should == (2 + 13)
+ obj.read(@io)
+ @io.pos.should == (2 + 13)
end
it "should succeed if offset is correct" do
- io = StringIO.new("12345678901234567890")
- io.seek(3)
+ @io.seek(3)
obj = TenByteAdjustingOffsetBase.new(:adjust_offset => 10)
- lambda { obj.read(io) }.should_not raise_error
- io.pos.should == (3 + 10)
+ lambda { obj.read(@io) }.should_not raise_error
+ @io.pos.should == (3 + 10)
end
it "should fail if cannot adjust offset" do
- io = StringIO.new("12345678901234567890")
- io.seek(3)
+ @io.seek(3)
obj = TenByteAdjustingOffsetBase.new(:adjust_offset => -4)
- lambda { obj.read(io) }.should raise_error(BinData::ValidityError)
+ lambda { obj.read(@io) }.should raise_error(BinData::ValidityError)
end
end
-describe BinData::Base, "with :onlyif => false" do
- before(:all) do
- eval <<-END
- class OnlyIfBase < BaseStub
- attr_accessor :mock
- def _do_read(io) mock._do_read(io); end
- def _do_write(io) mock._do_write(io); end
- def _do_num_bytes; mock._do_num_bytes; end
- def _snapshot; mock._snapshot; end
- end
- END
+describe BinData::Base, "as black box" do
+ it "should access parent" do
+ parent = BaseStub.new
+ child = BaseStub.new(nil, parent)
+ child.parent.should == parent
end
- before(:each) do
- @obj = OnlyIfBase.new :onlyif => false
- @obj.mock = mock('mock')
+ it "should instantiate self for ::read" do
+ BaseStub.read("").class.should == BaseStub
end
- it "should not read" do
- io = StringIO.new("12345678901234567890")
- @obj.mock.should_not_receive(:_do_read)
- @obj.read(io)
+ it "should return self for #read" do
+ obj = BaseStub.new
+ obj.read("").should == obj
end
- it "should not write" do
- io = StringIO.new
- @obj.mock.should_not_receive(:_do_write)
- @obj.write(io)
+ it "should return self for #write" do
+ obj = BaseStub.new
+ obj.write("").should == obj
end
- it "should have zero num_bytes" do
- @obj.mock.should_not_receive(:_do_num_bytes)
- @obj.num_bytes.should be_zero
+ it "should forward #inspect to snapshot" do
+ class SnapshotBase < BaseStub
+ def snapshot; [1, 2, 3]; end
+ end
+ obj = SnapshotBase.new
+ obj.inspect.should == obj.snapshot.inspect
end
- it "should have nil snapshot" do
- @obj.mock.should_not_receive(:_snapshot)
- @obj.snapshot.should be_nil
+ it "should forward #to_s to snapshot" do
+ class SnapshotBase < BaseStub
+ def snapshot; [1, 2, 3]; end
+ end
+ obj = SnapshotBase.new
+ obj.to_s.should == obj.snapshot.to_s
end
-end
-describe BinData::Base, "with :readwrite" do
- before(:all) do
- eval <<-END
- class NoIOBase < BinData::Base
- public :has_param?, :no_eval_param
- end
- END
- end
+ it "should write the same as to_binary_s" do
+ class WriteToSBase < BaseStub
+ def _do_write(io) io.writebytes("abc"); end
+ end
- it "should alias to :onlyif" do
- obj = NoIOBase.new(:readwrite => "a")
- obj.should_not have_param(:readwrite)
- obj.should have_param(:onlyif)
- obj.no_eval_param(:onlyif).should == "a"
+ obj = WriteToSBase.new
+ io = StringIO.new
+ obj.write(io)
+ io.rewind
+ written = io.read
+ obj.to_binary_s.should == written
end
end
-describe BinData::Base, "when subclassing" do
- before(:all) do
- eval <<-END
- class SubClassOfBase < BinData::Base
- public :_do_read, :_do_write, :_do_num_bytes, :_snapshot
- end
- END
- end
-
+describe BinData::Base, "as white box" do
before(:each) do
- @obj = SubClassOfBase.new
+ @obj = MockBaseStub.new
+ @obj.mock = mock('mock')
end
- it "should raise errors on unimplemented methods" do
- lambda { @obj.clear }.should raise_error(NotImplementedError)
- lambda { @obj.clear? }.should raise_error(NotImplementedError)
- lambda { @obj.single_value? }.should raise_error(NotImplementedError)
- lambda { @obj.done_read }.should raise_error(NotImplementedError)
- lambda { @obj._do_read(nil) }.should raise_error(NotImplementedError)
- lambda { @obj._do_write(nil) }.should raise_error(NotImplementedError)
- lambda { @obj._do_num_bytes }.should raise_error(NotImplementedError)
- lambda { @obj._snapshot }.should raise_error(NotImplementedError)
+ it "should forward read to _do_read" do
+ @obj.mock.should_receive(:clear).ordered
+ @obj.mock.should_receive(:_do_read).ordered
+ @obj.mock.should_receive(:_done_read).ordered
+ @obj.read(nil)
end
-end
-describe BinData::Base, "when subclassing as a single value" do
- before(:all) do
- eval <<-END
- class SingleValueSubClassOfBase < BaseStub
- def single_value?; true; end
- def value; 123; end
- end
- END
+ it "should forward write to _do_write" do
+ @obj.mock.should_receive(:_do_write)
+ @obj.write(nil)
end
- before(:each) do
- @obj = SingleValueSubClassOfBase.new
+ it "should forward num_bytes to _do_num_bytes" do
+ @obj.mock.should_receive(:_do_num_bytes).and_return(42)
+ @obj.num_bytes.should == 42
end
- it "should return value when reading" do
- SingleValueSubClassOfBase.read("").should == 123
+ it "should round up fractional num_bytes" do
+ @obj.mock.should_receive(:_do_num_bytes).and_return(42.1)
+ @obj.num_bytes.should == 43
end
-end
-describe BinData::Base, "when subclassing as multi values" do
- before(:all) do
- eval <<-END
- class MultiValueSubClassOfBase < BaseStub
- def single_value?; false; end
- def value; 123; end
- end
- END
- end
-
- it "should return self when reading" do
- obj = MultiValueSubClassOfBase.read("")
- obj.class.should == MultiValueSubClassOfBase
- end
-end
-
-describe BinData::Base do
- before(:all) do
- eval <<-END
- class InstanceOfBase < BaseStub
- def snapshot; 123; end
- def _do_write(io); io.writebytes('456'); end
- end
- END
- end
-
- before(:each) do
- @obj = InstanceOfBase.new
- end
-
- it "should forward #inspect to snapshot" do
- @obj.inspect.should == 123.inspect
- end
-
- it "should write the same as to_s" do
- io = StringIO.new
- @obj.write(io)
- io.rewind
- io.read.should == @obj.to_s
+ it "should forward snapshot to _snapshot" do
+ @obj.mock.should_receive(:_snapshot).and_return("abc")
+ @obj.snapshot.should == "abc"
end
end