Avoid crashing when going back/forward to debug URLs on a sad WebUI tab.
[chromium-blink-merge.git] / dbus / bus_unittest.cc
blob27d9bb2e3e029822ef5c4be9a796d524aa1b0e28
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "dbus/bus.h"
7 #include "base/bind.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/threading/thread.h"
12 #include "dbus/exported_object.h"
13 #include "dbus/object_path.h"
14 #include "dbus/object_proxy.h"
15 #include "dbus/scoped_dbus_error.h"
16 #include "dbus/test_service.h"
18 #include "testing/gtest/include/gtest/gtest.h"
20 namespace dbus {
22 namespace {
24 // Test helper for BusTest.ListenForServiceOwnerChange that wraps a
25 // base::RunLoop. At Run() time, the caller pass in the expected number of
26 // quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop
27 // if the expected number of quit calls have been reached.
28 class RunLoopWithExpectedCount {
29 public:
30 RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {}
31 ~RunLoopWithExpectedCount() {}
33 void Run(int expected_quit_calls) {
34 DCHECK_EQ(0, expected_quit_calls_);
35 DCHECK_EQ(0, actual_quit_calls_);
36 expected_quit_calls_ = expected_quit_calls;
37 run_loop_.reset(new base::RunLoop());
38 run_loop_->Run();
41 void QuitIfConditionIsSatisified() {
42 if (++actual_quit_calls_ != expected_quit_calls_)
43 return;
44 run_loop_->Quit();
45 expected_quit_calls_ = 0;
46 actual_quit_calls_ = 0;
49 private:
50 scoped_ptr<base::RunLoop> run_loop_;
51 int expected_quit_calls_;
52 int actual_quit_calls_;
54 DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount);
57 // Test helper for BusTest.ListenForServiceOwnerChange.
58 void OnServiceOwnerChanged(RunLoopWithExpectedCount* run_loop_state,
59 std::string* service_owner,
60 int* num_of_owner_changes,
61 const std::string& new_service_owner) {
62 *service_owner = new_service_owner;
63 ++(*num_of_owner_changes);
64 run_loop_state->QuitIfConditionIsSatisified();
67 } // namespace
69 TEST(BusTest, GetObjectProxy) {
70 Bus::Options options;
71 scoped_refptr<Bus> bus = new Bus(options);
73 ObjectProxy* object_proxy1 =
74 bus->GetObjectProxy("org.chromium.TestService",
75 ObjectPath("/org/chromium/TestObject"));
76 ASSERT_TRUE(object_proxy1);
78 // This should return the same object.
79 ObjectProxy* object_proxy2 =
80 bus->GetObjectProxy("org.chromium.TestService",
81 ObjectPath("/org/chromium/TestObject"));
82 ASSERT_TRUE(object_proxy2);
83 EXPECT_EQ(object_proxy1, object_proxy2);
85 // This should not.
86 ObjectProxy* object_proxy3 =
87 bus->GetObjectProxy(
88 "org.chromium.TestService",
89 ObjectPath("/org/chromium/DifferentTestObject"));
90 ASSERT_TRUE(object_proxy3);
91 EXPECT_NE(object_proxy1, object_proxy3);
93 bus->ShutdownAndBlock();
96 TEST(BusTest, GetObjectProxyIgnoreUnknownService) {
97 Bus::Options options;
98 scoped_refptr<Bus> bus = new Bus(options);
100 ObjectProxy* object_proxy1 =
101 bus->GetObjectProxyWithOptions(
102 "org.chromium.TestService",
103 ObjectPath("/org/chromium/TestObject"),
104 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
105 ASSERT_TRUE(object_proxy1);
107 // This should return the same object.
108 ObjectProxy* object_proxy2 =
109 bus->GetObjectProxyWithOptions(
110 "org.chromium.TestService",
111 ObjectPath("/org/chromium/TestObject"),
112 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
113 ASSERT_TRUE(object_proxy2);
114 EXPECT_EQ(object_proxy1, object_proxy2);
116 // This should not.
117 ObjectProxy* object_proxy3 =
118 bus->GetObjectProxyWithOptions(
119 "org.chromium.TestService",
120 ObjectPath("/org/chromium/DifferentTestObject"),
121 ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
122 ASSERT_TRUE(object_proxy3);
123 EXPECT_NE(object_proxy1, object_proxy3);
125 bus->ShutdownAndBlock();
128 TEST(BusTest, RemoveObjectProxy) {
129 // Setup the current thread's MessageLoop.
130 base::MessageLoop message_loop;
132 // Start the D-Bus thread.
133 base::Thread::Options thread_options;
134 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
135 base::Thread dbus_thread("D-Bus thread");
136 dbus_thread.StartWithOptions(thread_options);
138 // Create the bus.
139 Bus::Options options;
140 options.dbus_task_runner = dbus_thread.task_runner();
141 scoped_refptr<Bus> bus = new Bus(options);
142 ASSERT_FALSE(bus->shutdown_completed());
144 // Try to remove a non existant object proxy should return false.
145 ASSERT_FALSE(
146 bus->RemoveObjectProxy("org.chromium.TestService",
147 ObjectPath("/org/chromium/TestObject"),
148 base::Bind(&base::DoNothing)));
150 ObjectProxy* object_proxy1 =
151 bus->GetObjectProxy("org.chromium.TestService",
152 ObjectPath("/org/chromium/TestObject"));
153 ASSERT_TRUE(object_proxy1);
155 // Increment the reference count to the object proxy to avoid destroying it
156 // while removing the object.
157 object_proxy1->AddRef();
159 // Remove the object from the bus. This will invalidate any other usage of
160 // object_proxy1 other than destroy it. We keep this object for a comparison
161 // at a later time.
162 ASSERT_TRUE(
163 bus->RemoveObjectProxy("org.chromium.TestService",
164 ObjectPath("/org/chromium/TestObject"),
165 base::Bind(&base::DoNothing)));
167 // This should return a different object because the first object was removed
168 // from the bus, but not deleted from memory.
169 ObjectProxy* object_proxy2 =
170 bus->GetObjectProxy("org.chromium.TestService",
171 ObjectPath("/org/chromium/TestObject"));
172 ASSERT_TRUE(object_proxy2);
174 // Compare the new object with the first object. The first object still exists
175 // thanks to the increased reference.
176 EXPECT_NE(object_proxy1, object_proxy2);
178 // Release object_proxy1.
179 object_proxy1->Release();
181 // Shut down synchronously.
182 bus->ShutdownOnDBusThreadAndBlock();
183 EXPECT_TRUE(bus->shutdown_completed());
184 dbus_thread.Stop();
187 TEST(BusTest, GetExportedObject) {
188 Bus::Options options;
189 scoped_refptr<Bus> bus = new Bus(options);
191 ExportedObject* object_proxy1 =
192 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
193 ASSERT_TRUE(object_proxy1);
195 // This should return the same object.
196 ExportedObject* object_proxy2 =
197 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
198 ASSERT_TRUE(object_proxy2);
199 EXPECT_EQ(object_proxy1, object_proxy2);
201 // This should not.
202 ExportedObject* object_proxy3 =
203 bus->GetExportedObject(
204 ObjectPath("/org/chromium/DifferentTestObject"));
205 ASSERT_TRUE(object_proxy3);
206 EXPECT_NE(object_proxy1, object_proxy3);
208 bus->ShutdownAndBlock();
211 TEST(BusTest, UnregisterExportedObject) {
212 // Start the D-Bus thread.
213 base::Thread::Options thread_options;
214 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
215 base::Thread dbus_thread("D-Bus thread");
216 dbus_thread.StartWithOptions(thread_options);
218 // Create the bus.
219 Bus::Options options;
220 options.dbus_task_runner = dbus_thread.task_runner();
221 scoped_refptr<Bus> bus = new Bus(options);
222 ASSERT_FALSE(bus->shutdown_completed());
224 ExportedObject* object_proxy1 =
225 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
226 ASSERT_TRUE(object_proxy1);
228 // Increment the reference count to the object proxy to avoid destroying it
229 // calling UnregisterExportedObject. This ensures the dbus::ExportedObject is
230 // not freed from memory. See http://crbug.com/137846 for details.
231 object_proxy1->AddRef();
233 bus->UnregisterExportedObject(ObjectPath("/org/chromium/TestObject"));
235 // This should return a new object because the object_proxy1 is still in
236 // alloc'ed memory.
237 ExportedObject* object_proxy2 =
238 bus->GetExportedObject(ObjectPath("/org/chromium/TestObject"));
239 ASSERT_TRUE(object_proxy2);
240 EXPECT_NE(object_proxy1, object_proxy2);
242 // Release the incremented reference.
243 object_proxy1->Release();
245 // Shut down synchronously.
246 bus->ShutdownOnDBusThreadAndBlock();
247 EXPECT_TRUE(bus->shutdown_completed());
248 dbus_thread.Stop();
251 TEST(BusTest, ShutdownAndBlock) {
252 Bus::Options options;
253 scoped_refptr<Bus> bus = new Bus(options);
254 ASSERT_FALSE(bus->shutdown_completed());
256 // Shut down synchronously.
257 bus->ShutdownAndBlock();
258 EXPECT_TRUE(bus->shutdown_completed());
261 TEST(BusTest, ShutdownAndBlockWithDBusThread) {
262 // Start the D-Bus thread.
263 base::Thread::Options thread_options;
264 thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
265 base::Thread dbus_thread("D-Bus thread");
266 dbus_thread.StartWithOptions(thread_options);
268 // Create the bus.
269 Bus::Options options;
270 options.dbus_task_runner = dbus_thread.task_runner();
271 scoped_refptr<Bus> bus = new Bus(options);
272 ASSERT_FALSE(bus->shutdown_completed());
274 // Shut down synchronously.
275 bus->ShutdownOnDBusThreadAndBlock();
276 EXPECT_TRUE(bus->shutdown_completed());
277 dbus_thread.Stop();
280 TEST(BusTest, DoubleAddAndRemoveMatch) {
281 Bus::Options options;
282 scoped_refptr<Bus> bus = new Bus(options);
283 ScopedDBusError error;
285 bus->Connect();
287 // Adds the same rule twice.
288 bus->AddMatch(
289 "type='signal',interface='org.chromium.TestService',path='/'",
290 error.get());
291 ASSERT_FALSE(error.is_set());
293 bus->AddMatch(
294 "type='signal',interface='org.chromium.TestService',path='/'",
295 error.get());
296 ASSERT_FALSE(error.is_set());
298 // Removes the same rule twice.
299 ASSERT_TRUE(bus->RemoveMatch(
300 "type='signal',interface='org.chromium.TestService',path='/'",
301 error.get()));
302 ASSERT_FALSE(error.is_set());
304 // The rule should be still in the bus since it was removed only once.
305 // A second removal shouldn't give an error.
306 ASSERT_TRUE(bus->RemoveMatch(
307 "type='signal',interface='org.chromium.TestService',path='/'",
308 error.get()));
309 ASSERT_FALSE(error.is_set());
311 // A third attemp to remove the same rule should fail.
312 ASSERT_FALSE(bus->RemoveMatch(
313 "type='signal',interface='org.chromium.TestService',path='/'",
314 error.get()));
316 bus->ShutdownAndBlock();
319 TEST(BusTest, ListenForServiceOwnerChange) {
320 // Setup the current thread's MessageLoop. Must be of TYPE_IO for the
321 // listeners to work.
322 base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
323 RunLoopWithExpectedCount run_loop_state;
325 // Create the bus.
326 Bus::Options bus_options;
327 scoped_refptr<Bus> bus = new Bus(bus_options);
329 // Add a listener.
330 std::string service_owner1;
331 int num_of_owner_changes1 = 0;
332 Bus::GetServiceOwnerCallback callback1 =
333 base::Bind(&OnServiceOwnerChanged,
334 &run_loop_state,
335 &service_owner1,
336 &num_of_owner_changes1);
337 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
338 // This should be a no-op.
339 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1);
340 base::RunLoop().RunUntilIdle();
342 // Nothing has happened yet. Check initial state.
343 EXPECT_TRUE(service_owner1.empty());
344 EXPECT_EQ(0, num_of_owner_changes1);
346 // Make an ownership change.
347 ASSERT_TRUE(bus->RequestOwnershipAndBlock("org.chromium.TestService",
348 Bus::REQUIRE_PRIMARY));
349 run_loop_state.Run(1);
352 // Get the current service owner and check to make sure the listener got
353 // the right value.
354 std::string current_service_owner =
355 bus->GetServiceOwnerAndBlock("org.chromium.TestService",
356 Bus::REPORT_ERRORS);
357 ASSERT_FALSE(current_service_owner.empty());
359 // Make sure the listener heard about the new owner.
360 EXPECT_EQ(current_service_owner, service_owner1);
362 // Test the second ListenForServiceOwnerChange() above is indeed a no-op.
363 EXPECT_EQ(1, num_of_owner_changes1);
366 // Add a second listener.
367 std::string service_owner2;
368 int num_of_owner_changes2 = 0;
369 Bus::GetServiceOwnerCallback callback2 =
370 base::Bind(&OnServiceOwnerChanged,
371 &run_loop_state,
372 &service_owner2,
373 &num_of_owner_changes2);
374 bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2);
375 base::RunLoop().RunUntilIdle();
377 // Release the ownership and make sure the service owner listeners fire with
378 // the right values and the right number of times.
379 ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService"));
380 run_loop_state.Run(2);
382 EXPECT_TRUE(service_owner1.empty());
383 EXPECT_TRUE(service_owner2.empty());
384 EXPECT_EQ(2, num_of_owner_changes1);
385 EXPECT_EQ(1, num_of_owner_changes2);
387 // Unlisten so shutdown can proceed correctly.
388 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1);
389 bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2);
390 base::RunLoop().RunUntilIdle();
392 // Shut down synchronously.
393 bus->ShutdownAndBlock();
394 EXPECT_TRUE(bus->shutdown_completed());
397 TEST(BusTest, GetConnectionName) {
398 Bus::Options options;
399 scoped_refptr<Bus> bus = new Bus(options);
401 // Connection name is empty since bus is not connected.
402 EXPECT_FALSE(bus->is_connected());
403 EXPECT_TRUE(bus->GetConnectionName().empty());
405 // Connect bus to D-Bus.
406 bus->Connect();
408 // Connection name is not empty after connection is established.
409 EXPECT_TRUE(bus->is_connected());
410 EXPECT_FALSE(bus->GetConnectionName().empty());
412 // Shut down synchronously.
413 bus->ShutdownAndBlock();
414 EXPECT_TRUE(bus->shutdown_completed());
417 } // namespace dbus