ruby-dbus 0.16.0 → 0.25.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +288 -3
  3. data/README.md +3 -5
  4. data/Rakefile +27 -12
  5. data/VERSION +1 -1
  6. data/doc/Reference.md +109 -10
  7. data/examples/doc/_extract_examples +7 -0
  8. data/examples/no-bus/pulseaudio.rb +50 -0
  9. data/examples/no-introspect/nm-test.rb +2 -0
  10. data/examples/no-introspect/tracker-test.rb +3 -1
  11. data/examples/rhythmbox/playpause.rb +2 -1
  12. data/examples/service/call_service.rb +2 -1
  13. data/examples/service/complex-property.rb +21 -0
  14. data/examples/service/service_newapi.rb +3 -3
  15. data/examples/simple/call_introspect.rb +1 -0
  16. data/examples/simple/get_id.rb +6 -3
  17. data/examples/simple/properties.rb +2 -0
  18. data/examples/utils/listnames.rb +10 -1
  19. data/examples/utils/notify.rb +1 -0
  20. data/lib/dbus/api_options.rb +9 -0
  21. data/lib/dbus/auth.rb +307 -217
  22. data/lib/dbus/bus.rb +155 -588
  23. data/lib/dbus/bus_name.rb +12 -8
  24. data/lib/dbus/connection.rb +363 -0
  25. data/lib/dbus/core_ext/class/attribute.rb +1 -1
  26. data/lib/dbus/data.rb +821 -0
  27. data/lib/dbus/emits_changed_signal.rb +83 -0
  28. data/lib/dbus/error.rb +4 -2
  29. data/lib/dbus/introspect.rb +133 -31
  30. data/lib/dbus/logger.rb +6 -3
  31. data/lib/dbus/main.rb +66 -0
  32. data/lib/dbus/marshall.rb +247 -296
  33. data/lib/dbus/matchrule.rb +16 -16
  34. data/lib/dbus/message.rb +66 -57
  35. data/lib/dbus/message_queue.rb +40 -25
  36. data/lib/dbus/node_tree.rb +105 -0
  37. data/lib/dbus/object.rb +442 -33
  38. data/lib/dbus/object_manager.rb +61 -0
  39. data/lib/dbus/object_path.rb +11 -6
  40. data/lib/dbus/object_server.rb +149 -0
  41. data/lib/dbus/org.freedesktop.DBus.xml +97 -0
  42. data/lib/dbus/platform.rb +26 -0
  43. data/lib/dbus/proxy_object.rb +30 -11
  44. data/lib/dbus/proxy_object_factory.rb +13 -7
  45. data/lib/dbus/proxy_object_interface.rb +63 -30
  46. data/lib/dbus/proxy_service.rb +107 -0
  47. data/lib/dbus/raw_message.rb +91 -0
  48. data/lib/dbus/type.rb +319 -86
  49. data/lib/dbus/xml.rb +40 -19
  50. data/lib/dbus.rb +28 -17
  51. data/ruby-dbus.gemspec +13 -6
  52. data/spec/async_spec.rb +2 -0
  53. data/spec/auth_spec.rb +225 -0
  54. data/spec/binding_spec.rb +2 -0
  55. data/spec/bus_and_xml_backend_spec.rb +5 -1
  56. data/spec/bus_connection_spec.rb +93 -0
  57. data/spec/bus_name_spec.rb +3 -1
  58. data/spec/bus_spec.rb +2 -0
  59. data/spec/byte_array_spec.rb +2 -0
  60. data/spec/client_robustness_spec.rb +4 -2
  61. data/spec/connection_spec.rb +37 -0
  62. data/spec/coverage_helper.rb +39 -0
  63. data/spec/data/marshall.yaml +1667 -0
  64. data/spec/data_spec.rb +673 -0
  65. data/spec/dbus_spec.rb +22 -0
  66. data/spec/emits_changed_signal_spec.rb +58 -0
  67. data/spec/err_msg_spec.rb +2 -0
  68. data/spec/introspect_xml_parser_spec.rb +2 -0
  69. data/spec/introspection_spec.rb +2 -0
  70. data/spec/main_loop_spec.rb +17 -1
  71. data/spec/message_spec.rb +21 -0
  72. data/spec/mock-service/cockpit-dbustests.rb +29 -0
  73. data/spec/mock-service/com.redhat.Cockpit.DBusTests.xml +180 -0
  74. data/spec/mock-service/org.ruby.service.service +4 -0
  75. data/spec/mock-service/org.rubygems.ruby_dbus.DBusTests.service +4 -0
  76. data/spec/{service_newapi.rb → mock-service/spaghetti-monster.rb} +122 -75
  77. data/spec/node_spec.rb +65 -0
  78. data/spec/object_manager_spec.rb +33 -0
  79. data/spec/object_path_spec.rb +3 -0
  80. data/spec/object_server_spec.rb +138 -0
  81. data/spec/object_spec.rb +194 -0
  82. data/spec/packet_marshaller_spec.rb +41 -0
  83. data/spec/packet_unmarshaller_spec.rb +248 -0
  84. data/spec/platform_spec.rb +14 -0
  85. data/spec/property_spec.rb +214 -12
  86. data/spec/proxy_object_interface_spec.rb +35 -0
  87. data/spec/proxy_object_spec.rb +2 -0
  88. data/spec/{bus_driver_spec.rb → proxy_service_spec.rb} +15 -8
  89. data/spec/raw_message_spec.rb +32 -0
  90. data/spec/server_robustness_spec.rb +20 -6
  91. data/spec/session_bus_spec.rb +36 -1
  92. data/spec/session_bus_spec_manual.rb +2 -0
  93. data/spec/signal_spec.rb +16 -3
  94. data/spec/spec_helper.rb +25 -33
  95. data/spec/thread_safety_spec.rb +55 -12
  96. data/spec/tools/dbus-launch-simple +9 -6
  97. data/spec/tools/dbus-limited-session.conf +32 -0
  98. data/spec/tools/test_env +26 -6
  99. data/spec/type_spec.rb +214 -6
  100. data/spec/value_spec.rb +16 -1
  101. data/spec/variant_spec.rb +4 -2
  102. data/spec/zzz_quit_spec.rb +16 -0
  103. metadata +92 -20
  104. data/examples/gdbus/gdbus +0 -257
  105. data/examples/gdbus/gdbus.glade +0 -98
  106. data/examples/gdbus/launch.sh +0 -4
  107. data/spec/server_spec.rb +0 -53
  108. data/spec/tools/test_server +0 -39
data/lib/dbus/xml.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # dbus/xml.rb - introspection parser, rexml/nokogiri abstraction
2
4
  #
3
5
  # This file is part of the ruby-dbus project
@@ -9,11 +11,17 @@
9
11
  # License, version 2.1 as published by the Free Software Foundation.
10
12
  # See the file "COPYING" for the exact licensing terms.
11
13
 
12
- # TODO: check if it is slow, make replaceable
13
- require "rexml/document"
14
+ # Our gemspec says rexml is needed and nokogiri is optional
15
+ # but in fact either will do
16
+
14
17
  begin
15
18
  require "nokogiri"
16
19
  rescue LoadError
20
+ begin
21
+ require "rexml/document"
22
+ rescue LoadError
23
+ raise LoadError, "cannot load nokogiri OR rexml/document"
24
+ end
17
25
  end
18
26
 
19
27
  module DBus
@@ -26,14 +34,23 @@ module DBus
26
34
  attr_accessor :backend
27
35
  end
28
36
  # Creates a new parser for XML data in string _xml_.
37
+ # @param xml [String]
29
38
  def initialize(xml)
30
39
  @xml = xml
31
40
  end
32
41
 
33
42
  class AbstractXML
43
+ # @!method initialize(xml)
44
+ # @abstract
45
+
46
+ # @!method each(xpath)
47
+ # @abstract
48
+ # yields nodes which match xpath of type AbstractXML::Node
49
+
34
50
  def self.have_nokogiri?
35
51
  Object.const_defined?("Nokogiri")
36
52
  end
53
+
37
54
  class Node
38
55
  def initialize(node)
39
56
  @node = node
@@ -46,12 +63,6 @@ module DBus
46
63
  # yields child nodes which match xpath of type AbstractXML::Node
47
64
  def each(xpath); end
48
65
  end
49
- # required methods
50
- # initialize parser with xml string
51
- def initialize(xml); end
52
-
53
- # yields nodes which match xpath of type AbstractXML::Node
54
- def each(xpath); end
55
66
  end
56
67
 
57
68
  class NokogiriParser < AbstractXML
@@ -64,7 +75,9 @@ module DBus
64
75
  @node.search(path).each { |node| block.call NokogiriNode.new(node) }
65
76
  end
66
77
  end
78
+
67
79
  def initialize(xml)
80
+ super()
68
81
  @doc = Nokogiri.XML(xml)
69
82
  end
70
83
 
@@ -83,7 +96,9 @@ module DBus
83
96
  @node.elements.each(path) { |node| block.call REXMLNode.new(node) }
84
97
  end
85
98
  end
99
+
86
100
  def initialize(xml)
101
+ super()
87
102
  @doc = REXML::Document.new(xml)
88
103
  end
89
104
 
@@ -125,6 +140,10 @@ module DBus
125
140
  parse_methsig(se, s)
126
141
  i << s
127
142
  end
143
+ e.each("property") do |pe|
144
+ p = Property.from_xml(pe)
145
+ i << p
146
+ end
128
147
  end
129
148
  d = Time.now - t
130
149
  if d > 2
@@ -136,28 +155,30 @@ module DBus
136
155
  ######################################################################
137
156
  private
138
157
 
139
- # Parses a method signature XML element _e_ and initialises
140
- # method/signal _m_.
141
- def parse_methsig(e, m)
142
- e.each("arg") do |ae|
158
+ # Parses a method signature XML element *elem* and initialises
159
+ # method/signal *methsig*.
160
+ # @param elem [AbstractXML::Node]
161
+ def parse_methsig(elem, methsig)
162
+ elem.each("arg") do |ae|
143
163
  name = ae["name"]
144
164
  dir = ae["direction"]
145
165
  sig = ae["type"]
146
- if m.is_a?(DBus::Signal)
166
+ case methsig
167
+ when DBus::Signal
147
168
  # Direction can only be "out", ignore it
148
- m.add_fparam(name, sig)
149
- elsif m.is_a?(DBus::Method)
169
+ methsig.add_fparam(name, sig)
170
+ when DBus::Method
150
171
  case dir
151
172
  # This is a method, so dir defaults to "in"
152
173
  when "in", nil
153
- m.add_fparam(name, sig)
174
+ methsig.add_fparam(name, sig)
154
175
  when "out"
155
- m.add_return(name, sig)
176
+ methsig.add_return(name, sig)
156
177
  end
157
178
  else
158
179
  raise NotImplementedError, dir
159
180
  end
160
181
  end
161
182
  end
162
- end # class IntrospectXMLParser
163
- end # module DBus
183
+ end
184
+ end
data/lib/dbus.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # dbus.rb - Module containing the low-level D-Bus implementation
2
4
  #
3
5
  # This file is part of the ruby-dbus project
@@ -8,46 +10,55 @@
8
10
  # License, version 2.1 as published by the Free Software Foundation.
9
11
  # See the file "COPYING" for the exact licensing terms.
10
12
 
13
+ module DBus
14
+ # Protocol character signifying big endianness.
15
+ BIG_END = "B"
16
+ # Protocol character signifying little endianness.
17
+ LIL_END = "l"
18
+
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"))
22
+ end
23
+ # ^ That's because dbus/message needs HOST_END early
24
+
11
25
  require_relative "dbus/api_options"
12
26
  require_relative "dbus/auth"
13
27
  require_relative "dbus/bus"
14
28
  require_relative "dbus/bus_name"
29
+ require_relative "dbus/connection"
30
+ require_relative "dbus/data"
31
+ require_relative "dbus/emits_changed_signal"
15
32
  require_relative "dbus/error"
16
33
  require_relative "dbus/introspect"
17
34
  require_relative "dbus/logger"
35
+ require_relative "dbus/main"
18
36
  require_relative "dbus/marshall"
19
37
  require_relative "dbus/matchrule"
20
38
  require_relative "dbus/message"
21
39
  require_relative "dbus/message_queue"
40
+ require_relative "dbus/node_tree"
22
41
  require_relative "dbus/object"
42
+ require_relative "dbus/object_manager"
23
43
  require_relative "dbus/object_path"
44
+ require_relative "dbus/object_server"
45
+ require_relative "dbus/platform"
24
46
  require_relative "dbus/proxy_object"
25
47
  require_relative "dbus/proxy_object_factory"
26
48
  require_relative "dbus/proxy_object_interface"
49
+ require_relative "dbus/proxy_service"
50
+ require_relative "dbus/raw_message"
27
51
  require_relative "dbus/type"
28
52
  require_relative "dbus/xml"
29
53
 
30
54
  require "socket"
31
- require "thread"
32
-
33
55
  # = D-Bus main module
34
56
  #
35
57
  # Module containing all the D-Bus modules and classes.
36
58
  module DBus
37
- # Default socket name for the system bus.
38
- SystemSocketName = "unix:path=/var/run/dbus/system_bus_socket".freeze
39
-
40
- # Byte signifying big endianness.
41
- BIG_END = "B".freeze
42
- # Byte signifying little endianness.
43
- LIL_END = "l".freeze
44
-
45
- # Byte signifying the host's endianness.
46
- HOST_END = if [0x01020304].pack("L").unpack("V")[0] == 0x01020304
47
- LIL_END
48
- else
49
- BIG_END
50
- end
59
+ # Comparing symbols is faster than strings
60
+ # @return [:little,:big]
61
+ HOST_ENDIANNESS = RawMessage.endianness(HOST_END)
51
62
 
52
63
  # General exceptions.
53
64
 
@@ -69,4 +80,4 @@ module DBus
69
80
  # Exception raised when invalid introspection data is parsed/used.
70
81
  class InvalidIntrospectionData < Exception
71
82
  end
72
- end # module DBus
83
+ end
data/ruby-dbus.gemspec CHANGED
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # -*- ruby -*-
2
- require "rubygems"
3
4
 
4
5
  GEMSPEC = Gem::Specification.new do |s|
5
6
  s.name = "ruby-dbus"
@@ -7,7 +8,7 @@ GEMSPEC = Gem::Specification.new do |s|
7
8
  s.summary = "Ruby module for interaction with D-Bus"
8
9
  s.description = "Pure Ruby module for interaction with D-Bus IPC system"
9
10
  s.version = File.read("VERSION").strip
10
- s.license = "LGPL-2.1"
11
+ s.license = "LGPL-2.1-or-later"
11
12
  s.author = "Ruby DBus Team"
12
13
  s.email = "martin.github@vidner.net"
13
14
  s.homepage = "https://github.com/mvidner/ruby-dbus"
@@ -18,15 +19,21 @@ GEMSPEC = Gem::Specification.new do |s|
18
19
  ]
19
20
  s.require_path = "lib"
20
21
 
21
- s.required_ruby_version = ">= 2.0.0"
22
+ s.required_ruby_version = ">= 2.4.0"
22
23
 
23
- # This is optional
24
+ s.add_runtime_dependency "logger"
25
+ # Either of rexml and nokogiri is required
26
+ # but AFAIK gemspec cannot express that.
27
+ # Nokogiri is recommended as rexml is dead slow.
28
+ s.add_runtime_dependency "rexml"
24
29
  # s.add_runtime_dependency "nokogiri"
25
30
 
26
- s.add_development_dependency "coveralls"
31
+ # workaround: rubocop-1.0 needs base64 which is no longer in stdlib in newer rubies
32
+ s.add_development_dependency "base64"
33
+ s.add_development_dependency "ostruct"
27
34
  s.add_development_dependency "packaging_rake_tasks"
28
35
  s.add_development_dependency "rake"
29
36
  s.add_development_dependency "rspec", "~> 3"
30
- s.add_development_dependency "rubocop", "= 0.41.2"
31
37
  s.add_development_dependency "simplecov"
38
+ s.add_development_dependency "simplecov-lcov"
32
39
  end
data/spec/async_spec.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  # Test the binding of dbus concepts to ruby concepts
3
5
  require_relative "spec_helper"
4
6
  require "dbus"
data/spec/auth_spec.rb ADDED
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "spec_helper"
5
+ require "dbus"
6
+
7
+ describe DBus::Authentication::Client do
8
+ let(:socket) { instance_double("Socket") }
9
+ let(:subject) { described_class.new(socket) }
10
+
11
+ before(:each) do
12
+ allow(Process).to receive(:uid).and_return(999)
13
+ allow(subject).to receive(:send_nul_byte)
14
+ end
15
+
16
+ describe "#next_state" do
17
+ it "raises when I forget to handle a state" do
18
+ subject.instance_variable_set(:@state, :Denmark)
19
+ expect { subject.__send__(:next_state, []) }.to raise_error(RuntimeError, /unhandled state :Denmark/)
20
+ end
21
+ end
22
+
23
+ def expect_protocol(pairs)
24
+ pairs.each do |we_say, server_says|
25
+ expect(subject).to receive(:write_line).with(we_say)
26
+ next if server_says.nil?
27
+
28
+ expect(subject).to receive(:read_line).and_return(server_says)
29
+ end
30
+ end
31
+
32
+ context "with ANONYMOUS" do
33
+ let(:subject) { described_class.new(socket, [DBus::Authentication::Anonymous]) }
34
+
35
+ it "authentication passes" do
36
+ expect_protocol [
37
+ ["AUTH ANONYMOUS 527562792044427573\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
38
+ ["NEGOTIATE_UNIX_FD\r\n", "ERROR not for anonymous\r\n"],
39
+ ["BEGIN\r\n"]
40
+ ]
41
+
42
+ expect { subject.authenticate }.to_not raise_error
43
+ end
44
+ end
45
+
46
+ context "with EXTERNAL" do
47
+ let(:subject) { described_class.new(socket, [DBus::Authentication::External]) }
48
+
49
+ it "authentication passes, and address_uuid is set" do
50
+ expect_protocol [
51
+ ["AUTH EXTERNAL 393939\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
52
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
53
+ ["BEGIN\r\n"]
54
+ ]
55
+
56
+ expect { subject.authenticate }.to_not raise_error
57
+ expect(subject.address_uuid).to eq "ffffffffffffffffffffffffffffffff"
58
+ end
59
+
60
+ context "when the server says superfluous things before an OK" do
61
+ it "authentication passes" do
62
+ expect_protocol [
63
+ ["AUTH EXTERNAL 393939\r\n", "WOULD_YOU_LIKE_SOME_TEA\r\n"],
64
+ ["ERROR\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
65
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
66
+ ["BEGIN\r\n"]
67
+ ]
68
+
69
+ expect { subject.authenticate }.to_not raise_error
70
+ end
71
+ end
72
+
73
+ context "when the server messes up NEGOTIATE_UNIX_FD" do
74
+ it "authentication fails orderly" do
75
+ expect_protocol [
76
+ ["AUTH EXTERNAL 393939\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
77
+ ["NEGOTIATE_UNIX_FD\r\n", "I_DONT_NEGOTIATE_WITH_TENORISTS\r\n"]
78
+ ]
79
+
80
+ allow(socket).to receive(:close) # want to get rid of this
81
+ # TODO: quote the server error message?
82
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /Unknown server reply/)
83
+ end
84
+ end
85
+
86
+ context "when the server replies with ERROR" do
87
+ it "authentication fails orderly" do
88
+ expect_protocol [
89
+ ["AUTH EXTERNAL 393939\r\n", "ERROR something failed\r\n"],
90
+ ["CANCEL\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"]
91
+ ]
92
+
93
+ allow(socket).to receive(:close) # want to get rid of this
94
+ # TODO: quote the server error message?
95
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /exhausted/)
96
+ end
97
+ end
98
+ end
99
+
100
+ context "with EXTERNAL without uid" do
101
+ let(:subject) do
102
+ described_class.new(socket, [DBus::Authentication::External, DBus::Authentication::ExternalWithoutUid])
103
+ end
104
+
105
+ it "authentication passes" do
106
+ expect_protocol [
107
+ ["AUTH EXTERNAL 393939\r\n", "REJECTED EXTERNAL\r\n"],
108
+ # this succeeds when we connect to a privileged container,
109
+ # where outside-non-root becomes inside-root
110
+ ["AUTH EXTERNAL\r\n", "DATA\r\n"],
111
+ ["DATA\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
112
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
113
+ ["BEGIN\r\n"]
114
+ ]
115
+
116
+ expect { subject.authenticate }.to_not raise_error
117
+ end
118
+ end
119
+
120
+ context "with a rejected mechanism and then EXTERNAL" do
121
+ let(:rejected_mechanism) do
122
+ double("Mechanism", name: "WIMP", call: [:MechContinue, "I expect to be rejected"])
123
+ end
124
+
125
+ let(:subject) { described_class.new(socket, [rejected_mechanism, DBus::Authentication::External]) }
126
+
127
+ it "authentication eventually passes" do
128
+ expect_protocol [
129
+ [/^AUTH WIMP .*\r\n/, "REJECTED EXTERNAL\r\n"],
130
+ ["AUTH EXTERNAL 393939\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
131
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
132
+ ["BEGIN\r\n"]
133
+ ]
134
+
135
+ expect { subject.authenticate }.to_not raise_error
136
+ end
137
+ end
138
+
139
+ context "with a DATA-using mechanism" do
140
+ let(:mechanism) do
141
+ double("Mechanism", name: "CHALLENGE_ME", call: [:MechContinue, "1"])
142
+ end
143
+
144
+ # try it twice to test calling #use_next_mechanism
145
+ let(:subject) { described_class.new(socket, [mechanism, mechanism]) }
146
+
147
+ it "authentication fails orderly when the server says ERROR" do
148
+ expect_protocol [
149
+ ["AUTH CHALLENGE_ME 31\r\n", "ERROR something failed\r\n"],
150
+ ["CANCEL\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"],
151
+ ["AUTH CHALLENGE_ME 31\r\n", "ERROR something failed\r\n"],
152
+ ["CANCEL\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"]
153
+ ]
154
+
155
+ allow(socket).to receive(:close) # want to get rid of this
156
+ # TODO: quote the server error message?
157
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /exhausted/)
158
+ end
159
+
160
+ it "authentication fails orderly when the server says ERROR and then changes its mind" do
161
+ expect_protocol [
162
+ ["AUTH CHALLENGE_ME 31\r\n", "ERROR something failed\r\n"],
163
+ ["CANCEL\r\n", "I_CHANGED_MY_MIND please come back\r\n"]
164
+ ]
165
+
166
+ allow(socket).to receive(:close) # want to get rid of this
167
+ # TODO: quote the server error message?
168
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /Unknown.*MIND.*REJECTED/)
169
+ end
170
+
171
+ it "authentication passes when the server says superfluous things before DATA" do
172
+ expect_protocol [
173
+ ["AUTH CHALLENGE_ME 31\r\n", "WOULD_YOU_LIKE_SOME_TEA\r\n"],
174
+ ["ERROR\r\n", "DATA\r\n"],
175
+ ["DATA 31\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
176
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
177
+ ["BEGIN\r\n"]
178
+ ]
179
+
180
+ expect { subject.authenticate }.to_not raise_error
181
+ end
182
+
183
+ it "authentication passes when the server decides not to need the DATA" do
184
+ expect_protocol [
185
+ ["AUTH CHALLENGE_ME 31\r\n", "OK ffffffffffffffffffffffffffffffff\r\n"],
186
+ ["NEGOTIATE_UNIX_FD\r\n", "AGREE_UNIX_FD\r\n"],
187
+ ["BEGIN\r\n"]
188
+ ]
189
+
190
+ expect { subject.authenticate }.to_not raise_error
191
+ end
192
+ end
193
+
194
+ context "with a mechanism returning :MechError" do
195
+ let(:fallible_mechanism) do
196
+ double(name: "FALLIBLE", call: [:MechError, "not my best day"])
197
+ end
198
+
199
+ let(:subject) { described_class.new(socket, [fallible_mechanism]) }
200
+
201
+ it "authentication fails orderly" do
202
+ expect_protocol [
203
+ ["ERROR not my best day\r\n", "REJECTED DBUS_COOKIE_SHA1\r\n"]
204
+ ]
205
+
206
+ allow(socket).to receive(:close) # want to get rid of thise
207
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /exhausted/)
208
+ end
209
+ end
210
+
211
+ context "with a badly implemented mechanism" do
212
+ let(:buggy_mechanism) do
213
+ double(name: "buggy", call: [:smurf, nil])
214
+ end
215
+
216
+ let(:subject) { described_class.new(socket, [buggy_mechanism]) }
217
+
218
+ it "authentication fails before protoxol is exchanged" do
219
+ expect(subject).to_not receive(:write_line)
220
+ expect(subject).to_not receive(:read_line)
221
+
222
+ expect { subject.authenticate }.to raise_error(DBus::AuthenticationFailed, /smurf/)
223
+ end
224
+ end
225
+ end
data/spec/binding_spec.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  # Test the binding of dbus concepts to ruby concepts
3
5
  require_relative "spec_helper"
4
6
 
@@ -1,9 +1,13 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  # Test the bus class
3
5
  require_relative "spec_helper"
4
6
 
5
7
  require "rubygems"
6
- require "nokogiri"
8
+ # If we have nokogiri, rexml is normally omitted
9
+ # but here we include it for test coverage
10
+ require "rexml"
7
11
  require "dbus"
8
12
 
9
13
  describe "BusAndXmlBackendTest" do
@@ -0,0 +1,93 @@
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
+ it "raises NameRequestError... too late" do
25
+ name = "org.ruby.service"
26
+ expect do
27
+ bus.request_service(name)
28
+ _unrelated_call = bus.proxy.GetId.first
29
+ end.to raise_error(DBus::Connection::NameRequestError)
30
+ # The call fails but it means we did not get the name RIGHT AWAY
31
+ # but we are still queued to get it as soon as the current owner
32
+ # gives it up.
33
+ # So even now we have to the bus to remove us from the queue
34
+ bus.proxy.ReleaseName(name)
35
+ end
36
+ end
37
+
38
+ # This only works with our special bus setup
39
+ context "when we're not allowed to own the name", tag_limited_bus: true do
40
+ it "raises an error... too late" do
41
+ name = "org.rubygems.ruby_dbus.NobodyCanOwnThisName"
42
+ expect do
43
+ bus.request_service(name)
44
+ _unrelated_call = bus.proxy.GetId.first
45
+ end.to raise_error(DBus::Error, /not allowed to own the service/)
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "#request_name", tag_bus: true do
51
+ context "when the name request succeeds" do
52
+ it "returns a success code" do
53
+ name = "org.rubygems.ruby_dbus.RequestNameTest"
54
+ expect(bus.request_name(name)).to eq DBus::Connection::REQUEST_NAME_REPLY_PRIMARY_OWNER
55
+ # second time, considered also a success
56
+ expect(bus.request_name(name)).to eq DBus::Connection::REQUEST_NAME_REPLY_ALREADY_OWNER
57
+ bus.release_name(name)
58
+ end
59
+ end
60
+
61
+ context "when the name is taken already", tag_service: true do
62
+ it "raises NameRequestError" do
63
+ name = "org.ruby.service"
64
+ expect do
65
+ bus.request_name(name)
66
+ end.to raise_error(DBus::Connection::NameRequestError)
67
+ end
68
+ end
69
+
70
+ context "when the name is taken already but we request queuing", tag_service: true do
71
+ it "raises NameRequestError but we are queued" do
72
+ name = "org.ruby.service"
73
+ owning = nil
74
+ # TODO: we do not expect the handlers to run
75
+ bus.on_name_acquired { owning = true }
76
+ bus.on_name_lost { owning = false }
77
+ expect do
78
+ bus.request_name(name, queue: true)
79
+ end.to raise_error(DBus::Connection::NameRequestError)
80
+ expect(bus.release_name(name)).to eq DBus::BusConnection::RELEASE_NAME_REPLY_RELEASED
81
+ end
82
+ end
83
+
84
+ context "when we're not allowed to own the name", tag_limited_bus: true do
85
+ it "raises an error" do
86
+ name = "org.rubygems.ruby_dbus.NobodyCanOwnThisName"
87
+ expect do
88
+ bus.request_name(name)
89
+ end.to raise_error(DBus::Error, /not allowed to own the service/)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative "spec_helper"
3
5
  require "dbus"
4
6
 
@@ -17,7 +19,7 @@ describe DBus::BusName do
17
19
  expect(described_class.valid?("Empty.Last.Component.")).to be_falsey
18
20
  expect(described_class.valid?("Invalid.Ch@r@cter")).to be_falsey
19
21
  expect(described_class.valid?("/Invalid-Character")).to be_falsey
20
- long_name = "a." + ("long." * 100) + "name"
22
+ long_name = "a.#{"long." * 100}name"
21
23
  expect(described_class.valid?(long_name)).to be_falsey
22
24
  expect(described_class.valid?("org.7_zip.Archiver")).to be_falsey
23
25
  end
data/spec/bus_spec.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  # Test the bus class
3
5
  require_relative "spec_helper"
4
6
 
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  require_relative "spec_helper"
3
5
 
4
6
  require "dbus"
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env rspec
2
+ # frozen_string_literal: true
3
+
2
4
  # Test that a client survives various error cases
3
5
  require_relative "spec_helper"
4
6
  require "dbus"
@@ -12,14 +14,14 @@ describe "ClientRobustnessTest" do
12
14
  context "when the bus name is invalid" do
13
15
  it "tells the user the bus name is invalid" do
14
16
  # user mistake, should be "org.ruby.service"
15
- expect { @bus.service(".org.ruby.service") }.to raise_error(DBus::Error)
17
+ expect { @bus.service(".org.ruby.service") }.to raise_error(DBus::Error, /Invalid bus name/)
16
18
  end
17
19
  end
18
20
 
19
21
  context "when the object path is invalid" do
20
22
  it "tells the user the path is invalid" do
21
23
  # user mistake, should be "/org/ruby/MyInstance"
22
- expect { @svc.object("org.ruby.MyInstance") }.to raise_error(DBus::Error)
24
+ expect { @svc.object("org.ruby.MyInstance") }.to raise_error(DBus::Error, /Invalid object path/)
23
25
  end
24
26
  end
25
27
  end