spec/int_spec.rb in bindata-0.9.3 vs spec/int_spec.rb in bindata-0.10.0
- old
+ new
@@ -1,163 +1,253 @@
#!/usr/bin/env ruby
require File.expand_path(File.dirname(__FILE__)) + '/spec_common'
-require 'bindata/int'
+require 'bindata'
-describe "All signed integers" do
+share_examples_for "All Integers" do
+
+ it "should have correct num_bytes" do
+ all_classes do |int_class|
+ int_class.new.num_bytes.should == @nbytes
+ end
+ end
+
it "should have a sensible value of zero" do
- [BinData::Int8,
- BinData::Int16le,
- BinData::Int16be,
- BinData::Int32le,
- BinData::Int32be,
- BinData::Int64le,
- BinData::Int64be,
- BinData::Int128le,
- BinData::Int128be].each do |klass|
- klass.new.value.should be_zero
+ all_classes do |int_class|
+ int_class.new.value.should be_zero
end
end
- it "should pass these tests" do
- [
- [ 1, true, BinData::Int8],
- [ 2, false, BinData::Int16le],
- [ 2, true, BinData::Int16be],
- [ 4, false, BinData::Int32le],
- [ 4, true, BinData::Int32be],
- [ 8, false, BinData::Int64le],
- [ 8, true, BinData::Int64be],
- [16, false, BinData::Int128le],
- [16, true, BinData::Int128be],
- ].each do |nbytes, big_endian, klass|
- gen_int_test_data(nbytes, big_endian).each do |val, clamped_val, str|
- test_read_write(klass, val, clamped_val, str)
- end
+ it "should avoid underflow" do
+ all_classes do |int_class|
+ obj = int_class.new
+ obj.value = min_value - 1
+
+ obj.value.should == min_value
end
end
-end
-describe "All unsigned integers" do
- it "should have a sensible value of zero" do
- [BinData::Uint8,
- BinData::Uint16le,
- BinData::Uint16be,
- BinData::Uint32le,
- BinData::Uint32be,
- BinData::Uint64le,
- BinData::Uint64be,
- BinData::Uint128le,
- BinData::Uint128be].each do |klass|
- klass.new.value.should be_zero
+ it "should avoid overflow" do
+ all_classes do |int_class|
+ obj = int_class.new
+ obj.value = max_value + 1
+
+ obj.value.should == max_value
end
end
- it "should pass these tests" do
- [
- [ 1, true, BinData::Uint8],
- [ 2, false, BinData::Uint16le],
- [ 2, true, BinData::Uint16be],
- [ 4, false, BinData::Uint32le],
- [ 4, true, BinData::Uint32be],
- [ 8, false, BinData::Uint64le],
- [ 8, true, BinData::Uint64be],
- [16, false, BinData::Uint128le],
- [16, true, BinData::Uint128be],
- ].each do |nbytes, big_endian, klass|
- gen_uint_test_data(nbytes, big_endian).each do |val, clamped_val, str|
- test_read_write(klass, val, clamped_val, str)
+ it "should assign values" do
+ all_classes do |int_class|
+ obj = int_class.new
+ test_int = gen_test_int
+ obj.assign(test_int)
+ obj.value.should == test_int
+ end
+ end
+
+ it "should assign values from other int objects" do
+ all_classes do |int_class|
+ src = int_class.new
+ src.assign(gen_test_int)
+
+ obj = int_class.new
+ obj.assign(src)
+ obj.value.should == src.value
+ end
+ end
+
+ it "should symmetrically read and write a +ve number" do
+ all_classes do |int_class|
+ obj = int_class.new
+ obj.value = gen_test_int
+
+ str = obj.to_binary_s
+ int_class.read(str).should == obj.value
+ end
+ end
+
+ it "should symmetrically read and write a -ve number" do
+ all_classes do |int_class|
+ if @signed
+ obj = int_class.new
+ obj.value = -gen_test_int
+
+ str = obj.to_binary_s
+ int_class.read(str).should == obj.value
end
end
end
-end
-# run read / write tests for the given values
-def test_read_write(klass, val, clamped_val, str)
- # set the data and ensure clamping occurs
- data = klass.new
- data.value = val
- data.value.should == clamped_val
-
- # write the data
- io = StringIO.new
- data.write(io)
+ it "should convert a +ve number to string" do
+ all_classes do |int_class|
+ val = gen_test_int
- # check that we write the expected byte pattern
- io.rewind
- io.read.should == str
+ obj = int_class.new
+ obj.value = val
- # check that we read in the same data that was written
- io.rewind
- data = klass.new
- data.read(io)
- data.value.should == clamped_val
-end
+ obj.to_binary_s.should == int_to_binary_str(val)
+ end
+ end
-# return test data for testing unsigned ints
-def gen_uint_test_data(nbytes, big_endian)
- raise "nbytes too large" if nbytes > 16
- tests = []
+ it "should convert a -ve number to string" do
+ all_classes do |int_class|
+ if @signed
+ val = -gen_test_int
- max_value = (1 << (nbytes * 8)) - 1
- min_value = 0
+ obj = int_class.new
+ obj.value = val
- # test the minimum value
- v = min_value
- s = "\x00" * nbytes
- tests.push [v, v, big_endian ? s : s.reverse]
+ obj.to_binary_s.should == int_to_binary_str(val)
+ end
+ end
+ end
- # values below minimum should be clamped to minimum
- tests.push [v-1, v, big_endian ? s : s.reverse]
+ def all_classes(&block)
+ @ints.each_pair do |int_class, nbytes|
+ @nbytes = nbytes
+ yield int_class
+ end
+ end
- # test a value within range
- v = 0x123456789abcdef0123456789abcdef0 >> ((16-nbytes) * 8)
- s = "\x12\x34\x56\x78\x9a\xbc\xde\xf0\x12\x34\x56\x78\x9a\xbc\xde\xf0".slice(0, nbytes)
- tests.push [v, v, big_endian ? s : s.reverse]
+ def min_value
+ if @signed
+ -max_value - 1
+ else
+ 0
+ end
+ end
- # test the maximum value
- v = max_value
- s = "\xff" * nbytes
- tests.push [v, v, big_endian ? s : s.reverse]
+ def max_value
+ if @signed
+ (1 << (@nbytes * 8 - 1)) - 1
+ else
+ (1 << (@nbytes * 8)) - 1
+ end
+ end
- # values above maximum should be clamped to maximum
- tests.push [v+1, v, big_endian ? s : s.reverse]
+ def gen_test_int
+ # resulting int is guaranteed to be +ve for signed or unsigned integers
+ (0 ... @nbytes).inject(0) { |val, i| (val << 8) | ((val + 0x11) % 0x100) }
+ end
- tests
+ def int_to_binary_str(val)
+ str = ""
+ v = val & ((1 << (@nbytes * 8)) - 1)
+ @nbytes.times do
+ str.concat(v & 0xff)
+ v >>= 8
+ end
+ (@endian == :little) ? str : str.reverse
+ end
end
-# return test data for testing signed ints
-def gen_int_test_data(nbytes, big_endian)
- raise "nbytes too large" if nbytes > 16
- tests = []
+describe "All signed big endian integers" do
+ it_should_behave_like "All Integers"
- max_value = (1 << (nbytes * 8 - 1)) - 1
- min_value = -max_value - 1
+ before(:all) do
+ BinData::Integer.define_class(24, :big, :signed)
+ BinData::Integer.define_class(48, :big, :signed)
+ BinData::Integer.define_class(96, :big, :signed)
+ @endian = :big
+ @signed = true
+ @ints = {
+ BinData::Int8 => 1,
+ BinData::Int8be => 1,
+ BinData::Int16be => 2,
+ BinData::Int24be => 3,
+ BinData::Int32be => 4,
+ BinData::Int48be => 6,
+ BinData::Int64be => 8,
+ BinData::Int96be => 12,
+ BinData::Int128be => 16,
+ }
+ end
+end
- # test the minimum value
- v = min_value
- s = "\x80" + "\x00" * (nbytes - 1)
- tests.push [v, v, big_endian ? s : s.reverse]
+describe "All unsigned big endian integers" do
+ it_should_behave_like "All Integers"
- # values below minimum should be clamped to minimum
- tests.push [v-1, v, big_endian ? s : s.reverse]
+ before(:all) do
+ BinData::Integer.define_class(24, :big, :unsigned)
+ BinData::Integer.define_class(48, :big, :unsigned)
+ BinData::Integer.define_class(96, :big, :unsigned)
+ @endian = :big
+ @signed = false
+ @ints = {
+ BinData::Uint8 => 1,
+ BinData::Uint8be => 1,
+ BinData::Uint16be => 2,
+ BinData::Uint24be => 3,
+ BinData::Uint32be => 4,
+ BinData::Uint48be => 6,
+ BinData::Uint64be => 8,
+ BinData::Uint96be => 12,
+ BinData::Uint128be => 16,
+ }
+ end
+end
- # test a -ve value within range
- v = -1
- s = "\xff" * nbytes
- tests.push [v, v, big_endian ? s : s.reverse]
+describe "All signed little endian integers" do
+ it_should_behave_like "All Integers"
- # test a +ve value within range
- v = 0x123456789abcdef0123456789abcdef0 >> ((16-nbytes) * 8)
- s = "\x12\x34\x56\x78\x9a\xbc\xde\xf0\x12\x34\x56\x78\x9a\xbc\xde\xf0".slice(0, nbytes)
- tests.push [v, v, big_endian ? s : s.reverse]
+ before(:all) do
+ BinData::Integer.define_class(24, :little, :signed)
+ BinData::Integer.define_class(48, :little, :signed)
+ BinData::Integer.define_class(96, :little, :signed)
+ @endian = :little
+ @signed = true
+ @ints = {
+ BinData::Int8 => 1,
+ BinData::Int8le => 1,
+ BinData::Int16le => 2,
+ BinData::Int24le => 3,
+ BinData::Int32le => 4,
+ BinData::Int48le => 6,
+ BinData::Int64le => 8,
+ BinData::Int96le => 12,
+ BinData::Int128le => 16,
+ }
+ end
+end
- # test the maximum value
- v = max_value
- s = "\x7f" + "\xff" * (nbytes - 1)
- tests.push [v, v, big_endian ? s : s.reverse]
+describe "All unsigned little endian integers" do
+ it_should_behave_like "All Integers"
- # values above maximum should be clamped to maximum
- tests.push [v+1, v, big_endian ? s : s.reverse]
+ before(:all) do
+ BinData::Integer.define_class(24, :little, :unsigned)
+ BinData::Integer.define_class(48, :little, :unsigned)
+ BinData::Integer.define_class(96, :little, :unsigned)
+ @endian = :little
+ @signed = false
+ @ints = {
+ BinData::Uint8 => 1,
+ BinData::Uint8le => 1,
+ BinData::Uint16le => 2,
+ BinData::Uint24le => 3,
+ BinData::Uint32le => 4,
+ BinData::Uint48le => 6,
+ BinData::Uint64le => 8,
+ BinData::Uint96le => 12,
+ BinData::Uint128le => 16,
+ }
+ end
+end
- tests
+describe "Custom defined integers" do
+ it "should fail unless bits are a multiple of 8" do
+ lambda {
+ BinData::Integer.define_class(7, :little, :unsigned)
+ }.should raise_error
+
+ lambda {
+ BinData::Integer.define_class(7, :big, :unsigned)
+ }.should raise_error
+
+ lambda {
+ BinData::Integer.define_class(7, :little, :signed)
+ }.should raise_error
+
+ lambda {
+ BinData::Integer.define_class(7, :big, :signed)
+ }.should raise_error
+ end
end