em-ruby-dbus 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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