ruby-dbus 0.16.0 → 0.18.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +46 -0
  3. data/README.md +3 -5
  4. data/Rakefile +18 -8
  5. data/VERSION +1 -1
  6. data/doc/Reference.md +94 -4
  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 +126 -74
  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 +725 -0
  26. data/lib/dbus/error.rb +4 -2
  27. data/lib/dbus/introspect.rb +91 -30
  28. data/lib/dbus/logger.rb +3 -1
  29. data/lib/dbus/marshall.rb +228 -294
  30. data/lib/dbus/matchrule.rb +16 -16
  31. data/lib/dbus/message.rb +44 -37
  32. data/lib/dbus/message_queue.rb +16 -10
  33. data/lib/dbus/object.rb +296 -24
  34. data/lib/dbus/object_path.rb +11 -6
  35. data/lib/dbus/proxy_object.rb +22 -1
  36. data/lib/dbus/proxy_object_factory.rb +11 -7
  37. data/lib/dbus/proxy_object_interface.rb +26 -21
  38. data/lib/dbus/raw_message.rb +91 -0
  39. data/lib/dbus/type.rb +182 -80
  40. data/lib/dbus/xml.rb +28 -17
  41. data/lib/dbus.rb +13 -7
  42. data/ruby-dbus.gemspec +7 -3
  43. data/spec/async_spec.rb +2 -0
  44. data/spec/binding_spec.rb +2 -0
  45. data/spec/bus_and_xml_backend_spec.rb +2 -0
  46. data/spec/bus_driver_spec.rb +2 -0
  47. data/spec/bus_name_spec.rb +3 -1
  48. data/spec/bus_spec.rb +2 -0
  49. data/spec/byte_array_spec.rb +2 -0
  50. data/spec/client_robustness_spec.rb +4 -2
  51. data/spec/data/marshall.yaml +1639 -0
  52. data/spec/data_spec.rb +298 -0
  53. data/spec/err_msg_spec.rb +2 -0
  54. data/spec/introspect_xml_parser_spec.rb +2 -0
  55. data/spec/introspection_spec.rb +2 -0
  56. data/spec/main_loop_spec.rb +3 -1
  57. data/spec/node_spec.rb +23 -0
  58. data/spec/object_path_spec.rb +3 -0
  59. data/spec/packet_marshaller_spec.rb +34 -0
  60. data/spec/packet_unmarshaller_spec.rb +262 -0
  61. data/spec/property_spec.rb +88 -5
  62. data/spec/proxy_object_spec.rb +2 -0
  63. data/spec/server_robustness_spec.rb +2 -0
  64. data/spec/server_spec.rb +2 -0
  65. data/spec/service_newapi.rb +39 -70
  66. data/spec/session_bus_spec.rb +3 -1
  67. data/spec/session_bus_spec_manual.rb +2 -0
  68. data/spec/signal_spec.rb +5 -3
  69. data/spec/spec_helper.rb +35 -9
  70. data/spec/thread_safety_spec.rb +2 -0
  71. data/spec/tools/dbus-limited-session.conf +4 -0
  72. data/spec/type_spec.rb +69 -6
  73. data/spec/value_spec.rb +16 -1
  74. data/spec/variant_spec.rb +4 -2
  75. metadata +32 -10
data/spec/data_spec.rb ADDED
@@ -0,0 +1,298 @@
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
+ end
187
+
188
+ describe DBus::Data::ObjectPath do
189
+ good = [
190
+ "/"
191
+ # TODO: others
192
+ ]
193
+
194
+ bad = [
195
+ ["", DBus::InvalidPacketException, "Invalid object path"]
196
+ # TODO: others
197
+ ]
198
+
199
+ include_examples "constructor accepts plain or typed values", good
200
+ include_examples "constructor rejects values from this list", bad
201
+ end
202
+
203
+ describe DBus::Data::Signature do
204
+ good = [
205
+ "",
206
+ "i",
207
+ "ii"
208
+ # TODO: others
209
+ ]
210
+
211
+ bad = [
212
+ ["!", DBus::InvalidPacketException, "Unknown type code"]
213
+ # TODO: others
214
+ ]
215
+
216
+ include_examples "constructor accepts plain or typed values", good
217
+ include_examples "constructor rejects values from this list", bad
218
+ end
219
+ end
220
+
221
+ describe "containers" do
222
+ describe DBus::Data::Array do
223
+ good = [
224
+ # [[1, 2, 3], member_type: nil],
225
+ [[1, 2, 3], { member_type: "q" }],
226
+ [[1, 2, 3], { member_type: DBus::Type::UINT16 }],
227
+ [[1, 2, 3], { member_type: DBus.type("q") }],
228
+ [[DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), DBus::Data::UInt16.new(3)], { member_type: "q" }]
229
+ # TODO: others
230
+ ]
231
+
232
+ bad = [
233
+ # undesirable type guessing
234
+ ## [[1, 2, 3], { member_type: nil }, DBus::InvalidPacketException, "Unknown type code"],
235
+ ## [[1, 2, 3], { member_type: "!" }, DBus::InvalidPacketException, "Unknown type code"]
236
+ # TODO: others
237
+ ]
238
+
239
+ include_examples "constructor (kwargs) accepts values", good
240
+ include_examples "constructor (kwargs) rejects values", bad
241
+ end
242
+
243
+ describe DBus::Data::Struct do
244
+ three_words = ::Struct.new(:a, :b, :c)
245
+
246
+ qqq = ["q", "q", "q"]
247
+ integers = [1, 2, 3]
248
+ uints = [DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), DBus::Data::UInt16.new(3)]
249
+
250
+ # TODO: all the reasonable initialization params
251
+ # need to be normalized into one/few internal representation.
252
+ # So check what is the result
253
+ #
254
+ # Internally, it must be Data::Base
255
+ # Perhaps distinguish #value => Data::Base
256
+ # and #plain_value => plain Ruby
257
+ #
258
+ # but then, can they mutate?
259
+ #
260
+ # TODO: also check data ownership: reasonable to own the data?
261
+ # can make it explicit?
262
+ good = [
263
+ # from plain array; various m_t styles
264
+ [integers, { member_types: ["q", "q", "q"] }],
265
+ [integers, { member_types: [DBus::Type::UINT16, DBus::Type::UINT16, DBus::Type::UINT16] }],
266
+ [integers, { member_types: DBus.types("qqq") }],
267
+ # plain array of data
268
+ [uints, { member_types: DBus.types("qqq") }],
269
+ # ::Struct
270
+ [three_words.new(*integers), { member_types: qqq }],
271
+ [three_words.new(*uints), { member_types: qqq }]
272
+ # TODO: others
273
+ ]
274
+
275
+ _bad_but_valid = [
276
+ # Wrong member_types arg:
277
+ # hmm this is another reason to pass the type
278
+ # as the entire struct type, not the members:
279
+ # empty struct will be caught naturally
280
+ [integers, { member_types: [] }, ArgumentError, "???"],
281
+ [integers, { member_types: ["!"] }, DBus::InvalidPacketException, "Unknown type code"],
282
+ # STRUCT specific: member count mismatch
283
+ [[1, 2], { member_types: DBus.types("qqq") }, ArgumentError, "???"],
284
+ [[1, 2, 3, 4], { member_types: DBus.types("qqq") }, ArgumentError, "???"]
285
+ # TODO: others
286
+ ]
287
+
288
+ include_examples "constructor (kwargs) accepts values", good
289
+ # include_examples "constructor (kwargs) rejects values", bad
290
+ end
291
+
292
+ describe DBus::Data::Variant do
293
+ end
294
+
295
+ describe DBus::Data::DictEntry do
296
+ end
297
+ end
298
+ end
data/spec/err_msg_spec.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  # should report it missing on org.ruby.SampleInterface
3
5
  # (on object...) instead of on DBus::Proxy::ObjectInterface
4
6
  require_relative "spec_helper"
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative "spec_helper"
3
5
  require "dbus"
4
6
 
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative "spec_helper"
3
5
  require "dbus"
4
6
 
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  # Test the main loop
3
5
  require_relative "spec_helper"
4
6
  require "dbus"
@@ -70,7 +72,7 @@ describe "MainLoopTest" do
70
72
  @obj.on_signal "LongTaskEnd"
71
73
  end
72
74
 
73
- it "tests loop quit" do
75
+ it "tests loop quit", slow: true do
74
76
  test_loop_quit 1
75
77
  end
76
78
 
data/spec/node_spec.rb ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "spec_helper"
5
+ require "dbus"
6
+
7
+ describe DBus::Node do
8
+ describe "#inspect" do
9
+ # the behavior needs improvement
10
+ it "shows the node, poorly" do
11
+ parent = described_class.new("parent")
12
+ parent.object = DBus::Object.new("/parent")
13
+
14
+ 3.times do |i|
15
+ child_name = "child#{i}"
16
+ child = described_class.new(child_name)
17
+ parent[child_name] = child
18
+ end
19
+
20
+ expect(parent.inspect).to match(/<DBus::Node [0-9a-f]+ {child0 => {},child1 => {},child2 => {}}>/)
21
+ end
22
+ end
23
+ end
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative "spec_helper"
3
5
  require "dbus"
4
6
 
@@ -18,6 +20,7 @@ describe DBus::ObjectPath do
18
20
  expect(described_class.valid?("/EmptyLastComponent/")).to be_falsey
19
21
  expect(described_class.valid?("/Invalid Character")).to be_falsey
20
22
  expect(described_class.valid?("/Invalid-Character")).to be_falsey
23
+ expect(described_class.valid?("/InválídCháráctér")).to be_falsey
21
24
  end
22
25
  end
23
26
  end
@@ -0,0 +1,34 @@
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
+ end
33
+ end
34
+ 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