spec/packets/structure_spec.rb in cosmos-3.5.0 vs spec/packets/structure_spec.rb in cosmos-3.5.1

- old
+ new

@@ -1,565 +1,565 @@ -# encoding: ascii-8bit - -# Copyright 2014 Ball Aerospace & Technologies Corp. -# All Rights Reserved. -# -# This program is free software; you can modify and/or redistribute it -# under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 3 with -# attribution addendums as found in the LICENSE.txt - -require 'spec_helper' -require 'cosmos' -require 'cosmos/packets/structure' - -module Cosmos - - describe Structure do - - describe "initialize" do - it "complains about non string buffers" do - expect { Structure.new(:BIG_ENDIAN, Array.new) }.to raise_error(TypeError, "wrong argument type Array (expected String)") - end - - it "complains about unrecognized data types" do - expect { Structure.new(:BLAH) }.to raise_error(ArgumentError, "Unrecognized endianness: BLAH - Must be :BIG_ENDIAN or :LITTLE_ENDIAN") - end - - it "creates BIG_ENDIAN structures" do - expect(Structure.new(:BIG_ENDIAN).default_endianness).to eql :BIG_ENDIAN - end - - it "creates LITTLE_ENDIAN structures" do - expect(Structure.new(:LITTLE_ENDIAN).default_endianness).to eql :LITTLE_ENDIAN - end - end # describe "initialize" - - describe "defined?" do - it "returns true if any items have been defined" do - s = Structure.new - expect(s.defined?).to be false - s.define_item("test1",0,8,:UINT) - expect(s.defined?).to be true - end - end - - describe "rename_item" do - it "renames a previously defined item" do - s = Structure.new - expect(s.items["test1"]).to be_nil - expect(s.sorted_items[0]).to be_nil - s.define_item("test1", 0, 8, :UINT) - expect(s.items["TEST1"]).not_to be_nil - expect(s.sorted_items[0]).not_to be_nil - expect(s.sorted_items[0].name).to eql "TEST1" - s.rename_item("TEST1", "TEST2") - expect(s.items["TEST1"]).to be_nil - expect(s.items["TEST2"]).not_to be_nil - expect(s.sorted_items[0].name).to eql "TEST2" - end - end - - describe "define_item" do - before(:each) do - @s = Structure.new - end - - it "adds item to items and sorted_items" do - expect(@s.items["test1"]).to be_nil - expect(@s.sorted_items[0]).to be_nil - @s.define_item("test1", 0, 8, :UINT) - expect(@s.items["TEST1"]).not_to be_nil - expect(@s.sorted_items[0]).not_to be_nil - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.defined_length).to eql 1 - expect(@s.fixed_size).to be true - end - - it "adds items with negative bit offsets" do - @s.define_item("test1", -8, 8, :UINT) - expect(@s.defined_length).to eql 1 - @s.define_item("test2", 0, 4, :UINT) - expect(@s.defined_length).to eql 2 - @s.define_item("test3", 4, 4, :UINT) - expect(@s.defined_length).to eql 2 - @s.define_item("test4", 16, 0, :BLOCK) - expect(@s.defined_length).to eql 3 - @s.define_item("test5", -16, 8, :UINT) - expect(@s.defined_length).to eql 4 - expect(@s.fixed_size).to be false - end - - it "adds item with negative offset" do - expect { @s.define_item("test11", -64, 8, :UINT, 128) }.to raise_error(ArgumentError, "TEST11: Can't define an item with array_size 128 greater than negative bit_offset -64") - expect { @s.define_item("test10", -64, 8, :UINT, -64) }.to raise_error(ArgumentError, "TEST10: Can't define an item with negative array_size -64 and negative bit_offset -64") - expect { @s.define_item("test9", -64, -64, :BLOCK) }.to raise_error(ArgumentError, "TEST9: Can't define an item with negative bit_size -64 and negative bit_offset -64") - expect { @s.define_item("test8", 0, -32, :BLOCK, 64) }.to raise_error(ArgumentError, "TEST8: bit_size cannot be negative or zero for array items") - expect { @s.define_item("test7", 0, 0, :BLOCK, 64) }.to raise_error(ArgumentError, "TEST7: bit_size cannot be negative or zero for array items") - expect { @s.define_item("test6", -24, 32, :UINT) }.to raise_error(ArgumentError, "TEST6: Can't define an item with bit_size 32 greater than negative bit_offset -24") - @s.define_item("test5", -16, 8, :UINT) - expect(@s.defined_length).to eql 2 - @s.define_item("test1", -8, 8, :UINT) - expect(@s.defined_length).to eql 2 - @s.define_item("test2", 0, 4, :UINT) - expect(@s.defined_length).to eql 3 - @s.define_item("test3", 4, 4, :UINT) - expect(@s.defined_length).to eql 3 - @s.define_item("test4", 8, 0, :BLOCK) - expect(@s.defined_length).to eql 3 - expect(@s.fixed_size).to be false - end - - it "recalulates sorted_items when adding multiple items" do - @s.define_item("test1", 8, 32, :UINT) - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.defined_length).to eql 5 - @s.define_item("test2", 0, 8, :UINT) - expect(@s.sorted_items[0].name).to eql "TEST2" - expect(@s.defined_length).to eql 5 - @s.define_item("test3", 16, 8, :UINT) - expect(@s.sorted_items[-1].name).to eql "TEST3" - expect(@s.defined_length).to eql 5 - expect(@s.fixed_size).to be true - end - - it "overwrites existing items" do - @s.define_item("test1", 0, 8, :UINT) - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.items["TEST1"].bit_size).to eql 8 - expect(@s.items["TEST1"].data_type).to eql :UINT - expect(@s.defined_length).to eql 1 - @s.define_item("test1", 0, 16, :INT) - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.items["TEST1"].bit_size).to eql 16 - expect(@s.items["TEST1"].data_type).to eql :INT - expect(@s.defined_length).to eql 2 - expect(@s.fixed_size).to be true - end - end # describe "define_item" - - describe "define" do - before(:each) do - @s = Structure.new - end - - it "adds the item to items and sorted_items" do - expect(@s.items["test1"]).to be_nil - expect(@s.sorted_items[0]).to be_nil - si = StructureItem.new("test1",0,8,:UINT,:BIG_ENDIAN) - @s.define(si) - expect(@s.items["TEST1"]).not_to be_nil - expect(@s.sorted_items[0]).not_to be_nil - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.defined_length).to eql 1 - expect(@s.fixed_size).to be true - end - - it "allows items to be defined on top of each other" do - expect(@s.items["test1"]).to be_nil - expect(@s.sorted_items[0]).to be_nil - si = StructureItem.new("test1",0,8,:UINT,:BIG_ENDIAN) - @s.define(si) - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.items["TEST1"].bit_offset).to eql 0 - expect(@s.items["TEST1"].bit_size).to eql 8 - expect(@s.items["TEST1"].data_type).to eql :UINT - expect(@s.defined_length).to eql 1 - si = StructureItem.new("test2",0,16,:INT,:BIG_ENDIAN) - @s.define(si) - expect(@s.sorted_items[1].name).to eql "TEST2" - expect(@s.items["TEST2"].bit_offset).to eql 0 - expect(@s.items["TEST2"].bit_size).to eql 16 - expect(@s.items["TEST2"].data_type).to eql :INT - expect(@s.defined_length).to eql 2 - buffer = "\x01\x02" - expect(@s.read_item(@s.get_item("test1"), :RAW, buffer)).to eql 1 - expect(@s.read_item(@s.get_item("test2"), :RAW, buffer)).to eql 258 - end - - it "overwrites existing items" do - si = StructureItem.new("test1",0,8,:UINT,:BIG_ENDIAN) - @s.define(si) - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.items["TEST1"].bit_size).to eql 8 - expect(@s.items["TEST1"].data_type).to eql :UINT - expect(@s.defined_length).to eql 1 - si = StructureItem.new("test1",0,16,:INT,:BIG_ENDIAN) - @s.define(si) - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.items["TEST1"].bit_size).to eql 16 - expect(@s.items["TEST1"].data_type).to eql :INT - expect(@s.defined_length).to eql 2 - expect(@s.fixed_size).to be true - end - end - - describe "append_item" do - before(:each) do - @s = Structure.new - end - - it "appends an item to items" do - @s.define_item("test1", 0, 8, :UINT) - @s.append_item("test2", 16, :UINT) - expect(@s.items["TEST2"].bit_size).to eql 16 - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.sorted_items[1].name).to eql "TEST2" - expect(@s.defined_length).to eql 3 - end - - it "appends an item after an array item " do - @s.define_item("test1", 0, 8, :UINT, 16) - expect(@s.items["TEST1"].bit_size).to eql 8 - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.sorted_items[1]).to be_nil - expect(@s.defined_length).to eql 2 - @s.append_item("test2", 16, :UINT) - expect(@s.items["TEST2"].bit_size).to eql 16 - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.sorted_items[1].name).to eql "TEST2" - expect(@s.defined_length).to eql 4 - end - - it "complains if appending after a variably sized item" do - @s.define_item("test1", 0, 0, :BLOCK) - expect { @s.append_item("test2", 8, :UINT) }.to raise_error(ArgumentError, "Can't append an item after a variably sized item") - end - - it "complains if appending after a variably sized array" do - @s.define_item("test1", 0, 8, :UINT, -8) - expect { @s.append_item("test2", 8, :UINT) }.to raise_error(ArgumentError, "Can't append an item after a variably sized item") - end - end - - describe "append" do - before(:each) do - @s = Structure.new - end - - it "appends an item to the structure" do - @s.define_item("test1", 0, 8, :UINT) - item = StructureItem.new("test2", 0, 16, :UINT, :BIG_ENDIAN) - @s.append(item) - # Bit offset should change because we appended the item - expect(@s.items["TEST2"].bit_offset).to eql 8 - expect(@s.sorted_items[0].name).to eql "TEST1" - expect(@s.sorted_items[1].name).to eql "TEST2" - expect(@s.defined_length).to eql 3 - end - - it "complains if appending after a variably sized item" do - @s.define_item("test1", 0, 0, :BLOCK) - expect { @s.append(@item) }.to raise_error(ArgumentError, "Can't append an item after a variably sized item") - end - end - - describe "get_item" do - before(:each) do - @s = Structure.new - @s.define_item("test1", 0, 8, :UINT) - end - - it "returns a defined item" do - expect(@s.get_item("test1")).not_to be_nil - end - - it "complains if an item doesn't exist" do - expect { @s.get_item("test2") }.to raise_error(ArgumentError, "Unknown item: test2") - end - end - - describe "set_item" do - before(:each) do - @s = Structure.new - @s.define_item("test1", 0, 8, :UINT) - end - - it "sets a defined item" do - item = @s.get_item("test1") - expect(item.bit_size).to eql 8 - item.bit_size = 16 - @s.set_item(item) - expect(@s.get_item("test1").bit_size).to eql 16 - end - - it "complains if an item doesn't exist" do - item = @s.get_item("test1") - item.name = "TEST2" - expect { @s.set_item(item) }.to raise_error(ArgumentError, "Unknown item: TEST2 - Ensure item name is uppercase") - end - end - - describe "read_item" do - it "complains if no buffer given" do - s = Structure.new - s.define_item("test1", 0, 8, :UINT) - expect { s.read_item(s.get_item("test1"), :RAW, nil) }.to raise_error(RuntimeError, "No buffer given to read_item") - end - - it "reads data from the buffer" do - s = Structure.new - s.define_item("test1", 0, 8, :UINT) - buffer = "\x01" - expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql 1 - end - - it "reads array data from the buffer" do - s = Structure.new - s.define_item("test1", 0, 8, :UINT, 16) - buffer = "\x01\x02" - expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql [1,2] - end - end - - describe "write_item" do - it "complains if no buffer given" do - expect { Structure.new.write_item(nil, nil, nil, nil) }.to raise_error(RuntimeError, "No buffer given to write_item") - end - - it "writes data to the buffer" do - s = Structure.new - s.define_item("test1", 0, 8, :UINT) - buffer = "\x01" - expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql 1 - s.write_item(s.get_item("test1"), 2, :RAW, buffer) - expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql 2 - end - - it "writes array data to the buffer" do - s = Structure.new - s.define_item("test1", 0, 8, :UINT, 16) - buffer = "\x01\x02" - expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql [1,2] - s.write_item(s.get_item("test1"), [3,4], :RAW, buffer) - expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql [3,4] - end - end - - describe "read" do - it "complains if item doesn't exist" do - expect { Structure.new.read("BLAH") }.to raise_error(ArgumentError, "Unknown item: BLAH") - end - - it "reads data from the buffer" do - s = Structure.new - s.define_item("test1", 0, 8, :UINT) - buffer = "\x01" - expect(s.read("test1", :RAW, buffer)).to eql 1 - end - - it "reads array data from the buffer" do - s = Structure.new - s.define_item("test1", 0, 8, :UINT, 16) - buffer = "\x01\x02" - expect(s.read("test1", :RAW, buffer)).to eql [1,2] - end - end - - describe "write" do - it "complains if item doesn't exist" do - expect { Structure.new.write("BLAH", 0) }.to raise_error(ArgumentError, "Unknown item: BLAH") - end - - it "writes data to the buffer" do - s = Structure.new - s.define_item("test1", 0, 8, :UINT) - buffer = "\x01" - expect(s.read("test1", :RAW, buffer)).to eql 1 - s.write("test1", 2, :RAW, buffer) - expect(s.read("test1", :RAW, buffer)).to eql 2 - end - - it "writes array data to the buffer" do - s = Structure.new - s.define_item("test1", 0, 8, :UINT, 16) - buffer = "\x01\x02" - expect(s.read("test1", :RAW, buffer)).to eql [1,2] - s.write("test1", [3,4], :RAW, buffer) - expect(s.read("test1", :RAW, buffer)).to eql [3,4] - end - end - - describe "read_all" do - it "reads all defined items" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT, 16) - s.append_item("test2", 16, :UINT) - s.append_item("test3", 32, :UINT) - - buffer = "\x01\x02\x03\x04\x05\x06\x07\x08" - vals = s.read_all(:RAW, buffer) - expect(vals[0][0]).to eql "TEST1" - expect(vals[1][0]).to eql "TEST2" - expect(vals[2][0]).to eql "TEST3" - expect(vals[0][1]).to eql [1,2] - expect(vals[1][1]).to eql 0x0304 - expect(vals[2][1]).to eql 0x05060708 - end - - it "reads all defined items synchronized" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT, 16) - s.append_item("test2", 16, :UINT) - s.append_item("test3", 32, :UINT) - - buffer = "\x01\x02\x03\x04\x05\x06\x07\x08" - vals = s.read_all(:RAW, buffer, false) - expect(vals[0][0]).to eql "TEST1" - expect(vals[1][0]).to eql "TEST2" - expect(vals[2][0]).to eql "TEST3" - expect(vals[0][1]).to eql [1,2] - expect(vals[1][1]).to eql 0x0304 - expect(vals[2][1]).to eql 0x05060708 - end - end - - describe "formatted" do - it "prints out all the items and values" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT, 16) - s.write("test1", [1,2]) - s.append_item("test2", 16, :UINT) - s.write("test2", 3456) - s.append_item("test3", 32, :BLOCK) - s.write("test3", "\x07\x08\x09\x0A") - expect(s.formatted).to include("TEST1: [1, 2]") - expect(s.formatted).to include("TEST2: 3456") - expect(s.formatted).to include("TEST3") - expect(s.formatted).to include("00000000: 07 08 09 0A") - end - - it "alters the indentation of the item" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT, 16) - s.write("test1", [1,2]) - s.append_item("test2", 16, :UINT) - s.write("test2", 3456) - s.append_item("test3", 32, :BLOCK) - s.write("test3", "\x07\x08\x09\x0A") - expect(s.formatted(:CONVERTED, 4)).to include(" TEST1: [1, 2]") - expect(s.formatted(:CONVERTED, 4)).to include(" TEST2: 3456") - expect(s.formatted(:CONVERTED, 4)).to include(" TEST3") - expect(s.formatted(:CONVERTED, 4)).to include(" 00000000: 07 08 09 0A") - end - end - - describe "buffer" do - it "returns the buffer" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT, 16) - s.write("test1", [1,2]) - s.append_item("test2", 16, :UINT) - s.write("test2", 0x0304) - s.append_item("test3", 32, :UINT) - s.write("test3", 0x05060708) - expect(s.buffer).to eql "\x01\x02\x03\x04\x05\x06\x07\x08" - expect(s.buffer).to_not be s.buffer - expect(s.buffer(false)).to be s.buffer(false) - end - end - - describe "buffer=" do - it "complains if the given buffer is too small" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 16, :UINT) - expect { s.buffer = "\x00" }.to raise_error(RuntimeError, "Buffer length less than defined length") - end - - it "complains if the given buffer is too big" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 16, :UINT) - expect { s.buffer = "\x00\x00\x00" }.to raise_error(RuntimeError, "Buffer length greater than defined length") - end - - it "does not complain if the given buffer is too big and we're not fixed length" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT) - s.append_item("test2", 0, :BLOCK) - s.buffer = "\x01\x02\x03" - expect(s.read("test1")).to eql 1 - expect(s.read("test2")).to eql "\x02\x03" - end - - it "sets the buffer" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT, 16) - s.write("test1", [1,2]) - s.append_item("test2", 16, :UINT) - s.write("test2", 0x0304) - s.append_item("test3", 32, :UINT) - s.write("test3", 0x05060708) - expect(s.read("test1")).to eql [1,2] - expect(s.read("test2")).to eql 0x0304 - expect(s.read("test3")).to eql 0x05060708 - s.buffer = "\x00\x01\x02\x03\x04\x05\x06\x07" - expect(s.read("test1")).to eql [0,1] - expect(s.read("test2")).to eql 0x0203 - expect(s.read("test3")).to eql 0x04050607 - end - end - - describe "clone" do - it "duplicates the structure with a new buffer" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT, 16) - s.write("test1", [1,2]) - s.append_item("test2", 16, :UINT) - s.write("test2", 0x0304) - s.append_item("test3", 32, :UINT) - s.write("test3", 0x05060708) - # Get a reference to the original buffer - old_buffer = s.buffer(false) - - s2 = s.clone - # Ensure we didn't modify the original buffer object - expect(s.buffer(false)).to be old_buffer - # Check that they are equal in value - expect(s2.buffer(false)).to eql s.buffer(false) - # But not the same object - expect(s2.buffer(false)).to_not be s.buffer(false) - expect(s2.read("test1")).to eql [1,2] - expect(s2.read("test2")).to eql 0x0304 - expect(s2.read("test3")).to eql 0x05060708 - s2.write("test1", [0,0]) - expect(s2.read("test1")).to eql [0,0] - # Ensure we didn't change the original - expect(s.read("test1")).to eql [1,2] - end - end - - describe "enable_method_missing" do - it "enables reading by name" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT, 16) - s.write("test1", [1,2]) - s.enable_method_missing - expect(s.test1).to eql [1,2] - end - - it "enables writing by name" do - s = Structure.new(:BIG_ENDIAN) - s.append_item("test1", 8, :UINT, 16) - s.write("test1", [1,2]) - s.enable_method_missing - expect(s.test1).to eql [1,2] - s.test1 = [3,4] - expect(s.test1).to eql [3,4] - end - - it "raises an exception if there is no buffer" do - s = Structure.new(:BIG_ENDIAN, nil) - s.append_item("test1", 8, :UINT, 16) - s.enable_method_missing - expect { s.test1 }.to raise_error(/No buffer/) - end - - it "complains if it can't find an item" do - s = Structure.new(:BIG_ENDIAN) - s.enable_method_missing - expect { s.test1 }.to raise_error(ArgumentError, "Unknown item: test1") - end - end - - end # describe Structure - -end +# encoding: ascii-8bit + +# Copyright 2014 Ball Aerospace & Technologies Corp. +# All Rights Reserved. +# +# This program is free software; you can modify and/or redistribute it +# under the terms of the GNU General Public License +# as published by the Free Software Foundation; version 3 with +# attribution addendums as found in the LICENSE.txt + +require 'spec_helper' +require 'cosmos' +require 'cosmos/packets/structure' + +module Cosmos + + describe Structure do + + describe "initialize" do + it "complains about non string buffers" do + expect { Structure.new(:BIG_ENDIAN, Array.new) }.to raise_error(TypeError, "wrong argument type Array (expected String)") + end + + it "complains about unrecognized data types" do + expect { Structure.new(:BLAH) }.to raise_error(ArgumentError, "Unrecognized endianness: BLAH - Must be :BIG_ENDIAN or :LITTLE_ENDIAN") + end + + it "creates BIG_ENDIAN structures" do + expect(Structure.new(:BIG_ENDIAN).default_endianness).to eql :BIG_ENDIAN + end + + it "creates LITTLE_ENDIAN structures" do + expect(Structure.new(:LITTLE_ENDIAN).default_endianness).to eql :LITTLE_ENDIAN + end + end # describe "initialize" + + describe "defined?" do + it "returns true if any items have been defined" do + s = Structure.new + expect(s.defined?).to be false + s.define_item("test1",0,8,:UINT) + expect(s.defined?).to be true + end + end + + describe "rename_item" do + it "renames a previously defined item" do + s = Structure.new + expect(s.items["test1"]).to be_nil + expect(s.sorted_items[0]).to be_nil + s.define_item("test1", 0, 8, :UINT) + expect(s.items["TEST1"]).not_to be_nil + expect(s.sorted_items[0]).not_to be_nil + expect(s.sorted_items[0].name).to eql "TEST1" + s.rename_item("TEST1", "TEST2") + expect(s.items["TEST1"]).to be_nil + expect(s.items["TEST2"]).not_to be_nil + expect(s.sorted_items[0].name).to eql "TEST2" + end + end + + describe "define_item" do + before(:each) do + @s = Structure.new + end + + it "adds item to items and sorted_items" do + expect(@s.items["test1"]).to be_nil + expect(@s.sorted_items[0]).to be_nil + @s.define_item("test1", 0, 8, :UINT) + expect(@s.items["TEST1"]).not_to be_nil + expect(@s.sorted_items[0]).not_to be_nil + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.defined_length).to eql 1 + expect(@s.fixed_size).to be true + end + + it "adds items with negative bit offsets" do + @s.define_item("test1", -8, 8, :UINT) + expect(@s.defined_length).to eql 1 + @s.define_item("test2", 0, 4, :UINT) + expect(@s.defined_length).to eql 2 + @s.define_item("test3", 4, 4, :UINT) + expect(@s.defined_length).to eql 2 + @s.define_item("test4", 16, 0, :BLOCK) + expect(@s.defined_length).to eql 3 + @s.define_item("test5", -16, 8, :UINT) + expect(@s.defined_length).to eql 4 + expect(@s.fixed_size).to be false + end + + it "adds item with negative offset" do + expect { @s.define_item("test11", -64, 8, :UINT, 128) }.to raise_error(ArgumentError, "TEST11: Can't define an item with array_size 128 greater than negative bit_offset -64") + expect { @s.define_item("test10", -64, 8, :UINT, -64) }.to raise_error(ArgumentError, "TEST10: Can't define an item with negative array_size -64 and negative bit_offset -64") + expect { @s.define_item("test9", -64, -64, :BLOCK) }.to raise_error(ArgumentError, "TEST9: Can't define an item with negative bit_size -64 and negative bit_offset -64") + expect { @s.define_item("test8", 0, -32, :BLOCK, 64) }.to raise_error(ArgumentError, "TEST8: bit_size cannot be negative or zero for array items") + expect { @s.define_item("test7", 0, 0, :BLOCK, 64) }.to raise_error(ArgumentError, "TEST7: bit_size cannot be negative or zero for array items") + expect { @s.define_item("test6", -24, 32, :UINT) }.to raise_error(ArgumentError, "TEST6: Can't define an item with bit_size 32 greater than negative bit_offset -24") + @s.define_item("test5", -16, 8, :UINT) + expect(@s.defined_length).to eql 2 + @s.define_item("test1", -8, 8, :UINT) + expect(@s.defined_length).to eql 2 + @s.define_item("test2", 0, 4, :UINT) + expect(@s.defined_length).to eql 3 + @s.define_item("test3", 4, 4, :UINT) + expect(@s.defined_length).to eql 3 + @s.define_item("test4", 8, 0, :BLOCK) + expect(@s.defined_length).to eql 3 + expect(@s.fixed_size).to be false + end + + it "recalulates sorted_items when adding multiple items" do + @s.define_item("test1", 8, 32, :UINT) + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.defined_length).to eql 5 + @s.define_item("test2", 0, 8, :UINT) + expect(@s.sorted_items[0].name).to eql "TEST2" + expect(@s.defined_length).to eql 5 + @s.define_item("test3", 16, 8, :UINT) + expect(@s.sorted_items[-1].name).to eql "TEST3" + expect(@s.defined_length).to eql 5 + expect(@s.fixed_size).to be true + end + + it "overwrites existing items" do + @s.define_item("test1", 0, 8, :UINT) + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.items["TEST1"].bit_size).to eql 8 + expect(@s.items["TEST1"].data_type).to eql :UINT + expect(@s.defined_length).to eql 1 + @s.define_item("test1", 0, 16, :INT) + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.items["TEST1"].bit_size).to eql 16 + expect(@s.items["TEST1"].data_type).to eql :INT + expect(@s.defined_length).to eql 2 + expect(@s.fixed_size).to be true + end + end # describe "define_item" + + describe "define" do + before(:each) do + @s = Structure.new + end + + it "adds the item to items and sorted_items" do + expect(@s.items["test1"]).to be_nil + expect(@s.sorted_items[0]).to be_nil + si = StructureItem.new("test1",0,8,:UINT,:BIG_ENDIAN) + @s.define(si) + expect(@s.items["TEST1"]).not_to be_nil + expect(@s.sorted_items[0]).not_to be_nil + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.defined_length).to eql 1 + expect(@s.fixed_size).to be true + end + + it "allows items to be defined on top of each other" do + expect(@s.items["test1"]).to be_nil + expect(@s.sorted_items[0]).to be_nil + si = StructureItem.new("test1",0,8,:UINT,:BIG_ENDIAN) + @s.define(si) + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.items["TEST1"].bit_offset).to eql 0 + expect(@s.items["TEST1"].bit_size).to eql 8 + expect(@s.items["TEST1"].data_type).to eql :UINT + expect(@s.defined_length).to eql 1 + si = StructureItem.new("test2",0,16,:INT,:BIG_ENDIAN) + @s.define(si) + expect(@s.sorted_items[1].name).to eql "TEST2" + expect(@s.items["TEST2"].bit_offset).to eql 0 + expect(@s.items["TEST2"].bit_size).to eql 16 + expect(@s.items["TEST2"].data_type).to eql :INT + expect(@s.defined_length).to eql 2 + buffer = "\x01\x02" + expect(@s.read_item(@s.get_item("test1"), :RAW, buffer)).to eql 1 + expect(@s.read_item(@s.get_item("test2"), :RAW, buffer)).to eql 258 + end + + it "overwrites existing items" do + si = StructureItem.new("test1",0,8,:UINT,:BIG_ENDIAN) + @s.define(si) + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.items["TEST1"].bit_size).to eql 8 + expect(@s.items["TEST1"].data_type).to eql :UINT + expect(@s.defined_length).to eql 1 + si = StructureItem.new("test1",0,16,:INT,:BIG_ENDIAN) + @s.define(si) + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.items["TEST1"].bit_size).to eql 16 + expect(@s.items["TEST1"].data_type).to eql :INT + expect(@s.defined_length).to eql 2 + expect(@s.fixed_size).to be true + end + end + + describe "append_item" do + before(:each) do + @s = Structure.new + end + + it "appends an item to items" do + @s.define_item("test1", 0, 8, :UINT) + @s.append_item("test2", 16, :UINT) + expect(@s.items["TEST2"].bit_size).to eql 16 + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.sorted_items[1].name).to eql "TEST2" + expect(@s.defined_length).to eql 3 + end + + it "appends an item after an array item " do + @s.define_item("test1", 0, 8, :UINT, 16) + expect(@s.items["TEST1"].bit_size).to eql 8 + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.sorted_items[1]).to be_nil + expect(@s.defined_length).to eql 2 + @s.append_item("test2", 16, :UINT) + expect(@s.items["TEST2"].bit_size).to eql 16 + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.sorted_items[1].name).to eql "TEST2" + expect(@s.defined_length).to eql 4 + end + + it "complains if appending after a variably sized item" do + @s.define_item("test1", 0, 0, :BLOCK) + expect { @s.append_item("test2", 8, :UINT) }.to raise_error(ArgumentError, "Can't append an item after a variably sized item") + end + + it "complains if appending after a variably sized array" do + @s.define_item("test1", 0, 8, :UINT, -8) + expect { @s.append_item("test2", 8, :UINT) }.to raise_error(ArgumentError, "Can't append an item after a variably sized item") + end + end + + describe "append" do + before(:each) do + @s = Structure.new + end + + it "appends an item to the structure" do + @s.define_item("test1", 0, 8, :UINT) + item = StructureItem.new("test2", 0, 16, :UINT, :BIG_ENDIAN) + @s.append(item) + # Bit offset should change because we appended the item + expect(@s.items["TEST2"].bit_offset).to eql 8 + expect(@s.sorted_items[0].name).to eql "TEST1" + expect(@s.sorted_items[1].name).to eql "TEST2" + expect(@s.defined_length).to eql 3 + end + + it "complains if appending after a variably sized item" do + @s.define_item("test1", 0, 0, :BLOCK) + expect { @s.append(@item) }.to raise_error(ArgumentError, "Can't append an item after a variably sized item") + end + end + + describe "get_item" do + before(:each) do + @s = Structure.new + @s.define_item("test1", 0, 8, :UINT) + end + + it "returns a defined item" do + expect(@s.get_item("test1")).not_to be_nil + end + + it "complains if an item doesn't exist" do + expect { @s.get_item("test2") }.to raise_error(ArgumentError, "Unknown item: test2") + end + end + + describe "set_item" do + before(:each) do + @s = Structure.new + @s.define_item("test1", 0, 8, :UINT) + end + + it "sets a defined item" do + item = @s.get_item("test1") + expect(item.bit_size).to eql 8 + item.bit_size = 16 + @s.set_item(item) + expect(@s.get_item("test1").bit_size).to eql 16 + end + + it "complains if an item doesn't exist" do + item = @s.get_item("test1") + item.name = "TEST2" + expect { @s.set_item(item) }.to raise_error(ArgumentError, "Unknown item: TEST2 - Ensure item name is uppercase") + end + end + + describe "read_item" do + it "complains if no buffer given" do + s = Structure.new + s.define_item("test1", 0, 8, :UINT) + expect { s.read_item(s.get_item("test1"), :RAW, nil) }.to raise_error(RuntimeError, "No buffer given to read_item") + end + + it "reads data from the buffer" do + s = Structure.new + s.define_item("test1", 0, 8, :UINT) + buffer = "\x01" + expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql 1 + end + + it "reads array data from the buffer" do + s = Structure.new + s.define_item("test1", 0, 8, :UINT, 16) + buffer = "\x01\x02" + expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql [1,2] + end + end + + describe "write_item" do + it "complains if no buffer given" do + expect { Structure.new.write_item(nil, nil, nil, nil) }.to raise_error(RuntimeError, "No buffer given to write_item") + end + + it "writes data to the buffer" do + s = Structure.new + s.define_item("test1", 0, 8, :UINT) + buffer = "\x01" + expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql 1 + s.write_item(s.get_item("test1"), 2, :RAW, buffer) + expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql 2 + end + + it "writes array data to the buffer" do + s = Structure.new + s.define_item("test1", 0, 8, :UINT, 16) + buffer = "\x01\x02" + expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql [1,2] + s.write_item(s.get_item("test1"), [3,4], :RAW, buffer) + expect(s.read_item(s.get_item("test1"), :RAW, buffer)).to eql [3,4] + end + end + + describe "read" do + it "complains if item doesn't exist" do + expect { Structure.new.read("BLAH") }.to raise_error(ArgumentError, "Unknown item: BLAH") + end + + it "reads data from the buffer" do + s = Structure.new + s.define_item("test1", 0, 8, :UINT) + buffer = "\x01" + expect(s.read("test1", :RAW, buffer)).to eql 1 + end + + it "reads array data from the buffer" do + s = Structure.new + s.define_item("test1", 0, 8, :UINT, 16) + buffer = "\x01\x02" + expect(s.read("test1", :RAW, buffer)).to eql [1,2] + end + end + + describe "write" do + it "complains if item doesn't exist" do + expect { Structure.new.write("BLAH", 0) }.to raise_error(ArgumentError, "Unknown item: BLAH") + end + + it "writes data to the buffer" do + s = Structure.new + s.define_item("test1", 0, 8, :UINT) + buffer = "\x01" + expect(s.read("test1", :RAW, buffer)).to eql 1 + s.write("test1", 2, :RAW, buffer) + expect(s.read("test1", :RAW, buffer)).to eql 2 + end + + it "writes array data to the buffer" do + s = Structure.new + s.define_item("test1", 0, 8, :UINT, 16) + buffer = "\x01\x02" + expect(s.read("test1", :RAW, buffer)).to eql [1,2] + s.write("test1", [3,4], :RAW, buffer) + expect(s.read("test1", :RAW, buffer)).to eql [3,4] + end + end + + describe "read_all" do + it "reads all defined items" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT, 16) + s.append_item("test2", 16, :UINT) + s.append_item("test3", 32, :UINT) + + buffer = "\x01\x02\x03\x04\x05\x06\x07\x08" + vals = s.read_all(:RAW, buffer) + expect(vals[0][0]).to eql "TEST1" + expect(vals[1][0]).to eql "TEST2" + expect(vals[2][0]).to eql "TEST3" + expect(vals[0][1]).to eql [1,2] + expect(vals[1][1]).to eql 0x0304 + expect(vals[2][1]).to eql 0x05060708 + end + + it "reads all defined items synchronized" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT, 16) + s.append_item("test2", 16, :UINT) + s.append_item("test3", 32, :UINT) + + buffer = "\x01\x02\x03\x04\x05\x06\x07\x08" + vals = s.read_all(:RAW, buffer, false) + expect(vals[0][0]).to eql "TEST1" + expect(vals[1][0]).to eql "TEST2" + expect(vals[2][0]).to eql "TEST3" + expect(vals[0][1]).to eql [1,2] + expect(vals[1][1]).to eql 0x0304 + expect(vals[2][1]).to eql 0x05060708 + end + end + + describe "formatted" do + it "prints out all the items and values" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT, 16) + s.write("test1", [1,2]) + s.append_item("test2", 16, :UINT) + s.write("test2", 3456) + s.append_item("test3", 32, :BLOCK) + s.write("test3", "\x07\x08\x09\x0A") + expect(s.formatted).to include("TEST1: [1, 2]") + expect(s.formatted).to include("TEST2: 3456") + expect(s.formatted).to include("TEST3") + expect(s.formatted).to include("00000000: 07 08 09 0A") + end + + it "alters the indentation of the item" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT, 16) + s.write("test1", [1,2]) + s.append_item("test2", 16, :UINT) + s.write("test2", 3456) + s.append_item("test3", 32, :BLOCK) + s.write("test3", "\x07\x08\x09\x0A") + expect(s.formatted(:CONVERTED, 4)).to include(" TEST1: [1, 2]") + expect(s.formatted(:CONVERTED, 4)).to include(" TEST2: 3456") + expect(s.formatted(:CONVERTED, 4)).to include(" TEST3") + expect(s.formatted(:CONVERTED, 4)).to include(" 00000000: 07 08 09 0A") + end + end + + describe "buffer" do + it "returns the buffer" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT, 16) + s.write("test1", [1,2]) + s.append_item("test2", 16, :UINT) + s.write("test2", 0x0304) + s.append_item("test3", 32, :UINT) + s.write("test3", 0x05060708) + expect(s.buffer).to eql "\x01\x02\x03\x04\x05\x06\x07\x08" + expect(s.buffer).to_not be s.buffer + expect(s.buffer(false)).to be s.buffer(false) + end + end + + describe "buffer=" do + it "complains if the given buffer is too small" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 16, :UINT) + expect { s.buffer = "\x00" }.to raise_error(RuntimeError, "Buffer length less than defined length") + end + + it "complains if the given buffer is too big" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 16, :UINT) + expect { s.buffer = "\x00\x00\x00" }.to raise_error(RuntimeError, "Buffer length greater than defined length") + end + + it "does not complain if the given buffer is too big and we're not fixed length" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT) + s.append_item("test2", 0, :BLOCK) + s.buffer = "\x01\x02\x03" + expect(s.read("test1")).to eql 1 + expect(s.read("test2")).to eql "\x02\x03" + end + + it "sets the buffer" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT, 16) + s.write("test1", [1,2]) + s.append_item("test2", 16, :UINT) + s.write("test2", 0x0304) + s.append_item("test3", 32, :UINT) + s.write("test3", 0x05060708) + expect(s.read("test1")).to eql [1,2] + expect(s.read("test2")).to eql 0x0304 + expect(s.read("test3")).to eql 0x05060708 + s.buffer = "\x00\x01\x02\x03\x04\x05\x06\x07" + expect(s.read("test1")).to eql [0,1] + expect(s.read("test2")).to eql 0x0203 + expect(s.read("test3")).to eql 0x04050607 + end + end + + describe "clone" do + it "duplicates the structure with a new buffer" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT, 16) + s.write("test1", [1,2]) + s.append_item("test2", 16, :UINT) + s.write("test2", 0x0304) + s.append_item("test3", 32, :UINT) + s.write("test3", 0x05060708) + # Get a reference to the original buffer + old_buffer = s.buffer(false) + + s2 = s.clone + # Ensure we didn't modify the original buffer object + expect(s.buffer(false)).to be old_buffer + # Check that they are equal in value + expect(s2.buffer(false)).to eql s.buffer(false) + # But not the same object + expect(s2.buffer(false)).to_not be s.buffer(false) + expect(s2.read("test1")).to eql [1,2] + expect(s2.read("test2")).to eql 0x0304 + expect(s2.read("test3")).to eql 0x05060708 + s2.write("test1", [0,0]) + expect(s2.read("test1")).to eql [0,0] + # Ensure we didn't change the original + expect(s.read("test1")).to eql [1,2] + end + end + + describe "enable_method_missing" do + it "enables reading by name" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT, 16) + s.write("test1", [1,2]) + s.enable_method_missing + expect(s.test1).to eql [1,2] + end + + it "enables writing by name" do + s = Structure.new(:BIG_ENDIAN) + s.append_item("test1", 8, :UINT, 16) + s.write("test1", [1,2]) + s.enable_method_missing + expect(s.test1).to eql [1,2] + s.test1 = [3,4] + expect(s.test1).to eql [3,4] + end + + it "raises an exception if there is no buffer" do + s = Structure.new(:BIG_ENDIAN, nil) + s.append_item("test1", 8, :UINT, 16) + s.enable_method_missing + expect { s.test1 }.to raise_error(/No buffer/) + end + + it "complains if it can't find an item" do + s = Structure.new(:BIG_ENDIAN) + s.enable_method_missing + expect { s.test1 }.to raise_error(ArgumentError, "Unknown item: test1") + end + end + + end # describe Structure + +end