aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Gould <ted@gould.cx>2015-02-05 17:19:33 +0000
committerCI Train Bot <ci-train-bot@canonical.com>2015-02-05 17:19:33 +0000
commit2c5d6305dea48e2181a9180cea9e7cae19e94ef0 (patch)
tree3af41afd37e01998d62631843ce707266f926c21
parentc9cb54483cc5966b3c542ec86dd4e6bf7b33d4ec (diff)
parented66db9f090b5f98338e1a47103b0106cc02d980 (diff)
downloadayatana-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.txt2
-rw-r--r--debian/control5
-rwxr-xr-xdebian/rules3
-rw-r--r--tests/CMakeLists.txt45
-rw-r--r--tests/accounts-service-mock.h21
-rw-r--r--tests/accounts-service-user.cc3
-rw-r--r--tests/indicator-fixture.h444
-rw-r--r--tests/indicator-test.cc112
-rw-r--r--tests/media-player-user.cc16
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);