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.
- checksums.yaml +4 -4
- data/NEWS.md +33 -0
- data/VERSION +1 -1
- data/doc/Reference.md +1 -1
- data/examples/service/complex-property.rb +21 -0
- data/lib/dbus/data.rb +744 -0
- data/lib/dbus/introspect.rb +1 -0
- data/lib/dbus/marshall.rb +172 -259
- data/lib/dbus/message.rb +4 -10
- data/lib/dbus/object.rb +11 -11
- data/lib/dbus/object_path.rb +2 -1
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +147 -70
- data/lib/dbus.rb +6 -0
- data/spec/data/marshall.yaml +1667 -0
- data/spec/data_spec.rb +353 -0
- data/spec/object_path_spec.rb +1 -0
- data/spec/packet_marshaller_spec.rb +41 -0
- data/spec/packet_unmarshaller_spec.rb +262 -0
- data/spec/property_spec.rb +80 -2
- data/spec/service_newapi.rb +19 -1
- data/spec/spec_helper.rb +14 -0
- data/spec/type_spec.rb +67 -6
- data/spec/zzz_quit_spec.rb +16 -0
- metadata +10 -2
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
|
data/spec/object_path_spec.rb
CHANGED
@@ -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
|