diff options
Diffstat (limited to 'cras/src/tests/dbus_test.cc')
-rw-r--r-- | cras/src/tests/dbus_test.cc | 734 |
1 files changed, 0 insertions, 734 deletions
diff --git a/cras/src/tests/dbus_test.cc b/cras/src/tests/dbus_test.cc deleted file mode 100644 index b435825d..00000000 --- a/cras/src/tests/dbus_test.cc +++ /dev/null @@ -1,734 +0,0 @@ -// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "dbus_test.h" - -#include <stdlib.h> -#include <sys/select.h> -#include <unistd.h> - -#include <algorithm> - -namespace { - -const char kServerAddress[] = "unix:abstract=/org/chromium/DBusTest"; - -} // namespace - -DBusMatch::DBusMatch() - : message_type_(DBUS_MESSAGE_TYPE_INVALID), - as_property_dictionary_(false), - send_reply_(false), - send_error_(false), - expect_serial_(false), - matched_(false) {} - -DBusMatch& DBusMatch::WithString(std::string value) { - Arg arg; - arg.type = DBUS_TYPE_STRING; - arg.array = false; - arg.string_value = value; - - if (send_reply_) - reply_args_.push_back(arg); - else - args_.push_back(arg); - return *this; -} - -DBusMatch& DBusMatch::WithUnixFd(int value) { - Arg arg; - arg.type = DBUS_TYPE_UNIX_FD; - arg.array = false; - arg.int_value = value; - - if (send_reply_) - reply_args_.push_back(arg); - else - args_.push_back(arg); - return *this; -} - -DBusMatch& DBusMatch::WithObjectPath(std::string value) { - Arg arg; - arg.type = DBUS_TYPE_OBJECT_PATH; - arg.array = false; - arg.string_value = value; - - if (send_reply_) - reply_args_.push_back(arg); - else - args_.push_back(arg); - return *this; -} - -DBusMatch& DBusMatch::WithArrayOfStrings(std::vector<std::string> values) { - Arg arg; - arg.type = DBUS_TYPE_STRING; - arg.array = true; - arg.string_values = values; - - if (send_reply_) - reply_args_.push_back(arg); - else - args_.push_back(arg); - return *this; -} - -DBusMatch& DBusMatch::WithArrayOfObjectPaths(std::vector<std::string> values) { - Arg arg; - arg.type = DBUS_TYPE_OBJECT_PATH; - arg.array = true; - arg.string_values = values; - - if (send_reply_) - reply_args_.push_back(arg); - else - args_.push_back(arg); - return *this; -} - -DBusMatch& DBusMatch::WithNoMoreArgs() { - Arg arg; - arg.type = DBUS_TYPE_INVALID; - - args_.push_back(arg); - return *this; -} - -DBusMatch& DBusMatch::AsPropertyDictionary() { - as_property_dictionary_ = true; - return *this; -} - -DBusMatch& DBusMatch::SendReply() { - send_reply_ = true; - expect_serial_ = true; - return *this; -} - -DBusMatch& DBusMatch::SendError(std::string error_name, - std::string error_message) { - send_error_ = true; - error_name_ = error_name; - error_message_ = error_message; - expect_serial_ = true; - return *this; -} - -DBusMatch& DBusMatch::SendReplyNoWait() { - send_reply_ = true; - expect_serial_ = false; - return *this; -} - -DBusMatch& DBusMatch::Send() { - DBusMessage* message; - if (message_type_ == DBUS_MESSAGE_TYPE_SIGNAL) - message = dbus_message_new_signal(path_.c_str(), interface_.c_str(), - member_.c_str()); - else if (message_type_ == DBUS_MESSAGE_TYPE_METHOD_CALL) - message = dbus_message_new_method_call(NULL, path_.c_str(), - interface_.c_str(), member_.c_str()); - else - return *this; - - AppendArgsToMessage(message, &args_); - SendMessage(conn_, message); - - dbus_message_unref(message); - - return *this; -} - -void DBusMatch::ExpectMethodCall(std::string path, - std::string interface, - std::string method) { - message_type_ = DBUS_MESSAGE_TYPE_METHOD_CALL; - path_ = path; - interface_ = interface; - member_ = method; -} - -void DBusMatch::CreateSignal(DBusConnection* conn, - std::string path, - std::string interface, - std::string signal_name) { - message_type_ = DBUS_MESSAGE_TYPE_SIGNAL; - path_ = path; - interface_ = interface; - member_ = signal_name; - - conn_ = conn; - expect_serial_ = true; - matched_ = true; -} - -void DBusMatch::CreateMessageCall(DBusConnection* conn, - std::string path, - std::string interface, - std::string method_name) { - message_type_ = DBUS_MESSAGE_TYPE_METHOD_CALL; - path_ = path; - interface_ = interface; - member_ = method_name; - - conn_ = conn; - expect_serial_ = true; - matched_ = true; -} - -bool DBusMatch::MatchMessageArgs(DBusMessage* message, std::vector<Arg>* args) { - DBusMessageIter iter; - dbus_message_iter_init(message, &iter); - for (std::vector<Arg>::iterator it = args->begin(); it != args->end(); ++it) { - Arg& arg = *it; - - int type = dbus_message_iter_get_arg_type(&iter); - if (type != arg.type) - return false; - - if (arg.type == DBUS_TYPE_STRING || arg.type == DBUS_TYPE_OBJECT_PATH) { - const char* str_value; - dbus_message_iter_get_basic(&iter, &str_value); - if (strcmp(str_value, arg.string_value.c_str()) != 0) - return false; - } - // TODO(keybuk): additional argument types - - dbus_message_iter_next(&iter); - } - - return true; -} - -void DBusMatch::AppendArgsToMessage(DBusMessage* message, - std::vector<Arg>* args) { - DBusMessageIter message_iter; - DBusMessageIter dict_array_iter; - DBusMessageIter struct_iter; - DBusMessageIter iter; - - if (as_property_dictionary_) { - dbus_message_iter_init_append(message, &message_iter); - dbus_message_iter_open_container(&message_iter, DBUS_TYPE_ARRAY, "{sv}", - &dict_array_iter); - } else { - dbus_message_iter_init_append(message, &iter); - } - - for (std::vector<Arg>::iterator it = args->begin(); it != args->end(); ++it) { - Arg& arg = *it; - - if (as_property_dictionary_) { - dbus_message_iter_open_container(&dict_array_iter, DBUS_TYPE_DICT_ENTRY, - NULL, &struct_iter); - - const char* str_value = arg.string_value.c_str(); - dbus_message_iter_append_basic(&struct_iter, arg.type, &str_value); - - arg = *(++it); - } - - const char *array_type, *element_type; - switch (arg.type) { - case DBUS_TYPE_STRING: - array_type = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING; - element_type = DBUS_TYPE_STRING_AS_STRING; - break; - case DBUS_TYPE_OBJECT_PATH: - array_type = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING; - element_type = DBUS_TYPE_OBJECT_PATH_AS_STRING; - break; - case DBUS_TYPE_UNIX_FD: - array_type = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UNIX_FD_AS_STRING; - element_type = DBUS_TYPE_UNIX_FD_AS_STRING; - break; - default: - abort(); - // TODO(keybuk): additional argument types - } - - if (as_property_dictionary_) { - dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_VARIANT, - arg.array ? array_type : element_type, - &iter); - } - - DBusMessageIter array_iter; - if (arg.array) { - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, element_type, - &array_iter); - - if (arg.type == DBUS_TYPE_STRING || arg.type == DBUS_TYPE_OBJECT_PATH) { - for (std::vector<std::string>::const_iterator vit = - arg.string_values.begin(); - vit != arg.string_values.end(); ++vit) { - const char* str_value = vit->c_str(); - dbus_message_iter_append_basic(&array_iter, arg.type, &str_value); - } - } - // TODO(keybuk): additional element types - - dbus_message_iter_close_container(&iter, &array_iter); - } else { - if (arg.type == DBUS_TYPE_STRING || arg.type == DBUS_TYPE_OBJECT_PATH) { - const char* str_value = arg.string_value.c_str(); - dbus_message_iter_append_basic(&iter, arg.type, &str_value); - } else if (arg.type == DBUS_TYPE_UNIX_FD) { - dbus_message_iter_append_basic(&iter, arg.type, &arg.int_value); - } - // TODO(keybuk): additional argument types - } - - if (as_property_dictionary_) { - dbus_message_iter_close_container(&struct_iter, &iter); - dbus_message_iter_close_container(&dict_array_iter, &struct_iter); - } - } - - if (as_property_dictionary_) - dbus_message_iter_close_container(&message_iter, &dict_array_iter); -} - -void DBusMatch::SendMessage(DBusConnection* conn, DBusMessage* message) { - dbus_bool_t success; - dbus_uint32_t serial; - success = dbus_connection_send(conn, message, &serial); - - if (success && expect_serial_) - expected_serials_.push_back(serial); -} - -bool DBusMatch::HandleServerMessage(DBusConnection* conn, - DBusMessage* message) { - // Make sure we're expecting a method call or signal of this name - if (message_type_ == DBUS_MESSAGE_TYPE_METHOD_CALL && - !dbus_message_is_method_call(message, interface_.c_str(), - member_.c_str())) - return false; - else if (message_type_ == DBUS_MESSAGE_TYPE_SIGNAL && - !dbus_message_is_signal(message, interface_.c_str(), - member_.c_str())) - return false; - - // Make sure the path is what we expected. - if (path_.length() && - strcmp(path_.c_str(), dbus_message_get_path(message)) != 0) - return false; - - // And the arguments. - if (!MatchMessageArgs(message, &args_)) - return false; - - // Handle any actions. - matched_ = true; - if (send_reply_ || send_error_) { - // Send out the reply - DBusMessage* reply = NULL; - if (send_reply_) - reply = dbus_message_new_method_return(message); - else if (send_error_) - reply = dbus_message_new_error(message, error_name_.c_str(), - error_message_.c_str()); - - AppendArgsToMessage(reply, &reply_args_); - SendMessage(conn, reply); - - dbus_message_unref(reply); - } - - return true; -} - -bool DBusMatch::HandleClientMessage(DBusConnection* conn, - DBusMessage* message) { - // From the client side we check whether the message has a serial number - // we generated on our server side, and if so, remove it from the list of - // those we're expecting to see. - for (std::vector<dbus_uint32_t>::iterator it = expected_serials_.begin(); - it != expected_serials_.end(); ++it) { - if (*it == dbus_message_get_serial(message)) { - expected_serials_.erase(it); - return true; - } - } - - return false; -} - -bool DBusMatch::Complete() { - return matched_ && expected_serials_.size() == 0; -} - -DBusTest::DBusTest() - : conn_(NULL), server_(NULL), server_conn_(NULL), dispatch_(false) {} - -DBusTest::~DBusTest() {} - -DBusMatch& DBusTest::ExpectMethodCall(std::string path, - std::string interface, - std::string method) { - DBusMatch match; - match.ExpectMethodCall(path, interface, method); - pthread_mutex_lock(&mutex_); - matches_.push_back(match); - DBusMatch& ref = matches_.back(); - pthread_mutex_unlock(&mutex_); - return ref; -} - -DBusMatch& DBusTest::CreateSignal(std::string path, - std::string interface, - std::string signal_name) { - DBusMatch match; - match.CreateSignal(server_conn_, path, interface, signal_name); - pthread_mutex_lock(&mutex_); - matches_.push_back(match); - DBusMatch& ref = matches_.back(); - pthread_mutex_unlock(&mutex_); - return ref; -} - -DBusMatch& DBusTest::CreateMessageCall(std::string path, - std::string interface, - std::string signal_name) { - DBusMatch match; - match.CreateMessageCall(server_conn_, path, interface, signal_name); - pthread_mutex_lock(&mutex_); - matches_.push_back(match); - DBusMatch& ref = matches_.back(); - pthread_mutex_unlock(&mutex_); - return ref; -} - -void DBusTest::WaitForMatches() { - for (;;) { - pthread_mutex_lock(&mutex_); - size_t incomplete_matches = 0; - for (std::vector<DBusMatch>::iterator it = matches_.begin(); - it != matches_.end(); ++it) { - DBusMatch& match = *it; - if (!match.Complete()) - ++incomplete_matches; - } - pthread_mutex_unlock(&mutex_); - - if (!incomplete_matches) - break; - - // Fish a message from the queue. - DBusMessage* message; - while ((message = dbus_connection_borrow_message(conn_)) == NULL) - dbus_connection_read_write(conn_, -1); - - // Allow matches to verify the serial of the message. - pthread_mutex_lock(&mutex_); - for (std::vector<DBusMatch>::iterator it = matches_.begin(); - it != matches_.end(); ++it) { - DBusMatch& match = *it; - - if (match.HandleClientMessage(conn_, message)) - break; - } - pthread_mutex_unlock(&mutex_); - - // Throw it back and dispatch. - dbus_connection_return_message(conn_, message); - dbus_connection_dispatch(conn_); - } - - pthread_mutex_lock(&mutex_); - matches_.erase(matches_.begin(), matches_.end()); - pthread_mutex_unlock(&mutex_); -} - -void DBusTest::SetUp() { - dbus_threads_init_default(); - - // Create the D-Bus server that will accept a connection for us, since - // there's no "just give me a socketpair" option in libdbus. - server_ = dbus_server_listen(kServerAddress, NULL); - ASSERT_TRUE(server_ != NULL); - - dbus_server_set_new_connection_function(server_, NewConnectionThunk, this, - NULL); - - dbus_bool_t success; - success = dbus_server_set_watch_functions( - server_, AddWatchThunk, RemoveWatchThunk, WatchToggledThunk, this, NULL); - ASSERT_TRUE(success); - - success = dbus_server_set_timeout_functions(server_, AddTimeoutThunk, - RemoveTimeoutThunk, - TimeoutToggledThunk, this, NULL); - ASSERT_TRUE(success); - - // Open a connection to our server, this returns the "client" side of the - // connection. - conn_ = dbus_connection_open_private(kServerAddress, NULL); - ASSERT_TRUE(conn_ != NULL); - - // The "server" side of the connection comes from the NewConnection method - // we set above. Dispatch until we have it. - while (!server_conn_) - DispatchOnce(); - - // Now we set off "main loop" in the background to dispatch until the - // client is disconnected by the TearDown method. - int r; - r = pthread_mutex_init(&mutex_, NULL); - ASSERT_EQ(0, r); - - dispatch_ = true; - r = pthread_create(&thread_id_, NULL, DispatchLoopThunk, this); - ASSERT_EQ(0, r); -} - -void DBusTest::TearDown() { - WaitForMatches(); - - // Close the client end of the connection, this will result in a signal - // within the dispatch loop of the server. - if (conn_) { - dbus_connection_flush(conn_); - dbus_connection_close(conn_); - dbus_connection_unref(conn_); - conn_ = NULL; - } - - // Join the thread and wait for it to finish dispatch. - if (dispatch_) - pthread_join(thread_id_, NULL); - pthread_mutex_destroy(&mutex_); - - // Clean up the server end of the connection and the server itself. - if (server_conn_) { - dbus_connection_flush(server_conn_); - dbus_connection_close(server_conn_); - dbus_connection_unref(server_conn_); - server_conn_ = NULL; - } - - dbus_server_disconnect(server_); - dbus_server_unref(server_); - server_ = NULL; - - dbus_shutdown(); -} - -void DBusTest::NewConnectionThunk(DBusServer* server, - DBusConnection* conn, - void* data) { - DBusTest* test = static_cast<DBusTest*>(data); - test->NewConnection(server, conn); -} - -void DBusTest::NewConnection(DBusServer* server, DBusConnection* conn) { - ASSERT_TRUE(server_conn_ == NULL); - - dbus_bool_t success; - success = dbus_connection_set_watch_functions( - conn, AddWatchThunk, RemoveWatchThunk, WatchToggledThunk, this, NULL); - ASSERT_TRUE(success); - - success = dbus_connection_set_timeout_functions( - conn, AddTimeoutThunk, RemoveTimeoutThunk, TimeoutToggledThunk, this, - NULL); - ASSERT_TRUE(success); - - success = dbus_connection_add_filter(conn, HandleMessageThunk, this, NULL); - ASSERT_TRUE(success); - - server_conn_ = conn; - dbus_connection_ref(server_conn_); -} - -dbus_bool_t DBusTest::AddWatchThunk(DBusWatch* watch, void* data) { - DBusTest* test = static_cast<DBusTest*>(data); - return test->AddWatch(watch); -} - -dbus_bool_t DBusTest::AddWatch(DBusWatch* watch) { - watches_.push_back(watch); - return TRUE; -} - -void DBusTest::RemoveWatchThunk(DBusWatch* watch, void* data) { - DBusTest* test = static_cast<DBusTest*>(data); - test->RemoveWatch(watch); -} - -void DBusTest::RemoveWatch(DBusWatch* watch) { - std::vector<DBusWatch*>::iterator it = - find(watches_.begin(), watches_.end(), watch); - if (it != watches_.end()) - watches_.erase(it); -} - -void DBusTest::WatchToggledThunk(DBusWatch* watch, void* data) { - DBusTest* test = static_cast<DBusTest*>(data); - test->WatchToggled(watch); -} - -void DBusTest::WatchToggled(DBusWatch* watch) {} - -dbus_bool_t DBusTest::AddTimeoutThunk(DBusTimeout* timeout, void* data) { - DBusTest* test = static_cast<DBusTest*>(data); - return test->AddTimeout(timeout); -} - -dbus_bool_t DBusTest::AddTimeout(DBusTimeout* timeout) { - timeouts_.push_back(timeout); - return TRUE; -} - -void DBusTest::RemoveTimeoutThunk(DBusTimeout* timeout, void* data) { - DBusTest* test = static_cast<DBusTest*>(data); - test->RemoveTimeout(timeout); -} - -void DBusTest::RemoveTimeout(DBusTimeout* timeout) { - std::vector<DBusTimeout*>::iterator it = - find(timeouts_.begin(), timeouts_.end(), timeout); - if (it != timeouts_.end()) - timeouts_.erase(it); -} - -void DBusTest::TimeoutToggledThunk(DBusTimeout* timeout, void* data) { - DBusTest* test = static_cast<DBusTest*>(data); - test->TimeoutToggled(timeout); -} - -void DBusTest::TimeoutToggled(DBusTimeout* timeout) {} - -DBusHandlerResult DBusTest::HandleMessageThunk(DBusConnection* conn, - DBusMessage* message, - void* data) { - DBusTest* test = static_cast<DBusTest*>(data); - return test->HandleMessage(conn, message); -} - -DBusHandlerResult DBusTest::HandleMessage(DBusConnection* conn, - DBusMessage* message) { - if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { - dispatch_ = false; - return DBUS_HANDLER_RESULT_HANDLED; - } - - pthread_mutex_lock(&mutex_); - for (std::vector<DBusMatch>::iterator it = matches_.begin(); - it != matches_.end(); ++it) { - DBusMatch& match = *it; - - if (match.HandleServerMessage(conn, message)) { - pthread_mutex_unlock(&mutex_); - return DBUS_HANDLER_RESULT_HANDLED; - } - } - pthread_mutex_unlock(&mutex_); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -void* DBusTest::DispatchLoopThunk(void* ptr) { - DBusTest* test = static_cast<DBusTest*>(ptr); - return test->DispatchLoop(); -} - -void* DBusTest::DispatchLoop() { - while (dispatch_) - DispatchOnce(); - - return NULL; -} - -void DBusTest::DispatchOnce() { - fd_set readfds, writefds; - int nfds = 0; - int r; - - // Ideally we'd just use dbus_connection_read_write_dispatch() here, but - // we have to deal with both the server itself and its connection, so we - // have to do it all by hand. - FD_ZERO(&readfds); - FD_ZERO(&writefds); - - for (std::vector<DBusWatch*>::iterator it = watches_.begin(); - it != watches_.end(); ++it) { - DBusWatch* watch = *it; - - if (!dbus_watch_get_enabled(watch)) - continue; - - int fd = dbus_watch_get_unix_fd(watch); - if (fd + 1 > nfds) - nfds = fd + 1; - - unsigned int flags = dbus_watch_get_flags(watch); - if (flags & DBUS_WATCH_READABLE) - FD_SET(fd, &readfds); - if (flags & DBUS_WATCH_WRITABLE) - FD_SET(fd, &writefds); - } - - // Only block in select for the interval of the smallest timeout; this - // isn't quite right according to the D-Bus spec, since the interval is - // supposed to be since the time the timeout was added or toggled, but - // it's good enough for the purposes of testing. - DBusTimeout* earliest_timeout = NULL; - struct timeval timeval; - - for (std::vector<DBusTimeout*>::iterator it = timeouts_.begin(); - it != timeouts_.end(); ++it) { - DBusTimeout* timeout = *it; - - if (!dbus_timeout_get_enabled(timeout)) - continue; - - if (!earliest_timeout || (dbus_timeout_get_interval(timeout) < - dbus_timeout_get_interval(earliest_timeout))) - earliest_timeout = timeout; - } - - if (earliest_timeout) { - int interval = dbus_timeout_get_interval(earliest_timeout); - timeval.tv_sec = interval / 1000; - timeval.tv_usec = (interval % 1000) * 1000; - - r = select(nfds, &readfds, &writefds, NULL, &timeval); - } else { - r = select(nfds, &readfds, &writefds, NULL, NULL); - } - - ASSERT_LE(0, r); - - // Handle the timeout if we didn't poll for anything else. - if (r == 0 && earliest_timeout) - dbus_timeout_handle(earliest_timeout); - - // Handle the watches, use a copy of the vector since a watch handler - // might remove other watches in the vector. - std::vector<DBusWatch*> immutable_watches = watches_; - for (std::vector<DBusWatch*>::iterator it = immutable_watches.begin(); - it != immutable_watches.end(); ++it) { - DBusWatch* watch = *it; - - int fd = dbus_watch_get_unix_fd(watch); - unsigned int flags = 0; - - if (FD_ISSET(fd, &readfds)) - flags |= DBUS_WATCH_READABLE; - if (FD_ISSET(fd, &writefds)) - flags |= DBUS_WATCH_WRITABLE; - - if (flags) - dbus_watch_handle(watch, flags); - } - - // Dispatch data on the server-side of the connection. - while (server_conn_ && - dbus_connection_dispatch(server_conn_) == DBUS_DISPATCH_DATA_REMAINS) - ; -} |