diff options
Diffstat (limited to 'tests/utils/adbd-server.h')
-rw-r--r-- | tests/utils/adbd-server.h | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/tests/utils/adbd-server.h b/tests/utils/adbd-server.h new file mode 100644 index 0000000..740faaa --- /dev/null +++ b/tests/utils/adbd-server.h @@ -0,0 +1,148 @@ +/* + * Copyright 2016 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + */ + +#include <gio/gio.h> +#include <gio/gunixsocketaddress.h> + +#include <string> +#include <thread> +#include <vector> + + +/** + * A Mock ADBD server. + * + * Binds to a local domain socket, sends public key requests across it, + * and reads back the client's responses. + */ +class GAdbdServer +{ +public: + + GAdbdServer(const std::string& socket_path, + const std::vector<std::string>& requests): + m_requests{requests}, + m_socket_path{socket_path}, + m_cancellable{g_cancellable_new()}, + m_worker_thread{&GAdbdServer::worker_func, this} + { + } + + ~GAdbdServer() + { + // tell the worker thread to stop whatever it's doing and exit. + g_cancellable_cancel(m_cancellable); + m_worker_thread.join(); + g_clear_object(&m_cancellable); + } + + const std::vector<std::string> m_requests; + std::vector<std::string> m_responses; + +private: + + void worker_func() // runs in worker thread + { + auto server_socket = create_server_socket(m_socket_path); + auto requests = m_requests; + + GError* error {}; + g_socket_listen (server_socket, &error); + g_assert_no_error (error); + + while (!g_cancellable_is_cancelled(m_cancellable) && !requests.empty()) + { + // wait for a client connection + g_message("GAdbdServer::Impl::worker_func() calling g_socket_accept()"); + auto client_socket = g_socket_accept(server_socket, m_cancellable, &error); + if (error != nullptr) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("GAdbdServer: Error accepting socket connection: %s", error->message); + g_clear_error(&error); + break; + } + + // pop the next request off the stack + auto request = requests.front(); + requests.erase(requests.begin()); + + // send the request + g_message("GAdbdServer::Impl::worker_func() sending req [%s]", request.c_str()); + g_socket_send(client_socket, + request.c_str(), + request.size(), + m_cancellable, + &error); + if (error != nullptr) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("GAdbdServer: Error sending request: %s", error->message); + g_clear_error(&error); + g_clear_object(&client_socket); + break; + } + + // read the response + g_message("GAdbdServer::Impl::worker_func() reading response"); + char buf[4096]; + const auto n_bytes = g_socket_receive(client_socket, + buf, + sizeof(buf), + m_cancellable, + &error); + if (error != nullptr) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("GAdbdServer: Error reading response: %s", error->message); + g_clear_error(&error); + g_clear_object(&client_socket); + break; + } + const std::string response(buf, std::string::size_type(n_bytes)); + g_message("server got response: %s", response.c_str()); + m_responses.push_back(response); + + // cleanup + g_clear_object(&client_socket); + } + + g_clear_object(&server_socket); + } + + // bind to a local domain socket + static GSocket* create_server_socket(const std::string& socket_path) + { + GError* error {}; + auto socket = g_socket_new(G_SOCKET_FAMILY_UNIX, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error(error); + auto address = g_unix_socket_address_new (socket_path.c_str()); + g_socket_bind (socket, address, false, &error); + g_assert_no_error (error); + g_clear_object (&address); + + return socket; + } + + const std::string m_socket_path; + GCancellable* m_cancellable = nullptr; + std::thread m_worker_thread; +}; + + |