jbangert-bindata 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/.gitignore +1 -0
  2. data/BSDL +22 -0
  3. data/COPYING +52 -0
  4. data/ChangeLog.rdoc +204 -0
  5. data/Gemfile +2 -0
  6. data/INSTALL +11 -0
  7. data/NEWS.rdoc +164 -0
  8. data/README.md +54 -0
  9. data/Rakefile +13 -0
  10. data/bindata.gemspec +31 -0
  11. data/doc/manual.haml +407 -0
  12. data/doc/manual.md +1649 -0
  13. data/examples/NBT.txt +149 -0
  14. data/examples/gzip.rb +161 -0
  15. data/examples/ip_address.rb +22 -0
  16. data/examples/list.rb +124 -0
  17. data/examples/nbt.rb +178 -0
  18. data/lib/bindata.rb +33 -0
  19. data/lib/bindata/alignment.rb +83 -0
  20. data/lib/bindata/array.rb +335 -0
  21. data/lib/bindata/base.rb +388 -0
  22. data/lib/bindata/base_primitive.rb +214 -0
  23. data/lib/bindata/bits.rb +87 -0
  24. data/lib/bindata/choice.rb +216 -0
  25. data/lib/bindata/count_bytes_remaining.rb +35 -0
  26. data/lib/bindata/deprecated.rb +50 -0
  27. data/lib/bindata/dsl.rb +312 -0
  28. data/lib/bindata/float.rb +80 -0
  29. data/lib/bindata/int.rb +184 -0
  30. data/lib/bindata/io.rb +274 -0
  31. data/lib/bindata/lazy.rb +105 -0
  32. data/lib/bindata/offset.rb +91 -0
  33. data/lib/bindata/params.rb +135 -0
  34. data/lib/bindata/primitive.rb +135 -0
  35. data/lib/bindata/record.rb +110 -0
  36. data/lib/bindata/registry.rb +92 -0
  37. data/lib/bindata/rest.rb +35 -0
  38. data/lib/bindata/sanitize.rb +290 -0
  39. data/lib/bindata/skip.rb +48 -0
  40. data/lib/bindata/string.rb +145 -0
  41. data/lib/bindata/stringz.rb +96 -0
  42. data/lib/bindata/struct.rb +388 -0
  43. data/lib/bindata/trace.rb +94 -0
  44. data/lib/bindata/version.rb +3 -0
  45. data/setup.rb +1585 -0
  46. data/spec/alignment_spec.rb +61 -0
  47. data/spec/array_spec.rb +331 -0
  48. data/spec/base_primitive_spec.rb +238 -0
  49. data/spec/base_spec.rb +376 -0
  50. data/spec/bits_spec.rb +163 -0
  51. data/spec/choice_spec.rb +263 -0
  52. data/spec/count_bytes_remaining_spec.rb +38 -0
  53. data/spec/deprecated_spec.rb +31 -0
  54. data/spec/example.rb +21 -0
  55. data/spec/float_spec.rb +37 -0
  56. data/spec/int_spec.rb +216 -0
  57. data/spec/io_spec.rb +352 -0
  58. data/spec/lazy_spec.rb +217 -0
  59. data/spec/primitive_spec.rb +202 -0
  60. data/spec/record_spec.rb +530 -0
  61. data/spec/registry_spec.rb +108 -0
  62. data/spec/rest_spec.rb +26 -0
  63. data/spec/skip_spec.rb +27 -0
  64. data/spec/spec_common.rb +58 -0
  65. data/spec/string_spec.rb +300 -0
  66. data/spec/stringz_spec.rb +118 -0
  67. data/spec/struct_spec.rb +350 -0
  68. data/spec/system_spec.rb +380 -0
  69. data/tasks/manual.rake +36 -0
  70. data/tasks/rspec.rake +17 -0
  71. metadata +208 -0
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata'
5
+
6
+ describe BinData::Primitive do
7
+ it "is not registered" do
8
+ expect {
9
+ BinData::RegisteredClasses.lookup("Primitive")
10
+ }.to raise_error(BinData::UnRegisteredTypeError)
11
+ end
12
+ end
13
+
14
+ describe BinData::Primitive, "all subclasses" do
15
+ class SubClassOfPrimitive < BinData::Primitive
16
+ expose_methods_for_testing
17
+ end
18
+
19
+ subject { SubClassOfPrimitive.new }
20
+
21
+ it "raise errors on unimplemented methods" do
22
+ expect { subject.set(nil) }.to raise_error(NotImplementedError)
23
+ expect { subject.get }.to raise_error(NotImplementedError)
24
+ end
25
+ end
26
+
27
+ describe BinData::Primitive, "when defining with errors" do
28
+ it "fails on non registered types" do
29
+ lambda {
30
+ class BadTypePrimitive < BinData::Primitive
31
+ non_registered_type :a
32
+ end
33
+ }.should raise_error_on_line(TypeError, 2) { |err|
34
+ err.message.should == "unknown type 'non_registered_type' in #{BadTypePrimitive}"
35
+ }
36
+ end
37
+
38
+ it "fails on duplicate names" do
39
+ lambda {
40
+ class DuplicateNamePrimitive < BinData::Primitive
41
+ int8 :a
42
+ int8 :b
43
+ int8 :a
44
+ end
45
+ }.should raise_error_on_line(SyntaxError, 4) { |err|
46
+ err.message.should == "duplicate field 'a' in #{DuplicateNamePrimitive}"
47
+ }
48
+ end
49
+
50
+ it "fails when field name shadows an existing method" do
51
+ lambda {
52
+ class ExistingNamePrimitive < BinData::Primitive
53
+ int8 :object_id
54
+ end
55
+ }.should raise_error_on_line(NameError, 2) { |err|
56
+ err.message.should == "field 'object_id' shadows an existing method in #{ExistingNamePrimitive}"
57
+ }
58
+ end
59
+
60
+ it "fails on unknown endian" do
61
+ lambda {
62
+ class BadEndianPrimitive < BinData::Primitive
63
+ endian 'a bad value'
64
+ end
65
+ }.should raise_error_on_line(ArgumentError, 2) { |err|
66
+ err.message.should == "unknown value for endian 'a bad value' in #{BadEndianPrimitive}"
67
+ }
68
+ end
69
+ end
70
+
71
+ describe BinData::Primitive do
72
+ class PrimitiveWithEndian < BinData::Primitive
73
+ endian :little
74
+ int16 :a
75
+ def get; self.a; end
76
+ def set(v); self.a = v; end
77
+ end
78
+
79
+ subject { PrimitiveWithEndian.new }
80
+
81
+ it "assigns value" do
82
+ subject.value = 5
83
+ subject.value.should == 5
84
+ end
85
+
86
+ it "produces binary string" do
87
+ subject.assign(5)
88
+ subject.to_binary_s.should == "\x05\x00"
89
+ end
90
+
91
+ it "reads value" do
92
+ subject.read("\x00\x01")
93
+ subject.should == 0x100
94
+ end
95
+
96
+ it "accepts standard parameters" do
97
+ subject = PrimitiveWithEndian.new(:initial_value => 2)
98
+ subject.to_binary_s.should == "\x02\x00"
99
+ end
100
+
101
+ it "returns num_bytes" do
102
+ subject.num_bytes.should == 2
103
+ end
104
+
105
+ it "raises error on missing methods" do
106
+ expect {
107
+ subject.does_not_exist
108
+ }.to raise_error(NoMethodError)
109
+ end
110
+
111
+ it "uses read value whilst reading" do
112
+ subject = PrimitiveWithEndian.new(:value => 2)
113
+ subject.read "\x05\x00"
114
+ subject.should == 2
115
+
116
+ subject.stub(:reading?).and_return(true)
117
+ subject.should == 5
118
+ end
119
+
120
+ it "behaves as primitive" do
121
+ subject.assign(5)
122
+ (2 + subject).should == 7
123
+ end
124
+ end
125
+
126
+ describe BinData::Primitive, "requiring custom parameters" do
127
+ class PrimitiveWithCustom < BinData::Primitive
128
+ int8 :a, :initial_value => :iv
129
+ def get; self.a; end
130
+ def set(v); self.a = v; end
131
+ end
132
+
133
+ it "passes parameters correctly" do
134
+ subject = PrimitiveWithCustom.new(:iv => 5)
135
+ subject.should == 5
136
+ end
137
+ end
138
+
139
+ describe BinData::Primitive, "with custom mandatory parameters" do
140
+ class MandatoryPrimitive < BinData::Primitive
141
+ mandatory_parameter :arg1
142
+
143
+ uint8 :a, :value => :arg1
144
+ def get; self.a; end
145
+ def set(v); self.a = v; end
146
+ end
147
+
148
+ it "raises error if mandatory parameter is not supplied" do
149
+ expect { MandatoryPrimitive.new }.to raise_error(ArgumentError)
150
+ end
151
+
152
+ it "uses mandatory parameter" do
153
+ subject = MandatoryPrimitive.new(:arg1 => 5)
154
+ subject.should == 5
155
+ end
156
+ end
157
+
158
+ describe BinData::Primitive, "with custom default parameters" do
159
+ class DefaultPrimitive < BinData::Primitive
160
+ default_parameter :arg1 => 5
161
+
162
+ uint8 :a, :value => :arg1
163
+ def get; self.a; end
164
+ def set(v); self.a = v; end
165
+ end
166
+
167
+ it "does not raise error if default parameter is not supplied" do
168
+ expect { DefaultPrimitive.new }.not_to raise_error(ArgumentError)
169
+ end
170
+
171
+ it "uses default parameter" do
172
+ subject = DefaultPrimitive.new
173
+ subject.should == 5
174
+ end
175
+
176
+ it "overrides default parameter" do
177
+ subject = DefaultPrimitive.new(:arg1 => 7)
178
+ subject.should == 7
179
+ end
180
+ end
181
+
182
+ describe BinData::Primitive, "subclassed with default parameter" do
183
+ class ParentDerivedPrimitive < BinData::Primitive
184
+ uint16be :a
185
+ def get; self.a; end
186
+ def set(v); self.a = v; end
187
+ end
188
+
189
+ class ChildDerivedPrimitive < ParentDerivedPrimitive
190
+ default_parameter :initial_value => 5
191
+ end
192
+
193
+ it "overrides initial_value" do
194
+ a = ChildDerivedPrimitive.new(:initial_value => 7)
195
+ a.to_binary_s.should == "\000\007"
196
+ end
197
+
198
+ it "uses default parameter" do
199
+ a = ChildDerivedPrimitive.new
200
+ a.to_binary_s.should == "\000\005"
201
+ end
202
+ end
@@ -0,0 +1,530 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata'
5
+
6
+ describe BinData::Record do
7
+ it "is not registered" do
8
+ expect {
9
+ BinData::RegisteredClasses.lookup("Record")
10
+ }.to raise_error(BinData::UnRegisteredTypeError)
11
+ end
12
+ end
13
+
14
+ describe BinData::Record, "when defining with errors" do
15
+ it "fails on non registered types" do
16
+ lambda {
17
+ class BadTypeRecord < BinData::Record
18
+ non_registered_type :a
19
+ end
20
+ }.should raise_error_on_line(TypeError, 2) { |err|
21
+ err.message.should == "unknown type 'non_registered_type' in #{BadTypeRecord}"
22
+ }
23
+ end
24
+
25
+ it "gives correct error message for non registered nested types" do
26
+ lambda {
27
+ class BadNestedTypeRecord < BinData::Record
28
+ array :a, :type => :non_registered_type
29
+ end
30
+ }.should raise_error_on_line(TypeError, 2) { |err|
31
+ err.message.should == "unknown type 'non_registered_type' in #{BadNestedTypeRecord}"
32
+ }
33
+ end
34
+
35
+ it "gives correct error message for non registered nested types in blocks" do
36
+ lambda {
37
+ class BadNestedTypeInBlockRecord < BinData::Record
38
+ array :a do
39
+ non_registered_type
40
+ end
41
+ end
42
+ }.should raise_error_on_line(TypeError, 3) { |err|
43
+ err.message.should == "unknown type 'non_registered_type' in #{BinData::Array}"
44
+ }
45
+ end
46
+
47
+ it "fails on nested choice when missing names" do
48
+ lambda {
49
+ class MissingChoiceNamesRecord < BinData::Record
50
+ choice do
51
+ int8 :a
52
+ int8
53
+ end
54
+ end
55
+ }.should raise_error_on_line(SyntaxError, 4) { |err|
56
+ err.message.should == "fields must either all have names, or none must have names in BinData::Choice"
57
+ }
58
+ end
59
+
60
+ it "fails on malformed names" do
61
+ lambda {
62
+ class MalformedNameRecord < BinData::Record
63
+ int8 :a
64
+ int8 "45"
65
+ end
66
+ }.should raise_error_on_line(NameError, 3) { |err|
67
+ err.message.should == "field '45' is an illegal fieldname in #{MalformedNameRecord}"
68
+ }
69
+ end
70
+
71
+ it "fails on duplicate names" do
72
+ lambda {
73
+ class DuplicateNameRecord < BinData::Record
74
+ int8 :a
75
+ int8 :b
76
+ int8 :a
77
+ end
78
+ }.should raise_error_on_line(SyntaxError, 4) { |err|
79
+ err.message.should == "duplicate field 'a' in #{DuplicateNameRecord}"
80
+ }
81
+ end
82
+
83
+ it "fails on reserved names" do
84
+ lambda {
85
+ class ReservedNameRecord < BinData::Record
86
+ int8 :a
87
+ int8 :invert # from Hash.instance_methods
88
+ end
89
+ }.should raise_error_on_line(NameError, 3) { |err|
90
+ err.message.should == "field 'invert' is a reserved name in #{ReservedNameRecord}"
91
+ }
92
+ end
93
+
94
+ it "fails when field name shadows an existing method" do
95
+ lambda {
96
+ class ExistingNameRecord < BinData::Record
97
+ int8 :object_id
98
+ end
99
+ }.should raise_error_on_line(NameError, 2) { |err|
100
+ err.message.should == "field 'object_id' shadows an existing method in #{ExistingNameRecord}"
101
+ }
102
+ end
103
+
104
+ it "fails on unknown endian" do
105
+ lambda {
106
+ class BadEndianRecord < BinData::Record
107
+ endian 'a bad value'
108
+ end
109
+ }.should raise_error_on_line(ArgumentError, 2) { |err|
110
+ err.message.should == "unknown value for endian 'a bad value' in #{BadEndianRecord}"
111
+ }
112
+ end
113
+ end
114
+
115
+ describe BinData::Record, "with anonymous fields" do
116
+ class AnonymousRecord < BinData::Record
117
+ int8 'a', :initial_value => 10
118
+ int8 ''
119
+ int8 nil
120
+ int8
121
+ int8 :value => :a
122
+ end
123
+
124
+ subject { AnonymousRecord.new }
125
+
126
+ it "only shows non anonymous fields" do
127
+ subject.field_names.should == ["a"]
128
+ end
129
+
130
+ it "does not include anonymous fields in snapshot" do
131
+ subject.a = 5
132
+ subject.snapshot.should == {"a" => 5}
133
+ end
134
+
135
+ it "writes anonymous fields" do
136
+ str = "\001\002\003\004\005"
137
+ subject.read(str)
138
+ subject.a.clear
139
+ subject.to_binary_s.should == "\012\002\003\004\012"
140
+ end
141
+ end
142
+
143
+ describe BinData::Record, "with hidden fields" do
144
+ class HiddenRecord < BinData::Record
145
+ hide :b, :c
146
+ int8 :a
147
+ int8 'b', :initial_value => 10
148
+ int8 :c
149
+ int8 :d, :value => :b
150
+ end
151
+
152
+ subject { HiddenRecord.new }
153
+
154
+ it "only shows fields that aren't hidden" do
155
+ subject.field_names.should == ["a", "d"]
156
+ end
157
+
158
+ it "accesses hidden fields directly" do
159
+ subject.b.should == 10
160
+ subject.c = 15
161
+ subject.c.should == 15
162
+
163
+ subject.should respond_to(:b=)
164
+ end
165
+
166
+ it "does not include hidden fields in snapshot" do
167
+ subject.b = 5
168
+ subject.snapshot.should == {"a" => 0, "d" => 5}
169
+ end
170
+ end
171
+
172
+ describe BinData::Record, "with multiple fields" do
173
+ class MultiFieldRecord < BinData::Record
174
+ int8 :a
175
+ int8 :b
176
+ end
177
+
178
+ subject { MultiFieldRecord.new(:a => 1, :b => 2) }
179
+
180
+ it "returns num_bytes" do
181
+ subject.a.num_bytes.should == 1
182
+ subject.b.num_bytes.should == 1
183
+ subject.num_bytes.should == 2
184
+ end
185
+
186
+ it "identifies accepted parameters" do
187
+ BinData::Record.accepted_parameters.all.should include(:hide)
188
+ BinData::Record.accepted_parameters.all.should include(:endian)
189
+ end
190
+
191
+ it "clears" do
192
+ subject.a = 6
193
+ subject.clear
194
+ subject.should be_clear
195
+ end
196
+
197
+ it "clears individual elements" do
198
+ subject.a = 6
199
+ subject.b = 7
200
+ subject.a.clear
201
+ subject.a.should be_clear
202
+ subject.b.should_not be_clear
203
+ end
204
+
205
+ it "writes ordered" do
206
+ subject.to_binary_s.should == "\x01\x02"
207
+ end
208
+
209
+ it "reads ordered" do
210
+ subject.read("\x03\x04")
211
+
212
+ subject.a.should == 3
213
+ subject.b.should == 4
214
+ end
215
+
216
+ it "returns a snapshot" do
217
+ snap = subject.snapshot
218
+ snap.a.should == 1
219
+ snap.b.should == 2
220
+ snap.should == { "a" => 1, "b" => 2 }
221
+ end
222
+
223
+ it "returns field_names" do
224
+ subject.field_names.should == ["a", "b"]
225
+ end
226
+
227
+ it "fails on unknown method call" do
228
+ expect { subject.does_not_exist }.to raise_error(NoMethodError)
229
+ end
230
+ end
231
+
232
+ describe BinData::Record, "with nested structs" do
233
+ class NestedStructRecord < BinData::Record
234
+ int8 :a, :initial_value => 6
235
+ struct :b, :the_val => :a do
236
+ hide :w
237
+ int8 :w, :initial_value => 3
238
+ int8 :x, :value => :the_val
239
+ end
240
+ struct :c do
241
+ int8 :y, :value => lambda { b.w }
242
+ int8 :z
243
+ end
244
+ end
245
+
246
+ subject { NestedStructRecord.new }
247
+
248
+ it "includes nested field names" do
249
+ subject.field_names.should == ["a", "b", "c"]
250
+ end
251
+
252
+ it "hides nested field names" do
253
+ subject.b.field_names.should == ["x"]
254
+ end
255
+
256
+ it "accesses nested fields" do
257
+ subject.a.should == 6
258
+ subject.b.w.should == 3
259
+ subject.b.x.should == 6
260
+ subject.c.y.should == 3
261
+ end
262
+
263
+ it "returns correct offset" do
264
+ subject.offset.should == 0
265
+ subject.b.offset.should == 1
266
+ subject.b.w.offset.should == 1
267
+ subject.c.offset.should == 3
268
+ subject.c.z.offset.should == 4
269
+ end
270
+
271
+ it "returns correct rel_offset" do
272
+ subject.rel_offset.should == 0
273
+ subject.b.rel_offset.should == 1
274
+ subject.b.w.rel_offset.should == 0
275
+ subject.c.rel_offset.should == 3
276
+ subject.c.z.rel_offset.should == 1
277
+ end
278
+
279
+ it "assigns nested fields" do
280
+ subject.assign(:a => 2, :b => {:w => 4})
281
+ subject.a.should == 2
282
+ subject.b.w.should == 4
283
+ subject.b.x.should == 2
284
+ subject.c.y.should == 4
285
+ end
286
+ end
287
+
288
+ describe BinData::Record, "with nested array of primitives" do
289
+ class NestedPrimitiveArrayRecord < BinData::Record
290
+ array :a, :initial_length => 3 do
291
+ uint8 :value => lambda { index }
292
+ end
293
+ end
294
+
295
+ subject { NestedPrimitiveArrayRecord.new }
296
+
297
+ it "uses block as :type" do
298
+ subject.snapshot.should == {"a" => [0, 1, 2]}
299
+ end
300
+ end
301
+
302
+ describe BinData::Record, "with nested array of structs" do
303
+ class NestedStructArrayRecord < BinData::Record
304
+ array :a do
305
+ uint8 :b
306
+ uint8 :c
307
+ end
308
+ end
309
+
310
+ subject { NestedStructArrayRecord.new }
311
+
312
+ it "uses block as struct for :type" do
313
+ subject.a[0].b = 2
314
+ subject.snapshot.should == {"a" => [{"b" => 2, "c" => 0}]}
315
+ end
316
+ end
317
+
318
+ describe BinData::Record, "with nested choice with implied keys" do
319
+ class NestedChoiceWithImpliedKeysRecord < BinData::Record
320
+ choice :a, :selection => 1 do
321
+ uint8 :value => 1
322
+ uint8 :value => 2
323
+ end
324
+ end
325
+
326
+ subject { NestedChoiceWithImpliedKeysRecord.new }
327
+
328
+ its(:a) { should == 2 }
329
+ end
330
+
331
+ describe BinData::Record, "with nested choice with explicit keys" do
332
+ class NestedChoiceWithKeysRecord < BinData::Record
333
+ choice :a, :selection => 5 do
334
+ uint8 3, :value => 1
335
+ uint8 5, :value => 2
336
+ end
337
+ end
338
+
339
+ subject { NestedChoiceWithKeysRecord.new }
340
+
341
+ its(:a) { should == 2 }
342
+ end
343
+
344
+ describe BinData::Record, "with nested choice with names" do
345
+ class NestedChoiceWithNamesRecord < BinData::Record
346
+ choice :a, :selection => "b" do
347
+ uint8 "b", :value => 1
348
+ uint8 "c", :value => 2
349
+ end
350
+ end
351
+
352
+ subject { NestedChoiceWithNamesRecord.new }
353
+
354
+ its(:a) { should == 1 }
355
+ end
356
+
357
+ describe BinData::Record, "with an endian defined" do
358
+ class RecordWithEndian < BinData::Record
359
+ endian :little
360
+
361
+ uint16 :a
362
+ float :b
363
+ array :c, :initial_length => 2 do
364
+ int8
365
+ end
366
+ choice :d, :selection => 1 do
367
+ uint16
368
+ uint32
369
+ end
370
+ struct :e do
371
+ uint16 :f
372
+ uint32be :g
373
+ end
374
+ struct :h do
375
+ endian :big
376
+ struct :i do
377
+ uint16 :j
378
+ end
379
+ end
380
+ end
381
+
382
+ subject { RecordWithEndian.new }
383
+
384
+ it "uses correct endian" do
385
+ subject.a = 1
386
+ subject.b = 2.0
387
+ subject.c[0] = 3
388
+ subject.c[1] = 4
389
+ subject.d = 5
390
+ subject.e.f = 6
391
+ subject.e.g = 7
392
+ subject.h.i.j = 8
393
+
394
+ expected = [1, 2.0, 3, 4, 5, 6, 7, 8].pack('veCCVvNn')
395
+
396
+ subject.to_binary_s.should == expected
397
+ end
398
+ end
399
+
400
+ describe BinData::Record, "defined recursively" do
401
+ class RecursiveRecord < BinData::Record
402
+ endian :big
403
+ uint16 :val
404
+ uint8 :has_nxt, :value => lambda { nxt.clear? ? 0 : 1 }
405
+ recursive_record :nxt, :onlyif => lambda { has_nxt > 0 }
406
+ end
407
+
408
+ it "can be created" do
409
+ subject = RecursiveRecord.new
410
+ end
411
+
412
+ it "reads" do
413
+ str = "\x00\x01\x01\x00\x02\x01\x00\x03\x00"
414
+ subject = RecursiveRecord.read(str)
415
+ subject.val.should == 1
416
+ subject.nxt.val.should == 2
417
+ subject.nxt.nxt.val.should == 3
418
+ end
419
+
420
+ it "is assignable on demand" do
421
+ subject = RecursiveRecord.new
422
+ subject.val = 13
423
+ subject.nxt.val = 14
424
+ subject.nxt.nxt.val = 15
425
+ end
426
+
427
+ it "writes" do
428
+ subject = RecursiveRecord.new
429
+ subject.val = 5
430
+ subject.nxt.val = 6
431
+ subject.nxt.nxt.val = 7
432
+ subject.to_binary_s.should == "\x00\x05\x01\x00\x06\x01\x00\x07\x00"
433
+ end
434
+ end
435
+
436
+ describe BinData::Record, "with custom mandatory parameters" do
437
+ class MandatoryRecord < BinData::Record
438
+ mandatory_parameter :arg1
439
+
440
+ uint8 :a, :value => :arg1
441
+ end
442
+
443
+ it "raises error if mandatory parameter is not supplied" do
444
+ expect { MandatoryRecord.new }.to raise_error(ArgumentError)
445
+ end
446
+
447
+ it "uses mandatory parameter" do
448
+ subject = MandatoryRecord.new(:arg1 => 5)
449
+ subject.a.should == 5
450
+ end
451
+ end
452
+
453
+ describe BinData::Record, "with custom default parameters" do
454
+ class DefaultRecord < BinData::Record
455
+ default_parameter :arg1 => 5
456
+
457
+ uint8 :a, :value => :arg1
458
+ uint8 :b
459
+ end
460
+
461
+ it "does not raise error if default parameter is not supplied" do
462
+ expect { DefaultRecord.new }.not_to raise_error(ArgumentError)
463
+ end
464
+
465
+ it "uses default parameter" do
466
+ subject = DefaultRecord.new
467
+ subject.a.should == 5
468
+ end
469
+
470
+ it "overrides default parameter" do
471
+ subject = DefaultRecord.new(:arg1 => 7)
472
+ subject.a.should == 7
473
+ end
474
+
475
+ it "accepts values" do
476
+ subject = DefaultRecord.new(:b => 2)
477
+ subject.b.should == 2
478
+ end
479
+
480
+ it "accepts values and parameters" do
481
+ subject = DefaultRecord.new({:b => 2}, :arg1 => 3)
482
+ subject.a.should == 3
483
+ subject.b.should == 2
484
+ end
485
+ end
486
+
487
+ describe BinData::Record, "with :onlyif" do
488
+ class OnlyIfRecord < BinData::Record
489
+ uint8 :a, :initial_value => 3
490
+ uint8 :b, :initial_value => 5, :onlyif => lambda { a == 3 }
491
+ uint8 :c, :initial_value => 7, :onlyif => lambda { a != 3 }
492
+ end
493
+
494
+ subject { OnlyIfRecord.new }
495
+
496
+ its(:num_bytes) { should == 2 }
497
+ its(:snapshot) { should == {"a" => 3, "b" => 5} }
498
+ its(:to_binary_s) { should == "\x03\x05" }
499
+
500
+ it "reads as expected" do
501
+ subject.read("\x01\x02")
502
+ subject.snapshot.should == {"a" => 1, "c" => 2}
503
+ end
504
+ end
505
+
506
+ describe BinData::Record, "derived classes" do
507
+ class ParentRecord < BinData::Record
508
+ uint8 :a
509
+ end
510
+
511
+ class Child1Record < ParentRecord
512
+ uint8 :b
513
+ end
514
+
515
+ class Child2Record < Child1Record
516
+ uint8 :c
517
+ end
518
+
519
+ it "does not affect parent" do
520
+ ParentRecord.new.field_names.should == ["a"]
521
+ end
522
+
523
+ it "inherits fields for first child" do
524
+ Child1Record.new.field_names.should == ["a", "b"]
525
+ end
526
+
527
+ it "inherits fields for second child" do
528
+ Child2Record.new.field_names.should == ["a", "b", "c"]
529
+ end
530
+ end