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