ruby-dbus 0.16.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/NEWS.md +16 -0
- data/README.md +3 -5
- data/Rakefile +25 -7
- data/VERSION +1 -1
- data/doc/Reference.md +84 -1
- data/examples/doc/_extract_examples +5 -0
- data/examples/gdbus/gdbus +20 -19
- data/examples/service/call_service.rb +1 -1
- data/lib/dbus/auth.rb +8 -8
- data/lib/dbus/bus.rb +16 -8
- data/lib/dbus/core_ext/class/attribute.rb +1 -1
- data/lib/dbus/introspect.rb +69 -20
- data/lib/dbus/marshall.rb +1 -1
- data/lib/dbus/message_queue.rb +6 -5
- data/lib/dbus/object.rb +264 -13
- data/lib/dbus/proxy_object.rb +7 -0
- data/lib/dbus/proxy_object_interface.rb +1 -0
- data/lib/dbus/type.rb +18 -0
- data/ruby-dbus.gemspec +5 -3
- data/spec/main_loop_spec.rb +1 -1
- data/spec/property_spec.rb +53 -3
- data/spec/service_newapi.rb +20 -66
- data/spec/signal_spec.rb +3 -3
- data/spec/spec_helper.rb +18 -6
- data/spec/tools/dbus-limited-session.conf +4 -0
- metadata +22 -8
data/lib/dbus/object.rb
CHANGED
@@ -7,9 +7,11 @@
|
|
7
7
|
# See the file "COPYING" for the exact licensing terms.
|
8
8
|
|
9
9
|
require "thread"
|
10
|
-
|
10
|
+
require_relative "core_ext/class/attribute"
|
11
11
|
|
12
12
|
module DBus
|
13
|
+
PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties".freeze
|
14
|
+
|
13
15
|
# Exported object type
|
14
16
|
# = Exportable D-Bus object class
|
15
17
|
#
|
@@ -58,7 +60,7 @@ module DBus
|
|
58
60
|
meth.rets.zip(retdata).each do |rsig, rdata|
|
59
61
|
reply.add_param(rsig.type, rdata)
|
60
62
|
end
|
61
|
-
rescue => ex
|
63
|
+
rescue StandardError => ex
|
62
64
|
dbus_msg_exc = msg.annotate_exception(ex)
|
63
65
|
reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
|
64
66
|
end
|
@@ -85,45 +87,294 @@ module DBus
|
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
88
|
-
#
|
89
|
-
|
90
|
+
# Forgetting to declare the interface for a method/signal/property
|
91
|
+
# is a ScriptError.
|
92
|
+
class UndefinedInterface < ScriptError # rubocop:disable Lint/InheritException
|
90
93
|
def initialize(sym)
|
91
|
-
super "No interface specified for #{sym}"
|
94
|
+
super "No interface specified for #{sym}. Enclose it in dbus_interface."
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# A read-write property accessing an instance variable.
|
99
|
+
# A combination of `attr_accessor` and {.dbus_accessor}.
|
100
|
+
#
|
101
|
+
# PropertiesChanged signal will be emitted whenever `foo_bar=` is used
|
102
|
+
# but not when @foo_bar is written directly.
|
103
|
+
#
|
104
|
+
# @param ruby_name [Symbol] :foo_bar is exposed as FooBar;
|
105
|
+
# use dbus_name to override
|
106
|
+
# @param type a signature like "s" or "a(uus)" or Type::STRING
|
107
|
+
# @param dbus_name [String] if not given it is made
|
108
|
+
# by CamelCasing the ruby_name. foo_bar becomes FooBar
|
109
|
+
# to convert the Ruby convention to the DBus convention.
|
110
|
+
# @return [void]
|
111
|
+
def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil)
|
112
|
+
attr_accessor(ruby_name)
|
113
|
+
dbus_accessor(ruby_name, type, dbus_name: dbus_name)
|
114
|
+
end
|
115
|
+
|
116
|
+
# A read-only property accessing an instance variable.
|
117
|
+
# A combination of `attr_reader` and {.dbus_reader}.
|
118
|
+
#
|
119
|
+
# Whenever the property value gets changed from "inside" the object,
|
120
|
+
# you should emit the `PropertiesChanged` signal by calling
|
121
|
+
#
|
122
|
+
# object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
|
123
|
+
#
|
124
|
+
# or, omitting the value in the signal,
|
125
|
+
#
|
126
|
+
# object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
|
127
|
+
#
|
128
|
+
# @param (see .dbus_attr_accessor)
|
129
|
+
# @return (see .dbus_attr_accessor)
|
130
|
+
def self.dbus_attr_reader(ruby_name, type, dbus_name: nil)
|
131
|
+
attr_reader(ruby_name)
|
132
|
+
dbus_reader(ruby_name, type, dbus_name: dbus_name)
|
133
|
+
end
|
134
|
+
|
135
|
+
# A write-only property accessing an instance variable.
|
136
|
+
# A combination of `attr_writer` and {.dbus_writer}.
|
137
|
+
#
|
138
|
+
# @param (see .dbus_attr_accessor)
|
139
|
+
# @return (see .dbus_attr_accessor)
|
140
|
+
def self.dbus_attr_writer(ruby_name, type, dbus_name: nil)
|
141
|
+
attr_writer(ruby_name)
|
142
|
+
dbus_writer(ruby_name, type, dbus_name: dbus_name)
|
143
|
+
end
|
144
|
+
|
145
|
+
# A read-write property using a pair of reader/writer methods
|
146
|
+
# (which must already exist).
|
147
|
+
# (To directly access an instance variable, use {.dbus_attr_accessor} instead)
|
148
|
+
#
|
149
|
+
# Uses {.dbus_watcher} to set up the PropertiesChanged signal.
|
150
|
+
#
|
151
|
+
# @param (see .dbus_attr_accessor)
|
152
|
+
# @return (see .dbus_attr_accessor)
|
153
|
+
def self.dbus_accessor(ruby_name, type, dbus_name: nil)
|
154
|
+
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
155
|
+
|
156
|
+
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
157
|
+
property = Property.new(dbus_name, type, :readwrite, ruby_name: ruby_name)
|
158
|
+
@@cur_intf.define(property)
|
159
|
+
|
160
|
+
dbus_watcher(ruby_name, dbus_name: dbus_name)
|
161
|
+
end
|
162
|
+
|
163
|
+
# A read-only property accessing a reader method (which must already exist).
|
164
|
+
# (To directly access an instance variable, use {.dbus_attr_reader} instead)
|
165
|
+
#
|
166
|
+
# Whenever the property value gets changed from "inside" the object,
|
167
|
+
# you should emit the `PropertiesChanged` signal by calling
|
168
|
+
#
|
169
|
+
# object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
|
170
|
+
#
|
171
|
+
# or, omitting the value in the signal,
|
172
|
+
#
|
173
|
+
# object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
|
174
|
+
#
|
175
|
+
# @param (see .dbus_attr_accessor)
|
176
|
+
# @return (see .dbus_attr_accessor)
|
177
|
+
def self.dbus_reader(ruby_name, type, dbus_name: nil)
|
178
|
+
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
179
|
+
|
180
|
+
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
181
|
+
property = Property.new(dbus_name, type, :read, ruby_name: ruby_name)
|
182
|
+
@@cur_intf.define(property)
|
183
|
+
end
|
184
|
+
|
185
|
+
# A write-only property accessing a writer method (which must already exist).
|
186
|
+
# (To directly access an instance variable, use {.dbus_attr_writer} instead)
|
187
|
+
#
|
188
|
+
# Uses {.dbus_watcher} to set up the PropertiesChanged signal.
|
189
|
+
#
|
190
|
+
# @param (see .dbus_attr_accessor)
|
191
|
+
# @return (see .dbus_attr_accessor)
|
192
|
+
def self.dbus_writer(ruby_name, type, dbus_name: nil)
|
193
|
+
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
194
|
+
|
195
|
+
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
196
|
+
property = Property.new(dbus_name, type, :write, ruby_name: ruby_name)
|
197
|
+
@@cur_intf.define(property)
|
198
|
+
|
199
|
+
dbus_watcher(ruby_name, dbus_name: dbus_name)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Enables automatic sending of the PropertiesChanged signal.
|
203
|
+
# For *ruby_name* `foo_bar`, wrap `foo_bar=` so that it sends
|
204
|
+
# the signal for FooBar.
|
205
|
+
# The original version remains as `_original_foo_bar=`.
|
206
|
+
#
|
207
|
+
# @param ruby_name [Symbol] :foo_bar and :foo_bar= both mean the same thing
|
208
|
+
# @param dbus_name [String] if not given it is made
|
209
|
+
# by CamelCasing the ruby_name. foo_bar becomes FooBar
|
210
|
+
# to convert the Ruby convention to the DBus convention.
|
211
|
+
# @return [void]
|
212
|
+
def self.dbus_watcher(ruby_name, dbus_name: nil)
|
213
|
+
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
214
|
+
cur_intf = @@cur_intf
|
215
|
+
|
216
|
+
ruby_name = ruby_name.to_s.sub(/=$/, "").to_sym
|
217
|
+
ruby_name_eq = "#{ruby_name}=".to_sym
|
218
|
+
original_ruby_name_eq = "_original_#{ruby_name_eq}"
|
219
|
+
|
220
|
+
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
221
|
+
|
222
|
+
# the argument order is alias_method(new_name, existing_name)
|
223
|
+
alias_method original_ruby_name_eq, ruby_name_eq
|
224
|
+
define_method ruby_name_eq do |value|
|
225
|
+
public_send(original_ruby_name_eq, value)
|
226
|
+
|
227
|
+
# TODO: respect EmitsChangedSignal to use invalidated_properties instead
|
228
|
+
# PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
|
229
|
+
PropertiesChanged(cur_intf.name, { dbus_name.to_s => value }, [])
|
92
230
|
end
|
93
231
|
end
|
94
232
|
|
95
233
|
# Defines an exportable method on the object with the given name _sym_,
|
96
234
|
# _prototype_ and the code in a block.
|
97
|
-
|
235
|
+
# @param prototype [Prototype]
|
236
|
+
def self.dbus_method(sym, prototype = "", &block)
|
98
237
|
raise UndefinedInterface, sym if @@cur_intf.nil?
|
99
|
-
@@cur_intf.define(Method.new(sym.to_s).from_prototype(
|
100
|
-
|
238
|
+
@@cur_intf.define(Method.new(sym.to_s).from_prototype(prototype))
|
239
|
+
|
240
|
+
ruby_name = Object.make_method_name(@@cur_intf.name, sym.to_s)
|
241
|
+
# ::Module#define_method(name) { body }
|
242
|
+
define_method(ruby_name, &block)
|
101
243
|
end
|
102
244
|
|
103
245
|
# Emits a signal from the object with the given _interface_, signal
|
104
246
|
# _sig_ and arguments _args_.
|
247
|
+
# @param intf [Interface]
|
248
|
+
# @param sig [Signal]
|
249
|
+
# @param args arguments for the signal
|
105
250
|
def emit(intf, sig, *args)
|
106
251
|
@service.bus.emit(@service, self, intf, sig, *args)
|
107
252
|
end
|
108
253
|
|
109
254
|
# Defines a signal for the object with a given name _sym_ and _prototype_.
|
110
|
-
def self.dbus_signal(sym,
|
255
|
+
def self.dbus_signal(sym, prototype = "")
|
111
256
|
raise UndefinedInterface, sym if @@cur_intf.nil?
|
112
257
|
cur_intf = @@cur_intf
|
113
|
-
signal = Signal.new(sym.to_s).from_prototype(
|
114
|
-
cur_intf.define(Signal.new(sym.to_s).from_prototype(
|
258
|
+
signal = Signal.new(sym.to_s).from_prototype(prototype)
|
259
|
+
cur_intf.define(Signal.new(sym.to_s).from_prototype(prototype))
|
260
|
+
|
261
|
+
# ::Module#define_method(name) { body }
|
115
262
|
define_method(sym.to_s) do |*args|
|
116
263
|
emit(cur_intf, signal, *args)
|
117
264
|
end
|
118
265
|
end
|
119
266
|
|
120
|
-
####################################################################
|
121
|
-
|
122
267
|
# Helper method that returns a method name generated from the interface
|
123
268
|
# name _intfname_ and method name _methname_.
|
124
269
|
# @api private
|
125
270
|
def self.make_method_name(intfname, methname)
|
126
271
|
"#{intfname}%%#{methname}"
|
127
272
|
end
|
273
|
+
|
274
|
+
# TODO: borrow a proven implementation
|
275
|
+
# @param str [String]
|
276
|
+
# @return [String]
|
277
|
+
# @api private
|
278
|
+
def self.camelize(str)
|
279
|
+
str.split(/_/).map(&:capitalize).join("")
|
280
|
+
end
|
281
|
+
|
282
|
+
# Make a D-Bus conventional name, CamelCased.
|
283
|
+
# @param ruby_name [String,Symbol] eg :do_something
|
284
|
+
# @param dbus_name [String,Symbol,nil] use this if given
|
285
|
+
# @return [Symbol] eg DoSomething
|
286
|
+
def self.make_dbus_name(ruby_name, dbus_name: nil)
|
287
|
+
dbus_name ||= camelize(ruby_name.to_s)
|
288
|
+
dbus_name.to_sym
|
289
|
+
end
|
290
|
+
|
291
|
+
# @param interface_name [String]
|
292
|
+
# @param property_name [String]
|
293
|
+
# @return [Property]
|
294
|
+
# @raise [DBus::Error]
|
295
|
+
# @api private
|
296
|
+
def dbus_lookup_property(interface_name, property_name)
|
297
|
+
# what should happen for unknown properties
|
298
|
+
# plasma: InvalidArgs (propname), UnknownInterface (interface)
|
299
|
+
# systemd: UnknownProperty
|
300
|
+
interface = intfs[interface_name]
|
301
|
+
if !interface
|
302
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
|
303
|
+
"Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found: no such interface"
|
304
|
+
end
|
305
|
+
|
306
|
+
property = interface.properties[property_name.to_sym]
|
307
|
+
if !property
|
308
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
|
309
|
+
"Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found"
|
310
|
+
end
|
311
|
+
|
312
|
+
property
|
313
|
+
end
|
314
|
+
|
315
|
+
####################################################################
|
316
|
+
|
317
|
+
# use the above defined methods to declare the property-handling
|
318
|
+
# interfaces and methods
|
319
|
+
|
320
|
+
dbus_interface PROPERTY_INTERFACE do
|
321
|
+
dbus_method :Get, "in interface_name:s, in property_name:s, out value:v" do |interface_name, property_name|
|
322
|
+
property = dbus_lookup_property(interface_name, property_name)
|
323
|
+
|
324
|
+
if property.readable?
|
325
|
+
ruby_name = property.ruby_name
|
326
|
+
value = public_send(ruby_name)
|
327
|
+
[value]
|
328
|
+
else
|
329
|
+
raise DBus.error("org.freedesktop.DBus.Error.PropertyWriteOnly"),
|
330
|
+
"Property '#{interface_name}.#{property_name}' (on object '#{@path}') is not readable"
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
dbus_method :Set, "in interface_name:s, in property_name:s, in val:v" do |interface_name, property_name, value|
|
335
|
+
property = dbus_lookup_property(interface_name, property_name)
|
336
|
+
|
337
|
+
if property.writable?
|
338
|
+
ruby_name_eq = "#{property.ruby_name}="
|
339
|
+
public_send(ruby_name_eq, value)
|
340
|
+
else
|
341
|
+
raise DBus.error("org.freedesktop.DBus.Error.PropertyReadOnly"),
|
342
|
+
"Property '#{interface_name}.#{property_name}' (on object '#{@path}') is not writable"
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
dbus_method :GetAll, "in interface_name:s, out value:a{sv}" do |interface_name|
|
347
|
+
interface = intfs[interface_name]
|
348
|
+
if !interface
|
349
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
|
350
|
+
"Properties '#{interface_name}.*' (on object '#{@path}') not found: no such interface"
|
351
|
+
end
|
352
|
+
|
353
|
+
p_hash = {}
|
354
|
+
interface.properties.each do |p_name, property|
|
355
|
+
next unless property.readable?
|
356
|
+
|
357
|
+
ruby_name = property.ruby_name
|
358
|
+
begin
|
359
|
+
# D-Bus spec says:
|
360
|
+
# > If GetAll is called with a valid interface name for which some
|
361
|
+
# > properties are not accessible to the caller (for example, due
|
362
|
+
# > to per-property access control implemented in the service),
|
363
|
+
# > those properties should be silently omitted from the result
|
364
|
+
# > array.
|
365
|
+
# so we will silently omit properties that fail to read.
|
366
|
+
# Get'ting them individually will send DBus.Error
|
367
|
+
p_hash[p_name.to_s] = public_send(ruby_name)
|
368
|
+
rescue StandardError
|
369
|
+
DBus.logger.debug "Property '#{interface_name}.#{p_name}' (on object '#{@path}')" \
|
370
|
+
" has raised during GetAll, omitting it"
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
[p_hash]
|
375
|
+
end
|
376
|
+
|
377
|
+
dbus_signal :PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
|
378
|
+
end
|
128
379
|
end
|
129
380
|
end
|
data/lib/dbus/proxy_object.rb
CHANGED
@@ -44,6 +44,7 @@ module DBus
|
|
44
44
|
end
|
45
45
|
|
46
46
|
# Returns the interfaces of the object.
|
47
|
+
# @return [Array<String>] names of the interfaces
|
47
48
|
def interfaces
|
48
49
|
introspect unless introspected
|
49
50
|
@interfaces.keys
|
@@ -129,6 +130,11 @@ module DBus
|
|
129
130
|
####################################################
|
130
131
|
private
|
131
132
|
|
133
|
+
# rubocop:disable Style/MethodMissing
|
134
|
+
# but https://github.com/rubocop-hq/ruby-style-guide#no-method-missing
|
135
|
+
# and http://blog.marc-andre.ca/2010/11/15/methodmissing-politely/
|
136
|
+
# have a point to be investigated
|
137
|
+
|
132
138
|
# Handles all unkown methods, mostly to route method calls to the
|
133
139
|
# default interface.
|
134
140
|
def method_missing(name, *args, &reply_handler)
|
@@ -150,5 +156,6 @@ module DBus
|
|
150
156
|
raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
|
151
157
|
end
|
152
158
|
end
|
159
|
+
# rubocop:enable Style/MethodMissing
|
153
160
|
end # class ProxyObject
|
154
161
|
end
|
data/lib/dbus/type.rb
CHANGED
@@ -9,6 +9,24 @@
|
|
9
9
|
# See the file "COPYING" for the exact licensing terms.
|
10
10
|
|
11
11
|
module DBus
|
12
|
+
# Like a {Signature} but containing only a single complete type.
|
13
|
+
#
|
14
|
+
# For documentation purposes only.
|
15
|
+
class SingleCompleteType < String; end
|
16
|
+
|
17
|
+
# Zero or more {SingleCompleteType}s; its own type code is "g".
|
18
|
+
# For example "ssv" for a method taking two Strings and a Variant/
|
19
|
+
#
|
20
|
+
# For documentation purposes only.
|
21
|
+
class Signature < String; end
|
22
|
+
|
23
|
+
# Similar to {Signature} but for {DBus::Object.define_method},
|
24
|
+
# contains names and direction of the parameters.
|
25
|
+
# For example "in query:s, in case_sensitive:b, out results:ao".
|
26
|
+
#
|
27
|
+
# For documentation purposes only.
|
28
|
+
class Prototype < String; end
|
29
|
+
|
12
30
|
# = D-Bus type module
|
13
31
|
#
|
14
32
|
# This module containts the constants of the types specified in the D-Bus
|
data/ruby-dbus.gemspec
CHANGED
@@ -18,15 +18,17 @@ GEMSPEC = Gem::Specification.new do |s|
|
|
18
18
|
]
|
19
19
|
s.require_path = "lib"
|
20
20
|
|
21
|
-
s.required_ruby_version = ">= 2.
|
21
|
+
s.required_ruby_version = ">= 2.1.0"
|
22
|
+
|
23
|
+
s.add_dependency "rexml"
|
22
24
|
|
23
25
|
# This is optional
|
24
26
|
# s.add_runtime_dependency "nokogiri"
|
25
27
|
|
26
|
-
s.add_development_dependency "coveralls"
|
27
28
|
s.add_development_dependency "packaging_rake_tasks"
|
28
29
|
s.add_development_dependency "rake"
|
29
30
|
s.add_development_dependency "rspec", "~> 3"
|
30
|
-
s.add_development_dependency "rubocop", "= 0.
|
31
|
+
s.add_development_dependency "rubocop", "= 0.50.0"
|
31
32
|
s.add_development_dependency "simplecov"
|
33
|
+
s.add_development_dependency "simplecov-lcov"
|
32
34
|
end
|
data/spec/main_loop_spec.rb
CHANGED
data/spec/property_spec.rb
CHANGED
@@ -4,8 +4,8 @@ require "dbus"
|
|
4
4
|
|
5
5
|
describe "PropertyTest" do
|
6
6
|
before(:each) do
|
7
|
-
session_bus = DBus::ASessionBus.new
|
8
|
-
@svc = session_bus.service("org.ruby.service")
|
7
|
+
@session_bus = DBus::ASessionBus.new
|
8
|
+
@svc = @session_bus.service("org.ruby.service")
|
9
9
|
@obj = @svc.object("/org/ruby/MyInstance")
|
10
10
|
@iface = @obj["org.ruby.SampleInterface"]
|
11
11
|
end
|
@@ -21,6 +21,10 @@ describe "PropertyTest" do
|
|
21
21
|
expect(iface["ReadMe"]).to eq("READ ME")
|
22
22
|
end
|
23
23
|
|
24
|
+
it "gets an error when reading a property whose implementation raises" do
|
25
|
+
expect { @iface["Explosive"] }.to raise_error(DBus::Error, /Something failed/)
|
26
|
+
end
|
27
|
+
|
24
28
|
it "tests property nonreading" do
|
25
29
|
expect { @iface["WriteMe"] }.to raise_error(DBus::Error, /not readable/)
|
26
30
|
end
|
@@ -31,7 +35,7 @@ describe "PropertyTest" do
|
|
31
35
|
end
|
32
36
|
|
33
37
|
# https://github.com/mvidner/ruby-dbus/pull/19
|
34
|
-
it "tests service select timeout" do
|
38
|
+
it "tests service select timeout", slow: true do
|
35
39
|
@iface["ReadOrWriteMe"] = "VALUE"
|
36
40
|
expect(@iface["ReadOrWriteMe"]).to eq("VALUE")
|
37
41
|
# wait for the service to become idle
|
@@ -64,4 +68,50 @@ describe "PropertyTest" do
|
|
64
68
|
it "tests unknown property writing" do
|
65
69
|
expect { @iface["Spoon"] = "FPRK" }.to raise_error(DBus::Error, /not found/)
|
66
70
|
end
|
71
|
+
|
72
|
+
it "errors for a property on an unknown interface" do
|
73
|
+
# our idiomatic way would error out on interface lookup already,
|
74
|
+
# so do it the low level way
|
75
|
+
prop_if = @obj[DBus::PROPERTY_INTERFACE]
|
76
|
+
expect { prop_if.Get("org.ruby.NoSuchInterface", "SomeProperty") }.to raise_error(DBus::Error) do |e|
|
77
|
+
expect(e.name).to match(/UnknownProperty/)
|
78
|
+
expect(e.message).to match(/no such interface/)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it "errors for GetAll on an unknown interface" do
|
83
|
+
# no idiomatic way?
|
84
|
+
# so do it the low level way
|
85
|
+
prop_if = @obj[DBus::PROPERTY_INTERFACE]
|
86
|
+
expect { prop_if.GetAll("org.ruby.NoSuchInterface") }.to raise_error(DBus::Error) do |e|
|
87
|
+
expect(e.name).to match(/UnknownProperty/)
|
88
|
+
expect(e.message).to match(/no such interface/)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it "receives a PropertiesChanged signal", slow: true do
|
93
|
+
received = {}
|
94
|
+
|
95
|
+
# TODO: for client side, provide a helper on_properties_changed,
|
96
|
+
# or automate it even more in ProxyObject, ProxyObjectInterface
|
97
|
+
prop_if = @obj[DBus::PROPERTY_INTERFACE]
|
98
|
+
prop_if.on_signal("PropertiesChanged") do |_interface_name, changed_props, _invalidated_props|
|
99
|
+
received.merge!(changed_props)
|
100
|
+
end
|
101
|
+
|
102
|
+
@iface["ReadOrWriteMe"] = "VALUE"
|
103
|
+
|
104
|
+
# loop to process the signal. complicated :-( see signal_spec.rb
|
105
|
+
loop = DBus::Main.new
|
106
|
+
loop << @session_bus
|
107
|
+
quitter = Thread.new do
|
108
|
+
sleep 1
|
109
|
+
loop.quit
|
110
|
+
end
|
111
|
+
loop.run
|
112
|
+
# quitter has told loop.run to quit
|
113
|
+
quitter.join
|
114
|
+
|
115
|
+
expect(received["ReadOrWriteMe"]).to eq("VALUE")
|
116
|
+
end
|
67
117
|
end
|
data/spec/service_newapi.rb
CHANGED
@@ -59,6 +59,26 @@ class Test < DBus::Object
|
|
59
59
|
dbus_method :mirror_byte_array, "in bytes:ay, out mirrored:ay" do |bytes|
|
60
60
|
[bytes]
|
61
61
|
end
|
62
|
+
|
63
|
+
# Properties:
|
64
|
+
# ReadMe:string, returns "READ ME" at first, then what WriteMe received
|
65
|
+
# WriteMe:string
|
66
|
+
# ReadOrWriteMe:string, returns "READ OR WRITE ME" at first
|
67
|
+
dbus_attr_accessor :read_or_write_me, "s"
|
68
|
+
dbus_attr_reader :read_me, "s"
|
69
|
+
|
70
|
+
def write_me=(value)
|
71
|
+
@read_me = value
|
72
|
+
end
|
73
|
+
dbus_writer :write_me, "s"
|
74
|
+
|
75
|
+
dbus_attr_writer :password, "s"
|
76
|
+
|
77
|
+
# a property that raises when client tries to read it
|
78
|
+
def explosive
|
79
|
+
raise "Something failed"
|
80
|
+
end
|
81
|
+
dbus_reader :explosive, "s"
|
62
82
|
end
|
63
83
|
|
64
84
|
# closing and reopening the same interface
|
@@ -118,72 +138,6 @@ class Test < DBus::Object
|
|
118
138
|
dbus_signal :LongTaskStart
|
119
139
|
dbus_signal :LongTaskEnd
|
120
140
|
end
|
121
|
-
|
122
|
-
# Properties:
|
123
|
-
# ReadMe:string, returns "READ ME" at first, then what WriteMe received
|
124
|
-
# WriteMe:string
|
125
|
-
# ReadOrWriteMe:string, returns "READ OR WRITE ME" at first
|
126
|
-
dbus_interface PROPERTY_INTERFACE do
|
127
|
-
dbus_method :Get, "in interface:s, in propname:s, out value:v" do |interface, propname|
|
128
|
-
unless interface == INTERFACE
|
129
|
-
raise DBus.error("org.freedesktop.DBus.Error.UnknownInterface"),
|
130
|
-
"Interface '#{interface}' not found on object '#{@path}'"
|
131
|
-
end
|
132
|
-
|
133
|
-
case propname
|
134
|
-
when "ReadMe"
|
135
|
-
[@read_me]
|
136
|
-
when "ReadOrWriteMe"
|
137
|
-
[@read_or_write_me]
|
138
|
-
when "WriteMe"
|
139
|
-
raise DBus.error("org.freedesktop.DBus.Error.InvalidArgs"),
|
140
|
-
"Property '#{interface}.#{propname}' (on object '#{@path}') is not readable"
|
141
|
-
else
|
142
|
-
# what should happen for unknown properties
|
143
|
-
# plasma: InvalidArgs (propname), UnknownInterface (interface)
|
144
|
-
raise DBus.error("org.freedesktop.DBus.Error.InvalidArgs"),
|
145
|
-
"Property '#{interface}.#{propname}' not found on object '#{@path}'"
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
dbus_method :Set, "in interface:s, in propname:s, in value:v" do |interface, propname, value|
|
150
|
-
unless interface == INTERFACE
|
151
|
-
raise DBus.error("org.freedesktop.DBus.Error.UnknownInterface"),
|
152
|
-
"Interface '#{interface}' not found on object '#{@path}'"
|
153
|
-
end
|
154
|
-
|
155
|
-
case propname
|
156
|
-
when "ReadMe"
|
157
|
-
raise DBus.error("org.freedesktop.DBus.Error.InvalidArgs"),
|
158
|
-
"Property '#{interface}.#{propname}' (on object '#{@path}') is not writable"
|
159
|
-
when "ReadOrWriteMe"
|
160
|
-
@read_or_write_me = value
|
161
|
-
self.PropertiesChanged(interface, { propname => value }, [])
|
162
|
-
when "WriteMe"
|
163
|
-
@read_me = value
|
164
|
-
self.PropertiesChanged(interface, { "ReadMe" => value }, [])
|
165
|
-
else
|
166
|
-
raise DBus.error("org.freedesktop.DBus.Error.InvalidArgs"),
|
167
|
-
"Property '#{interface}.#{propname}' not found on object '#{@path}'"
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
dbus_method :GetAll, "in interface:s, out value:a{sv}" do |interface|
|
172
|
-
unless interface == INTERFACE
|
173
|
-
raise DBus.error("org.freedesktop.DBus.Error.UnknownInterface"),
|
174
|
-
"Interface '#{interface}' not found on object '#{@path}'"
|
175
|
-
end
|
176
|
-
|
177
|
-
[
|
178
|
-
{
|
179
|
-
"ReadMe" => @read_me,
|
180
|
-
"ReadOrWriteMe" => @read_or_write_me
|
181
|
-
}
|
182
|
-
]
|
183
|
-
end
|
184
|
-
|
185
|
-
dbus_signal :PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
|
186
|
-
end
|
187
141
|
end
|
188
142
|
|
189
143
|
class Derived < Test
|
data/spec/signal_spec.rb
CHANGED
@@ -29,7 +29,7 @@ describe "SignalHandlerTest" do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# testing for commit 017c83 (kkaempf)
|
32
|
-
it "tests overriding a handler" do
|
32
|
+
it "tests overriding a handler", slow: true do
|
33
33
|
DBus.logger.debug "Inside test_overriding_a_handler"
|
34
34
|
counter = 0
|
35
35
|
|
@@ -52,7 +52,7 @@ describe "SignalHandlerTest" do
|
|
52
52
|
expect(counter).to eq(1)
|
53
53
|
end
|
54
54
|
|
55
|
-
it "tests on signal overload" do
|
55
|
+
it "tests on signal overload", slow: true do
|
56
56
|
DBus.logger.debug "Inside test_on_signal_overload"
|
57
57
|
counter = 0
|
58
58
|
started = false
|
@@ -74,7 +74,7 @@ describe "SignalHandlerTest" do
|
|
74
74
|
expect { @intf.on_signal "to", "many", "yarrrrr!" }.to raise_error(ArgumentError)
|
75
75
|
end
|
76
76
|
|
77
|
-
it "is possible to add signal handlers from within handlers" do
|
77
|
+
it "is possible to add signal handlers from within handlers", slow: true do
|
78
78
|
ended = false
|
79
79
|
@intf.on_signal "LongTaskStart" do
|
80
80
|
@intf.on_signal "LongTaskEnd" do
|
data/spec/spec_helper.rb
CHANGED
@@ -2,8 +2,7 @@ coverage = if ENV["COVERAGE"]
|
|
2
2
|
ENV["COVERAGE"] == "true"
|
3
3
|
else
|
4
4
|
# heuristics: enable for interactive builds (but not in OBS)
|
5
|
-
|
6
|
-
ENV["DISPLAY"] || ENV["TRAVIS"]
|
5
|
+
ENV["DISPLAY"]
|
7
6
|
end
|
8
7
|
|
9
8
|
if coverage
|
@@ -15,11 +14,24 @@ if coverage
|
|
15
14
|
# do not cover the activesupport helpers
|
16
15
|
SimpleCov.add_filter "/core_ext/"
|
17
16
|
|
18
|
-
# use coveralls for on-line code coverage reporting at Travis CI
|
19
|
-
if ENV["TRAVIS"]
|
20
|
-
require "coveralls"
|
21
|
-
end
|
22
17
|
SimpleCov.start
|
18
|
+
|
19
|
+
# additionally use the LCOV format for on-line code coverage reporting at CI
|
20
|
+
if ENV["COVERAGE_LCOV"] == "true"
|
21
|
+
require "simplecov-lcov"
|
22
|
+
|
23
|
+
SimpleCov::Formatter::LcovFormatter.config do |c|
|
24
|
+
c.report_with_single_file = true
|
25
|
+
# this is the default Coveralls GitHub Action location
|
26
|
+
# https://github.com/marketplace/actions/coveralls-github-action
|
27
|
+
c.single_report_path = "coverage/lcov.info"
|
28
|
+
end
|
29
|
+
|
30
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
31
|
+
SimpleCov::Formatter::HTMLFormatter,
|
32
|
+
SimpleCov::Formatter::LcovFormatter
|
33
|
+
]
|
34
|
+
end
|
23
35
|
end
|
24
36
|
|
25
37
|
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
@@ -25,4 +25,8 @@
|
|
25
25
|
Instead, lower some so that we can test resource leaks. -->
|
26
26
|
<limit name="max_match_rules_per_connection">50</limit><!-- was 512 -->
|
27
27
|
|
28
|
+
<!--
|
29
|
+
dbus-daemon[1700]: [session uid=1001 pid=1700] Unable to set up new connection: Failed to get AppArmor confinement information of socket peer: Protocol not available
|
30
|
+
-->
|
31
|
+
<apparmor mode="disabled"/>
|
28
32
|
</busconfig>
|