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