ruby-dbus 0.22.1 → 0.23.0.beta1
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 +13 -0
- data/VERSION +1 -1
- data/doc/Reference.md +4 -4
- data/examples/service/complex-property.rb +2 -2
- data/examples/service/service_newapi.rb +2 -2
- data/lib/dbus/bus.rb +50 -302
- data/lib/dbus/logger.rb +3 -2
- data/lib/dbus/main.rb +66 -0
- data/lib/dbus/message.rb +6 -8
- data/lib/dbus/node_tree.rb +105 -0
- data/lib/dbus/object.rb +16 -7
- data/lib/dbus/object_manager.rb +2 -3
- data/lib/dbus/object_server.rb +119 -0
- data/lib/dbus/proxy_object.rb +4 -4
- data/lib/dbus/proxy_service.rb +95 -0
- data/lib/dbus.rb +9 -8
- data/spec/bus_connection_spec.rb +81 -0
- data/spec/coverage_helper.rb +39 -0
- data/spec/main_loop_spec.rb +14 -0
- 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} +22 -10
- data/spec/node_spec.rb +1 -5
- data/spec/object_server_spec.rb +104 -0
- data/spec/object_spec.rb +30 -0
- data/spec/{bus_driver_spec.rb → proxy_service_spec.rb} +1 -1
- data/spec/spec_helper.rb +9 -45
- data/spec/thread_safety_spec.rb +9 -11
- data/spec/tools/dbus-launch-simple +4 -1
- data/spec/tools/test_env +26 -6
- metadata +18 -9
- data/spec/server_spec.rb +0 -55
- data/spec/service_spec.rb +0 -18
- data/spec/tools/test_server +0 -39
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
5
|
+
# Copyright (C) 2023 Martin Vidner
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
10
|
+
# See the file "COPYING" for the exact licensing terms.
|
11
|
+
|
12
|
+
module DBus
|
13
|
+
# Has a tree of {Node}s, refering to {Object}s or to {ProxyObject}s.
|
14
|
+
class NodeTree
|
15
|
+
# @return [Node]
|
16
|
+
attr_reader :root
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@root = Node.new("/")
|
20
|
+
end
|
21
|
+
|
22
|
+
# Get the object node corresponding to the given *path*.
|
23
|
+
# @param path [ObjectPath]
|
24
|
+
# @param create [Boolean] if true, the the {Node}s in the path are created
|
25
|
+
# if they do not already exist.
|
26
|
+
# @return [Node,nil]
|
27
|
+
def get_node(path, create: false)
|
28
|
+
n = @root
|
29
|
+
path.sub(%r{^/}, "").split("/").each do |elem|
|
30
|
+
if !(n[elem])
|
31
|
+
return nil if !create
|
32
|
+
|
33
|
+
n[elem] = Node.new(elem)
|
34
|
+
end
|
35
|
+
n = n[elem]
|
36
|
+
end
|
37
|
+
n
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# = Object path node class
|
42
|
+
#
|
43
|
+
# Class representing a node on an object path.
|
44
|
+
class Node < Hash
|
45
|
+
# @return [DBus::Object,DBus::ProxyObject,nil]
|
46
|
+
# The D-Bus object contained by the node.
|
47
|
+
attr_accessor :object
|
48
|
+
|
49
|
+
# The name of the node.
|
50
|
+
# @return [String] the last component of its object path, or "/"
|
51
|
+
attr_reader :name
|
52
|
+
|
53
|
+
# Create a new node with a given _name_.
|
54
|
+
def initialize(name)
|
55
|
+
super()
|
56
|
+
@name = name
|
57
|
+
@object = nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return an XML string representation of the node.
|
61
|
+
# It is shallow, not recursing into subnodes
|
62
|
+
# @param node_opath [String]
|
63
|
+
def to_xml(node_opath)
|
64
|
+
xml = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
65
|
+
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
66
|
+
'
|
67
|
+
xml += "<node name=\"#{node_opath}\">\n"
|
68
|
+
each_key do |k|
|
69
|
+
xml += " <node name=\"#{k}\" />\n"
|
70
|
+
end
|
71
|
+
@object&.intfs&.each_value do |v|
|
72
|
+
xml += v.to_xml
|
73
|
+
end
|
74
|
+
xml += "</node>"
|
75
|
+
xml
|
76
|
+
end
|
77
|
+
|
78
|
+
# Return inspect information of the node.
|
79
|
+
def inspect
|
80
|
+
# Need something here
|
81
|
+
"<DBus::Node #{sub_inspect}>"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return instance inspect information, used by Node#inspect.
|
85
|
+
def sub_inspect
|
86
|
+
s = ""
|
87
|
+
if !@object.nil?
|
88
|
+
s += format("%x ", @object.object_id)
|
89
|
+
end
|
90
|
+
contents_sub_inspect = keys
|
91
|
+
.map { |k| "#{k} => #{self[k].sub_inspect}" }
|
92
|
+
.join(",")
|
93
|
+
"#{s}{#{contents_sub_inspect}}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# All objects (not paths) under this path (except itself).
|
97
|
+
# @return [Array<DBus::Object>]
|
98
|
+
def descendant_objects
|
99
|
+
children_objects = values.map(&:object).compact
|
100
|
+
descendants = values.map(&:descendant_objects)
|
101
|
+
flat_descendants = descendants.reduce([], &:+)
|
102
|
+
children_objects + flat_descendants
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/dbus/object.rb
CHANGED
@@ -26,20 +26,29 @@ module DBus
|
|
26
26
|
my_class_attribute :intfs
|
27
27
|
self.intfs = {}
|
28
28
|
|
29
|
-
#
|
30
|
-
|
29
|
+
# @return [Connection] the connection the object is exported by
|
30
|
+
attr_reader :connection
|
31
31
|
|
32
32
|
@@cur_intf = nil # Interface
|
33
33
|
@@intfs_mutex = Mutex.new
|
34
34
|
|
35
35
|
# Create a new object with a given _path_.
|
36
|
-
# Use
|
36
|
+
# Use ObjectServer#export to export it.
|
37
37
|
def initialize(path)
|
38
38
|
@path = path
|
39
|
-
@
|
39
|
+
@connection = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param connection [Connection] the connection the object is exported by
|
43
|
+
def connection=(connection)
|
44
|
+
@connection = connection
|
45
|
+
# deprecated, keeping @service for compatibility
|
46
|
+
@service = connection&.object_server
|
40
47
|
end
|
41
48
|
|
42
49
|
# Dispatch a message _msg_ to call exported methods
|
50
|
+
# @param msg [Message] only METHOD_CALLS do something
|
51
|
+
# @api private
|
43
52
|
def dispatch(msg)
|
44
53
|
case msg.message_type
|
45
54
|
when Message::METHOD_CALL
|
@@ -69,7 +78,7 @@ module DBus
|
|
69
78
|
dbus_msg_exc = msg.annotate_exception(e)
|
70
79
|
reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
|
71
80
|
end
|
72
|
-
@
|
81
|
+
@connection.message_queue.push(reply)
|
73
82
|
end
|
74
83
|
end
|
75
84
|
|
@@ -307,7 +316,7 @@ module DBus
|
|
307
316
|
# @param sig [Signal]
|
308
317
|
# @param args arguments for the signal
|
309
318
|
def emit(intf, sig, *args)
|
310
|
-
@
|
319
|
+
@connection.emit(nil, self, intf, sig, *args)
|
311
320
|
end
|
312
321
|
|
313
322
|
# Defines a signal for the object with a given name _sym_ and _prototype_.
|
@@ -316,7 +325,7 @@ module DBus
|
|
316
325
|
|
317
326
|
cur_intf = @@cur_intf
|
318
327
|
signal = Signal.new(sym.to_s).from_prototype(prototype)
|
319
|
-
cur_intf.define(
|
328
|
+
cur_intf.define(signal)
|
320
329
|
|
321
330
|
# ::Module#define_method(name) { body }
|
322
331
|
define_method(sym.to_s) do |*args|
|
data/lib/dbus/object_manager.rb
CHANGED
@@ -14,7 +14,7 @@ module DBus
|
|
14
14
|
# {https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
|
15
15
|
# org.freedesktop.DBus.ObjectManager}.
|
16
16
|
#
|
17
|
-
# {
|
17
|
+
# {ObjectServer#export} and {ObjectServer#unexport} will look for an ObjectManager
|
18
18
|
# parent in the path hierarchy. If found, it will emit InterfacesAdded
|
19
19
|
# or InterfacesRemoved, as appropriate.
|
20
20
|
module ObjectManager
|
@@ -23,8 +23,7 @@ module DBus
|
|
23
23
|
# @return [Hash{ObjectPath => Hash{String => Hash{String => Data::Base}}}]
|
24
24
|
# object -> interface -> property -> value
|
25
25
|
def managed_objects
|
26
|
-
|
27
|
-
descendant_objects = @service.descendants_for(path)
|
26
|
+
descendant_objects = connection.object_server.descendants_for(path)
|
28
27
|
descendant_objects.each_with_object({}) do |obj, hash|
|
29
28
|
hash[obj.path] = obj.interfaces_and_properties
|
30
29
|
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
5
|
+
# Copyright (C) 2023 Martin Vidner
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
10
|
+
# See the file "COPYING" for the exact licensing terms.
|
11
|
+
|
12
|
+
require_relative "node_tree"
|
13
|
+
|
14
|
+
module DBus
|
15
|
+
# The part of a {Connection} that can export {DBus::Object}s to provide
|
16
|
+
# services to clients.
|
17
|
+
#
|
18
|
+
# Note that an ObjectServer does not have a name. Typically a {Connection}
|
19
|
+
# has one well known name, but can have none or more.
|
20
|
+
#
|
21
|
+
# Formerly this class was intermixed with {ProxyService} as Service.
|
22
|
+
#
|
23
|
+
# @example Usage
|
24
|
+
# bus = DBus.session_bus
|
25
|
+
# obj = DBus::Object.new("/path") # a subclass more likely
|
26
|
+
# bus.object_server.export(obj)
|
27
|
+
# bus.request_name("org.example.Test")
|
28
|
+
class ObjectServer < NodeTree
|
29
|
+
# @return [Connection] The connection we're using.
|
30
|
+
attr_reader :connection
|
31
|
+
|
32
|
+
def initialize(connection)
|
33
|
+
@connection = connection
|
34
|
+
super()
|
35
|
+
end
|
36
|
+
|
37
|
+
# Retrieves an object at the given _path_
|
38
|
+
# @param path [ObjectPath]
|
39
|
+
# @return [DBus::Object]
|
40
|
+
def object(path)
|
41
|
+
node = get_node(path, create: false)
|
42
|
+
node&.object
|
43
|
+
end
|
44
|
+
alias [] object
|
45
|
+
|
46
|
+
# Export an object
|
47
|
+
# @param obj [DBus::Object]
|
48
|
+
def export(obj)
|
49
|
+
node = get_node(obj.path, create: true)
|
50
|
+
# TODO: clarify that this is indeed the right thing, and document
|
51
|
+
# raise "At #{obj.path} there is already an object #{node.object.inspect}" if node.object
|
52
|
+
|
53
|
+
node.object = obj
|
54
|
+
|
55
|
+
obj.connection = @connection
|
56
|
+
object_manager_for(obj)&.object_added(obj)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Undo exporting an object _obj_.
|
60
|
+
# Raises ArgumentError if it is not a DBus::Object.
|
61
|
+
# Returns the object, or false if _obj_ was not exported.
|
62
|
+
# @param obj [DBus::Object]
|
63
|
+
def unexport(obj)
|
64
|
+
raise ArgumentError, "Expecting a DBus::Object argument" unless obj.is_a?(DBus::Object)
|
65
|
+
|
66
|
+
last_path_separator_idx = obj.path.rindex("/")
|
67
|
+
parent_path = obj.path[1..last_path_separator_idx - 1]
|
68
|
+
node_name = obj.path[last_path_separator_idx + 1..-1]
|
69
|
+
|
70
|
+
parent_node = get_node(parent_path, create: false)
|
71
|
+
return false unless parent_node
|
72
|
+
|
73
|
+
object_manager_for(obj)&.object_removed(obj)
|
74
|
+
obj.connection = nil
|
75
|
+
parent_node.delete(node_name).object
|
76
|
+
end
|
77
|
+
|
78
|
+
# Find the (closest) parent of *object*
|
79
|
+
# implementing the ObjectManager interface, or nil
|
80
|
+
# @return [DBus::Object,nil]
|
81
|
+
def object_manager_for(object)
|
82
|
+
path = object.path
|
83
|
+
node_chain = get_node_chain(path)
|
84
|
+
om_node = node_chain.reverse_each.find do |node|
|
85
|
+
node.object&.is_a? DBus::ObjectManager
|
86
|
+
end
|
87
|
+
om_node&.object
|
88
|
+
end
|
89
|
+
|
90
|
+
# All objects (not paths) under this path (except itself).
|
91
|
+
# @param path [ObjectPath]
|
92
|
+
# @return [Array<DBus::Object>]
|
93
|
+
# @raise ArgumentError if the *path* does not exist
|
94
|
+
def descendants_for(path)
|
95
|
+
node = get_node(path, create: false)
|
96
|
+
raise ArgumentError, "Object path #{path} doesn't exist" if node.nil?
|
97
|
+
|
98
|
+
node.descendant_objects
|
99
|
+
end
|
100
|
+
|
101
|
+
#########
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
#########
|
106
|
+
|
107
|
+
# @param path [ObjectPath] a path that must exist
|
108
|
+
# @return [Array<Node>] nodes from the root to the leaf
|
109
|
+
def get_node_chain(path)
|
110
|
+
n = @root
|
111
|
+
result = [n]
|
112
|
+
path.sub(%r{^/}, "").split("/").each do |elem|
|
113
|
+
n = n[elem]
|
114
|
+
result.push(n)
|
115
|
+
end
|
116
|
+
result
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/dbus/proxy_object.rb
CHANGED
@@ -10,20 +10,20 @@
|
|
10
10
|
# See the file "COPYING" for the exact licensing terms.
|
11
11
|
|
12
12
|
module DBus
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# Class representing a remote object in an external application.
|
13
|
+
# Represents a remote object in an external application.
|
16
14
|
# Typically, calling a method on an instance of a ProxyObject sends a message
|
17
|
-
# over the bus so that the method is executed remotely on the
|
15
|
+
# over the bus so that the method is executed remotely on the corresponding
|
18
16
|
# object.
|
19
17
|
class ProxyObject
|
20
18
|
# The names of direct subnodes of the object in the tree.
|
21
19
|
attr_accessor :subnodes
|
22
20
|
# Flag determining whether the object has been introspected.
|
21
|
+
# @return [Boolean]
|
23
22
|
attr_accessor :introspected
|
24
23
|
# The (remote) destination of the object.
|
25
24
|
attr_reader :destination
|
26
25
|
# The path to the object.
|
26
|
+
# @return [ObjectPath]
|
27
27
|
attr_reader :path
|
28
28
|
# The bus the object is reachable via.
|
29
29
|
attr_reader :bus
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
5
|
+
# Copyright (C) 2023 Martin Vidner
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
10
|
+
# See the file "COPYING" for the exact licensing terms.
|
11
|
+
|
12
|
+
require_relative "node_tree"
|
13
|
+
|
14
|
+
module DBus
|
15
|
+
# Used by clients to represent a named service on the other side of the bus.
|
16
|
+
#
|
17
|
+
# Formerly this class was intermixed with {ObjectServer} as Service.
|
18
|
+
#
|
19
|
+
# @example Usage
|
20
|
+
# svc = DBus.system_bus["org.freedesktop.machine1"]
|
21
|
+
# manager = svc["/org/freedesktop/machine1"]
|
22
|
+
# p manager.ListImages
|
23
|
+
class ProxyService < NodeTree
|
24
|
+
# @return [BusName,nil] The service name.
|
25
|
+
# May be nil for peer connections
|
26
|
+
attr_reader :name
|
27
|
+
# @return [Connection] The connection we're using.
|
28
|
+
attr_reader :connection
|
29
|
+
|
30
|
+
def initialize(name, connection)
|
31
|
+
@name = BusName.new(name)
|
32
|
+
@connection = connection
|
33
|
+
super()
|
34
|
+
end
|
35
|
+
|
36
|
+
# Determine whether the service name already exists.
|
37
|
+
def exists?
|
38
|
+
bus = connection # TODO: raise a better error if this is a peer connection
|
39
|
+
bus.proxy.ListNames[0].member?(@name)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Perform an introspection on all the objects on the service
|
43
|
+
# (starting recursively from the root).
|
44
|
+
def introspect
|
45
|
+
raise NotImplementedError if block_given?
|
46
|
+
|
47
|
+
rec_introspect(@root, "/")
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
# Retrieves an object at the given _path_.
|
52
|
+
# @param path [ObjectPath]
|
53
|
+
# @return [ProxyObject]
|
54
|
+
def [](path)
|
55
|
+
object(path, api: ApiOptions::A1)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Retrieves an object at the given _path_
|
59
|
+
# whose methods always return an array.
|
60
|
+
# @param path [ObjectPath]
|
61
|
+
# @param api [ApiOptions]
|
62
|
+
# @return [ProxyObject]
|
63
|
+
def object(path, api: ApiOptions::A0)
|
64
|
+
node = get_node(path, create: true)
|
65
|
+
if node.object.nil? || node.object.api != api
|
66
|
+
node.object = ProxyObject.new(
|
67
|
+
@connection, @name, path,
|
68
|
+
api: api
|
69
|
+
)
|
70
|
+
end
|
71
|
+
node.object
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Perform a recursive retrospection on the given current _node_
|
77
|
+
# on the given _path_.
|
78
|
+
def rec_introspect(node, path)
|
79
|
+
xml = bus.introspect_data(@name, path)
|
80
|
+
intfs, subnodes = IntrospectXMLParser.new(xml).parse
|
81
|
+
subnodes.each do |nodename|
|
82
|
+
subnode = node[nodename] = Node.new(nodename)
|
83
|
+
subpath = if path == "/"
|
84
|
+
"/#{nodename}"
|
85
|
+
else
|
86
|
+
"#{path}/#{nodename}"
|
87
|
+
end
|
88
|
+
rec_introspect(subnode, subpath)
|
89
|
+
end
|
90
|
+
return if intfs.empty?
|
91
|
+
|
92
|
+
node.object = ProxyObjectFactory.new(xml, @connection, @name, path).build
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/dbus.rb
CHANGED
@@ -11,17 +11,14 @@
|
|
11
11
|
# See the file "COPYING" for the exact licensing terms.
|
12
12
|
|
13
13
|
module DBus
|
14
|
-
#
|
14
|
+
# Protocol character signifying big endianness.
|
15
15
|
BIG_END = "B"
|
16
|
-
#
|
16
|
+
# Protocol character signifying little endianness.
|
17
17
|
LIL_END = "l"
|
18
18
|
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
else
|
23
|
-
BIG_END
|
24
|
-
end
|
19
|
+
# Protocol character signifying the host's endianness.
|
20
|
+
# "S": unpack as uint16, native endian
|
21
|
+
HOST_END = { 1 => BIG_END, 256 => LIL_END }.fetch("\x00\x01".unpack1("S"))
|
25
22
|
end
|
26
23
|
# ^ That's because dbus/message needs HOST_END early
|
27
24
|
|
@@ -34,17 +31,21 @@ require_relative "dbus/emits_changed_signal"
|
|
34
31
|
require_relative "dbus/error"
|
35
32
|
require_relative "dbus/introspect"
|
36
33
|
require_relative "dbus/logger"
|
34
|
+
require_relative "dbus/main"
|
37
35
|
require_relative "dbus/marshall"
|
38
36
|
require_relative "dbus/matchrule"
|
39
37
|
require_relative "dbus/message"
|
40
38
|
require_relative "dbus/message_queue"
|
39
|
+
require_relative "dbus/node_tree"
|
41
40
|
require_relative "dbus/object"
|
42
41
|
require_relative "dbus/object_manager"
|
43
42
|
require_relative "dbus/object_path"
|
43
|
+
require_relative "dbus/object_server"
|
44
44
|
require_relative "dbus/platform"
|
45
45
|
require_relative "dbus/proxy_object"
|
46
46
|
require_relative "dbus/proxy_object_factory"
|
47
47
|
require_relative "dbus/proxy_object_interface"
|
48
|
+
require_relative "dbus/proxy_service"
|
48
49
|
require_relative "dbus/raw_message"
|
49
50
|
require_relative "dbus/type"
|
50
51
|
require_relative "dbus/xml"
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "spec_helper"
|
5
|
+
require "dbus"
|
6
|
+
|
7
|
+
describe DBus::BusConnection do
|
8
|
+
let(:bus) { DBus::ASessionBus.new }
|
9
|
+
|
10
|
+
# deprecated method
|
11
|
+
describe "#request_service", tag_bus: true, tag_deprecated: true do
|
12
|
+
context "when the name request succeeds" do
|
13
|
+
# Formerly it returned Service, now ObjectServer takes its role.
|
14
|
+
# Replacement: server = bus.object_server; bus.request_name(name)
|
15
|
+
it "returns something which can export objects" do
|
16
|
+
name = "org.rubygems.ruby_dbus.RequestServiceTest"
|
17
|
+
server = bus.request_service(name)
|
18
|
+
expect(server).to respond_to(:export)
|
19
|
+
bus.proxy.ReleaseName(name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when the name is taken already", tag_service: true do
|
24
|
+
# formerly it returned Service, now ObjectServer takes its role
|
25
|
+
it "raises NameRequestError... too late" do
|
26
|
+
name = "org.ruby.service"
|
27
|
+
expect do
|
28
|
+
bus.request_service(name)
|
29
|
+
_unrelated_call = bus.proxy.GetId.first
|
30
|
+
end.to raise_error(DBus::Connection::NameRequestError)
|
31
|
+
# The call fails but it means we did not get the name RIGHT AWAY
|
32
|
+
# but we are still queued to get it as soon as the current owner
|
33
|
+
# gives it up.
|
34
|
+
# So even now we have to the bus to remove us from the queue
|
35
|
+
bus.proxy.ReleaseName(name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when we're not allowed to own the name", tag_system_bus: true do
|
40
|
+
let(:bus) { DBus::ASystemBus.new }
|
41
|
+
xit "raises an error... too late" do
|
42
|
+
name = "org.rubygems.ruby_dbus.NotAllowedToOwnThisNameAnyway"
|
43
|
+
expect do
|
44
|
+
bus.request_service(name)
|
45
|
+
_unrelated_call = bus.proxy.GetId.first
|
46
|
+
end.to raise_error(DBus::Error, /not allowed to own the service/)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#request_name", tag_bus: true do
|
52
|
+
context "when the name request succeeds" do
|
53
|
+
it "returns something which can export objects" do
|
54
|
+
name = "org.rubygems.ruby_dbus.RequestNameTest"
|
55
|
+
expect { bus.request_name(name) }.to_not raise_error
|
56
|
+
bus.proxy.ReleaseName(name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when the name is taken already", tag_service: true do
|
61
|
+
# formerly it returned Service, now ObjectServer takes its role
|
62
|
+
it "raises NameRequestError" do
|
63
|
+
name = "org.ruby.service"
|
64
|
+
expect do
|
65
|
+
# flags: avoid getting the name sometime later, unexpectedly
|
66
|
+
bus.request_name(name, flags: DBus::Connection::NAME_FLAG_DO_NOT_QUEUE)
|
67
|
+
end.to raise_error(DBus::Connection::NameRequestError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when we're not allowed to own the name", tag_system_bus: true do
|
72
|
+
let(:bus) { DBus::ASystemBus.new }
|
73
|
+
xit "raises an error... too late" do
|
74
|
+
name = "org.rubygems.ruby_dbus.NotAllowedToOwnThisNameAnyway"
|
75
|
+
expect do
|
76
|
+
bus.request_name(name)
|
77
|
+
end.to raise_error(DBus::Error, /not allowed to own the service/)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
coverage = if ENV["COVERAGE"]
|
4
|
+
ENV["COVERAGE"] == "true"
|
5
|
+
else
|
6
|
+
# heuristics: enable for interactive builds (but not in OBS)
|
7
|
+
ENV["DISPLAY"]
|
8
|
+
end
|
9
|
+
|
10
|
+
if coverage
|
11
|
+
require "simplecov"
|
12
|
+
SimpleCov.root File.expand_path("..", __dir__)
|
13
|
+
|
14
|
+
# do not cover specs
|
15
|
+
SimpleCov.add_filter "_spec.rb"
|
16
|
+
# do not cover the activesupport helpers
|
17
|
+
SimpleCov.add_filter "/core_ext/"
|
18
|
+
# measure all if/else branches on a line
|
19
|
+
SimpleCov.enable_coverage :branch
|
20
|
+
|
21
|
+
SimpleCov.start
|
22
|
+
|
23
|
+
# additionally use the LCOV format for on-line code coverage reporting at CI
|
24
|
+
if ENV["COVERAGE_LCOV"] == "true"
|
25
|
+
require "simplecov-lcov"
|
26
|
+
|
27
|
+
SimpleCov::Formatter::LcovFormatter.config do |c|
|
28
|
+
c.report_with_single_file = true
|
29
|
+
# this is the default Coveralls GitHub Action location
|
30
|
+
# https://github.com/marketplace/actions/coveralls-github-action
|
31
|
+
c.single_report_path = "coverage/lcov.info"
|
32
|
+
end
|
33
|
+
|
34
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new [
|
35
|
+
SimpleCov::Formatter::HTMLFormatter,
|
36
|
+
SimpleCov::Formatter::LcovFormatter
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
data/spec/main_loop_spec.rb
CHANGED
@@ -5,6 +5,20 @@
|
|
5
5
|
require_relative "spec_helper"
|
6
6
|
require "dbus"
|
7
7
|
|
8
|
+
describe "DBus.logger" do
|
9
|
+
it "will log debug messages if $DEBUG is true" do
|
10
|
+
logger_old = DBus.logger
|
11
|
+
DBus.logger = nil
|
12
|
+
debug_old = $DEBUG
|
13
|
+
$DEBUG = true
|
14
|
+
|
15
|
+
DBus.logger.debug "this debug message will always be shown"
|
16
|
+
|
17
|
+
$DEBUG = debug_old
|
18
|
+
DBus.logger = logger_old
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
8
22
|
describe "MainLoopTest" do
|
9
23
|
before(:each) do
|
10
24
|
@session_bus = DBus::ASessionBus.new
|
@@ -0,0 +1,21 @@
|
|
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::Message do
|
10
|
+
describe "#marshall" do
|
11
|
+
it "raises when the object path is /org/freedesktop/DBus/Local" do
|
12
|
+
m = DBus::Message.new(DBus::Message::SIGNAL)
|
13
|
+
# the path is valid, it just must not be sent
|
14
|
+
m.path = DBus::ObjectPath.new("/org/freedesktop/DBus/Local")
|
15
|
+
m.interface = "org.example.spam"
|
16
|
+
m.member = "Spam"
|
17
|
+
|
18
|
+
expect { m.marshall }.to raise_error(RuntimeError, /Cannot send a message with the reserved path/)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "../coverage_helper"
|
5
|
+
SimpleCov.command_name "Cockpit Tests (#{Process.pid})" if Object.const_defined? "SimpleCov"
|
6
|
+
|
7
|
+
# find the library without external help
|
8
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
9
|
+
require "dbus"
|
10
|
+
|
11
|
+
SERVICE_NAME = "org.rubygems.ruby_dbus.DBusTests"
|
12
|
+
ROOT_OPATH = "/otree/frobber"
|
13
|
+
|
14
|
+
class DBusTests < DBus::Object
|
15
|
+
FROBBER_INTERFACE = "com.redhat.Cockpit.DBusTests.Frobber"
|
16
|
+
|
17
|
+
dbus_interface FROBBER_INTERFACE do
|
18
|
+
dbus_method :HelloWorld, "in greeting:s, out response:s" do |greeting|
|
19
|
+
# TODO: return the same thing as the original implementation
|
20
|
+
# and try substituting it?
|
21
|
+
[format("Word! You said `%s'. I'm Skeleton, btw!", greeting)]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
bus = DBus::SessionBus.instance
|
27
|
+
bus.object_server.export(DBusTests.new(ROOT_OPATH))
|
28
|
+
bus.request_name(SERVICE_NAME)
|
29
|
+
DBus::Main.new.tap { |m| m << bus }.run
|