em-ruby-dbus 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COPYING +504 -0
- data/NEWS +253 -0
- data/README.md +93 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/doc/Reference.md +207 -0
- data/doc/Tutorial.md +480 -0
- data/doc/ex-calling-methods.body.rb +8 -0
- data/doc/ex-calling-methods.rb +3 -0
- data/doc/ex-properties.body.rb +9 -0
- data/doc/ex-properties.rb +3 -0
- data/doc/ex-setup.rb +7 -0
- data/doc/ex-signal.body.rb +20 -0
- data/doc/ex-signal.rb +3 -0
- data/doc/example-helper.rb +6 -0
- data/em-ruby-dbus.gemspec +20 -0
- data/examples/gdbus/gdbus +255 -0
- data/examples/gdbus/gdbus.glade +184 -0
- data/examples/gdbus/launch.sh +4 -0
- data/examples/no-introspect/nm-test.rb +21 -0
- data/examples/no-introspect/tracker-test.rb +16 -0
- data/examples/rhythmbox/playpause.rb +25 -0
- data/examples/service/call_service.rb +25 -0
- data/examples/service/service_newapi.rb +51 -0
- data/examples/simple/call_introspect.rb +34 -0
- data/examples/simple/properties.rb +19 -0
- data/examples/utils/listnames.rb +11 -0
- data/examples/utils/notify.rb +19 -0
- data/lib/dbus.rb +82 -0
- data/lib/dbus/auth.rb +269 -0
- data/lib/dbus/bus.rb +739 -0
- data/lib/dbus/core_ext/array/extract_options.rb +31 -0
- data/lib/dbus/core_ext/class/attribute.rb +129 -0
- data/lib/dbus/core_ext/kernel/singleton_class.rb +8 -0
- data/lib/dbus/core_ext/module/remove_method.rb +14 -0
- data/lib/dbus/error.rb +46 -0
- data/lib/dbus/export.rb +128 -0
- data/lib/dbus/introspect.rb +219 -0
- data/lib/dbus/logger.rb +31 -0
- data/lib/dbus/loop-em.rb +19 -0
- data/lib/dbus/marshall.rb +434 -0
- data/lib/dbus/matchrule.rb +101 -0
- data/lib/dbus/message.rb +276 -0
- data/lib/dbus/message_queue.rb +166 -0
- data/lib/dbus/proxy_object.rb +149 -0
- data/lib/dbus/proxy_object_factory.rb +41 -0
- data/lib/dbus/proxy_object_interface.rb +128 -0
- data/lib/dbus/type.rb +193 -0
- data/lib/dbus/xml.rb +161 -0
- data/test/async_spec.rb +47 -0
- data/test/binding_spec.rb +74 -0
- data/test/bus_and_xml_backend_spec.rb +39 -0
- data/test/bus_driver_spec.rb +20 -0
- data/test/bus_spec.rb +20 -0
- data/test/byte_array_spec.rb +38 -0
- data/test/err_msg_spec.rb +42 -0
- data/test/introspect_xml_parser_spec.rb +26 -0
- data/test/introspection_spec.rb +32 -0
- data/test/main_loop_spec.rb +82 -0
- data/test/property_spec.rb +53 -0
- data/test/server_robustness_spec.rb +66 -0
- data/test/server_spec.rb +53 -0
- data/test/service_newapi.rb +217 -0
- data/test/session_bus_spec_manual.rb +15 -0
- data/test/signal_spec.rb +90 -0
- data/test/spec_helper.rb +33 -0
- data/test/thread_safety_spec.rb +31 -0
- data/test/tools/dbus-launch-simple +35 -0
- data/test/tools/dbus-limited-session.conf +28 -0
- data/test/tools/test_env +13 -0
- data/test/tools/test_server +39 -0
- data/test/type_spec.rb +19 -0
- data/test/value_spec.rb +81 -0
- data/test/variant_spec.rb +66 -0
- metadata +145 -0
data/lib/dbus/xml.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
# dbus/xml.rb - introspection parser, rexml/nokogiri abstraction
|
2
|
+
#
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
5
|
+
# Copyright (C) 2012 Geoff Youngs
|
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
|
+
# TODO check if it is slow, make replaceable
|
13
|
+
require 'rexml/document'
|
14
|
+
begin
|
15
|
+
require 'nokogiri'
|
16
|
+
rescue LoadError
|
17
|
+
end
|
18
|
+
|
19
|
+
module DBus
|
20
|
+
# = D-Bus introspect XML parser class
|
21
|
+
#
|
22
|
+
# This class parses introspection XML of an object and constructs a tree
|
23
|
+
# of Node, Interface, Method, Signal instances.
|
24
|
+
class IntrospectXMLParser
|
25
|
+
class << self
|
26
|
+
attr_accessor :backend
|
27
|
+
end
|
28
|
+
# Creates a new parser for XML data in string _xml_.
|
29
|
+
def initialize(xml)
|
30
|
+
@xml = xml
|
31
|
+
end
|
32
|
+
|
33
|
+
class AbstractXML
|
34
|
+
def self.have_nokogiri?
|
35
|
+
Object.const_defined?('Nokogiri')
|
36
|
+
end
|
37
|
+
class Node
|
38
|
+
def initialize(node)
|
39
|
+
@node = node
|
40
|
+
end
|
41
|
+
# required methods
|
42
|
+
# returns node attribute value
|
43
|
+
def [](key)
|
44
|
+
end
|
45
|
+
# yields child nodes which match xpath of type AbstractXML::Node
|
46
|
+
def each(xpath)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# required methods
|
50
|
+
# initialize parser with xml string
|
51
|
+
def initialize(xml)
|
52
|
+
end
|
53
|
+
# yields nodes which match xpath of type AbstractXML::Node
|
54
|
+
def each(xpath)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class NokogiriParser < AbstractXML
|
59
|
+
class NokogiriNode < AbstractXML::Node
|
60
|
+
def [](key)
|
61
|
+
@node[key]
|
62
|
+
end
|
63
|
+
def each(path, &block)
|
64
|
+
@node.search(path).each { |node| block.call NokogiriNode.new(node) }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
def initialize(xml)
|
68
|
+
@doc = Nokogiri.XML(xml)
|
69
|
+
end
|
70
|
+
def each(path, &block)
|
71
|
+
@doc.search("//#{path}").each { |node| block.call NokogiriNode.new(node) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class REXMLParser < AbstractXML
|
76
|
+
class REXMLNode < AbstractXML::Node
|
77
|
+
def [](key)
|
78
|
+
@node.attributes[key]
|
79
|
+
end
|
80
|
+
def each(path, &block)
|
81
|
+
@node.elements.each(path) { |node| block.call REXMLNode.new(node) }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
def initialize(xml)
|
85
|
+
@doc = REXML::Document.new(xml)
|
86
|
+
end
|
87
|
+
def each(path, &block)
|
88
|
+
@doc.elements.each(path) { |node| block.call REXMLNode.new(node) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if AbstractXML.have_nokogiri?
|
93
|
+
@backend = NokogiriParser
|
94
|
+
else
|
95
|
+
@backend = REXMLParser
|
96
|
+
end
|
97
|
+
|
98
|
+
# return a pair: [list of Interfaces, list of direct subnode names]
|
99
|
+
def parse
|
100
|
+
# Using a Hash instead of a list helps merge split-up interfaces,
|
101
|
+
# a quirk observed in ModemManager (I#41).
|
102
|
+
interfaces = Hash.new do |hash, missing_key|
|
103
|
+
hash[missing_key] = Interface.new(missing_key)
|
104
|
+
end
|
105
|
+
subnodes = []
|
106
|
+
t = Time.now
|
107
|
+
|
108
|
+
|
109
|
+
d = IntrospectXMLParser.backend.new(@xml)
|
110
|
+
d.each("node/node") do |e|
|
111
|
+
subnodes << e["name"]
|
112
|
+
end
|
113
|
+
d.each("node/interface") do |e|
|
114
|
+
i = interfaces[e["name"]]
|
115
|
+
e.each("method") do |me|
|
116
|
+
m = Method.new(me["name"])
|
117
|
+
parse_methsig(me, m)
|
118
|
+
i << m
|
119
|
+
end
|
120
|
+
e.each("signal") do |se|
|
121
|
+
s = Signal.new(se["name"])
|
122
|
+
parse_methsig(se, s)
|
123
|
+
i << s
|
124
|
+
end
|
125
|
+
end
|
126
|
+
d = Time.now - t
|
127
|
+
if d > 2
|
128
|
+
DBus.logger.debug "Some XML took more that two secs to parse. Optimize me!"
|
129
|
+
end
|
130
|
+
[interfaces.values, subnodes]
|
131
|
+
end
|
132
|
+
|
133
|
+
######################################################################
|
134
|
+
private
|
135
|
+
|
136
|
+
# Parses a method signature XML element _e_ and initialises
|
137
|
+
# method/signal _m_.
|
138
|
+
def parse_methsig(e, m)
|
139
|
+
e.each("arg") do |ae|
|
140
|
+
name = ae["name"]
|
141
|
+
dir = ae["direction"]
|
142
|
+
sig = ae["type"]
|
143
|
+
if m.is_a?(DBus::Signal)
|
144
|
+
# Direction can only be "out", ignore it
|
145
|
+
m.add_fparam(name, sig)
|
146
|
+
elsif m.is_a?(DBus::Method)
|
147
|
+
case dir
|
148
|
+
# This is a method, so dir defaults to "in"
|
149
|
+
when "in", nil
|
150
|
+
m.add_fparam(name, sig)
|
151
|
+
when "out"
|
152
|
+
m.add_return(name, sig)
|
153
|
+
end
|
154
|
+
else
|
155
|
+
raise NotImplementedError, dir
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end # class IntrospectXMLParser
|
160
|
+
end # module DBus
|
161
|
+
|
data/test/async_spec.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# Test the binding of dbus concepts to ruby concepts
|
3
|
+
require_relative "spec_helper"
|
4
|
+
require "dbus"
|
5
|
+
|
6
|
+
describe "AsyncTest" do
|
7
|
+
before(:each) do
|
8
|
+
@bus = DBus::ASessionBus.new
|
9
|
+
@svc = @bus.service("org.ruby.service")
|
10
|
+
@obj = @svc.object "/org/ruby/MyInstance"
|
11
|
+
@obj.introspect
|
12
|
+
@obj.default_iface = "org.ruby.SampleInterface"
|
13
|
+
end
|
14
|
+
|
15
|
+
# https://github.com/mvidner/ruby-dbus/issues/13
|
16
|
+
it "tests async_call_to_default_interface" do
|
17
|
+
loop = DBus::Main.new
|
18
|
+
loop << @bus
|
19
|
+
|
20
|
+
immediate_answer = @obj.the_answer do |msg, retval|
|
21
|
+
expect(retval).to eq(42)
|
22
|
+
loop.quit
|
23
|
+
end
|
24
|
+
|
25
|
+
expect(immediate_answer).to be_nil
|
26
|
+
|
27
|
+
# wait for the async reply
|
28
|
+
loop.run
|
29
|
+
end
|
30
|
+
|
31
|
+
it "tests async_call_to_explicit_interface" do
|
32
|
+
loop = DBus::Main.new
|
33
|
+
loop << @bus
|
34
|
+
|
35
|
+
ifc = @obj["org.ruby.AnotherInterface"]
|
36
|
+
immediate_answer = ifc.Reverse("abcd") do |msg, retval|
|
37
|
+
expect(retval).to eq("dcba")
|
38
|
+
loop.quit
|
39
|
+
end
|
40
|
+
|
41
|
+
expect(immediate_answer).to be_nil
|
42
|
+
|
43
|
+
# wait for the async reply
|
44
|
+
loop.run
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# Test the binding of dbus concepts to ruby concepts
|
3
|
+
require_relative "spec_helper"
|
4
|
+
|
5
|
+
require "dbus"
|
6
|
+
|
7
|
+
describe "BindingTest" do
|
8
|
+
before(:each) do
|
9
|
+
@bus = DBus::ASessionBus.new
|
10
|
+
@svc = @bus.service("org.ruby.service")
|
11
|
+
@base = @svc.object "/org/ruby/MyInstance"
|
12
|
+
@base.introspect
|
13
|
+
@base.default_iface = "org.ruby.SampleInterface"
|
14
|
+
end
|
15
|
+
|
16
|
+
# https://trac.luon.net/ruby-dbus/ticket/36#comment:3
|
17
|
+
it "tests class inheritance" do
|
18
|
+
derived = @svc.object "/org/ruby/MyDerivedInstance"
|
19
|
+
derived.introspect
|
20
|
+
|
21
|
+
# it should inherit from the parent
|
22
|
+
expect(derived["org.ruby.SampleInterface"]).not_to be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# https://trac.luon.net/ruby-dbus/ticket/36
|
26
|
+
# Interfaces and methods/signals appeared on all classes
|
27
|
+
it "tests separation of classes" do
|
28
|
+
test2 = @svc.object "/org/ruby/MyInstance2"
|
29
|
+
test2.introspect
|
30
|
+
|
31
|
+
# it should have its own interface
|
32
|
+
expect(test2["org.ruby.Test2"]).not_to be_nil
|
33
|
+
# but not an interface of the Test class
|
34
|
+
expect(test2["org.ruby.SampleInterface"]).to be_nil
|
35
|
+
|
36
|
+
# and the parent should not get polluted by the child
|
37
|
+
expect(@base["org.ruby.Test2"]).to be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "tests translating errors into exceptions" do
|
41
|
+
# this is a generic call that will reply with the specified error
|
42
|
+
expect { @base.Error "org.example.Fail", "as you wish" }.to raise_error(DBus::Error) do |e|
|
43
|
+
expect(e.name).to eq("org.example.Fail")
|
44
|
+
expect(e.message).to match(/as you wish/)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "tests generic dbus error" do
|
49
|
+
# this is a generic call that will reply with the specified error
|
50
|
+
expect { @base.will_raise_error_failed }.to raise_error(DBus::Error) do |e|
|
51
|
+
expect(e.name).to eq("org.freedesktop.DBus.Error.Failed")
|
52
|
+
expect(e.message).to match(/failed as designed/)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it "tests dynamic interface definition" do
|
57
|
+
# interfaces can be defined dynamicaly
|
58
|
+
derived = DBus::Object.new "/org/ruby/MyDerivedInstance"
|
59
|
+
|
60
|
+
#define a new interface
|
61
|
+
derived.singleton_class.instance_eval do
|
62
|
+
dbus_interface "org.ruby.DynamicInterface" do
|
63
|
+
dbus_method :hello2, "in name:s, in name2:s" do |name, name2|
|
64
|
+
puts "hello(#{name}, #{name2})"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# the object should have the new iface
|
70
|
+
ifaces = derived.intfs
|
71
|
+
expect(ifaces && ifaces.include?("org.ruby.DynamicInterface")).to be_true
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# Test the bus class
|
3
|
+
require_relative "spec_helper"
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'nokogiri'
|
7
|
+
require "dbus"
|
8
|
+
|
9
|
+
describe "BusAndXmlBackendTest" do
|
10
|
+
before(:each) do
|
11
|
+
@bus = DBus::ASessionBus.new
|
12
|
+
end
|
13
|
+
|
14
|
+
it "tests introspection reading rexml" do
|
15
|
+
DBus::IntrospectXMLParser.backend = DBus::IntrospectXMLParser::REXMLParser
|
16
|
+
@svc = @bus.service("org.ruby.service")
|
17
|
+
obj = @svc.object("/org/ruby/MyInstance")
|
18
|
+
obj.default_iface = 'org.ruby.SampleInterface'
|
19
|
+
obj.introspect
|
20
|
+
# "should respond to :the_answer"
|
21
|
+
expect(obj.the_answer[0]).to eq(42)
|
22
|
+
# "should work with multiple interfaces"
|
23
|
+
expect(obj["org.ruby.AnotherInterface"].Reverse('foo')[0]).to eq("oof")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "tests introspection reading nokogiri" do
|
27
|
+
# peek inside the object to see if a cleanup step worked or not
|
28
|
+
DBus::IntrospectXMLParser.backend = DBus::IntrospectXMLParser::NokogiriParser
|
29
|
+
@svc = @bus.service("org.ruby.service")
|
30
|
+
obj = @svc.object("/org/ruby/MyInstance")
|
31
|
+
obj.default_iface = 'org.ruby.SampleInterface'
|
32
|
+
obj.introspect
|
33
|
+
# "should respond to :the_answer"
|
34
|
+
expect(obj.the_answer[0]).to eq(42)
|
35
|
+
# "should work with multiple interfaces"
|
36
|
+
expect(obj["org.ruby.AnotherInterface"].Reverse('foo')[0]).to eq("oof")
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
require_relative "spec_helper"
|
3
|
+
require "dbus"
|
4
|
+
|
5
|
+
describe DBus::Service do
|
6
|
+
let(:bus) { DBus::ASessionBus.new }
|
7
|
+
|
8
|
+
describe "#exists?" do
|
9
|
+
it "is true for an existing service" do
|
10
|
+
svc = bus.service("org.ruby.service")
|
11
|
+
svc.object("/").introspect # must activate the service first :-/
|
12
|
+
expect(svc.exists?).to be_true
|
13
|
+
end
|
14
|
+
|
15
|
+
it "is false for a nonexisting service" do
|
16
|
+
svc = bus.service("org.ruby.nosuchservice")
|
17
|
+
expect(svc.exists?).to be_false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/test/bus_spec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# Test the bus class
|
3
|
+
require_relative "spec_helper"
|
4
|
+
|
5
|
+
require "dbus"
|
6
|
+
|
7
|
+
describe "BusTest" do
|
8
|
+
before(:each) do
|
9
|
+
@bus = DBus::ASessionBus.new
|
10
|
+
@svc = @bus.service("org.ruby.service")
|
11
|
+
@svc.object("/").introspect
|
12
|
+
end
|
13
|
+
|
14
|
+
it "tests introspection not leaking" do
|
15
|
+
# peek inside the object to see if a cleanup step worked or not
|
16
|
+
some_hash = @bus.instance_eval { @method_call_replies || Hash.new }
|
17
|
+
# fail: "there are leftover method handlers"
|
18
|
+
expect(some_hash.size).to eq(0)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
require_relative "spec_helper"
|
3
|
+
|
4
|
+
require "dbus"
|
5
|
+
|
6
|
+
describe "ByteArrayTest" do
|
7
|
+
before(:each) do
|
8
|
+
@bus = DBus::ASessionBus.new
|
9
|
+
@svc = @bus.service("org.ruby.service")
|
10
|
+
@obj = @svc.object("/org/ruby/MyInstance")
|
11
|
+
@obj.introspect
|
12
|
+
@obj.default_iface = "org.ruby.SampleInterface"
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
it "tests passing byte array" do
|
17
|
+
data = [0, 77, 255]
|
18
|
+
result = @obj.mirror_byte_array(data).first
|
19
|
+
expect(result).to eq(data)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "tests passing byte array from string" do
|
23
|
+
data = "AAA"
|
24
|
+
result = @obj.mirror_byte_array(data).first
|
25
|
+
expect(result).to eq([65, 65, 65])
|
26
|
+
end
|
27
|
+
|
28
|
+
it "tests passing byte array from hash" do
|
29
|
+
# Hash is an Enumerable, but is caught earlier
|
30
|
+
data = { "this will" => "fail" }
|
31
|
+
expect { @obj.mirror_byte_array(data).first }.to raise_error(DBus::TypeException)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "tests passing byte array from nonenumerable" do
|
35
|
+
data = Time.now
|
36
|
+
expect { @obj.mirror_byte_array(data).first }.to raise_error(DBus::TypeException)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# should report it missing on org.ruby.SampleInterface
|
3
|
+
# (on object...) instead of on DBus::Proxy::ObjectInterface
|
4
|
+
require_relative "spec_helper"
|
5
|
+
require "dbus"
|
6
|
+
|
7
|
+
describe "ErrMsgTest" do
|
8
|
+
before(:each) do
|
9
|
+
session_bus = DBus::ASessionBus.new
|
10
|
+
svc = session_bus.service("org.ruby.service")
|
11
|
+
@obj = svc.object("/org/ruby/MyInstance")
|
12
|
+
@obj.introspect # necessary
|
13
|
+
@obj.default_iface = "org.ruby.SampleInterface"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "tests report dbus interface" do
|
17
|
+
# a specific exception...
|
18
|
+
# mentioning DBus and the interface
|
19
|
+
expect { @obj.NoSuchMethod }.to raise_error(NameError, /DBus interface.*#{@obj.default_iface}/)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "tests report short struct" do
|
23
|
+
expect { @obj.test_variant ["(ss)", ["too few"] ] }.to raise_error(DBus::TypeException, /1 elements but type info for 2/)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "tests report long struct" do
|
27
|
+
expect { @obj.test_variant ["(ss)", ["a", "b", "too many"] ] }.to raise_error(DBus::TypeException, /3 elements but type info for 2/)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "tests report nil" do
|
31
|
+
nils = [
|
32
|
+
["(s)", [nil] ], # would get disconnected
|
33
|
+
["i", nil ],
|
34
|
+
["a{ss}", {"foo" => nil} ],
|
35
|
+
]
|
36
|
+
nils.each do |has_nil|
|
37
|
+
# TODO want backtrace from the perspective of the caller:
|
38
|
+
# rescue/reraise in send_sync?
|
39
|
+
expect { @obj.test_variant has_nil }.to raise_error(DBus::TypeException, /Cannot send nil/)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|