cosmos 4.4.0 → 4.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +5 -5
  2. data/.dockerignore +2 -0
  3. data/.gitignore +1 -0
  4. data/.travis.yml +6 -6
  5. data/Dockerfile +65 -0
  6. data/Manifest.txt +12 -2
  7. data/README.md +5 -0
  8. data/Rakefile +52 -0
  9. data/appveyor.yml +18 -8
  10. data/autohotkey/config/tools/cmd_sequence/cmd_sequence.txt +2 -0
  11. data/autohotkey/lib/cmd_sequence_exporter.rb +52 -0
  12. data/autohotkey/procedures/collect.rb +2 -2
  13. data/autohotkey/procedures/collect_util.rb +1 -1
  14. data/autohotkey/procedures/script_test.rb +1 -1
  15. data/autohotkey/tools/CmdSenderAHK2 +18 -0
  16. data/autohotkey/tools/cmd_sender.ahk +34 -6
  17. data/autohotkey/tools/cmd_sender2.ahk +4 -0
  18. data/autohotkey/tools/cmd_sequence.ahk +21 -8
  19. data/autohotkey/tools/config_editor.ahk +4 -4
  20. data/bin/cstol_converter +1 -1
  21. data/cosmos.gemspec +1 -1
  22. data/data/config/command_modifiers.yaml +16 -1
  23. data/data/config/param_item_modifiers.yaml +5 -0
  24. data/data/config/system.yaml +31 -1
  25. data/data/config/telemetry_modifiers.yaml +16 -1
  26. data/data/crc.txt +415 -410
  27. data/demo/config/dart/Gemfile +1 -6
  28. data/demo/config/data/crc.txt +244 -243
  29. data/demo/config/system/system.txt +3 -0
  30. data/demo/config/system/system2.txt +3 -0
  31. data/demo/config/system/system_alt_ports.txt +3 -0
  32. data/demo/config/targets/INST/cmd_tlm/inst_cmds.txt +3 -3
  33. data/demo/config/targets/INST/cmd_tlm/inst_tlm.txt +4 -0
  34. data/demo/config/targets/INST/cmd_tlm/inst_tlm_override.txt +12 -0
  35. data/demo/config/targets/INST/lib/sim_inst.rb +2 -2
  36. data/demo/config/targets/INST/target.txt +1 -0
  37. data/demo/procedures/cosmos_api_test.rb +8 -8
  38. data/install/config/dart/Gemfile +2 -7
  39. data/install/config/data/crc.txt +143 -143
  40. data/install/config/system/system.txt +3 -0
  41. data/lib/cosmos/dart/config/boot.rb +1 -1
  42. data/lib/cosmos/dart/config/database.yml +2 -0
  43. data/lib/cosmos/dart/lib/dart_common.rb +11 -4
  44. data/lib/cosmos/dart/lib/dart_constants.rb +15 -0
  45. data/lib/cosmos/dart/lib/dart_decom_query.rb +5 -6
  46. data/lib/cosmos/dart/lib/dart_decommutator.rb +66 -56
  47. data/lib/cosmos/dart/lib/dart_master_query.rb +71 -0
  48. data/lib/cosmos/dart/lib/dart_reducer_worker_thread.rb +165 -134
  49. data/lib/cosmos/dart/processes/dart.rb +4 -2
  50. data/lib/cosmos/dart/processes/dart_decom_server.rb +2 -2
  51. data/lib/cosmos/dart/processes/dart_ingester.rb +38 -1
  52. data/lib/cosmos/dart/processes/dart_master.rb +44 -0
  53. data/lib/cosmos/dart/processes/dart_util.rb +115 -0
  54. data/lib/cosmos/gui/widgets/dart_meta_frame.rb +21 -2
  55. data/lib/cosmos/interfaces/protocols/length_protocol.rb +5 -0
  56. data/lib/cosmos/io/json_drb.rb +3 -3
  57. data/lib/cosmos/io/posix_serial_driver.rb +1 -1
  58. data/lib/cosmos/io/win32_serial_driver.rb +23 -2
  59. data/lib/cosmos/packet_logs/packet_log_reader.rb +2 -2
  60. data/lib/cosmos/packets/packet.rb +1 -1
  61. data/lib/cosmos/packets/packet_config.rb +26 -8
  62. data/lib/cosmos/packets/structure.rb +17 -0
  63. data/lib/cosmos/packets/structure_item.rb +5 -1
  64. data/lib/cosmos/packets/telemetry.rb +7 -1
  65. data/lib/cosmos/system/system.rb +115 -48
  66. data/lib/cosmos/tools/cmd_sender/cmd_params.rb +360 -0
  67. data/lib/cosmos/tools/cmd_sender/cmd_sender.rb +23 -319
  68. data/lib/cosmos/tools/cmd_sequence/cmd_sequence.rb +14 -17
  69. data/lib/cosmos/tools/cmd_sequence/sequence_item.rb +43 -331
  70. data/lib/cosmos/tools/cmd_sequence/sequence_list.rb +16 -11
  71. data/lib/cosmos/tools/cmd_tlm_server/cmd_tlm_server_gui.rb +1 -0
  72. data/lib/cosmos/tools/config_editor/config_editor.rb +33 -2
  73. data/lib/cosmos/tools/config_editor/config_editor_frame.rb +8 -9
  74. data/lib/cosmos/tools/config_editor/system_config_dialog.rb +158 -0
  75. data/lib/cosmos/tools/script_runner/script_runner_frame.rb +2 -2
  76. data/lib/cosmos/tools/test_runner/test.rb +5 -2
  77. data/lib/cosmos/tools/test_runner/test_runner.rb +2 -2
  78. data/lib/cosmos/tools/tlm_extractor/tlm_extractor_processor.rb +17 -13
  79. data/lib/cosmos/tools/tlm_grapher/tabbed_plots_tool/tabbed_plots_dart_thread.rb +20 -16
  80. data/lib/cosmos/tools/tlm_grapher/tlm_grapher.rb +18 -11
  81. data/lib/cosmos/tools/tlm_viewer/tlm_viewer.rb +16 -5
  82. data/lib/cosmos/utilities/ruby_lex_utils.rb +34 -30
  83. data/lib/cosmos/version.rb +4 -4
  84. data/lib/cosmos/win32/excel.rb +23 -17
  85. data/run_gui_tests.bat +1 -0
  86. data/spec/core_ext/socket_spec.rb +1 -1
  87. data/spec/install/yaml_docs_spec.rb +26 -6
  88. data/spec/interfaces/protocols/length_protocol_spec.rb +39 -0
  89. data/spec/io/json_drb_spec.rb +14 -0
  90. data/spec/io/win32_serial_driver_spec.rb +16 -2
  91. data/spec/packet_logs/packet_log_reader_spec.rb +2 -2
  92. data/spec/packets/structure_spec.rb +52 -2
  93. data/spec/packets/telemetry_spec.rb +29 -1
  94. data/spec/system/system_spec.rb +2 -2
  95. data/spec/utilities/message_log_spec.rb +6 -3
  96. data/tasks/gemfile_stats.rake +22 -13
  97. metadata +17 -7
  98. data/lib/cosmos/dart/Gemfile +0 -69
@@ -73,17 +73,24 @@ module Cosmos
73
73
  target_name.upcase!
74
74
  packet_name.upcase!
75
75
  item_name.upcase!
76
- # Check to see if the item name is followed by an array index,
77
- # notated by square brackets around an integer; i.e. ARRAY_ITEM[1]
78
- if (item_name =~ /\[\d+\]$/)
79
- # We found an array index.
80
- # The $` special variable is the string before the regex match, i.e. ARRAY_ITEM
81
- item_name = $`
82
- # The $& special variable is the string matched by the regex, i.e. [1].
83
- # Strip off the brackets and then convert the array index to an integer.
84
- item_array_index = $&.gsub(/[\[\]]/, "").to_i
85
- else
86
- item_array_index = nil
76
+
77
+ item_array_index = nil
78
+ begin
79
+ # See if we can access the item
80
+ _, item = System.telemetry.packet_and_item(target_name, packet_name, item_name)
81
+ rescue => error
82
+ # Check to see if the item name is followed by an array index,
83
+ # notated by square brackets around an integer; i.e. ARRAY_ITEM[1]
84
+ if (item_name =~ /\[\d+\]$/)
85
+ # The $` special variable is the string before the regex match, i.e. ARRAY_ITEM
86
+ item_name = $`
87
+ # The $& special variable is the string matched by the regex, i.e. [1].
88
+ # Strip off the brackets and then convert the array index to an integer.
89
+ item_array_index = $&.gsub(/[\[\]]/, "").to_i
90
+ else
91
+ # If we couldn't access the item and it's not bracket notation then it's an error
92
+ raise error
93
+ end
87
94
  end
88
95
  # Default configuration has one plot so don't add plot for first item
89
96
  data_object = HousekeepingDataObject.new
@@ -53,6 +53,12 @@ module Cosmos
53
53
  @@instance = nil
54
54
 
55
55
  def self.instance
56
+ unless @@instance
57
+ _, options = create_default_options()
58
+ options.listen = false
59
+ options.show_main = false
60
+ TlmViewer.new(options)
61
+ end
56
62
  @@instance
57
63
  end
58
64
 
@@ -118,12 +124,14 @@ module Cosmos
118
124
 
119
125
  Splash.execute(self) do |splash|
120
126
  ConfigParser.splash = splash
121
- splash.message = "Displaying requested screens"
127
+ if options.show_main
128
+ splash.message = "Displaying requested screens"
122
129
 
123
- # Startup desired screens once we're running
124
- @tlm_viewer_config.screen_infos.each do |screen_full_name, screen_info|
125
- if screen_info.show_on_startup
126
- display(screen_full_name, screen_info.x_pos, screen_info.y_pos)
130
+ # Startup desired screens once we're running
131
+ @tlm_viewer_config.screen_infos.each do |screen_full_name, screen_info|
132
+ if screen_info.show_on_startup
133
+ display(screen_full_name, screen_info.x_pos, screen_info.y_pos)
134
+ end
127
135
  end
128
136
  end
129
137
 
@@ -163,6 +171,8 @@ module Cosmos
163
171
 
164
172
  ConfigParser.splash = nil
165
173
  end
174
+
175
+ hide() unless options.show_main
166
176
  end
167
177
 
168
178
  def initialize_actions
@@ -551,6 +561,7 @@ module Cosmos
551
561
  options.title = 'Telemetry Viewer'
552
562
  options.screen = nil
553
563
  options.listen = true
564
+ options.show_main = true
554
565
  options.restore_size = false
555
566
  options.production = false
556
567
  options.replay = false
@@ -45,41 +45,45 @@ class RubyLex
45
45
  @exception_on_syntax_error = true
46
46
  @prompt = nil
47
47
  end
48
+
49
+ # Monkey patch to keep this from looping forever if the string never is closed with a right brace
50
+ def identify_string_dvar
51
+ begin
52
+ getc
48
53
 
49
- # Monkey patch to fix performance issue caused by call to reverse
50
- def get_readed
51
- if idx = @readed.rindex("\n")
52
- @base_char_no = @readed.size - (idx + 1)
53
- else
54
- @base_char_no += @readed.size
55
- end
54
+ reserve_continue = @continue
55
+ reserve_ltype = @ltype
56
+ reserve_indent = @indent
57
+ reserve_indent_stack = @indent_stack
58
+ reserve_state = @lex_state
59
+ reserve_quoted = @quoted
56
60
 
57
- readed = @readed.join("")
58
- @readed = []
59
- readed
60
- end
61
+ @ltype = nil
62
+ @quoted = nil
63
+ @indent = 0
64
+ @indent_stack = []
65
+ @lex_state = EXPR_BEG
61
66
 
62
- # Monkey patch to fix performance issue caused by call to reverse
63
- def ungetc(c = nil)
64
- if @here_readed.empty?
65
- c2 = @readed.pop
66
- else
67
- c2 = @here_readed.pop
68
- end
69
- c = c2 unless c
70
- @rests.unshift c #c =
71
- @seek -= 1
72
- if c == "\n"
73
- @line_no -= 1
74
- if idx = @readed.rindex("\n")
75
- @char_no = idx + 1
76
- else
77
- @char_no = @base_char_no + @readed.size
67
+ loop do
68
+ @continue = false
69
+ prompt
70
+ tk = token
71
+ break if tk.nil? # This is the patch
72
+ if @ltype or @continue or @indent >= 0
73
+ next
74
+ end
75
+ break if tk.kind_of?(TkRBRACE)
78
76
  end
79
- else
80
- @char_no -= 1
77
+ ensure
78
+ @continue = reserve_continue
79
+ @ltype = reserve_ltype
80
+ @indent = reserve_indent
81
+ @indent_stack = reserve_indent_stack
82
+ @lex_state = reserve_state
83
+ @quoted = reserve_quoted
81
84
  end
82
- end
85
+ end
86
+
83
87
  end
84
88
  $VERBOSE = old_verbose
85
89
 
@@ -1,12 +1,12 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- COSMOS_VERSION = '4.4.0'
3
+ COSMOS_VERSION = '4.4.1'
4
4
  module Cosmos
5
5
  module Version
6
6
  MAJOR = '4'
7
7
  MINOR = '4'
8
- PATCH = '0'
9
- BUILD = '30bd354efce074e6684f2fb4f4df8dc12d2f3df5'
8
+ PATCH = '1'
9
+ BUILD = 'f8e9c89b2d7fcd0c558806ff6c9dd59d167786e4'
10
10
  end
11
- VERSION = '4.4.0'
11
+ VERSION = '4.4.1'
12
12
  end
@@ -31,8 +31,10 @@ module Cosmos
31
31
 
32
32
  # Build a lookup table based on the first column
33
33
  @lkup = {}
34
- @data.each do |row|
35
- @lkup[row[0]] = row[1..-1]
34
+ if @data
35
+ @data.each do |row|
36
+ @lkup[row[0]] = row[1..-1]
37
+ end
36
38
  end
37
39
  end
38
40
 
@@ -70,23 +72,27 @@ module Cosmos
70
72
  File.chmod(0444, archive) # Mark read-only
71
73
  end
72
74
 
73
- excel = WIN32OLE.new('excel.application')
74
- excel.visible = false
75
- wb = excel.workbooks.open(filename)
75
+ begin
76
+ excel = WIN32OLE.new('excel.application')
77
+ excel.visible = false
78
+ wb = excel.workbooks.open(filename)
76
79
 
77
- @worksheets = []
78
- @lkup = {}
79
- count = wb.worksheets.count
80
- count.times do |index|
81
- ws = wb.worksheets(index + 1)
82
- @worksheets << ExcelWorksheet.new(ws)
83
- @lkup[ws.name] = @worksheets[-1]
80
+ @worksheets = []
81
+ @lkup = {}
82
+ count = wb.worksheets.count
83
+ count.times do |index|
84
+ ws = wb.worksheets(index + 1)
85
+ @worksheets << ExcelWorksheet.new(ws)
86
+ @lkup[ws.name] = @worksheets[-1]
87
+ end
88
+ ensure
89
+ if excel
90
+ excel.DisplayAlerts = false
91
+ excel.quit
92
+ end
93
+ excel = nil
94
+ GC.start
84
95
  end
85
-
86
- excel.DisplayAlerts = false
87
- excel.quit
88
- excel = nil
89
- GC.start
90
96
  end
91
97
 
92
98
  # @return [Array<String>] Array of all the worksheet names
@@ -1,5 +1,6 @@
1
1
  call bundle exec ruby autohotkey\tools\CmdExtractorAHK --defaultsize
2
2
  call bundle exec ruby autohotkey\tools\CmdSenderAHK -w 650 -t 650
3
+ call bundle exec ruby autohotkey\tools\CmdSenderAHK2 -w 650 -t 650 -p "INST COLLECT" --production
3
4
  call bundle exec ruby autohotkey\tools\CmdSequenceAHK -w 650 -t 650
4
5
  call bundle exec ruby autohotkey\tools\CmdSequenceAHK2 -w 650 -t 650 -r run_sequence.txt -o procedures
5
6
  call bundle exec ruby autohotkey\tools\CmdTlmServerAHK -x 50 -y 50 -w 900 -t 1000
@@ -27,7 +27,7 @@ describe Socket do
27
27
  it "returns the hostname for the ip address" do
28
28
  if !ENV['APPVEYOR']
29
29
  ipaddr = Resolv.getaddress "localhost"
30
- expect(Socket.lookup_hostname_from_ip(ipaddr)).to match("localhost")
30
+ expect(Socket.lookup_hostname_from_ip(ipaddr)).to_not be_nil
31
31
  end
32
32
  end
33
33
  end
@@ -15,6 +15,25 @@ module Cosmos
15
15
  OPENGL = %w(STL_FILE TEXTURE_MAPPED_SPHERE TIP_TEXT POSITION ROTATION_X ROTATION_Y ROTATION_Z)
16
16
  OPENGL.concat(%w(ZOOM ORIENTATION CENTER BOUNDS))
17
17
 
18
+ def process_line(line)
19
+ line.split(',').each do |item|
20
+ item.strip!
21
+ if (item[0] == "'" || item[0] == '"') && (item[-1] == "'" || item[-1] == '"')
22
+ @src_keywords << item[1..-2]
23
+ end
24
+ end
25
+ end
26
+
27
+ def process_continuation(line)
28
+ if line[-1] == "\\"
29
+ continuation = true
30
+ line = line[0..-2] # remove the continuation character
31
+ else
32
+ continuation = false
33
+ end
34
+ return continuation
35
+ end
36
+
18
37
  def get_src_keywords
19
38
  @src_keywords = []
20
39
  path = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/**/*.rb"))
@@ -27,15 +46,16 @@ module Cosmos
27
46
  part = data.split('handle_keyword(parser, keyword, parameters)')[1..-1].join
28
47
  end
29
48
  if part
49
+ continuation = false
30
50
  part.split("\n").each do |line|
51
+ if continuation
52
+ continuation = process_continuation(line)
53
+ process_line(line)
54
+ end
31
55
  if match = line.match(/when (.*)/)
32
56
  line = match.captures[0]
33
- line.split(',').each do |item|
34
- item.strip!
35
- if (item[0] == "'" || item[0] == '"') && (item[-1] == "'" || item[-1] == '"')
36
- @src_keywords << item[1..-2]
37
- end
38
- end
57
+ continuation = process_continuation(line)
58
+ process_line(line)
39
59
  end
40
60
  end
41
61
  end
@@ -160,6 +160,45 @@ module Cosmos
160
160
  expect(packet.buffer.length).to eql 5
161
161
  end
162
162
 
163
+ it "raises an error with a packet length of 0" do
164
+ @interface.instance_variable_set(:@stream, LengthStream.new)
165
+ @interface.add_protocol(LengthProtocol, [
166
+ 16, # bit offset
167
+ 16, # bit size
168
+ 0, # length offset
169
+ 1, # bytes per count
170
+ 'BIG_ENDIAN'], :READ_WRITE)
171
+ $buffer = "\x00\x01\x00\x00\x03\x04\x05\x06\x07\x08\x09"
172
+ expect { @interface.read }.to raise_error(RuntimeError, /Calculated packet length of 0 bits/)
173
+ end
174
+
175
+ it "raises an error if packet length not enough to support offset and size" do
176
+ @interface.instance_variable_set(:@stream, LengthStream.new)
177
+ @interface.add_protocol(LengthProtocol, [
178
+ 16, # bit offset
179
+ 16, # bit size
180
+ 3, # length offset of 3 not enough to support 2 byte length field at offset 2 bytes
181
+ 1, # bytes per count
182
+ 'BIG_ENDIAN'], :READ_WRITE)
183
+ $buffer = "\x00\x01\x00\x00\x03\x04\x05\x06\x07\x08\x09"
184
+ expect { @interface.read }.to raise_error(RuntimeError, /Calculated packet length of 24 bits/)
185
+ end
186
+
187
+ it "processes a 0 length with a non-zero length offset" do
188
+ @interface.instance_variable_set(:@stream, LengthStream.new)
189
+ @interface.add_protocol(LengthProtocol, [
190
+ 0, # bit offset
191
+ 16, # bit size
192
+ 4, # length offset
193
+ 1, # bytes per count
194
+ 'BIG_ENDIAN'], :READ_WRITE)
195
+ $buffer = "\x00\x00\x01\x02\x00\x00\x03\x04"
196
+ packet = @interface.read
197
+ expect(packet.buffer).to eql "\x00\x00\x01\x02"
198
+ packet = @interface.read
199
+ expect(packet.buffer).to eql "\x00\x00\x03\x04"
200
+ end
201
+
163
202
  it "validates length against the maximum length" do
164
203
  @interface.instance_variable_set(:@stream, LengthStream.new)
165
204
  @interface.add_protocol(LengthProtocol, [
@@ -200,6 +200,20 @@ module Cosmos
200
200
  sleep(0.1)
201
201
  end
202
202
 
203
+ it "processes success requests with uppercase" do
204
+ class MyServer5
205
+ def my_method(param)
206
+ end
207
+ end
208
+
209
+ @json.start_service('127.0.0.1', 7777, MyServer5.new)
210
+ request_data = JsonRpcRequest.new('MY_METHOD', 'param', 1).to_json
211
+ _, error_code = @json.process_request(request_data, Time.now)
212
+ expect(error_code).to eq nil
213
+ @json.stop_service
214
+ sleep(0.1)
215
+ end
216
+
203
217
  it "does not allow dangerous methods" do
204
218
  @json.start_service('127.0.0.1', 7777, self)
205
219
  request_data = JsonRpcRequest.new('send', 'param', 1).to_json
@@ -50,6 +50,22 @@ if RUBY_ENGINE == 'ruby' or Gem.win_platform?
50
50
  expect { Win32SerialDriver.new('COM1',9600,:NONE,1,10,nil,0.01,1000,:NONE,8) }.to_not raise_error
51
51
  expect { Win32SerialDriver.new('COM1',9600,:NONE,1,10,nil,0.01,1000,:NONE,9) }.to raise_error(ArgumentError, "Invalid data bits: 9")
52
52
  end
53
+
54
+ it "calculates the correct timeouts" do
55
+ Win32::BAUD_RATES.each do |baud|
56
+ (5..8).each do |data_bits|
57
+ (1..2).each do |stop_bits|
58
+ [:EVEN, :ODD, :NONE].each do |parity|
59
+ # data_bits + 1 start bit + stop bits + potentially a parity bit
60
+ symbols = data_bits + 1 + stop_bits + (parity == :NONE ? 0 : 1)
61
+ delay = 1000.0 / (baud.to_f / symbols)
62
+ expect(Win32).to receive(:set_comm_timeouts).with(anything, 0xFFFFFFFF,0,0,delay.ceil,1000)
63
+ Win32SerialDriver.new('COM1',baud,parity,stop_bits,10,nil,0.01,1000,:NONE,data_bits)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
53
69
  end
54
70
 
55
71
  describe "close, closed?" do
@@ -92,8 +108,6 @@ if RUBY_ENGINE == 'ruby' or Gem.win_platform?
92
108
  expect { driver.read }.to raise_error(Timeout::Error)
93
109
  end
94
110
  end
95
-
96
111
  end
97
112
  end
98
-
99
113
  end
@@ -121,7 +121,7 @@ module Cosmos
121
121
  file.write [pkt.buffer.length].pack('N')
122
122
  file.write pkt.buffer
123
123
  end
124
- expect(@plr.open(filename)).to eql [false, nil]
124
+ expect(@plr.open(filename)[0]).to eql false
125
125
  pkt1 = @plr.read
126
126
  expect(pkt1.target_name).to eql 'TGT1'
127
127
  expect(pkt1.packet_name).to eql 'PKT1'
@@ -149,7 +149,7 @@ module Cosmos
149
149
  file.write [pkt.buffer.length].pack('N')
150
150
  file.write pkt.buffer
151
151
  end
152
- expect(@plr.open(filename)).to eql [false, nil]
152
+ expect(@plr.open(filename)[0]).to eql false
153
153
  pkt1 = @plr.read
154
154
  expect(pkt1.target_name).to eql 'TGT1'
155
155
  expect(pkt1.packet_name).to eql 'PKT1'
@@ -185,6 +185,8 @@ module Cosmos
185
185
  expect(@s.defined_length).to eql 1
186
186
  si = StructureItem.new("test1",0,16,:INT,:BIG_ENDIAN)
187
187
  @s.define(si)
188
+ expect(@s.items.length).to eql 1
189
+ expect(@s.sorted_items.length).to eql 1
188
190
  expect(@s.sorted_items[0].name).to eql "TEST1"
189
191
  expect(@s.items["TEST1"].bit_size).to eql 16
190
192
  expect(@s.items["TEST1"].data_type).to eql :INT
@@ -290,6 +292,56 @@ module Cosmos
290
292
  end
291
293
  end
292
294
 
295
+ describe "delete_item" do
296
+ before(:each) do
297
+ @s = Structure.new(:BIG_ENDIAN)
298
+ @s.define_item("test1", 0, 8, :UINT)
299
+ end
300
+
301
+ it "removes the item and leaves a hole" do
302
+ @s.append_item("test2", 16, :UINT)
303
+ expect(@s.defined_length).to eql 3
304
+ @s.delete_item("test1")
305
+ expect { @s.get_item("test1") }.to raise_error(ArgumentError, "Unknown item: test1")
306
+ expect(@s.defined_length).to eql 3
307
+ expect(@s.items["TEST1"]).to be_nil
308
+ expect(@s.items["TEST2"]).not_to be_nil
309
+ expect(@s.sorted_items.length).to eql 1
310
+ expect(@s.sorted_items[0]).to eql(@s.get_item("test2"))
311
+ buffer = "\x01\x02\x03"
312
+ expect(@s.read("test2", :RAW, buffer)).to eql 0x0203
313
+ end
314
+
315
+ it "allows new items to be defined in place" do
316
+ @s.append_item("test2", 16, :UINT)
317
+ @s.append_item("test3", 8, :UINT)
318
+ expect(@s.defined_length).to eql 4
319
+ # Delete the first 2 items, note a 3 byte hole now exists
320
+ @s.delete_item("test1")
321
+ @s.delete_item("test2")
322
+ expect(@s.defined_length).to eql 4
323
+ expect(@s.items.length).to eql 1
324
+ expect(@s.sorted_items.length).to eql 1
325
+ # Fill the hole and overlap the last byte
326
+ @s.define_item("test4", 0, 16, :UINT)
327
+ @s.define_item("test5", 16, 16, :UINT)
328
+ @s.define_item("test6", 32, 32, :UINT)
329
+ buffer = "\x01\x02\x03\x04\x05\x06\x07\x08"
330
+ expect(@s.read("test4", :RAW, buffer)).to eql 0x0102
331
+ expect(@s.read("test5", :RAW, buffer)).to eql 0x0304
332
+ expect(@s.read("test6", :RAW, buffer)).to eql 0x05060708
333
+ # test3 is still defined
334
+ expect(@s.read("test3", :RAW, buffer)).to eql 0x04
335
+ expect(@s.items.length).to eql 4
336
+ expect(@s.sorted_items.length).to eql 4
337
+ # Check that everything is sorted correctly
338
+ expect(@s.sorted_items[0].name).to eql "TEST4"
339
+ expect(@s.sorted_items[1].name).to eql "TEST5"
340
+ expect(@s.sorted_items[2].name).to eql "TEST3"
341
+ expect(@s.sorted_items[3].name).to eql "TEST6"
342
+ end
343
+ end
344
+
293
345
  describe "read_item" do
294
346
  it "complains if no buffer given" do
295
347
  s = Structure.new
@@ -601,7 +653,5 @@ module Cosmos
601
653
  expect { s.test1 }.to raise_error(ArgumentError, "Unknown item: test1")
602
654
  end
603
655
  end
604
-
605
656
  end # describe Structure
606
-
607
657
  end