• 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 <dbus/dbus.h>
10 #include <gtest/gtest.h>
11 #include <pthread.h>
12 #include <stdint.h>
13 
14 #include <string>
15 #include <vector>
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,
191                         std::string interface,
192                         std::string method);
193 
194   void CreateSignal(DBusConnection* conn,
195                     std::string path,
196                     std::string interface,
197                     std::string signal_name);
198 
199   void CreateMessageCall(DBusConnection* conn,
200                          std::string path,
201                          std::string interface,
202                          std::string signal_name);
203 
204   // Determine whether a message matches a set of arguments.
205   bool MatchMessageArgs(DBusMessage* message, std::vector<Arg>* args);
206 
207   // Append a set of arguments to a message.
208   void AppendArgsToMessage(DBusMessage* message, std::vector<Arg>* args);
209 
210   // Send a message on a connection.
211   void SendMessage(DBusConnection* conn, DBusMessage* message);
212 
213   // Handle a message received by the server connection.
214   bool HandleServerMessage(DBusConnection* conn, DBusMessage* message);
215 
216   // Handle a message received by the client connection.
217   bool HandleClientMessage(DBusConnection* conn, DBusMessage* message);
218 
219   // Verify whether the match is complete.
220   bool Complete();
221 
222   int message_type_;
223   std::string path_;
224   std::string interface_;
225   std::string member_;
226 
227   bool as_property_dictionary_;
228   std::vector<Arg> args_;
229 
230   DBusConnection* conn_;
231 
232   bool send_reply_;
233   std::vector<Arg> reply_args_;
234 
235   bool send_error_;
236   std::string error_name_;
237   std::string error_message_;
238 
239   bool expect_serial_;
240   std::vector<dbus_uint32_t> expected_serials_;
241 
242   bool matched_;
243 };
244 
245 class DBusTest : public ::testing::Test {
246  public:
247   DBusTest();
248   virtual ~DBusTest();
249 
250  protected:
251   // Connection to the D-Bus server, this may be used during tests as the
252   // "bus" connection, all messages go to and from the internal D-Bus server.
253   DBusConnection* conn_;
254 
255   // Expect a method call to be received by the server.
256   DBusMatch& ExpectMethodCall(std::string path,
257                               std::string interface,
258                               std::string method);
259 
260   // Send a signal from the client to the server.
261   DBusMatch& CreateSignal(std::string path,
262                           std::string interface,
263                           std::string signal_name);
264 
265   // Send a message from the client to the server.
266   DBusMatch& CreateMessageCall(std::string path,
267                                std::string interface,
268                                std::string signal_name);
269 
270   // Wait for all matches created by Expect*() or Create*() methods to
271   // be complete.
272   void WaitForMatches();
273 
274   // When overriding be sure to call these parent methods to allow the
275   // D-Bus server thread to be cleanly initialized and shut down.
276   virtual void SetUp();
277   virtual void TearDown();
278 
279  private:
280   DBusServer* server_;
281   DBusConnection* server_conn_;
282 
283   std::vector<DBusWatch*> watches_;
284   std::vector<DBusTimeout*> timeouts_;
285 
286   pthread_t thread_id_;
287   pthread_mutex_t mutex_;
288   bool dispatch_;
289 
290   std::vector<DBusMatch> matches_;
291 
292   static void NewConnectionThunk(DBusServer* server,
293                                  DBusConnection* conn,
294                                  void* data);
295   void NewConnection(DBusServer* server, DBusConnection* conn);
296 
297   static dbus_bool_t AddWatchThunk(DBusWatch* watch, void* data);
298   dbus_bool_t AddWatch(DBusWatch* watch);
299 
300   static void RemoveWatchThunk(DBusWatch* watch, void* data);
301   void RemoveWatch(DBusWatch* watch);
302 
303   static void WatchToggledThunk(DBusWatch* watch, void* data);
304   void WatchToggled(DBusWatch* watch);
305 
306   static dbus_bool_t AddTimeoutThunk(DBusTimeout* timeout, void* data);
307   dbus_bool_t AddTimeout(DBusTimeout* timeout);
308 
309   static void RemoveTimeoutThunk(DBusTimeout* timeout, void* data);
310   void RemoveTimeout(DBusTimeout* timeout);
311 
312   static void TimeoutToggledThunk(DBusTimeout* timeout, void* data);
313   void TimeoutToggled(DBusTimeout* timeout);
314 
315   static DBusHandlerResult HandleMessageThunk(DBusConnection* conn,
316                                               DBusMessage* message,
317                                               void* data);
318   DBusHandlerResult HandleMessage(DBusConnection* conn, DBusMessage* message);
319 
320   static void* DispatchLoopThunk(void* ptr);
321   void* DispatchLoop();
322   void DispatchOnce();
323 };
324 
325 #endif /* CRAS_DBUS_TEST_H_ */
326