ruby-dbus 0.16.0 → 0.25.0
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.
- checksums.yaml +4 -4
- data/NEWS.md +288 -3
- data/README.md +3 -5
- data/Rakefile +27 -12
- data/VERSION +1 -1
- data/doc/Reference.md +109 -10
- data/examples/doc/_extract_examples +7 -0
- data/examples/no-bus/pulseaudio.rb +50 -0
- data/examples/no-introspect/nm-test.rb +2 -0
- data/examples/no-introspect/tracker-test.rb +3 -1
- data/examples/rhythmbox/playpause.rb +2 -1
- data/examples/service/call_service.rb +2 -1
- data/examples/service/complex-property.rb +21 -0
- data/examples/service/service_newapi.rb +3 -3
- data/examples/simple/call_introspect.rb +1 -0
- data/examples/simple/get_id.rb +6 -3
- data/examples/simple/properties.rb +2 -0
- data/examples/utils/listnames.rb +10 -1
- data/examples/utils/notify.rb +1 -0
- data/lib/dbus/api_options.rb +9 -0
- data/lib/dbus/auth.rb +307 -217
- data/lib/dbus/bus.rb +155 -588
- data/lib/dbus/bus_name.rb +12 -8
- data/lib/dbus/connection.rb +363 -0
- data/lib/dbus/core_ext/class/attribute.rb +1 -1
- data/lib/dbus/data.rb +821 -0
- data/lib/dbus/emits_changed_signal.rb +83 -0
- data/lib/dbus/error.rb +4 -2
- data/lib/dbus/introspect.rb +133 -31
- data/lib/dbus/logger.rb +6 -3
- data/lib/dbus/main.rb +66 -0
- data/lib/dbus/marshall.rb +247 -296
- data/lib/dbus/matchrule.rb +16 -16
- data/lib/dbus/message.rb +66 -57
- data/lib/dbus/message_queue.rb +40 -25
- data/lib/dbus/node_tree.rb +105 -0
- data/lib/dbus/object.rb +442 -33
- data/lib/dbus/object_manager.rb +61 -0
- data/lib/dbus/object_path.rb +11 -6
- data/lib/dbus/object_server.rb +149 -0
- data/lib/dbus/org.freedesktop.DBus.xml +97 -0
- data/lib/dbus/platform.rb +26 -0
- data/lib/dbus/proxy_object.rb +30 -11
- data/lib/dbus/proxy_object_factory.rb +13 -7
- data/lib/dbus/proxy_object_interface.rb +63 -30
- data/lib/dbus/proxy_service.rb +107 -0
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +319 -86
- data/lib/dbus/xml.rb +40 -19
- data/lib/dbus.rb +28 -17
- data/ruby-dbus.gemspec +13 -6
- data/spec/async_spec.rb +2 -0
- data/spec/auth_spec.rb +225 -0
- data/spec/binding_spec.rb +2 -0
- data/spec/bus_and_xml_backend_spec.rb +5 -1
- data/spec/bus_connection_spec.rb +93 -0
- data/spec/bus_name_spec.rb +3 -1
- data/spec/bus_spec.rb +2 -0
- data/spec/byte_array_spec.rb +2 -0
- data/spec/client_robustness_spec.rb +4 -2
- data/spec/connection_spec.rb +37 -0
- data/spec/coverage_helper.rb +39 -0
- data/spec/data/marshall.yaml +1667 -0
- data/spec/data_spec.rb +673 -0
- data/spec/dbus_spec.rb +22 -0
- data/spec/emits_changed_signal_spec.rb +58 -0
- data/spec/err_msg_spec.rb +2 -0
- data/spec/introspect_xml_parser_spec.rb +2 -0
- data/spec/introspection_spec.rb +2 -0
- data/spec/main_loop_spec.rb +17 -1
- data/spec/message_spec.rb +21 -0
- data/spec/mock-service/cockpit-dbustests.rb +29 -0
- data/spec/mock-service/com.redhat.Cockpit.DBusTests.xml +180 -0
- data/spec/mock-service/org.ruby.service.service +4 -0
- data/spec/mock-service/org.rubygems.ruby_dbus.DBusTests.service +4 -0
- data/spec/{service_newapi.rb → mock-service/spaghetti-monster.rb} +122 -75
- data/spec/node_spec.rb +65 -0
- data/spec/object_manager_spec.rb +33 -0
- data/spec/object_path_spec.rb +3 -0
- data/spec/object_server_spec.rb +138 -0
- data/spec/object_spec.rb +194 -0
- data/spec/packet_marshaller_spec.rb +41 -0
- data/spec/packet_unmarshaller_spec.rb +248 -0
- data/spec/platform_spec.rb +14 -0
- data/spec/property_spec.rb +214 -12
- data/spec/proxy_object_interface_spec.rb +35 -0
- data/spec/proxy_object_spec.rb +2 -0
- data/spec/{bus_driver_spec.rb → proxy_service_spec.rb} +15 -8
- data/spec/raw_message_spec.rb +32 -0
- data/spec/server_robustness_spec.rb +20 -6
- data/spec/session_bus_spec.rb +36 -1
- data/spec/session_bus_spec_manual.rb +2 -0
- data/spec/signal_spec.rb +16 -3
- data/spec/spec_helper.rb +25 -33
- data/spec/thread_safety_spec.rb +55 -12
- data/spec/tools/dbus-launch-simple +9 -6
- data/spec/tools/dbus-limited-session.conf +32 -0
- data/spec/tools/test_env +26 -6
- data/spec/type_spec.rb +214 -6
- data/spec/value_spec.rb +16 -1
- data/spec/variant_spec.rb +4 -2
- data/spec/zzz_quit_spec.rb +16 -0
- metadata +92 -20
- data/examples/gdbus/gdbus +0 -257
- data/examples/gdbus/gdbus.glade +0 -98
- data/examples/gdbus/launch.sh +0 -4
- data/spec/server_spec.rb +0 -53
- data/spec/tools/test_server +0 -39
|
@@ -0,0 +1,248 @@
|
|
|
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
|
+
expect(result.value).to eq(expected)
|
|
52
|
+
|
|
53
|
+
expect(remaining_buffer(subject)).to be_empty
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# this is necessary because we do an early switch on the signature
|
|
60
|
+
RSpec.shared_examples "reports empty data" do
|
|
61
|
+
it "reports empty data" do
|
|
62
|
+
[:big, :little].each do |endianness|
|
|
63
|
+
subject = described_class.new("", endianness)
|
|
64
|
+
expect { subject.unmarshall(signature) }.to raise_error(DBus::IncompleteBufferException)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe DBus::PacketUnmarshaller do
|
|
70
|
+
context "marshall.yaml" do
|
|
71
|
+
marshall_yaml.each do |test|
|
|
72
|
+
t = OpenStruct.new(test)
|
|
73
|
+
signature = t.sig
|
|
74
|
+
buffer = buffer_from_yaml(t.buf)
|
|
75
|
+
endianness = t.end.to_sym
|
|
76
|
+
|
|
77
|
+
# successful parse
|
|
78
|
+
if !t.val.nil?
|
|
79
|
+
expected = t.val
|
|
80
|
+
|
|
81
|
+
it "parses a '#{signature}' to get #{t.val.inspect} (plain)" do
|
|
82
|
+
subject = described_class.new(buffer, endianness)
|
|
83
|
+
results = subject.unmarshall(signature, mode: :plain)
|
|
84
|
+
# unmarshall works on multiple signatures but we use one
|
|
85
|
+
expect(results).to be_an(Array)
|
|
86
|
+
expect(results.size).to eq(1)
|
|
87
|
+
result = results.first
|
|
88
|
+
|
|
89
|
+
expect(result).to eq(expected)
|
|
90
|
+
expect(remaining_buffer(subject)).to be_empty
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "parses a '#{t.sig}' to get #{t.val.inspect} (exact)" do
|
|
94
|
+
subject = described_class.new(buffer, endianness)
|
|
95
|
+
results = subject.unmarshall(signature, mode: :exact)
|
|
96
|
+
# unmarshall works on multiple signatures but we use one
|
|
97
|
+
expect(results).to be_an(Array)
|
|
98
|
+
expect(results.size).to eq(1)
|
|
99
|
+
result = results.first
|
|
100
|
+
|
|
101
|
+
expect(result).to be_a(DBus::Data::Base)
|
|
102
|
+
expect(result.value).to eq(expected)
|
|
103
|
+
|
|
104
|
+
expect(remaining_buffer(subject)).to be_empty
|
|
105
|
+
end
|
|
106
|
+
elsif t.exc
|
|
107
|
+
next if t.unmarshall == false
|
|
108
|
+
|
|
109
|
+
exc_class = DBus.const_get(t.exc)
|
|
110
|
+
msg_re = Regexp.new(Regexp.escape(t.msg))
|
|
111
|
+
|
|
112
|
+
# TODO: InvalidPacketException is never rescued.
|
|
113
|
+
# The other end is sending invalid data. Can we do better than crashing?
|
|
114
|
+
# When we can test with peer connections, try it out.
|
|
115
|
+
it "parses a '#{signature}' to report a #{t.exc}" do
|
|
116
|
+
subject = described_class.new(buffer, endianness)
|
|
117
|
+
expect { subject.unmarshall(signature, mode: :plain) }.to raise_error(exc_class, msg_re)
|
|
118
|
+
|
|
119
|
+
subject = described_class.new(buffer, endianness)
|
|
120
|
+
expect { subject.unmarshall(signature, mode: :exact) }.to raise_error(exc_class, msg_re)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context "BYTEs" do
|
|
127
|
+
let(:signature) { "y" }
|
|
128
|
+
include_examples "reports empty data"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context "BOOLEANs" do
|
|
132
|
+
let(:signature) { "b" }
|
|
133
|
+
include_examples "reports empty data"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
context "INT16s" do
|
|
137
|
+
let(:signature) { "n" }
|
|
138
|
+
include_examples "reports empty data"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
context "UINT16s" do
|
|
142
|
+
let(:signature) { "q" }
|
|
143
|
+
include_examples "reports empty data"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
context "INT32s" do
|
|
147
|
+
let(:signature) { "i" }
|
|
148
|
+
include_examples "reports empty data"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
context "UINT32s" do
|
|
152
|
+
let(:signature) { "u" }
|
|
153
|
+
include_examples "reports empty data"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
context "UNIX_FDs" do
|
|
157
|
+
let(:signature) { "h" }
|
|
158
|
+
include_examples "reports empty data"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
context "INT64s" do
|
|
162
|
+
let(:signature) { "x" }
|
|
163
|
+
include_examples "reports empty data"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context "UINT64s" do
|
|
167
|
+
let(:signature) { "t" }
|
|
168
|
+
include_examples "reports empty data"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
context "DOUBLEs" do
|
|
172
|
+
let(:signature) { "d" }
|
|
173
|
+
# See https://en.wikipedia.org/wiki/Double-precision_floating-point_format
|
|
174
|
+
# for binary representations
|
|
175
|
+
# TODO: figure out IEEE754 comparisons
|
|
176
|
+
good = [
|
|
177
|
+
# But == cant distinguish -0.0
|
|
178
|
+
["\x00\x00\x00\x00\x00\x00\x00\x80", :little, -0.0],
|
|
179
|
+
# But NaN == NaN is false!
|
|
180
|
+
# ["\xff\xff\xff\xff\xff\xff\xff\xff", :little, Float::NAN],
|
|
181
|
+
["\x80\x00\x00\x00\x00\x00\x00\x00", :big, -0.0]
|
|
182
|
+
# ["\xff\xff\xff\xff\xff\xff\xff\xff", :big, Float::NAN]
|
|
183
|
+
]
|
|
184
|
+
include_examples "parses good data", good
|
|
185
|
+
include_examples "reports empty data"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
context "STRINGs" do
|
|
189
|
+
let(:signature) { "s" }
|
|
190
|
+
include_examples "reports empty data"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
context "OBJECT_PATHs" do
|
|
194
|
+
let(:signature) { "o" }
|
|
195
|
+
include_examples "reports empty data"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
context "SIGNATUREs" do
|
|
199
|
+
let(:signature) { "g" }
|
|
200
|
+
include_examples "reports empty data"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
context "ARRAYs" do
|
|
204
|
+
context "of BYTEs" do
|
|
205
|
+
let(:signature) { "ay" }
|
|
206
|
+
include_examples "reports empty data"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
context "of UINT64s" do
|
|
210
|
+
let(:signature) { "at" }
|
|
211
|
+
include_examples "reports empty data"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
context "of STRUCT of 2 UINT16s" do
|
|
215
|
+
let(:signature) { "a(qq)" }
|
|
216
|
+
include_examples "reports empty data"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
context "of DICT_ENTRIES" do
|
|
220
|
+
let(:signature) { "a{yq}" }
|
|
221
|
+
include_examples "reports empty data"
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
context "STRUCTs" do
|
|
226
|
+
# TODO: this is invalid but does not raise
|
|
227
|
+
context "(generic 'r' struct)" do
|
|
228
|
+
let(:signature) { "r" }
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
context "of two shorts" do
|
|
232
|
+
let(:signature) { "(qq)" }
|
|
233
|
+
include_examples "reports empty data"
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# makes sense here? or in array? remember invalid sigs are rejected elsewhere
|
|
238
|
+
context "DICT_ENTRYs" do
|
|
239
|
+
context "(generic 'e' dict_entry)" do
|
|
240
|
+
let(:signature) { "e" }
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
context "VARIANTs" do
|
|
245
|
+
let(:signature) { "v" }
|
|
246
|
+
include_examples "reports empty data"
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "spec_helper"
|
|
5
|
+
require "dbus"
|
|
6
|
+
|
|
7
|
+
describe DBus::Platform do
|
|
8
|
+
describe ".macos?" do
|
|
9
|
+
# code coverage chasing, as other tests mock it out
|
|
10
|
+
it "doesn't crash" do
|
|
11
|
+
expect { described_class.macos? }.to_not raise_error
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/spec/property_spec.rb
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
require_relative "spec_helper"
|
|
3
5
|
require "dbus"
|
|
4
6
|
|
|
7
|
+
# FIXME: factor out DBus::TestFixtures::Value in spec_helper
|
|
8
|
+
require "ostruct"
|
|
9
|
+
require "yaml"
|
|
10
|
+
|
|
11
|
+
data_dir = File.expand_path("data", __dir__)
|
|
12
|
+
marshall_yaml_s = File.read("#{data_dir}/marshall.yaml")
|
|
13
|
+
marshall_yaml = YAML.safe_load(marshall_yaml_s)
|
|
14
|
+
|
|
5
15
|
describe "PropertyTest" do
|
|
6
16
|
before(:each) do
|
|
7
|
-
session_bus = DBus::ASessionBus.new
|
|
8
|
-
@svc = session_bus.service("org.ruby.service")
|
|
17
|
+
@session_bus = DBus::ASessionBus.new
|
|
18
|
+
@svc = @session_bus.service("org.ruby.service")
|
|
9
19
|
@obj = @svc.object("/org/ruby/MyInstance")
|
|
10
20
|
@iface = @obj["org.ruby.SampleInterface"]
|
|
11
21
|
end
|
|
@@ -21,17 +31,40 @@ describe "PropertyTest" do
|
|
|
21
31
|
expect(iface["ReadMe"]).to eq("READ ME")
|
|
22
32
|
end
|
|
23
33
|
|
|
34
|
+
context "when reading a property fails" do
|
|
35
|
+
it "gets an error, mentioning the qualified property name" do
|
|
36
|
+
expect { @iface["Explosive"] }
|
|
37
|
+
.to raise_error(DBus::Error, /getting.*SampleInterface.Explosive.*Something failed/)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
24
41
|
it "tests property nonreading" do
|
|
25
42
|
expect { @iface["WriteMe"] }.to raise_error(DBus::Error, /not readable/)
|
|
26
43
|
end
|
|
27
44
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
context "writing properties" do
|
|
46
|
+
it "tests property writing" do
|
|
47
|
+
@iface["ReadOrWriteMe"] = "VALUE"
|
|
48
|
+
expect(@iface["ReadOrWriteMe"]).to eq("VALUE")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context "when writing a read-only property" do
|
|
52
|
+
it "gets an error, mentioning the qualified property name" do
|
|
53
|
+
expect { @iface["ReadMe"] = "WROTE" }
|
|
54
|
+
.to raise_error(DBus::Error, /SampleInterface.ReadMe.*not writable/)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context "when writing a property fails" do
|
|
59
|
+
it "gets an error, mentioning the qualified property name" do
|
|
60
|
+
expect { @iface["WriteMe"] = "Bruno is a city in Czechia" }
|
|
61
|
+
.to raise_error(DBus::Error, /setting.*SampleInterface.WriteMe/)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
31
64
|
end
|
|
32
65
|
|
|
33
66
|
# https://github.com/mvidner/ruby-dbus/pull/19
|
|
34
|
-
it "tests service select timeout" do
|
|
67
|
+
it "tests service select timeout", slow: true do
|
|
35
68
|
@iface["ReadOrWriteMe"] = "VALUE"
|
|
36
69
|
expect(@iface["ReadOrWriteMe"]).to eq("VALUE")
|
|
37
70
|
# wait for the service to become idle
|
|
@@ -40,13 +73,9 @@ describe "PropertyTest" do
|
|
|
40
73
|
expect(@iface["ReadOrWriteMe"]).to eq("VALUE")
|
|
41
74
|
end
|
|
42
75
|
|
|
43
|
-
it "tests property nonwriting" do
|
|
44
|
-
expect { @iface["ReadMe"] = "WROTE" }.to raise_error(DBus::Error, /not writable/)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
76
|
it "tests get all" do
|
|
48
77
|
all = @iface.all_properties
|
|
49
|
-
expect(all.keys.sort).to eq(["ReadMe", "ReadOrWriteMe"])
|
|
78
|
+
expect(all.keys.sort).to eq(["MyArray", "MyByte", "MyDict", "MyStruct", "MyVariant", "ReadMe", "ReadOrWriteMe"])
|
|
50
79
|
end
|
|
51
80
|
|
|
52
81
|
it "tests get all on a V1 object" do
|
|
@@ -54,7 +83,7 @@ describe "PropertyTest" do
|
|
|
54
83
|
iface = obj["org.ruby.SampleInterface"]
|
|
55
84
|
|
|
56
85
|
all = iface.all_properties
|
|
57
|
-
expect(all.keys.sort).to eq(["ReadMe", "ReadOrWriteMe"])
|
|
86
|
+
expect(all.keys.sort).to eq(["MyArray", "MyByte", "MyDict", "MyStruct", "MyVariant", "ReadMe", "ReadOrWriteMe"])
|
|
58
87
|
end
|
|
59
88
|
|
|
60
89
|
it "tests unknown property reading" do
|
|
@@ -64,4 +93,177 @@ describe "PropertyTest" do
|
|
|
64
93
|
it "tests unknown property writing" do
|
|
65
94
|
expect { @iface["Spoon"] = "FPRK" }.to raise_error(DBus::Error, /not found/)
|
|
66
95
|
end
|
|
96
|
+
|
|
97
|
+
it "errors for a property on an unknown interface" do
|
|
98
|
+
# our idiomatic way would error out on interface lookup already,
|
|
99
|
+
# so do it the low level way
|
|
100
|
+
prop_if = @obj[DBus::PROPERTY_INTERFACE]
|
|
101
|
+
expect { prop_if.Get("org.ruby.NoSuchInterface", "SomeProperty") }.to raise_error(DBus::Error) do |e|
|
|
102
|
+
expect(e.name).to match(/UnknownProperty/)
|
|
103
|
+
expect(e.message).to match(/no such interface/)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "errors for GetAll on an unknown interface" do
|
|
108
|
+
# no idiomatic way?
|
|
109
|
+
# so do it the low level way
|
|
110
|
+
prop_if = @obj[DBus::PROPERTY_INTERFACE]
|
|
111
|
+
expect { prop_if.GetAll("org.ruby.NoSuchInterface") }.to raise_error(DBus::Error) do |e|
|
|
112
|
+
expect(e.name).to match(/UnknownProperty/)
|
|
113
|
+
expect(e.message).to match(/no such interface/)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "receives a PropertiesChanged signal", slow: true do
|
|
118
|
+
received = {}
|
|
119
|
+
|
|
120
|
+
# TODO: for client side, provide a helper on_properties_changed,
|
|
121
|
+
# or automate it even more in ProxyObject, ProxyObjectInterface
|
|
122
|
+
prop_if = @obj[DBus::PROPERTY_INTERFACE]
|
|
123
|
+
prop_if.on_signal("PropertiesChanged") do |_interface_name, changed_props, _invalidated_props|
|
|
124
|
+
received.merge!(changed_props)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
@iface["ReadOrWriteMe"] = "VALUE"
|
|
128
|
+
@iface.SetTwoProperties("REAMDE", 255)
|
|
129
|
+
|
|
130
|
+
# loop to process the signal. complicated :-( see signal_spec.rb
|
|
131
|
+
loop = DBus::Main.new
|
|
132
|
+
loop << @session_bus
|
|
133
|
+
quitter = Thread.new do
|
|
134
|
+
sleep 1
|
|
135
|
+
loop.quit
|
|
136
|
+
end
|
|
137
|
+
loop.run
|
|
138
|
+
# quitter has told loop.run to quit
|
|
139
|
+
quitter.join
|
|
140
|
+
|
|
141
|
+
expect(received["ReadOrWriteMe"]).to eq("VALUE")
|
|
142
|
+
expect(received["ReadMe"]).to eq("REAMDE")
|
|
143
|
+
expect(received["MyByte"]).to eq(255)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
context "a struct-typed property" do
|
|
147
|
+
it "gets read as a struct, not an array (#97)" do
|
|
148
|
+
struct = @iface["MyStruct"]
|
|
149
|
+
expect(struct).to be_frozen
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "Get returns the correctly typed value (check with dbus-send)" do
|
|
153
|
+
# As big as the DBus::Data branch is,
|
|
154
|
+
# it still does not handle the :exact mode on the client/proxy side.
|
|
155
|
+
# So we resort to parsing dbus-send output.
|
|
156
|
+
cmd = "dbus-send --print-reply " \
|
|
157
|
+
"--dest=org.ruby.service " \
|
|
158
|
+
"/org/ruby/MyInstance " \
|
|
159
|
+
"org.freedesktop.DBus.Properties.Get " \
|
|
160
|
+
"string:org.ruby.SampleInterface " \
|
|
161
|
+
"string:MyStruct"
|
|
162
|
+
reply = `#{cmd}`
|
|
163
|
+
expect(reply).to match(/variant\s+struct {\s+string "three"\s+string "strings"\s+string "in a struct"\s+}/)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "GetAll returns the correctly typed value (check with dbus-send)" do
|
|
167
|
+
cmd = "dbus-send --print-reply " \
|
|
168
|
+
"--dest=org.ruby.service " \
|
|
169
|
+
"/org/ruby/MyInstance " \
|
|
170
|
+
"org.freedesktop.DBus.Properties.GetAll " \
|
|
171
|
+
"string:org.ruby.SampleInterface "
|
|
172
|
+
reply = `#{cmd}`
|
|
173
|
+
expect(reply).to match(/variant\s+struct {\s+string "three"\s+string "strings"\s+string "in a struct"\s+}/)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
context "an array-typed property" do
|
|
178
|
+
it "gets read as an array" do
|
|
179
|
+
val = @iface["MyArray"]
|
|
180
|
+
expect(val).to eq([42, 43])
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
context "a dict-typed property" do
|
|
185
|
+
it "gets read as a hash" do
|
|
186
|
+
val = @iface["MyDict"]
|
|
187
|
+
expect(val).to eq({
|
|
188
|
+
"one" => 1,
|
|
189
|
+
"two" => "dva",
|
|
190
|
+
"three" => [3, 3, 3]
|
|
191
|
+
})
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "Get returns the correctly typed value (check with dbus-send)" do
|
|
195
|
+
cmd = "dbus-send --print-reply " \
|
|
196
|
+
"--dest=org.ruby.service " \
|
|
197
|
+
"/org/ruby/MyInstance " \
|
|
198
|
+
"org.freedesktop.DBus.Properties.Get " \
|
|
199
|
+
"string:org.ruby.SampleInterface " \
|
|
200
|
+
"string:MyDict"
|
|
201
|
+
reply = `#{cmd}`
|
|
202
|
+
# a bug about variant nesting lead to a "variant variant int32 1" value
|
|
203
|
+
match_rx = /variant \s+ array \s \[ \s+
|
|
204
|
+
dict \s entry\( \s+
|
|
205
|
+
string \s "one" \s+
|
|
206
|
+
variant \s+ int32 \s 1 \s+
|
|
207
|
+
\)/x
|
|
208
|
+
expect(reply).to match(match_rx)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
context "a variant-typed property" do
|
|
213
|
+
it "gets read at all" do
|
|
214
|
+
obj = @svc.object("/org/ruby/MyDerivedInstance")
|
|
215
|
+
iface = obj["org.ruby.SampleInterface"]
|
|
216
|
+
val = iface["MyVariant"]
|
|
217
|
+
expect(val).to eq([42, 43])
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
context "a byte-typed property" do
|
|
222
|
+
# Slightly advanced RSpec:
|
|
223
|
+
# https://rspec.info/documentation/3.9/rspec-expectations/RSpec/Matchers.html#satisfy-instance_method
|
|
224
|
+
let(:a_byte_in_a_variant) do
|
|
225
|
+
satisfying { |x| x.is_a?(DBus::Data::Variant) && x.member_type.to_s == DBus::Type::BYTE }
|
|
226
|
+
# ^ This formatting keeps the matcher on a single line
|
|
227
|
+
# which enables RSpec to cite it if it fails, instead of saying "block".
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
let(:prop_iface) { @obj[DBus::PROPERTY_INTERFACE] }
|
|
231
|
+
|
|
232
|
+
it "gets set with a correct type (#108)" do
|
|
233
|
+
expect(prop_iface).to receive(:Set).with(
|
|
234
|
+
"org.ruby.SampleInterface",
|
|
235
|
+
"MyByte",
|
|
236
|
+
a_byte_in_a_variant
|
|
237
|
+
)
|
|
238
|
+
@iface["MyByte"] = 1
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
it "gets set with a correct type (#108), when using the DBus.variant workaround" do
|
|
242
|
+
expect(prop_iface).to receive(:Set).with(
|
|
243
|
+
"org.ruby.SampleInterface",
|
|
244
|
+
"MyByte",
|
|
245
|
+
a_byte_in_a_variant
|
|
246
|
+
)
|
|
247
|
+
@iface["MyByte"] = DBus.variant("y", 1)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
context "marshall.yaml round-trip via a VARIANT property" do
|
|
252
|
+
marshall_yaml.each do |test|
|
|
253
|
+
t = OpenStruct.new(test)
|
|
254
|
+
next if t.val.nil?
|
|
255
|
+
|
|
256
|
+
# Round trips do not work yet because the properties
|
|
257
|
+
# must present a plain Ruby value so the exact D-Bus type is lost.
|
|
258
|
+
# Round trips will work once users can declare accepting DBus::Data
|
|
259
|
+
# in properties and method arguments.
|
|
260
|
+
it "Sets #{t.sig.inspect}:#{t.val.inspect} and Gets something back" do
|
|
261
|
+
before = DBus::Data.make_typed(t.sig, t.val)
|
|
262
|
+
expect { @iface["MyVariant"] = before }.to_not raise_error
|
|
263
|
+
expect { _after = @iface["MyVariant"] }.to_not raise_error
|
|
264
|
+
# round-trip:
|
|
265
|
+
# expect(after).to eq(before.value)
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
67
269
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "spec_helper"
|
|
5
|
+
require "dbus"
|
|
6
|
+
|
|
7
|
+
describe DBus::ProxyObjectInterface do
|
|
8
|
+
# TODO: tag tests that need a service, eg "needs-service"
|
|
9
|
+
# TODO: maybe remove this and rely on a packaged tool
|
|
10
|
+
around(:each) do |example|
|
|
11
|
+
with_private_bus do
|
|
12
|
+
with_service_by_activation(&example)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
let(:bus) { DBus::ASessionBus.new }
|
|
17
|
+
|
|
18
|
+
context "when calling org.ruby.service" do
|
|
19
|
+
let(:svc) { bus["org.ruby.service"] }
|
|
20
|
+
|
|
21
|
+
# This is white box testing, knowing the implementation
|
|
22
|
+
# A better way would be structuring it according to the D-Bus Spec
|
|
23
|
+
# Or testing the service side doing the right thing? (What if our bugs cancel out)
|
|
24
|
+
describe "#define_method_from_descriptor" do
|
|
25
|
+
it "can call a method with multiple OUT arguments" do
|
|
26
|
+
obj = svc["/org/ruby/MyInstance"]
|
|
27
|
+
ifc = obj["org.ruby.SampleInterface"]
|
|
28
|
+
|
|
29
|
+
even, odd = ifc.EvenOdd([3, 1, 4, 1, 5, 9, 2, 6])
|
|
30
|
+
expect(even).to eq [4, 2, 6]
|
|
31
|
+
expect(odd).to eq [3, 1, 1, 5, 9]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/spec/proxy_object_spec.rb
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
require_relative "spec_helper"
|
|
3
5
|
require "dbus"
|
|
4
6
|
|
|
5
|
-
describe DBus::
|
|
6
|
-
context "when a private bus is set up" do
|
|
7
|
-
around(:each) do |example|
|
|
8
|
-
with_private_bus do
|
|
9
|
-
with_service_by_activation(&example)
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
7
|
+
describe DBus::ProxyService do
|
|
8
|
+
context "when a private bus is set up", tag_service: true do
|
|
13
9
|
let(:bus) { DBus::ASessionBus.new }
|
|
14
10
|
|
|
15
11
|
describe "#exists?" do
|
|
@@ -24,5 +20,16 @@ describe DBus::Service do
|
|
|
24
20
|
expect(svc.exists?).to be false
|
|
25
21
|
end
|
|
26
22
|
end
|
|
23
|
+
|
|
24
|
+
# This method is used by dbus-gui-gtk.
|
|
25
|
+
# Deprecate it? In favor of introspecting the tree gradually
|
|
26
|
+
# or move it to the application code?
|
|
27
|
+
describe "#introspect" do
|
|
28
|
+
it "creates the whole node tree" do
|
|
29
|
+
svc = bus.service("org.ruby.service")
|
|
30
|
+
expect { svc.introspect }.to_not raise_error
|
|
31
|
+
expect(svc.root.dig("org", "ruby", "MyInstance")).to be_a DBus::Node
|
|
32
|
+
end
|
|
33
|
+
end
|
|
27
34
|
end
|
|
28
35
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env rspec
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "spec_helper"
|
|
5
|
+
require "dbus"
|
|
6
|
+
|
|
7
|
+
# Pedantic full coverage test.
|
|
8
|
+
# The happy paths are covered via calling classes
|
|
9
|
+
describe DBus::RawMessage do
|
|
10
|
+
describe ".endianness" do
|
|
11
|
+
it "returns :little for 'l'" do
|
|
12
|
+
expect(described_class.endianness("l")).to eq :little
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "returns :big for 'B'" do
|
|
16
|
+
expect(described_class.endianness("B")).to eq :big
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "raises for other strings" do
|
|
20
|
+
expect { described_class.endianness("m") }
|
|
21
|
+
.to raise_error(DBus::InvalidPacketException, /Incorrect endianness/)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "#align" do
|
|
26
|
+
it "raises for values other than 1 2 4 8" do
|
|
27
|
+
subject = described_class.new("l")
|
|
28
|
+
expect { subject.align(3) }.to raise_error(ArgumentError)
|
|
29
|
+
expect { subject.align(16) }.to raise_error(ArgumentError)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|