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