Merge lp:~xavi-garcia-mena/indicator-sound/restore-osd-notifications into lp:indicator-sound/15.10

Proposed by Xavi Garcia
Status: Merged
Approved by: Xavi Garcia
Approved revision: 514
Merged at revision: 518
Proposed branch: lp:~xavi-garcia-mena/indicator-sound/restore-osd-notifications
Merge into: lp:indicator-sound/15.10
Diff against target: 7143 lines (+6337/-185)
59 files modified
include/CMakeLists.txt (+1/-0)
include/unity/CMakeLists.txt (+1/-0)
include/unity/gmenuharness/MatchResult.h (+66/-0)
include/unity/gmenuharness/MatchUtils.h (+42/-0)
include/unity/gmenuharness/MenuItemMatcher.h (+143/-0)
include/unity/gmenuharness/MenuMatcher.h (+95/-0)
src/CMakeLists.txt (+12/-9)
src/gmenuharness/CMakeLists.txt (+17/-0)
src/gmenuharness/MatchResult.cpp (+187/-0)
src/gmenuharness/MatchUtils.cpp (+77/-0)
src/gmenuharness/MenuItemMatcher.cpp (+1008/-0)
src/gmenuharness/MenuMatcher.cpp (+208/-0)
src/service.vala (+355/-52)
src/sound-menu.vala (+38/-0)
src/volume-control-pulse.vala (+69/-14)
src/volume-control.vala (+14/-0)
tests/CMakeLists.txt (+114/-107)
tests/dbus-types/CMakeLists.txt (+53/-0)
tests/dbus-types/com.ubuntu.AccountsService.Sound.xml (+9/-0)
tests/dbus-types/dbus-types.h (+48/-0)
tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml (+7/-0)
tests/dbus-types/org.freedesktop.Accounts.xml (+9/-0)
tests/dbus-types/org.freedesktop.DBus.Properties.xml (+22/-0)
tests/dbus-types/org.freedesktop.Notifications.xml (+47/-0)
tests/dbus-types/org.gtk.Actions.xml (+13/-0)
tests/dbus-types/pulseaudio-volume.cpp (+156/-0)
tests/dbus-types/pulseaudio-volume.h (+69/-0)
tests/integration/CMakeLists.txt (+132/-0)
tests/integration/indicator-sound-test-base.cpp (+823/-0)
tests/integration/indicator-sound-test-base.h (+160/-0)
tests/integration/main.cpp (+58/-0)
tests/integration/test-indicator.cpp (+981/-0)
tests/integration/touch-stream-restore.table (+4/-0)
tests/integration/utils/dbus-pulse-volume.cpp (+232/-0)
tests/integration/utils/dbus-pulse-volume.h (+57/-0)
tests/integration/utils/get-volume.cpp (+33/-0)
tests/integration/utils/set-volume.cpp (+36/-0)
tests/notifications-test.cc (+3/-3)
tests/service-mocks/CMakeLists.txt (+2/-0)
tests/service-mocks/DBusPropertiesNotifier.cpp (+41/-0)
tests/service-mocks/DBusPropertiesNotifier.h (+48/-0)
tests/service-mocks/accounts-mock/AccountsDefs.h (+37/-0)
tests/service-mocks/accounts-mock/AccountsMock.cpp (+40/-0)
tests/service-mocks/accounts-mock/AccountsMock.h (+50/-0)
tests/service-mocks/accounts-mock/AccountsServiceSoundMock.cpp (+48/-0)
tests/service-mocks/accounts-mock/AccountsServiceSoundMock.h (+58/-0)
tests/service-mocks/accounts-mock/CMakeLists.txt (+42/-0)
tests/service-mocks/accounts-mock/com.ubuntu.AccountsService.Sound.Mock.xml (+6/-0)
tests/service-mocks/accounts-mock/main.cpp (+63/-0)
tests/service-mocks/accounts-mock/org.freedesktop.Accounts.Mock.xml (+15/-0)
tests/service-mocks/media-player-mpris-mock/CMakeLists.txt (+63/-0)
tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisDefs.h (+37/-0)
tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.cpp (+103/-0)
tests/service-mocks/media-player-mpris-mock/MediaPlayerMprisMock.h (+77/-0)
tests/service-mocks/media-player-mpris-mock/applications/testplayer1.desktop (+21/-0)
tests/service-mocks/media-player-mpris-mock/main.cpp (+64/-0)
tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.Player.xml (+24/-0)
tests/service-mocks/media-player-mpris-mock/org.mpris.MediaPlayer2.xml (+6/-0)
tests/service-mocks/media-player-mpris-mock/player-update.cpp (+93/-0)
To merge this branch: bzr merge lp:~xavi-garcia-mena/indicator-sound/restore-osd-notifications
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Xavi Garcia Approve
Review via email: mp+281290@code.launchpad.net

Commit message

This branch just readds the OSD notifications code, that was reverted in trunk as the corresponding silo was also rolled back after landing.

Description of the change

This branch just readds the OSD notifications code, that was reverted in trunk as the corresponding silo was also rolled back after landing.

To post a comment you must log in.
Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

Approving myself as it was already reviewed and approved.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
515. By Xavi Garcia

restore debian/changelog changes

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'include'
=== added file 'include/CMakeLists.txt'
--- include/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ include/CMakeLists.txt 2016-01-05 11:11:34 +0000
@@ -0,0 +1,1 @@
1add_subdirectory(unity)
02
=== added directory 'include/unity'
=== added file 'include/unity/CMakeLists.txt'
--- include/unity/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ include/unity/CMakeLists.txt 2016-01-05 11:11:34 +0000
@@ -0,0 +1,1 @@
1add_subdirectory(gmenuharness)
02
=== added directory 'include/unity/gmenuharness'
=== added file 'include/unity/gmenuharness/MatchResult.h'
--- include/unity/gmenuharness/MatchResult.h 1970-01-01 00:00:00 +0000
+++ include/unity/gmenuharness/MatchResult.h 2016-01-05 11:11:34 +0000
@@ -0,0 +1,66 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Pete Woods <pete.woods@canonical.com>
17 */
18
19#pragma once
20
21#include <vector>
22#include <memory>
23#include <string>
24
25namespace unity
26{
27
28namespace gmenuharness
29{
30
31class MatchResult
32{
33public:
34 MatchResult();
35
36 MatchResult(MatchResult&& other);
37
38 MatchResult(const MatchResult& other);
39
40 MatchResult& operator=(const MatchResult& other);
41
42 MatchResult& operator=(MatchResult&& other);
43
44 ~MatchResult() = default;
45
46 MatchResult createChild() const;
47
48 void failure(const std::vector<unsigned int>& location, const std::string& message);
49
50 void merge(const MatchResult& other);
51
52 bool success() const;
53
54 bool hasTimedOut() const;
55
56 std::string concat_failures() const;
57
58protected:
59 struct Priv;
60
61 std::shared_ptr<Priv> p;
62};
63
64} // namespace gmenuharness
65
66} // namespace unity
067
=== added file 'include/unity/gmenuharness/MatchUtils.h'
--- include/unity/gmenuharness/MatchUtils.h 1970-01-01 00:00:00 +0000
+++ include/unity/gmenuharness/MatchUtils.h 2016-01-05 11:11:34 +0000
@@ -0,0 +1,42 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Pete Woods <pete.woods@canonical.com>
17 */
18
19#pragma once
20
21#include <memory>
22#include <string>
23
24#include <gio/gio.h>
25
26namespace unity
27{
28
29namespace gmenuharness
30{
31
32void waitForCore(GObject* obj, const std::string& signalName, unsigned int timeout = 10);
33
34void menuWaitForItems(const std::shared_ptr<GMenuModel>& menu, unsigned int timeout = 10);
35
36void g_object_deleter(gpointer object);
37
38void gvariant_deleter(GVariant* varptr);
39
40} //namespace gmenuharness
41
42} // namespace unity
043
=== added file 'include/unity/gmenuharness/MenuItemMatcher.h'
--- include/unity/gmenuharness/MenuItemMatcher.h 1970-01-01 00:00:00 +0000
+++ include/unity/gmenuharness/MenuItemMatcher.h 2016-01-05 11:11:34 +0000
@@ -0,0 +1,143 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Pete Woods <pete.woods@canonical.com>
17 */
18
19#pragma once
20
21#include <map>
22#include <memory>
23#include <string>
24
25#include <gio/gio.h>
26
27namespace unity
28{
29
30namespace gmenuharness
31{
32
33class MatchResult;
34
35class MenuItemMatcher
36{
37public:
38 enum class Mode
39 {
40 all,
41 starts_with,
42 ends_with
43 };
44
45 enum class Type
46 {
47 plain,
48 checkbox,
49 radio
50 };
51
52 static MenuItemMatcher checkbox();
53
54 static MenuItemMatcher radio();
55
56 MenuItemMatcher();
57
58 ~MenuItemMatcher();
59
60 MenuItemMatcher(const MenuItemMatcher& other);
61
62 MenuItemMatcher(MenuItemMatcher&& other);
63
64 MenuItemMatcher& operator=(const MenuItemMatcher& other);
65
66 MenuItemMatcher& operator=(MenuItemMatcher&& other);
67
68 MenuItemMatcher& type(Type type);
69
70 MenuItemMatcher& label(const std::string& label);
71
72 MenuItemMatcher& action(const std::string& action);
73
74 MenuItemMatcher& state_icons(const std::vector<std::string>& state);
75
76 MenuItemMatcher& icon(const std::string& icon);
77
78 MenuItemMatcher& themed_icon(const std::string& iconName, const std::vector<std::string>& icons);
79
80 MenuItemMatcher& widget(const std::string& widget);
81
82 MenuItemMatcher& pass_through_attribute(const std::string& actionName, const std::shared_ptr<GVariant>& value);
83
84 MenuItemMatcher& pass_through_boolean_attribute(const std::string& actionName, bool value);
85
86 MenuItemMatcher& pass_through_string_attribute(const std::string& actionName, const std::string& value);
87
88 MenuItemMatcher& pass_through_double_attribute(const std::string& actionName, double value);
89
90 MenuItemMatcher& round_doubles(double maxDifference);
91
92 MenuItemMatcher& attribute(const std::string& name, const std::shared_ptr<GVariant>& value);
93
94 MenuItemMatcher& boolean_attribute(const std::string& name, bool value);
95
96 MenuItemMatcher& string_attribute(const std::string& name, const std::string& value);
97
98 MenuItemMatcher& int32_attribute(const std::string& name, int value);
99
100 MenuItemMatcher& int64_attribute(const std::string& name, int value);
101
102 MenuItemMatcher& double_attribute(const std::string& name, double value);
103
104 MenuItemMatcher& attribute_not_set(const std::string& name);
105
106 MenuItemMatcher& toggled(bool toggled);
107
108 MenuItemMatcher& mode(Mode mode);
109
110 MenuItemMatcher& submenu();
111
112 MenuItemMatcher& section();
113
114 MenuItemMatcher& is_empty();
115
116 MenuItemMatcher& has_exactly(std::size_t children);
117
118 MenuItemMatcher& item(const MenuItemMatcher& item);
119
120 MenuItemMatcher& item(MenuItemMatcher&& item);
121
122 MenuItemMatcher& pass_through_activate(const std::string& action, const std::shared_ptr<GVariant>& parameter = nullptr);
123
124 MenuItemMatcher& activate(const std::shared_ptr<GVariant>& parameter = nullptr);
125
126 MenuItemMatcher& set_pass_through_action_state(const std::string& action, const std::shared_ptr<GVariant>& state);
127
128 MenuItemMatcher& set_action_state(const std::shared_ptr<GVariant>& state);
129
130 void match(MatchResult& matchResult, const std::vector<unsigned int>& location,
131 const std::shared_ptr<GMenuModel>& menu,
132 std::map<std::string, std::shared_ptr<GActionGroup>>& actions,
133 int index) const;
134
135protected:
136 struct Priv;
137
138 std::shared_ptr<Priv> p;
139};
140
141} // namespace gmenuharness
142
143} // namespace unity
0144
=== added file 'include/unity/gmenuharness/MenuMatcher.h'
--- include/unity/gmenuharness/MenuMatcher.h 1970-01-01 00:00:00 +0000
+++ include/unity/gmenuharness/MenuMatcher.h 2016-01-05 11:11:34 +0000
@@ -0,0 +1,95 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Pete Woods <pete.woods@canonical.com>
17 */
18
19#pragma once
20
21#define EXPECT_MATCHRESULT(statement) \
22do {\
23 auto result = (statement);\
24 GTEST_TEST_BOOLEAN_(result.success(), #statement, false, true, \
25 GTEST_NONFATAL_FAILURE_) << result.concat_failures().c_str(); \
26} while (0)
27
28#include <unity/gmenuharness/MatchResult.h>
29#include <unity/gmenuharness/MenuItemMatcher.h>
30
31#include <memory>
32#include <vector>
33
34namespace unity
35{
36
37namespace gmenuharness
38{
39
40class MenuMatcher
41{
42public:
43 class Parameters
44 {
45 public:
46 Parameters(
47 const std::string& busName,
48 const std::vector<std::pair<std::string, std::string>>& actions,
49 const std::string& menuObjectPath);
50
51 ~Parameters();
52
53 Parameters(const Parameters& other);
54
55 Parameters(Parameters&& other);
56
57 Parameters& operator=(const Parameters& other);
58
59 Parameters& operator=(Parameters&& other);
60
61 protected:
62 friend MenuMatcher;
63
64 struct Priv;
65
66 std::shared_ptr<Priv> p;
67 };
68
69 MenuMatcher(const Parameters& parameters);
70
71 ~MenuMatcher();
72
73 MenuMatcher(const MenuMatcher& other) = delete;
74
75 MenuMatcher(MenuMatcher&& other) = delete;
76
77 MenuMatcher& operator=(const MenuMatcher& other) = delete;
78
79 MenuMatcher& operator=(MenuMatcher&& other) = delete;
80
81 MenuMatcher& item(const MenuItemMatcher& item);
82
83 MatchResult match() const;
84
85 void match(MatchResult& matchResult) const;
86
87protected:
88 struct Priv;
89
90 std::shared_ptr<Priv> p;
91};
92
93} // gmenuharness
94
95} // unity
096
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2015-12-23 11:08:16 +0000
+++ src/CMakeLists.txt 2016-01-05 11:11:34 +0000
@@ -8,12 +8,12 @@
8set(VAPI_PATH "${CMAKE_CURRENT_BINARY_DIR}/indicator-sound-service.vapi")8set(VAPI_PATH "${CMAKE_CURRENT_BINARY_DIR}/indicator-sound-service.vapi")
99
10vapi_gen(accounts-service10vapi_gen(accounts-service
11 LIBRARY11 LIBRARY
12 accounts-service12 accounts-service
13 PACKAGES13 PACKAGES
14 gio-2.014 gio-2.0
15 INPUT15 INPUT
16 /usr/share/gir-1.0/AccountsService-1.0.gir16 /usr/share/gir-1.0/AccountsService-1.0.gir
17)17)
1818
19vala_init(indicator-sound-service19vala_init(indicator-sound-service
@@ -70,7 +70,7 @@
70 media-player-user.vala70 media-player-user.vala
71 DEPENDS71 DEPENDS
72 media-player72 media-player
73 accounts-service-sound-settings73 accounts-service-sound-settings
74 greeter-broadcast74 greeter-broadcast
75)75)
76vala_add(indicator-sound-service76vala_add(indicator-sound-service
@@ -103,6 +103,7 @@
103 sound-menu.vala103 sound-menu.vala
104 DEPENDS104 DEPENDS
105 media-player105 media-player
106 volume-control
106)107)
107vala_add(indicator-sound-service108vala_add(indicator-sound-service
108 accounts-service-user.vala109 accounts-service-user.vala
@@ -164,8 +165,8 @@
164)165)
165166
166add_library(167add_library(
167 indicator-sound-service-lib STATIC168 indicator-sound-service-lib STATIC
168 ${INDICATOR_SOUND_SOURCES}169 ${INDICATOR_SOUND_SOURCES}
169)170)
170171
171target_link_libraries(172target_link_libraries(
@@ -206,3 +207,5 @@
206 RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/indicator-sound/207 RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/indicator-sound/
207)208)
208209
210# Disable integration tests
211# add_subdirectory(gmenuharness)
209212
=== added directory 'src/gmenuharness'
=== added file 'src/gmenuharness/CMakeLists.txt'
--- src/gmenuharness/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/gmenuharness/CMakeLists.txt 2016-01-05 11:11:34 +0000
@@ -0,0 +1,17 @@
1pkg_check_modules(UNITY_API libunity-api>=0.1.3 REQUIRED)
2include_directories(${UNITY_API_INCLUDE_DIRS})
3
4include_directories("${CMAKE_SOURCE_DIR}/include")
5
6add_library(
7 gmenuharness-shared SHARED
8 MatchResult.cpp
9 MatchUtils.cpp
10 MenuItemMatcher.cpp
11 MenuMatcher.cpp
12)
13
14target_link_libraries(
15 gmenuharness-shared
16 ${GLIB_LDFLAGS}
17)
018
=== added file 'src/gmenuharness/MatchResult.cpp'
--- src/gmenuharness/MatchResult.cpp 1970-01-01 00:00:00 +0000
+++ src/gmenuharness/MatchResult.cpp 2016-01-05 11:11:34 +0000
@@ -0,0 +1,187 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Pete Woods <pete.woods@canonical.com>
17 */
18
19#include <unity/gmenuharness/MatchResult.h>
20
21#include <chrono>
22#include <map>
23#include <sstream>
24#include <iostream>
25
26using namespace std;
27
28namespace unity
29{
30
31namespace gmenuharness
32{
33
34namespace
35{
36
37
38static void printLocation(ostream& ss, const vector<unsigned int>& location, bool first)
39{
40 for (int i : location)
41 {
42 ss << " ";
43 if (first)
44 {
45 ss << i;
46 }
47 else
48 {
49 ss << " ";
50 }
51 }
52 ss << " ";
53}
54
55struct compare_vector
56{
57 bool operator()(const vector<unsigned int>& a,
58 const vector<unsigned int>& b) const
59 {
60 auto p1 = a.begin();
61 auto p2 = b.begin();
62
63 while (p1 != a.end())
64 {
65 if (p2 == b.end())
66 {
67 return false;
68 }
69 if (*p2 > *p1)
70 {
71 return true;
72 }
73 if (*p1 > *p2)
74 {
75 return false;
76 }
77
78 ++p1;
79 ++p2;
80 }
81
82 if (p2 != b.end())
83 {
84 return true;
85 }
86
87 return false;
88 }
89};
90}
91
92struct MatchResult::Priv
93{
94 bool m_success = true;
95
96 map<vector<unsigned int>, vector<string>, compare_vector> m_failures;
97
98 chrono::time_point<chrono::system_clock> m_timeout = chrono::system_clock::now() + chrono::seconds(10);
99};
100
101MatchResult::MatchResult() :
102 p(new Priv)
103{
104}
105
106MatchResult::MatchResult(MatchResult&& other)
107{
108 *this = move(other);
109}
110
111MatchResult::MatchResult(const MatchResult& other) :
112 p(new Priv)
113{
114 *this = other;
115}
116
117MatchResult& MatchResult::operator=(const MatchResult& other)
118{
119 p->m_success = other.p->m_success;
120 p->m_failures= other.p->m_failures;
121 return *this;
122}
123
124MatchResult& MatchResult::operator=(MatchResult&& other)
125{
126 p = move(other.p);
127 return *this;
128}
129
130MatchResult MatchResult::createChild() const
131{
132 MatchResult child;
133 child.p->m_timeout = p->m_timeout;
134 return child;
135}
136
137void MatchResult::failure(const vector<unsigned int>& location, const string& message)
138{
139 p->m_success = false;
140 auto it = p->m_failures.find(location);
141 if (it == p->m_failures.end())
142 {
143 it = p->m_failures.insert(make_pair(location, vector<string>())).first;
144 }
145 it->second.emplace_back(message);
146}
147
148void MatchResult::merge(const MatchResult& other)
149{
150 p->m_success &= other.p->m_success;
151 for (const auto& e : other.p->m_failures)
152 {
153 p->m_failures.insert(make_pair(e.first, e.second));
154 }
155}
156
157bool MatchResult::success() const
158{
159 return p->m_success;
160}
161
162bool MatchResult::hasTimedOut() const
163{
164 auto now = chrono::system_clock::now();
165 return (now >= p->m_timeout);
166}
167
168string MatchResult::concat_failures() const
169{
170 stringstream ss;
171 ss << "Failed expectations:" << endl;
172 for (const auto& failure : p->m_failures)
173 {
174 bool first = true;
175 for (const string& s: failure.second)
176 {
177 printLocation(ss, failure.first, first);
178 first = false;
179 ss << s << endl;
180 }
181 }
182 return ss.str();
183}
184
185} // namespace gmenuharness
186
187} // namespace unity
0188
=== added file 'src/gmenuharness/MatchUtils.cpp'
--- src/gmenuharness/MatchUtils.cpp 1970-01-01 00:00:00 +0000
+++ src/gmenuharness/MatchUtils.cpp 2016-01-05 11:11:34 +0000
@@ -0,0 +1,77 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Pete Woods <pete.woods@canonical.com>
17 */
18
19#include <unity/gmenuharness/MatchUtils.h>
20
21#include <unity/util/ResourcePtr.h>
22
23using namespace std;
24namespace util = unity::util;
25
26namespace unity
27{
28
29namespace gmenuharness
30{
31
32void waitForCore (GObject * obj, const string& signalName, unsigned int timeout) {
33 shared_ptr<GMainLoop> loop(g_main_loop_new(nullptr, false), &g_main_loop_unref);
34
35 /* Our two exit criteria */
36 util::ResourcePtr<gulong, function<void(gulong)>> signal(
37 g_signal_connect_swapped(obj, signalName.c_str(),
38 G_CALLBACK(g_main_loop_quit), loop.get()),
39 [obj](gulong s)
40 {
41 g_signal_handler_disconnect(obj, s);
42 });
43
44 util::ResourcePtr<guint, function<void(guint)>> timer(g_timeout_add(timeout,
45 [](gpointer user_data) -> gboolean
46 {
47 g_main_loop_quit((GMainLoop *)user_data);
48 return G_SOURCE_CONTINUE;
49 },
50 loop.get()),
51 &g_source_remove);
52
53 /* Wait for sync */
54 g_main_loop_run(loop.get());
55}
56
57void menuWaitForItems(const shared_ptr<GMenuModel>& menu, unsigned int timeout)
58{
59 waitForCore(G_OBJECT(menu.get()), "items-changed", timeout);
60}
61
62void g_object_deleter(gpointer object)
63{
64 g_clear_object(&object);
65}
66
67void gvariant_deleter(GVariant* varptr)
68{
69 if (varptr != nullptr)
70 {
71 g_variant_unref(varptr);
72 }
73}
74
75} // namespace gmenuharness
76
77} // namespace unity
078
=== added file 'src/gmenuharness/MenuItemMatcher.cpp'
--- src/gmenuharness/MenuItemMatcher.cpp 1970-01-01 00:00:00 +0000
+++ src/gmenuharness/MenuItemMatcher.cpp 2016-01-05 11:11:34 +0000
@@ -0,0 +1,1008 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Pete Woods <pete.woods@canonical.com>
17 */
18
19#include <unity/gmenuharness/MatchResult.h>
20#include <unity/gmenuharness/MatchUtils.h>
21#include <unity/gmenuharness/MenuItemMatcher.h>
22
23#include <iostream>
24#include <vector>
25#include <map>
26
27using namespace std;
28
29namespace unity
30{
31
32namespace gmenuharness
33{
34
35namespace
36{
37
38enum class LinkType
39{
40 any,
41 section,
42 submenu
43};
44
45static string bool_to_string(bool value)
46{
47 return value? "true" : "false";
48}
49
50static shared_ptr<GVariant> get_action_group_attribute(const shared_ptr<GActionGroup>& actionGroup, const gchar* attribute)
51{
52 shared_ptr<GVariant> value(
53 g_action_group_get_action_state(actionGroup.get(), attribute),
54 &gvariant_deleter);
55 return value;
56}
57
58static shared_ptr<GVariant> get_attribute(const shared_ptr<GMenuItem> menuItem, const gchar* attribute)
59{
60 shared_ptr<GVariant> value(
61 g_menu_item_get_attribute_value(menuItem.get(), attribute, nullptr),
62 &gvariant_deleter);
63 return value;
64}
65
66static string get_string_attribute(const shared_ptr<GMenuItem> menuItem, const gchar* attribute)
67{
68 string result;
69 char* temp = nullptr;
70 if (g_menu_item_get_attribute(menuItem.get(), attribute, "s", &temp))
71 {
72 result = temp;
73 g_free(temp);
74 }
75 return result;
76}
77
78static pair<string, string> split_action(const string& action)
79{
80 auto index = action.find('.');
81
82 if (index == string::npos)
83 {
84 return make_pair(string(), action);
85 }
86
87 return make_pair(action.substr(0, index), action.substr(index + 1, action.size()));
88}
89
90static string type_to_string(MenuItemMatcher::Type type)
91{
92 switch(type)
93 {
94 case MenuItemMatcher::Type::plain:
95 return "plain";
96 case MenuItemMatcher::Type::checkbox:
97 return "checkbox";
98 case MenuItemMatcher::Type::radio:
99 return "radio";
100 }
101
102 return string();
103}
104}
105
106struct MenuItemMatcher::Priv
107{
108 void all(MatchResult& matchResult, const vector<unsigned int>& location,
109 const shared_ptr<GMenuModel>& menu,
110 map<string, shared_ptr<GActionGroup>>& actions)
111 {
112 int count = g_menu_model_get_n_items(menu.get());
113
114 if (m_items.size() != (unsigned int) count)
115 {
116 matchResult.failure(
117 location,
118 "Expected " + to_string(m_items.size())
119 + " children, but found " + to_string(count));
120 return;
121 }
122
123 for (size_t i = 0; i < m_items.size(); ++i)
124 {
125 const auto& matcher = m_items.at(i);
126 matcher.match(matchResult, location, menu, actions, i);
127 }
128 }
129
130 void startsWith(MatchResult& matchResult, const vector<unsigned int>& location,
131 const shared_ptr<GMenuModel>& menu,
132 map<string, shared_ptr<GActionGroup>>& actions)
133 {
134 int count = g_menu_model_get_n_items(menu.get());
135 if (m_items.size() > (unsigned int) count)
136 {
137 matchResult.failure(
138 location,
139 "Expected at least " + to_string(m_items.size())
140 + " children, but found " + to_string(count));
141 return;
142 }
143
144 for (size_t i = 0; i < m_items.size(); ++i)
145 {
146 const auto& matcher = m_items.at(i);
147 matcher.match(matchResult, location, menu, actions, i);
148 }
149 }
150
151 void endsWith(MatchResult& matchResult, const vector<unsigned int>& location,
152 const shared_ptr<GMenuModel>& menu,
153 map<string, shared_ptr<GActionGroup>>& actions)
154 {
155 int count = g_menu_model_get_n_items(menu.get());
156 if (m_items.size() > (unsigned int) count)
157 {
158 matchResult.failure(
159 location,
160 "Expected at least " + to_string(m_items.size())
161 + " children, but found " + to_string(count));
162 return;
163 }
164
165 // match the last N items
166 size_t j;
167 for (size_t i = count - m_items.size(), j = 0; i < count && j < m_items.size(); ++i, ++j)
168 {
169 const auto& matcher = m_items.at(j);
170 matcher.match(matchResult, location, menu, actions, i);
171 }
172 }
173
174 Type m_type = Type::plain;
175
176 Mode m_mode = Mode::all;
177
178 LinkType m_linkType = LinkType::any;
179
180 shared_ptr<size_t> m_expectedSize;
181
182 shared_ptr<string> m_label;
183
184 shared_ptr<string> m_icon;
185
186 map<shared_ptr<string>, vector<std::string>> m_themed_icons;
187
188 shared_ptr<string> m_action;
189
190 vector<std::string> m_state_icons;
191
192 vector<pair<string, shared_ptr<GVariant>>> m_attributes;
193
194 vector<string> m_not_exist_attributes;
195
196 vector<pair<string, shared_ptr<GVariant>>> m_pass_through_attributes;
197
198 shared_ptr<bool> m_isToggled;
199
200 vector<MenuItemMatcher> m_items;
201
202 vector<pair<string, shared_ptr<GVariant>>> m_activations;
203
204 vector<pair<string, shared_ptr<GVariant>>> m_setActionStates;
205
206 double m_maxDifference = 0.0;
207};
208
209MenuItemMatcher MenuItemMatcher::checkbox()
210{
211 MenuItemMatcher matcher;
212 matcher.type(Type::checkbox);
213 return matcher;
214}
215
216MenuItemMatcher MenuItemMatcher::radio()
217{
218 MenuItemMatcher matcher;
219 matcher.type(Type::radio);
220 return matcher;
221}
222
223MenuItemMatcher::MenuItemMatcher() :
224 p(new Priv)
225{
226}
227
228MenuItemMatcher::~MenuItemMatcher()
229{
230}
231
232MenuItemMatcher::MenuItemMatcher(const MenuItemMatcher& other) :
233 p(new Priv)
234{
235 *this = other;
236}
237
238MenuItemMatcher::MenuItemMatcher(MenuItemMatcher&& other)
239{
240 *this = move(other);
241}
242
243MenuItemMatcher& MenuItemMatcher::operator=(const MenuItemMatcher& other)
244{
245 p->m_type = other.p->m_type;
246 p->m_mode = other.p->m_mode;
247 p->m_expectedSize = other.p->m_expectedSize;
248 p->m_label = other.p->m_label;
249 p->m_icon = other.p->m_icon;
250 p->m_themed_icons = other.p->m_themed_icons;
251 p->m_action = other.p->m_action;
252 p->m_state_icons = other.p->m_state_icons;
253 p->m_attributes = other.p->m_attributes;
254 p->m_not_exist_attributes = other.p->m_not_exist_attributes;
255 p->m_pass_through_attributes = other.p->m_pass_through_attributes;
256 p->m_isToggled = other.p->m_isToggled;
257 p->m_linkType = other.p->m_linkType;
258 p->m_items = other.p->m_items;
259 p->m_activations = other.p->m_activations;
260 p->m_setActionStates = other.p->m_setActionStates;
261 p->m_maxDifference = other.p->m_maxDifference;
262 return *this;
263}
264
265MenuItemMatcher& MenuItemMatcher::operator=(MenuItemMatcher&& other)
266{
267 p = move(other.p);
268 return *this;
269}
270
271MenuItemMatcher& MenuItemMatcher::type(Type type)
272{
273 p->m_type = type;
274 return *this;
275}
276
277MenuItemMatcher& MenuItemMatcher::label(const string& label)
278{
279 p->m_label = make_shared<string>(label);
280 return *this;
281}
282
283MenuItemMatcher& MenuItemMatcher::action(const string& action)
284{
285 p->m_action = make_shared<string>(action);
286 return *this;
287}
288
289MenuItemMatcher& MenuItemMatcher::state_icons(const std::vector<std::string>& state_icons)
290{
291 p->m_state_icons = state_icons;
292 return *this;
293}
294
295MenuItemMatcher& MenuItemMatcher::icon(const string& icon)
296{
297 p->m_icon = make_shared<string>(icon);
298 return *this;
299}
300
301MenuItemMatcher& MenuItemMatcher::themed_icon(const std::string& iconName, const std::vector<std::string>& icons)
302{
303 p->m_themed_icons[make_shared<string>(iconName)] = icons;
304 return *this;
305}
306
307MenuItemMatcher& MenuItemMatcher::widget(const string& widget)
308{
309 return string_attribute("x-canonical-type", widget);
310}
311
312MenuItemMatcher& MenuItemMatcher::pass_through_attribute(const string& actionName, const shared_ptr<GVariant>& value)
313{
314 p->m_pass_through_attributes.emplace_back(actionName, value);
315 return *this;
316}
317
318MenuItemMatcher& MenuItemMatcher::pass_through_boolean_attribute(const string& actionName, bool value)
319{
320 return pass_through_attribute(
321 actionName,
322 shared_ptr<GVariant>(g_variant_new_boolean(value),
323 &gvariant_deleter));
324}
325
326MenuItemMatcher& MenuItemMatcher::pass_through_string_attribute(const string& actionName, const string& value)
327{
328 return pass_through_attribute(
329 actionName,
330 shared_ptr<GVariant>(g_variant_new_string(value.c_str()),
331 &gvariant_deleter));
332}
333
334MenuItemMatcher& MenuItemMatcher::pass_through_double_attribute(const std::string& actionName, double value)
335{
336 return pass_through_attribute(
337 actionName,
338 shared_ptr<GVariant>(g_variant_new_double(value),
339 &gvariant_deleter));
340}
341
342MenuItemMatcher& MenuItemMatcher::round_doubles(double maxDifference)
343{
344 p->m_maxDifference = maxDifference;
345 return *this;
346}
347
348MenuItemMatcher& MenuItemMatcher::attribute(const string& name, const shared_ptr<GVariant>& value)
349{
350 p->m_attributes.emplace_back(name, value);
351 return *this;
352}
353
354MenuItemMatcher& MenuItemMatcher::boolean_attribute(const string& name, bool value)
355{
356 return attribute(
357 name,
358 shared_ptr<GVariant>(g_variant_new_boolean(value),
359 &gvariant_deleter));
360}
361
362MenuItemMatcher& MenuItemMatcher::string_attribute(const string& name, const string& value)
363{
364 return attribute(
365 name,
366 shared_ptr<GVariant>(g_variant_new_string(value.c_str()),
367 &gvariant_deleter));
368}
369
370MenuItemMatcher& MenuItemMatcher::int32_attribute(const std::string& name, int value)
371{
372 return attribute(
373 name,
374 shared_ptr<GVariant>(g_variant_new_int32 (value),
375 &gvariant_deleter));
376}
377
378MenuItemMatcher& MenuItemMatcher::int64_attribute(const std::string& name, int value)
379{
380 return attribute(
381 name,
382 shared_ptr<GVariant>(g_variant_new_int64 (value),
383 &gvariant_deleter));
384}
385
386MenuItemMatcher& MenuItemMatcher::double_attribute(const std::string& name, double value)
387{
388 return attribute(
389 name,
390 shared_ptr<GVariant>(g_variant_new_double (value),
391 &gvariant_deleter));
392}
393
394MenuItemMatcher& MenuItemMatcher::attribute_not_set(const std::string& name)
395{
396 p->m_not_exist_attributes.emplace_back (name);
397 return *this;
398}
399
400MenuItemMatcher& MenuItemMatcher::toggled(bool isToggled)
401{
402 p->m_isToggled = make_shared<bool>(isToggled);
403 return *this;
404}
405
406MenuItemMatcher& MenuItemMatcher::mode(Mode mode)
407{
408 p->m_mode = mode;
409 return *this;
410}
411
412MenuItemMatcher& MenuItemMatcher::submenu()
413{
414 p->m_linkType = LinkType::submenu;
415 return *this;
416}
417
418MenuItemMatcher& MenuItemMatcher::section()
419{
420 p->m_linkType = LinkType::section;
421 return *this;
422}
423
424MenuItemMatcher& MenuItemMatcher::is_empty()
425{
426 return has_exactly(0);
427}
428
429MenuItemMatcher& MenuItemMatcher::has_exactly(size_t children)
430{
431 p->m_expectedSize = make_shared<size_t>(children);
432 return *this;
433}
434
435MenuItemMatcher& MenuItemMatcher::item(const MenuItemMatcher& item)
436{
437 p->m_items.emplace_back(item);
438 return *this;
439}
440
441MenuItemMatcher& MenuItemMatcher::item(MenuItemMatcher&& item)
442{
443 p->m_items.emplace_back(item);
444 return *this;
445}
446
447MenuItemMatcher& MenuItemMatcher::pass_through_activate(std::string const& action, const shared_ptr<GVariant>& parameter)
448{
449 p->m_activations.emplace_back(action, parameter);
450 return *this;
451}
452
453MenuItemMatcher& MenuItemMatcher::activate(const shared_ptr<GVariant>& parameter)
454{
455 p->m_activations.emplace_back(string(), parameter);
456 return *this;
457}
458
459MenuItemMatcher& MenuItemMatcher::set_pass_through_action_state(const std::string& action, const std::shared_ptr<GVariant>& state)
460{
461 p->m_setActionStates.emplace_back(action, state);
462 return *this;
463}
464
465MenuItemMatcher& MenuItemMatcher::set_action_state(const std::shared_ptr<GVariant>& state)
466{
467 p->m_setActionStates.emplace_back("", state);
468 return *this;
469}
470
471void MenuItemMatcher::match(
472 MatchResult& matchResult,
473 const vector<unsigned int>& parentLocation,
474 const shared_ptr<GMenuModel>& menu,
475 map<string, shared_ptr<GActionGroup>>& actions,
476 int index) const
477{
478 shared_ptr<GMenuItem> menuItem(g_menu_item_new_from_model(menu.get(), index), &g_object_deleter);
479
480 vector<unsigned int> location(parentLocation);
481 location.emplace_back(index);
482
483 string action = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_ACTION);
484
485 bool isCheckbox = false;
486 bool isRadio = false;
487 bool isToggled = false;
488
489 pair<string, string> idPair;
490 shared_ptr<GActionGroup> actionGroup;
491 shared_ptr<GVariant> state;
492
493 if (!action.empty())
494 {
495 idPair = split_action(action);
496 actionGroup = actions[idPair.first];
497 state = shared_ptr<GVariant>(g_action_group_get_action_state(actionGroup.get(),
498 idPair.second.c_str()),
499 &gvariant_deleter);
500 auto attributeTarget = get_attribute(menuItem, G_MENU_ATTRIBUTE_TARGET);
501
502 if (attributeTarget && state)
503 {
504 isToggled = g_variant_equal(state.get(), attributeTarget.get());
505 isRadio = true;
506 }
507 else if (state
508 && g_variant_is_of_type(state.get(), G_VARIANT_TYPE_BOOLEAN))
509 {
510 isToggled = g_variant_get_boolean(state.get());
511 isCheckbox = true;
512 }
513 }
514
515 Type actualType = Type::plain;
516 if (isCheckbox)
517 {
518 actualType = Type::checkbox;
519 }
520 else if (isRadio)
521 {
522 actualType = Type::radio;
523 }
524
525 if (actualType != p->m_type)
526 {
527 matchResult.failure(
528 location,
529 "Expected " + type_to_string(p->m_type) + ", found "
530 + type_to_string(actualType));
531 }
532
533 // check themed icons
534 map<shared_ptr<string>, vector<string>>::iterator iter;
535 for (iter = p->m_themed_icons.begin(); iter != p->m_themed_icons.end(); ++iter)
536 {
537 auto icon_val = g_menu_item_get_attribute_value(menuItem.get(), (*iter).first->c_str(), nullptr);
538 if (!icon_val)
539 {
540 matchResult.failure(
541 location,
542 "Expected themed icon " + (*(*iter).first) + " was not found");
543 }
544
545 auto gicon = g_icon_deserialize(icon_val);
546 if (!gicon || !G_IS_THEMED_ICON(gicon))
547 {
548 matchResult.failure(
549 location,
550 "Expected attribute " + (*(*iter).first) + " is not a themed icon");
551 }
552 auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon));
553 int nb_icons = 0;
554 while(iconNames[nb_icons])
555 {
556 ++nb_icons;
557 }
558
559 if (nb_icons != (*iter).second.size())
560 {
561 matchResult.failure(
562 location,
563 "Expected " + to_string((*iter).second.size()) +
564 " icons for themed icon [" + (*(*iter).first) +
565 "], but " + to_string(nb_icons) + " were found.");
566 }
567 else
568 {
569 // now compare all the icons
570 for (int i = 0; i < nb_icons; ++i)
571 {
572 if (string(iconNames[i]) != (*iter).second[i])
573 {
574 matchResult.failure(
575 location,
576 "Icon at position " + to_string(i) +
577 " for themed icon [" + (*(*iter).first) +
578 "], mismatchs. Expected: " + iconNames[i] + " but found " + (*iter).second[i]);
579 }
580 }
581 }
582 g_object_unref(gicon);
583 }
584
585 string label = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_LABEL);
586 if (p->m_label && (*p->m_label) != label)
587 {
588 matchResult.failure(
589 location,
590 "Expected label '" + *p->m_label + "', but found '" + label
591 + "'");
592 }
593
594 string icon = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_ICON);
595 if (p->m_icon && (*p->m_icon) != icon)
596 {
597 matchResult.failure(
598 location,
599 "Expected icon '" + *p->m_icon + "', but found '" + icon + "'");
600 }
601
602 if (p->m_action && (*p->m_action) != action)
603 {
604 matchResult.failure(
605 location,
606 "Expected action '" + *p->m_action + "', but found '" + action
607 + "'");
608 }
609
610 if (!p->m_state_icons.empty() && !state)
611 {
612 matchResult.failure(
613 location,
614 "Expected state icons but no state was found");
615 }
616 else if (!p->m_state_icons.empty() && state &&
617 !g_variant_is_of_type(state.get(), G_VARIANT_TYPE_VARDICT))
618 {
619 matchResult.failure(
620 location,
621 "Expected state icons vardict, found "
622 + type_to_string(actualType));
623 }
624 else if (!p->m_state_icons.empty() && state &&
625 g_variant_is_of_type(state.get(), G_VARIANT_TYPE_VARDICT))
626 {
627 std::vector<std::string> actual_state_icons;
628 GVariantIter it;
629 gchar* key;
630 GVariant* value;
631
632 g_variant_iter_init(&it, state.get());
633 while (g_variant_iter_loop(&it, "{sv}", &key, &value))
634 {
635 if (std::string(key) == "icon") {
636 auto gicon = g_icon_deserialize(value);
637 if (gicon && G_IS_THEMED_ICON(gicon))
638 {
639 auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon));
640 // Just take the first icon in the list (there is only ever one)
641 actual_state_icons.push_back(iconNames[0]);
642 g_object_unref(gicon);
643 }
644 }
645 else if (std::string(key) == "icons" && g_variant_is_of_type(value, G_VARIANT_TYPE("av")))
646 {
647 // If we find "icons" in the map, clear any icons we may have found in "icon",
648 // then break from the loop as we have found all icons now.
649 actual_state_icons.clear();
650 GVariantIter icon_it;
651 GVariant* icon_value;
652
653 g_variant_iter_init(&icon_it, value);
654 while (g_variant_iter_loop(&icon_it, "v", &icon_value))
655 {
656 auto gicon = g_icon_deserialize(icon_value);
657 if (gicon && G_IS_THEMED_ICON(gicon))
658 {
659 auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon));
660 // Just take the first icon in the list (there is only ever one)
661 actual_state_icons.push_back(iconNames[0]);
662 g_object_unref(gicon);
663 }
664 }
665 // We're breaking out of g_variant_iter_loop here so clean up
666 g_variant_unref(value);
667 g_free(key);
668 break;
669 }
670 }
671
672 if (p->m_state_icons != actual_state_icons)
673 {
674 std::string expected_icons;
675 for (unsigned int i = 0; i < p->m_state_icons.size(); ++i)
676 {
677 expected_icons += i == 0 ? p->m_state_icons[i] : ", " + p->m_state_icons[i];
678 }
679 std::string actual_icons;
680 for (unsigned int i = 0; i < actual_state_icons.size(); ++i)
681 {
682 actual_icons += i == 0 ? actual_state_icons[i] : ", " + actual_state_icons[i];
683 }
684 matchResult.failure(
685 location,
686 "Expected state_icons == {" + expected_icons
687 + "} but found {" + actual_icons + "}");
688 }
689 }
690
691 for (const auto& e: p->m_pass_through_attributes)
692 {
693 string actionName = get_string_attribute(menuItem, e.first.c_str());
694 if (actionName.empty())
695 {
696 matchResult.failure(
697 location,
698 "Could not find action name '" + e.first + "'");
699 }
700 else
701 {
702 auto passThroughIdPair = split_action(actionName);
703 auto actionGroup = actions[passThroughIdPair.first];
704 if (actionGroup)
705 {
706 auto value = get_action_group_attribute(
707 actionGroup, passThroughIdPair.second.c_str());
708 if (!value)
709 {
710 matchResult.failure(
711 location,
712 "Expected pass-through attribute '" + e.first
713 + "' was not present");
714 }
715 else if (!g_variant_is_of_type(e.second.get(), g_variant_get_type(value.get())))
716 {
717 std::string expectedType = g_variant_get_type_string(e.second.get());
718 std::string actualType = g_variant_get_type_string(value.get());
719 matchResult.failure(
720 location,
721 "Expected pass-through attribute type '" + expectedType
722 + "' but found '" + actualType + "'");
723 }
724 else if (g_variant_compare(e.second.get(), value.get()))
725 {
726 bool reportMismatch = true;
727 if (g_strcmp0(g_variant_get_type_string(value.get()),"d") == 0 && p->m_maxDifference)
728 {
729 auto actualDouble = g_variant_get_double(value.get());
730 auto expectedDouble = g_variant_get_double(e.second.get());
731 auto difference = actualDouble-expectedDouble;
732 if (difference < 0) difference = difference * -1.0;
733 if (difference <= p->m_maxDifference)
734 {
735 reportMismatch = false;
736 }
737 }
738 if (reportMismatch)
739 {
740 gchar* expectedString = g_variant_print(e.second.get(), true);
741 gchar* actualString = g_variant_print(value.get(), true);
742 matchResult.failure(
743 location,
744 "Expected pass-through attribute '" + e.first
745 + "' == " + expectedString + " but found "
746 + actualString);
747
748 g_free(expectedString);
749 g_free(actualString);
750 }
751 }
752 }
753 else
754 {
755 matchResult.failure(location, "Could not find action group for ID '" + passThroughIdPair.first + "'");
756 }
757 }
758 }
759
760 for (const auto& e: p->m_attributes)
761 {
762 auto value = get_attribute(menuItem, e.first.c_str());
763 if (!value)
764 {
765 matchResult.failure(location,
766 "Expected attribute '" + e.first
767 + "' could not be found");
768 }
769 else if (!g_variant_is_of_type(e.second.get(), g_variant_get_type(value.get())))
770 {
771 std::string expectedType = g_variant_get_type_string(e.second.get());
772 std::string actualType = g_variant_get_type_string(value.get());
773 matchResult.failure(
774 location,
775 "Expected attribute type '" + expectedType
776 + "' but found '" + actualType + "'");
777 }
778 else if (g_variant_compare(e.second.get(), value.get()))
779 {
780 gchar* expectedString = g_variant_print(e.second.get(), true);
781 gchar* actualString = g_variant_print(value.get(), true);
782 matchResult.failure(
783 location,
784 "Expected attribute '" + e.first + "' == " + expectedString
785 + ", but found " + actualString);
786 g_free(expectedString);
787 g_free(actualString);
788 }
789 }
790
791 for (const auto& e: p->m_not_exist_attributes)
792 {
793 auto value = get_attribute(menuItem, e.c_str());
794 if (value)
795 {
796 matchResult.failure(location,
797 "Not expected attribute '" + e
798 + "' was found");
799 }
800 }
801
802 if (p->m_isToggled && (*p->m_isToggled) != isToggled)
803 {
804 matchResult.failure(
805 location,
806 "Expected toggled = " + bool_to_string(*p->m_isToggled)
807 + ", but found " + bool_to_string(isToggled));
808 }
809
810 if (!matchResult.success())
811 {
812 return;
813 }
814
815 if (!p->m_items.empty() || p->m_expectedSize)
816 {
817 shared_ptr<GMenuModel> link;
818
819 switch (p->m_linkType)
820 {
821 case LinkType::any:
822 {
823 link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SUBMENU), &g_object_deleter);
824 if (!link)
825 {
826 link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SECTION), &g_object_deleter);
827 }
828 break;
829 }
830 case LinkType::submenu:
831 {
832 link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SUBMENU), &g_object_deleter);
833 break;
834 }
835 case LinkType::section:
836 {
837 link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SECTION), &g_object_deleter);
838 break;
839 }
840 }
841
842
843 if (!link)
844 {
845 if (p->m_expectedSize)
846 {
847 matchResult.failure(
848 location,
849 "Expected " + to_string(*p->m_expectedSize)
850 + " children, but found none");
851 }
852 else
853 {
854 matchResult.failure(
855 location,
856 "Expected " + to_string(p->m_items.size())
857 + " children, but found none");
858 }
859 return;
860 }
861 else
862 {
863 while (true)
864 {
865 MatchResult childMatchResult(matchResult.createChild());
866
867 if (p->m_expectedSize
868 && *p->m_expectedSize
869 != (unsigned int) g_menu_model_get_n_items(
870 link.get()))
871 {
872 childMatchResult.failure(
873 location,
874 "Expected " + to_string(*p->m_expectedSize)
875 + " child items, but found "
876 + to_string(
877 g_menu_model_get_n_items(
878 link.get())));
879 }
880 else if (!p->m_items.empty())
881 {
882 switch (p->m_mode)
883 {
884 case Mode::all:
885 p->all(childMatchResult, location, link, actions);
886 break;
887 case Mode::starts_with:
888 p->startsWith(childMatchResult, location, link, actions);
889 break;
890 case Mode::ends_with:
891 p->endsWith(childMatchResult, location, link, actions);
892 break;
893 }
894 }
895
896 if (childMatchResult.success())
897 {
898 matchResult.merge(childMatchResult);
899 break;
900 }
901 else
902 {
903 if (matchResult.hasTimedOut())
904 {
905 matchResult.merge(childMatchResult);
906 break;
907 }
908 menuWaitForItems(link);
909 }
910 }
911 }
912 }
913
914
915 for (const auto& a: p->m_setActionStates)
916 {
917 auto stateAction = action;
918 auto stateIdPair = idPair;
919 auto stateActionGroup = actionGroup;
920 if (!a.first.empty())
921 {
922 stateAction = get_string_attribute(menuItem, a.first.c_str());;
923 stateIdPair = split_action(stateAction);
924 stateActionGroup = actions[stateIdPair.first];
925 }
926
927 if (stateAction.empty())
928 {
929 matchResult.failure(
930 location,
931 "Tried to set action state, but no action was found");
932 }
933 else if(!stateActionGroup)
934 {
935 matchResult.failure(
936 location,
937 "Tried to set action state for action group '" + stateIdPair.first
938 + "', but action group wasn't found");
939 }
940 else if (!g_action_group_has_action(stateActionGroup.get(), stateIdPair.second.c_str()))
941 {
942 matchResult.failure(
943 location,
944 "Tried to set action state for action '" + stateAction
945 + "', but action was not found");
946 }
947 else
948 {
949 g_action_group_change_action_state(stateActionGroup.get(), stateIdPair.second.c_str(),
950 g_variant_ref(a.second.get()));
951 }
952
953 // FIXME this is a dodgy way to ensure the action state change gets dispatched
954 menuWaitForItems(menu, 100);
955 }
956
957 for (const auto& a: p->m_activations)
958 {
959 string tmpAction = action;
960 auto tmpIdPair = idPair;
961 auto tmpActionGroup = actionGroup;
962 if (!a.first.empty())
963 {
964 tmpAction = get_string_attribute(menuItem, a.first.c_str());
965 tmpIdPair = split_action(tmpAction);
966 tmpActionGroup = actions[tmpIdPair.first];
967 }
968
969 if (tmpAction.empty())
970 {
971 matchResult.failure(
972 location,
973 "Tried to activate action, but no action was found");
974 }
975 else if(!tmpActionGroup)
976 {
977 matchResult.failure(
978 location,
979 "Tried to activate action group '" + tmpIdPair.first
980 + "', but action group wasn't found");
981 }
982 else if (!g_action_group_has_action(tmpActionGroup.get(), tmpIdPair.second.c_str()))
983 {
984 matchResult.failure(
985 location,
986 "Tried to activate action '" + tmpAction + "', but action was not found");
987 }
988 else
989 {
990 if (a.second)
991 {
992 g_action_group_activate_action(tmpActionGroup.get(), tmpIdPair.second.c_str(),
993 g_variant_ref(a.second.get()));
994 }
995 else
996 {
997 g_action_group_activate_action(tmpActionGroup.get(), tmpIdPair.second.c_str(), nullptr);
998 }
999
1000 // FIXME this is a dodgy way to ensure the activation gets dispatched
1001 menuWaitForItems(menu, 100);
1002 }
1003 }
1004}
1005
1006} // namepsace gmenuharness
1007
1008} // namespace unity
01009
=== added file 'src/gmenuharness/MenuMatcher.cpp'
--- src/gmenuharness/MenuMatcher.cpp 1970-01-01 00:00:00 +0000
+++ src/gmenuharness/MenuMatcher.cpp 2016-01-05 11:11:34 +0000
@@ -0,0 +1,208 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Pete Woods <pete.woods@canonical.com>
17 */
18
19#include <unity/gmenuharness/MenuMatcher.h>
20#include <unity/gmenuharness/MatchUtils.h>
21
22#include <iostream>
23
24#include <gio/gio.h>
25
26using namespace std;
27
28namespace unity
29{
30
31namespace gmenuharness
32{
33
34namespace
35{
36
37static void gdbus_connection_deleter(GDBusConnection* connection)
38{
39// if (!g_dbus_connection_is_closed(connection))
40// {
41// g_dbus_connection_close_sync(connection, nullptr, nullptr);
42// }
43 g_clear_object(&connection);
44}
45}
46
47struct MenuMatcher::Parameters::Priv
48{
49 string m_busName;
50
51 vector<pair<string, string>> m_actions;
52
53 string m_menuObjectPath;
54};
55
56MenuMatcher::Parameters::Parameters(const string& busName,
57 const vector<pair<string, string>>& actions,
58 const string& menuObjectPath) :
59 p(new Priv)
60{
61 p->m_busName = busName;
62 p->m_actions = actions;
63 p->m_menuObjectPath = menuObjectPath;
64}
65
66MenuMatcher::Parameters::~Parameters()
67{
68}
69
70MenuMatcher::Parameters::Parameters(const Parameters& other) :
71 p(new Priv)
72{
73 *this = other;
74}
75
76MenuMatcher::Parameters::Parameters(Parameters&& other)
77{
78 *this = move(other);
79}
80
81MenuMatcher::Parameters& MenuMatcher::Parameters::operator=(const Parameters& other)
82{
83 p->m_busName = other.p->m_busName;
84 p->m_actions = other.p->m_actions;
85 p->m_menuObjectPath = other.p->m_menuObjectPath;
86 return *this;
87}
88
89MenuMatcher::Parameters& MenuMatcher::Parameters::operator=(Parameters&& other)
90{
91 p = move(other.p);
92 return *this;
93}
94
95struct MenuMatcher::Priv
96{
97 Priv(const Parameters& parameters) :
98 m_parameters(parameters)
99 {
100 }
101
102 Parameters m_parameters;
103
104 vector<MenuItemMatcher> m_items;
105
106 shared_ptr<GDBusConnection> m_system;
107
108 shared_ptr<GDBusConnection> m_session;
109
110 shared_ptr<GMenuModel> m_menu;
111
112 map<string, shared_ptr<GActionGroup>> m_actions;
113};
114
115MenuMatcher::MenuMatcher(const Parameters& parameters) :
116 p(new Priv(parameters))
117{
118 p->m_system.reset(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr),
119 &gdbus_connection_deleter);
120 g_dbus_connection_set_exit_on_close(p->m_system.get(), false);
121
122 p->m_session.reset(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr),
123 &gdbus_connection_deleter);
124 g_dbus_connection_set_exit_on_close(p->m_session.get(), false);
125
126 p->m_menu.reset(
127 G_MENU_MODEL(
128 g_dbus_menu_model_get(
129 p->m_session.get(),
130 p->m_parameters.p->m_busName.c_str(),
131 p->m_parameters.p->m_menuObjectPath.c_str())),
132 &g_object_deleter);
133
134 for (const auto& action : p->m_parameters.p->m_actions)
135 {
136 shared_ptr<GActionGroup> actionGroup(
137 G_ACTION_GROUP(
138 g_dbus_action_group_get(
139 p->m_session.get(),
140 p->m_parameters.p->m_busName.c_str(),
141 action.second.c_str())),
142 &g_object_deleter);
143 p->m_actions[action.first] = actionGroup;
144 }
145}
146
147MenuMatcher::~MenuMatcher()
148{
149}
150
151MenuMatcher& MenuMatcher::item(const MenuItemMatcher& item)
152{
153 p->m_items.emplace_back(item);
154 return *this;
155}
156
157void MenuMatcher::match(MatchResult& matchResult) const
158{
159 vector<unsigned int> location;
160
161 while (true)
162 {
163 MatchResult childMatchResult(matchResult.createChild());
164
165 int menuSize = g_menu_model_get_n_items(p->m_menu.get());
166 if (p->m_items.size() > (unsigned int) menuSize)
167 {
168 childMatchResult.failure(
169 location,
170 "Row count mismatch, expected " + to_string(p->m_items.size())
171 + " but found " + to_string(menuSize));
172 }
173 else
174 {
175 for (size_t i = 0; i < p->m_items.size(); ++i)
176 {
177 const auto& matcher = p->m_items.at(i);
178 matcher.match(childMatchResult, location, p->m_menu, p->m_actions, i);
179 }
180 }
181
182 if (childMatchResult.success())
183 {
184 matchResult.merge(childMatchResult);
185 break;
186 }
187 else
188 {
189 if (matchResult.hasTimedOut())
190 {
191 matchResult.merge(childMatchResult);
192 break;
193 }
194 menuWaitForItems(p->m_menu);
195 }
196 }
197}
198
199MatchResult MenuMatcher::match() const
200{
201 MatchResult matchResult;
202 match(matchResult);
203 return matchResult;
204}
205
206} // namespace gmenuharness
207
208} // namespace unity
0209
=== modified file 'src/service.vala'
--- src/service.vala 2015-12-23 11:08:16 +0000
+++ src/service.vala 2016-01-05 11:11:34 +0000
@@ -43,7 +43,7 @@
43 warn_notification.closed.connect((n) => { n.clear_actions(); });43 warn_notification.closed.connect((n) => { n.clear_actions(); });
44 BusWatcher.watch_namespace (GLib.BusType.SESSION,44 BusWatcher.watch_namespace (GLib.BusType.SESSION,
45 "org.freedesktop.Notifications",45 "org.freedesktop.Notifications",
46 () => { debug("Notifications name appeared"); notify_server_caps_checked = false; },46 () => { debug("Notifications name appeared"); },
47 () => { debug("Notifications name vanshed"); notify_server_caps_checked = false; });47 () => { debug("Notifications name vanshed"); notify_server_caps_checked = false; });
4848
49 this.settings = new Settings ("com.canonical.indicator.sound");49 this.settings = new Settings ("com.canonical.indicator.sound");
@@ -52,6 +52,8 @@
52 this.notify["visible"].connect ( () => this.update_root_icon () );52 this.notify["visible"].connect ( () => this.update_root_icon () );
5353
54 this.volume_control = volume;54 this.volume_control = volume;
55 this.volume_control.active_output_changed.connect (this.update_root_icon);
56 this.volume_control.active_output_changed.connect (this.update_notification);
5557
56 this.accounts_service = accounts;58 this.accounts_service = accounts;
57 /* If we're on the greeter, don't export */59 /* If we're on the greeter, don't export */
@@ -90,6 +92,10 @@
90 this.volume_control.bind_property ("high-volume", menu, "show-high-volume-warning", BindingFlags.SYNC_CREATE);92 this.volume_control.bind_property ("high-volume", menu, "show-high-volume-warning", BindingFlags.SYNC_CREATE);
91 });93 });
9294
95 this.menus.@foreach ( (profile, menu) => {
96 this.volume_control.active_output_changed.connect (menu.update_volume_slider);
97 });
98
93 this.sync_preferred_players ();99 this.sync_preferred_players ();
94 this.settings.changed["interested-media-players"].connect ( () => {100 this.settings.changed["interested-media-players"].connect ( () => {
95 this.sync_preferred_players ();101 this.sync_preferred_players ();
@@ -245,17 +251,7 @@
245251
246 void update_root_icon () {252 void update_root_icon () {
247 double volume = this.volume_control.volume.volume;253 double volume = this.volume_control.volume.volume;
248 string icon;254 string icon = get_volume_root_icon (volume, this.volume_control.mute, volume_control.active_output);
249 if (this.volume_control.mute || volume <= 0.0)
250 icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel";
251 else if (this.accounts_service != null && this.accounts_service.silentMode)
252 icon = "audio-volume-muted-panel";
253 else if (volume <= 0.3)
254 icon = "audio-volume-low-panel";
255 else if (volume <= 0.7)
256 icon = "audio-volume-medium-panel";
257 else
258 icon = "audio-volume-high-panel";
259255
260 string accessible_name;256 string accessible_name;
261 if (this.volume_control.mute) {257 if (this.volume_control.mute) {
@@ -281,15 +277,333 @@
281 private bool notify_server_supports_actions = false;277 private bool notify_server_supports_actions = false;
282 private bool notify_server_supports_sync = false;278 private bool notify_server_supports_sync = false;
283 private bool block_info_notifications = false;279 private bool block_info_notifications = false;
280 private bool waiting_user_approve_warn = false;
281
282 private string get_volume_icon (double volume, VolumeControl.ActiveOutput active_output)
283 {
284 string icon = "";
285 switch (active_output)
286 {
287 case VolumeControl.ActiveOutput.SPEAKERS:
288 if (volume <= 0.0)
289 icon = "audio-volume-muted";
290 else if (volume <= 0.3)
291 icon = "audio-volume-low";
292 else if (volume <= 0.7)
293 icon = "audio-volume-medium";
294 else
295 icon = "audio-volume-high";
296 break;
297 case VolumeControl.ActiveOutput.HEADPHONES:
298 if (volume <= 0.0)
299 icon = "audio-volume-muted";
300 else if (volume <= 0.3)
301 icon = "audio-volume-low";
302 else if (volume <= 0.7)
303 icon = "audio-volume-medium";
304 else
305 icon = "audio-volume-high";
306 break;
307 case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES:
308 if (volume <= 0.0)
309 icon = "audio-volume-muted";
310 else if (volume <= 0.3)
311 icon = "audio-volume-low";
312 else if (volume <= 0.7)
313 icon = "audio-volume-medium";
314 else
315 icon = "audio-volume-high";
316 break;
317 case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER:
318 if (volume <= 0.0)
319 icon = "audio-volume-muted";
320 else if (volume <= 0.3)
321 icon = "audio-volume-low";
322 else if (volume <= 0.7)
323 icon = "audio-volume-medium";
324 else
325 icon = "audio-volume-high";
326 break;
327 case VolumeControl.ActiveOutput.USB_SPEAKER:
328 if (volume <= 0.0)
329 icon = "audio-volume-muted";
330 else if (volume <= 0.3)
331 icon = "audio-volume-low";
332 else if (volume <= 0.7)
333 icon = "audio-volume-medium";
334 else
335 icon = "audio-volume-high";
336 break;
337 case VolumeControl.ActiveOutput.USB_HEADPHONES:
338 if (volume <= 0.0)
339 icon = "audio-volume-muted";
340 else if (volume <= 0.3)
341 icon = "audio-volume-low";
342 else if (volume <= 0.7)
343 icon = "audio-volume-medium";
344 else
345 icon = "audio-volume-high";
346 break;
347 case VolumeControl.ActiveOutput.HDMI_SPEAKER:
348 if (volume <= 0.0)
349 icon = "audio-volume-muted";
350 else if (volume <= 0.3)
351 icon = "audio-volume-low";
352 else if (volume <= 0.7)
353 icon = "audio-volume-medium";
354 else
355 icon = "audio-volume-high";
356 break;
357 case VolumeControl.ActiveOutput.HDMI_HEADPHONES:
358 if (volume <= 0.0)
359 icon = "audio-volume-muted";
360 else if (volume <= 0.3)
361 icon = "audio-volume-low";
362 else if (volume <= 0.7)
363 icon = "audio-volume-medium";
364 else
365 icon = "audio-volume-high";
366 break;
367 }
368 return icon;
369 }
370
371 private string get_volume_root_icon_by_volume (double volume, VolumeControl.ActiveOutput active_output)
372 {
373 string icon = "";
374 switch (active_output)
375 {
376 case VolumeControl.ActiveOutput.SPEAKERS:
377 if (volume <= 0.0)
378 icon = "audio-volume-muted-panel";
379 else if (volume <= 0.3)
380 icon = "audio-volume-low-panel";
381 else if (volume <= 0.7)
382 icon = "audio-volume-medium-panel";
383 else
384 icon = "audio-volume-high-panel";
385 break;
386 case VolumeControl.ActiveOutput.HEADPHONES:
387 if (volume <= 0.0)
388 icon = "audio-volume-muted-panel";
389 else if (volume <= 0.3)
390 icon = "audio-volume-low-panel";
391 else if (volume <= 0.7)
392 icon = "audio-volume-medium-panel";
393 else
394 icon = "audio-volume-high-panel";
395 break;
396 case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES:
397 if (volume <= 0.0)
398 icon = "audio-volume-muted-panel";
399 else if (volume <= 0.3)
400 icon = "audio-volume-low-panel";
401 else if (volume <= 0.7)
402 icon = "audio-volume-medium-panel";
403 else
404 icon = "audio-volume-high-panel";
405 break;
406 case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER:
407 if (volume <= 0.0)
408 icon = "audio-volume-muted-panel";
409 else if (volume <= 0.3)
410 icon = "audio-volume-low-panel";
411 else if (volume <= 0.7)
412 icon = "audio-volume-medium-panel";
413 else
414 icon = "audio-volume-high-panel";
415 break;
416 case VolumeControl.ActiveOutput.USB_SPEAKER:
417 if (volume <= 0.0)
418 icon = "audio-volume-muted-panel";
419 else if (volume <= 0.3)
420 icon = "audio-volume-low-panel";
421 else if (volume <= 0.7)
422 icon = "audio-volume-medium-panel";
423 else
424 icon = "audio-volume-high-panel";
425 break;
426 case VolumeControl.ActiveOutput.USB_HEADPHONES:
427 if (volume <= 0.0)
428 icon = "audio-volume-muted-panel";
429 else if (volume <= 0.3)
430 icon = "audio-volume-low-panel";
431 else if (volume <= 0.7)
432 icon = "audio-volume-medium-panel";
433 else
434 icon = "audio-volume-high-panel";
435 break;
436 case VolumeControl.ActiveOutput.HDMI_SPEAKER:
437 if (volume <= 0.0)
438 icon = "audio-volume-muted-panel";
439 else if (volume <= 0.3)
440 icon = "audio-volume-low-panel";
441 else if (volume <= 0.7)
442 icon = "audio-volume-medium-panel";
443 else
444 icon = "audio-volume-high-panel";
445 break;
446 case VolumeControl.ActiveOutput.HDMI_HEADPHONES:
447 if (volume <= 0.0)
448 icon = "audio-volume-muted-panel";
449 else if (volume <= 0.3)
450 icon = "audio-volume-low-panel";
451 else if (volume <= 0.7)
452 icon = "audio-volume-medium-panel";
453 else
454 icon = "audio-volume-high-panel";
455 break;
456 }
457 return icon;
458 }
459
460 private string get_volume_notification_icon (double volume, bool loud, VolumeControl.ActiveOutput active_output) {
461 string icon = "";
462 if (loud) {
463 switch (active_output)
464 {
465 case VolumeControl.ActiveOutput.SPEAKERS:
466 icon = "audio-volume-high";
467 break;
468 case VolumeControl.ActiveOutput.HEADPHONES:
469 icon = "audio-volume-high";
470 break;
471 case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES:
472 icon = "audio-volume-high";
473 break;
474 case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER:
475 icon = "audio-volume-high";
476 break;
477 case VolumeControl.ActiveOutput.USB_SPEAKER:
478 icon = "audio-volume-high";
479 break;
480 case VolumeControl.ActiveOutput.USB_HEADPHONES:
481 icon = "audio-volume-high";
482 break;
483 case VolumeControl.ActiveOutput.HDMI_SPEAKER:
484 icon = "audio-volume-high";
485 break;
486 case VolumeControl.ActiveOutput.HDMI_HEADPHONES:
487 icon = "audio-volume-high";
488 break;
489 }
490 } else {
491 icon = get_volume_icon (volume, active_output);
492 }
493 return icon;
494 }
495
496 private string get_volume_root_icon (double volume, bool mute, VolumeControl.ActiveOutput active_output) {
497 string icon = "";
498 switch (active_output)
499 {
500 case VolumeControl.ActiveOutput.SPEAKERS:
501 if (mute || volume <= 0.0)
502 icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel";
503 else if (this.accounts_service != null && this.accounts_service.silentMode)
504 icon = "audio-volume-muted-panel";
505 else
506 icon = get_volume_root_icon_by_volume (volume, active_output);
507 break;
508 case VolumeControl.ActiveOutput.HEADPHONES:
509 if (mute || volume <= 0.0)
510 icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel";
511 else if (this.accounts_service != null && this.accounts_service.silentMode)
512 icon = "audio-volume-muted-panel";
513 else
514 icon = get_volume_root_icon_by_volume (volume, active_output);
515 break;
516 case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES:
517 if (mute || volume <= 0.0)
518 icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel";
519 else if (this.accounts_service != null && this.accounts_service.silentMode)
520 icon = "audio-volume-muted-panel";
521 else
522 icon = get_volume_root_icon_by_volume (volume, active_output);
523 break;
524 case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER:
525 if (mute || volume <= 0.0)
526 icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel";
527 else if (this.accounts_service != null && this.accounts_service.silentMode)
528 icon = "audio-volume-muted-panel";
529 else
530 icon = get_volume_root_icon_by_volume (volume, active_output);
531 break;
532 case VolumeControl.ActiveOutput.USB_SPEAKER:
533 if (mute || volume <= 0.0)
534 icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel";
535 else if (this.accounts_service != null && this.accounts_service.silentMode)
536 icon = "audio-volume-muted-panel";
537 else
538 icon = get_volume_root_icon_by_volume (volume, active_output);
539 break;
540 case VolumeControl.ActiveOutput.USB_HEADPHONES:
541 if (mute || volume <= 0.0)
542 icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel";
543 else if (this.accounts_service != null && this.accounts_service.silentMode)
544 icon = "audio-volume-muted-panel";
545 else
546 icon = get_volume_root_icon_by_volume (volume, active_output);
547 break;
548 case VolumeControl.ActiveOutput.HDMI_SPEAKER:
549 if (mute || volume <= 0.0)
550 icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel";
551 else if (this.accounts_service != null && this.accounts_service.silentMode)
552 icon = "audio-volume-muted-panel";
553 else
554 icon = get_volume_root_icon_by_volume (volume, active_output);
555 break;
556 case VolumeControl.ActiveOutput.HDMI_HEADPHONES:
557 if (mute || volume <= 0.0)
558 icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel";
559 else if (this.accounts_service != null && this.accounts_service.silentMode)
560 icon = "audio-volume-muted-panel";
561 else
562 icon = get_volume_root_icon_by_volume (volume, active_output);
563 break;
564 }
565 return icon;
566 }
567
568 private string get_notification_label () {
569 string volume_label = "";
570 switch (volume_control.active_output)
571 {
572 case VolumeControl.ActiveOutput.SPEAKERS:
573 volume_label = _("Speakers");
574 break;
575 case VolumeControl.ActiveOutput.HEADPHONES:
576 volume_label = _("Headphones");
577 break;
578 case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES:
579 volume_label = _("Bluetooth headphones");
580 break;
581 case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER:
582 volume_label = _("Bluetooth speaker");
583 break;
584 case VolumeControl.ActiveOutput.USB_SPEAKER:
585 volume_label = _("Usb speaker");
586 break;
587 case VolumeControl.ActiveOutput.USB_HEADPHONES:
588 volume_label = _("Usb headphones");
589 break;
590 case VolumeControl.ActiveOutput.HDMI_SPEAKER:
591 volume_label = _("HDMI speaker");
592 break;
593 case VolumeControl.ActiveOutput.HDMI_HEADPHONES:
594 volume_label = _("HDMI headphones");
595 break;
596 }
597
598 return volume_label;
599 }
284600
285 private void update_notification () {601 private void update_notification () {
286602
287 if (!notify_server_caps_checked) {603 List<string> caps = Notify.get_server_caps ();
288 List<string> caps = Notify.get_server_caps ();604 notify_server_supports_actions = caps.find_custom ("actions", strcmp) != null;
289 notify_server_supports_actions = caps.find_custom ("actions", strcmp) != null;605 notify_server_supports_sync = caps.find_custom ("x-canonical-private-synchronous", strcmp) != null;
290 notify_server_supports_sync = caps.find_custom ("x-canonical-private-synchronous", strcmp) != null;606 notify_server_caps_checked = true;
291 notify_server_caps_checked = true;
292 }
293607
294 var loud = volume_control.high_volume;608 var loud = volume_control.high_volume;
295 var warn = loud609 var warn = loud
@@ -312,47 +626,36 @@
312 _pre_warn_volume = null;626 _pre_warn_volume = null;
313 volume_control.volume = tmp;627 volume_control.volume = tmp;
314 }628 }
629 waiting_user_approve_warn = false;
315 });630 });
316 warn_notification.add_action ("cancel", _("Cancel"), (n, a) => {631 warn_notification.add_action ("cancel", _("Cancel"), (n, a) => {
317 _pre_warn_volume = null;632 _pre_warn_volume = null;
633 waiting_user_approve_warn = false;
318 });634 });
635 waiting_user_approve_warn = true;
319 show_notification(warn_notification);636 show_notification(warn_notification);
320 } else {637 } else {
321 close_notification(warn_notification);638 if (!waiting_user_approve_warn) {
322639 close_notification(warn_notification);
323 if (notify_server_supports_sync && !block_info_notifications) {640
324641 if (notify_server_supports_sync && !block_info_notifications) {
325 /* Determine Label */642
326 unowned string volume_label = loud643 /* Determine Label */
327 ? _("High volume can damage your hearing.")644 string volume_label = get_notification_label ();
328 : "";645
329646 /* Choose an icon */
330 /* Choose an icon */647 string icon = get_volume_notification_icon (volume_control.volume.volume, loud, volume_control.active_output);
331 unowned string icon;648
332 if (loud) {649 /* Reset the notification */
333 icon = "audio-volume-high";650 var n = this.info_notification;
334 } else {651 n.update (_("Volume"), volume_label, icon);
335 var volume = volume_control.volume.volume;652 n.clear_hints();
336653 n.set_hint ("x-canonical-non-shaped-icon", "true");
337 if (volume <= 0.0)654 n.set_hint ("x-canonical-private-synchronous", "true");
338 icon = "audio-volume-muted";655 n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false");
339 else if (volume <= 0.3)656 n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0));
340 icon = "audio-volume-low";657 show_notification(n);
341 else if (volume <= 0.7)
342 icon = "audio-volume-medium";
343 else
344 icon = "audio-volume-high";
345 }658 }
346
347 /* Reset the notification */
348 var n = this.info_notification;
349 n.update (_("Volume"), volume_label, icon);
350 n.clear_hints();
351 n.set_hint ("x-canonical-non-shaped-icon", "true");
352 n.set_hint ("x-canonical-private-synchronous", "true");
353 n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false");
354 n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0));
355 show_notification(n);
356 }659 }
357 }660 }
358 }661 }
359662
=== modified file 'src/sound-menu.vala'
--- src/sound-menu.vala 2015-12-23 11:08:16 +0000
+++ src/sound-menu.vala 2016-01-05 11:11:34 +0000
@@ -71,6 +71,7 @@
71 this.notify_handlers = new HashTable<MediaPlayer, ulong> (direct_hash, direct_equal);71 this.notify_handlers = new HashTable<MediaPlayer, ulong> (direct_hash, direct_equal);
7272
73 this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0;73 this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0;
74
74 }75 }
7576
76 ~SoundMenu () {77 ~SoundMenu () {
@@ -193,6 +194,43 @@
193 this.notify_handlers.remove (player);194 this.notify_handlers.remove (player);
194 }195 }
195196
197 public void update_volume_slider (VolumeControl.ActiveOutput active_output) {
198 int index = find_action (this.volume_section, "indicator.volume");
199 if (index != -1) {
200 string label = "Volume";
201 switch (active_output) {
202 case VolumeControl.ActiveOutput.SPEAKERS:
203 label = _("Volume");
204 break;
205 case VolumeControl.ActiveOutput.HEADPHONES:
206 label = _("Volume (Headphones)");
207 break;
208 case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER:
209 label = _("Volume (Bluetooth)");
210 break;
211 case VolumeControl.ActiveOutput.USB_SPEAKER:
212 label = _("Volume (Usb)");
213 break;
214 case VolumeControl.ActiveOutput.HDMI_SPEAKER:
215 label = _("Volume (HDMI)");
216 break;
217 case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES:
218 label = _("Volume (Bluetooth headphones)");
219 break;
220 case VolumeControl.ActiveOutput.USB_HEADPHONES:
221 label = _("Volume (Usb headphones)");
222 break;
223 case VolumeControl.ActiveOutput.HDMI_HEADPHONES:
224 label = _("Volume (HDMI headphones)");
225 break;
226 }
227 this.volume_section.remove (index);
228 this.volume_section.insert_item (index, this.create_slider_menu_item (_(label), "indicator.volume(0)", 0.0, 1.0, 0.01,
229 "audio-volume-low-zero-panel",
230 "audio-volume-high-panel"));
231 }
232 }
233
196 public Menu root;234 public Menu root;
197 public Menu menu;235 public Menu menu;
198 Menu volume_section;236 Menu volume_section;
199237
=== modified file 'src/volume-control-pulse.vala'
--- src/volume-control-pulse.vala 2015-12-23 11:08:16 +0000
+++ src/volume-control-pulse.vala 2016-01-05 11:11:34 +0000
@@ -87,6 +87,7 @@
87 private bool _send_next_local_volume = false;87 private bool _send_next_local_volume = false;
88 private double _account_service_volume = 0.0;88 private double _account_service_volume = 0.0;
89 private bool _active_port_headphone = false;89 private bool _active_port_headphone = false;
90 private VolumeControl.ActiveOutput _active_output = VolumeControl.ActiveOutput.SPEAKERS;
9091
91 /** true when connected to the pulse server */92 /** true when connected to the pulse server */
92 public override bool ready { get; private set; }93 public override bool ready { get; private set; }
@@ -135,6 +136,49 @@
135 stop_high_volume_approved_timer();136 stop_high_volume_approved_timer();
136 }137 }
137138
139 private VolumeControl.ActiveOutput calculate_active_output (SinkInfo? sink) {
140
141 VolumeControl.ActiveOutput ret_output = VolumeControl.ActiveOutput.SPEAKERS;
142 /* Check if the current active port is headset/headphone */
143 /* There is not easy way to check if the port is a headset/headphone besides
144 * checking for the port name. On touch (with the pulseaudio droid element)
145 * the headset/headphone port is called 'output-headset' and 'output-headphone'.
146 * On the desktop this is usually called 'analog-output-headphones' */
147 // look if it's a headset/headphones
148 if (sink.name == "indicator_sound_test_headphones" ||
149 (sink.active_port != null &&
150 (sink.active_port.name.contains("headset") ||
151 sink.active_port.name.contains("headphone")))) {
152 _active_port_headphone = true;
153 // check if it's a bluetooth device
154 var device_bus = sink.proplist.gets ("device.bus");
155 if (device_bus != null && device_bus == "bluetooth") {
156 ret_output = VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES;
157 } else if (device_bus != null && device_bus == "usb") {
158 ret_output = VolumeControl.ActiveOutput.USB_HEADPHONES;
159 } else if (device_bus != null && device_bus == "hdmi") {
160 ret_output = VolumeControl.ActiveOutput.HDMI_HEADPHONES;
161 } else {
162 ret_output = VolumeControl.ActiveOutput.HEADPHONES;
163 }
164 } else {
165 // speaker
166 _active_port_headphone = false;
167 var device_bus = sink.proplist.gets ("device.bus");
168 if (device_bus != null && device_bus == "bluetooth") {
169 ret_output = VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER;
170 } else if (device_bus != null && device_bus == "usb") {
171 ret_output = VolumeControl.ActiveOutput.USB_SPEAKER;
172 } else if (device_bus != null && device_bus == "hdmi") {
173 ret_output = VolumeControl.ActiveOutput.HDMI_SPEAKER;
174 } else {
175 ret_output = VolumeControl.ActiveOutput.SPEAKERS;
176 }
177 }
178
179 return ret_output;
180 }
181
138 /* PulseAudio logic*/182 /* PulseAudio logic*/
139 private void context_events_cb (Context c, Context.SubscriptionEventType t, uint32 index)183 private void context_events_cb (Context c, Context.SubscriptionEventType t, uint32 index)
140 {184 {
@@ -201,18 +245,20 @@
201 this.notify_property ("is-playing");245 this.notify_property ("is-playing");
202 }246 }
203247
204 /* Check if the current active port is headset/headphone */248 // store the current status of the active output
205 /* There is not easy way to check if the port is a headset/headphone besides249 VolumeControl.ActiveOutput active_output_before = active_output;
206 * checking for the port name. On touch (with the pulseaudio droid element)250
207 * the headset/headphone port is called 'output-headset' and 'output-headphone'.251 // calculate the output
208 * On the desktop this is usually called 'analog-output-headphones' */252 _active_output = calculate_active_output (i);
209 if (i.active_port != null &&253
210 (i.active_port.name == "output-wired_headset" ||254 // check if the output has changed, if so... emit a signal
211 i.active_port.name == "output-wired_headphone" ||255 VolumeControl.ActiveOutput active_output_now = active_output;
212 i.active_port.name == "analog-output-headphones")) {256 if (active_output_now != active_output_before) {
213 _active_port_headphone = true;257 this.active_output_changed (active_output_now);
214 } else {258 if (active_output_now == VolumeControl.ActiveOutput.SPEAKERS) {
215 _active_port_headphone = false;259 _high_volume_approved = false;
260 }
261 update_high_volume();
216 }262 }
217263
218 if (_pulse_use_stream_restore == false &&264 if (_pulse_use_stream_restore == false &&
@@ -478,7 +524,8 @@
478 this.context = new PulseAudio.Context (loop.get_api(), null, props);524 this.context = new PulseAudio.Context (loop.get_api(), null, props);
479 this.context.set_state_callback (context_state_callback);525 this.context.set_state_callback (context_state_callback);
480526
481 if (context.connect(null, Context.Flags.NOFAIL, null) < 0)527 var server_string = Environment.get_variable("PULSE_SERVER");
528 if (context.connect(server_string, Context.Flags.NOFAIL, null) < 0)
482 warning( "pa_context_connect() failed: %s\n", PulseAudio.strerror(context.errno()));529 warning( "pa_context_connect() failed: %s\n", PulseAudio.strerror(context.errno()));
483 }530 }
484531
@@ -535,6 +582,14 @@
535 }582 }
536 }583 }
537584
585 public override VolumeControl.ActiveOutput active_output
586 {
587 get
588 {
589 return _active_output;
590 }
591 }
592
538 /* Volume operations */593 /* Volume operations */
539 private static PulseAudio.Volume double_to_volume (double vol)594 private static PulseAudio.Volume double_to_volume (double vol)
540 {595 {
@@ -710,7 +765,7 @@
710 private bool calculate_high_volume_from_volume(double volume) {765 private bool calculate_high_volume_from_volume(double volume) {
711 return _active_port_headphone766 return _active_port_headphone
712 && _warning_volume_enabled767 && _warning_volume_enabled
713 && volume >= _warning_volume_norms768 && volume > _warning_volume_norms
714 && (stream == "multimedia");769 && (stream == "multimedia");
715 }770 }
716771
717772
=== modified file 'src/volume-control.vala'
--- src/volume-control.vala 2015-12-23 11:08:16 +0000
+++ src/volume-control.vala 2016-01-05 11:11:34 +0000
@@ -28,6 +28,17 @@
28 VOLUME_STREAM_CHANGE28 VOLUME_STREAM_CHANGE
29 }29 }
3030
31 public enum ActiveOutput {
32 SPEAKERS,
33 HEADPHONES,
34 BLUETOOTH_HEADPHONES,
35 BLUETOOTH_SPEAKER,
36 USB_SPEAKER,
37 USB_HEADPHONES,
38 HDMI_SPEAKER,
39 HDMI_HEADPHONES
40 }
41
31 public class Volume : Object {42 public class Volume : Object {
32 public double volume;43 public double volume;
33 public VolumeReasons reason;44 public VolumeReasons reason;
@@ -39,6 +50,7 @@
39 public virtual bool high_volume { get { return false; } protected set { } }50 public virtual bool high_volume { get { return false; } protected set { } }
40 public virtual bool mute { get { return false; } }51 public virtual bool mute { get { return false; } }
41 public virtual bool is_playing { get { return false; } }52 public virtual bool is_playing { get { return false; } }
53 public virtual VolumeControl.ActiveOutput active_output { get { return VolumeControl.ActiveOutput.SPEAKERS; } }
42 private Volume _volume;54 private Volume _volume;
43 public virtual Volume volume { get { return _volume; } set { } }55 public virtual Volume volume { get { return _volume; } set { } }
44 public virtual double mic_volume { get { return 0.0; } set { } }56 public virtual double mic_volume { get { return 0.0; } set { } }
@@ -56,4 +68,6 @@
56 v.reason = reason;68 v.reason = reason;
57 this.volume = v;69 this.volume = v;
58 }70 }
71
72 public signal void active_output_changed (VolumeControl.ActiveOutput active_output);
59}73}
6074
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2015-12-23 11:08:16 +0000
+++ tests/CMakeLists.txt 2016-01-05 11:11:34 +0000
@@ -5,10 +5,10 @@
55
6include_directories(${GTEST_INCLUDE_DIR})6include_directories(${GTEST_INCLUDE_DIR})
77
8add_library (gtest STATIC8add_library (gtest-static STATIC
9 ${GTEST_SOURCE_DIR}/gtest-all.cc9 ${GTEST_SOURCE_DIR}/gtest-all.cc
10 ${GTEST_SOURCE_DIR}/gtest_main.cc)10 ${GTEST_SOURCE_DIR}/gtest_main.cc)
11target_link_libraries(gtest ${GTEST_LIBS})11target_link_libraries(gtest-static ${GTEST_LIBS})
1212
13###########################13###########################
14# GSettings Schema14# GSettings Schema
@@ -22,7 +22,8 @@
22# GSettings:22# GSettings:
23# compile the indicator-sound schema into a gschemas.compiled file in this directory,23# compile the indicator-sound schema into a gschemas.compiled file in this directory,
24# and help the tests to find that file by setting -DSCHEMA_DIR24# and help the tests to find that file by setting -DSCHEMA_DIR
25set (SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}/gsettings-schemas")25set (XDG_DATA_DIRS "${CMAKE_CURRENT_BINARY_DIR}/gsettings-schemas")
26set (SCHEMA_DIR "${XDG_DATA_DIRS}/glib-2.0/schemas")
26add_definitions(-DSCHEMA_DIR="${SCHEMA_DIR}")27add_definitions(-DSCHEMA_DIR="${SCHEMA_DIR}")
27execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas28execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas
28 OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE29 OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE
@@ -31,6 +32,7 @@
31 DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.sound.gschema.xml32 DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.sound.gschema.xml
32 COMMAND mkdir -p ${SCHEMA_DIR}33 COMMAND mkdir -p ${SCHEMA_DIR}
33 COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR}34 COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR}
35 COMMAND cp -f /usr/share/glib-2.0/schemas/com.ubuntu.sound.gschema.xml ${SCHEMA_DIR}
34 COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR})36 COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR})
3537
36###########################38###########################
@@ -41,74 +43,74 @@
41set(VALA_MOCKS_SYMBOLS_PATH "${CMAKE_CURRENT_BINARY_DIR}/vala-mocks.def")43set(VALA_MOCKS_SYMBOLS_PATH "${CMAKE_CURRENT_BINARY_DIR}/vala-mocks.def")
4244
43vala_init(vala-mocks45vala_init(vala-mocks
44 DEPENDS46 DEPENDS
45 indicator-sound-service-lib47 indicator-sound-service-lib
46 PACKAGES48 PACKAGES
47 config49 config
48 gio-2.050 gio-2.0
49 gio-unix-2.051 gio-unix-2.0
50 libxml-2.052 libxml-2.0
51 libpulse53 libpulse
52 libpulse-mainloop-glib54 libpulse-mainloop-glib
53 libnotify55 libnotify
54 accounts-service56 accounts-service
55 indicator-sound-service57 indicator-sound-service
56 OPTIONS58 OPTIONS
57 --ccode59 --ccode
58 --thread60 --thread
59 --vapidir=${CMAKE_BINARY_DIR}/src/61 --vapidir=${CMAKE_BINARY_DIR}/src/
60 --vapidir=${CMAKE_SOURCE_DIR}/vapi/62 --vapidir=${CMAKE_SOURCE_DIR}/vapi/
61 --vapidir=.63 --vapidir=.
62)64)
6365
64vala_add(vala-mocks66vala_add(vala-mocks
65 media-player-mock.vala67 media-player-mock.vala
66)68)
6769
68vala_add(vala-mocks70vala_add(vala-mocks
69 media-player-list-mock.vala71 media-player-list-mock.vala
70)72)
7173
72vala_add(vala-mocks74vala_add(vala-mocks
73 volume-control-mock.vala75 volume-control-mock.vala
74)76)
7577
76vala_finish(vala-mocks78vala_finish(vala-mocks
77 SOURCES79 SOURCES
78 vala_mocks_VALA_SOURCES80 vala_mocks_VALA_SOURCES
79 OUTPUTS81 OUTPUTS
80 vala_mocks_VALA_C82 vala_mocks_VALA_C
81 GENERATE_HEADER83 GENERATE_HEADER
82 ${VALA_MOCKS_HEADER_PATH}84 ${VALA_MOCKS_HEADER_PATH}
83 GENERATE_SYMBOLS85 GENERATE_SYMBOLS
84 ${VALA_MOCKS_SYMBOLS_PATH}86 ${VALA_MOCKS_SYMBOLS_PATH}
85)87)
8688
87set_source_files_properties(89set_source_files_properties(
88 ${vala_mocks_VALA_SOURCES}90 ${vala_mocks_VALA_SOURCES}
89 PROPERTIES91 PROPERTIES
90 HEADER_FILE_ONLY TRUE92 HEADER_FILE_ONLY TRUE
91)93)
9294
93set(95set(
94 VALA_MOCKS_SOURCES96 VALA_MOCKS_SOURCES
95 ${vala_mocks_VALA_SOURCES}97 ${vala_mocks_VALA_SOURCES}
96 ${vala_mocks_VALA_C}98 ${vala_mocks_VALA_C}
97 ${VALA_MOCKS_SYMBOLS_PATH}99 ${VALA_MOCKS_SYMBOLS_PATH}
98)100)
99101
100add_definitions(102add_definitions(
101 -Wno-unused-but-set-variable103 -Wno-unused-but-set-variable
102)104)
103105
104add_library(106add_library(
105 vala-mocks-lib STATIC107 vala-mocks-lib STATIC
106 ${VALA_MOCKS_SOURCES}108 ${VALA_MOCKS_SOURCES}
107)109)
108110
109target_link_libraries(111target_link_libraries(
110 vala-mocks-lib112 vala-mocks-lib
111 indicator-sound-service-lib113 indicator-sound-service-lib
112)114)
113115
114include_directories(${CMAKE_CURRENT_BINARY_DIR})116include_directories(${CMAKE_CURRENT_BINARY_DIR})
@@ -118,9 +120,9 @@
118###########################120###########################
119121
120add_library(122add_library(
121 pulse-mock123 pulse-mock
122 SHARED124 SHARED
123 pa-mock.cpp125 pa-mock.cpp
124)126)
125127
126target_link_libraries (pulse-mock ${PULSEAUDIO_LIBRARIES})128target_link_libraries (pulse-mock ${PULSEAUDIO_LIBRARIES})
@@ -131,7 +133,7 @@
131133
132include_directories(${CMAKE_SOURCE_DIR}/src)134include_directories(${CMAKE_SOURCE_DIR}/src)
133add_executable (name-watch-test name-watch-test.cc ${CMAKE_SOURCE_DIR}/src/bus-watch-namespace.c)135add_executable (name-watch-test name-watch-test.cc ${CMAKE_SOURCE_DIR}/src/bus-watch-namespace.c)
134target_link_libraries (name-watch-test gtest ${SOUNDSERVICE_LIBRARIES})136target_link_libraries (name-watch-test gtest-static ${SOUNDSERVICE_LIBRARIES})
135add_test(name-watch-test name-watch-test)137add_test(name-watch-test name-watch-test)
136138
137###########################139###########################
@@ -141,21 +143,21 @@
141include_directories(${CMAKE_SOURCE_DIR}/src)143include_directories(${CMAKE_SOURCE_DIR}/src)
142add_executable (accounts-service-user-test accounts-service-user.cc)144add_executable (accounts-service-user-test accounts-service-user.cc)
143target_link_libraries (145target_link_libraries (
144 accounts-service-user-test146 accounts-service-user-test
145 indicator-sound-service-lib147 indicator-sound-service-lib
146 vala-mocks-lib148 vala-mocks-lib
147 gtest149 gtest-static
148 ${SOUNDSERVICE_LIBRARIES}150 ${SOUNDSERVICE_LIBRARIES}
149 ${TEST_LIBRARIES}151 ${TEST_LIBRARIES}
150)152)
151153
152# Split tests to work around libaccountservice sucking154# Split tests to work around libaccountservice sucking
153add_test(accounts-service-user-test-basic155add_test(accounts-service-user-test-basic
154 accounts-service-user-test --gtest_filter=AccountsServiceUserTest.BasicObject156 accounts-service-user-test --gtest_filter=AccountsServiceUserTest.BasicObject
155)157)
156158
157add_test(accounts-service-user-test-player159add_test(accounts-service-user-test-player
158 accounts-service-user-test --gtest_filter=AccountsServiceUserTest.SetMediaPlayer160 accounts-service-user-test --gtest_filter=AccountsServiceUserTest.SetMediaPlayer
159)161)
160162
161###########################163###########################
@@ -165,11 +167,11 @@
165include_directories(${CMAKE_SOURCE_DIR}/src)167include_directories(${CMAKE_SOURCE_DIR}/src)
166add_executable (volume-control-test volume-control-test.cc gschemas.compiled)168add_executable (volume-control-test volume-control-test.cc gschemas.compiled)
167target_link_libraries (169target_link_libraries (
168 volume-control-test170 volume-control-test
169 indicator-sound-service-lib171 indicator-sound-service-lib
170 pulse-mock172 pulse-mock
171 gtest173 gtest-static
172 ${TEST_LIBRARIES}174 ${TEST_LIBRARIES}
173)175)
174176
175add_test(volume-control-test volume-control-test)177add_test(volume-control-test volume-control-test)
@@ -181,12 +183,12 @@
181include_directories(${CMAKE_SOURCE_DIR}/src)183include_directories(${CMAKE_SOURCE_DIR}/src)
182add_executable (sound-menu-test sound-menu.cc)184add_executable (sound-menu-test sound-menu.cc)
183target_link_libraries (185target_link_libraries (
184 sound-menu-test186 sound-menu-test
185 indicator-sound-service-lib187 indicator-sound-service-lib
186 vala-mocks-lib188 vala-mocks-lib
187 gtest189 gtest-static
188 ${SOUNDSERVICE_LIBRARIES}190 ${SOUNDSERVICE_LIBRARIES}
189 ${TEST_LIBRARIES}191 ${TEST_LIBRARIES}
190)192)
191193
192add_test(sound-menu-test sound-menu-test)194add_test(sound-menu-test sound-menu-test)
@@ -198,13 +200,13 @@
198include_directories(${CMAKE_SOURCE_DIR}/src)200include_directories(${CMAKE_SOURCE_DIR}/src)
199add_executable (notifications-test notifications-test.cc)201add_executable (notifications-test notifications-test.cc)
200target_link_libraries (202target_link_libraries (
201 notifications-test203 notifications-test
202 indicator-sound-service-lib204 indicator-sound-service-lib
203 vala-mocks-lib205 vala-mocks-lib
204 pulse-mock206 pulse-mock
205 gtest207 gtest-static
206 ${SOUNDSERVICE_LIBRARIES}208 ${SOUNDSERVICE_LIBRARIES}
207 ${TEST_LIBRARIES}209 ${TEST_LIBRARIES}
208)210)
209211
210add_test(notifications-test notifications-test)212add_test(notifications-test notifications-test)
@@ -216,23 +218,23 @@
216include_directories(${CMAKE_SOURCE_DIR}/src)218include_directories(${CMAKE_SOURCE_DIR}/src)
217add_executable (media-player-user-test media-player-user.cc)219add_executable (media-player-user-test media-player-user.cc)
218target_link_libraries (220target_link_libraries (
219 media-player-user-test221 media-player-user-test
220 indicator-sound-service-lib222 indicator-sound-service-lib
221 vala-mocks-lib223 vala-mocks-lib
222 gtest224 gtest-static
223 ${SOUNDSERVICE_LIBRARIES}225 ${SOUNDSERVICE_LIBRARIES}
224 ${TEST_LIBRARIES}226 ${TEST_LIBRARIES}
225)227)
226228
227# Split tests to work around libaccountservice sucking229# Split tests to work around libaccountservice sucking
228add_test(media-player-user-test-basic230add_test(media-player-user-test-basic
229 media-player-user-test --gtest_filter=MediaPlayerUserTest.BasicObject231 media-player-user-test --gtest_filter=MediaPlayerUserTest.BasicObject
230)232)
231add_test(media-player-user-test-dataset233add_test(media-player-user-test-dataset
232 media-player-user-test --gtest_filter=MediaPlayerUserTest.DataSet234 media-player-user-test --gtest_filter=MediaPlayerUserTest.DataSet
233)235)
234add_test(media-player-user-test-timeout236add_test(media-player-user-test-timeout
235 media-player-user-test --gtest_filter=MediaPlayerUserTest.TimeoutTest237 media-player-user-test --gtest_filter=MediaPlayerUserTest.TimeoutTest
236)238)
237239
238###########################240###########################
@@ -242,20 +244,20 @@
242include_directories(${CMAKE_SOURCE_DIR}/src)244include_directories(${CMAKE_SOURCE_DIR}/src)
243add_executable (greeter-list-test greeter-list.cc)245add_executable (greeter-list-test greeter-list.cc)
244target_link_libraries (246target_link_libraries (
245 greeter-list-test247 greeter-list-test
246 indicator-sound-service-lib248 indicator-sound-service-lib
247 vala-mocks-lib249 vala-mocks-lib
248 gtest250 gtest-static
249 ${SOUNDSERVICE_LIBRARIES}251 ${SOUNDSERVICE_LIBRARIES}
250 ${TEST_LIBRARIES}252 ${TEST_LIBRARIES}
251)253)
252254
253# Split tests to work around libaccountservice sucking255# Split tests to work around libaccountservice sucking
254add_test(greeter-list-test-basic256add_test(greeter-list-test-basic
255 greeter-list-test --gtest_filter=GreeterListTest.BasicObject257 greeter-list-test --gtest_filter=GreeterListTest.BasicObject
256)258)
257add_test(greeter-list-test-iterator259add_test(greeter-list-test-iterator
258 greeter-list-test --gtest_filter=GreeterListTest.BasicIterator260 greeter-list-test --gtest_filter=GreeterListTest.BasicIterator
259)261)
260262
261###########################263###########################
@@ -263,18 +265,23 @@
263###########################265###########################
264266
265add_definitions(267add_definitions(
266 -DINDICATOR_SOUND_SERVICE_BINARY="${CMAKE_BINARY_DIR}/src/indicator-sound-service"268 -DINDICATOR_SOUND_SERVICE_BINARY="${CMAKE_BINARY_DIR}/src/indicator-sound-service"
267 -DPA_MOCK_LIB="${CMAKE_CURRENT_BINARY_DIR}/libpulse-mock.so"269 -DPA_MOCK_LIB="${CMAKE_CURRENT_BINARY_DIR}/libpulse-mock.so"
268)270)
269add_executable (indicator-test indicator-test.cc gschemas.compiled)271add_executable (indicator-test indicator-test.cc gschemas.compiled)
270target_link_libraries (272target_link_libraries (
271 indicator-test273 indicator-test
272 gtest274 gtest-static
273 ${SOUNDSERVICE_LIBRARIES}275 ${SOUNDSERVICE_LIBRARIES}
274 ${TEST_LIBRARIES}276 ${TEST_LIBRARIES}
275)277)
276278
277# Split tests to work around libaccountservice sucking279# Split tests to work around libaccountservice sucking
278add_test(indcator-test280add_test(indcator-test
279 indicator-test281 indicator-test
280)282)
283
284# Disable integration tests
285# add_subdirectory(integration)
286# add_subdirectory(dbus-types)
287# add_subdirectory(service-mocks)
281288
=== added directory 'tests/dbus-types'
=== added file 'tests/dbus-types/CMakeLists.txt'
--- tests/dbus-types/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/CMakeLists.txt 2016-01-05 11:11:34 +0000
@@ -0,0 +1,53 @@
1set(CMAKE_AUTOMOC ON)
2set(CMAKE_INCLUDE_CURRENT_DIR ON)
3
4find_package(Qt5DBus REQUIRED)
5include_directories(${Qt5DBus_INCLUDE_DIRS})
6
7add_definitions(-DQT_NO_KEYWORDS=1)
8
9set(dbusinterface_streamrestore_xml "org.PulseAudio.Ext.StreamRestore1.xml")
10set_source_files_properties(${dbusinterface_streamrestore_xml} PROPERTIES
11 CLASSNAME StreamRestoreInterface)
12
13set(dbusinterface_accounts_xml "org.freedesktop.Accounts.xml")
14set_source_files_properties(${dbusinterface_accounts_xml} PROPERTIES
15 CLASSNAME AccountsInterface)
16
17set(dbusinterface_accountssound_xml "com.ubuntu.AccountsService.Sound.xml")
18set_source_files_properties(${dbusinterface_accountssound_xml} PROPERTIES
19 CLASSNAME AccountsSoundInterface)
20
21set(dbusinterface_properties_xml "org.freedesktop.DBus.Properties.xml")
22set_source_files_properties(${dbusinterface_properties_xml} PROPERTIES
23 CLASSNAME DBusPropertiesInterface
24 NO_NAMESPACE YES
25 INCLUDE "dbus-types.h")
26
27set(dbusinterface_actions_xml "org.gtk.Actions.xml")
28set_source_files_properties(${dbusinterface_actions_xml} PROPERTIES
29 CLASSNAME MenusInterface)
30
31set(dbusinterface_notifications_xml "org.freedesktop.Notifications.xml")
32set_source_files_properties(${dbusinterface_notifications_xml} PROPERTIES
33 CLASSNAME NotificationsInterface)
34
35qt5_add_dbus_interface(interface_files ${dbusinterface_streamrestore_xml} stream_restore_interface)
36qt5_add_dbus_interface(interface_files ${dbusinterface_properties_xml} dbus_properties_interface)
37qt5_add_dbus_interface(interface_files ${dbusinterface_accounts_xml} dbus_accounts_interface)
38qt5_add_dbus_interface(interface_files ${dbusinterface_accountssound_xml} dbus_accountssound_interface)
39qt5_add_dbus_interface(interface_files ${dbusinterface_actions_xml} dbus_menus_interface)
40qt5_add_dbus_interface(interface_files ${dbusinterface_notifications_xml} dbus_notifications_interface)
41
42add_library(
43 sound-indicator-dbus-interfaces
44 STATIC
45 ${interface_files}
46 pulseaudio-volume.cpp
47)
48
49qt5_use_modules(
50 sound-indicator-dbus-interfaces
51 Core
52 DBus
53)
054
=== added file 'tests/dbus-types/com.ubuntu.AccountsService.Sound.xml'
--- tests/dbus-types/com.ubuntu.AccountsService.Sound.xml 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/com.ubuntu.AccountsService.Sound.xml 2016-01-05 11:11:34 +0000
@@ -0,0 +1,9 @@
1<node>
2 <interface name="com.ubuntu.AccountsService.Sound">
3 <method name="Set">
4 <arg direction="in" type="s" name="interface" />
5 <arg direction="in" type="s" name="property" />
6 <arg direction="out" type="o" name="path" />
7 </method>
8 </interface>
9</node>
010
=== added file 'tests/dbus-types/dbus-types.h'
--- tests/dbus-types/dbus-types.h 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/dbus-types.h 2016-01-05 11:11:34 +0000
@@ -0,0 +1,48 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Xavi Garcia <xavi.garcia.mena@canonical.com>
17 */
18#pragma once
19
20#include <QDBusMetaType>
21#include "pulseaudio-volume.h"
22
23namespace DBusTypes
24{
25 inline void registerMetaTypes()
26 {
27 PulseaudioVolume::registerMetaType();
28 PulseaudioVolumeArray::registerMetaType();
29 }
30
31 static constexpr char const* DBUS_NAME = "com.canonical.indicator.sound";
32
33 static constexpr char const* DBUS_PULSE = "org.PulseAudio1";
34
35 static constexpr char const* ACCOUNTS_SERVICE = "org.freedesktop.Accounts";
36
37 static constexpr char const* STREAM_RESTORE_NAME = "org.PulseAudio.Ext.StreamRestore1";
38
39 static constexpr char const* STREAM_RESTORE_PATH = "/org/pulseaudio/stream_restore1";
40
41 static constexpr char const* STREAM_RESTORE_ENTRY_NAME = "org.PulseAudio.Ext.StreamRestore1.RestoreEntry";
42
43 static constexpr char const* MAIN_SERVICE_PATH = "/com/canonical/indicator/sound";
44
45 static constexpr char const* ACTIONS_INTERFACE = "org.gtk.Actions";
46
47} // namespace DBusTypes
48
049
=== added file 'tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml'
--- tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml 2016-01-05 11:11:34 +0000
@@ -0,0 +1,7 @@
1<node>
2 <interface name="org.PulseAudio.Ext.StreamRestore1">
3 <method name="GetEntryByName">
4 <arg direction="in" type="s" name="entry" />
5 <arg direction="out" type="o" name="value" />
6 </interface>
7</node>
08
=== added file 'tests/dbus-types/org.freedesktop.Accounts.xml'
--- tests/dbus-types/org.freedesktop.Accounts.xml 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/org.freedesktop.Accounts.xml 2016-01-05 11:11:34 +0000
@@ -0,0 +1,9 @@
1<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
2<node>
3 <interface name="org.freedesktop.Accounts">
4 <method name="FindUserByName">
5 <arg direction="in" type="s" name="user" />
6 <arg direction="out" type="o" name="path" />
7 </method>
8 </interface>
9</node>
010
=== added file 'tests/dbus-types/org.freedesktop.DBus.Properties.xml'
--- tests/dbus-types/org.freedesktop.DBus.Properties.xml 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/org.freedesktop.DBus.Properties.xml 2016-01-05 11:11:34 +0000
@@ -0,0 +1,22 @@
1<node>
2 <interface name="org.freedesktop.DBus.Properties">
3 <method name="Set">
4 <arg direction="in" type="s" name="entry" />
5 <arg direction="in" type="s" name="property" />
6 <arg direction="in" type="v" name="value" />
7 </method>
8
9 <method name="Get">
10 <arg direction="in" type="s" name="entry" />
11 <arg direction="in" type="s" name="property" />
12 <arg direction="out" type="v" name="value" />
13 </method>
14
15 <signal name="PropertiesChanged">
16 <arg type="s" name="interface_name"/>
17 <arg type="a{sv}" name="changed_properties"/>
18 <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
19 <arg type="as" name="invalidated_properties"/>
20 </signal>
21 </interface>
22</node>
023
=== added file 'tests/dbus-types/org.freedesktop.Notifications.xml'
--- tests/dbus-types/org.freedesktop.Notifications.xml 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/org.freedesktop.Notifications.xml 2016-01-05 11:11:34 +0000
@@ -0,0 +1,47 @@
1<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
2"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
3<node>
4 <interface name="org.freedesktop.Notifications">
5 <signal name="NotificationClosed">
6 <arg name="id" type="u" direction="out"/>
7 <arg name="reason" type="u" direction="out"/>
8 </signal>
9 <signal name="ActionInvoked">
10 <arg name="id" type="u" direction="out"/>
11 <arg name="action_key" type="s" direction="out"/>
12 </signal>
13 <signal name="dataChanged">
14 <arg name="id" type="u" direction="out"/>
15 </signal>
16 <method name="CloseNotification">
17 <arg name="id" type="u" direction="in"/>
18 </method>
19 <method name="GetServerInformation">
20 <arg name="name" type="s" direction="out"/>
21 <arg name="vendor" type="s" direction="out"/>
22 <arg name="version" type="s" direction="out"/>
23 <arg name="specVersion" type="s" direction="out"/>
24 </method>
25 <method name="GetCapabilities">
26 <arg type="as" direction="out"/>
27 </method>
28 <method name="Notify">
29 <arg type="u" direction="out"/>
30 <arg name="app_name" type="s" direction="in"/>
31 <arg name="replaces_id" type="u" direction="in"/>
32 <arg name="app_icon" type="s" direction="in"/>
33 <arg name="summary" type="s" direction="in"/>
34 <arg name="body" type="s" direction="in"/>
35 <arg name="actions" type="as" direction="in"/>
36 <arg name="hints" type="a{sv}" direction="in"/>
37 <annotation name="org.qtproject.QtDBus.QtTypeName.In6" value="QVariantMap"/>
38 <arg name="expire_timeout" type="i" direction="in"/>
39 </method>
40 <method name="onDataChanged">
41 <arg name="id" type="u" direction="in"/>
42 </method>
43 <method name="onCompleted">
44 <arg name="id" type="u" direction="in"/>
45 </method>
46 </interface>
47</node>
048
=== added file 'tests/dbus-types/org.gtk.Actions.xml'
--- tests/dbus-types/org.gtk.Actions.xml 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/org.gtk.Actions.xml 2016-01-05 11:11:34 +0000
@@ -0,0 +1,13 @@
1<node>
2 <interface name="org.gtk.Actions">
3 <signal name="Changed">
4 <arg name="removedActions" type="as" direction="in" />
5 <arg name="actionsFlags" type="a{sb}" direction="in" />
6 <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
7 <arg name="actionsChanged" type="a{sv}" direction="in" />
8 <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/>
9 <arg name="actionsAdded" type="a{s(bgav)}" direction="in" />
10 <annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QVariantMap"/>
11 </signal>
12 </interface>
13</node>
014
=== added file 'tests/dbus-types/pulseaudio-volume.cpp'
--- tests/dbus-types/pulseaudio-volume.cpp 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/pulseaudio-volume.cpp 2016-01-05 11:11:34 +0000
@@ -0,0 +1,156 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Xavi Garcia <xavi.garcia.mena@canonical.com>
17 */
18#include "dbus-types.h"
19
20PulseaudioVolume::PulseaudioVolume() :
21 type_(0),
22 volume_(10)
23{
24}
25
26PulseaudioVolume::PulseaudioVolume(unsigned int type, unsigned int volume) :
27 type_(type)
28 , volume_(volume)
29{
30}
31
32PulseaudioVolume::PulseaudioVolume(const PulseaudioVolume &other) :
33 type_(other.type_),
34 volume_(other.volume_)
35{
36}
37
38PulseaudioVolume& PulseaudioVolume::operator=(const PulseaudioVolume &other)
39{
40 type_ = other.type_;
41 volume_ = other.volume_;
42
43 return *this;
44}
45
46PulseaudioVolume::~PulseaudioVolume()
47{
48}
49
50unsigned int PulseaudioVolume::getType() const
51{
52 return type_;
53}
54
55unsigned int PulseaudioVolume::getVolume() const
56{
57 return volume_;
58}
59
60void PulseaudioVolume::registerMetaType()
61{
62 qRegisterMetaType<PulseaudioVolume>("PulseaudioVolume");
63
64 qDBusRegisterMetaType<PulseaudioVolume>();
65}
66
67QDBusArgument &operator<<(QDBusArgument &argument, const PulseaudioVolume& volume)
68{
69 argument.beginStructure();
70 argument << volume.type_;
71 argument << volume.volume_;
72 argument.endStructure();
73
74 return argument;
75}
76
77const QDBusArgument &operator>>(const QDBusArgument &argument, PulseaudioVolume &volume)
78{
79 argument.beginStructure();
80 argument >> volume.type_;
81 argument >> volume.volume_;
82 argument.endStructure();
83
84 return argument;
85}
86
87PulseaudioVolumeArray::PulseaudioVolumeArray()
88{
89}
90
91PulseaudioVolumeArray::PulseaudioVolumeArray(const PulseaudioVolumeArray &other) :
92 volume_array_(other.volume_array_)
93{
94}
95
96PulseaudioVolumeArray& PulseaudioVolumeArray::operator=(const PulseaudioVolumeArray &other)
97{
98 volume_array_ = other.volume_array_;
99
100 return *this;
101}
102
103PulseaudioVolumeArray::~PulseaudioVolumeArray()
104{
105}
106
107int PulseaudioVolumeArray::getNumItems() const
108{
109 return volume_array_.size();
110}
111
112PulseaudioVolume PulseaudioVolumeArray::getItem(int i) const
113{
114 if (i < volume_array_.size())
115 {
116 return volume_array_[i];
117 }
118 return PulseaudioVolume();
119}
120
121void PulseaudioVolumeArray::addItem(PulseaudioVolume const &item)
122{
123 volume_array_.push_back(item);
124}
125
126void PulseaudioVolumeArray::registerMetaType()
127{
128 qRegisterMetaType<PulseaudioVolumeArray>("PulseaudioVolumeArray");
129
130 qDBusRegisterMetaType<PulseaudioVolumeArray>();
131}
132
133QDBusArgument &operator<<(QDBusArgument &argument, const PulseaudioVolumeArray& volume)
134{
135 argument.beginArray( qMetaTypeId<PulseaudioVolume>() );
136 for (int i = 0; i < volume.volume_array_.size(); ++ i)
137 {
138 PulseaudioVolume item = volume.getItem(i);
139 argument << item;
140 }
141 argument.endArray();
142 return argument;
143}
144
145const QDBusArgument &operator>>(const QDBusArgument &argument, PulseaudioVolumeArray &volume)
146{
147 argument.beginArray();
148 while ( !argument.atEnd() ) {
149 PulseaudioVolume item;
150 argument >> item;
151 volume.volume_array_.push_back(item);
152 }
153 argument.endArray();
154
155 return argument;
156}
0157
=== added file 'tests/dbus-types/pulseaudio-volume.h'
--- tests/dbus-types/pulseaudio-volume.h 1970-01-01 00:00:00 +0000
+++ tests/dbus-types/pulseaudio-volume.h 2016-01-05 11:11:34 +0000
@@ -0,0 +1,69 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Xavi Garcia <xavi.garcia.mena@canonical.com>
17 */
18#pragma once
19
20#include <QtDBus>
21
22class PulseaudioVolume
23{
24public:
25 PulseaudioVolume();
26 PulseaudioVolume(unsigned int type, unsigned int volume);
27 PulseaudioVolume(const PulseaudioVolume &other);
28 PulseaudioVolume& operator=(const PulseaudioVolume &other);
29 ~PulseaudioVolume();
30
31 friend QDBusArgument &operator<<(QDBusArgument &argument, PulseaudioVolume const & volume);
32 friend const QDBusArgument &operator>>(QDBusArgument const & argument, PulseaudioVolume &volume);
33
34 unsigned int getType() const;
35 unsigned int getVolume() const;
36
37 //register Message with the Qt type system
38 static void registerMetaType();
39
40private:
41 unsigned int type_;
42 unsigned int volume_;
43};
44
45class PulseaudioVolumeArray
46{
47public:
48 PulseaudioVolumeArray();
49 PulseaudioVolumeArray(QString const &interface, QString const &property, QDBusVariant const& value);
50 PulseaudioVolumeArray(const PulseaudioVolumeArray &other);
51 PulseaudioVolumeArray& operator=(const PulseaudioVolumeArray &other);
52 ~PulseaudioVolumeArray();
53
54 friend QDBusArgument &operator<<(QDBusArgument &argument, PulseaudioVolumeArray const & volume);
55 friend const QDBusArgument &operator>>(QDBusArgument const & argument, PulseaudioVolumeArray &volume);
56
57 int getNumItems() const;
58 PulseaudioVolume getItem(int i) const;
59 void addItem(PulseaudioVolume const &item);
60
61 //register Message with the Qt type system
62 static void registerMetaType();
63
64private:
65 QVector<PulseaudioVolume> volume_array_;
66};
67
68Q_DECLARE_METATYPE(PulseaudioVolume)
69Q_DECLARE_METATYPE(PulseaudioVolumeArray)
070
=== added directory 'tests/integration'
=== added file 'tests/integration/CMakeLists.txt'
--- tests/integration/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/integration/CMakeLists.txt 2016-01-05 11:11:34 +0000
@@ -0,0 +1,132 @@
1set(CMAKE_AUTOMOC ON)
2set(CMAKE_INCLUDE_CURRENT_DIR ON)
3
4include(FindGMock)
5
6#pkg_check_modules(GMENUHARNESS REQUIRED libgmenuharness REQUIRED)
7#include_directories(${GMENUHARNESS_INCLUDE_DIRS})
8include_directories("${CMAKE_SOURCE_DIR}/include")
9
10pkg_check_modules(QTDBUSTEST REQUIRED libqtdbustest-1 REQUIRED)
11include_directories(${QTDBUSTEST_INCLUDE_DIRS})
12
13pkg_check_modules(QTDBUSMOCK REQUIRED libqtdbusmock-1 REQUIRED)
14include_directories(${QTDBUSMOCK_INCLUDE_DIRS})
15
16find_package(Qt5Test REQUIRED)
17include_directories(${Qt5Test_INCLUDE_DIRS})
18
19find_package(Qt5DBus REQUIRED)
20include_directories(${Qt5DBus_INCLUDE_DIRS})
21
22include_directories(${CMAKE_CURRENT_SOURCE_DIR})
23include_directories(${GMOCK_INCLUDE_DIRS})
24include_directories(${GTEST_INCLUDE_DIRS})
25
26include_directories("${CMAKE_SOURCE_DIR}/tests/dbus-types")
27include_directories("${CMAKE_BINARY_DIR}/tests/dbus-types")
28
29add_definitions(-DSOUND_SERVICE_BIN="${CMAKE_BINARY_DIR}/src/indicator-sound-service"
30 -DSTREAM_RESTORE_TABLE="${CMAKE_SOURCE_DIR}/tests/integration/touch-stream-restore.table"
31 -DVOLUME_SET_BIN="${CMAKE_BINARY_DIR}/tests/integration/set-volume"
32 -DACCOUNTS_SERVICE_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/accounts-mock/accounts-service-sound"
33 -DMEDIA_PLAYER_MPRIS_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/media-player-mpris-mock/media-player-mpris-mock"
34 -DMEDIA_PLAYER_MPRIS_UPDATE_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/media-player-mpris-mock/media-player-mpris-mock-update"
35 -DTEST_SOUND="${CMAKE_SOURCE_DIR}/tests/integration/test-sound.wav"
36 -DQT_NO_KEYWORDS=1
37 -DXDG_DATA_DIRS="${XDG_DATA_DIRS}"
38)
39
40set(GLIB_REQUIRED_VERSION 2.26)
41
42pkg_check_modules(
43 GLIB REQUIRED
44 glib-2.0>=${GLIB_REQUIRED_VERSION}
45 gio-2.0>=${GLIB_REQUIRED_VERSION}
46)
47include_directories(${GLIB_INCLUDE_DIRS})
48
49set(
50 INTEGRATION_TESTS_SRC
51 indicator-sound-test-base.cpp
52 test-indicator.cpp
53 utils/dbus-pulse-volume.cpp
54 main.cpp
55)
56
57add_executable(
58 integration-tests
59 ${INTEGRATION_TESTS_SRC}
60)
61
62qt5_use_modules(
63 integration-tests
64 Core
65 DBus
66 Test
67)
68
69target_link_libraries(
70 integration-tests
71 sound-indicator-dbus-interfaces
72 ${QTDBUSMOCK_LDFLAGS}
73 ${QTDBUSTEST_LDFLAGS}
74 ${GTEST_LIBRARIES}
75 ${GMOCK_LIBRARIES}
76# ${GMENUHARNESS_LDFLAGS}
77 ${GLIB_LDFLAGS}
78 gmenuharness-shared
79)
80
81add_test(
82 integration-tests
83 integration-tests
84)
85
86set(
87 SET-VOLUME-SRC
88 utils/dbus-pulse-volume.cpp
89 utils/set-volume.cpp
90)
91
92set(
93 GET-VOLUME-SRC
94 utils/dbus-pulse-volume.cpp
95 utils/get-volume.cpp
96)
97
98add_executable(
99 set-volume
100 ${SET-VOLUME-SRC}
101)
102
103add_executable(
104 get-volume
105 ${GET-VOLUME-SRC}
106)
107
108qt5_use_modules(
109 set-volume
110 Core
111 DBus
112 Test
113)
114
115qt5_use_modules(
116 get-volume
117 Core
118 DBus
119 Test
120)
121
122target_link_libraries(
123 get-volume
124 sound-indicator-dbus-interfaces
125)
126
127target_link_libraries(
128 set-volume
129 sound-indicator-dbus-interfaces
130)
131
132#add_subdirectory(utils)
0\ No newline at end of file133\ No newline at end of file
1134
=== added file 'tests/integration/Copy of test-sound.wav'
2Binary files tests/integration/Copy of test-sound.wav 1970-01-01 00:00:00 +0000 and tests/integration/Copy of test-sound.wav 2016-01-05 11:11:34 +0000 differ135Binary files tests/integration/Copy of test-sound.wav 1970-01-01 00:00:00 +0000 and tests/integration/Copy of test-sound.wav 2016-01-05 11:11:34 +0000 differ
=== added file 'tests/integration/indicator-sound-test-base.cpp'
--- tests/integration/indicator-sound-test-base.cpp 1970-01-01 00:00:00 +0000
+++ tests/integration/indicator-sound-test-base.cpp 2016-01-05 11:11:34 +0000
@@ -0,0 +1,823 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Xavi Garcia <xavi.garcia.mena@canonical.com>
17 */
18
19#include "indicator-sound-test-base.h"
20
21#include "dbus_menus_interface.h"
22#include "dbus_properties_interface.h"
23#include "dbus_accounts_interface.h"
24#include "dbus_accountssound_interface.h"
25#include "dbus_notifications_interface.h"
26#include "dbus-types.h"
27
28#include <gio/gio.h>
29#include <chrono>
30#include <thread>
31
32#include <QSignalSpy>
33#include "utils/dbus-pulse-volume.h"
34
35using namespace QtDBusTest;
36using namespace QtDBusMock;
37using namespace std;
38using namespace testing;
39namespace mh = unity::gmenuharness;
40
41namespace
42{
43 const int MAX_TIME_WAITING_FOR_NOTIFICATIONS = 2000;
44}
45
46IndicatorSoundTestBase::IndicatorSoundTestBase() :
47 dbusMock(dbusTestRunner)
48{
49}
50
51IndicatorSoundTestBase::~IndicatorSoundTestBase()
52{
53}
54
55void IndicatorSoundTestBase::SetUp()
56{
57 setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, true);
58 setenv("DBUS_SYSTEM_BUS_ADDRESS", dbusTestRunner.systemBus().toStdString().c_str(), true);
59 setenv("DBUS_SESSION_BUS_ADDRESS", dbusTestRunner.sessionBus().toStdString().c_str(), true);
60 dbusMock.registerNotificationDaemon();
61
62 dbusTestRunner.startServices();
63
64 auto& notifications = notificationsMockInterface();
65 notifications.AddMethod("org.freedesktop.Notifications",
66 "GetCapabilities",
67 "",
68 "as",
69 "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']"
70 ).waitForFinished();
71
72 int waitedTime = 0;
73 while (!dbusTestRunner.sessionConnection().interface()->isServiceRegistered("org.freedesktop.Notifications") && waitedTime < MAX_TIME_WAITING_FOR_NOTIFICATIONS)
74 {
75 std::this_thread::sleep_for(std::chrono::milliseconds(10));
76 waitedTime += 10;
77 }
78}
79
80void IndicatorSoundTestBase::TearDown()
81{
82 unsetenv("XDG_DATA_DIRS");
83 unsetenv("PULSE_SERVER");
84 unsetenv("DBUS_SYSTEM_BUS_ADDRESS");
85}
86
87void gvariant_deleter(GVariant* varptr)
88{
89 if (varptr != nullptr)
90 {
91 g_variant_unref(varptr);
92 }
93}
94
95std::shared_ptr<GVariant> IndicatorSoundTestBase::volume_variant(double volume)
96{
97 GVariantBuilder builder;
98
99 g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
100 g_variant_builder_add(&builder,
101 "{sv}",
102 "title",
103 g_variant_new_string("_Sound"));
104
105 g_variant_builder_add(&builder,
106 "{sv}",
107 "accessible-desc",
108 g_variant_new_string("_Sound"));
109
110 auto icon = g_themed_icon_new("icon");
111 g_variant_builder_add(&builder,
112 "{sv}",
113 "icon",
114 g_icon_serialize(icon));
115
116 g_variant_builder_add(&builder,
117 "{sv}",
118 "visible",
119 g_variant_new_boolean(true));
120 return shared_ptr<GVariant>(g_variant_builder_end(&builder), &gvariant_deleter);
121}
122
123bool IndicatorSoundTestBase::setStreamRestoreVolume(QString const &role, double volume)
124{
125 QProcess setVolume;
126 setVolume.start(VOLUME_SET_BIN, QStringList()
127 << role
128 << QString("%1").arg(volume));
129 if (!setVolume.waitForStarted())
130 return false;
131
132 if (!setVolume.waitForFinished())
133 return false;
134
135 return setVolume.exitCode() == 0;
136}
137
138bool IndicatorSoundTestBase::setSinkVolume(double volume)
139{
140 QString volume_percentage = QString("%1\%").arg(volume*100);
141 QProcess setVolume;
142 setVolume.start("pactl", QStringList()
143 << "-s"
144 << "127.0.0.1"
145 << "set-sink-volume"
146 << "0"
147 << volume_percentage);
148 if (!setVolume.waitForStarted())
149 return false;
150
151 if (!setVolume.waitForFinished())
152 return false;
153
154 return setVolume.exitCode() == 0;
155}
156
157bool IndicatorSoundTestBase::clearGSettingsPlayers()
158{
159 QProcess clearPlayers;
160
161 clearPlayers.start("gsettings", QStringList()
162 << "set"
163 << "com.canonical.indicator.sound"
164 << "interested-media-players"
165 << "[]");
166 if (!clearPlayers.waitForStarted())
167 return false;
168
169 if (!clearPlayers.waitForFinished())
170 return false;
171
172 return clearPlayers.exitCode() == 0;
173}
174
175bool IndicatorSoundTestBase::startTestMprisPlayer(QString const& playerName)
176{
177 testPlayer1.terminate();
178 testPlayer1.start(MEDIA_PLAYER_MPRIS_BIN, QStringList()
179 << playerName);
180 if (!testPlayer1.waitForStarted())
181 return false;
182
183
184 return true;
185}
186
187bool IndicatorSoundTestBase::setTestMprisPlayerProperty(QString const &testPlayer, QString const &property, bool value)
188{
189 QProcess setProperty;
190 QString strValue;
191 strValue = value ? "true" : "false";
192
193 setProperty.start(MEDIA_PLAYER_MPRIS_UPDATE_BIN, QStringList()
194 << testPlayer
195 << property
196 << strValue);
197 if (!setProperty.waitForStarted())
198 return false;
199
200 if (!setProperty.waitForFinished())
201 return false;
202
203 return setProperty.exitCode() == 0;
204}
205
206bool IndicatorSoundTestBase::startTestSound(QString const &role)
207{
208 testSoundProcess.terminate();
209 testSoundProcess.start("paplay", QStringList()
210 << "-s"
211 << "127.0.0.1"
212 << TEST_SOUND
213 << QString("--property=media.role=%1").arg(role));
214
215 if (!testSoundProcess.waitForStarted())
216 return false;
217
218 return true;
219}
220
221void IndicatorSoundTestBase::stopTestSound()
222{
223 testSoundProcess.terminate();
224}
225
226void IndicatorSoundTestBase::startPulseDesktop(DevicePortType speakerPort, DevicePortType headphonesPort)
227{
228 try
229 {
230 pulseaudio.reset(
231 new QProcessDBusService(DBusTypes::DBUS_PULSE,
232 QDBusConnection::SessionBus,
233 "pulseaudio",
234 QStringList() << "--start"
235 << "-vvvv"
236 << "--disable-shm=true"
237 << "--daemonize=false"
238 << "--use-pid-file=false"
239 << "--system=false"
240 << "--exit-idle-time=-1"
241 << "-n"
242 << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort))
243 << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort))
244 << "--log-target=file:/tmp/pulse-daemon.log"
245 << "--load=module-dbus-protocol"
246 << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1"
247 ));
248 pulseaudio->start(dbusTestRunner.sessionConnection());
249 }
250 catch (exception const& e)
251 {
252 cout << "pulseaudio(): " << e.what() << endl;
253 throw;
254 }
255}
256
257void IndicatorSoundTestBase::startPulsePhone(DevicePortType speakerPort, DevicePortType headphonesPort)
258{
259 try
260 {
261 pulseaudio.reset(
262 new QProcessDBusService(DBusTypes::DBUS_PULSE,
263 QDBusConnection::SessionBus,
264 "pulseaudio",
265 QStringList() << "--start"
266 << "-vvvv"
267 << "--disable-shm=true"
268 << "--daemonize=false"
269 << "--use-pid-file=false"
270 << "--system=false"
271 << "--exit-idle-time=-1"
272 << "-n"
273 << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort))
274 << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort))
275 << "--log-target=file:/tmp/pulse-daemon.log"
276 << QString("--load=module-stream-restore restore_device=false restore_muted=false fallback_table=%1").arg(STREAM_RESTORE_TABLE)
277 << "--load=module-dbus-protocol"
278 << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1"
279 ));
280 pulseaudio->start(dbusTestRunner.sessionConnection());
281 }
282 catch (exception const& e)
283 {
284 cout << "pulseaudio(): " << e.what() << endl;
285 throw;
286 }
287}
288
289void IndicatorSoundTestBase::startAccountsService()
290{
291 try
292 {
293 accountsService.reset(
294 new QProcessDBusService(DBusTypes::ACCOUNTS_SERVICE,
295 QDBusConnection::SystemBus,
296 ACCOUNTS_SERVICE_BIN,
297 QStringList()));
298 accountsService->start(dbusTestRunner.systemConnection());
299
300 initializeAccountsInterface();
301 }
302 catch (exception const& e)
303 {
304 cout << "accountsService(): " << e.what() << endl;
305 throw;
306 }
307}
308
309void IndicatorSoundTestBase::startIndicator()
310{
311 try
312 {
313 setenv("PULSE_SERVER", "127.0.0.1", true);
314 indicator.reset(
315 new QProcessDBusService(DBusTypes::DBUS_NAME,
316 QDBusConnection::SessionBus,
317 SOUND_SERVICE_BIN,
318 QStringList()));
319 indicator->start(dbusTestRunner.sessionConnection());
320 }
321 catch (exception const& e)
322 {
323 cout << "startIndicator(): " << e.what() << endl;
324 throw;
325 }
326}
327
328mh::MenuMatcher::Parameters IndicatorSoundTestBase::desktopParameters()
329{
330 return mh::MenuMatcher::Parameters(
331 "com.canonical.indicator.sound",
332 { { "indicator", "/com/canonical/indicator/sound" } },
333 "/com/canonical/indicator/sound/desktop");
334}
335
336mh::MenuMatcher::Parameters IndicatorSoundTestBase::phoneParameters()
337{
338 return mh::MenuMatcher::Parameters(
339 "com.canonical.indicator.sound",
340 { { "indicator", "/com/canonical/indicator/sound" } },
341 "/com/canonical/indicator/sound/phone");
342}
343
344unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::volumeSlider(double volume, QString const &label)
345{
346 return mh::MenuItemMatcher().radio()
347 .label(label.toStdString())
348 .round_doubles(0.1)
349 .int32_attribute("target", 0)
350 .double_attribute("min-value", 0.0)
351 .double_attribute("max-value", 1.0)
352 .double_attribute("step", 0.01)
353 .string_attribute("x-canonical-type", "com.canonical.unity.slider")
354 .themed_icon("max-icon", {"audio-volume-high-panel", "audio-volume-high", "audio-volume", "audio"})
355 .themed_icon("min-icon", {"audio-volume-low-zero-panel", "audio-volume-low-zero", "audio-volume-low", "audio-volume", "audio"})
356 .pass_through_double_attribute("action", volume);
357}
358
359unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::silentModeSwitch(bool toggled)
360{
361 return mh::MenuItemMatcher::checkbox()
362 .label("Silent Mode")
363 .action("indicator.silent-mode")
364 .toggled(toggled);
365}
366
367bool IndicatorSoundTestBase::waitMenuChange()
368{
369 if (signal_spy_menu_changed_)
370 {
371 return signal_spy_menu_changed_->wait();
372 }
373 return false;
374}
375
376bool IndicatorSoundTestBase::initializeMenuChangedSignal()
377{
378 if (!menu_interface_)
379 {
380 menu_interface_.reset(new MenusInterface("com.canonical.indicator.sound",
381 "/com/canonical/indicator/sound",
382 dbusTestRunner.sessionConnection(), 0));
383 }
384 if (menu_interface_)
385 {
386 qDebug() << "Waiting for signal";
387 signal_spy_menu_changed_.reset(new QSignalSpy(menu_interface_.get(), &MenusInterface::Changed));
388 }
389 if (!menu_interface_ || !signal_spy_menu_changed_)
390 {
391 return false;
392 }
393 return true;
394}
395
396bool IndicatorSoundTestBase::waitVolumeChangedInIndicator()
397{
398 if (signal_spy_volume_changed_)
399 {
400 return signal_spy_volume_changed_->wait();
401 }
402 return false;
403}
404
405void IndicatorSoundTestBase::initializeAccountsInterface()
406{
407 auto username = qgetenv("USER");
408 if (username != "")
409 {
410 main_accounts_interface_.reset(new AccountsInterface("org.freedesktop.Accounts",
411 "/org/freedesktop/Accounts",
412 dbusTestRunner.systemConnection(), 0));
413
414 QDBusReply<QDBusObjectPath> userResp = main_accounts_interface_->call(QLatin1String("FindUserByName"),
415 QLatin1String(username));
416
417 if (!userResp.isValid())
418 {
419 qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << userResp.error().message();
420 }
421
422 auto userPath = userResp.value().path();
423 if (userPath != "")
424 {
425 std::unique_ptr<AccountsSoundInterface> soundInterface(new AccountsSoundInterface("org.freedesktop.Accounts",
426 userPath,
427 dbusTestRunner.systemConnection(), 0));
428
429 accounts_interface_.reset(new DBusPropertiesInterface("org.freedesktop.Accounts",
430 userPath,
431 dbusTestRunner.systemConnection(), 0));
432 if (!accounts_interface_->isValid())
433 {
434 qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << accounts_interface_->lastError().message();
435 }
436 signal_spy_volume_changed_.reset(new QSignalSpy(accounts_interface_.get(),&DBusPropertiesInterface::PropertiesChanged));
437 }
438 }
439}
440
441OrgFreedesktopDBusMockInterface& IndicatorSoundTestBase::notificationsMockInterface()
442{
443 return dbusMock.mockInterface("org.freedesktop.Notifications",
444 "/org/freedesktop/Notifications",
445 "org.freedesktop.Notifications",
446 QDBusConnection::SessionBus);
447}
448
449bool IndicatorSoundTestBase::setActionValue(const QString & action, QVariant value)
450{
451 QDBusInterface actionsInterface(DBusTypes::DBUS_NAME,
452 DBusTypes::MAIN_SERVICE_PATH,
453 DBusTypes::ACTIONS_INTERFACE,
454 dbusTestRunner.sessionConnection());
455
456 QDBusVariant dbusVar(value);
457 auto resp = actionsInterface.call("SetState",
458 action,
459 QVariant::fromValue(dbusVar),
460 QVariant::fromValue(QVariantMap()));
461
462 if (resp.type() == QDBusMessage::ErrorMessage)
463 {
464 qCritical() << "IndicatorSoundTestBase::setActionValue(): Failed to set value for action "
465 << action
466 << " "
467 << resp.errorMessage();
468 return false;
469 }
470 else
471 {
472 return true;
473 }
474}
475
476bool IndicatorSoundTestBase::pressNotificationButton(int id, const QString & button)
477{
478 OrgFreedesktopDBusMockInterface actionsInterface("org.freedesktop.Notifications",
479 "/org/freedesktop/Notifications",
480 dbusTestRunner.sessionConnection());
481
482 actionsInterface.EmitSignal(
483 "org.freedesktop.Notifications",
484 "ActionInvoked", "us", QVariantList() << id << button);
485
486 return true;
487}
488
489bool IndicatorSoundTestBase::qDBusArgumentToMap(QVariant const& variant, QVariantMap& map)
490{
491 if (variant.canConvert<QDBusArgument>())
492 {
493 QDBusArgument value(variant.value<QDBusArgument>());
494 if (value.currentType() == QDBusArgument::MapType)
495 {
496 value >> map;
497 return true;
498 }
499 }
500 return false;
501}
502
503void IndicatorSoundTestBase::checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call)
504{
505 QString icon;
506 if (volume <= 0.0)
507 {
508 icon = "audio-volume-muted";
509 }
510 else if (volume <= 0.3)
511 {
512 icon = "audio-volume-low";
513 }
514 else if (volume <= 0.7)
515 {
516 icon = "audio-volume-medium";
517 }
518 else
519 {
520 icon = "audio-volume-high";
521 }
522
523 ASSERT_NE(call.size(), 0);
524 EXPECT_EQ("Notify", call.at(0));
525
526 QVariantList const& args(call.at(1).toList());
527 ASSERT_EQ(8, args.size());
528 EXPECT_EQ("indicator-sound", args.at(0));
529 EXPECT_EQ(icon, args.at(2));
530 EXPECT_EQ("Volume", args.at(3));
531 EXPECT_EQ(label, args.at(4));
532 EXPECT_EQ(QStringList(), args.at(5));
533
534 QVariantMap hints;
535 ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints));
536 ASSERT_TRUE(hints.contains("value"));
537 ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon"));
538 ASSERT_TRUE(hints.contains("x-canonical-value-bar-tint"));
539 ASSERT_TRUE(hints.contains("x-canonical-private-synchronous"));
540
541 EXPECT_EQ(volume*100, hints["value"]);
542 EXPECT_EQ(true, hints["x-canonical-non-shaped-icon"]);
543 EXPECT_EQ(isLoud, hints["x-canonical-value-bar-tint"]);
544 EXPECT_EQ(true, hints["x-canonical-private-synchronous"]);
545}
546
547void IndicatorSoundTestBase::checkHighVolumeNotification(QVariantList call)
548{
549 ASSERT_NE(call.size(), 0);
550 EXPECT_EQ("Notify", call.at(0));
551
552 QVariantList const& args(call.at(1).toList());
553 ASSERT_EQ(8, args.size());
554 EXPECT_EQ("indicator-sound", args.at(0));
555 EXPECT_EQ("Volume", args.at(3));
556}
557
558void IndicatorSoundTestBase::checkCloseNotification(int id, QVariantList call)
559{
560 EXPECT_EQ("CloseNotification", call.at(0));
561 QVariantList const& args(call.at(1).toList());
562 ASSERT_EQ(1, args.size());
563}
564
565void IndicatorSoundTestBase::checkNotificationWithNoArgs(QString const& method, QVariantList call)
566{
567 EXPECT_EQ(method, call.at(0));
568 QVariantList const& args(call.at(1).toList());
569 ASSERT_EQ(0, args.size());
570}
571
572int IndicatorSoundTestBase::getNotificationID(QVariantList call)
573{
574 if (call.size() == 0)
575 {
576 return -1;
577 }
578 QVariantList const& args(call.at(1).toList());
579 if (args.size() != 8)
580 {
581 return -1;
582 }
583 if (args.at(0) != "indicator-sound")
584 {
585 return -1;
586 }
587
588 bool isInt;
589 int id = args.at(1).toInt(&isInt);
590 if (!isInt)
591 {
592 return -1;
593 }
594 return id;
595}
596
597bool IndicatorSoundTestBase::activateHeadphones(bool headphonesActive)
598{
599 QProcess pacltProcess;
600
601 QString defaultSinkName = "indicator_sound_test_speaker";
602 QString suspendedSinkName = "indicator_sound_test_headphones";
603 if (headphonesActive)
604 {
605 defaultSinkName = "indicator_sound_test_headphones";
606 suspendedSinkName = "indicator_sound_test_speaker";
607 }
608
609 pacltProcess.start("pactl", QStringList() << "-s"
610 << "127.0.0.1"
611 << "set-default-sink"
612 << defaultSinkName);
613 if (!pacltProcess.waitForStarted())
614 return false;
615
616 if (!pacltProcess.waitForFinished())
617 return false;
618
619 pacltProcess.start("pactl", QStringList() << "-s"
620 << "127.0.0.1"
621 << "suspend-sink"
622 << defaultSinkName
623 << "0");
624 if (!pacltProcess.waitForStarted())
625 return false;
626
627 if (!pacltProcess.waitForFinished())
628 return false;
629
630 pacltProcess.start("pactl", QStringList() << "-s"
631 << "127.0.0.1"
632 << "suspend-sink"
633 << suspendedSinkName
634 << "1");
635 if (!pacltProcess.waitForStarted())
636 return false;
637
638 if (!pacltProcess.waitForFinished())
639 return false;
640
641 return pacltProcess.exitCode() == 0;
642}
643
644QString IndicatorSoundTestBase::getDevicePortString(DevicePortType port)
645{
646 QString portString;
647
648 switch (port)
649 {
650 case WIRED:
651 portString = "wired";
652 break;
653 case BLUETOOTH:
654 portString = "bluetooth";
655 break;
656 case USB:
657 portString = "usb";
658 break;
659 case HDMI:
660 portString = "hdmi";
661 break;
662 default:
663 portString = "not_defined";
664 break;
665 }
666
667 return portString;
668}
669
670void IndicatorSoundTestBase::checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort)
671{
672 double INITIAL_VOLUME = 0.0;
673
674 QString speakerString;
675 QString speakerStringMenu;
676 switch(speakerPort)
677 {
678 case WIRED:
679 speakerString = "Speakers";
680 speakerStringMenu = "Volume";
681 break;
682 case BLUETOOTH:
683 speakerString = "Bluetooth speaker";
684 speakerStringMenu = "Volume (Bluetooth)";
685 break;
686 case USB:
687 speakerString = "Usb speaker";
688 speakerStringMenu = "Volume (Usb)";
689 break;
690 case HDMI:
691 speakerString = "HDMI speaker";
692 speakerStringMenu = "Volume (HDMI)";
693 break;
694 }
695
696 QString headphonesString;
697 QString headphonesStringMenu;
698 switch(headphonesPort)
699 {
700 case WIRED:
701 headphonesString = "Headphones";
702 headphonesStringMenu = "Volume (Headphones)";
703 break;
704 case BLUETOOTH:
705 headphonesString = "Bluetooth headphones";
706 headphonesStringMenu = "Volume (Bluetooth headphones)";
707 break;
708 case USB:
709 headphonesString = "Usb headphones";
710 headphonesStringMenu = "Volume (Usb headphones)";
711 break;
712 case HDMI:
713 headphonesString = "HDMI headphones";
714 headphonesStringMenu = "Volume (HDMI headphones)";
715 break;
716 }
717
718 QSignalSpy notificationsSpy(&notificationsMockInterface(),
719 SIGNAL(MethodCalled(const QString &, const QVariantList &)));
720
721 ASSERT_NO_THROW(startAccountsService());
722 ASSERT_NO_THROW(startPulsePhone(speakerPort, headphonesPort));
723
724 // initialize volumes in pulseaudio
725 EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
726 EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME));
727
728 // start now the indicator, so it picks the new volumes
729 ASSERT_NO_THROW(startIndicator());
730
731 // if the speaker is the normal one it does not emit any notification, as that's
732 // the default one.
733 // for the rest it notifies the output
734 if (speakerPort != WIRED)
735 {
736 WAIT_FOR_SIGNALS(notificationsSpy, 3);
737
738 // the first time we also have the calls to
739 // GetServerInformation and GetCapabilities
740 checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0));
741 checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1));
742 checkVolumeNotification(INITIAL_VOLUME, speakerString, false, notificationsSpy.at(2));
743 notificationsSpy.clear();
744 }
745
746 notificationsSpy.clear();
747 // activate the headphones
748 EXPECT_TRUE(activateHeadphones(true));
749
750 if (speakerPort == WIRED)
751 {
752 WAIT_FOR_SIGNALS(notificationsSpy, 3);
753
754 // the first time we also have the calls to
755 // GetServerInformation and GetCapabilities
756 checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0));
757 checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1));
758 checkVolumeNotification(INITIAL_VOLUME, headphonesString, false, notificationsSpy.at(2));
759 notificationsSpy.clear();
760 }
761 else
762 {
763 WAIT_FOR_SIGNALS(notificationsSpy, 2);
764 checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
765 checkVolumeNotification(INITIAL_VOLUME, headphonesString, false, notificationsSpy.at(1));
766 notificationsSpy.clear();
767 }
768
769 // check the label in the menu
770 EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
771 .item(mh::MenuItemMatcher()
772 .action("indicator.root")
773 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
774 .string_attribute("x-canonical-scroll-action", "indicator.scroll")
775 .string_attribute("x-canonical-secondary-action", "indicator.mute")
776 .string_attribute("submenu-action", "indicator.indicator-shown")
777 .mode(mh::MenuItemMatcher::Mode::starts_with)
778 .submenu()
779 .item(mh::MenuItemMatcher()
780 .section()
781 .item(silentModeSwitch(false))
782 .item(volumeSlider(INITIAL_VOLUME, headphonesStringMenu))
783 )
784 ).match());
785
786 // deactivate the headphones
787 EXPECT_TRUE(activateHeadphones(false));
788
789 WAIT_FOR_SIGNALS(notificationsSpy, 2);
790 checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(0));
791 checkVolumeNotification(INITIAL_VOLUME, speakerString, false, notificationsSpy.at(1));
792 notificationsSpy.clear();
793
794 // check the label in the menu
795 EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
796 .item(mh::MenuItemMatcher()
797 .action("indicator.root")
798 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
799 .string_attribute("x-canonical-scroll-action", "indicator.scroll")
800 .string_attribute("x-canonical-secondary-action", "indicator.mute")
801 .string_attribute("submenu-action", "indicator.indicator-shown")
802 .mode(mh::MenuItemMatcher::Mode::starts_with)
803 .submenu()
804 .item(mh::MenuItemMatcher()
805 .section()
806 .item(silentModeSwitch(false))
807 .item(volumeSlider(INITIAL_VOLUME, speakerStringMenu))
808 )
809 ).match());
810}
811
812bool IndicatorSoundTestBase::setVolumeUntilAccountsIsConnected(double volume)
813{
814 int RETRY_TIME = 5000;
815
816 setActionValue("volume", QVariant::fromValue(volume));
817 while(!signal_spy_volume_changed_->wait(10) && RETRY_TIME)
818 {
819 RETRY_TIME -= 10;
820 setActionValue("volume", QVariant::fromValue(volume));
821 }
822 return (signal_spy_volume_changed_->count() != 0);
823}
0824
=== added file 'tests/integration/indicator-sound-test-base.h'
--- tests/integration/indicator-sound-test-base.h 1970-01-01 00:00:00 +0000
+++ tests/integration/indicator-sound-test-base.h 2016-01-05 11:11:34 +0000
@@ -0,0 +1,160 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Xavi Garcia <xavi.garcia.mena@canonical.com>
17 */
18
19#pragma once
20
21#include <libqtdbustest/DBusTestRunner.h>
22#include <libqtdbustest/QProcessDBusService.h>
23#include <libqtdbusmock/DBusMock.h>
24
25#include <unity/gmenuharness/MatchUtils.h>
26#include <unity/gmenuharness/MenuMatcher.h>
27
28#include <gmock/gmock.h>
29#include <gtest/gtest.h>
30
31class MenusInterface;
32class DBusPulseVolume;
33class DBusPropertiesInterface;
34class AccountsInterface;
35class QSignalSpy;
36
37#define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\
38{\
39 while (signalSpy.size() < signalsExpected)\
40 {\
41 ASSERT_TRUE(signalSpy.wait());\
42 }\
43 ASSERT_EQ(signalsExpected, signalSpy.size());\
44}
45
46#define WAIT_AT_LEAST_SIGNALS(signalSpy, signalsExpected)\
47{\
48 while (signalSpy.size() < signalsExpected)\
49 {\
50 ASSERT_TRUE(signalSpy.wait());\
51 }\
52 ASSERT_TRUE(signalsExpected <= signalSpy.size());\
53}
54
55class IndicatorSoundTestBase: public testing::Test
56{
57public:
58 IndicatorSoundTestBase();
59
60 ~IndicatorSoundTestBase();
61
62 enum DevicePortType
63 {
64 WIRED,
65 BLUETOOTH,
66 USB,
67 HDMI
68 };
69
70protected:
71 void SetUp() override;
72 void TearDown() override;
73
74 void startIndicator();
75 void startPulseDesktop(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED);
76 void startPulsePhone(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED);
77 void startAccountsService();
78
79 bool clearGSettingsPlayers();
80
81 bool startTestMprisPlayer(QString const& playerName);
82
83 bool setTestMprisPlayerProperty(QString const &testPlayer, QString const &property, bool value);
84
85 bool setStreamRestoreVolume(QString const &role, double volume);
86
87 bool setSinkVolume(double volume);
88
89 bool startTestSound(QString const &role);
90
91 void stopTestSound();
92
93 static std::shared_ptr<GVariant> volume_variant(double volume);
94
95 static unity::gmenuharness::MenuMatcher::Parameters desktopParameters();
96
97 static unity::gmenuharness::MenuMatcher::Parameters phoneParameters();
98
99 static unity::gmenuharness::MenuItemMatcher volumeSlider(double volume, QString const &label);
100
101 static unity::gmenuharness::MenuItemMatcher silentModeSwitch(bool toggled);
102
103 bool waitMenuChange();
104
105 bool initializeMenuChangedSignal();
106
107 bool waitVolumeChangedInIndicator();
108
109 void initializeAccountsInterface();
110
111 OrgFreedesktopDBusMockInterface& notificationsMockInterface();
112
113 bool setActionValue(const QString & action, QVariant value);
114
115 bool pressNotificationButton(int id, const QString & button);
116
117 bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map);
118
119 void checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call);
120
121 void checkHighVolumeNotification(QVariantList call);
122
123 void checkCloseNotification(int id, QVariantList call);
124
125 void checkNotificationWithNoArgs(QString const& method, QVariantList call);
126
127 int getNotificationID(QVariantList call);
128
129 bool activateHeadphones(bool headphonesActive);
130
131 QString getDevicePortString(DevicePortType port);
132
133 void checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort);
134
135 bool setVolumeUntilAccountsIsConnected(double volume);
136
137 QtDBusTest::DBusTestRunner dbusTestRunner;
138
139 QtDBusMock::DBusMock dbusMock;
140
141 QtDBusTest::DBusServicePtr indicator;
142
143 QtDBusTest::DBusServicePtr pulseaudio;
144
145 QtDBusTest::DBusServicePtr accountsService;
146
147 QProcess testSoundProcess;
148
149 QProcess testPlayer1;
150
151 std::unique_ptr<MenusInterface> menu_interface_;
152
153 std::unique_ptr<DBusPropertiesInterface> accounts_interface_;
154
155 std::unique_ptr<AccountsInterface> main_accounts_interface_;
156
157 std::unique_ptr<QSignalSpy> signal_spy_volume_changed_;
158
159 std::unique_ptr<QSignalSpy> signal_spy_menu_changed_;
160};
0161
=== added file 'tests/integration/main.cpp'
--- tests/integration/main.cpp 1970-01-01 00:00:00 +0000
+++ tests/integration/main.cpp 2016-01-05 11:11:34 +0000
@@ -0,0 +1,58 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Pete Woods <pete.woods@canonical.com>
18 */
19
20//#include <config.h>
21
22#include <QCoreApplication>
23#include <QTimer>
24#include <gtest/gtest.h>
25
26#include <libqtdbusmock/DBusMock.h>
27
28#include "dbus-types.h"
29
30using namespace QtDBusMock;
31
32class Runner: public QObject
33{
34 Q_OBJECT
35public Q_SLOTS:
36 void run()
37 {
38 QCoreApplication::exit(RUN_ALL_TESTS());
39 }
40};
41
42int main(int argc, char **argv)
43{
44 qputenv("LANG", "C.UTF-8");
45 unsetenv("LC_ALL");
46
47 QCoreApplication application(argc, argv);
48 DBusMock::registerMetaTypes();
49 DBusTypes::registerMetaTypes();
50 ::testing::InitGoogleTest(&argc, argv);
51
52 Runner runner;
53 QTimer::singleShot(0, &runner, SLOT(run()));
54
55 return application.exec();
56}
57
58#include "main.moc"
059
=== added file 'tests/integration/test-indicator.cpp'
--- tests/integration/test-indicator.cpp 1970-01-01 00:00:00 +0000
+++ tests/integration/test-indicator.cpp 2016-01-05 11:11:34 +0000
@@ -0,0 +1,981 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Xavi Garcia <xavi.garcia.mena@canonical.com>
17 */
18
19#include <indicator-sound-test-base.h>
20
21#include <QDebug>
22#include <QTestEventLoop>
23#include <QSignalSpy>
24
25using namespace std;
26using namespace testing;
27namespace mh = unity::gmenuharness;
28namespace
29{
30
31class TestIndicator: public IndicatorSoundTestBase
32{
33};
34
35TEST_F(TestIndicator, PhoneChangeRoleVolume)
36{
37 double INITIAL_VOLUME = 0.0;
38
39 ASSERT_NO_THROW(startAccountsService());
40 ASSERT_NO_THROW(startPulsePhone());
41
42 // initialize volumes in pulseaudio
43 EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
44 EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME));
45
46 // start now the indicator, so it picks the new volumes
47 ASSERT_NO_THROW(startIndicator());
48
49 // Generate a random volume
50 QTime now = QTime::currentTime();
51 qsrand(now.msec());
52 int randInt = qrand() % 100;
53 double randomVolume = randInt / 100.0;
54
55 QSignalSpy &userAccountsSpy = *signal_spy_volume_changed_;
56 // set an initial volume to the alert role
57 userAccountsSpy.clear();
58 EXPECT_TRUE(setVolumeUntilAccountsIsConnected(1.0));
59 userAccountsSpy.clear();
60 // play a test sound, it should change the role in the indicator
61 EXPECT_TRUE(startTestSound("multimedia"));
62
63 // this time we only expect 1 signal as it's only the indicator
64 // updating the value
65 WAIT_FOR_SIGNALS(userAccountsSpy, 1);
66 //EXPECT_TRUE(waitVolumeChangedInIndicator());
67
68 userAccountsSpy.clear();
69 // set the random volume to the multimedia role
70 setActionValue("volume", QVariant::fromValue(randomVolume));
71 if (randomVolume != INITIAL_VOLUME)
72 {
73 WAIT_FOR_SIGNALS(userAccountsSpy, 1);
74 }
75
76 // check the indicator
77 EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
78 .item(mh::MenuItemMatcher()
79 .action("indicator.root")
80 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
81 .string_attribute("x-canonical-scroll-action", "indicator.scroll")
82 .string_attribute("x-canonical-secondary-action", "indicator.mute")
83 .string_attribute("submenu-action", "indicator.indicator-shown")
84 .mode(mh::MenuItemMatcher::Mode::starts_with)
85 .submenu()
86 .item(mh::MenuItemMatcher()
87 .section()
88 .item(silentModeSwitch(false))
89 .item(volumeSlider(randomVolume, "Volume"))
90 )
91 ).match());
92
93 // check that the last item is Sound Settings
94 EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
95 .item(mh::MenuItemMatcher()
96 .action("indicator.root")
97 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
98 .string_attribute("x-canonical-secondary-action", "indicator.mute")
99 .mode(mh::MenuItemMatcher::Mode::ends_with)
100 .submenu()
101 .item(mh::MenuItemMatcher()
102 .label("Sound Settings…")
103 .action("indicator.phone-settings")
104 )
105 ).match());
106
107 // initialize the signal spy
108 EXPECT_TRUE(initializeMenuChangedSignal());
109 userAccountsSpy.clear();
110 // stop the test sound, the role should change again to alert
111 stopTestSound();
112 if (randomVolume != 1.0)
113 {
114 // wait for the menu change
115 EXPECT_TRUE(waitMenuChange());
116 }
117
118 // check the initial volume for the alert role
119 EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
120 .item(mh::MenuItemMatcher()
121 .action("indicator.root")
122 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
123 .string_attribute("x-canonical-scroll-action", "indicator.scroll")
124 .string_attribute("x-canonical-secondary-action", "indicator.mute")
125 .string_attribute("submenu-action", "indicator.indicator-shown")
126 .mode(mh::MenuItemMatcher::Mode::starts_with)
127 .submenu()
128 .item(mh::MenuItemMatcher()
129 .section()
130 .item(silentModeSwitch(false))
131 .item(volumeSlider(1.0, "Volume"))
132 )
133 ).match());
134
135 // check that the last item is Sound Settings
136 EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
137 .item(mh::MenuItemMatcher()
138 .action("indicator.root")
139 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
140 .string_attribute("x-canonical-secondary-action", "indicator.mute")
141 .mode(mh::MenuItemMatcher::Mode::ends_with)
142 .submenu()
143 .item(mh::MenuItemMatcher()
144 .label("Sound Settings…")
145 .action("indicator.phone-settings")
146 )
147 ).match());
148}
149
150TEST_F(TestIndicator, PhoneBasicInitialVolume)
151{
152 double INITIAL_VOLUME = 0.0;
153
154 ASSERT_NO_THROW(startAccountsService());
155 EXPECT_TRUE(clearGSettingsPlayers());
156 ASSERT_NO_THROW(startPulsePhone());
157
158 // initialize volumes in pulseaudio
159 EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
160
161 // start now the indicator, so it picks the new volumes
162 ASSERT_NO_THROW(startIndicator());
163
164 EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
165 .item(mh::MenuItemMatcher()
166 .action("indicator.root")
167 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
168 .string_attribute("x-canonical-scroll-action", "indicator.scroll")
169 .string_attribute("x-canonical-secondary-action", "indicator.mute")
170 .string_attribute("submenu-action", "indicator.indicator-shown")
171 .mode(mh::MenuItemMatcher::Mode::all)
172 .submenu()
173 .item(mh::MenuItemMatcher()
174 .section()
175 .item(silentModeSwitch(false))
176 .item(volumeSlider(INITIAL_VOLUME, "Volume"))
177 )
178 .item(mh::MenuItemMatcher()
179 .label("Sound Settings…")
180 .action("indicator.phone-settings")
181 )
182 ).match());
183}
184
185TEST_F(TestIndicator, PhoneAddMprisPlayer)
186{
187 double INITIAL_VOLUME = 0.0;
188
189 ASSERT_NO_THROW(startAccountsService());
190 EXPECT_TRUE(clearGSettingsPlayers());
191 ASSERT_NO_THROW(startPulsePhone());
192
193 // initialize volumes in pulseaudio
194 EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
195
196 // start now the indicator, so it picks the new volumes
197 ASSERT_NO_THROW(startIndicator());
198
199 EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
200 .item(mh::MenuItemMatcher()
201 .action("indicator.root")
202 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
203 .string_attribute("x-canonical-scroll-action", "indicator.scroll")
204 .string_attribute("x-canonical-secondary-action", "indicator.mute")
205 .string_attribute("submenu-action", "indicator.indicator-shown")
206 .mode(mh::MenuItemMatcher::Mode::all)
207 .submenu()
208 .item(mh::MenuItemMatcher()
209 .section()
210 .item(silentModeSwitch(false))
211 .item(volumeSlider(INITIAL_VOLUME, "Volume"))
212 )
213 .item(mh::MenuItemMatcher()
214 .label("Sound Settings…")
215 .action("indicator.phone-settings")
216 )
217 ).match());
218
219 // initialize the signal spy
220 EXPECT_TRUE(initializeMenuChangedSignal());
221
222 // start the test player
223 EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
224
225 // wait for the menu change
226 EXPECT_TRUE(waitMenuChange());
227
228 // finally verify that the player is added
229 EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters())
230 .item(mh::MenuItemMatcher()
231 .action("indicator.root")
232 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
233 .string_attribute("x-canonical-scroll-action", "indicator.scroll")
234 .string_attribute("x-canonical-secondary-action", "indicator.mute")
235 .string_attribute("submenu-action", "indicator.indicator-shown")
236 .mode(mh::MenuItemMatcher::Mode::all)
237 .submenu()
238 .item(mh::MenuItemMatcher()
239 .section()
240 .item(silentModeSwitch(false))
241 .item(volumeSlider(INITIAL_VOLUME, "Volume"))
242 )
243 .item(mh::MenuItemMatcher()
244 .section()
245 .item(mh::MenuItemMatcher()
246 .action("indicator.testplayer1.desktop")
247 .label("TestPlayer1")
248 .themed_icon("icon", {"testplayer"})
249 .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
250 )
251 .item(mh::MenuItemMatcher()
252 .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop")
253 .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop")
254 .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop")
255 .string_attribute("x-canonical-type","com.canonical.unity.playback-item")
256 )
257 )
258 .item(mh::MenuItemMatcher()
259 .label("Sound Settings…")
260 .action("indicator.phone-settings")
261 )
262 ).match());
263}
264
265TEST_F(TestIndicator, DesktopBasicInitialVolume)
266{
267 double INITIAL_VOLUME = 0.0;
268
269 ASSERT_NO_THROW(startAccountsService());
270 EXPECT_TRUE(clearGSettingsPlayers());
271 ASSERT_NO_THROW(startPulseDesktop());
272
273 // initialize volumes in pulseaudio
274 EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
275 EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME));
276
277 // start the test player
278 EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
279
280 // start now the indicator, so it picks the new volumes
281 ASSERT_NO_THROW(startIndicator());
282
283 EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
284 .item(mh::MenuItemMatcher()
285 .action("indicator.root")
286 .string_attribute("x-canonical-type", "com.canonical.indicator.root")
287 .string_attribute("x-canonical-secondary-action", "indicator.mute")
288 .mode(mh::MenuItemMatcher::Mode::all)
289 .submenu()
290 .item(mh::MenuItemMatcher()
291 .section()
292 .item(mh::MenuItemMatcher().checkbox()
293 .label("Mute")
294 )
295 .item(volumeSlider(INITIAL_VOLUME, "Volume"))
296 )
297 .item(mh::MenuItemMatcher()
298 .section()
299 .item(mh::MenuItemMatcher()
300 .action("indicator.testplayer1.desktop")
301 .label("TestPlayer1")
302 .themed_icon("icon", {"testplayer"})
303 .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
304 )
305 .item(mh::MenuItemMatcher()
306 .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop")
307 .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop")
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches