ruby-dbus 0.22.1 → 0.23.0.beta2
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 +38 -0
- data/VERSION +1 -1
- data/doc/Reference.md +5 -5
- data/examples/no-bus/pulseaudio.rb +50 -0
- data/examples/service/complex-property.rb +2 -2
- data/examples/service/service_newapi.rb +2 -2
- data/lib/dbus/bus.rb +48 -694
- data/lib/dbus/connection.rb +350 -0
- 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 +28 -9
- data/lib/dbus/object_manager.rb +6 -3
- data/lib/dbus/object_server.rb +149 -0
- data/lib/dbus/org.freedesktop.DBus.xml +97 -0
- data/lib/dbus/proxy_object.rb +4 -4
- data/lib/dbus/proxy_service.rb +107 -0
- data/lib/dbus.rb +10 -8
- data/ruby-dbus.gemspec +1 -1
- data/spec/bus_connection_spec.rb +80 -0
- data/spec/connection_spec.rb +37 -0
- data/spec/coverage_helper.rb +39 -0
- data/spec/dbus_spec.rb +22 -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} +21 -10
- data/spec/node_spec.rb +1 -5
- data/spec/object_server_spec.rb +138 -0
- data/spec/object_spec.rb +46 -0
- data/spec/{bus_driver_spec.rb → proxy_service_spec.rb} +13 -8
- 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/dbus-limited-session.conf +3 -0
- data/spec/tools/test_env +26 -6
- metadata +24 -10
- data/spec/server_spec.rb +0 -55
- data/spec/service_spec.rb +0 -18
- data/spec/tools/test_server +0 -39
@@ -0,0 +1,149 @@
|
|
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,nil]
|
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
|
+
# @raise RuntimeError if there's already an exported object at the same path
|
49
|
+
def export(obj)
|
50
|
+
node = get_node(obj.path, create: true)
|
51
|
+
raise "At #{obj.path} there is already an object #{node.object.inspect}" if node.object
|
52
|
+
|
53
|
+
node.object = obj
|
54
|
+
|
55
|
+
obj.object_server = self
|
56
|
+
object_manager_for(obj)&.object_added(obj)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Undo exporting an object *obj_or_path*.
|
60
|
+
# Raises ArgumentError if it is not a DBus::Object.
|
61
|
+
# Returns the object, or false if _obj_ was not exported.
|
62
|
+
# @param obj_or_path [DBus::Object,ObjectPath,String] an object or a valid object path
|
63
|
+
def unexport(obj_or_path)
|
64
|
+
path = self.class.path_of(obj_or_path)
|
65
|
+
parent_path, _separator, node_name = path.rpartition("/")
|
66
|
+
|
67
|
+
parent_node = get_node(parent_path, create: false)
|
68
|
+
return false unless parent_node
|
69
|
+
|
70
|
+
node = if node_name == "" # path == "/"
|
71
|
+
parent_node
|
72
|
+
else
|
73
|
+
parent_node[node_name]
|
74
|
+
end
|
75
|
+
obj = node&.object
|
76
|
+
raise ArgumentError, "Cannot unexport, no object at #{path}" unless obj
|
77
|
+
|
78
|
+
object_manager_for(obj)&.object_removed(obj)
|
79
|
+
obj.object_server = nil
|
80
|
+
node.object = nil
|
81
|
+
|
82
|
+
# node can be deleted if
|
83
|
+
# - it has no children
|
84
|
+
# - it is not root
|
85
|
+
if node.empty? && !node.equal?(parent_node)
|
86
|
+
parent_node.delete(node_name)
|
87
|
+
end
|
88
|
+
|
89
|
+
obj
|
90
|
+
end
|
91
|
+
|
92
|
+
# Find the (closest) parent of *object*
|
93
|
+
# implementing the ObjectManager interface, or nil
|
94
|
+
# @return [DBus::Object,nil]
|
95
|
+
def object_manager_for(object)
|
96
|
+
path = object.path
|
97
|
+
node_chain = get_node_chain(path)
|
98
|
+
om_node = node_chain.reverse_each.find do |node|
|
99
|
+
node.object&.is_a? DBus::ObjectManager
|
100
|
+
end
|
101
|
+
om_node&.object
|
102
|
+
end
|
103
|
+
|
104
|
+
# All objects (not paths) under this path (except itself).
|
105
|
+
# @param path [ObjectPath]
|
106
|
+
# @return [Array<DBus::Object>]
|
107
|
+
# @raise ArgumentError if the *path* does not exist
|
108
|
+
def descendants_for(path)
|
109
|
+
node = get_node(path, create: false)
|
110
|
+
raise ArgumentError, "Object path #{path} doesn't exist" if node.nil?
|
111
|
+
|
112
|
+
node.descendant_objects
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param obj_or_path [DBus::Object,ObjectPath,String] an object or a valid object path
|
116
|
+
# @return [ObjectPath]
|
117
|
+
# @api private
|
118
|
+
def self.path_of(obj_or_path)
|
119
|
+
case obj_or_path
|
120
|
+
when ObjectPath
|
121
|
+
obj_or_path
|
122
|
+
when String
|
123
|
+
ObjectPath.new(obj_or_path)
|
124
|
+
when DBus::Object
|
125
|
+
obj_or_path.path
|
126
|
+
else
|
127
|
+
raise ArgumentError, "Expecting a DBus::Object argument or DBus::ObjectPath or String which parses as one"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#########
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
#########
|
136
|
+
|
137
|
+
# @param path [ObjectPath] a path that must exist
|
138
|
+
# @return [Array<Node>] nodes from the root to the leaf
|
139
|
+
def get_node_chain(path)
|
140
|
+
n = @root
|
141
|
+
result = [n]
|
142
|
+
path.sub(%r{^/}, "").split("/").each do |elem|
|
143
|
+
n = n[elem]
|
144
|
+
result.push(n)
|
145
|
+
end
|
146
|
+
result
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
2
|
+
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
3
|
+
<node>
|
4
|
+
<interface name="org.freedesktop.DBus.Introspectable">
|
5
|
+
<method name="Introspect">
|
6
|
+
<arg direction="out" type="s"/>
|
7
|
+
</method>
|
8
|
+
</interface>
|
9
|
+
<interface name="org.freedesktop.DBus">
|
10
|
+
<method name="Hello">
|
11
|
+
<arg direction="out" type="s"/>
|
12
|
+
</method>
|
13
|
+
<method name="RequestName">
|
14
|
+
<arg direction="in" type="s"/>
|
15
|
+
<arg direction="in" type="u"/>
|
16
|
+
<arg direction="out" type="u"/>
|
17
|
+
</method>
|
18
|
+
<method name="ReleaseName">
|
19
|
+
<arg direction="in" type="s"/>
|
20
|
+
<arg direction="out" type="u"/>
|
21
|
+
</method>
|
22
|
+
<method name="StartServiceByName">
|
23
|
+
<arg direction="in" type="s"/>
|
24
|
+
<arg direction="in" type="u"/>
|
25
|
+
<arg direction="out" type="u"/>
|
26
|
+
</method>
|
27
|
+
<method name="UpdateActivationEnvironment">
|
28
|
+
<arg direction="in" type="a{ss}"/>
|
29
|
+
</method>
|
30
|
+
<method name="NameHasOwner">
|
31
|
+
<arg direction="in" type="s"/>
|
32
|
+
<arg direction="out" type="b"/>
|
33
|
+
</method>
|
34
|
+
<method name="ListNames">
|
35
|
+
<arg direction="out" type="as"/>
|
36
|
+
</method>
|
37
|
+
<method name="ListActivatableNames">
|
38
|
+
<arg direction="out" type="as"/>
|
39
|
+
</method>
|
40
|
+
<method name="AddMatch">
|
41
|
+
<arg direction="in" type="s"/>
|
42
|
+
</method>
|
43
|
+
<method name="RemoveMatch">
|
44
|
+
<arg direction="in" type="s"/>
|
45
|
+
</method>
|
46
|
+
<method name="GetNameOwner">
|
47
|
+
<arg direction="in" type="s"/>
|
48
|
+
<arg direction="out" type="s"/>
|
49
|
+
</method>
|
50
|
+
<method name="ListQueuedOwners">
|
51
|
+
<arg direction="in" type="s"/>
|
52
|
+
<arg direction="out" type="as"/>
|
53
|
+
</method>
|
54
|
+
<method name="GetConnectionUnixUser">
|
55
|
+
<arg direction="in" type="s"/>
|
56
|
+
<arg direction="out" type="u"/>
|
57
|
+
</method>
|
58
|
+
<method name="GetConnectionUnixProcessID">
|
59
|
+
<arg direction="in" type="s"/>
|
60
|
+
<arg direction="out" type="u"/>
|
61
|
+
</method>
|
62
|
+
<method name="GetAdtAuditSessionData">
|
63
|
+
<arg direction="in" type="s"/>
|
64
|
+
<arg direction="out" type="ay"/>
|
65
|
+
</method>
|
66
|
+
<method name="GetConnectionSELinuxSecurityContext">
|
67
|
+
<arg direction="in" type="s"/>
|
68
|
+
<arg direction="out" type="ay"/>
|
69
|
+
</method>
|
70
|
+
<method name="ReloadConfig">
|
71
|
+
</method>
|
72
|
+
<method name="GetId">
|
73
|
+
<arg direction="out" type="s"/>
|
74
|
+
</method>
|
75
|
+
<method name="GetConnectionCredentials">
|
76
|
+
<arg direction="in" type="s"/>
|
77
|
+
<arg direction="out" type="a{sv}"/>
|
78
|
+
</method>
|
79
|
+
<property name="Features" type="as" access="read">
|
80
|
+
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
|
81
|
+
</property>
|
82
|
+
<property name="Interfaces" type="as" access="read">
|
83
|
+
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
|
84
|
+
</property>
|
85
|
+
<signal name="NameOwnerChanged">
|
86
|
+
<arg type="s"/>
|
87
|
+
<arg type="s"/>
|
88
|
+
<arg type="s"/>
|
89
|
+
</signal>
|
90
|
+
<signal name="NameLost">
|
91
|
+
<arg type="s"/>
|
92
|
+
</signal>
|
93
|
+
<signal name="NameAcquired">
|
94
|
+
<arg type="s"/>
|
95
|
+
</signal>
|
96
|
+
</interface>
|
97
|
+
</node>
|
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,107 @@
|
|
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
|
+
# Will be nil for a {PeerConnection}
|
26
|
+
attr_reader :name
|
27
|
+
# @return [Connection] The connection we're using.
|
28
|
+
attr_reader :connection
|
29
|
+
|
30
|
+
# @param connection [Connection] The connection we're using.
|
31
|
+
def initialize(name, connection)
|
32
|
+
@name = BusName.new(name)
|
33
|
+
@connection = connection
|
34
|
+
super()
|
35
|
+
end
|
36
|
+
|
37
|
+
# Determine whether the service name already exists.
|
38
|
+
def exists?
|
39
|
+
bus = connection # TODO: raise a better error if this is a peer connection
|
40
|
+
bus.proxy.ListNames[0].member?(@name)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Perform an introspection on all the objects on the service
|
44
|
+
# (starting recursively from the root).
|
45
|
+
def introspect
|
46
|
+
raise NotImplementedError if block_given?
|
47
|
+
|
48
|
+
rec_introspect(@root, "/")
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
# Retrieves an object at the given _path_.
|
53
|
+
# @param path [ObjectPath]
|
54
|
+
# @return [ProxyObject]
|
55
|
+
def [](path)
|
56
|
+
object(path, api: ApiOptions::A1)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Retrieves an object at the given _path_
|
60
|
+
# whose methods always return an array.
|
61
|
+
# @param path [ObjectPath]
|
62
|
+
# @param api [ApiOptions]
|
63
|
+
# @return [ProxyObject]
|
64
|
+
def object(path, api: ApiOptions::A0)
|
65
|
+
node = get_node(path, create: true)
|
66
|
+
if node.object.nil? || node.object.api != api
|
67
|
+
node.object = ProxyObject.new(
|
68
|
+
@connection, @name, path,
|
69
|
+
api: api
|
70
|
+
)
|
71
|
+
end
|
72
|
+
node.object
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Perform a recursive retrospection on the given current _node_
|
78
|
+
# on the given _path_.
|
79
|
+
def rec_introspect(node, path)
|
80
|
+
xml = connection.introspect_data(@name, path)
|
81
|
+
intfs, subnodes = IntrospectXMLParser.new(xml).parse
|
82
|
+
subnodes.each do |nodename|
|
83
|
+
subnode = node[nodename] = Node.new(nodename)
|
84
|
+
subpath = if path == "/"
|
85
|
+
"/#{nodename}"
|
86
|
+
else
|
87
|
+
"#{path}/#{nodename}"
|
88
|
+
end
|
89
|
+
rec_introspect(subnode, subpath)
|
90
|
+
end
|
91
|
+
return if intfs.empty?
|
92
|
+
|
93
|
+
node.object = ProxyObjectFactory.new(xml, @connection, @name, path).build
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# A hack for pretending that a {PeerConnection} has a single unnamed {ProxyService}
|
98
|
+
# so that we can get {ProxyObject}s from it.
|
99
|
+
class ProxyPeerService < ProxyService
|
100
|
+
# @param connection [Connection] The peer connection we're using.
|
101
|
+
def initialize(connection)
|
102
|
+
# this way we disallow ProxyService taking a nil name by accident
|
103
|
+
super(":0.0", connection)
|
104
|
+
@name = nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
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
|
|
@@ -29,22 +26,27 @@ require_relative "dbus/api_options"
|
|
29
26
|
require_relative "dbus/auth"
|
30
27
|
require_relative "dbus/bus"
|
31
28
|
require_relative "dbus/bus_name"
|
29
|
+
require_relative "dbus/connection"
|
32
30
|
require_relative "dbus/data"
|
33
31
|
require_relative "dbus/emits_changed_signal"
|
34
32
|
require_relative "dbus/error"
|
35
33
|
require_relative "dbus/introspect"
|
36
34
|
require_relative "dbus/logger"
|
35
|
+
require_relative "dbus/main"
|
37
36
|
require_relative "dbus/marshall"
|
38
37
|
require_relative "dbus/matchrule"
|
39
38
|
require_relative "dbus/message"
|
40
39
|
require_relative "dbus/message_queue"
|
40
|
+
require_relative "dbus/node_tree"
|
41
41
|
require_relative "dbus/object"
|
42
42
|
require_relative "dbus/object_manager"
|
43
43
|
require_relative "dbus/object_path"
|
44
|
+
require_relative "dbus/object_server"
|
44
45
|
require_relative "dbus/platform"
|
45
46
|
require_relative "dbus/proxy_object"
|
46
47
|
require_relative "dbus/proxy_object_factory"
|
47
48
|
require_relative "dbus/proxy_object_interface"
|
49
|
+
require_relative "dbus/proxy_service"
|
48
50
|
require_relative "dbus/raw_message"
|
49
51
|
require_relative "dbus/type"
|
50
52
|
require_relative "dbus/xml"
|
data/ruby-dbus.gemspec
CHANGED
@@ -9,7 +9,7 @@ GEMSPEC = Gem::Specification.new do |s|
|
|
9
9
|
s.summary = "Ruby module for interaction with D-Bus"
|
10
10
|
s.description = "Pure Ruby module for interaction with D-Bus IPC system"
|
11
11
|
s.version = File.read("VERSION").strip
|
12
|
-
s.license = "LGPL-2.1"
|
12
|
+
s.license = "LGPL-2.1-or-later"
|
13
13
|
s.author = "Ruby DBus Team"
|
14
14
|
s.email = "martin.github@vidner.net"
|
15
15
|
s.homepage = "https://github.com/mvidner/ruby-dbus"
|
@@ -0,0 +1,80 @@
|
|
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
|
+
# This only works with our special bus setup
|
40
|
+
context "when we're not allowed to own the name", tag_limited_bus: true do
|
41
|
+
it "raises an error... too late" do
|
42
|
+
name = "org.rubygems.ruby_dbus.NobodyCanOwnThisName"
|
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_limited_bus: true do
|
72
|
+
it "raises an error" do
|
73
|
+
name = "org.rubygems.ruby_dbus.NobodyCanOwnThisName"
|
74
|
+
expect do
|
75
|
+
bus.request_name(name)
|
76
|
+
end.to raise_error(DBus::Error, /not allowed to own the service/)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "spec_helper"
|
5
|
+
require "dbus"
|
6
|
+
|
7
|
+
describe DBus::PeerConnection do
|
8
|
+
let(:address) { ENV["DBUS_SESSION_BUS_ADDRESS"] }
|
9
|
+
subject { described_class.new(address) }
|
10
|
+
|
11
|
+
describe "#peer_service" do
|
12
|
+
it "returns a PeerService with a nil name" do
|
13
|
+
svc = subject.peer_service
|
14
|
+
expect(svc).to be_a(DBus::ProxyService)
|
15
|
+
expect(svc.name).to be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#add_match, #remove_match" do
|
20
|
+
it "doesn't crash trying to call AddMatch, RemoveMatch" do
|
21
|
+
mr = DBus::MatchRule.new
|
22
|
+
mr.member = "StateUpdated"
|
23
|
+
mr.interface = "org.PulseAudio.Core1.Device"
|
24
|
+
handler = ->(_msg) {}
|
25
|
+
|
26
|
+
# Cheating a bit with the mocking:
|
27
|
+
# a PulseAudio peer connection would error with
|
28
|
+
# > DBus::Error: Method "AddMatch" with signature "s" on interface
|
29
|
+
# > "org.freedesktop.DBus" doesn't exist
|
30
|
+
# but here we do have a bus at the other end, which replies with
|
31
|
+
# > DBus::Error: Client tried to send a message other than Hello without being registered
|
32
|
+
# where "registering" is a libdbus-1 thing meaning "internal bookkeeping and send Hello"
|
33
|
+
expect { subject.add_match(mr, &handler) }.to_not raise_error
|
34
|
+
expect { subject.remove_match(mr) }.to_not raise_error
|
35
|
+
end
|
36
|
+
end
|
37
|
+
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/dbus_spec.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "spec_helper"
|
5
|
+
require "dbus"
|
6
|
+
|
7
|
+
describe DBus do
|
8
|
+
describe ".session_bus", tag_bus: true do
|
9
|
+
it "returns a BusConnection" do
|
10
|
+
expect(DBus.session_bus).to be_a(DBus::BusConnection)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ".system_bus" do
|
15
|
+
# coverage obsession: mock it out,
|
16
|
+
# system bus may not exist during RPM builds
|
17
|
+
it "calls SystemBus.instance" do
|
18
|
+
expect(DBus::SystemBus).to receive(:instance)
|
19
|
+
DBus.system_bus
|
20
|
+
end
|
21
|
+
end
|
22
|
+
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
|