From 9900cc2940d9d917a511d9390fccefd92caff730 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Mon, 21 Dec 2015 16:12:47 +0100 Subject: Changed number of times upstart can try to respawn the process --- data/indicator-sound.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/indicator-sound.conf.in b/data/indicator-sound.conf.in index 4912b61..e1618be 100644 --- a/data/indicator-sound.conf.in +++ b/data/indicator-sound.conf.in @@ -4,6 +4,6 @@ start on indicator-services-start stop on desktop-end or indicator-services-end respawn -respawn limit 2 10 +respawn limit 30 10 exec @CMAKE_INSTALL_FULL_LIBEXECDIR@/indicator-sound/indicator-sound-service -- cgit v1.2.3 From 3a75ebb5eae371de3b50ce4716a4af886d9eeac3 Mon Sep 17 00:00:00 2001 From: CI Train Bot Date: Tue, 22 Dec 2015 09:50:08 +0000 Subject: Releasing 12.10.2+16.04.20151222-0ubuntu1 --- debian/changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index 547b25e..039831b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium + + [ Xavi Garcia Mena ] + * Very simple change to make upstart try to respawn the process 30 + times (instead of 2). This is a workaround for the case when dbus + session is not ready yet at startup. + + -- Xavi Garcia Tue, 22 Dec 2015 09:50:08 +0000 + indicator-sound (12.10.2+16.04.20151103.2-0ubuntu1) xenial; urgency=medium [ Xavi Garcia ] -- cgit v1.2.3 From d8d60bc83824359c6e22b9114fc0a6bde45b8eca Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Wed, 23 Dec 2015 12:08:16 +0100 Subject: Reverted to release 507 --- data/indicator-sound.conf.in | 2 +- debian/changelog | 20 - include/CMakeLists.txt | 1 - include/unity/CMakeLists.txt | 1 - include/unity/gmenuharness/MatchResult.h | 66 -- include/unity/gmenuharness/MatchUtils.h | 42 - include/unity/gmenuharness/MenuItemMatcher.h | 143 --- include/unity/gmenuharness/MenuMatcher.h | 95 -- src/CMakeLists.txt | 21 +- src/gmenuharness/CMakeLists.txt | 17 - src/gmenuharness/MatchResult.cpp | 187 ---- src/gmenuharness/MatchUtils.cpp | 77 -- src/gmenuharness/MenuItemMatcher.cpp | 1008 -------------------- src/gmenuharness/MenuMatcher.cpp | 208 ---- src/service.vala | 407 +------- src/sound-menu.vala | 38 - src/volume-control-pulse.vala | 83 +- src/volume-control.vala | 14 - tests/CMakeLists.txt | 203 ++-- tests/dbus-types/CMakeLists.txt | 53 - .../com.ubuntu.AccountsService.Sound.xml | 9 - tests/dbus-types/dbus-types.h | 48 - .../org.PulseAudio.Ext.StreamRestore1.xml | 7 - tests/dbus-types/org.freedesktop.Accounts.xml | 9 - .../dbus-types/org.freedesktop.DBus.Properties.xml | 22 - tests/dbus-types/org.freedesktop.Notifications.xml | 47 - tests/dbus-types/org.gtk.Actions.xml | 13 - tests/dbus-types/pulseaudio-volume.cpp | 156 --- tests/dbus-types/pulseaudio-volume.h | 69 -- tests/integration/CMakeLists.txt | 132 --- tests/integration/Copy of test-sound.wav | Bin 191989 -> 0 bytes tests/integration/indicator-sound-test-base.cpp | 823 ---------------- tests/integration/indicator-sound-test-base.h | 160 ---- tests/integration/main.cpp | 58 -- tests/integration/test-indicator.cpp | 981 ------------------- tests/integration/test-sound.wav | Bin 7665640 -> 0 bytes tests/integration/touch-stream-restore.table | 4 - tests/integration/utils/dbus-pulse-volume.cpp | 232 ----- tests/integration/utils/dbus-pulse-volume.h | 57 -- tests/integration/utils/get-volume.cpp | 33 - tests/integration/utils/set-volume.cpp | 36 - tests/notifications-test.cc | 6 +- tests/service-mocks/CMakeLists.txt | 2 - tests/service-mocks/DBusPropertiesNotifier.cpp | 41 - tests/service-mocks/DBusPropertiesNotifier.h | 48 - tests/service-mocks/accounts-mock/AccountsDefs.h | 37 - tests/service-mocks/accounts-mock/AccountsMock.cpp | 40 - tests/service-mocks/accounts-mock/AccountsMock.h | 50 - .../accounts-mock/AccountsServiceSoundMock.cpp | 48 - .../accounts-mock/AccountsServiceSoundMock.h | 58 -- tests/service-mocks/accounts-mock/CMakeLists.txt | 42 - .../com.ubuntu.AccountsService.Sound.Mock.xml | 6 - tests/service-mocks/accounts-mock/main.cpp | 63 -- .../org.freedesktop.Accounts.Mock.xml | 15 - .../media-player-mpris-mock/CMakeLists.txt | 63 -- .../media-player-mpris-mock/MediaPlayerMprisDefs.h | 37 - .../MediaPlayerMprisMock.cpp | 103 -- .../media-player-mpris-mock/MediaPlayerMprisMock.h | 77 -- .../applications/testplayer1.desktop | 21 - .../service-mocks/media-player-mpris-mock/main.cpp | 64 -- .../org.mpris.MediaPlayer2.Player.xml | 24 - .../org.mpris.MediaPlayer2.xml | 6 - .../media-player-mpris-mock/player-update.cpp | 93 -- 63 files changed, 177 insertions(+), 6349 deletions(-) delete mode 100644 include/CMakeLists.txt delete mode 100644 include/unity/CMakeLists.txt delete mode 100644 include/unity/gmenuharness/MatchResult.h delete mode 100644 include/unity/gmenuharness/MatchUtils.h delete mode 100644 include/unity/gmenuharness/MenuItemMatcher.h delete mode 100644 include/unity/gmenuharness/MenuMatcher.h delete mode 100644 src/gmenuharness/CMakeLists.txt delete mode 100644 src/gmenuharness/MatchResult.cpp delete mode 100644 src/gmenuharness/MatchUtils.cpp delete mode 100644 src/gmenuharness/MenuItemMatcher.cpp delete mode 100644 src/gmenuharness/MenuMatcher.cpp delete mode 100644 tests/dbus-types/CMakeLists.txt delete mode 100644 tests/dbus-types/com.ubuntu.AccountsService.Sound.xml delete mode 100644 tests/dbus-types/dbus-types.h delete mode 100644 tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml delete mode 100644 tests/dbus-types/org.freedesktop.Accounts.xml delete mode 100644 tests/dbus-types/org.freedesktop.DBus.Properties.xml delete mode 100644 tests/dbus-types/org.freedesktop.Notifications.xml delete mode 100644 tests/dbus-types/org.gtk.Actions.xml delete mode 100644 tests/dbus-types/pulseaudio-volume.cpp delete mode 100644 tests/dbus-types/pulseaudio-volume.h delete mode 100644 tests/integration/CMakeLists.txt delete mode 100644 tests/integration/Copy of test-sound.wav delete mode 100644 tests/integration/indicator-sound-test-base.cpp delete mode 100644 tests/integration/indicator-sound-test-base.h delete mode 100644 tests/integration/main.cpp delete mode 100644 tests/integration/test-indicator.cpp delete mode 100644 tests/integration/test-sound.wav delete mode 100755 tests/integration/touch-stream-restore.table delete mode 100644 tests/integration/utils/dbus-pulse-volume.cpp delete mode 100644 tests/integration/utils/dbus-pulse-volume.h delete mode 100644 tests/integration/utils/get-volume.cpp delete mode 100644 tests/integration/utils/set-volume.cpp delete mode 100644 tests/service-mocks/CMakeLists.txt delete mode 100644 tests/service-mocks/DBusPropertiesNotifier.cpp delete mode 100644 tests/service-mocks/DBusPropertiesNotifier.h delete mode 100644 tests/service-mocks/accounts-mock/AccountsDefs.h delete mode 100644 tests/service-mocks/accounts-mock/AccountsMock.cpp delete mode 100644 tests/service-mocks/accounts-mock/AccountsMock.h delete mode 100644 tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp delete mode 100644 tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h delete mode 100644 tests/service-mocks/accounts-mock/CMakeLists.txt delete mode 100644 tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml delete mode 100644 tests/service-mocks/accounts-mock/main.cpp delete mode 100644 tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml delete mode 100644 tests/service-mocks/media-player-mpris-mock/CMakeLists.txt delete mode 100644 tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h delete mode 100644 tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp delete mode 100644 tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h delete mode 100644 tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop delete mode 100644 tests/service-mocks/media-player-mpris-mock/main.cpp delete mode 100644 tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml delete mode 100644 tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml delete mode 100644 tests/service-mocks/media-player-mpris-mock/player-update.cpp diff --git a/data/indicator-sound.conf.in b/data/indicator-sound.conf.in index e1618be..4912b61 100644 --- a/data/indicator-sound.conf.in +++ b/data/indicator-sound.conf.in @@ -4,6 +4,6 @@ start on indicator-services-start stop on desktop-end or indicator-services-end respawn -respawn limit 30 10 +respawn limit 2 10 exec @CMAKE_INSTALL_FULL_LIBEXECDIR@/indicator-sound/indicator-sound-service diff --git a/debian/changelog b/debian/changelog index 039831b..b01835d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,23 +1,3 @@ -indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium - - [ Xavi Garcia Mena ] - * Very simple change to make upstart try to respawn the process 30 - times (instead of 2). This is a workaround for the case when dbus - session is not ready yet at startup. - - -- Xavi Garcia Tue, 22 Dec 2015 09:50:08 +0000 - -indicator-sound (12.10.2+16.04.20151103.2-0ubuntu1) xenial; urgency=medium - - [ Xavi Garcia ] - * Added OSD notifications to show output source when the volume or - source change (LP: #1382548, LP: #1415480, LP: #1484589) - - [ CI Train Bot ] - * New rebuild forced. - - -- Xavi Garcia Tue, 03 Nov 2015 17:47:10 +0000 - indicator-sound (12.10.2+15.10.20151019-0ubuntu1) wily; urgency=medium [ Xavi Garcia Mena ] diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt deleted file mode 100644 index 9d74e15..0000000 --- a/include/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(unity) diff --git a/include/unity/CMakeLists.txt b/include/unity/CMakeLists.txt deleted file mode 100644 index ef57b9e..0000000 --- a/include/unity/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(gmenuharness) diff --git a/include/unity/gmenuharness/MatchResult.h b/include/unity/gmenuharness/MatchResult.h deleted file mode 100644 index 8503a98..0000000 --- a/include/unity/gmenuharness/MatchResult.h +++ /dev/null @@ -1,66 +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 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 . - * - * Authored by: Pete Woods - */ - -#pragma once - -#include -#include -#include - -namespace unity -{ - -namespace gmenuharness -{ - -class MatchResult -{ -public: - MatchResult(); - - MatchResult(MatchResult&& other); - - MatchResult(const MatchResult& other); - - MatchResult& operator=(const MatchResult& other); - - MatchResult& operator=(MatchResult&& other); - - ~MatchResult() = default; - - MatchResult createChild() const; - - void failure(const std::vector& location, const std::string& message); - - void merge(const MatchResult& other); - - bool success() const; - - bool hasTimedOut() const; - - std::string concat_failures() const; - -protected: - struct Priv; - - std::shared_ptr p; -}; - -} // namespace gmenuharness - -} // namespace unity diff --git a/include/unity/gmenuharness/MatchUtils.h b/include/unity/gmenuharness/MatchUtils.h deleted file mode 100644 index 899e506..0000000 --- a/include/unity/gmenuharness/MatchUtils.h +++ /dev/null @@ -1,42 +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 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 . - * - * Authored by: Pete Woods - */ - -#pragma once - -#include -#include - -#include - -namespace unity -{ - -namespace gmenuharness -{ - -void waitForCore(GObject* obj, const std::string& signalName, unsigned int timeout = 10); - -void menuWaitForItems(const std::shared_ptr& menu, unsigned int timeout = 10); - -void g_object_deleter(gpointer object); - -void gvariant_deleter(GVariant* varptr); - -} //namespace gmenuharness - -} // namespace unity diff --git a/include/unity/gmenuharness/MenuItemMatcher.h b/include/unity/gmenuharness/MenuItemMatcher.h deleted file mode 100644 index 38a5187..0000000 --- a/include/unity/gmenuharness/MenuItemMatcher.h +++ /dev/null @@ -1,143 +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 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 . - * - * Authored by: Pete Woods - */ - -#pragma once - -#include -#include -#include - -#include - -namespace unity -{ - -namespace gmenuharness -{ - -class MatchResult; - -class MenuItemMatcher -{ -public: - enum class Mode - { - all, - starts_with, - ends_with - }; - - enum class Type - { - plain, - checkbox, - radio - }; - - static MenuItemMatcher checkbox(); - - static MenuItemMatcher radio(); - - MenuItemMatcher(); - - ~MenuItemMatcher(); - - MenuItemMatcher(const MenuItemMatcher& other); - - MenuItemMatcher(MenuItemMatcher&& other); - - MenuItemMatcher& operator=(const MenuItemMatcher& other); - - MenuItemMatcher& operator=(MenuItemMatcher&& other); - - MenuItemMatcher& type(Type type); - - MenuItemMatcher& label(const std::string& label); - - MenuItemMatcher& action(const std::string& action); - - MenuItemMatcher& state_icons(const std::vector& state); - - MenuItemMatcher& icon(const std::string& icon); - - MenuItemMatcher& themed_icon(const std::string& iconName, const std::vector& icons); - - MenuItemMatcher& widget(const std::string& widget); - - MenuItemMatcher& pass_through_attribute(const std::string& actionName, const std::shared_ptr& value); - - MenuItemMatcher& pass_through_boolean_attribute(const std::string& actionName, bool value); - - MenuItemMatcher& pass_through_string_attribute(const std::string& actionName, const std::string& value); - - MenuItemMatcher& pass_through_double_attribute(const std::string& actionName, double value); - - MenuItemMatcher& round_doubles(double maxDifference); - - MenuItemMatcher& attribute(const std::string& name, const std::shared_ptr& value); - - MenuItemMatcher& boolean_attribute(const std::string& name, bool value); - - MenuItemMatcher& string_attribute(const std::string& name, const std::string& value); - - MenuItemMatcher& int32_attribute(const std::string& name, int value); - - MenuItemMatcher& int64_attribute(const std::string& name, int value); - - MenuItemMatcher& double_attribute(const std::string& name, double value); - - MenuItemMatcher& attribute_not_set(const std::string& name); - - MenuItemMatcher& toggled(bool toggled); - - MenuItemMatcher& mode(Mode mode); - - MenuItemMatcher& submenu(); - - MenuItemMatcher& section(); - - MenuItemMatcher& is_empty(); - - MenuItemMatcher& has_exactly(std::size_t children); - - MenuItemMatcher& item(const MenuItemMatcher& item); - - MenuItemMatcher& item(MenuItemMatcher&& item); - - MenuItemMatcher& pass_through_activate(const std::string& action, const std::shared_ptr& parameter = nullptr); - - MenuItemMatcher& activate(const std::shared_ptr& parameter = nullptr); - - MenuItemMatcher& set_pass_through_action_state(const std::string& action, const std::shared_ptr& state); - - MenuItemMatcher& set_action_state(const std::shared_ptr& state); - - void match(MatchResult& matchResult, const std::vector& location, - const std::shared_ptr& menu, - std::map>& actions, - int index) const; - -protected: - struct Priv; - - std::shared_ptr p; -}; - -} // namespace gmenuharness - -} // namespace unity diff --git a/include/unity/gmenuharness/MenuMatcher.h b/include/unity/gmenuharness/MenuMatcher.h deleted file mode 100644 index c2eb65e..0000000 --- a/include/unity/gmenuharness/MenuMatcher.h +++ /dev/null @@ -1,95 +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 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 . - * - * Authored by: Pete Woods - */ - -#pragma once - -#define EXPECT_MATCHRESULT(statement) \ -do {\ - auto result = (statement);\ - GTEST_TEST_BOOLEAN_(result.success(), #statement, false, true, \ - GTEST_NONFATAL_FAILURE_) << result.concat_failures().c_str(); \ -} while (0) - -#include -#include - -#include -#include - -namespace unity -{ - -namespace gmenuharness -{ - -class MenuMatcher -{ -public: - class Parameters - { - public: - Parameters( - const std::string& busName, - const std::vector>& actions, - const std::string& menuObjectPath); - - ~Parameters(); - - Parameters(const Parameters& other); - - Parameters(Parameters&& other); - - Parameters& operator=(const Parameters& other); - - Parameters& operator=(Parameters&& other); - - protected: - friend MenuMatcher; - - struct Priv; - - std::shared_ptr p; - }; - - MenuMatcher(const Parameters& parameters); - - ~MenuMatcher(); - - MenuMatcher(const MenuMatcher& other) = delete; - - MenuMatcher(MenuMatcher&& other) = delete; - - MenuMatcher& operator=(const MenuMatcher& other) = delete; - - MenuMatcher& operator=(MenuMatcher&& other) = delete; - - MenuMatcher& item(const MenuItemMatcher& item); - - MatchResult match() const; - - void match(MatchResult& matchResult) const; - -protected: - struct Priv; - - std::shared_ptr p; -}; - -} // gmenuharness - -} // unity diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c621ae..194dfc9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,12 +8,12 @@ set(SYMBOLS_PATH "${CMAKE_CURRENT_BINARY_DIR}/indicator-sound-service.def") set(VAPI_PATH "${CMAKE_CURRENT_BINARY_DIR}/indicator-sound-service.vapi") vapi_gen(accounts-service - LIBRARY - accounts-service - PACKAGES - gio-2.0 - INPUT - /usr/share/gir-1.0/AccountsService-1.0.gir + LIBRARY + accounts-service + PACKAGES + gio-2.0 + INPUT + /usr/share/gir-1.0/AccountsService-1.0.gir ) vala_init(indicator-sound-service @@ -70,7 +70,7 @@ vala_add(indicator-sound-service media-player-user.vala DEPENDS media-player - accounts-service-sound-settings + accounts-service-sound-settings greeter-broadcast ) vala_add(indicator-sound-service @@ -103,7 +103,6 @@ vala_add(indicator-sound-service sound-menu.vala DEPENDS media-player - volume-control ) vala_add(indicator-sound-service accounts-service-user.vala @@ -165,8 +164,8 @@ add_definitions( ) add_library( - indicator-sound-service-lib STATIC - ${INDICATOR_SOUND_SOURCES} + indicator-sound-service-lib STATIC + ${INDICATOR_SOUND_SOURCES} ) target_link_libraries( @@ -207,5 +206,3 @@ install( RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/indicator-sound/ ) -# Disable integration tests -# add_subdirectory(gmenuharness) diff --git a/src/gmenuharness/CMakeLists.txt b/src/gmenuharness/CMakeLists.txt deleted file mode 100644 index c9e613a..0000000 --- a/src/gmenuharness/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -pkg_check_modules(UNITY_API libunity-api>=0.1.3 REQUIRED) -include_directories(${UNITY_API_INCLUDE_DIRS}) - -include_directories("${CMAKE_SOURCE_DIR}/include") - -add_library( - gmenuharness-shared SHARED - MatchResult.cpp - MatchUtils.cpp - MenuItemMatcher.cpp - MenuMatcher.cpp -) - -target_link_libraries( - gmenuharness-shared - ${GLIB_LDFLAGS} -) diff --git a/src/gmenuharness/MatchResult.cpp b/src/gmenuharness/MatchResult.cpp deleted file mode 100644 index 40629aa..0000000 --- a/src/gmenuharness/MatchResult.cpp +++ /dev/null @@ -1,187 +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 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 . - * - * Authored by: Pete Woods - */ - -#include - -#include -#include -#include -#include - -using namespace std; - -namespace unity -{ - -namespace gmenuharness -{ - -namespace -{ - - -static void printLocation(ostream& ss, const vector& location, bool first) -{ - for (int i : location) - { - ss << " "; - if (first) - { - ss << i; - } - else - { - ss << " "; - } - } - ss << " "; -} - -struct compare_vector -{ - bool operator()(const vector& a, - const vector& b) const - { - auto p1 = a.begin(); - auto p2 = b.begin(); - - while (p1 != a.end()) - { - if (p2 == b.end()) - { - return false; - } - if (*p2 > *p1) - { - return true; - } - if (*p1 > *p2) - { - return false; - } - - ++p1; - ++p2; - } - - if (p2 != b.end()) - { - return true; - } - - return false; - } -}; -} - -struct MatchResult::Priv -{ - bool m_success = true; - - map, vector, compare_vector> m_failures; - - chrono::time_point m_timeout = chrono::system_clock::now() + chrono::seconds(10); -}; - -MatchResult::MatchResult() : - p(new Priv) -{ -} - -MatchResult::MatchResult(MatchResult&& other) -{ - *this = move(other); -} - -MatchResult::MatchResult(const MatchResult& other) : - p(new Priv) -{ - *this = other; -} - -MatchResult& MatchResult::operator=(const MatchResult& other) -{ - p->m_success = other.p->m_success; - p->m_failures= other.p->m_failures; - return *this; -} - -MatchResult& MatchResult::operator=(MatchResult&& other) -{ - p = move(other.p); - return *this; -} - -MatchResult MatchResult::createChild() const -{ - MatchResult child; - child.p->m_timeout = p->m_timeout; - return child; -} - -void MatchResult::failure(const vector& location, const string& message) -{ - p->m_success = false; - auto it = p->m_failures.find(location); - if (it == p->m_failures.end()) - { - it = p->m_failures.insert(make_pair(location, vector())).first; - } - it->second.emplace_back(message); -} - -void MatchResult::merge(const MatchResult& other) -{ - p->m_success &= other.p->m_success; - for (const auto& e : other.p->m_failures) - { - p->m_failures.insert(make_pair(e.first, e.second)); - } -} - -bool MatchResult::success() const -{ - return p->m_success; -} - -bool MatchResult::hasTimedOut() const -{ - auto now = chrono::system_clock::now(); - return (now >= p->m_timeout); -} - -string MatchResult::concat_failures() const -{ - stringstream ss; - ss << "Failed expectations:" << endl; - for (const auto& failure : p->m_failures) - { - bool first = true; - for (const string& s: failure.second) - { - printLocation(ss, failure.first, first); - first = false; - ss << s << endl; - } - } - return ss.str(); -} - -} // namespace gmenuharness - -} // namespace unity diff --git a/src/gmenuharness/MatchUtils.cpp b/src/gmenuharness/MatchUtils.cpp deleted file mode 100644 index 7b87a25..0000000 --- a/src/gmenuharness/MatchUtils.cpp +++ /dev/null @@ -1,77 +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 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 . - * - * Authored by: Pete Woods - */ - -#include - -#include - -using namespace std; -namespace util = unity::util; - -namespace unity -{ - -namespace gmenuharness -{ - -void waitForCore (GObject * obj, const string& signalName, unsigned int timeout) { - shared_ptr loop(g_main_loop_new(nullptr, false), &g_main_loop_unref); - - /* Our two exit criteria */ - util::ResourcePtr> signal( - g_signal_connect_swapped(obj, signalName.c_str(), - G_CALLBACK(g_main_loop_quit), loop.get()), - [obj](gulong s) - { - g_signal_handler_disconnect(obj, s); - }); - - util::ResourcePtr> timer(g_timeout_add(timeout, - [](gpointer user_data) -> gboolean - { - g_main_loop_quit((GMainLoop *)user_data); - return G_SOURCE_CONTINUE; - }, - loop.get()), - &g_source_remove); - - /* Wait for sync */ - g_main_loop_run(loop.get()); -} - -void menuWaitForItems(const shared_ptr& menu, unsigned int timeout) -{ - waitForCore(G_OBJECT(menu.get()), "items-changed", timeout); -} - -void g_object_deleter(gpointer object) -{ - g_clear_object(&object); -} - -void gvariant_deleter(GVariant* varptr) -{ - if (varptr != nullptr) - { - g_variant_unref(varptr); - } -} - -} // namespace gmenuharness - -} // namespace unity diff --git a/src/gmenuharness/MenuItemMatcher.cpp b/src/gmenuharness/MenuItemMatcher.cpp deleted file mode 100644 index f39acef..0000000 --- a/src/gmenuharness/MenuItemMatcher.cpp +++ /dev/null @@ -1,1008 +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 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 . - * - * Authored by: Pete Woods - */ - -#include -#include -#include - -#include -#include -#include - -using namespace std; - -namespace unity -{ - -namespace gmenuharness -{ - -namespace -{ - -enum class LinkType -{ - any, - section, - submenu -}; - -static string bool_to_string(bool value) -{ - return value? "true" : "false"; -} - -static shared_ptr get_action_group_attribute(const shared_ptr& actionGroup, const gchar* attribute) -{ - shared_ptr value( - g_action_group_get_action_state(actionGroup.get(), attribute), - &gvariant_deleter); - return value; -} - -static shared_ptr get_attribute(const shared_ptr menuItem, const gchar* attribute) -{ - shared_ptr value( - g_menu_item_get_attribute_value(menuItem.get(), attribute, nullptr), - &gvariant_deleter); - return value; -} - -static string get_string_attribute(const shared_ptr menuItem, const gchar* attribute) -{ - string result; - char* temp = nullptr; - if (g_menu_item_get_attribute(menuItem.get(), attribute, "s", &temp)) - { - result = temp; - g_free(temp); - } - return result; -} - -static pair split_action(const string& action) -{ - auto index = action.find('.'); - - if (index == string::npos) - { - return make_pair(string(), action); - } - - return make_pair(action.substr(0, index), action.substr(index + 1, action.size())); -} - -static string type_to_string(MenuItemMatcher::Type type) -{ - switch(type) - { - case MenuItemMatcher::Type::plain: - return "plain"; - case MenuItemMatcher::Type::checkbox: - return "checkbox"; - case MenuItemMatcher::Type::radio: - return "radio"; - } - - return string(); -} -} - -struct MenuItemMatcher::Priv -{ - void all(MatchResult& matchResult, const vector& location, - const shared_ptr& menu, - map>& actions) - { - int count = g_menu_model_get_n_items(menu.get()); - - if (m_items.size() != (unsigned int) count) - { - matchResult.failure( - location, - "Expected " + to_string(m_items.size()) - + " children, but found " + to_string(count)); - return; - } - - for (size_t i = 0; i < m_items.size(); ++i) - { - const auto& matcher = m_items.at(i); - matcher.match(matchResult, location, menu, actions, i); - } - } - - void startsWith(MatchResult& matchResult, const vector& location, - const shared_ptr& menu, - map>& actions) - { - int count = g_menu_model_get_n_items(menu.get()); - if (m_items.size() > (unsigned int) count) - { - matchResult.failure( - location, - "Expected at least " + to_string(m_items.size()) - + " children, but found " + to_string(count)); - return; - } - - for (size_t i = 0; i < m_items.size(); ++i) - { - const auto& matcher = m_items.at(i); - matcher.match(matchResult, location, menu, actions, i); - } - } - - void endsWith(MatchResult& matchResult, const vector& location, - const shared_ptr& menu, - map>& actions) - { - int count = g_menu_model_get_n_items(menu.get()); - if (m_items.size() > (unsigned int) count) - { - matchResult.failure( - location, - "Expected at least " + to_string(m_items.size()) - + " children, but found " + to_string(count)); - return; - } - - // match the last N items - size_t j; - for (size_t i = count - m_items.size(), j = 0; i < count && j < m_items.size(); ++i, ++j) - { - const auto& matcher = m_items.at(j); - matcher.match(matchResult, location, menu, actions, i); - } - } - - Type m_type = Type::plain; - - Mode m_mode = Mode::all; - - LinkType m_linkType = LinkType::any; - - shared_ptr m_expectedSize; - - shared_ptr m_label; - - shared_ptr m_icon; - - map, vector> m_themed_icons; - - shared_ptr m_action; - - vector m_state_icons; - - vector>> m_attributes; - - vector m_not_exist_attributes; - - vector>> m_pass_through_attributes; - - shared_ptr m_isToggled; - - vector m_items; - - vector>> m_activations; - - vector>> m_setActionStates; - - double m_maxDifference = 0.0; -}; - -MenuItemMatcher MenuItemMatcher::checkbox() -{ - MenuItemMatcher matcher; - matcher.type(Type::checkbox); - return matcher; -} - -MenuItemMatcher MenuItemMatcher::radio() -{ - MenuItemMatcher matcher; - matcher.type(Type::radio); - return matcher; -} - -MenuItemMatcher::MenuItemMatcher() : - p(new Priv) -{ -} - -MenuItemMatcher::~MenuItemMatcher() -{ -} - -MenuItemMatcher::MenuItemMatcher(const MenuItemMatcher& other) : - p(new Priv) -{ - *this = other; -} - -MenuItemMatcher::MenuItemMatcher(MenuItemMatcher&& other) -{ - *this = move(other); -} - -MenuItemMatcher& MenuItemMatcher::operator=(const MenuItemMatcher& other) -{ - p->m_type = other.p->m_type; - p->m_mode = other.p->m_mode; - p->m_expectedSize = other.p->m_expectedSize; - p->m_label = other.p->m_label; - p->m_icon = other.p->m_icon; - p->m_themed_icons = other.p->m_themed_icons; - p->m_action = other.p->m_action; - p->m_state_icons = other.p->m_state_icons; - p->m_attributes = other.p->m_attributes; - p->m_not_exist_attributes = other.p->m_not_exist_attributes; - p->m_pass_through_attributes = other.p->m_pass_through_attributes; - p->m_isToggled = other.p->m_isToggled; - p->m_linkType = other.p->m_linkType; - p->m_items = other.p->m_items; - p->m_activations = other.p->m_activations; - p->m_setActionStates = other.p->m_setActionStates; - p->m_maxDifference = other.p->m_maxDifference; - return *this; -} - -MenuItemMatcher& MenuItemMatcher::operator=(MenuItemMatcher&& other) -{ - p = move(other.p); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::type(Type type) -{ - p->m_type = type; - return *this; -} - -MenuItemMatcher& MenuItemMatcher::label(const string& label) -{ - p->m_label = make_shared(label); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::action(const string& action) -{ - p->m_action = make_shared(action); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::state_icons(const std::vector& state_icons) -{ - p->m_state_icons = state_icons; - return *this; -} - -MenuItemMatcher& MenuItemMatcher::icon(const string& icon) -{ - p->m_icon = make_shared(icon); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::themed_icon(const std::string& iconName, const std::vector& icons) -{ - p->m_themed_icons[make_shared(iconName)] = icons; - return *this; -} - -MenuItemMatcher& MenuItemMatcher::widget(const string& widget) -{ - return string_attribute("x-canonical-type", widget); -} - -MenuItemMatcher& MenuItemMatcher::pass_through_attribute(const string& actionName, const shared_ptr& value) -{ - p->m_pass_through_attributes.emplace_back(actionName, value); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::pass_through_boolean_attribute(const string& actionName, bool value) -{ - return pass_through_attribute( - actionName, - shared_ptr(g_variant_new_boolean(value), - &gvariant_deleter)); -} - -MenuItemMatcher& MenuItemMatcher::pass_through_string_attribute(const string& actionName, const string& value) -{ - return pass_through_attribute( - actionName, - shared_ptr(g_variant_new_string(value.c_str()), - &gvariant_deleter)); -} - -MenuItemMatcher& MenuItemMatcher::pass_through_double_attribute(const std::string& actionName, double value) -{ - return pass_through_attribute( - actionName, - shared_ptr(g_variant_new_double(value), - &gvariant_deleter)); -} - -MenuItemMatcher& MenuItemMatcher::round_doubles(double maxDifference) -{ - p->m_maxDifference = maxDifference; - return *this; -} - -MenuItemMatcher& MenuItemMatcher::attribute(const string& name, const shared_ptr& value) -{ - p->m_attributes.emplace_back(name, value); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::boolean_attribute(const string& name, bool value) -{ - return attribute( - name, - shared_ptr(g_variant_new_boolean(value), - &gvariant_deleter)); -} - -MenuItemMatcher& MenuItemMatcher::string_attribute(const string& name, const string& value) -{ - return attribute( - name, - shared_ptr(g_variant_new_string(value.c_str()), - &gvariant_deleter)); -} - -MenuItemMatcher& MenuItemMatcher::int32_attribute(const std::string& name, int value) -{ - return attribute( - name, - shared_ptr(g_variant_new_int32 (value), - &gvariant_deleter)); -} - -MenuItemMatcher& MenuItemMatcher::int64_attribute(const std::string& name, int value) -{ - return attribute( - name, - shared_ptr(g_variant_new_int64 (value), - &gvariant_deleter)); -} - -MenuItemMatcher& MenuItemMatcher::double_attribute(const std::string& name, double value) -{ - return attribute( - name, - shared_ptr(g_variant_new_double (value), - &gvariant_deleter)); -} - -MenuItemMatcher& MenuItemMatcher::attribute_not_set(const std::string& name) -{ - p->m_not_exist_attributes.emplace_back (name); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::toggled(bool isToggled) -{ - p->m_isToggled = make_shared(isToggled); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::mode(Mode mode) -{ - p->m_mode = mode; - return *this; -} - -MenuItemMatcher& MenuItemMatcher::submenu() -{ - p->m_linkType = LinkType::submenu; - return *this; -} - -MenuItemMatcher& MenuItemMatcher::section() -{ - p->m_linkType = LinkType::section; - return *this; -} - -MenuItemMatcher& MenuItemMatcher::is_empty() -{ - return has_exactly(0); -} - -MenuItemMatcher& MenuItemMatcher::has_exactly(size_t children) -{ - p->m_expectedSize = make_shared(children); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::item(const MenuItemMatcher& item) -{ - p->m_items.emplace_back(item); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::item(MenuItemMatcher&& item) -{ - p->m_items.emplace_back(item); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::pass_through_activate(std::string const& action, const shared_ptr& parameter) -{ - p->m_activations.emplace_back(action, parameter); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::activate(const shared_ptr& parameter) -{ - p->m_activations.emplace_back(string(), parameter); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::set_pass_through_action_state(const std::string& action, const std::shared_ptr& state) -{ - p->m_setActionStates.emplace_back(action, state); - return *this; -} - -MenuItemMatcher& MenuItemMatcher::set_action_state(const std::shared_ptr& state) -{ - p->m_setActionStates.emplace_back("", state); - return *this; -} - -void MenuItemMatcher::match( - MatchResult& matchResult, - const vector& parentLocation, - const shared_ptr& menu, - map>& actions, - int index) const -{ - shared_ptr menuItem(g_menu_item_new_from_model(menu.get(), index), &g_object_deleter); - - vector location(parentLocation); - location.emplace_back(index); - - string action = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_ACTION); - - bool isCheckbox = false; - bool isRadio = false; - bool isToggled = false; - - pair idPair; - shared_ptr actionGroup; - shared_ptr state; - - if (!action.empty()) - { - idPair = split_action(action); - actionGroup = actions[idPair.first]; - state = shared_ptr(g_action_group_get_action_state(actionGroup.get(), - idPair.second.c_str()), - &gvariant_deleter); - auto attributeTarget = get_attribute(menuItem, G_MENU_ATTRIBUTE_TARGET); - - if (attributeTarget && state) - { - isToggled = g_variant_equal(state.get(), attributeTarget.get()); - isRadio = true; - } - else if (state - && g_variant_is_of_type(state.get(), G_VARIANT_TYPE_BOOLEAN)) - { - isToggled = g_variant_get_boolean(state.get()); - isCheckbox = true; - } - } - - Type actualType = Type::plain; - if (isCheckbox) - { - actualType = Type::checkbox; - } - else if (isRadio) - { - actualType = Type::radio; - } - - if (actualType != p->m_type) - { - matchResult.failure( - location, - "Expected " + type_to_string(p->m_type) + ", found " - + type_to_string(actualType)); - } - - // check themed icons - map, vector>::iterator iter; - for (iter = p->m_themed_icons.begin(); iter != p->m_themed_icons.end(); ++iter) - { - auto icon_val = g_menu_item_get_attribute_value(menuItem.get(), (*iter).first->c_str(), nullptr); - if (!icon_val) - { - matchResult.failure( - location, - "Expected themed icon " + (*(*iter).first) + " was not found"); - } - - auto gicon = g_icon_deserialize(icon_val); - if (!gicon || !G_IS_THEMED_ICON(gicon)) - { - matchResult.failure( - location, - "Expected attribute " + (*(*iter).first) + " is not a themed icon"); - } - auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon)); - int nb_icons = 0; - while(iconNames[nb_icons]) - { - ++nb_icons; - } - - if (nb_icons != (*iter).second.size()) - { - matchResult.failure( - location, - "Expected " + to_string((*iter).second.size()) + - " icons for themed icon [" + (*(*iter).first) + - "], but " + to_string(nb_icons) + " were found."); - } - else - { - // now compare all the icons - for (int i = 0; i < nb_icons; ++i) - { - if (string(iconNames[i]) != (*iter).second[i]) - { - matchResult.failure( - location, - "Icon at position " + to_string(i) + - " for themed icon [" + (*(*iter).first) + - "], mismatchs. Expected: " + iconNames[i] + " but found " + (*iter).second[i]); - } - } - } - g_object_unref(gicon); - } - - string label = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_LABEL); - if (p->m_label && (*p->m_label) != label) - { - matchResult.failure( - location, - "Expected label '" + *p->m_label + "', but found '" + label - + "'"); - } - - string icon = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_ICON); - if (p->m_icon && (*p->m_icon) != icon) - { - matchResult.failure( - location, - "Expected icon '" + *p->m_icon + "', but found '" + icon + "'"); - } - - if (p->m_action && (*p->m_action) != action) - { - matchResult.failure( - location, - "Expected action '" + *p->m_action + "', but found '" + action - + "'"); - } - - if (!p->m_state_icons.empty() && !state) - { - matchResult.failure( - location, - "Expected state icons but no state was found"); - } - else if (!p->m_state_icons.empty() && state && - !g_variant_is_of_type(state.get(), G_VARIANT_TYPE_VARDICT)) - { - matchResult.failure( - location, - "Expected state icons vardict, found " - + type_to_string(actualType)); - } - else if (!p->m_state_icons.empty() && state && - g_variant_is_of_type(state.get(), G_VARIANT_TYPE_VARDICT)) - { - std::vector actual_state_icons; - GVariantIter it; - gchar* key; - GVariant* value; - - g_variant_iter_init(&it, state.get()); - while (g_variant_iter_loop(&it, "{sv}", &key, &value)) - { - if (std::string(key) == "icon") { - auto gicon = g_icon_deserialize(value); - if (gicon && G_IS_THEMED_ICON(gicon)) - { - auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon)); - // Just take the first icon in the list (there is only ever one) - actual_state_icons.push_back(iconNames[0]); - g_object_unref(gicon); - } - } - else if (std::string(key) == "icons" && g_variant_is_of_type(value, G_VARIANT_TYPE("av"))) - { - // If we find "icons" in the map, clear any icons we may have found in "icon", - // then break from the loop as we have found all icons now. - actual_state_icons.clear(); - GVariantIter icon_it; - GVariant* icon_value; - - g_variant_iter_init(&icon_it, value); - while (g_variant_iter_loop(&icon_it, "v", &icon_value)) - { - auto gicon = g_icon_deserialize(icon_value); - if (gicon && G_IS_THEMED_ICON(gicon)) - { - auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon)); - // Just take the first icon in the list (there is only ever one) - actual_state_icons.push_back(iconNames[0]); - g_object_unref(gicon); - } - } - // We're breaking out of g_variant_iter_loop here so clean up - g_variant_unref(value); - g_free(key); - break; - } - } - - if (p->m_state_icons != actual_state_icons) - { - std::string expected_icons; - for (unsigned int i = 0; i < p->m_state_icons.size(); ++i) - { - expected_icons += i == 0 ? p->m_state_icons[i] : ", " + p->m_state_icons[i]; - } - std::string actual_icons; - for (unsigned int i = 0; i < actual_state_icons.size(); ++i) - { - actual_icons += i == 0 ? actual_state_icons[i] : ", " + actual_state_icons[i]; - } - matchResult.failure( - location, - "Expected state_icons == {" + expected_icons - + "} but found {" + actual_icons + "}"); - } - } - - for (const auto& e: p->m_pass_through_attributes) - { - string actionName = get_string_attribute(menuItem, e.first.c_str()); - if (actionName.empty()) - { - matchResult.failure( - location, - "Could not find action name '" + e.first + "'"); - } - else - { - auto passThroughIdPair = split_action(actionName); - auto actionGroup = actions[passThroughIdPair.first]; - if (actionGroup) - { - auto value = get_action_group_attribute( - actionGroup, passThroughIdPair.second.c_str()); - if (!value) - { - matchResult.failure( - location, - "Expected pass-through attribute '" + e.first - + "' was not present"); - } - else if (!g_variant_is_of_type(e.second.get(), g_variant_get_type(value.get()))) - { - std::string expectedType = g_variant_get_type_string(e.second.get()); - std::string actualType = g_variant_get_type_string(value.get()); - matchResult.failure( - location, - "Expected pass-through attribute type '" + expectedType - + "' but found '" + actualType + "'"); - } - else if (g_variant_compare(e.second.get(), value.get())) - { - bool reportMismatch = true; - if (g_strcmp0(g_variant_get_type_string(value.get()),"d") == 0 && p->m_maxDifference) - { - auto actualDouble = g_variant_get_double(value.get()); - auto expectedDouble = g_variant_get_double(e.second.get()); - auto difference = actualDouble-expectedDouble; - if (difference < 0) difference = difference * -1.0; - if (difference <= p->m_maxDifference) - { - reportMismatch = false; - } - } - if (reportMismatch) - { - gchar* expectedString = g_variant_print(e.second.get(), true); - gchar* actualString = g_variant_print(value.get(), true); - matchResult.failure( - location, - "Expected pass-through attribute '" + e.first - + "' == " + expectedString + " but found " - + actualString); - - g_free(expectedString); - g_free(actualString); - } - } - } - else - { - matchResult.failure(location, "Could not find action group for ID '" + passThroughIdPair.first + "'"); - } - } - } - - for (const auto& e: p->m_attributes) - { - auto value = get_attribute(menuItem, e.first.c_str()); - if (!value) - { - matchResult.failure(location, - "Expected attribute '" + e.first - + "' could not be found"); - } - else if (!g_variant_is_of_type(e.second.get(), g_variant_get_type(value.get()))) - { - std::string expectedType = g_variant_get_type_string(e.second.get()); - std::string actualType = g_variant_get_type_string(value.get()); - matchResult.failure( - location, - "Expected attribute type '" + expectedType - + "' but found '" + actualType + "'"); - } - else if (g_variant_compare(e.second.get(), value.get())) - { - gchar* expectedString = g_variant_print(e.second.get(), true); - gchar* actualString = g_variant_print(value.get(), true); - matchResult.failure( - location, - "Expected attribute '" + e.first + "' == " + expectedString - + ", but found " + actualString); - g_free(expectedString); - g_free(actualString); - } - } - - for (const auto& e: p->m_not_exist_attributes) - { - auto value = get_attribute(menuItem, e.c_str()); - if (value) - { - matchResult.failure(location, - "Not expected attribute '" + e - + "' was found"); - } - } - - if (p->m_isToggled && (*p->m_isToggled) != isToggled) - { - matchResult.failure( - location, - "Expected toggled = " + bool_to_string(*p->m_isToggled) - + ", but found " + bool_to_string(isToggled)); - } - - if (!matchResult.success()) - { - return; - } - - if (!p->m_items.empty() || p->m_expectedSize) - { - shared_ptr link; - - switch (p->m_linkType) - { - case LinkType::any: - { - link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SUBMENU), &g_object_deleter); - if (!link) - { - link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SECTION), &g_object_deleter); - } - break; - } - case LinkType::submenu: - { - link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SUBMENU), &g_object_deleter); - break; - } - case LinkType::section: - { - link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SECTION), &g_object_deleter); - break; - } - } - - - if (!link) - { - if (p->m_expectedSize) - { - matchResult.failure( - location, - "Expected " + to_string(*p->m_expectedSize) - + " children, but found none"); - } - else - { - matchResult.failure( - location, - "Expected " + to_string(p->m_items.size()) - + " children, but found none"); - } - return; - } - else - { - while (true) - { - MatchResult childMatchResult(matchResult.createChild()); - - if (p->m_expectedSize - && *p->m_expectedSize - != (unsigned int) g_menu_model_get_n_items( - link.get())) - { - childMatchResult.failure( - location, - "Expected " + to_string(*p->m_expectedSize) - + " child items, but found " - + to_string( - g_menu_model_get_n_items( - link.get()))); - } - else if (!p->m_items.empty()) - { - switch (p->m_mode) - { - case Mode::all: - p->all(childMatchResult, location, link, actions); - break; - case Mode::starts_with: - p->startsWith(childMatchResult, location, link, actions); - break; - case Mode::ends_with: - p->endsWith(childMatchResult, location, link, actions); - break; - } - } - - if (childMatchResult.success()) - { - matchResult.merge(childMatchResult); - break; - } - else - { - if (matchResult.hasTimedOut()) - { - matchResult.merge(childMatchResult); - break; - } - menuWaitForItems(link); - } - } - } - } - - - for (const auto& a: p->m_setActionStates) - { - auto stateAction = action; - auto stateIdPair = idPair; - auto stateActionGroup = actionGroup; - if (!a.first.empty()) - { - stateAction = get_string_attribute(menuItem, a.first.c_str());; - stateIdPair = split_action(stateAction); - stateActionGroup = actions[stateIdPair.first]; - } - - if (stateAction.empty()) - { - matchResult.failure( - location, - "Tried to set action state, but no action was found"); - } - else if(!stateActionGroup) - { - matchResult.failure( - location, - "Tried to set action state for action group '" + stateIdPair.first - + "', but action group wasn't found"); - } - else if (!g_action_group_has_action(stateActionGroup.get(), stateIdPair.second.c_str())) - { - matchResult.failure( - location, - "Tried to set action state for action '" + stateAction - + "', but action was not found"); - } - else - { - g_action_group_change_action_state(stateActionGroup.get(), stateIdPair.second.c_str(), - g_variant_ref(a.second.get())); - } - - // FIXME this is a dodgy way to ensure the action state change gets dispatched - menuWaitForItems(menu, 100); - } - - for (const auto& a: p->m_activations) - { - string tmpAction = action; - auto tmpIdPair = idPair; - auto tmpActionGroup = actionGroup; - if (!a.first.empty()) - { - tmpAction = get_string_attribute(menuItem, a.first.c_str()); - tmpIdPair = split_action(tmpAction); - tmpActionGroup = actions[tmpIdPair.first]; - } - - if (tmpAction.empty()) - { - matchResult.failure( - location, - "Tried to activate action, but no action was found"); - } - else if(!tmpActionGroup) - { - matchResult.failure( - location, - "Tried to activate action group '" + tmpIdPair.first - + "', but action group wasn't found"); - } - else if (!g_action_group_has_action(tmpActionGroup.get(), tmpIdPair.second.c_str())) - { - matchResult.failure( - location, - "Tried to activate action '" + tmpAction + "', but action was not found"); - } - else - { - if (a.second) - { - g_action_group_activate_action(tmpActionGroup.get(), tmpIdPair.second.c_str(), - g_variant_ref(a.second.get())); - } - else - { - g_action_group_activate_action(tmpActionGroup.get(), tmpIdPair.second.c_str(), nullptr); - } - - // FIXME this is a dodgy way to ensure the activation gets dispatched - menuWaitForItems(menu, 100); - } - } -} - -} // namepsace gmenuharness - -} // namespace unity diff --git a/src/gmenuharness/MenuMatcher.cpp b/src/gmenuharness/MenuMatcher.cpp deleted file mode 100644 index 5bb4fbd..0000000 --- a/src/gmenuharness/MenuMatcher.cpp +++ /dev/null @@ -1,208 +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 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 . - * - * Authored by: Pete Woods - */ - -#include -#include - -#include - -#include - -using namespace std; - -namespace unity -{ - -namespace gmenuharness -{ - -namespace -{ - -static void gdbus_connection_deleter(GDBusConnection* connection) -{ -// if (!g_dbus_connection_is_closed(connection)) -// { -// g_dbus_connection_close_sync(connection, nullptr, nullptr); -// } - g_clear_object(&connection); -} -} - -struct MenuMatcher::Parameters::Priv -{ - string m_busName; - - vector> m_actions; - - string m_menuObjectPath; -}; - -MenuMatcher::Parameters::Parameters(const string& busName, - const vector>& actions, - const string& menuObjectPath) : - p(new Priv) -{ - p->m_busName = busName; - p->m_actions = actions; - p->m_menuObjectPath = menuObjectPath; -} - -MenuMatcher::Parameters::~Parameters() -{ -} - -MenuMatcher::Parameters::Parameters(const Parameters& other) : - p(new Priv) -{ - *this = other; -} - -MenuMatcher::Parameters::Parameters(Parameters&& other) -{ - *this = move(other); -} - -MenuMatcher::Parameters& MenuMatcher::Parameters::operator=(const Parameters& other) -{ - p->m_busName = other.p->m_busName; - p->m_actions = other.p->m_actions; - p->m_menuObjectPath = other.p->m_menuObjectPath; - return *this; -} - -MenuMatcher::Parameters& MenuMatcher::Parameters::operator=(Parameters&& other) -{ - p = move(other.p); - return *this; -} - -struct MenuMatcher::Priv -{ - Priv(const Parameters& parameters) : - m_parameters(parameters) - { - } - - Parameters m_parameters; - - vector m_items; - - shared_ptr m_system; - - shared_ptr m_session; - - shared_ptr m_menu; - - map> m_actions; -}; - -MenuMatcher::MenuMatcher(const Parameters& parameters) : - p(new Priv(parameters)) -{ - p->m_system.reset(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), - &gdbus_connection_deleter); - g_dbus_connection_set_exit_on_close(p->m_system.get(), false); - - p->m_session.reset(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr), - &gdbus_connection_deleter); - g_dbus_connection_set_exit_on_close(p->m_session.get(), false); - - p->m_menu.reset( - G_MENU_MODEL( - g_dbus_menu_model_get( - p->m_session.get(), - p->m_parameters.p->m_busName.c_str(), - p->m_parameters.p->m_menuObjectPath.c_str())), - &g_object_deleter); - - for (const auto& action : p->m_parameters.p->m_actions) - { - shared_ptr actionGroup( - G_ACTION_GROUP( - g_dbus_action_group_get( - p->m_session.get(), - p->m_parameters.p->m_busName.c_str(), - action.second.c_str())), - &g_object_deleter); - p->m_actions[action.first] = actionGroup; - } -} - -MenuMatcher::~MenuMatcher() -{ -} - -MenuMatcher& MenuMatcher::item(const MenuItemMatcher& item) -{ - p->m_items.emplace_back(item); - return *this; -} - -void MenuMatcher::match(MatchResult& matchResult) const -{ - vector location; - - while (true) - { - MatchResult childMatchResult(matchResult.createChild()); - - int menuSize = g_menu_model_get_n_items(p->m_menu.get()); - if (p->m_items.size() > (unsigned int) menuSize) - { - childMatchResult.failure( - location, - "Row count mismatch, expected " + to_string(p->m_items.size()) - + " but found " + to_string(menuSize)); - } - else - { - for (size_t i = 0; i < p->m_items.size(); ++i) - { - const auto& matcher = p->m_items.at(i); - matcher.match(childMatchResult, location, p->m_menu, p->m_actions, i); - } - } - - if (childMatchResult.success()) - { - matchResult.merge(childMatchResult); - break; - } - else - { - if (matchResult.hasTimedOut()) - { - matchResult.merge(childMatchResult); - break; - } - menuWaitForItems(p->m_menu); - } - } -} - -MatchResult MenuMatcher::match() const -{ - MatchResult matchResult; - match(matchResult); - return matchResult; -} - -} // namespace gmenuharness - -} // namespace unity diff --git a/src/service.vala b/src/service.vala index 12e2ac2..a08edf3 100644 --- a/src/service.vala +++ b/src/service.vala @@ -43,7 +43,7 @@ public class IndicatorSound.Service: Object { warn_notification.closed.connect((n) => { n.clear_actions(); }); BusWatcher.watch_namespace (GLib.BusType.SESSION, "org.freedesktop.Notifications", - () => { debug("Notifications name appeared"); }, + () => { debug("Notifications name appeared"); notify_server_caps_checked = false; }, () => { debug("Notifications name vanshed"); notify_server_caps_checked = false; }); this.settings = new Settings ("com.canonical.indicator.sound"); @@ -52,8 +52,6 @@ public class IndicatorSound.Service: Object { this.notify["visible"].connect ( () => this.update_root_icon () ); this.volume_control = volume; - this.volume_control.active_output_changed.connect (this.update_root_icon); - this.volume_control.active_output_changed.connect (this.update_notification); this.accounts_service = accounts; /* If we're on the greeter, don't export */ @@ -92,10 +90,6 @@ public class IndicatorSound.Service: Object { this.volume_control.bind_property ("high-volume", menu, "show-high-volume-warning", BindingFlags.SYNC_CREATE); }); - this.menus.@foreach ( (profile, menu) => { - this.volume_control.active_output_changed.connect (menu.update_volume_slider); - }); - this.sync_preferred_players (); this.settings.changed["interested-media-players"].connect ( () => { this.sync_preferred_players (); @@ -251,7 +245,17 @@ public class IndicatorSound.Service: Object { void update_root_icon () { double volume = this.volume_control.volume.volume; - string icon = get_volume_root_icon (volume, this.volume_control.mute, volume_control.active_output); + string icon; + if (this.volume_control.mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; string accessible_name; if (this.volume_control.mute) { @@ -277,333 +281,15 @@ public class IndicatorSound.Service: Object { private bool notify_server_supports_actions = false; private bool notify_server_supports_sync = false; private bool block_info_notifications = false; - private bool waiting_user_approve_warn = false; - - private string get_volume_icon (double volume, VolumeControl.ActiveOutput active_output) - { - string icon = ""; - switch (active_output) - { - case VolumeControl.ActiveOutput.SPEAKERS: - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.HEADPHONES: - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.USB_SPEAKER: - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.USB_HEADPHONES: - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; - break; - } - return icon; - } - - private string get_volume_root_icon_by_volume (double volume, VolumeControl.ActiveOutput active_output) - { - string icon = ""; - switch (active_output) - { - case VolumeControl.ActiveOutput.SPEAKERS: - if (volume <= 0.0) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; - break; - case VolumeControl.ActiveOutput.HEADPHONES: - if (volume <= 0.0) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; - break; - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - if (volume <= 0.0) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; - break; - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - if (volume <= 0.0) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; - break; - case VolumeControl.ActiveOutput.USB_SPEAKER: - if (volume <= 0.0) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; - break; - case VolumeControl.ActiveOutput.USB_HEADPHONES: - if (volume <= 0.0) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; - break; - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - if (volume <= 0.0) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; - break; - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - if (volume <= 0.0) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; - break; - } - return icon; - } - - private string get_volume_notification_icon (double volume, bool loud, VolumeControl.ActiveOutput active_output) { - string icon = ""; - if (loud) { - switch (active_output) - { - case VolumeControl.ActiveOutput.SPEAKERS: - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.HEADPHONES: - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.USB_SPEAKER: - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.USB_HEADPHONES: - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - icon = "audio-volume-high"; - break; - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - icon = "audio-volume-high"; - break; - } - } else { - icon = get_volume_icon (volume, active_output); - } - return icon; - } - - private string get_volume_root_icon (double volume, bool mute, VolumeControl.ActiveOutput active_output) { - string icon = ""; - switch (active_output) - { - case VolumeControl.ActiveOutput.SPEAKERS: - if (mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else - icon = get_volume_root_icon_by_volume (volume, active_output); - break; - case VolumeControl.ActiveOutput.HEADPHONES: - if (mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else - icon = get_volume_root_icon_by_volume (volume, active_output); - break; - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - if (mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else - icon = get_volume_root_icon_by_volume (volume, active_output); - break; - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - if (mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else - icon = get_volume_root_icon_by_volume (volume, active_output); - break; - case VolumeControl.ActiveOutput.USB_SPEAKER: - if (mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else - icon = get_volume_root_icon_by_volume (volume, active_output); - break; - case VolumeControl.ActiveOutput.USB_HEADPHONES: - if (mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else - icon = get_volume_root_icon_by_volume (volume, active_output); - break; - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - if (mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else - icon = get_volume_root_icon_by_volume (volume, active_output); - break; - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - if (mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else - icon = get_volume_root_icon_by_volume (volume, active_output); - break; - } - return icon; - } - - private string get_notification_label () { - string volume_label = ""; - switch (volume_control.active_output) - { - case VolumeControl.ActiveOutput.SPEAKERS: - volume_label = _("Speakers"); - break; - case VolumeControl.ActiveOutput.HEADPHONES: - volume_label = _("Headphones"); - break; - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - volume_label = _("Bluetooth headphones"); - break; - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - volume_label = _("Bluetooth speaker"); - break; - case VolumeControl.ActiveOutput.USB_SPEAKER: - volume_label = _("Usb speaker"); - break; - case VolumeControl.ActiveOutput.USB_HEADPHONES: - volume_label = _("Usb headphones"); - break; - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - volume_label = _("HDMI speaker"); - break; - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - volume_label = _("HDMI headphones"); - break; - } - - return volume_label; - } private void update_notification () { - List caps = Notify.get_server_caps (); - notify_server_supports_actions = caps.find_custom ("actions", strcmp) != null; - notify_server_supports_sync = caps.find_custom ("x-canonical-private-synchronous", strcmp) != null; - notify_server_caps_checked = true; + if (!notify_server_caps_checked) { + List caps = Notify.get_server_caps (); + notify_server_supports_actions = caps.find_custom ("actions", strcmp) != null; + notify_server_supports_sync = caps.find_custom ("x-canonical-private-synchronous", strcmp) != null; + notify_server_caps_checked = true; + } var loud = volume_control.high_volume; var warn = loud @@ -626,36 +312,47 @@ public class IndicatorSound.Service: Object { _pre_warn_volume = null; volume_control.volume = tmp; } - waiting_user_approve_warn = false; }); warn_notification.add_action ("cancel", _("Cancel"), (n, a) => { _pre_warn_volume = null; - waiting_user_approve_warn = false; }); - waiting_user_approve_warn = true; show_notification(warn_notification); } else { - if (!waiting_user_approve_warn) { - close_notification(warn_notification); - - if (notify_server_supports_sync && !block_info_notifications) { - - /* Determine Label */ - string volume_label = get_notification_label (); - - /* Choose an icon */ - string icon = get_volume_notification_icon (volume_control.volume.volume, loud, volume_control.active_output); - - /* Reset the notification */ - var n = this.info_notification; - n.update (_("Volume"), volume_label, icon); - n.clear_hints(); - n.set_hint ("x-canonical-non-shaped-icon", "true"); - n.set_hint ("x-canonical-private-synchronous", "true"); - n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false"); - n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0)); - show_notification(n); + close_notification(warn_notification); + + if (notify_server_supports_sync && !block_info_notifications) { + + /* Determine Label */ + unowned string volume_label = loud + ? _("High volume can damage your hearing.") + : ""; + + /* Choose an icon */ + unowned string icon; + if (loud) { + icon = "audio-volume-high"; + } else { + var volume = volume_control.volume.volume; + + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; } + + /* Reset the notification */ + var n = this.info_notification; + n.update (_("Volume"), volume_label, icon); + n.clear_hints(); + n.set_hint ("x-canonical-non-shaped-icon", "true"); + n.set_hint ("x-canonical-private-synchronous", "true"); + n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false"); + n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0)); + show_notification(n); } } } diff --git a/src/sound-menu.vala b/src/sound-menu.vala index 3d682e4..7a6044b 100644 --- a/src/sound-menu.vala +++ b/src/sound-menu.vala @@ -71,7 +71,6 @@ public class SoundMenu: Object this.notify_handlers = new HashTable (direct_hash, direct_equal); this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0; - } ~SoundMenu () { @@ -194,43 +193,6 @@ public class SoundMenu: Object this.notify_handlers.remove (player); } - public void update_volume_slider (VolumeControl.ActiveOutput active_output) { - int index = find_action (this.volume_section, "indicator.volume"); - if (index != -1) { - string label = "Volume"; - switch (active_output) { - case VolumeControl.ActiveOutput.SPEAKERS: - label = _("Volume"); - break; - case VolumeControl.ActiveOutput.HEADPHONES: - label = _("Volume (Headphones)"); - break; - case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: - label = _("Volume (Bluetooth)"); - break; - case VolumeControl.ActiveOutput.USB_SPEAKER: - label = _("Volume (Usb)"); - break; - case VolumeControl.ActiveOutput.HDMI_SPEAKER: - label = _("Volume (HDMI)"); - break; - case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: - label = _("Volume (Bluetooth headphones)"); - break; - case VolumeControl.ActiveOutput.USB_HEADPHONES: - label = _("Volume (Usb headphones)"); - break; - case VolumeControl.ActiveOutput.HDMI_HEADPHONES: - label = _("Volume (HDMI headphones)"); - break; - } - this.volume_section.remove (index); - this.volume_section.insert_item (index, this.create_slider_menu_item (_(label), "indicator.volume(0)", 0.0, 1.0, 0.01, - "audio-volume-low-zero-panel", - "audio-volume-high-panel")); - } - } - public Menu root; public Menu menu; Menu volume_section; diff --git a/src/volume-control-pulse.vala b/src/volume-control-pulse.vala index 8122f26..87af258 100644 --- a/src/volume-control-pulse.vala +++ b/src/volume-control-pulse.vala @@ -87,7 +87,6 @@ public class VolumeControlPulse : VolumeControl private bool _send_next_local_volume = false; private double _account_service_volume = 0.0; private bool _active_port_headphone = false; - private VolumeControl.ActiveOutput _active_output = VolumeControl.ActiveOutput.SPEAKERS; /** true when connected to the pulse server */ public override bool ready { get; private set; } @@ -136,49 +135,6 @@ public class VolumeControlPulse : VolumeControl stop_high_volume_approved_timer(); } - private VolumeControl.ActiveOutput calculate_active_output (SinkInfo? sink) { - - VolumeControl.ActiveOutput ret_output = VolumeControl.ActiveOutput.SPEAKERS; - /* Check if the current active port is headset/headphone */ - /* There is not easy way to check if the port is a headset/headphone besides - * checking for the port name. On touch (with the pulseaudio droid element) - * the headset/headphone port is called 'output-headset' and 'output-headphone'. - * On the desktop this is usually called 'analog-output-headphones' */ - // look if it's a headset/headphones - if (sink.name == "indicator_sound_test_headphones" || - (sink.active_port != null && - (sink.active_port.name.contains("headset") || - sink.active_port.name.contains("headphone")))) { - _active_port_headphone = true; - // check if it's a bluetooth device - var device_bus = sink.proplist.gets ("device.bus"); - if (device_bus != null && device_bus == "bluetooth") { - ret_output = VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES; - } else if (device_bus != null && device_bus == "usb") { - ret_output = VolumeControl.ActiveOutput.USB_HEADPHONES; - } else if (device_bus != null && device_bus == "hdmi") { - ret_output = VolumeControl.ActiveOutput.HDMI_HEADPHONES; - } else { - ret_output = VolumeControl.ActiveOutput.HEADPHONES; - } - } else { - // speaker - _active_port_headphone = false; - var device_bus = sink.proplist.gets ("device.bus"); - if (device_bus != null && device_bus == "bluetooth") { - ret_output = VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER; - } else if (device_bus != null && device_bus == "usb") { - ret_output = VolumeControl.ActiveOutput.USB_SPEAKER; - } else if (device_bus != null && device_bus == "hdmi") { - ret_output = VolumeControl.ActiveOutput.HDMI_SPEAKER; - } else { - ret_output = VolumeControl.ActiveOutput.SPEAKERS; - } - } - - return ret_output; - } - /* PulseAudio logic*/ private void context_events_cb (Context c, Context.SubscriptionEventType t, uint32 index) { @@ -245,20 +201,18 @@ public class VolumeControlPulse : VolumeControl this.notify_property ("is-playing"); } - // store the current status of the active output - VolumeControl.ActiveOutput active_output_before = active_output; - - // calculate the output - _active_output = calculate_active_output (i); - - // check if the output has changed, if so... emit a signal - VolumeControl.ActiveOutput active_output_now = active_output; - if (active_output_now != active_output_before) { - this.active_output_changed (active_output_now); - if (active_output_now == VolumeControl.ActiveOutput.SPEAKERS) { - _high_volume_approved = false; - } - update_high_volume(); + /* Check if the current active port is headset/headphone */ + /* There is not easy way to check if the port is a headset/headphone besides + * checking for the port name. On touch (with the pulseaudio droid element) + * the headset/headphone port is called 'output-headset' and 'output-headphone'. + * On the desktop this is usually called 'analog-output-headphones' */ + if (i.active_port != null && + (i.active_port.name == "output-wired_headset" || + i.active_port.name == "output-wired_headphone" || + i.active_port.name == "analog-output-headphones")) { + _active_port_headphone = true; + } else { + _active_port_headphone = false; } if (_pulse_use_stream_restore == false && @@ -524,8 +478,7 @@ public class VolumeControlPulse : VolumeControl this.context = new PulseAudio.Context (loop.get_api(), null, props); this.context.set_state_callback (context_state_callback); - var server_string = Environment.get_variable("PULSE_SERVER"); - if (context.connect(server_string, Context.Flags.NOFAIL, null) < 0) + if (context.connect(null, Context.Flags.NOFAIL, null) < 0) warning( "pa_context_connect() failed: %s\n", PulseAudio.strerror(context.errno())); } @@ -582,14 +535,6 @@ public class VolumeControlPulse : VolumeControl } } - public override VolumeControl.ActiveOutput active_output - { - get - { - return _active_output; - } - } - /* Volume operations */ private static PulseAudio.Volume double_to_volume (double vol) { @@ -765,7 +710,7 @@ public class VolumeControlPulse : VolumeControl private bool calculate_high_volume_from_volume(double volume) { return _active_port_headphone && _warning_volume_enabled - && volume > _warning_volume_norms + && volume >= _warning_volume_norms && (stream == "multimedia"); } diff --git a/src/volume-control.vala b/src/volume-control.vala index 8e615ea..6efac35 100644 --- a/src/volume-control.vala +++ b/src/volume-control.vala @@ -28,17 +28,6 @@ public abstract class VolumeControl : Object VOLUME_STREAM_CHANGE } - public enum ActiveOutput { - SPEAKERS, - HEADPHONES, - BLUETOOTH_HEADPHONES, - BLUETOOTH_SPEAKER, - USB_SPEAKER, - USB_HEADPHONES, - HDMI_SPEAKER, - HDMI_HEADPHONES - } - public class Volume : Object { public double volume; public VolumeReasons reason; @@ -50,7 +39,6 @@ public abstract class VolumeControl : Object public virtual bool high_volume { get { return false; } protected set { } } public virtual bool mute { get { return false; } } public virtual bool is_playing { get { return false; } } - public virtual VolumeControl.ActiveOutput active_output { get { return VolumeControl.ActiveOutput.SPEAKERS; } } private Volume _volume; public virtual Volume volume { get { return _volume; } set { } } public virtual double mic_volume { get { return 0.0; } set { } } @@ -68,6 +56,4 @@ public abstract class VolumeControl : Object v.reason = reason; this.volume = v; } - - public signal void active_output_changed (VolumeControl.ActiveOutput active_output); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f77bcb1..2bbd8c5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,10 +5,10 @@ include_directories(${GTEST_INCLUDE_DIR}) -add_library (gtest-static STATIC +add_library (gtest STATIC ${GTEST_SOURCE_DIR}/gtest-all.cc ${GTEST_SOURCE_DIR}/gtest_main.cc) -target_link_libraries(gtest-static ${GTEST_LIBS}) +target_link_libraries(gtest ${GTEST_LIBS}) ########################### # GSettings Schema @@ -22,8 +22,7 @@ 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 (XDG_DATA_DIRS "${CMAKE_CURRENT_BINARY_DIR}/gsettings-schemas") -set (SCHEMA_DIR "${XDG_DATA_DIRS}/glib-2.0/schemas") +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 @@ -32,7 +31,6 @@ 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 cp -f /usr/share/glib-2.0/schemas/com.ubuntu.sound.gschema.xml ${SCHEMA_DIR} COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR}) ########################### @@ -43,74 +41,74 @@ set(VALA_MOCKS_HEADER_PATH "${CMAKE_CURRENT_BINARY_DIR}/vala-mocks.h") set(VALA_MOCKS_SYMBOLS_PATH "${CMAKE_CURRENT_BINARY_DIR}/vala-mocks.def") vala_init(vala-mocks - DEPENDS - indicator-sound-service-lib - PACKAGES - config - gio-2.0 - gio-unix-2.0 - libxml-2.0 - libpulse - libpulse-mainloop-glib - libnotify - accounts-service - indicator-sound-service - OPTIONS - --ccode - --thread - --vapidir=${CMAKE_BINARY_DIR}/src/ - --vapidir=${CMAKE_SOURCE_DIR}/vapi/ - --vapidir=. + DEPENDS + indicator-sound-service-lib + PACKAGES + config + gio-2.0 + gio-unix-2.0 + libxml-2.0 + libpulse + libpulse-mainloop-glib + libnotify + accounts-service + indicator-sound-service + OPTIONS + --ccode + --thread + --vapidir=${CMAKE_BINARY_DIR}/src/ + --vapidir=${CMAKE_SOURCE_DIR}/vapi/ + --vapidir=. ) vala_add(vala-mocks - media-player-mock.vala + media-player-mock.vala ) vala_add(vala-mocks - media-player-list-mock.vala + media-player-list-mock.vala ) vala_add(vala-mocks - volume-control-mock.vala + volume-control-mock.vala ) vala_finish(vala-mocks - SOURCES - vala_mocks_VALA_SOURCES - OUTPUTS - vala_mocks_VALA_C - GENERATE_HEADER - ${VALA_MOCKS_HEADER_PATH} - GENERATE_SYMBOLS - ${VALA_MOCKS_SYMBOLS_PATH} + SOURCES + vala_mocks_VALA_SOURCES + OUTPUTS + vala_mocks_VALA_C + GENERATE_HEADER + ${VALA_MOCKS_HEADER_PATH} + GENERATE_SYMBOLS + ${VALA_MOCKS_SYMBOLS_PATH} ) set_source_files_properties( - ${vala_mocks_VALA_SOURCES} - PROPERTIES - HEADER_FILE_ONLY TRUE + ${vala_mocks_VALA_SOURCES} + PROPERTIES + HEADER_FILE_ONLY TRUE ) set( - VALA_MOCKS_SOURCES - ${vala_mocks_VALA_SOURCES} - ${vala_mocks_VALA_C} - ${VALA_MOCKS_SYMBOLS_PATH} + VALA_MOCKS_SOURCES + ${vala_mocks_VALA_SOURCES} + ${vala_mocks_VALA_C} + ${VALA_MOCKS_SYMBOLS_PATH} ) add_definitions( - -Wno-unused-but-set-variable + -Wno-unused-but-set-variable ) add_library( - vala-mocks-lib STATIC - ${VALA_MOCKS_SOURCES} + vala-mocks-lib STATIC + ${VALA_MOCKS_SOURCES} ) target_link_libraries( - vala-mocks-lib - indicator-sound-service-lib + vala-mocks-lib + indicator-sound-service-lib ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) @@ -120,9 +118,9 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) ########################### add_library( - pulse-mock - SHARED - pa-mock.cpp + pulse-mock + SHARED + pa-mock.cpp ) target_link_libraries (pulse-mock ${PULSEAUDIO_LIBRARIES}) @@ -133,7 +131,7 @@ target_link_libraries (pulse-mock ${PULSEAUDIO_LIBRARIES}) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (name-watch-test name-watch-test.cc ${CMAKE_SOURCE_DIR}/src/bus-watch-namespace.c) -target_link_libraries (name-watch-test gtest-static ${SOUNDSERVICE_LIBRARIES}) +target_link_libraries (name-watch-test gtest ${SOUNDSERVICE_LIBRARIES}) add_test(name-watch-test name-watch-test) ########################### @@ -143,21 +141,21 @@ add_test(name-watch-test name-watch-test) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (accounts-service-user-test accounts-service-user.cc) target_link_libraries ( - accounts-service-user-test - indicator-sound-service-lib - vala-mocks-lib - gtest-static - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + accounts-service-user-test + indicator-sound-service-lib + vala-mocks-lib + gtest + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) # Split tests to work around libaccountservice sucking add_test(accounts-service-user-test-basic - accounts-service-user-test --gtest_filter=AccountsServiceUserTest.BasicObject + accounts-service-user-test --gtest_filter=AccountsServiceUserTest.BasicObject ) add_test(accounts-service-user-test-player - accounts-service-user-test --gtest_filter=AccountsServiceUserTest.SetMediaPlayer + accounts-service-user-test --gtest_filter=AccountsServiceUserTest.SetMediaPlayer ) ########################### @@ -167,11 +165,11 @@ add_test(accounts-service-user-test-player include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (volume-control-test volume-control-test.cc gschemas.compiled) target_link_libraries ( - volume-control-test - indicator-sound-service-lib - pulse-mock - gtest-static - ${TEST_LIBRARIES} + volume-control-test + indicator-sound-service-lib + pulse-mock + gtest + ${TEST_LIBRARIES} ) add_test(volume-control-test volume-control-test) @@ -183,12 +181,12 @@ add_test(volume-control-test volume-control-test) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (sound-menu-test sound-menu.cc) target_link_libraries ( - sound-menu-test - indicator-sound-service-lib - vala-mocks-lib - gtest-static - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + sound-menu-test + indicator-sound-service-lib + vala-mocks-lib + gtest + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) add_test(sound-menu-test sound-menu-test) @@ -200,13 +198,13 @@ add_test(sound-menu-test sound-menu-test) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (notifications-test notifications-test.cc) target_link_libraries ( - notifications-test - indicator-sound-service-lib - vala-mocks-lib - pulse-mock - gtest-static - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + notifications-test + indicator-sound-service-lib + vala-mocks-lib + pulse-mock + gtest + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) add_test(notifications-test notifications-test) @@ -218,23 +216,23 @@ add_test(notifications-test notifications-test) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (media-player-user-test media-player-user.cc) target_link_libraries ( - media-player-user-test - indicator-sound-service-lib - vala-mocks-lib - gtest-static - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + media-player-user-test + indicator-sound-service-lib + vala-mocks-lib + gtest + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) # Split tests to work around libaccountservice sucking add_test(media-player-user-test-basic - media-player-user-test --gtest_filter=MediaPlayerUserTest.BasicObject + media-player-user-test --gtest_filter=MediaPlayerUserTest.BasicObject ) add_test(media-player-user-test-dataset - media-player-user-test --gtest_filter=MediaPlayerUserTest.DataSet + media-player-user-test --gtest_filter=MediaPlayerUserTest.DataSet ) add_test(media-player-user-test-timeout - media-player-user-test --gtest_filter=MediaPlayerUserTest.TimeoutTest + media-player-user-test --gtest_filter=MediaPlayerUserTest.TimeoutTest ) ########################### @@ -244,20 +242,20 @@ add_test(media-player-user-test-timeout include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (greeter-list-test greeter-list.cc) target_link_libraries ( - greeter-list-test - indicator-sound-service-lib - vala-mocks-lib - gtest-static - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + greeter-list-test + indicator-sound-service-lib + vala-mocks-lib + gtest + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) # Split tests to work around libaccountservice sucking add_test(greeter-list-test-basic - greeter-list-test --gtest_filter=GreeterListTest.BasicObject + greeter-list-test --gtest_filter=GreeterListTest.BasicObject ) add_test(greeter-list-test-iterator - greeter-list-test --gtest_filter=GreeterListTest.BasicIterator + greeter-list-test --gtest_filter=GreeterListTest.BasicIterator ) ########################### @@ -265,23 +263,18 @@ add_test(greeter-list-test-iterator ########################### add_definitions( - -DINDICATOR_SOUND_SERVICE_BINARY="${CMAKE_BINARY_DIR}/src/indicator-sound-service" - -DPA_MOCK_LIB="${CMAKE_CURRENT_BINARY_DIR}/libpulse-mock.so" + -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-static - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + indicator-test + gtest + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) # Split tests to work around libaccountservice sucking add_test(indcator-test - indicator-test + indicator-test ) - -# Disable integration tests -# add_subdirectory(integration) -# add_subdirectory(dbus-types) -# add_subdirectory(service-mocks) diff --git a/tests/dbus-types/CMakeLists.txt b/tests/dbus-types/CMakeLists.txt deleted file mode 100644 index cb7f512..0000000 --- a/tests/dbus-types/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -find_package(Qt5DBus REQUIRED) -include_directories(${Qt5DBus_INCLUDE_DIRS}) - -add_definitions(-DQT_NO_KEYWORDS=1) - -set(dbusinterface_streamrestore_xml "org.PulseAudio.Ext.StreamRestore1.xml") -set_source_files_properties(${dbusinterface_streamrestore_xml} PROPERTIES - CLASSNAME StreamRestoreInterface) - -set(dbusinterface_accounts_xml "org.freedesktop.Accounts.xml") -set_source_files_properties(${dbusinterface_accounts_xml} PROPERTIES - CLASSNAME AccountsInterface) - -set(dbusinterface_accountssound_xml "com.ubuntu.AccountsService.Sound.xml") -set_source_files_properties(${dbusinterface_accountssound_xml} PROPERTIES - CLASSNAME AccountsSoundInterface) - -set(dbusinterface_properties_xml "org.freedesktop.DBus.Properties.xml") -set_source_files_properties(${dbusinterface_properties_xml} PROPERTIES - CLASSNAME DBusPropertiesInterface - NO_NAMESPACE YES - INCLUDE "dbus-types.h") - -set(dbusinterface_actions_xml "org.gtk.Actions.xml") -set_source_files_properties(${dbusinterface_actions_xml} PROPERTIES - CLASSNAME MenusInterface) - -set(dbusinterface_notifications_xml "org.freedesktop.Notifications.xml") -set_source_files_properties(${dbusinterface_notifications_xml} PROPERTIES - CLASSNAME NotificationsInterface) - -qt5_add_dbus_interface(interface_files ${dbusinterface_streamrestore_xml} stream_restore_interface) -qt5_add_dbus_interface(interface_files ${dbusinterface_properties_xml} dbus_properties_interface) -qt5_add_dbus_interface(interface_files ${dbusinterface_accounts_xml} dbus_accounts_interface) -qt5_add_dbus_interface(interface_files ${dbusinterface_accountssound_xml} dbus_accountssound_interface) -qt5_add_dbus_interface(interface_files ${dbusinterface_actions_xml} dbus_menus_interface) -qt5_add_dbus_interface(interface_files ${dbusinterface_notifications_xml} dbus_notifications_interface) - -add_library( - sound-indicator-dbus-interfaces - STATIC - ${interface_files} - pulseaudio-volume.cpp -) - -qt5_use_modules( - sound-indicator-dbus-interfaces - Core - DBus -) diff --git a/tests/dbus-types/com.ubuntu.AccountsService.Sound.xml b/tests/dbus-types/com.ubuntu.AccountsService.Sound.xml deleted file mode 100644 index 27c915a..0000000 --- a/tests/dbus-types/com.ubuntu.AccountsService.Sound.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/tests/dbus-types/dbus-types.h b/tests/dbus-types/dbus-types.h deleted file mode 100644 index b75acf0..0000000 --- a/tests/dbus-types/dbus-types.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#pragma once - -#include -#include "pulseaudio-volume.h" - -namespace DBusTypes -{ - inline void registerMetaTypes() - { - PulseaudioVolume::registerMetaType(); - PulseaudioVolumeArray::registerMetaType(); - } - - static constexpr char const* DBUS_NAME = "com.canonical.indicator.sound"; - - static constexpr char const* DBUS_PULSE = "org.PulseAudio1"; - - static constexpr char const* ACCOUNTS_SERVICE = "org.freedesktop.Accounts"; - - static constexpr char const* STREAM_RESTORE_NAME = "org.PulseAudio.Ext.StreamRestore1"; - - static constexpr char const* STREAM_RESTORE_PATH = "/org/pulseaudio/stream_restore1"; - - static constexpr char const* STREAM_RESTORE_ENTRY_NAME = "org.PulseAudio.Ext.StreamRestore1.RestoreEntry"; - - static constexpr char const* MAIN_SERVICE_PATH = "/com/canonical/indicator/sound"; - - static constexpr char const* ACTIONS_INTERFACE = "org.gtk.Actions"; - -} // namespace DBusTypes - diff --git a/tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml b/tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml deleted file mode 100644 index bf9af76..0000000 --- a/tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/tests/dbus-types/org.freedesktop.Accounts.xml b/tests/dbus-types/org.freedesktop.Accounts.xml deleted file mode 100644 index e308120..0000000 --- a/tests/dbus-types/org.freedesktop.Accounts.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/tests/dbus-types/org.freedesktop.DBus.Properties.xml b/tests/dbus-types/org.freedesktop.DBus.Properties.xml deleted file mode 100644 index bdf6cad..0000000 --- a/tests/dbus-types/org.freedesktop.DBus.Properties.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/dbus-types/org.freedesktop.Notifications.xml b/tests/dbus-types/org.freedesktop.Notifications.xml deleted file mode 100644 index f7d923e..0000000 --- a/tests/dbus-types/org.freedesktop.Notifications.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/dbus-types/org.gtk.Actions.xml b/tests/dbus-types/org.gtk.Actions.xml deleted file mode 100644 index b691f1f..0000000 --- a/tests/dbus-types/org.gtk.Actions.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/tests/dbus-types/pulseaudio-volume.cpp b/tests/dbus-types/pulseaudio-volume.cpp deleted file mode 100644 index 8ee305f..0000000 --- a/tests/dbus-types/pulseaudio-volume.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#include "dbus-types.h" - -PulseaudioVolume::PulseaudioVolume() : - type_(0), - volume_(10) -{ -} - -PulseaudioVolume::PulseaudioVolume(unsigned int type, unsigned int volume) : - type_(type) - , volume_(volume) -{ -} - -PulseaudioVolume::PulseaudioVolume(const PulseaudioVolume &other) : - type_(other.type_), - volume_(other.volume_) -{ -} - -PulseaudioVolume& PulseaudioVolume::operator=(const PulseaudioVolume &other) -{ - type_ = other.type_; - volume_ = other.volume_; - - return *this; -} - -PulseaudioVolume::~PulseaudioVolume() -{ -} - -unsigned int PulseaudioVolume::getType() const -{ - return type_; -} - -unsigned int PulseaudioVolume::getVolume() const -{ - return volume_; -} - -void PulseaudioVolume::registerMetaType() -{ - qRegisterMetaType("PulseaudioVolume"); - - qDBusRegisterMetaType(); -} - -QDBusArgument &operator<<(QDBusArgument &argument, const PulseaudioVolume& volume) -{ - argument.beginStructure(); - argument << volume.type_; - argument << volume.volume_; - argument.endStructure(); - - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, PulseaudioVolume &volume) -{ - argument.beginStructure(); - argument >> volume.type_; - argument >> volume.volume_; - argument.endStructure(); - - return argument; -} - -PulseaudioVolumeArray::PulseaudioVolumeArray() -{ -} - -PulseaudioVolumeArray::PulseaudioVolumeArray(const PulseaudioVolumeArray &other) : - volume_array_(other.volume_array_) -{ -} - -PulseaudioVolumeArray& PulseaudioVolumeArray::operator=(const PulseaudioVolumeArray &other) -{ - volume_array_ = other.volume_array_; - - return *this; -} - -PulseaudioVolumeArray::~PulseaudioVolumeArray() -{ -} - -int PulseaudioVolumeArray::getNumItems() const -{ - return volume_array_.size(); -} - -PulseaudioVolume PulseaudioVolumeArray::getItem(int i) const -{ - if (i < volume_array_.size()) - { - return volume_array_[i]; - } - return PulseaudioVolume(); -} - -void PulseaudioVolumeArray::addItem(PulseaudioVolume const &item) -{ - volume_array_.push_back(item); -} - -void PulseaudioVolumeArray::registerMetaType() -{ - qRegisterMetaType("PulseaudioVolumeArray"); - - qDBusRegisterMetaType(); -} - -QDBusArgument &operator<<(QDBusArgument &argument, const PulseaudioVolumeArray& volume) -{ - argument.beginArray( qMetaTypeId() ); - for (int i = 0; i < volume.volume_array_.size(); ++ i) - { - PulseaudioVolume item = volume.getItem(i); - argument << item; - } - argument.endArray(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, PulseaudioVolumeArray &volume) -{ - argument.beginArray(); - while ( !argument.atEnd() ) { - PulseaudioVolume item; - argument >> item; - volume.volume_array_.push_back(item); - } - argument.endArray(); - - return argument; -} diff --git a/tests/dbus-types/pulseaudio-volume.h b/tests/dbus-types/pulseaudio-volume.h deleted file mode 100644 index 6d5a222..0000000 --- a/tests/dbus-types/pulseaudio-volume.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#pragma once - -#include - -class PulseaudioVolume -{ -public: - PulseaudioVolume(); - PulseaudioVolume(unsigned int type, unsigned int volume); - PulseaudioVolume(const PulseaudioVolume &other); - PulseaudioVolume& operator=(const PulseaudioVolume &other); - ~PulseaudioVolume(); - - friend QDBusArgument &operator<<(QDBusArgument &argument, PulseaudioVolume const & volume); - friend const QDBusArgument &operator>>(QDBusArgument const & argument, PulseaudioVolume &volume); - - unsigned int getType() const; - unsigned int getVolume() const; - - //register Message with the Qt type system - static void registerMetaType(); - -private: - unsigned int type_; - unsigned int volume_; -}; - -class PulseaudioVolumeArray -{ -public: - PulseaudioVolumeArray(); - PulseaudioVolumeArray(QString const &interface, QString const &property, QDBusVariant const& value); - PulseaudioVolumeArray(const PulseaudioVolumeArray &other); - PulseaudioVolumeArray& operator=(const PulseaudioVolumeArray &other); - ~PulseaudioVolumeArray(); - - friend QDBusArgument &operator<<(QDBusArgument &argument, PulseaudioVolumeArray const & volume); - friend const QDBusArgument &operator>>(QDBusArgument const & argument, PulseaudioVolumeArray &volume); - - int getNumItems() const; - PulseaudioVolume getItem(int i) const; - void addItem(PulseaudioVolume const &item); - - //register Message with the Qt type system - static void registerMetaType(); - -private: - QVector volume_array_; -}; - -Q_DECLARE_METATYPE(PulseaudioVolume) -Q_DECLARE_METATYPE(PulseaudioVolumeArray) diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt deleted file mode 100644 index 939e329..0000000 --- a/tests/integration/CMakeLists.txt +++ /dev/null @@ -1,132 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -include(FindGMock) - -#pkg_check_modules(GMENUHARNESS REQUIRED libgmenuharness REQUIRED) -#include_directories(${GMENUHARNESS_INCLUDE_DIRS}) -include_directories("${CMAKE_SOURCE_DIR}/include") - -pkg_check_modules(QTDBUSTEST REQUIRED libqtdbustest-1 REQUIRED) -include_directories(${QTDBUSTEST_INCLUDE_DIRS}) - -pkg_check_modules(QTDBUSMOCK REQUIRED libqtdbusmock-1 REQUIRED) -include_directories(${QTDBUSMOCK_INCLUDE_DIRS}) - -find_package(Qt5Test REQUIRED) -include_directories(${Qt5Test_INCLUDE_DIRS}) - -find_package(Qt5DBus REQUIRED) -include_directories(${Qt5DBus_INCLUDE_DIRS}) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${GMOCK_INCLUDE_DIRS}) -include_directories(${GTEST_INCLUDE_DIRS}) - -include_directories("${CMAKE_SOURCE_DIR}/tests/dbus-types") -include_directories("${CMAKE_BINARY_DIR}/tests/dbus-types") - -add_definitions(-DSOUND_SERVICE_BIN="${CMAKE_BINARY_DIR}/src/indicator-sound-service" - -DSTREAM_RESTORE_TABLE="${CMAKE_SOURCE_DIR}/tests/integration/touch-stream-restore.table" - -DVOLUME_SET_BIN="${CMAKE_BINARY_DIR}/tests/integration/set-volume" - -DACCOUNTS_SERVICE_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/accounts-mock/accounts-service-sound" - -DMEDIA_PLAYER_MPRIS_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/media-player-mpris-mock/media-player-mpris-mock" - -DMEDIA_PLAYER_MPRIS_UPDATE_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/media-player-mpris-mock/media-player-mpris-mock-update" - -DTEST_SOUND="${CMAKE_SOURCE_DIR}/tests/integration/test-sound.wav" - -DQT_NO_KEYWORDS=1 - -DXDG_DATA_DIRS="${XDG_DATA_DIRS}" -) - -set(GLIB_REQUIRED_VERSION 2.26) - -pkg_check_modules( - GLIB REQUIRED - glib-2.0>=${GLIB_REQUIRED_VERSION} - gio-2.0>=${GLIB_REQUIRED_VERSION} -) -include_directories(${GLIB_INCLUDE_DIRS}) - -set( - INTEGRATION_TESTS_SRC - indicator-sound-test-base.cpp - test-indicator.cpp - utils/dbus-pulse-volume.cpp - main.cpp -) - -add_executable( - integration-tests - ${INTEGRATION_TESTS_SRC} -) - -qt5_use_modules( - integration-tests - Core - DBus - Test -) - -target_link_libraries( - integration-tests - sound-indicator-dbus-interfaces - ${QTDBUSMOCK_LDFLAGS} - ${QTDBUSTEST_LDFLAGS} - ${GTEST_LIBRARIES} - ${GMOCK_LIBRARIES} -# ${GMENUHARNESS_LDFLAGS} - ${GLIB_LDFLAGS} - gmenuharness-shared -) - -add_test( - integration-tests - integration-tests -) - -set( - SET-VOLUME-SRC - utils/dbus-pulse-volume.cpp - utils/set-volume.cpp -) - -set( - GET-VOLUME-SRC - utils/dbus-pulse-volume.cpp - utils/get-volume.cpp -) - -add_executable( - set-volume - ${SET-VOLUME-SRC} -) - -add_executable( - get-volume - ${GET-VOLUME-SRC} -) - -qt5_use_modules( - set-volume - Core - DBus - Test -) - -qt5_use_modules( - get-volume - Core - DBus - Test -) - -target_link_libraries( - get-volume - sound-indicator-dbus-interfaces -) - -target_link_libraries( - set-volume - sound-indicator-dbus-interfaces -) - -#add_subdirectory(utils) \ No newline at end of file diff --git a/tests/integration/Copy of test-sound.wav b/tests/integration/Copy of test-sound.wav deleted file mode 100644 index 709c6eb..0000000 Binary files a/tests/integration/Copy of test-sound.wav and /dev/null differ diff --git a/tests/integration/indicator-sound-test-base.cpp b/tests/integration/indicator-sound-test-base.cpp deleted file mode 100644 index 5005e2c..0000000 --- a/tests/integration/indicator-sound-test-base.cpp +++ /dev/null @@ -1,823 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ - -#include "indicator-sound-test-base.h" - -#include "dbus_menus_interface.h" -#include "dbus_properties_interface.h" -#include "dbus_accounts_interface.h" -#include "dbus_accountssound_interface.h" -#include "dbus_notifications_interface.h" -#include "dbus-types.h" - -#include -#include -#include - -#include -#include "utils/dbus-pulse-volume.h" - -using namespace QtDBusTest; -using namespace QtDBusMock; -using namespace std; -using namespace testing; -namespace mh = unity::gmenuharness; - -namespace -{ - const int MAX_TIME_WAITING_FOR_NOTIFICATIONS = 2000; -} - -IndicatorSoundTestBase::IndicatorSoundTestBase() : - dbusMock(dbusTestRunner) -{ -} - -IndicatorSoundTestBase::~IndicatorSoundTestBase() -{ -} - -void IndicatorSoundTestBase::SetUp() -{ - setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, true); - setenv("DBUS_SYSTEM_BUS_ADDRESS", dbusTestRunner.systemBus().toStdString().c_str(), true); - setenv("DBUS_SESSION_BUS_ADDRESS", dbusTestRunner.sessionBus().toStdString().c_str(), true); - dbusMock.registerNotificationDaemon(); - - dbusTestRunner.startServices(); - - auto& notifications = notificationsMockInterface(); - notifications.AddMethod("org.freedesktop.Notifications", - "GetCapabilities", - "", - "as", - "ret = ['actions', 'body', 'body-markup', 'icon-static', 'image/svg+xml', 'x-canonical-private-synchronous', 'x-canonical-append', 'x-canonical-private-icon-only', 'x-canonical-truncation', 'private-synchronous', 'append', 'private-icon-only', 'truncation']" - ).waitForFinished(); - - int waitedTime = 0; - while (!dbusTestRunner.sessionConnection().interface()->isServiceRegistered("org.freedesktop.Notifications") && waitedTime < MAX_TIME_WAITING_FOR_NOTIFICATIONS) - { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - waitedTime += 10; - } -} - -void IndicatorSoundTestBase::TearDown() -{ - unsetenv("XDG_DATA_DIRS"); - unsetenv("PULSE_SERVER"); - unsetenv("DBUS_SYSTEM_BUS_ADDRESS"); -} - -void gvariant_deleter(GVariant* varptr) -{ - if (varptr != nullptr) - { - g_variant_unref(varptr); - } -} - -std::shared_ptr IndicatorSoundTestBase::volume_variant(double volume) -{ - GVariantBuilder builder; - - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add(&builder, - "{sv}", - "title", - g_variant_new_string("_Sound")); - - g_variant_builder_add(&builder, - "{sv}", - "accessible-desc", - g_variant_new_string("_Sound")); - - auto icon = g_themed_icon_new("icon"); - g_variant_builder_add(&builder, - "{sv}", - "icon", - g_icon_serialize(icon)); - - g_variant_builder_add(&builder, - "{sv}", - "visible", - g_variant_new_boolean(true)); - return shared_ptr(g_variant_builder_end(&builder), &gvariant_deleter); -} - -bool IndicatorSoundTestBase::setStreamRestoreVolume(QString const &role, double volume) -{ - QProcess setVolume; - setVolume.start(VOLUME_SET_BIN, QStringList() - << role - << QString("%1").arg(volume)); - if (!setVolume.waitForStarted()) - return false; - - if (!setVolume.waitForFinished()) - return false; - - return setVolume.exitCode() == 0; -} - -bool IndicatorSoundTestBase::setSinkVolume(double volume) -{ - QString volume_percentage = QString("%1\%").arg(volume*100); - QProcess setVolume; - setVolume.start("pactl", QStringList() - << "-s" - << "127.0.0.1" - << "set-sink-volume" - << "0" - << volume_percentage); - if (!setVolume.waitForStarted()) - return false; - - if (!setVolume.waitForFinished()) - return false; - - return setVolume.exitCode() == 0; -} - -bool IndicatorSoundTestBase::clearGSettingsPlayers() -{ - QProcess clearPlayers; - - clearPlayers.start("gsettings", QStringList() - << "set" - << "com.canonical.indicator.sound" - << "interested-media-players" - << "[]"); - if (!clearPlayers.waitForStarted()) - return false; - - if (!clearPlayers.waitForFinished()) - return false; - - return clearPlayers.exitCode() == 0; -} - -bool IndicatorSoundTestBase::startTestMprisPlayer(QString const& playerName) -{ - testPlayer1.terminate(); - testPlayer1.start(MEDIA_PLAYER_MPRIS_BIN, QStringList() - << playerName); - if (!testPlayer1.waitForStarted()) - return false; - - - return true; -} - -bool IndicatorSoundTestBase::setTestMprisPlayerProperty(QString const &testPlayer, QString const &property, bool value) -{ - QProcess setProperty; - QString strValue; - strValue = value ? "true" : "false"; - - setProperty.start(MEDIA_PLAYER_MPRIS_UPDATE_BIN, QStringList() - << testPlayer - << property - << strValue); - if (!setProperty.waitForStarted()) - return false; - - if (!setProperty.waitForFinished()) - return false; - - return setProperty.exitCode() == 0; -} - -bool IndicatorSoundTestBase::startTestSound(QString const &role) -{ - testSoundProcess.terminate(); - testSoundProcess.start("paplay", QStringList() - << "-s" - << "127.0.0.1" - << TEST_SOUND - << QString("--property=media.role=%1").arg(role)); - - if (!testSoundProcess.waitForStarted()) - return false; - - return true; -} - -void IndicatorSoundTestBase::stopTestSound() -{ - testSoundProcess.terminate(); -} - -void IndicatorSoundTestBase::startPulseDesktop(DevicePortType speakerPort, DevicePortType headphonesPort) -{ - try - { - pulseaudio.reset( - new QProcessDBusService(DBusTypes::DBUS_PULSE, - QDBusConnection::SessionBus, - "pulseaudio", - QStringList() << "--start" - << "-vvvv" - << "--disable-shm=true" - << "--daemonize=false" - << "--use-pid-file=false" - << "--system=false" - << "--exit-idle-time=-1" - << "-n" - << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort)) - << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort)) - << "--log-target=file:/tmp/pulse-daemon.log" - << "--load=module-dbus-protocol" - << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1" - )); - pulseaudio->start(dbusTestRunner.sessionConnection()); - } - catch (exception const& e) - { - cout << "pulseaudio(): " << e.what() << endl; - throw; - } -} - -void IndicatorSoundTestBase::startPulsePhone(DevicePortType speakerPort, DevicePortType headphonesPort) -{ - try - { - pulseaudio.reset( - new QProcessDBusService(DBusTypes::DBUS_PULSE, - QDBusConnection::SessionBus, - "pulseaudio", - QStringList() << "--start" - << "-vvvv" - << "--disable-shm=true" - << "--daemonize=false" - << "--use-pid-file=false" - << "--system=false" - << "--exit-idle-time=-1" - << "-n" - << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort)) - << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort)) - << "--log-target=file:/tmp/pulse-daemon.log" - << QString("--load=module-stream-restore restore_device=false restore_muted=false fallback_table=%1").arg(STREAM_RESTORE_TABLE) - << "--load=module-dbus-protocol" - << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1" - )); - pulseaudio->start(dbusTestRunner.sessionConnection()); - } - catch (exception const& e) - { - cout << "pulseaudio(): " << e.what() << endl; - throw; - } -} - -void IndicatorSoundTestBase::startAccountsService() -{ - try - { - accountsService.reset( - new QProcessDBusService(DBusTypes::ACCOUNTS_SERVICE, - QDBusConnection::SystemBus, - ACCOUNTS_SERVICE_BIN, - QStringList())); - accountsService->start(dbusTestRunner.systemConnection()); - - initializeAccountsInterface(); - } - catch (exception const& e) - { - cout << "accountsService(): " << e.what() << endl; - throw; - } -} - -void IndicatorSoundTestBase::startIndicator() -{ - try - { - setenv("PULSE_SERVER", "127.0.0.1", true); - indicator.reset( - new QProcessDBusService(DBusTypes::DBUS_NAME, - QDBusConnection::SessionBus, - SOUND_SERVICE_BIN, - QStringList())); - indicator->start(dbusTestRunner.sessionConnection()); - } - catch (exception const& e) - { - cout << "startIndicator(): " << e.what() << endl; - throw; - } -} - -mh::MenuMatcher::Parameters IndicatorSoundTestBase::desktopParameters() -{ - return mh::MenuMatcher::Parameters( - "com.canonical.indicator.sound", - { { "indicator", "/com/canonical/indicator/sound" } }, - "/com/canonical/indicator/sound/desktop"); -} - -mh::MenuMatcher::Parameters IndicatorSoundTestBase::phoneParameters() -{ - return mh::MenuMatcher::Parameters( - "com.canonical.indicator.sound", - { { "indicator", "/com/canonical/indicator/sound" } }, - "/com/canonical/indicator/sound/phone"); -} - -unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::volumeSlider(double volume, QString const &label) -{ - return mh::MenuItemMatcher().radio() - .label(label.toStdString()) - .round_doubles(0.1) - .int32_attribute("target", 0) - .double_attribute("min-value", 0.0) - .double_attribute("max-value", 1.0) - .double_attribute("step", 0.01) - .string_attribute("x-canonical-type", "com.canonical.unity.slider") - .themed_icon("max-icon", {"audio-volume-high-panel", "audio-volume-high", "audio-volume", "audio"}) - .themed_icon("min-icon", {"audio-volume-low-zero-panel", "audio-volume-low-zero", "audio-volume-low", "audio-volume", "audio"}) - .pass_through_double_attribute("action", volume); -} - -unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::silentModeSwitch(bool toggled) -{ - return mh::MenuItemMatcher::checkbox() - .label("Silent Mode") - .action("indicator.silent-mode") - .toggled(toggled); -} - -bool IndicatorSoundTestBase::waitMenuChange() -{ - if (signal_spy_menu_changed_) - { - return signal_spy_menu_changed_->wait(); - } - return false; -} - -bool IndicatorSoundTestBase::initializeMenuChangedSignal() -{ - if (!menu_interface_) - { - menu_interface_.reset(new MenusInterface("com.canonical.indicator.sound", - "/com/canonical/indicator/sound", - dbusTestRunner.sessionConnection(), 0)); - } - if (menu_interface_) - { - qDebug() << "Waiting for signal"; - signal_spy_menu_changed_.reset(new QSignalSpy(menu_interface_.get(), &MenusInterface::Changed)); - } - if (!menu_interface_ || !signal_spy_menu_changed_) - { - return false; - } - return true; -} - -bool IndicatorSoundTestBase::waitVolumeChangedInIndicator() -{ - if (signal_spy_volume_changed_) - { - return signal_spy_volume_changed_->wait(); - } - return false; -} - -void IndicatorSoundTestBase::initializeAccountsInterface() -{ - auto username = qgetenv("USER"); - if (username != "") - { - main_accounts_interface_.reset(new AccountsInterface("org.freedesktop.Accounts", - "/org/freedesktop/Accounts", - dbusTestRunner.systemConnection(), 0)); - - QDBusReply userResp = main_accounts_interface_->call(QLatin1String("FindUserByName"), - QLatin1String(username)); - - if (!userResp.isValid()) - { - qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << userResp.error().message(); - } - - auto userPath = userResp.value().path(); - if (userPath != "") - { - std::unique_ptr soundInterface(new AccountsSoundInterface("org.freedesktop.Accounts", - userPath, - dbusTestRunner.systemConnection(), 0)); - - accounts_interface_.reset(new DBusPropertiesInterface("org.freedesktop.Accounts", - userPath, - dbusTestRunner.systemConnection(), 0)); - if (!accounts_interface_->isValid()) - { - qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << accounts_interface_->lastError().message(); - } - signal_spy_volume_changed_.reset(new QSignalSpy(accounts_interface_.get(),&DBusPropertiesInterface::PropertiesChanged)); - } - } -} - -OrgFreedesktopDBusMockInterface& IndicatorSoundTestBase::notificationsMockInterface() -{ - return dbusMock.mockInterface("org.freedesktop.Notifications", - "/org/freedesktop/Notifications", - "org.freedesktop.Notifications", - QDBusConnection::SessionBus); -} - -bool IndicatorSoundTestBase::setActionValue(const QString & action, QVariant value) -{ - QDBusInterface actionsInterface(DBusTypes::DBUS_NAME, - DBusTypes::MAIN_SERVICE_PATH, - DBusTypes::ACTIONS_INTERFACE, - dbusTestRunner.sessionConnection()); - - QDBusVariant dbusVar(value); - auto resp = actionsInterface.call("SetState", - action, - QVariant::fromValue(dbusVar), - QVariant::fromValue(QVariantMap())); - - if (resp.type() == QDBusMessage::ErrorMessage) - { - qCritical() << "IndicatorSoundTestBase::setActionValue(): Failed to set value for action " - << action - << " " - << resp.errorMessage(); - return false; - } - else - { - return true; - } -} - -bool IndicatorSoundTestBase::pressNotificationButton(int id, const QString & button) -{ - OrgFreedesktopDBusMockInterface actionsInterface("org.freedesktop.Notifications", - "/org/freedesktop/Notifications", - dbusTestRunner.sessionConnection()); - - actionsInterface.EmitSignal( - "org.freedesktop.Notifications", - "ActionInvoked", "us", QVariantList() << id << button); - - return true; -} - -bool IndicatorSoundTestBase::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 IndicatorSoundTestBase::checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call) -{ - QString icon; - if (volume <= 0.0) - { - icon = "audio-volume-muted"; - } - else if (volume <= 0.3) - { - icon = "audio-volume-low"; - } - else if (volume <= 0.7) - { - icon = "audio-volume-medium"; - } - else - { - icon = "audio-volume-high"; - } - - ASSERT_NE(call.size(), 0); - EXPECT_EQ("Notify", call.at(0)); - - QVariantList const& args(call.at(1).toList()); - ASSERT_EQ(8, args.size()); - EXPECT_EQ("indicator-sound", args.at(0)); - EXPECT_EQ(icon, args.at(2)); - EXPECT_EQ("Volume", args.at(3)); - EXPECT_EQ(label, args.at(4)); - EXPECT_EQ(QStringList(), args.at(5)); - - QVariantMap hints; - ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); - ASSERT_TRUE(hints.contains("value")); - ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); - ASSERT_TRUE(hints.contains("x-canonical-value-bar-tint")); - ASSERT_TRUE(hints.contains("x-canonical-private-synchronous")); - - EXPECT_EQ(volume*100, hints["value"]); - EXPECT_EQ(true, hints["x-canonical-non-shaped-icon"]); - EXPECT_EQ(isLoud, hints["x-canonical-value-bar-tint"]); - EXPECT_EQ(true, hints["x-canonical-private-synchronous"]); -} - -void IndicatorSoundTestBase::checkHighVolumeNotification(QVariantList call) -{ - ASSERT_NE(call.size(), 0); - EXPECT_EQ("Notify", call.at(0)); - - QVariantList const& args(call.at(1).toList()); - ASSERT_EQ(8, args.size()); - EXPECT_EQ("indicator-sound", args.at(0)); - EXPECT_EQ("Volume", args.at(3)); -} - -void IndicatorSoundTestBase::checkCloseNotification(int id, QVariantList call) -{ - EXPECT_EQ("CloseNotification", call.at(0)); - QVariantList const& args(call.at(1).toList()); - ASSERT_EQ(1, args.size()); -} - -void IndicatorSoundTestBase::checkNotificationWithNoArgs(QString const& method, QVariantList call) -{ - EXPECT_EQ(method, call.at(0)); - QVariantList const& args(call.at(1).toList()); - ASSERT_EQ(0, args.size()); -} - -int IndicatorSoundTestBase::getNotificationID(QVariantList call) -{ - if (call.size() == 0) - { - return -1; - } - QVariantList const& args(call.at(1).toList()); - if (args.size() != 8) - { - return -1; - } - if (args.at(0) != "indicator-sound") - { - return -1; - } - - bool isInt; - int id = args.at(1).toInt(&isInt); - if (!isInt) - { - return -1; - } - return id; -} - -bool IndicatorSoundTestBase::activateHeadphones(bool headphonesActive) -{ - QProcess pacltProcess; - - QString defaultSinkName = "indicator_sound_test_speaker"; - QString suspendedSinkName = "indicator_sound_test_headphones"; - if (headphonesActive) - { - defaultSinkName = "indicator_sound_test_headphones"; - suspendedSinkName = "indicator_sound_test_speaker"; - } - - pacltProcess.start("pactl", QStringList() << "-s" - << "127.0.0.1" - << "set-default-sink" - << defaultSinkName); - if (!pacltProcess.waitForStarted()) - return false; - - if (!pacltProcess.waitForFinished()) - return false; - - pacltProcess.start("pactl", QStringList() << "-s" - << "127.0.0.1" - << "suspend-sink" - << defaultSinkName - << "0"); - if (!pacltProcess.waitForStarted()) - return false; - - if (!pacltProcess.waitForFinished()) - return false; - - pacltProcess.start("pactl", QStringList() << "-s" - << "127.0.0.1" - << "suspend-sink" - << suspendedSinkName - << "1"); - if (!pacltProcess.waitForStarted()) - return false; - - if (!pacltProcess.waitForFinished()) - return false; - - return pacltProcess.exitCode() == 0; -} - -QString IndicatorSoundTestBase::getDevicePortString(DevicePortType port) -{ - QString portString; - - switch (port) - { - case WIRED: - portString = "wired"; - break; - case BLUETOOTH: - portString = "bluetooth"; - break; - case USB: - portString = "usb"; - break; - case HDMI: - portString = "hdmi"; - break; - default: - portString = "not_defined"; - break; - } - - return portString; -} - -void IndicatorSoundTestBase::checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort) -{ - double INITIAL_VOLUME = 0.0; - - QString speakerString; - QString speakerStringMenu; - switch(speakerPort) - { - case WIRED: - speakerString = "Speakers"; - speakerStringMenu = "Volume"; - break; - case BLUETOOTH: - speakerString = "Bluetooth speaker"; - speakerStringMenu = "Volume (Bluetooth)"; - break; - case USB: - speakerString = "Usb speaker"; - speakerStringMenu = "Volume (Usb)"; - break; - case HDMI: - speakerString = "HDMI speaker"; - speakerStringMenu = "Volume (HDMI)"; - break; - } - - QString headphonesString; - QString headphonesStringMenu; - switch(headphonesPort) - { - case WIRED: - headphonesString = "Headphones"; - headphonesStringMenu = "Volume (Headphones)"; - break; - case BLUETOOTH: - headphonesString = "Bluetooth headphones"; - headphonesStringMenu = "Volume (Bluetooth headphones)"; - break; - case USB: - headphonesString = "Usb headphones"; - headphonesStringMenu = "Volume (Usb headphones)"; - break; - case HDMI: - headphonesString = "HDMI headphones"; - headphonesStringMenu = "Volume (HDMI headphones)"; - break; - } - - QSignalSpy notificationsSpy(¬ificationsMockInterface(), - SIGNAL(MethodCalled(const QString &, const QVariantList &))); - - ASSERT_NO_THROW(startAccountsService()); - ASSERT_NO_THROW(startPulsePhone(speakerPort, headphonesPort)); - - // initialize volumes in pulseaudio - EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - // if the speaker is the normal one it does not emit any notification, as that's - // the default one. - // for the rest it notifies the output - if (speakerPort != WIRED) - { - WAIT_FOR_SIGNALS(notificationsSpy, 3); - - // the first time we also have the calls to - // GetServerInformation and GetCapabilities - checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); - checkVolumeNotification(INITIAL_VOLUME, speakerString, false, notificationsSpy.at(2)); - notificationsSpy.clear(); - } - - notificationsSpy.clear(); - // activate the headphones - EXPECT_TRUE(activateHeadphones(true)); - - if (speakerPort == WIRED) - { - WAIT_FOR_SIGNALS(notificationsSpy, 3); - - // the first time we also have the calls to - // GetServerInformation and GetCapabilities - checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); - checkVolumeNotification(INITIAL_VOLUME, headphonesString, false, notificationsSpy.at(2)); - notificationsSpy.clear(); - } - else - { - WAIT_FOR_SIGNALS(notificationsSpy, 2); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkVolumeNotification(INITIAL_VOLUME, headphonesString, false, notificationsSpy.at(1)); - notificationsSpy.clear(); - } - - // check the label in the menu - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::starts_with) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME, headphonesStringMenu)) - ) - ).match()); - - // deactivate the headphones - EXPECT_TRUE(activateHeadphones(false)); - - WAIT_FOR_SIGNALS(notificationsSpy, 2); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkVolumeNotification(INITIAL_VOLUME, speakerString, false, notificationsSpy.at(1)); - notificationsSpy.clear(); - - // check the label in the menu - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::starts_with) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME, speakerStringMenu)) - ) - ).match()); -} - -bool IndicatorSoundTestBase::setVolumeUntilAccountsIsConnected(double volume) -{ - int RETRY_TIME = 5000; - - setActionValue("volume", QVariant::fromValue(volume)); - while(!signal_spy_volume_changed_->wait(10) && RETRY_TIME) - { - RETRY_TIME -= 10; - setActionValue("volume", QVariant::fromValue(volume)); - } - return (signal_spy_volume_changed_->count() != 0); -} diff --git a/tests/integration/indicator-sound-test-base.h b/tests/integration/indicator-sound-test-base.h deleted file mode 100644 index 41dd1c7..0000000 --- a/tests/integration/indicator-sound-test-base.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ - -#pragma once - -#include -#include -#include - -#include -#include - -#include -#include - -class MenusInterface; -class DBusPulseVolume; -class DBusPropertiesInterface; -class AccountsInterface; -class QSignalSpy; - -#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ -{\ - while (signalSpy.size() < signalsExpected)\ - {\ - ASSERT_TRUE(signalSpy.wait());\ - }\ - ASSERT_EQ(signalsExpected, signalSpy.size());\ -} - -#define WAIT_AT_LEAST_SIGNALS(signalSpy, signalsExpected)\ -{\ - while (signalSpy.size() < signalsExpected)\ - {\ - ASSERT_TRUE(signalSpy.wait());\ - }\ - ASSERT_TRUE(signalsExpected <= signalSpy.size());\ -} - -class IndicatorSoundTestBase: public testing::Test -{ -public: - IndicatorSoundTestBase(); - - ~IndicatorSoundTestBase(); - - enum DevicePortType - { - WIRED, - BLUETOOTH, - USB, - HDMI - }; - -protected: - void SetUp() override; - void TearDown() override; - - void startIndicator(); - void startPulseDesktop(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED); - void startPulsePhone(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED); - void startAccountsService(); - - bool clearGSettingsPlayers(); - - bool startTestMprisPlayer(QString const& playerName); - - bool setTestMprisPlayerProperty(QString const &testPlayer, QString const &property, bool value); - - bool setStreamRestoreVolume(QString const &role, double volume); - - bool setSinkVolume(double volume); - - bool startTestSound(QString const &role); - - void stopTestSound(); - - static std::shared_ptr volume_variant(double volume); - - static unity::gmenuharness::MenuMatcher::Parameters desktopParameters(); - - static unity::gmenuharness::MenuMatcher::Parameters phoneParameters(); - - static unity::gmenuharness::MenuItemMatcher volumeSlider(double volume, QString const &label); - - static unity::gmenuharness::MenuItemMatcher silentModeSwitch(bool toggled); - - bool waitMenuChange(); - - bool initializeMenuChangedSignal(); - - bool waitVolumeChangedInIndicator(); - - void initializeAccountsInterface(); - - OrgFreedesktopDBusMockInterface& notificationsMockInterface(); - - bool setActionValue(const QString & action, QVariant value); - - bool pressNotificationButton(int id, const QString & button); - - bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map); - - void checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call); - - void checkHighVolumeNotification(QVariantList call); - - void checkCloseNotification(int id, QVariantList call); - - void checkNotificationWithNoArgs(QString const& method, QVariantList call); - - int getNotificationID(QVariantList call); - - bool activateHeadphones(bool headphonesActive); - - QString getDevicePortString(DevicePortType port); - - void checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort); - - bool setVolumeUntilAccountsIsConnected(double volume); - - QtDBusTest::DBusTestRunner dbusTestRunner; - - QtDBusMock::DBusMock dbusMock; - - QtDBusTest::DBusServicePtr indicator; - - QtDBusTest::DBusServicePtr pulseaudio; - - QtDBusTest::DBusServicePtr accountsService; - - QProcess testSoundProcess; - - QProcess testPlayer1; - - std::unique_ptr menu_interface_; - - std::unique_ptr accounts_interface_; - - std::unique_ptr main_accounts_interface_; - - std::unique_ptr signal_spy_volume_changed_; - - std::unique_ptr signal_spy_menu_changed_; -}; diff --git a/tests/integration/main.cpp b/tests/integration/main.cpp deleted file mode 100644 index a29b3b6..0000000 --- a/tests/integration/main.cpp +++ /dev/null @@ -1,58 +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 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 - -#include "dbus-types.h" - -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"); - - QCoreApplication application(argc, argv); - DBusMock::registerMetaTypes(); - DBusTypes::registerMetaTypes(); - ::testing::InitGoogleTest(&argc, argv); - - Runner runner; - QTimer::singleShot(0, &runner, SLOT(run())); - - return application.exec(); -} - -#include "main.moc" diff --git a/tests/integration/test-indicator.cpp b/tests/integration/test-indicator.cpp deleted file mode 100644 index 33e62b5..0000000 --- a/tests/integration/test-indicator.cpp +++ /dev/null @@ -1,981 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ - -#include - -#include -#include -#include - -using namespace std; -using namespace testing; -namespace mh = unity::gmenuharness; -namespace -{ - -class TestIndicator: public IndicatorSoundTestBase -{ -}; - -TEST_F(TestIndicator, PhoneChangeRoleVolume) -{ - double INITIAL_VOLUME = 0.0; - - ASSERT_NO_THROW(startAccountsService()); - ASSERT_NO_THROW(startPulsePhone()); - - // initialize volumes in pulseaudio - EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - // Generate a random volume - QTime now = QTime::currentTime(); - qsrand(now.msec()); - int randInt = qrand() % 100; - double randomVolume = randInt / 100.0; - - QSignalSpy &userAccountsSpy = *signal_spy_volume_changed_; - // set an initial volume to the alert role - userAccountsSpy.clear(); - EXPECT_TRUE(setVolumeUntilAccountsIsConnected(1.0)); - userAccountsSpy.clear(); - // play a test sound, it should change the role in the indicator - EXPECT_TRUE(startTestSound("multimedia")); - - // this time we only expect 1 signal as it's only the indicator - // updating the value - WAIT_FOR_SIGNALS(userAccountsSpy, 1); - //EXPECT_TRUE(waitVolumeChangedInIndicator()); - - userAccountsSpy.clear(); - // set the random volume to the multimedia role - setActionValue("volume", QVariant::fromValue(randomVolume)); - if (randomVolume != INITIAL_VOLUME) - { - WAIT_FOR_SIGNALS(userAccountsSpy, 1); - } - - // check the indicator - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::starts_with) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(randomVolume, "Volume")) - ) - ).match()); - - // check that the last item is Sound Settings - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::ends_with) - .submenu() - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - .action("indicator.phone-settings") - ) - ).match()); - - // initialize the signal spy - EXPECT_TRUE(initializeMenuChangedSignal()); - userAccountsSpy.clear(); - // stop the test sound, the role should change again to alert - stopTestSound(); - if (randomVolume != 1.0) - { - // wait for the menu change - EXPECT_TRUE(waitMenuChange()); - } - - // check the initial volume for the alert role - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::starts_with) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(1.0, "Volume")) - ) - ).match()); - - // check that the last item is Sound Settings - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::ends_with) - .submenu() - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - .action("indicator.phone-settings") - ) - ).match()); -} - -TEST_F(TestIndicator, PhoneBasicInitialVolume) -{ - double INITIAL_VOLUME = 0.0; - - ASSERT_NO_THROW(startAccountsService()); - EXPECT_TRUE(clearGSettingsPlayers()); - ASSERT_NO_THROW(startPulsePhone()); - - // initialize volumes in pulseaudio - EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - .action("indicator.phone-settings") - ) - ).match()); -} - -TEST_F(TestIndicator, PhoneAddMprisPlayer) -{ - double INITIAL_VOLUME = 0.0; - - ASSERT_NO_THROW(startAccountsService()); - EXPECT_TRUE(clearGSettingsPlayers()); - ASSERT_NO_THROW(startPulsePhone()); - - // initialize volumes in pulseaudio - EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - .action("indicator.phone-settings") - ) - ).match()); - - // initialize the signal spy - EXPECT_TRUE(initializeMenuChangedSignal()); - - // start the test player - EXPECT_TRUE(startTestMprisPlayer("testplayer1")); - - // wait for the menu change - EXPECT_TRUE(waitMenuChange()); - - // finally verify that the player is added - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher() - .action("indicator.testplayer1.desktop") - .label("TestPlayer1") - .themed_icon("icon", {"testplayer"}) - .string_attribute("x-canonical-type", "com.canonical.unity.media-player") - ) - .item(mh::MenuItemMatcher() - .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") - .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") - .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") - .string_attribute("x-canonical-type","com.canonical.unity.playback-item") - ) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - .action("indicator.phone-settings") - ) - ).match()); -} - -TEST_F(TestIndicator, DesktopBasicInitialVolume) -{ - double INITIAL_VOLUME = 0.0; - - ASSERT_NO_THROW(startAccountsService()); - EXPECT_TRUE(clearGSettingsPlayers()); - ASSERT_NO_THROW(startPulseDesktop()); - - // initialize volumes in pulseaudio - EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME)); - - // start the test player - EXPECT_TRUE(startTestMprisPlayer("testplayer1")); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher().checkbox() - .label("Mute") - ) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher() - .action("indicator.testplayer1.desktop") - .label("TestPlayer1") - .themed_icon("icon", {"testplayer"}) - .string_attribute("x-canonical-type", "com.canonical.unity.media-player") - ) - .item(mh::MenuItemMatcher() - .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") - .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") - .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") - .string_attribute("x-canonical-type","com.canonical.unity.playback-item") - ) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - ) - ).match()); -} - -TEST_F(TestIndicator, DesktopAddMprisPlayer) -{ - double INITIAL_VOLUME = 0.0; - - ASSERT_NO_THROW(startAccountsService()); - EXPECT_TRUE(clearGSettingsPlayers()); - ASSERT_NO_THROW(startPulseDesktop()); - - // initialize volumes in pulseaudio - EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME)); - - // start the test player - EXPECT_TRUE(startTestMprisPlayer("testplayer1")); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - // check that the player is added - EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher().checkbox() - .label("Mute") - ) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher() - .action("indicator.testplayer1.desktop") - .label("TestPlayer1") - .themed_icon("icon", {"testplayer"}) - .string_attribute("x-canonical-type", "com.canonical.unity.media-player") - ) - .item(mh::MenuItemMatcher() - .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") - .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") - .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") - .string_attribute("x-canonical-type","com.canonical.unity.playback-item") - ) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - ) - ).match()); -} - -TEST_F(TestIndicator, DesktopMprisPlayerButtonsState) -{ - double INITIAL_VOLUME = 0.0; - - ASSERT_NO_THROW(startAccountsService()); - EXPECT_TRUE(clearGSettingsPlayers()); - ASSERT_NO_THROW(startPulseDesktop()); - - // initialize volumes in pulseaudio - EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME)); - - // start the test player - EXPECT_TRUE(startTestMprisPlayer("testplayer1")); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - // check that the player is added - EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher().checkbox() - .label("Mute") - ) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher() - .action("indicator.testplayer1.desktop") - .label("TestPlayer1") - .themed_icon("icon", {"testplayer"}) - .string_attribute("x-canonical-type", "com.canonical.unity.media-player") - ) - .item(mh::MenuItemMatcher() - .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") - .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") - .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") - .string_attribute("x-canonical-type","com.canonical.unity.playback-item") - ) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - ) - ).match()); - - // change the state of CanGoNext - EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoNext", false)); - - // verify that the action changes - EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher().checkbox() - .label("Mute") - ) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher() - .action("indicator.testplayer1.desktop") - .label("TestPlayer1") - .themed_icon("icon", {"testplayer"}) - .string_attribute("x-canonical-type", "com.canonical.unity.media-player") - ) - .item(mh::MenuItemMatcher() - .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") - .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") - .attribute_not_set("x-canonical-next-action") - .string_attribute("x-canonical-type","com.canonical.unity.playback-item") - ) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - ) - ).match()); - - - // change the state of CanGoPrevious - EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoPrevious", false)); - - // verify that the action changes - EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher().checkbox() - .label("Mute") - ) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher() - .action("indicator.testplayer1.desktop") - .label("TestPlayer1") - .themed_icon("icon", {"testplayer"}) - .string_attribute("x-canonical-type", "com.canonical.unity.media-player") - ) - .item(mh::MenuItemMatcher() - .attribute_not_set("x-canonical-previous-action") - .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") - .attribute_not_set("x-canonical-next-action") - .string_attribute("x-canonical-type","com.canonical.unity.playback-item") - ) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - ) - ).match()); - - // set back both to true - EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoNext", true)); - EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoPrevious", true)); - - EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher().checkbox() - .label("Mute") - ) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher() - .action("indicator.testplayer1.desktop") - .label("TestPlayer1") - .themed_icon("icon", {"testplayer"}) - .string_attribute("x-canonical-type", "com.canonical.unity.media-player") - ) - .item(mh::MenuItemMatcher() - .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") - .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") - .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") - .string_attribute("x-canonical-type","com.canonical.unity.playback-item") - ) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - ) - ).match()); -} - -TEST_F(TestIndicator, DesktopChangeRoleVolume) -{ - double INITIAL_VOLUME = 0.0; - - ASSERT_NO_THROW(startAccountsService()); - ASSERT_NO_THROW(startPulseDesktop()); - - // initialize volumes in pulseaudio - // expect false for stream restore, because the module - // is not loaded in the desktop pulseaudio instance - EXPECT_FALSE(setStreamRestoreVolume("mutimedia", INITIAL_VOLUME)); - EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - - EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME)); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - // Generate a random volume - QTime now = QTime::currentTime(); - qsrand(now.msec()); - int randInt = qrand() % 100; - double randomVolume = randInt / 100.0; - - // play a test sound, it should NOT change the role in the indicator - EXPECT_TRUE(startTestSound("multimedia")); - EXPECT_FALSE(waitVolumeChangedInIndicator()); - - // set the random volume - EXPECT_TRUE(setSinkVolume(randomVolume)); - if (randomVolume != INITIAL_VOLUME) - { - EXPECT_TRUE(waitVolumeChangedInIndicator()); - } - - // check the indicator - EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::starts_with) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(mh::MenuItemMatcher().checkbox() - .label("Mute") - ) - .item(volumeSlider(randomVolume, "Volume")) - ) - ).match()); - - // check that the last item is Sound Settings - EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::ends_with) - .submenu() - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - ) - ).match()); - - // stop the test sound, the role should change again to alert - stopTestSound(); - - // although we were playing something in the multimedia role - // the server does not have the streamrestore module, so - // the volume does not change (the role does not change) - EXPECT_FALSE(waitVolumeChangedInIndicator()); - - // check the initial volume for the alert role - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::starts_with) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(randomVolume, "Volume")) - ) - ).match()); - - // check that the last item is Sound Settings - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .mode(mh::MenuItemMatcher::Mode::ends_with) - .submenu() - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - .action("indicator.phone-settings") - ) - ).match()); -} - -TEST_F(TestIndicator, PhoneNotificationVolume) -{ - double INITIAL_VOLUME = 0.0; - - QSignalSpy notificationsSpy(¬ificationsMockInterface(), - SIGNAL(MethodCalled(const QString &, const QVariantList &))); - - ASSERT_NO_THROW(startAccountsService()); - EXPECT_TRUE(clearGSettingsPlayers()); - ASSERT_NO_THROW(startPulsePhone()); - - // initialize volumes in pulseaudio - EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - // check the initial state - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::all) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME, "Volume")) - ) - .item(mh::MenuItemMatcher() - .label("Sound Settings…") - .action("indicator.phone-settings") - ) - ).match()); - - // change volume to 1.0 - setActionValue("volume", QVariant::fromValue(1.0)); - - WAIT_FOR_SIGNALS(notificationsSpy, 3); - - // the first time we also have the calls to - // GetServerInformation and GetCapabilities - checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); - checkVolumeNotification(1.0, "Speakers", false, notificationsSpy.at(2)); - - notificationsSpy.clear(); - setActionValue("volume", QVariant::fromValue(0.0)); - - WAIT_FOR_SIGNALS(notificationsSpy, 2) - - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkVolumeNotification(0.0, "Speakers", false, notificationsSpy.at(1)); - - notificationsSpy.clear(); - setActionValue("volume", QVariant::fromValue(0.5)); - - WAIT_FOR_SIGNALS(notificationsSpy, 2) - - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkVolumeNotification(0.5, "Speakers", false, notificationsSpy.at(1)); -} - -TEST_F(TestIndicator, PhoneNotificationWarningVolume) -{ - double INITIAL_VOLUME = 0.0; - - QSignalSpy notificationsSpy(¬ificationsMockInterface(), - SIGNAL(MethodCalled(const QString &, const QVariantList &))); - - ASSERT_NO_THROW(startAccountsService()); - ASSERT_NO_THROW(startPulsePhone()); - - // initialize volumes in pulseaudio - EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - // activate the headphones - EXPECT_TRUE(activateHeadphones(true)); - - // set an initial volume to the alert role - setStreamRestoreVolume("alert", 1.0); - EXPECT_TRUE(waitVolumeChangedInIndicator()); - - // play a test sound, it should change the role in the indicator - EXPECT_TRUE(startTestSound("multimedia")); - EXPECT_TRUE(waitVolumeChangedInIndicator()); - - WAIT_FOR_SIGNALS(notificationsSpy, 3); - // the first time we also have the calls to - // GetServerInformation and GetCapabilities - checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); - checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(2)); - notificationsSpy.clear(); - - // change volume to 0.3... no warning should be emitted - setActionValue("volume", QVariant::fromValue(0.3)); - EXPECT_TRUE(waitVolumeChangedInIndicator()); - - WAIT_FOR_SIGNALS(notificationsSpy, 2); - - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkVolumeNotification(0.3, "Headphones", false, notificationsSpy.at(1)); - notificationsSpy.clear(); - - // change volume to 0.5... no warning should be emitted - setActionValue("volume", QVariant::fromValue(0.5)); - EXPECT_TRUE(waitVolumeChangedInIndicator()); - - WAIT_FOR_SIGNALS(notificationsSpy, 2); - - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(1)); - notificationsSpy.clear(); - - // change volume to 1.0... warning should be emitted - setActionValue("volume", QVariant::fromValue(1.0)); - EXPECT_TRUE(waitVolumeChangedInIndicator()); - - WAIT_FOR_SIGNALS(notificationsSpy, 8); - - // the notification is sent twice (TODO check why) - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkCloseNotification(1, notificationsSpy.at(1)); - checkHighVolumeNotification(notificationsSpy.at(2)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(3)); - checkCloseNotification(1, notificationsSpy.at(4)); - checkHighVolumeNotification(notificationsSpy.at(5)); - - // get the last notification ID - int idNotification = getNotificationID(notificationsSpy.at(5)); - ASSERT_NE(-1, idNotification); - - qWarning() << "XGM: id Notification: " << idNotification; - - // cancel the dialog - pressNotificationButton(idNotification, "cancel"); - - // check that the volume was clamped - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::starts_with) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(0.74, "Volume (Headphones)")) - ) - ).match()); - - // try again... - notificationsSpy.clear(); - - qWarning() << "-----------------------------------------------------------"; - // change volume to 1.0... warning should be emitted - setActionValue("volume", QVariant::fromValue(1.0)); - EXPECT_TRUE(waitVolumeChangedInIndicator()); - - WAIT_FOR_SIGNALS(notificationsSpy, 6); - - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkHighVolumeNotification(notificationsSpy.at(1)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(2)); - checkHighVolumeNotification(notificationsSpy.at(3)); - - // get the last notification ID - idNotification = getNotificationID(notificationsSpy.at(1)); - ASSERT_NE(-1, idNotification); - - // this time we approve - pressNotificationButton(idNotification, "ok"); - - // check that the volume was applied - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::starts_with) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(1.0, "Volume (Headphones)")) - .item(mh::MenuItemMatcher() - .action("indicator.high-volume-warning-item") - .label("High volume can damage your hearing.") - ) - ) - ).match()); - - // after the warning was approved we should be able to modify the volume - // and don't get the warning - notificationsSpy.clear(); - - // change volume to 0.5... no warning should be emitted - setActionValue("volume", QVariant::fromValue(0.5)); - EXPECT_TRUE(waitVolumeChangedInIndicator()); - - WAIT_FOR_SIGNALS(notificationsSpy, 6); - - // check the notification TODO check why the sound indicator sends it twice - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkCloseNotification(idNotification, notificationsSpy.at(1)); - checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(2)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(3)); - checkCloseNotification(idNotification, notificationsSpy.at(4)); - checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(5)); - - // check that the volume was applied - // and that we don't have the warning item - EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) - .item(mh::MenuItemMatcher() - .action("indicator.root") - .string_attribute("x-canonical-type", "com.canonical.indicator.root") - .string_attribute("x-canonical-scroll-action", "indicator.scroll") - .string_attribute("x-canonical-secondary-action", "indicator.mute") - .string_attribute("submenu-action", "indicator.indicator-shown") - .mode(mh::MenuItemMatcher::Mode::starts_with) - .submenu() - .item(mh::MenuItemMatcher() - .section() - .item(silentModeSwitch(false)) - .item(volumeSlider(0.5, "Volume (Headphones)")) - ) - ).match()); - - // now set high volume again, we should not get the warning dialog - // as we already approved it - notificationsSpy.clear(); - - setActionValue("volume", QVariant::fromValue(1.0)); - EXPECT_TRUE(waitVolumeChangedInIndicator()); - - WAIT_FOR_SIGNALS(notificationsSpy, 4); - - // check the notification TODO check why the sound indicator sends it twice - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkVolumeNotification(1.0, "Headphones", true, notificationsSpy.at(1)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(2)); - checkVolumeNotification(1.0, "Headphones", true, notificationsSpy.at(3)); -} - - -TEST_F(TestIndicator, PhoneNotificationWarningVolumeAlertMode) -{ - double INITIAL_VOLUME = 0.0; - - QSignalSpy notificationsSpy(¬ificationsMockInterface(), - SIGNAL(MethodCalled(const QString &, const QVariantList &))); - - ASSERT_NO_THROW(startAccountsService()); - ASSERT_NO_THROW(startPulsePhone()); - - // initialize volumes in pulseaudio - EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); - EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); - - // start now the indicator, so it picks the new volumes - ASSERT_NO_THROW(startIndicator()); - - // activate the headphones - EXPECT_TRUE(activateHeadphones(true)); - - // set an initial volume to the alert role - setStreamRestoreVolume("alert", 1.0); - EXPECT_TRUE(waitVolumeChangedInIndicator()); - - // change volume to 0.0... no warning should be emitted - setActionValue("volume", QVariant::fromValue(0.0)); - - WAIT_FOR_SIGNALS(notificationsSpy, 5); - - // the first time we also have the calls to - // GetServerInformation and GetCapabilities - checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); - checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(2)); - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(3)); - checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(4)); - notificationsSpy.clear(); - - // change volume to 0.5... no warning should be emitted - setActionValue("volume", QVariant::fromValue(0.5)); - - WAIT_FOR_SIGNALS(notificationsSpy, 2); - - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(1)); - notificationsSpy.clear(); - - // change volume to 1.0... no warning should be emitted, we are in alert mode - setActionValue("volume", QVariant::fromValue(1.0)); - - WAIT_FOR_SIGNALS(notificationsSpy, 2); - - checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); - checkVolumeNotification(1.0, "Headphones", false, notificationsSpy.at(1)); - notificationsSpy.clear(); -} - -TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerWiredLabels) -{ - checkPortDevicesLabels(WIRED, WIRED); -} - -TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerBluetoothLabels) -{ - checkPortDevicesLabels(BLUETOOTH, BLUETOOTH); -} - -TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerUSBLabels) -{ - checkPortDevicesLabels(USB, USB); -} - -TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerHDMILabels) -{ - checkPortDevicesLabels(HDMI, HDMI); -} - -} // namespace diff --git a/tests/integration/test-sound.wav b/tests/integration/test-sound.wav deleted file mode 100644 index f696657..0000000 Binary files a/tests/integration/test-sound.wav and /dev/null differ diff --git a/tests/integration/touch-stream-restore.table b/tests/integration/touch-stream-restore.table deleted file mode 100755 index 146b02e..0000000 --- a/tests/integration/touch-stream-restore.table +++ /dev/null @@ -1,4 +0,0 @@ -sink-input-by-media-role:multimedia -8 -sink-input-by-media-role:alert -8 -sink-input-by-media-role:alarm -8 -sink-input-by-media-role:phone -15 diff --git a/tests/integration/utils/dbus-pulse-volume.cpp b/tests/integration/utils/dbus-pulse-volume.cpp deleted file mode 100644 index c8b6ae6..0000000 --- a/tests/integration/utils/dbus-pulse-volume.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ - -#include "dbus-pulse-volume.h" - -#include "dbus_properties_interface.h" -#include "dbus_accounts_interface.h" -#include "dbus_accountssound_interface.h" -#include "stream_restore_interface.h" - -#include - -#include - -unsigned int volumeDoubleToUint(double volume) -{ - double tmp = (double)(PA_VOLUME_NORM - PA_VOLUME_MUTED) * volume; - return (unsigned int)tmp + PA_VOLUME_MUTED; -} - -double volumeUIntToDoulbe(uint volume) -{ - double tmp = (double)(volume - PA_VOLUME_MUTED); - return tmp / (double)(PA_VOLUME_NORM - PA_VOLUME_MUTED); -} - -DBusPulseVolume::DBusPulseVolume() : - QObject() -{ - std::unique_ptr basicConnectionInterface(new DBusPropertiesInterface("org.PulseAudio1", - "/org/pulseaudio/server_lookup1", - QDBusConnection::sessionBus(), 0)); - - QDBusReply connection_string = basicConnectionInterface->call(QLatin1String("Get"), - QLatin1String("org.PulseAudio.ServerLookup1"), - QLatin1String("Address")); - - if (!connection_string.isValid()) - { - qWarning() << "DBusPulseVolume::DBusPulseVolume(): D-Bus error: " << connection_string.error().message(); - } - - connection_.reset(new QDBusConnection(QDBusConnection::connectToPeer(connection_string.value().toString(), "set-volume"))); - - if (connection_->isConnected()) - { - interface_paths_.reset(new StreamRestoreInterface("org.PulseAudio.Ext.StreamRestore1", - "/org/pulseaudio/stream_restore1", - *(connection_.get()), 0)); - if (interface_paths_) - { - // get the role paths - fillRolePath("multimedia"); - fillRolePath("alert"); - fillRolePath("alarm"); - fillRolePath("phone"); - } - - initializeAccountsInterface(); - } - else - { - qWarning() << "DBusPulseVolume::DBusPulseVolume(): Error connecting: " << connection_->lastError().message(); - } -} - -DBusPulseVolume::~DBusPulseVolume() -{ -} - -QString DBusPulseVolume::fillRolePath(QString const &role) -{ - QString role_complete_name = QString("sink-input-by-media-role:") + role; - // get the role paths - QDBusReply objectPath = interface_paths_->call(QLatin1String("GetEntryByName"), role_complete_name); - if (!objectPath.isValid()) - { - qWarning() << "SetVolume::fillRolePath(): D-Bus error: " << objectPath.error().message(); - return ""; - } - - auto role_info = std::make_shared(); - role_info->interface_.reset(new DBusPropertiesInterface("org.PulseAudio.Ext.StreamRestore1.RestoreEntry", - objectPath.value().path(), - *(connection_.get()), 0)); - if (!role_info->interface_) - { - qWarning() << "SetVolume::fillRolePath() - Error obtaining properties interface"; - return ""; - } - role_info->path_ = objectPath.value().path(); - roles_map_[role] = role_info; - return role_info->path_; -} - -bool DBusPulseVolume::setVolume(QString const & role, double volume) -{ - if (!interface_paths_) - { - qWarning() << "SetVolume::setVolume(): error: Volume interfaces are not initialized"; - return false; - } - - RolesMap::const_iterator iter = roles_map_.find(role); - if (iter != roles_map_.end()) - { - QVariant var; - PulseaudioVolumeArray t; - PulseaudioVolume vv(0, volumeDoubleToUint(volume)); - t.addItem(vv); - var.setValue(t); - QDBusVariant dbusVar(var); - QDBusReply set_vol = (*iter).second->interface_->call(QLatin1String("Set"), - QVariant::fromValue(QString("org.PulseAudio.Ext.StreamRestore1.RestoreEntry")), - QVariant::fromValue(QString("Volume")), - QVariant::fromValue(dbusVar)); - - if (!set_vol.isValid()) - { - qWarning() << "SetVolume::setVolume(): D-Bus error: " << set_vol.error().message(); - return false; - } - - if (accounts_interface_) - { - QDBusVariant dbusVar(QVariant::fromValue(volume)); - QDBusReply set_vol = accounts_interface_->call(QLatin1String("Set"), - QVariant::fromValue(QString("com.ubuntu.AccountsService.Sound")), - QVariant::fromValue(QString("Volume")), - QVariant::fromValue(dbusVar)); - if (!set_vol.isValid()) - { - qWarning() << "SetVolume::setVolume(): D-Bus error: " << set_vol.error().message(); - return false; - } - } - } - else - { - qWarning() << "SetVolume::setVolume(): role " << role << " was not found."; - return false; - } - return true; -} - -double DBusPulseVolume::getVolume(QString const & role) -{ - if (interface_paths_) - { - RolesMap::const_iterator iter = roles_map_.find(role); - if (iter != roles_map_.end()) - { - QDBusReply prev_vol = (*iter).second->interface_->call(QLatin1String("Get"), - QLatin1String("org.PulseAudio.Ext.StreamRestore1.RestoreEntry"), - QLatin1String("Volume")); - - if (!prev_vol.isValid()) - { - qWarning() << "SetVolume::setVolume(): D-Bus error: " << prev_vol.error().message(); - } - QDBusArgument arg = prev_vol.value().value(); - PulseaudioVolumeArray element; - arg >> element; - return volumeUIntToDoulbe(element.getItem(0).getVolume()); - } - } - return -1.0; -} - -void DBusPulseVolume::initializeAccountsInterface() -{ - auto username = qgetenv("USER"); - if (username != "") - { - qDebug() << "Setting Accounts interface for user: " << username; - std::unique_ptr setInterface(new AccountsInterface("org.freedesktop.Accounts", - "/org/freedesktop/Accounts", - QDBusConnection::systemBus(), 0)); - - QDBusReply userResp = setInterface->call(QLatin1String("FindUserByName"), - QLatin1String(username)); - - if (!userResp.isValid()) - { - qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << userResp.error().message(); - } - auto userPath = userResp.value().path(); - if (userPath != "") - { - std::unique_ptr soundInterface(new AccountsSoundInterface("org.freedesktop.Accounts", - userPath, - QDBusConnection::systemBus(), 0)); - - accounts_interface_.reset(new DBusPropertiesInterface("org.freedesktop.Accounts", - userPath, - soundInterface->connection(), 0)); - if (!accounts_interface_->isValid()) - { - qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << accounts_interface_->lastError().message(); - } - signal_spy_volume_changed_.reset(new QSignalSpy(accounts_interface_.get(),&DBusPropertiesInterface::PropertiesChanged)); - } - } -} - -bool DBusPulseVolume::waitForVolumeChangedInAccountsService() -{ - if (signal_spy_volume_changed_) - { - return signal_spy_volume_changed_->wait(); - } - else - { - qWarning() << "DBusPulseVolume::waitForVolumeChangedInAccountsService(): signal was not instantiated"; - } - return false; -} diff --git a/tests/integration/utils/dbus-pulse-volume.h b/tests/integration/utils/dbus-pulse-volume.h deleted file mode 100644 index 2d55e89..0000000 --- a/tests/integration/utils/dbus-pulse-volume.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#pragma once - -#include -#include - -#include -#include - -class StreamRestoreInterface; -class DBusPropertiesInterface; -class AccountsInterface; -class AccountsSoundInterface; -class QSignalSpy; - -struct RoleInformation -{ - std::unique_ptr interface_; - QString path_; -}; - -class DBusPulseVolume : public QObject -{ -public: - DBusPulseVolume(); - ~DBusPulseVolume(); - - bool setVolume(QString const & role, double volume); - double getVolume(QString const & role); - bool waitForVolumeChangedInAccountsService(); - -protected: - QString fillRolePath(QString const &role); - void initializeAccountsInterface(); - std::unique_ptr connection_; - std::unique_ptr interface_paths_; - typedef std::map> RolesMap; - RolesMap roles_map_; - std::unique_ptr accounts_interface_; - std::unique_ptr signal_spy_volume_changed_; -}; diff --git a/tests/integration/utils/get-volume.cpp b/tests/integration/utils/get-volume.cpp deleted file mode 100644 index 309da36..0000000 --- a/tests/integration/utils/get-volume.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ - -#include "dbus-types.h" - -#include -#include "dbus-pulse-volume.h" - -int main(int argc, char **argv) -{ - DBusTypes::registerMetaTypes(); - if (argc == 2) - { - DBusPulseVolume volume; - volume.getVolume(argv[1]); - } - return 0; -} diff --git a/tests/integration/utils/set-volume.cpp b/tests/integration/utils/set-volume.cpp deleted file mode 100644 index d29e5ef..0000000 --- a/tests/integration/utils/set-volume.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ - -#include "dbus-types.h" - -#include -#include "dbus-pulse-volume.h" - -int main(int argc, char **argv) -{ - DBusTypes::registerMetaTypes(); - if (argc == 3) - { - DBusPulseVolume volume; - if(!volume.setVolume(argv[1], std::stod(argv[2]))) - { - return 1; - } - } - return 0; -} diff --git a/tests/notifications-test.cc b/tests/notifications-test.cc index c5d9770..8d5617d 100644 --- a/tests/notifications-test.cc +++ b/tests/notifications-test.cc @@ -345,7 +345,7 @@ TEST_F(NotificationsTest, HighVolume) { auto notev = notifications->getNotifications(); ASSERT_EQ(1, notev.size()); EXPECT_EQ("Volume", notev[0].summary); - EXPECT_EQ("Speakers", notev[0].body); + EXPECT_EQ("", notev[0].body); EXPECT_GVARIANT_EQ("@s 'false'", notev[0].hints["x-canonical-value-bar-tint"]); /* Set high volume with volume change */ @@ -356,7 +356,7 @@ TEST_F(NotificationsTest, HighVolume) { notev = notifications->getNotifications(); ASSERT_LT(0, notev.size()); /* This passes with one or two since it would just be an update to the first if a second was sent */ EXPECT_EQ("Volume", notev[0].summary); - EXPECT_EQ("Speakers", notev[0].body); + EXPECT_EQ("High volume can damage your hearing.", notev[0].body); EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]); /* Move it back */ @@ -372,7 +372,7 @@ TEST_F(NotificationsTest, HighVolume) { notev = notifications->getNotifications(); ASSERT_EQ(1, notev.size()); EXPECT_EQ("Volume", notev[0].summary); - EXPECT_EQ("Speakers", notev[0].body); + EXPECT_EQ("High volume can damage your hearing.", notev[0].body); EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]); } diff --git a/tests/service-mocks/CMakeLists.txt b/tests/service-mocks/CMakeLists.txt deleted file mode 100644 index 9cd8acb..0000000 --- a/tests/service-mocks/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_subdirectory(accounts-mock) -add_subdirectory(media-player-mpris-mock) diff --git a/tests/service-mocks/DBusPropertiesNotifier.cpp b/tests/service-mocks/DBusPropertiesNotifier.cpp deleted file mode 100644 index 686e4e9..0000000 --- a/tests/service-mocks/DBusPropertiesNotifier.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ - -#include "DBusPropertiesNotifier.h" - -#include - -using namespace ubuntu::indicators::testing; - -void DBusPropertiesNotifier::notifyPropertyChanged(QDBusConnection const & connection, - QString const & interface, - QString const & path, - QString const & propertyName, - QVariant const & propertyValue) -{ - QDBusMessage signal = QDBusMessage::createSignal( - path, - "org.freedesktop.DBus.Properties", - "PropertiesChanged"); - signal << interface; - QVariantMap changedProps; - changedProps.insert(propertyName, propertyValue); - signal << changedProps; - signal << QStringList(); - connection.send(signal); -} diff --git a/tests/service-mocks/DBusPropertiesNotifier.h b/tests/service-mocks/DBusPropertiesNotifier.h deleted file mode 100644 index 9fa013b..0000000 --- a/tests/service-mocks/DBusPropertiesNotifier.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#pragma once - -#include - -namespace ubuntu -{ - -namespace indicators -{ - -namespace testing -{ - -class DBusPropertiesNotifier -{ -public: - DBusPropertiesNotifier() = default; - ~DBusPropertiesNotifier() = default; - - void notifyPropertyChanged(QDBusConnection const & connection, - QString const & interface, - QString const & path, - QString const & propertyName, - QVariant const & propertyValue); -}; - -} // namespace testing - -} // namespace indicators - -} // namespace ubuntu diff --git a/tests/service-mocks/accounts-mock/AccountsDefs.h b/tests/service-mocks/accounts-mock/AccountsDefs.h deleted file mode 100644 index 0e4f270..0000000 --- a/tests/service-mocks/accounts-mock/AccountsDefs.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#pragma once - -namespace ubuntu -{ - -namespace indicators -{ - -namespace testing -{ - constexpr const char ACCOUNTS_SERVICE[] = "org.freedesktop.Accounts"; - constexpr const char USER_PATH[] = "/org/freedesktop/Accounts/UserTest"; - constexpr const char ACCOUNTS_PATH[] = "/org/freedesktop/Accounts"; - constexpr const char ACCOUNTS_SOUND_INTERFACE[] = "com.ubuntu.AccountsService.Sound"; -} // namespace testing - -} // namespace indicators - -} // namespace ubuntu - diff --git a/tests/service-mocks/accounts-mock/AccountsMock.cpp b/tests/service-mocks/accounts-mock/AccountsMock.cpp deleted file mode 100644 index 5c92dc5..0000000 --- a/tests/service-mocks/accounts-mock/AccountsMock.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#include - -#include "AccountsMock.h" -#include "AccountsDefs.h" - -using namespace ubuntu::indicators::testing; - -AccountsMock::AccountsMock(QObject* parent) - : QObject(parent) -{ -} - -AccountsMock::~AccountsMock() = default; - -QDBusObjectPath AccountsMock::FindUserByName(QString const & username) const -{ - return QDBusObjectPath(USER_PATH); -} - -QDBusObjectPath AccountsMock::FindUserById(int64_t uid) const -{ - return QDBusObjectPath(USER_PATH); -} diff --git a/tests/service-mocks/accounts-mock/AccountsMock.h b/tests/service-mocks/accounts-mock/AccountsMock.h deleted file mode 100644 index 72372e0..0000000 --- a/tests/service-mocks/accounts-mock/AccountsMock.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#pragma once - -#include -#include -#include - -namespace ubuntu -{ - -namespace indicators -{ - -namespace testing -{ - -class AccountsMock : public QObject, protected QDBusContext -{ - Q_OBJECT - -public Q_SLOTS: - QDBusObjectPath FindUserByName(QString const & username) const; - QDBusObjectPath FindUserById(int64_t uid) const; - -public: - AccountsMock(QObject* parent = 0); - virtual ~AccountsMock(); -}; - -} // namespace testing - -} // namespace indicators - -} // namespace ubuntu diff --git a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp deleted file mode 100644 index 37de377..0000000 --- a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#include -#include -#include - -#include "AccountsServiceSoundMock.h" -#include "AccountsDefs.h" - -using namespace ubuntu::indicators::testing; - -AccountsServiceSoundMock::AccountsServiceSoundMock(QObject* parent) - : QObject(parent) - , volume_(0.0) -{ -} - -AccountsServiceSoundMock::~AccountsServiceSoundMock() = default; - -double AccountsServiceSoundMock::volume() const -{ - return volume_; -} - -void AccountsServiceSoundMock::setVolume(double volume) -{ - volume_ = volume; - notifier_.notifyPropertyChanged(QDBusConnection::systemBus(), - ACCOUNTS_SOUND_INTERFACE, - USER_PATH, - "Volume", - property("Volume")); -} diff --git a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h deleted file mode 100644 index bb3dbe8..0000000 --- a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#pragma once - -#include -#include - -#include "DBusPropertiesNotifier.h" - -namespace ubuntu -{ - -namespace indicators -{ - -namespace testing -{ - -class DBusPropertiesNotifier; - -class AccountsServiceSoundMock : public QObject, protected QDBusContext -{ - Q_OBJECT - Q_PROPERTY(double Volume READ volume WRITE setVolume) - -public Q_SLOTS: - double volume() const; - void setVolume(double volume); - -public: - AccountsServiceSoundMock(QObject* parent = 0); - virtual ~AccountsServiceSoundMock(); - -private: - double volume_; - DBusPropertiesNotifier notifier_; -}; - -} // namespace testing - -} // namespace indicators - -} // namespace ubuntu diff --git a/tests/service-mocks/accounts-mock/CMakeLists.txt b/tests/service-mocks/accounts-mock/CMakeLists.txt deleted file mode 100644 index ad858bd..0000000 --- a/tests/service-mocks/accounts-mock/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -find_package(Qt5DBus REQUIRED) -include_directories(${Qt5DBus_INCLUDE_DIRS} - "${CMAKE_SOURCE_DIR}/tests/service-mocks") - -add_definitions(-DQT_NO_KEYWORDS=1) - -set(dbusinterface_sound_xml "com.ubuntu.AccountsService.Sound.Mock.xml") -set_source_files_properties(${dbusinterface_sound_xml} PROPERTIES - CLASSNAME AccountsServiceSoundMockInterface) - -qt5_add_dbus_interface(interface_files ${dbusinterface_sound_xml} accountsservice_sound_interface) - -qt5_add_dbus_adaptor(adaptor_files - com.ubuntu.AccountsService.Sound.Mock.xml - AccountsServiceSoundMock.h - ubuntu::indicators::testing::AccountsServiceSoundMock - AccountsServiceSoundMockAdaptor) - -qt5_add_dbus_adaptor(adaptor_files - ${CMAKE_SOURCE_DIR}/tests/dbus-types/org.freedesktop.Accounts.xml - AccountsMock.h - ubuntu::indicators::testing::AccountsMock - AccountsMockAdaptor) - -add_executable( - accounts-service-sound - ${interface_files} - ${adaptor_files} - AccountsServiceSoundMock.cpp - AccountsMock.cpp - ${CMAKE_SOURCE_DIR}/tests/service-mocks/DBusPropertiesNotifier.cpp - main.cpp -) - -qt5_use_modules( - accounts-service-sound - Core - DBus -) diff --git a/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml b/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml deleted file mode 100644 index 859cd46..0000000 --- a/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/tests/service-mocks/accounts-mock/main.cpp b/tests/service-mocks/accounts-mock/main.cpp deleted file mode 100644 index d6cd1d3..0000000 --- a/tests/service-mocks/accounts-mock/main.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#include -#include - -#include - -#include "AccountsDefs.h" -#include "AccountsServiceSoundMock.h" -#include "AccountsServiceSoundMockAdaptor.h" -#include "AccountsMock.h" -#include "AccountsMockAdaptor.h" - -using namespace ubuntu::indicators::testing; - -int main(int argc, char *argv[]) -{ - QCoreApplication app(argc, argv); - QDBusConnection connection = QDBusConnection::systemBus(); - if (!connection.interface()->isServiceRegistered(ACCOUNTS_SERVICE)) - { - auto service = new AccountsServiceSoundMock(&app); - new SoundAdaptor(service); - - auto accounts_service = new AccountsMock(&app); - new AccountsAdaptor(accounts_service); - - if (!connection.registerService(ACCOUNTS_SERVICE)) - { - qFatal("Could not register AccountsService Volume service."); - } - - if (!connection.registerObject(USER_PATH, service)) - { - qFatal("Could not register AccountsService Volume object."); - } - - if (!connection.registerObject(ACCOUNTS_PATH, accounts_service)) - { - qFatal("Could not register Accounts object."); - } - } - else - { - qDebug() << "Service is already registered!."; - } - return app.exec(); -} diff --git a/tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml b/tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml deleted file mode 100644 index f977e7c..0000000 --- a/tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/tests/service-mocks/media-player-mpris-mock/CMakeLists.txt b/tests/service-mocks/media-player-mpris-mock/CMakeLists.txt deleted file mode 100644 index b5f5861..0000000 --- a/tests/service-mocks/media-player-mpris-mock/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fPIC -pthread") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -fno-strict-aliasing -Wextra -fPIC -pthread") - -find_package(Qt5DBus REQUIRED) -include_directories(${Qt5DBus_INCLUDE_DIRS} - "${CMAKE_SOURCE_DIR}/tests/service-mocks") - -add_definitions(-DQT_NO_KEYWORDS=1) - -set(dbusinterface_mediaplayermpris_xml "org.mpris.MediaPlayer2.Player.xml") -set_source_files_properties(${dbusinterface_mediaplayermpris_xml} PROPERTIES - CLASSNAME MediaPlayerMprisMockInterface) - -qt5_add_dbus_interface(interface_files ${dbusinterface_mediaplayermpris_xml} MediaPlayerMprisMockInterface) - -qt5_add_dbus_adaptor(adaptor_files - org.mpris.MediaPlayer2.Player.xml - MediaPlayerMprisMock.h - ubuntu::indicators::testing::MediaPlayerMprisMock - MediaPlayerMprisMockAdaptor) - -qt5_add_dbus_adaptor(adaptor_files - org.mpris.MediaPlayer2.xml - MediaPlayerMprisMock.h - ubuntu::indicators::testing::MediaPlayerMprisMock - MediaPlayer2MockAdaptor) - -add_executable( - media-player-mpris-mock - ${adaptor_files} - MediaPlayerMprisMock.cpp - ${CMAKE_SOURCE_DIR}/tests/service-mocks/DBusPropertiesNotifier.cpp - main.cpp - testplayers -) - -add_executable( - media-player-mpris-mock-update - ${interface_files} - player-update.cpp - testplayers -) - -qt5_use_modules( - media-player-mpris-mock - Core - DBus -) - -qt5_use_modules( - media-player-mpris-mock-update - Core - DBus -) - -# test players desktop files -add_custom_command (OUTPUT testplayers - DEPENDS ${CMAKE_SOURCE_DIR}/tests/service-mocks/media-player-mpris-mock/applications - COMMAND mkdir -p ${XDG_DATA_DIRS} - COMMAND cp -r ${CMAKE_SOURCE_DIR}/tests/service-mocks/media-player-mpris-mock/applications ${XDG_DATA_DIRS}) diff --git a/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h b/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h deleted file mode 100644 index 4d28b38..0000000 --- a/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#pragma once - -namespace ubuntu -{ - -namespace indicators -{ - -namespace testing -{ - constexpr const char MEDIA_PLAYER_MPRIS_SERVICE[] = "org.freedesktop.Accounts"; - constexpr const char USER_PATH[] = "/org/freedesktop/Accounts/UserTest"; - constexpr const char ACCOUNTS_PATH[] = "/org/freedesktop/Accounts"; - constexpr const char ACCOUNTS_SOUND_INTERFACE[] = "com.ubuntu.AccountsService.Sound"; -} // namespace testing - -} // namespace indicators - -} // namespace ubuntu - diff --git a/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp b/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp deleted file mode 100644 index 25fe0b7..0000000 --- a/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#include - -#include "MediaPlayerMprisMock.h" - -using namespace ubuntu::indicators::testing; - -MediaPlayerMprisMock::MediaPlayerMprisMock(QString const &playerName, QObject* parent) - : QObject(parent) - , can_play_(true) - , can_pause_(true) - , can_gonext_(true) - , can_goprevious_(true) - , player_name_(playerName) -{ -} - -MediaPlayerMprisMock::~MediaPlayerMprisMock() = default; - -bool MediaPlayerMprisMock::canPlay() const -{ - return can_play_; -} - -void MediaPlayerMprisMock::setCanPlay(bool canPlay) -{ - can_play_ = canPlay; - notifier_.notifyPropertyChanged(QDBusConnection::sessionBus(), - "org.mpris.MediaPlayer2.Player", - "/org/mpris/MediaPlayer2", - "CanPlay", - property("CanPlay")); -} - -bool MediaPlayerMprisMock::canPause() const -{ - return can_pause_; -} - -void MediaPlayerMprisMock::setCanPause(bool canPause) -{ - can_pause_ = canPause; - notifier_.notifyPropertyChanged(QDBusConnection::sessionBus(), - "org.mpris.MediaPlayer2.Player", - "/org/mpris/MediaPlayer2", - "CanPause", - property("CanPause")); -} - -bool MediaPlayerMprisMock::canGoNext() const -{ - return can_gonext_; -} - -void MediaPlayerMprisMock::setCanGoNext(bool canGoNext) -{ - can_gonext_ = canGoNext; - notifier_.notifyPropertyChanged(QDBusConnection::sessionBus(), - "org.mpris.MediaPlayer2.Player", - "/org/mpris/MediaPlayer2", - "CanGoNext", - property("CanGoNext")); -} - -bool MediaPlayerMprisMock::canGoPrevious() const -{ - return can_goprevious_; -} - -void MediaPlayerMprisMock::setCanGoPrevious(bool canGoPrevious) -{ - can_goprevious_ = canGoPrevious; - notifier_.notifyPropertyChanged(QDBusConnection::sessionBus(), - "org.mpris.MediaPlayer2.Player", - "/org/mpris/MediaPlayer2", - "CanGoPrevious", - property("CanGoPrevious")); -} - -QString MediaPlayerMprisMock::desktopEntry() const -{ - return player_name_; -} - -void MediaPlayerMprisMock::setDesktopEntry(QString const &) -{ -} diff --git a/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h b/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h deleted file mode 100644 index 58dce8d..0000000 --- a/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#pragma once - -#include -#include -#include - -#include "DBusPropertiesNotifier.h" - -namespace ubuntu -{ - -namespace indicators -{ - -namespace testing -{ - -class MediaPlayerMprisMock : public QObject, protected QDBusContext -{ - Q_OBJECT - Q_PROPERTY(bool CanPlay READ canPlay WRITE setCanPlay) - Q_PROPERTY(bool CanPause READ canPause WRITE setCanPause) - Q_PROPERTY(bool CanGoNext READ canGoNext WRITE setCanGoNext) - Q_PROPERTY(bool CanGoPrevious READ canGoPrevious WRITE setCanGoPrevious) - Q_PROPERTY(QString DesktopEntry READ desktopEntry WRITE setDesktopEntry) - -public Q_SLOTS: - bool canPlay() const; - void setCanPlay(bool canPlay); - - bool canPause() const; - void setCanPause(bool canPause); - - bool canGoNext() const; - void setCanGoNext(bool canGoNext); - - bool canGoPrevious() const; - void setCanGoPrevious(bool canGoPrevious); - - QString desktopEntry() const; - void setDesktopEntry(QString const &destopEntry); - -public: - MediaPlayerMprisMock(QString const &playerName, QObject* parent = 0); - virtual ~MediaPlayerMprisMock(); - -private: - bool can_play_; - bool can_pause_; - bool can_gonext_; - bool can_goprevious_; - DBusPropertiesNotifier notifier_; - QString player_name_; -}; - -} // namespace testing - -} // namespace indicators - -} // namespace ubuntu diff --git a/tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop b/tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop deleted file mode 100644 index 2ed5008..0000000 --- a/tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop +++ /dev/null @@ -1,21 +0,0 @@ -[Desktop Entry] -Name=TestPlayer1 -GenericName=Test Player 1 -X-GNOME-FullName=Test Player 1 -Comment=Play and organize your music collection -Keywords=Audio;Song;MP3;CD;Podcast;MTP;iPod;Playlist;Last.fm;UPnP;DLNA;Radio; -Exec=echo %U -Terminal=false -Type=Application -Icon=testplayer -X-GNOME-DocPath=testplayer/testplayer.xml -Categories=GNOME;GTK;AudioVideo;Audio;Player; -MimeType=application/x-ogg;application/ogg;audio/x-vorbis+ogg;audio/x-scpls;audio/x-mp3;audio/x-mpeg;audio/mpeg;audio/x-mpegurl;audio/x-flac;audio/mp4;x-scheme-handler/itms;x-scheme-handler/itmss; -StartupNotify=true -X-GNOME-Bugzilla-Bugzilla=GNOME -X-GNOME-Bugzilla-Product=testplayer -X-GNOME-Bugzilla-Component=general -X-GNOME-Bugzilla-OtherBinaries=rhythmbox-client;rhythmbox-metadata; -X-GNOME-Bugzilla-Version=3.1 -X-GNOME-UsesNotifications=true - diff --git a/tests/service-mocks/media-player-mpris-mock/main.cpp b/tests/service-mocks/media-player-mpris-mock/main.cpp deleted file mode 100644 index 8945673..0000000 --- a/tests/service-mocks/media-player-mpris-mock/main.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#include -#include - -#include - -#include "MediaPlayerMprisDefs.h" -#include "MediaPlayerMprisMock.h" -#include "MediaPlayerMprisMockAdaptor.h" -#include "MediaPlayer2MockAdaptor.h" - -using namespace ubuntu::indicators::testing; - -int main(int argc, char *argv[]) -{ - if (argc != 2) - { - qWarning() << "usage: " << argv[0] << "TEST_PLAYER_NAME"; - return 1; - } - QString playerName = QString(argv[1]); - - QCoreApplication app(argc, argv); - QString playerService = QString("org.mpris.MediaPlayer2.") + playerName; - QDBusConnection connection = QDBusConnection::sessionBus(); - if (!connection.interface()->isServiceRegistered(playerService)) - { - auto service = new MediaPlayerMprisMock(playerName, &app); - new PlayerAdaptor(service); - new MediaPlayer2Adaptor(service); - - if (!connection.registerService(playerService)) - { - qDebug() << connection.lastError(); - qFatal("Could not register MediaPlayerMprisMock service."); - } - - if (!connection.registerObject("/org/mpris/MediaPlayer2", service)) - { - qFatal("Could not register MediaPlayerMprisMock object."); - } - } - else - { - qDebug() << "Service is already registered!."; - } - return app.exec(); -} diff --git a/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml b/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml deleted file mode 100644 index 3efd002..0000000 --- a/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml b/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml deleted file mode 100644 index 489c68a..0000000 --- a/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/tests/service-mocks/media-player-mpris-mock/player-update.cpp b/tests/service-mocks/media-player-mpris-mock/player-update.cpp deleted file mode 100644 index 5768372..0000000 --- a/tests/service-mocks/media-player-mpris-mock/player-update.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2015 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: Xavi Garcia - */ -#include "MediaPlayerMprisMockInterface.h" - -#include - -QMap getPropertiesMap() -{ - QMap retMap; - - retMap["CANPLAY"] = "setCanPlay"; - retMap["CANPAUSE"] = "setCanPause"; - retMap["CANGONEXT"] = "setCanGoNext"; - retMap["CANGOPREVIOUS"] = "setCanGoPrevious"; - - return retMap; -} - -bool getBoolValue(QString const & str) -{ - if (str == "TRUE") - { - return true; - } - return false; -} - -QTextStream& qStdErr() -{ - static QTextStream ts( stderr ); - return ts; -} - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, argv); - - if (argc != 4) - { - qStdErr() << "usage: " << argv[0] << "TEST_PLAYER_NAME PropertyName true|false\n"; - return 1; - } - - QString state = QString(argv[3]).toUpper(); - QString property = QString(argv[2]).toUpper(); - QString playerName = QString(argv[1]); - - auto propertiesMap = getPropertiesMap(); - - std::shared_ptr iface( - new MediaPlayerMprisMockInterface("org.mpris.MediaPlayer2." + playerName, - "/org/mpris/MediaPlayer2", - QDBusConnection::sessionBus())); - if (!iface) - { - qWarning() << argv[0] << ": error creating interface"; - return 1; - } - - QMap::iterator iter = propertiesMap.find(property); - if (iter == propertiesMap.end()) - { - qWarning() << argv[0] << ": property " << property << " was not found."; - return 1; - } - - QDBusReply set_prop = iface->call(QLatin1String((*iter).toStdString().c_str()), - QVariant::fromValue(getBoolValue(state))); - - if (!set_prop.isValid()) - { - qWarning() << argv[0] << ": D-Bus error: " << set_prop.error().message(); - return 1; - } - - return 0; -} - -- cgit v1.2.3 From 478fe08844d29d0383c2c939e504f65ffcd25573 Mon Sep 17 00:00:00 2001 From: Timo Jyrinki Date: Wed, 23 Dec 2015 14:53:01 +0200 Subject: Restore changelog entries for CI Train purposes but note that they should be ignored as they were reverted. --- debian/changelog | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/debian/changelog b/debian/changelog index b01835d..906ab89 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,29 @@ +indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium + + [ Xavi Garcia Mena ] + * Very simple change to make upstart try to respawn the process 30 + times (instead of 2). This is a workaround for the case when dbus + session is not ready yet at startup. + + [ Timo Jyrinki ] + * Note: this release was reverted on 20151223, ignore this changelog entry. + + -- Xavi Garcia Tue, 22 Dec 2015 09:50:08 +0000 + +indicator-sound (12.10.2+16.04.20151103.2-0ubuntu1) xenial; urgency=medium + + [ Xavi Garcia ] + * Added OSD notifications to show output source when the volume or + source change (LP: #1382548, LP: #1415480, LP: #1484589) + + [ CI Train Bot ] + * New rebuild forced. + + [ Timo Jyrinki ] + * Note: this release was reverted on 20151223, ignore this changelog entry. + + -- Xavi Garcia Tue, 03 Nov 2015 17:47:10 +0000 + indicator-sound (12.10.2+15.10.20151019-0ubuntu1) wily; urgency=medium [ Xavi Garcia Mena ] -- cgit v1.2.3 -- cgit v1.2.3 From 1c234a0a40617b2f696f56b8d748596fbb8ff6e6 Mon Sep 17 00:00:00 2001 From: CI Train Bot Date: Wed, 23 Dec 2015 12:54:33 +0000 Subject: Releasing 12.10.2+16.04.20151223-0ubuntu1 --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index 906ab89..60d1d97 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +indicator-sound (12.10.2+16.04.20151223-0ubuntu1) xenial; urgency=medium + + [ CI Train Bot ] + * No-change rebuild. + + -- Xavi Garcia Wed, 23 Dec 2015 12:54:33 +0000 + indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium [ Xavi Garcia Mena ] -- cgit v1.2.3 From da78e6c75cab23d837cb4768efa3838ddb2286e7 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Wed, 23 Dec 2015 14:22:35 +0100 Subject: restore OSD notifications --- debian/changelog | 23 +- include/CMakeLists.txt | 1 + include/unity/CMakeLists.txt | 1 + include/unity/gmenuharness/MatchResult.h | 66 ++ include/unity/gmenuharness/MatchUtils.h | 42 + include/unity/gmenuharness/MenuItemMatcher.h | 143 +++ include/unity/gmenuharness/MenuMatcher.h | 95 ++ src/CMakeLists.txt | 21 +- src/gmenuharness/CMakeLists.txt | 17 + src/gmenuharness/MatchResult.cpp | 187 ++++ src/gmenuharness/MatchUtils.cpp | 77 ++ src/gmenuharness/MenuItemMatcher.cpp | 1008 ++++++++++++++++++++ src/gmenuharness/MenuMatcher.cpp | 208 ++++ src/service.vala | 407 +++++++- src/sound-menu.vala | 38 + src/volume-control-pulse.vala | 83 +- src/volume-control.vala | 14 + tests/CMakeLists.txt | 203 ++-- tests/dbus-types/CMakeLists.txt | 53 + .../com.ubuntu.AccountsService.Sound.xml | 9 + tests/dbus-types/dbus-types.h | 48 + .../org.PulseAudio.Ext.StreamRestore1.xml | 7 + tests/dbus-types/org.freedesktop.Accounts.xml | 9 + .../dbus-types/org.freedesktop.DBus.Properties.xml | 22 + tests/dbus-types/org.freedesktop.Notifications.xml | 47 + tests/dbus-types/org.gtk.Actions.xml | 13 + tests/dbus-types/pulseaudio-volume.cpp | 156 +++ tests/dbus-types/pulseaudio-volume.h | 69 ++ tests/integration/CMakeLists.txt | 132 +++ tests/integration/Copy of test-sound.wav | Bin 0 -> 191989 bytes tests/integration/indicator-sound-test-base.cpp | 823 ++++++++++++++++ tests/integration/indicator-sound-test-base.h | 160 ++++ tests/integration/main.cpp | 58 ++ tests/integration/test-indicator.cpp | 981 +++++++++++++++++++ tests/integration/test-sound.wav | Bin 0 -> 7665640 bytes tests/integration/touch-stream-restore.table | 4 + tests/integration/utils/dbus-pulse-volume.cpp | 232 +++++ tests/integration/utils/dbus-pulse-volume.h | 57 ++ tests/integration/utils/get-volume.cpp | 33 + tests/integration/utils/set-volume.cpp | 36 + tests/notifications-test.cc | 6 +- tests/service-mocks/CMakeLists.txt | 2 + tests/service-mocks/DBusPropertiesNotifier.cpp | 41 + tests/service-mocks/DBusPropertiesNotifier.h | 48 + tests/service-mocks/accounts-mock/AccountsDefs.h | 37 + tests/service-mocks/accounts-mock/AccountsMock.cpp | 40 + tests/service-mocks/accounts-mock/AccountsMock.h | 50 + .../accounts-mock/AccountsServiceSoundMock.cpp | 48 + .../accounts-mock/AccountsServiceSoundMock.h | 58 ++ tests/service-mocks/accounts-mock/CMakeLists.txt | 42 + .../com.ubuntu.AccountsService.Sound.Mock.xml | 6 + tests/service-mocks/accounts-mock/main.cpp | 63 ++ .../org.freedesktop.Accounts.Mock.xml | 15 + .../media-player-mpris-mock/CMakeLists.txt | 63 ++ .../media-player-mpris-mock/MediaPlayerMprisDefs.h | 37 + .../MediaPlayerMprisMock.cpp | 103 ++ .../media-player-mpris-mock/MediaPlayerMprisMock.h | 77 ++ .../applications/testplayer1.desktop | 21 + .../service-mocks/media-player-mpris-mock/main.cpp | 64 ++ .../org.mpris.MediaPlayer2.Player.xml | 24 + .../org.mpris.MediaPlayer2.xml | 6 + .../media-player-mpris-mock/player-update.cpp | 93 ++ 62 files changed, 6330 insertions(+), 197 deletions(-) create mode 100644 include/CMakeLists.txt create mode 100644 include/unity/CMakeLists.txt create mode 100644 include/unity/gmenuharness/MatchResult.h create mode 100644 include/unity/gmenuharness/MatchUtils.h create mode 100644 include/unity/gmenuharness/MenuItemMatcher.h create mode 100644 include/unity/gmenuharness/MenuMatcher.h create mode 100644 src/gmenuharness/CMakeLists.txt create mode 100644 src/gmenuharness/MatchResult.cpp create mode 100644 src/gmenuharness/MatchUtils.cpp create mode 100644 src/gmenuharness/MenuItemMatcher.cpp create mode 100644 src/gmenuharness/MenuMatcher.cpp create mode 100644 tests/dbus-types/CMakeLists.txt create mode 100644 tests/dbus-types/com.ubuntu.AccountsService.Sound.xml create mode 100644 tests/dbus-types/dbus-types.h create mode 100644 tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml create mode 100644 tests/dbus-types/org.freedesktop.Accounts.xml create mode 100644 tests/dbus-types/org.freedesktop.DBus.Properties.xml create mode 100644 tests/dbus-types/org.freedesktop.Notifications.xml create mode 100644 tests/dbus-types/org.gtk.Actions.xml create mode 100644 tests/dbus-types/pulseaudio-volume.cpp create mode 100644 tests/dbus-types/pulseaudio-volume.h create mode 100644 tests/integration/CMakeLists.txt create mode 100644 tests/integration/Copy of test-sound.wav create mode 100644 tests/integration/indicator-sound-test-base.cpp create mode 100644 tests/integration/indicator-sound-test-base.h create mode 100644 tests/integration/main.cpp create mode 100644 tests/integration/test-indicator.cpp create mode 100644 tests/integration/test-sound.wav create mode 100755 tests/integration/touch-stream-restore.table create mode 100644 tests/integration/utils/dbus-pulse-volume.cpp create mode 100644 tests/integration/utils/dbus-pulse-volume.h create mode 100644 tests/integration/utils/get-volume.cpp create mode 100644 tests/integration/utils/set-volume.cpp create mode 100644 tests/service-mocks/CMakeLists.txt create mode 100644 tests/service-mocks/DBusPropertiesNotifier.cpp create mode 100644 tests/service-mocks/DBusPropertiesNotifier.h create mode 100644 tests/service-mocks/accounts-mock/AccountsDefs.h create mode 100644 tests/service-mocks/accounts-mock/AccountsMock.cpp create mode 100644 tests/service-mocks/accounts-mock/AccountsMock.h create mode 100644 tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp create mode 100644 tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h create mode 100644 tests/service-mocks/accounts-mock/CMakeLists.txt create mode 100644 tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml create mode 100644 tests/service-mocks/accounts-mock/main.cpp create mode 100644 tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml create mode 100644 tests/service-mocks/media-player-mpris-mock/CMakeLists.txt create mode 100644 tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h create mode 100644 tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp create mode 100644 tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h create mode 100644 tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop create mode 100644 tests/service-mocks/media-player-mpris-mock/main.cpp create mode 100644 tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml create mode 100644 tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml create mode 100644 tests/service-mocks/media-player-mpris-mock/player-update.cpp diff --git a/debian/changelog b/debian/changelog index 906ab89..d7ed9bd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,28 +1,9 @@ -indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium +indicator-sound (12.10.2+15.10.20151019-0ubuntu2) UNRELEASED; urgency=medium - [ Xavi Garcia Mena ] - * Very simple change to make upstart try to respawn the process 30 - times (instead of 2). This is a workaround for the case when dbus - session is not ready yet at startup. - - [ Timo Jyrinki ] - * Note: this release was reverted on 20151223, ignore this changelog entry. - - -- Xavi Garcia Tue, 22 Dec 2015 09:50:08 +0000 - -indicator-sound (12.10.2+16.04.20151103.2-0ubuntu1) xenial; urgency=medium - - [ Xavi Garcia ] * Added OSD notifications to show output source when the volume or source change (LP: #1382548, LP: #1415480, LP: #1484589) - [ CI Train Bot ] - * New rebuild forced. - - [ Timo Jyrinki ] - * Note: this release was reverted on 20151223, ignore this changelog entry. - - -- Xavi Garcia Tue, 03 Nov 2015 17:47:10 +0000 + -- Xavi Garcia Tue, 03 Nov 2015 12:29:19 +0100 indicator-sound (12.10.2+15.10.20151019-0ubuntu1) wily; urgency=medium diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..9d74e15 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(unity) diff --git a/include/unity/CMakeLists.txt b/include/unity/CMakeLists.txt new file mode 100644 index 0000000..ef57b9e --- /dev/null +++ b/include/unity/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(gmenuharness) diff --git a/include/unity/gmenuharness/MatchResult.h b/include/unity/gmenuharness/MatchResult.h new file mode 100644 index 0000000..8503a98 --- /dev/null +++ b/include/unity/gmenuharness/MatchResult.h @@ -0,0 +1,66 @@ +/* + * 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 . + * + * Authored by: Pete Woods + */ + +#pragma once + +#include +#include +#include + +namespace unity +{ + +namespace gmenuharness +{ + +class MatchResult +{ +public: + MatchResult(); + + MatchResult(MatchResult&& other); + + MatchResult(const MatchResult& other); + + MatchResult& operator=(const MatchResult& other); + + MatchResult& operator=(MatchResult&& other); + + ~MatchResult() = default; + + MatchResult createChild() const; + + void failure(const std::vector& location, const std::string& message); + + void merge(const MatchResult& other); + + bool success() const; + + bool hasTimedOut() const; + + std::string concat_failures() const; + +protected: + struct Priv; + + std::shared_ptr p; +}; + +} // namespace gmenuharness + +} // namespace unity diff --git a/include/unity/gmenuharness/MatchUtils.h b/include/unity/gmenuharness/MatchUtils.h new file mode 100644 index 0000000..899e506 --- /dev/null +++ b/include/unity/gmenuharness/MatchUtils.h @@ -0,0 +1,42 @@ +/* + * 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 . + * + * Authored by: Pete Woods + */ + +#pragma once + +#include +#include + +#include + +namespace unity +{ + +namespace gmenuharness +{ + +void waitForCore(GObject* obj, const std::string& signalName, unsigned int timeout = 10); + +void menuWaitForItems(const std::shared_ptr& menu, unsigned int timeout = 10); + +void g_object_deleter(gpointer object); + +void gvariant_deleter(GVariant* varptr); + +} //namespace gmenuharness + +} // namespace unity diff --git a/include/unity/gmenuharness/MenuItemMatcher.h b/include/unity/gmenuharness/MenuItemMatcher.h new file mode 100644 index 0000000..38a5187 --- /dev/null +++ b/include/unity/gmenuharness/MenuItemMatcher.h @@ -0,0 +1,143 @@ +/* + * 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 . + * + * Authored by: Pete Woods + */ + +#pragma once + +#include +#include +#include + +#include + +namespace unity +{ + +namespace gmenuharness +{ + +class MatchResult; + +class MenuItemMatcher +{ +public: + enum class Mode + { + all, + starts_with, + ends_with + }; + + enum class Type + { + plain, + checkbox, + radio + }; + + static MenuItemMatcher checkbox(); + + static MenuItemMatcher radio(); + + MenuItemMatcher(); + + ~MenuItemMatcher(); + + MenuItemMatcher(const MenuItemMatcher& other); + + MenuItemMatcher(MenuItemMatcher&& other); + + MenuItemMatcher& operator=(const MenuItemMatcher& other); + + MenuItemMatcher& operator=(MenuItemMatcher&& other); + + MenuItemMatcher& type(Type type); + + MenuItemMatcher& label(const std::string& label); + + MenuItemMatcher& action(const std::string& action); + + MenuItemMatcher& state_icons(const std::vector& state); + + MenuItemMatcher& icon(const std::string& icon); + + MenuItemMatcher& themed_icon(const std::string& iconName, const std::vector& icons); + + MenuItemMatcher& widget(const std::string& widget); + + MenuItemMatcher& pass_through_attribute(const std::string& actionName, const std::shared_ptr& value); + + MenuItemMatcher& pass_through_boolean_attribute(const std::string& actionName, bool value); + + MenuItemMatcher& pass_through_string_attribute(const std::string& actionName, const std::string& value); + + MenuItemMatcher& pass_through_double_attribute(const std::string& actionName, double value); + + MenuItemMatcher& round_doubles(double maxDifference); + + MenuItemMatcher& attribute(const std::string& name, const std::shared_ptr& value); + + MenuItemMatcher& boolean_attribute(const std::string& name, bool value); + + MenuItemMatcher& string_attribute(const std::string& name, const std::string& value); + + MenuItemMatcher& int32_attribute(const std::string& name, int value); + + MenuItemMatcher& int64_attribute(const std::string& name, int value); + + MenuItemMatcher& double_attribute(const std::string& name, double value); + + MenuItemMatcher& attribute_not_set(const std::string& name); + + MenuItemMatcher& toggled(bool toggled); + + MenuItemMatcher& mode(Mode mode); + + MenuItemMatcher& submenu(); + + MenuItemMatcher& section(); + + MenuItemMatcher& is_empty(); + + MenuItemMatcher& has_exactly(std::size_t children); + + MenuItemMatcher& item(const MenuItemMatcher& item); + + MenuItemMatcher& item(MenuItemMatcher&& item); + + MenuItemMatcher& pass_through_activate(const std::string& action, const std::shared_ptr& parameter = nullptr); + + MenuItemMatcher& activate(const std::shared_ptr& parameter = nullptr); + + MenuItemMatcher& set_pass_through_action_state(const std::string& action, const std::shared_ptr& state); + + MenuItemMatcher& set_action_state(const std::shared_ptr& state); + + void match(MatchResult& matchResult, const std::vector& location, + const std::shared_ptr& menu, + std::map>& actions, + int index) const; + +protected: + struct Priv; + + std::shared_ptr p; +}; + +} // namespace gmenuharness + +} // namespace unity diff --git a/include/unity/gmenuharness/MenuMatcher.h b/include/unity/gmenuharness/MenuMatcher.h new file mode 100644 index 0000000..c2eb65e --- /dev/null +++ b/include/unity/gmenuharness/MenuMatcher.h @@ -0,0 +1,95 @@ +/* + * 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 . + * + * Authored by: Pete Woods + */ + +#pragma once + +#define EXPECT_MATCHRESULT(statement) \ +do {\ + auto result = (statement);\ + GTEST_TEST_BOOLEAN_(result.success(), #statement, false, true, \ + GTEST_NONFATAL_FAILURE_) << result.concat_failures().c_str(); \ +} while (0) + +#include +#include + +#include +#include + +namespace unity +{ + +namespace gmenuharness +{ + +class MenuMatcher +{ +public: + class Parameters + { + public: + Parameters( + const std::string& busName, + const std::vector>& actions, + const std::string& menuObjectPath); + + ~Parameters(); + + Parameters(const Parameters& other); + + Parameters(Parameters&& other); + + Parameters& operator=(const Parameters& other); + + Parameters& operator=(Parameters&& other); + + protected: + friend MenuMatcher; + + struct Priv; + + std::shared_ptr p; + }; + + MenuMatcher(const Parameters& parameters); + + ~MenuMatcher(); + + MenuMatcher(const MenuMatcher& other) = delete; + + MenuMatcher(MenuMatcher&& other) = delete; + + MenuMatcher& operator=(const MenuMatcher& other) = delete; + + MenuMatcher& operator=(MenuMatcher&& other) = delete; + + MenuMatcher& item(const MenuItemMatcher& item); + + MatchResult match() const; + + void match(MatchResult& matchResult) const; + +protected: + struct Priv; + + std::shared_ptr p; +}; + +} // gmenuharness + +} // unity diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 194dfc9..6c621ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,12 +8,12 @@ set(SYMBOLS_PATH "${CMAKE_CURRENT_BINARY_DIR}/indicator-sound-service.def") set(VAPI_PATH "${CMAKE_CURRENT_BINARY_DIR}/indicator-sound-service.vapi") vapi_gen(accounts-service - LIBRARY - accounts-service - PACKAGES - gio-2.0 - INPUT - /usr/share/gir-1.0/AccountsService-1.0.gir + LIBRARY + accounts-service + PACKAGES + gio-2.0 + INPUT + /usr/share/gir-1.0/AccountsService-1.0.gir ) vala_init(indicator-sound-service @@ -70,7 +70,7 @@ vala_add(indicator-sound-service media-player-user.vala DEPENDS media-player - accounts-service-sound-settings + accounts-service-sound-settings greeter-broadcast ) vala_add(indicator-sound-service @@ -103,6 +103,7 @@ vala_add(indicator-sound-service sound-menu.vala DEPENDS media-player + volume-control ) vala_add(indicator-sound-service accounts-service-user.vala @@ -164,8 +165,8 @@ add_definitions( ) add_library( - indicator-sound-service-lib STATIC - ${INDICATOR_SOUND_SOURCES} + indicator-sound-service-lib STATIC + ${INDICATOR_SOUND_SOURCES} ) target_link_libraries( @@ -206,3 +207,5 @@ install( RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/indicator-sound/ ) +# Disable integration tests +# add_subdirectory(gmenuharness) diff --git a/src/gmenuharness/CMakeLists.txt b/src/gmenuharness/CMakeLists.txt new file mode 100644 index 0000000..c9e613a --- /dev/null +++ b/src/gmenuharness/CMakeLists.txt @@ -0,0 +1,17 @@ +pkg_check_modules(UNITY_API libunity-api>=0.1.3 REQUIRED) +include_directories(${UNITY_API_INCLUDE_DIRS}) + +include_directories("${CMAKE_SOURCE_DIR}/include") + +add_library( + gmenuharness-shared SHARED + MatchResult.cpp + MatchUtils.cpp + MenuItemMatcher.cpp + MenuMatcher.cpp +) + +target_link_libraries( + gmenuharness-shared + ${GLIB_LDFLAGS} +) diff --git a/src/gmenuharness/MatchResult.cpp b/src/gmenuharness/MatchResult.cpp new file mode 100644 index 0000000..40629aa --- /dev/null +++ b/src/gmenuharness/MatchResult.cpp @@ -0,0 +1,187 @@ +/* + * 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 . + * + * Authored by: Pete Woods + */ + +#include + +#include +#include +#include +#include + +using namespace std; + +namespace unity +{ + +namespace gmenuharness +{ + +namespace +{ + + +static void printLocation(ostream& ss, const vector& location, bool first) +{ + for (int i : location) + { + ss << " "; + if (first) + { + ss << i; + } + else + { + ss << " "; + } + } + ss << " "; +} + +struct compare_vector +{ + bool operator()(const vector& a, + const vector& b) const + { + auto p1 = a.begin(); + auto p2 = b.begin(); + + while (p1 != a.end()) + { + if (p2 == b.end()) + { + return false; + } + if (*p2 > *p1) + { + return true; + } + if (*p1 > *p2) + { + return false; + } + + ++p1; + ++p2; + } + + if (p2 != b.end()) + { + return true; + } + + return false; + } +}; +} + +struct MatchResult::Priv +{ + bool m_success = true; + + map, vector, compare_vector> m_failures; + + chrono::time_point m_timeout = chrono::system_clock::now() + chrono::seconds(10); +}; + +MatchResult::MatchResult() : + p(new Priv) +{ +} + +MatchResult::MatchResult(MatchResult&& other) +{ + *this = move(other); +} + +MatchResult::MatchResult(const MatchResult& other) : + p(new Priv) +{ + *this = other; +} + +MatchResult& MatchResult::operator=(const MatchResult& other) +{ + p->m_success = other.p->m_success; + p->m_failures= other.p->m_failures; + return *this; +} + +MatchResult& MatchResult::operator=(MatchResult&& other) +{ + p = move(other.p); + return *this; +} + +MatchResult MatchResult::createChild() const +{ + MatchResult child; + child.p->m_timeout = p->m_timeout; + return child; +} + +void MatchResult::failure(const vector& location, const string& message) +{ + p->m_success = false; + auto it = p->m_failures.find(location); + if (it == p->m_failures.end()) + { + it = p->m_failures.insert(make_pair(location, vector())).first; + } + it->second.emplace_back(message); +} + +void MatchResult::merge(const MatchResult& other) +{ + p->m_success &= other.p->m_success; + for (const auto& e : other.p->m_failures) + { + p->m_failures.insert(make_pair(e.first, e.second)); + } +} + +bool MatchResult::success() const +{ + return p->m_success; +} + +bool MatchResult::hasTimedOut() const +{ + auto now = chrono::system_clock::now(); + return (now >= p->m_timeout); +} + +string MatchResult::concat_failures() const +{ + stringstream ss; + ss << "Failed expectations:" << endl; + for (const auto& failure : p->m_failures) + { + bool first = true; + for (const string& s: failure.second) + { + printLocation(ss, failure.first, first); + first = false; + ss << s << endl; + } + } + return ss.str(); +} + +} // namespace gmenuharness + +} // namespace unity diff --git a/src/gmenuharness/MatchUtils.cpp b/src/gmenuharness/MatchUtils.cpp new file mode 100644 index 0000000..7b87a25 --- /dev/null +++ b/src/gmenuharness/MatchUtils.cpp @@ -0,0 +1,77 @@ +/* + * 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 . + * + * Authored by: Pete Woods + */ + +#include + +#include + +using namespace std; +namespace util = unity::util; + +namespace unity +{ + +namespace gmenuharness +{ + +void waitForCore (GObject * obj, const string& signalName, unsigned int timeout) { + shared_ptr loop(g_main_loop_new(nullptr, false), &g_main_loop_unref); + + /* Our two exit criteria */ + util::ResourcePtr> signal( + g_signal_connect_swapped(obj, signalName.c_str(), + G_CALLBACK(g_main_loop_quit), loop.get()), + [obj](gulong s) + { + g_signal_handler_disconnect(obj, s); + }); + + util::ResourcePtr> timer(g_timeout_add(timeout, + [](gpointer user_data) -> gboolean + { + g_main_loop_quit((GMainLoop *)user_data); + return G_SOURCE_CONTINUE; + }, + loop.get()), + &g_source_remove); + + /* Wait for sync */ + g_main_loop_run(loop.get()); +} + +void menuWaitForItems(const shared_ptr& menu, unsigned int timeout) +{ + waitForCore(G_OBJECT(menu.get()), "items-changed", timeout); +} + +void g_object_deleter(gpointer object) +{ + g_clear_object(&object); +} + +void gvariant_deleter(GVariant* varptr) +{ + if (varptr != nullptr) + { + g_variant_unref(varptr); + } +} + +} // namespace gmenuharness + +} // namespace unity diff --git a/src/gmenuharness/MenuItemMatcher.cpp b/src/gmenuharness/MenuItemMatcher.cpp new file mode 100644 index 0000000..f39acef --- /dev/null +++ b/src/gmenuharness/MenuItemMatcher.cpp @@ -0,0 +1,1008 @@ +/* + * 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 . + * + * Authored by: Pete Woods + */ + +#include +#include +#include + +#include +#include +#include + +using namespace std; + +namespace unity +{ + +namespace gmenuharness +{ + +namespace +{ + +enum class LinkType +{ + any, + section, + submenu +}; + +static string bool_to_string(bool value) +{ + return value? "true" : "false"; +} + +static shared_ptr get_action_group_attribute(const shared_ptr& actionGroup, const gchar* attribute) +{ + shared_ptr value( + g_action_group_get_action_state(actionGroup.get(), attribute), + &gvariant_deleter); + return value; +} + +static shared_ptr get_attribute(const shared_ptr menuItem, const gchar* attribute) +{ + shared_ptr value( + g_menu_item_get_attribute_value(menuItem.get(), attribute, nullptr), + &gvariant_deleter); + return value; +} + +static string get_string_attribute(const shared_ptr menuItem, const gchar* attribute) +{ + string result; + char* temp = nullptr; + if (g_menu_item_get_attribute(menuItem.get(), attribute, "s", &temp)) + { + result = temp; + g_free(temp); + } + return result; +} + +static pair split_action(const string& action) +{ + auto index = action.find('.'); + + if (index == string::npos) + { + return make_pair(string(), action); + } + + return make_pair(action.substr(0, index), action.substr(index + 1, action.size())); +} + +static string type_to_string(MenuItemMatcher::Type type) +{ + switch(type) + { + case MenuItemMatcher::Type::plain: + return "plain"; + case MenuItemMatcher::Type::checkbox: + return "checkbox"; + case MenuItemMatcher::Type::radio: + return "radio"; + } + + return string(); +} +} + +struct MenuItemMatcher::Priv +{ + void all(MatchResult& matchResult, const vector& location, + const shared_ptr& menu, + map>& actions) + { + int count = g_menu_model_get_n_items(menu.get()); + + if (m_items.size() != (unsigned int) count) + { + matchResult.failure( + location, + "Expected " + to_string(m_items.size()) + + " children, but found " + to_string(count)); + return; + } + + for (size_t i = 0; i < m_items.size(); ++i) + { + const auto& matcher = m_items.at(i); + matcher.match(matchResult, location, menu, actions, i); + } + } + + void startsWith(MatchResult& matchResult, const vector& location, + const shared_ptr& menu, + map>& actions) + { + int count = g_menu_model_get_n_items(menu.get()); + if (m_items.size() > (unsigned int) count) + { + matchResult.failure( + location, + "Expected at least " + to_string(m_items.size()) + + " children, but found " + to_string(count)); + return; + } + + for (size_t i = 0; i < m_items.size(); ++i) + { + const auto& matcher = m_items.at(i); + matcher.match(matchResult, location, menu, actions, i); + } + } + + void endsWith(MatchResult& matchResult, const vector& location, + const shared_ptr& menu, + map>& actions) + { + int count = g_menu_model_get_n_items(menu.get()); + if (m_items.size() > (unsigned int) count) + { + matchResult.failure( + location, + "Expected at least " + to_string(m_items.size()) + + " children, but found " + to_string(count)); + return; + } + + // match the last N items + size_t j; + for (size_t i = count - m_items.size(), j = 0; i < count && j < m_items.size(); ++i, ++j) + { + const auto& matcher = m_items.at(j); + matcher.match(matchResult, location, menu, actions, i); + } + } + + Type m_type = Type::plain; + + Mode m_mode = Mode::all; + + LinkType m_linkType = LinkType::any; + + shared_ptr m_expectedSize; + + shared_ptr m_label; + + shared_ptr m_icon; + + map, vector> m_themed_icons; + + shared_ptr m_action; + + vector m_state_icons; + + vector>> m_attributes; + + vector m_not_exist_attributes; + + vector>> m_pass_through_attributes; + + shared_ptr m_isToggled; + + vector m_items; + + vector>> m_activations; + + vector>> m_setActionStates; + + double m_maxDifference = 0.0; +}; + +MenuItemMatcher MenuItemMatcher::checkbox() +{ + MenuItemMatcher matcher; + matcher.type(Type::checkbox); + return matcher; +} + +MenuItemMatcher MenuItemMatcher::radio() +{ + MenuItemMatcher matcher; + matcher.type(Type::radio); + return matcher; +} + +MenuItemMatcher::MenuItemMatcher() : + p(new Priv) +{ +} + +MenuItemMatcher::~MenuItemMatcher() +{ +} + +MenuItemMatcher::MenuItemMatcher(const MenuItemMatcher& other) : + p(new Priv) +{ + *this = other; +} + +MenuItemMatcher::MenuItemMatcher(MenuItemMatcher&& other) +{ + *this = move(other); +} + +MenuItemMatcher& MenuItemMatcher::operator=(const MenuItemMatcher& other) +{ + p->m_type = other.p->m_type; + p->m_mode = other.p->m_mode; + p->m_expectedSize = other.p->m_expectedSize; + p->m_label = other.p->m_label; + p->m_icon = other.p->m_icon; + p->m_themed_icons = other.p->m_themed_icons; + p->m_action = other.p->m_action; + p->m_state_icons = other.p->m_state_icons; + p->m_attributes = other.p->m_attributes; + p->m_not_exist_attributes = other.p->m_not_exist_attributes; + p->m_pass_through_attributes = other.p->m_pass_through_attributes; + p->m_isToggled = other.p->m_isToggled; + p->m_linkType = other.p->m_linkType; + p->m_items = other.p->m_items; + p->m_activations = other.p->m_activations; + p->m_setActionStates = other.p->m_setActionStates; + p->m_maxDifference = other.p->m_maxDifference; + return *this; +} + +MenuItemMatcher& MenuItemMatcher::operator=(MenuItemMatcher&& other) +{ + p = move(other.p); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::type(Type type) +{ + p->m_type = type; + return *this; +} + +MenuItemMatcher& MenuItemMatcher::label(const string& label) +{ + p->m_label = make_shared(label); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::action(const string& action) +{ + p->m_action = make_shared(action); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::state_icons(const std::vector& state_icons) +{ + p->m_state_icons = state_icons; + return *this; +} + +MenuItemMatcher& MenuItemMatcher::icon(const string& icon) +{ + p->m_icon = make_shared(icon); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::themed_icon(const std::string& iconName, const std::vector& icons) +{ + p->m_themed_icons[make_shared(iconName)] = icons; + return *this; +} + +MenuItemMatcher& MenuItemMatcher::widget(const string& widget) +{ + return string_attribute("x-canonical-type", widget); +} + +MenuItemMatcher& MenuItemMatcher::pass_through_attribute(const string& actionName, const shared_ptr& value) +{ + p->m_pass_through_attributes.emplace_back(actionName, value); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::pass_through_boolean_attribute(const string& actionName, bool value) +{ + return pass_through_attribute( + actionName, + shared_ptr(g_variant_new_boolean(value), + &gvariant_deleter)); +} + +MenuItemMatcher& MenuItemMatcher::pass_through_string_attribute(const string& actionName, const string& value) +{ + return pass_through_attribute( + actionName, + shared_ptr(g_variant_new_string(value.c_str()), + &gvariant_deleter)); +} + +MenuItemMatcher& MenuItemMatcher::pass_through_double_attribute(const std::string& actionName, double value) +{ + return pass_through_attribute( + actionName, + shared_ptr(g_variant_new_double(value), + &gvariant_deleter)); +} + +MenuItemMatcher& MenuItemMatcher::round_doubles(double maxDifference) +{ + p->m_maxDifference = maxDifference; + return *this; +} + +MenuItemMatcher& MenuItemMatcher::attribute(const string& name, const shared_ptr& value) +{ + p->m_attributes.emplace_back(name, value); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::boolean_attribute(const string& name, bool value) +{ + return attribute( + name, + shared_ptr(g_variant_new_boolean(value), + &gvariant_deleter)); +} + +MenuItemMatcher& MenuItemMatcher::string_attribute(const string& name, const string& value) +{ + return attribute( + name, + shared_ptr(g_variant_new_string(value.c_str()), + &gvariant_deleter)); +} + +MenuItemMatcher& MenuItemMatcher::int32_attribute(const std::string& name, int value) +{ + return attribute( + name, + shared_ptr(g_variant_new_int32 (value), + &gvariant_deleter)); +} + +MenuItemMatcher& MenuItemMatcher::int64_attribute(const std::string& name, int value) +{ + return attribute( + name, + shared_ptr(g_variant_new_int64 (value), + &gvariant_deleter)); +} + +MenuItemMatcher& MenuItemMatcher::double_attribute(const std::string& name, double value) +{ + return attribute( + name, + shared_ptr(g_variant_new_double (value), + &gvariant_deleter)); +} + +MenuItemMatcher& MenuItemMatcher::attribute_not_set(const std::string& name) +{ + p->m_not_exist_attributes.emplace_back (name); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::toggled(bool isToggled) +{ + p->m_isToggled = make_shared(isToggled); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::mode(Mode mode) +{ + p->m_mode = mode; + return *this; +} + +MenuItemMatcher& MenuItemMatcher::submenu() +{ + p->m_linkType = LinkType::submenu; + return *this; +} + +MenuItemMatcher& MenuItemMatcher::section() +{ + p->m_linkType = LinkType::section; + return *this; +} + +MenuItemMatcher& MenuItemMatcher::is_empty() +{ + return has_exactly(0); +} + +MenuItemMatcher& MenuItemMatcher::has_exactly(size_t children) +{ + p->m_expectedSize = make_shared(children); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::item(const MenuItemMatcher& item) +{ + p->m_items.emplace_back(item); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::item(MenuItemMatcher&& item) +{ + p->m_items.emplace_back(item); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::pass_through_activate(std::string const& action, const shared_ptr& parameter) +{ + p->m_activations.emplace_back(action, parameter); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::activate(const shared_ptr& parameter) +{ + p->m_activations.emplace_back(string(), parameter); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::set_pass_through_action_state(const std::string& action, const std::shared_ptr& state) +{ + p->m_setActionStates.emplace_back(action, state); + return *this; +} + +MenuItemMatcher& MenuItemMatcher::set_action_state(const std::shared_ptr& state) +{ + p->m_setActionStates.emplace_back("", state); + return *this; +} + +void MenuItemMatcher::match( + MatchResult& matchResult, + const vector& parentLocation, + const shared_ptr& menu, + map>& actions, + int index) const +{ + shared_ptr menuItem(g_menu_item_new_from_model(menu.get(), index), &g_object_deleter); + + vector location(parentLocation); + location.emplace_back(index); + + string action = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_ACTION); + + bool isCheckbox = false; + bool isRadio = false; + bool isToggled = false; + + pair idPair; + shared_ptr actionGroup; + shared_ptr state; + + if (!action.empty()) + { + idPair = split_action(action); + actionGroup = actions[idPair.first]; + state = shared_ptr(g_action_group_get_action_state(actionGroup.get(), + idPair.second.c_str()), + &gvariant_deleter); + auto attributeTarget = get_attribute(menuItem, G_MENU_ATTRIBUTE_TARGET); + + if (attributeTarget && state) + { + isToggled = g_variant_equal(state.get(), attributeTarget.get()); + isRadio = true; + } + else if (state + && g_variant_is_of_type(state.get(), G_VARIANT_TYPE_BOOLEAN)) + { + isToggled = g_variant_get_boolean(state.get()); + isCheckbox = true; + } + } + + Type actualType = Type::plain; + if (isCheckbox) + { + actualType = Type::checkbox; + } + else if (isRadio) + { + actualType = Type::radio; + } + + if (actualType != p->m_type) + { + matchResult.failure( + location, + "Expected " + type_to_string(p->m_type) + ", found " + + type_to_string(actualType)); + } + + // check themed icons + map, vector>::iterator iter; + for (iter = p->m_themed_icons.begin(); iter != p->m_themed_icons.end(); ++iter) + { + auto icon_val = g_menu_item_get_attribute_value(menuItem.get(), (*iter).first->c_str(), nullptr); + if (!icon_val) + { + matchResult.failure( + location, + "Expected themed icon " + (*(*iter).first) + " was not found"); + } + + auto gicon = g_icon_deserialize(icon_val); + if (!gicon || !G_IS_THEMED_ICON(gicon)) + { + matchResult.failure( + location, + "Expected attribute " + (*(*iter).first) + " is not a themed icon"); + } + auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon)); + int nb_icons = 0; + while(iconNames[nb_icons]) + { + ++nb_icons; + } + + if (nb_icons != (*iter).second.size()) + { + matchResult.failure( + location, + "Expected " + to_string((*iter).second.size()) + + " icons for themed icon [" + (*(*iter).first) + + "], but " + to_string(nb_icons) + " were found."); + } + else + { + // now compare all the icons + for (int i = 0; i < nb_icons; ++i) + { + if (string(iconNames[i]) != (*iter).second[i]) + { + matchResult.failure( + location, + "Icon at position " + to_string(i) + + " for themed icon [" + (*(*iter).first) + + "], mismatchs. Expected: " + iconNames[i] + " but found " + (*iter).second[i]); + } + } + } + g_object_unref(gicon); + } + + string label = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_LABEL); + if (p->m_label && (*p->m_label) != label) + { + matchResult.failure( + location, + "Expected label '" + *p->m_label + "', but found '" + label + + "'"); + } + + string icon = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_ICON); + if (p->m_icon && (*p->m_icon) != icon) + { + matchResult.failure( + location, + "Expected icon '" + *p->m_icon + "', but found '" + icon + "'"); + } + + if (p->m_action && (*p->m_action) != action) + { + matchResult.failure( + location, + "Expected action '" + *p->m_action + "', but found '" + action + + "'"); + } + + if (!p->m_state_icons.empty() && !state) + { + matchResult.failure( + location, + "Expected state icons but no state was found"); + } + else if (!p->m_state_icons.empty() && state && + !g_variant_is_of_type(state.get(), G_VARIANT_TYPE_VARDICT)) + { + matchResult.failure( + location, + "Expected state icons vardict, found " + + type_to_string(actualType)); + } + else if (!p->m_state_icons.empty() && state && + g_variant_is_of_type(state.get(), G_VARIANT_TYPE_VARDICT)) + { + std::vector actual_state_icons; + GVariantIter it; + gchar* key; + GVariant* value; + + g_variant_iter_init(&it, state.get()); + while (g_variant_iter_loop(&it, "{sv}", &key, &value)) + { + if (std::string(key) == "icon") { + auto gicon = g_icon_deserialize(value); + if (gicon && G_IS_THEMED_ICON(gicon)) + { + auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon)); + // Just take the first icon in the list (there is only ever one) + actual_state_icons.push_back(iconNames[0]); + g_object_unref(gicon); + } + } + else if (std::string(key) == "icons" && g_variant_is_of_type(value, G_VARIANT_TYPE("av"))) + { + // If we find "icons" in the map, clear any icons we may have found in "icon", + // then break from the loop as we have found all icons now. + actual_state_icons.clear(); + GVariantIter icon_it; + GVariant* icon_value; + + g_variant_iter_init(&icon_it, value); + while (g_variant_iter_loop(&icon_it, "v", &icon_value)) + { + auto gicon = g_icon_deserialize(icon_value); + if (gicon && G_IS_THEMED_ICON(gicon)) + { + auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon)); + // Just take the first icon in the list (there is only ever one) + actual_state_icons.push_back(iconNames[0]); + g_object_unref(gicon); + } + } + // We're breaking out of g_variant_iter_loop here so clean up + g_variant_unref(value); + g_free(key); + break; + } + } + + if (p->m_state_icons != actual_state_icons) + { + std::string expected_icons; + for (unsigned int i = 0; i < p->m_state_icons.size(); ++i) + { + expected_icons += i == 0 ? p->m_state_icons[i] : ", " + p->m_state_icons[i]; + } + std::string actual_icons; + for (unsigned int i = 0; i < actual_state_icons.size(); ++i) + { + actual_icons += i == 0 ? actual_state_icons[i] : ", " + actual_state_icons[i]; + } + matchResult.failure( + location, + "Expected state_icons == {" + expected_icons + + "} but found {" + actual_icons + "}"); + } + } + + for (const auto& e: p->m_pass_through_attributes) + { + string actionName = get_string_attribute(menuItem, e.first.c_str()); + if (actionName.empty()) + { + matchResult.failure( + location, + "Could not find action name '" + e.first + "'"); + } + else + { + auto passThroughIdPair = split_action(actionName); + auto actionGroup = actions[passThroughIdPair.first]; + if (actionGroup) + { + auto value = get_action_group_attribute( + actionGroup, passThroughIdPair.second.c_str()); + if (!value) + { + matchResult.failure( + location, + "Expected pass-through attribute '" + e.first + + "' was not present"); + } + else if (!g_variant_is_of_type(e.second.get(), g_variant_get_type(value.get()))) + { + std::string expectedType = g_variant_get_type_string(e.second.get()); + std::string actualType = g_variant_get_type_string(value.get()); + matchResult.failure( + location, + "Expected pass-through attribute type '" + expectedType + + "' but found '" + actualType + "'"); + } + else if (g_variant_compare(e.second.get(), value.get())) + { + bool reportMismatch = true; + if (g_strcmp0(g_variant_get_type_string(value.get()),"d") == 0 && p->m_maxDifference) + { + auto actualDouble = g_variant_get_double(value.get()); + auto expectedDouble = g_variant_get_double(e.second.get()); + auto difference = actualDouble-expectedDouble; + if (difference < 0) difference = difference * -1.0; + if (difference <= p->m_maxDifference) + { + reportMismatch = false; + } + } + if (reportMismatch) + { + gchar* expectedString = g_variant_print(e.second.get(), true); + gchar* actualString = g_variant_print(value.get(), true); + matchResult.failure( + location, + "Expected pass-through attribute '" + e.first + + "' == " + expectedString + " but found " + + actualString); + + g_free(expectedString); + g_free(actualString); + } + } + } + else + { + matchResult.failure(location, "Could not find action group for ID '" + passThroughIdPair.first + "'"); + } + } + } + + for (const auto& e: p->m_attributes) + { + auto value = get_attribute(menuItem, e.first.c_str()); + if (!value) + { + matchResult.failure(location, + "Expected attribute '" + e.first + + "' could not be found"); + } + else if (!g_variant_is_of_type(e.second.get(), g_variant_get_type(value.get()))) + { + std::string expectedType = g_variant_get_type_string(e.second.get()); + std::string actualType = g_variant_get_type_string(value.get()); + matchResult.failure( + location, + "Expected attribute type '" + expectedType + + "' but found '" + actualType + "'"); + } + else if (g_variant_compare(e.second.get(), value.get())) + { + gchar* expectedString = g_variant_print(e.second.get(), true); + gchar* actualString = g_variant_print(value.get(), true); + matchResult.failure( + location, + "Expected attribute '" + e.first + "' == " + expectedString + + ", but found " + actualString); + g_free(expectedString); + g_free(actualString); + } + } + + for (const auto& e: p->m_not_exist_attributes) + { + auto value = get_attribute(menuItem, e.c_str()); + if (value) + { + matchResult.failure(location, + "Not expected attribute '" + e + + "' was found"); + } + } + + if (p->m_isToggled && (*p->m_isToggled) != isToggled) + { + matchResult.failure( + location, + "Expected toggled = " + bool_to_string(*p->m_isToggled) + + ", but found " + bool_to_string(isToggled)); + } + + if (!matchResult.success()) + { + return; + } + + if (!p->m_items.empty() || p->m_expectedSize) + { + shared_ptr link; + + switch (p->m_linkType) + { + case LinkType::any: + { + link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SUBMENU), &g_object_deleter); + if (!link) + { + link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SECTION), &g_object_deleter); + } + break; + } + case LinkType::submenu: + { + link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SUBMENU), &g_object_deleter); + break; + } + case LinkType::section: + { + link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SECTION), &g_object_deleter); + break; + } + } + + + if (!link) + { + if (p->m_expectedSize) + { + matchResult.failure( + location, + "Expected " + to_string(*p->m_expectedSize) + + " children, but found none"); + } + else + { + matchResult.failure( + location, + "Expected " + to_string(p->m_items.size()) + + " children, but found none"); + } + return; + } + else + { + while (true) + { + MatchResult childMatchResult(matchResult.createChild()); + + if (p->m_expectedSize + && *p->m_expectedSize + != (unsigned int) g_menu_model_get_n_items( + link.get())) + { + childMatchResult.failure( + location, + "Expected " + to_string(*p->m_expectedSize) + + " child items, but found " + + to_string( + g_menu_model_get_n_items( + link.get()))); + } + else if (!p->m_items.empty()) + { + switch (p->m_mode) + { + case Mode::all: + p->all(childMatchResult, location, link, actions); + break; + case Mode::starts_with: + p->startsWith(childMatchResult, location, link, actions); + break; + case Mode::ends_with: + p->endsWith(childMatchResult, location, link, actions); + break; + } + } + + if (childMatchResult.success()) + { + matchResult.merge(childMatchResult); + break; + } + else + { + if (matchResult.hasTimedOut()) + { + matchResult.merge(childMatchResult); + break; + } + menuWaitForItems(link); + } + } + } + } + + + for (const auto& a: p->m_setActionStates) + { + auto stateAction = action; + auto stateIdPair = idPair; + auto stateActionGroup = actionGroup; + if (!a.first.empty()) + { + stateAction = get_string_attribute(menuItem, a.first.c_str());; + stateIdPair = split_action(stateAction); + stateActionGroup = actions[stateIdPair.first]; + } + + if (stateAction.empty()) + { + matchResult.failure( + location, + "Tried to set action state, but no action was found"); + } + else if(!stateActionGroup) + { + matchResult.failure( + location, + "Tried to set action state for action group '" + stateIdPair.first + + "', but action group wasn't found"); + } + else if (!g_action_group_has_action(stateActionGroup.get(), stateIdPair.second.c_str())) + { + matchResult.failure( + location, + "Tried to set action state for action '" + stateAction + + "', but action was not found"); + } + else + { + g_action_group_change_action_state(stateActionGroup.get(), stateIdPair.second.c_str(), + g_variant_ref(a.second.get())); + } + + // FIXME this is a dodgy way to ensure the action state change gets dispatched + menuWaitForItems(menu, 100); + } + + for (const auto& a: p->m_activations) + { + string tmpAction = action; + auto tmpIdPair = idPair; + auto tmpActionGroup = actionGroup; + if (!a.first.empty()) + { + tmpAction = get_string_attribute(menuItem, a.first.c_str()); + tmpIdPair = split_action(tmpAction); + tmpActionGroup = actions[tmpIdPair.first]; + } + + if (tmpAction.empty()) + { + matchResult.failure( + location, + "Tried to activate action, but no action was found"); + } + else if(!tmpActionGroup) + { + matchResult.failure( + location, + "Tried to activate action group '" + tmpIdPair.first + + "', but action group wasn't found"); + } + else if (!g_action_group_has_action(tmpActionGroup.get(), tmpIdPair.second.c_str())) + { + matchResult.failure( + location, + "Tried to activate action '" + tmpAction + "', but action was not found"); + } + else + { + if (a.second) + { + g_action_group_activate_action(tmpActionGroup.get(), tmpIdPair.second.c_str(), + g_variant_ref(a.second.get())); + } + else + { + g_action_group_activate_action(tmpActionGroup.get(), tmpIdPair.second.c_str(), nullptr); + } + + // FIXME this is a dodgy way to ensure the activation gets dispatched + menuWaitForItems(menu, 100); + } + } +} + +} // namepsace gmenuharness + +} // namespace unity diff --git a/src/gmenuharness/MenuMatcher.cpp b/src/gmenuharness/MenuMatcher.cpp new file mode 100644 index 0000000..5bb4fbd --- /dev/null +++ b/src/gmenuharness/MenuMatcher.cpp @@ -0,0 +1,208 @@ +/* + * 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 . + * + * Authored by: Pete Woods + */ + +#include +#include + +#include + +#include + +using namespace std; + +namespace unity +{ + +namespace gmenuharness +{ + +namespace +{ + +static void gdbus_connection_deleter(GDBusConnection* connection) +{ +// if (!g_dbus_connection_is_closed(connection)) +// { +// g_dbus_connection_close_sync(connection, nullptr, nullptr); +// } + g_clear_object(&connection); +} +} + +struct MenuMatcher::Parameters::Priv +{ + string m_busName; + + vector> m_actions; + + string m_menuObjectPath; +}; + +MenuMatcher::Parameters::Parameters(const string& busName, + const vector>& actions, + const string& menuObjectPath) : + p(new Priv) +{ + p->m_busName = busName; + p->m_actions = actions; + p->m_menuObjectPath = menuObjectPath; +} + +MenuMatcher::Parameters::~Parameters() +{ +} + +MenuMatcher::Parameters::Parameters(const Parameters& other) : + p(new Priv) +{ + *this = other; +} + +MenuMatcher::Parameters::Parameters(Parameters&& other) +{ + *this = move(other); +} + +MenuMatcher::Parameters& MenuMatcher::Parameters::operator=(const Parameters& other) +{ + p->m_busName = other.p->m_busName; + p->m_actions = other.p->m_actions; + p->m_menuObjectPath = other.p->m_menuObjectPath; + return *this; +} + +MenuMatcher::Parameters& MenuMatcher::Parameters::operator=(Parameters&& other) +{ + p = move(other.p); + return *this; +} + +struct MenuMatcher::Priv +{ + Priv(const Parameters& parameters) : + m_parameters(parameters) + { + } + + Parameters m_parameters; + + vector m_items; + + shared_ptr m_system; + + shared_ptr m_session; + + shared_ptr m_menu; + + map> m_actions; +}; + +MenuMatcher::MenuMatcher(const Parameters& parameters) : + p(new Priv(parameters)) +{ + p->m_system.reset(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), + &gdbus_connection_deleter); + g_dbus_connection_set_exit_on_close(p->m_system.get(), false); + + p->m_session.reset(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr), + &gdbus_connection_deleter); + g_dbus_connection_set_exit_on_close(p->m_session.get(), false); + + p->m_menu.reset( + G_MENU_MODEL( + g_dbus_menu_model_get( + p->m_session.get(), + p->m_parameters.p->m_busName.c_str(), + p->m_parameters.p->m_menuObjectPath.c_str())), + &g_object_deleter); + + for (const auto& action : p->m_parameters.p->m_actions) + { + shared_ptr actionGroup( + G_ACTION_GROUP( + g_dbus_action_group_get( + p->m_session.get(), + p->m_parameters.p->m_busName.c_str(), + action.second.c_str())), + &g_object_deleter); + p->m_actions[action.first] = actionGroup; + } +} + +MenuMatcher::~MenuMatcher() +{ +} + +MenuMatcher& MenuMatcher::item(const MenuItemMatcher& item) +{ + p->m_items.emplace_back(item); + return *this; +} + +void MenuMatcher::match(MatchResult& matchResult) const +{ + vector location; + + while (true) + { + MatchResult childMatchResult(matchResult.createChild()); + + int menuSize = g_menu_model_get_n_items(p->m_menu.get()); + if (p->m_items.size() > (unsigned int) menuSize) + { + childMatchResult.failure( + location, + "Row count mismatch, expected " + to_string(p->m_items.size()) + + " but found " + to_string(menuSize)); + } + else + { + for (size_t i = 0; i < p->m_items.size(); ++i) + { + const auto& matcher = p->m_items.at(i); + matcher.match(childMatchResult, location, p->m_menu, p->m_actions, i); + } + } + + if (childMatchResult.success()) + { + matchResult.merge(childMatchResult); + break; + } + else + { + if (matchResult.hasTimedOut()) + { + matchResult.merge(childMatchResult); + break; + } + menuWaitForItems(p->m_menu); + } + } +} + +MatchResult MenuMatcher::match() const +{ + MatchResult matchResult; + match(matchResult); + return matchResult; +} + +} // namespace gmenuharness + +} // namespace unity diff --git a/src/service.vala b/src/service.vala index a08edf3..12e2ac2 100644 --- a/src/service.vala +++ b/src/service.vala @@ -43,7 +43,7 @@ public class IndicatorSound.Service: Object { warn_notification.closed.connect((n) => { n.clear_actions(); }); BusWatcher.watch_namespace (GLib.BusType.SESSION, "org.freedesktop.Notifications", - () => { debug("Notifications name appeared"); notify_server_caps_checked = false; }, + () => { debug("Notifications name appeared"); }, () => { debug("Notifications name vanshed"); notify_server_caps_checked = false; }); this.settings = new Settings ("com.canonical.indicator.sound"); @@ -52,6 +52,8 @@ public class IndicatorSound.Service: Object { this.notify["visible"].connect ( () => this.update_root_icon () ); this.volume_control = volume; + this.volume_control.active_output_changed.connect (this.update_root_icon); + this.volume_control.active_output_changed.connect (this.update_notification); this.accounts_service = accounts; /* If we're on the greeter, don't export */ @@ -90,6 +92,10 @@ public class IndicatorSound.Service: Object { this.volume_control.bind_property ("high-volume", menu, "show-high-volume-warning", BindingFlags.SYNC_CREATE); }); + this.menus.@foreach ( (profile, menu) => { + this.volume_control.active_output_changed.connect (menu.update_volume_slider); + }); + this.sync_preferred_players (); this.settings.changed["interested-media-players"].connect ( () => { this.sync_preferred_players (); @@ -245,17 +251,7 @@ public class IndicatorSound.Service: Object { void update_root_icon () { double volume = this.volume_control.volume.volume; - string icon; - if (this.volume_control.mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; + string icon = get_volume_root_icon (volume, this.volume_control.mute, volume_control.active_output); string accessible_name; if (this.volume_control.mute) { @@ -281,16 +277,334 @@ public class IndicatorSound.Service: Object { private bool notify_server_supports_actions = false; private bool notify_server_supports_sync = false; private bool block_info_notifications = false; + private bool waiting_user_approve_warn = false; - private void update_notification () { + private string get_volume_icon (double volume, VolumeControl.ActiveOutput active_output) + { + string icon = ""; + switch (active_output) + { + case VolumeControl.ActiveOutput.SPEAKERS: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + } + return icon; + } + + private string get_volume_root_icon_by_volume (double volume, VolumeControl.ActiveOutput active_output) + { + string icon = ""; + switch (active_output) + { + case VolumeControl.ActiveOutput.SPEAKERS: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + } + return icon; + } + + private string get_volume_notification_icon (double volume, bool loud, VolumeControl.ActiveOutput active_output) { + string icon = ""; + if (loud) { + switch (active_output) + { + case VolumeControl.ActiveOutput.SPEAKERS: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HEADPHONES: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + icon = "audio-volume-high"; + break; + } + } else { + icon = get_volume_icon (volume, active_output); + } + return icon; + } + + private string get_volume_root_icon (double volume, bool mute, VolumeControl.ActiveOutput active_output) { + string icon = ""; + switch (active_output) + { + case VolumeControl.ActiveOutput.SPEAKERS: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.HEADPHONES: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + } + return icon; + } - if (!notify_server_caps_checked) { - List caps = Notify.get_server_caps (); - notify_server_supports_actions = caps.find_custom ("actions", strcmp) != null; - notify_server_supports_sync = caps.find_custom ("x-canonical-private-synchronous", strcmp) != null; - notify_server_caps_checked = true; + private string get_notification_label () { + string volume_label = ""; + switch (volume_control.active_output) + { + case VolumeControl.ActiveOutput.SPEAKERS: + volume_label = _("Speakers"); + break; + case VolumeControl.ActiveOutput.HEADPHONES: + volume_label = _("Headphones"); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + volume_label = _("Bluetooth headphones"); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + volume_label = _("Bluetooth speaker"); + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + volume_label = _("Usb speaker"); + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + volume_label = _("Usb headphones"); + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + volume_label = _("HDMI speaker"); + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + volume_label = _("HDMI headphones"); + break; } + return volume_label; + } + + private void update_notification () { + + List caps = Notify.get_server_caps (); + notify_server_supports_actions = caps.find_custom ("actions", strcmp) != null; + notify_server_supports_sync = caps.find_custom ("x-canonical-private-synchronous", strcmp) != null; + notify_server_caps_checked = true; + var loud = volume_control.high_volume; var warn = loud && this.notify_server_supports_actions @@ -312,47 +626,36 @@ public class IndicatorSound.Service: Object { _pre_warn_volume = null; volume_control.volume = tmp; } + waiting_user_approve_warn = false; }); warn_notification.add_action ("cancel", _("Cancel"), (n, a) => { _pre_warn_volume = null; + waiting_user_approve_warn = false; }); + waiting_user_approve_warn = true; show_notification(warn_notification); } else { - close_notification(warn_notification); - - if (notify_server_supports_sync && !block_info_notifications) { - - /* Determine Label */ - unowned string volume_label = loud - ? _("High volume can damage your hearing.") - : ""; - - /* Choose an icon */ - unowned string icon; - if (loud) { - icon = "audio-volume-high"; - } else { - var volume = volume_control.volume.volume; - - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; + if (!waiting_user_approve_warn) { + close_notification(warn_notification); + + if (notify_server_supports_sync && !block_info_notifications) { + + /* Determine Label */ + string volume_label = get_notification_label (); + + /* Choose an icon */ + string icon = get_volume_notification_icon (volume_control.volume.volume, loud, volume_control.active_output); + + /* Reset the notification */ + var n = this.info_notification; + n.update (_("Volume"), volume_label, icon); + n.clear_hints(); + n.set_hint ("x-canonical-non-shaped-icon", "true"); + n.set_hint ("x-canonical-private-synchronous", "true"); + n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false"); + n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0)); + show_notification(n); } - - /* Reset the notification */ - var n = this.info_notification; - n.update (_("Volume"), volume_label, icon); - n.clear_hints(); - n.set_hint ("x-canonical-non-shaped-icon", "true"); - n.set_hint ("x-canonical-private-synchronous", "true"); - n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false"); - n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0)); - show_notification(n); } } } diff --git a/src/sound-menu.vala b/src/sound-menu.vala index 7a6044b..3d682e4 100644 --- a/src/sound-menu.vala +++ b/src/sound-menu.vala @@ -71,6 +71,7 @@ public class SoundMenu: Object this.notify_handlers = new HashTable (direct_hash, direct_equal); this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0; + } ~SoundMenu () { @@ -193,6 +194,43 @@ public class SoundMenu: Object this.notify_handlers.remove (player); } + public void update_volume_slider (VolumeControl.ActiveOutput active_output) { + int index = find_action (this.volume_section, "indicator.volume"); + if (index != -1) { + string label = "Volume"; + switch (active_output) { + case VolumeControl.ActiveOutput.SPEAKERS: + label = _("Volume"); + break; + case VolumeControl.ActiveOutput.HEADPHONES: + label = _("Volume (Headphones)"); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + label = _("Volume (Bluetooth)"); + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + label = _("Volume (Usb)"); + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + label = _("Volume (HDMI)"); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + label = _("Volume (Bluetooth headphones)"); + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + label = _("Volume (Usb headphones)"); + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + label = _("Volume (HDMI headphones)"); + break; + } + this.volume_section.remove (index); + this.volume_section.insert_item (index, this.create_slider_menu_item (_(label), "indicator.volume(0)", 0.0, 1.0, 0.01, + "audio-volume-low-zero-panel", + "audio-volume-high-panel")); + } + } + public Menu root; public Menu menu; Menu volume_section; diff --git a/src/volume-control-pulse.vala b/src/volume-control-pulse.vala index 87af258..8122f26 100644 --- a/src/volume-control-pulse.vala +++ b/src/volume-control-pulse.vala @@ -87,6 +87,7 @@ public class VolumeControlPulse : VolumeControl private bool _send_next_local_volume = false; private double _account_service_volume = 0.0; private bool _active_port_headphone = false; + private VolumeControl.ActiveOutput _active_output = VolumeControl.ActiveOutput.SPEAKERS; /** true when connected to the pulse server */ public override bool ready { get; private set; } @@ -135,6 +136,49 @@ public class VolumeControlPulse : VolumeControl stop_high_volume_approved_timer(); } + private VolumeControl.ActiveOutput calculate_active_output (SinkInfo? sink) { + + VolumeControl.ActiveOutput ret_output = VolumeControl.ActiveOutput.SPEAKERS; + /* Check if the current active port is headset/headphone */ + /* There is not easy way to check if the port is a headset/headphone besides + * checking for the port name. On touch (with the pulseaudio droid element) + * the headset/headphone port is called 'output-headset' and 'output-headphone'. + * On the desktop this is usually called 'analog-output-headphones' */ + // look if it's a headset/headphones + if (sink.name == "indicator_sound_test_headphones" || + (sink.active_port != null && + (sink.active_port.name.contains("headset") || + sink.active_port.name.contains("headphone")))) { + _active_port_headphone = true; + // check if it's a bluetooth device + var device_bus = sink.proplist.gets ("device.bus"); + if (device_bus != null && device_bus == "bluetooth") { + ret_output = VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES; + } else if (device_bus != null && device_bus == "usb") { + ret_output = VolumeControl.ActiveOutput.USB_HEADPHONES; + } else if (device_bus != null && device_bus == "hdmi") { + ret_output = VolumeControl.ActiveOutput.HDMI_HEADPHONES; + } else { + ret_output = VolumeControl.ActiveOutput.HEADPHONES; + } + } else { + // speaker + _active_port_headphone = false; + var device_bus = sink.proplist.gets ("device.bus"); + if (device_bus != null && device_bus == "bluetooth") { + ret_output = VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER; + } else if (device_bus != null && device_bus == "usb") { + ret_output = VolumeControl.ActiveOutput.USB_SPEAKER; + } else if (device_bus != null && device_bus == "hdmi") { + ret_output = VolumeControl.ActiveOutput.HDMI_SPEAKER; + } else { + ret_output = VolumeControl.ActiveOutput.SPEAKERS; + } + } + + return ret_output; + } + /* PulseAudio logic*/ private void context_events_cb (Context c, Context.SubscriptionEventType t, uint32 index) { @@ -201,18 +245,20 @@ public class VolumeControlPulse : VolumeControl this.notify_property ("is-playing"); } - /* Check if the current active port is headset/headphone */ - /* There is not easy way to check if the port is a headset/headphone besides - * checking for the port name. On touch (with the pulseaudio droid element) - * the headset/headphone port is called 'output-headset' and 'output-headphone'. - * On the desktop this is usually called 'analog-output-headphones' */ - if (i.active_port != null && - (i.active_port.name == "output-wired_headset" || - i.active_port.name == "output-wired_headphone" || - i.active_port.name == "analog-output-headphones")) { - _active_port_headphone = true; - } else { - _active_port_headphone = false; + // store the current status of the active output + VolumeControl.ActiveOutput active_output_before = active_output; + + // calculate the output + _active_output = calculate_active_output (i); + + // check if the output has changed, if so... emit a signal + VolumeControl.ActiveOutput active_output_now = active_output; + if (active_output_now != active_output_before) { + this.active_output_changed (active_output_now); + if (active_output_now == VolumeControl.ActiveOutput.SPEAKERS) { + _high_volume_approved = false; + } + update_high_volume(); } if (_pulse_use_stream_restore == false && @@ -478,7 +524,8 @@ public class VolumeControlPulse : VolumeControl this.context = new PulseAudio.Context (loop.get_api(), null, props); this.context.set_state_callback (context_state_callback); - if (context.connect(null, Context.Flags.NOFAIL, null) < 0) + var server_string = Environment.get_variable("PULSE_SERVER"); + if (context.connect(server_string, Context.Flags.NOFAIL, null) < 0) warning( "pa_context_connect() failed: %s\n", PulseAudio.strerror(context.errno())); } @@ -535,6 +582,14 @@ public class VolumeControlPulse : VolumeControl } } + public override VolumeControl.ActiveOutput active_output + { + get + { + return _active_output; + } + } + /* Volume operations */ private static PulseAudio.Volume double_to_volume (double vol) { @@ -710,7 +765,7 @@ public class VolumeControlPulse : VolumeControl private bool calculate_high_volume_from_volume(double volume) { return _active_port_headphone && _warning_volume_enabled - && volume >= _warning_volume_norms + && volume > _warning_volume_norms && (stream == "multimedia"); } diff --git a/src/volume-control.vala b/src/volume-control.vala index 6efac35..8e615ea 100644 --- a/src/volume-control.vala +++ b/src/volume-control.vala @@ -28,6 +28,17 @@ public abstract class VolumeControl : Object VOLUME_STREAM_CHANGE } + public enum ActiveOutput { + SPEAKERS, + HEADPHONES, + BLUETOOTH_HEADPHONES, + BLUETOOTH_SPEAKER, + USB_SPEAKER, + USB_HEADPHONES, + HDMI_SPEAKER, + HDMI_HEADPHONES + } + public class Volume : Object { public double volume; public VolumeReasons reason; @@ -39,6 +50,7 @@ public abstract class VolumeControl : Object public virtual bool high_volume { get { return false; } protected set { } } public virtual bool mute { get { return false; } } public virtual bool is_playing { get { return false; } } + public virtual VolumeControl.ActiveOutput active_output { get { return VolumeControl.ActiveOutput.SPEAKERS; } } private Volume _volume; public virtual Volume volume { get { return _volume; } set { } } public virtual double mic_volume { get { return 0.0; } set { } } @@ -56,4 +68,6 @@ public abstract class VolumeControl : Object v.reason = reason; this.volume = v; } + + public signal void active_output_changed (VolumeControl.ActiveOutput active_output); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2bbd8c5..f77bcb1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,10 +5,10 @@ include_directories(${GTEST_INCLUDE_DIR}) -add_library (gtest STATIC +add_library (gtest-static STATIC ${GTEST_SOURCE_DIR}/gtest-all.cc ${GTEST_SOURCE_DIR}/gtest_main.cc) -target_link_libraries(gtest ${GTEST_LIBS}) +target_link_libraries(gtest-static ${GTEST_LIBS}) ########################### # GSettings Schema @@ -22,7 +22,8 @@ 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") +set (XDG_DATA_DIRS "${CMAKE_CURRENT_BINARY_DIR}/gsettings-schemas") +set (SCHEMA_DIR "${XDG_DATA_DIRS}/glib-2.0/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 @@ -31,6 +32,7 @@ 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 cp -f /usr/share/glib-2.0/schemas/com.ubuntu.sound.gschema.xml ${SCHEMA_DIR} COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR}) ########################### @@ -41,74 +43,74 @@ set(VALA_MOCKS_HEADER_PATH "${CMAKE_CURRENT_BINARY_DIR}/vala-mocks.h") set(VALA_MOCKS_SYMBOLS_PATH "${CMAKE_CURRENT_BINARY_DIR}/vala-mocks.def") vala_init(vala-mocks - DEPENDS - indicator-sound-service-lib - PACKAGES - config - gio-2.0 - gio-unix-2.0 - libxml-2.0 - libpulse - libpulse-mainloop-glib - libnotify - accounts-service - indicator-sound-service - OPTIONS - --ccode - --thread - --vapidir=${CMAKE_BINARY_DIR}/src/ - --vapidir=${CMAKE_SOURCE_DIR}/vapi/ - --vapidir=. + DEPENDS + indicator-sound-service-lib + PACKAGES + config + gio-2.0 + gio-unix-2.0 + libxml-2.0 + libpulse + libpulse-mainloop-glib + libnotify + accounts-service + indicator-sound-service + OPTIONS + --ccode + --thread + --vapidir=${CMAKE_BINARY_DIR}/src/ + --vapidir=${CMAKE_SOURCE_DIR}/vapi/ + --vapidir=. ) vala_add(vala-mocks - media-player-mock.vala + media-player-mock.vala ) vala_add(vala-mocks - media-player-list-mock.vala + media-player-list-mock.vala ) vala_add(vala-mocks - volume-control-mock.vala + volume-control-mock.vala ) vala_finish(vala-mocks - SOURCES - vala_mocks_VALA_SOURCES - OUTPUTS - vala_mocks_VALA_C - GENERATE_HEADER - ${VALA_MOCKS_HEADER_PATH} - GENERATE_SYMBOLS - ${VALA_MOCKS_SYMBOLS_PATH} + SOURCES + vala_mocks_VALA_SOURCES + OUTPUTS + vala_mocks_VALA_C + GENERATE_HEADER + ${VALA_MOCKS_HEADER_PATH} + GENERATE_SYMBOLS + ${VALA_MOCKS_SYMBOLS_PATH} ) set_source_files_properties( - ${vala_mocks_VALA_SOURCES} - PROPERTIES - HEADER_FILE_ONLY TRUE + ${vala_mocks_VALA_SOURCES} + PROPERTIES + HEADER_FILE_ONLY TRUE ) set( - VALA_MOCKS_SOURCES - ${vala_mocks_VALA_SOURCES} - ${vala_mocks_VALA_C} - ${VALA_MOCKS_SYMBOLS_PATH} + VALA_MOCKS_SOURCES + ${vala_mocks_VALA_SOURCES} + ${vala_mocks_VALA_C} + ${VALA_MOCKS_SYMBOLS_PATH} ) add_definitions( - -Wno-unused-but-set-variable + -Wno-unused-but-set-variable ) add_library( - vala-mocks-lib STATIC - ${VALA_MOCKS_SOURCES} + vala-mocks-lib STATIC + ${VALA_MOCKS_SOURCES} ) target_link_libraries( - vala-mocks-lib - indicator-sound-service-lib + vala-mocks-lib + indicator-sound-service-lib ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) @@ -118,9 +120,9 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) ########################### add_library( - pulse-mock - SHARED - pa-mock.cpp + pulse-mock + SHARED + pa-mock.cpp ) target_link_libraries (pulse-mock ${PULSEAUDIO_LIBRARIES}) @@ -131,7 +133,7 @@ target_link_libraries (pulse-mock ${PULSEAUDIO_LIBRARIES}) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (name-watch-test name-watch-test.cc ${CMAKE_SOURCE_DIR}/src/bus-watch-namespace.c) -target_link_libraries (name-watch-test gtest ${SOUNDSERVICE_LIBRARIES}) +target_link_libraries (name-watch-test gtest-static ${SOUNDSERVICE_LIBRARIES}) add_test(name-watch-test name-watch-test) ########################### @@ -141,21 +143,21 @@ add_test(name-watch-test name-watch-test) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (accounts-service-user-test accounts-service-user.cc) target_link_libraries ( - accounts-service-user-test - indicator-sound-service-lib - vala-mocks-lib - gtest - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + accounts-service-user-test + indicator-sound-service-lib + vala-mocks-lib + gtest-static + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) # Split tests to work around libaccountservice sucking add_test(accounts-service-user-test-basic - accounts-service-user-test --gtest_filter=AccountsServiceUserTest.BasicObject + accounts-service-user-test --gtest_filter=AccountsServiceUserTest.BasicObject ) add_test(accounts-service-user-test-player - accounts-service-user-test --gtest_filter=AccountsServiceUserTest.SetMediaPlayer + accounts-service-user-test --gtest_filter=AccountsServiceUserTest.SetMediaPlayer ) ########################### @@ -165,11 +167,11 @@ add_test(accounts-service-user-test-player include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (volume-control-test volume-control-test.cc gschemas.compiled) target_link_libraries ( - volume-control-test - indicator-sound-service-lib - pulse-mock - gtest - ${TEST_LIBRARIES} + volume-control-test + indicator-sound-service-lib + pulse-mock + gtest-static + ${TEST_LIBRARIES} ) add_test(volume-control-test volume-control-test) @@ -181,12 +183,12 @@ add_test(volume-control-test volume-control-test) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (sound-menu-test sound-menu.cc) target_link_libraries ( - sound-menu-test - indicator-sound-service-lib - vala-mocks-lib - gtest - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + sound-menu-test + indicator-sound-service-lib + vala-mocks-lib + gtest-static + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) add_test(sound-menu-test sound-menu-test) @@ -198,13 +200,13 @@ add_test(sound-menu-test sound-menu-test) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (notifications-test notifications-test.cc) target_link_libraries ( - notifications-test - indicator-sound-service-lib - vala-mocks-lib - pulse-mock - gtest - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + notifications-test + indicator-sound-service-lib + vala-mocks-lib + pulse-mock + gtest-static + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) add_test(notifications-test notifications-test) @@ -216,23 +218,23 @@ add_test(notifications-test notifications-test) include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (media-player-user-test media-player-user.cc) target_link_libraries ( - media-player-user-test - indicator-sound-service-lib - vala-mocks-lib - gtest - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + media-player-user-test + indicator-sound-service-lib + vala-mocks-lib + gtest-static + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) # Split tests to work around libaccountservice sucking add_test(media-player-user-test-basic - media-player-user-test --gtest_filter=MediaPlayerUserTest.BasicObject + media-player-user-test --gtest_filter=MediaPlayerUserTest.BasicObject ) add_test(media-player-user-test-dataset - media-player-user-test --gtest_filter=MediaPlayerUserTest.DataSet + media-player-user-test --gtest_filter=MediaPlayerUserTest.DataSet ) add_test(media-player-user-test-timeout - media-player-user-test --gtest_filter=MediaPlayerUserTest.TimeoutTest + media-player-user-test --gtest_filter=MediaPlayerUserTest.TimeoutTest ) ########################### @@ -242,20 +244,20 @@ add_test(media-player-user-test-timeout include_directories(${CMAKE_SOURCE_DIR}/src) add_executable (greeter-list-test greeter-list.cc) target_link_libraries ( - greeter-list-test - indicator-sound-service-lib - vala-mocks-lib - gtest - ${SOUNDSERVICE_LIBRARIES} - ${TEST_LIBRARIES} + greeter-list-test + indicator-sound-service-lib + vala-mocks-lib + gtest-static + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) # Split tests to work around libaccountservice sucking add_test(greeter-list-test-basic - greeter-list-test --gtest_filter=GreeterListTest.BasicObject + greeter-list-test --gtest_filter=GreeterListTest.BasicObject ) add_test(greeter-list-test-iterator - greeter-list-test --gtest_filter=GreeterListTest.BasicIterator + greeter-list-test --gtest_filter=GreeterListTest.BasicIterator ) ########################### @@ -263,18 +265,23 @@ add_test(greeter-list-test-iterator ########################### add_definitions( - -DINDICATOR_SOUND_SERVICE_BINARY="${CMAKE_BINARY_DIR}/src/indicator-sound-service" - -DPA_MOCK_LIB="${CMAKE_CURRENT_BINARY_DIR}/libpulse-mock.so" + -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} + indicator-test + gtest-static + ${SOUNDSERVICE_LIBRARIES} + ${TEST_LIBRARIES} ) # Split tests to work around libaccountservice sucking add_test(indcator-test - indicator-test + indicator-test ) + +# Disable integration tests +# add_subdirectory(integration) +# add_subdirectory(dbus-types) +# add_subdirectory(service-mocks) diff --git a/tests/dbus-types/CMakeLists.txt b/tests/dbus-types/CMakeLists.txt new file mode 100644 index 0000000..cb7f512 --- /dev/null +++ b/tests/dbus-types/CMakeLists.txt @@ -0,0 +1,53 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(Qt5DBus REQUIRED) +include_directories(${Qt5DBus_INCLUDE_DIRS}) + +add_definitions(-DQT_NO_KEYWORDS=1) + +set(dbusinterface_streamrestore_xml "org.PulseAudio.Ext.StreamRestore1.xml") +set_source_files_properties(${dbusinterface_streamrestore_xml} PROPERTIES + CLASSNAME StreamRestoreInterface) + +set(dbusinterface_accounts_xml "org.freedesktop.Accounts.xml") +set_source_files_properties(${dbusinterface_accounts_xml} PROPERTIES + CLASSNAME AccountsInterface) + +set(dbusinterface_accountssound_xml "com.ubuntu.AccountsService.Sound.xml") +set_source_files_properties(${dbusinterface_accountssound_xml} PROPERTIES + CLASSNAME AccountsSoundInterface) + +set(dbusinterface_properties_xml "org.freedesktop.DBus.Properties.xml") +set_source_files_properties(${dbusinterface_properties_xml} PROPERTIES + CLASSNAME DBusPropertiesInterface + NO_NAMESPACE YES + INCLUDE "dbus-types.h") + +set(dbusinterface_actions_xml "org.gtk.Actions.xml") +set_source_files_properties(${dbusinterface_actions_xml} PROPERTIES + CLASSNAME MenusInterface) + +set(dbusinterface_notifications_xml "org.freedesktop.Notifications.xml") +set_source_files_properties(${dbusinterface_notifications_xml} PROPERTIES + CLASSNAME NotificationsInterface) + +qt5_add_dbus_interface(interface_files ${dbusinterface_streamrestore_xml} stream_restore_interface) +qt5_add_dbus_interface(interface_files ${dbusinterface_properties_xml} dbus_properties_interface) +qt5_add_dbus_interface(interface_files ${dbusinterface_accounts_xml} dbus_accounts_interface) +qt5_add_dbus_interface(interface_files ${dbusinterface_accountssound_xml} dbus_accountssound_interface) +qt5_add_dbus_interface(interface_files ${dbusinterface_actions_xml} dbus_menus_interface) +qt5_add_dbus_interface(interface_files ${dbusinterface_notifications_xml} dbus_notifications_interface) + +add_library( + sound-indicator-dbus-interfaces + STATIC + ${interface_files} + pulseaudio-volume.cpp +) + +qt5_use_modules( + sound-indicator-dbus-interfaces + Core + DBus +) diff --git a/tests/dbus-types/com.ubuntu.AccountsService.Sound.xml b/tests/dbus-types/com.ubuntu.AccountsService.Sound.xml new file mode 100644 index 0000000..27c915a --- /dev/null +++ b/tests/dbus-types/com.ubuntu.AccountsService.Sound.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/dbus-types/dbus-types.h b/tests/dbus-types/dbus-types.h new file mode 100644 index 0000000..b75acf0 --- /dev/null +++ b/tests/dbus-types/dbus-types.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#pragma once + +#include +#include "pulseaudio-volume.h" + +namespace DBusTypes +{ + inline void registerMetaTypes() + { + PulseaudioVolume::registerMetaType(); + PulseaudioVolumeArray::registerMetaType(); + } + + static constexpr char const* DBUS_NAME = "com.canonical.indicator.sound"; + + static constexpr char const* DBUS_PULSE = "org.PulseAudio1"; + + static constexpr char const* ACCOUNTS_SERVICE = "org.freedesktop.Accounts"; + + static constexpr char const* STREAM_RESTORE_NAME = "org.PulseAudio.Ext.StreamRestore1"; + + static constexpr char const* STREAM_RESTORE_PATH = "/org/pulseaudio/stream_restore1"; + + static constexpr char const* STREAM_RESTORE_ENTRY_NAME = "org.PulseAudio.Ext.StreamRestore1.RestoreEntry"; + + static constexpr char const* MAIN_SERVICE_PATH = "/com/canonical/indicator/sound"; + + static constexpr char const* ACTIONS_INTERFACE = "org.gtk.Actions"; + +} // namespace DBusTypes + diff --git a/tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml b/tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml new file mode 100644 index 0000000..bf9af76 --- /dev/null +++ b/tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/dbus-types/org.freedesktop.Accounts.xml b/tests/dbus-types/org.freedesktop.Accounts.xml new file mode 100644 index 0000000..e308120 --- /dev/null +++ b/tests/dbus-types/org.freedesktop.Accounts.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/dbus-types/org.freedesktop.DBus.Properties.xml b/tests/dbus-types/org.freedesktop.DBus.Properties.xml new file mode 100644 index 0000000..bdf6cad --- /dev/null +++ b/tests/dbus-types/org.freedesktop.DBus.Properties.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/dbus-types/org.freedesktop.Notifications.xml b/tests/dbus-types/org.freedesktop.Notifications.xml new file mode 100644 index 0000000..f7d923e --- /dev/null +++ b/tests/dbus-types/org.freedesktop.Notifications.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/dbus-types/org.gtk.Actions.xml b/tests/dbus-types/org.gtk.Actions.xml new file mode 100644 index 0000000..b691f1f --- /dev/null +++ b/tests/dbus-types/org.gtk.Actions.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/tests/dbus-types/pulseaudio-volume.cpp b/tests/dbus-types/pulseaudio-volume.cpp new file mode 100644 index 0000000..8ee305f --- /dev/null +++ b/tests/dbus-types/pulseaudio-volume.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#include "dbus-types.h" + +PulseaudioVolume::PulseaudioVolume() : + type_(0), + volume_(10) +{ +} + +PulseaudioVolume::PulseaudioVolume(unsigned int type, unsigned int volume) : + type_(type) + , volume_(volume) +{ +} + +PulseaudioVolume::PulseaudioVolume(const PulseaudioVolume &other) : + type_(other.type_), + volume_(other.volume_) +{ +} + +PulseaudioVolume& PulseaudioVolume::operator=(const PulseaudioVolume &other) +{ + type_ = other.type_; + volume_ = other.volume_; + + return *this; +} + +PulseaudioVolume::~PulseaudioVolume() +{ +} + +unsigned int PulseaudioVolume::getType() const +{ + return type_; +} + +unsigned int PulseaudioVolume::getVolume() const +{ + return volume_; +} + +void PulseaudioVolume::registerMetaType() +{ + qRegisterMetaType("PulseaudioVolume"); + + qDBusRegisterMetaType(); +} + +QDBusArgument &operator<<(QDBusArgument &argument, const PulseaudioVolume& volume) +{ + argument.beginStructure(); + argument << volume.type_; + argument << volume.volume_; + argument.endStructure(); + + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, PulseaudioVolume &volume) +{ + argument.beginStructure(); + argument >> volume.type_; + argument >> volume.volume_; + argument.endStructure(); + + return argument; +} + +PulseaudioVolumeArray::PulseaudioVolumeArray() +{ +} + +PulseaudioVolumeArray::PulseaudioVolumeArray(const PulseaudioVolumeArray &other) : + volume_array_(other.volume_array_) +{ +} + +PulseaudioVolumeArray& PulseaudioVolumeArray::operator=(const PulseaudioVolumeArray &other) +{ + volume_array_ = other.volume_array_; + + return *this; +} + +PulseaudioVolumeArray::~PulseaudioVolumeArray() +{ +} + +int PulseaudioVolumeArray::getNumItems() const +{ + return volume_array_.size(); +} + +PulseaudioVolume PulseaudioVolumeArray::getItem(int i) const +{ + if (i < volume_array_.size()) + { + return volume_array_[i]; + } + return PulseaudioVolume(); +} + +void PulseaudioVolumeArray::addItem(PulseaudioVolume const &item) +{ + volume_array_.push_back(item); +} + +void PulseaudioVolumeArray::registerMetaType() +{ + qRegisterMetaType("PulseaudioVolumeArray"); + + qDBusRegisterMetaType(); +} + +QDBusArgument &operator<<(QDBusArgument &argument, const PulseaudioVolumeArray& volume) +{ + argument.beginArray( qMetaTypeId() ); + for (int i = 0; i < volume.volume_array_.size(); ++ i) + { + PulseaudioVolume item = volume.getItem(i); + argument << item; + } + argument.endArray(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, PulseaudioVolumeArray &volume) +{ + argument.beginArray(); + while ( !argument.atEnd() ) { + PulseaudioVolume item; + argument >> item; + volume.volume_array_.push_back(item); + } + argument.endArray(); + + return argument; +} diff --git a/tests/dbus-types/pulseaudio-volume.h b/tests/dbus-types/pulseaudio-volume.h new file mode 100644 index 0000000..6d5a222 --- /dev/null +++ b/tests/dbus-types/pulseaudio-volume.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#pragma once + +#include + +class PulseaudioVolume +{ +public: + PulseaudioVolume(); + PulseaudioVolume(unsigned int type, unsigned int volume); + PulseaudioVolume(const PulseaudioVolume &other); + PulseaudioVolume& operator=(const PulseaudioVolume &other); + ~PulseaudioVolume(); + + friend QDBusArgument &operator<<(QDBusArgument &argument, PulseaudioVolume const & volume); + friend const QDBusArgument &operator>>(QDBusArgument const & argument, PulseaudioVolume &volume); + + unsigned int getType() const; + unsigned int getVolume() const; + + //register Message with the Qt type system + static void registerMetaType(); + +private: + unsigned int type_; + unsigned int volume_; +}; + +class PulseaudioVolumeArray +{ +public: + PulseaudioVolumeArray(); + PulseaudioVolumeArray(QString const &interface, QString const &property, QDBusVariant const& value); + PulseaudioVolumeArray(const PulseaudioVolumeArray &other); + PulseaudioVolumeArray& operator=(const PulseaudioVolumeArray &other); + ~PulseaudioVolumeArray(); + + friend QDBusArgument &operator<<(QDBusArgument &argument, PulseaudioVolumeArray const & volume); + friend const QDBusArgument &operator>>(QDBusArgument const & argument, PulseaudioVolumeArray &volume); + + int getNumItems() const; + PulseaudioVolume getItem(int i) const; + void addItem(PulseaudioVolume const &item); + + //register Message with the Qt type system + static void registerMetaType(); + +private: + QVector volume_array_; +}; + +Q_DECLARE_METATYPE(PulseaudioVolume) +Q_DECLARE_METATYPE(PulseaudioVolumeArray) diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt new file mode 100644 index 0000000..939e329 --- /dev/null +++ b/tests/integration/CMakeLists.txt @@ -0,0 +1,132 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +include(FindGMock) + +#pkg_check_modules(GMENUHARNESS REQUIRED libgmenuharness REQUIRED) +#include_directories(${GMENUHARNESS_INCLUDE_DIRS}) +include_directories("${CMAKE_SOURCE_DIR}/include") + +pkg_check_modules(QTDBUSTEST REQUIRED libqtdbustest-1 REQUIRED) +include_directories(${QTDBUSTEST_INCLUDE_DIRS}) + +pkg_check_modules(QTDBUSMOCK REQUIRED libqtdbusmock-1 REQUIRED) +include_directories(${QTDBUSMOCK_INCLUDE_DIRS}) + +find_package(Qt5Test REQUIRED) +include_directories(${Qt5Test_INCLUDE_DIRS}) + +find_package(Qt5DBus REQUIRED) +include_directories(${Qt5DBus_INCLUDE_DIRS}) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${GMOCK_INCLUDE_DIRS}) +include_directories(${GTEST_INCLUDE_DIRS}) + +include_directories("${CMAKE_SOURCE_DIR}/tests/dbus-types") +include_directories("${CMAKE_BINARY_DIR}/tests/dbus-types") + +add_definitions(-DSOUND_SERVICE_BIN="${CMAKE_BINARY_DIR}/src/indicator-sound-service" + -DSTREAM_RESTORE_TABLE="${CMAKE_SOURCE_DIR}/tests/integration/touch-stream-restore.table" + -DVOLUME_SET_BIN="${CMAKE_BINARY_DIR}/tests/integration/set-volume" + -DACCOUNTS_SERVICE_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/accounts-mock/accounts-service-sound" + -DMEDIA_PLAYER_MPRIS_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/media-player-mpris-mock/media-player-mpris-mock" + -DMEDIA_PLAYER_MPRIS_UPDATE_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/media-player-mpris-mock/media-player-mpris-mock-update" + -DTEST_SOUND="${CMAKE_SOURCE_DIR}/tests/integration/test-sound.wav" + -DQT_NO_KEYWORDS=1 + -DXDG_DATA_DIRS="${XDG_DATA_DIRS}" +) + +set(GLIB_REQUIRED_VERSION 2.26) + +pkg_check_modules( + GLIB REQUIRED + glib-2.0>=${GLIB_REQUIRED_VERSION} + gio-2.0>=${GLIB_REQUIRED_VERSION} +) +include_directories(${GLIB_INCLUDE_DIRS}) + +set( + INTEGRATION_TESTS_SRC + indicator-sound-test-base.cpp + test-indicator.cpp + utils/dbus-pulse-volume.cpp + main.cpp +) + +add_executable( + integration-tests + ${INTEGRATION_TESTS_SRC} +) + +qt5_use_modules( + integration-tests + Core + DBus + Test +) + +target_link_libraries( + integration-tests + sound-indicator-dbus-interfaces + ${QTDBUSMOCK_LDFLAGS} + ${QTDBUSTEST_LDFLAGS} + ${GTEST_LIBRARIES} + ${GMOCK_LIBRARIES} +# ${GMENUHARNESS_LDFLAGS} + ${GLIB_LDFLAGS} + gmenuharness-shared +) + +add_test( + integration-tests + integration-tests +) + +set( + SET-VOLUME-SRC + utils/dbus-pulse-volume.cpp + utils/set-volume.cpp +) + +set( + GET-VOLUME-SRC + utils/dbus-pulse-volume.cpp + utils/get-volume.cpp +) + +add_executable( + set-volume + ${SET-VOLUME-SRC} +) + +add_executable( + get-volume + ${GET-VOLUME-SRC} +) + +qt5_use_modules( + set-volume + Core + DBus + Test +) + +qt5_use_modules( + get-volume + Core + DBus + Test +) + +target_link_libraries( + get-volume + sound-indicator-dbus-interfaces +) + +target_link_libraries( + set-volume + sound-indicator-dbus-interfaces +) + +#add_subdirectory(utils) \ No newline at end of file diff --git a/tests/integration/Copy of test-sound.wav b/tests/integration/Copy of test-sound.wav new file mode 100644 index 0000000..709c6eb Binary files /dev/null and b/tests/integration/Copy of test-sound.wav differ diff --git a/tests/integration/indicator-sound-test-base.cpp b/tests/integration/indicator-sound-test-base.cpp new file mode 100644 index 0000000..5005e2c --- /dev/null +++ b/tests/integration/indicator-sound-test-base.cpp @@ -0,0 +1,823 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ + +#include "indicator-sound-test-base.h" + +#include "dbus_menus_interface.h" +#include "dbus_properties_interface.h" +#include "dbus_accounts_interface.h" +#include "dbus_accountssound_interface.h" +#include "dbus_notifications_interface.h" +#include "dbus-types.h" + +#include +#include +#include + +#include +#include "utils/dbus-pulse-volume.h" + +using namespace QtDBusTest; +using namespace QtDBusMock; +using namespace std; +using namespace testing; +namespace mh = unity::gmenuharness; + +namespace +{ + const int MAX_TIME_WAITING_FOR_NOTIFICATIONS = 2000; +} + +IndicatorSoundTestBase::IndicatorSoundTestBase() : + dbusMock(dbusTestRunner) +{ +} + +IndicatorSoundTestBase::~IndicatorSoundTestBase() +{ +} + +void IndicatorSoundTestBase::SetUp() +{ + setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, true); + setenv("DBUS_SYSTEM_BUS_ADDRESS", dbusTestRunner.systemBus().toStdString().c_str(), true); + setenv("DBUS_SESSION_BUS_ADDRESS", dbusTestRunner.sessionBus().toStdString().c_str(), true); + dbusMock.registerNotificationDaemon(); + + dbusTestRunner.startServices(); + + auto& notifications = notificationsMockInterface(); + notifications.AddMethod("org.freedesktop.Notifications", + "GetCapabilities", + "", + "as", + "ret = ['actions', 'body', 'body-markup', 'icon-static', 'image/svg+xml', 'x-canonical-private-synchronous', 'x-canonical-append', 'x-canonical-private-icon-only', 'x-canonical-truncation', 'private-synchronous', 'append', 'private-icon-only', 'truncation']" + ).waitForFinished(); + + int waitedTime = 0; + while (!dbusTestRunner.sessionConnection().interface()->isServiceRegistered("org.freedesktop.Notifications") && waitedTime < MAX_TIME_WAITING_FOR_NOTIFICATIONS) + { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + waitedTime += 10; + } +} + +void IndicatorSoundTestBase::TearDown() +{ + unsetenv("XDG_DATA_DIRS"); + unsetenv("PULSE_SERVER"); + unsetenv("DBUS_SYSTEM_BUS_ADDRESS"); +} + +void gvariant_deleter(GVariant* varptr) +{ + if (varptr != nullptr) + { + g_variant_unref(varptr); + } +} + +std::shared_ptr IndicatorSoundTestBase::volume_variant(double volume) +{ + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add(&builder, + "{sv}", + "title", + g_variant_new_string("_Sound")); + + g_variant_builder_add(&builder, + "{sv}", + "accessible-desc", + g_variant_new_string("_Sound")); + + auto icon = g_themed_icon_new("icon"); + g_variant_builder_add(&builder, + "{sv}", + "icon", + g_icon_serialize(icon)); + + g_variant_builder_add(&builder, + "{sv}", + "visible", + g_variant_new_boolean(true)); + return shared_ptr(g_variant_builder_end(&builder), &gvariant_deleter); +} + +bool IndicatorSoundTestBase::setStreamRestoreVolume(QString const &role, double volume) +{ + QProcess setVolume; + setVolume.start(VOLUME_SET_BIN, QStringList() + << role + << QString("%1").arg(volume)); + if (!setVolume.waitForStarted()) + return false; + + if (!setVolume.waitForFinished()) + return false; + + return setVolume.exitCode() == 0; +} + +bool IndicatorSoundTestBase::setSinkVolume(double volume) +{ + QString volume_percentage = QString("%1\%").arg(volume*100); + QProcess setVolume; + setVolume.start("pactl", QStringList() + << "-s" + << "127.0.0.1" + << "set-sink-volume" + << "0" + << volume_percentage); + if (!setVolume.waitForStarted()) + return false; + + if (!setVolume.waitForFinished()) + return false; + + return setVolume.exitCode() == 0; +} + +bool IndicatorSoundTestBase::clearGSettingsPlayers() +{ + QProcess clearPlayers; + + clearPlayers.start("gsettings", QStringList() + << "set" + << "com.canonical.indicator.sound" + << "interested-media-players" + << "[]"); + if (!clearPlayers.waitForStarted()) + return false; + + if (!clearPlayers.waitForFinished()) + return false; + + return clearPlayers.exitCode() == 0; +} + +bool IndicatorSoundTestBase::startTestMprisPlayer(QString const& playerName) +{ + testPlayer1.terminate(); + testPlayer1.start(MEDIA_PLAYER_MPRIS_BIN, QStringList() + << playerName); + if (!testPlayer1.waitForStarted()) + return false; + + + return true; +} + +bool IndicatorSoundTestBase::setTestMprisPlayerProperty(QString const &testPlayer, QString const &property, bool value) +{ + QProcess setProperty; + QString strValue; + strValue = value ? "true" : "false"; + + setProperty.start(MEDIA_PLAYER_MPRIS_UPDATE_BIN, QStringList() + << testPlayer + << property + << strValue); + if (!setProperty.waitForStarted()) + return false; + + if (!setProperty.waitForFinished()) + return false; + + return setProperty.exitCode() == 0; +} + +bool IndicatorSoundTestBase::startTestSound(QString const &role) +{ + testSoundProcess.terminate(); + testSoundProcess.start("paplay", QStringList() + << "-s" + << "127.0.0.1" + << TEST_SOUND + << QString("--property=media.role=%1").arg(role)); + + if (!testSoundProcess.waitForStarted()) + return false; + + return true; +} + +void IndicatorSoundTestBase::stopTestSound() +{ + testSoundProcess.terminate(); +} + +void IndicatorSoundTestBase::startPulseDesktop(DevicePortType speakerPort, DevicePortType headphonesPort) +{ + try + { + pulseaudio.reset( + new QProcessDBusService(DBusTypes::DBUS_PULSE, + QDBusConnection::SessionBus, + "pulseaudio", + QStringList() << "--start" + << "-vvvv" + << "--disable-shm=true" + << "--daemonize=false" + << "--use-pid-file=false" + << "--system=false" + << "--exit-idle-time=-1" + << "-n" + << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort)) + << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort)) + << "--log-target=file:/tmp/pulse-daemon.log" + << "--load=module-dbus-protocol" + << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1" + )); + pulseaudio->start(dbusTestRunner.sessionConnection()); + } + catch (exception const& e) + { + cout << "pulseaudio(): " << e.what() << endl; + throw; + } +} + +void IndicatorSoundTestBase::startPulsePhone(DevicePortType speakerPort, DevicePortType headphonesPort) +{ + try + { + pulseaudio.reset( + new QProcessDBusService(DBusTypes::DBUS_PULSE, + QDBusConnection::SessionBus, + "pulseaudio", + QStringList() << "--start" + << "-vvvv" + << "--disable-shm=true" + << "--daemonize=false" + << "--use-pid-file=false" + << "--system=false" + << "--exit-idle-time=-1" + << "-n" + << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort)) + << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort)) + << "--log-target=file:/tmp/pulse-daemon.log" + << QString("--load=module-stream-restore restore_device=false restore_muted=false fallback_table=%1").arg(STREAM_RESTORE_TABLE) + << "--load=module-dbus-protocol" + << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1" + )); + pulseaudio->start(dbusTestRunner.sessionConnection()); + } + catch (exception const& e) + { + cout << "pulseaudio(): " << e.what() << endl; + throw; + } +} + +void IndicatorSoundTestBase::startAccountsService() +{ + try + { + accountsService.reset( + new QProcessDBusService(DBusTypes::ACCOUNTS_SERVICE, + QDBusConnection::SystemBus, + ACCOUNTS_SERVICE_BIN, + QStringList())); + accountsService->start(dbusTestRunner.systemConnection()); + + initializeAccountsInterface(); + } + catch (exception const& e) + { + cout << "accountsService(): " << e.what() << endl; + throw; + } +} + +void IndicatorSoundTestBase::startIndicator() +{ + try + { + setenv("PULSE_SERVER", "127.0.0.1", true); + indicator.reset( + new QProcessDBusService(DBusTypes::DBUS_NAME, + QDBusConnection::SessionBus, + SOUND_SERVICE_BIN, + QStringList())); + indicator->start(dbusTestRunner.sessionConnection()); + } + catch (exception const& e) + { + cout << "startIndicator(): " << e.what() << endl; + throw; + } +} + +mh::MenuMatcher::Parameters IndicatorSoundTestBase::desktopParameters() +{ + return mh::MenuMatcher::Parameters( + "com.canonical.indicator.sound", + { { "indicator", "/com/canonical/indicator/sound" } }, + "/com/canonical/indicator/sound/desktop"); +} + +mh::MenuMatcher::Parameters IndicatorSoundTestBase::phoneParameters() +{ + return mh::MenuMatcher::Parameters( + "com.canonical.indicator.sound", + { { "indicator", "/com/canonical/indicator/sound" } }, + "/com/canonical/indicator/sound/phone"); +} + +unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::volumeSlider(double volume, QString const &label) +{ + return mh::MenuItemMatcher().radio() + .label(label.toStdString()) + .round_doubles(0.1) + .int32_attribute("target", 0) + .double_attribute("min-value", 0.0) + .double_attribute("max-value", 1.0) + .double_attribute("step", 0.01) + .string_attribute("x-canonical-type", "com.canonical.unity.slider") + .themed_icon("max-icon", {"audio-volume-high-panel", "audio-volume-high", "audio-volume", "audio"}) + .themed_icon("min-icon", {"audio-volume-low-zero-panel", "audio-volume-low-zero", "audio-volume-low", "audio-volume", "audio"}) + .pass_through_double_attribute("action", volume); +} + +unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::silentModeSwitch(bool toggled) +{ + return mh::MenuItemMatcher::checkbox() + .label("Silent Mode") + .action("indicator.silent-mode") + .toggled(toggled); +} + +bool IndicatorSoundTestBase::waitMenuChange() +{ + if (signal_spy_menu_changed_) + { + return signal_spy_menu_changed_->wait(); + } + return false; +} + +bool IndicatorSoundTestBase::initializeMenuChangedSignal() +{ + if (!menu_interface_) + { + menu_interface_.reset(new MenusInterface("com.canonical.indicator.sound", + "/com/canonical/indicator/sound", + dbusTestRunner.sessionConnection(), 0)); + } + if (menu_interface_) + { + qDebug() << "Waiting for signal"; + signal_spy_menu_changed_.reset(new QSignalSpy(menu_interface_.get(), &MenusInterface::Changed)); + } + if (!menu_interface_ || !signal_spy_menu_changed_) + { + return false; + } + return true; +} + +bool IndicatorSoundTestBase::waitVolumeChangedInIndicator() +{ + if (signal_spy_volume_changed_) + { + return signal_spy_volume_changed_->wait(); + } + return false; +} + +void IndicatorSoundTestBase::initializeAccountsInterface() +{ + auto username = qgetenv("USER"); + if (username != "") + { + main_accounts_interface_.reset(new AccountsInterface("org.freedesktop.Accounts", + "/org/freedesktop/Accounts", + dbusTestRunner.systemConnection(), 0)); + + QDBusReply userResp = main_accounts_interface_->call(QLatin1String("FindUserByName"), + QLatin1String(username)); + + if (!userResp.isValid()) + { + qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << userResp.error().message(); + } + + auto userPath = userResp.value().path(); + if (userPath != "") + { + std::unique_ptr soundInterface(new AccountsSoundInterface("org.freedesktop.Accounts", + userPath, + dbusTestRunner.systemConnection(), 0)); + + accounts_interface_.reset(new DBusPropertiesInterface("org.freedesktop.Accounts", + userPath, + dbusTestRunner.systemConnection(), 0)); + if (!accounts_interface_->isValid()) + { + qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << accounts_interface_->lastError().message(); + } + signal_spy_volume_changed_.reset(new QSignalSpy(accounts_interface_.get(),&DBusPropertiesInterface::PropertiesChanged)); + } + } +} + +OrgFreedesktopDBusMockInterface& IndicatorSoundTestBase::notificationsMockInterface() +{ + return dbusMock.mockInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::SessionBus); +} + +bool IndicatorSoundTestBase::setActionValue(const QString & action, QVariant value) +{ + QDBusInterface actionsInterface(DBusTypes::DBUS_NAME, + DBusTypes::MAIN_SERVICE_PATH, + DBusTypes::ACTIONS_INTERFACE, + dbusTestRunner.sessionConnection()); + + QDBusVariant dbusVar(value); + auto resp = actionsInterface.call("SetState", + action, + QVariant::fromValue(dbusVar), + QVariant::fromValue(QVariantMap())); + + if (resp.type() == QDBusMessage::ErrorMessage) + { + qCritical() << "IndicatorSoundTestBase::setActionValue(): Failed to set value for action " + << action + << " " + << resp.errorMessage(); + return false; + } + else + { + return true; + } +} + +bool IndicatorSoundTestBase::pressNotificationButton(int id, const QString & button) +{ + OrgFreedesktopDBusMockInterface actionsInterface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + dbusTestRunner.sessionConnection()); + + actionsInterface.EmitSignal( + "org.freedesktop.Notifications", + "ActionInvoked", "us", QVariantList() << id << button); + + return true; +} + +bool IndicatorSoundTestBase::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 IndicatorSoundTestBase::checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call) +{ + QString icon; + if (volume <= 0.0) + { + icon = "audio-volume-muted"; + } + else if (volume <= 0.3) + { + icon = "audio-volume-low"; + } + else if (volume <= 0.7) + { + icon = "audio-volume-medium"; + } + else + { + icon = "audio-volume-high"; + } + + ASSERT_NE(call.size(), 0); + EXPECT_EQ("Notify", call.at(0)); + + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(8, args.size()); + EXPECT_EQ("indicator-sound", args.at(0)); + EXPECT_EQ(icon, args.at(2)); + EXPECT_EQ("Volume", args.at(3)); + EXPECT_EQ(label, args.at(4)); + EXPECT_EQ(QStringList(), args.at(5)); + + QVariantMap hints; + ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); + ASSERT_TRUE(hints.contains("value")); + ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); + ASSERT_TRUE(hints.contains("x-canonical-value-bar-tint")); + ASSERT_TRUE(hints.contains("x-canonical-private-synchronous")); + + EXPECT_EQ(volume*100, hints["value"]); + EXPECT_EQ(true, hints["x-canonical-non-shaped-icon"]); + EXPECT_EQ(isLoud, hints["x-canonical-value-bar-tint"]); + EXPECT_EQ(true, hints["x-canonical-private-synchronous"]); +} + +void IndicatorSoundTestBase::checkHighVolumeNotification(QVariantList call) +{ + ASSERT_NE(call.size(), 0); + EXPECT_EQ("Notify", call.at(0)); + + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(8, args.size()); + EXPECT_EQ("indicator-sound", args.at(0)); + EXPECT_EQ("Volume", args.at(3)); +} + +void IndicatorSoundTestBase::checkCloseNotification(int id, QVariantList call) +{ + EXPECT_EQ("CloseNotification", call.at(0)); + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(1, args.size()); +} + +void IndicatorSoundTestBase::checkNotificationWithNoArgs(QString const& method, QVariantList call) +{ + EXPECT_EQ(method, call.at(0)); + QVariantList const& args(call.at(1).toList()); + ASSERT_EQ(0, args.size()); +} + +int IndicatorSoundTestBase::getNotificationID(QVariantList call) +{ + if (call.size() == 0) + { + return -1; + } + QVariantList const& args(call.at(1).toList()); + if (args.size() != 8) + { + return -1; + } + if (args.at(0) != "indicator-sound") + { + return -1; + } + + bool isInt; + int id = args.at(1).toInt(&isInt); + if (!isInt) + { + return -1; + } + return id; +} + +bool IndicatorSoundTestBase::activateHeadphones(bool headphonesActive) +{ + QProcess pacltProcess; + + QString defaultSinkName = "indicator_sound_test_speaker"; + QString suspendedSinkName = "indicator_sound_test_headphones"; + if (headphonesActive) + { + defaultSinkName = "indicator_sound_test_headphones"; + suspendedSinkName = "indicator_sound_test_speaker"; + } + + pacltProcess.start("pactl", QStringList() << "-s" + << "127.0.0.1" + << "set-default-sink" + << defaultSinkName); + if (!pacltProcess.waitForStarted()) + return false; + + if (!pacltProcess.waitForFinished()) + return false; + + pacltProcess.start("pactl", QStringList() << "-s" + << "127.0.0.1" + << "suspend-sink" + << defaultSinkName + << "0"); + if (!pacltProcess.waitForStarted()) + return false; + + if (!pacltProcess.waitForFinished()) + return false; + + pacltProcess.start("pactl", QStringList() << "-s" + << "127.0.0.1" + << "suspend-sink" + << suspendedSinkName + << "1"); + if (!pacltProcess.waitForStarted()) + return false; + + if (!pacltProcess.waitForFinished()) + return false; + + return pacltProcess.exitCode() == 0; +} + +QString IndicatorSoundTestBase::getDevicePortString(DevicePortType port) +{ + QString portString; + + switch (port) + { + case WIRED: + portString = "wired"; + break; + case BLUETOOTH: + portString = "bluetooth"; + break; + case USB: + portString = "usb"; + break; + case HDMI: + portString = "hdmi"; + break; + default: + portString = "not_defined"; + break; + } + + return portString; +} + +void IndicatorSoundTestBase::checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort) +{ + double INITIAL_VOLUME = 0.0; + + QString speakerString; + QString speakerStringMenu; + switch(speakerPort) + { + case WIRED: + speakerString = "Speakers"; + speakerStringMenu = "Volume"; + break; + case BLUETOOTH: + speakerString = "Bluetooth speaker"; + speakerStringMenu = "Volume (Bluetooth)"; + break; + case USB: + speakerString = "Usb speaker"; + speakerStringMenu = "Volume (Usb)"; + break; + case HDMI: + speakerString = "HDMI speaker"; + speakerStringMenu = "Volume (HDMI)"; + break; + } + + QString headphonesString; + QString headphonesStringMenu; + switch(headphonesPort) + { + case WIRED: + headphonesString = "Headphones"; + headphonesStringMenu = "Volume (Headphones)"; + break; + case BLUETOOTH: + headphonesString = "Bluetooth headphones"; + headphonesStringMenu = "Volume (Bluetooth headphones)"; + break; + case USB: + headphonesString = "Usb headphones"; + headphonesStringMenu = "Volume (Usb headphones)"; + break; + case HDMI: + headphonesString = "HDMI headphones"; + headphonesStringMenu = "Volume (HDMI headphones)"; + break; + } + + QSignalSpy notificationsSpy(¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + ASSERT_NO_THROW(startAccountsService()); + ASSERT_NO_THROW(startPulsePhone(speakerPort, headphonesPort)); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // if the speaker is the normal one it does not emit any notification, as that's + // the default one. + // for the rest it notifies the output + if (speakerPort != WIRED) + { + WAIT_FOR_SIGNALS(notificationsSpy, 3); + + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(INITIAL_VOLUME, speakerString, false, notificationsSpy.at(2)); + notificationsSpy.clear(); + } + + notificationsSpy.clear(); + // activate the headphones + EXPECT_TRUE(activateHeadphones(true)); + + if (speakerPort == WIRED) + { + WAIT_FOR_SIGNALS(notificationsSpy, 3); + + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(INITIAL_VOLUME, headphonesString, false, notificationsSpy.at(2)); + notificationsSpy.clear(); + } + else + { + WAIT_FOR_SIGNALS(notificationsSpy, 2); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkVolumeNotification(INITIAL_VOLUME, headphonesString, false, notificationsSpy.at(1)); + notificationsSpy.clear(); + } + + // check the label in the menu + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(INITIAL_VOLUME, headphonesStringMenu)) + ) + ).match()); + + // deactivate the headphones + EXPECT_TRUE(activateHeadphones(false)); + + WAIT_FOR_SIGNALS(notificationsSpy, 2); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkVolumeNotification(INITIAL_VOLUME, speakerString, false, notificationsSpy.at(1)); + notificationsSpy.clear(); + + // check the label in the menu + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(INITIAL_VOLUME, speakerStringMenu)) + ) + ).match()); +} + +bool IndicatorSoundTestBase::setVolumeUntilAccountsIsConnected(double volume) +{ + int RETRY_TIME = 5000; + + setActionValue("volume", QVariant::fromValue(volume)); + while(!signal_spy_volume_changed_->wait(10) && RETRY_TIME) + { + RETRY_TIME -= 10; + setActionValue("volume", QVariant::fromValue(volume)); + } + return (signal_spy_volume_changed_->count() != 0); +} diff --git a/tests/integration/indicator-sound-test-base.h b/tests/integration/indicator-sound-test-base.h new file mode 100644 index 0000000..41dd1c7 --- /dev/null +++ b/tests/integration/indicator-sound-test-base.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ + +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include + +class MenusInterface; +class DBusPulseVolume; +class DBusPropertiesInterface; +class AccountsInterface; +class QSignalSpy; + +#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ +{\ + while (signalSpy.size() < signalsExpected)\ + {\ + ASSERT_TRUE(signalSpy.wait());\ + }\ + ASSERT_EQ(signalsExpected, signalSpy.size());\ +} + +#define WAIT_AT_LEAST_SIGNALS(signalSpy, signalsExpected)\ +{\ + while (signalSpy.size() < signalsExpected)\ + {\ + ASSERT_TRUE(signalSpy.wait());\ + }\ + ASSERT_TRUE(signalsExpected <= signalSpy.size());\ +} + +class IndicatorSoundTestBase: public testing::Test +{ +public: + IndicatorSoundTestBase(); + + ~IndicatorSoundTestBase(); + + enum DevicePortType + { + WIRED, + BLUETOOTH, + USB, + HDMI + }; + +protected: + void SetUp() override; + void TearDown() override; + + void startIndicator(); + void startPulseDesktop(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED); + void startPulsePhone(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED); + void startAccountsService(); + + bool clearGSettingsPlayers(); + + bool startTestMprisPlayer(QString const& playerName); + + bool setTestMprisPlayerProperty(QString const &testPlayer, QString const &property, bool value); + + bool setStreamRestoreVolume(QString const &role, double volume); + + bool setSinkVolume(double volume); + + bool startTestSound(QString const &role); + + void stopTestSound(); + + static std::shared_ptr volume_variant(double volume); + + static unity::gmenuharness::MenuMatcher::Parameters desktopParameters(); + + static unity::gmenuharness::MenuMatcher::Parameters phoneParameters(); + + static unity::gmenuharness::MenuItemMatcher volumeSlider(double volume, QString const &label); + + static unity::gmenuharness::MenuItemMatcher silentModeSwitch(bool toggled); + + bool waitMenuChange(); + + bool initializeMenuChangedSignal(); + + bool waitVolumeChangedInIndicator(); + + void initializeAccountsInterface(); + + OrgFreedesktopDBusMockInterface& notificationsMockInterface(); + + bool setActionValue(const QString & action, QVariant value); + + bool pressNotificationButton(int id, const QString & button); + + bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map); + + void checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call); + + void checkHighVolumeNotification(QVariantList call); + + void checkCloseNotification(int id, QVariantList call); + + void checkNotificationWithNoArgs(QString const& method, QVariantList call); + + int getNotificationID(QVariantList call); + + bool activateHeadphones(bool headphonesActive); + + QString getDevicePortString(DevicePortType port); + + void checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort); + + bool setVolumeUntilAccountsIsConnected(double volume); + + QtDBusTest::DBusTestRunner dbusTestRunner; + + QtDBusMock::DBusMock dbusMock; + + QtDBusTest::DBusServicePtr indicator; + + QtDBusTest::DBusServicePtr pulseaudio; + + QtDBusTest::DBusServicePtr accountsService; + + QProcess testSoundProcess; + + QProcess testPlayer1; + + std::unique_ptr menu_interface_; + + std::unique_ptr accounts_interface_; + + std::unique_ptr main_accounts_interface_; + + std::unique_ptr signal_spy_volume_changed_; + + std::unique_ptr signal_spy_menu_changed_; +}; diff --git a/tests/integration/main.cpp b/tests/integration/main.cpp new file mode 100644 index 0000000..a29b3b6 --- /dev/null +++ b/tests/integration/main.cpp @@ -0,0 +1,58 @@ +/* + * 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 + +#include "dbus-types.h" + +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"); + + QCoreApplication application(argc, argv); + DBusMock::registerMetaTypes(); + DBusTypes::registerMetaTypes(); + ::testing::InitGoogleTest(&argc, argv); + + Runner runner; + QTimer::singleShot(0, &runner, SLOT(run())); + + return application.exec(); +} + +#include "main.moc" diff --git a/tests/integration/test-indicator.cpp b/tests/integration/test-indicator.cpp new file mode 100644 index 0000000..33e62b5 --- /dev/null +++ b/tests/integration/test-indicator.cpp @@ -0,0 +1,981 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ + +#include + +#include +#include +#include + +using namespace std; +using namespace testing; +namespace mh = unity::gmenuharness; +namespace +{ + +class TestIndicator: public IndicatorSoundTestBase +{ +}; + +TEST_F(TestIndicator, PhoneChangeRoleVolume) +{ + double INITIAL_VOLUME = 0.0; + + ASSERT_NO_THROW(startAccountsService()); + ASSERT_NO_THROW(startPulsePhone()); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // Generate a random volume + QTime now = QTime::currentTime(); + qsrand(now.msec()); + int randInt = qrand() % 100; + double randomVolume = randInt / 100.0; + + QSignalSpy &userAccountsSpy = *signal_spy_volume_changed_; + // set an initial volume to the alert role + userAccountsSpy.clear(); + EXPECT_TRUE(setVolumeUntilAccountsIsConnected(1.0)); + userAccountsSpy.clear(); + // play a test sound, it should change the role in the indicator + EXPECT_TRUE(startTestSound("multimedia")); + + // this time we only expect 1 signal as it's only the indicator + // updating the value + WAIT_FOR_SIGNALS(userAccountsSpy, 1); + //EXPECT_TRUE(waitVolumeChangedInIndicator()); + + userAccountsSpy.clear(); + // set the random volume to the multimedia role + setActionValue("volume", QVariant::fromValue(randomVolume)); + if (randomVolume != INITIAL_VOLUME) + { + WAIT_FOR_SIGNALS(userAccountsSpy, 1); + } + + // check the indicator + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(randomVolume, "Volume")) + ) + ).match()); + + // check that the last item is Sound Settings + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::ends_with) + .submenu() + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + .action("indicator.phone-settings") + ) + ).match()); + + // initialize the signal spy + EXPECT_TRUE(initializeMenuChangedSignal()); + userAccountsSpy.clear(); + // stop the test sound, the role should change again to alert + stopTestSound(); + if (randomVolume != 1.0) + { + // wait for the menu change + EXPECT_TRUE(waitMenuChange()); + } + + // check the initial volume for the alert role + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(1.0, "Volume")) + ) + ).match()); + + // check that the last item is Sound Settings + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::ends_with) + .submenu() + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + .action("indicator.phone-settings") + ) + ).match()); +} + +TEST_F(TestIndicator, PhoneBasicInitialVolume) +{ + double INITIAL_VOLUME = 0.0; + + ASSERT_NO_THROW(startAccountsService()); + EXPECT_TRUE(clearGSettingsPlayers()); + ASSERT_NO_THROW(startPulsePhone()); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + .action("indicator.phone-settings") + ) + ).match()); +} + +TEST_F(TestIndicator, PhoneAddMprisPlayer) +{ + double INITIAL_VOLUME = 0.0; + + ASSERT_NO_THROW(startAccountsService()); + EXPECT_TRUE(clearGSettingsPlayers()); + ASSERT_NO_THROW(startPulsePhone()); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + .action("indicator.phone-settings") + ) + ).match()); + + // initialize the signal spy + EXPECT_TRUE(initializeMenuChangedSignal()); + + // start the test player + EXPECT_TRUE(startTestMprisPlayer("testplayer1")); + + // wait for the menu change + EXPECT_TRUE(waitMenuChange()); + + // finally verify that the player is added + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher() + .action("indicator.testplayer1.desktop") + .label("TestPlayer1") + .themed_icon("icon", {"testplayer"}) + .string_attribute("x-canonical-type", "com.canonical.unity.media-player") + ) + .item(mh::MenuItemMatcher() + .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") + .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") + .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") + .string_attribute("x-canonical-type","com.canonical.unity.playback-item") + ) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + .action("indicator.phone-settings") + ) + ).match()); +} + +TEST_F(TestIndicator, DesktopBasicInitialVolume) +{ + double INITIAL_VOLUME = 0.0; + + ASSERT_NO_THROW(startAccountsService()); + EXPECT_TRUE(clearGSettingsPlayers()); + ASSERT_NO_THROW(startPulseDesktop()); + + // initialize volumes in pulseaudio + EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME)); + + // start the test player + EXPECT_TRUE(startTestMprisPlayer("testplayer1")); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher().checkbox() + .label("Mute") + ) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher() + .action("indicator.testplayer1.desktop") + .label("TestPlayer1") + .themed_icon("icon", {"testplayer"}) + .string_attribute("x-canonical-type", "com.canonical.unity.media-player") + ) + .item(mh::MenuItemMatcher() + .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") + .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") + .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") + .string_attribute("x-canonical-type","com.canonical.unity.playback-item") + ) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + ) + ).match()); +} + +TEST_F(TestIndicator, DesktopAddMprisPlayer) +{ + double INITIAL_VOLUME = 0.0; + + ASSERT_NO_THROW(startAccountsService()); + EXPECT_TRUE(clearGSettingsPlayers()); + ASSERT_NO_THROW(startPulseDesktop()); + + // initialize volumes in pulseaudio + EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME)); + + // start the test player + EXPECT_TRUE(startTestMprisPlayer("testplayer1")); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // check that the player is added + EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher().checkbox() + .label("Mute") + ) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher() + .action("indicator.testplayer1.desktop") + .label("TestPlayer1") + .themed_icon("icon", {"testplayer"}) + .string_attribute("x-canonical-type", "com.canonical.unity.media-player") + ) + .item(mh::MenuItemMatcher() + .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") + .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") + .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") + .string_attribute("x-canonical-type","com.canonical.unity.playback-item") + ) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + ) + ).match()); +} + +TEST_F(TestIndicator, DesktopMprisPlayerButtonsState) +{ + double INITIAL_VOLUME = 0.0; + + ASSERT_NO_THROW(startAccountsService()); + EXPECT_TRUE(clearGSettingsPlayers()); + ASSERT_NO_THROW(startPulseDesktop()); + + // initialize volumes in pulseaudio + EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME)); + + // start the test player + EXPECT_TRUE(startTestMprisPlayer("testplayer1")); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // check that the player is added + EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher().checkbox() + .label("Mute") + ) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher() + .action("indicator.testplayer1.desktop") + .label("TestPlayer1") + .themed_icon("icon", {"testplayer"}) + .string_attribute("x-canonical-type", "com.canonical.unity.media-player") + ) + .item(mh::MenuItemMatcher() + .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") + .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") + .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") + .string_attribute("x-canonical-type","com.canonical.unity.playback-item") + ) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + ) + ).match()); + + // change the state of CanGoNext + EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoNext", false)); + + // verify that the action changes + EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher().checkbox() + .label("Mute") + ) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher() + .action("indicator.testplayer1.desktop") + .label("TestPlayer1") + .themed_icon("icon", {"testplayer"}) + .string_attribute("x-canonical-type", "com.canonical.unity.media-player") + ) + .item(mh::MenuItemMatcher() + .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") + .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") + .attribute_not_set("x-canonical-next-action") + .string_attribute("x-canonical-type","com.canonical.unity.playback-item") + ) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + ) + ).match()); + + + // change the state of CanGoPrevious + EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoPrevious", false)); + + // verify that the action changes + EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher().checkbox() + .label("Mute") + ) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher() + .action("indicator.testplayer1.desktop") + .label("TestPlayer1") + .themed_icon("icon", {"testplayer"}) + .string_attribute("x-canonical-type", "com.canonical.unity.media-player") + ) + .item(mh::MenuItemMatcher() + .attribute_not_set("x-canonical-previous-action") + .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") + .attribute_not_set("x-canonical-next-action") + .string_attribute("x-canonical-type","com.canonical.unity.playback-item") + ) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + ) + ).match()); + + // set back both to true + EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoNext", true)); + EXPECT_TRUE(setTestMprisPlayerProperty("testplayer1", "CanGoPrevious", true)); + + EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher().checkbox() + .label("Mute") + ) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher() + .action("indicator.testplayer1.desktop") + .label("TestPlayer1") + .themed_icon("icon", {"testplayer"}) + .string_attribute("x-canonical-type", "com.canonical.unity.media-player") + ) + .item(mh::MenuItemMatcher() + .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") + .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") + .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") + .string_attribute("x-canonical-type","com.canonical.unity.playback-item") + ) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + ) + ).match()); +} + +TEST_F(TestIndicator, DesktopChangeRoleVolume) +{ + double INITIAL_VOLUME = 0.0; + + ASSERT_NO_THROW(startAccountsService()); + ASSERT_NO_THROW(startPulseDesktop()); + + // initialize volumes in pulseaudio + // expect false for stream restore, because the module + // is not loaded in the desktop pulseaudio instance + EXPECT_FALSE(setStreamRestoreVolume("mutimedia", INITIAL_VOLUME)); + EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + + EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // Generate a random volume + QTime now = QTime::currentTime(); + qsrand(now.msec()); + int randInt = qrand() % 100; + double randomVolume = randInt / 100.0; + + // play a test sound, it should NOT change the role in the indicator + EXPECT_TRUE(startTestSound("multimedia")); + EXPECT_FALSE(waitVolumeChangedInIndicator()); + + // set the random volume + EXPECT_TRUE(setSinkVolume(randomVolume)); + if (randomVolume != INITIAL_VOLUME) + { + EXPECT_TRUE(waitVolumeChangedInIndicator()); + } + + // check the indicator + EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(mh::MenuItemMatcher().checkbox() + .label("Mute") + ) + .item(volumeSlider(randomVolume, "Volume")) + ) + ).match()); + + // check that the last item is Sound Settings + EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::ends_with) + .submenu() + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + ) + ).match()); + + // stop the test sound, the role should change again to alert + stopTestSound(); + + // although we were playing something in the multimedia role + // the server does not have the streamrestore module, so + // the volume does not change (the role does not change) + EXPECT_FALSE(waitVolumeChangedInIndicator()); + + // check the initial volume for the alert role + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(randomVolume, "Volume")) + ) + ).match()); + + // check that the last item is Sound Settings + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .mode(mh::MenuItemMatcher::Mode::ends_with) + .submenu() + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + .action("indicator.phone-settings") + ) + ).match()); +} + +TEST_F(TestIndicator, PhoneNotificationVolume) +{ + double INITIAL_VOLUME = 0.0; + + QSignalSpy notificationsSpy(¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + ASSERT_NO_THROW(startAccountsService()); + EXPECT_TRUE(clearGSettingsPlayers()); + ASSERT_NO_THROW(startPulsePhone()); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // check the initial state + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::all) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) + ) + .item(mh::MenuItemMatcher() + .label("Sound Settings…") + .action("indicator.phone-settings") + ) + ).match()); + + // change volume to 1.0 + setActionValue("volume", QVariant::fromValue(1.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 3); + + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(1.0, "Speakers", false, notificationsSpy.at(2)); + + notificationsSpy.clear(); + setActionValue("volume", QVariant::fromValue(0.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 2) + + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkVolumeNotification(0.0, "Speakers", false, notificationsSpy.at(1)); + + notificationsSpy.clear(); + setActionValue("volume", QVariant::fromValue(0.5)); + + WAIT_FOR_SIGNALS(notificationsSpy, 2) + + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkVolumeNotification(0.5, "Speakers", false, notificationsSpy.at(1)); +} + +TEST_F(TestIndicator, PhoneNotificationWarningVolume) +{ + double INITIAL_VOLUME = 0.0; + + QSignalSpy notificationsSpy(¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + ASSERT_NO_THROW(startAccountsService()); + ASSERT_NO_THROW(startPulsePhone()); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // activate the headphones + EXPECT_TRUE(activateHeadphones(true)); + + // set an initial volume to the alert role + setStreamRestoreVolume("alert", 1.0); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + // play a test sound, it should change the role in the indicator + EXPECT_TRUE(startTestSound("multimedia")); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + WAIT_FOR_SIGNALS(notificationsSpy, 3); + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(2)); + notificationsSpy.clear(); + + // change volume to 0.3... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.3)); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + WAIT_FOR_SIGNALS(notificationsSpy, 2); + + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkVolumeNotification(0.3, "Headphones", false, notificationsSpy.at(1)); + notificationsSpy.clear(); + + // change volume to 0.5... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.5)); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + WAIT_FOR_SIGNALS(notificationsSpy, 2); + + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(1)); + notificationsSpy.clear(); + + // change volume to 1.0... warning should be emitted + setActionValue("volume", QVariant::fromValue(1.0)); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + WAIT_FOR_SIGNALS(notificationsSpy, 8); + + // the notification is sent twice (TODO check why) + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkCloseNotification(1, notificationsSpy.at(1)); + checkHighVolumeNotification(notificationsSpy.at(2)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(3)); + checkCloseNotification(1, notificationsSpy.at(4)); + checkHighVolumeNotification(notificationsSpy.at(5)); + + // get the last notification ID + int idNotification = getNotificationID(notificationsSpy.at(5)); + ASSERT_NE(-1, idNotification); + + qWarning() << "XGM: id Notification: " << idNotification; + + // cancel the dialog + pressNotificationButton(idNotification, "cancel"); + + // check that the volume was clamped + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(0.74, "Volume (Headphones)")) + ) + ).match()); + + // try again... + notificationsSpy.clear(); + + qWarning() << "-----------------------------------------------------------"; + // change volume to 1.0... warning should be emitted + setActionValue("volume", QVariant::fromValue(1.0)); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + WAIT_FOR_SIGNALS(notificationsSpy, 6); + + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkHighVolumeNotification(notificationsSpy.at(1)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(2)); + checkHighVolumeNotification(notificationsSpy.at(3)); + + // get the last notification ID + idNotification = getNotificationID(notificationsSpy.at(1)); + ASSERT_NE(-1, idNotification); + + // this time we approve + pressNotificationButton(idNotification, "ok"); + + // check that the volume was applied + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(1.0, "Volume (Headphones)")) + .item(mh::MenuItemMatcher() + .action("indicator.high-volume-warning-item") + .label("High volume can damage your hearing.") + ) + ) + ).match()); + + // after the warning was approved we should be able to modify the volume + // and don't get the warning + notificationsSpy.clear(); + + // change volume to 0.5... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.5)); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + WAIT_FOR_SIGNALS(notificationsSpy, 6); + + // check the notification TODO check why the sound indicator sends it twice + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkCloseNotification(idNotification, notificationsSpy.at(1)); + checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(2)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(3)); + checkCloseNotification(idNotification, notificationsSpy.at(4)); + checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(5)); + + // check that the volume was applied + // and that we don't have the warning item + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(0.5, "Volume (Headphones)")) + ) + ).match()); + + // now set high volume again, we should not get the warning dialog + // as we already approved it + notificationsSpy.clear(); + + setActionValue("volume", QVariant::fromValue(1.0)); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + WAIT_FOR_SIGNALS(notificationsSpy, 4); + + // check the notification TODO check why the sound indicator sends it twice + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkVolumeNotification(1.0, "Headphones", true, notificationsSpy.at(1)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(2)); + checkVolumeNotification(1.0, "Headphones", true, notificationsSpy.at(3)); +} + + +TEST_F(TestIndicator, PhoneNotificationWarningVolumeAlertMode) +{ + double INITIAL_VOLUME = 0.0; + + QSignalSpy notificationsSpy(¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + ASSERT_NO_THROW(startAccountsService()); + ASSERT_NO_THROW(startPulsePhone()); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // activate the headphones + EXPECT_TRUE(activateHeadphones(true)); + + // set an initial volume to the alert role + setStreamRestoreVolume("alert", 1.0); + EXPECT_TRUE(waitVolumeChangedInIndicator()); + + // change volume to 0.0... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 5); + + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(2)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(3)); + checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(4)); + notificationsSpy.clear(); + + // change volume to 0.5... no warning should be emitted + setActionValue("volume", QVariant::fromValue(0.5)); + + WAIT_FOR_SIGNALS(notificationsSpy, 2); + + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(1)); + notificationsSpy.clear(); + + // change volume to 1.0... no warning should be emitted, we are in alert mode + setActionValue("volume", QVariant::fromValue(1.0)); + + WAIT_FOR_SIGNALS(notificationsSpy, 2); + + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0)); + checkVolumeNotification(1.0, "Headphones", false, notificationsSpy.at(1)); + notificationsSpy.clear(); +} + +TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerWiredLabels) +{ + checkPortDevicesLabels(WIRED, WIRED); +} + +TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerBluetoothLabels) +{ + checkPortDevicesLabels(BLUETOOTH, BLUETOOTH); +} + +TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerUSBLabels) +{ + checkPortDevicesLabels(USB, USB); +} + +TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerHDMILabels) +{ + checkPortDevicesLabels(HDMI, HDMI); +} + +} // namespace diff --git a/tests/integration/test-sound.wav b/tests/integration/test-sound.wav new file mode 100644 index 0000000..f696657 Binary files /dev/null and b/tests/integration/test-sound.wav differ diff --git a/tests/integration/touch-stream-restore.table b/tests/integration/touch-stream-restore.table new file mode 100755 index 0000000..146b02e --- /dev/null +++ b/tests/integration/touch-stream-restore.table @@ -0,0 +1,4 @@ +sink-input-by-media-role:multimedia -8 +sink-input-by-media-role:alert -8 +sink-input-by-media-role:alarm -8 +sink-input-by-media-role:phone -15 diff --git a/tests/integration/utils/dbus-pulse-volume.cpp b/tests/integration/utils/dbus-pulse-volume.cpp new file mode 100644 index 0000000..c8b6ae6 --- /dev/null +++ b/tests/integration/utils/dbus-pulse-volume.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ + +#include "dbus-pulse-volume.h" + +#include "dbus_properties_interface.h" +#include "dbus_accounts_interface.h" +#include "dbus_accountssound_interface.h" +#include "stream_restore_interface.h" + +#include + +#include + +unsigned int volumeDoubleToUint(double volume) +{ + double tmp = (double)(PA_VOLUME_NORM - PA_VOLUME_MUTED) * volume; + return (unsigned int)tmp + PA_VOLUME_MUTED; +} + +double volumeUIntToDoulbe(uint volume) +{ + double tmp = (double)(volume - PA_VOLUME_MUTED); + return tmp / (double)(PA_VOLUME_NORM - PA_VOLUME_MUTED); +} + +DBusPulseVolume::DBusPulseVolume() : + QObject() +{ + std::unique_ptr basicConnectionInterface(new DBusPropertiesInterface("org.PulseAudio1", + "/org/pulseaudio/server_lookup1", + QDBusConnection::sessionBus(), 0)); + + QDBusReply connection_string = basicConnectionInterface->call(QLatin1String("Get"), + QLatin1String("org.PulseAudio.ServerLookup1"), + QLatin1String("Address")); + + if (!connection_string.isValid()) + { + qWarning() << "DBusPulseVolume::DBusPulseVolume(): D-Bus error: " << connection_string.error().message(); + } + + connection_.reset(new QDBusConnection(QDBusConnection::connectToPeer(connection_string.value().toString(), "set-volume"))); + + if (connection_->isConnected()) + { + interface_paths_.reset(new StreamRestoreInterface("org.PulseAudio.Ext.StreamRestore1", + "/org/pulseaudio/stream_restore1", + *(connection_.get()), 0)); + if (interface_paths_) + { + // get the role paths + fillRolePath("multimedia"); + fillRolePath("alert"); + fillRolePath("alarm"); + fillRolePath("phone"); + } + + initializeAccountsInterface(); + } + else + { + qWarning() << "DBusPulseVolume::DBusPulseVolume(): Error connecting: " << connection_->lastError().message(); + } +} + +DBusPulseVolume::~DBusPulseVolume() +{ +} + +QString DBusPulseVolume::fillRolePath(QString const &role) +{ + QString role_complete_name = QString("sink-input-by-media-role:") + role; + // get the role paths + QDBusReply objectPath = interface_paths_->call(QLatin1String("GetEntryByName"), role_complete_name); + if (!objectPath.isValid()) + { + qWarning() << "SetVolume::fillRolePath(): D-Bus error: " << objectPath.error().message(); + return ""; + } + + auto role_info = std::make_shared(); + role_info->interface_.reset(new DBusPropertiesInterface("org.PulseAudio.Ext.StreamRestore1.RestoreEntry", + objectPath.value().path(), + *(connection_.get()), 0)); + if (!role_info->interface_) + { + qWarning() << "SetVolume::fillRolePath() - Error obtaining properties interface"; + return ""; + } + role_info->path_ = objectPath.value().path(); + roles_map_[role] = role_info; + return role_info->path_; +} + +bool DBusPulseVolume::setVolume(QString const & role, double volume) +{ + if (!interface_paths_) + { + qWarning() << "SetVolume::setVolume(): error: Volume interfaces are not initialized"; + return false; + } + + RolesMap::const_iterator iter = roles_map_.find(role); + if (iter != roles_map_.end()) + { + QVariant var; + PulseaudioVolumeArray t; + PulseaudioVolume vv(0, volumeDoubleToUint(volume)); + t.addItem(vv); + var.setValue(t); + QDBusVariant dbusVar(var); + QDBusReply set_vol = (*iter).second->interface_->call(QLatin1String("Set"), + QVariant::fromValue(QString("org.PulseAudio.Ext.StreamRestore1.RestoreEntry")), + QVariant::fromValue(QString("Volume")), + QVariant::fromValue(dbusVar)); + + if (!set_vol.isValid()) + { + qWarning() << "SetVolume::setVolume(): D-Bus error: " << set_vol.error().message(); + return false; + } + + if (accounts_interface_) + { + QDBusVariant dbusVar(QVariant::fromValue(volume)); + QDBusReply set_vol = accounts_interface_->call(QLatin1String("Set"), + QVariant::fromValue(QString("com.ubuntu.AccountsService.Sound")), + QVariant::fromValue(QString("Volume")), + QVariant::fromValue(dbusVar)); + if (!set_vol.isValid()) + { + qWarning() << "SetVolume::setVolume(): D-Bus error: " << set_vol.error().message(); + return false; + } + } + } + else + { + qWarning() << "SetVolume::setVolume(): role " << role << " was not found."; + return false; + } + return true; +} + +double DBusPulseVolume::getVolume(QString const & role) +{ + if (interface_paths_) + { + RolesMap::const_iterator iter = roles_map_.find(role); + if (iter != roles_map_.end()) + { + QDBusReply prev_vol = (*iter).second->interface_->call(QLatin1String("Get"), + QLatin1String("org.PulseAudio.Ext.StreamRestore1.RestoreEntry"), + QLatin1String("Volume")); + + if (!prev_vol.isValid()) + { + qWarning() << "SetVolume::setVolume(): D-Bus error: " << prev_vol.error().message(); + } + QDBusArgument arg = prev_vol.value().value(); + PulseaudioVolumeArray element; + arg >> element; + return volumeUIntToDoulbe(element.getItem(0).getVolume()); + } + } + return -1.0; +} + +void DBusPulseVolume::initializeAccountsInterface() +{ + auto username = qgetenv("USER"); + if (username != "") + { + qDebug() << "Setting Accounts interface for user: " << username; + std::unique_ptr setInterface(new AccountsInterface("org.freedesktop.Accounts", + "/org/freedesktop/Accounts", + QDBusConnection::systemBus(), 0)); + + QDBusReply userResp = setInterface->call(QLatin1String("FindUserByName"), + QLatin1String(username)); + + if (!userResp.isValid()) + { + qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << userResp.error().message(); + } + auto userPath = userResp.value().path(); + if (userPath != "") + { + std::unique_ptr soundInterface(new AccountsSoundInterface("org.freedesktop.Accounts", + userPath, + QDBusConnection::systemBus(), 0)); + + accounts_interface_.reset(new DBusPropertiesInterface("org.freedesktop.Accounts", + userPath, + soundInterface->connection(), 0)); + if (!accounts_interface_->isValid()) + { + qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << accounts_interface_->lastError().message(); + } + signal_spy_volume_changed_.reset(new QSignalSpy(accounts_interface_.get(),&DBusPropertiesInterface::PropertiesChanged)); + } + } +} + +bool DBusPulseVolume::waitForVolumeChangedInAccountsService() +{ + if (signal_spy_volume_changed_) + { + return signal_spy_volume_changed_->wait(); + } + else + { + qWarning() << "DBusPulseVolume::waitForVolumeChangedInAccountsService(): signal was not instantiated"; + } + return false; +} diff --git a/tests/integration/utils/dbus-pulse-volume.h b/tests/integration/utils/dbus-pulse-volume.h new file mode 100644 index 0000000..2d55e89 --- /dev/null +++ b/tests/integration/utils/dbus-pulse-volume.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#pragma once + +#include +#include + +#include +#include + +class StreamRestoreInterface; +class DBusPropertiesInterface; +class AccountsInterface; +class AccountsSoundInterface; +class QSignalSpy; + +struct RoleInformation +{ + std::unique_ptr interface_; + QString path_; +}; + +class DBusPulseVolume : public QObject +{ +public: + DBusPulseVolume(); + ~DBusPulseVolume(); + + bool setVolume(QString const & role, double volume); + double getVolume(QString const & role); + bool waitForVolumeChangedInAccountsService(); + +protected: + QString fillRolePath(QString const &role); + void initializeAccountsInterface(); + std::unique_ptr connection_; + std::unique_ptr interface_paths_; + typedef std::map> RolesMap; + RolesMap roles_map_; + std::unique_ptr accounts_interface_; + std::unique_ptr signal_spy_volume_changed_; +}; diff --git a/tests/integration/utils/get-volume.cpp b/tests/integration/utils/get-volume.cpp new file mode 100644 index 0000000..309da36 --- /dev/null +++ b/tests/integration/utils/get-volume.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ + +#include "dbus-types.h" + +#include +#include "dbus-pulse-volume.h" + +int main(int argc, char **argv) +{ + DBusTypes::registerMetaTypes(); + if (argc == 2) + { + DBusPulseVolume volume; + volume.getVolume(argv[1]); + } + return 0; +} diff --git a/tests/integration/utils/set-volume.cpp b/tests/integration/utils/set-volume.cpp new file mode 100644 index 0000000..d29e5ef --- /dev/null +++ b/tests/integration/utils/set-volume.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ + +#include "dbus-types.h" + +#include +#include "dbus-pulse-volume.h" + +int main(int argc, char **argv) +{ + DBusTypes::registerMetaTypes(); + if (argc == 3) + { + DBusPulseVolume volume; + if(!volume.setVolume(argv[1], std::stod(argv[2]))) + { + return 1; + } + } + return 0; +} diff --git a/tests/notifications-test.cc b/tests/notifications-test.cc index 8d5617d..c5d9770 100644 --- a/tests/notifications-test.cc +++ b/tests/notifications-test.cc @@ -345,7 +345,7 @@ TEST_F(NotificationsTest, HighVolume) { auto notev = notifications->getNotifications(); ASSERT_EQ(1, notev.size()); EXPECT_EQ("Volume", notev[0].summary); - EXPECT_EQ("", notev[0].body); + EXPECT_EQ("Speakers", notev[0].body); EXPECT_GVARIANT_EQ("@s 'false'", notev[0].hints["x-canonical-value-bar-tint"]); /* Set high volume with volume change */ @@ -356,7 +356,7 @@ TEST_F(NotificationsTest, HighVolume) { notev = notifications->getNotifications(); ASSERT_LT(0, notev.size()); /* This passes with one or two since it would just be an update to the first if a second was sent */ EXPECT_EQ("Volume", notev[0].summary); - EXPECT_EQ("High volume can damage your hearing.", notev[0].body); + EXPECT_EQ("Speakers", notev[0].body); EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]); /* Move it back */ @@ -372,7 +372,7 @@ TEST_F(NotificationsTest, HighVolume) { notev = notifications->getNotifications(); ASSERT_EQ(1, notev.size()); EXPECT_EQ("Volume", notev[0].summary); - EXPECT_EQ("High volume can damage your hearing.", notev[0].body); + EXPECT_EQ("Speakers", notev[0].body); EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]); } diff --git a/tests/service-mocks/CMakeLists.txt b/tests/service-mocks/CMakeLists.txt new file mode 100644 index 0000000..9cd8acb --- /dev/null +++ b/tests/service-mocks/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(accounts-mock) +add_subdirectory(media-player-mpris-mock) diff --git a/tests/service-mocks/DBusPropertiesNotifier.cpp b/tests/service-mocks/DBusPropertiesNotifier.cpp new file mode 100644 index 0000000..686e4e9 --- /dev/null +++ b/tests/service-mocks/DBusPropertiesNotifier.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ + +#include "DBusPropertiesNotifier.h" + +#include + +using namespace ubuntu::indicators::testing; + +void DBusPropertiesNotifier::notifyPropertyChanged(QDBusConnection const & connection, + QString const & interface, + QString const & path, + QString const & propertyName, + QVariant const & propertyValue) +{ + QDBusMessage signal = QDBusMessage::createSignal( + path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged"); + signal << interface; + QVariantMap changedProps; + changedProps.insert(propertyName, propertyValue); + signal << changedProps; + signal << QStringList(); + connection.send(signal); +} diff --git a/tests/service-mocks/DBusPropertiesNotifier.h b/tests/service-mocks/DBusPropertiesNotifier.h new file mode 100644 index 0000000..9fa013b --- /dev/null +++ b/tests/service-mocks/DBusPropertiesNotifier.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#pragma once + +#include + +namespace ubuntu +{ + +namespace indicators +{ + +namespace testing +{ + +class DBusPropertiesNotifier +{ +public: + DBusPropertiesNotifier() = default; + ~DBusPropertiesNotifier() = default; + + void notifyPropertyChanged(QDBusConnection const & connection, + QString const & interface, + QString const & path, + QString const & propertyName, + QVariant const & propertyValue); +}; + +} // namespace testing + +} // namespace indicators + +} // namespace ubuntu diff --git a/tests/service-mocks/accounts-mock/AccountsDefs.h b/tests/service-mocks/accounts-mock/AccountsDefs.h new file mode 100644 index 0000000..0e4f270 --- /dev/null +++ b/tests/service-mocks/accounts-mock/AccountsDefs.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#pragma once + +namespace ubuntu +{ + +namespace indicators +{ + +namespace testing +{ + constexpr const char ACCOUNTS_SERVICE[] = "org.freedesktop.Accounts"; + constexpr const char USER_PATH[] = "/org/freedesktop/Accounts/UserTest"; + constexpr const char ACCOUNTS_PATH[] = "/org/freedesktop/Accounts"; + constexpr const char ACCOUNTS_SOUND_INTERFACE[] = "com.ubuntu.AccountsService.Sound"; +} // namespace testing + +} // namespace indicators + +} // namespace ubuntu + diff --git a/tests/service-mocks/accounts-mock/AccountsMock.cpp b/tests/service-mocks/accounts-mock/AccountsMock.cpp new file mode 100644 index 0000000..5c92dc5 --- /dev/null +++ b/tests/service-mocks/accounts-mock/AccountsMock.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#include + +#include "AccountsMock.h" +#include "AccountsDefs.h" + +using namespace ubuntu::indicators::testing; + +AccountsMock::AccountsMock(QObject* parent) + : QObject(parent) +{ +} + +AccountsMock::~AccountsMock() = default; + +QDBusObjectPath AccountsMock::FindUserByName(QString const & username) const +{ + return QDBusObjectPath(USER_PATH); +} + +QDBusObjectPath AccountsMock::FindUserById(int64_t uid) const +{ + return QDBusObjectPath(USER_PATH); +} diff --git a/tests/service-mocks/accounts-mock/AccountsMock.h b/tests/service-mocks/accounts-mock/AccountsMock.h new file mode 100644 index 0000000..72372e0 --- /dev/null +++ b/tests/service-mocks/accounts-mock/AccountsMock.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#pragma once + +#include +#include +#include + +namespace ubuntu +{ + +namespace indicators +{ + +namespace testing +{ + +class AccountsMock : public QObject, protected QDBusContext +{ + Q_OBJECT + +public Q_SLOTS: + QDBusObjectPath FindUserByName(QString const & username) const; + QDBusObjectPath FindUserById(int64_t uid) const; + +public: + AccountsMock(QObject* parent = 0); + virtual ~AccountsMock(); +}; + +} // namespace testing + +} // namespace indicators + +} // namespace ubuntu diff --git a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp new file mode 100644 index 0000000..37de377 --- /dev/null +++ b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#include +#include +#include + +#include "AccountsServiceSoundMock.h" +#include "AccountsDefs.h" + +using namespace ubuntu::indicators::testing; + +AccountsServiceSoundMock::AccountsServiceSoundMock(QObject* parent) + : QObject(parent) + , volume_(0.0) +{ +} + +AccountsServiceSoundMock::~AccountsServiceSoundMock() = default; + +double AccountsServiceSoundMock::volume() const +{ + return volume_; +} + +void AccountsServiceSoundMock::setVolume(double volume) +{ + volume_ = volume; + notifier_.notifyPropertyChanged(QDBusConnection::systemBus(), + ACCOUNTS_SOUND_INTERFACE, + USER_PATH, + "Volume", + property("Volume")); +} diff --git a/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h new file mode 100644 index 0000000..bb3dbe8 --- /dev/null +++ b/tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#pragma once + +#include +#include + +#include "DBusPropertiesNotifier.h" + +namespace ubuntu +{ + +namespace indicators +{ + +namespace testing +{ + +class DBusPropertiesNotifier; + +class AccountsServiceSoundMock : public QObject, protected QDBusContext +{ + Q_OBJECT + Q_PROPERTY(double Volume READ volume WRITE setVolume) + +public Q_SLOTS: + double volume() const; + void setVolume(double volume); + +public: + AccountsServiceSoundMock(QObject* parent = 0); + virtual ~AccountsServiceSoundMock(); + +private: + double volume_; + DBusPropertiesNotifier notifier_; +}; + +} // namespace testing + +} // namespace indicators + +} // namespace ubuntu diff --git a/tests/service-mocks/accounts-mock/CMakeLists.txt b/tests/service-mocks/accounts-mock/CMakeLists.txt new file mode 100644 index 0000000..ad858bd --- /dev/null +++ b/tests/service-mocks/accounts-mock/CMakeLists.txt @@ -0,0 +1,42 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(Qt5DBus REQUIRED) +include_directories(${Qt5DBus_INCLUDE_DIRS} + "${CMAKE_SOURCE_DIR}/tests/service-mocks") + +add_definitions(-DQT_NO_KEYWORDS=1) + +set(dbusinterface_sound_xml "com.ubuntu.AccountsService.Sound.Mock.xml") +set_source_files_properties(${dbusinterface_sound_xml} PROPERTIES + CLASSNAME AccountsServiceSoundMockInterface) + +qt5_add_dbus_interface(interface_files ${dbusinterface_sound_xml} accountsservice_sound_interface) + +qt5_add_dbus_adaptor(adaptor_files + com.ubuntu.AccountsService.Sound.Mock.xml + AccountsServiceSoundMock.h + ubuntu::indicators::testing::AccountsServiceSoundMock + AccountsServiceSoundMockAdaptor) + +qt5_add_dbus_adaptor(adaptor_files + ${CMAKE_SOURCE_DIR}/tests/dbus-types/org.freedesktop.Accounts.xml + AccountsMock.h + ubuntu::indicators::testing::AccountsMock + AccountsMockAdaptor) + +add_executable( + accounts-service-sound + ${interface_files} + ${adaptor_files} + AccountsServiceSoundMock.cpp + AccountsMock.cpp + ${CMAKE_SOURCE_DIR}/tests/service-mocks/DBusPropertiesNotifier.cpp + main.cpp +) + +qt5_use_modules( + accounts-service-sound + Core + DBus +) diff --git a/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml b/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml new file mode 100644 index 0000000..859cd46 --- /dev/null +++ b/tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tests/service-mocks/accounts-mock/main.cpp b/tests/service-mocks/accounts-mock/main.cpp new file mode 100644 index 0000000..d6cd1d3 --- /dev/null +++ b/tests/service-mocks/accounts-mock/main.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#include +#include + +#include + +#include "AccountsDefs.h" +#include "AccountsServiceSoundMock.h" +#include "AccountsServiceSoundMockAdaptor.h" +#include "AccountsMock.h" +#include "AccountsMockAdaptor.h" + +using namespace ubuntu::indicators::testing; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QDBusConnection connection = QDBusConnection::systemBus(); + if (!connection.interface()->isServiceRegistered(ACCOUNTS_SERVICE)) + { + auto service = new AccountsServiceSoundMock(&app); + new SoundAdaptor(service); + + auto accounts_service = new AccountsMock(&app); + new AccountsAdaptor(accounts_service); + + if (!connection.registerService(ACCOUNTS_SERVICE)) + { + qFatal("Could not register AccountsService Volume service."); + } + + if (!connection.registerObject(USER_PATH, service)) + { + qFatal("Could not register AccountsService Volume object."); + } + + if (!connection.registerObject(ACCOUNTS_PATH, accounts_service)) + { + qFatal("Could not register Accounts object."); + } + } + else + { + qDebug() << "Service is already registered!."; + } + return app.exec(); +} diff --git a/tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml b/tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml new file mode 100644 index 0000000..f977e7c --- /dev/null +++ b/tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/tests/service-mocks/media-player-mpris-mock/CMakeLists.txt b/tests/service-mocks/media-player-mpris-mock/CMakeLists.txt new file mode 100644 index 0000000..b5f5861 --- /dev/null +++ b/tests/service-mocks/media-player-mpris-mock/CMakeLists.txt @@ -0,0 +1,63 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fPIC -pthread") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -fno-strict-aliasing -Wextra -fPIC -pthread") + +find_package(Qt5DBus REQUIRED) +include_directories(${Qt5DBus_INCLUDE_DIRS} + "${CMAKE_SOURCE_DIR}/tests/service-mocks") + +add_definitions(-DQT_NO_KEYWORDS=1) + +set(dbusinterface_mediaplayermpris_xml "org.mpris.MediaPlayer2.Player.xml") +set_source_files_properties(${dbusinterface_mediaplayermpris_xml} PROPERTIES + CLASSNAME MediaPlayerMprisMockInterface) + +qt5_add_dbus_interface(interface_files ${dbusinterface_mediaplayermpris_xml} MediaPlayerMprisMockInterface) + +qt5_add_dbus_adaptor(adaptor_files + org.mpris.MediaPlayer2.Player.xml + MediaPlayerMprisMock.h + ubuntu::indicators::testing::MediaPlayerMprisMock + MediaPlayerMprisMockAdaptor) + +qt5_add_dbus_adaptor(adaptor_files + org.mpris.MediaPlayer2.xml + MediaPlayerMprisMock.h + ubuntu::indicators::testing::MediaPlayerMprisMock + MediaPlayer2MockAdaptor) + +add_executable( + media-player-mpris-mock + ${adaptor_files} + MediaPlayerMprisMock.cpp + ${CMAKE_SOURCE_DIR}/tests/service-mocks/DBusPropertiesNotifier.cpp + main.cpp + testplayers +) + +add_executable( + media-player-mpris-mock-update + ${interface_files} + player-update.cpp + testplayers +) + +qt5_use_modules( + media-player-mpris-mock + Core + DBus +) + +qt5_use_modules( + media-player-mpris-mock-update + Core + DBus +) + +# test players desktop files +add_custom_command (OUTPUT testplayers + DEPENDS ${CMAKE_SOURCE_DIR}/tests/service-mocks/media-player-mpris-mock/applications + COMMAND mkdir -p ${XDG_DATA_DIRS} + COMMAND cp -r ${CMAKE_SOURCE_DIR}/tests/service-mocks/media-player-mpris-mock/applications ${XDG_DATA_DIRS}) diff --git a/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h b/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h new file mode 100644 index 0000000..4d28b38 --- /dev/null +++ b/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#pragma once + +namespace ubuntu +{ + +namespace indicators +{ + +namespace testing +{ + constexpr const char MEDIA_PLAYER_MPRIS_SERVICE[] = "org.freedesktop.Accounts"; + constexpr const char USER_PATH[] = "/org/freedesktop/Accounts/UserTest"; + constexpr const char ACCOUNTS_PATH[] = "/org/freedesktop/Accounts"; + constexpr const char ACCOUNTS_SOUND_INTERFACE[] = "com.ubuntu.AccountsService.Sound"; +} // namespace testing + +} // namespace indicators + +} // namespace ubuntu + diff --git a/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp b/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp new file mode 100644 index 0000000..25fe0b7 --- /dev/null +++ b/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#include + +#include "MediaPlayerMprisMock.h" + +using namespace ubuntu::indicators::testing; + +MediaPlayerMprisMock::MediaPlayerMprisMock(QString const &playerName, QObject* parent) + : QObject(parent) + , can_play_(true) + , can_pause_(true) + , can_gonext_(true) + , can_goprevious_(true) + , player_name_(playerName) +{ +} + +MediaPlayerMprisMock::~MediaPlayerMprisMock() = default; + +bool MediaPlayerMprisMock::canPlay() const +{ + return can_play_; +} + +void MediaPlayerMprisMock::setCanPlay(bool canPlay) +{ + can_play_ = canPlay; + notifier_.notifyPropertyChanged(QDBusConnection::sessionBus(), + "org.mpris.MediaPlayer2.Player", + "/org/mpris/MediaPlayer2", + "CanPlay", + property("CanPlay")); +} + +bool MediaPlayerMprisMock::canPause() const +{ + return can_pause_; +} + +void MediaPlayerMprisMock::setCanPause(bool canPause) +{ + can_pause_ = canPause; + notifier_.notifyPropertyChanged(QDBusConnection::sessionBus(), + "org.mpris.MediaPlayer2.Player", + "/org/mpris/MediaPlayer2", + "CanPause", + property("CanPause")); +} + +bool MediaPlayerMprisMock::canGoNext() const +{ + return can_gonext_; +} + +void MediaPlayerMprisMock::setCanGoNext(bool canGoNext) +{ + can_gonext_ = canGoNext; + notifier_.notifyPropertyChanged(QDBusConnection::sessionBus(), + "org.mpris.MediaPlayer2.Player", + "/org/mpris/MediaPlayer2", + "CanGoNext", + property("CanGoNext")); +} + +bool MediaPlayerMprisMock::canGoPrevious() const +{ + return can_goprevious_; +} + +void MediaPlayerMprisMock::setCanGoPrevious(bool canGoPrevious) +{ + can_goprevious_ = canGoPrevious; + notifier_.notifyPropertyChanged(QDBusConnection::sessionBus(), + "org.mpris.MediaPlayer2.Player", + "/org/mpris/MediaPlayer2", + "CanGoPrevious", + property("CanGoPrevious")); +} + +QString MediaPlayerMprisMock::desktopEntry() const +{ + return player_name_; +} + +void MediaPlayerMprisMock::setDesktopEntry(QString const &) +{ +} diff --git a/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h b/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h new file mode 100644 index 0000000..58dce8d --- /dev/null +++ b/tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#pragma once + +#include +#include +#include + +#include "DBusPropertiesNotifier.h" + +namespace ubuntu +{ + +namespace indicators +{ + +namespace testing +{ + +class MediaPlayerMprisMock : public QObject, protected QDBusContext +{ + Q_OBJECT + Q_PROPERTY(bool CanPlay READ canPlay WRITE setCanPlay) + Q_PROPERTY(bool CanPause READ canPause WRITE setCanPause) + Q_PROPERTY(bool CanGoNext READ canGoNext WRITE setCanGoNext) + Q_PROPERTY(bool CanGoPrevious READ canGoPrevious WRITE setCanGoPrevious) + Q_PROPERTY(QString DesktopEntry READ desktopEntry WRITE setDesktopEntry) + +public Q_SLOTS: + bool canPlay() const; + void setCanPlay(bool canPlay); + + bool canPause() const; + void setCanPause(bool canPause); + + bool canGoNext() const; + void setCanGoNext(bool canGoNext); + + bool canGoPrevious() const; + void setCanGoPrevious(bool canGoPrevious); + + QString desktopEntry() const; + void setDesktopEntry(QString const &destopEntry); + +public: + MediaPlayerMprisMock(QString const &playerName, QObject* parent = 0); + virtual ~MediaPlayerMprisMock(); + +private: + bool can_play_; + bool can_pause_; + bool can_gonext_; + bool can_goprevious_; + DBusPropertiesNotifier notifier_; + QString player_name_; +}; + +} // namespace testing + +} // namespace indicators + +} // namespace ubuntu diff --git a/tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop b/tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop new file mode 100644 index 0000000..2ed5008 --- /dev/null +++ b/tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop @@ -0,0 +1,21 @@ +[Desktop Entry] +Name=TestPlayer1 +GenericName=Test Player 1 +X-GNOME-FullName=Test Player 1 +Comment=Play and organize your music collection +Keywords=Audio;Song;MP3;CD;Podcast;MTP;iPod;Playlist;Last.fm;UPnP;DLNA;Radio; +Exec=echo %U +Terminal=false +Type=Application +Icon=testplayer +X-GNOME-DocPath=testplayer/testplayer.xml +Categories=GNOME;GTK;AudioVideo;Audio;Player; +MimeType=application/x-ogg;application/ogg;audio/x-vorbis+ogg;audio/x-scpls;audio/x-mp3;audio/x-mpeg;audio/mpeg;audio/x-mpegurl;audio/x-flac;audio/mp4;x-scheme-handler/itms;x-scheme-handler/itmss; +StartupNotify=true +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=testplayer +X-GNOME-Bugzilla-Component=general +X-GNOME-Bugzilla-OtherBinaries=rhythmbox-client;rhythmbox-metadata; +X-GNOME-Bugzilla-Version=3.1 +X-GNOME-UsesNotifications=true + diff --git a/tests/service-mocks/media-player-mpris-mock/main.cpp b/tests/service-mocks/media-player-mpris-mock/main.cpp new file mode 100644 index 0000000..8945673 --- /dev/null +++ b/tests/service-mocks/media-player-mpris-mock/main.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#include +#include + +#include + +#include "MediaPlayerMprisDefs.h" +#include "MediaPlayerMprisMock.h" +#include "MediaPlayerMprisMockAdaptor.h" +#include "MediaPlayer2MockAdaptor.h" + +using namespace ubuntu::indicators::testing; + +int main(int argc, char *argv[]) +{ + if (argc != 2) + { + qWarning() << "usage: " << argv[0] << "TEST_PLAYER_NAME"; + return 1; + } + QString playerName = QString(argv[1]); + + QCoreApplication app(argc, argv); + QString playerService = QString("org.mpris.MediaPlayer2.") + playerName; + QDBusConnection connection = QDBusConnection::sessionBus(); + if (!connection.interface()->isServiceRegistered(playerService)) + { + auto service = new MediaPlayerMprisMock(playerName, &app); + new PlayerAdaptor(service); + new MediaPlayer2Adaptor(service); + + if (!connection.registerService(playerService)) + { + qDebug() << connection.lastError(); + qFatal("Could not register MediaPlayerMprisMock service."); + } + + if (!connection.registerObject("/org/mpris/MediaPlayer2", service)) + { + qFatal("Could not register MediaPlayerMprisMock object."); + } + } + else + { + qDebug() << "Service is already registered!."; + } + return app.exec(); +} diff --git a/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml b/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml new file mode 100644 index 0000000..3efd002 --- /dev/null +++ b/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml b/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml new file mode 100644 index 0000000..489c68a --- /dev/null +++ b/tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/tests/service-mocks/media-player-mpris-mock/player-update.cpp b/tests/service-mocks/media-player-mpris-mock/player-update.cpp new file mode 100644 index 0000000..5768372 --- /dev/null +++ b/tests/service-mocks/media-player-mpris-mock/player-update.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 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: Xavi Garcia + */ +#include "MediaPlayerMprisMockInterface.h" + +#include + +QMap getPropertiesMap() +{ + QMap retMap; + + retMap["CANPLAY"] = "setCanPlay"; + retMap["CANPAUSE"] = "setCanPause"; + retMap["CANGONEXT"] = "setCanGoNext"; + retMap["CANGOPREVIOUS"] = "setCanGoPrevious"; + + return retMap; +} + +bool getBoolValue(QString const & str) +{ + if (str == "TRUE") + { + return true; + } + return false; +} + +QTextStream& qStdErr() +{ + static QTextStream ts( stderr ); + return ts; +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + if (argc != 4) + { + qStdErr() << "usage: " << argv[0] << "TEST_PLAYER_NAME PropertyName true|false\n"; + return 1; + } + + QString state = QString(argv[3]).toUpper(); + QString property = QString(argv[2]).toUpper(); + QString playerName = QString(argv[1]); + + auto propertiesMap = getPropertiesMap(); + + std::shared_ptr iface( + new MediaPlayerMprisMockInterface("org.mpris.MediaPlayer2." + playerName, + "/org/mpris/MediaPlayer2", + QDBusConnection::sessionBus())); + if (!iface) + { + qWarning() << argv[0] << ": error creating interface"; + return 1; + } + + QMap::iterator iter = propertiesMap.find(property); + if (iter == propertiesMap.end()) + { + qWarning() << argv[0] << ": property " << property << " was not found."; + return 1; + } + + QDBusReply set_prop = iface->call(QLatin1String((*iter).toStdString().c_str()), + QVariant::fromValue(getBoolValue(state))); + + if (!set_prop.isValid()) + { + qWarning() << argv[0] << ": D-Bus error: " << set_prop.error().message(); + return 1; + } + + return 0; +} + -- cgit v1.2.3 From f3015f8c24f798757b356b68a0616f20b1f1bc2b Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Wed, 23 Dec 2015 15:13:32 +0100 Subject: Changed number of upstart respawn retries --- data/indicator-sound.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/indicator-sound.conf.in b/data/indicator-sound.conf.in index 4912b61..e1618be 100644 --- a/data/indicator-sound.conf.in +++ b/data/indicator-sound.conf.in @@ -4,6 +4,6 @@ start on indicator-services-start stop on desktop-end or indicator-services-end respawn -respawn limit 2 10 +respawn limit 30 10 exec @CMAKE_INSTALL_FULL_LIBEXECDIR@/indicator-sound/indicator-sound-service -- cgit v1.2.3 From c0c4180561fba3e1ebe3f22381a4b5609e22f17b Mon Sep 17 00:00:00 2001 From: CI Train Bot Date: Wed, 23 Dec 2015 14:20:45 +0000 Subject: Releasing 12.10.2+16.04.20151223.1-0ubuntu1 --- debian/changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index 60d1d97..d1b8742 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +indicator-sound (12.10.2+16.04.20151223.1-0ubuntu1) xenial; urgency=medium + + [ Xavi Garcia Mena ] + * Very simple change to make upstart try to respawn the process 30 + times (instead of 2). This is a workaround for the case when dbus + session is not ready yet at startup. + + -- Xavi Garcia Wed, 23 Dec 2015 14:20:45 +0000 + indicator-sound (12.10.2+16.04.20151223-0ubuntu1) xenial; urgency=medium [ CI Train Bot ] -- cgit v1.2.3 From 58e4e5dd08444a193a52ae103f6bd432772dacc7 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Tue, 5 Jan 2016 12:11:02 +0100 Subject: restore debian/changelog changes --- debian/changelog | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index d7ed9bd..906ab89 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,28 @@ -indicator-sound (12.10.2+15.10.20151019-0ubuntu2) UNRELEASED; urgency=medium +indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium + [ Xavi Garcia Mena ] + * Very simple change to make upstart try to respawn the process 30 + times (instead of 2). This is a workaround for the case when dbus + session is not ready yet at startup. + + [ Timo Jyrinki ] + * Note: this release was reverted on 20151223, ignore this changelog entry. + + -- Xavi Garcia Tue, 22 Dec 2015 09:50:08 +0000 + +indicator-sound (12.10.2+16.04.20151103.2-0ubuntu1) xenial; urgency=medium + + [ Xavi Garcia ] * Added OSD notifications to show output source when the volume or source change (LP: #1382548, LP: #1415480, LP: #1484589) - -- Xavi Garcia Tue, 03 Nov 2015 12:29:19 +0100 + [ CI Train Bot ] + * New rebuild forced. + + [ Timo Jyrinki ] + * Note: this release was reverted on 20151223, ignore this changelog entry. + + -- Xavi Garcia Tue, 03 Nov 2015 17:47:10 +0000 indicator-sound (12.10.2+15.10.20151019-0ubuntu1) wily; urgency=medium -- cgit v1.2.3 From ebdf9695f9587e2738fa8a1fd7959524e56dc1ef Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Tue, 5 Jan 2016 12:20:12 +0100 Subject: removed extra line in debian/changelog --- debian/changelog | 1 - 1 file changed, 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index b47dc02..879288e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -72,7 +72,6 @@ indicator-sound (12.10.2+15.10.20151019-0ubuntu3) UNRELEASED; urgency=medium - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml - tests/service-mocks/media-player-mpris-mock/player-update.cpp - -- Xavi Garcia Wed, 11 Nov 2015 12:40:10 +0100 indicator-sound (12.10.2+15.10.20151019-0ubuntu2) UNRELEASED; urgency=medium -- cgit v1.2.3 From ea2f15b028b89f18f47fd22b2c5cb9271dab189b Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Tue, 5 Jan 2016 12:32:00 +0100 Subject: sync debian/changelog with pre-requisite branch --- debian/changelog | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/debian/changelog b/debian/changelog index 879288e..d5bf1a1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,5 @@ -indicator-sound (12.10.2+15.10.20151019-0ubuntu3) UNRELEASED; urgency=medium +indicator-sound (12.10.2+16.04.20151222-0ubuntu3) UNRELEASED; urgency=medium - [ Xavi Garcia ] - * Added OSD notifications to show output source when the volume or - source change (LP: #1382548, LP: #1415480, LP: #1484589) - * debian/control: Added the following build dependencies: - cmake-extras (>= 0.4), @@ -70,16 +66,35 @@ indicator-sound (12.10.2+15.10.20151019-0ubuntu3) UNRELEASED; urgency=medium - tests/service-mocks/media-player-mpris-mock/main.cpp - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml - - tests/service-mocks/media-player-mpris-mock/player-update.cpp + - tests/service-mocks/media-player-mpris-mock/player-update.cpp + + -- xavigarcia Tue, 05 Jan 2016 12:30:42 +0100 + +indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium + + [ Xavi Garcia Mena ] + * Very simple change to make upstart try to respawn the process 30 + times (instead of 2). This is a workaround for the case when dbus + session is not ready yet at startup. - -- Xavi Garcia Wed, 11 Nov 2015 12:40:10 +0100 + [ Timo Jyrinki ] + * Note: this release was reverted on 20151223, ignore this changelog entry. -indicator-sound (12.10.2+15.10.20151019-0ubuntu2) UNRELEASED; urgency=medium + -- Xavi Garcia Tue, 22 Dec 2015 09:50:08 +0000 +indicator-sound (12.10.2+16.04.20151103.2-0ubuntu1) xenial; urgency=medium + + [ Xavi Garcia ] * Added OSD notifications to show output source when the volume or source change (LP: #1382548, LP: #1415480, LP: #1484589) - -- Xavi Garcia Tue, 03 Nov 2015 12:29:19 +0100 + [ CI Train Bot ] + * New rebuild forced. + + [ Timo Jyrinki ] + * Note: this release was reverted on 20151223, ignore this changelog entry. + + -- Xavi Garcia Tue, 03 Nov 2015 17:47:10 +0000 indicator-sound (12.10.2+15.10.20151019-0ubuntu1) wily; urgency=medium -- cgit v1.2.3 From 928a81049f2812ec936da9dca1328e0533b8e9bc Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Tue, 5 Jan 2016 12:37:52 +0100 Subject: Rolling back to release 516 --- debian/changelog | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/debian/changelog b/debian/changelog index d5bf1a1..879288e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,9 @@ -indicator-sound (12.10.2+16.04.20151222-0ubuntu3) UNRELEASED; urgency=medium +indicator-sound (12.10.2+15.10.20151019-0ubuntu3) UNRELEASED; urgency=medium + [ Xavi Garcia ] + * Added OSD notifications to show output source when the volume or + source change (LP: #1382548, LP: #1415480, LP: #1484589) + * debian/control: Added the following build dependencies: - cmake-extras (>= 0.4), @@ -66,35 +70,16 @@ indicator-sound (12.10.2+16.04.20151222-0ubuntu3) UNRELEASED; urgency=medium - tests/service-mocks/media-player-mpris-mock/main.cpp - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml - - tests/service-mocks/media-player-mpris-mock/player-update.cpp - - -- xavigarcia Tue, 05 Jan 2016 12:30:42 +0100 - -indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium - - [ Xavi Garcia Mena ] - * Very simple change to make upstart try to respawn the process 30 - times (instead of 2). This is a workaround for the case when dbus - session is not ready yet at startup. + - tests/service-mocks/media-player-mpris-mock/player-update.cpp - [ Timo Jyrinki ] - * Note: this release was reverted on 20151223, ignore this changelog entry. + -- Xavi Garcia Wed, 11 Nov 2015 12:40:10 +0100 - -- Xavi Garcia Tue, 22 Dec 2015 09:50:08 +0000 +indicator-sound (12.10.2+15.10.20151019-0ubuntu2) UNRELEASED; urgency=medium -indicator-sound (12.10.2+16.04.20151103.2-0ubuntu1) xenial; urgency=medium - - [ Xavi Garcia ] * Added OSD notifications to show output source when the volume or source change (LP: #1382548, LP: #1415480, LP: #1484589) - [ CI Train Bot ] - * New rebuild forced. - - [ Timo Jyrinki ] - * Note: this release was reverted on 20151223, ignore this changelog entry. - - -- Xavi Garcia Tue, 03 Nov 2015 17:47:10 +0000 + -- Xavi Garcia Tue, 03 Nov 2015 12:29:19 +0100 indicator-sound (12.10.2+15.10.20151019-0ubuntu1) wily; urgency=medium -- cgit v1.2.3 From 4cda9fac2f441e47fab07bce827202390ef42db7 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Tue, 5 Jan 2016 12:44:00 +0100 Subject: sync changelog with previous branch --- debian/changelog | 95 ++++++++++++-------------------------------------------- 1 file changed, 19 insertions(+), 76 deletions(-) diff --git a/debian/changelog b/debian/changelog index 879288e..906ab89 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,85 +1,28 @@ -indicator-sound (12.10.2+15.10.20151019-0ubuntu3) UNRELEASED; urgency=medium +indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium + + [ Xavi Garcia Mena ] + * Very simple change to make upstart try to respawn the process 30 + times (instead of 2). This is a workaround for the case when dbus + session is not ready yet at startup. + + [ Timo Jyrinki ] + * Note: this release was reverted on 20151223, ignore this changelog entry. + + -- Xavi Garcia Tue, 22 Dec 2015 09:50:08 +0000 + +indicator-sound (12.10.2+16.04.20151103.2-0ubuntu1) xenial; urgency=medium [ Xavi Garcia ] * Added OSD notifications to show output source when the volume or source change (LP: #1382548, LP: #1415480, LP: #1484589) - - * debian/control: - Added the following build dependencies: - - cmake-extras (>= 0.4), - - google-mock (>= 1.6.0+svn437), - - libqtdbusmock1-dev (>= 0.3), - - libqtdbustest1-dev, - - libunity-api-dev, - - pulseaudio, - - qt5-default, - - qtbase5-dev, - - qtbase5-dev-tools, - - qtdeclarative5-dev, - - qtdeclarative5-dev-tools - * Added gmenuharness library for integration tests support. - New Files: - - src/gmenuharness/CMakeLists.txt - - src/gmenuharness/MatchResult.cpp - - src/gmenuharness/MatchUtils.cpp - - src/gmenuharness/MenuItemMatcher.cpp - - src/gmenuharness/MenuMatcher.cpp - - include/unity/gmenuharness/MatchResult.h - - include/unity/gmenuharness/MatchUtils.h - - include/unity/gmenuharness/MenuItemMatcher.h - - include/unity/gmenuharness/MenuMatcher.h - * Added integration tests using gmenuharness - New Files: - - tests/dbus-types/CMakeLists.txt - - tests/dbus-types/com.ubuntu.AccountsService.Sound.xml - - tests/dbus-types/dbus-types.h - - tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml - - tests/dbus-types/org.freedesktop.Accounts.xml - - tests/dbus-types/org.freedesktop.DBus.Properties.xml - - tests/dbus-types/org.freedesktop.Notifications.xml - - tests/dbus-types/org.gtk.Actions.xml - - tests/dbus-types/pulseaudio-volume.cpp - - tests/dbus-types/pulseaudio-volume.h - - tests/integration/CMakeLists.txt - - tests/integration/indicator-sound-test-base.cpp - - tests/integration/indicator-sound-test-base.h - - tests/integration/main.cpp - - tests/integration/test-indicator.cpp - - tests/integration/touch-stream-restore.table - - tests/integration/utils/dbus-pulse-volume.cpp - - tests/integration/utils/dbus-pulse-volume.h - - tests/integration/utils/get-volume.cpp - - tests/integration/utils/set-volume.cpp - - tests/service-mocks/CMakeLists.txt - - tests/service-mocks/DBusPropertiesNotifier.cpp - - tests/service-mocks/DBusPropertiesNotifier.h - - tests/service-mocks/accounts-mock/AccountsDefs.h - - tests/service-mocks/accounts-mock/AccountsMock.cpp - - tests/service-mocks/accounts-mock/AccountsMock.h - - tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp - - tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h - - tests/service-mocks/accounts-mock/CMakeLists.txt - - tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml - - tests/service-mocks/accounts-mock/main.cpp - - tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml - - tests/service-mocks/media-player-mpris-mock/CMakeLists.txt - - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h - - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp - - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h - - tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop - - tests/service-mocks/media-player-mpris-mock/main.cpp - - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml - - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml - - tests/service-mocks/media-player-mpris-mock/player-update.cpp - - -- Xavi Garcia Wed, 11 Nov 2015 12:40:10 +0100 - -indicator-sound (12.10.2+15.10.20151019-0ubuntu2) UNRELEASED; urgency=medium - * Added OSD notifications to show output source when the volume or - source change (LP: #1382548, LP: #1415480, LP: #1484589) + [ CI Train Bot ] + * New rebuild forced. + + [ Timo Jyrinki ] + * Note: this release was reverted on 20151223, ignore this changelog entry. - -- Xavi Garcia Tue, 03 Nov 2015 12:29:19 +0100 + -- Xavi Garcia Tue, 03 Nov 2015 17:47:10 +0000 indicator-sound (12.10.2+15.10.20151019-0ubuntu1) wily; urgency=medium -- cgit v1.2.3 From a2f1d71ad198fbcf15536cb1ef1c2dadef521c2f Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Tue, 5 Jan 2016 12:48:00 +0100 Subject: re-added new stuff --- debian/changelog | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/debian/changelog b/debian/changelog index 906ab89..39df7a0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,79 @@ +indicator-sound (12.10.2+16.04.20151222-0ubuntu2) UNRELEASED; urgency=medium + + * [ Xavi Garcia ] + * Added OSD notifications to show output source when the volume or + source change (LP: #1382548, LP: #1415480, LP: #1484589) + + * debian/control: + Added the following build dependencies: + - cmake-extras (>= 0.4), + - google-mock (>= 1.6.0+svn437), + - libqtdbusmock1-dev (>= 0.3), + - libqtdbustest1-dev, + - libunity-api-dev, + - pulseaudio, + - qt5-default, + - qtbase5-dev, + - qtbase5-dev-tools, + - qtdeclarative5-dev, + - qtdeclarative5-dev-tools + * Added gmenuharness library for integration tests support. + New Files: + - src/gmenuharness/CMakeLists.txt + - src/gmenuharness/MatchResult.cpp + - src/gmenuharness/MatchUtils.cpp + - src/gmenuharness/MenuItemMatcher.cpp + - src/gmenuharness/MenuMatcher.cpp + - include/unity/gmenuharness/MatchResult.h + - include/unity/gmenuharness/MatchUtils.h + - include/unity/gmenuharness/MenuItemMatcher.h + - include/unity/gmenuharness/MenuMatcher.h + * Added integration tests using gmenuharness + New Files: + - tests/dbus-types/CMakeLists.txt + - tests/dbus-types/com.ubuntu.AccountsService.Sound.xml + - tests/dbus-types/dbus-types.h + - tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml + - tests/dbus-types/org.freedesktop.Accounts.xml + - tests/dbus-types/org.freedesktop.DBus.Properties.xml + - tests/dbus-types/org.freedesktop.Notifications.xml + - tests/dbus-types/org.gtk.Actions.xml + - tests/dbus-types/pulseaudio-volume.cpp + - tests/dbus-types/pulseaudio-volume.h + - tests/integration/CMakeLists.txt + - tests/integration/indicator-sound-test-base.cpp + - tests/integration/indicator-sound-test-base.h + - tests/integration/main.cpp + - tests/integration/test-indicator.cpp + - tests/integration/touch-stream-restore.table + - tests/integration/utils/dbus-pulse-volume.cpp + - tests/integration/utils/dbus-pulse-volume.h + - tests/integration/utils/get-volume.cpp + - tests/integration/utils/set-volume.cpp + - tests/service-mocks/CMakeLists.txt + - tests/service-mocks/DBusPropertiesNotifier.cpp + - tests/service-mocks/DBusPropertiesNotifier.h + - tests/service-mocks/accounts-mock/AccountsDefs.h + - tests/service-mocks/accounts-mock/AccountsMock.cpp + - tests/service-mocks/accounts-mock/AccountsMock.h + - tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp + - tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h + - tests/service-mocks/accounts-mock/CMakeLists.txt + - tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml + - tests/service-mocks/accounts-mock/main.cpp + - tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml + - tests/service-mocks/media-player-mpris-mock/CMakeLists.txt + - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h + - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp + - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h + - tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop + - tests/service-mocks/media-player-mpris-mock/main.cpp + - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml + - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml + - tests/service-mocks/media-player-mpris-mock/player-update.cpp + + -- xavigarcia Tue, 05 Jan 2016 12:46:07 +0100 + indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium [ Xavi Garcia Mena ] -- cgit v1.2.3 From 28ed83b92722529eaf4176bd67d0a4188a6c18c6 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Tue, 5 Jan 2016 12:50:24 +0100 Subject: remove changed in debian/changelog --- debian/changelog | 76 -------------------------------------------------------- 1 file changed, 76 deletions(-) diff --git a/debian/changelog b/debian/changelog index 39df7a0..906ab89 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,79 +1,3 @@ -indicator-sound (12.10.2+16.04.20151222-0ubuntu2) UNRELEASED; urgency=medium - - * [ Xavi Garcia ] - * Added OSD notifications to show output source when the volume or - source change (LP: #1382548, LP: #1415480, LP: #1484589) - - * debian/control: - Added the following build dependencies: - - cmake-extras (>= 0.4), - - google-mock (>= 1.6.0+svn437), - - libqtdbusmock1-dev (>= 0.3), - - libqtdbustest1-dev, - - libunity-api-dev, - - pulseaudio, - - qt5-default, - - qtbase5-dev, - - qtbase5-dev-tools, - - qtdeclarative5-dev, - - qtdeclarative5-dev-tools - * Added gmenuharness library for integration tests support. - New Files: - - src/gmenuharness/CMakeLists.txt - - src/gmenuharness/MatchResult.cpp - - src/gmenuharness/MatchUtils.cpp - - src/gmenuharness/MenuItemMatcher.cpp - - src/gmenuharness/MenuMatcher.cpp - - include/unity/gmenuharness/MatchResult.h - - include/unity/gmenuharness/MatchUtils.h - - include/unity/gmenuharness/MenuItemMatcher.h - - include/unity/gmenuharness/MenuMatcher.h - * Added integration tests using gmenuharness - New Files: - - tests/dbus-types/CMakeLists.txt - - tests/dbus-types/com.ubuntu.AccountsService.Sound.xml - - tests/dbus-types/dbus-types.h - - tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml - - tests/dbus-types/org.freedesktop.Accounts.xml - - tests/dbus-types/org.freedesktop.DBus.Properties.xml - - tests/dbus-types/org.freedesktop.Notifications.xml - - tests/dbus-types/org.gtk.Actions.xml - - tests/dbus-types/pulseaudio-volume.cpp - - tests/dbus-types/pulseaudio-volume.h - - tests/integration/CMakeLists.txt - - tests/integration/indicator-sound-test-base.cpp - - tests/integration/indicator-sound-test-base.h - - tests/integration/main.cpp - - tests/integration/test-indicator.cpp - - tests/integration/touch-stream-restore.table - - tests/integration/utils/dbus-pulse-volume.cpp - - tests/integration/utils/dbus-pulse-volume.h - - tests/integration/utils/get-volume.cpp - - tests/integration/utils/set-volume.cpp - - tests/service-mocks/CMakeLists.txt - - tests/service-mocks/DBusPropertiesNotifier.cpp - - tests/service-mocks/DBusPropertiesNotifier.h - - tests/service-mocks/accounts-mock/AccountsDefs.h - - tests/service-mocks/accounts-mock/AccountsMock.cpp - - tests/service-mocks/accounts-mock/AccountsMock.h - - tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp - - tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h - - tests/service-mocks/accounts-mock/CMakeLists.txt - - tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml - - tests/service-mocks/accounts-mock/main.cpp - - tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml - - tests/service-mocks/media-player-mpris-mock/CMakeLists.txt - - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h - - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp - - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h - - tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop - - tests/service-mocks/media-player-mpris-mock/main.cpp - - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml - - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml - - tests/service-mocks/media-player-mpris-mock/player-update.cpp - - -- xavigarcia Tue, 05 Jan 2016 12:46:07 +0100 - indicator-sound (12.10.2+16.04.20151222-0ubuntu1) xenial; urgency=medium [ Xavi Garcia Mena ] -- cgit v1.2.3 From 0eea8e8a2cee47f72292384ac856d3f68208d680 Mon Sep 17 00:00:00 2001 From: Xavi Garcia Mena Date: Tue, 5 Jan 2016 14:47:46 +0100 Subject: [ Xavi Garcia ] * Added OSD notifications to show output source when the volume or source change (LP: #1382548, LP: #1415480, LP: #1484589) * debian/control: Added the following build dependencies: - cmake-extras (>= 0.4), - google-mock (>= 1.6.0+svn437), - libqtdbusmock1-dev (>= 0.3), - libqtdbustest1-dev, - libunity-api-dev, - pulseaudio, - qt5-default, - qtbase5-dev, - qtbase5-dev-tools, - qtdeclarative5-dev, - qtdeclarative5-dev-tools * Added gmenuharness library for integration tests support. New Files: - src/gmenuharness/CMakeLists.txt - src/gmenuharness/MatchResult.cpp - src/gmenuharness/MatchUtils.cpp - src/gmenuharness/MenuItemMatcher.cpp - src/gmenuharness/MenuMatcher.cpp - include/unity/gmenuharness/MatchResult.h - include/unity/gmenuharness/MatchUtils.h - include/unity/gmenuharness/MenuItemMatcher.h - include/unity/gmenuharness/MenuMatcher.h * Added integration tests using gmenuharness New Files: - tests/dbus-types/CMakeLists.txt - tests/dbus-types/com.ubuntu.AccountsService.Sound.xml - tests/dbus-types/dbus-types.h - tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml - tests/dbus-types/org.freedesktop.Accounts.xml - tests/dbus-types/org.freedesktop.DBus.Properties.xml - tests/dbus-types/org.freedesktop.Notifications.xml - tests/dbus-types/org.gtk.Actions.xml - tests/dbus-types/pulseaudio-volume.cpp - tests/dbus-types/pulseaudio-volume.h - tests/integration/CMakeLists.txt - tests/integration/indicator-sound-test-base.cpp - tests/integration/indicator-sound-test-base.h - tests/integration/main.cpp - tests/integration/test-indicator.cpp - tests/integration/touch-stream-restore.table - tests/integration/utils/dbus-pulse-volume.cpp - tests/integration/utils/dbus-pulse-volume.h - tests/integration/utils/get-volume.cpp - tests/integration/utils/set-volume.cpp - tests/service-mocks/CMakeLists.txt - tests/service-mocks/DBusPropertiesNotifier.cpp - tests/service-mocks/DBusPropertiesNotifier.h - tests/service-mocks/accounts-mock/AccountsDefs.h - tests/service-mocks/accounts-mock/AccountsMock.cpp - tests/service-mocks/accounts-mock/AccountsMock.h - tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp - tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h - tests/service-mocks/accounts-mock/CMakeLists.txt - tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml - tests/service-mocks/accounts-mock/main.cpp - tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml - tests/service-mocks/media-player-mpris-mock/CMakeLists.txt - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp - tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h - tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop - tests/service-mocks/media-player-mpris-mock/main.cpp - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml - tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml - tests/service-mocks/media-player-mpris-mock/player-update.cpp --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index be4e079..222ffd6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,4 @@ Charles Kerr Ted Gould Cody Russell +Xavi Garcia -- cgit v1.2.3