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/test_service.h"
8 #include "base/test/test_timeouts.h"
9 #include "base/threading/platform_thread.h"
11 #include "dbus/exported_object.h"
12 #include "dbus/message.h"
13 #include "dbus/object_manager.h"
14 #include "dbus/object_path.h"
15 #include "dbus/property.h"
19 void EmptyCallback(bool /* success */) {
26 // Echo, SlowEcho, AsyncEcho, BrokenMethod, GetAll, Get, Set, PerformAction,
28 const int TestService::kNumMethodsToExport
= 9;
30 TestService::Options::Options()
31 : request_ownership_options(Bus::REQUIRE_PRIMARY
) {
34 TestService::Options::~Options() {
37 TestService::TestService(const Options
& options
)
38 : base::Thread("TestService"),
39 request_ownership_options_(options
.request_ownership_options
),
40 dbus_task_runner_(options
.dbus_task_runner
),
41 on_all_methods_exported_(false, false),
42 num_exported_methods_(0) {
45 TestService::~TestService() {
49 bool TestService::StartService() {
50 base::Thread::Options thread_options
;
51 thread_options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
52 return StartWithOptions(thread_options
);
55 bool TestService::WaitUntilServiceIsStarted() {
56 const base::TimeDelta
timeout(TestTimeouts::action_max_timeout());
57 // Wait until all methods are exported.
58 return on_all_methods_exported_
.TimedWait(timeout
);
61 void TestService::ShutdownAndBlock() {
62 message_loop()->PostTask(
64 base::Bind(&TestService::ShutdownAndBlockInternal
,
65 base::Unretained(this)));
68 bool TestService::HasDBusThread() {
69 return bus_
->HasDBusThread();
72 void TestService::ShutdownAndBlockInternal() {
74 bus_
->ShutdownOnDBusThreadAndBlock();
76 bus_
->ShutdownAndBlock();
79 void TestService::SendTestSignal(const std::string
& message
) {
80 message_loop()->PostTask(
82 base::Bind(&TestService::SendTestSignalInternal
,
83 base::Unretained(this),
87 void TestService::SendTestSignalFromRoot(const std::string
& message
) {
88 message_loop()->PostTask(
90 base::Bind(&TestService::SendTestSignalFromRootInternal
,
91 base::Unretained(this),
95 void TestService::SendTestSignalInternal(const std::string
& message
) {
96 Signal
signal("org.chromium.TestInterface", "Test");
97 MessageWriter
writer(&signal
);
98 writer
.AppendString(message
);
99 exported_object_
->SendSignal(&signal
);
102 void TestService::SendTestSignalFromRootInternal(const std::string
& message
) {
103 Signal
signal("org.chromium.TestInterface", "Test");
104 MessageWriter
writer(&signal
);
105 writer
.AppendString(message
);
107 bus_
->RequestOwnership("org.chromium.TestService",
108 request_ownership_options_
,
109 base::Bind(&TestService::OnOwnership
,
110 base::Unretained(this),
111 base::Bind(&EmptyCallback
)));
113 // Use "/" just like dbus-send does.
114 ExportedObject
* root_object
= bus_
->GetExportedObject(ObjectPath("/"));
115 root_object
->SendSignal(&signal
);
118 void TestService::RequestOwnership(base::Callback
<void(bool)> callback
) {
119 message_loop()->PostTask(
121 base::Bind(&TestService::RequestOwnershipInternal
,
122 base::Unretained(this),
126 void TestService::RequestOwnershipInternal(
127 base::Callback
<void(bool)> callback
) {
128 bus_
->RequestOwnership("org.chromium.TestService",
129 request_ownership_options_
,
130 base::Bind(&TestService::OnOwnership
,
131 base::Unretained(this),
135 void TestService::OnOwnership(base::Callback
<void(bool)> callback
,
136 const std::string
& service_name
,
138 has_ownership_
= success
;
139 LOG_IF(ERROR
, !success
) << "Failed to own: " << service_name
;
140 callback
.Run(success
);
143 void TestService::OnExported(const std::string
& interface_name
,
144 const std::string
& method_name
,
147 LOG(ERROR
) << "Failed to export: " << interface_name
<< "."
149 // Returning here will make WaitUntilServiceIsStarted() to time out
154 ++num_exported_methods_
;
155 if (num_exported_methods_
== kNumMethodsToExport
)
156 on_all_methods_exported_
.Signal();
159 void TestService::Run(base::MessageLoop
* message_loop
) {
160 Bus::Options bus_options
;
161 bus_options
.bus_type
= Bus::SESSION
;
162 bus_options
.connection_type
= Bus::PRIVATE
;
163 bus_options
.dbus_task_runner
= dbus_task_runner_
;
164 bus_
= new Bus(bus_options
);
166 bus_
->RequestOwnership("org.chromium.TestService",
167 request_ownership_options_
,
168 base::Bind(&TestService::OnOwnership
,
169 base::Unretained(this),
170 base::Bind(&EmptyCallback
)));
172 exported_object_
= bus_
->GetExportedObject(
173 ObjectPath("/org/chromium/TestObject"));
176 exported_object_
->ExportMethod(
177 "org.chromium.TestInterface",
179 base::Bind(&TestService::Echo
,
180 base::Unretained(this)),
181 base::Bind(&TestService::OnExported
,
182 base::Unretained(this)));
185 exported_object_
->ExportMethod(
186 "org.chromium.TestInterface",
188 base::Bind(&TestService::SlowEcho
,
189 base::Unretained(this)),
190 base::Bind(&TestService::OnExported
,
191 base::Unretained(this)));
194 exported_object_
->ExportMethod(
195 "org.chromium.TestInterface",
197 base::Bind(&TestService::AsyncEcho
,
198 base::Unretained(this)),
199 base::Bind(&TestService::OnExported
,
200 base::Unretained(this)));
203 exported_object_
->ExportMethod(
204 "org.chromium.TestInterface",
206 base::Bind(&TestService::BrokenMethod
,
207 base::Unretained(this)),
208 base::Bind(&TestService::OnExported
,
209 base::Unretained(this)));
212 exported_object_
->ExportMethod(
213 "org.chromium.TestInterface",
215 base::Bind(&TestService::PerformAction
,
216 base::Unretained(this)),
217 base::Bind(&TestService::OnExported
,
218 base::Unretained(this)));
221 exported_object_
->ExportMethod(
222 kPropertiesInterface
,
224 base::Bind(&TestService::GetAllProperties
,
225 base::Unretained(this)),
226 base::Bind(&TestService::OnExported
,
227 base::Unretained(this)));
230 exported_object_
->ExportMethod(
231 kPropertiesInterface
,
233 base::Bind(&TestService::GetProperty
,
234 base::Unretained(this)),
235 base::Bind(&TestService::OnExported
,
236 base::Unretained(this)));
239 exported_object_
->ExportMethod(
240 kPropertiesInterface
,
242 base::Bind(&TestService::SetProperty
,
243 base::Unretained(this)),
244 base::Bind(&TestService::OnExported
,
245 base::Unretained(this)));
248 exported_object_manager_
= bus_
->GetExportedObject(
249 ObjectPath("/org/chromium/TestService"));
251 exported_object_manager_
->ExportMethod(
252 kObjectManagerInterface
,
253 kObjectManagerGetManagedObjects
,
254 base::Bind(&TestService::GetManagedObjects
,
255 base::Unretained(this)),
256 base::Bind(&TestService::OnExported
,
257 base::Unretained(this)));
260 // Just print an error message as we don't want to crash tests.
261 // Tests will fail at a call to WaitUntilServiceIsStarted().
262 if (num_methods
!= kNumMethodsToExport
) {
263 LOG(ERROR
) << "The number of methods does not match";
268 void TestService::Echo(MethodCall
* method_call
,
269 ExportedObject::ResponseSender response_sender
) {
270 MessageReader
reader(method_call
);
271 std::string text_message
;
272 if (!reader
.PopString(&text_message
)) {
273 response_sender
.Run(scoped_ptr
<Response
>());
277 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
278 MessageWriter
writer(response
.get());
279 writer
.AppendString(text_message
);
280 response_sender
.Run(response
.Pass());
283 void TestService::SlowEcho(MethodCall
* method_call
,
284 ExportedObject::ResponseSender response_sender
) {
285 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
286 Echo(method_call
, response_sender
);
289 void TestService::AsyncEcho(MethodCall
* method_call
,
290 ExportedObject::ResponseSender response_sender
) {
291 // Schedule a call to Echo() to send an asynchronous response after we return.
292 message_loop()->PostDelayedTask(FROM_HERE
,
293 base::Bind(&TestService::Echo
,
294 base::Unretained(this),
297 TestTimeouts::tiny_timeout());
300 void TestService::BrokenMethod(MethodCall
* method_call
,
301 ExportedObject::ResponseSender response_sender
) {
302 response_sender
.Run(scoped_ptr
<Response
>());
306 void TestService::GetAllProperties(
307 MethodCall
* method_call
,
308 ExportedObject::ResponseSender response_sender
) {
309 MessageReader
reader(method_call
);
310 std::string interface
;
311 if (!reader
.PopString(&interface
)) {
312 response_sender
.Run(scoped_ptr
<Response
>());
316 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
317 MessageWriter
writer(response
.get());
319 AddPropertiesToWriter(&writer
);
321 response_sender
.Run(response
.Pass());
324 void TestService::GetProperty(MethodCall
* method_call
,
325 ExportedObject::ResponseSender response_sender
) {
326 MessageReader
reader(method_call
);
327 std::string interface
;
328 if (!reader
.PopString(&interface
)) {
329 response_sender
.Run(scoped_ptr
<Response
>());
334 if (!reader
.PopString(&name
)) {
335 response_sender
.Run(scoped_ptr
<Response
>());
339 if (name
== "Name") {
340 // Return the previous value for the "Name" property:
341 // Variant<"TestService">
342 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
343 MessageWriter
writer(response
.get());
345 writer
.AppendVariantOfString("TestService");
347 response_sender
.Run(response
.Pass());
348 } else if (name
== "Version") {
349 // Return a new value for the "Version" property:
351 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
352 MessageWriter
writer(response
.get());
354 writer
.AppendVariantOfInt16(20);
356 response_sender
.Run(response
.Pass());
357 } else if (name
== "Methods") {
358 // Return the previous value for the "Methods" property:
359 // Variant<["Echo", "SlowEcho", "AsyncEcho", "BrokenMethod"]>
360 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
361 MessageWriter
writer(response
.get());
362 MessageWriter
variant_writer(NULL
);
363 MessageWriter
variant_array_writer(NULL
);
365 writer
.OpenVariant("as", &variant_writer
);
366 variant_writer
.OpenArray("s", &variant_array_writer
);
367 variant_array_writer
.AppendString("Echo");
368 variant_array_writer
.AppendString("SlowEcho");
369 variant_array_writer
.AppendString("AsyncEcho");
370 variant_array_writer
.AppendString("BrokenMethod");
371 variant_writer
.CloseContainer(&variant_array_writer
);
372 writer
.CloseContainer(&variant_writer
);
374 response_sender
.Run(response
.Pass());
375 } else if (name
== "Objects") {
376 // Return the previous value for the "Objects" property:
377 // Variant<[objectpath:"/TestObjectPath"]>
378 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
379 MessageWriter
writer(response
.get());
380 MessageWriter
variant_writer(NULL
);
381 MessageWriter
variant_array_writer(NULL
);
383 writer
.OpenVariant("ao", &variant_writer
);
384 variant_writer
.OpenArray("o", &variant_array_writer
);
385 variant_array_writer
.AppendObjectPath(ObjectPath("/TestObjectPath"));
386 variant_writer
.CloseContainer(&variant_array_writer
);
387 writer
.CloseContainer(&variant_writer
);
389 response_sender
.Run(response
.Pass());
392 response_sender
.Run(scoped_ptr
<Response
>());
397 void TestService::SetProperty(MethodCall
* method_call
,
398 ExportedObject::ResponseSender response_sender
) {
399 MessageReader
reader(method_call
);
400 std::string interface
;
401 if (!reader
.PopString(&interface
)) {
402 response_sender
.Run(scoped_ptr
<Response
>());
407 if (!reader
.PopString(&name
)) {
408 response_sender
.Run(scoped_ptr
<Response
>());
412 if (name
!= "Name") {
413 response_sender
.Run(scoped_ptr
<Response
>());
418 if (!reader
.PopVariantOfString(&value
)) {
419 response_sender
.Run(scoped_ptr
<Response
>());
423 SendPropertyChangedSignal(value
);
425 response_sender
.Run(Response::FromMethodCall(method_call
));
428 void TestService::PerformAction(
429 MethodCall
* method_call
,
430 ExportedObject::ResponseSender response_sender
) {
431 MessageReader
reader(method_call
);
433 ObjectPath object_path
;
434 if (!reader
.PopString(&action
) || !reader
.PopObjectPath(&object_path
)) {
435 response_sender
.Run(scoped_ptr
<Response
>());
439 if (action
== "AddObject")
440 AddObject(object_path
);
441 else if (action
== "RemoveObject")
442 RemoveObject(object_path
);
444 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
445 response_sender
.Run(response
.Pass());
448 void TestService::GetManagedObjects(
449 MethodCall
* method_call
,
450 ExportedObject::ResponseSender response_sender
) {
451 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
452 MessageWriter
writer(response
.get());
454 // The managed objects response is a dictionary of object paths identifying
455 // the object(s) with a dictionary of strings identifying the interface(s)
456 // they implement and then a dictionary of property values.
458 // Thus this looks something like:
461 // "/org/chromium/TestObject": {
462 // "org.chromium.TestInterface": { /* Properties */ }
467 MessageWriter
array_writer(NULL
);
468 MessageWriter
dict_entry_writer(NULL
);
469 MessageWriter
object_array_writer(NULL
);
470 MessageWriter
object_dict_entry_writer(NULL
);
472 writer
.OpenArray("{oa{sa{sv}}}", &array_writer
);
474 array_writer
.OpenDictEntry(&dict_entry_writer
);
475 dict_entry_writer
.AppendObjectPath(ObjectPath("/org/chromium/TestObject"));
476 dict_entry_writer
.OpenArray("{sa{sv}}", &object_array_writer
);
478 object_array_writer
.OpenDictEntry(&object_dict_entry_writer
);
479 object_dict_entry_writer
.AppendString("org.chromium.TestInterface");
480 AddPropertiesToWriter(&object_dict_entry_writer
);
481 object_array_writer
.CloseContainer(&object_dict_entry_writer
);
483 dict_entry_writer
.CloseContainer(&object_array_writer
);
485 array_writer
.CloseContainer(&dict_entry_writer
);
486 writer
.CloseContainer(&array_writer
);
488 response_sender
.Run(response
.Pass());
491 void TestService::AddPropertiesToWriter(MessageWriter
* writer
) {
492 // The properties response is a dictionary of strings identifying the
493 // property and a variant containing the property value. We return all
494 // of the properties, thus the response is:
497 // "Name": Variant<"TestService">,
498 // "Version": Variant<10>,
499 // "Methods": Variant<["Echo", "SlowEcho", "AsyncEcho", "BrokenMethod"]>,
500 // "Objects": Variant<[objectpath:"/TestObjectPath"]>
503 MessageWriter
array_writer(NULL
);
504 MessageWriter
dict_entry_writer(NULL
);
505 MessageWriter
variant_writer(NULL
);
506 MessageWriter
variant_array_writer(NULL
);
508 writer
->OpenArray("{sv}", &array_writer
);
510 array_writer
.OpenDictEntry(&dict_entry_writer
);
511 dict_entry_writer
.AppendString("Name");
512 dict_entry_writer
.AppendVariantOfString("TestService");
513 array_writer
.CloseContainer(&dict_entry_writer
);
515 array_writer
.OpenDictEntry(&dict_entry_writer
);
516 dict_entry_writer
.AppendString("Version");
517 dict_entry_writer
.AppendVariantOfInt16(10);
518 array_writer
.CloseContainer(&dict_entry_writer
);
520 array_writer
.OpenDictEntry(&dict_entry_writer
);
521 dict_entry_writer
.AppendString("Methods");
522 dict_entry_writer
.OpenVariant("as", &variant_writer
);
523 variant_writer
.OpenArray("s", &variant_array_writer
);
524 variant_array_writer
.AppendString("Echo");
525 variant_array_writer
.AppendString("SlowEcho");
526 variant_array_writer
.AppendString("AsyncEcho");
527 variant_array_writer
.AppendString("BrokenMethod");
528 variant_writer
.CloseContainer(&variant_array_writer
);
529 dict_entry_writer
.CloseContainer(&variant_writer
);
530 array_writer
.CloseContainer(&dict_entry_writer
);
532 array_writer
.OpenDictEntry(&dict_entry_writer
);
533 dict_entry_writer
.AppendString("Objects");
534 dict_entry_writer
.OpenVariant("ao", &variant_writer
);
535 variant_writer
.OpenArray("o", &variant_array_writer
);
536 variant_array_writer
.AppendObjectPath(ObjectPath("/TestObjectPath"));
537 variant_writer
.CloseContainer(&variant_array_writer
);
538 dict_entry_writer
.CloseContainer(&variant_writer
);
539 array_writer
.CloseContainer(&dict_entry_writer
);
541 writer
->CloseContainer(&array_writer
);
544 void TestService::AddObject(const ObjectPath
& object_path
) {
545 message_loop()->PostTask(
547 base::Bind(&TestService::AddObjectInternal
,
548 base::Unretained(this),
552 void TestService::AddObjectInternal(const ObjectPath
& object_path
) {
553 Signal
signal(kObjectManagerInterface
, kObjectManagerInterfacesAdded
);
554 MessageWriter
writer(&signal
);
555 writer
.AppendObjectPath(object_path
);
557 MessageWriter
array_writer(NULL
);
558 MessageWriter
dict_entry_writer(NULL
);
560 writer
.OpenArray("{sa{sv}}", &array_writer
);
561 array_writer
.OpenDictEntry(&dict_entry_writer
);
562 dict_entry_writer
.AppendString("org.chromium.TestInterface");
563 AddPropertiesToWriter(&dict_entry_writer
);
564 array_writer
.CloseContainer(&dict_entry_writer
);
565 writer
.CloseContainer(&array_writer
);
567 exported_object_manager_
->SendSignal(&signal
);
570 void TestService::RemoveObject(const ObjectPath
& object_path
) {
571 message_loop()->PostTask(FROM_HERE
,
572 base::Bind(&TestService::RemoveObjectInternal
,
573 base::Unretained(this),
577 void TestService::RemoveObjectInternal(const ObjectPath
& object_path
) {
578 Signal
signal(kObjectManagerInterface
, kObjectManagerInterfacesRemoved
);
579 MessageWriter
writer(&signal
);
581 writer
.AppendObjectPath(object_path
);
583 std::vector
<std::string
> interfaces
;
584 interfaces
.push_back("org.chromium.TestInterface");
585 writer
.AppendArrayOfStrings(interfaces
);
587 exported_object_manager_
->SendSignal(&signal
);
590 void TestService::SendPropertyChangedSignal(const std::string
& name
) {
591 message_loop()->PostTask(
593 base::Bind(&TestService::SendPropertyChangedSignalInternal
,
594 base::Unretained(this),
598 void TestService::SendPropertyChangedSignalInternal(const std::string
& name
) {
599 Signal
signal(kPropertiesInterface
, kPropertiesChanged
);
600 MessageWriter
writer(&signal
);
601 writer
.AppendString("org.chromium.TestInterface");
603 MessageWriter
array_writer(NULL
);
604 MessageWriter
dict_entry_writer(NULL
);
606 writer
.OpenArray("{sv}", &array_writer
);
607 array_writer
.OpenDictEntry(&dict_entry_writer
);
608 dict_entry_writer
.AppendString("Name");
609 dict_entry_writer
.AppendVariantOfString(name
);
610 array_writer
.CloseContainer(&dict_entry_writer
);
611 writer
.CloseContainer(&array_writer
);
613 exported_object_
->SendSignal(&signal
);