property_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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
22namespace dbus {
23
24// The property test exerises the asynchronous APIs in PropertySet and
25// Property<>.
26class 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
155TEST_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
180TEST_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
244TEST_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
259TEST_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