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. 4 5 #include "dbus/property.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/basictypes.h" 11 #include "base/bind.h" 12 #include "base/logging.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/threading/thread.h" 15 #include "base/threading/thread_restrictions.h" 16 #include "dbus/bus.h" 17 #include "dbus/object_path.h" 18 #include "dbus/object_proxy.h" 19 #include "dbus/test_service.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 22 namespace dbus { 23 24 // The property test exerises the asynchronous APIs in PropertySet and 25 // Property<>. 26 class PropertyTest : public testing::Test { 27 public: 28 PropertyTest() { 29 } 30 31 struct Properties : public PropertySet { 32 Property<std::string> name; 33 Property<int16> version; 34 Property<std::vector<std::string> > methods; 35 Property<std::vector<ObjectPath> > objects; 36 37 Properties(ObjectProxy* object_proxy, 38 PropertyChangedCallback property_changed_callback) 39 : PropertySet(object_proxy, 40 "org.chromium.TestInterface", 41 property_changed_callback) { 42 RegisterProperty("Name", &name); 43 RegisterProperty("Version", &version); 44 RegisterProperty("Methods", &methods); 45 RegisterProperty("Objects", &objects); 46 } 47 }; 48 49 virtual void SetUp() { 50 // Make the main thread not to allow IO. 51 base::ThreadRestrictions::SetIOAllowed(false); 52 53 // Start the D-Bus thread. 54 dbus_thread_.reset(new base::Thread("D-Bus Thread")); 55 base::Thread::Options thread_options; 56 thread_options.message_loop_type = base::MessageLoop::TYPE_IO; 57 ASSERT_TRUE(dbus_thread_->StartWithOptions(thread_options)); 58 59 // Start the test service, using the D-Bus thread. 60 TestService::Options options; 61 options.dbus_task_runner = dbus_thread_->message_loop_proxy(); 62 test_service_.reset(new TestService(options)); 63 ASSERT_TRUE(test_service_->StartService()); 64 ASSERT_TRUE(test_service_->WaitUntilServiceIsStarted()); 65 ASSERT_TRUE(test_service_->HasDBusThread()); 66 67 // Create the client, using the D-Bus thread. 68 Bus::Options bus_options; 69 bus_options.bus_type = Bus::SESSION; 70 bus_options.connection_type = Bus::PRIVATE; 71 bus_options.dbus_task_runner = dbus_thread_->message_loop_proxy(); 72 bus_ = new Bus(bus_options); 73 object_proxy_ = bus_->GetObjectProxy( 74 "org.chromium.TestService", 75 ObjectPath("/org/chromium/TestObject")); 76 ASSERT_TRUE(bus_->HasDBusThread()); 77 78 // Create the properties structure 79 properties_.reset(new Properties( 80 object_proxy_, 81 base::Bind(&PropertyTest::OnPropertyChanged, 82 base::Unretained(this)))); 83 properties_->ConnectSignals(); 84 properties_->GetAll(); 85 } 86 87 virtual void TearDown() { 88 bus_->ShutdownOnDBusThreadAndBlock(); 89 90 // Shut down the service. 91 test_service_->ShutdownAndBlock(); 92 93 // Reset to the default. 94 base::ThreadRestrictions::SetIOAllowed(true); 95 96 // Stopping a thread is considered an IO operation, so do this after 97 // allowing IO. 98 test_service_->Stop(); 99 } 100 101 // Generic callback, bind with a string |id| for passing to 102 // WaitForCallback() to ensure the callback for the right method is 103 // waited for. 104 void PropertyCallback(const std::string& id, bool success) { 105 last_callback_ = id; 106 message_loop_.Quit(); 107 } 108 109 protected: 110 // Called when a property value is updated. 111 void OnPropertyChanged(const std::string& name) { 112 updated_properties_.push_back(name); 113 message_loop_.Quit(); 114 } 115 116 // Waits for the given number of updates. 117 void WaitForUpdates(size_t num_updates) { 118 while (updated_properties_.size() < num_updates) 119 message_loop_.Run(); 120 for (size_t i = 0; i < num_updates; ++i) 121 updated_properties_.erase(updated_properties_.begin()); 122 } 123 124 // Name, Version, Methods, Objects 125 static const int kExpectedSignalUpdates = 4; 126 127 // Waits for initial values to be set. 128 void WaitForGetAll() { 129 WaitForUpdates(kExpectedSignalUpdates); 130 } 131 132 // Waits for the callback. |id| is the string bound to the callback when 133 // the method call is made that identifies it and distinguishes from any 134 // other; you can set this to whatever you wish. 135 void WaitForCallback(const std::string& id) { 136 while (last_callback_ != id) { 137 message_loop_.Run(); 138 } 139 } 140 141 base::MessageLoop message_loop_; 142 scoped_ptr<base::Thread> dbus_thread_; 143 scoped_refptr<Bus> bus_; 144 ObjectProxy* object_proxy_; 145 scoped_ptr<Properties> properties_; 146 scoped_ptr<TestService> test_service_; 147 // Properties updated. 148 std::vector<std::string> updated_properties_; 149 // Last callback received. 150 std::string last_callback_; 151 }; 152 153 TEST_F(PropertyTest, InitialValues) { 154 WaitForGetAll(); 155 156 EXPECT_EQ("TestService", properties_->name.value()); 157 EXPECT_EQ(10, properties_->version.value()); 158 159 std::vector<std::string> methods = properties_->methods.value(); 160 ASSERT_EQ(4U, methods.size()); 161 EXPECT_EQ("Echo", methods[0]); 162 EXPECT_EQ("SlowEcho", methods[1]); 163 EXPECT_EQ("AsyncEcho", methods[2]); 164 EXPECT_EQ("BrokenMethod", methods[3]); 165 166 std::vector<ObjectPath> objects = properties_->objects.value(); 167 ASSERT_EQ(1U, objects.size()); 168 EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]); 169 } 170 171 TEST_F(PropertyTest, UpdatedValues) { 172 WaitForGetAll(); 173 174 // Update the value of the "Name" property, this value should not change. 175 properties_->name.Get(base::Bind(&PropertyTest::PropertyCallback, 176 base::Unretained(this), 177 "Name")); 178 WaitForCallback("Name"); 179 WaitForUpdates(1); 180 181 EXPECT_EQ("TestService", properties_->name.value()); 182 183 // Update the value of the "Version" property, this value should be changed. 184 properties_->version.Get(base::Bind(&PropertyTest::PropertyCallback, 185 base::Unretained(this), 186 "Version")); 187 WaitForCallback("Version"); 188 WaitForUpdates(1); 189 190 EXPECT_EQ(20, properties_->version.value()); 191 192 // Update the value of the "Methods" property, this value should not change 193 // and should not grow to contain duplicate entries. 194 properties_->methods.Get(base::Bind(&PropertyTest::PropertyCallback, 195 base::Unretained(this), 196 "Methods")); 197 WaitForCallback("Methods"); 198 WaitForUpdates(1); 199 200 std::vector<std::string> methods = properties_->methods.value(); 201 ASSERT_EQ(4U, methods.size()); 202 EXPECT_EQ("Echo", methods[0]); 203 EXPECT_EQ("SlowEcho", methods[1]); 204 EXPECT_EQ("AsyncEcho", methods[2]); 205 EXPECT_EQ("BrokenMethod", methods[3]); 206 207 // Update the value of the "Objects" property, this value should not change 208 // and should not grow to contain duplicate entries. 209 properties_->objects.Get(base::Bind(&PropertyTest::PropertyCallback, 210 base::Unretained(this), 211 "Objects")); 212 WaitForCallback("Objects"); 213 WaitForUpdates(1); 214 215 std::vector<ObjectPath> objects = properties_->objects.value(); 216 ASSERT_EQ(1U, objects.size()); 217 EXPECT_EQ(ObjectPath("/TestObjectPath"), objects[0]); 218 } 219 220 TEST_F(PropertyTest, Get) { 221 WaitForGetAll(); 222 223 // Ask for the new Version property. 224 properties_->version.Get(base::Bind(&PropertyTest::PropertyCallback, 225 base::Unretained(this), 226 "Get")); 227 WaitForCallback("Get"); 228 229 // Make sure we got a property update too. 230 WaitForUpdates(1); 231 232 EXPECT_EQ(20, properties_->version.value()); 233 } 234 235 TEST_F(PropertyTest, Set) { 236 WaitForGetAll(); 237 238 // Set a new name. 239 properties_->name.Set("NewService", 240 base::Bind(&PropertyTest::PropertyCallback, 241 base::Unretained(this), 242 "Set")); 243 WaitForCallback("Set"); 244 245 // TestService sends a property update. 246 WaitForUpdates(1); 247 248 EXPECT_EQ("NewService", properties_->name.value()); 249 } 250 251 } // namespace dbus 252