ruby-dbus 0.16.0 → 0.18.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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +160 -0
  3. data/README.md +3 -5
  4. data/Rakefile +18 -8
  5. data/VERSION +1 -1
  6. data/doc/Reference.md +106 -7
  7. data/examples/doc/_extract_examples +7 -0
  8. data/examples/gdbus/gdbus +31 -24
  9. data/examples/no-introspect/nm-test.rb +2 -0
  10. data/examples/no-introspect/tracker-test.rb +3 -1
  11. data/examples/rhythmbox/playpause.rb +2 -1
  12. data/examples/service/call_service.rb +2 -1
  13. data/examples/service/complex-property.rb +21 -0
  14. data/examples/service/service_newapi.rb +1 -1
  15. data/examples/simple/call_introspect.rb +1 -0
  16. data/examples/simple/get_id.rb +2 -1
  17. data/examples/simple/properties.rb +2 -0
  18. data/examples/utils/listnames.rb +1 -0
  19. data/examples/utils/notify.rb +1 -0
  20. data/lib/dbus/api_options.rb +9 -0
  21. data/lib/dbus/auth.rb +20 -15
  22. data/lib/dbus/bus.rb +123 -75
  23. data/lib/dbus/bus_name.rb +12 -8
  24. data/lib/dbus/core_ext/class/attribute.rb +1 -1
  25. data/lib/dbus/data.rb +821 -0
  26. data/lib/dbus/emits_changed_signal.rb +83 -0
  27. data/lib/dbus/error.rb +4 -2
  28. data/lib/dbus/introspect.rb +132 -31
  29. data/lib/dbus/logger.rb +3 -1
  30. data/lib/dbus/marshall.rb +247 -296
  31. data/lib/dbus/matchrule.rb +16 -16
  32. data/lib/dbus/message.rb +44 -37
  33. data/lib/dbus/message_queue.rb +16 -10
  34. data/lib/dbus/object.rb +358 -24
  35. data/lib/dbus/object_path.rb +11 -6
  36. data/lib/dbus/proxy_object.rb +22 -1
  37. data/lib/dbus/proxy_object_factory.rb +13 -7
  38. data/lib/dbus/proxy_object_interface.rb +63 -30
  39. data/lib/dbus/raw_message.rb +91 -0
  40. data/lib/dbus/type.rb +318 -86
  41. data/lib/dbus/xml.rb +32 -17
  42. data/lib/dbus.rb +14 -7
  43. data/ruby-dbus.gemspec +7 -3
  44. data/spec/async_spec.rb +2 -0
  45. data/spec/binding_spec.rb +2 -0
  46. data/spec/bus_and_xml_backend_spec.rb +2 -0
  47. data/spec/bus_driver_spec.rb +2 -0
  48. data/spec/bus_name_spec.rb +3 -1
  49. data/spec/bus_spec.rb +2 -0
  50. data/spec/byte_array_spec.rb +2 -0
  51. data/spec/client_robustness_spec.rb +4 -2
  52. data/spec/data/marshall.yaml +1667 -0
  53. data/spec/data_spec.rb +673 -0
  54. data/spec/emits_changed_signal_spec.rb +58 -0
  55. data/spec/err_msg_spec.rb +2 -0
  56. data/spec/introspect_xml_parser_spec.rb +2 -0
  57. data/spec/introspection_spec.rb +2 -0
  58. data/spec/main_loop_spec.rb +3 -1
  59. data/spec/node_spec.rb +23 -0
  60. data/spec/object_path_spec.rb +3 -0
  61. data/spec/object_spec.rb +138 -0
  62. data/spec/packet_marshaller_spec.rb +41 -0
  63. data/spec/packet_unmarshaller_spec.rb +248 -0
  64. data/spec/property_spec.rb +192 -5
  65. data/spec/proxy_object_spec.rb +2 -0
  66. data/spec/server_robustness_spec.rb +2 -0
  67. data/spec/server_spec.rb +2 -0
  68. data/spec/service_newapi.rb +70 -70
  69. data/spec/session_bus_spec.rb +3 -1
  70. data/spec/session_bus_spec_manual.rb +2 -0
  71. data/spec/signal_spec.rb +5 -3
  72. data/spec/spec_helper.rb +37 -9
  73. data/spec/thread_safety_spec.rb +2 -0
  74. data/spec/tools/dbus-limited-session.conf +4 -0
  75. data/spec/type_spec.rb +214 -6
  76. data/spec/value_spec.rb +16 -1
  77. data/spec/variant_spec.rb +4 -2
  78. data/spec/zzz_quit_spec.rb +16 -0
  79. metadata +34 -8
data/spec/data_spec.rb ADDED
@@ -0,0 +1,673 @@
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 "#== and #eql? work for basic types" do |*args|
10
+ plain_a = args.fetch(0, 22)
11
+ plain_b = args.fetch(1, 222)
12
+
13
+ context "with #{plain_a.inspect} and #{plain_b.inspect}" do
14
+ describe "#eql?" do
15
+ it "returns true for same class and value" do
16
+ a = described_class.new(plain_a)
17
+ b = described_class.new(plain_a)
18
+ expect(a).to eql(b)
19
+ end
20
+
21
+ it "returns false for same class, different value" do
22
+ a = described_class.new(plain_a)
23
+ b = described_class.new(plain_b)
24
+ expect(a).to_not eql(b)
25
+ end
26
+
27
+ it "returns false for same value but plain class" do
28
+ a = described_class.new(plain_a)
29
+ b = plain_a
30
+ expect(a).to_not eql(b)
31
+ end
32
+ end
33
+
34
+ describe "#==" do
35
+ it "returns true for same class and value" do
36
+ a = described_class.new(plain_a)
37
+ b = described_class.new(plain_a)
38
+ expect(a).to eq(b)
39
+ end
40
+
41
+ it "returns false for same class, different value" do
42
+ a = described_class.new(plain_a)
43
+ b = described_class.new(plain_b)
44
+ expect(a).to_not eq(b)
45
+ end
46
+
47
+ it "returns true for same value but plain class" do
48
+ a = described_class.new(plain_a)
49
+ b = plain_a
50
+ expect(a).to eq(b)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ RSpec.shared_examples "#== and #eql? work for container types (1 value)" do |plain_a, a_kwargs|
57
+ a1 = described_class.new(plain_a, **a_kwargs)
58
+ a2 = described_class.new(plain_a, **a_kwargs)
59
+
60
+ context "with #{plain_a.inspect}, #{a_kwargs.inspect}" do
61
+ describe "#eql?" do
62
+ it "returns true for same class and value" do
63
+ expect(a1).to eql(a2)
64
+ end
65
+
66
+ it "returns false for same value but plain class" do
67
+ expect(a1).to_not eql(plain_a)
68
+ end
69
+ end
70
+
71
+ describe "#==" do
72
+ it "returns true for same class and value" do
73
+ expect(a1).to eq(a2)
74
+ end
75
+
76
+ it "returns true for same value but plain class" do
77
+ expect(a1).to eq(plain_a)
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ RSpec.shared_examples "#== and #eql? work for container types (inequal)" do |plain_a, a_kwargs, plain_b, b_kwargs|
84
+ # RSpec note: if the shared_examples is used via include_examples more than
85
+ # once in a single context, `let` would take value from just one of them.
86
+ # So use plain assignment.
87
+ a = described_class.new(plain_a, **a_kwargs)
88
+ b = described_class.new(plain_b, **b_kwargs)
89
+
90
+ include_examples "#== and #eql? work for container types (1 value)", plain_a, a_kwargs
91
+
92
+ context "with #{plain_a.inspect}, #{a_kwargs.inspect} and #{plain_b.inspect}, #{b_kwargs.inspect}" do
93
+ describe "#eql?" do
94
+ it "returns false for same class, different value" do
95
+ expect(a).to_not eql(b)
96
+ end
97
+ end
98
+
99
+ describe "#==" do
100
+ it "returns false for same class, different value" do
101
+ expect(a).to_not eq(b)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ RSpec.shared_examples "#== and #eql? work for container types (equal)" do |plain_a, a_kwargs, plain_b, b_kwargs|
108
+ a = described_class.new(plain_a, **a_kwargs)
109
+ b = described_class.new(plain_b, **b_kwargs)
110
+
111
+ include_examples "#== and #eql? work for container types (1 value)", plain_a, a_kwargs
112
+
113
+ context "with #{plain_a.inspect}, #{a_kwargs.inspect} and #{plain_b.inspect}, #{b_kwargs.inspect}" do
114
+ describe "#eql?" do
115
+ it "returns true for same class, differently expressed value" do
116
+ expect(a).to eql(b)
117
+ end
118
+ end
119
+
120
+ describe "#==" do
121
+ it "returns true for same class, differently expressed value" do
122
+ expect(a).to eq(b)
123
+ end
124
+ end
125
+
126
+ describe "#==" do
127
+ it "returns true for plain, differently expressed value" do
128
+ expect(a).to eq(plain_b)
129
+ expect(b).to eq(plain_a)
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ RSpec.shared_examples "constructor accepts numeric range" do |min, max|
136
+ describe "#initialize" do
137
+ it "accepts the min value #{min}" do
138
+ expect(described_class.new(min).value).to eql(min)
139
+ end
140
+
141
+ it "accepts the max value #{max}" do
142
+ expect(described_class.new(max).value).to eql(max)
143
+ end
144
+
145
+ it "raises on too small a value #{min - 1}" do
146
+ expect { described_class.new(min - 1) }.to raise_error(RangeError)
147
+ end
148
+
149
+ it "raises on too big a value #{max + 1}" do
150
+ expect { described_class.new(max + 1) }.to raise_error(RangeError)
151
+ end
152
+
153
+ it "raises on nil" do
154
+ expect { described_class.new(nil) }.to raise_error(RangeError)
155
+ end
156
+ end
157
+ end
158
+
159
+ RSpec.shared_examples "constructor accepts plain or typed values" do |plain_list|
160
+ describe "#initialize" do
161
+ Array(plain_list).each do |plain|
162
+ it "accepts the plain value #{plain.inspect}" do
163
+ expect(described_class.new(plain).value).to eql(plain)
164
+ expect(described_class.new(plain)).to eq(plain)
165
+ end
166
+
167
+ it "accepts the typed value #{plain.inspect}" do
168
+ typed = described_class.new(plain)
169
+ expect(described_class.new(typed).value).to eql(plain)
170
+ expect(described_class.new(typed)).to eq(plain)
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ # FIXME: decide eq and eql here
177
+ RSpec.shared_examples "constructor (kwargs) accepts values" do |list|
178
+ describe "#initialize" do
179
+ list.each do |value, kwargs_hash|
180
+ it "accepts the plain value #{value.inspect}, #{kwargs_hash.inspect}" do
181
+ expect(described_class.new(value, **kwargs_hash)).to eq(value)
182
+ end
183
+
184
+ it "accepts the typed value #{value.inspect}, #{kwargs_hash.inspect}" do
185
+ typed = described_class.new(value, **kwargs_hash)
186
+ expect(described_class.new(typed, **kwargs_hash)).to eq(value)
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ RSpec.shared_examples "constructor rejects values from this list" do |bad_list|
193
+ describe "#initialize" do
194
+ bad_list.each do |(value, exc_class, msg_substr)|
195
+ it "rejects #{value.inspect} with #{exc_class}: #{msg_substr}" do
196
+ msg_re = Regexp.try_convert(msg_substr) || Regexp.new(Regexp.quote(msg_substr))
197
+ expect { described_class.new(value) }.to raise_error(exc_class, msg_re)
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ RSpec.shared_examples "constructor (kwargs) rejects values" do |bad_list|
204
+ describe "#initialize" do
205
+ bad_list.each do |(value, kwargs_hash, exc_class, msg_substr)|
206
+ it "rejects #{value.inspect}, #{kwargs_hash.inspect} with #{exc_class}: #{msg_substr}" do
207
+ msg_re = Regexp.try_convert(msg_substr) || Regexp.new(Regexp.quote(msg_substr))
208
+ expect { described_class.new(value, **kwargs_hash) }.to raise_error(exc_class, msg_re)
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ # TODO: Look at conversions? to_str, to_int?
215
+
216
+ describe DBus::Data do
217
+ T = DBus::Type unless const_defined? "T"
218
+
219
+ # test initialization, from user code, or from packet (from_raw)
220
+ # remember to unpack if initializing from Data::Base
221
+ # #value should recurse inside so that the user doesnt have to
222
+ # Kick InvalidPacketException out of here?
223
+
224
+ describe DBus::Data::Byte do
225
+ include_examples "#== and #eql? work for basic types"
226
+ include_examples "constructor accepts numeric range", 0, 2**8 - 1
227
+ include_examples "constructor accepts plain or typed values", 42
228
+ end
229
+
230
+ describe DBus::Data::Int16 do
231
+ include_examples "#== and #eql? work for basic types"
232
+ include_examples "constructor accepts numeric range", -2**15, 2**15 - 1
233
+ include_examples "constructor accepts plain or typed values", 42
234
+ end
235
+
236
+ describe DBus::Data::UInt16 do
237
+ include_examples "#== and #eql? work for basic types"
238
+ include_examples "constructor accepts numeric range", 0, 2**16 - 1
239
+ include_examples "constructor accepts plain or typed values", 42
240
+ end
241
+
242
+ describe DBus::Data::Int32 do
243
+ include_examples "#== and #eql? work for basic types"
244
+ include_examples "constructor accepts numeric range", -2**31, 2**31 - 1
245
+ include_examples "constructor accepts plain or typed values", 42
246
+ end
247
+
248
+ describe DBus::Data::UInt32 do
249
+ include_examples "#== and #eql? work for basic types"
250
+ include_examples "constructor accepts numeric range", 0, 2**32 - 1
251
+ include_examples "constructor accepts plain or typed values", 42
252
+ end
253
+
254
+ describe DBus::Data::Int64 do
255
+ include_examples "#== and #eql? work for basic types"
256
+ include_examples "constructor accepts numeric range", -2**63, 2**63 - 1
257
+ include_examples "constructor accepts plain or typed values", 42
258
+ end
259
+
260
+ describe DBus::Data::UInt64 do
261
+ include_examples "#== and #eql? work for basic types"
262
+ include_examples "constructor accepts numeric range", 0, 2**64 - 1
263
+ include_examples "constructor accepts plain or typed values", 42
264
+ end
265
+
266
+ describe DBus::Data::Boolean do
267
+ describe "#initialize" do
268
+ it "accepts false and true" do
269
+ expect(described_class.new(false).value).to eq(false)
270
+ expect(described_class.new(true).value).to eq(true)
271
+ end
272
+
273
+ it "accepts truth value of other objects" do
274
+ expect(described_class.new(nil).value).to eq(false)
275
+ expect(described_class.new(0).value).to eq(true) # !
276
+ expect(described_class.new(1).value).to eq(true)
277
+ expect(described_class.new(Time.now).value).to eq(true)
278
+ end
279
+ end
280
+
281
+ include_examples "#== and #eql? work for basic types", false, true
282
+ include_examples "constructor accepts plain or typed values", false
283
+ end
284
+
285
+ describe DBus::Data::Double do
286
+ include_examples "#== and #eql? work for basic types"
287
+ include_examples "constructor accepts plain or typed values", Math::PI
288
+
289
+ describe "#initialize" do
290
+ it "raises on values that can't be made a Float" do
291
+ expect { described_class.new(nil) }.to raise_error(TypeError)
292
+ expect { described_class.new("one") }.to raise_error(ArgumentError)
293
+ expect { described_class.new(/itsaregexp/) }.to raise_error(TypeError)
294
+ end
295
+ end
296
+ end
297
+
298
+ describe "basic, string-like types" do
299
+ describe DBus::Data::String do
300
+ # TODO: what about strings with good codepoints but encoded in
301
+ # let's say Encoding::ISO8859_2?
302
+ good = [
303
+ "",
304
+ "Ř",
305
+ # a Noncharacter, but well-formed Unicode
306
+ # https://www.unicode.org/versions/corrigendum9.html
307
+ "\uffff",
308
+ # maximal UTF-8 codepoint U+10FFFF
309
+ "\u{10ffff}"
310
+ ]
311
+
312
+ bad = [
313
+ # NUL in the middle
314
+ # FIXME: InvalidPacketException is wrong here, it should be ArgumentError
315
+ ["a\x00b", DBus::InvalidPacketException, "contains NUL"],
316
+ # invalid UTF-8
317
+ ["\xFF\xFF\xFF\xFF", DBus::InvalidPacketException, "not in UTF-8"],
318
+ # overlong sequence encoding an "A"
319
+ ["\xC1\x81", DBus::InvalidPacketException, "not in UTF-8"],
320
+ # first codepoint outside UTF-8, U+110000
321
+ ["\xF4\x90\xC0\xC0", DBus::InvalidPacketException, "not in UTF-8"]
322
+ ]
323
+
324
+ include_examples "#== and #eql? work for basic types", "foo", "bar"
325
+ include_examples "constructor accepts plain or typed values", good
326
+ include_examples "constructor rejects values from this list", bad
327
+
328
+ describe ".alignment" do
329
+ # this overly specific test avoids a redundant alignment call
330
+ # in the production code
331
+ it "returns the correct value" do
332
+ expect(described_class.alignment).to eq 4
333
+ end
334
+ end
335
+ end
336
+
337
+ describe DBus::Data::ObjectPath do
338
+ good = [
339
+ "/"
340
+ # TODO: others
341
+ ]
342
+
343
+ bad = [
344
+ ["", DBus::InvalidPacketException, "Invalid object path"]
345
+ # TODO: others
346
+ ]
347
+
348
+ include_examples "#== and #eql? work for basic types", "/foo", "/bar"
349
+ include_examples "constructor accepts plain or typed values", good
350
+ include_examples "constructor rejects values from this list", bad
351
+
352
+ describe ".alignment" do
353
+ # this overly specific test avoids a redundant alignment call
354
+ # in the production code
355
+ it "returns the correct value" do
356
+ expect(described_class.alignment).to eq 4
357
+ end
358
+ end
359
+ end
360
+
361
+ describe DBus::Data::Signature do
362
+ good = [
363
+ "",
364
+ "i",
365
+ "ii"
366
+ # TODO: others
367
+ ]
368
+
369
+ bad = [
370
+ ["!", DBus::InvalidPacketException, "Unknown type code"]
371
+ # TODO: others
372
+ ]
373
+
374
+ include_examples "#== and #eql? work for basic types", "aah", "aaaaah"
375
+ include_examples "constructor accepts plain or typed values", good
376
+ include_examples "constructor rejects values from this list", bad
377
+
378
+ describe ".alignment" do
379
+ # this overly specific test avoids a redundant alignment call
380
+ # in the production code
381
+ it "returns the correct value" do
382
+ expect(described_class.alignment).to eq 1
383
+ end
384
+ end
385
+ end
386
+ end
387
+
388
+ describe "containers" do
389
+ describe DBus::Data::Array do
390
+ aq = DBus::Data::Array.new([1, 2, 3], type: "aq")
391
+
392
+ good = [
393
+ [[1, 2, 3], { type: "aq" }],
394
+ [[1, 2, 3], { type: T::Array[T::UINT16] }],
395
+ [[1, 2, 3], { type: T::Array["q"] }],
396
+ [[DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), DBus::Data::UInt16.new(3)], { type: T::Array["q"] }]
397
+ # TODO: others
398
+ ]
399
+
400
+ bad = [
401
+ # undesirable type guessing
402
+ [[1, 2, 3], { type: nil }, ArgumentError, /Expecting DBus::Type.*got nil/],
403
+ [[1, 2, 3], { type: "!" }, DBus::Type::SignatureException, "Unknown type code"],
404
+ [aq, { type: "q" }, ArgumentError, "Expecting \"a\""],
405
+ [aq, { type: "ao" }, ArgumentError,
406
+ "Specified type is ARRAY: [OBJECT_PATH] but value type is ARRAY: [UINT16]"]
407
+ # TODO: how to handle these?
408
+ # [{1 => 2, 3 => 4}, { type: "aq" }, ArgumentError, "?"],
409
+ # [/i am not an array/, { type: "aq" }, ArgumentError, "?"],
410
+ ]
411
+
412
+ include_examples "#== and #eql? work for container types (inequal)",
413
+ [1, 2, 3], { type: "aq" },
414
+ [3, 2, 1], { type: "aq" }
415
+
416
+ include_examples "#== and #eql? work for container types (inequal)",
417
+ [[1, 2, 3]], { type: "aaq" },
418
+ [[3, 2, 1]], { type: "aaq" }
419
+
420
+ include_examples "constructor (kwargs) accepts values", good
421
+ include_examples "constructor (kwargs) rejects values", bad
422
+
423
+ describe ".from_typed" do
424
+ it "creates new instance from given object and type" do
425
+ type = T::Array[String]
426
+ expect(described_class.from_typed(["test", "lest"], type: type)).to be_a(described_class)
427
+ end
428
+ end
429
+ end
430
+
431
+ describe DBus::Data::Struct do
432
+ three_words = ::Struct.new(:a, :b, :c)
433
+
434
+ qqq = T::Struct[T::UINT16, T::UINT16, T::UINT16]
435
+ integers = [1, 2, 3]
436
+ uints = [DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), DBus::Data::UInt16.new(3)]
437
+
438
+ # TODO: all the reasonable initialization params
439
+ # need to be normalized into one/few internal representation.
440
+ # So check what is the result
441
+ #
442
+ # Internally, it must be Data::Base
443
+ # Perhaps distinguish #value => Data::Base
444
+ # and #plain_value => plain Ruby
445
+ #
446
+ # but then, can they mutate?
447
+ #
448
+ # TODO: also check data ownership: reasonable to own the data?
449
+ # can make it explicit?
450
+ good = [
451
+ # from plain array; various *type* styles
452
+ [integers, { type: DBus.type("(qqq)") }],
453
+ [integers, { type: T::Struct["q", "q", "q"] }],
454
+ [integers, { type: T::Struct[T::UINT16, T::UINT16, T::UINT16] }],
455
+ [integers, { type: T::Struct[*DBus.types("qqq")] }],
456
+ # plain array of data
457
+ [uints, { type: qqq }],
458
+ # ::Struct
459
+ [three_words.new(*integers), { type: qqq }],
460
+ [three_words.new(*uints), { type: qqq }]
461
+ # TODO: others
462
+ ]
463
+
464
+ # check these only when canonicalizing @value, because that will
465
+ # type-check the value deeply
466
+ _bad_but_valid = [
467
+ # STRUCT specific: member count mismatch
468
+ [[1, 2], { type: qqq }, ArgumentError, "???"],
469
+ [[1, 2, 3, 4], { type: qqq }, ArgumentError, "???"]
470
+ # TODO: others
471
+ ]
472
+
473
+ include_examples "#== and #eql? work for container types (inequal)",
474
+ [1, 2, 3], { type: qqq },
475
+ [3, 2, 1], { type: qqq }
476
+
477
+ include_examples "#== and #eql? work for container types (equal)",
478
+ three_words.new(*integers), { type: qqq },
479
+ [1, 2, 3], { type: qqq }
480
+
481
+ include_examples "constructor (kwargs) accepts values", good
482
+ # include_examples "constructor (kwargs) rejects values", bad
483
+
484
+ describe ".from_typed" do
485
+ it "creates new instance from given object and type" do
486
+ type = T::Struct[T::STRING, T::STRING]
487
+ expect(described_class.from_typed(["test", "lest"].freeze, type: type))
488
+ .to be_a(described_class)
489
+ end
490
+ end
491
+
492
+ describe "#initialize" do
493
+ it "converts type to Type" do
494
+ value = [1, 2, 3]
495
+ type = "(uuu)"
496
+ result = described_class.new(value, type: type)
497
+ expect(result.type).to be_a DBus::Type
498
+ end
499
+
500
+ it "checks that type matches class" do
501
+ value = [1, 2, 3]
502
+ type = T::Array[T::INT32]
503
+ expect { described_class.new(value, type: type) }
504
+ .to raise_error(ArgumentError, /Expecting "r"/)
505
+ end
506
+
507
+ it "checks type of a Data::Struct value" do
508
+ value1 = [1, 2, 3]
509
+ type1 = "(uuu)"
510
+ result1 = described_class.new(value1, type: type1)
511
+
512
+ value2 = result1
513
+ type2 = "(xxx)"
514
+ expect { described_class.new(value2, type: type2) }
515
+ .to raise_error(ArgumentError, /value type is STRUCT.*UINT32/)
516
+ end
517
+
518
+ it "checks that size of type and value match" do
519
+ value = [1, 2, 3, 4]
520
+ type = "(uuu)"
521
+ expect { described_class.new(value, type: type) }
522
+ .to raise_error(ArgumentError, /type has 3 members.*value has 4 members/)
523
+ end
524
+
525
+ it "converts value to ::Array of Data::Base" do
526
+ value = three_words.new(*integers)
527
+ type = T::Struct[T::INT32, T::INT32, T::INT32]
528
+ result = described_class.new(value, type: type)
529
+
530
+ expect(result.exact_value).to be_an(::Array)
531
+ expect(result.exact_value[0]).to be_a(DBus::Data::Base)
532
+ end
533
+ end
534
+ end
535
+
536
+ describe DBus::Data::DictEntry do
537
+ describe ".from_typed" do
538
+ it "creates new instance from given object and type" do
539
+ type = T::Hash[String, T::INT16].child
540
+ expect(described_class.from_typed(["test", 12], type: type))
541
+ .to be_a(described_class)
542
+ end
543
+ end
544
+
545
+ describe "#initialize" do
546
+ it "checks that type matches class" do
547
+ value = [1, 2]
548
+ type = T::Array[T::INT32]
549
+
550
+ expect { described_class.new(value, type: type) }
551
+ .to raise_error(ArgumentError, /Expecting "e"/)
552
+ end
553
+
554
+ it "checks type of a Data::DictEntry value" do
555
+ value1 = [1, 2]
556
+ type1 = T::Hash[T::UINT32, T::UINT32].child
557
+ result1 = described_class.new(value1, type: type1)
558
+
559
+ value2 = result1
560
+ type2 = T::Hash[T::UINT64, T::UINT64].child
561
+ expect { described_class.new(value2, type: type2) }
562
+ .to raise_error(ArgumentError, /value type is DICT_ENTRY.*UINT32/)
563
+ end
564
+
565
+ it "checks that size of type and value match" do
566
+ value = [1, 2, 3]
567
+ type = T::Hash[T::UINT32, T::UINT32].child
568
+ expect { described_class.new(value, type: type) }
569
+ .to raise_error(ArgumentError, /type has 2 members.*value has 3 members/)
570
+ end
571
+
572
+ it "converts value to ::Array of Data::Base" do
573
+ two_words = ::Struct.new(:k, :v)
574
+ value = two_words.new(1, 2)
575
+ type = T::Hash[T::UINT32, T::UINT32].child
576
+ result = described_class.new(value, type: type)
577
+
578
+ expect(result.exact_value).to be_an(::Array)
579
+ expect(result.exact_value[0]).to be_a(DBus::Data::Base)
580
+ end
581
+
582
+ it "takes a plain value" do
583
+ input = ["test", 23]
584
+
585
+ type = T::Hash[String, T::INT16].child
586
+ value = described_class.new(input, type: type)
587
+
588
+ expect(value).to be_a(described_class)
589
+ expect(value.type.to_s).to eq "{sn}"
590
+ expect(value.value).to eql input
591
+ end
592
+ end
593
+ end
594
+
595
+ describe DBus::Data::Variant do
596
+ describe ".from_typed" do
597
+ it "creates new instance from given object and type" do
598
+ type = DBus.type(T::VARIANT)
599
+ value = described_class.from_typed("test", type: type)
600
+ expect(value).to be_a(described_class)
601
+ expect(value.type.to_s).to eq "v"
602
+ expect(value.member_type.to_s).to eq "s"
603
+ end
604
+ end
605
+
606
+ describe "#initialize" do
607
+ it "takes a plain value" do
608
+ input = 42
609
+
610
+ type = DBus.type(T::INT16)
611
+ value = described_class.new(input, member_type: type)
612
+ expect(value).to be_a(described_class)
613
+ expect(value.type.to_s).to eq "v"
614
+ expect(value.member_type.to_s).to eq "n"
615
+ expect(value.value).to eq 42
616
+ end
617
+
618
+ # FIXME: verify that @value has the correct class
619
+ it "takes an exact value" do
620
+ input = DBus::Data::Int16.new(42)
621
+
622
+ type = DBus.type(T::INT16)
623
+ value = described_class.new(input, member_type: type)
624
+ expect(value).to be_a(described_class)
625
+ expect(value.type.to_s).to eq "v"
626
+ expect(value.member_type.to_s).to eq "n"
627
+ expect(value.value).to eq 42
628
+ end
629
+
630
+ it "checks the type of the exact value" do
631
+ input = DBus::Data::UInt16.new(42)
632
+
633
+ type = DBus.type(T::INT16)
634
+ expect { described_class.new(input, member_type: type) }
635
+ .to raise_error(ArgumentError, /Variant type n does not match value type q/)
636
+ end
637
+ end
638
+
639
+ include_examples "#== and #eql? work for container types (1 value)",
640
+ "/foo", { member_type: DBus.type(T::STRING) }
641
+
642
+ describe "DBus.variant compatibility" do
643
+ let(:v) { DBus.variant("o", "/foo") }
644
+
645
+ describe "#[]" do
646
+ it "returns the type for 0" do
647
+ expect(v[0]).to eq DBus.type(DBus::Type::OBJECT_PATH)
648
+ end
649
+
650
+ it "returns the value for 1" do
651
+ expect(v[1]).to eq DBus::ObjectPath.new("/foo")
652
+ end
653
+
654
+ it "returns an error for other indices" do
655
+ expect { v[2] }.to raise_error(ArgumentError, /DBus.variant can only be indexed with 0 or 1/)
656
+ end
657
+ end
658
+
659
+ describe "#first" do
660
+ it "returns the type" do
661
+ expect(v.first).to eq DBus.type(DBus::Type::OBJECT_PATH)
662
+ end
663
+ end
664
+
665
+ describe "#last" do
666
+ it "returns the value" do
667
+ expect(v.last).to eq DBus::ObjectPath.new("/foo")
668
+ end
669
+ end
670
+ end
671
+ end
672
+ end
673
+ end