ruby-dbus 0.18.0.beta1 → 0.18.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
data/spec/data_spec.rb ADDED
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "spec_helper"
5
+ require "dbus"
6
+
7
+ # The from_raw methods are tested in packet_unmarshaller_spec.rb
8
+
9
+ RSpec.shared_examples "constructor accepts numeric range" do |min, max|
10
+ describe "#initialize" do
11
+ it "accepts the min value #{min}" do
12
+ expect(described_class.new(min).value).to eq(min)
13
+ end
14
+
15
+ it "accepts the max value #{max}" do
16
+ expect(described_class.new(max).value).to eq(max)
17
+ end
18
+
19
+ it "raises on too small a value #{min - 1}" do
20
+ expect { described_class.new(min - 1) }.to raise_error(RangeError)
21
+ end
22
+
23
+ it "raises on too big a value #{max + 1}" do
24
+ expect { described_class.new(max + 1) }.to raise_error(RangeError)
25
+ end
26
+
27
+ it "raises on nil" do
28
+ expect { described_class.new(nil) }.to raise_error(RangeError)
29
+ end
30
+ end
31
+ end
32
+
33
+ RSpec.shared_examples "constructor accepts plain or typed values" do |plain_list|
34
+ describe "#initialize" do
35
+ Array(plain_list).each do |plain|
36
+ it "accepts the plain value #{plain.inspect}" do
37
+ expect(described_class.new(plain).value).to eq(plain)
38
+ end
39
+
40
+ it "accepts the typed value #{plain.inspect}" do
41
+ typed = described_class.new(plain)
42
+ expect(described_class.new(typed).value).to eq(plain)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ RSpec.shared_examples "constructor (kwargs) accepts values" do |list|
49
+ describe "#initialize" do
50
+ list.each do |value, kwargs_hash|
51
+ it "accepts the plain value #{value.inspect}, #{kwargs_hash.inspect}" do
52
+ expect(described_class.new(value, **kwargs_hash).value).to eq(value)
53
+ end
54
+
55
+ it "accepts the typed value #{value.inspect}, #{kwargs_hash.inspect}" do
56
+ typed = described_class.new(value, **kwargs_hash)
57
+ expect(described_class.new(typed, **kwargs_hash).value).to eq(value)
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ RSpec.shared_examples "constructor rejects values from this list" do |bad_list|
64
+ describe "#initialize" do
65
+ bad_list.each do |(value, exc_class, msg_substr)|
66
+ it "rejects #{value.inspect} with #{exc_class}: #{msg_substr}" do
67
+ msg_re = Regexp.new(Regexp.quote(msg_substr))
68
+ expect { described_class.new(value) }.to raise_error(exc_class, msg_re)
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ RSpec.shared_examples "constructor (kwargs) rejects values" do |bad_list|
75
+ describe "#initialize" do
76
+ bad_list.each do |(value, kwargs_hash, exc_class, msg_substr)|
77
+ it "rejects #{value.inspect}, #{kwargs_hash.inspect} with #{exc_class}: #{msg_substr}" do
78
+ msg_re = Regexp.new(Regexp.quote(msg_substr))
79
+ expect { described_class.new(value, **kwargs_hash) }.to raise_error(exc_class, msg_re)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ # TODO: Look at conversions? to_str, to_int?
86
+
87
+ describe DBus::Data do
88
+ # test initialization, from user code, or from packet (from_raw)
89
+ # remember to unpack if initializing from Data::Base
90
+ # #value should recurse inside so that the user doesnt have to
91
+ # Kick InvalidPacketException out of here?
92
+
93
+ describe DBus::Data::Byte do
94
+ include_examples "constructor accepts numeric range", 0, 2**8 - 1
95
+ include_examples "constructor accepts plain or typed values", 42
96
+ end
97
+
98
+ describe DBus::Data::Int16 do
99
+ include_examples "constructor accepts numeric range", -2**15, 2**15 - 1
100
+ include_examples "constructor accepts plain or typed values", 42
101
+ end
102
+
103
+ describe DBus::Data::UInt16 do
104
+ include_examples "constructor accepts numeric range", 0, 2**16 - 1
105
+ include_examples "constructor accepts plain or typed values", 42
106
+ end
107
+
108
+ describe DBus::Data::Int32 do
109
+ include_examples "constructor accepts numeric range", -2**31, 2**31 - 1
110
+ include_examples "constructor accepts plain or typed values", 42
111
+ end
112
+
113
+ describe DBus::Data::UInt32 do
114
+ include_examples "constructor accepts numeric range", 0, 2**32 - 1
115
+ include_examples "constructor accepts plain or typed values", 42
116
+ end
117
+
118
+ describe DBus::Data::Int64 do
119
+ include_examples "constructor accepts numeric range", -2**63, 2**63 - 1
120
+ include_examples "constructor accepts plain or typed values", 42
121
+ end
122
+
123
+ describe DBus::Data::UInt64 do
124
+ include_examples "constructor accepts numeric range", 0, 2**64 - 1
125
+ include_examples "constructor accepts plain or typed values", 42
126
+ end
127
+
128
+ describe DBus::Data::Boolean do
129
+ describe "#initialize" do
130
+ it "accepts false and true" do
131
+ expect(described_class.new(false).value).to eq(false)
132
+ expect(described_class.new(true).value).to eq(true)
133
+ end
134
+
135
+ it "accepts truth value of other objects" do
136
+ expect(described_class.new(nil).value).to eq(false)
137
+ expect(described_class.new(0).value).to eq(true) # !
138
+ expect(described_class.new(1).value).to eq(true)
139
+ expect(described_class.new(Time.now).value).to eq(true)
140
+ end
141
+ end
142
+
143
+ include_examples "constructor accepts plain or typed values", false
144
+ end
145
+
146
+ describe DBus::Data::Double do
147
+ include_examples "constructor accepts plain or typed values", Math::PI
148
+
149
+ describe "#initialize" do
150
+ it "raises on values that can't be made a Float" do
151
+ expect { described_class.new(nil) }.to raise_error(TypeError)
152
+ expect { described_class.new("one") }.to raise_error(ArgumentError)
153
+ expect { described_class.new(/itsaregexp/) }.to raise_error(TypeError)
154
+ end
155
+ end
156
+ end
157
+
158
+ describe "basic, string-like types" do
159
+ describe DBus::Data::String do
160
+ # TODO: what about strings with good codepoints but encoded in
161
+ # let's say Encoding::ISO8859_2?
162
+ good = [
163
+ "",
164
+ "Ř",
165
+ # a Noncharacter, but well-formed Unicode
166
+ # https://www.unicode.org/versions/corrigendum9.html
167
+ "\uffff",
168
+ # maximal UTF-8 codepoint U+10FFFF
169
+ "\u{10ffff}"
170
+ ]
171
+
172
+ bad = [
173
+ # NUL in the middle
174
+ # FIXME: InvalidPacketException is wrong here, it should be ArgumentError
175
+ ["a\x00b", DBus::InvalidPacketException, "contains NUL"],
176
+ # invalid UTF-8
177
+ ["\xFF\xFF\xFF\xFF", DBus::InvalidPacketException, "not in UTF-8"],
178
+ # overlong sequence encoding an "A"
179
+ ["\xC1\x81", DBus::InvalidPacketException, "not in UTF-8"],
180
+ # first codepoint outside UTF-8, U+110000
181
+ ["\xF4\x90\xC0\xC0", DBus::InvalidPacketException, "not in UTF-8"]
182
+ ]
183
+
184
+ include_examples "constructor accepts plain or typed values", good
185
+ include_examples "constructor rejects values from this list", bad
186
+
187
+ describe ".alignment" do
188
+ # this overly specific test avoids a redundant alignment call
189
+ # in the production code
190
+ it "returns the correct value" do
191
+ expect(described_class.alignment).to eq 4
192
+ end
193
+ end
194
+ end
195
+
196
+ describe DBus::Data::ObjectPath do
197
+ good = [
198
+ "/"
199
+ # TODO: others
200
+ ]
201
+
202
+ bad = [
203
+ ["", DBus::InvalidPacketException, "Invalid object path"]
204
+ # TODO: others
205
+ ]
206
+
207
+ include_examples "constructor accepts plain or typed values", good
208
+ include_examples "constructor rejects values from this list", bad
209
+
210
+ describe ".alignment" do
211
+ # this overly specific test avoids a redundant alignment call
212
+ # in the production code
213
+ it "returns the correct value" do
214
+ expect(described_class.alignment).to eq 4
215
+ end
216
+ end
217
+ end
218
+
219
+ describe DBus::Data::Signature do
220
+ good = [
221
+ "",
222
+ "i",
223
+ "ii"
224
+ # TODO: others
225
+ ]
226
+
227
+ bad = [
228
+ ["!", DBus::InvalidPacketException, "Unknown type code"]
229
+ # TODO: others
230
+ ]
231
+
232
+ include_examples "constructor accepts plain or typed values", good
233
+ include_examples "constructor rejects values from this list", bad
234
+
235
+ describe ".alignment" do
236
+ # this overly specific test avoids a redundant alignment call
237
+ # in the production code
238
+ it "returns the correct value" do
239
+ expect(described_class.alignment).to eq 1
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+ describe "containers" do
246
+ describe DBus::Data::Array do
247
+ good = [
248
+ # [[1, 2, 3], member_type: nil],
249
+ [[1, 2, 3], { member_type: "q" }],
250
+ [[1, 2, 3], { member_type: DBus::Type::UINT16 }],
251
+ [[1, 2, 3], { member_type: DBus.type("q") }],
252
+ [[DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), DBus::Data::UInt16.new(3)], { member_type: "q" }]
253
+ # TODO: others
254
+ ]
255
+
256
+ bad = [
257
+ # undesirable type guessing
258
+ ## [[1, 2, 3], { member_type: nil }, DBus::InvalidPacketException, "Unknown type code"],
259
+ ## [[1, 2, 3], { member_type: "!" }, DBus::InvalidPacketException, "Unknown type code"]
260
+ # TODO: others
261
+ ]
262
+
263
+ include_examples "constructor (kwargs) accepts values", good
264
+ include_examples "constructor (kwargs) rejects values", bad
265
+
266
+ describe ".from_typed" do
267
+ it "creates new instance from given object and type" do
268
+ type = DBus::Type.new("s")
269
+ expect(described_class.from_typed(["test", "lest"], member_types: [type])).to be_a(described_class)
270
+ end
271
+ end
272
+ end
273
+
274
+ describe DBus::Data::Struct do
275
+ three_words = ::Struct.new(:a, :b, :c)
276
+
277
+ qqq = ["q", "q", "q"]
278
+ integers = [1, 2, 3]
279
+ uints = [DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), DBus::Data::UInt16.new(3)]
280
+
281
+ # TODO: all the reasonable initialization params
282
+ # need to be normalized into one/few internal representation.
283
+ # So check what is the result
284
+ #
285
+ # Internally, it must be Data::Base
286
+ # Perhaps distinguish #value => Data::Base
287
+ # and #plain_value => plain Ruby
288
+ #
289
+ # but then, can they mutate?
290
+ #
291
+ # TODO: also check data ownership: reasonable to own the data?
292
+ # can make it explicit?
293
+ good = [
294
+ # from plain array; various m_t styles
295
+ [integers, { member_types: ["q", "q", "q"] }],
296
+ [integers, { member_types: [DBus::Type::UINT16, DBus::Type::UINT16, DBus::Type::UINT16] }],
297
+ [integers, { member_types: DBus.types("qqq") }],
298
+ # plain array of data
299
+ [uints, { member_types: DBus.types("qqq") }],
300
+ # ::Struct
301
+ [three_words.new(*integers), { member_types: qqq }],
302
+ [three_words.new(*uints), { member_types: qqq }]
303
+ # TODO: others
304
+ ]
305
+
306
+ _bad_but_valid = [
307
+ # Wrong member_types arg:
308
+ # hmm this is another reason to pass the type
309
+ # as the entire struct type, not the members:
310
+ # empty struct will be caught naturally
311
+ [integers, { member_types: [] }, ArgumentError, "???"],
312
+ [integers, { member_types: ["!"] }, DBus::InvalidPacketException, "Unknown type code"],
313
+ # STRUCT specific: member count mismatch
314
+ [[1, 2], { member_types: DBus.types("qqq") }, ArgumentError, "???"],
315
+ [[1, 2, 3, 4], { member_types: DBus.types("qqq") }, ArgumentError, "???"]
316
+ # TODO: others
317
+ ]
318
+
319
+ include_examples "constructor (kwargs) accepts values", good
320
+ # include_examples "constructor (kwargs) rejects values", bad
321
+
322
+ describe ".from_typed" do
323
+ it "creates new instance from given object and type" do
324
+ type = DBus::Type.new("s")
325
+ expect(described_class.from_typed(["test", "lest"].freeze, member_types: [type, type]))
326
+ .to be_a(described_class)
327
+ end
328
+ end
329
+ end
330
+
331
+ describe DBus::Data::Variant do
332
+ describe ".from_typed" do
333
+ it "creates new instance from given object and type" do
334
+ type = DBus::Type.new("s")
335
+ expect(described_class.from_typed("test", member_types: [type])).to be_a(described_class)
336
+ end
337
+
338
+ it "ignores the member_types argument" do
339
+ type = DBus::Type.new("s")
340
+ # Base.from_typed is a generic interface with a fixed signature;
341
+ # So it must offer the member_types parameter, which is misleading
342
+ # for a Variant
343
+ value = described_class.from_typed("test", member_types: [type])
344
+ expect(value.type.to_s).to eq "v"
345
+ expect(value.member_type.to_s).to eq "s"
346
+ end
347
+ end
348
+ end
349
+
350
+ describe DBus::Data::DictEntry do
351
+ end
352
+ end
353
+ end
@@ -20,6 +20,7 @@ describe DBus::ObjectPath do
20
20
  expect(described_class.valid?("/EmptyLastComponent/")).to be_falsey
21
21
  expect(described_class.valid?("/Invalid Character")).to be_falsey
22
22
  expect(described_class.valid?("/Invalid-Character")).to be_falsey
23
+ expect(described_class.valid?("/InválídCháráctér")).to be_falsey
23
24
  end
24
25
  end
25
26
  end
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "spec_helper"
5
+ require "dbus"
6
+ require "ostruct"
7
+ require "yaml"
8
+
9
+ data_dir = File.expand_path("data", __dir__)
10
+ marshall_yaml_s = File.read("#{data_dir}/marshall.yaml")
11
+ marshall_yaml = YAML.safe_load(marshall_yaml_s)
12
+
13
+ describe DBus::PacketMarshaller do
14
+ context "marshall.yaml" do
15
+ marshall_yaml.each do |test|
16
+ t = OpenStruct.new(test)
17
+ next if t.marshall == false
18
+ # skip test cases for invalid unmarshalling
19
+ next if t.val.nil?
20
+
21
+ # while the marshaller can use only native endianness, skip the other
22
+ endianness = t.end.to_sym
23
+
24
+ signature = t.sig
25
+ expected = buffer_from_yaml(t.buf)
26
+
27
+ it "writes a '#{signature}' with value #{t.val.inspect} (#{endianness})" do
28
+ subject = described_class.new(endianness: endianness)
29
+ subject.append(signature, t.val)
30
+ expect(subject.packet).to eq(expected)
31
+ end
32
+
33
+ it "writes a '#{signature}' with typed value #{t.val.inspect} (#{endianness})" do
34
+ subject = described_class.new(endianness: endianness)
35
+ typed_val = DBus::Data.make_typed(signature, t.val)
36
+ subject.append(signature, typed_val)
37
+ expect(subject.packet).to eq(expected)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "spec_helper"
5
+ require "dbus"
6
+ require "ostruct"
7
+ require "yaml"
8
+
9
+ data_dir = File.expand_path("data", __dir__)
10
+ marshall_yaml_s = File.read("#{data_dir}/marshall.yaml")
11
+ marshall_yaml = YAML.safe_load(marshall_yaml_s)
12
+
13
+ # Helper to access PacketUnmarshaller internals.
14
+ # Add it to its public API?
15
+ # @param p_u [PacketUnmarshaller]
16
+ # @return [String] the binary string with unconsumed data
17
+ def remaining_buffer(p_u)
18
+ raw_msg = p_u.instance_variable_get(:@raw_msg)
19
+ raw_msg.remaining_bytes
20
+ end
21
+
22
+ RSpec.shared_examples "parses good data" do |cases|
23
+ describe "parses all the instances of good test data" do
24
+ cases.each_with_index do |(buffer, endianness, expected), i|
25
+ it "parses plain data ##{i}" do
26
+ buffer = String.new(buffer, encoding: Encoding::BINARY)
27
+ subject = described_class.new(buffer, endianness)
28
+
29
+ results = subject.unmarshall(signature, mode: :plain)
30
+ # unmarshall works on multiple signatures but we use one
31
+ expect(results).to be_an(Array)
32
+ expect(results.size).to eq(1)
33
+ result = results.first
34
+
35
+ expect(result).to eq(expected)
36
+
37
+ expect(remaining_buffer(subject)).to be_empty
38
+ end
39
+
40
+ it "parses exact data ##{i}" do
41
+ buffer = String.new(buffer, encoding: Encoding::BINARY)
42
+ subject = described_class.new(buffer, endianness)
43
+
44
+ results = subject.unmarshall(signature, mode: :exact)
45
+ # unmarshall works on multiple signatures but we use one
46
+ expect(results).to be_an(Array)
47
+ expect(results.size).to eq(1)
48
+ result = results.first
49
+
50
+ expect(result).to be_a(DBus::Data::Base)
51
+ if expected.is_a?(Hash)
52
+ expect(result.value.size).to eq(expected.size)
53
+ result.value.each_key do |result_key|
54
+ expect(result.value[result_key]).to eq(expected[result_key.value])
55
+ end
56
+ else
57
+ expect(result.value).to eq(expected)
58
+ end
59
+
60
+ expect(remaining_buffer(subject)).to be_empty
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ # this is necessary because we do an early switch on the signature
67
+ RSpec.shared_examples "reports empty data" do
68
+ it "reports empty data" do
69
+ [:big, :little].each do |endianness|
70
+ subject = described_class.new("", endianness)
71
+ expect { subject.unmarshall(signature) }.to raise_error(DBus::IncompleteBufferException)
72
+ end
73
+ end
74
+ end
75
+
76
+ describe DBus::PacketUnmarshaller do
77
+ context "marshall.yaml" do
78
+ marshall_yaml.each do |test|
79
+ t = OpenStruct.new(test)
80
+ signature = t.sig
81
+ buffer = buffer_from_yaml(t.buf)
82
+ endianness = t.end.to_sym
83
+
84
+ # successful parse
85
+ if !t.val.nil?
86
+ expected = t.val
87
+
88
+ it "parses a '#{signature}' to get #{t.val.inspect} (plain)" do
89
+ subject = described_class.new(buffer, endianness)
90
+ results = subject.unmarshall(signature, mode: :plain)
91
+ # unmarshall works on multiple signatures but we use one
92
+ expect(results).to be_an(Array)
93
+ expect(results.size).to eq(1)
94
+ result = results.first
95
+
96
+ expect(result).to eq(expected)
97
+ expect(remaining_buffer(subject)).to be_empty
98
+ end
99
+
100
+ it "parses a '#{t.sig}' to get #{t.val.inspect} (exact)" do
101
+ subject = described_class.new(buffer, endianness)
102
+ results = subject.unmarshall(signature, mode: :exact)
103
+ # unmarshall works on multiple signatures but we use one
104
+ expect(results).to be_an(Array)
105
+ expect(results.size).to eq(1)
106
+ result = results.first
107
+
108
+ expect(result).to be_a(DBus::Data::Base)
109
+ if expected.is_a?(Hash)
110
+ expect(result.value.size).to eq(expected.size)
111
+ result.value.each_key do |result_key|
112
+ expect(result.value[result_key]).to eq(expected[result_key.value])
113
+ end
114
+ else
115
+ expect(result.value).to eq(expected)
116
+ end
117
+
118
+ expect(remaining_buffer(subject)).to be_empty
119
+ end
120
+ elsif t.exc
121
+ next if t.unmarshall == false
122
+
123
+ exc_class = DBus.const_get(t.exc)
124
+ msg_re = Regexp.new(Regexp.escape(t.msg))
125
+
126
+ # TODO: InvalidPacketException is never rescued.
127
+ # The other end is sending invalid data. Can we do better than crashing?
128
+ # When we can test with peer connections, try it out.
129
+ it "parses a '#{signature}' to report a #{t.exc}" do
130
+ subject = described_class.new(buffer, endianness)
131
+ expect { subject.unmarshall(signature, mode: :plain) }.to raise_error(exc_class, msg_re)
132
+
133
+ subject = described_class.new(buffer, endianness)
134
+ expect { subject.unmarshall(signature, mode: :exact) }.to raise_error(exc_class, msg_re)
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ context "BYTEs" do
141
+ let(:signature) { "y" }
142
+ include_examples "reports empty data"
143
+ end
144
+
145
+ context "BOOLEANs" do
146
+ let(:signature) { "b" }
147
+ include_examples "reports empty data"
148
+ end
149
+
150
+ context "INT16s" do
151
+ let(:signature) { "n" }
152
+ include_examples "reports empty data"
153
+ end
154
+
155
+ context "UINT16s" do
156
+ let(:signature) { "q" }
157
+ include_examples "reports empty data"
158
+ end
159
+
160
+ context "INT32s" do
161
+ let(:signature) { "i" }
162
+ include_examples "reports empty data"
163
+ end
164
+
165
+ context "UINT32s" do
166
+ let(:signature) { "u" }
167
+ include_examples "reports empty data"
168
+ end
169
+
170
+ context "UNIX_FDs" do
171
+ let(:signature) { "h" }
172
+ include_examples "reports empty data"
173
+ end
174
+
175
+ context "INT64s" do
176
+ let(:signature) { "x" }
177
+ include_examples "reports empty data"
178
+ end
179
+
180
+ context "UINT64s" do
181
+ let(:signature) { "t" }
182
+ include_examples "reports empty data"
183
+ end
184
+
185
+ context "DOUBLEs" do
186
+ let(:signature) { "d" }
187
+ # See https://en.wikipedia.org/wiki/Double-precision_floating-point_format
188
+ # for binary representations
189
+ # TODO: figure out IEEE754 comparisons
190
+ good = [
191
+ # But == cant distinguish -0.0
192
+ ["\x00\x00\x00\x00\x00\x00\x00\x80", :little, -0.0],
193
+ # But NaN == NaN is false!
194
+ # ["\xff\xff\xff\xff\xff\xff\xff\xff", :little, Float::NAN],
195
+ ["\x80\x00\x00\x00\x00\x00\x00\x00", :big, -0.0]
196
+ # ["\xff\xff\xff\xff\xff\xff\xff\xff", :big, Float::NAN]
197
+ ]
198
+ include_examples "parses good data", good
199
+ include_examples "reports empty data"
200
+ end
201
+
202
+ context "STRINGs" do
203
+ let(:signature) { "s" }
204
+ include_examples "reports empty data"
205
+ end
206
+
207
+ context "OBJECT_PATHs" do
208
+ let(:signature) { "o" }
209
+ include_examples "reports empty data"
210
+ end
211
+
212
+ context "SIGNATUREs" do
213
+ let(:signature) { "g" }
214
+ include_examples "reports empty data"
215
+ end
216
+
217
+ context "ARRAYs" do
218
+ context "of BYTEs" do
219
+ let(:signature) { "ay" }
220
+ include_examples "reports empty data"
221
+ end
222
+
223
+ context "of UINT64s" do
224
+ let(:signature) { "at" }
225
+ include_examples "reports empty data"
226
+ end
227
+
228
+ context "of STRUCT of 2 UINT16s" do
229
+ let(:signature) { "a(qq)" }
230
+ include_examples "reports empty data"
231
+ end
232
+
233
+ context "of DICT_ENTRIES" do
234
+ let(:signature) { "a{yq}" }
235
+ include_examples "reports empty data"
236
+ end
237
+ end
238
+
239
+ context "STRUCTs" do
240
+ # TODO: this is invalid but does not raise
241
+ context "(generic 'r' struct)" do
242
+ let(:signature) { "r" }
243
+ end
244
+
245
+ context "of two shorts" do
246
+ let(:signature) { "(qq)" }
247
+ include_examples "reports empty data"
248
+ end
249
+ end
250
+
251
+ # makes sense here? or in array? remember invalid sigs are rejected elsewhere
252
+ context "DICT_ENTRYs" do
253
+ context "(generic 'e' dict_entry)" do
254
+ let(:signature) { "e" }
255
+ end
256
+ end
257
+
258
+ context "VARIANTs" do
259
+ let(:signature) { "v" }
260
+ include_examples "reports empty data"
261
+ end
262
+ end