From b1be45c3b8cef5fede101985a26363319f3a9645 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 4 Mar 2016 12:40:23 -0600 Subject: update glib/dbus test fixtures from indicator-datetime/15.10 branch --- tests/CMakeLists.txt | 2 +- tests/glib-fixture.h | 89 +++++++++++------------------------ tests/gtestdbus-fixture.h | 108 ------------------------------------------- tests/test-dbus-fixture.h | 106 ++++++++++++++++++++++++++++++++++++++++++ tests/test-rotation-lock.cpp | 6 +-- 5 files changed, 136 insertions(+), 175 deletions(-) delete mode 100644 tests/gtestdbus-fixture.h create mode 100644 tests/test-dbus-fixture.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 054a676..6c8be9b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,5 +31,5 @@ function(add_test_by_name name) endfunction() add_test_by_name(test-rotation-lock) -add_test (cppcheck cppcheck --enable=all -q --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) +add_test (cppcheck cppcheck --enable=all -USCHEMA_DIR --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h index 65d2921..41ac6e8 100644 --- a/tests/glib-fixture.h +++ b/tests/glib-fixture.h @@ -1,5 +1,8 @@ /* - * Copyright 2014 Canonical Ltd. + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr * * 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 @@ -12,13 +15,9 @@ * * You should have received a copy of the GNU General Public License along * with this program. If not, see . - * - * Authors: - * Charles Kerr */ -#ifndef INDICATOR_TESTS_GLIB_FIXTURE_H -#define INDICATOR_TESTS_GLIB_FIXTURE_H +#pragma once #include @@ -32,75 +31,44 @@ class GlibFixture : public ::testing::Test { - private: - - GLogFunc realLogHandler; - - std::map expected_log; - std::map> log; - - void test_log_counts() - { - const GLogLevelFlags levels_to_test[] = { G_LOG_LEVEL_ERROR, - G_LOG_LEVEL_CRITICAL, - G_LOG_LEVEL_MESSAGE, - G_LOG_LEVEL_WARNING }; - - for(const auto& level : levels_to_test) - { - const auto& v = log[level]; - const auto n = v.size(); - - EXPECT_EQ(expected_log[level], n); - - if (expected_log[level] != n) - for (size_t i=0; i(self)->log[log_level].push_back(tmp); - g_free(tmp); - } + virtual ~GlibFixture() =default; protected: - void increment_expected_errors(GLogLevelFlags level, size_t n=1) - { - expected_log[level] += n; - } - - virtual void SetUp() + virtual void SetUp() override { setlocale(LC_ALL, "C.UTF-8"); loop = g_main_loop_new(nullptr, false); - g_log_set_default_handler(default_log_handler, this); - +#ifdef SCHEMA_DIR + // only use local, temporary settings + g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); + g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); +#endif + + // fail on unexpected messages from this domain + g_log_set_fatal_mask(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING); g_unsetenv("DISPLAY"); + } - virtual void TearDown() + virtual void TearDown() override { - test_log_counts(); - - g_log_set_default_handler(realLogHandler, this); + g_test_assert_expected_messages (); g_clear_pointer(&loop, g_main_loop_unref); } + void expectLogMessage (const gchar *domain, GLogLevelFlags level, const gchar *pattern) + { + g_test_expect_message (domain, level, pattern); + } + private: static gboolean @@ -120,7 +88,7 @@ class GlibFixture : public ::testing::Test protected: /* convenience func to loop while waiting for a GObject's signal */ - void wait_for_signal(gpointer o, const gchar * signal, const guint timeout_seconds=5) + void wait_for_signal(gpointer o, const gchar * signal, const int timeout_seconds=5) { // wait for the signal or for timeout, whichever comes first const auto handler_id = g_signal_connect_swapped(o, signal, @@ -135,7 +103,7 @@ class GlibFixture : public ::testing::Test } /* convenience func to loop for N msec */ - void wait_msec(guint msec=50) + void wait_msec(int msec=50) { const auto id = g_timeout_add(msec, wait_msec__timeout, loop); g_main_loop_run(loop); @@ -143,10 +111,5 @@ class GlibFixture : public ::testing::Test } GMainLoop * loop; - - public: - - virtual ~GlibFixture() =default; }; -#endif /* INDICATOR_TESTS_GLIB_FIXTURE_H */ diff --git a/tests/gtestdbus-fixture.h b/tests/gtestdbus-fixture.h deleted file mode 100644 index c592033..0000000 --- a/tests/gtestdbus-fixture.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2013 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 . - * - * Authors: - * Charles Kerr - */ - -#ifndef INDICATOR_TESTS_GTESTDBUS_FIXTURE_H -#define INDICATOR_TESTS_GTESTDBUS_FIXTURE_H - -#include "glib-fixture.h" - -/*** -**** -***/ - -class GTestDBusFixture: public GlibFixture -{ - public: - - GTestDBusFixture() =default; - virtual ~GTestDBusFixture() =default; - - explicit GTestDBusFixture(const std::vector& service_dirs_in): service_dirs(service_dirs_in) {} - - private: - - typedef GlibFixture super; - - static void - on_bus_opened (GObject* /*object*/, GAsyncResult * res, gpointer gself) - { - auto self = static_cast(gself); - - GError * err = 0; - self->bus = g_bus_get_finish (res, &err); - g_assert_no_error (err); - - g_main_loop_quit (self->loop); - } - - static void - on_bus_closed (GObject* /*object*/, GAsyncResult * res, gpointer gself) - { - auto self = static_cast(gself); - - GError * err = 0; - g_dbus_connection_close_finish (self->bus, res, &err); - g_assert_no_error (err); - - g_main_loop_quit (self->loop); - } - - protected: - - GTestDBus * test_dbus = nullptr; - GDBusConnection * bus = nullptr; - const std::vector service_dirs; - - virtual void SetUp () - { - super::SetUp (); - - // pull up a test dbus - test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); - for (const auto& dir : service_dirs) - g_test_dbus_add_service_dir (test_dbus, dir.c_str()); - g_test_dbus_up (test_dbus); - const char * address = g_test_dbus_get_bus_address (test_dbus); - g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, true); - g_setenv ("DBUS_SESSION_BUS_ADDRESS", address, true); - g_debug ("test_dbus's address is %s", address); - - // wait for the GDBusConnection before returning - g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); - g_main_loop_run (loop); - } - - virtual void TearDown () - { - wait_msec(); - - // close the system bus - g_dbus_connection_close(bus, nullptr, on_bus_closed, this); - g_main_loop_run(loop); - g_clear_object(&bus); - - // tear down the test dbus - g_test_dbus_down(test_dbus); - g_clear_object(&test_dbus); - - super::TearDown(); - } -}; - -#endif /* INDICATOR_TESTS_GTESTDBUS_FIXTURE_H */ diff --git a/tests/test-dbus-fixture.h b/tests/test-dbus-fixture.h new file mode 100644 index 0000000..3947e58 --- /dev/null +++ b/tests/test-dbus-fixture.h @@ -0,0 +1,106 @@ +/* + * Copyright 2013 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#include "glib-fixture.h" + +/*** +**** +***/ + +class TestDBusFixture: public GlibFixture +{ + public: + + TestDBusFixture() =default; + virtual ~TestDBusFixture() =default; + + explicit TestDBusFixture(const std::vector& service_dirs_in): service_dirs(service_dirs_in) {} + + private: + + typedef GlibFixture super; + + static void + on_bus_opened (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + self->system_bus = g_bus_get_finish (res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + static void + on_bus_closed (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + g_dbus_connection_close_finish (self->system_bus, res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + protected: + + GTestDBus * test_dbus = nullptr; + GDBusConnection * system_bus = nullptr; + const std::vector service_dirs; + + virtual void SetUp() override + { + super::SetUp (); + + // pull up a test dbus + test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); + for (const auto& dir : service_dirs) + g_test_dbus_add_service_dir (test_dbus, dir.c_str()); + g_test_dbus_up (test_dbus); + const char * address = g_test_dbus_get_bus_address (test_dbus); + g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", address, true); + g_debug ("test_dbus's address is %s", address); + + // wait for the GDBusConnection before returning + g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); + g_main_loop_run (loop); + } + + virtual void TearDown() override + { + wait_msec(); + + // close the system bus + g_dbus_connection_close(system_bus, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&system_bus); + + // tear down the test dbus + g_test_dbus_down(test_dbus); + g_clear_object(&test_dbus); + + super::TearDown(); + } +}; + diff --git a/tests/test-rotation-lock.cpp b/tests/test-rotation-lock.cpp index 946b1dd..b8661cc 100644 --- a/tests/test-rotation-lock.cpp +++ b/tests/test-rotation-lock.cpp @@ -17,14 +17,14 @@ * Charles Kerr */ -#include "gtestdbus-fixture.h" +#include "test-dbus-fixture.h" #include -class RotationLockFixture: public GTestDBusFixture +class RotationLockFixture: public TestDBusFixture { private: - typedef GTestDBusFixture super; + typedef TestDBusFixture super; protected: -- cgit v1.2.3 From d6cf064484558336c3258302857c89ef50b01fec Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 4 Mar 2016 12:49:10 -0600 Subject: fix RotationLock shutdown issue that messed with getting clean test runs --- src/rotation-lock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rotation-lock.cpp b/src/rotation-lock.cpp index f19ac9f..88c7e1b 100644 --- a/src/rotation-lock.cpp +++ b/src/rotation-lock.cpp @@ -43,6 +43,7 @@ public: ~Impl() { + g_signal_handlers_disconnect_by_data(m_settings, this); g_clear_object(&m_action_group); g_clear_object(&m_settings); } -- cgit v1.2.3 From 8cf3f1a9b4e9d102dfeaf5fcfa7bc4a43711d0ae Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 4 Mar 2016 12:51:36 -0600 Subject: cosmetic: use -test as the suffix for test files --- tests/CMakeLists.txt | 2 +- tests/rotation-lock-test.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++ tests/test-rotation-lock.cpp | 61 -------------------------------------------- 3 files changed, 62 insertions(+), 62 deletions(-) create mode 100644 tests/rotation-lock-test.cpp delete mode 100644 tests/test-rotation-lock.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6c8be9b..59e50bc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,7 +29,7 @@ function(add_test_by_name name) add_dependencies (${TEST_NAME} libindicatordisplayservice) target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES}) endfunction() -add_test_by_name(test-rotation-lock) +add_test_by_name(rotation-lock-test) add_test (cppcheck cppcheck --enable=all -USCHEMA_DIR --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) diff --git a/tests/rotation-lock-test.cpp b/tests/rotation-lock-test.cpp new file mode 100644 index 0000000..b8661cc --- /dev/null +++ b/tests/rotation-lock-test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2014 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 . + * + * Authors: + * Charles Kerr + */ + +#include "test-dbus-fixture.h" + +#include + +class RotationLockFixture: public TestDBusFixture +{ +private: + typedef TestDBusFixture super; + +protected: + + void SetUp() + { + super::SetUp(); + } + + void TearDown() + { + super::TearDown(); + } +}; + +/*** +**** +***/ + +TEST_F(RotationLockFixture, CheckIndicator) +{ + RotationLockIndicator indicator; + + ASSERT_STREQ("rotation_lock", indicator.name()); + auto actions = indicator.action_group(); + ASSERT_TRUE(actions != nullptr); + ASSERT_TRUE(g_action_group_has_action(G_ACTION_GROUP(actions), "rotation-lock")); + + std::vector> profiles = indicator.profiles(); + ASSERT_EQ(1, profiles.size()); + std::shared_ptr phone = profiles[0]; + ASSERT_EQ(std::string("phone"), phone->name()); + ASSERT_FALSE(phone->header()->is_visible); +} + diff --git a/tests/test-rotation-lock.cpp b/tests/test-rotation-lock.cpp deleted file mode 100644 index b8661cc..0000000 --- a/tests/test-rotation-lock.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2014 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 . - * - * Authors: - * Charles Kerr - */ - -#include "test-dbus-fixture.h" - -#include - -class RotationLockFixture: public TestDBusFixture -{ -private: - typedef TestDBusFixture super; - -protected: - - void SetUp() - { - super::SetUp(); - } - - void TearDown() - { - super::TearDown(); - } -}; - -/*** -**** -***/ - -TEST_F(RotationLockFixture, CheckIndicator) -{ - RotationLockIndicator indicator; - - ASSERT_STREQ("rotation_lock", indicator.name()); - auto actions = indicator.action_group(); - ASSERT_TRUE(actions != nullptr); - ASSERT_TRUE(g_action_group_has_action(G_ACTION_GROUP(actions), "rotation-lock")); - - std::vector> profiles = indicator.profiles(); - ASSERT_EQ(1, profiles.size()); - std::shared_ptr phone = profiles[0]; - ASSERT_EQ(std::string("phone"), phone->name()); - ASSERT_FALSE(phone->header()->is_visible); -} - -- cgit v1.2.3 From d911528cfb367fac34a5764ad6bce339a12f56d0 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sun, 6 Mar 2016 23:00:42 -0600 Subject: add ADB server/client + tests --- CMakeLists.txt | 5 +- src/CMakeLists.txt | 3 +- src/adbd-client.cpp | 258 +++++++++++++++++++++++++++++++++++++++++++++ src/adbd-client.h | 73 +++++++++++++ src/main.cpp | 11 ++ tests/CMakeLists.txt | 12 +-- tests/adbd-client-test.cpp | 94 +++++++++++++++++ tests/adbd-server.h | 148 ++++++++++++++++++++++++++ tests/glib-fixture.h | 94 ++++++++++++++++- 9 files changed, 686 insertions(+), 12 deletions(-) create mode 100644 src/adbd-client.cpp create mode 100644 src/adbd-client.h create mode 100644 tests/adbd-client-test.cpp create mode 100644 tests/adbd-server.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a35d95..9fa4d10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,9 @@ set (SERVICE_EXEC "${PACKAGE}-service") option (enable_tests "Build the package's automatic tests." ON) option (enable_lcov "Generate lcov code coverage reports." ON) +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + ## ## GNU standard paths ## @@ -47,7 +50,7 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR}) # set the compiler warnings if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Weverything -Wno-c++98-compat") + set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Weverything -Wno-c++98-compat -Wno-padded") else() set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wall -Wextra -Wpedantic") endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 982aa49..414a750 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ add_definitions (-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}") # handwritten source code... set (SERVICE_LIB_HANDWRITTEN_SOURCES + adbd-client.cpp exporter.cpp rotation-lock.cpp) @@ -19,7 +20,7 @@ link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) set (SERVICE_EXEC_HANDWRITTEN_SOURCES main.cpp) add_executable (${SERVICE_EXEC} ${SERVICE_EXEC_HANDWRITTEN_SOURCES}) -target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) +target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} Threads::Threads ${GCOV_LIBS}) install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) # add warnings/coverage info on handwritten files diff --git a/src/adbd-client.cpp b/src/adbd-client.cpp new file mode 100644 index 0000000..38f202f --- /dev/null +++ b/src/adbd-client.cpp @@ -0,0 +1,258 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include + +#include +#include +#include +#include + +class GAdbdClient::Impl +{ +public: + + explicit Impl(const std::string& socket_path): + m_socket_path{socket_path}, + m_cancellable{g_cancellable_new()}, + m_worker_thread{&Impl::worker_func, this} + { + } + + ~Impl() + { + // tell the worker thread to stop whatever it's doing and exit. + g_cancellable_cancel(m_cancellable); + m_sleep_cv.notify_one(); + m_worker_thread.join(); + g_clear_object(&m_cancellable); + } + + core::Signal& on_pk_request() + { + return m_on_pk_request; + } + +private: + + // struct to carry request info from the worker thread to the GMainContext thread + struct PKIdleData + { + Impl* self = nullptr; + GCancellable* cancellable = nullptr; + const std::string public_key; + + PKIdleData(Impl* self_, GCancellable* cancellable_, std::string public_key_): + self(self_), + cancellable(G_CANCELLABLE(g_object_ref(cancellable_))), + public_key(public_key_) {} + + ~PKIdleData() {g_clear_object(&cancellable);} + + }; + + void pass_public_key_to_main_thread(const std::string& public_key) + { + g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, + on_public_key_request_static, + new PKIdleData{this, m_cancellable, public_key}, + [](gpointer id){delete static_cast(id);}); + } + + static gboolean on_public_key_request_static (gpointer gdata) // runs in main thread + { + /* NB: It's possible (though unlikely) that data.self was destroyed + while this callback was pending, so we must check is-cancelled FIRST */ + auto data = static_cast(gdata); + if (!g_cancellable_is_cancelled(data->cancellable)) + { + // notify our listeners of the request + auto self = data->self; + struct PKRequest req; + req.public_key = data->public_key; + req.respond = [self](PKResponse response){self->on_public_key_response(response);}; + self->m_on_pk_request(req); + } + + return G_SOURCE_REMOVE; + } + + void on_public_key_response(PKResponse response) + { + // set m_pkresponse and wake up the waiting worker thread + std::unique_lock lk(m_pkresponse_mutex); + m_pkresponse = response; + m_pkresponse_ready = true; + m_pkresponse_cv.notify_one(); + } + + /*** + **** + ***/ + + void worker_func() // runs in worker thread + { + const auto socket_path {m_socket_path}; + + while (!g_cancellable_is_cancelled(m_cancellable)) + { + g_debug("%s creating a client socket", G_STRLOC); + auto socket = create_client_socket(socket_path); + bool got_valid_req = false; + + g_debug("%s calling read_request", G_STRLOC); + std::string reqstr; + if (socket != nullptr) + reqstr = read_request(socket); + if (!reqstr.empty()) + g_debug("%s got request [%s]", G_STRLOC, reqstr.c_str()); + + if (reqstr.substr(0,2) == "PK") { + PKResponse response = PKResponse::DENY; + const auto public_key = reqstr.substr(2); + g_debug("%s got pk [%s]", G_STRLOC, public_key.c_str()); + if (!public_key.empty()) { + got_valid_req = true; + std::unique_lock lk(m_pkresponse_mutex); + m_pkresponse_ready = false; + pass_public_key_to_main_thread(public_key); + m_pkresponse_cv.wait(lk, [this](){ + return m_pkresponse_ready || g_cancellable_is_cancelled(m_cancellable); + }); + response = m_pkresponse; + g_debug("%s got response '%d'", G_STRLOC, int(response)); + } + if (!g_cancellable_is_cancelled(m_cancellable)) + send_pk_response(socket, response); + } else if (!reqstr.empty()) { + g_warning("Invalid ADB request: [%s]", reqstr.c_str()); + } + + g_clear_object(&socket); + + // If nothing interesting's happening, sleep a bit. + // (Interval copied from UsbDebuggingManager.java) + static constexpr auto sleep_interval {std::chrono::seconds(1)}; + if (!got_valid_req && !g_cancellable_is_cancelled(m_cancellable)) { + std::unique_lock lk(m_sleep_mutex); + m_sleep_cv.wait_for(lk, sleep_interval); + } + } + } + + // connect to a local domain socket + GSocket* create_client_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); + if (error != nullptr) { + g_warning("Error creating adbd client socket: %s", error->message); + g_clear_error(&error); + g_clear_object(&socket); + return nullptr; + } + + auto address = g_unix_socket_address_new(socket_path.c_str()); + const auto connected = g_socket_connect(socket, address, m_cancellable, nullptr); + g_clear_object(&address); + if (!connected) { + g_clear_object(&socket); + return nullptr; + } + + return socket; + } + + std::string read_request(GSocket* socket) + { + char buf[4096] = {}; + g_debug("%s calling g_socket_receive()", G_STRLOC); + const auto n_bytes = g_socket_receive (socket, buf, sizeof(buf), m_cancellable, nullptr); + std::string ret; + if (n_bytes > 0) + ret.append(buf, std::string::size_type(n_bytes)); + g_debug("%s g_socket_receive got %d bytes: [%s]", G_STRLOC, int(n_bytes), ret.c_str()); + return ret; + } + + void send_pk_response(GSocket* socket, PKResponse response) + { + std::string response_str; + switch(response) { + case PKResponse::ALLOW: response_str = "OK"; break; + case PKResponse::DENY: response_str = "NO"; break; + } + g_debug("%s sending reply: [%s]", G_STRLOC, response_str.c_str()); + + GError* error {}; + g_socket_send(socket, + response_str.c_str(), + response_str.size(), + 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); + } + } + + const std::string m_socket_path; + GCancellable* m_cancellable = nullptr; + std::thread m_worker_thread; + core::Signal m_on_pk_request; + + std::mutex m_sleep_mutex; + std::condition_variable m_sleep_cv; + + std::mutex m_pkresponse_mutex; + std::condition_variable m_pkresponse_cv; + bool m_pkresponse_ready = false; + PKResponse m_pkresponse = PKResponse::DENY; +}; + +/*** +**** +***/ + +AdbdClient::~AdbdClient() +{ +} + +GAdbdClient::GAdbdClient(const std::string& socket_path): + impl{new Impl{socket_path}} +{ +} + +GAdbdClient::~GAdbdClient() +{ +} + +core::Signal& +GAdbdClient::on_pk_request() +{ + return impl->on_pk_request(); +} + diff --git a/src/adbd-client.h b/src/adbd-client.h new file mode 100644 index 0000000..aef7674 --- /dev/null +++ b/src/adbd-client.h @@ -0,0 +1,73 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#include +#include +#include + +#include + +/** + * Receives public key requests from ADBD and sends a response back. + * + * AdbClient only provides a receive/respond mechanism. The decision + * of what response gets sent is delegated out to a listener via + * the on_pk_request signal. + * + * The decider should connect to on_pk_request, listen for PKRequests, + * and call the request's `respond' method with the desired response. + */ +class AdbdClient +{ +public: + virtual ~AdbdClient(); + + enum class PKResponse { DENY, ALLOW }; + + struct PKRequest { + std::string public_key; + std::function respond; + }; + + virtual core::Signal& on_pk_request() =0; + +protected: + AdbdClient() =default; +}; + +/** + * An AdbdClient designed to work with GLib's event loop. + * + * The on_pk_request() signal will be called in global GMainContext's thread; + * ie, just like a function passed to g_idle_add() or g_timeout_add(). + */ +class GAdbdClient: public AdbdClient +{ +public: + explicit GAdbdClient(const std::string& socket_path); + ~GAdbdClient(); + core::Signal& on_pk_request() override; + +private: + class Impl; + std::unique_ptr impl; +}; + diff --git a/src/main.cpp b/src/main.cpp index 86bdeb3..0c56bd6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ * Charles Kerr */ +#include #include #include @@ -54,6 +55,16 @@ main(int /*argc*/, char** /*argv*/) exporters.push_back(exporter); } + // We need the ADBD handler running, + // even though it doesn't have an indicator component yet + static constexpr char const * ADB_SOCKET_PATH {"/dev/socket/adb"}; + GAdbdClient adbd_client{ADB_SOCKET_PATH}; + adbd_client.on_pk_request().connect([](const AdbdClient::PKRequest& req){ + g_debug("%s got pk_request [%s]", G_STRLOC, req.public_key.c_str()); + // FIXME: actually decide what response to send back + req.respond(AdbdClient::PKResponse::ALLOW); + }); + g_main_loop_run(loop); // cleanup diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 59e50bc..706e35b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,19 +3,14 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${GMOCK_INCLUDE_DIRS}) include_directories(${GTEST_INCLUDE_DIRS}) -# build libgtest -#add_library (gtest STATIC -# ${GTEST_SOURCE_DIR}/gtest-all.cc -# ${GTEST_SOURCE_DIR}/gtest_main.cc) -#set_target_properties (gtest PROPERTIES INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${GTEST_INCLUDE_DIR}) -#set_target_properties (gtest PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -w) +set(CTEST_ENVIRONMENT "${CTEST_ENVIRONMENT};G_MESSAGES_DEBUG=all") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # turn off the warnings that break Google Test set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-global-constructors -Wno-weak-vtables -Wno-undef -Wno-c++98-compat-pedantic -Wno-missing-noreturn -Wno-used-but-marked-unused -Wno-padded -Wno-deprecated -Wno-sign-compare -Wno-shift-sign-overflow") endif() -SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}") +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -pthread ${CXX_WARNING_ARGS}") # look for headers in our src dir, and also in the directories where we autogenerate files... include_directories (${CMAKE_SOURCE_DIR}/src) @@ -26,10 +21,13 @@ function(add_test_by_name name) set (TEST_NAME ${name}) add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test (${TEST_NAME} ${TEST_NAME}) + set_property(TEST ${TEST_NAME} PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") + add_dependencies (${TEST_NAME} libindicatordisplayservice) target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES}) endfunction() add_test_by_name(rotation-lock-test) +add_test_by_name(adbd-client-test) add_test (cppcheck cppcheck --enable=all -USCHEMA_DIR --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) diff --git a/tests/adbd-client-test.cpp b/tests/adbd-client-test.cpp new file mode 100644 index 0000000..4fa16a8 --- /dev/null +++ b/tests/adbd-client-test.cpp @@ -0,0 +1,94 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include + +#include // mkdtemp + +class AdbdClientFixture: public TestDBusFixture +{ +private: + typedef TestDBusFixture super; + +protected: + + std::string m_tmpdir; + + void SetUp() + { + super::SetUp(); + + char tmpl[] {"adb-client-test-XXXXXX"}; + m_tmpdir = mkdtemp(tmpl); + g_message("using tmpdir '%s'", m_tmpdir.c_str()); + } + + void TearDown() + { + g_rmdir(m_tmpdir.c_str()); + + super::TearDown(); + } +}; + + +TEST_F(AdbdClientFixture, SocketPlumbing) +{ + const auto socket_path = m_tmpdir + "/test-socket-plumbing"; + g_message("socket_path is %s", socket_path.c_str()); + + struct { + const std::string request; + const std::string expected_pk; + AdbdClient::PKResponse response; + const std::string expected_response; + } tests[] = { + { "PKHelloWorld", "HelloWorld", AdbdClient::PKResponse::ALLOW, "OK" }, + { "PKHelloWorld", "HelloWorld", AdbdClient::PKResponse::DENY, "NO" }, + { "PKFooBar", "FooBar", AdbdClient::PKResponse::ALLOW, "OK" }, + { "PK", "", AdbdClient::PKResponse::DENY, "NO" } + }; + + for (const auto& test : tests) + { + // make the AdbdClient and start listening for Requests + std::string pk; + auto adbd_client = std::make_shared(socket_path); + adbd_client->on_pk_request().connect([&pk, test](const AdbdClient::PKRequest& req){ + g_message("in on_pk_request with %s", req.public_key.c_str()); + pk = req.public_key; + req.respond(test.response); + }); + + // fire up a mock ADB server with a preloaded request, and wait for a response + auto adbd_server = std::make_shared(socket_path, std::vector{test.request}); + wait_for([adbd_server](){return !adbd_server->m_responses.empty();}, 2000); + EXPECT_EQ(test.expected_pk, pk); + ASSERT_EQ(1, adbd_server->m_responses.size()); + EXPECT_EQ(test.expected_response, adbd_server->m_responses.front()); + + // cleanup + adbd_client.reset(); + adbd_server.reset(); + g_unlink(socket_path.c_str()); + } +} diff --git a/tests/adbd-server.h b/tests/adbd-server.h new file mode 100644 index 0000000..740faaa --- /dev/null +++ b/tests/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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include +#include +#include + + +/** + * 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& 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 m_requests; + std::vector 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; +}; + + diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h index 41ac6e8..ccdeccd 100644 --- a/tests/glib-fixture.h +++ b/tests/glib-fixture.h @@ -19,7 +19,9 @@ #pragma once +#include // std::function #include +#include // std::shared_ptr #include #include @@ -74,7 +76,7 @@ class GlibFixture : public ::testing::Test static gboolean wait_for_signal__timeout(gpointer name) { - g_error("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name); + g_error("%s: timed out waiting for signal '%s'", G_STRLOC, static_cast(name)); return G_SOURCE_REMOVE; } @@ -88,7 +90,7 @@ class GlibFixture : public ::testing::Test protected: /* convenience func to loop while waiting for a GObject's signal */ - void wait_for_signal(gpointer o, const gchar * signal, const int timeout_seconds=5) + void wait_for_signal(gpointer o, const gchar * signal, guint timeout_seconds=5) { // wait for the signal or for timeout, whichever comes first const auto handler_id = g_signal_connect_swapped(o, signal, @@ -103,13 +105,99 @@ class GlibFixture : public ::testing::Test } /* convenience func to loop for N msec */ - void wait_msec(int msec=50) + void wait_msec(guint msec=50) { const auto id = g_timeout_add(msec, wait_msec__timeout, loop); g_main_loop_run(loop); g_source_remove(id); } + bool wait_for(std::function test_function, guint timeout_msec=1000) + { + auto timer = std::shared_ptr(g_timer_new(), [](GTimer* t){g_timer_destroy(t);}); + const auto timeout_sec = timeout_msec / 1000.0; + for (;;) { + if (test_function()) + return true; + //g_message("%f ... %f", g_timer_elapsed(timer.get(), nullptr), timeout_sec); + if (g_timer_elapsed(timer.get(), nullptr) >= timeout_sec) + return false; + wait_msec(); + } + } + + bool wait_for_name_owned(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + struct Data { + GMainLoop* loop = nullptr; + bool owned = false; + }; + Data data; + + auto on_name_appeared = [](GDBusConnection* /*connection*/, + const gchar* /*name_*/, + const gchar* name_owner, + gpointer gdata) + { + if (name_owner == nullptr) + return; + auto tmp = static_cast(gdata); + tmp->owned = true; + g_main_loop_quit(tmp->loop); + }; + + const auto timeout_id = g_timeout_add(timeout_msec, wait_msec__timeout, loop); + data.loop = loop; + const auto watch_id = g_bus_watch_name_on_connection(connection, + name, + flags, + on_name_appeared, + nullptr, /* name_vanished */ + &data, + nullptr); /* user_data_free_func */ + g_main_loop_run(loop); + + g_bus_unwatch_name(watch_id); + g_source_remove(timeout_id); + + return data.owned; + } + + void EXPECT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + EXPECT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; + } + + void EXPECT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + EXPECT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; + } + + void ASSERT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + ASSERT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; + } + + void ASSERT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + ASSERT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; + } + GMainLoop * loop; }; -- cgit v1.2.3 From 95277f3c7344e537ad42835c64607339e1d3e6e9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 7 Mar 2016 09:34:37 -0600 Subject: add test to confirm on_pk() signal is fired in the main thread rather than the worker thread --- tests/adbd-client-test.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/adbd-client-test.cpp b/tests/adbd-client-test.cpp index 4fa16a8..ae0378b 100644 --- a/tests/adbd-client-test.cpp +++ b/tests/adbd-client-test.cpp @@ -53,9 +53,6 @@ protected: TEST_F(AdbdClientFixture, SocketPlumbing) { - const auto socket_path = m_tmpdir + "/test-socket-plumbing"; - g_message("socket_path is %s", socket_path.c_str()); - struct { const std::string request; const std::string expected_pk; @@ -68,18 +65,24 @@ TEST_F(AdbdClientFixture, SocketPlumbing) { "PK", "", AdbdClient::PKResponse::DENY, "NO" } }; + const auto main_thread = g_thread_self(); + + const auto socket_path = m_tmpdir + "/test-socket-plumbing"; + g_message("socket_path is %s", socket_path.c_str()); + for (const auto& test : tests) { - // make the AdbdClient and start listening for Requests + // start an AdbdClient that listens for PKRequests std::string pk; auto adbd_client = std::make_shared(socket_path); - adbd_client->on_pk_request().connect([&pk, test](const AdbdClient::PKRequest& req){ + adbd_client->on_pk_request().connect([&pk, main_thread, test](const AdbdClient::PKRequest& req){ + EXPECT_EQ(main_thread, g_thread_self()); g_message("in on_pk_request with %s", req.public_key.c_str()); pk = req.public_key; req.respond(test.response); }); - // fire up a mock ADB server with a preloaded request, and wait for a response + // start a mock AdbdServer with to fire test key requests and wait for a response auto adbd_server = std::make_shared(socket_path, std::vector{test.request}); wait_for([adbd_server](){return !adbd_server->m_responses.empty();}, 2000); EXPECT_EQ(test.expected_pk, pk); -- cgit v1.2.3 From 40f48471fe531ba5b9f1e1c4371f252fca4c2d52 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 8 Mar 2016 13:08:07 -0600 Subject: add out-of-line virtual method definitions to Indicator to silence clang++ warnings --- src/CMakeLists.txt | 4 +++- src/indicator.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/indicator.h | 13 ++++++------- 3 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 src/indicator.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 414a750..ff385d9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,9 @@ add_definitions (-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}") set (SERVICE_LIB_HANDWRITTEN_SOURCES adbd-client.cpp exporter.cpp - rotation-lock.cpp) + indicator.cpp + rotation-lock.cpp + usb-snap.cpp) add_library (${SERVICE_LIB} STATIC ${SERVICE_LIB_HANDWRITTEN_SOURCES}) diff --git a/src/indicator.cpp b/src/indicator.cpp new file mode 100644 index 0000000..77c4af7 --- /dev/null +++ b/src/indicator.cpp @@ -0,0 +1,37 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +Profile::Profile() +{ +} + +Profile::~Profile() +{ +} + +SimpleProfile::~SimpleProfile() +{ +} + +Indicator::~Indicator() +{ +} + diff --git a/src/indicator.h b/src/indicator.h index d0834fd..c55be79 100644 --- a/src/indicator.h +++ b/src/indicator.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 Canonical Ltd. + * Copyright 2014-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 @@ -17,8 +17,7 @@ * Charles Kerr */ -#ifndef INDICATOR_DISPLAY_INDICATOR_H -#define INDICATOR_DISPLAY_INDICATOR_H +#pragma once #include @@ -52,10 +51,10 @@ public: virtual std::string name() const =0; virtual const core::Property
& header() const =0; virtual std::shared_ptr menu_model() const =0; - virtual ~Profile() =default; + virtual ~Profile(); protected: - Profile() =default; + Profile(); }; @@ -63,6 +62,7 @@ class SimpleProfile: public Profile { public: SimpleProfile(const char* name, const std::shared_ptr& menu): m_name(name), m_menu(menu) {} + virtual ~SimpleProfile(); std::string name() const {return m_name;} core::Property
& header() {return m_header;} @@ -79,11 +79,10 @@ protected: class Indicator { public: - virtual ~Indicator() =default; + virtual ~Indicator(); virtual const char* name() const =0; virtual GSimpleActionGroup* action_group() const =0; virtual std::vector> profiles() const =0; }; -#endif -- cgit v1.2.3 From 68b671ce04b8b5d6b37025ad093c73a3e14d4d64 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 8 Mar 2016 22:04:56 -0600 Subject: add fingerprint snap decisions; test with notification dbusmock --- CMakeLists.txt | 13 +-- cmake/FindGMock.cmake | 10 -- debian/control | 5 + src/main.cpp | 9 +- src/usb-snap.cpp | 254 ++++++++++++++++++++++++++++++++++++++++++++++++ src/usb-snap.h | 39 ++++++++ tests/CMakeLists.txt | 56 ++++++++--- tests/dbus-types.h | 47 +++++++++ tests/usb-snap-test.cpp | 216 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 611 insertions(+), 38 deletions(-) delete mode 100644 cmake/FindGMock.cmake create mode 100644 src/usb-snap.cpp create mode 100644 src/usb-snap.h create mode 100644 tests/dbus-types.h create mode 100644 tests/usb-snap-test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fa4d10..8b8ef65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -project (indicator-display) -cmake_minimum_required (VERSION 2.8.9) +project(indicator-display LANGUAGES C CXX) +cmake_minimum_required(VERSION 2.8.9) list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) @@ -15,9 +15,6 @@ set (SERVICE_EXEC "${PACKAGE}-service") option (enable_tests "Build the package's automatic tests." ON) option (enable_lcov "Generate lcov code coverage reports." ON) -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) - ## ## GNU standard paths ## @@ -33,9 +30,10 @@ set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_ ## Check for prerequisites ## +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) find_package (PkgConfig REQUIRED) -include (FindPkgConfig) pkg_check_modules (SERVICE_DEPS REQUIRED gio-unix-2.0>=2.36 glib-2.0>=2.36) @@ -58,9 +56,6 @@ set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-missing-field-initializers") # G # testing & coverage if (${enable_tests}) - set (GTEST_SOURCE_DIR /usr/src/gtest/src) - set (GTEST_INCLUDE_DIR ${GTEST_SOURCE_DIR}/..) - set (GTEST_LIBS -lpthread) enable_testing () if (${enable_lcov}) include(GCov) diff --git a/cmake/FindGMock.cmake b/cmake/FindGMock.cmake deleted file mode 100644 index 74a1c15..0000000 --- a/cmake/FindGMock.cmake +++ /dev/null @@ -1,10 +0,0 @@ -# Build with system gmock and embedded gtest -set (GMOCK_INCLUDE_DIRS "/usr/include/gmock/include" CACHE PATH "gmock source include directory") -set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory") -set (GTEST_INCLUDE_DIRS "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory") - -add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock") - -set(GTEST_LIBRARIES gtest) -set(GTEST_MAIN_LIBRARIES gtest_main) -set(GMOCK_LIBRARIES gmock gmock_main) diff --git a/debian/control b/debian/control index 4f05f74..5088af5 100644 --- a/debian/control +++ b/debian/control @@ -3,12 +3,17 @@ Section: misc Priority: optional Maintainer: Charles Kerr Build-Depends: cmake, + cmake-extras (>= 0.4), dbus, libglib2.0-dev (>= 2.36), libproperties-cpp-dev, # for coverage reports lcov, # for tests + qt5-default, + qtbase5-dev, + libqtdbusmock1-dev (>= 0.4), + libqtdbustest1-dev, cppcheck, libgtest-dev, google-mock (>= 1.6.0+svn437), diff --git a/src/main.cpp b/src/main.cpp index 0c56bd6..62eca62 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include // bindtextdomain() #include @@ -60,9 +61,11 @@ main(int /*argc*/, char** /*argv*/) static constexpr char const * ADB_SOCKET_PATH {"/dev/socket/adb"}; GAdbdClient adbd_client{ADB_SOCKET_PATH}; adbd_client.on_pk_request().connect([](const AdbdClient::PKRequest& req){ - g_debug("%s got pk_request [%s]", G_STRLOC, req.public_key.c_str()); - // FIXME: actually decide what response to send back - req.respond(AdbdClient::PKResponse::ALLOW); + auto snap = new UsbSnap(req.public_key); + snap->on_user_response().connect([req,snap](AdbdClient::PKResponse response, bool /*FIXME: remember_choice*/){ + req.respond(response); + g_idle_add([](gpointer gsnap){delete static_cast(gsnap); return G_SOURCE_REMOVE;}, snap); // delete-later + }); }); g_main_loop_run(loop); diff --git a/src/usb-snap.cpp b/src/usb-snap.cpp new file mode 100644 index 0000000..40f02a2 --- /dev/null +++ b/src/usb-snap.cpp @@ -0,0 +1,254 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include + +/*** +**** +***/ + +class UsbSnap::Impl +{ +public: + + explicit Impl(const std::string& fingerprint): + m_fingerprint{fingerprint}, + m_cancellable{g_cancellable_new()} + { + g_bus_get (G_BUS_TYPE_SESSION, m_cancellable, on_bus_ready_static, this); + } + + ~Impl() + { + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); + + if (m_subscription_id != 0) + g_dbus_connection_signal_unsubscribe (m_bus, m_subscription_id); + + if (m_notification_id != 0) { + GError* error {}; + g_dbus_connection_call_sync(m_bus, + BUS_NAME, + OBJECT_PATH, + IFACE_NAME, + "CloseNotification", + g_variant_new("(u)", m_notification_id), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + if (error != nullptr) { + g_warning("Error closing notification: %s", error->message); + g_clear_error(&error); + } + } + + g_clear_object(&m_bus); + } + + core::Signal& on_user_response() + { + return m_on_user_response; + } + +private: + + static void on_bus_ready_static(GObject* /*source*/, GAsyncResult* res, gpointer gself) + { + GError* error {}; + auto bus = g_bus_get_finish (res, &error); + if (error != nullptr) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("UsbSnap: Error getting session bus: %s", error->message); + g_clear_error(&error); + } else { + static_cast(gself)->on_bus_ready(bus); + } + g_clear_object(&bus); + } + + void on_bus_ready(GDBusConnection* bus) + { + m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(bus))); + + auto body = g_strdup_printf(_("The computer's RSA key fingerprint is: %s"), m_fingerprint.c_str()); + + GVariantBuilder actions_builder; + g_variant_builder_init(&actions_builder, G_VARIANT_TYPE_STRING_ARRAY); + g_variant_builder_add(&actions_builder, "s", ACTION_ALLOW); + g_variant_builder_add(&actions_builder, "s", _("Allow")); + g_variant_builder_add(&actions_builder, "s", ACTION_DENY); + g_variant_builder_add(&actions_builder, "s", _("Deny")); + + GVariantBuilder hints_builder; + g_variant_builder_init(&hints_builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&hints_builder, "{sv}", "x-canonical-non-shaped-icon", g_variant_new_string("true")); + g_variant_builder_add(&hints_builder, "{sv}", "x-canonical-snap-decisions", g_variant_new_string("true")); + g_variant_builder_add(&hints_builder, "{sv}", "x-canonical-private-affirmative-tint", g_variant_new_string("true")); + + auto args = g_variant_new("(susssasa{sv}i)", + "", + uint32_t(0), + "computer-symbolic", + _("Allow USB Debugging?"), + body, + &actions_builder, + &hints_builder, + -1); + g_dbus_connection_call(m_bus, + BUS_NAME, + OBJECT_PATH, + IFACE_NAME, + "Notify", + args, + G_VARIANT_TYPE("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, // timeout + m_cancellable, + on_notify_reply_static, + this); + + g_clear_pointer(&body, g_free); + } + + static void on_notify_reply_static(GObject* obus, GAsyncResult* res, gpointer gself) + { + GError* error {}; + auto reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION(obus), res, &error); + if (error != nullptr) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("UsbSnap: Error calling Notify: %s", error->message); + g_clear_error(&error); + } else { + uint32_t id {}; + g_variant_get(reply, "(u)", &id); + static_cast(gself)->on_notify_reply(id); + } + g_clear_pointer(&reply, g_variant_unref); + } + + void on_notify_reply(uint32_t id) + { + m_notification_id = id; + + m_subscription_id = g_dbus_connection_signal_subscribe(m_bus, + BUS_NAME, + IFACE_NAME, + nullptr, + OBJECT_PATH, + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + on_notification_signal_static, + this, + nullptr); + } + + static void on_notification_signal_static(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer gself) + { + g_return_if_fail(!g_strcmp0(object_path, OBJECT_PATH)); + g_return_if_fail(!g_strcmp0(interface_name, IFACE_NAME)); + + auto self = static_cast(gself); + + if (!g_strcmp0(signal_name, "ActionInvoked")) + { + uint32_t id {}; + const char* action_name {}; + g_variant_get(parameters, "(u&s)", &id, &action_name); + if (id == self->m_notification_id) + self->on_action_invoked(action_name); + } + else if (!g_strcmp0(signal_name, "NotificationClosed")) + { + uint32_t id {}; + uint32_t close_reason {}; + g_variant_get(parameters, "(uu)", &id, &close_reason); + if (id == self->m_notification_id) + self->on_notification_closed(close_reason); + } + } + + void on_action_invoked(const char* action_name) + { + const auto response = !g_strcmp0(action_name, ACTION_ALLOW) + ? AdbdClient::PKResponse::ALLOW + : AdbdClient::PKResponse::DENY; + + // FIXME: the current default is to cover the most common use case. + // We need to get the notification ui's checkbox working ASAP so + // that the user can provide this flag + const bool remember_this_choice = response == AdbdClient::PKResponse::ALLOW; + + m_on_user_response(response, remember_this_choice); + } + + void on_notification_closed(uint32_t close_reason) + { + if (close_reason == CloseReason::EXPIRED) + m_on_user_response(AdbdClient::PKResponse::DENY, false); + + m_notification_id = 0; + } + + static constexpr char const * ACTION_ALLOW{"allow"}; + static constexpr char const * ACTION_DENY{"deny"}; + + static constexpr char const * BUS_NAME {"org.freedesktop.Notifications" }; + static constexpr char const * IFACE_NAME {"org.freedesktop.Notifications" }; + static constexpr char const * OBJECT_PATH {"/org/freedesktop/Notifications" }; + enum CloseReason { EXPIRED=1, DISMISSED=2, API=3, UNDEFINED=4 }; + + const std::string m_fingerprint; + core::Signal m_on_user_response; + GCancellable* m_cancellable {}; + GDBusConnection* m_bus {}; + uint32_t m_notification_id {}; + unsigned int m_subscription_id {}; +}; + +/*** +**** +***/ + +UsbSnap::UsbSnap(const std::string& public_key): + impl{new Impl{public_key}} +{ +} + +UsbSnap::~UsbSnap() +{ +} + +core::Signal& +UsbSnap::on_user_response() +{ + return impl->on_user_response(); +} + diff --git a/src/usb-snap.h b/src/usb-snap.h new file mode 100644 index 0000000..6ad3a4c --- /dev/null +++ b/src/usb-snap.h @@ -0,0 +1,39 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#include // AdbdClient::PKResponse + +#include + +#include +#include + +class UsbSnap +{ +public: + explicit UsbSnap(const std::string& public_key); + ~UsbSnap(); + core::Signal& on_user_response(); + +protected: + class Impl; + std::unique_ptr impl; +}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 706e35b..1fbe407 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,33 +1,57 @@ -include(FindGMock) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${GMOCK_INCLUDE_DIRS}) -include_directories(${GTEST_INCLUDE_DIRS}) + +set(CMAKE_AUTOMOC ON) + +find_package(GMock REQUIRED) +find_package(Qt5Core REQUIRED) +find_package(Qt5Test REQUIRED) +find_package(Qt5DBus COMPONENTS Qt5DBusMacros REQUIRED) + +pkg_check_modules(TEST_DEPS + libqtdbustest-1 REQUIRED + libqtdbusmock-1 REQUIRED +) + +include_directories( + ${CMAKE_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) +include_directories(SYSTEM + ${DBUSTEST_INCLUDE_DIRS} + ${TEST_DEPS_INCLUDE_DIRS} + ${GTEST_INCLUDE_DIRS} + ${GMOCK_INCLUDE_DIRS} +) set(CTEST_ENVIRONMENT "${CTEST_ENVIRONMENT};G_MESSAGES_DEBUG=all") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # turn off the warnings that break Google Test - set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-global-constructors -Wno-weak-vtables -Wno-undef -Wno-c++98-compat-pedantic -Wno-missing-noreturn -Wno-used-but-marked-unused -Wno-padded -Wno-deprecated -Wno-sign-compare -Wno-shift-sign-overflow") + set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-global-constructors -Wno-weak-vtables") endif() -SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -pthread ${CXX_WARNING_ARGS}") - -# look for headers in our src dir, and also in the directories where we autogenerate files... -include_directories (${CMAKE_SOURCE_DIR}/src) -include_directories (${CMAKE_CURRENT_BINARY_DIR}) -include_directories (${DBUSTEST_INCLUDE_DIRS}) +SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++11 -g ${CXX_WARNING_ARGS}") function(add_test_by_name name) set (TEST_NAME ${name}) add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test (${TEST_NAME} ${TEST_NAME}) - set_property(TEST ${TEST_NAME} PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") - - add_dependencies (${TEST_NAME} libindicatordisplayservice) - target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES}) + set_property(TEST ${TEST_NAME} PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") + add_dependencies (${TEST_NAME} indicatordisplayservice) + target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} ${TEST_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES}) endfunction() add_test_by_name(rotation-lock-test) add_test_by_name(adbd-client-test) -add_test (cppcheck cppcheck --enable=all -USCHEMA_DIR --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) +function(add_qt_test_by_name name) + set (TEST_NAME ${name}) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp qmain.cpp) + add_test (${TEST_NAME} ${TEST_NAME}) + set_property(TEST ${TEST_NAME} PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") + add_dependencies (${TEST_NAME} indicatordisplayservice) + target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} Qt5::Core Qt5::Test Qt5::DBus ${TEST_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES}) +endfunction() +add_qt_test_by_name(usb-snap-test) + +add_test(cppcheck cppcheck --enable=all -USCHEMA_DIR --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) diff --git a/tests/dbus-types.h b/tests/dbus-types.h new file mode 100644 index 0000000..c2dfb81 --- /dev/null +++ b/tests/dbus-types.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013-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 . + * + * Author: Pete Woods + */ + +#pragma once + +#include +#include +#include +#include + +typedef QMap QVariantDictMap; +Q_DECLARE_METATYPE(QVariantDictMap) + +typedef QMap QStringMap; +Q_DECLARE_METATYPE(QStringMap) + +namespace DBusTypes +{ + inline void registerMetaTypes() + { + qRegisterMetaType("QVariantDictMap"); + qRegisterMetaType("QStringMap"); + + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + } + static constexpr char const* NOTIFY_DBUS_NAME = "org.freedesktop.Notifications"; + + static constexpr char const* NOTIFY_DBUS_INTERFACE = "org.freedesktop.Notifications"; + + static constexpr char const* NOTIFY_DBUS_PATH = "/org/freedesktop/Notifications"; +} diff --git a/tests/usb-snap-test.cpp b/tests/usb-snap-test.cpp new file mode 100644 index 0000000..309980a --- /dev/null +++ b/tests/usb-snap-test.cpp @@ -0,0 +1,216 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#define QT_NO_KEYWORDS +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +#include + +using namespace QtDBusTest; +using namespace QtDBusMock; + +inline QString qVariantToString(const QVariant& variant) { + QString output; + QDebug dbg(&output); + dbg << variant; + return output; +} + +inline void PrintTo(const QVariant& variant, std::ostream* os) { + QString output; + QDebug dbg(&output); + dbg << variant; + + *os << "QVariant(" << output.toStdString() << ")"; +} + +inline void PrintTo(const QString& s, std::ostream* os) { + *os << "\"" << s.toStdString() << "\""; +} + +inline void PrintTo(const QStringList& list, std::ostream* os) { + QString output; + QDebug dbg(&output); + dbg << list; + + *os << "QStringList(" << output.toStdString() << ")"; +} + +inline void PrintTo(const QList& list, std::ostream* os) { + QString output; + for (const auto& path: list) + { + output.append("\"" + path.path() + "\","); + } + + *os << "QList(" << output.toStdString() << ")"; +} + +#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ +{\ + while (signalSpy.size() < signalsExpected)\ + {\ + ASSERT_TRUE(signalSpy.wait());\ + }\ + ASSERT_EQ(signalsExpected, signalSpy.size());\ +} + +class UsbSnapFixture: public GlibFixture +{ + using super = GlibFixture; + +public: + + UsbSnapFixture(): + dbusMock{dbusTestRunner} + { + DBusTypes::registerMetaTypes(); + + dbusTestRunner.startServices(); + } + + ~UsbSnapFixture() =default; + +protected: + + bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) + { + if (variant.canConvert()) + { + QDBusArgument value(variant.value()); + if (value.currentType() == QDBusArgument::MapType) + { + value >> map; + return true; + } + } + return false; + } + + void SetUp() override + { + super::SetUp(); + + dbusMock.registerNotificationDaemon(); + dbusTestRunner.startServices(); + } + + OrgFreedesktopDBusMockInterface& notificationsMockInterface() + { + return dbusMock.mockInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::SessionBus); + } + + QtDBusTest::DBusTestRunner dbusTestRunner; + QtDBusMock::DBusMock dbusMock; + QtDBusTest::DBusServicePtr indicator; +}; + +TEST_F(UsbSnapFixture, TestRoundTrip) +{ + struct { + const char* fingerprint; + const char* action_to_invoke; + const AdbdClient::PKResponse expected_response; + } tests[] = { + { "Fingerprint", "allow", AdbdClient::PKResponse::ALLOW }, + { "Fingerprint", "deny", AdbdClient::PKResponse::DENY } + }; + + uint32_t next_id = 1; + for(const auto& test : tests) + { + // Minor wart: we don't have a way of getting the fdo notification id + // from dbusmock so instead we copy its (simple) id generation here + const auto id = next_id++; + + QSignalSpy notificationsSpy( + ¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + // start up a UsbSnap to ask about a fingerprint + auto snap = std::make_shared(test.fingerprint); + AdbdClient::PKResponse user_response {}; + bool user_response_set = false; + snap->on_user_response().connect([&user_response,&user_response_set](AdbdClient::PKResponse response, bool /*remember*/){ + user_response = response; + user_response_set = true; + }); + + // test that UsbSnap creates a fdo notification + WAIT_FOR_SIGNALS(notificationsSpy, 1); + { + QVariantList const& call(notificationsSpy.at(0)); + EXPECT_EQ("Notify", call.at(0)); + + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(8, args.size()); + EXPECT_EQ("", args.at(0)); // app name + EXPECT_EQ(0, args.at(1)); // replaces-id + EXPECT_EQ("computer-symbolic", args.at(2)); // icon name + EXPECT_EQ("Allow USB Debugging?", args.at(3)); // summary + EXPECT_EQ(QString::fromUtf8("The computer's RSA key fingerprint is: ") + test.fingerprint, args.at(4)); // body + EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Deny"}), args.at(5)); // actions + EXPECT_EQ(-1, args.at(7)); + + QVariantMap hints; + ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); + ASSERT_EQ(3, hints.size()); + ASSERT_TRUE(hints.contains("x-canonical-private-affirmative-tint")); + ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); + ASSERT_TRUE(hints.contains("x-canonical-snap-decisions")); + } + notificationsSpy.clear(); + + // fake a user interaction with the fdo notification + notificationsMockInterface().EmitSignal( + DBusTypes::NOTIFY_DBUS_INTERFACE, + "ActionInvoked", + "us", + QVariantList() << id << test.action_to_invoke); + + // test that UsbSnap emits on_user_response() as a result + wait_for([&user_response_set](){return user_response_set;}); + EXPECT_TRUE(user_response_set); + ASSERT_EQ(test.expected_response, user_response); + + // confirm that the snap dtor cleans up the notification + snap.reset(); + WAIT_FOR_SIGNALS(notificationsSpy, 1); + { + QVariantList const& call(notificationsSpy.at(0)); + EXPECT_EQ("CloseNotification", call.at(0)); + QVariantList const& args(call.at(1).toList()); + EXPECT_EQ(id, args.at(0)); + } + } +} -- cgit v1.2.3 From 13a0b901492901638a7abc90bb2935a9c0387f75 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 9 Mar 2016 17:19:23 -0600 Subject: add human-readable fingerprint extraction from the adb public keys --- src/adbd-client.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/adbd-client.h | 1 + src/main.cpp | 2 +- src/usb-snap.cpp | 4 ++-- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/adbd-client.cpp b/src/adbd-client.cpp index 38f202f..edd403c 100644 --- a/src/adbd-client.cpp +++ b/src/adbd-client.cpp @@ -22,6 +22,9 @@ #include #include +#include +#include +#include #include #include #include @@ -89,6 +92,7 @@ private: auto self = data->self; struct PKRequest req; req.public_key = data->public_key; + req.fingerprint = get_fingerprint(req.public_key); req.respond = [self](PKResponse response){self->on_public_key_response(response);}; self->m_on_pk_request(req); } @@ -219,6 +223,37 @@ private: } } + static std::string get_fingerprint(const std::string& public_key) + { + // The first token is base64-encoded data, so cut on the first whitespace + const std::string base64 ( + public_key.begin(), + std::find_if( + public_key.begin(), public_key.end(), + [](const std::string::value_type& ch){return std::isspace(ch);} + ) + ); + + gsize digest_len {}; + auto digest = g_base64_decode(base64.c_str(), &digest_len); + + auto checksum = g_compute_checksum_for_data(G_CHECKSUM_MD5, digest, digest_len); + const gsize checksum_len = checksum ? strlen(checksum) : 0; + + // insert ':' between character pairs; eg "ff27b5f3" --> "ff:27:b5:f3" + std::string fingerprint; + for (gsize i=0; i respond; }; diff --git a/src/main.cpp b/src/main.cpp index 62eca62..151b642 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,7 +61,7 @@ main(int /*argc*/, char** /*argv*/) static constexpr char const * ADB_SOCKET_PATH {"/dev/socket/adb"}; GAdbdClient adbd_client{ADB_SOCKET_PATH}; adbd_client.on_pk_request().connect([](const AdbdClient::PKRequest& req){ - auto snap = new UsbSnap(req.public_key); + auto snap = new UsbSnap(req.fingerprint); snap->on_user_response().connect([req,snap](AdbdClient::PKResponse response, bool /*FIXME: remember_choice*/){ req.respond(response); g_idle_add([](gpointer gsnap){delete static_cast(gsnap); return G_SOURCE_REMOVE;}, snap); // delete-later diff --git a/src/usb-snap.cpp b/src/usb-snap.cpp index 40f02a2..87f4673 100644 --- a/src/usb-snap.cpp +++ b/src/usb-snap.cpp @@ -217,8 +217,8 @@ private: m_notification_id = 0; } - static constexpr char const * ACTION_ALLOW{"allow"}; - static constexpr char const * ACTION_DENY{"deny"}; + static constexpr char const * ACTION_ALLOW {"allow"}; + static constexpr char const * ACTION_DENY {"deny"}; static constexpr char const * BUS_NAME {"org.freedesktop.Notifications" }; static constexpr char const * IFACE_NAME {"org.freedesktop.Notifications" }; -- cgit v1.2.3 From 0194e5f3ea83f13a79f9d87053f6138b79014709 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 10 Mar 2016 11:39:31 -0600 Subject: add src/usb-snap.cpp to POTFILES.in --- po/POTFILES.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/po/POTFILES.in b/po/POTFILES.in index f8bd80a..6b34951 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1 +1,3 @@ src/rotation-lock.cpp +src/usb-snap.cpp +~ -- cgit v1.2.3 From f8a5d99b5ac03b5b759f67b33ed2c989fc0d0ceb Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 10 Mar 2016 12:13:20 -0600 Subject: cmake and test directory cleanup --- CMakeLists.txt | 76 ++++++++------ src/CMakeLists.txt | 61 +++++------ tests/CMakeLists.txt | 50 +++------ tests/adbd-client-test.cpp | 97 ----------------- tests/adbd-server.h | 148 -------------------------- tests/dbus-types.h | 47 --------- tests/glib-fixture.h | 203 ----------------------------------- tests/rotation-lock-test.cpp | 61 ----------- tests/test-dbus-fixture.h | 106 ------------------- tests/unit/adbd-client-test.cpp | 97 +++++++++++++++++ tests/unit/rotation-lock-test.cpp | 61 +++++++++++ tests/unit/usb-snap-test.cpp | 216 ++++++++++++++++++++++++++++++++++++++ tests/usb-snap-test.cpp | 216 -------------------------------------- tests/utils/adbd-server.h | 148 ++++++++++++++++++++++++++ tests/utils/dbus-types.h | 47 +++++++++ tests/utils/glib-fixture.h | 203 +++++++++++++++++++++++++++++++++++ tests/utils/qmain.cpp | 60 +++++++++++ tests/utils/test-dbus-fixture.h | 106 +++++++++++++++++++ 18 files changed, 1022 insertions(+), 981 deletions(-) delete mode 100644 tests/adbd-client-test.cpp delete mode 100644 tests/adbd-server.h delete mode 100644 tests/dbus-types.h delete mode 100644 tests/glib-fixture.h delete mode 100644 tests/rotation-lock-test.cpp delete mode 100644 tests/test-dbus-fixture.h create mode 100644 tests/unit/adbd-client-test.cpp create mode 100644 tests/unit/rotation-lock-test.cpp create mode 100644 tests/unit/usb-snap-test.cpp delete mode 100644 tests/usb-snap-test.cpp create mode 100644 tests/utils/adbd-server.h create mode 100644 tests/utils/dbus-types.h create mode 100644 tests/utils/glib-fixture.h create mode 100644 tests/utils/qmain.cpp create mode 100644 tests/utils/test-dbus-fixture.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b8ef65..5110c9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,30 +1,31 @@ project(indicator-display LANGUAGES C CXX) cmake_minimum_required(VERSION 2.8.9) -list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) -set (PROJECT_VERSION "14.10.0") -set (PACKAGE ${CMAKE_PROJECT_NAME}) -set (GETTEXT_PACKAGE indicator-display) -add_definitions (-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}" - -DGNOMELOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}") +set(PACKAGE ${CMAKE_PROJECT_NAME}) +set(GETTEXT_PACKAGE indicator-display) +add_definitions( + -DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}" + -DGNOMELOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}" +) -set (SERVICE_LIB ${PACKAGE}) -set (SERVICE_EXEC "${PACKAGE}-service") +set(SERVICE_LIB ${PACKAGE}) +set(SERVICE_EXEC "${PACKAGE}-service") -option (enable_tests "Build the package's automatic tests." ON) -option (enable_lcov "Generate lcov code coverage reports." ON) +option(enable_tests "Build the package's automatic tests." ON) +option(enable_lcov "Generate lcov code coverage reports." ON) ## ## GNU standard paths ## -include (GNUInstallDirs) -if (EXISTS "/etc/debian_version") # Workaround for libexecdir on debian - set (CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBDIR}") - set (CMAKE_INSTALL_FULL_LIBEXECDIR "${CMAKE_INSTALL_FULL_LIBDIR}") -endif () -set (CMAKE_INSTALL_PKGLIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}") -set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}") +include(GNUInstallDirs) +if(EXISTS "/etc/debian_version") # Workaround for libexecdir on debian + set(CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBDIR}") + set(CMAKE_INSTALL_FULL_LIBEXECDIR "${CMAKE_INSTALL_FULL_LIBDIR}") +endif() +set(CMAKE_INSTALL_PKGLIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}") +set(CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}") ## ## Check for prerequisites @@ -32,12 +33,15 @@ set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_ set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) -find_package (PkgConfig REQUIRED) +find_package(PkgConfig REQUIRED) -pkg_check_modules (SERVICE_DEPS REQUIRED - gio-unix-2.0>=2.36 - glib-2.0>=2.36) -include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) +pkg_check_modules(SERVICE_DEPS REQUIRED + gio-unix-2.0>=2.36 + glib-2.0>=2.36 +) +include_directories (SYSTEM + ${SERVICE_DEPS_INCLUDE_DIRS} +) ## ## @@ -47,24 +51,26 @@ set (CMAKE_INCLUDE_CURRENT_DIR OFF) include_directories (${CMAKE_CURRENT_SOURCE_DIR}) # set the compiler warnings -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Weverything -Wno-c++98-compat -Wno-padded") +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Weverything -Wno-c++98-compat -Wno-padded") else() - set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wall -Wextra -Wpedantic") + set(CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wall -Wextra -Wpedantic") endif() -set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-missing-field-initializers") # GActionEntry +set(CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-missing-field-initializers") # GActionEntry + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fPIC -g") # testing & coverage -if (${enable_tests}) - enable_testing () - if (${enable_lcov}) +if(${enable_tests}) + enable_testing() + if(${enable_lcov}) include(GCov) - endif () -endif () + endif() +endif() -add_subdirectory (src) -add_subdirectory (data) -add_subdirectory (po) +add_subdirectory(src) +add_subdirectory(data) +add_subdirectory(po) if (${enable_tests}) - add_subdirectory (tests) + add_subdirectory(tests) endif () diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ff385d9..6cfda91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,32 +1,33 @@ -set (SERVICE_LIB "indicatordisplayservice") -set (SERVICE_EXEC "indicator-display-service") -add_definitions (-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}") - -# handwritten source code... -set (SERVICE_LIB_HANDWRITTEN_SOURCES - adbd-client.cpp - exporter.cpp - indicator.cpp - rotation-lock.cpp - usb-snap.cpp) - -add_library (${SERVICE_LIB} STATIC - ${SERVICE_LIB_HANDWRITTEN_SOURCES}) - -# add the bin dir to the include path so that -# the compiler can find the generated header files -include_directories (${CMAKE_CURRENT_BINARY_DIR}) - -link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) - -set (SERVICE_EXEC_HANDWRITTEN_SOURCES main.cpp) -add_executable (${SERVICE_EXEC} ${SERVICE_EXEC_HANDWRITTEN_SOURCES}) -target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} Threads::Threads ${GCOV_LIBS}) -install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) - -# add warnings/coverage info on handwritten files -# but not the generated ones... -set_property (SOURCE ${SERVICE_LIB_HANDWRITTEN_SOURCES} ${SERVICE_EXEC_HANDWRITTEN_SOURCES} - APPEND_STRING PROPERTY COMPILE_FLAGS " -std=c++11 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") +add_definitions(-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") + +add_library( + ${SERVICE_LIB} + STATIC + adbd-client.cpp + exporter.cpp + indicator.cpp + rotation-lock.cpp + usb-snap.cpp +) + +add_executable( + ${SERVICE_EXEC} + main.cpp +) + +target_link_libraries(${SERVICE_EXEC} + ${SERVICE_LIB} + ${SERVICE_DEPS_LIBRARIES} + Threads::Threads + ${GCOV_LIBS} +) + +install( + TARGETS + ${SERVICE_EXEC} + RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR} +) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1fbe407..205792e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,57 +1,31 @@ - set(CMAKE_AUTOMOC ON) - find_package(GMock REQUIRED) find_package(Qt5Core REQUIRED) find_package(Qt5Test REQUIRED) find_package(Qt5DBus COMPONENTS Qt5DBusMacros REQUIRED) pkg_check_modules(TEST_DEPS - libqtdbustest-1 REQUIRED - libqtdbusmock-1 REQUIRED + libqtdbustest-1 REQUIRED + libqtdbusmock-1 REQUIRED ) -include_directories( - ${CMAKE_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} -) include_directories(SYSTEM - ${DBUSTEST_INCLUDE_DIRS} - ${TEST_DEPS_INCLUDE_DIRS} - ${GTEST_INCLUDE_DIRS} - ${GMOCK_INCLUDE_DIRS} + ${DBUSTEST_INCLUDE_DIRS} + ${TEST_DEPS_INCLUDE_DIRS} + ${GTEST_INCLUDE_DIRS} + ${GMOCK_INCLUDE_DIRS} ) set(CTEST_ENVIRONMENT "${CTEST_ENVIRONMENT};G_MESSAGES_DEBUG=all") -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - # turn off the warnings that break Google Test - set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-global-constructors -Wno-weak-vtables") +# turn off the warnings that break Google Test +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-global-constructors -Wno-weak-vtables") endif() -SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++11 -g ${CXX_WARNING_ARGS}") - -function(add_test_by_name name) - set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cpp) - add_test (${TEST_NAME} ${TEST_NAME}) - set_property(TEST ${TEST_NAME} PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") - add_dependencies (${TEST_NAME} indicatordisplayservice) - target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} ${TEST_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES}) -endfunction() -add_test_by_name(rotation-lock-test) -add_test_by_name(adbd-client-test) - -function(add_qt_test_by_name name) - set (TEST_NAME ${name}) - add_executable (${TEST_NAME} ${TEST_NAME}.cpp qmain.cpp) - add_test (${TEST_NAME} ${TEST_NAME}) - set_property(TEST ${TEST_NAME} PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") - add_dependencies (${TEST_NAME} indicatordisplayservice) - target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} Qt5::Core Qt5::Test Qt5::DBus ${TEST_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES}) -endfunction() -add_qt_test_by_name(usb-snap-test) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNING_ARGS}") add_test(cppcheck cppcheck --enable=all -USCHEMA_DIR --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) +add_subdirectory(unit) +add_subdirectory(utils) diff --git a/tests/adbd-client-test.cpp b/tests/adbd-client-test.cpp deleted file mode 100644 index ae0378b..0000000 --- a/tests/adbd-client-test.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#include -#include - -#include - -#include // mkdtemp - -class AdbdClientFixture: public TestDBusFixture -{ -private: - typedef TestDBusFixture super; - -protected: - - std::string m_tmpdir; - - void SetUp() - { - super::SetUp(); - - char tmpl[] {"adb-client-test-XXXXXX"}; - m_tmpdir = mkdtemp(tmpl); - g_message("using tmpdir '%s'", m_tmpdir.c_str()); - } - - void TearDown() - { - g_rmdir(m_tmpdir.c_str()); - - super::TearDown(); - } -}; - - -TEST_F(AdbdClientFixture, SocketPlumbing) -{ - struct { - const std::string request; - const std::string expected_pk; - AdbdClient::PKResponse response; - const std::string expected_response; - } tests[] = { - { "PKHelloWorld", "HelloWorld", AdbdClient::PKResponse::ALLOW, "OK" }, - { "PKHelloWorld", "HelloWorld", AdbdClient::PKResponse::DENY, "NO" }, - { "PKFooBar", "FooBar", AdbdClient::PKResponse::ALLOW, "OK" }, - { "PK", "", AdbdClient::PKResponse::DENY, "NO" } - }; - - const auto main_thread = g_thread_self(); - - const auto socket_path = m_tmpdir + "/test-socket-plumbing"; - g_message("socket_path is %s", socket_path.c_str()); - - for (const auto& test : tests) - { - // start an AdbdClient that listens for PKRequests - std::string pk; - auto adbd_client = std::make_shared(socket_path); - adbd_client->on_pk_request().connect([&pk, main_thread, test](const AdbdClient::PKRequest& req){ - EXPECT_EQ(main_thread, g_thread_self()); - g_message("in on_pk_request with %s", req.public_key.c_str()); - pk = req.public_key; - req.respond(test.response); - }); - - // start a mock AdbdServer with to fire test key requests and wait for a response - auto adbd_server = std::make_shared(socket_path, std::vector{test.request}); - wait_for([adbd_server](){return !adbd_server->m_responses.empty();}, 2000); - EXPECT_EQ(test.expected_pk, pk); - ASSERT_EQ(1, adbd_server->m_responses.size()); - EXPECT_EQ(test.expected_response, adbd_server->m_responses.front()); - - // cleanup - adbd_client.reset(); - adbd_server.reset(); - g_unlink(socket_path.c_str()); - } -} diff --git a/tests/adbd-server.h b/tests/adbd-server.h deleted file mode 100644 index 740faaa..0000000 --- a/tests/adbd-server.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#include -#include - -#include -#include -#include - - -/** - * 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& 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 m_requests; - std::vector 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; -}; - - diff --git a/tests/dbus-types.h b/tests/dbus-types.h deleted file mode 100644 index c2dfb81..0000000 --- a/tests/dbus-types.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013-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 . - * - * Author: Pete Woods - */ - -#pragma once - -#include -#include -#include -#include - -typedef QMap QVariantDictMap; -Q_DECLARE_METATYPE(QVariantDictMap) - -typedef QMap QStringMap; -Q_DECLARE_METATYPE(QStringMap) - -namespace DBusTypes -{ - inline void registerMetaTypes() - { - qRegisterMetaType("QVariantDictMap"); - qRegisterMetaType("QStringMap"); - - qDBusRegisterMetaType(); - qDBusRegisterMetaType(); - } - static constexpr char const* NOTIFY_DBUS_NAME = "org.freedesktop.Notifications"; - - static constexpr char const* NOTIFY_DBUS_INTERFACE = "org.freedesktop.Notifications"; - - static constexpr char const* NOTIFY_DBUS_PATH = "/org/freedesktop/Notifications"; -} diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h deleted file mode 100644 index ccdeccd..0000000 --- a/tests/glib-fixture.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2013 Canonical Ltd. - * - * Authors: - * Charles Kerr - * - * 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 . - */ - -#pragma once - -#include // std::function -#include -#include // std::shared_ptr - -#include -#include -#include - -#include - -#include // setlocale() - -class GlibFixture : public ::testing::Test -{ - public: - - virtual ~GlibFixture() =default; - - protected: - - virtual void SetUp() override - { - setlocale(LC_ALL, "C.UTF-8"); - - loop = g_main_loop_new(nullptr, false); - -#ifdef SCHEMA_DIR - // only use local, temporary settings - g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); - g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); - g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); -#endif - - // fail on unexpected messages from this domain - g_log_set_fatal_mask(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING); - - g_unsetenv("DISPLAY"); - - } - - virtual void TearDown() override - { - g_test_assert_expected_messages (); - - g_clear_pointer(&loop, g_main_loop_unref); - } - - void expectLogMessage (const gchar *domain, GLogLevelFlags level, const gchar *pattern) - { - g_test_expect_message (domain, level, pattern); - } - - private: - - static gboolean - wait_for_signal__timeout(gpointer name) - { - g_error("%s: timed out waiting for signal '%s'", G_STRLOC, static_cast(name)); - return G_SOURCE_REMOVE; - } - - static gboolean - wait_msec__timeout(gpointer loop) - { - g_main_loop_quit(static_cast(loop)); - return G_SOURCE_CONTINUE; - } - - protected: - - /* convenience func to loop while waiting for a GObject's signal */ - void wait_for_signal(gpointer o, const gchar * signal, guint timeout_seconds=5) - { - // wait for the signal or for timeout, whichever comes first - const auto handler_id = g_signal_connect_swapped(o, signal, - G_CALLBACK(g_main_loop_quit), - loop); - const auto timeout_id = g_timeout_add_seconds(timeout_seconds, - wait_for_signal__timeout, - loop); - g_main_loop_run(loop); - g_source_remove(timeout_id); - g_signal_handler_disconnect(o, handler_id); - } - - /* convenience func to loop for N msec */ - void wait_msec(guint msec=50) - { - const auto id = g_timeout_add(msec, wait_msec__timeout, loop); - g_main_loop_run(loop); - g_source_remove(id); - } - - bool wait_for(std::function test_function, guint timeout_msec=1000) - { - auto timer = std::shared_ptr(g_timer_new(), [](GTimer* t){g_timer_destroy(t);}); - const auto timeout_sec = timeout_msec / 1000.0; - for (;;) { - if (test_function()) - return true; - //g_message("%f ... %f", g_timer_elapsed(timer.get(), nullptr), timeout_sec); - if (g_timer_elapsed(timer.get(), nullptr) >= timeout_sec) - return false; - wait_msec(); - } - } - - bool wait_for_name_owned(GDBusConnection* connection, - const gchar* name, - guint timeout_msec=1000, - GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) - { - struct Data { - GMainLoop* loop = nullptr; - bool owned = false; - }; - Data data; - - auto on_name_appeared = [](GDBusConnection* /*connection*/, - const gchar* /*name_*/, - const gchar* name_owner, - gpointer gdata) - { - if (name_owner == nullptr) - return; - auto tmp = static_cast(gdata); - tmp->owned = true; - g_main_loop_quit(tmp->loop); - }; - - const auto timeout_id = g_timeout_add(timeout_msec, wait_msec__timeout, loop); - data.loop = loop; - const auto watch_id = g_bus_watch_name_on_connection(connection, - name, - flags, - on_name_appeared, - nullptr, /* name_vanished */ - &data, - nullptr); /* user_data_free_func */ - g_main_loop_run(loop); - - g_bus_unwatch_name(watch_id); - g_source_remove(timeout_id); - - return data.owned; - } - - void EXPECT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection, - const gchar* name, - guint timeout_msec=1000, - GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) - { - EXPECT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; - } - - void EXPECT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection, - const gchar* name, - guint timeout_msec=1000, - GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) - { - EXPECT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; - } - - void ASSERT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection, - const gchar* name, - guint timeout_msec=1000, - GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) - { - ASSERT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; - } - - void ASSERT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection, - const gchar* name, - guint timeout_msec=1000, - GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) - { - ASSERT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; - } - - GMainLoop * loop; -}; - diff --git a/tests/rotation-lock-test.cpp b/tests/rotation-lock-test.cpp deleted file mode 100644 index b8661cc..0000000 --- a/tests/rotation-lock-test.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2014 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 . - * - * Authors: - * Charles Kerr - */ - -#include "test-dbus-fixture.h" - -#include - -class RotationLockFixture: public TestDBusFixture -{ -private: - typedef TestDBusFixture super; - -protected: - - void SetUp() - { - super::SetUp(); - } - - void TearDown() - { - super::TearDown(); - } -}; - -/*** -**** -***/ - -TEST_F(RotationLockFixture, CheckIndicator) -{ - RotationLockIndicator indicator; - - ASSERT_STREQ("rotation_lock", indicator.name()); - auto actions = indicator.action_group(); - ASSERT_TRUE(actions != nullptr); - ASSERT_TRUE(g_action_group_has_action(G_ACTION_GROUP(actions), "rotation-lock")); - - std::vector> profiles = indicator.profiles(); - ASSERT_EQ(1, profiles.size()); - std::shared_ptr phone = profiles[0]; - ASSERT_EQ(std::string("phone"), phone->name()); - ASSERT_FALSE(phone->header()->is_visible); -} - diff --git a/tests/test-dbus-fixture.h b/tests/test-dbus-fixture.h deleted file mode 100644 index 3947e58..0000000 --- a/tests/test-dbus-fixture.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2013 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 . - * - * Authors: - * Charles Kerr - */ - -#pragma once - -#include "glib-fixture.h" - -/*** -**** -***/ - -class TestDBusFixture: public GlibFixture -{ - public: - - TestDBusFixture() =default; - virtual ~TestDBusFixture() =default; - - explicit TestDBusFixture(const std::vector& service_dirs_in): service_dirs(service_dirs_in) {} - - private: - - typedef GlibFixture super; - - static void - on_bus_opened (GObject* /*object*/, GAsyncResult * res, gpointer gself) - { - auto self = static_cast(gself); - - GError * err = 0; - self->system_bus = g_bus_get_finish (res, &err); - g_assert_no_error (err); - - g_main_loop_quit (self->loop); - } - - static void - on_bus_closed (GObject* /*object*/, GAsyncResult * res, gpointer gself) - { - auto self = static_cast(gself); - - GError * err = 0; - g_dbus_connection_close_finish (self->system_bus, res, &err); - g_assert_no_error (err); - - g_main_loop_quit (self->loop); - } - - protected: - - GTestDBus * test_dbus = nullptr; - GDBusConnection * system_bus = nullptr; - const std::vector service_dirs; - - virtual void SetUp() override - { - super::SetUp (); - - // pull up a test dbus - test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); - for (const auto& dir : service_dirs) - g_test_dbus_add_service_dir (test_dbus, dir.c_str()); - g_test_dbus_up (test_dbus); - const char * address = g_test_dbus_get_bus_address (test_dbus); - g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, true); - g_setenv ("DBUS_SESSION_BUS_ADDRESS", address, true); - g_debug ("test_dbus's address is %s", address); - - // wait for the GDBusConnection before returning - g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); - g_main_loop_run (loop); - } - - virtual void TearDown() override - { - wait_msec(); - - // close the system bus - g_dbus_connection_close(system_bus, nullptr, on_bus_closed, this); - g_main_loop_run(loop); - g_clear_object(&system_bus); - - // tear down the test dbus - g_test_dbus_down(test_dbus); - g_clear_object(&test_dbus); - - super::TearDown(); - } -}; - diff --git a/tests/unit/adbd-client-test.cpp b/tests/unit/adbd-client-test.cpp new file mode 100644 index 0000000..1e28cc9 --- /dev/null +++ b/tests/unit/adbd-client-test.cpp @@ -0,0 +1,97 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include + +#include // mkdtemp + +class AdbdClientFixture: public TestDBusFixture +{ +private: + typedef TestDBusFixture super; + +protected: + + std::string m_tmpdir; + + void SetUp() + { + super::SetUp(); + + char tmpl[] {"adb-client-test-XXXXXX"}; + m_tmpdir = mkdtemp(tmpl); + g_message("using tmpdir '%s'", m_tmpdir.c_str()); + } + + void TearDown() + { + g_rmdir(m_tmpdir.c_str()); + + super::TearDown(); + } +}; + + +TEST_F(AdbdClientFixture, SocketPlumbing) +{ + struct { + const std::string request; + const std::string expected_pk; + AdbdClient::PKResponse response; + const std::string expected_response; + } tests[] = { + { "PKHelloWorld", "HelloWorld", AdbdClient::PKResponse::ALLOW, "OK" }, + { "PKHelloWorld", "HelloWorld", AdbdClient::PKResponse::DENY, "NO" }, + { "PKFooBar", "FooBar", AdbdClient::PKResponse::ALLOW, "OK" }, + { "PK", "", AdbdClient::PKResponse::DENY, "NO" } + }; + + const auto main_thread = g_thread_self(); + + const auto socket_path = m_tmpdir + "/test-socket-plumbing"; + g_message("socket_path is %s", socket_path.c_str()); + + for (const auto& test : tests) + { + // start an AdbdClient that listens for PKRequests + std::string pk; + auto adbd_client = std::make_shared(socket_path); + adbd_client->on_pk_request().connect([&pk, main_thread, test](const AdbdClient::PKRequest& req){ + EXPECT_EQ(main_thread, g_thread_self()); + g_message("in on_pk_request with %s", req.public_key.c_str()); + pk = req.public_key; + req.respond(test.response); + }); + + // start a mock AdbdServer with to fire test key requests and wait for a response + auto adbd_server = std::make_shared(socket_path, std::vector{test.request}); + wait_for([adbd_server](){return !adbd_server->m_responses.empty();}, 2000); + EXPECT_EQ(test.expected_pk, pk); + ASSERT_EQ(1, adbd_server->m_responses.size()); + EXPECT_EQ(test.expected_response, adbd_server->m_responses.front()); + + // cleanup + adbd_client.reset(); + adbd_server.reset(); + g_unlink(socket_path.c_str()); + } +} diff --git a/tests/unit/rotation-lock-test.cpp b/tests/unit/rotation-lock-test.cpp new file mode 100644 index 0000000..b9630b5 --- /dev/null +++ b/tests/unit/rotation-lock-test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2014 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include + +class RotationLockFixture: public TestDBusFixture +{ +private: + typedef TestDBusFixture super; + +protected: + + void SetUp() + { + super::SetUp(); + } + + void TearDown() + { + super::TearDown(); + } +}; + +/*** +**** +***/ + +TEST_F(RotationLockFixture, CheckIndicator) +{ + RotationLockIndicator indicator; + + ASSERT_STREQ("rotation_lock", indicator.name()); + auto actions = indicator.action_group(); + ASSERT_TRUE(actions != nullptr); + ASSERT_TRUE(g_action_group_has_action(G_ACTION_GROUP(actions), "rotation-lock")); + + std::vector> profiles = indicator.profiles(); + ASSERT_EQ(1, profiles.size()); + std::shared_ptr phone = profiles[0]; + ASSERT_EQ(std::string("phone"), phone->name()); + ASSERT_FALSE(phone->header()->is_visible); +} + diff --git a/tests/unit/usb-snap-test.cpp b/tests/unit/usb-snap-test.cpp new file mode 100644 index 0000000..70c9d97 --- /dev/null +++ b/tests/unit/usb-snap-test.cpp @@ -0,0 +1,216 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#define QT_NO_KEYWORDS +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +#include + +using namespace QtDBusTest; +using namespace QtDBusMock; + +inline QString qVariantToString(const QVariant& variant) { + QString output; + QDebug dbg(&output); + dbg << variant; + return output; +} + +inline void PrintTo(const QVariant& variant, std::ostream* os) { + QString output; + QDebug dbg(&output); + dbg << variant; + + *os << "QVariant(" << output.toStdString() << ")"; +} + +inline void PrintTo(const QString& s, std::ostream* os) { + *os << "\"" << s.toStdString() << "\""; +} + +inline void PrintTo(const QStringList& list, std::ostream* os) { + QString output; + QDebug dbg(&output); + dbg << list; + + *os << "QStringList(" << output.toStdString() << ")"; +} + +inline void PrintTo(const QList& list, std::ostream* os) { + QString output; + for (const auto& path: list) + { + output.append("\"" + path.path() + "\","); + } + + *os << "QList(" << output.toStdString() << ")"; +} + +#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ +{\ + while (signalSpy.size() < signalsExpected)\ + {\ + ASSERT_TRUE(signalSpy.wait());\ + }\ + ASSERT_EQ(signalsExpected, signalSpy.size());\ +} + +class UsbSnapFixture: public GlibFixture +{ + using super = GlibFixture; + +public: + + UsbSnapFixture(): + dbusMock{dbusTestRunner} + { + DBusTypes::registerMetaTypes(); + + dbusTestRunner.startServices(); + } + + ~UsbSnapFixture() =default; + +protected: + + bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) + { + if (variant.canConvert()) + { + QDBusArgument value(variant.value()); + if (value.currentType() == QDBusArgument::MapType) + { + value >> map; + return true; + } + } + return false; + } + + void SetUp() override + { + super::SetUp(); + + dbusMock.registerNotificationDaemon(); + dbusTestRunner.startServices(); + } + + OrgFreedesktopDBusMockInterface& notificationsMockInterface() + { + return dbusMock.mockInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::SessionBus); + } + + QtDBusTest::DBusTestRunner dbusTestRunner; + QtDBusMock::DBusMock dbusMock; + QtDBusTest::DBusServicePtr indicator; +}; + +TEST_F(UsbSnapFixture, TestRoundTrip) +{ + struct { + const char* fingerprint; + const char* action_to_invoke; + const AdbdClient::PKResponse expected_response; + } tests[] = { + { "Fingerprint", "allow", AdbdClient::PKResponse::ALLOW }, + { "Fingerprint", "deny", AdbdClient::PKResponse::DENY } + }; + + uint32_t next_id = 1; + for(const auto& test : tests) + { + // Minor wart: we don't have a way of getting the fdo notification id + // from dbusmock so instead we copy its (simple) id generation here + const auto id = next_id++; + + QSignalSpy notificationsSpy( + ¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + // start up a UsbSnap to ask about a fingerprint + auto snap = std::make_shared(test.fingerprint); + AdbdClient::PKResponse user_response {}; + bool user_response_set = false; + snap->on_user_response().connect([&user_response,&user_response_set](AdbdClient::PKResponse response, bool /*remember*/){ + user_response = response; + user_response_set = true; + }); + + // test that UsbSnap creates a fdo notification + WAIT_FOR_SIGNALS(notificationsSpy, 1); + { + QVariantList const& call(notificationsSpy.at(0)); + EXPECT_EQ("Notify", call.at(0)); + + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(8, args.size()); + EXPECT_EQ("", args.at(0)); // app name + EXPECT_EQ(0, args.at(1)); // replaces-id + EXPECT_EQ("computer-symbolic", args.at(2)); // icon name + EXPECT_EQ("Allow USB Debugging?", args.at(3)); // summary + EXPECT_EQ(QString::fromUtf8("The computer's RSA key fingerprint is: ") + test.fingerprint, args.at(4)); // body + EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Deny"}), args.at(5)); // actions + EXPECT_EQ(-1, args.at(7)); + + QVariantMap hints; + ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); + ASSERT_EQ(3, hints.size()); + ASSERT_TRUE(hints.contains("x-canonical-private-affirmative-tint")); + ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); + ASSERT_TRUE(hints.contains("x-canonical-snap-decisions")); + } + notificationsSpy.clear(); + + // fake a user interaction with the fdo notification + notificationsMockInterface().EmitSignal( + DBusTypes::NOTIFY_DBUS_INTERFACE, + "ActionInvoked", + "us", + QVariantList() << id << test.action_to_invoke); + + // test that UsbSnap emits on_user_response() as a result + wait_for([&user_response_set](){return user_response_set;}); + EXPECT_TRUE(user_response_set); + ASSERT_EQ(test.expected_response, user_response); + + // confirm that the snap dtor cleans up the notification + snap.reset(); + WAIT_FOR_SIGNALS(notificationsSpy, 1); + { + QVariantList const& call(notificationsSpy.at(0)); + EXPECT_EQ("CloseNotification", call.at(0)); + QVariantList const& args(call.at(1).toList()); + EXPECT_EQ(id, args.at(0)); + } + } +} diff --git a/tests/usb-snap-test.cpp b/tests/usb-snap-test.cpp deleted file mode 100644 index 309980a..0000000 --- a/tests/usb-snap-test.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - * 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 . - * - * Authors: - * Charles Kerr - */ - -#define QT_NO_KEYWORDS -#include -#include - -#include - -#include - -#include -#include -#include - -#include - -#include - -using namespace QtDBusTest; -using namespace QtDBusMock; - -inline QString qVariantToString(const QVariant& variant) { - QString output; - QDebug dbg(&output); - dbg << variant; - return output; -} - -inline void PrintTo(const QVariant& variant, std::ostream* os) { - QString output; - QDebug dbg(&output); - dbg << variant; - - *os << "QVariant(" << output.toStdString() << ")"; -} - -inline void PrintTo(const QString& s, std::ostream* os) { - *os << "\"" << s.toStdString() << "\""; -} - -inline void PrintTo(const QStringList& list, std::ostream* os) { - QString output; - QDebug dbg(&output); - dbg << list; - - *os << "QStringList(" << output.toStdString() << ")"; -} - -inline void PrintTo(const QList& list, std::ostream* os) { - QString output; - for (const auto& path: list) - { - output.append("\"" + path.path() + "\","); - } - - *os << "QList(" << output.toStdString() << ")"; -} - -#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ -{\ - while (signalSpy.size() < signalsExpected)\ - {\ - ASSERT_TRUE(signalSpy.wait());\ - }\ - ASSERT_EQ(signalsExpected, signalSpy.size());\ -} - -class UsbSnapFixture: public GlibFixture -{ - using super = GlibFixture; - -public: - - UsbSnapFixture(): - dbusMock{dbusTestRunner} - { - DBusTypes::registerMetaTypes(); - - dbusTestRunner.startServices(); - } - - ~UsbSnapFixture() =default; - -protected: - - bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) - { - if (variant.canConvert()) - { - QDBusArgument value(variant.value()); - if (value.currentType() == QDBusArgument::MapType) - { - value >> map; - return true; - } - } - return false; - } - - void SetUp() override - { - super::SetUp(); - - dbusMock.registerNotificationDaemon(); - dbusTestRunner.startServices(); - } - - OrgFreedesktopDBusMockInterface& notificationsMockInterface() - { - return dbusMock.mockInterface("org.freedesktop.Notifications", - "/org/freedesktop/Notifications", - "org.freedesktop.Notifications", - QDBusConnection::SessionBus); - } - - QtDBusTest::DBusTestRunner dbusTestRunner; - QtDBusMock::DBusMock dbusMock; - QtDBusTest::DBusServicePtr indicator; -}; - -TEST_F(UsbSnapFixture, TestRoundTrip) -{ - struct { - const char* fingerprint; - const char* action_to_invoke; - const AdbdClient::PKResponse expected_response; - } tests[] = { - { "Fingerprint", "allow", AdbdClient::PKResponse::ALLOW }, - { "Fingerprint", "deny", AdbdClient::PKResponse::DENY } - }; - - uint32_t next_id = 1; - for(const auto& test : tests) - { - // Minor wart: we don't have a way of getting the fdo notification id - // from dbusmock so instead we copy its (simple) id generation here - const auto id = next_id++; - - QSignalSpy notificationsSpy( - ¬ificationsMockInterface(), - SIGNAL(MethodCalled(const QString &, const QVariantList &))); - - // start up a UsbSnap to ask about a fingerprint - auto snap = std::make_shared(test.fingerprint); - AdbdClient::PKResponse user_response {}; - bool user_response_set = false; - snap->on_user_response().connect([&user_response,&user_response_set](AdbdClient::PKResponse response, bool /*remember*/){ - user_response = response; - user_response_set = true; - }); - - // test that UsbSnap creates a fdo notification - WAIT_FOR_SIGNALS(notificationsSpy, 1); - { - QVariantList const& call(notificationsSpy.at(0)); - EXPECT_EQ("Notify", call.at(0)); - - QVariantList const& args(call.at(1).toList()); - ASSERT_EQ(8, args.size()); - EXPECT_EQ("", args.at(0)); // app name - EXPECT_EQ(0, args.at(1)); // replaces-id - EXPECT_EQ("computer-symbolic", args.at(2)); // icon name - EXPECT_EQ("Allow USB Debugging?", args.at(3)); // summary - EXPECT_EQ(QString::fromUtf8("The computer's RSA key fingerprint is: ") + test.fingerprint, args.at(4)); // body - EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Deny"}), args.at(5)); // actions - EXPECT_EQ(-1, args.at(7)); - - QVariantMap hints; - ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); - ASSERT_EQ(3, hints.size()); - ASSERT_TRUE(hints.contains("x-canonical-private-affirmative-tint")); - ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); - ASSERT_TRUE(hints.contains("x-canonical-snap-decisions")); - } - notificationsSpy.clear(); - - // fake a user interaction with the fdo notification - notificationsMockInterface().EmitSignal( - DBusTypes::NOTIFY_DBUS_INTERFACE, - "ActionInvoked", - "us", - QVariantList() << id << test.action_to_invoke); - - // test that UsbSnap emits on_user_response() as a result - wait_for([&user_response_set](){return user_response_set;}); - EXPECT_TRUE(user_response_set); - ASSERT_EQ(test.expected_response, user_response); - - // confirm that the snap dtor cleans up the notification - snap.reset(); - WAIT_FOR_SIGNALS(notificationsSpy, 1); - { - QVariantList const& call(notificationsSpy.at(0)); - EXPECT_EQ("CloseNotification", call.at(0)); - QVariantList const& args(call.at(1).toList()); - EXPECT_EQ(id, args.at(0)); - } - } -} 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include +#include +#include + + +/** + * 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& 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 m_requests; + std::vector 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; +}; + + diff --git a/tests/utils/dbus-types.h b/tests/utils/dbus-types.h new file mode 100644 index 0000000..c2dfb81 --- /dev/null +++ b/tests/utils/dbus-types.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013-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 . + * + * Author: Pete Woods + */ + +#pragma once + +#include +#include +#include +#include + +typedef QMap QVariantDictMap; +Q_DECLARE_METATYPE(QVariantDictMap) + +typedef QMap QStringMap; +Q_DECLARE_METATYPE(QStringMap) + +namespace DBusTypes +{ + inline void registerMetaTypes() + { + qRegisterMetaType("QVariantDictMap"); + qRegisterMetaType("QStringMap"); + + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + } + static constexpr char const* NOTIFY_DBUS_NAME = "org.freedesktop.Notifications"; + + static constexpr char const* NOTIFY_DBUS_INTERFACE = "org.freedesktop.Notifications"; + + static constexpr char const* NOTIFY_DBUS_PATH = "/org/freedesktop/Notifications"; +} diff --git a/tests/utils/glib-fixture.h b/tests/utils/glib-fixture.h new file mode 100644 index 0000000..ccdeccd --- /dev/null +++ b/tests/utils/glib-fixture.h @@ -0,0 +1,203 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr + * + * 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 . + */ + +#pragma once + +#include // std::function +#include +#include // std::shared_ptr + +#include +#include +#include + +#include + +#include // setlocale() + +class GlibFixture : public ::testing::Test +{ + public: + + virtual ~GlibFixture() =default; + + protected: + + virtual void SetUp() override + { + setlocale(LC_ALL, "C.UTF-8"); + + loop = g_main_loop_new(nullptr, false); + +#ifdef SCHEMA_DIR + // only use local, temporary settings + g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); + g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); + g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); +#endif + + // fail on unexpected messages from this domain + g_log_set_fatal_mask(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING); + + g_unsetenv("DISPLAY"); + + } + + virtual void TearDown() override + { + g_test_assert_expected_messages (); + + g_clear_pointer(&loop, g_main_loop_unref); + } + + void expectLogMessage (const gchar *domain, GLogLevelFlags level, const gchar *pattern) + { + g_test_expect_message (domain, level, pattern); + } + + private: + + static gboolean + wait_for_signal__timeout(gpointer name) + { + g_error("%s: timed out waiting for signal '%s'", G_STRLOC, static_cast(name)); + return G_SOURCE_REMOVE; + } + + static gboolean + wait_msec__timeout(gpointer loop) + { + g_main_loop_quit(static_cast(loop)); + return G_SOURCE_CONTINUE; + } + + protected: + + /* convenience func to loop while waiting for a GObject's signal */ + void wait_for_signal(gpointer o, const gchar * signal, guint timeout_seconds=5) + { + // wait for the signal or for timeout, whichever comes first + const auto handler_id = g_signal_connect_swapped(o, signal, + G_CALLBACK(g_main_loop_quit), + loop); + const auto timeout_id = g_timeout_add_seconds(timeout_seconds, + wait_for_signal__timeout, + loop); + g_main_loop_run(loop); + g_source_remove(timeout_id); + g_signal_handler_disconnect(o, handler_id); + } + + /* convenience func to loop for N msec */ + void wait_msec(guint msec=50) + { + const auto id = g_timeout_add(msec, wait_msec__timeout, loop); + g_main_loop_run(loop); + g_source_remove(id); + } + + bool wait_for(std::function test_function, guint timeout_msec=1000) + { + auto timer = std::shared_ptr(g_timer_new(), [](GTimer* t){g_timer_destroy(t);}); + const auto timeout_sec = timeout_msec / 1000.0; + for (;;) { + if (test_function()) + return true; + //g_message("%f ... %f", g_timer_elapsed(timer.get(), nullptr), timeout_sec); + if (g_timer_elapsed(timer.get(), nullptr) >= timeout_sec) + return false; + wait_msec(); + } + } + + bool wait_for_name_owned(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + struct Data { + GMainLoop* loop = nullptr; + bool owned = false; + }; + Data data; + + auto on_name_appeared = [](GDBusConnection* /*connection*/, + const gchar* /*name_*/, + const gchar* name_owner, + gpointer gdata) + { + if (name_owner == nullptr) + return; + auto tmp = static_cast(gdata); + tmp->owned = true; + g_main_loop_quit(tmp->loop); + }; + + const auto timeout_id = g_timeout_add(timeout_msec, wait_msec__timeout, loop); + data.loop = loop; + const auto watch_id = g_bus_watch_name_on_connection(connection, + name, + flags, + on_name_appeared, + nullptr, /* name_vanished */ + &data, + nullptr); /* user_data_free_func */ + g_main_loop_run(loop); + + g_bus_unwatch_name(watch_id); + g_source_remove(timeout_id); + + return data.owned; + } + + void EXPECT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + EXPECT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; + } + + void EXPECT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + EXPECT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; + } + + void ASSERT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + ASSERT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; + } + + void ASSERT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection, + const gchar* name, + guint timeout_msec=1000, + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) + { + ASSERT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; + } + + GMainLoop * loop; +}; + diff --git a/tests/utils/qmain.cpp b/tests/utils/qmain.cpp new file mode 100644 index 0000000..72a49b1 --- /dev/null +++ b/tests/utils/qmain.cpp @@ -0,0 +1,60 @@ +/* + * Copyright © 2014 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser 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 warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Pete Woods + */ + +//#include + +#include +#include +#include + +#include + +using namespace QtDBusMock; + +class Runner: public QObject +{ + Q_OBJECT +public Q_SLOTS: + void run() + { + QCoreApplication::exit(RUN_ALL_TESTS()); + } +}; + +int main(int argc, char **argv) +{ + qputenv("LANG", "C.UTF-8"); + unsetenv("LC_ALL"); + + // boilerplate i18n + setlocale(LC_ALL, ""); + bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain(GETTEXT_PACKAGE); + + QCoreApplication application(argc, argv); + DBusMock::registerMetaTypes(); + ::testing::InitGoogleTest(&argc, argv); + + Runner runner; + QTimer::singleShot(0, &runner, SLOT(run())); + + return application.exec(); +} + +#include "qmain.moc" diff --git a/tests/utils/test-dbus-fixture.h b/tests/utils/test-dbus-fixture.h new file mode 100644 index 0000000..3947e58 --- /dev/null +++ b/tests/utils/test-dbus-fixture.h @@ -0,0 +1,106 @@ +/* + * Copyright 2013 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#include "glib-fixture.h" + +/*** +**** +***/ + +class TestDBusFixture: public GlibFixture +{ + public: + + TestDBusFixture() =default; + virtual ~TestDBusFixture() =default; + + explicit TestDBusFixture(const std::vector& service_dirs_in): service_dirs(service_dirs_in) {} + + private: + + typedef GlibFixture super; + + static void + on_bus_opened (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + self->system_bus = g_bus_get_finish (res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + static void + on_bus_closed (GObject* /*object*/, GAsyncResult * res, gpointer gself) + { + auto self = static_cast(gself); + + GError * err = 0; + g_dbus_connection_close_finish (self->system_bus, res, &err); + g_assert_no_error (err); + + g_main_loop_quit (self->loop); + } + + protected: + + GTestDBus * test_dbus = nullptr; + GDBusConnection * system_bus = nullptr; + const std::vector service_dirs; + + virtual void SetUp() override + { + super::SetUp (); + + // pull up a test dbus + test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE); + for (const auto& dir : service_dirs) + g_test_dbus_add_service_dir (test_dbus, dir.c_str()); + g_test_dbus_up (test_dbus); + const char * address = g_test_dbus_get_bus_address (test_dbus); + g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, true); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", address, true); + g_debug ("test_dbus's address is %s", address); + + // wait for the GDBusConnection before returning + g_bus_get (G_BUS_TYPE_SYSTEM, nullptr, on_bus_opened, this); + g_main_loop_run (loop); + } + + virtual void TearDown() override + { + wait_msec(); + + // close the system bus + g_dbus_connection_close(system_bus, nullptr, on_bus_closed, this); + g_main_loop_run(loop); + g_clear_object(&system_bus); + + // tear down the test dbus + g_test_dbus_down(test_dbus); + g_clear_object(&test_dbus); + + super::TearDown(); + } +}; + -- cgit v1.2.3 From d8ef8e68805ab7f53258427c79ee5aaafec916ba Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 10 Mar 2016 17:09:59 -0600 Subject: add UsbManager --- src/CMakeLists.txt | 1 + src/main.cpp | 14 +-- src/usb-manager.cpp | 109 +++++++++++++++++++ src/usb-manager.h | 34 ++++++ tests/CMakeLists.txt | 1 + tests/integration/CMakeLists.txt | 24 +++++ tests/integration/usb-manager-test.cpp | 186 +++++++++++++++++++++++++++++++++ tests/unit/adbd-client-test.cpp | 22 ++-- tests/unit/usb-snap-test.cpp | 38 +------ tests/utils/gtest-qt-print-helpers.h | 45 ++++++++ 10 files changed, 416 insertions(+), 58 deletions(-) create mode 100644 src/usb-manager.cpp create mode 100644 src/usb-manager.h create mode 100644 tests/integration/CMakeLists.txt create mode 100644 tests/integration/usb-manager-test.cpp create mode 100644 tests/utils/gtest-qt-print-helpers.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6cfda91..d0ab901 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,6 +10,7 @@ add_library( exporter.cpp indicator.cpp rotation-lock.cpp + usb-manager.cpp usb-snap.cpp ) diff --git a/src/main.cpp b/src/main.cpp index 151b642..eb1bb2c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,10 +17,9 @@ * Charles Kerr */ -#include #include #include -#include +#include #include // bindtextdomain() #include @@ -59,15 +58,10 @@ main(int /*argc*/, char** /*argv*/) // We need the ADBD handler running, // even though it doesn't have an indicator component yet static constexpr char const * ADB_SOCKET_PATH {"/dev/socket/adb"}; - GAdbdClient adbd_client{ADB_SOCKET_PATH}; - adbd_client.on_pk_request().connect([](const AdbdClient::PKRequest& req){ - auto snap = new UsbSnap(req.fingerprint); - snap->on_user_response().connect([req,snap](AdbdClient::PKResponse response, bool /*FIXME: remember_choice*/){ - req.respond(response); - g_idle_add([](gpointer gsnap){delete static_cast(gsnap); return G_SOURCE_REMOVE;}, snap); // delete-later - }); - }); + static constexpr char const * PUBLIC_KEYS_FILENAME {"/data/misc/adb/adb_keys"}; + UsbManager usb_manager {ADB_SOCKET_PATH, PUBLIC_KEYS_FILENAME}; + // let's go! g_main_loop_run(loop); // cleanup diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp new file mode 100644 index 0000000..6b40cea --- /dev/null +++ b/src/usb-manager.cpp @@ -0,0 +1,109 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +class UsbManager::Impl +{ +public: + + explicit Impl( + const std::string& socket_path, + const std::string& public_keys_filename + ): + m_adbd_client{std::make_shared(socket_path)}, + m_public_keys_filename{public_keys_filename} + { + m_adbd_client->on_pk_request().connect([this](const AdbdClient::PKRequest& req){ + auto snap = new UsbSnap(req.fingerprint); + snap->on_user_response().connect([this,req,snap](AdbdClient::PKResponse response, bool /*FIXME: remember_choice*/){ + req.respond(response); + if (response == AdbdClient::PKResponse::ALLOW) + write_public_key(req.public_key); + + // delete later + g_idle_add([](gpointer gsnap){delete static_cast(gsnap); return G_SOURCE_REMOVE;}, snap); + }); + }); + } + + ~Impl() + { + } + +private: + + void write_public_key(const std::string& public_key) + { + // confirm the directory exists + auto dirname = g_path_get_dirname(m_public_keys_filename.c_str()); + const auto dir_exists = g_file_test(dirname, G_FILE_TEST_IS_DIR); + if (!dir_exists) + g_warning("ADB data directory '%s' does not exist", dirname); + g_clear_pointer(&dirname, g_free); + if (!dir_exists) + return; + + // open the file in append mode, with user rw and group r permissions + const auto fd = open( + m_public_keys_filename.c_str(), + O_APPEND|O_CREAT|O_WRONLY, + S_IRUSR|S_IWUSR|S_IRGRP + ); + if (fd == -1) { + g_warning("Error opening ADB datafile: %s", g_strerror(errno)); + return; + } + + // write the new public key on its own line + std::string buf {public_key + '\n'}; + if (write(fd, buf.c_str(), buf.size()) == -1) + g_warning("Error writing ADB datafile: %d %s", errno, g_strerror(errno)); + close(fd); + } + + std::shared_ptr m_adbd_client; + const std::string m_public_keys_filename; +}; + +/*** +**** +***/ + +UsbManager::UsbManager( + const std::string& socket_path, + const std::string& public_keys_filename +): + impl{new Impl{socket_path, public_keys_filename}} +{ +} + +UsbManager::~UsbManager() +{ +} + diff --git a/src/usb-manager.h b/src/usb-manager.h new file mode 100644 index 0000000..28a27f3 --- /dev/null +++ b/src/usb-manager.h @@ -0,0 +1,34 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#include +#include + +class UsbManager +{ +public: + UsbManager(const std::string& socket_path, const std::string& public_key_filename); + ~UsbManager(); + +protected: + class Impl; + std::unique_ptr impl; +}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 205792e..be0000d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,5 +27,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNING_ARGS}") add_test(cppcheck cppcheck --enable=all -USCHEMA_DIR --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) +add_subdirectory(integration) add_subdirectory(unit) add_subdirectory(utils) diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt new file mode 100644 index 0000000..c04ecc8 --- /dev/null +++ b/tests/integration/CMakeLists.txt @@ -0,0 +1,24 @@ +set(SERVICE_LINK_LIBRARIES + ${SERVICE_LIB} + ${SERVICE_DEPS_LIBRARIES} +) +set(QT_LINK_LIBRARIES + test-utils + Qt5::Core + Qt5::Test + Qt5::DBus +) +set(TEST_LINK_LIBRARIES + ${TEST_DEPS_LIBRARIES} + ${GTEST_LIBRARIES} + ${GMOCK_LIBRARIES} +) + +function(add_qt_test_by_name name) + set(TEST_NAME ${name}) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) + add_test(${TEST_NAME} ${TEST_NAME}) + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") + target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES}) +endfunction() +add_qt_test_by_name(usb-manager-test) diff --git a/tests/integration/usb-manager-test.cpp b/tests/integration/usb-manager-test.cpp new file mode 100644 index 0000000..aca5325 --- /dev/null +++ b/tests/integration/usb-manager-test.cpp @@ -0,0 +1,186 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#define QT_NO_KEYWORDS + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include + +/*** +**** +***/ + +#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ +{\ + while (signalSpy.size() < signalsExpected)\ + {\ + ASSERT_TRUE(signalSpy.wait());\ + }\ + ASSERT_EQ(signalsExpected, signalSpy.size());\ +} + +class UsbManagerFixture: public GlibFixture +{ + using super = GlibFixture; + +public: + + UsbManagerFixture(): + dbusMock{dbusTestRunner} + { + DBusTypes::registerMetaTypes(); + + dbusTestRunner.startServices(); + } + + ~UsbManagerFixture() =default; + +protected: + + static void file_deleter (std::string* s) + { + fprintf(stderr, "remove \"%s\"\n", s->c_str()); + remove(s->c_str()); + delete s; + } + + bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) + { + if (variant.canConvert()) + { + QDBusArgument value(variant.value()); + if (value.currentType() == QDBusArgument::MapType) + { + value >> map; + return true; + } + } + return false; + } + + void SetUp() override + { + super::SetUp(); + + char tmpl[] = {"usb-manager-test-XXXXXX"}; + m_tmpdir.reset(new std::string{g_mkdtemp(tmpl)}, file_deleter); + g_message("using tmpdir '%s'", m_tmpdir->c_str()); + + dbusMock.registerNotificationDaemon(); + dbusTestRunner.startServices(); + } + + OrgFreedesktopDBusMockInterface& notificationsMockInterface() + { + return dbusMock.mockInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::SessionBus); + } + + QtDBusTest::DBusTestRunner dbusTestRunner; + QtDBusMock::DBusMock dbusMock; + QtDBusTest::DBusServicePtr indicator; + std::shared_ptr m_tmpdir; +}; + +TEST_F(UsbManagerFixture, Allow) +{ + const std::shared_ptr socket_path {new std::string{*m_tmpdir+"/socket"}, file_deleter}; + const std::shared_ptr public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter}; + + // add a signal spy to listen to the notification daemon + QSignalSpy notificationsSpy( + ¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &)) + ); + + // start a mock AdbdServer ready to submit a request + const std::string public_key {"qAAAALUHllFjEZjl5jbS9ivjpQpaTNpibl28Re71D/S8sV3usNJTkbpvZYoVPfxtmHSNdCgLkWN6qcDZsHZqE/4myzmx/8Y/RqBy1oirudugi3YUUcJh7aWkY8lKQe9shCLTcrT7cFLZIJIidTvfmWTm0UcU+xmdPALze11I3lGo1Ty5KpCe9oP+qYM8suHbxhm78LKLlo0QJ2QqM8T5isr1pvoPHDgRb+mSESElG+xDIfPWA2BTu77/xk4EnXmOYfcuCr5akF3N4fRo/ACnYgXWDZFX2XdklBXyDj78lVlinF37xdMk7BMQh166X7UNkpH1uG2y5F6lUzyLg8SsFtRnJkw7eVe/gnJj3feQaFQbF5oVDhWhLMtWLtejhX6umvroVBVA4rynG4xEgs00K4u4ly8DUIIJYDO22Ml4myFR5CUm3lOlyitNdzYGh0utLXPq9oc8EbMVxM3i+O7PRxQw5Ul04X6K8GLiGUDV98DB+xYUqfEveq1BRnXi/ZrdPDhQ8Lfkg5xnLccPTFamAqutPtZXV6s7dXJInBTZf0NtBaWL0RdR2cOJBrpeBYkrc9yIyeqFLFdxr66rjaehjaa4pS4S+CD6PkGiIpPWSQtwNC4RlT10qTQ0/K9lRux2p0D8Z8ubUTFuh4kBScGUkN1OV3Z+7d7B+ghmBtZrrgleXsbehjRuKgEAAQA= foo@bar"}; + const std::string fingerprint {"12:23:5f:2d:8c:40:ae:1d:05:7b:ae:bd:88:8a:f0:80"}; + auto adbd_server = std::make_shared(*socket_path, std::vector{"PK"+public_key}); + + // set up a UsbManager to process the request + auto usb_manager = std::make_shared(*socket_path, *public_keys_path); + + // wait for the notification to show up, confirm it looks right + WAIT_FOR_SIGNALS(notificationsSpy, 1); + { + QVariantList const& call(notificationsSpy.at(0)); + EXPECT_EQ("Notify", call.at(0)); + + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(8, args.size()); + EXPECT_EQ("", args.at(0)); // app name + EXPECT_EQ(0, args.at(1)); // replaces-id + EXPECT_EQ("computer-symbolic", args.at(2)); // icon name + EXPECT_EQ("Allow USB Debugging?", args.at(3)); // summary + EXPECT_EQ(QString::fromUtf8("The computer's RSA key fingerprint is: ") + QString::fromUtf8(fingerprint.c_str()), args.at(4)); // body + EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Deny"}), args.at(5)); // actions + EXPECT_EQ(-1, args.at(7)); + + QVariantMap hints; + ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); + ASSERT_EQ(3, hints.size()); + ASSERT_TRUE(hints.contains("x-canonical-private-affirmative-tint")); + ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); + ASSERT_TRUE(hints.contains("x-canonical-snap-decisions")); + } + notificationsSpy.clear(); + + // click on allow in the notification + notificationsMockInterface().EmitSignal( + DBusTypes::NOTIFY_DBUS_INTERFACE, + "ActionInvoked", + "us", + QVariantList() << uint32_t(1) << "allow" + ); + + // confirm that the AdbdServer got the right response + wait_for([adbd_server](){return !adbd_server->m_responses.empty();}, 2000); + ASSERT_EQ(1, adbd_server->m_responses.size()); + EXPECT_EQ("OK", adbd_server->m_responses.front()); + + // confirm that the public_keys file got the public key appended to it + std::ifstream ifkeys {*public_keys_path}; + std::vector lines; + std::string line; + while(getline(ifkeys, line)) + lines.emplace_back(std::move(line)); + ASSERT_EQ(1, lines.size()); + EXPECT_EQ(public_key, lines[0]); +} diff --git a/tests/unit/adbd-client-test.cpp b/tests/unit/adbd-client-test.cpp index 1e28cc9..6514456 100644 --- a/tests/unit/adbd-client-test.cpp +++ b/tests/unit/adbd-client-test.cpp @@ -31,22 +31,22 @@ private: protected: - std::string m_tmpdir; + static void file_deleter (std::string* s) + { + fprintf(stderr, "remove \"%s\"\n", s->c_str()); + remove(s->c_str()); + delete s; + } + + std::shared_ptr m_tmpdir; void SetUp() { super::SetUp(); char tmpl[] {"adb-client-test-XXXXXX"}; - m_tmpdir = mkdtemp(tmpl); - g_message("using tmpdir '%s'", m_tmpdir.c_str()); - } - - void TearDown() - { - g_rmdir(m_tmpdir.c_str()); - - super::TearDown(); + m_tmpdir.reset(new std::string{mkdtemp(tmpl)}, file_deleter); + g_message("using tmpdir '%s'", m_tmpdir->c_str()); } }; @@ -67,7 +67,7 @@ TEST_F(AdbdClientFixture, SocketPlumbing) const auto main_thread = g_thread_self(); - const auto socket_path = m_tmpdir + "/test-socket-plumbing"; + const auto socket_path = *m_tmpdir + "/test-socket-plumbing"; g_message("socket_path is %s", socket_path.c_str()); for (const auto& test : tests) diff --git a/tests/unit/usb-snap-test.cpp b/tests/unit/usb-snap-test.cpp index 70c9d97..84555cc 100644 --- a/tests/unit/usb-snap-test.cpp +++ b/tests/unit/usb-snap-test.cpp @@ -20,6 +20,7 @@ #define QT_NO_KEYWORDS #include #include +#include #include @@ -36,43 +37,6 @@ using namespace QtDBusTest; using namespace QtDBusMock; -inline QString qVariantToString(const QVariant& variant) { - QString output; - QDebug dbg(&output); - dbg << variant; - return output; -} - -inline void PrintTo(const QVariant& variant, std::ostream* os) { - QString output; - QDebug dbg(&output); - dbg << variant; - - *os << "QVariant(" << output.toStdString() << ")"; -} - -inline void PrintTo(const QString& s, std::ostream* os) { - *os << "\"" << s.toStdString() << "\""; -} - -inline void PrintTo(const QStringList& list, std::ostream* os) { - QString output; - QDebug dbg(&output); - dbg << list; - - *os << "QStringList(" << output.toStdString() << ")"; -} - -inline void PrintTo(const QList& list, std::ostream* os) { - QString output; - for (const auto& path: list) - { - output.append("\"" + path.path() + "\","); - } - - *os << "QList(" << output.toStdString() << ")"; -} - #define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ {\ while (signalSpy.size() < signalsExpected)\ diff --git a/tests/utils/gtest-qt-print-helpers.h b/tests/utils/gtest-qt-print-helpers.h new file mode 100644 index 0000000..7a0897e --- /dev/null +++ b/tests/utils/gtest-qt-print-helpers.h @@ -0,0 +1,45 @@ + +#pragma once + +#include +#include +#include +#include + +inline QString qVariantToString(const QVariant& variant) { + QString output; + QDebug dbg(&output); + dbg << variant; + return output; +} + +inline void PrintTo(const QVariant& variant, std::ostream* os) { + QString output; + QDebug dbg(&output); + dbg << variant; + + *os << "QVariant(" << output.toStdString() << ")"; +} + +inline void PrintTo(const QString& s, std::ostream* os) { + *os << "\"" << s.toStdString() << "\""; +} + +inline void PrintTo(const QStringList& list, std::ostream* os) { + QString output; + QDebug dbg(&output); + dbg << list; + + *os << "QStringList(" << output.toStdString() << ")"; +} + +inline void PrintTo(const QList& list, std::ostream* os) { + QString output; + for (const auto& path: list) + { + output.append("\"" + path.path() + "\","); + } + + *os << "QList(" << output.toStdString() << ")"; +} + -- cgit v1.2.3 From 0734731e7296f84ab30ac403886635b7cb89da49 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 11 Mar 2016 14:06:34 -0600 Subject: clean up compile_options and warnings. Add DBUS debug log messages to ctest environment variables. --- CMakeLists.txt | 25 ++++++++++++++----------- src/CMakeLists.txt | 5 ++++- tests/CMakeLists.txt | 9 ++++++--- tests/integration/CMakeLists.txt | 2 +- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5110c9e..3230a2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ option(enable_lcov "Generate lcov code coverage reports." ON) ## ## GNU standard paths ## + include(GNUInstallDirs) if(EXISTS "/etc/debian_version") # Workaround for libexecdir on debian set(CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBDIR}") @@ -35,32 +36,34 @@ set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) find_package(PkgConfig REQUIRED) +set(GLIB_MINIMUM 2.36) pkg_check_modules(SERVICE_DEPS REQUIRED - gio-unix-2.0>=2.36 - glib-2.0>=2.36 + gio-unix-2.0>=${GLIB_MINIMUM} + glib-2.0>=${GLIB_MINIMUM} ) include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS} ) ## -## +## Compiler settings ## -set (CMAKE_INCLUDE_CURRENT_DIR OFF) -include_directories (${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) # set the compiler warnings if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Weverything -Wno-c++98-compat -Wno-padded") -else() - set(CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wall -Wextra -Wpedantic") + list(APPEND CXX_WARNING_ARGS -Weverything -Wno-c++98-compat -Wno-padded) +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + list(APPEND CXX_WARNING_ARGS -Wall -Wextra -Wpedantic) endif() -set(CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-missing-field-initializers") # GActionEntry -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fPIC -g") +add_compile_options(-std=c++14 -fPIC -g) + +## +## Testing & Coverage +## -# testing & coverage if(${enable_tests}) enable_testing() if(${enable_lcov}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d0ab901..02d973e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,10 @@ add_definitions(-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") +add_compile_options( + ${CXX_WARNING_ARGS} + ${GCOV_FLAGS} +) add_library( ${SERVICE_LIB} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index be0000d..7be2acd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,14 +16,17 @@ include_directories(SYSTEM ${GMOCK_INCLUDE_DIRS} ) -set(CTEST_ENVIRONMENT "${CTEST_ENVIRONMENT};G_MESSAGES_DEBUG=all") +list(APPEND CTEST_ENVIRONMENT + G_MESSAGES_DEBUG=all + G_DBUS_DEBUG=call,signal,return,message +) # turn off the warnings that break Google Test if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-global-constructors -Wno-weak-vtables") + list(APPEND CXX_WARNING_ARGS -Wno-global-constructors -Wno-weak-vtables) endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARNING_ARGS}") +add_compile_options(${CXX_WARNING_ARGS}) add_test(cppcheck cppcheck --enable=all -USCHEMA_DIR --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index c04ecc8..1eb7b3e 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -18,7 +18,7 @@ function(add_qt_test_by_name name) set(TEST_NAME ${name}) add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test(${TEST_NAME} ${TEST_NAME}) - set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "${CTEST_ENVIRONMENT}") + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT}) target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES}) endfunction() add_qt_test_by_name(usb-manager-test) -- cgit v1.2.3 From bb4fe392f34fc6f839870967fe273dd1e3d1ed53 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 13:38:58 -0500 Subject: add new CMakeLists.txt files in tests/utils/ and tests/unit/ --- tests/unit/CMakeLists.txt | 34 ++++++++++++++++++++++++++++++++++ tests/utils/CMakeLists.txt | 17 +++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/unit/CMakeLists.txt create mode 100644 tests/utils/CMakeLists.txt diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt new file mode 100644 index 0000000..26f2cfb --- /dev/null +++ b/tests/unit/CMakeLists.txt @@ -0,0 +1,34 @@ +set(SERVICE_LINK_LIBRARIES + ${SERVICE_LIB} + ${SERVICE_DEPS_LIBRARIES} +) +set(QT_LINK_LIBRARIES + test-utils + Qt5::Core + Qt5::Test + Qt5::DBus +) +set(TEST_LINK_LIBRARIES + ${TEST_DEPS_LIBRARIES} + ${GTEST_LIBRARIES} + ${GMOCK_LIBRARIES} +) + +function(add_test_by_name name) + set(TEST_NAME ${name}) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) + add_test(${TEST_NAME} ${TEST_NAME}) + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT}) + target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES}) +endfunction() +add_test_by_name(adbd-client-test) +add_test_by_name(rotation-lock-test) + +function(add_qt_test_by_name name) + set(TEST_NAME ${name}) + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) + add_test(${TEST_NAME} ${TEST_NAME}) + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT}) + target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES}) +endfunction() +add_qt_test_by_name(usb-snap-test) diff --git a/tests/utils/CMakeLists.txt b/tests/utils/CMakeLists.txt new file mode 100644 index 0000000..e458c82 --- /dev/null +++ b/tests/utils/CMakeLists.txt @@ -0,0 +1,17 @@ +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_library( + test-utils + STATIC + qmain.cpp +) + +qt5_use_modules( + test-utils + Core + DBus +) + -- cgit v1.2.3 From 6174e922ef0e819d0b8f8f16ad863c83f8fa6421 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 13:49:19 -0500 Subject: tweak a couple of 'auto' declarations that g++ 5.3.1 understands but 4.9.2 on vivid doesn't --- src/adbd-client.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/adbd-client.cpp b/src/adbd-client.cpp index edd403c..30929a7 100644 --- a/src/adbd-client.cpp +++ b/src/adbd-client.cpp @@ -115,7 +115,7 @@ private: void worker_func() // runs in worker thread { - const auto socket_path {m_socket_path}; + const std::string socket_path {m_socket_path}; while (!g_cancellable_is_cancelled(m_cancellable)) { @@ -155,7 +155,7 @@ private: // If nothing interesting's happening, sleep a bit. // (Interval copied from UsbDebuggingManager.java) - static constexpr auto sleep_interval {std::chrono::seconds(1)}; + static constexpr std::chrono::seconds sleep_interval {std::chrono::seconds(1)}; if (!got_valid_req && !g_cancellable_is_cancelled(m_cancellable)) { std::unique_lock lk(m_sleep_mutex); m_sleep_cv.wait_for(lk, sleep_interval); -- cgit v1.2.3 From b3f65b988fe0a646acaee6d3e71a9b1682e0b014 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 14:17:21 -0500 Subject: cmake's Thread::Thread import doesn't exist in vivid's version of cmake (3.0.2), so fall back gracefully to -pthread there --- src/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 02d973e..4fe7b1a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,10 +22,16 @@ add_executable( main.cpp ) +if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 3.2) + set(SERVICE_THREAD_LIBS -pthread) +else() + set(SERVICE_THREAD_LIBS Threads::Threads) +endif() + target_link_libraries(${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} - Threads::Threads + ${SERVICE_THREAD_LIBS} ${GCOV_LIBS} ) -- cgit v1.2.3 From 4aeb345821bad91430fe9cdb7244c1135051bd92 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 14:23:40 -0500 Subject: in tests/unit/adbd-client-test.cpp, fix tmpl character buffer intialization for g++ 4.9x --- tests/unit/adbd-client-test.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/unit/adbd-client-test.cpp b/tests/unit/adbd-client-test.cpp index 6514456..c00c07a 100644 --- a/tests/unit/adbd-client-test.cpp +++ b/tests/unit/adbd-client-test.cpp @@ -22,8 +22,6 @@ #include -#include // mkdtemp - class AdbdClientFixture: public TestDBusFixture { private: @@ -44,8 +42,8 @@ protected: { super::SetUp(); - char tmpl[] {"adb-client-test-XXXXXX"}; - m_tmpdir.reset(new std::string{mkdtemp(tmpl)}, file_deleter); + char tmpl[] = {"adb-client-test-XXXXXX"}; + m_tmpdir.reset(new std::string{g_mkdtemp(tmpl)}, file_deleter); g_message("using tmpdir '%s'", m_tmpdir->c_str()); } }; -- cgit v1.2.3 From df322a475939f3d30e4ff20213df1c01f7ff26d5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 15:41:55 -0500 Subject: add some debug stubs --- src/adbd-client.cpp | 6 ++++-- src/main.cpp | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/adbd-client.cpp b/src/adbd-client.cpp index 30929a7..4f7d28f 100644 --- a/src/adbd-client.cpp +++ b/src/adbd-client.cpp @@ -119,7 +119,7 @@ private: while (!g_cancellable_is_cancelled(m_cancellable)) { - g_debug("%s creating a client socket", G_STRLOC); + g_debug("%s creating a client socket to '%s'", G_STRLOC, socket_path.c_str()); auto socket = create_client_socket(socket_path); bool got_valid_req = false; @@ -179,9 +179,11 @@ private: } auto address = g_unix_socket_address_new(socket_path.c_str()); - const auto connected = g_socket_connect(socket, address, m_cancellable, nullptr); + const auto connected = g_socket_connect(socket, address, m_cancellable, &error); g_clear_object(&address); if (!connected) { + g_debug("unable to connect to '%s': %s", socket_path.c_str(), error->message); + g_clear_error(&error); g_clear_object(&socket); return nullptr; } diff --git a/src/main.cpp b/src/main.cpp index eb1bb2c..2e84afa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,9 @@ int main(int /*argc*/, char** /*argv*/) { +#warning temp +g_assert(g_setenv("G_MESSAGES_DEBUG", "all", true)); + // Work around a deadlock in glib's type initialization. // It can be removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is fixed. g_type_ensure(G_TYPE_DBUS_CONNECTION); -- cgit v1.2.3 From 670f6fd05bd49e58ea326656fe8f231df4685533 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 15:55:19 -0500 Subject: fix test timing issue, had max timeout at 1 sec for listening for fdo notification user response, but the tests on the phone are sometimes too slow for that timeout --- tests/unit/usb-snap-test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/usb-snap-test.cpp b/tests/unit/usb-snap-test.cpp index 84555cc..4300c47 100644 --- a/tests/unit/usb-snap-test.cpp +++ b/tests/unit/usb-snap-test.cpp @@ -163,7 +163,7 @@ TEST_F(UsbSnapFixture, TestRoundTrip) QVariantList() << id << test.action_to_invoke); // test that UsbSnap emits on_user_response() as a result - wait_for([&user_response_set](){return user_response_set;}); + wait_for([&user_response_set](){return user_response_set;}, 2000); EXPECT_TRUE(user_response_set); ASSERT_EQ(test.expected_response, user_response); -- cgit v1.2.3 From ee369babc9185bac7c7910a68a1e58bab7efa64c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 16:12:05 -0500 Subject: oops, last commit's diagnosis was incorrect. The timing test issue came from async dbus handling interfering with fast setup/teardown of automated tests. Revert the last change and fix by setting up the dbus signal subscription immediately upon getting the dbus connection. --- src/usb-snap.cpp | 22 +++++++++++----------- tests/unit/usb-snap-test.cpp | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/usb-snap.cpp b/src/usb-snap.cpp index 87f4673..c42f9f0 100644 --- a/src/usb-snap.cpp +++ b/src/usb-snap.cpp @@ -92,6 +92,17 @@ private: { m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(bus))); + m_subscription_id = g_dbus_connection_signal_subscribe(m_bus, + BUS_NAME, + IFACE_NAME, + nullptr, + OBJECT_PATH, + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + on_notification_signal_static, + this, + nullptr); + auto body = g_strdup_printf(_("The computer's RSA key fingerprint is: %s"), m_fingerprint.c_str()); GVariantBuilder actions_builder; @@ -151,17 +162,6 @@ private: void on_notify_reply(uint32_t id) { m_notification_id = id; - - m_subscription_id = g_dbus_connection_signal_subscribe(m_bus, - BUS_NAME, - IFACE_NAME, - nullptr, - OBJECT_PATH, - nullptr, - G_DBUS_SIGNAL_FLAGS_NONE, - on_notification_signal_static, - this, - nullptr); } static void on_notification_signal_static(GDBusConnection* /*connection*/, diff --git a/tests/unit/usb-snap-test.cpp b/tests/unit/usb-snap-test.cpp index 4300c47..84555cc 100644 --- a/tests/unit/usb-snap-test.cpp +++ b/tests/unit/usb-snap-test.cpp @@ -163,7 +163,7 @@ TEST_F(UsbSnapFixture, TestRoundTrip) QVariantList() << id << test.action_to_invoke); // test that UsbSnap emits on_user_response() as a result - wait_for([&user_response_set](){return user_response_set;}, 2000); + wait_for([&user_response_set](){return user_response_set;}); EXPECT_TRUE(user_response_set); ASSERT_EQ(test.expected_response, user_response); -- cgit v1.2.3 From 8ddb1713fa0816ea1c98ae039fea181d629acc7d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 17:09:47 -0500 Subject: listen on /dev/socket/adbd, not /dev/socket/adb --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 2e84afa..f195bda 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,7 +60,7 @@ g_assert(g_setenv("G_MESSAGES_DEBUG", "all", true)); // We need the ADBD handler running, // even though it doesn't have an indicator component yet - static constexpr char const * ADB_SOCKET_PATH {"/dev/socket/adb"}; + static constexpr char const * ADB_SOCKET_PATH {"/dev/socket/adbd"}; static constexpr char const * PUBLIC_KEYS_FILENAME {"/data/misc/adb/adb_keys"}; UsbManager usb_manager {ADB_SOCKET_PATH, PUBLIC_KEYS_FILENAME}; -- cgit v1.2.3 From 2b21545845221a841c11db2c3b4782f6893e72d7 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 19:25:33 -0500 Subject: use cmake's find_package(Threads) output everywhere instead of just in src/ --- CMakeLists.txt | 8 ++++++++ src/CMakeLists.txt | 8 +------- tests/integration/CMakeLists.txt | 2 +- tests/unit/CMakeLists.txt | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3230a2b..9302542 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,10 +32,18 @@ set(CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_P ## Check for prerequisites ## +# threads... set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) +if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 3.1) + set(THREAD_LINK_LIBRARIES -pthread) +else() + set(THREAD_LINK_LIBRARIES Threads::Threads) # introduced in cmake 3.1 +endif() + find_package(PkgConfig REQUIRED) +# glib... set(GLIB_MINIMUM 2.36) pkg_check_modules(SERVICE_DEPS REQUIRED gio-unix-2.0>=${GLIB_MINIMUM} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4fe7b1a..63c236d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,16 +22,10 @@ add_executable( main.cpp ) -if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 3.2) - set(SERVICE_THREAD_LIBS -pthread) -else() - set(SERVICE_THREAD_LIBS Threads::Threads) -endif() - target_link_libraries(${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} - ${SERVICE_THREAD_LIBS} + ${THREAD_LINK_LIBRARIES} ${GCOV_LIBS} ) diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 1eb7b3e..9ec6688 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -19,6 +19,6 @@ function(add_qt_test_by_name name) add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test(${TEST_NAME} ${TEST_NAME}) set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT}) - target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES}) + target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES} ${THREAD_LINK_LIBRARIES}) endfunction() add_qt_test_by_name(usb-manager-test) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 26f2cfb..fe70461 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -19,7 +19,7 @@ function(add_test_by_name name) add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test(${TEST_NAME} ${TEST_NAME}) set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT}) - target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES}) + target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES} ${THREAD_LINK_LIBRARIES}) endfunction() add_test_by_name(adbd-client-test) add_test_by_name(rotation-lock-test) @@ -29,6 +29,6 @@ function(add_qt_test_by_name name) add_executable (${TEST_NAME} ${TEST_NAME}.cpp) add_test(${TEST_NAME} ${TEST_NAME}) set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT}) - target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES}) + target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES} ${THREAD_LINK_LIBRARIES}) endfunction() add_qt_test_by_name(usb-snap-test) -- cgit v1.2.3 From 1e659cd3e96554c77cea09266f8b24fc153a186c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 15 Mar 2016 20:55:15 -0500 Subject: Remove the minimum version of 0.4 for libqtdbusmock1-dev, it's causing a failure on Jenkins wily and was only included due to copy-paste from indicator-network. So let's see how Wily goes without it. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 5088af5..9de530d 100644 --- a/debian/control +++ b/debian/control @@ -12,7 +12,7 @@ Build-Depends: cmake, # for tests qt5-default, qtbase5-dev, - libqtdbusmock1-dev (>= 0.4), + libqtdbusmock1-dev, libqtdbustest1-dev, cppcheck, libgtest-dev, -- cgit v1.2.3 From 9482a77503a72cbda7ca824c2ff9bd66b04098fb Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 16 Mar 2016 08:29:07 -0500 Subject: use cmake-extras' EnableCoverageReport instead of home-rolled rules --- CMakeLists.txt | 6 +++--- cmake/GCov.cmake | 51 ------------------------------------------------ cmake/GdbusCodegen.cmake | 36 ---------------------------------- src/CMakeLists.txt | 2 -- 4 files changed, 3 insertions(+), 92 deletions(-) delete mode 100644 cmake/GCov.cmake delete mode 100644 cmake/GdbusCodegen.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 9302542..bb7568e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ set(SERVICE_LIB ${PACKAGE}) set(SERVICE_EXEC "${PACKAGE}-service") option(enable_tests "Build the package's automatic tests." ON) -option(enable_lcov "Generate lcov code coverage reports." ON) +option(enable_coverage "Generate code coverage reports." ON) ## ## GNU standard paths @@ -74,8 +74,8 @@ add_compile_options(-std=c++14 -fPIC -g) if(${enable_tests}) enable_testing() - if(${enable_lcov}) - include(GCov) + if(${enable_coverage}) + include(EnableCoverageReport) endif() endif() diff --git a/cmake/GCov.cmake b/cmake/GCov.cmake deleted file mode 100644 index 81c0c40..0000000 --- a/cmake/GCov.cmake +++ /dev/null @@ -1,51 +0,0 @@ -if (CMAKE_BUILD_TYPE MATCHES coverage) - set(GCOV_FLAGS "${GCOV_FLAGS} --coverage") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCOV_FLAGS}") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${GCOV_FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GCOV_FLAGS}") - set(GCOV_LIBS ${GCOV_LIBS} gcov) - - find_program(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin") - if (NOT GCOVR_EXECUTABLE) - message(STATUS "Gcovr binary was not found, can not generate XML coverage info.") - else () - message(STATUS "Gcovr found, can generate XML coverage info.") - add_custom_target (coverage-xml - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND "${GCOVR_EXECUTABLE}" --exclude="test.*" -x -r "${CMAKE_SOURCE_DIR}" - --object-directory=${CMAKE_BINARY_DIR} -o coverage.xml) - endif() - - find_program(LCOV_EXECUTABLE lcov HINTS ${LCOV_ROOT} "${GCOVR_ROOT}/bin") - find_program(GENHTML_EXECUTABLE genhtml HINTS ${GENHTML_ROOT}) - if (NOT LCOV_EXECUTABLE) - message(STATUS "Lcov binary was not found, can not generate HTML coverage info.") - else () - if(NOT GENHTML_EXECUTABLE) - message(STATUS "Genthml binary not found, can not generate HTML coverage info.") - else() - message(STATUS "Lcov and genhtml found, can generate HTML coverage info.") - add_custom_target (coverage-html - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND "${CMAKE_CTEST_COMMAND}" --force-new-ctest-process --verbose - COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture | ${CMAKE_SOURCE_DIR}/trim-lcov.py > dconf-lcov.info - COMMAND "${LCOV_EXECUTABLE}" -r dconf-lcov.info /usr/include/\\* -o nosys-lcov.info - COMMAND LANG=C "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory lcov-html --legend --show-details nosys-lcov.info - COMMAND ${CMAKE_COMMAND} -E echo "" - COMMAND ${CMAKE_COMMAND} -E echo "file://${CMAKE_BINARY_DIR}/lcov-html/index.html" - COMMAND ${CMAKE_COMMAND} -E echo "") - #COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture --output-file coverage.info --no-checksum - #COMMAND "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info - #COMMAND ${CMAKE_COMMAND} -E echo "\\#define foo \\\"bar\\\"" - #) - endif() - endif() -endif() - - - #$(MAKE) $(AM_MAKEFLAGS) check - #lcov --directory $(top_builddir) --capture --test-name dconf | $(top_srcdir)/trim-lcov.py > dconf-lcov.info - #LANG=C genhtml --prefix $(top_builddir) --output-directory lcov-html --legend --show-details dconf-lcov.info - #@echo - #@echo " file://$(abs_top_builddir)/lcov-html/index.html" - #@echo diff --git a/cmake/GdbusCodegen.cmake b/cmake/GdbusCodegen.cmake deleted file mode 100644 index ddb2995..0000000 --- a/cmake/GdbusCodegen.cmake +++ /dev/null @@ -1,36 +0,0 @@ -cmake_minimum_required(VERSION 2.6) -if(POLICY CMP0011) - cmake_policy(SET CMP0011 NEW) -endif(POLICY CMP0011) - -find_program(GDBUS_CODEGEN NAMES gdbus-codegen DOC "gdbus-codegen executable") -if(NOT GDBUS_CODEGEN) - message(FATAL_ERROR "Excutable gdbus-codegen not found") -endif() - -macro(add_gdbus_codegen outfiles name prefix service_xml) - add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c" - COMMAND "${GDBUS_CODEGEN}" - --interface-prefix "${prefix}" - --generate-c-code "${name}" - "${service_xml}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${ARGN} "${service_xml}" - ) - list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c") -endmacro(add_gdbus_codegen) - -macro(add_gdbus_codegen_with_namespace outfiles name prefix namespace service_xml) - add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c" - COMMAND "${GDBUS_CODEGEN}" - --interface-prefix "${prefix}" - --generate-c-code "${name}" - --c-namespace "${namespace}" - "${service_xml}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${ARGN} "${service_xml}" - ) - list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c") -endmacro(add_gdbus_codegen_with_namespace) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 63c236d..d3a021b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,6 @@ add_definitions(-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}") add_compile_options( ${CXX_WARNING_ARGS} - ${GCOV_FLAGS} ) add_library( @@ -26,7 +25,6 @@ target_link_libraries(${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${THREAD_LINK_LIBRARIES} - ${GCOV_LIBS} ) install( -- cgit v1.2.3 From 60bcf370ae5f294aea4d3f2aded217b7786f2a68 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 16 Mar 2016 08:40:57 -0500 Subject: in POTFILES.in, fix copy-paste typo --- po/POTFILES.in | 1 - 1 file changed, 1 deletion(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 6b34951..339765f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,3 +1,2 @@ src/rotation-lock.cpp src/usb-snap.cpp -~ -- cgit v1.2.3 From 078459b6c837264eb2d6b45a84a1641a3aeab2cc Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 16 Mar 2016 09:04:26 -0500 Subject: in usb-manager.cpp, remove the remember_choice workaround since is already working around it --- src/usb-manager.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index 6b40cea..f089a22 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -41,12 +41,11 @@ public: { m_adbd_client->on_pk_request().connect([this](const AdbdClient::PKRequest& req){ auto snap = new UsbSnap(req.fingerprint); - snap->on_user_response().connect([this,req,snap](AdbdClient::PKResponse response, bool /*FIXME: remember_choice*/){ + snap->on_user_response().connect([this,req,snap](AdbdClient::PKResponse response, bool remember_choice){ req.respond(response); - if (response == AdbdClient::PKResponse::ALLOW) + if (remember_choice) write_public_key(req.public_key); - - // delete later + // delete_later g_idle_add([](gpointer gsnap){delete static_cast(gsnap); return G_SOURCE_REMOVE;}, snap); }); }); -- cgit v1.2.3 From 3ef931c4bc8d87497dbe9f6ceda9ce4ca3bb116f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 16 Mar 2016 09:04:44 -0500 Subject: tweak class description comments --- src/usb-manager.h | 3 +++ src/usb-snap.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/usb-manager.h b/src/usb-manager.h index 28a27f3..ec405c0 100644 --- a/src/usb-manager.h +++ b/src/usb-manager.h @@ -22,6 +22,9 @@ #include #include +/** + * Manager class that connects the AdbdClient, UsbSnap, and manages the public key file + */ class UsbManager { public: diff --git a/src/usb-snap.h b/src/usb-snap.h index 6ad3a4c..94de394 100644 --- a/src/usb-snap.h +++ b/src/usb-snap.h @@ -26,6 +26,9 @@ #include #include +/** + * A snap decision prompt for whether or not to allow an ADB connection + */ class UsbSnap { public: -- cgit v1.2.3 From 65eba5e107b0499aec4bed55be202bf1df6710bb Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 17 Mar 2016 09:25:23 -0500 Subject: add tests/utils/qdbus-helpers.h so that we only define qDBusArgumentToMap() in one place --- tests/integration/usb-manager-test.cpp | 17 ++--------------- tests/unit/usb-snap-test.cpp | 15 +-------------- tests/utils/qdbus-helpers.h | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 29 deletions(-) create mode 100644 tests/utils/qdbus-helpers.h diff --git a/tests/integration/usb-manager-test.cpp b/tests/integration/usb-manager-test.cpp index aca5325..5e3377d 100644 --- a/tests/integration/usb-manager-test.cpp +++ b/tests/integration/usb-manager-test.cpp @@ -20,9 +20,10 @@ #define QT_NO_KEYWORDS #include -#include #include +#include #include +#include #include @@ -78,20 +79,6 @@ protected: delete s; } - bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) - { - if (variant.canConvert()) - { - QDBusArgument value(variant.value()); - if (value.currentType() == QDBusArgument::MapType) - { - value >> map; - return true; - } - } - return false; - } - void SetUp() override { super::SetUp(); diff --git a/tests/unit/usb-snap-test.cpp b/tests/unit/usb-snap-test.cpp index 84555cc..e8f8bb2 100644 --- a/tests/unit/usb-snap-test.cpp +++ b/tests/unit/usb-snap-test.cpp @@ -19,6 +19,7 @@ #define QT_NO_KEYWORDS #include +#include #include #include @@ -64,20 +65,6 @@ public: protected: - bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) - { - if (variant.canConvert()) - { - QDBusArgument value(variant.value()); - if (value.currentType() == QDBusArgument::MapType) - { - value >> map; - return true; - } - } - return false; - } - void SetUp() override { super::SetUp(); diff --git a/tests/utils/qdbus-helpers.h b/tests/utils/qdbus-helpers.h new file mode 100644 index 0000000..f873e23 --- /dev/null +++ b/tests/utils/qdbus-helpers.h @@ -0,0 +1,21 @@ +#pragma once + +#define QT_NO_KEYWORDS +#include +#include + +bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) +{ + if (variant.canConvert()) + { + QDBusArgument value(variant.value()); + if (value.currentType() == QDBusArgument::MapType) + { + value >> map; + return true; + } + } + + return false; +} + -- cgit v1.2.3 From c63d90da0f1d9cbd1eee5dd66a9828c51cc8dcc9 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 17 Mar 2016 09:59:32 -0500 Subject: de-dupe use of dbus names --- src/dbus-names.h | 42 ++++++++++++++++++++++++++++++++++ src/usb-snap.cpp | 34 ++++++++++++--------------- tests/integration/usb-manager-test.cpp | 13 ++++++----- tests/unit/usb-snap-test.cpp | 13 ++++++----- tests/utils/dbus-types.h | 5 ---- 5 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 src/dbus-names.h diff --git a/src/dbus-names.h b/src/dbus-names.h new file mode 100644 index 0000000..753b8c8 --- /dev/null +++ b/src/dbus-names.h @@ -0,0 +1,42 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +namespace DBusNames +{ + namespace Notify + { + static constexpr char const * NAME = "org.freedesktop.Notifications"; + static constexpr char const * PATH = "/org/freedesktop/Notifications"; + static constexpr char const * INTERFACE = "org.freedesktop.Notifications"; + + namespace ActionInvoked + { + static constexpr char const * NAME = "ActionInvoked"; + } + + namespace NotificationClosed + { + static constexpr char const * NAME = "NotificationClosed"; + enum Reason { EXPIRED=1, DISMISSED=2, API=3, UNDEFINED=4 }; + } + } +} + diff --git a/src/usb-snap.cpp b/src/usb-snap.cpp index c42f9f0..41c78c6 100644 --- a/src/usb-snap.cpp +++ b/src/usb-snap.cpp @@ -17,6 +17,7 @@ * Charles Kerr */ +#include #include #include @@ -48,9 +49,9 @@ public: if (m_notification_id != 0) { GError* error {}; g_dbus_connection_call_sync(m_bus, - BUS_NAME, - OBJECT_PATH, - IFACE_NAME, + DBusNames::Notify::NAME, + DBusNames::Notify::PATH, + DBusNames::Notify::INTERFACE, "CloseNotification", g_variant_new("(u)", m_notification_id), nullptr, @@ -93,10 +94,10 @@ private: m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(bus))); m_subscription_id = g_dbus_connection_signal_subscribe(m_bus, - BUS_NAME, - IFACE_NAME, + DBusNames::Notify::NAME, + DBusNames::Notify::INTERFACE, nullptr, - OBJECT_PATH, + DBusNames::Notify::PATH, nullptr, G_DBUS_SIGNAL_FLAGS_NONE, on_notification_signal_static, @@ -128,9 +129,9 @@ private: &hints_builder, -1); g_dbus_connection_call(m_bus, - BUS_NAME, - OBJECT_PATH, - IFACE_NAME, + DBusNames::Notify::NAME, + DBusNames::Notify::PATH, + DBusNames::Notify::INTERFACE, "Notify", args, G_VARIANT_TYPE("(u)"), @@ -172,12 +173,12 @@ private: GVariant* parameters, gpointer gself) { - g_return_if_fail(!g_strcmp0(object_path, OBJECT_PATH)); - g_return_if_fail(!g_strcmp0(interface_name, IFACE_NAME)); + g_return_if_fail(!g_strcmp0(object_path, DBusNames::Notify::PATH)); + g_return_if_fail(!g_strcmp0(interface_name, DBusNames::Notify::INTERFACE)); auto self = static_cast(gself); - if (!g_strcmp0(signal_name, "ActionInvoked")) + if (!g_strcmp0(signal_name, DBusNames::Notify::ActionInvoked::NAME)) { uint32_t id {}; const char* action_name {}; @@ -185,7 +186,7 @@ private: if (id == self->m_notification_id) self->on_action_invoked(action_name); } - else if (!g_strcmp0(signal_name, "NotificationClosed")) + else if (!g_strcmp0(signal_name, DBusNames::Notify::NotificationClosed::NAME)) { uint32_t id {}; uint32_t close_reason {}; @@ -211,7 +212,7 @@ private: void on_notification_closed(uint32_t close_reason) { - if (close_reason == CloseReason::EXPIRED) + if (close_reason == DBusNames::Notify::NotificationClosed::Reason::EXPIRED) m_on_user_response(AdbdClient::PKResponse::DENY, false); m_notification_id = 0; @@ -220,11 +221,6 @@ private: static constexpr char const * ACTION_ALLOW {"allow"}; static constexpr char const * ACTION_DENY {"deny"}; - static constexpr char const * BUS_NAME {"org.freedesktop.Notifications" }; - static constexpr char const * IFACE_NAME {"org.freedesktop.Notifications" }; - static constexpr char const * OBJECT_PATH {"/org/freedesktop/Notifications" }; - enum CloseReason { EXPIRED=1, DISMISSED=2, API=3, UNDEFINED=4 }; - const std::string m_fingerprint; core::Signal m_on_user_response; GCancellable* m_cancellable {}; diff --git a/tests/integration/usb-manager-test.cpp b/tests/integration/usb-manager-test.cpp index 5e3377d..82c170e 100644 --- a/tests/integration/usb-manager-test.cpp +++ b/tests/integration/usb-manager-test.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -93,10 +94,10 @@ protected: OrgFreedesktopDBusMockInterface& notificationsMockInterface() { - return dbusMock.mockInterface("org.freedesktop.Notifications", - "/org/freedesktop/Notifications", - "org.freedesktop.Notifications", - QDBusConnection::SessionBus); + return dbusMock.mockInterface(DBusNames::Notify::NAME, + DBusNames::Notify::PATH, + DBusNames::Notify::INTERFACE, + QDBusConnection::SessionBus); } QtDBusTest::DBusTestRunner dbusTestRunner; @@ -151,8 +152,8 @@ TEST_F(UsbManagerFixture, Allow) // click on allow in the notification notificationsMockInterface().EmitSignal( - DBusTypes::NOTIFY_DBUS_INTERFACE, - "ActionInvoked", + DBusNames::Notify::INTERFACE, + DBusNames::Notify::ActionInvoked::NAME, "us", QVariantList() << uint32_t(1) << "allow" ); diff --git a/tests/unit/usb-snap-test.cpp b/tests/unit/usb-snap-test.cpp index e8f8bb2..dc17696 100644 --- a/tests/unit/usb-snap-test.cpp +++ b/tests/unit/usb-snap-test.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -75,10 +76,10 @@ protected: OrgFreedesktopDBusMockInterface& notificationsMockInterface() { - return dbusMock.mockInterface("org.freedesktop.Notifications", - "/org/freedesktop/Notifications", - "org.freedesktop.Notifications", - QDBusConnection::SessionBus); + return dbusMock.mockInterface(DBusNames::Notify::NAME, + DBusNames::Notify::PATH, + DBusNames::Notify::INTERFACE, + QDBusConnection::SessionBus); } QtDBusTest::DBusTestRunner dbusTestRunner; @@ -144,8 +145,8 @@ TEST_F(UsbSnapFixture, TestRoundTrip) // fake a user interaction with the fdo notification notificationsMockInterface().EmitSignal( - DBusTypes::NOTIFY_DBUS_INTERFACE, - "ActionInvoked", + DBusNames::Notify::INTERFACE, + DBusNames::Notify::ActionInvoked::NAME, "us", QVariantList() << id << test.action_to_invoke); diff --git a/tests/utils/dbus-types.h b/tests/utils/dbus-types.h index c2dfb81..3b3a02d 100644 --- a/tests/utils/dbus-types.h +++ b/tests/utils/dbus-types.h @@ -39,9 +39,4 @@ namespace DBusTypes qDBusRegisterMetaType(); qDBusRegisterMetaType(); } - static constexpr char const* NOTIFY_DBUS_NAME = "org.freedesktop.Notifications"; - - static constexpr char const* NOTIFY_DBUS_INTERFACE = "org.freedesktop.Notifications"; - - static constexpr char const* NOTIFY_DBUS_PATH = "/org/freedesktop/Notifications"; } -- cgit v1.2.3 From 5b6008fa3b5a9b0373b2f03791140ca1fa8dc8de Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 17 Mar 2016 10:24:41 -0500 Subject: introduce a QtFixture gtest base class to reduce redundancy in test fixtures' helper/util code --- tests/integration/usb-manager-test.cpp | 31 +++--------------- tests/unit/adbd-client-test.cpp | 2 +- tests/unit/usb-snap-test.cpp | 34 +++----------------- tests/utils/qt-fixture.h | 58 ++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 56 deletions(-) create mode 100644 tests/utils/qt-fixture.h diff --git a/tests/integration/usb-manager-test.cpp b/tests/integration/usb-manager-test.cpp index 82c170e..21fdc97 100644 --- a/tests/integration/usb-manager-test.cpp +++ b/tests/integration/usb-manager-test.cpp @@ -20,10 +20,7 @@ #define QT_NO_KEYWORDS #include -#include -#include -#include -#include +#include #include #include @@ -32,12 +29,6 @@ #include #include -#include - -#include - -#include - #include #include #include @@ -46,26 +37,15 @@ **** ***/ -#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ -{\ - while (signalSpy.size() < signalsExpected)\ - {\ - ASSERT_TRUE(signalSpy.wait());\ - }\ - ASSERT_EQ(signalsExpected, signalSpy.size());\ -} - -class UsbManagerFixture: public GlibFixture +class UsbManagerFixture: public QtFixture { - using super = GlibFixture; + using super = QtFixture; public: UsbManagerFixture(): dbusMock{dbusTestRunner} { - DBusTypes::registerMetaTypes(); - dbusTestRunner.startServices(); } @@ -76,7 +56,7 @@ protected: static void file_deleter (std::string* s) { fprintf(stderr, "remove \"%s\"\n", s->c_str()); - remove(s->c_str()); + g_remove(s->c_str()); delete s; } @@ -102,7 +82,6 @@ protected: QtDBusTest::DBusTestRunner dbusTestRunner; QtDBusMock::DBusMock dbusMock; - QtDBusTest::DBusServicePtr indicator; std::shared_ptr m_tmpdir; }; @@ -126,7 +105,7 @@ TEST_F(UsbManagerFixture, Allow) auto usb_manager = std::make_shared(*socket_path, *public_keys_path); // wait for the notification to show up, confirm it looks right - WAIT_FOR_SIGNALS(notificationsSpy, 1); + wait_for_signals(notificationsSpy, 1); { QVariantList const& call(notificationsSpy.at(0)); EXPECT_EQ("Notify", call.at(0)); diff --git a/tests/unit/adbd-client-test.cpp b/tests/unit/adbd-client-test.cpp index c00c07a..754f76c 100644 --- a/tests/unit/adbd-client-test.cpp +++ b/tests/unit/adbd-client-test.cpp @@ -32,7 +32,7 @@ protected: static void file_deleter (std::string* s) { fprintf(stderr, "remove \"%s\"\n", s->c_str()); - remove(s->c_str()); + g_remove(s->c_str()); delete s; } diff --git a/tests/unit/usb-snap-test.cpp b/tests/unit/usb-snap-test.cpp index dc17696..663f9e6 100644 --- a/tests/unit/usb-snap-test.cpp +++ b/tests/unit/usb-snap-test.cpp @@ -18,47 +18,24 @@ */ #define QT_NO_KEYWORDS -#include -#include -#include -#include +#include #include #include -#include - #include #include #include -#include - -#include - -using namespace QtDBusTest; -using namespace QtDBusMock; - -#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ -{\ - while (signalSpy.size() < signalsExpected)\ - {\ - ASSERT_TRUE(signalSpy.wait());\ - }\ - ASSERT_EQ(signalsExpected, signalSpy.size());\ -} - -class UsbSnapFixture: public GlibFixture +class UsbSnapFixture: public QtFixture { - using super = GlibFixture; + using super = QtFixture; public: UsbSnapFixture(): dbusMock{dbusTestRunner} { - DBusTypes::registerMetaTypes(); - dbusTestRunner.startServices(); } @@ -84,7 +61,6 @@ protected: QtDBusTest::DBusTestRunner dbusTestRunner; QtDBusMock::DBusMock dbusMock; - QtDBusTest::DBusServicePtr indicator; }; TEST_F(UsbSnapFixture, TestRoundTrip) @@ -119,7 +95,7 @@ TEST_F(UsbSnapFixture, TestRoundTrip) }); // test that UsbSnap creates a fdo notification - WAIT_FOR_SIGNALS(notificationsSpy, 1); + wait_for_signals(notificationsSpy, 1); { QVariantList const& call(notificationsSpy.at(0)); EXPECT_EQ("Notify", call.at(0)); @@ -157,7 +133,7 @@ TEST_F(UsbSnapFixture, TestRoundTrip) // confirm that the snap dtor cleans up the notification snap.reset(); - WAIT_FOR_SIGNALS(notificationsSpy, 1); + wait_for_signals(notificationsSpy, 1); { QVariantList const& call(notificationsSpy.at(0)); EXPECT_EQ("CloseNotification", call.at(0)); diff --git a/tests/utils/qt-fixture.h b/tests/utils/qt-fixture.h new file mode 100644 index 0000000..321d56e --- /dev/null +++ b/tests/utils/qt-fixture.h @@ -0,0 +1,58 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#define QT_NO_KEYWORDS + +#include +#include +#include +#include + +#include + +#include + +class QtFixture: public GlibFixture +{ + using super = GlibFixture; + +public: + + QtFixture() + { + DBusTypes::registerMetaTypes(); + } + + ~QtFixture() =default; + +protected: + + void wait_for_signals(QSignalSpy& signalSpy, int signalsExpected) + { + while (signalSpy.size() < signalsExpected) + { + ASSERT_TRUE(signalSpy.wait()); + } + + ASSERT_EQ(signalsExpected, signalSpy.size()); + } +}; + -- cgit v1.2.3 From b7d7f15ab25708c6ee7a3c22389246e07fd6183d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 17 Mar 2016 13:27:12 -0500 Subject: sync with lp:~ubuntu-branches/ubuntu/wily/indicator-display/wily/ to pick up 0.1+15.10.20150727-0ubuntu2~gcc5.1 --- .bzr-builddeb/default.conf | 2 -- debian/changelog | 6 ++++++ debian/control | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 .bzr-builddeb/default.conf diff --git a/.bzr-builddeb/default.conf b/.bzr-builddeb/default.conf deleted file mode 100644 index 6c96a98..0000000 --- a/.bzr-builddeb/default.conf +++ /dev/null @@ -1,2 +0,0 @@ -[BUILDDEB] -split = True diff --git a/debian/changelog b/debian/changelog index 9d66cb6..d2e5b8e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +indicator-display (0.1+15.10.20150727-0ubuntu2~gcc5.1) wily; urgency=medium + + * No-change test rebuild for g++5 ABI transition + + -- Steve Langasek Mon, 27 Jul 2015 22:57:15 +0000 + indicator-display (0.1+15.10.20150727-0ubuntu1) wily; urgency=medium [ CI Train Bot ] diff --git a/debian/control b/debian/control index 9de530d..529fa37 100644 --- a/debian/control +++ b/debian/control @@ -1,7 +1,8 @@ Source: indicator-display Section: misc Priority: optional -Maintainer: Charles Kerr +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Charles Kerr Build-Depends: cmake, cmake-extras (>= 0.4), dbus, -- cgit v1.2.3 From ff5be6a08a602e8a4454cbfcd8eeb38e28db3e1f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 17 Mar 2016 13:29:09 -0500 Subject: fix warning message --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index f195bda..2e428f9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,7 +29,7 @@ int main(int /*argc*/, char** /*argv*/) { -#warning temp +#warning NB the next line turns on verbose debug logging and is used for developement. Remove it before landing. g_assert(g_setenv("G_MESSAGES_DEBUG", "all", true)); // Work around a deadlock in glib's type initialization. -- cgit v1.2.3 From f55775498feffedcd0ef717a50913f41429a2d1a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 17 Mar 2016 14:35:39 -0500 Subject: re-add .bzr-builddeb, which got removed by accident --- .bzr-builddeb/default.conf | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .bzr-builddeb/default.conf diff --git a/.bzr-builddeb/default.conf b/.bzr-builddeb/default.conf new file mode 100644 index 0000000..6c96a98 --- /dev/null +++ b/.bzr-builddeb/default.conf @@ -0,0 +1,2 @@ +[BUILDDEB] +split = True -- cgit v1.2.3 From ccecdd46da33ff51b2d45528439de09fe87a393c Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 18 Mar 2016 10:42:41 -0500 Subject: add some extra debug statements to usb-manager.cpp to track user response and the act of writing the pk out to disk --- src/usb-manager.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index f089a22..335db00 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -42,8 +42,9 @@ public: m_adbd_client->on_pk_request().connect([this](const AdbdClient::PKRequest& req){ auto snap = new UsbSnap(req.fingerprint); snap->on_user_response().connect([this,req,snap](AdbdClient::PKResponse response, bool remember_choice){ + g_debug("user responded! response %d, remember %d", int(response), int(remember_choice)); req.respond(response); - if (remember_choice) + if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) write_public_key(req.public_key); // delete_later g_idle_add([](gpointer gsnap){delete static_cast(gsnap); return G_SOURCE_REMOVE;}, snap); @@ -59,6 +60,8 @@ private: void write_public_key(const std::string& public_key) { + g_debug("writing public key '%s' to '%s'", public_key.c_str(), m_public_keys_filename.c_str()); + // confirm the directory exists auto dirname = g_path_get_dirname(m_public_keys_filename.c_str()); const auto dir_exists = g_file_test(dirname, G_FILE_TEST_IS_DIR); -- cgit v1.2.3 From 45709c48f34e0909c1309dccac1dd3e047f518fb Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 18 Mar 2016 12:39:58 -0500 Subject: turn off verbose debugging --- src/main.cpp | 3 --- src/usb-manager.cpp | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2e428f9..7d6eb5f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,9 +29,6 @@ int main(int /*argc*/, char** /*argv*/) { -#warning NB the next line turns on verbose debug logging and is used for developement. Remove it before landing. -g_assert(g_setenv("G_MESSAGES_DEBUG", "all", true)); - // Work around a deadlock in glib's type initialization. // It can be removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is fixed. g_type_ensure(G_TYPE_DBUS_CONNECTION); diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index 335db00..7f43520 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -42,7 +42,7 @@ public: m_adbd_client->on_pk_request().connect([this](const AdbdClient::PKRequest& req){ auto snap = new UsbSnap(req.fingerprint); snap->on_user_response().connect([this,req,snap](AdbdClient::PKResponse response, bool remember_choice){ - g_debug("user responded! response %d, remember %d", int(response), int(remember_choice)); + g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); req.respond(response); if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) write_public_key(req.public_key); @@ -78,12 +78,12 @@ private: S_IRUSR|S_IWUSR|S_IRGRP ); if (fd == -1) { - g_warning("Error opening ADB datafile: %s", g_strerror(errno)); + g_warning("Error opening ADB datafile '%s': %s", m_public_keys_filename.c_str(), g_strerror(errno)); return; } // write the new public key on its own line - std::string buf {public_key + '\n'}; + const std::string buf {public_key + '\n'}; if (write(fd, buf.c_str(), buf.size()) == -1) g_warning("Error writing ADB datafile: %d %s", errno, g_strerror(errno)); close(fd); -- cgit v1.2.3 From 7a25132c125f6e5e413ad26ea950ae22bee982f5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 21 Mar 2016 13:40:11 -0500 Subject: if our USB device is disconnected while prompting the user for ADBD, cancel the prompt. --- CMakeLists.txt | 1 + debian/control | 1 + src/CMakeLists.txt | 1 + src/adbd-client.cpp | 1 + src/main.cpp | 4 +- src/usb-manager.cpp | 63 +++++++++++++++++--------- src/usb-manager.h | 11 ++++- src/usb-monitor.cpp | 81 ++++++++++++++++++++++++++++++++++ src/usb-monitor.h | 52 ++++++++++++++++++++++ src/usb-snap.cpp | 1 + tests/integration/usb-manager-test.cpp | 44 ++++++++++++++++-- tests/unit/usb-snap-test.cpp | 1 - tests/utils/mock-usb-monitor.h | 32 ++++++++++++++ tests/utils/qdbus-helpers.h | 21 --------- tests/utils/qt-fixture.h | 18 +++++++- 15 files changed, 284 insertions(+), 48 deletions(-) create mode 100644 src/usb-monitor.cpp create mode 100644 src/usb-monitor.h create mode 100644 tests/utils/mock-usb-monitor.h delete mode 100644 tests/utils/qdbus-helpers.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bb7568e..8a1a6aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ set(GLIB_MINIMUM 2.36) pkg_check_modules(SERVICE_DEPS REQUIRED gio-unix-2.0>=${GLIB_MINIMUM} glib-2.0>=${GLIB_MINIMUM} + gudev-1.0 ) include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS} diff --git a/debian/control b/debian/control index 529fa37..90e2590 100644 --- a/debian/control +++ b/debian/control @@ -7,6 +7,7 @@ Build-Depends: cmake, cmake-extras (>= 0.4), dbus, libglib2.0-dev (>= 2.36), + libgudev-1.0-dev, libproperties-cpp-dev, # for coverage reports lcov, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d3a021b..cdd2384 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,6 +13,7 @@ add_library( indicator.cpp rotation-lock.cpp usb-manager.cpp + usb-monitor.cpp usb-snap.cpp ) diff --git a/src/adbd-client.cpp b/src/adbd-client.cpp index 4f7d28f..937215e 100644 --- a/src/adbd-client.cpp +++ b/src/adbd-client.cpp @@ -45,6 +45,7 @@ public: { // tell the worker thread to stop whatever it's doing and exit. g_cancellable_cancel(m_cancellable); + m_pkresponse_cv.notify_one(); m_sleep_cv.notify_one(); m_worker_thread.join(); g_clear_object(&m_cancellable); diff --git a/src/main.cpp b/src/main.cpp index 7d6eb5f..27e6bcc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include // bindtextdomain() #include @@ -59,7 +60,8 @@ main(int /*argc*/, char** /*argv*/) // even though it doesn't have an indicator component yet static constexpr char const * ADB_SOCKET_PATH {"/dev/socket/adbd"}; static constexpr char const * PUBLIC_KEYS_FILENAME {"/data/misc/adb/adb_keys"}; - UsbManager usb_manager {ADB_SOCKET_PATH, PUBLIC_KEYS_FILENAME}; + auto usb_monitor = std::make_shared(); + UsbManager usb_manager {ADB_SOCKET_PATH, PUBLIC_KEYS_FILENAME, usb_monitor}; // let's go! g_main_loop_run(loop); diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index 7f43520..840a04b 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -28,39 +28,57 @@ #include #include +#include + class UsbManager::Impl { public: explicit Impl( const std::string& socket_path, - const std::string& public_keys_filename + const std::string& public_keys_filename, + const std::shared_ptr& usb_monitor ): m_adbd_client{std::make_shared(socket_path)}, - m_public_keys_filename{public_keys_filename} + m_public_keys_filename{public_keys_filename}, + m_usb_monitor{usb_monitor} { - m_adbd_client->on_pk_request().connect([this](const AdbdClient::PKRequest& req){ - auto snap = new UsbSnap(req.fingerprint); - snap->on_user_response().connect([this,req,snap](AdbdClient::PKResponse response, bool remember_choice){ - g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); - req.respond(response); - if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) - write_public_key(req.public_key); - // delete_later - g_idle_add([](gpointer gsnap){delete static_cast(gsnap); return G_SOURCE_REMOVE;}, snap); - }); + m_usb_monitor->on_usb_disconnected().connect([this](const std::string& /*usb_name*/) { + m_snap.reset(); }); - } - ~Impl() - { + m_adbd_client->on_pk_request().connect( + [this](const AdbdClient::PKRequest& req){ + + m_snap.reset(new UsbSnap(req.fingerprint), + [this](UsbSnap* snap){ + m_snap_connections.clear(); + delete snap; + } + ); + + m_snap_connections.insert((*m_snap).on_user_response().connect( + [this,req](AdbdClient::PKResponse response, bool remember_choice){ + g_message("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); + req.respond(response); + g_message("%s", G_STRLOC); + if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) + write_public_key(req.public_key); + g_idle_add([](gpointer gself){static_cast(gself)->m_snap.reset(); return G_SOURCE_REMOVE;}, this); + } + )); + } + ); + } + ~Impl() =default; + private: void write_public_key(const std::string& public_key) { - g_debug("writing public key '%s' to '%s'", public_key.c_str(), m_public_keys_filename.c_str()); + g_message("%s writing public key '%s' to '%s'", G_STRLOC, public_key.c_str(), m_public_keys_filename.c_str()); // confirm the directory exists auto dirname = g_path_get_dirname(m_public_keys_filename.c_str()); @@ -78,12 +96,12 @@ private: S_IRUSR|S_IWUSR|S_IRGRP ); if (fd == -1) { - g_warning("Error opening ADB datafile '%s': %s", m_public_keys_filename.c_str(), g_strerror(errno)); + g_warning("Error opening ADB datafile: %s", g_strerror(errno)); return; } // write the new public key on its own line - const std::string buf {public_key + '\n'}; + std::string buf {public_key + '\n'}; if (write(fd, buf.c_str(), buf.size()) == -1) g_warning("Error writing ADB datafile: %d %s", errno, g_strerror(errno)); close(fd); @@ -91,6 +109,10 @@ private: std::shared_ptr m_adbd_client; const std::string m_public_keys_filename; + std::shared_ptr m_usb_monitor; + + std::shared_ptr m_snap; + std::set m_snap_connections; }; /*** @@ -99,9 +121,10 @@ private: UsbManager::UsbManager( const std::string& socket_path, - const std::string& public_keys_filename + const std::string& public_keys_filename, + const std::shared_ptr& usb_monitor ): - impl{new Impl{socket_path, public_keys_filename}} + impl{new Impl{socket_path, public_keys_filename, usb_monitor}} { } diff --git a/src/usb-manager.h b/src/usb-manager.h index ec405c0..960d634 100644 --- a/src/usb-manager.h +++ b/src/usb-manager.h @@ -19,6 +19,8 @@ #pragma once +#include + #include #include @@ -28,10 +30,17 @@ class UsbManager { public: - UsbManager(const std::string& socket_path, const std::string& public_key_filename); + + UsbManager( + const std::string& socket_path, + const std::string& public_key_filename, + const std::shared_ptr& + ); + ~UsbManager(); protected: + class Impl; std::unique_ptr impl; }; diff --git a/src/usb-monitor.cpp b/src/usb-monitor.cpp new file mode 100644 index 0000000..5fc5a6d --- /dev/null +++ b/src/usb-monitor.cpp @@ -0,0 +1,81 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include + +#include +#include + +class GUDevUsbMonitor::Impl +{ +public: + + Impl() + { + const char* subsystems[] = {"android_usb", nullptr}; + m_udev_client = g_udev_client_new(subsystems); + g_signal_connect(m_udev_client, "uevent", G_CALLBACK(on_android_usb_event), this); + } + + ~Impl() + { + g_signal_handlers_disconnect_by_data(m_udev_client, this); + g_clear_object(&m_udev_client); + } + + core::Signal& on_usb_disconnected() + { + return m_on_usb_disconnected; + } + +private: + + static void on_android_usb_event(GUdevClient*, gchar* action, GUdevDevice* device, gpointer gself) + { + if (!g_strcmp0(action, "change")) + if (!g_strcmp0(g_udev_device_get_property(device, "USB_STATE"), "DISCONNECTED")) + static_cast(gself)->m_on_usb_disconnected(g_udev_device_get_name(device)); + } + + core::Signal m_on_usb_disconnected; + + GUdevClient* m_udev_client = nullptr; +}; + +/*** +**** +***/ + +UsbMonitor::UsbMonitor() =default; + +UsbMonitor::~UsbMonitor() =default; + +GUDevUsbMonitor::GUDevUsbMonitor(): + impl{new Impl{}} +{ +} + +GUDevUsbMonitor::~GUDevUsbMonitor() =default; + +core::Signal& +GUDevUsbMonitor::on_usb_disconnected() +{ + return impl->on_usb_disconnected(); +} + diff --git a/src/usb-monitor.h b/src/usb-monitor.h new file mode 100644 index 0000000..d9be539 --- /dev/null +++ b/src/usb-monitor.h @@ -0,0 +1,52 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#include + +#include +#include + +/** + * Simple interface that emits signals on USB device state changes + */ +class UsbMonitor +{ +public: + UsbMonitor(); + virtual ~UsbMonitor(); + virtual core::Signal& on_usb_disconnected() =0; +}; + +/** + * Simple GUDev wrapper that notifies on android_usb device state changes + */ +class GUDevUsbMonitor: public UsbMonitor +{ +public: + GUDevUsbMonitor(); + virtual ~GUDevUsbMonitor(); + core::Signal& on_usb_disconnected() override; + +protected: + class Impl; + std::unique_ptr impl; +}; + diff --git a/src/usb-snap.cpp b/src/usb-snap.cpp index 41c78c6..349d80e 100644 --- a/src/usb-snap.cpp +++ b/src/usb-snap.cpp @@ -148,6 +148,7 @@ private: { GError* error {}; auto reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION(obus), res, &error); +g_message("%s got notify response %s", G_STRLOC, g_variant_print(reply, true)); if (error != nullptr) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning("UsbSnap: Error calling Notify: %s", error->message); diff --git a/tests/integration/usb-manager-test.cpp b/tests/integration/usb-manager-test.cpp index 21fdc97..19c0401 100644 --- a/tests/integration/usb-manager-test.cpp +++ b/tests/integration/usb-manager-test.cpp @@ -17,10 +17,9 @@ * Charles Kerr */ -#define QT_NO_KEYWORDS - #include #include +#include #include #include @@ -64,6 +63,8 @@ protected: { super::SetUp(); + m_usb_monitor.reset(new MockUsbMonitor{}); + char tmpl[] = {"usb-manager-test-XXXXXX"}; m_tmpdir.reset(new std::string{g_mkdtemp(tmpl)}, file_deleter); g_message("using tmpdir '%s'", m_tmpdir->c_str()); @@ -83,6 +84,7 @@ protected: QtDBusTest::DBusTestRunner dbusTestRunner; QtDBusMock::DBusMock dbusMock; std::shared_ptr m_tmpdir; + std::shared_ptr m_usb_monitor; }; TEST_F(UsbManagerFixture, Allow) @@ -102,7 +104,7 @@ TEST_F(UsbManagerFixture, Allow) auto adbd_server = std::make_shared(*socket_path, std::vector{"PK"+public_key}); // set up a UsbManager to process the request - auto usb_manager = std::make_shared(*socket_path, *public_keys_path); + auto usb_manager = std::make_shared(*socket_path, *public_keys_path, m_usb_monitor); // wait for the notification to show up, confirm it looks right wait_for_signals(notificationsSpy, 1); @@ -151,3 +153,39 @@ TEST_F(UsbManagerFixture, Allow) ASSERT_EQ(1, lines.size()); EXPECT_EQ(public_key, lines[0]); } + +TEST_F(UsbManagerFixture, Cancel) +{ + const std::shared_ptr socket_path {new std::string{*m_tmpdir+"/socket"}, file_deleter}; + const std::shared_ptr public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter}; + + // add a signal spy to listen to the notification daemon + QSignalSpy notificationsSpy( + ¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &)) + ); + + // start a mock AdbdServer ready to submit a request + const std::string public_key {"public_key"}; + auto adbd_server = std::make_shared(*socket_path, std::vector{"PK"+public_key}); + + // set up a UsbManager to process the request + auto usb_manager = std::make_shared(*socket_path, *public_keys_path, m_usb_monitor); + + // wait for a notification to show up + wait_for_signals(notificationsSpy, 1); + EXPECT_EQ("Notify", notificationsSpy.at(0).at(0)); + notificationsSpy.clear(); + + // wait for UsbSnap to receive dbusmock's response to the Notify request. + // there's no event to key off of for this, so just wait for a moment + wait_msec(); + + // disconnect the USB before the user has a chance to allow/deny + m_usb_monitor->m_on_usb_disconnected("android0"); + + // confirm that we requested the notification to be pulled down + wait_for_signals(notificationsSpy, 1); + EXPECT_EQ("CloseNotification", notificationsSpy.at(0).at(0)); + notificationsSpy.clear(); +} diff --git a/tests/unit/usb-snap-test.cpp b/tests/unit/usb-snap-test.cpp index 663f9e6..40de94a 100644 --- a/tests/unit/usb-snap-test.cpp +++ b/tests/unit/usb-snap-test.cpp @@ -17,7 +17,6 @@ * Charles Kerr */ -#define QT_NO_KEYWORDS #include #include diff --git a/tests/utils/mock-usb-monitor.h b/tests/utils/mock-usb-monitor.h new file mode 100644 index 0000000..92b89db --- /dev/null +++ b/tests/utils/mock-usb-monitor.h @@ -0,0 +1,32 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#include + +class MockUsbMonitor: public UsbMonitor +{ +public: + MockUsbMonitor() =default; + virtual ~MockUsbMonitor() =default; + core::Signal& on_usb_disconnected() override {return m_on_usb_disconnected;} + core::Signal m_on_usb_disconnected; +}; + diff --git a/tests/utils/qdbus-helpers.h b/tests/utils/qdbus-helpers.h deleted file mode 100644 index f873e23..0000000 --- a/tests/utils/qdbus-helpers.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#define QT_NO_KEYWORDS -#include -#include - -bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) -{ - if (variant.canConvert()) - { - QDBusArgument value(variant.value()); - if (value.currentType() == QDBusArgument::MapType) - { - value >> map; - return true; - } - } - - return false; -} - diff --git a/tests/utils/qt-fixture.h b/tests/utils/qt-fixture.h index 321d56e..0f5722b 100644 --- a/tests/utils/qt-fixture.h +++ b/tests/utils/qt-fixture.h @@ -22,12 +22,13 @@ #define QT_NO_KEYWORDS #include -#include #include #include #include +#include +#include #include class QtFixture: public GlibFixture @@ -54,5 +55,20 @@ protected: ASSERT_EQ(signalsExpected, signalSpy.size()); } + + bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) + { + if (variant.canConvert()) + { + QDBusArgument value(variant.value()); + if (value.currentType() == QDBusArgument::MapType) + { + value >> map; + return true; + } + } + + return false; + } }; -- cgit v1.2.3 From 1c4f005f0765f460b28808a624fbec7737324b1a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 21 Mar 2016 17:05:01 -0500 Subject: in UsbManager, reset AdbdClient on usb disconnect --- src/usb-manager.cpp | 44 ++++++++++++++++++++++++++------------------ src/usb-snap.cpp | 1 - 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index 840a04b..f5957d9 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -39,29 +39,40 @@ public: const std::string& public_keys_filename, const std::shared_ptr& usb_monitor ): - m_adbd_client{std::make_shared(socket_path)}, + m_socket_path{socket_path}, m_public_keys_filename{public_keys_filename}, m_usb_monitor{usb_monitor} { m_usb_monitor->on_usb_disconnected().connect([this](const std::string& /*usb_name*/) { - m_snap.reset(); + restart(); }); + restart(); + } + + ~Impl() =default; + +private: + + void restart() + { + // clear out old state + m_snap_connections.clear(); + m_snap.reset(); + m_adbd_client.reset(); + + // add a new client + m_adbd_client.reset(new GAdbdClient{m_socket_path}); m_adbd_client->on_pk_request().connect( - [this](const AdbdClient::PKRequest& req){ + [this](const AdbdClient::PKRequest& req) { - m_snap.reset(new UsbSnap(req.fingerprint), - [this](UsbSnap* snap){ - m_snap_connections.clear(); - delete snap; - } - ); + g_debug("%s got pk request", G_STRLOC); + m_snap = std::make_shared(req.fingerprint); m_snap_connections.insert((*m_snap).on_user_response().connect( [this,req](AdbdClient::PKResponse response, bool remember_choice){ - g_message("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); + g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); req.respond(response); - g_message("%s", G_STRLOC); if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) write_public_key(req.public_key); g_idle_add([](gpointer gself){static_cast(gself)->m_snap.reset(); return G_SOURCE_REMOVE;}, this); @@ -69,16 +80,11 @@ public: )); } ); - } - ~Impl() =default; - -private: - void write_public_key(const std::string& public_key) { - g_message("%s writing public key '%s' to '%s'", G_STRLOC, public_key.c_str(), m_public_keys_filename.c_str()); + g_debug("%s writing public key '%s' to '%s'", G_STRLOC, public_key.c_str(), m_public_keys_filename.c_str()); // confirm the directory exists auto dirname = g_path_get_dirname(m_public_keys_filename.c_str()); @@ -107,10 +113,12 @@ private: close(fd); } - std::shared_ptr m_adbd_client; + const std::string m_socket_path; const std::string m_public_keys_filename; + std::shared_ptr m_usb_monitor; + std::shared_ptr m_adbd_client; std::shared_ptr m_snap; std::set m_snap_connections; }; diff --git a/src/usb-snap.cpp b/src/usb-snap.cpp index 349d80e..41c78c6 100644 --- a/src/usb-snap.cpp +++ b/src/usb-snap.cpp @@ -148,7 +148,6 @@ private: { GError* error {}; auto reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION(obus), res, &error); -g_message("%s got notify response %s", G_STRLOC, g_variant_print(reply, true)); if (error != nullptr) { if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning("UsbSnap: Error calling Notify: %s", error->message); -- cgit v1.2.3 From ecf802d7c939fcc73838d19de546294bc1c89e33 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 21 Mar 2016 18:15:05 -0500 Subject: add Greeter skeleton --- src/CMakeLists.txt | 1 + src/dbus-names.h | 7 +++ src/greeter.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/greeter.h | 47 ++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 src/greeter.cpp create mode 100644 src/greeter.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cdd2384..060071d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,6 +10,7 @@ add_library( STATIC adbd-client.cpp exporter.cpp + greeter.cpp indicator.cpp rotation-lock.cpp usb-manager.cpp diff --git a/src/dbus-names.h b/src/dbus-names.h index 753b8c8..3127b9f 100644 --- a/src/dbus-names.h +++ b/src/dbus-names.h @@ -38,5 +38,12 @@ namespace DBusNames enum Reason { EXPIRED=1, DISMISSED=2, API=3, UNDEFINED=4 }; } } + + namespace UnityGreeter + { + static constexpr char const * NAME = "com.canonical.UnityGreeter"; + static constexpr char const * PATH = "/"; + static constexpr char const * INTERFACE = "com.canonical.UnityGreeter"; + } } diff --git a/src/greeter.cpp b/src/greeter.cpp new file mode 100644 index 0000000..351b870 --- /dev/null +++ b/src/greeter.cpp @@ -0,0 +1,123 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#include +#include + +#include + +class UnityGreeter::Impl +{ +public: + + Impl(): + m_cancellable{g_cancellable_new()} + { + g_bus_get(G_BUS_TYPE_SESSION, m_cancellable, on_bus_ready_static, this); + } + + ~Impl() + { + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); + + if (m_subscription_id != 0) + g_dbus_connection_signal_unsubscribe (m_bus, m_subscription_id); + + g_clear_object(&m_bus); + } + + core::Property& is_active() + { + return m_is_active; + } + +private: + + static void on_bus_ready_static(GObject* /*source*/, GAsyncResult* res, gpointer gself) + { + GError* error {}; + auto bus = g_bus_get_finish (res, &error); + if (error != nullptr) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("UsbSnap: Error getting session bus: %s", error->message); + g_clear_error(&error); + } else { + static_cast(gself)->on_bus_ready(bus); + } + g_clear_object(&bus); + } + + void on_bus_ready(GDBusConnection* bus) + { + m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(bus))); + + m_subscription_id = g_dbus_connection_signal_subscribe(m_bus, + DBusNames::UnityGreeter::NAME, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + DBusNames::UnityGreeter::PATH, + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + on_properties_changed_signal_static, + this, + nullptr); + } + + static void on_properties_changed_signal_static(GDBusConnection* /*connection*/, + const gchar* sender_name, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer gself) + { + g_return_if_fail(!g_strcmp0(sender_name, DBusNames::UnityGreeter::NAME)); + g_return_if_fail(!g_strcmp0(object_path, DBusNames::UnityGreeter::PATH)); + g_return_if_fail(!g_strcmp0(interface_name, "org.freedesktop.DBus.Properties")); + g_return_if_fail(!g_strcmp0(signal_name, "PropertiesChanged")); + + static_cast(gself)->on_properties_changed_signal(parameters); + } + + void on_properties_changed_signal(GVariant* parameters) + { + g_message("%s %s", G_STRLOC, g_variant_print(parameters, true)); + } + + core::Property m_is_active; + GCancellable* m_cancellable {}; + GDBusConnection* m_bus {}; + unsigned int m_subscription_id {}; +}; + +/*** +**** +***/ + +Greeter::Greeter() =default; + +Greeter::~Greeter() =default; + +UnityGreeter::~UnityGreeter() =default; + +UnityGreeter::UnityGreeter(): + impl{new Impl{}} +{ +} diff --git a/src/greeter.h b/src/greeter.h new file mode 100644 index 0000000..e084d25 --- /dev/null +++ b/src/greeter.h @@ -0,0 +1,47 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#include + +#include +#include + +class Greeter +{ +public: + Greeter(); + virtual ~Greeter(); + virtual core::Property& is_active() =0; +}; + + +class UnityGreeter: public Greeter +{ +public: + UnityGreeter(); + virtual ~UnityGreeter(); + core::Property& is_active() override; + +protected: + class Impl; + std::unique_ptr impl; +}; + -- cgit v1.2.3 From 70209f30bc6907ad545e33dc6e7de78cf63b9e9a Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 21 Mar 2016 19:33:35 -0500 Subject: get greeter's IsActive property working --- src/dbus-names.h | 11 ++++++++ src/greeter.cpp | 80 +++++++++++++++++++++++++++++++++++++++++--------------- src/main.cpp | 3 +++ 3 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/dbus-names.h b/src/dbus-names.h index 3127b9f..b31098a 100644 --- a/src/dbus-names.h +++ b/src/dbus-names.h @@ -45,5 +45,16 @@ namespace DBusNames static constexpr char const * PATH = "/"; static constexpr char const * INTERFACE = "com.canonical.UnityGreeter"; } + + namespace Properties + { + static constexpr char const * INTERFACE = "org.freedesktop.DBus.Properties"; + + namespace PropertiesChanged + { + static constexpr char const* NAME = "PropertiesChanged"; + static constexpr char const* ARGS_VARIANT_TYPE = "(sa{sv}as)"; + } + } } diff --git a/src/greeter.cpp b/src/greeter.cpp index 351b870..cffa376 100644 --- a/src/greeter.cpp +++ b/src/greeter.cpp @@ -68,37 +68,69 @@ private: { m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(bus))); + g_dbus_connection_call(m_bus, + DBusNames::UnityGreeter::NAME, + DBusNames::UnityGreeter::PATH, + DBusNames::Properties::INTERFACE, + "Get", + g_variant_new("(ss)", DBusNames::UnityGreeter::INTERFACE, "IsActive"), + G_VARIANT_TYPE("(v)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + m_cancellable, + on_get_is_active_ready, + this); + m_subscription_id = g_dbus_connection_signal_subscribe(m_bus, DBusNames::UnityGreeter::NAME, - "org.freedesktop.DBus.Properties", - "PropertiesChanged", + DBusNames::Properties::INTERFACE, + DBusNames::Properties::PropertiesChanged::NAME, DBusNames::UnityGreeter::PATH, - nullptr, + DBusNames::UnityGreeter::INTERFACE, G_DBUS_SIGNAL_FLAGS_NONE, - on_properties_changed_signal_static, + on_properties_changed_signal, this, nullptr); } - static void on_properties_changed_signal_static(GDBusConnection* /*connection*/, - const gchar* sender_name, - const gchar* object_path, - const gchar* interface_name, - const gchar* signal_name, - GVariant* parameters, - gpointer gself) + static void on_get_is_active_ready(GObject* source, GAsyncResult* res, gpointer gself) { - g_return_if_fail(!g_strcmp0(sender_name, DBusNames::UnityGreeter::NAME)); - g_return_if_fail(!g_strcmp0(object_path, DBusNames::UnityGreeter::PATH)); - g_return_if_fail(!g_strcmp0(interface_name, "org.freedesktop.DBus.Properties")); - g_return_if_fail(!g_strcmp0(signal_name, "PropertiesChanged")); - - static_cast(gself)->on_properties_changed_signal(parameters); + GError* error {}; + auto v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error); + if (error != nullptr) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning("UsbSnap: Error getting session bus: %s", error->message); + g_clear_error(&error); + } else { + GVariant* is_active {}; + g_variant_get_child(v, 0, "v", &is_active_v); + static_cast(gself)->m_is_active.set(g_variant_get_boolean(is_active); + g_clear_pointer(&is_active, g_variant_unref); + } + g_clear_pointer(&v, g_variant_unref); } - void on_properties_changed_signal(GVariant* parameters) + static void on_properties_changed_signal(GDBusConnection* /*connection*/, + const gchar* /*sender_name*/, + const gchar* object_path, + const gchar* interface_name, + const gchar* signal_name, + GVariant* parameters, + gpointer gself) { - g_message("%s %s", G_STRLOC, g_variant_print(parameters, true)); + g_return_if_fail(!g_strcmp0(object_path, DBusNames::UnityGreeter::PATH)); + g_return_if_fail(!g_strcmp0(interface_name, DBusNames::Properties::INTERFACE)); + g_return_if_fail(!g_strcmp0(signal_name, DBusNames::Properties::PropertiesChanged::NAME)); + g_return_if_fail(g_variant_is_of_type(parameters, G_VARIANT_TYPE(DBusNames::Properties::PropertiesChanged::ARGS_VARIANT_TYPE))); + + auto v = g_variant_get_child_value (parameters, 1); + gboolean is_active {}; + if (g_variant_lookup(v, "IsActive", "b", &is_active)) + { + g_debug("%s is_active changed to %d", G_STRLOC, int(is_active)); + static_cast(gself)->m_is_active.set(is_active); + } + g_clear_pointer(&v, g_variant_unref); } core::Property m_is_active; @@ -115,9 +147,15 @@ Greeter::Greeter() =default; Greeter::~Greeter() =default; -UnityGreeter::~UnityGreeter() =default; - UnityGreeter::UnityGreeter(): impl{new Impl{}} { } + +UnityGreeter::~UnityGreeter() =default; + +core::Property& +UnityGreeter::is_active() +{ + return impl->is_active(); +} diff --git a/src/main.cpp b/src/main.cpp index 27e6bcc..6c111f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,8 @@ #include #include + +#include #include #include @@ -61,6 +63,7 @@ main(int /*argc*/, char** /*argv*/) static constexpr char const * ADB_SOCKET_PATH {"/dev/socket/adbd"}; static constexpr char const * PUBLIC_KEYS_FILENAME {"/data/misc/adb/adb_keys"}; auto usb_monitor = std::make_shared(); + auto greeter = std::make_shared(); UsbManager usb_manager {ADB_SOCKET_PATH, PUBLIC_KEYS_FILENAME, usb_monitor}; // let's go! -- cgit v1.2.3 From cc2e2265ada413826e199248dee90a19db31b741 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Mar 2016 08:15:38 -0500 Subject: fix typo --- src/greeter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/greeter.cpp b/src/greeter.cpp index cffa376..f9cd965 100644 --- a/src/greeter.cpp +++ b/src/greeter.cpp @@ -103,8 +103,8 @@ private: g_clear_error(&error); } else { GVariant* is_active {}; - g_variant_get_child(v, 0, "v", &is_active_v); - static_cast(gself)->m_is_active.set(g_variant_get_boolean(is_active); + g_variant_get_child(v, 0, "v", &is_active); + static_cast(gself)->m_is_active.set(g_variant_get_boolean(is_active)); g_clear_pointer(&is_active, g_variant_unref); } g_clear_pointer(&v, g_variant_unref); -- cgit v1.2.3 From 0b9a8d08b2ea7d245ffd00ce465e6c859905c547 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Mar 2016 15:34:00 -0500 Subject: make wait_for_signals() a macro again so that the GTest log messages will give the test file and line number --- tests/utils/qt-fixture.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/utils/qt-fixture.h b/tests/utils/qt-fixture.h index 0f5722b..95b9b14 100644 --- a/tests/utils/qt-fixture.h +++ b/tests/utils/qt-fixture.h @@ -31,6 +31,16 @@ #include #include +#define wait_for_signals(signalSpy,signalsExpected) \ +{ \ + while (signalSpy.size() < signalsExpected) \ + { \ + ASSERT_TRUE(signalSpy.wait()); \ + } \ + \ + ASSERT_EQ(signalsExpected, signalSpy.size()); \ +} + class QtFixture: public GlibFixture { using super = GlibFixture; @@ -46,16 +56,6 @@ public: protected: - void wait_for_signals(QSignalSpy& signalSpy, int signalsExpected) - { - while (signalSpy.size() < signalsExpected) - { - ASSERT_TRUE(signalSpy.wait()); - } - - ASSERT_EQ(signalsExpected, signalSpy.size()); - } - bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) { if (variant.canConvert()) -- cgit v1.2.3 From ba426a05796244070d9cae1e18d1885bed261b55 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Mar 2016 15:34:55 -0500 Subject: in the mock ADB server, keep making a request until a response is received. --- tests/utils/adbd-server.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/utils/adbd-server.h b/tests/utils/adbd-server.h index 740faaa..968c1f0 100644 --- a/tests/utils/adbd-server.h +++ b/tests/utils/adbd-server.h @@ -80,7 +80,6 @@ private: // 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()); @@ -114,7 +113,10 @@ private: } 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); + if (!response.empty()) { + m_responses.push_back(response); + requests.erase(requests.begin()); + } // cleanup g_clear_object(&client_socket); -- cgit v1.2.3 From 952552b8a92cdec3c03fec81711211d8c82c984f Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Mar 2016 15:39:19 -0500 Subject: add multiple usb requests + disconnects to confirm the notification appears on subsequent requests --- tests/integration/usb-manager-test.cpp | 57 +++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/tests/integration/usb-manager-test.cpp b/tests/integration/usb-manager-test.cpp index 19c0401..3ce79d0 100644 --- a/tests/integration/usb-manager-test.cpp +++ b/tests/integration/usb-manager-test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -64,6 +65,7 @@ protected: super::SetUp(); m_usb_monitor.reset(new MockUsbMonitor{}); + m_greeter.reset(new MockGreeter{}); char tmpl[] = {"usb-manager-test-XXXXXX"}; m_tmpdir.reset(new std::string{g_mkdtemp(tmpl)}, file_deleter); @@ -85,6 +87,7 @@ protected: QtDBusMock::DBusMock dbusMock; std::shared_ptr m_tmpdir; std::shared_ptr m_usb_monitor; + std::shared_ptr m_greeter; }; TEST_F(UsbManagerFixture, Allow) @@ -104,7 +107,7 @@ TEST_F(UsbManagerFixture, Allow) auto adbd_server = std::make_shared(*socket_path, std::vector{"PK"+public_key}); // set up a UsbManager to process the request - auto usb_manager = std::make_shared(*socket_path, *public_keys_path, m_usb_monitor); + auto usb_manager = std::make_shared(*socket_path, *public_keys_path, m_usb_monitor, m_greeter); // wait for the notification to show up, confirm it looks right wait_for_signals(notificationsSpy, 1); @@ -154,38 +157,42 @@ TEST_F(UsbManagerFixture, Allow) EXPECT_EQ(public_key, lines[0]); } -TEST_F(UsbManagerFixture, Cancel) +TEST_F(UsbManagerFixture, AndroidInterruptus) { const std::shared_ptr socket_path {new std::string{*m_tmpdir+"/socket"}, file_deleter}; const std::shared_ptr public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter}; - // add a signal spy to listen to the notification daemon - QSignalSpy notificationsSpy( - ¬ificationsMockInterface(), - SIGNAL(MethodCalled(const QString &, const QVariantList &)) - ); - // start a mock AdbdServer ready to submit a request const std::string public_key {"public_key"}; auto adbd_server = std::make_shared(*socket_path, std::vector{"PK"+public_key}); // set up a UsbManager to process the request - auto usb_manager = std::make_shared(*socket_path, *public_keys_path, m_usb_monitor); + auto usb_manager = std::make_shared(*socket_path, *public_keys_path, m_usb_monitor, m_greeter); - // wait for a notification to show up - wait_for_signals(notificationsSpy, 1); - EXPECT_EQ("Notify", notificationsSpy.at(0).at(0)); - notificationsSpy.clear(); - - // wait for UsbSnap to receive dbusmock's response to the Notify request. - // there's no event to key off of for this, so just wait for a moment - wait_msec(); - - // disconnect the USB before the user has a chance to allow/deny - m_usb_monitor->m_on_usb_disconnected("android0"); - - // confirm that we requested the notification to be pulled down - wait_for_signals(notificationsSpy, 1); - EXPECT_EQ("CloseNotification", notificationsSpy.at(0).at(0)); - notificationsSpy.clear(); + for (int i=0; i<3; i++) + { +g_message("i %d", i); + // add a signal spy to listen to the notification daemon + QSignalSpy notificationsSpy( + ¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &)) + ); + + // wait for a notification to show up + wait_for_signals(notificationsSpy, 1); + EXPECT_EQ("Notify", notificationsSpy.at(0).at(0)); + notificationsSpy.clear(); + + // wait for UsbSnap to receive dbusmock's response to the Notify request. + // there's no event to key off of for this, so just wait for a moment + wait_msec(); + + // disconnect the USB before the user has a chance to allow/deny + m_usb_monitor->m_on_usb_disconnected("android0"); + + // confirm that we requested the notification to be pulled down + wait_for_signals(notificationsSpy, 1); + EXPECT_EQ("CloseNotification", notificationsSpy.at(0).at(0)); + notificationsSpy.clear(); + } } -- cgit v1.2.3 From a5f330f6b73101d7bbdeadc6a5f53b8da3349999 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Mar 2016 15:39:36 -0500 Subject: don't show the snap decision until we're out of the greeter --- src/main.cpp | 2 +- src/usb-manager.cpp | 76 +++++++++++++++++++++++++++++++++------------- src/usb-manager.h | 4 ++- tests/utils/mock-greeter.h | 32 +++++++++++++++++++ 4 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 tests/utils/mock-greeter.h diff --git a/src/main.cpp b/src/main.cpp index 6c111f1..52cdd58 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,7 +64,7 @@ main(int /*argc*/, char** /*argv*/) static constexpr char const * PUBLIC_KEYS_FILENAME {"/data/misc/adb/adb_keys"}; auto usb_monitor = std::make_shared(); auto greeter = std::make_shared(); - UsbManager usb_manager {ADB_SOCKET_PATH, PUBLIC_KEYS_FILENAME, usb_monitor}; + UsbManager usb_manager {ADB_SOCKET_PATH, PUBLIC_KEYS_FILENAME, usb_monitor, greeter}; // let's go! g_main_loop_run(loop); diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index f5957d9..0e59ca2 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -37,51 +37,83 @@ public: explicit Impl( const std::string& socket_path, const std::string& public_keys_filename, - const std::shared_ptr& usb_monitor + const std::shared_ptr& usb_monitor, + const std::shared_ptr& greeter ): m_socket_path{socket_path}, m_public_keys_filename{public_keys_filename}, - m_usb_monitor{usb_monitor} + m_usb_monitor{usb_monitor}, + m_greeter{greeter} { m_usb_monitor->on_usb_disconnected().connect([this](const std::string& /*usb_name*/) { restart(); }); + m_greeter->is_active().changed().connect([this](bool /*is_active*/) { + maybe_snap_now(); + }); + restart(); } - ~Impl() =default; + ~Impl() + { + clear(); + } private: - void restart() + void clear() { // clear out old state m_snap_connections.clear(); m_snap.reset(); + m_req = AdbdClient::PKRequest{}; m_adbd_client.reset(); + } - // add a new client + void restart() + { + clear(); + + // set a new client m_adbd_client.reset(new GAdbdClient{m_socket_path}); m_adbd_client->on_pk_request().connect( [this](const AdbdClient::PKRequest& req) { - g_debug("%s got pk request", G_STRLOC); - - m_snap = std::make_shared(req.fingerprint); - m_snap_connections.insert((*m_snap).on_user_response().connect( - [this,req](AdbdClient::PKResponse response, bool remember_choice){ - g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); - req.respond(response); - if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) - write_public_key(req.public_key); - g_idle_add([](gpointer gself){static_cast(gself)->m_snap.reset(); return G_SOURCE_REMOVE;}, this); - } - )); + m_req = req; + maybe_snap_now(); } ); } + bool ready_to_snap() + { + return !m_greeter->is_active().get() && !m_req.public_key.empty(); + } + + void maybe_snap_now() + { + if (ready_to_snap()) + snap_now(); + } + + void snap_now() + { + g_return_if_fail(ready_to_snap()); + + m_snap = std::make_shared(m_req.fingerprint); + m_snap_connections.insert((*m_snap).on_user_response().connect( + [this](AdbdClient::PKResponse response, bool remember_choice){ + g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); + m_req.respond(response); + if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) + write_public_key(m_req.public_key); + g_idle_add([](gpointer gself){static_cast(gself)->restart(); return G_SOURCE_REMOVE;}, this); + } + )); + } + void write_public_key(const std::string& public_key) { g_debug("%s writing public key '%s' to '%s'", G_STRLOC, public_key.c_str(), m_public_keys_filename.c_str()); @@ -115,10 +147,11 @@ private: const std::string m_socket_path; const std::string m_public_keys_filename; - - std::shared_ptr m_usb_monitor; + const std::shared_ptr m_usb_monitor; + const std::shared_ptr m_greeter; std::shared_ptr m_adbd_client; + AdbdClient::PKRequest m_req; std::shared_ptr m_snap; std::set m_snap_connections; }; @@ -130,9 +163,10 @@ private: UsbManager::UsbManager( const std::string& socket_path, const std::string& public_keys_filename, - const std::shared_ptr& usb_monitor + const std::shared_ptr& usb_monitor, + const std::shared_ptr& greeter ): - impl{new Impl{socket_path, public_keys_filename, usb_monitor}} + impl{new Impl{socket_path, public_keys_filename, usb_monitor, greeter}} { } diff --git a/src/usb-manager.h b/src/usb-manager.h index 960d634..b93992f 100644 --- a/src/usb-manager.h +++ b/src/usb-manager.h @@ -19,6 +19,7 @@ #pragma once +#include #include #include @@ -34,7 +35,8 @@ public: UsbManager( const std::string& socket_path, const std::string& public_key_filename, - const std::shared_ptr& + const std::shared_ptr&, + const std::shared_ptr& ); ~UsbManager(); diff --git a/tests/utils/mock-greeter.h b/tests/utils/mock-greeter.h new file mode 100644 index 0000000..5ac85a0 --- /dev/null +++ b/tests/utils/mock-greeter.h @@ -0,0 +1,32 @@ +/* + * 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 . + * + * Authors: + * Charles Kerr + */ + +#pragma once + +#include + +class MockGreeter: public Greeter +{ +public: + MockGreeter() =default; + virtual ~MockGreeter() =default; + core::Property& is_active() override {return m_is_active;} + core::Property m_is_active {false}; +}; + -- cgit v1.2.3 From 4f8a17f23a0bba7da7654c147ca377e271abe0db Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Tue, 22 Mar 2016 16:01:47 -0500 Subject: add tests for not showing snap decisions in greeter mode --- src/usb-manager.cpp | 48 ++++++++++++---------------------- tests/integration/usb-manager-test.cpp | 34 +++++++++++++++++++++--- tests/utils/adbd-server.h | 8 +++--- 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index 0e59ca2..4fdfdc1 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -50,7 +50,7 @@ public: }); m_greeter->is_active().changed().connect([this](bool /*is_active*/) { - maybe_snap_now(); + restart(); }); restart(); @@ -68,7 +68,6 @@ private: // clear out old state m_snap_connections.clear(); m_snap.reset(); - m_req = AdbdClient::PKRequest{}; m_adbd_client.reset(); } @@ -76,44 +75,30 @@ private: { clear(); + // don't prompt in the greeter! + if (m_greeter->is_active().get()) + return; + // set a new client +g_message("creating a new adbd client"); m_adbd_client.reset(new GAdbdClient{m_socket_path}); m_adbd_client->on_pk_request().connect( [this](const AdbdClient::PKRequest& req) { g_debug("%s got pk request", G_STRLOC); - m_req = req; - maybe_snap_now(); + m_snap = std::make_shared(req.fingerprint); + m_snap_connections.insert((*m_snap).on_user_response().connect( + [this,req](AdbdClient::PKResponse response, bool remember_choice){ + g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); + req.respond(response); + if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) + write_public_key(req.public_key); + g_idle_add([](gpointer gself){static_cast(gself)->restart(); return G_SOURCE_REMOVE;}, this); + } + )); } ); } - bool ready_to_snap() - { - return !m_greeter->is_active().get() && !m_req.public_key.empty(); - } - - void maybe_snap_now() - { - if (ready_to_snap()) - snap_now(); - } - - void snap_now() - { - g_return_if_fail(ready_to_snap()); - - m_snap = std::make_shared(m_req.fingerprint); - m_snap_connections.insert((*m_snap).on_user_response().connect( - [this](AdbdClient::PKResponse response, bool remember_choice){ - g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); - m_req.respond(response); - if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) - write_public_key(m_req.public_key); - g_idle_add([](gpointer gself){static_cast(gself)->restart(); return G_SOURCE_REMOVE;}, this); - } - )); - } - void write_public_key(const std::string& public_key) { g_debug("%s writing public key '%s' to '%s'", G_STRLOC, public_key.c_str(), m_public_keys_filename.c_str()); @@ -151,7 +136,6 @@ private: const std::shared_ptr m_greeter; std::shared_ptr m_adbd_client; - AdbdClient::PKRequest m_req; std::shared_ptr m_snap; std::set m_snap_connections; }; diff --git a/tests/integration/usb-manager-test.cpp b/tests/integration/usb-manager-test.cpp index 3ce79d0..03c80c7 100644 --- a/tests/integration/usb-manager-test.cpp +++ b/tests/integration/usb-manager-test.cpp @@ -87,7 +87,7 @@ protected: QtDBusMock::DBusMock dbusMock; std::shared_ptr m_tmpdir; std::shared_ptr m_usb_monitor; - std::shared_ptr m_greeter; + std::shared_ptr m_greeter; }; TEST_F(UsbManagerFixture, Allow) @@ -157,7 +157,7 @@ TEST_F(UsbManagerFixture, Allow) EXPECT_EQ(public_key, lines[0]); } -TEST_F(UsbManagerFixture, AndroidInterruptus) +TEST_F(UsbManagerFixture, USBDisconnectedDuringPrompt) { const std::shared_ptr socket_path {new std::string{*m_tmpdir+"/socket"}, file_deleter}; const std::shared_ptr public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter}; @@ -171,7 +171,6 @@ TEST_F(UsbManagerFixture, AndroidInterruptus) for (int i=0; i<3; i++) { -g_message("i %d", i); // add a signal spy to listen to the notification daemon QSignalSpy notificationsSpy( ¬ificationsMockInterface(), @@ -196,3 +195,32 @@ g_message("i %d", i); notificationsSpy.clear(); } } + +TEST_F(UsbManagerFixture, Greeter) +{ + const std::shared_ptr socket_path {new std::string{*m_tmpdir+"/socket"}, file_deleter}; + const std::shared_ptr public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter}; + + // start a mock AdbdServer ready to submit a request + const std::string public_key {"public_key"}; + auto adbd_server = std::make_shared(*socket_path, std::vector{"PK"+public_key}); + + // set up a UsbManager to process the request + m_greeter->m_is_active.set(true); + auto usb_manager = std::make_shared(*socket_path, *public_keys_path, m_usb_monitor, m_greeter); + + // add a signal spy to listen to the notification daemon + QSignalSpy notificationsSpy( + ¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &)) + ); + + // the greeter is active, so the notification should not appear + EXPECT_FALSE(notificationsSpy.wait(2000)); + + // disable the greeter, the notification should appear + m_greeter->m_is_active.set(false); + wait_for_signals(notificationsSpy, 1); + EXPECT_EQ("Notify", notificationsSpy.at(0).at(0)); + notificationsSpy.clear(); +} diff --git a/tests/utils/adbd-server.h b/tests/utils/adbd-server.h index 968c1f0..5e66379 100644 --- a/tests/utils/adbd-server.h +++ b/tests/utils/adbd-server.h @@ -73,7 +73,7 @@ private: 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_message("GAdbdServer: Error accepting socket connection: %s", error->message); g_clear_error(&error); break; } @@ -90,7 +90,7 @@ private: &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_message("GAdbdServer: Error sending request: %s", error->message); g_clear_error(&error); g_clear_object(&client_socket); break; @@ -106,10 +106,10 @@ private: &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_message("GAdbdServer: Error reading response: %s", error->message); g_clear_error(&error); g_clear_object(&client_socket); - break; + continue; } const std::string response(buf, std::string::size_type(n_bytes)); g_message("server got response: %s", response.c_str()); -- cgit v1.2.3 From 82588108a40fb50b2bbd3c7b89b990f76f488edc Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Mar 2016 12:16:06 -0500 Subject: replace text 'Deny' with 'Don't Allow' for consistency with other permission prompts --- src/usb-snap.cpp | 2 +- tests/integration/usb-manager-test.cpp | 2 +- tests/unit/usb-snap-test.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/usb-snap.cpp b/src/usb-snap.cpp index 41c78c6..ba964fb 100644 --- a/src/usb-snap.cpp +++ b/src/usb-snap.cpp @@ -111,7 +111,7 @@ private: g_variant_builder_add(&actions_builder, "s", ACTION_ALLOW); g_variant_builder_add(&actions_builder, "s", _("Allow")); g_variant_builder_add(&actions_builder, "s", ACTION_DENY); - g_variant_builder_add(&actions_builder, "s", _("Deny")); + g_variant_builder_add(&actions_builder, "s", _("Don't Allow")); GVariantBuilder hints_builder; g_variant_builder_init(&hints_builder, G_VARIANT_TYPE_VARDICT); diff --git a/tests/integration/usb-manager-test.cpp b/tests/integration/usb-manager-test.cpp index 03c80c7..d62756f 100644 --- a/tests/integration/usb-manager-test.cpp +++ b/tests/integration/usb-manager-test.cpp @@ -122,7 +122,7 @@ TEST_F(UsbManagerFixture, Allow) EXPECT_EQ("computer-symbolic", args.at(2)); // icon name EXPECT_EQ("Allow USB Debugging?", args.at(3)); // summary EXPECT_EQ(QString::fromUtf8("The computer's RSA key fingerprint is: ") + QString::fromUtf8(fingerprint.c_str()), args.at(4)); // body - EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Deny"}), args.at(5)); // actions + EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Don't Allow"}), args.at(5)); // actions EXPECT_EQ(-1, args.at(7)); QVariantMap hints; diff --git a/tests/unit/usb-snap-test.cpp b/tests/unit/usb-snap-test.cpp index 40de94a..3b778dd 100644 --- a/tests/unit/usb-snap-test.cpp +++ b/tests/unit/usb-snap-test.cpp @@ -106,7 +106,7 @@ TEST_F(UsbSnapFixture, TestRoundTrip) EXPECT_EQ("computer-symbolic", args.at(2)); // icon name EXPECT_EQ("Allow USB Debugging?", args.at(3)); // summary EXPECT_EQ(QString::fromUtf8("The computer's RSA key fingerprint is: ") + test.fingerprint, args.at(4)); // body - EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Deny"}), args.at(5)); // actions + EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Don't Allow"}), args.at(5)); // actions EXPECT_EQ(-1, args.at(7)); QVariantMap hints; -- cgit v1.2.3 From ccc831d425006a803ca64c5525d38fef8912aac5 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Mar 2016 14:02:48 -0500 Subject: keep the adbd socket open even when the lockscreen is closed. hold the pkrequest state in USBManager until the screen's unlocked. --- src/usb-manager.cpp | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index 4fdfdc1..656b6a0 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -50,7 +50,7 @@ public: }); m_greeter->is_active().changed().connect([this](bool /*is_active*/) { - restart(); + maybe_snap(); }); restart(); @@ -68,6 +68,7 @@ private: // clear out old state m_snap_connections.clear(); m_snap.reset(); + m_req = decltype(m_req){}; m_adbd_client.reset(); } @@ -75,30 +76,38 @@ private: { clear(); - // don't prompt in the greeter! - if (m_greeter->is_active().get()) - return; - // set a new client -g_message("creating a new adbd client"); m_adbd_client.reset(new GAdbdClient{m_socket_path}); m_adbd_client->on_pk_request().connect( [this](const AdbdClient::PKRequest& req) { - g_debug("%s got pk request", G_STRLOC); - m_snap = std::make_shared(req.fingerprint); - m_snap_connections.insert((*m_snap).on_user_response().connect( - [this,req](AdbdClient::PKResponse response, bool remember_choice){ - g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); - req.respond(response); - if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) - write_public_key(req.public_key); - g_idle_add([](gpointer gself){static_cast(gself)->restart(); return G_SOURCE_REMOVE;}, this); - } - )); + g_debug("%s got pk request: %s", G_STRLOC, req.fingerprint.c_str()); + m_req = req; + maybe_snap(); } ); } + void maybe_snap() + { + // don't prompt in the greeter! + if (!m_req.public_key.empty() && !m_greeter->is_active().get()) + snap(); + } + + void snap() + { + m_snap = std::make_shared(m_req.fingerprint); + m_snap_connections.insert((*m_snap).on_user_response().connect( + [this](AdbdClient::PKResponse response, bool remember_choice){ + g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); + m_req.respond(response); + if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) + write_public_key(m_req.public_key); + g_idle_add([](gpointer gself){static_cast(gself)->restart(); return G_SOURCE_REMOVE;}, this); + } + )); + } + void write_public_key(const std::string& public_key) { g_debug("%s writing public key '%s' to '%s'", G_STRLOC, public_key.c_str(), m_public_keys_filename.c_str()); @@ -136,6 +145,7 @@ g_message("creating a new adbd client"); const std::shared_ptr m_greeter; std::shared_ptr m_adbd_client; + AdbdClient::PKRequest m_req; std::shared_ptr m_snap; std::set m_snap_connections; }; -- cgit v1.2.3 From 8f396a525d1353d724b6a96b8a777be2dde35984 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Mar 2016 14:12:05 -0500 Subject: fix missing field initialization compiler warning --- src/usb-manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index 656b6a0..f67f0e2 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -68,7 +68,7 @@ private: // clear out old state m_snap_connections.clear(); m_snap.reset(); - m_req = decltype(m_req){}; + m_req = decltype(m_req)(); m_adbd_client.reset(); } -- cgit v1.2.3 From 9f03876ba4aef09f5b93905f90df9a7b9d1073e4 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Wed, 23 Mar 2016 14:44:23 -0500 Subject: fix UsbManager dtor issue found by valgrind --- src/usb-manager.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/usb-manager.cpp b/src/usb-manager.cpp index f67f0e2..4d750c0 100644 --- a/src/usb-manager.cpp +++ b/src/usb-manager.cpp @@ -58,6 +58,9 @@ public: ~Impl() { + if (m_restart_idle_tag) + g_source_remove(m_restart_idle_tag); + clear(); } @@ -103,7 +106,12 @@ private: m_req.respond(response); if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) write_public_key(m_req.public_key); - g_idle_add([](gpointer gself){static_cast(gself)->restart(); return G_SOURCE_REMOVE;}, this); + m_restart_idle_tag = g_idle_add([](gpointer gself){ + auto self = static_cast(gself); + self->m_restart_idle_tag = 0; + self->restart(); + return G_SOURCE_REMOVE; + }, this); } )); } @@ -143,6 +151,8 @@ private: const std::string m_public_keys_filename; const std::shared_ptr m_usb_monitor; const std::shared_ptr m_greeter; + + unsigned int m_restart_idle_tag {}; std::shared_ptr m_adbd_client; AdbdClient::PKRequest m_req; -- cgit v1.2.3 From 194d7e85a52cbc0060a2d85b71b9ddd8b606aee4 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 24 Mar 2016 11:01:16 -0500 Subject: add tracer g_debug() calls for the benefit of the integration tests --- src/adbd-client.cpp | 5 ++++- tests/utils/adbd-server.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/adbd-client.cpp b/src/adbd-client.cpp index 937215e..400c7c9 100644 --- a/src/adbd-client.cpp +++ b/src/adbd-client.cpp @@ -44,6 +44,7 @@ public: ~Impl() { // tell the worker thread to stop whatever it's doing and exit. + g_debug("%s Client::Impl dtor, cancelling m_cancellable", G_STRLOC); g_cancellable_cancel(m_cancellable); m_pkresponse_cv.notify_one(); m_sleep_cv.notify_one(); @@ -144,7 +145,9 @@ private: return m_pkresponse_ready || g_cancellable_is_cancelled(m_cancellable); }); response = m_pkresponse; - g_debug("%s got response '%d'", G_STRLOC, int(response)); + g_debug("%s got response '%d', is-cancelled %d", G_STRLOC, + int(response), + int(g_cancellable_is_cancelled(m_cancellable))); } if (!g_cancellable_is_cancelled(m_cancellable)) send_pk_response(socket, response); diff --git a/tests/utils/adbd-server.h b/tests/utils/adbd-server.h index 5e66379..b574622 100644 --- a/tests/utils/adbd-server.h +++ b/tests/utils/adbd-server.h @@ -112,7 +112,7 @@ private: continue; } const std::string response(buf, std::string::size_type(n_bytes)); - g_message("server got response: %s", response.c_str()); + g_message("server read %d bytes, got response: '%s'", int(n_bytes), response.c_str()); if (!response.empty()) { m_responses.push_back(response); requests.erase(requests.begin()); -- cgit v1.2.3