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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +504 -0
  3. data/NEWS +253 -0
  4. data/README.md +93 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/doc/Reference.md +207 -0
  8. data/doc/Tutorial.md +480 -0
  9. data/doc/ex-calling-methods.body.rb +8 -0
  10. data/doc/ex-calling-methods.rb +3 -0
  11. data/doc/ex-properties.body.rb +9 -0
  12. data/doc/ex-properties.rb +3 -0
  13. data/doc/ex-setup.rb +7 -0
  14. data/doc/ex-signal.body.rb +20 -0
  15. data/doc/ex-signal.rb +3 -0
  16. data/doc/example-helper.rb +6 -0
  17. data/em-ruby-dbus.gemspec +20 -0
  18. data/examples/gdbus/gdbus +255 -0
  19. data/examples/gdbus/gdbus.glade +184 -0
  20. data/examples/gdbus/launch.sh +4 -0
  21. data/examples/no-introspect/nm-test.rb +21 -0
  22. data/examples/no-introspect/tracker-test.rb +16 -0
  23. data/examples/rhythmbox/playpause.rb +25 -0
  24. data/examples/service/call_service.rb +25 -0
  25. data/examples/service/service_newapi.rb +51 -0
  26. data/examples/simple/call_introspect.rb +34 -0
  27. data/examples/simple/properties.rb +19 -0
  28. data/examples/utils/listnames.rb +11 -0
  29. data/examples/utils/notify.rb +19 -0
  30. data/lib/dbus.rb +82 -0
  31. data/lib/dbus/auth.rb +269 -0
  32. data/lib/dbus/bus.rb +739 -0
  33. data/lib/dbus/core_ext/array/extract_options.rb +31 -0
  34. data/lib/dbus/core_ext/class/attribute.rb +129 -0
  35. data/lib/dbus/core_ext/kernel/singleton_class.rb +8 -0
  36. data/lib/dbus/core_ext/module/remove_method.rb +14 -0
  37. data/lib/dbus/error.rb +46 -0
  38. data/lib/dbus/export.rb +128 -0
  39. data/lib/dbus/introspect.rb +219 -0
  40. data/lib/dbus/logger.rb +31 -0
  41. data/lib/dbus/loop-em.rb +19 -0
  42. data/lib/dbus/marshall.rb +434 -0
  43. data/lib/dbus/matchrule.rb +101 -0
  44. data/lib/dbus/message.rb +276 -0
  45. data/lib/dbus/message_queue.rb +166 -0
  46. data/lib/dbus/proxy_object.rb +149 -0
  47. data/lib/dbus/proxy_object_factory.rb +41 -0
  48. data/lib/dbus/proxy_object_interface.rb +128 -0
  49. data/lib/dbus/type.rb +193 -0
  50. data/lib/dbus/xml.rb +161 -0
  51. data/test/async_spec.rb +47 -0
  52. data/test/binding_spec.rb +74 -0
  53. data/test/bus_and_xml_backend_spec.rb +39 -0
  54. data/test/bus_driver_spec.rb +20 -0
  55. data/test/bus_spec.rb +20 -0
  56. data/test/byte_array_spec.rb +38 -0
  57. data/test/err_msg_spec.rb +42 -0
  58. data/test/introspect_xml_parser_spec.rb +26 -0
  59. data/test/introspection_spec.rb +32 -0
  60. data/test/main_loop_spec.rb +82 -0
  61. data/test/property_spec.rb +53 -0
  62. data/test/server_robustness_spec.rb +66 -0
  63. data/test/server_spec.rb +53 -0
  64. data/test/service_newapi.rb +217 -0
  65. data/test/session_bus_spec_manual.rb +15 -0
  66. data/test/signal_spec.rb +90 -0
  67. data/test/spec_helper.rb +33 -0
  68. data/test/thread_safety_spec.rb +31 -0
  69. data/test/tools/dbus-launch-simple +35 -0
  70. data/test/tools/dbus-limited-session.conf +28 -0
  71. data/test/tools/test_env +13 -0
  72. data/test/tools/test_server +39 -0
  73. data/test/type_spec.rb +19 -0
  74. data/test/value_spec.rb +81 -0
  75. data/test/variant_spec.rb +66 -0
  76. 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
+
@@ -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