jbangert-bindata 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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,376 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata/base'
5
+
6
+ class BaseStub < BinData::Base
7
+ # Override to avoid NotImplemented errors
8
+ def clear; end
9
+ def clear?; end
10
+ def assign(x); @data = x; end
11
+ def snapshot; @data; end
12
+ def do_read(io) end
13
+ def do_write(io) end
14
+ def do_num_bytes; end
15
+ end
16
+
17
+ describe BinData::Base, "all subclasses" do
18
+ class SubClassOfBase < BinData::Base
19
+ expose_methods_for_testing
20
+ end
21
+
22
+ subject { SubClassOfBase.new }
23
+
24
+ it "raises errors on unimplemented methods" do
25
+ expect { subject.clear }.to raise_error(NotImplementedError)
26
+ expect { subject.clear? }.to raise_error(NotImplementedError)
27
+ expect { subject.assign(nil) }.to raise_error(NotImplementedError)
28
+ expect { subject.snapshot }.to raise_error(NotImplementedError)
29
+ expect { subject.do_read(nil) }.to raise_error(NotImplementedError)
30
+ expect { subject.do_write(nil) }.to raise_error(NotImplementedError)
31
+ expect { subject.do_num_bytes }.to raise_error(NotImplementedError)
32
+ end
33
+ end
34
+
35
+ describe BinData::Base, "with parameters" do
36
+ it "raises error when parameter name is invalid" do
37
+ expect {
38
+ class InvalidParameterNameBase < BinData::Base
39
+ optional_parameter :eval # i.e. Kernel#eval
40
+ end
41
+ }.to raise_error(NameError)
42
+ end
43
+
44
+ it "raises an error when parameter has nil value" do
45
+ expect { BaseStub.new(:a => nil) }.to raise_error(ArgumentError)
46
+ end
47
+
48
+ it "converts parameter keys to symbols" do
49
+ subject = BaseStub.new('a' => 3)
50
+ subject.should have_parameter(:a)
51
+ end
52
+ end
53
+
54
+ describe BinData::Base, "with mandatory parameters" do
55
+ class MandatoryBase < BaseStub
56
+ mandatory_parameter :p1
57
+ mandatory_parameter :p2
58
+ end
59
+
60
+ it "ensures that all mandatory parameters are present" do
61
+ params = {:p1 => "a", :p2 => "b" }
62
+ expect { MandatoryBase.new(params) }.not_to raise_error
63
+ end
64
+
65
+ it "fails when only some mandatory parameters are present" do
66
+ params = {:p1 => "a", :xx => "b" }
67
+ expect { MandatoryBase.new(params) }.to raise_error(ArgumentError)
68
+ end
69
+
70
+ it "fails when no mandatory parameters are present" do
71
+ expect { MandatoryBase.new() }.to raise_error(ArgumentError)
72
+ end
73
+ end
74
+
75
+ describe BinData::Base, "with default parameters" do
76
+ class DefaultBase < BaseStub
77
+ default_parameter :p1 => "a"
78
+ end
79
+
80
+ it "uses default parameters when not specified" do
81
+ subject = DefaultBase.new
82
+ subject.eval_parameter(:p1).should == "a"
83
+ end
84
+
85
+ it "can override default parameters" do
86
+ subject = DefaultBase.new(:p1 => "b")
87
+ subject.eval_parameter(:p1).should == "b"
88
+ end
89
+ end
90
+
91
+ describe BinData::Base, "with mutually exclusive parameters" do
92
+ class MutexParamBase < BaseStub
93
+ optional_parameters :p1, :p2
94
+ mutually_exclusive_parameters :p1, :p2
95
+ end
96
+
97
+ it "does not fail when neither of those parameters are present" do
98
+ expect { MutexParamBase.new }.not_to raise_error
99
+ end
100
+
101
+ it "does not fail when only one of those parameters is present" do
102
+ expect { MutexParamBase.new(:p1 => "a") }.not_to raise_error
103
+ expect { MutexParamBase.new(:p2 => "b") }.not_to raise_error
104
+ end
105
+
106
+ it "fails when both those parameters are present" do
107
+ expect { MutexParamBase.new(:p1 => "a", :p2 => "b") }.to raise_error(ArgumentError)
108
+ end
109
+ end
110
+
111
+ describe BinData::Base, "with multiple parameters" do
112
+ class WithParamBase < BaseStub
113
+ mandatory_parameter :p1
114
+ default_parameter :p2 => 2
115
+ optional_parameter :p3
116
+ end
117
+
118
+ it "identifies internally accepted parameters" do
119
+ accepted = WithParamBase.accepted_parameters.all
120
+ accepted.should include(:p1)
121
+ accepted.should include(:p2)
122
+ accepted.should include(:p3)
123
+ accepted.should_not include(:xx)
124
+ end
125
+
126
+ context "examining parameters" do
127
+ subject {
128
+ params = {:p1 => 1, :p3 => :xx, :p4 => lambda { 4 }}
129
+ WithParamBase.new(params)
130
+ }
131
+
132
+ it "evaluates parameters" do
133
+ subject.eval_parameter(:p1).should == 1
134
+ subject.eval_parameter(:p2).should == 2
135
+ expect { subject.eval_parameter(:p3) }.to raise_error(NoMethodError)
136
+ subject.eval_parameter(:p4).should == 4
137
+ end
138
+
139
+ it "gets parameters without evaluating" do
140
+ subject.get_parameter(:p1).should == 1
141
+ subject.get_parameter(:p2).should == 2
142
+ subject.get_parameter(:p3).should == :xx
143
+ subject.get_parameter(:p4).should respond_to(:arity)
144
+ end
145
+
146
+ it "has parameters" do
147
+ subject.should have_parameter(:p1)
148
+ subject.should have_parameter(:p2)
149
+ subject.should have_parameter(:p3)
150
+ subject.should have_parameter(:p4)
151
+ end
152
+ end
153
+ end
154
+
155
+ describe BinData::Base, "when initializing" do
156
+ class BaseInit < BaseStub
157
+ class << self
158
+ attr_accessor :calls
159
+ def recorded_calls(&block)
160
+ self.calls = []
161
+ block.call
162
+ calls
163
+ end
164
+ end
165
+
166
+ def initialize_instance
167
+ self.class.calls << :initialize_instance
168
+ end
169
+
170
+ def initialize_shared_instance
171
+ self.class.calls << :initialize_shared_instance
172
+ end
173
+ end
174
+
175
+ it "calls both #initialize_xxx methods" do
176
+ BaseInit.recorded_calls {
177
+ BaseInit.new
178
+ }.should == [:initialize_shared_instance, :initialize_instance]
179
+ end
180
+
181
+ context "as a factory" do
182
+ subject { BaseInit.new(:check_offset => 1) }
183
+
184
+ describe "#new" do
185
+ it "calls #initialize_instance" do
186
+ obj = subject
187
+
188
+ BaseInit.recorded_calls {
189
+ obj.new
190
+ }.should == [:initialize_instance]
191
+ end
192
+
193
+ it "copies parameters" do
194
+ obj = subject.new
195
+ obj.eval_parameter(:check_offset).should == 1
196
+ end
197
+
198
+ it "performs action for :check_offset" do
199
+ obj = subject.new
200
+ expect {
201
+ obj.read("abc")
202
+ }.to raise_error(BinData::ValidityError)
203
+ end
204
+
205
+ it "assigns value" do
206
+ obj = subject.new(3)
207
+ obj.snapshot.should == 3
208
+ end
209
+
210
+ it "sets parent" do
211
+ obj = subject.new(3, "p")
212
+ obj.parent.should == "p"
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ describe BinData::Base, "as black box" do
219
+ context "class methods" do
220
+ it "returns bindata_name" do
221
+ BaseStub.bindata_name.should == "base_stub"
222
+ end
223
+
224
+ it "instantiates self for ::read" do
225
+ BaseStub.read("").class.should == BaseStub
226
+ end
227
+ end
228
+
229
+ it "accesses parent" do
230
+ parent = BaseStub.new
231
+ child = BaseStub.new(nil, parent)
232
+ child.parent.should == parent
233
+ end
234
+
235
+ subject { BaseStub.new }
236
+
237
+ it "returns self for #read" do
238
+ subject.read("").should == subject
239
+ end
240
+
241
+ it "returns self for #write" do
242
+ subject.write("").should == subject
243
+ end
244
+
245
+ it "forwards #inspect to snapshot" do
246
+ subject.stub(:snapshot).and_return([1, 2, 3])
247
+ subject.inspect.should == subject.snapshot.inspect
248
+ end
249
+
250
+ it "forwards #to_s to snapshot" do
251
+ subject.stub(:snapshot).and_return([1, 2, 3])
252
+ subject.to_s.should == subject.snapshot.to_s
253
+ end
254
+
255
+ it "pretty prints object as snapshot" do
256
+ subject.stub(:snapshot).and_return([1, 2, 3])
257
+ actual_io = StringIO.new
258
+ expected_io = StringIO.new
259
+
260
+ require 'pp'
261
+ PP.pp(subject, actual_io)
262
+ PP.pp(subject.snapshot, expected_io)
263
+
264
+ actual_io.value.should == expected_io.value
265
+ end
266
+
267
+ it "writes the same as to_binary_s" do
268
+ class WriteToSBase < BaseStub
269
+ def do_write(io) io.writebytes("abc"); end
270
+ end
271
+
272
+ subject = WriteToSBase.new
273
+ io = StringIO.new
274
+ subject.write(io)
275
+ io.value.should == subject.to_binary_s
276
+ end
277
+ end
278
+
279
+ describe BinData::Base, "as white box" do
280
+ subject { BaseStub.new }
281
+
282
+ it "forwards read to do_read" do
283
+ subject.should_receive(:clear).ordered
284
+ subject.should_receive(:do_read).ordered
285
+ subject.read(nil)
286
+ end
287
+
288
+ it "forwards write to do_write" do
289
+ subject.should_receive(:do_write)
290
+ subject.write(nil)
291
+ end
292
+
293
+ it "forwards num_bytes to do_num_bytes" do
294
+ subject.should_receive(:do_num_bytes).and_return(42)
295
+ subject.num_bytes.should == 42
296
+ end
297
+
298
+ it "rounds up fractional num_bytes" do
299
+ subject.should_receive(:do_num_bytes).and_return(42.1)
300
+ subject.num_bytes.should == 43
301
+ end
302
+ end
303
+
304
+ describe BinData::Base, "checking offsets" do
305
+ class TenByteOffsetBase < BaseStub
306
+ def self.create(params)
307
+ obj = self.new
308
+ obj.initialize_child(params)
309
+ obj
310
+ end
311
+
312
+ def initialize_child(params)
313
+ @child = BaseStub.new(params, self)
314
+ end
315
+
316
+ def do_read(io)
317
+ io.seekbytes(10)
318
+ @child.do_read(io)
319
+ end
320
+ end
321
+
322
+ let(:io) { StringIO.new("12345678901234567890") }
323
+
324
+ context "with :check_offset" do
325
+ it "fails when offset is incorrect" do
326
+ io.seek(2)
327
+ subject = TenByteOffsetBase.create(:check_offset => 8)
328
+ expect { subject.read(io) }.to raise_error(BinData::ValidityError)
329
+ end
330
+
331
+ it "succeeds when offset is correct" do
332
+ io.seek(3)
333
+ subject = TenByteOffsetBase.create(:check_offset => 10)
334
+ expect { subject.read(io) }.not_to raise_error
335
+ end
336
+
337
+ it "fails when :check_offset fails" do
338
+ io.seek(4)
339
+ subject = TenByteOffsetBase.create(:check_offset => lambda { offset == 11 } )
340
+ expect { subject.read(io) }.to raise_error(BinData::ValidityError)
341
+ end
342
+
343
+ it "succeeds when :check_offset succeeds" do
344
+ io.seek(5)
345
+ subject = TenByteOffsetBase.create(:check_offset => lambda { offset == 10 } )
346
+ expect { subject.read(io) }.not_to raise_error
347
+ end
348
+ end
349
+
350
+ context "with :adjust_offset" do
351
+ it "is mutually exclusive with :check_offset" do
352
+ params = { :check_offset => 8, :adjust_offset => 8 }
353
+ expect { TenByteOffsetBase.create(params) }.to raise_error(ArgumentError)
354
+ end
355
+
356
+ it "adjust offset when incorrect" do
357
+ io.seek(2)
358
+ subject = TenByteOffsetBase.create(:adjust_offset => 13)
359
+ subject.read(io)
360
+ io.pos.should == (2 + 13)
361
+ end
362
+
363
+ it "succeeds when offset is correct" do
364
+ io.seek(3)
365
+ subject = TenByteOffsetBase.create(:adjust_offset => 10)
366
+ expect { subject.read(io) }.not_to raise_error
367
+ io.pos.should == (3 + 10)
368
+ end
369
+
370
+ it "fails if cannot adjust offset" do
371
+ io.seek(4)
372
+ subject = TenByteOffsetBase.create(:adjust_offset => -5)
373
+ expect { subject.read(io) }.to raise_error(BinData::ValidityError)
374
+ end
375
+ end
376
+ end
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata/bits'
5
+
6
+ describe "Bits of size 1" do
7
+ let(:bit_classes) { [BinData::Bit1, BinData::Bit1le] }
8
+
9
+ it "accept true as value" do
10
+ bit_classes.each do |bit_class|
11
+ subject = bit_class.new
12
+ subject.assign(true)
13
+ subject.should == 1
14
+ end
15
+ end
16
+
17
+ it "accept false as value" do
18
+ bit_classes.each do |bit_class|
19
+ subject = bit_class.new
20
+ subject.assign(false)
21
+ subject.should == 0
22
+ end
23
+ end
24
+
25
+ it "accept nil as value" do
26
+ bit_classes.each do |bit_class|
27
+ subject = bit_class.new
28
+ subject.assign(nil)
29
+ subject.should == 0
30
+ end
31
+ end
32
+ end
33
+
34
+ shared_examples "All bitfields" do
35
+
36
+ it "have a sensible value of zero" do
37
+ all_classes do |bit_class|
38
+ bit_class.new.should be_zero
39
+ end
40
+ end
41
+
42
+ it "avoid underflow" do
43
+ all_classes do |bit_class|
44
+ subject = bit_class.new
45
+
46
+ subject.assign(min_value - 1)
47
+ subject.should == min_value
48
+ end
49
+ end
50
+
51
+ it "avoid overflow" do
52
+ all_classes do |bit_class|
53
+ subject = bit_class.new
54
+
55
+ subject.assign(max_value + 1)
56
+ subject.should == max_value
57
+ end
58
+ end
59
+
60
+ it "assign values" do
61
+ all_classes do |bit_class|
62
+ some_values_within_range.each do |val|
63
+ subject = bit_class.new
64
+ subject.assign(val)
65
+
66
+ subject.should == val
67
+ end
68
+ end
69
+ end
70
+
71
+ it "assign values from other bit objects" do
72
+ all_classes do |bit_class|
73
+ some_values_within_range.each do |val|
74
+ subject = bit_class.new
75
+ subject.assign(bit_class.new(val))
76
+
77
+ subject.should == val
78
+ end
79
+ end
80
+ end
81
+
82
+ it "symmetrically #read and #write" do
83
+ all_classes do |bit_class|
84
+ some_values_within_range.each do |val|
85
+ subject = bit_class.new
86
+ subject.assign(val)
87
+
88
+ subject.value_read_from_written.should == subject
89
+ end
90
+ end
91
+ end
92
+
93
+ def all_classes(&block)
94
+ @bits.each_pair do |bit_class, nbits|
95
+ @nbits = nbits
96
+ yield bit_class
97
+ end
98
+ end
99
+
100
+ def min_value
101
+ 0
102
+ end
103
+
104
+ def max_value
105
+ (1 << @nbits) - 1
106
+ end
107
+
108
+ def some_values_within_range
109
+ lo = min_value + 1
110
+ mid = (min_value + max_value) / 2
111
+ hi = max_value - 1
112
+
113
+ [lo, mid, hi].select { |val| value_within_range?(val) }
114
+ end
115
+
116
+ def value_within_range?(val)
117
+ (min_value .. max_value).include?(val)
118
+ end
119
+ end
120
+
121
+ def generate_bit_classes_to_test(endian)
122
+ bits = {}
123
+ (1 .. 50).each do |nbits|
124
+ name = (endian == :big) ? "Bit#{nbits}" : "Bit#{nbits}le"
125
+ bit_class = BinData.const_get(name)
126
+ bits[bit_class] = nbits
127
+ end
128
+ bits
129
+ end
130
+
131
+ describe "Big endian bitfields" do
132
+ include_examples "All bitfields"
133
+
134
+ before(:all) do
135
+ @bits = generate_bit_classes_to_test(:big)
136
+ end
137
+
138
+ it "read big endian values" do
139
+ @bits.each_pair do |bit_class, nbits|
140
+ nbytes = (nbits + 7) / 8
141
+ str = [0b1000_0000].pack("C") + "\000" * (nbytes - 1)
142
+
143
+ bit_class.read(str).should == 1 << (nbits - 1)
144
+ end
145
+ end
146
+ end
147
+
148
+ describe "Little endian bitfields" do
149
+ include_examples "All bitfields"
150
+
151
+ before(:all) do
152
+ @bits = generate_bit_classes_to_test(:little)
153
+ end
154
+
155
+ it "read little endian values" do
156
+ @bits.each_pair do |bit_class, nbits|
157
+ nbytes = (nbits + 7) / 8
158
+ str = [0b0000_0001].pack("C") + "\000" * (nbytes - 1)
159
+
160
+ bit_class.read(str).should == 1
161
+ end
162
+ end
163
+ end