xref: /aosp_15_r20/external/libchrome/dbus/object_manager_unittest.cc (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1 // Copyright (c) 2013 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.
4 
5 #include "dbus/object_manager.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <string>
11 #include <vector>
12 
13 #include "base/bind.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/threading/thread.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "dbus/bus.h"
20 #include "dbus/object_path.h"
21 #include "dbus/object_proxy.h"
22 #include "dbus/property.h"
23 #include "dbus/test_service.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 namespace dbus {
27 
28 // The object manager test exercises the asynchronous APIs in ObjectManager,
29 // and by extension PropertySet and Property<>.
30 class ObjectManagerTest
31     : public testing::Test,
32       public ObjectManager::Interface {
33  public:
ObjectManagerTest()34   ObjectManagerTest() : timeout_expired_(false) {
35   }
36 
37   struct Properties : public PropertySet {
38     Property<std::string> name;
39     Property<int16_t> version;
40     Property<std::vector<std::string>> methods;
41     Property<std::vector<ObjectPath>> objects;
42 
Propertiesdbus::ObjectManagerTest::Properties43     Properties(ObjectProxy* object_proxy,
44                const std::string& interface_name,
45                PropertyChangedCallback property_changed_callback)
46         : PropertySet(object_proxy, interface_name, property_changed_callback) {
47       RegisterProperty("Name", &name);
48       RegisterProperty("Version", &version);
49       RegisterProperty("Methods", &methods);
50       RegisterProperty("Objects", &objects);
51     }
52   };
53 
CreateProperties(ObjectProxy * object_proxy,const ObjectPath & object_path,const std::string & interface_name)54   PropertySet* CreateProperties(ObjectProxy* object_proxy,
55                                 const ObjectPath& object_path,
56                                 const std::string& interface_name) override {
57     Properties* properties = new Properties(
58         object_proxy, interface_name,
59         base::Bind(&ObjectManagerTest::OnPropertyChanged,
60                    base::Unretained(this), object_path));
61     return static_cast<PropertySet*>(properties);
62   }
63 
SetUp()64   void SetUp() override {
65     // Make the main thread not to allow IO.
66     base::ThreadRestrictions::SetIOAllowed(false);
67 
68     // Start the D-Bus thread.
69     dbus_thread_.reset(new base::Thread("D-Bus Thread"));
70     base::Thread::Options thread_options;
71     thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
72     ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options));
73 
74     // Start the test service, using the D-Bus thread.
75     TestService::Options options;
76     options.dbus_task_runner = dbus_thread_->task_runner();
77     test_service_.reset(new TestService(options));
78     ASSERT_TRUE(test_service_->StartService());
79     ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted());
80     ASSERT_TRUE(test_service_->HasDBusThread());
81 
82     // Create the client, using the D-Bus thread.
83     Bus::Options bus_options;
84     bus_options.bus_type = Bus::SESSION;
85     bus_options.connection_type = Bus::PRIVATE;
86     bus_options.dbus_task_runner = dbus_thread_->task_runner();
87     bus_ = new Bus(bus_options);
88     ASSERT_TRUE(bus_->HasDBusThread());
89 
90     object_manager_ = bus_->GetObjectManager(
91         test_service_->service_name(),
92         ObjectPath("/org/chromium/TestService"));
93     object_manager_->RegisterInterface("org.chromium.TestInterface", this);
94 
95     WaitForObject();
96   }
97 
TearDown()98   void TearDown() override {
99     bus_->ShutdownOnDBusThreadAndBlock();
100 
101     // Shut down the service.
102     test_service_->ShutdownAndBlock();
103 
104     // Reset to the default.
105     base::ThreadRestrictions::SetIOAllowed(true);
106 
107     // Stopping a thread is considered an IO operation, so do this after
108     // allowing IO.
109     test_service_->Stop();
110 
111     base::RunLoop().RunUntilIdle();
112   }
113 
MethodCallback(Response * response)114   void MethodCallback(Response* response) {
115     method_callback_called_ = true;
116     run_loop_->Quit();
117   }
118 
119   // Called from the PropertiesChangedAsObjectsReceived test case. The test will
120   // not run the message loop if it receives the expected PropertiesChanged
121   // signal before the timeout. This method immediately fails the test.
PropertiesChangedTestTimeout()122   void PropertiesChangedTestTimeout() {
123     timeout_expired_ = true;
124     run_loop_->Quit();
125 
126     FAIL() << "Never received PropertiesChanged";
127   }
128 
129  protected:
130   // Called when an object is added.
ObjectAdded(const ObjectPath & object_path,const std::string & interface_name)131   void ObjectAdded(const ObjectPath& object_path,
132                    const std::string& interface_name) override {
133     added_objects_.push_back(std::make_pair(object_path, interface_name));
134     run_loop_->Quit();
135   }
136 
137   // Called when an object is removed.
ObjectRemoved(const ObjectPath & object_path,const std::string & interface_name)138   void ObjectRemoved(const ObjectPath& object_path,
139                      const std::string& interface_name) override {
140     removed_objects_.push_back(std::make_pair(object_path, interface_name));
141     run_loop_->Quit();
142   }
143 
144   // Called when a property value is updated.
OnPropertyChanged(const ObjectPath & object_path,const std::string & name)145   void OnPropertyChanged(const ObjectPath& object_path,
146                          const std::string& name) {
147     // Store the value of the "Name" property if that's the one that
148     // changed.
149     Properties* properties = static_cast<Properties*>(
150         object_manager_->GetProperties(
151             object_path,
152             "org.chromium.TestInterface"));
153     if (name == properties->name.name())
154       last_name_value_ = properties->name.value();
155 
156     // Store the updated property.
157     updated_properties_.push_back(name);
158     run_loop_->Quit();
159   }
160 
161   static const size_t kExpectedObjects = 1;
162   static const size_t kExpectedProperties = 4;
163 
WaitForObject()164   void WaitForObject() {
165     while (added_objects_.size() < kExpectedObjects ||
166            updated_properties_.size() < kExpectedProperties) {
167       run_loop_.reset(new base::RunLoop);
168       run_loop_->Run();
169     }
170     for (size_t i = 0; i < kExpectedObjects; ++i)
171       added_objects_.erase(added_objects_.begin());
172     for (size_t i = 0; i < kExpectedProperties; ++i)
173       updated_properties_.erase(updated_properties_.begin());
174   }
175 
WaitForRemoveObject()176   void WaitForRemoveObject() {
177     while (removed_objects_.size() < kExpectedObjects) {
178       run_loop_.reset(new base::RunLoop);
179       run_loop_->Run();
180     }
181     for (size_t i = 0; i < kExpectedObjects; ++i)
182       removed_objects_.erase(removed_objects_.begin());
183   }
184 
WaitForMethodCallback()185   void WaitForMethodCallback() {
186     run_loop_.reset(new base::RunLoop);
187     run_loop_->Run();
188     method_callback_called_ = false;
189   }
190 
PerformAction(const std::string & action,const ObjectPath & object_path)191   void PerformAction(const std::string& action, const ObjectPath& object_path) {
192     ObjectProxy* object_proxy = bus_->GetObjectProxy(
193         test_service_->service_name(),
194         ObjectPath("/org/chromium/TestObject"));
195 
196     MethodCall method_call("org.chromium.TestInterface", "PerformAction");
197     MessageWriter writer(&method_call);
198     writer.AppendString(action);
199     writer.AppendObjectPath(object_path);
200 
201     object_proxy->CallMethod(&method_call,
202                              ObjectProxy::TIMEOUT_USE_DEFAULT,
203                              base::Bind(&ObjectManagerTest::MethodCallback,
204                                         base::Unretained(this)));
205     WaitForMethodCallback();
206   }
207 
208   base::MessageLoop message_loop_;
209   std::unique_ptr<base::RunLoop> run_loop_;
210   std::unique_ptr<base::Thread> dbus_thread_;
211   scoped_refptr<Bus> bus_;
212   ObjectManager* object_manager_;
213   std::unique_ptr<TestService> test_service_;
214 
215   std::string last_name_value_;
216   bool timeout_expired_;
217 
218   std::vector<std::pair<ObjectPath, std::string>> added_objects_;
219   std::vector<std::pair<ObjectPath, std::string>> removed_objects_;
220   std::vector<std::string> updated_properties_;
221 
222   bool method_callback_called_;
223 };
224 
225 
TEST_F(ObjectManagerTest,InitialObject)226 TEST_F(ObjectManagerTest, InitialObject) {
227   ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
228       ObjectPath("/org/chromium/TestObject"));
229   EXPECT_NE(nullptr, object_proxy);
230 
231   Properties* properties = static_cast<Properties*>(
232       object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
233                                      "org.chromium.TestInterface"));
234   EXPECT_NE(nullptr, properties);
235 
236   EXPECT_EQ("TestService", properties->name.value());
237   EXPECT_EQ(10, properties->version.value());
238 
239   std::vector<std::string> methods = properties->methods.value();
240   ASSERT_EQ(4U, methods.size());
241   EXPECT_EQ("Echo", methods[0]);
242   EXPECT_EQ("SlowEcho", methods[1]);
243   EXPECT_EQ("AsyncEcho", methods[2]);
244   EXPECT_EQ("BrokenMethod", methods[3]);
245 
246   std::vector<ObjectPath> objects = properties->objects.value();
247   ASSERT_EQ(1U, objects.size());
248   EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]);
249 }
250 
TEST_F(ObjectManagerTest,UnknownObjectProxy)251 TEST_F(ObjectManagerTest, UnknownObjectProxy) {
252   ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
253       ObjectPath("/org/chromium/UnknownObject"));
254   EXPECT_EQ(nullptr, object_proxy);
255 }
256 
TEST_F(ObjectManagerTest,UnknownObjectProperties)257 TEST_F(ObjectManagerTest, UnknownObjectProperties) {
258   Properties* properties = static_cast<Properties*>(
259       object_manager_->GetProperties(ObjectPath("/org/chromium/UnknownObject"),
260                                      "org.chromium.TestInterface"));
261   EXPECT_EQ(nullptr, properties);
262 }
263 
TEST_F(ObjectManagerTest,UnknownInterfaceProperties)264 TEST_F(ObjectManagerTest, UnknownInterfaceProperties) {
265   Properties* properties = static_cast<Properties*>(
266       object_manager_->GetProperties(ObjectPath("/org/chromium/TestObject"),
267                                      "org.chromium.UnknownService"));
268   EXPECT_EQ(nullptr, properties);
269 }
270 
TEST_F(ObjectManagerTest,GetObjects)271 TEST_F(ObjectManagerTest, GetObjects) {
272   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
273   ASSERT_EQ(1U, object_paths.size());
274   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
275 }
276 
TEST_F(ObjectManagerTest,GetObjectsWithInterface)277 TEST_F(ObjectManagerTest, GetObjectsWithInterface) {
278   std::vector<ObjectPath> object_paths =
279       object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
280   ASSERT_EQ(1U, object_paths.size());
281   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
282 }
283 
TEST_F(ObjectManagerTest,GetObjectsWithUnknownInterface)284 TEST_F(ObjectManagerTest, GetObjectsWithUnknownInterface) {
285   std::vector<ObjectPath> object_paths =
286       object_manager_->GetObjectsWithInterface("org.chromium.UnknownService");
287   EXPECT_EQ(0U, object_paths.size());
288 }
289 
TEST_F(ObjectManagerTest,SameObject)290 TEST_F(ObjectManagerTest, SameObject) {
291   ObjectManager* object_manager = bus_->GetObjectManager(
292       test_service_->service_name(),
293       ObjectPath("/org/chromium/TestService"));
294   EXPECT_EQ(object_manager_, object_manager);
295 }
296 
TEST_F(ObjectManagerTest,DifferentObjectForService)297 TEST_F(ObjectManagerTest, DifferentObjectForService) {
298   ObjectManager* object_manager = bus_->GetObjectManager(
299       "org.chromium.DifferentService",
300       ObjectPath("/org/chromium/TestService"));
301   EXPECT_NE(object_manager_, object_manager);
302 }
303 
TEST_F(ObjectManagerTest,DifferentObjectForPath)304 TEST_F(ObjectManagerTest, DifferentObjectForPath) {
305   ObjectManager* object_manager = bus_->GetObjectManager(
306       test_service_->service_name(),
307       ObjectPath("/org/chromium/DifferentService"));
308   EXPECT_NE(object_manager_, object_manager);
309 }
310 
TEST_F(ObjectManagerTest,SecondObject)311 TEST_F(ObjectManagerTest, SecondObject) {
312   PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
313   WaitForObject();
314 
315   ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
316       ObjectPath("/org/chromium/SecondObject"));
317   EXPECT_NE(nullptr, object_proxy);
318 
319   Properties* properties = static_cast<Properties*>(
320       object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
321                                      "org.chromium.TestInterface"));
322   EXPECT_NE(nullptr, properties);
323 
324   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
325   ASSERT_EQ(2U, object_paths.size());
326 
327   std::sort(object_paths.begin(), object_paths.end());
328   EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
329   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
330 
331   object_paths =
332       object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
333   ASSERT_EQ(2U, object_paths.size());
334 
335   std::sort(object_paths.begin(), object_paths.end());
336   EXPECT_EQ(ObjectPath("/org/chromium/SecondObject"), object_paths[0]);
337   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[1]);
338 }
339 
TEST_F(ObjectManagerTest,RemoveSecondObject)340 TEST_F(ObjectManagerTest, RemoveSecondObject) {
341   PerformAction("AddObject", ObjectPath("/org/chromium/SecondObject"));
342   WaitForObject();
343 
344   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
345   ASSERT_EQ(2U, object_paths.size());
346 
347   PerformAction("RemoveObject", ObjectPath("/org/chromium/SecondObject"));
348   WaitForRemoveObject();
349 
350   ObjectProxy* object_proxy = object_manager_->GetObjectProxy(
351       ObjectPath("/org/chromium/SecondObject"));
352   EXPECT_EQ(nullptr, object_proxy);
353 
354   Properties* properties = static_cast<Properties*>(
355       object_manager_->GetProperties(ObjectPath("/org/chromium/SecondObject"),
356                                      "org.chromium.TestInterface"));
357   EXPECT_EQ(nullptr, properties);
358 
359   object_paths = object_manager_->GetObjects();
360   ASSERT_EQ(1U, object_paths.size());
361   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
362 
363   object_paths =
364       object_manager_->GetObjectsWithInterface("org.chromium.TestInterface");
365   ASSERT_EQ(1U, object_paths.size());
366   EXPECT_EQ(ObjectPath("/org/chromium/TestObject"), object_paths[0]);
367 }
368 
TEST_F(ObjectManagerTest,OwnershipLost)369 TEST_F(ObjectManagerTest, OwnershipLost) {
370   PerformAction("ReleaseOwnership", ObjectPath("/org/chromium/TestService"));
371   WaitForRemoveObject();
372 
373   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
374   ASSERT_EQ(0U, object_paths.size());
375 }
376 
TEST_F(ObjectManagerTest,OwnershipLostAndRegained)377 TEST_F(ObjectManagerTest, OwnershipLostAndRegained) {
378   PerformAction("Ownership", ObjectPath("/org/chromium/TestService"));
379   WaitForRemoveObject();
380   WaitForObject();
381 
382   std::vector<ObjectPath> object_paths = object_manager_->GetObjects();
383   ASSERT_EQ(1U, object_paths.size());
384 }
385 
TEST_F(ObjectManagerTest,PropertiesChangedAsObjectsReceived)386 TEST_F(ObjectManagerTest, PropertiesChangedAsObjectsReceived) {
387   // Remove the existing object manager.
388   object_manager_->UnregisterInterface("org.chromium.TestInterface");
389   run_loop_.reset(new base::RunLoop);
390   EXPECT_TRUE(bus_->RemoveObjectManager(
391       test_service_->service_name(),
392       ObjectPath("/org/chromium/TestService"),
393       run_loop_->QuitClosure()));
394   run_loop_->Run();
395 
396   PerformAction("SetSendImmediatePropertiesChanged",
397                 ObjectPath("/org/chromium/TestService"));
398 
399   object_manager_ = bus_->GetObjectManager(
400       test_service_->service_name(),
401       ObjectPath("/org/chromium/TestService"));
402   object_manager_->RegisterInterface("org.chromium.TestInterface", this);
403 
404   // The newly created object manager should call GetManagedObjects immediately
405   // after setting up the match rule for PropertiesChanged. We should process
406   // the PropertiesChanged event right after that. If we don't receive it within
407   // 2 seconds, then fail the test.
408   message_loop_.task_runner()->PostDelayedTask(
409       FROM_HERE, base::Bind(&ObjectManagerTest::PropertiesChangedTestTimeout,
410                             base::Unretained(this)),
411       base::TimeDelta::FromSeconds(2));
412 
413   while (last_name_value_ != "ChangedTestServiceName" && !timeout_expired_) {
414     run_loop_.reset(new base::RunLoop);
415     run_loop_->Run();
416   }
417 }
418 
419 }  // namespace dbus
420