diff options
author | Ted Gould <ted@gould.cx> | 2015-02-05 17:19:33 +0000 |
---|---|---|
committer | CI Train Bot <ci-train-bot@canonical.com> | 2015-02-05 17:19:33 +0000 |
commit | 2c5d6305dea48e2181a9180cea9e7cae19e94ef0 (patch) | |
tree | 3af41afd37e01998d62631843ce707266f926c21 | |
parent | c9cb54483cc5966b3c542ec86dd4e6bf7b33d4ec (diff) | |
parent | ed66db9f090b5f98338e1a47103b0106cc02d980 (diff) | |
download | ayatana-indicator-sound-2c5d6305dea48e2181a9180cea9e7cae19e94ef0.tar.gz ayatana-indicator-sound-2c5d6305dea48e2181a9180cea9e7cae19e94ef0.tar.bz2 ayatana-indicator-sound-2c5d6305dea48e2181a9180cea9e7cae19e94ef0.zip |
Initial support for indicator service testing from the GMenu/GAction interfaces
Approved by: Jussi Pakkanen, PS Jenkins bot
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | debian/control | 5 | ||||
-rwxr-xr-x | debian/rules | 3 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 45 | ||||
-rw-r--r-- | tests/accounts-service-mock.h | 21 | ||||
-rw-r--r-- | tests/accounts-service-user.cc | 3 | ||||
-rw-r--r-- | tests/indicator-fixture.h | 444 | ||||
-rw-r--r-- | tests/indicator-test.cc | 112 | ||||
-rw-r--r-- | tests/media-player-user.cc | 16 |
9 files changed, 635 insertions, 16 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c8d69c..c1fbb8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ include_directories(${SOUNDSERVICE_INCLUDE_DIRS}) pkg_check_modules( TEST REQUIRED - dbustest-1 + dbustest-1>=15.04.0 ) include_directories(${TEST_INCLUDE_DIRS}) diff --git a/debian/control b/debian/control index d6c52bf..cdb1267 100644 --- a/debian/control +++ b/debian/control @@ -6,14 +6,15 @@ XSBC-Original-Maintainer: Conor Curran <conor.curran@canonical.com> Build-Depends: debhelper (>= 9.0), cmake, dbus, - dbus-test-runner (>= 14.04.0+14.04.20140226), + dbus-test-runner (>> 14.04.0+14.04.20150120.1), dh-translations, gir1.2-accountsservice-1.0, gnome-common, + gsettings-ubuntu-schemas, autotools-dev, valac (>= 0.20), libaccountsservice-dev, - libdbustest1-dev (>= 14.04.1), + libdbustest1-dev (>= 15.04.0), libgirepository1.0-dev, libglib2.0-dev (>= 2.22.3), libgtest-dev, diff --git a/debian/rules b/debian/rules index 8fb0f91..7e4e4ef 100755 --- a/debian/rules +++ b/debian/rules @@ -17,3 +17,6 @@ override_dh_install: install -m 644 debian/indicator-sound-crashdb.conf debian/indicator-sound/etc/apport/crashdb.conf.d/ dh_install --fail-missing +# For live test logs: +#override_dh_auto_test: +# ARGS=-V dh_auto_test diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb93aca..38a76ae 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,6 +11,29 @@ add_library (gtest STATIC target_link_libraries(gtest ${GTEST_LIBS}) ########################### +# GSettings Schema +########################### + +# build the necessary schemas +set_directory_properties (PROPERTIES + ADDITIONAL_MAKE_CLEAN_FILES gschemas.compiled) +set_source_files_properties (gschemas.compiled GENERATED) + +# GSettings: +# compile the indicator-sound schema into a gschemas.compiled file in this directory, +# and help the tests to find that file by setting -DSCHEMA_DIR +set (SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}/gsettings-schemas") +add_definitions(-DSCHEMA_DIR="${SCHEMA_DIR}") +execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas + OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE + OUTPUT_STRIP_TRAILING_WHITESPACE) +add_custom_command (OUTPUT gschemas.compiled + DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.sound.gschema.xml + COMMAND mkdir -p ${SCHEMA_DIR} + COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR} + COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR}) + +########################### # Vala Mocks ########################### @@ -92,6 +115,8 @@ add_library( pa-mock.cpp ) +target_link_libraries (pulse-mock ${PULSEAUDIO_LIBRARIES}) + ########################### # Name Watch Test ########################### @@ -207,3 +232,23 @@ add_test(greeter-list-test-iterator greeter-list-test --gtest_filter=GreeterListTest.BasicIterator ) +########################### +# Indicator Test +########################### + +add_definitions( + -DINDICATOR_SOUND_SERVICE_BINARY="${CMAKE_BINARY_DIR}/src/indicator-sound-service" + -DPA_MOCK_LIB="${CMAKE_CURRENT_BINARY_DIR}/libpulse-mock.so" +) +add_executable (indicator-test indicator-test.cc gschemas.compiled) +target_link_libraries ( + indicator-test + gtest + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} +) + +# Split tests to work around libaccountservice sucking +add_test(indcator-test + indicator-test +) diff --git a/tests/accounts-service-mock.h b/tests/accounts-service-mock.h index d4dae7e..1d0c1fe 100644 --- a/tests/accounts-service-mock.h +++ b/tests/accounts-service-mock.h @@ -17,6 +17,7 @@ * Ted Gould <ted@canonical.com> */ +#include <memory> #include <libdbustest/dbus-test.h> class AccountsServiceMock @@ -24,11 +25,14 @@ class AccountsServiceMock DbusTestDbusMock * mock = nullptr; DbusTestDbusMockObject * soundobj = nullptr; DbusTestDbusMockObject * userobj = nullptr; + DbusTestDbusMockObject * syssoundobj = nullptr; public: AccountsServiceMock () { mock = dbus_test_dbus_mock_new("org.freedesktop.Accounts"); + dbus_test_task_set_bus(DBUS_TEST_TASK(mock), DBUS_TEST_SERVICE_BUS_SYSTEM); + DbusTestDbusMockObject * baseobj = dbus_test_dbus_mock_get_object(mock, "/org/freedesktop/Accounts", "org.freedesktop.Accounts", NULL); dbus_test_dbus_mock_object_add_method(mock, baseobj, @@ -80,6 +84,11 @@ class AccountsServiceMock dbus_test_dbus_mock_object_add_property(mock, soundobj, "ArtUrl", G_VARIANT_TYPE_STRING, g_variant_new_string(""), NULL); + + syssoundobj = dbus_test_dbus_mock_get_object(mock, "/user", "com.ubuntu.touch.AccountsService.Sound", NULL); + dbus_test_dbus_mock_object_add_property(mock, syssoundobj, + "SilentMode", G_VARIANT_TYPE_BOOLEAN, + g_variant_new_boolean(FALSE), NULL); } ~AccountsServiceMock () { @@ -87,6 +96,18 @@ class AccountsServiceMock g_clear_object(&mock); } + void setSilentMode (bool modeValue) { + dbus_test_dbus_mock_object_update_property(mock, syssoundobj, + "SilentMode", g_variant_new_boolean(modeValue ? TRUE : FALSE), + NULL); + } + + operator std::shared_ptr<DbusTestTask> () { + return std::shared_ptr<DbusTestTask>( + DBUS_TEST_TASK(g_object_ref(mock)), + [](DbusTestTask * task) { g_clear_object(&task); }); + } + operator DbusTestTask* () { return DBUS_TEST_TASK(mock); } diff --git a/tests/accounts-service-user.cc b/tests/accounts-service-user.cc index b39b546..7110fb3 100644 --- a/tests/accounts-service-user.cc +++ b/tests/accounts-service-user.cc @@ -42,14 +42,13 @@ class AccountsServiceUserTest : public ::testing::Test virtual void SetUp() { service = dbus_test_service_new(NULL); + dbus_test_service_set_bus(service, DBUS_TEST_SERVICE_BUS_BOTH); AccountsServiceMock service_mock; dbus_test_service_add_task(service, (DbusTestTask*)service_mock); dbus_test_service_start_tasks(service); - g_setenv("DBUS_SYSTEM_BUS_ADDRESS", g_getenv("DBUS_SESSION_BUS_ADDRESS"), TRUE); - session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); ASSERT_NE(nullptr, session); g_dbus_connection_set_exit_on_close(session, FALSE); diff --git a/tests/indicator-fixture.h b/tests/indicator-fixture.h new file mode 100644 index 0000000..a26f61d --- /dev/null +++ b/tests/indicator-fixture.h @@ -0,0 +1,444 @@ +/* + * 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 as published by + * the Free Software Foundation; version 3. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Ted Gould <ted@canonical.com> + */ + +#include <memory> +#include <algorithm> +#include <string> + +#include <gtest/gtest.h> +#include <gio/gio.h> +#include <libdbustest/dbus-test.h> + +class IndicatorFixture : public ::testing::Test +{ + private: + std::string _indicatorPath; + std::string _indicatorAddress; + std::vector<std::shared_ptr<DbusTestTask>> _mocks; + + class PerRunData { + public: + /* We're private in the fixture but other than that we don't care, + we don't leak out. This object's purpose isn't to hide data it is + to make the lifecycle of the items more clear. */ + std::shared_ptr<GMenuModel> _menu; + std::shared_ptr<GActionGroup> _actions; + DbusTestService * _session_service; + DbusTestService * _system_service; + DbusTestTask * _test_indicator; + DbusTestTask * _test_dummy; + GDBusConnection * _session; + GDBusConnection * _system; + + PerRunData (const std::string& indicatorPath, const std::string& indicatorAddress, std::vector<std::shared_ptr<DbusTestTask>>& mocks) + : _menu(nullptr) + , _session(nullptr) + { + _session_service = dbus_test_service_new(nullptr); + dbus_test_service_set_bus(_session_service, DBUS_TEST_SERVICE_BUS_SESSION); + + _system_service = dbus_test_service_new(nullptr); + dbus_test_service_set_bus(_system_service, DBUS_TEST_SERVICE_BUS_SYSTEM); + + _test_indicator = DBUS_TEST_TASK(dbus_test_process_new(indicatorPath.c_str())); + dbus_test_task_set_name(_test_indicator, "Indicator"); + dbus_test_service_add_task(_session_service, _test_indicator); + + _test_dummy = dbus_test_task_new(); + dbus_test_task_set_wait_for(_test_dummy, indicatorAddress.c_str()); + dbus_test_task_set_name(_test_dummy, "Dummy"); + dbus_test_service_add_task(_session_service, _test_dummy); + + std::for_each(mocks.begin(), mocks.end(), [this](std::shared_ptr<DbusTestTask> task) { + if (dbus_test_task_get_bus(task.get()) == DBUS_TEST_SERVICE_BUS_SYSTEM) { + dbus_test_service_add_task(_system_service, task.get()); + } else { + dbus_test_service_add_task(_session_service, task.get()); + } + }); + + g_debug("Starting System Bus"); + dbus_test_service_start_tasks(_system_service); + _system = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr); + g_dbus_connection_set_exit_on_close(_system, FALSE); + + g_debug("Starting Session Bus"); + dbus_test_service_start_tasks(_session_service); + _session = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr); + g_dbus_connection_set_exit_on_close(_session, FALSE); + } + + virtual ~PerRunData (void) { + _menu.reset(); + _actions.reset(); + + /* D-Bus Test Stuff */ + g_clear_object(&_test_dummy); + g_clear_object(&_test_indicator); + g_clear_object(&_session_service); + g_clear_object(&_system_service); + + /* Wait for D-Bus session bus to go */ + if (!g_dbus_connection_is_closed(_session)) { + g_dbus_connection_close_sync(_session, nullptr, nullptr); + } + g_clear_object(&_session); + + if (!g_dbus_connection_is_closed(_system)) { + g_dbus_connection_close_sync(_system, nullptr, nullptr); + } + g_clear_object(&_system); + } + }; + + std::shared_ptr<PerRunData> run; + + public: + virtual ~IndicatorFixture() = default; + + IndicatorFixture (const std::string& path, + const std::string& addr) + : _indicatorPath(path) + , _indicatorAddress(addr) + { + }; + + + protected: + virtual void SetUp() override + { + run = std::make_shared<PerRunData>(_indicatorPath, _indicatorAddress, _mocks); + + _mocks.clear(); + } + + virtual void TearDown() override + { + run.reset(); + } + + void addMock (std::shared_ptr<DbusTestTask> mock) + { + _mocks.push_back(mock); + } + + std::shared_ptr<DbusTestTask> buildBustleMock (const std::string& filename, DbusTestServiceBus bus = DBUS_TEST_SERVICE_BUS_BOTH) + { + return std::shared_ptr<DbusTestTask>([filename, bus]() { + DbusTestTask * bustle = DBUS_TEST_TASK(dbus_test_bustle_new(filename.c_str())); + dbus_test_task_set_name(bustle, "Bustle"); + dbus_test_task_set_bus(bustle, bus); + return bustle; + }(), [](DbusTestTask * bustle) { + g_clear_object(&bustle); + }); + } + + private: + void waitForCore (GObject * obj, const gchar * signalname) { + auto loop = g_main_loop_new(nullptr, FALSE); + + /* Our two exit criteria */ + gulong signal = g_signal_connect_swapped(obj, signalname, G_CALLBACK(g_main_loop_quit), loop); + guint timer = g_timeout_add_seconds(5, [](gpointer user_data) -> gboolean { + g_warning("Menu Timeout"); + g_main_loop_quit((GMainLoop *)user_data); + return G_SOURCE_CONTINUE; + }, loop); + + /* Wait for sync */ + g_main_loop_run(loop); + + /* Clean up */ + g_source_remove(timer); + g_signal_handler_disconnect(obj, signal); + + g_main_loop_unref(loop); + } + + void menuWaitForItems (const std::shared_ptr<GMenuModel>& menu) { + auto count = g_menu_model_get_n_items(menu.get()); + + if (count != 0) + return; + + waitForCore(G_OBJECT(menu.get()), "items-changed"); + } + + void agWaitForActions (const std::shared_ptr<GActionGroup>& group) { + auto list = std::shared_ptr<gchar *>( + g_action_group_list_actions(group.get()), + [](gchar ** list) { + g_strfreev(list); + }); + + if (g_strv_length(list.get()) != 0) { + return; + } + + waitForCore(G_OBJECT(group.get()), "action-added"); + } + + protected: + void setMenu (const std::string& path) { + run->_menu.reset(); + + g_debug("Getting Menu: %s:%s", _indicatorAddress.c_str(), path.c_str()); + run->_menu = std::shared_ptr<GMenuModel>(G_MENU_MODEL(g_dbus_menu_model_get(run->_session, _indicatorAddress.c_str(), path.c_str())), [](GMenuModel * modelptr) { + g_clear_object(&modelptr); + }); + + menuWaitForItems(run->_menu); + } + + void setActions (const std::string& path) { + run->_actions.reset(); + + run->_actions = std::shared_ptr<GActionGroup>(G_ACTION_GROUP(g_dbus_action_group_get(run->_session, _indicatorAddress.c_str(), path.c_str())), [](GActionGroup * groupptr) { + g_clear_object(&groupptr); + }); + + agWaitForActions(run->_actions); + } + + testing::AssertionResult expectActionExists (const gchar * nameStr, const std::string& name) { + bool hasit = g_action_group_has_action(run->_actions.get(), name.c_str()); + + if (!hasit) { + auto result = testing::AssertionFailure(); + result << + " Action: " << nameStr << std::endl << + " Expected: " << "Exists" << std::endl << + " Actual: " << "No action found" << std::endl; + + return result; + } + + auto result = testing::AssertionSuccess(); + return result; + } + + testing::AssertionResult expectActionStateType (const char * nameStr, const char * typeStr, const std::string& name, const GVariantType * type) { + auto atype = g_action_group_get_action_state_type(run->_actions.get(), name.c_str()); + bool same = false; + + if (atype != nullptr) { + same = g_variant_type_equal(atype, type); + } + + if (!same) { + auto result = testing::AssertionFailure(); + result << + " Action: " << nameStr << std::endl << + " Expected: " << typeStr << std::endl << + " Actual: " << g_variant_type_peek_string(atype) << std::endl; + + return result; + } + + auto result = testing::AssertionSuccess(); + return result; + } + + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, GVariant * value) { + auto varref = std::shared_ptr<GVariant>(g_variant_ref_sink(value), [](GVariant * varptr) { + if (varptr != nullptr) + g_variant_unref(varptr); + }); + auto aval = std::shared_ptr<GVariant>(g_action_group_get_action_state(run->_actions.get(), name.c_str()), [] (GVariant * varptr) { + if (varptr != nullptr) + g_variant_unref(varptr); + }); + bool match = false; + + if (aval != nullptr) { + match = g_variant_equal(aval.get(), varref.get()); + } + + if (!match) { + gchar * attstr = nullptr; + + if (aval != nullptr) { + attstr = g_variant_print(aval.get(), TRUE); + } else { + attstr = g_strdup("nullptr"); + } + + auto result = testing::AssertionFailure(); + result << + " Action: " << nameStr << std::endl << + " Expected: " << valueStr << std::endl << + " Actual: " << attstr << std::endl; + + g_free(attstr); + + return result; + } else { + auto result = testing::AssertionSuccess(); + return result; + } + } + + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, bool value) { + GVariant * var = g_variant_new_boolean(value); + return expectActionStateIs(nameStr, valueStr, name, var); + } + + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, std::string value) { + GVariant * var = g_variant_new_string(value.c_str()); + return expectActionStateIs(nameStr, valueStr, name, var); + } + + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, const char * value) { + GVariant * var = g_variant_new_string(value); + return expectActionStateIs(nameStr, valueStr, name, var); + } + + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, double value) { + GVariant * var = g_variant_new_double(value); + return expectActionStateIs(nameStr, valueStr, name, var); + } + + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, float value) { + GVariant * var = g_variant_new_double(value); + return expectActionStateIs(nameStr, valueStr, name, var); + } + + + private: + std::shared_ptr<GVariant> getMenuAttributeVal (int location, std::shared_ptr<GMenuModel>& menu, const std::string& attribute, std::shared_ptr<GVariant>& value) { + if (!(location < g_menu_model_get_n_items(menu.get()))) { + return nullptr; + } + + if (location >= g_menu_model_get_n_items(menu.get())) + return nullptr; + + auto menuval = std::shared_ptr<GVariant>(g_menu_model_get_item_attribute_value(menu.get(), location, attribute.c_str(), g_variant_get_type(value.get())), [](GVariant * varptr) { + if (varptr != nullptr) + g_variant_unref(varptr); + }); + + return menuval; + } + + std::shared_ptr<GVariant> getMenuAttributeRecurse (std::vector<int>::const_iterator menuLocation, std::vector<int>::const_iterator menuEnd, const std::string& attribute, std::shared_ptr<GVariant>& value, std::shared_ptr<GMenuModel>& menu) { + if (menuLocation == menuEnd) + return nullptr; + + if (menuLocation + 1 == menuEnd) + return getMenuAttributeVal(*menuLocation, menu, attribute, value); + + auto clearfunc = [](GMenuModel * modelptr) { + g_clear_object(&modelptr); + }; + + auto submenu = std::shared_ptr<GMenuModel>(g_menu_model_get_item_link(menu.get(), *menuLocation, G_MENU_LINK_SUBMENU), clearfunc); + + if (submenu == nullptr) + submenu = std::shared_ptr<GMenuModel>(g_menu_model_get_item_link(menu.get(), *menuLocation, G_MENU_LINK_SECTION), clearfunc); + + if (submenu == nullptr) + return nullptr; + + menuWaitForItems(submenu); + + return getMenuAttributeRecurse(menuLocation + 1, menuEnd, attribute, value, submenu); + } + + protected: + testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const gchar * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, GVariant * value) { + auto varref = std::shared_ptr<GVariant>(g_variant_ref_sink(value), [](GVariant * varptr) { + if (varptr != nullptr) + g_variant_unref(varptr); + }); + + auto attrib = getMenuAttributeRecurse(menuLocation.cbegin(), menuLocation.cend(), attribute, varref, run->_menu); + bool same = false; + + if (attrib != nullptr && varref != nullptr) { + same = g_variant_equal(attrib.get(), varref.get()); + } + + if (!same) { + gchar * attstr = nullptr; + + if (attrib != nullptr) { + attstr = g_variant_print(attrib.get(), TRUE); + } else { + attstr = g_strdup("nullptr"); + } + + auto result = testing::AssertionFailure(); + result << + " Menu: " << menuLocationStr << std::endl << + " Attribute: " << attributeStr << std::endl << + " Expected: " << valueStr << std::endl << + " Actual: " << attstr << std::endl; + + g_free(attstr); + + return result; + } else { + auto result = testing::AssertionSuccess(); + return result; + } + } + + testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const gchar * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, bool value) { + GVariant * var = g_variant_new_boolean(value); + return expectMenuAttribute(menuLocationStr, attributeStr, valueStr, menuLocation, attribute, var); + } + + testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const gchar * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, std::string value) { + GVariant * var = g_variant_new_string(value.c_str()); + return expectMenuAttribute(menuLocationStr, attributeStr, valueStr, menuLocation, attribute, var); + } + + testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const gchar * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, const char * value) { + GVariant * var = g_variant_new_string(value); + return expectMenuAttribute(menuLocationStr, attributeStr, valueStr, menuLocation, attribute, var); + } + +}; + +#define EXPECT_MENU_ATTRIB(menu, attrib, value) \ + EXPECT_PRED_FORMAT3(IndicatorFixture::expectMenuAttribute, menu, attrib, value) + +#define ASSERT_MENU_ATTRIB(menu, attrib, value) \ + ASSERT_PRED_FORMAT3(IndicatorFixture::expectMenuAttribute, menu, attrib, value) + +#define ASSERT_ACTION_EXISTS(action) \ + ASSERT_PRED_FORMAT1(IndicatorFixture::expectActionExists, action) + +#define EXPECT_ACTION_EXISTS(action) \ + EXPECT_PRED_FORMAT1(IndicatorFixture::expectActionExists, action) + +#define EXPECT_ACTION_STATE(action, value) \ + EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionStateIs, action, value) + +#define ASSERT_ACTION_STATE(action, value) \ + ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionStateIs, action, value) + +#define EXPECT_ACTION_STATE_TYPE(action, type) \ + EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionStateType, action, type) + +#define ASSERT_ACTION_STATE_TYPE(action, type) \ + ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionStateType, action, type) + diff --git a/tests/indicator-test.cc b/tests/indicator-test.cc new file mode 100644 index 0000000..636db1d --- /dev/null +++ b/tests/indicator-test.cc @@ -0,0 +1,112 @@ +/* + * 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 as published by + * the Free Software Foundation; version 3. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Ted Gould <ted@canonical.com> + */ + +#include <gtest/gtest.h> +#include <gio/gio.h> + +#include "indicator-fixture.h" +#include "accounts-service-mock.h" + +class IndicatorTest : public IndicatorFixture +{ +protected: + IndicatorTest (void) : + IndicatorFixture(INDICATOR_SOUND_SERVICE_BINARY, "com.canonical.indicator.sound") + { + } + + std::shared_ptr<AccountsServiceMock> as; + + virtual void SetUp() override + { + //addMock(buildBustleMock("indicator-test-session.bustle", DBUS_TEST_SERVICE_BUS_SESSION)); + //addMock(buildBustleMock("indicator-test-system.bustle", DBUS_TEST_SERVICE_BUS_SYSTEM)); + g_setenv("LD_PRELOAD", PA_MOCK_LIB, TRUE); + + g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE); + g_setenv("GSETTINGS_BACKEND", "memory", TRUE); + + as = std::make_shared<AccountsServiceMock>(); + addMock(*as); + + IndicatorFixture::SetUp(); + } + + virtual void TearDown() override + { + as.reset(); + + IndicatorFixture::TearDown(); + } + +}; + + +TEST_F(IndicatorTest, PhoneMenu) { + setMenu("/com/canonical/indicator/sound/phone"); + + EXPECT_MENU_ATTRIB({0}, "action", "indicator.root"); + EXPECT_MENU_ATTRIB({0}, "x-canonical-type", "com.canonical.indicator.root"); + EXPECT_MENU_ATTRIB({0}, "x-canonical-scroll-action", "indicator.scroll"); + EXPECT_MENU_ATTRIB({0}, "x-canonical-secondary-action", "indicator.mute"); + + EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "action", "indicator.silent-mode"); + EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "label", "Silent Mode"); + + EXPECT_MENU_ATTRIB(std::vector<int>({0, 1}), "action", "indicator.phone-settings"); + EXPECT_MENU_ATTRIB(std::vector<int>({0, 1}), "label", "Sound Settings…"); +} + +TEST_F(IndicatorTest, DesktopMenu) { + setMenu("/com/canonical/indicator/sound/desktop"); + + EXPECT_MENU_ATTRIB({0}, "action", "indicator.root"); + EXPECT_MENU_ATTRIB({0}, "x-canonical-type", "com.canonical.indicator.root"); + EXPECT_MENU_ATTRIB({0}, "x-canonical-scroll-action", "indicator.scroll"); + EXPECT_MENU_ATTRIB({0}, "x-canonical-secondary-action", "indicator.mute"); + + EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "action", "indicator.mute"); + EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "label", "Mute"); + + EXPECT_MENU_ATTRIB(std::vector<int>({0, 1}), "action", "indicator.desktop-settings"); + EXPECT_MENU_ATTRIB(std::vector<int>({0, 1}), "label", "Sound Settings…"); +} + +TEST_F(IndicatorTest, BaseActions) { + setActions("/com/canonical/indicator/sound"); + + ASSERT_ACTION_EXISTS("root"); + ASSERT_ACTION_STATE_TYPE("root", G_VARIANT_TYPE("a{sv}")); + + ASSERT_ACTION_EXISTS("scroll"); + + ASSERT_ACTION_EXISTS("silent-mode"); + ASSERT_ACTION_STATE_TYPE("silent-mode", G_VARIANT_TYPE_BOOLEAN); + EXPECT_ACTION_STATE("silent-mode", false); + + ASSERT_ACTION_EXISTS("mute"); + ASSERT_ACTION_STATE_TYPE("mute", G_VARIANT_TYPE_BOOLEAN); + + ASSERT_ACTION_EXISTS("mic-volume"); + ASSERT_ACTION_STATE_TYPE("mic-volume", G_VARIANT_TYPE_DOUBLE); + + ASSERT_ACTION_EXISTS("volume"); + ASSERT_ACTION_STATE_TYPE("volume", G_VARIANT_TYPE_DOUBLE); +} + diff --git a/tests/media-player-user.cc b/tests/media-player-user.cc index 32c1181..8d2fcb1 100644 --- a/tests/media-player-user.cc +++ b/tests/media-player-user.cc @@ -34,30 +34,22 @@ class MediaPlayerUserTest : public ::testing::Test DbusTestService * service = NULL; AccountsServiceMock service_mock; - GDBusConnection * session = NULL; GDBusConnection * system = NULL; GDBusProxy * proxy = NULL; virtual void SetUp() { service = dbus_test_service_new(NULL); - + dbus_test_service_set_bus(service, DBUS_TEST_SERVICE_BUS_SYSTEM); dbus_test_service_add_task(service, (DbusTestTask*)service_mock); dbus_test_service_start_tasks(service); - g_setenv("DBUS_SYSTEM_BUS_ADDRESS", g_getenv("DBUS_SESSION_BUS_ADDRESS"), TRUE); - - session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); - ASSERT_NE(nullptr, session); - g_dbus_connection_set_exit_on_close(session, FALSE); - g_object_add_weak_pointer(G_OBJECT(session), (gpointer *)&session); - system = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); ASSERT_NE(nullptr, system); g_dbus_connection_set_exit_on_close(system, FALSE); g_object_add_weak_pointer(G_OBJECT(system), (gpointer *)&system); - proxy = g_dbus_proxy_new_sync(session, + proxy = g_dbus_proxy_new_sync(system, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.Accounts", @@ -71,7 +63,6 @@ class MediaPlayerUserTest : public ::testing::Test g_clear_object(&proxy); g_clear_object(&service); - g_object_unref(session); g_object_unref(system); #if 0 @@ -189,6 +180,9 @@ TEST_F(MediaPlayerUserTest, TimeoutTest) { set_property("Album", g_variant_new_string("Vinyl is dead")); set_property("ArtUrl", g_variant_new_string("http://art.url")); + /* Ensure the properties get set before we pull them */ + loop(100); + /* Build our media player */ MediaPlayerUser * player = media_player_user_new("user"); ASSERT_NE(nullptr, player); |