ruby-dbus 0.16.0 → 0.18.1

Sign up to get free protection for your applications and to get access to all the features.
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