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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +38 -0
  3. data/VERSION +1 -1
  4. data/doc/Reference.md +5 -5
  5. data/examples/no-bus/pulseaudio.rb +50 -0
  6. data/examples/service/complex-property.rb +2 -2
  7. data/examples/service/service_newapi.rb +2 -2
  8. data/lib/dbus/bus.rb +48 -694
  9. data/lib/dbus/connection.rb +350 -0
  10. data/lib/dbus/logger.rb +3 -2
  11. data/lib/dbus/main.rb +66 -0
  12. data/lib/dbus/message.rb +6 -8
  13. data/lib/dbus/node_tree.rb +105 -0
  14. data/lib/dbus/object.rb +28 -9
  15. data/lib/dbus/object_manager.rb +6 -3
  16. data/lib/dbus/object_server.rb +149 -0
  17. data/lib/dbus/org.freedesktop.DBus.xml +97 -0
  18. data/lib/dbus/proxy_object.rb +4 -4
  19. data/lib/dbus/proxy_service.rb +107 -0
  20. data/lib/dbus.rb +10 -8
  21. data/ruby-dbus.gemspec +1 -1
  22. data/spec/bus_connection_spec.rb +80 -0
  23. data/spec/connection_spec.rb +37 -0
  24. data/spec/coverage_helper.rb +39 -0
  25. data/spec/dbus_spec.rb +22 -0
  26. data/spec/main_loop_spec.rb +14 -0
  27. data/spec/message_spec.rb +21 -0
  28. data/spec/mock-service/cockpit-dbustests.rb +29 -0
  29. data/spec/mock-service/com.redhat.Cockpit.DBusTests.xml +180 -0
  30. data/spec/mock-service/org.ruby.service.service +4 -0
  31. data/spec/mock-service/org.rubygems.ruby_dbus.DBusTests.service +4 -0
  32. data/spec/{service_newapi.rb → mock-service/spaghetti-monster.rb} +21 -10
  33. data/spec/node_spec.rb +1 -5
  34. data/spec/object_server_spec.rb +138 -0
  35. data/spec/object_spec.rb +46 -0
  36. data/spec/{bus_driver_spec.rb → proxy_service_spec.rb} +13 -8
  37. data/spec/spec_helper.rb +9 -45
  38. data/spec/thread_safety_spec.rb +9 -11
  39. data/spec/tools/dbus-launch-simple +4 -1
  40. data/spec/tools/dbus-limited-session.conf +3 -0
  41. data/spec/tools/test_env +26 -6
  42. metadata +24 -10
  43. data/spec/server_spec.rb +0 -55
  44. data/spec/service_spec.rb +0 -18
  45. 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>
@@ -10,20 +10,20 @@
10
10
  # See the file "COPYING" for the exact licensing terms.
11
11
 
12
12
  module DBus
13
- # D-Bus proxy object class
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 correctponding
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
- # Byte signifying big endianness.
14
+ # Protocol character signifying big endianness.
15
15
  BIG_END = "B"
16
- # Byte signifying little endianness.
16
+ # Protocol character signifying little endianness.
17
17
  LIL_END = "l"
18
18
 
19
- # Byte signifying the host's endianness.
20
- HOST_END = if [0x01020304].pack("L").unpack1("V") == 0x01020304
21
- LIL_END
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
@@ -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