bindata 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bindata might be problematic. Click here for more details.

@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.dirname(__FILE__)) + '/spec_common'
4
+ require 'bindata/lazy'
5
+
6
+ # A mock data object that can substitute for BinData::Simple or BinData::Struct
7
+ class MockDataObject
8
+ def initialize(value = nil, fields = {})
9
+ @value = value
10
+ @fields = fields
11
+ end
12
+ attr_accessor :value
13
+
14
+ def field_names
15
+ @fields.keys.collect { |k| k.to_s }
16
+ end
17
+ def respond_to?(symbol, include_private = false)
18
+ field_names.include?(symbol.id2name) ? true : super
19
+ end
20
+ def method_missing(symbol, *args)
21
+ @fields[symbol] || super
22
+ end
23
+ end
24
+
25
+ context "A single environment" do
26
+ setup do
27
+ @do1 = MockDataObject.new('v1', :f1 => 'f1')
28
+ @e1 = BinData::LazyEvalEnv.new
29
+ @e1.data_object = @do1
30
+ @e1.params = {:p1 => 'p1'}
31
+ end
32
+
33
+ specify "should evaluate value" do
34
+ @e1.lazy_eval(lambda { value }).should == 'v1'
35
+ end
36
+
37
+ specify "should evaluate index" do
38
+ @e1.index = 7
39
+ @e1.lazy_eval(lambda { index }).should == 7
40
+ end
41
+
42
+ specify "should evaluate offset" do
43
+ @e1.offset = 9
44
+ @e1.lazy_eval(lambda { offset }).should == 9
45
+ end
46
+
47
+ specify "should not resolve any unknown fields" do
48
+ lambda { @e1.lazy_eval(lambda { unknown }) }.should raise_error(NameError)
49
+ lambda { @e1.lazy_eval(lambda { p1 }) }.should raise_error(NameError)
50
+ lambda { @e1.lazy_eval(lambda { f1 }) }.should raise_error(NameError)
51
+ end
52
+
53
+ specify "should accept symbols as a shortcut to lambda" do
54
+ @e1.index = 7
55
+ @e1.offset = 9
56
+ @e1.lazy_eval(:value).should == 'v1'
57
+ @e1.lazy_eval(:index).should == 7
58
+ @e1.lazy_eval(:offset).should == 9
59
+ end
60
+ end
61
+
62
+ context "An environment with one parent" do
63
+ setup do
64
+ @do2 = MockDataObject.new(nil, :f2 => 'f2', :common => 'field2')
65
+ @do1 = MockDataObject.new
66
+
67
+ @e2 = BinData::LazyEvalEnv.new
68
+ @e1 = BinData::LazyEvalEnv.new(@e2)
69
+
70
+ @e2.data_object = @do2
71
+ @e1.data_object = @do1
72
+
73
+ @e2.params = {:p2 => 'p2', :l2 => lambda { 'l2' }, :common => 'param2'}
74
+ end
75
+
76
+ specify "should evaluate parent parameter" do
77
+ @e1.lazy_eval(:p2).should == 'p2'
78
+ end
79
+
80
+ specify "should evaluate parent field" do
81
+ @e1.lazy_eval(:f2).should == 'f2'
82
+ end
83
+
84
+ specify "should prefer parent param over parent field" do
85
+ @e1.lazy_eval(:common).should == 'param2'
86
+ end
87
+ end
88
+
89
+ context "A nested environment" do
90
+ setup do
91
+ @do4 = MockDataObject.new(nil, :f4 => 'f4')
92
+ @do3 = MockDataObject.new(nil, :f3 => 'f3')
93
+ @do2 = MockDataObject.new(nil, :f2 => 'f2')
94
+ @do1 = MockDataObject.new(nil, :f1 => 'f1')
95
+
96
+ @e4 = BinData::LazyEvalEnv.new
97
+ @e3 = BinData::LazyEvalEnv.new(@e4)
98
+ @e2 = BinData::LazyEvalEnv.new(@e3)
99
+ @e1 = BinData::LazyEvalEnv.new(@e2)
100
+
101
+ @e4.data_object = @do4
102
+ @e3.data_object = @do3
103
+ @e2.data_object = @do2
104
+ @e1.data_object = @do1
105
+
106
+ @e4.params = {:p4 => 'p4', :s4 => 's4', :l4 => 'l4'}
107
+ @e3.params = {:p3 => 'p3', :s3 => :s4, :l3 => lambda { l4 }}
108
+ @e2.params = {:p2 => 'p2', :s2 => :s3, :l2 => lambda { l3 }}
109
+ end
110
+
111
+ specify "should access parent environments" do
112
+ @e1.lazy_eval(lambda { parent.p3 }).should == 'p3'
113
+ @e1.lazy_eval(lambda { parent.parent.p4 }).should == 'p4'
114
+ end
115
+
116
+ specify "should cascade lambdas" do
117
+ @e1.lazy_eval(lambda { l2 }).should == 'l4'
118
+ @e1.lazy_eval(lambda { s2 }).should == 's4'
119
+ end
120
+ end
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.dirname(__FILE__)) + '/spec_common'
4
+ require 'bindata/registry'
5
+
6
+ context "The Registry" do
7
+ setup do
8
+ @r = BinData::Registry.instance
9
+ end
10
+
11
+ specify "should be a singleton" do
12
+ BinData::Registry.instance.should equal(BinData::Registry.instance)
13
+ end
14
+
15
+ specify "should lookup registered names" do
16
+ A = Class.new
17
+ B = Class.new
18
+ @r.register('ASubClass', A)
19
+ @r.register('AnotherSubClass', B)
20
+
21
+ @r.lookup('a_sub_class').should == A
22
+ @r.lookup('another_sub_class').should == B
23
+ end
24
+
25
+ specify "should not lookup unregistered names" do
26
+ @r.lookup('a_non_existent_sub_class').should be_nil
27
+ end
28
+
29
+ specify "should convert CamelCase to underscores" do
30
+ @r.register('CamelCase', A).should == 'camel_case'
31
+ end
32
+
33
+ specify "should convert adjacent caps camelCase to underscores" do
34
+ @r.register('XYZCamelCase', A).should == 'xyz_camel_case'
35
+ end
36
+
37
+ specify "should ignore the outer nestings of classes" do
38
+ @r.register('A::B::C', A).should == 'c'
39
+ end
40
+
41
+ specify "should allow overriding of registered classes" do
42
+ @r.register('A', A)
43
+ @r.register('A', B)
44
+
45
+ @r.lookup('a').should == B
46
+ end
47
+ end
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.dirname(__FILE__)) + '/spec_common'
4
+ require 'bindata/single'
5
+
6
+ # An implementation of a unsigned 4 byte integer.
7
+ class ConcreteSingle < BinData::Single
8
+ def val_to_str(val) [val].pack("V") end
9
+ def read_val(io) readbytes(io, 4).unpack("V")[0] end
10
+ def sensible_default() 0 end
11
+
12
+ def in_read?() @in_read end
13
+ end
14
+
15
+ context "The sample implementation of Single" do
16
+ specify "should have symmetric IO" do
17
+ io = StringIO.new
18
+ data = ConcreteSingle.new
19
+ data.value = 42
20
+ data.write(io)
21
+
22
+ io.rewind
23
+ data = ConcreteSingle.new
24
+ data.read(io)
25
+ data.value.should == 42
26
+ end
27
+ end
28
+
29
+ context "The class Single" do
30
+ specify "should register subclasses" do
31
+ BinData::Single.lookup(:concrete_single).should == ConcreteSingle
32
+ end
33
+
34
+ specify "should read and return a value" do
35
+ io = StringIO.new([123456].pack("V"))
36
+ ConcreteSingle.read(io).should == 123456
37
+ data = ConcreteSingle.new
38
+ end
39
+ end
40
+
41
+ context "A Single object" do
42
+ specify "should conform to rule 1 for returning a value" do
43
+ data = ConcreteSingle.new(:value => 5)
44
+ data.should_not be_in_read
45
+ data.value.should == 5
46
+ end
47
+
48
+ specify "should conform to rule 2 for returning a value" do
49
+ io = StringIO.new([42].pack("V"))
50
+ data = ConcreteSingle.new(:value => 5)
51
+ data.do_read(io)
52
+ data.should be_in_read
53
+ data.value.should == 42
54
+ end
55
+
56
+ specify "should conform to rule 3 for returning a value" do
57
+ data = ConcreteSingle.new(:initial_value => 5)
58
+ data.should be_clear
59
+ data.value.should == 5
60
+ end
61
+
62
+ specify "should conform to rule 4 for returning a value" do
63
+ data = ConcreteSingle.new(:initial_value => 5)
64
+ data.value = 17
65
+ data.should_not be_clear
66
+ data.value.should == 17
67
+ end
68
+
69
+ specify "should conform to rule 5 for returning a value" do
70
+ data = ConcreteSingle.new
71
+ data.should be_clear
72
+ data.value.should == 0
73
+ end
74
+
75
+ specify "should conform to rule 6 for returning a value" do
76
+ data = ConcreteSingle.new
77
+ data.value = 8
78
+ data.should_not be_clear
79
+ data.value.should == 8
80
+ end
81
+ end
82
+
83
+ context "A new Single object" do
84
+ setup do
85
+ @data = ConcreteSingle.new
86
+ end
87
+
88
+ specify "should not allow both :initial_value and :value" do
89
+ params = {:initial_value => 1, :value => 2}
90
+ lambda { ConcreteSingle.new(params) }.should raise_error(ArgumentError)
91
+ end
92
+
93
+ specify "should have a sensible value" do
94
+ @data.value.should == 0
95
+ end
96
+
97
+ specify "should allowing setting and retrieving value" do
98
+ @data.value = 5
99
+ @data.value.should == 5
100
+ end
101
+
102
+ specify "should be clear" do
103
+ @data.should be_clear
104
+ end
105
+
106
+ specify "should not be clear after setting value" do
107
+ @data.value = 5
108
+ @data.should_not be_clear
109
+ end
110
+
111
+ specify "should not be clear after reading" do
112
+ io = StringIO.new([123456].pack("V"))
113
+ @data.read(io)
114
+ @data.should_not be_clear
115
+ end
116
+
117
+ specify "should return num_bytes" do
118
+ @data.num_bytes.should == 4
119
+ end
120
+
121
+ specify "should not contain any field names" do
122
+ @data.field_names.should be_empty
123
+ end
124
+
125
+ specify "should return a snapshot" do
126
+ @data.value = 5
127
+ @data.snapshot.should == 5
128
+ end
129
+ end
130
+
131
+ context "A Single with :initial_value" do
132
+ setup do
133
+ @data = ConcreteSingle.new(:initial_value => 5)
134
+ end
135
+
136
+ specify "should return that initial value before reading or being set" do
137
+ @data.value.should == 5
138
+ end
139
+
140
+ specify "should forget :initial_value after being set" do
141
+ @data.value = 17
142
+ @data.value.should_not == 5
143
+ end
144
+
145
+ specify "should forget :initial_value after reading" do
146
+ io = StringIO.new([56].pack("V"))
147
+ @data.read(io)
148
+ @data.value.should_not == 5
149
+ end
150
+
151
+ specify "should remember :initial_value after being cleared" do
152
+ @data.value = 17
153
+ @data.clear
154
+ @data.value.should == 5
155
+ end
156
+ end
157
+
158
+ context "A Single with :value" do
159
+ setup do
160
+ @data = ConcreteSingle.new(:value => 5)
161
+ end
162
+
163
+ specify "should return that :value" do
164
+ @data.value.should == 5
165
+ end
166
+
167
+ specify "should change during reading" do
168
+ io = StringIO.new([56].pack("V"))
169
+ @data.do_read(io)
170
+ @data.value.should == 56
171
+ @data.done_read
172
+ end
173
+
174
+ specify "should not change after reading" do
175
+ io = StringIO.new([56].pack("V"))
176
+ @data.read(io)
177
+ @data.value.should == 5
178
+ end
179
+
180
+ specify "should not be able to change the value" do
181
+ @data.value = 17
182
+ @data.value.should == 5
183
+ end
184
+ end
185
+
186
+ context "A Single with :check_value" do
187
+ setup do
188
+ @io = StringIO.new([34].pack("V"))
189
+ end
190
+
191
+ specify "should succeed when check_value is non boolean and correct" do
192
+ data = ConcreteSingle.new(:check_value => 34)
193
+ lambda { data.read(@io) }.should_not raise_error
194
+ end
195
+
196
+ specify "should fail when check_value is non boolean and incorrect" do
197
+ data = ConcreteSingle.new(:check_value => lambda { 123 * 5 })
198
+ lambda { data.read(@io) }.should raise_error(BinData::ValidityError)
199
+ end
200
+
201
+ specify "should succeed when check_value is boolean and true" do
202
+ data = ConcreteSingle.new(:check_value => lambda { (value % 2) == 0})
203
+ lambda { data.read(@io) }.should_not raise_error
204
+ end
205
+
206
+ specify "should fail when check_value is boolean and false" do
207
+ data = ConcreteSingle.new(:check_value => lambda { value > 100 })
208
+ lambda { data.read(@io) }.should raise_error(BinData::ValidityError)
209
+ end
210
+ end
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
2
+
3
+ begin
4
+ require 'rubygems'
5
+ gem 'rspec', '> 0.8.0'
6
+ rescue LoadError
7
+ end
8
+
9
+ require 'spec'
10
+ require 'stringio'
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.dirname(__FILE__)) + '/spec_common'
4
+ require 'bindata/string'
5
+
6
+ context "Test mutual exclusion of parameters" do
7
+ specify ":value and :initial_value" do
8
+ params = {:value => "", :initial_value => ""}
9
+ lambda { BinData::String.new(params) }.should raise_error(ArgumentError)
10
+ end
11
+
12
+ specify ":length and :initial_length" do
13
+ params = {:length => 5, :initial_length => 5}
14
+ lambda { BinData::String.new(params) }.should raise_error(ArgumentError)
15
+ end
16
+
17
+ specify ":initial_value and :initial_length" do
18
+ params = {:initial_value => "", :initial_length => 5}
19
+ lambda { BinData::String.new(params) }.should raise_error(ArgumentError)
20
+ end
21
+
22
+ specify ":value and :length" do
23
+ params = {:value => "", :length => 5}
24
+ lambda { BinData::String.new(params) }.should raise_error(ArgumentError)
25
+ end
26
+ end
27
+
28
+ context "A String with :initial_length" do
29
+ setup do
30
+ @str = BinData::String.new(:initial_length => 5)
31
+ end
32
+
33
+ specify "should set num_bytes" do
34
+ @str.num_bytes.should == 5
35
+ end
36
+
37
+ specify "should fill value with pad_char" do
38
+ @str.value.should == "\0\0\0\0\0"
39
+ end
40
+
41
+ specify "should read :initial_length bytes" do
42
+ io = StringIO.new("abcdefghij")
43
+ @str.read(io)
44
+ @str.value.should == "abcde"
45
+ end
46
+
47
+ specify "should forget :initial_length after value is set" do
48
+ @str.value = "abc"
49
+ @str.num_bytes.should == 3
50
+ end
51
+
52
+ specify "should remember :initial_length after value is cleared" do
53
+ @str.value = "abc"
54
+ @str.num_bytes.should == 3
55
+ @str.clear
56
+ @str.num_bytes.should == 5
57
+ end
58
+ end
59
+
60
+ context "A String with :length" do
61
+ setup do
62
+ @str = BinData::String.new(:length => 5)
63
+ end
64
+
65
+ specify "should set num_bytes" do
66
+ @str.num_bytes.should == 5
67
+ end
68
+
69
+ specify "should fill value with pad_char" do
70
+ @str.value.should == "\0\0\0\0\0"
71
+ end
72
+
73
+ specify "should retain :length after value is set" do
74
+ @str.value = "abcdefghij"
75
+ @str.num_bytes.should == 5
76
+ end
77
+
78
+ specify "should read :length bytes" do
79
+ io = StringIO.new("abcdefghij")
80
+ @str.read(io)
81
+ @str.value.should == "abcde"
82
+ end
83
+
84
+ specify "should pad values less than :length" do
85
+ @str.value = "abc"
86
+ @str.value.should == "abc\0\0"
87
+ end
88
+
89
+ specify "should accept values exactly :length" do
90
+ @str.value = "abcde"
91
+ @str.value.should == "abcde"
92
+ end
93
+
94
+ specify "should truncate values greater than :length" do
95
+ @str.value = "abcdefg"
96
+ @str.value.should == "abcde"
97
+ end
98
+ end
99
+
100
+ context "A String with :initial_length and :value" do
101
+ setup do
102
+ @str = BinData::String.new(:initial_length => 5, :value => "abcdefghij")
103
+ end
104
+
105
+ specify "should use :initial_length before value is read" do
106
+ @str.num_bytes.should == 5
107
+ @str.value.should == "abcde"
108
+ end
109
+
110
+ specify "should use :initial_length for reading" do
111
+ io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
112
+ @str.read(io)
113
+ io.pos.should == 5
114
+ end
115
+
116
+ specify "should forget :initial_length after reading" do
117
+ io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
118
+ @str.read(io)
119
+ @str.num_bytes.should == 10
120
+ @str.value.should == "abcdefghij"
121
+ end
122
+
123
+ specify "should return read value before calling done_read" do
124
+ io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
125
+
126
+ @str.do_read(io)
127
+ @str.value.should == "ABCDE"
128
+
129
+ @str.done_read
130
+ @str.value.should == "abcdefghij"
131
+ end
132
+ end
133
+
134
+ context "A String with :length and :initial_value" do
135
+ setup do
136
+ @str = BinData::String.new(:length => 5, :initial_value => "abcdefghij")
137
+ end
138
+
139
+ specify "should apply :length to :initial_value" do
140
+ @str.num_bytes.should == 5
141
+ @str.value.should == "abcde"
142
+ end
143
+
144
+ specify "should forget :initial_value after reading" do
145
+ io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
146
+ @str.read(io)
147
+ io.pos.should == 5
148
+ @str.num_bytes.should == 5
149
+ @str.value.should == "ABCDE"
150
+ end
151
+ end
152
+
153
+ context "A String with :pad_char" do
154
+ specify "should accept a numeric value for :pad_char" do
155
+ @str = BinData::String.new(:length => 5, :pad_char => 6)
156
+ @str.value = "abc"
157
+ @str.value.should == "abc\x06\x06"
158
+ end
159
+
160
+ specify "should accept a character for :pad_char" do
161
+ @str = BinData::String.new(:length => 5, :pad_char => "R")
162
+ @str.value = "abc"
163
+ @str.value.should == "abcRR"
164
+ end
165
+
166
+ specify "should not accept a string for :pad_char" do
167
+ params = {:length => 5, :pad_char => "RR"}
168
+ lambda { BinData::String.new(params) }.should raise_error(ArgumentError)
169
+ end
170
+ end
171
+
172
+ context "A String with :trim_value" do
173
+ specify "set false is the default" do
174
+ str1 = BinData::String.new(:length => 5)
175
+ str2 = BinData::String.new(:length => 5, :trim_value => false)
176
+ str1.value = "abc"
177
+ str2.value = "abc"
178
+ str1.value.should == "abc\0\0"
179
+ str2.value.should == "abc\0\0"
180
+ end
181
+
182
+ specify "should trim the value" do
183
+ str = BinData::String.new(:pad_char => 'R', :trim_value => true)
184
+ str.value = "abcRR"
185
+ str.value.should == "abc"
186
+ end
187
+
188
+ specify "should not affect num_bytes" do
189
+ str = BinData::String.new(:pad_char => 'R', :trim_value => true)
190
+ str.value = "abcRR"
191
+ str.num_bytes.should == 5
192
+ end
193
+
194
+ specify "should trim if last char is :pad_char" do
195
+ str = BinData::String.new(:pad_char => 'R', :trim_value => true)
196
+ str.value = "abcRR"
197
+ str.value.should == "abc"
198
+ end
199
+
200
+ specify "should not trim if value contains :pad_char not at the end" do
201
+ str = BinData::String.new(:pad_char => 'R', :trim_value => true)
202
+ str.value = "abcRRde"
203
+ str.value.should == "abcRRde"
204
+ end
205
+ end