1 // Copyright 2013 The Chromium Authors
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/object_manager.h"
14 #include "base/functional/bind.h"
15 #include "base/memory/raw_ptr.h"
16 #include "base/message_loop/message_pump_type.h"
17 #include "base/run_loop.h"
18 #include "base/task/single_thread_task_runner.h"
19 #include "base/test/task_environment.h"
20 #include "base/threading/thread.h"
21 #include "base/threading/thread_restrictions.h"
22 #include "base/time/time.h"
24 #include "dbus/object_path.h"
25 #include "dbus/object_proxy.h"
26 #include "dbus/property.h"
27 #include "dbus/test_service.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "third_party/abseil-cpp/absl/types/optional.h"
33 // The object manager test exercises the asynchronous APIs in ObjectManager,
34 // and by extension PropertySet and Property<>.
35 class ObjectManagerTest
36 : public testing::Test,
37 public ObjectManager::Interface {
39 ObjectManagerTest() : timeout_expired_(false) {
42 struct Properties : public PropertySet {
43 Property<std::string> name;
44 Property<int16_t> version;
45 Property<std::vector<std::string>> methods;
46 Property<std::vector<ObjectPath>> objects;
48 Properties(ObjectProxy* object_proxy,
49 const std::string& interface_name,
50 PropertyChangedCallback property_changed_callback)
51 : PropertySet(object_proxy, interface_name, property_changed_callback) {
52 RegisterProperty("Name", &name);
53 RegisterProperty("Version", &version);
54 RegisterProperty("Methods", &methods);
55 RegisterProperty("Objects", &objects);
59 PropertySet* CreateProperties(ObjectProxy* object_proxy,
60 const ObjectPath& object_path,
61 const std::string& interface_name) override {
62 Properties* properties = new Properties(
63 object_proxy, interface_name,
64 base::BindRepeating(&ObjectManagerTest::OnPropertyChanged,
65 base::Unretained(this), object_path));
66 return static_cast<PropertySet*>(properties);
69 void SetUp() override {
70 // Make the main thread not to allow IO.
71 disallow_blocking_.emplace();
73 // Start the D-Bus thread.
74 dbus_thread_ = std::make_unique<base::Thread>("D-Bus Thread");
75 base::Thread::Options thread_options;
76 thread_options.message_pump_type = base::MessagePumpType::IO;
77 ASSERT_TRUE(dbus_thread_->StartWithOptions(std::move(thread_options)));
79 // Start the test service, using the D-Bus thread.
80 TestService::Options options;
81 options.dbus_task_runner = dbus_thread_->task_runner();
82 test_service_ = std::make_unique<TestService>(options);
83 ASSERT_TRUE(test_service_->StartService());
84 test_service_->WaitUntilServiceIsStarted();
85 ASSERT_TRUE(test_service_->HasDBusThread());
87 // Create the client, using the D-Bus thread.
88 Bus::Options bus_options;
89 bus_options.bus_type = Bus::SESSION;
90 bus_options.connection_type = Bus::PRIVATE;
91 bus_options.dbus_task_runner = dbus_thread_->task_runner();
92 bus_ = new Bus(bus_options);
93 ASSERT_TRUE(bus_->HasDBusThread());
95 object_manager_ = bus_->GetObjectManager(
96 test_service_->service_name(),
97 ObjectPath("/org/chromium/TestService"));
98 object_manager_->RegisterInterface("org.chromium.TestInterface", this);
103 void TearDown() override {
104 bus_->ShutdownOnDBusThreadAndBlock();
106 // Shut down the service.
107 test_service_->ShutdownAndBlock();
109 // Stopping a thread is considered an IO operation, so do this after
111 disallow_blocking_.reset();
112 test_service_->Stop();
114 base::RunLoop().RunUntilIdle();
117 void MethodCallback(Response* response) {
118 method_callback_called_ = true;
122 // Called from the PropertiesChangedAsObjectsReceived test case. The test will
123 // not run the message loop if it receives the expected PropertiesChanged
124 // signal before the timeout. This method immediately fails the test.
125 void PropertiesChangedTestTimeout() {
126 timeout_expired_ = true;
129 FAIL() << "Never received PropertiesChanged";
133 // Called when an object is added.
134 void ObjectAdded(const ObjectPath& object_path,
135 const std::string& interface_name) override {
136 added_objects_.push_back(std::make_pair(object_path, interface_name));
140 // Called when an object is removed.
141 void ObjectRemoved(const ObjectPath& object_path,
142 const std::string& interface_name) override {
143 removed_objects_.push_back(std::make_pair(object_path, interface_name));
147 // Called when a property value is updated.
148 void OnPropertyChanged(const ObjectPath& object_path,
149 const std::string& name) {
150 // Store the value of the "Name" property if that's the one that
152 Properties* properties = static_cast<Properties*>(
153 object_manager_->GetProperties(
155 "org.chromium.TestInterface"));
156 if (name == properties->name.name())
157 last_name_value_ = properties->name.value();
159 // Store the updated property.
160 updated_properties_.push_back(name);
164 static const size_t kExpectedObjects = 1;
165 static const size_t kExpectedProperties = 4;
167 void WaitForObject() {
168 while (added_objects_.size() < kExpectedObjects ||
169 updated_properties_.size() < kExpectedProperties) {
170 run_loop_ = std::make_unique<base::RunLoop>();
173 for (size_t i = 0; i < kExpectedObjects; ++i)
174 added_objects_.erase(added_objects_.begin());
175 for (size_t i = 0; i < kExpectedProperties; ++i)
176 updated_properties_.erase(updated_properties_.begin());
179 void WaitForRemoveObject() {
180 while (removed_objects_.size() < kExpectedObjects) {
181 run_loop_ = std::make_unique<base::RunLoop>();
184 for (size_t i = 0; i < kExpectedObjects; ++i)
185 removed_objects_.erase(removed_objects_.begin());
188 void WaitForMethodCallback() {
189 run_loop_ = std::make_unique<base::RunLoop>();
191 method_callback_called_ = false;
194 void PerformAction(const std::string& action, const ObjectPath& object_path) {
195 ObjectProxy* object_proxy = bus_->GetObjectProxy(
196 test_service_->service_name(),
197 ObjectPath("/org/chromium/TestObject"));
199 MethodCall method_call("org.chromium.TestInterface", "PerformAction");
200 MessageWriter writer(&method_call);
201 writer.AppendString(action);
202 writer.AppendObjectPath(object_path);
204 object_proxy->CallMethod(&method_call, ObjectProxy::TIMEOUT_USE_DEFAULT,
205 base::BindOnce(&ObjectManagerTest::MethodCallback,
206 base::Unretained(this)));
207 WaitForMethodCallback();
210 base::test::SingleThreadTaskEnvironment task_environment_;
211 absl::optional<base::ScopedDisallowBlocking> disallow_blocking_;
212 std::unique_ptr<base::RunLoop> run_loop_;
213 std::unique_ptr<base::Thread> dbus_thread_;
214 scoped_refptr<Bus> bus_;
215 raw_ptr<ObjectManager> object_manager_;
216 std::unique_ptr<TestService> test_service_;
218 std::string last_name_value_;
219 bool timeout_expired_;
221 std::vector<std::pair<ObjectPath, std::string>> added_objects_;
222 std::vector<std::pair<ObjectPath, std::string>> removed_objects_;
223 std::vector<std::string> updated_properties_;
225 bool method_callback_called_;
229 TEST_F(ObjectManagerTest, InitialObject) {
230 ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
231 ObjectPath("/org/chromium/TestObject"));
232 EXPECT_NE(nullptr, object_proxy);
234 Properties* properties = static_cast<Properties*>(
235 object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
236 "org.chromium.TestInterface"));
237 EXPECT_NE(nullptr, properties);
239 EXPECT_EQ("TestService", properties->name.value());
240 EXPECT_EQ(10, properties->version.value());
242 std::vector<std::string> methods = properties->methods.value();
243 ASSERT_EQ(4U, methods.size());
244 EXPECT_EQ("Echo", methods[0]);
245 EXPECT_EQ("SlowEcho", methods[1]);
246 EXPECT_EQ("AsyncEcho", methods[2]);
247 EXPECT_EQ("BrokenMethod", methods[3]);
249 std::vector<ObjectPath> objects = properties->objects.value();
250 ASSERT_EQ(1U, objects.size());
251 EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]);
254 TEST_F(ObjectManagerTest, UnknownObjectProxy) {
255 ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
256 ObjectPath("/org/chromium/UnknownObject"));
257 EXPECT_EQ(nullptr, object_proxy);
260 TEST_F(ObjectManagerTest, UnknownObjectProperties) {
261 Properties* properties = static_cast<Properties*>(
262 object_manager_->GetProperties(ObjectPath("/org/chromium/UnknownObject"),
263 "org.chromium.TestInterface"));
264 EXPECT_EQ(nullptr, properties);
267 TEST_F(ObjectManagerTest, UnknownInterfaceProperties) {
268 Properties* properties = static_cast<Properties*>(
269 object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
270 "org.chromium.UnknownService"));
271 EXPECT_EQ(nullptr, properties);
274 TEST_F(ObjectManagerTest, GetObjects) {
275 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
276 ASSERT_EQ(1U, object_paths.size());
277 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
280 TEST_F(ObjectManagerTest, GetObjectsWithInterface) {
281 std::vector<ObjectPath> object_paths =
282 object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
283 ASSERT_EQ(1U, object_paths.size());
284 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
287 TEST_F(ObjectManagerTest, GetObjectsWithUnknownInterface) {
288 std::vector<ObjectPath> object_paths =
289 object_manager_->GetObjectsWithInterface("org.chromium.UnknownService");
290 EXPECT_EQ(0U, object_paths.size());
293 TEST_F(ObjectManagerTest, SameObject) {
294 ObjectManager* object_manager = bus_->GetObjectManager(
295 test_service_->service_name(),
296 ObjectPath("/org/chromium/TestService"));
297 EXPECT_EQ(object_manager_, object_manager);
300 TEST_F(ObjectManagerTest, DifferentObjectForService) {
301 ObjectManager* object_manager = bus_->GetObjectManager(
302 "org.chromium.DifferentService",
303 ObjectPath("/org/chromium/TestService"));
304 EXPECT_NE(object_manager_, object_manager);
307 TEST_F(ObjectManagerTest, DifferentObjectForPath) {
308 ObjectManager* object_manager = bus_->GetObjectManager(
309 test_service_->service_name(),
310 ObjectPath("/org/chromium/DifferentService"));
311 EXPECT_NE(object_manager_, object_manager);
314 TEST_F(ObjectManagerTest, SecondObject) {
315 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
318 ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
319 ObjectPath("/org/chromium/SecondObject"));
320 EXPECT_NE(nullptr, object_proxy);
322 Properties* properties = static_cast<Properties*>(
323 object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
324 "org.chromium.TestInterface"));
325 EXPECT_NE(nullptr, properties);
327 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
328 ASSERT_EQ(2U, object_paths.size());
330 std::sort(object_paths.begin(), object_paths.end());
331 EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
332 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
335 object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
336 ASSERT_EQ(2U, object_paths.size());
338 std::sort(object_paths.begin(), object_paths.end());
339 EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
340 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
343 TEST_F(ObjectManagerTest, RemoveSecondObject) {
344 PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
347 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
348 ASSERT_EQ(2U, object_paths.size());
350 PerformAction("RemoveObject", ObjectPath("/org/chromium/SecondObject"));
351 WaitForRemoveObject();
353 ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
354 ObjectPath("/org/chromium/SecondObject"));
355 EXPECT_EQ(nullptr, object_proxy);
357 Properties* properties = static_cast<Properties*>(
358 object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
359 "org.chromium.TestInterface"));
360 EXPECT_EQ(nullptr, properties);
362 object_paths = object_manager_->GetObjects();
363 ASSERT_EQ(1U, object_paths.size());
364 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
367 object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
368 ASSERT_EQ(1U, object_paths.size());
369 EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
372 TEST_F(ObjectManagerTest, OwnershipLost) {
373 PerformAction("ReleaseOwnership", ObjectPath("/org/chromium/TestService"));
374 WaitForRemoveObject();
376 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
377 ASSERT_EQ(0U, object_paths.size());
380 TEST_F(ObjectManagerTest, OwnershipLostAndRegained) {
381 PerformAction("Ownership", ObjectPath("/org/chromium/TestService"));
382 WaitForRemoveObject();
385 std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
386 ASSERT_EQ(1U, object_paths.size());
389 // Flaky: crbug.com/1174515
390 TEST_F(ObjectManagerTest, DISABLED_PropertiesChangedAsObjectsReceived) {
391 // Remove the existing object manager.
392 object_manager_->UnregisterInterface("org.chromium.TestInterface");
393 run_loop_ = std::make_unique<base::RunLoop>();
394 EXPECT_TRUE(bus_->RemoveObjectManager(
395 test_service_->service_name(),
396 ObjectPath("/org/chromium/TestService"),
397 run_loop_->QuitClosure()));
400 PerformAction("SetSendImmediatePropertiesChanged",
401 ObjectPath("/org/chromium/TestService"));
403 object_manager_ = bus_->GetObjectManager(
404 test_service_->service_name(),
405 ObjectPath("/org/chromium/TestService"));
406 object_manager_->RegisterInterface("org.chromium.TestInterface", this);
408 // The newly created object manager should call GetManagedObjects immediately
409 // after setting up the match rule for PropertiesChanged. We should process
410 // the PropertiesChanged event right after that. If we don't receive it within
411 // 2 seconds, then fail the test.
412 task_environment_.GetMainThreadTaskRunner()->PostDelayedTask(
414 base::BindOnce(&ObjectManagerTest::PropertiesChangedTestTimeout,
415 base::Unretained(this)),
418 while (last_name_value_ != "ChangedTestServiceName" && !timeout_expired_) {
419 run_loop_ = std::make_unique<base::RunLoop>();