bit-struct 0.13.1
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.
- data/.gitignore +2 -0
- data/History.txt +102 -0
- data/README.txt +189 -0
- data/Rakefile +34 -0
- data/TODO +23 -0
- data/TODO-ALSO +71 -0
- data/examples/ara-player-data.rb +82 -0
- data/examples/bignum.rb +18 -0
- data/examples/bits.rb +19 -0
- data/examples/byte-bdy.rb +30 -0
- data/examples/field-ripper.rb +22 -0
- data/examples/fixed-point.rb +17 -0
- data/examples/ip.rb +81 -0
- data/examples/longlong.rb +30 -0
- data/examples/md.rb +23 -0
- data/examples/modular-def.rb +38 -0
- data/examples/native.rb +31 -0
- data/examples/nested.rb +33 -0
- data/examples/pad.rb +14 -0
- data/examples/ping-recv.rb +25 -0
- data/examples/ping.rb +73 -0
- data/examples/player-data.rb +75 -0
- data/examples/raw.rb +62 -0
- data/examples/rest.rb +30 -0
- data/examples/switch-endian.rb +49 -0
- data/examples/vector.rb +98 -0
- data/lib/bit-struct.rb +6 -0
- data/lib/bit-struct/bit-struct.rb +549 -0
- data/lib/bit-struct/char-field.rb +48 -0
- data/lib/bit-struct/fields.rb +273 -0
- data/lib/bit-struct/float-field.rb +61 -0
- data/lib/bit-struct/hex-octet-field.rb +20 -0
- data/lib/bit-struct/nested-field.rb +76 -0
- data/lib/bit-struct/octet-field.rb +45 -0
- data/lib/bit-struct/pad-field.rb +15 -0
- data/lib/bit-struct/signed-field.rb +258 -0
- data/lib/bit-struct/text-field.rb +44 -0
- data/lib/bit-struct/unsigned-field.rb +248 -0
- data/lib/bit-struct/vector-field.rb +77 -0
- data/lib/bit-struct/vector.rb +173 -0
- data/lib/bit-struct/yaml.rb +69 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test-endian.rb +39 -0
- data/test/test-vector.rb +38 -0
- data/test/test.rb +433 -0
- metadata +126 -0
data/tasks/spec.rake
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
if HAVE_SPEC_RAKE_SPECTASK and not PROJ.spec.files.to_a.empty?
|
3
|
+
require 'spec/rake/verify_rcov'
|
4
|
+
|
5
|
+
namespace :spec do
|
6
|
+
|
7
|
+
desc 'Run all specs with basic output'
|
8
|
+
Spec::Rake::SpecTask.new(:run) do |t|
|
9
|
+
t.ruby_opts = PROJ.ruby_opts
|
10
|
+
t.spec_opts = PROJ.spec.opts
|
11
|
+
t.spec_files = PROJ.spec.files
|
12
|
+
t.libs += PROJ.libs
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Run all specs with text output'
|
16
|
+
Spec::Rake::SpecTask.new(:specdoc) do |t|
|
17
|
+
t.ruby_opts = PROJ.ruby_opts
|
18
|
+
t.spec_opts = PROJ.spec.opts + ['--format', 'specdoc']
|
19
|
+
t.spec_files = PROJ.spec.files
|
20
|
+
t.libs += PROJ.libs
|
21
|
+
end
|
22
|
+
|
23
|
+
if HAVE_RCOV
|
24
|
+
desc 'Run all specs with RCov'
|
25
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
26
|
+
t.ruby_opts = PROJ.ruby_opts
|
27
|
+
t.spec_opts = PROJ.spec.opts
|
28
|
+
t.spec_files = PROJ.spec.files
|
29
|
+
t.libs += PROJ.libs
|
30
|
+
t.rcov = true
|
31
|
+
t.rcov_dir = PROJ.rcov.dir
|
32
|
+
t.rcov_opts = PROJ.rcov.opts + ['--exclude', 'spec']
|
33
|
+
end
|
34
|
+
|
35
|
+
RCov::VerifyTask.new(:verify) do |t|
|
36
|
+
t.threshold = PROJ.rcov.threshold
|
37
|
+
t.index_html = File.join(PROJ.rcov.dir, 'index.html')
|
38
|
+
t.require_exact_threshold = PROJ.rcov.threshold_exact
|
39
|
+
end
|
40
|
+
|
41
|
+
task :verify => :rcov
|
42
|
+
remove_desc_for_task %w(spec:clobber_rcov)
|
43
|
+
end
|
44
|
+
|
45
|
+
end # namespace :spec
|
46
|
+
|
47
|
+
desc 'Alias to spec:run'
|
48
|
+
task :spec => 'spec:run'
|
49
|
+
|
50
|
+
task :clobber => 'spec:clobber_rcov' if HAVE_RCOV
|
51
|
+
|
52
|
+
end # if HAVE_SPEC_RAKE_SPECTASK
|
53
|
+
|
54
|
+
# EOF
|
data/tasks/svn.rake
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
if HAVE_SVN
|
3
|
+
|
4
|
+
unless PROJ.svn.root
|
5
|
+
info = %x/svn info ./
|
6
|
+
m = %r/^Repository Root:\s+(.*)$/.match(info)
|
7
|
+
PROJ.svn.root = (m.nil? ? '' : m[1])
|
8
|
+
end
|
9
|
+
PROJ.svn.root = File.join(PROJ.svn.root, PROJ.svn.path) unless PROJ.svn.path.empty?
|
10
|
+
|
11
|
+
namespace :svn do
|
12
|
+
|
13
|
+
# A prerequisites task that all other tasks depend upon
|
14
|
+
task :prereqs
|
15
|
+
|
16
|
+
desc 'Show tags from the SVN repository'
|
17
|
+
task :show_tags => 'svn:prereqs' do |t|
|
18
|
+
tags = %x/svn list #{File.join(PROJ.svn.root, PROJ.svn.tags)}/
|
19
|
+
tags.gsub!(%r/\/$/, '')
|
20
|
+
tags = tags.split("\n").sort {|a,b| b <=> a}
|
21
|
+
puts tags
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Create a new tag in the SVN repository'
|
25
|
+
task :create_tag => 'svn:prereqs' do |t|
|
26
|
+
v = ENV['VERSION'] or abort 'Must supply VERSION=x.y.z'
|
27
|
+
abort "Versions don't match #{v} vs #{PROJ.version}" if v != PROJ.version
|
28
|
+
|
29
|
+
svn = PROJ.svn
|
30
|
+
trunk = File.join(svn.root, svn.trunk)
|
31
|
+
tag = "%s-%s" % [PROJ.name, PROJ.version]
|
32
|
+
tag = File.join(svn.root, svn.tags, tag)
|
33
|
+
msg = "Creating tag for #{PROJ.name} version #{PROJ.version}"
|
34
|
+
|
35
|
+
puts "Creating SVN tag '#{tag}'"
|
36
|
+
unless system "svn cp -m '#{msg}' #{trunk} #{tag}"
|
37
|
+
abort "Tag creation failed"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end # namespace :svn
|
42
|
+
|
43
|
+
task 'gem:release' => 'svn:create_tag'
|
44
|
+
|
45
|
+
end # if PROJ.svn.path
|
46
|
+
|
47
|
+
# EOF
|
data/tasks/test.rake
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
namespace :test do
|
6
|
+
|
7
|
+
Rake::TestTask.new(:run) do |t|
|
8
|
+
t.libs = PROJ.libs
|
9
|
+
t.test_files = if test(?f, PROJ.test.file) then [PROJ.test.file]
|
10
|
+
else PROJ.test.files end
|
11
|
+
t.ruby_opts += PROJ.ruby_opts
|
12
|
+
t.ruby_opts += PROJ.test.opts
|
13
|
+
end
|
14
|
+
|
15
|
+
if HAVE_RCOV
|
16
|
+
desc 'Run rcov on the unit tests'
|
17
|
+
task :rcov => :clobber_rcov do
|
18
|
+
opts = PROJ.rcov.opts.dup << '-o' << PROJ.rcov.dir
|
19
|
+
opts = opts.join(' ')
|
20
|
+
files = if test(?f, PROJ.test.file) then [PROJ.test.file]
|
21
|
+
else PROJ.test.files end
|
22
|
+
files = files.join(' ')
|
23
|
+
sh "#{RCOV} #{files} #{opts}"
|
24
|
+
end
|
25
|
+
|
26
|
+
task :clobber_rcov do
|
27
|
+
rm_r 'coverage' rescue nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end # namespace :test
|
32
|
+
|
33
|
+
desc 'Alias to test:run'
|
34
|
+
task :test => 'test:run'
|
35
|
+
|
36
|
+
task :clobber => 'test:clobber_rcov' if HAVE_RCOV
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# EOF
|
data/tasks/zentest.rake
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
if HAVE_ZENTEST
|
2
|
+
|
3
|
+
# --------------------------------------------------------------------------
|
4
|
+
if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
|
5
|
+
require 'autotest'
|
6
|
+
|
7
|
+
namespace :test do
|
8
|
+
task :autotest do
|
9
|
+
Autotest.run
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Run the autotest loop"
|
14
|
+
task :autotest => 'test:autotest'
|
15
|
+
|
16
|
+
end # if test
|
17
|
+
|
18
|
+
# --------------------------------------------------------------------------
|
19
|
+
if HAVE_SPEC_RAKE_SPECTASK and not PROJ.spec.files.to_a.empty?
|
20
|
+
require 'autotest/rspec'
|
21
|
+
|
22
|
+
namespace :spec do
|
23
|
+
task :autotest do
|
24
|
+
load '.autotest' if test(?f, '.autotest')
|
25
|
+
Autotest::Rspec.run
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Run the autotest loop"
|
30
|
+
task :autotest => 'spec:autotest'
|
31
|
+
|
32
|
+
end # if rspec
|
33
|
+
|
34
|
+
end # if HAVE_ZENTEST
|
35
|
+
|
36
|
+
# EOF
|
data/test/test-endian.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'bit-struct'
|
3
|
+
|
4
|
+
class Test_Endian < Test::Unit::TestCase
|
5
|
+
class Endian < BitStruct
|
6
|
+
unsigned :f_big, 32, :endian => :big
|
7
|
+
unsigned :f_little, 32, :endian => :little
|
8
|
+
unsigned :f_native, 32, :endian => :native
|
9
|
+
unsigned :f_network, 32, :endian => :network
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :bs
|
13
|
+
|
14
|
+
def setup
|
15
|
+
@bs = Endian.new
|
16
|
+
bs.f_big = bs.f_little = bs.f_native = bs.f_network = 0x01020304
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_readers
|
20
|
+
assert_equal(0x01020304, bs.f_big)
|
21
|
+
assert_equal(0x01020304, bs.f_little)
|
22
|
+
assert_equal(0x01020304, bs.f_native)
|
23
|
+
assert_equal(0x01020304, bs.f_network)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_writers
|
27
|
+
bs.fields.each do |field|
|
28
|
+
byte_offset = field.offset / 8
|
29
|
+
valstr = bs.to_s[byte_offset, 4]
|
30
|
+
case field.options[:endian]
|
31
|
+
when :big, :network
|
32
|
+
assert_equal("\01\02\03\04", valstr)
|
33
|
+
when :little
|
34
|
+
assert_equal("\04\03\02\01", valstr)
|
35
|
+
when :native
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/test/test-vector.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'bit-struct'
|
3
|
+
|
4
|
+
class Test_Vector < Test::Unit::TestCase
|
5
|
+
class Packet < BitStruct
|
6
|
+
unsigned :stuff, 32, "whatever"
|
7
|
+
|
8
|
+
vector :v, "a vector", :length => 5 do
|
9
|
+
unsigned :x, 16
|
10
|
+
signed :y, 32
|
11
|
+
end
|
12
|
+
|
13
|
+
unsigned :other, 16, "other stuff"
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :pkt
|
17
|
+
|
18
|
+
def setup
|
19
|
+
@pkt = Packet.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_length
|
23
|
+
assert_equal(Packet.round_byte_length, pkt.length)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_writers
|
27
|
+
assert_equal(pkt.v[2].x, 0)
|
28
|
+
v = pkt.v
|
29
|
+
xy = v[2]
|
30
|
+
xy.x = 3
|
31
|
+
xy.y = -4
|
32
|
+
v[2] = xy
|
33
|
+
assert_equal(pkt.v[2].x, 0)
|
34
|
+
pkt.v = v
|
35
|
+
assert_equal(pkt.v[2].x, 3)
|
36
|
+
assert_equal(pkt.v[2].y, -4)
|
37
|
+
end
|
38
|
+
end
|
data/test/test.rb
ADDED
@@ -0,0 +1,433 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'bit-struct'
|
5
|
+
|
6
|
+
class Test_BitStruct < Test::Unit::TestCase
|
7
|
+
|
8
|
+
class T1 < BitStruct
|
9
|
+
unsigned :foo, 8
|
10
|
+
end
|
11
|
+
|
12
|
+
class T2 < BitStruct
|
13
|
+
unsigned :bar, 8
|
14
|
+
end
|
15
|
+
|
16
|
+
class Rest < BitStruct
|
17
|
+
unsigned :zap, 8
|
18
|
+
rest :body, T2
|
19
|
+
end
|
20
|
+
|
21
|
+
class NestedPart < BitStruct
|
22
|
+
unsigned :x, 5
|
23
|
+
unsigned :y, 3, :default => 2
|
24
|
+
char :s, 5*8
|
25
|
+
end
|
26
|
+
|
27
|
+
class Container < BitStruct
|
28
|
+
nest :n1, NestedPart, :default => NestedPart.new(:x=>1, :s=>"deflt")
|
29
|
+
nest :n2, NestedPart
|
30
|
+
end
|
31
|
+
|
32
|
+
class Overflow < BitStruct
|
33
|
+
unsigned :f1, 3
|
34
|
+
unsigned :f2, 5
|
35
|
+
unsigned :f3, 8
|
36
|
+
unsigned :f4, 16
|
37
|
+
unsigned :f5, 32
|
38
|
+
char :f6, 24
|
39
|
+
unsigned :f7, 8
|
40
|
+
end
|
41
|
+
|
42
|
+
class BS < BitStruct
|
43
|
+
def self.next_name
|
44
|
+
(@@next_name ||= "f000").succ!.dup
|
45
|
+
end
|
46
|
+
|
47
|
+
unsigned next_name, 13
|
48
|
+
unsigned next_name, 6
|
49
|
+
signed next_name, 5
|
50
|
+
|
51
|
+
char next_name, 16
|
52
|
+
|
53
|
+
unsigned next_name, 2
|
54
|
+
signed next_name, 3, :default => -2
|
55
|
+
unsigned next_name, 3
|
56
|
+
|
57
|
+
unsigned f1=next_name, 16
|
58
|
+
|
59
|
+
signed next_name, 8
|
60
|
+
signed next_name, 16, :endian => :little
|
61
|
+
signed next_name, 16, :endian => :native
|
62
|
+
signed f2=next_name, 32
|
63
|
+
unsigned next_name, 56, :endian => :big
|
64
|
+
signed next_name, 48, :endian => :little
|
65
|
+
|
66
|
+
char next_name, 8, :default => "b"
|
67
|
+
float next_name, 32, :default => 1.23
|
68
|
+
float next_name, 64, :format => "%10.5f"
|
69
|
+
|
70
|
+
octets next_name, 32, :default => "192.168.1.123"
|
71
|
+
|
72
|
+
hex_octets next_name, 48, :default => "ab:cd:ef:01:23:45"
|
73
|
+
|
74
|
+
unsigned next_name, 1
|
75
|
+
unsigned next_name, 11, :fixed => 100 # unaligned!
|
76
|
+
signed next_name, 7 # unaligned!
|
77
|
+
signed next_name, 14 # unaligned and touches 3 bytes
|
78
|
+
signed next_name, 7, :fixed => 1000 # unaligned!
|
79
|
+
|
80
|
+
char next_name, 24
|
81
|
+
|
82
|
+
rest :bs_body
|
83
|
+
|
84
|
+
INITIAL_VALUES = {
|
85
|
+
f1 => 1234,
|
86
|
+
f2 => 5678
|
87
|
+
}
|
88
|
+
|
89
|
+
INITIAL_VALUES.each do |f, v|
|
90
|
+
initial_value.send "#{f}=", v
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
class BS_1 < BS
|
96
|
+
|
97
|
+
signed next_name, 4, :fixed => 25
|
98
|
+
unsigned next_name, 3, :fixed => 0.01
|
99
|
+
unsigned next_name, 1
|
100
|
+
|
101
|
+
unsigned next_name, 32, :fixed => 1000
|
102
|
+
|
103
|
+
char next_name, 40
|
104
|
+
text next_name, 40
|
105
|
+
|
106
|
+
unsigned next_name, 8, :default => 0xEF
|
107
|
+
|
108
|
+
unsigned next_name, 8
|
109
|
+
|
110
|
+
unsigned next_name, 1
|
111
|
+
|
112
|
+
rest :bs1_body
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
def setup
|
117
|
+
srand(767343)
|
118
|
+
@bs = BS.new
|
119
|
+
@bs_1 = BS_1.new
|
120
|
+
@simple = [T1.new, T2.new]
|
121
|
+
@testers = @simple + [@bs, @bs_1]
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_init
|
125
|
+
@testers.each do |bs|
|
126
|
+
if defined?(bs.class::INITIAL_VALUES)
|
127
|
+
initial_values = bs.class::INITIAL_VALUES
|
128
|
+
end
|
129
|
+
bs.fields.each do |field|
|
130
|
+
iv = initial_values && initial_values[field.name]
|
131
|
+
iv ||= field.default
|
132
|
+
|
133
|
+
if iv
|
134
|
+
case field
|
135
|
+
when BitStruct::FloatField
|
136
|
+
assert_in_delta(iv, bs.send(field.name), 100,
|
137
|
+
"In #{field.name} of a #{bs.class}")
|
138
|
+
else
|
139
|
+
assert_equal(iv, bs.send(field.name),
|
140
|
+
"In #{field.name} of a #{bs.class}")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_init_with_value
|
148
|
+
randomize(@bs_1)
|
149
|
+
|
150
|
+
b = BS_1.new(@bs_1)
|
151
|
+
assert_equal(@bs_1, b, "Initialize with argument failed.")
|
152
|
+
|
153
|
+
c = BS_1.new(b)
|
154
|
+
assert_equal(@bs_1, c, "Initialize with argument failed.")
|
155
|
+
|
156
|
+
b1 = BS_1.new("")
|
157
|
+
b2 = BS_1.new(nil)
|
158
|
+
assert_equal(b1, b2, "Initialize with short argument failed.")
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_init_with_hash
|
162
|
+
randomize(@bs_1)
|
163
|
+
|
164
|
+
h = {}
|
165
|
+
@bs_1.fields.each do |f|
|
166
|
+
h[f.name] = @bs_1.send(f.name)
|
167
|
+
end
|
168
|
+
|
169
|
+
b = BS_1.new(h)
|
170
|
+
assert_equal(@bs_1, b, "Initialize with argument failed.")
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_join
|
174
|
+
assert_equal(@bs+@bs_1, BitStruct.join(@bs,@bs_1))
|
175
|
+
assert_equal(@bs+@bs_1, [@bs,@bs_1].join(""))
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_parse
|
179
|
+
orig = @testers
|
180
|
+
orig.each do |bs|
|
181
|
+
randomize(bs)
|
182
|
+
end
|
183
|
+
|
184
|
+
data = BitStruct.join(orig)
|
185
|
+
round_trip = BitStruct.parse(data, orig.map{|bs|bs.class})
|
186
|
+
orig.zip(round_trip) do |bs1, bs2|
|
187
|
+
assert_equal(bs1, bs2)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_closed
|
192
|
+
assert_raises(BitStruct::ClosedClassError) do
|
193
|
+
BS.class_eval do
|
194
|
+
unsigned :foo, 3
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_rest
|
200
|
+
len0 = @bs_1.length
|
201
|
+
|
202
|
+
@bs_1.bs1_body = "a"*50
|
203
|
+
assert_equal("a"*50, @bs_1.bs1_body)
|
204
|
+
assert_equal(len0+50, @bs_1.length)
|
205
|
+
|
206
|
+
@bs_1.bs1_body = "b"*60
|
207
|
+
assert_equal("b"*60, @bs_1.bs1_body)
|
208
|
+
assert_equal(len0+60, @bs_1.length)
|
209
|
+
|
210
|
+
@bs_1.bs1_body = "c"*40
|
211
|
+
assert_equal("c"*40, @bs_1.bs1_body)
|
212
|
+
assert_equal(len0+40, @bs_1.length)
|
213
|
+
|
214
|
+
@bs_1.bs1_body = ""
|
215
|
+
assert_equal("", @bs_1.bs1_body)
|
216
|
+
assert_equal(len0, @bs_1.length)
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_rest_with_class
|
220
|
+
r = Rest.new
|
221
|
+
t2 = T2.new
|
222
|
+
t2.bar = 123
|
223
|
+
r.body = t2
|
224
|
+
assert_equal(123, r.body.bar)
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_nest
|
228
|
+
cont = Container.new
|
229
|
+
n1 = cont.n1
|
230
|
+
|
231
|
+
assert_equal(1, n1.x)
|
232
|
+
assert_equal(2, n1.y)
|
233
|
+
assert_equal("deflt", n1.s)
|
234
|
+
|
235
|
+
n1.sub(/./, " ")
|
236
|
+
|
237
|
+
assert_equal(1, n1.x)
|
238
|
+
assert_equal(2, n1.y)
|
239
|
+
assert_equal("deflt", n1.s)
|
240
|
+
|
241
|
+
n1 = cont.n1
|
242
|
+
n2 = cont.n2
|
243
|
+
|
244
|
+
assert_equal(0, n2.x)
|
245
|
+
assert_equal(2, n2.y)
|
246
|
+
assert_equal("\0"*5, n2.s)
|
247
|
+
|
248
|
+
n = NestedPart.new(:x=>4, :y=>1, :s=>"qwert")
|
249
|
+
cont.n1 = n
|
250
|
+
|
251
|
+
assert_equal(4, cont.n1.x)
|
252
|
+
assert_equal(1, cont.n1.y)
|
253
|
+
assert_equal("qwert", cont.n1.s)
|
254
|
+
|
255
|
+
assert_raises(ArgumentError) {cont.n2 = Container.new}
|
256
|
+
end
|
257
|
+
|
258
|
+
def test_overflow
|
259
|
+
ov = Overflow.new
|
260
|
+
empty = ov.dup
|
261
|
+
|
262
|
+
ov.fields.each do |field|
|
263
|
+
ov.gsub(/./, "\0")
|
264
|
+
|
265
|
+
case field
|
266
|
+
when BitStruct::CharField
|
267
|
+
val1 = "a" * (field.length+5)
|
268
|
+
val2 = ""
|
269
|
+
|
270
|
+
when BitStruct::UnsignedField
|
271
|
+
val1 = 2**32 - 5**10 # mixed bit pattern
|
272
|
+
val2 = 0
|
273
|
+
end
|
274
|
+
|
275
|
+
ov.send("#{field.name}=", val1)
|
276
|
+
ov.send("#{field.name}=", val2)
|
277
|
+
|
278
|
+
assert_equal(empty, ov)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def test_access
|
283
|
+
repeat_access_test(@bs, 10)
|
284
|
+
end
|
285
|
+
|
286
|
+
def test_inheritance
|
287
|
+
assert_equal(@bs.fields.size, @bs_1.fields.size - BS_1.own_fields.size,
|
288
|
+
"Wrong number of fields inherited in BS_1.")
|
289
|
+
|
290
|
+
repeat_access_test(@bs_1, 10)
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_initial_value
|
294
|
+
bs = @bs
|
295
|
+
bs.class::INITIAL_VALUES.each do |f,v|
|
296
|
+
assert_equal(v, bs.send(f), "In #{f} of a #{bs.class}")
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def test_inherited_initial_value
|
301
|
+
bs = @bs_1
|
302
|
+
bs.class::INITIAL_VALUES.each do |f,v|
|
303
|
+
assert_equal(v, bs.send(f), "In #{f} of a #{bs.class}")
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def test_to_h
|
308
|
+
h = @bs_1.to_h(:convert_keys => :to_s)
|
309
|
+
field_names = @bs_1.fields.map{|f|f.name.to_s}
|
310
|
+
assert_equal(field_names.sort, h.keys.sort)
|
311
|
+
field_names.each do |name|
|
312
|
+
assert_equal(@bs_1.send(name), h[name])
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def test_format_option
|
317
|
+
formatted_fields = @bs.fields.select {|f|f.format}
|
318
|
+
formatted_fields.each do |f|
|
319
|
+
val = @bs.send(f.name)
|
320
|
+
assert_equal(f.format % val, f.inspect_in_object(@bs, {}))
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def test_yaml
|
325
|
+
assert_equal(@bs_1, YAML.load(@bs_1.to_yaml))
|
326
|
+
end
|
327
|
+
|
328
|
+
def test_field_by_name
|
329
|
+
name = :f007
|
330
|
+
f = @bs.field_by_name(name)
|
331
|
+
assert(f)
|
332
|
+
assert_equal(f.name, name)
|
333
|
+
end
|
334
|
+
|
335
|
+
#--------
|
336
|
+
def repeat_access_test(bs, n)
|
337
|
+
last_set_value = {}
|
338
|
+
|
339
|
+
start_length = bs.length
|
340
|
+
|
341
|
+
n.times do
|
342
|
+
bs.fields.each do |field|
|
343
|
+
last_set_value[field] = randomize_field(bs, field)
|
344
|
+
|
345
|
+
bs.fields.each do |f2|
|
346
|
+
lsv2 = last_set_value[f2]
|
347
|
+
if lsv2
|
348
|
+
case f2
|
349
|
+
when BitStruct::FloatField
|
350
|
+
assert_in_delta(lsv2, bs.send(f2.name), 100)
|
351
|
+
else
|
352
|
+
begin
|
353
|
+
assert_equal(lsv2, bs.send(f2.name))
|
354
|
+
rescue Test::Unit::AssertionFailedError => ex
|
355
|
+
msg =
|
356
|
+
"In #{f2.inspect} after setting #{field.inspect} to" +
|
357
|
+
" #{last_set_value[field].inspect}"
|
358
|
+
raise ex, msg + "\n" + ex.message, ex.backtrace
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
finish_length = bs.length
|
367
|
+
|
368
|
+
assert_equal(start_length, finish_length, "Length differs after test!")
|
369
|
+
end
|
370
|
+
|
371
|
+
def randomize(bs)
|
372
|
+
bs.fields.each do |f|
|
373
|
+
randomize_field(bs, f)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
def randomize_field(bs, field)
|
378
|
+
case field
|
379
|
+
when BitStruct::SignedField
|
380
|
+
divisor = field.options[:fixed]
|
381
|
+
if divisor
|
382
|
+
value = (rand(2**field.size) - 2**(field.size-1))/divisor.to_f
|
383
|
+
else
|
384
|
+
value = rand(2**field.size) - 2**(field.size-1)
|
385
|
+
end
|
386
|
+
bs.send "#{field.name}=", value
|
387
|
+
last_set_value = value
|
388
|
+
|
389
|
+
when BitStruct::UnsignedField
|
390
|
+
divisor = field.options[:fixed]
|
391
|
+
if divisor
|
392
|
+
value = rand(2**field.size)/divisor.to_f
|
393
|
+
else
|
394
|
+
value = rand(2**field.size)
|
395
|
+
## should ensure that there are some very low and very high walues
|
396
|
+
## esp. 0, 1, 2**n - 1, and so on
|
397
|
+
end
|
398
|
+
bs.send "#{field.name}=", value
|
399
|
+
last_set_value = value
|
400
|
+
|
401
|
+
when BitStruct::HexOctetField
|
402
|
+
val = (1..field.length/8).map {"%02x" % rand(256)}.join(":")
|
403
|
+
bs.send "#{field.name}=", val
|
404
|
+
last_set_value = val
|
405
|
+
|
406
|
+
when BitStruct::OctetField
|
407
|
+
val = (1..field.length/8).map {"%d" % rand(256)}.join(".")
|
408
|
+
bs.send "#{field.name}=", val
|
409
|
+
last_set_value = val
|
410
|
+
|
411
|
+
when BitStruct::CharField
|
412
|
+
s = (rand(64)+32).chr
|
413
|
+
value = s * (field.length/8)
|
414
|
+
bs.send "#{field.name}=", value
|
415
|
+
last_set_value = value
|
416
|
+
|
417
|
+
when BitStruct::TextField
|
418
|
+
s = (rand(64)+32).chr
|
419
|
+
value = s * rand(field.length*2/8)
|
420
|
+
bs.send "#{field.name}=", value
|
421
|
+
last_set_value = s * [field.length/8, value.length].min
|
422
|
+
|
423
|
+
when BitStruct::FloatField
|
424
|
+
value = rand(2**30)
|
425
|
+
bs.send "#{field.name}=", value
|
426
|
+
last_set_value = value
|
427
|
+
|
428
|
+
else raise
|
429
|
+
end
|
430
|
+
|
431
|
+
return last_set_value
|
432
|
+
end
|
433
|
+
end
|