• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2012 The Chromium OS 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 
6 #ifndef CRAS_DBUS_TEST_H_
7 #define CRAS_DBUS_TEST_H_
8 
9 #include <string>
10 #include <vector>
11 
12 #include <stdint.h>
13 #include <dbus/dbus.h>
14 #include <gtest/gtest.h>
15 #include <pthread.h>
16 
17 /* DBusTest, and the related DBusMatch class, are used to provide a
18  * GMock-like experience for testing D-Bus code within cras.
19  *
20  * It works by providing a connection to a private D-Bus Server for use
21  * by code you intend to test. Before making calls, you set expectations
22  * of method calls that the server should receive and reply to, or
23  * instructions for the server to send signals that your connection
24  * should receive and handle.
25  *
26  * The code style is similar to GMock for purposes of familiarity.
27  *
28  * Examples
29  * --------
30  *
31  * To create a test suite class implementing a SetUp and TearDown method,
32  * be sure to call the base-class methods at the appropriate time.
33  *
34  *   class ExampleTestSuite : public DBusTest {
35  *     virtual void SetUp() {
36  *       DBusTest::SetUp();
37  *       // your setup code here
38  *     }
39  *
40  *     virtual void TearDown() {
41  *       // your teardown code here
42  *       DBusTest::TearDown();
43  *     }
44  *   };
45  *
46  * To expect a method call to be made against the server; matching the
47  * object path, interface and method name and then generating an empty
48  * reply. The test code ensures that the reply is received during the
49  * TearDown method.
50  *
51  *   TEST_F(ExampleTestSuite, ExampleTest) {
52  *     ExpectMethodCall("/object/path", "object.Interface", "MethodName")
53  *         .SendReply();
54  *
55  *     // code to generate the method call here
56  *   }
57  *
58  * Due to the asynchronous nature of D-Bus, if you need to check some
59  * state, it's not enough to immediately follow the code that generates
60  * the method call. You must instead ensure that all expectations up to
61  * that point have been met:
62  *
63  *   TEST_F(ExampleTestSuite, ExampleTest) {
64  *     ExpectMethodCall("/object/path", "object.Interface", "MethodName")
65  *         .SendReply();
66  *
67  *     // code to generate the method call here
68  *
69  *     WaitForMatches();
70  *
71  *     // code to examine state here
72  *   }
73  *
74  * To verify the arguments to method calls, place .With*() calls before
75  * sending the reply:
76  *
77  *   ExpectMethodCall("/object/path", "object.Interface", "MethodName")
78  *       .WithObjectPath("/arg0/object/path")
79  *       .WithString("arg1")
80  *       .WithString("arg2")
81  *       .SendReply();
82  *
83  * Normally additional arguments are permitted, since most D-Bus services
84  * don't go out of their way to check they aren't provided; to verify
85  * there are no more arguments use .WithNoMoreArgs():
86  *
87  *   ExpectMethodCall("/object/path", "object.Interface", "MethodName")
88  *       .WithString("arg0")
89  *       .WithNoMoreArgs()
90  *       .SendReply();
91  *
92  * To append arguments to the reply, place .With*() calls after the
93  * instruction to send the reply:
94  *
95  *   ExpectMethodCall("/object/path", "object.Interface", "MethodName")
96  *       .SendReply()
97  *       .WithString("arg0")
98  *       .WithObjectPath("/arg1/object/path");
99  *
100  * Property dictionaries are sufficiently difficult to deal with that
101  * there is special handling for them; to append one to the reply use
102  * .AsPropertyDictionary() and follow with alternate .WithString() and
103  * other .With*() calls for each property:
104  *
105  *  ExpectMethodCall("/object/path", "object.Interface", "GetProperties")
106  *       .SendReply()
107  *       .AsPropertyDictionary()
108  *       .WithString("Keyword")
109  *       .WithObjectPath("/value/of/keyword");
110  *
111  * To reply with an error use .SendError() instead of .SendReply(),
112  * passing the error name and message
113  *
114  *   ExpectMethodCall("/object/path", "object.Interface", "MethodName")
115  *       .SendError("some.error.Name", "Message for error");
116  *
117  * In some cases (notably "AddMatch" method calls) the method call will
118  * be handled by libdbus itself and the mechanism DBusTest uses to verify
119  * that the reply is recieved does not work. In which case you need to use
120  * .SendReplyNoWait() instead.
121  *
122  *    ExpectMethodCall("", DBUS_INTERFACE_DBUS, "AddMatch")
123  *        .SendReplyNoWait();
124  *
125  * Sending signals from the server side is very similar:
126  *
127  *    CreateSignal("/object/path", "object.Interface", "SignalName")
128  *        .WithString("arg0")
129  *        .WithObjectPat("/arg1/object/path")
130  *        .Send();
131  *
132  * Create messages from server side:
133  *    CreateMessageCall("/object/path". "object.Interface", "MethodName")
134  *        .WithString("arg0")
135  *        .WithUnixFd(arg1)
136  *        .Send();
137  *
138  * The TearDown() method will verify that it is received by the client,
139  * use WaitForMatches() to force verification earlier in order to check
140  * state.
141  */
142 
143 class DBusTest;
144 
145 class DBusMatch {
146  public:
147   DBusMatch();
148 
149   struct Arg {
150     int type;
151     bool array;
152     std::string string_value;
153     int int_value;
154     std::vector<std::string> string_values;
155   };
156 
157   // Append arguments to a match.
158   DBusMatch& WithString(std::string value);
159   DBusMatch& WithUnixFd(int value);
160   DBusMatch& WithObjectPath(std::string value);
161   DBusMatch& WithArrayOfStrings(std::vector<std::string> values);
162   DBusMatch& WithArrayOfObjectPaths(std::vector<std::string> values);
163   DBusMatch& WithNoMoreArgs();
164 
165   // Indicates that all arguments in either the method call or reply
166   // should be wrapped into a property dictionary with a string for keys
167   // and a variant for the data.
168   DBusMatch& AsPropertyDictionary();
169 
170   // Send a reply to a method call and wait for it to be received by the
171   // client; may be followed by methods to append arguments.
172   DBusMatch& SendReply();
173 
174   // Send an error in reply to a method call and wait for it to be received
175   // by the client; may also be followed by methods to append arguments.
176   DBusMatch& SendError(std::string error_name, std::string error_message);
177 
178   // Send a reply to a method call but do not wait for it to be received;
179   // mostly needed for internal D-Bus messages.
180   DBusMatch& SendReplyNoWait();
181 
182   // Send a created signal.
183   DBusMatch& Send();
184 
185  private:
186   friend class DBusTest;
187 
188   // Methods used by DBusTest after constructing the DBusMatch instance
189   // to set the type of match.
190   void ExpectMethodCall(std::string path, std::string interface,
191                         std::string method);
192 
193   void CreateSignal(DBusConnection *conn,
194                     std::string path, std::string interface,
195                     std::string signal_name);
196 
197   void CreateMessageCall(DBusConnection *conn,
198                          std::string path, std::string interface,
199                          std::string signal_name);
200 
201   // Determine whether a message matches a set of arguments.
202   bool MatchMessageArgs(DBusMessage *message, std::vector<Arg> *args);
203 
204   // Append a set of arguments to a message.
205   void AppendArgsToMessage(DBusMessage *message, std::vector<Arg> *args);
206 
207   // Send a message on a connection.
208   void SendMessage(DBusConnection *conn, DBusMessage *message);
209 
210   // Handle a message received by the server connection.
211   bool HandleServerMessage(DBusConnection *conn, DBusMessage *message);
212 
213   // Handle a message received by the client connection.
214   bool HandleClientMessage(DBusConnection *conn, DBusMessage *message);
215 
216   // Verify whether the match is complete.
217   bool Complete();
218 
219   int message_type_;
220   std::string path_;
221   std::string interface_;
222   std::string member_;
223 
224   bool as_property_dictionary_;
225   std::vector<Arg> args_;
226 
227   DBusConnection *conn_;
228 
229   bool send_reply_;
230   std::vector<Arg> reply_args_;
231 
232   bool send_error_;
233   std::string error_name_;
234   std::string error_message_;
235 
236   bool expect_serial_;
237   std::vector<dbus_uint32_t> expected_serials_;
238 
239   bool matched_;
240 };
241 
242 class DBusTest : public ::testing::Test {
243  public:
244   DBusTest();
245   virtual ~DBusTest();
246 
247  protected:
248   // Connection to the D-Bus server, this may be used during tests as the
249   // "bus" connection, all messages go to and from the internal D-Bus server.
250   DBusConnection *conn_;
251 
252   // Expect a method call to be received by the server.
253   DBusMatch& ExpectMethodCall(std::string path, std::string interface,
254                               std::string method);
255 
256   // Send a signal from the client to the server.
257   DBusMatch& CreateSignal(std::string path, std::string interface,
258                           std::string signal_name);
259 
260   // Send a message from the client to the server.
261   DBusMatch& CreateMessageCall(std::string path, std::string interface,
262                                std::string signal_name);
263 
264   // Wait for all matches created by Expect*() or Create*() methods to
265   // be complete.
266   void WaitForMatches();
267 
268   // When overriding be sure to call these parent methods to allow the
269   // D-Bus server thread to be cleanly initialized and shut down.
270   virtual void SetUp();
271   virtual void TearDown();
272 
273  private:
274   DBusServer *server_;
275   DBusConnection *server_conn_;
276 
277   std::vector<DBusWatch *> watches_;
278   std::vector<DBusTimeout *> timeouts_;
279 
280   pthread_t thread_id_;
281   pthread_mutex_t mutex_;
282   bool dispatch_;
283 
284   std::vector<DBusMatch> matches_;
285 
286   static void NewConnectionThunk(DBusServer *server, DBusConnection *conn,
287                                  void *data);
288   void NewConnection(DBusServer *server, DBusConnection *conn);
289 
290   static dbus_bool_t AddWatchThunk(DBusWatch *watch, void *data);
291   dbus_bool_t AddWatch(DBusWatch *watch);
292 
293   static void RemoveWatchThunk(DBusWatch *watch, void *data);
294   void RemoveWatch(DBusWatch *watch);
295 
296   static void WatchToggledThunk(DBusWatch *watch, void *data);
297   void WatchToggled(DBusWatch *watch);
298 
299   static dbus_bool_t AddTimeoutThunk(DBusTimeout *timeout, void *data);
300   dbus_bool_t AddTimeout(DBusTimeout *timeout);
301 
302   static void RemoveTimeoutThunk(DBusTimeout *timeout, void *data);
303   void RemoveTimeout(DBusTimeout *timeout);
304 
305   static void TimeoutToggledThunk(DBusTimeout *timeout, void *data);
306   void TimeoutToggled(DBusTimeout *timeout);
307 
308   static DBusHandlerResult HandleMessageThunk(DBusConnection *conn,
309                                               DBusMessage *message, void *data);
310   DBusHandlerResult HandleMessage(DBusConnection *conn, DBusMessage *message);
311 
312   static void *DispatchLoopThunk(void *ptr);
313   void *DispatchLoop();
314   void DispatchOnce();
315 };
316 
317 
318 #endif /* CRAS_DBUS_TEST_H_ */
319