Merge lp:~xavi-garcia-mena/indicator-sound/bluetooth-usb-hdmi-labels-with-tests into lp:indicator-sound/15.10
- bluetooth-usb-hdmi-labels-with-tests
- Merge into trunk.15.10
Status: | Approved |
---|---|
Approved by: | Pete Woods |
Approved revision: | 509 |
Proposed branch: | lp:~xavi-garcia-mena/indicator-sound/bluetooth-usb-hdmi-labels-with-tests |
Merge into: | lp:indicator-sound/15.10 |
Prerequisite: | lp:~xavi-garcia-mena/indicator-sound/notifications-integration-tests |
Diff against target: |
7104 lines (+6292/-177) 60 files modified
debian/control (+11/-0) 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 (+11/-9) src/gmenuharness/CMakeLists.txt (+17/-0) src/gmenuharness/MatchResult.cpp (+187/-0) src/gmenuharness/MatchUtils.cpp (+74/-0) src/gmenuharness/MenuItemMatcher.cpp (+1011/-0) src/gmenuharness/MenuMatcher.cpp (+208/-0) src/service.vala (+354/-46) src/sound-menu.vala (+38/-0) src/volume-control-pulse.vala (+69/-14) src/volume-control.vala (+14/-0) tests/CMakeLists.txt (+113/-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 (+8/-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 (+807/-0) tests/integration/indicator-sound-test-base.h (+146/-0) tests/integration/main.cpp (+58/-0) tests/integration/test-indicator.cpp (+963/-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 (+1/-1) 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 (+13/-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/bluetooth-usb-hdmi-labels-with-tests |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Charles Kerr (community) | Approve | ||
Pete Woods (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Review via email: mp+274536@code.launchpad.net |
Commit message
This branch adds the labels to wired/bluetooth
It includes integration tests with gmenuharness.
Description of the change
This branch adds the labels to wired/bluetooth
It includes integration tests with gmenuharness.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:512
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:512
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:513
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:513
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 506. By Xavi Garcia
-
Wily branch for MPRIS controls Fixes: #1373313
Approved by: PS Jenkins bot, Xavi Garcia - 507. By CI Train Bot Account
-
Releasing 12.10.2+
15.10.20151019- 0ubuntu1 - 508. By Xavi Garcia
-
Fixed conflict. Updated changelog to be in sync with trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:514
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Pete Woods (pete-woods) wrote : | # |
Looks good, but please remove the commented code and qDebug() << "=============" type output.
- 509. By Xavi Garcia
-
Erased log statements and commented lines
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:514
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Charles Kerr (charlesk) wrote : | # |
Overall this looks great. As I've said before I /love/ how readable gmenuharness makes indicator test code. All our indicator tests should look this good.
That said there are a handful bugs ranging from minor signed/unsigned mismatches to memory leaks and a crasher. Some of these are in the i-sound code/tests and some are in the harness and should also be looked at upstream by Pete.
Also, I'm stealing code that wraps ResourcePtr around gio tags.
Xavi Garcia (xavi-garcia-mena) wrote : | # |
Thanks a lot for the good review, Charles!
Comments inline...
- 510. By Xavi Garcia
-
Fixes after review
Charles Kerr (charlesk) wrote : | # |
Thanks for the fixes Xavi!
Unmerged revisions
- 510. By Xavi Garcia
-
Fixes after review
- 509. By Xavi Garcia
-
Erased log statements and commented lines
Preview Diff
1 | === modified file 'debian/control' | |||
2 | --- debian/control 2015-02-27 17:52:05 +0000 | |||
3 | +++ debian/control 2015-10-28 09:41:22 +0000 | |||
4 | @@ -5,11 +5,13 @@ | |||
5 | 5 | XSBC-Original-Maintainer: Conor Curran <conor.curran@canonical.com> | 5 | XSBC-Original-Maintainer: Conor Curran <conor.curran@canonical.com> |
6 | 6 | Build-Depends: debhelper (>= 9.0), | 6 | Build-Depends: debhelper (>= 9.0), |
7 | 7 | cmake, | 7 | cmake, |
8 | 8 | cmake-extras (>= 0.4), | ||
9 | 8 | dbus, | 9 | dbus, |
10 | 9 | dbus-test-runner (>> 14.04.0+14.04.20150120.1), | 10 | dbus-test-runner (>> 14.04.0+14.04.20150120.1), |
11 | 10 | dh-translations, | 11 | dh-translations, |
12 | 11 | gir1.2-accountsservice-1.0, | 12 | gir1.2-accountsservice-1.0, |
13 | 12 | gnome-common, | 13 | gnome-common, |
14 | 14 | google-mock (>= 1.6.0+svn437), | ||
15 | 13 | gsettings-ubuntu-schemas, | 15 | gsettings-ubuntu-schemas, |
16 | 14 | autotools-dev, | 16 | autotools-dev, |
17 | 15 | valac (>= 0.20), | 17 | valac (>= 0.20), |
18 | @@ -18,13 +20,22 @@ | |||
19 | 18 | libgirepository1.0-dev, | 20 | libgirepository1.0-dev, |
20 | 19 | libglib2.0-dev (>= 2.22.3), | 21 | libglib2.0-dev (>= 2.22.3), |
21 | 20 | libgtest-dev, | 22 | libgtest-dev, |
22 | 23 | libqtdbusmock1-dev (>= 0.3), | ||
23 | 24 | libqtdbustest1-dev, | ||
24 | 25 | libunity-api-dev, | ||
25 | 21 | liburl-dispatcher1-dev, | 26 | liburl-dispatcher1-dev, |
26 | 22 | libpulse-dev (>= 1:4.0-0ubuntu21), | 27 | libpulse-dev (>= 1:4.0-0ubuntu21), |
27 | 23 | libpulse-mainloop-glib0 (>= 0.9.18), | 28 | libpulse-mainloop-glib0 (>= 0.9.18), |
28 | 24 | libnotify-dev, | 29 | libnotify-dev, |
29 | 25 | libgee-dev, | 30 | libgee-dev, |
30 | 26 | libxml2-dev, | 31 | libxml2-dev, |
31 | 32 | pulseaudio, | ||
32 | 27 | python3-dbusmock, | 33 | python3-dbusmock, |
33 | 34 | qt5-default, | ||
34 | 35 | qtbase5-dev, | ||
35 | 36 | qtbase5-dev-tools, | ||
36 | 37 | qtdeclarative5-dev, | ||
37 | 38 | qtdeclarative5-dev-tools, | ||
38 | 28 | Standards-Version: 3.9.4 | 39 | Standards-Version: 3.9.4 |
39 | 29 | Homepage: https://launchpad.net/indicator-sound | 40 | Homepage: https://launchpad.net/indicator-sound |
40 | 30 | # If you aren't a member of ~indicator-applet-developers but need to upload | 41 | # If you aren't a member of ~indicator-applet-developers but need to upload |
41 | 31 | 42 | ||
42 | === added directory 'include' | |||
43 | === added file 'include/CMakeLists.txt' | |||
44 | --- include/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
45 | +++ include/CMakeLists.txt 2015-10-28 09:41:22 +0000 | |||
46 | @@ -0,0 +1,1 @@ | |||
47 | 1 | add_subdirectory(unity) | ||
48 | 0 | 2 | ||
49 | === added directory 'include/unity' | |||
50 | === added file 'include/unity/CMakeLists.txt' | |||
51 | --- include/unity/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
52 | +++ include/unity/CMakeLists.txt 2015-10-28 09:41:22 +0000 | |||
53 | @@ -0,0 +1,1 @@ | |||
54 | 1 | add_subdirectory(gmenuharness) | ||
55 | 0 | 2 | ||
56 | === added directory 'include/unity/gmenuharness' | |||
57 | === added file 'include/unity/gmenuharness/MatchResult.h' | |||
58 | --- include/unity/gmenuharness/MatchResult.h 1970-01-01 00:00:00 +0000 | |||
59 | +++ include/unity/gmenuharness/MatchResult.h 2015-10-28 09:41:22 +0000 | |||
60 | @@ -0,0 +1,66 @@ | |||
61 | 1 | /* | ||
62 | 2 | * Copyright © 2014 Canonical Ltd. | ||
63 | 3 | * | ||
64 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
65 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
66 | 6 | * as published by the Free Software Foundation. | ||
67 | 7 | * | ||
68 | 8 | * This program is distributed in the hope that it will be useful, | ||
69 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
70 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
71 | 11 | * GNU Lesser General Public License for more details. | ||
72 | 12 | * | ||
73 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
74 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
75 | 15 | * | ||
76 | 16 | * Authored by: Pete Woods <pete.woods@canonical.com> | ||
77 | 17 | */ | ||
78 | 18 | |||
79 | 19 | #pragma once | ||
80 | 20 | |||
81 | 21 | #include <vector> | ||
82 | 22 | #include <memory> | ||
83 | 23 | #include <string> | ||
84 | 24 | |||
85 | 25 | namespace unity | ||
86 | 26 | { | ||
87 | 27 | |||
88 | 28 | namespace gmenuharness | ||
89 | 29 | { | ||
90 | 30 | |||
91 | 31 | class MatchResult | ||
92 | 32 | { | ||
93 | 33 | public: | ||
94 | 34 | MatchResult(); | ||
95 | 35 | |||
96 | 36 | MatchResult(MatchResult&& other); | ||
97 | 37 | |||
98 | 38 | MatchResult(const MatchResult& other); | ||
99 | 39 | |||
100 | 40 | MatchResult& operator=(const MatchResult& other); | ||
101 | 41 | |||
102 | 42 | MatchResult& operator=(MatchResult&& other); | ||
103 | 43 | |||
104 | 44 | ~MatchResult() = default; | ||
105 | 45 | |||
106 | 46 | MatchResult createChild() const; | ||
107 | 47 | |||
108 | 48 | void failure(const std::vector<unsigned int>& location, const std::string& message); | ||
109 | 49 | |||
110 | 50 | void merge(const MatchResult& other); | ||
111 | 51 | |||
112 | 52 | bool success() const; | ||
113 | 53 | |||
114 | 54 | bool hasTimedOut() const; | ||
115 | 55 | |||
116 | 56 | std::string concat_failures() const; | ||
117 | 57 | |||
118 | 58 | protected: | ||
119 | 59 | struct Priv; | ||
120 | 60 | |||
121 | 61 | std::shared_ptr<Priv> p; | ||
122 | 62 | }; | ||
123 | 63 | |||
124 | 64 | } // namespace gmenuharness | ||
125 | 65 | |||
126 | 66 | } // namespace unity | ||
127 | 0 | 67 | ||
128 | === added file 'include/unity/gmenuharness/MatchUtils.h' | |||
129 | --- include/unity/gmenuharness/MatchUtils.h 1970-01-01 00:00:00 +0000 | |||
130 | +++ include/unity/gmenuharness/MatchUtils.h 2015-10-28 09:41:22 +0000 | |||
131 | @@ -0,0 +1,42 @@ | |||
132 | 1 | /* | ||
133 | 2 | * Copyright © 2014 Canonical Ltd. | ||
134 | 3 | * | ||
135 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
136 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
137 | 6 | * as published by the Free Software Foundation. | ||
138 | 7 | * | ||
139 | 8 | * This program is distributed in the hope that it will be useful, | ||
140 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
141 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
142 | 11 | * GNU Lesser General Public License for more details. | ||
143 | 12 | * | ||
144 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
145 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
146 | 15 | * | ||
147 | 16 | * Authored by: Pete Woods <pete.woods@canonical.com> | ||
148 | 17 | */ | ||
149 | 18 | |||
150 | 19 | #pragma once | ||
151 | 20 | |||
152 | 21 | #include <memory> | ||
153 | 22 | #include <string> | ||
154 | 23 | |||
155 | 24 | #include <gio/gio.h> | ||
156 | 25 | |||
157 | 26 | namespace unity | ||
158 | 27 | { | ||
159 | 28 | |||
160 | 29 | namespace gmenuharness | ||
161 | 30 | { | ||
162 | 31 | |||
163 | 32 | void waitForCore(GObject* obj, const std::string& signalName, unsigned int timeout = 10); | ||
164 | 33 | |||
165 | 34 | void menuWaitForItems(const std::shared_ptr<GMenuModel>& menu, unsigned int timeout = 10); | ||
166 | 35 | |||
167 | 36 | void g_object_deleter(gpointer object); | ||
168 | 37 | |||
169 | 38 | void gvariant_deleter(GVariant* varptr); | ||
170 | 39 | |||
171 | 40 | } //namespace gmenuharness | ||
172 | 41 | |||
173 | 42 | } // namespace unity | ||
174 | 0 | 43 | ||
175 | === added file 'include/unity/gmenuharness/MenuItemMatcher.h' | |||
176 | --- include/unity/gmenuharness/MenuItemMatcher.h 1970-01-01 00:00:00 +0000 | |||
177 | +++ include/unity/gmenuharness/MenuItemMatcher.h 2015-10-28 09:41:22 +0000 | |||
178 | @@ -0,0 +1,143 @@ | |||
179 | 1 | /* | ||
180 | 2 | * Copyright © 2014 Canonical Ltd. | ||
181 | 3 | * | ||
182 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
183 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
184 | 6 | * as published by the Free Software Foundation. | ||
185 | 7 | * | ||
186 | 8 | * This program is distributed in the hope that it will be useful, | ||
187 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
188 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
189 | 11 | * GNU Lesser General Public License for more details. | ||
190 | 12 | * | ||
191 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
192 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
193 | 15 | * | ||
194 | 16 | * Authored by: Pete Woods <pete.woods@canonical.com> | ||
195 | 17 | */ | ||
196 | 18 | |||
197 | 19 | #pragma once | ||
198 | 20 | |||
199 | 21 | #include <map> | ||
200 | 22 | #include <memory> | ||
201 | 23 | #include <string> | ||
202 | 24 | |||
203 | 25 | #include <gio/gio.h> | ||
204 | 26 | |||
205 | 27 | namespace unity | ||
206 | 28 | { | ||
207 | 29 | |||
208 | 30 | namespace gmenuharness | ||
209 | 31 | { | ||
210 | 32 | |||
211 | 33 | class MatchResult; | ||
212 | 34 | |||
213 | 35 | class MenuItemMatcher | ||
214 | 36 | { | ||
215 | 37 | public: | ||
216 | 38 | enum class Mode | ||
217 | 39 | { | ||
218 | 40 | all, | ||
219 | 41 | starts_with, | ||
220 | 42 | ends_with | ||
221 | 43 | }; | ||
222 | 44 | |||
223 | 45 | enum class Type | ||
224 | 46 | { | ||
225 | 47 | plain, | ||
226 | 48 | checkbox, | ||
227 | 49 | radio | ||
228 | 50 | }; | ||
229 | 51 | |||
230 | 52 | static MenuItemMatcher checkbox(); | ||
231 | 53 | |||
232 | 54 | static MenuItemMatcher radio(); | ||
233 | 55 | |||
234 | 56 | MenuItemMatcher(); | ||
235 | 57 | |||
236 | 58 | ~MenuItemMatcher(); | ||
237 | 59 | |||
238 | 60 | MenuItemMatcher(const MenuItemMatcher& other); | ||
239 | 61 | |||
240 | 62 | MenuItemMatcher(MenuItemMatcher&& other); | ||
241 | 63 | |||
242 | 64 | MenuItemMatcher& operator=(const MenuItemMatcher& other); | ||
243 | 65 | |||
244 | 66 | MenuItemMatcher& operator=(MenuItemMatcher&& other); | ||
245 | 67 | |||
246 | 68 | MenuItemMatcher& type(Type type); | ||
247 | 69 | |||
248 | 70 | MenuItemMatcher& label(const std::string& label); | ||
249 | 71 | |||
250 | 72 | MenuItemMatcher& action(const std::string& action); | ||
251 | 73 | |||
252 | 74 | MenuItemMatcher& state_icons(const std::vector<std::string>& state); | ||
253 | 75 | |||
254 | 76 | MenuItemMatcher& icon(const std::string& icon); | ||
255 | 77 | |||
256 | 78 | MenuItemMatcher& themed_icon(const std::string& iconName, const std::vector<std::string>& icons); | ||
257 | 79 | |||
258 | 80 | MenuItemMatcher& widget(const std::string& widget); | ||
259 | 81 | |||
260 | 82 | MenuItemMatcher& pass_through_attribute(const std::string& actionName, const std::shared_ptr<GVariant>& value); | ||
261 | 83 | |||
262 | 84 | MenuItemMatcher& pass_through_boolean_attribute(const std::string& actionName, bool value); | ||
263 | 85 | |||
264 | 86 | MenuItemMatcher& pass_through_string_attribute(const std::string& actionName, const std::string& value); | ||
265 | 87 | |||
266 | 88 | MenuItemMatcher& pass_through_double_attribute(const std::string& actionName, double value); | ||
267 | 89 | |||
268 | 90 | MenuItemMatcher& round_doubles(double maxDifference); | ||
269 | 91 | |||
270 | 92 | MenuItemMatcher& attribute(const std::string& name, const std::shared_ptr<GVariant>& value); | ||
271 | 93 | |||
272 | 94 | MenuItemMatcher& boolean_attribute(const std::string& name, bool value); | ||
273 | 95 | |||
274 | 96 | MenuItemMatcher& string_attribute(const std::string& name, const std::string& value); | ||
275 | 97 | |||
276 | 98 | MenuItemMatcher& int32_attribute(const std::string& name, int value); | ||
277 | 99 | |||
278 | 100 | MenuItemMatcher& int64_attribute(const std::string& name, int value); | ||
279 | 101 | |||
280 | 102 | MenuItemMatcher& double_attribute(const std::string& name, double value); | ||
281 | 103 | |||
282 | 104 | MenuItemMatcher& attribute_not_set(const std::string& name); | ||
283 | 105 | |||
284 | 106 | MenuItemMatcher& toggled(bool toggled); | ||
285 | 107 | |||
286 | 108 | MenuItemMatcher& mode(Mode mode); | ||
287 | 109 | |||
288 | 110 | MenuItemMatcher& submenu(); | ||
289 | 111 | |||
290 | 112 | MenuItemMatcher& section(); | ||
291 | 113 | |||
292 | 114 | MenuItemMatcher& is_empty(); | ||
293 | 115 | |||
294 | 116 | MenuItemMatcher& has_exactly(std::size_t children); | ||
295 | 117 | |||
296 | 118 | MenuItemMatcher& item(const MenuItemMatcher& item); | ||
297 | 119 | |||
298 | 120 | MenuItemMatcher& item(MenuItemMatcher&& item); | ||
299 | 121 | |||
300 | 122 | MenuItemMatcher& pass_through_activate(const std::string& action, const std::shared_ptr<GVariant>& parameter = nullptr); | ||
301 | 123 | |||
302 | 124 | MenuItemMatcher& activate(const std::shared_ptr<GVariant>& parameter = nullptr); | ||
303 | 125 | |||
304 | 126 | MenuItemMatcher& set_pass_through_action_state(const std::string& action, const std::shared_ptr<GVariant>& state); | ||
305 | 127 | |||
306 | 128 | MenuItemMatcher& set_action_state(const std::shared_ptr<GVariant>& state); | ||
307 | 129 | |||
308 | 130 | void match(MatchResult& matchResult, const std::vector<unsigned int>& location, | ||
309 | 131 | const std::shared_ptr<GMenuModel>& menu, | ||
310 | 132 | std::map<std::string, std::shared_ptr<GActionGroup>>& actions, | ||
311 | 133 | unsigned int index) const; | ||
312 | 134 | |||
313 | 135 | protected: | ||
314 | 136 | struct Priv; | ||
315 | 137 | |||
316 | 138 | std::shared_ptr<Priv> p; | ||
317 | 139 | }; | ||
318 | 140 | |||
319 | 141 | } // namespace gmenuharness | ||
320 | 142 | |||
321 | 143 | } // namespace unity | ||
322 | 0 | 144 | ||
323 | === added file 'include/unity/gmenuharness/MenuMatcher.h' | |||
324 | --- include/unity/gmenuharness/MenuMatcher.h 1970-01-01 00:00:00 +0000 | |||
325 | +++ include/unity/gmenuharness/MenuMatcher.h 2015-10-28 09:41:22 +0000 | |||
326 | @@ -0,0 +1,95 @@ | |||
327 | 1 | /* | ||
328 | 2 | * Copyright © 2014 Canonical Ltd. | ||
329 | 3 | * | ||
330 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
331 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
332 | 6 | * as published by the Free Software Foundation. | ||
333 | 7 | * | ||
334 | 8 | * This program is distributed in the hope that it will be useful, | ||
335 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
336 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
337 | 11 | * GNU Lesser General Public License for more details. | ||
338 | 12 | * | ||
339 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
340 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
341 | 15 | * | ||
342 | 16 | * Authored by: Pete Woods <pete.woods@canonical.com> | ||
343 | 17 | */ | ||
344 | 18 | |||
345 | 19 | #pragma once | ||
346 | 20 | |||
347 | 21 | #define EXPECT_MATCHRESULT(statement) \ | ||
348 | 22 | do {\ | ||
349 | 23 | auto result = (statement);\ | ||
350 | 24 | GTEST_TEST_BOOLEAN_(result.success(), #statement, false, true, \ | ||
351 | 25 | GTEST_NONFATAL_FAILURE_) << result.concat_failures().c_str(); \ | ||
352 | 26 | } while (0) | ||
353 | 27 | |||
354 | 28 | #include <unity/gmenuharness/MatchResult.h> | ||
355 | 29 | #include <unity/gmenuharness/MenuItemMatcher.h> | ||
356 | 30 | |||
357 | 31 | #include <memory> | ||
358 | 32 | #include <vector> | ||
359 | 33 | |||
360 | 34 | namespace unity | ||
361 | 35 | { | ||
362 | 36 | |||
363 | 37 | namespace gmenuharness | ||
364 | 38 | { | ||
365 | 39 | |||
366 | 40 | class MenuMatcher | ||
367 | 41 | { | ||
368 | 42 | public: | ||
369 | 43 | class Parameters | ||
370 | 44 | { | ||
371 | 45 | public: | ||
372 | 46 | Parameters( | ||
373 | 47 | const std::string& busName, | ||
374 | 48 | const std::vector<std::pair<std::string, std::string>>& actions, | ||
375 | 49 | const std::string& menuObjectPath); | ||
376 | 50 | |||
377 | 51 | ~Parameters(); | ||
378 | 52 | |||
379 | 53 | Parameters(const Parameters& other); | ||
380 | 54 | |||
381 | 55 | Parameters(Parameters&& other); | ||
382 | 56 | |||
383 | 57 | Parameters& operator=(const Parameters& other); | ||
384 | 58 | |||
385 | 59 | Parameters& operator=(Parameters&& other); | ||
386 | 60 | |||
387 | 61 | protected: | ||
388 | 62 | friend MenuMatcher; | ||
389 | 63 | |||
390 | 64 | struct Priv; | ||
391 | 65 | |||
392 | 66 | std::shared_ptr<Priv> p; | ||
393 | 67 | }; | ||
394 | 68 | |||
395 | 69 | MenuMatcher(const Parameters& parameters); | ||
396 | 70 | |||
397 | 71 | ~MenuMatcher(); | ||
398 | 72 | |||
399 | 73 | MenuMatcher(const MenuMatcher& other) = delete; | ||
400 | 74 | |||
401 | 75 | MenuMatcher(MenuMatcher&& other) = delete; | ||
402 | 76 | |||
403 | 77 | MenuMatcher& operator=(const MenuMatcher& other) = delete; | ||
404 | 78 | |||
405 | 79 | MenuMatcher& operator=(MenuMatcher&& other) = delete; | ||
406 | 80 | |||
407 | 81 | MenuMatcher& item(const MenuItemMatcher& item); | ||
408 | 82 | |||
409 | 83 | MatchResult match() const; | ||
410 | 84 | |||
411 | 85 | void match(MatchResult& matchResult) const; | ||
412 | 86 | |||
413 | 87 | protected: | ||
414 | 88 | struct Priv; | ||
415 | 89 | |||
416 | 90 | std::shared_ptr<Priv> p; | ||
417 | 91 | }; | ||
418 | 92 | |||
419 | 93 | } // gmenuharness | ||
420 | 94 | |||
421 | 95 | } // unity | ||
422 | 0 | 96 | ||
423 | === modified file 'src/CMakeLists.txt' | |||
424 | --- src/CMakeLists.txt 2015-02-19 16:23:01 +0000 | |||
425 | +++ src/CMakeLists.txt 2015-10-28 09:41:22 +0000 | |||
426 | @@ -8,12 +8,12 @@ | |||
427 | 8 | set(VAPI_PATH "${CMAKE_CURRENT_BINARY_DIR}/indicator-sound-service.vapi") | 8 | set(VAPI_PATH "${CMAKE_CURRENT_BINARY_DIR}/indicator-sound-service.vapi") |
428 | 9 | 9 | ||
429 | 10 | vapi_gen(accounts-service | 10 | vapi_gen(accounts-service |
436 | 11 | LIBRARY | 11 | LIBRARY |
437 | 12 | accounts-service | 12 | accounts-service |
438 | 13 | PACKAGES | 13 | PACKAGES |
439 | 14 | gio-2.0 | 14 | gio-2.0 |
440 | 15 | INPUT | 15 | INPUT |
441 | 16 | /usr/share/gir-1.0/AccountsService-1.0.gir | 16 | /usr/share/gir-1.0/AccountsService-1.0.gir |
442 | 17 | ) | 17 | ) |
443 | 18 | 18 | ||
444 | 19 | vala_init(indicator-sound-service | 19 | vala_init(indicator-sound-service |
445 | @@ -70,7 +70,7 @@ | |||
446 | 70 | media-player-user.vala | 70 | media-player-user.vala |
447 | 71 | DEPENDS | 71 | DEPENDS |
448 | 72 | media-player | 72 | media-player |
450 | 73 | accounts-service-sound-settings | 73 | accounts-service-sound-settings |
451 | 74 | greeter-broadcast | 74 | greeter-broadcast |
452 | 75 | ) | 75 | ) |
453 | 76 | vala_add(indicator-sound-service | 76 | vala_add(indicator-sound-service |
454 | @@ -103,6 +103,7 @@ | |||
455 | 103 | sound-menu.vala | 103 | sound-menu.vala |
456 | 104 | DEPENDS | 104 | DEPENDS |
457 | 105 | media-player | 105 | media-player |
458 | 106 | volume-control | ||
459 | 106 | ) | 107 | ) |
460 | 107 | vala_add(indicator-sound-service | 108 | vala_add(indicator-sound-service |
461 | 108 | accounts-service-user.vala | 109 | accounts-service-user.vala |
462 | @@ -164,8 +165,8 @@ | |||
463 | 164 | ) | 165 | ) |
464 | 165 | 166 | ||
465 | 166 | add_library( | 167 | add_library( |
468 | 167 | indicator-sound-service-lib STATIC | 168 | indicator-sound-service-lib STATIC |
469 | 168 | ${INDICATOR_SOUND_SOURCES} | 169 | ${INDICATOR_SOUND_SOURCES} |
470 | 169 | ) | 170 | ) |
471 | 170 | 171 | ||
472 | 171 | target_link_libraries( | 172 | target_link_libraries( |
473 | @@ -206,3 +207,4 @@ | |||
474 | 206 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/indicator-sound/ | 207 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/indicator-sound/ |
475 | 207 | ) | 208 | ) |
476 | 208 | 209 | ||
477 | 210 | add_subdirectory(gmenuharness) | ||
478 | 209 | 211 | ||
479 | === added directory 'src/gmenuharness' | |||
480 | === added file 'src/gmenuharness/CMakeLists.txt' | |||
481 | --- src/gmenuharness/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
482 | +++ src/gmenuharness/CMakeLists.txt 2015-10-28 09:41:22 +0000 | |||
483 | @@ -0,0 +1,17 @@ | |||
484 | 1 | pkg_check_modules(UNITY_API libunity-api>=0.1.3 REQUIRED) | ||
485 | 2 | include_directories(${UNITY_API_INCLUDE_DIRS}) | ||
486 | 3 | |||
487 | 4 | include_directories("${CMAKE_SOURCE_DIR}/include") | ||
488 | 5 | |||
489 | 6 | add_library( | ||
490 | 7 | gmenuharness-shared SHARED | ||
491 | 8 | MatchResult.cpp | ||
492 | 9 | MatchUtils.cpp | ||
493 | 10 | MenuItemMatcher.cpp | ||
494 | 11 | MenuMatcher.cpp | ||
495 | 12 | ) | ||
496 | 13 | |||
497 | 14 | target_link_libraries( | ||
498 | 15 | gmenuharness-shared | ||
499 | 16 | ${GLIB_LDFLAGS} | ||
500 | 17 | ) | ||
501 | 0 | 18 | ||
502 | === added file 'src/gmenuharness/MatchResult.cpp' | |||
503 | --- src/gmenuharness/MatchResult.cpp 1970-01-01 00:00:00 +0000 | |||
504 | +++ src/gmenuharness/MatchResult.cpp 2015-10-28 09:41:22 +0000 | |||
505 | @@ -0,0 +1,187 @@ | |||
506 | 1 | /* | ||
507 | 2 | * Copyright © 2014 Canonical Ltd. | ||
508 | 3 | * | ||
509 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
510 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
511 | 6 | * as published by the Free Software Foundation. | ||
512 | 7 | * | ||
513 | 8 | * This program is distributed in the hope that it will be useful, | ||
514 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
515 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
516 | 11 | * GNU Lesser General Public License for more details. | ||
517 | 12 | * | ||
518 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
519 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
520 | 15 | * | ||
521 | 16 | * Authored by: Pete Woods <pete.woods@canonical.com> | ||
522 | 17 | */ | ||
523 | 18 | |||
524 | 19 | #include <unity/gmenuharness/MatchResult.h> | ||
525 | 20 | |||
526 | 21 | #include <chrono> | ||
527 | 22 | #include <map> | ||
528 | 23 | #include <sstream> | ||
529 | 24 | #include <iostream> | ||
530 | 25 | |||
531 | 26 | using namespace std; | ||
532 | 27 | |||
533 | 28 | namespace unity | ||
534 | 29 | { | ||
535 | 30 | |||
536 | 31 | namespace gmenuharness | ||
537 | 32 | { | ||
538 | 33 | |||
539 | 34 | namespace | ||
540 | 35 | { | ||
541 | 36 | |||
542 | 37 | |||
543 | 38 | static void printLocation(ostream& ss, const vector<unsigned int>& location, bool first) | ||
544 | 39 | { | ||
545 | 40 | for (auto i : location) | ||
546 | 41 | { | ||
547 | 42 | ss << " "; | ||
548 | 43 | if (first) | ||
549 | 44 | { | ||
550 | 45 | ss << i; | ||
551 | 46 | } | ||
552 | 47 | else | ||
553 | 48 | { | ||
554 | 49 | ss << " "; | ||
555 | 50 | } | ||
556 | 51 | } | ||
557 | 52 | ss << " "; | ||
558 | 53 | } | ||
559 | 54 | |||
560 | 55 | struct compare_vector | ||
561 | 56 | { | ||
562 | 57 | bool operator()(const vector<unsigned int>& a, | ||
563 | 58 | const vector<unsigned int>& b) const | ||
564 | 59 | { | ||
565 | 60 | auto p1 = a.begin(); | ||
566 | 61 | auto p2 = b.begin(); | ||
567 | 62 | |||
568 | 63 | while (p1 != a.end()) | ||
569 | 64 | { | ||
570 | 65 | if (p2 == b.end()) | ||
571 | 66 | { | ||
572 | 67 | return false; | ||
573 | 68 | } | ||
574 | 69 | if (*p2 > *p1) | ||
575 | 70 | { | ||
576 | 71 | return true; | ||
577 | 72 | } | ||
578 | 73 | if (*p1 > *p2) | ||
579 | 74 | { | ||
580 | 75 | return false; | ||
581 | 76 | } | ||
582 | 77 | |||
583 | 78 | ++p1; | ||
584 | 79 | ++p2; | ||
585 | 80 | } | ||
586 | 81 | |||
587 | 82 | if (p2 != b.end()) | ||
588 | 83 | { | ||
589 | 84 | return true; | ||
590 | 85 | } | ||
591 | 86 | |||
592 | 87 | return false; | ||
593 | 88 | } | ||
594 | 89 | }; | ||
595 | 90 | } | ||
596 | 91 | |||
597 | 92 | struct MatchResult::Priv | ||
598 | 93 | { | ||
599 | 94 | bool m_success = true; | ||
600 | 95 | |||
601 | 96 | map<vector<unsigned int>, vector<string>, compare_vector> m_failures; | ||
602 | 97 | |||
603 | 98 | chrono::time_point<chrono::system_clock> m_timeout = chrono::system_clock::now() + chrono::seconds(10); | ||
604 | 99 | }; | ||
605 | 100 | |||
606 | 101 | MatchResult::MatchResult() : | ||
607 | 102 | p(new Priv) | ||
608 | 103 | { | ||
609 | 104 | } | ||
610 | 105 | |||
611 | 106 | MatchResult::MatchResult(MatchResult&& other) | ||
612 | 107 | { | ||
613 | 108 | *this = move(other); | ||
614 | 109 | } | ||
615 | 110 | |||
616 | 111 | MatchResult::MatchResult(const MatchResult& other) : | ||
617 | 112 | p(new Priv) | ||
618 | 113 | { | ||
619 | 114 | *this = other; | ||
620 | 115 | } | ||
621 | 116 | |||
622 | 117 | MatchResult& MatchResult::operator=(const MatchResult& other) | ||
623 | 118 | { | ||
624 | 119 | p->m_success = other.p->m_success; | ||
625 | 120 | p->m_failures= other.p->m_failures; | ||
626 | 121 | return *this; | ||
627 | 122 | } | ||
628 | 123 | |||
629 | 124 | MatchResult& MatchResult::operator=(MatchResult&& other) | ||
630 | 125 | { | ||
631 | 126 | p = move(other.p); | ||
632 | 127 | return *this; | ||
633 | 128 | } | ||
634 | 129 | |||
635 | 130 | MatchResult MatchResult::createChild() const | ||
636 | 131 | { | ||
637 | 132 | MatchResult child; | ||
638 | 133 | child.p->m_timeout = p->m_timeout; | ||
639 | 134 | return child; | ||
640 | 135 | } | ||
641 | 136 | |||
642 | 137 | void MatchResult::failure(const vector<unsigned int>& location, const string& message) | ||
643 | 138 | { | ||
644 | 139 | p->m_success = false; | ||
645 | 140 | auto it = p->m_failures.find(location); | ||
646 | 141 | if (it == p->m_failures.end()) | ||
647 | 142 | { | ||
648 | 143 | it = p->m_failures.insert(make_pair(location, vector<string>())).first; | ||
649 | 144 | } | ||
650 | 145 | it->second.emplace_back(message); | ||
651 | 146 | } | ||
652 | 147 | |||
653 | 148 | void MatchResult::merge(const MatchResult& other) | ||
654 | 149 | { | ||
655 | 150 | p->m_success &= other.p->m_success; | ||
656 | 151 | for (const auto& e : other.p->m_failures) | ||
657 | 152 | { | ||
658 | 153 | p->m_failures.insert(make_pair(e.first, e.second)); | ||
659 | 154 | } | ||
660 | 155 | } | ||
661 | 156 | |||
662 | 157 | bool MatchResult::success() const | ||
663 | 158 | { | ||
664 | 159 | return p->m_success; | ||
665 | 160 | } | ||
666 | 161 | |||
667 | 162 | bool MatchResult::hasTimedOut() const | ||
668 | 163 | { | ||
669 | 164 | auto now = chrono::system_clock::now(); | ||
670 | 165 | return (now >= p->m_timeout); | ||
671 | 166 | } | ||
672 | 167 | |||
673 | 168 | string MatchResult::concat_failures() const | ||
674 | 169 | { | ||
675 | 170 | stringstream ss; | ||
676 | 171 | ss << "Failed expectations:" << endl; | ||
677 | 172 | for (const auto& failure : p->m_failures) | ||
678 | 173 | { | ||
679 | 174 | bool first = true; | ||
680 | 175 | for (const string& s: failure.second) | ||
681 | 176 | { | ||
682 | 177 | printLocation(ss, failure.first, first); | ||
683 | 178 | first = false; | ||
684 | 179 | ss << s << endl; | ||
685 | 180 | } | ||
686 | 181 | } | ||
687 | 182 | return ss.str(); | ||
688 | 183 | } | ||
689 | 184 | |||
690 | 185 | } // namespace gmenuharness | ||
691 | 186 | |||
692 | 187 | } // namespace unity | ||
693 | 0 | 188 | ||
694 | === added file 'src/gmenuharness/MatchUtils.cpp' | |||
695 | --- src/gmenuharness/MatchUtils.cpp 1970-01-01 00:00:00 +0000 | |||
696 | +++ src/gmenuharness/MatchUtils.cpp 2015-10-28 09:41:22 +0000 | |||
697 | @@ -0,0 +1,74 @@ | |||
698 | 1 | /* | ||
699 | 2 | * Copyright © 2014 Canonical Ltd. | ||
700 | 3 | * | ||
701 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
702 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
703 | 6 | * as published by the Free Software Foundation. | ||
704 | 7 | * | ||
705 | 8 | * This program is distributed in the hope that it will be useful, | ||
706 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
707 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
708 | 11 | * GNU Lesser General Public License for more details. | ||
709 | 12 | * | ||
710 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
711 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
712 | 15 | * | ||
713 | 16 | * Authored by: Pete Woods <pete.woods@canonical.com> | ||
714 | 17 | */ | ||
715 | 18 | |||
716 | 19 | #include <unity/gmenuharness/MatchUtils.h> | ||
717 | 20 | |||
718 | 21 | #include <unity/util/ResourcePtr.h> | ||
719 | 22 | |||
720 | 23 | using namespace std; | ||
721 | 24 | namespace util = unity::util; | ||
722 | 25 | |||
723 | 26 | namespace unity | ||
724 | 27 | { | ||
725 | 28 | |||
726 | 29 | namespace gmenuharness | ||
727 | 30 | { | ||
728 | 31 | |||
729 | 32 | void waitForCore (GObject * obj, const string& signalName, unsigned int timeout) { | ||
730 | 33 | shared_ptr<GMainLoop> loop(g_main_loop_new(nullptr, false), &g_main_loop_unref); | ||
731 | 34 | |||
732 | 35 | /* Our two exit criteria */ | ||
733 | 36 | util::ResourcePtr<gulong, function<void(gulong)>> signal( | ||
734 | 37 | g_signal_connect_swapped(obj, signalName.c_str(), | ||
735 | 38 | G_CALLBACK(g_main_loop_quit), loop.get()), | ||
736 | 39 | [obj](gulong s) | ||
737 | 40 | { | ||
738 | 41 | g_signal_handler_disconnect(obj, s); | ||
739 | 42 | }); | ||
740 | 43 | |||
741 | 44 | util::ResourcePtr<guint, function<void(guint)>> timer(g_timeout_add(timeout, | ||
742 | 45 | [](gpointer user_data) -> gboolean | ||
743 | 46 | { | ||
744 | 47 | g_main_loop_quit((GMainLoop *)user_data); | ||
745 | 48 | return G_SOURCE_CONTINUE; | ||
746 | 49 | }, | ||
747 | 50 | loop.get()), | ||
748 | 51 | &g_source_remove); | ||
749 | 52 | |||
750 | 53 | /* Wait for sync */ | ||
751 | 54 | g_main_loop_run(loop.get()); | ||
752 | 55 | } | ||
753 | 56 | |||
754 | 57 | void menuWaitForItems(const shared_ptr<GMenuModel>& menu, unsigned int timeout) | ||
755 | 58 | { | ||
756 | 59 | waitForCore(G_OBJECT(menu.get()), "items-changed", timeout); | ||
757 | 60 | } | ||
758 | 61 | |||
759 | 62 | void g_object_deleter(gpointer object) | ||
760 | 63 | { | ||
761 | 64 | g_clear_object(&object); | ||
762 | 65 | } | ||
763 | 66 | |||
764 | 67 | void gvariant_deleter(GVariant* varptr) | ||
765 | 68 | { | ||
766 | 69 | g_clear_pointer(&varptr, g_variant_unref); | ||
767 | 70 | } | ||
768 | 71 | |||
769 | 72 | } // namespace gmenuharness | ||
770 | 73 | |||
771 | 74 | } // namespace unity | ||
772 | 0 | 75 | ||
773 | === added file 'src/gmenuharness/MenuItemMatcher.cpp' | |||
774 | --- src/gmenuharness/MenuItemMatcher.cpp 1970-01-01 00:00:00 +0000 | |||
775 | +++ src/gmenuharness/MenuItemMatcher.cpp 2015-10-28 09:41:22 +0000 | |||
776 | @@ -0,0 +1,1011 @@ | |||
777 | 1 | /* | ||
778 | 2 | * Copyright © 2014 Canonical Ltd. | ||
779 | 3 | * | ||
780 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
781 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
782 | 6 | * as published by the Free Software Foundation. | ||
783 | 7 | * | ||
784 | 8 | * This program is distributed in the hope that it will be useful, | ||
785 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
786 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
787 | 11 | * GNU Lesser General Public License for more details. | ||
788 | 12 | * | ||
789 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
790 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
791 | 15 | * | ||
792 | 16 | * Authored by: Pete Woods <pete.woods@canonical.com> | ||
793 | 17 | */ | ||
794 | 18 | |||
795 | 19 | #include <unity/gmenuharness/MatchResult.h> | ||
796 | 20 | #include <unity/gmenuharness/MatchUtils.h> | ||
797 | 21 | #include <unity/gmenuharness/MenuItemMatcher.h> | ||
798 | 22 | |||
799 | 23 | #include <iostream> | ||
800 | 24 | #include <vector> | ||
801 | 25 | #include <map> | ||
802 | 26 | |||
803 | 27 | using namespace std; | ||
804 | 28 | |||
805 | 29 | namespace unity | ||
806 | 30 | { | ||
807 | 31 | |||
808 | 32 | namespace gmenuharness | ||
809 | 33 | { | ||
810 | 34 | |||
811 | 35 | namespace | ||
812 | 36 | { | ||
813 | 37 | |||
814 | 38 | enum class LinkType | ||
815 | 39 | { | ||
816 | 40 | any, | ||
817 | 41 | section, | ||
818 | 42 | submenu | ||
819 | 43 | }; | ||
820 | 44 | |||
821 | 45 | static string bool_to_string(bool value) | ||
822 | 46 | { | ||
823 | 47 | return value? "true" : "false"; | ||
824 | 48 | } | ||
825 | 49 | |||
826 | 50 | static shared_ptr<GVariant> get_action_group_attribute(const shared_ptr<GActionGroup>& actionGroup, const gchar* attribute) | ||
827 | 51 | { | ||
828 | 52 | shared_ptr<GVariant> value( | ||
829 | 53 | g_action_group_get_action_state(actionGroup.get(), attribute), | ||
830 | 54 | &gvariant_deleter); | ||
831 | 55 | return value; | ||
832 | 56 | } | ||
833 | 57 | |||
834 | 58 | static shared_ptr<GVariant> get_attribute(const shared_ptr<GMenuItem> menuItem, const gchar* attribute) | ||
835 | 59 | { | ||
836 | 60 | shared_ptr<GVariant> value( | ||
837 | 61 | g_menu_item_get_attribute_value(menuItem.get(), attribute, nullptr), | ||
838 | 62 | &gvariant_deleter); | ||
839 | 63 | return value; | ||
840 | 64 | } | ||
841 | 65 | |||
842 | 66 | static string get_string_attribute(const shared_ptr<GMenuItem> menuItem, const gchar* attribute) | ||
843 | 67 | { | ||
844 | 68 | string result; | ||
845 | 69 | char* temp = nullptr; | ||
846 | 70 | if (g_menu_item_get_attribute(menuItem.get(), attribute, "s", &temp)) | ||
847 | 71 | { | ||
848 | 72 | result = temp; | ||
849 | 73 | g_free(temp); | ||
850 | 74 | } | ||
851 | 75 | return result; | ||
852 | 76 | } | ||
853 | 77 | |||
854 | 78 | static pair<string, string> split_action(const string& action) | ||
855 | 79 | { | ||
856 | 80 | auto index = action.find('.'); | ||
857 | 81 | |||
858 | 82 | if (index == string::npos) | ||
859 | 83 | { | ||
860 | 84 | return make_pair(string(), action); | ||
861 | 85 | } | ||
862 | 86 | |||
863 | 87 | return make_pair(action.substr(0, index), action.substr(index + 1, action.size())); | ||
864 | 88 | } | ||
865 | 89 | |||
866 | 90 | static string type_to_string(MenuItemMatcher::Type type) | ||
867 | 91 | { | ||
868 | 92 | switch(type) | ||
869 | 93 | { | ||
870 | 94 | case MenuItemMatcher::Type::plain: | ||
871 | 95 | return "plain"; | ||
872 | 96 | case MenuItemMatcher::Type::checkbox: | ||
873 | 97 | return "checkbox"; | ||
874 | 98 | case MenuItemMatcher::Type::radio: | ||
875 | 99 | return "radio"; | ||
876 | 100 | } | ||
877 | 101 | |||
878 | 102 | return string(); | ||
879 | 103 | } | ||
880 | 104 | } | ||
881 | 105 | |||
882 | 106 | struct MenuItemMatcher::Priv | ||
883 | 107 | { | ||
884 | 108 | void all(MatchResult& matchResult, const vector<unsigned int>& location, | ||
885 | 109 | const shared_ptr<GMenuModel>& menu, | ||
886 | 110 | map<string, shared_ptr<GActionGroup>>& actions) | ||
887 | 111 | { | ||
888 | 112 | int count = g_menu_model_get_n_items(menu.get()); | ||
889 | 113 | |||
890 | 114 | if (m_items.size() != (unsigned int) count) | ||
891 | 115 | { | ||
892 | 116 | matchResult.failure( | ||
893 | 117 | location, | ||
894 | 118 | "Expected " + to_string(m_items.size()) | ||
895 | 119 | + " children, but found " + to_string(count)); | ||
896 | 120 | return; | ||
897 | 121 | } | ||
898 | 122 | |||
899 | 123 | for (size_t i = 0; i < m_items.size(); ++i) | ||
900 | 124 | { | ||
901 | 125 | const auto& matcher = m_items.at(i); | ||
902 | 126 | matcher.match(matchResult, location, menu, actions, i); | ||
903 | 127 | } | ||
904 | 128 | } | ||
905 | 129 | |||
906 | 130 | void startsWith(MatchResult& matchResult, const vector<unsigned int>& location, | ||
907 | 131 | const shared_ptr<GMenuModel>& menu, | ||
908 | 132 | map<string, shared_ptr<GActionGroup>>& actions) | ||
909 | 133 | { | ||
910 | 134 | int count = g_menu_model_get_n_items(menu.get()); | ||
911 | 135 | if (m_items.size() > (unsigned int) count) | ||
912 | 136 | { | ||
913 | 137 | matchResult.failure( | ||
914 | 138 | location, | ||
915 | 139 | "Expected at least " + to_string(m_items.size()) | ||
916 | 140 | + " children, but found " + to_string(count)); | ||
917 | 141 | return; | ||
918 | 142 | } | ||
919 | 143 | |||
920 | 144 | for (size_t i = 0; i < m_items.size(); ++i) | ||
921 | 145 | { | ||
922 | 146 | const auto& matcher = m_items.at(i); | ||
923 | 147 | matcher.match(matchResult, location, menu, actions, i); | ||
924 | 148 | } | ||
925 | 149 | } | ||
926 | 150 | |||
927 | 151 | void endsWith(MatchResult& matchResult, const vector<unsigned int>& location, | ||
928 | 152 | const shared_ptr<GMenuModel>& menu, | ||
929 | 153 | map<string, shared_ptr<GActionGroup>>& actions) | ||
930 | 154 | { | ||
931 | 155 | int count = g_menu_model_get_n_items(menu.get()); | ||
932 | 156 | if (m_items.size() > (unsigned int) count) | ||
933 | 157 | { | ||
934 | 158 | matchResult.failure( | ||
935 | 159 | location, | ||
936 | 160 | "Expected at least " + to_string(m_items.size()) | ||
937 | 161 | + " children, but found " + to_string(count)); | ||
938 | 162 | return; | ||
939 | 163 | } | ||
940 | 164 | |||
941 | 165 | // match the last N items | ||
942 | 166 | size_t j; | ||
943 | 167 | for (size_t i = count - m_items.size(), j = 0; i < count && j < m_items.size(); ++i, ++j) | ||
944 | 168 | { | ||
945 | 169 | const auto& matcher = m_items.at(j); | ||
946 | 170 | matcher.match(matchResult, location, menu, actions, i); | ||
947 | 171 | } | ||
948 | 172 | } | ||
949 | 173 | |||
950 | 174 | Type m_type = Type::plain; | ||
951 | 175 | |||
952 | 176 | Mode m_mode = Mode::all; | ||
953 | 177 | |||
954 | 178 | LinkType m_linkType = LinkType::any; | ||
955 | 179 | |||
956 | 180 | shared_ptr<size_t> m_expectedSize; | ||
957 | 181 | |||
958 | 182 | shared_ptr<string> m_label; | ||
959 | 183 | |||
960 | 184 | shared_ptr<string> m_icon; | ||
961 | 185 | |||
962 | 186 | map<string, vector<std::string>> m_themed_icons; | ||
963 | 187 | |||
964 | 188 | shared_ptr<string> m_action; | ||
965 | 189 | |||
966 | 190 | vector<std::string> m_state_icons; | ||
967 | 191 | |||
968 | 192 | vector<pair<string, shared_ptr<GVariant>>> m_attributes; | ||
969 | 193 | |||
970 | 194 | vector<string> m_not_exist_attributes; | ||
971 | 195 | |||
972 | 196 | vector<pair<string, shared_ptr<GVariant>>> m_pass_through_attributes; | ||
973 | 197 | |||
974 | 198 | shared_ptr<bool> m_isToggled; | ||
975 | 199 | |||
976 | 200 | vector<MenuItemMatcher> m_items; | ||
977 | 201 | |||
978 | 202 | vector<pair<string, shared_ptr<GVariant>>> m_activations; | ||
979 | 203 | |||
980 | 204 | vector<pair<string, shared_ptr<GVariant>>> m_setActionStates; | ||
981 | 205 | |||
982 | 206 | double m_maxDifference = 0.0; | ||
983 | 207 | }; | ||
984 | 208 | |||
985 | 209 | MenuItemMatcher MenuItemMatcher::checkbox() | ||
986 | 210 | { | ||
987 | 211 | MenuItemMatcher matcher; | ||
988 | 212 | matcher.type(Type::checkbox); | ||
989 | 213 | return matcher; | ||
990 | 214 | } | ||
991 | 215 | |||
992 | 216 | MenuItemMatcher MenuItemMatcher::radio() | ||
993 | 217 | { | ||
994 | 218 | MenuItemMatcher matcher; | ||
995 | 219 | matcher.type(Type::radio); | ||
996 | 220 | return matcher; | ||
997 | 221 | } | ||
998 | 222 | |||
999 | 223 | MenuItemMatcher::MenuItemMatcher() : | ||
1000 | 224 | p(new Priv) | ||
1001 | 225 | { | ||
1002 | 226 | } | ||
1003 | 227 | |||
1004 | 228 | MenuItemMatcher::~MenuItemMatcher() | ||
1005 | 229 | { | ||
1006 | 230 | } | ||
1007 | 231 | |||
1008 | 232 | MenuItemMatcher::MenuItemMatcher(const MenuItemMatcher& other) : | ||
1009 | 233 | p(new Priv) | ||
1010 | 234 | { | ||
1011 | 235 | *this = other; | ||
1012 | 236 | } | ||
1013 | 237 | |||
1014 | 238 | MenuItemMatcher::MenuItemMatcher(MenuItemMatcher&& other) | ||
1015 | 239 | { | ||
1016 | 240 | *this = move(other); | ||
1017 | 241 | } | ||
1018 | 242 | |||
1019 | 243 | MenuItemMatcher& MenuItemMatcher::operator=(const MenuItemMatcher& other) | ||
1020 | 244 | { | ||
1021 | 245 | p->m_type = other.p->m_type; | ||
1022 | 246 | p->m_mode = other.p->m_mode; | ||
1023 | 247 | p->m_expectedSize = other.p->m_expectedSize; | ||
1024 | 248 | p->m_label = other.p->m_label; | ||
1025 | 249 | p->m_icon = other.p->m_icon; | ||
1026 | 250 | p->m_themed_icons = other.p->m_themed_icons; | ||
1027 | 251 | p->m_action = other.p->m_action; | ||
1028 | 252 | p->m_state_icons = other.p->m_state_icons; | ||
1029 | 253 | p->m_attributes = other.p->m_attributes; | ||
1030 | 254 | p->m_not_exist_attributes = other.p->m_not_exist_attributes; | ||
1031 | 255 | p->m_pass_through_attributes = other.p->m_pass_through_attributes; | ||
1032 | 256 | p->m_isToggled = other.p->m_isToggled; | ||
1033 | 257 | p->m_linkType = other.p->m_linkType; | ||
1034 | 258 | p->m_items = other.p->m_items; | ||
1035 | 259 | p->m_activations = other.p->m_activations; | ||
1036 | 260 | p->m_setActionStates = other.p->m_setActionStates; | ||
1037 | 261 | p->m_maxDifference = other.p->m_maxDifference; | ||
1038 | 262 | return *this; | ||
1039 | 263 | } | ||
1040 | 264 | |||
1041 | 265 | MenuItemMatcher& MenuItemMatcher::operator=(MenuItemMatcher&& other) | ||
1042 | 266 | { | ||
1043 | 267 | p = move(other.p); | ||
1044 | 268 | return *this; | ||
1045 | 269 | } | ||
1046 | 270 | |||
1047 | 271 | MenuItemMatcher& MenuItemMatcher::type(Type type) | ||
1048 | 272 | { | ||
1049 | 273 | p->m_type = type; | ||
1050 | 274 | return *this; | ||
1051 | 275 | } | ||
1052 | 276 | |||
1053 | 277 | MenuItemMatcher& MenuItemMatcher::label(const string& label) | ||
1054 | 278 | { | ||
1055 | 279 | p->m_label = make_shared<string>(label); | ||
1056 | 280 | return *this; | ||
1057 | 281 | } | ||
1058 | 282 | |||
1059 | 283 | MenuItemMatcher& MenuItemMatcher::action(const string& action) | ||
1060 | 284 | { | ||
1061 | 285 | p->m_action = make_shared<string>(action); | ||
1062 | 286 | return *this; | ||
1063 | 287 | } | ||
1064 | 288 | |||
1065 | 289 | MenuItemMatcher& MenuItemMatcher::state_icons(const std::vector<std::string>& state_icons) | ||
1066 | 290 | { | ||
1067 | 291 | p->m_state_icons = state_icons; | ||
1068 | 292 | return *this; | ||
1069 | 293 | } | ||
1070 | 294 | |||
1071 | 295 | MenuItemMatcher& MenuItemMatcher::icon(const string& icon) | ||
1072 | 296 | { | ||
1073 | 297 | p->m_icon = make_shared<string>(icon); | ||
1074 | 298 | return *this; | ||
1075 | 299 | } | ||
1076 | 300 | |||
1077 | 301 | MenuItemMatcher& MenuItemMatcher::themed_icon(const std::string& iconName, const std::vector<std::string>& icons) | ||
1078 | 302 | { | ||
1079 | 303 | p->m_themed_icons[iconName] = icons; | ||
1080 | 304 | return *this; | ||
1081 | 305 | } | ||
1082 | 306 | |||
1083 | 307 | MenuItemMatcher& MenuItemMatcher::widget(const string& widget) | ||
1084 | 308 | { | ||
1085 | 309 | return string_attribute("x-canonical-type", widget); | ||
1086 | 310 | } | ||
1087 | 311 | |||
1088 | 312 | MenuItemMatcher& MenuItemMatcher::pass_through_attribute(const string& actionName, const shared_ptr<GVariant>& value) | ||
1089 | 313 | { | ||
1090 | 314 | p->m_pass_through_attributes.emplace_back(actionName, value); | ||
1091 | 315 | return *this; | ||
1092 | 316 | } | ||
1093 | 317 | |||
1094 | 318 | MenuItemMatcher& MenuItemMatcher::pass_through_boolean_attribute(const string& actionName, bool value) | ||
1095 | 319 | { | ||
1096 | 320 | return pass_through_attribute( | ||
1097 | 321 | actionName, | ||
1098 | 322 | shared_ptr<GVariant>(g_variant_new_boolean(value), | ||
1099 | 323 | &gvariant_deleter)); | ||
1100 | 324 | } | ||
1101 | 325 | |||
1102 | 326 | MenuItemMatcher& MenuItemMatcher::pass_through_string_attribute(const string& actionName, const string& value) | ||
1103 | 327 | { | ||
1104 | 328 | return pass_through_attribute( | ||
1105 | 329 | actionName, | ||
1106 | 330 | shared_ptr<GVariant>(g_variant_new_string(value.c_str()), | ||
1107 | 331 | &gvariant_deleter)); | ||
1108 | 332 | } | ||
1109 | 333 | |||
1110 | 334 | MenuItemMatcher& MenuItemMatcher::pass_through_double_attribute(const std::string& actionName, double value) | ||
1111 | 335 | { | ||
1112 | 336 | return pass_through_attribute( | ||
1113 | 337 | actionName, | ||
1114 | 338 | shared_ptr<GVariant>(g_variant_new_double(value), | ||
1115 | 339 | &gvariant_deleter)); | ||
1116 | 340 | } | ||
1117 | 341 | |||
1118 | 342 | MenuItemMatcher& MenuItemMatcher::round_doubles(double maxDifference) | ||
1119 | 343 | { | ||
1120 | 344 | p->m_maxDifference = maxDifference; | ||
1121 | 345 | return *this; | ||
1122 | 346 | } | ||
1123 | 347 | |||
1124 | 348 | MenuItemMatcher& MenuItemMatcher::attribute(const string& name, const shared_ptr<GVariant>& value) | ||
1125 | 349 | { | ||
1126 | 350 | p->m_attributes.emplace_back(name, value); | ||
1127 | 351 | return *this; | ||
1128 | 352 | } | ||
1129 | 353 | |||
1130 | 354 | MenuItemMatcher& MenuItemMatcher::boolean_attribute(const string& name, bool value) | ||
1131 | 355 | { | ||
1132 | 356 | return attribute( | ||
1133 | 357 | name, | ||
1134 | 358 | shared_ptr<GVariant>(g_variant_new_boolean(value), | ||
1135 | 359 | &gvariant_deleter)); | ||
1136 | 360 | } | ||
1137 | 361 | |||
1138 | 362 | MenuItemMatcher& MenuItemMatcher::string_attribute(const string& name, const string& value) | ||
1139 | 363 | { | ||
1140 | 364 | return attribute( | ||
1141 | 365 | name, | ||
1142 | 366 | shared_ptr<GVariant>(g_variant_new_string(value.c_str()), | ||
1143 | 367 | &gvariant_deleter)); | ||
1144 | 368 | } | ||
1145 | 369 | |||
1146 | 370 | MenuItemMatcher& MenuItemMatcher::int32_attribute(const std::string& name, int value) | ||
1147 | 371 | { | ||
1148 | 372 | return attribute( | ||
1149 | 373 | name, | ||
1150 | 374 | shared_ptr<GVariant>(g_variant_new_int32 (value), | ||
1151 | 375 | &gvariant_deleter)); | ||
1152 | 376 | } | ||
1153 | 377 | |||
1154 | 378 | MenuItemMatcher& MenuItemMatcher::int64_attribute(const std::string& name, int value) | ||
1155 | 379 | { | ||
1156 | 380 | return attribute( | ||
1157 | 381 | name, | ||
1158 | 382 | shared_ptr<GVariant>(g_variant_new_int64 (value), | ||
1159 | 383 | &gvariant_deleter)); | ||
1160 | 384 | } | ||
1161 | 385 | |||
1162 | 386 | MenuItemMatcher& MenuItemMatcher::double_attribute(const std::string& name, double value) | ||
1163 | 387 | { | ||
1164 | 388 | return attribute( | ||
1165 | 389 | name, | ||
1166 | 390 | shared_ptr<GVariant>(g_variant_new_double (value), | ||
1167 | 391 | &gvariant_deleter)); | ||
1168 | 392 | } | ||
1169 | 393 | |||
1170 | 394 | MenuItemMatcher& MenuItemMatcher::attribute_not_set(const std::string& name) | ||
1171 | 395 | { | ||
1172 | 396 | p->m_not_exist_attributes.emplace_back (name); | ||
1173 | 397 | return *this; | ||
1174 | 398 | } | ||
1175 | 399 | |||
1176 | 400 | MenuItemMatcher& MenuItemMatcher::toggled(bool isToggled) | ||
1177 | 401 | { | ||
1178 | 402 | p->m_isToggled = make_shared<bool>(isToggled); | ||
1179 | 403 | return *this; | ||
1180 | 404 | } | ||
1181 | 405 | |||
1182 | 406 | MenuItemMatcher& MenuItemMatcher::submenu() | ||
1183 | 407 | { | ||
1184 | 408 | p->m_linkType = LinkType::submenu; | ||
1185 | 409 | return *this; | ||
1186 | 410 | } | ||
1187 | 411 | |||
1188 | 412 | MenuItemMatcher& MenuItemMatcher::section() | ||
1189 | 413 | { | ||
1190 | 414 | p->m_linkType = LinkType::section; | ||
1191 | 415 | return *this; | ||
1192 | 416 | } | ||
1193 | 417 | |||
1194 | 418 | MenuItemMatcher& MenuItemMatcher::is_empty() | ||
1195 | 419 | { | ||
1196 | 420 | return has_exactly(0); | ||
1197 | 421 | } | ||
1198 | 422 | |||
1199 | 423 | MenuItemMatcher& MenuItemMatcher::has_exactly(size_t children) | ||
1200 | 424 | { | ||
1201 | 425 | p->m_expectedSize = make_shared<size_t>(children); | ||
1202 | 426 | return *this; | ||
1203 | 427 | } | ||
1204 | 428 | |||
1205 | 429 | MenuItemMatcher& MenuItemMatcher::item(const MenuItemMatcher& item) | ||
1206 | 430 | { | ||
1207 | 431 | p->m_items.emplace_back(item); | ||
1208 | 432 | return *this; | ||
1209 | 433 | } | ||
1210 | 434 | |||
1211 | 435 | MenuItemMatcher& MenuItemMatcher::item(MenuItemMatcher&& item) | ||
1212 | 436 | { | ||
1213 | 437 | p->m_items.emplace_back(item); | ||
1214 | 438 | return *this; | ||
1215 | 439 | } | ||
1216 | 440 | |||
1217 | 441 | MenuItemMatcher& MenuItemMatcher::pass_through_activate(std::string const& action, const shared_ptr<GVariant>& parameter) | ||
1218 | 442 | { | ||
1219 | 443 | p->m_activations.emplace_back(action, parameter); | ||
1220 | 444 | return *this; | ||
1221 | 445 | } | ||
1222 | 446 | |||
1223 | 447 | MenuItemMatcher& MenuItemMatcher::activate(const shared_ptr<GVariant>& parameter) | ||
1224 | 448 | { | ||
1225 | 449 | p->m_activations.emplace_back(string(), parameter); | ||
1226 | 450 | return *this; | ||
1227 | 451 | } | ||
1228 | 452 | |||
1229 | 453 | MenuItemMatcher& MenuItemMatcher::set_pass_through_action_state(const std::string& action, const std::shared_ptr<GVariant>& state) | ||
1230 | 454 | { | ||
1231 | 455 | p->m_setActionStates.emplace_back(action, state); | ||
1232 | 456 | return *this; | ||
1233 | 457 | } | ||
1234 | 458 | |||
1235 | 459 | MenuItemMatcher& MenuItemMatcher::set_action_state(const std::shared_ptr<GVariant>& state) | ||
1236 | 460 | { | ||
1237 | 461 | p->m_setActionStates.emplace_back("", state); | ||
1238 | 462 | return *this; | ||
1239 | 463 | } | ||
1240 | 464 | |||
1241 | 465 | MenuItemMatcher& MenuItemMatcher::mode(Mode mode) | ||
1242 | 466 | { | ||
1243 | 467 | p->m_mode = mode; | ||
1244 | 468 | return *this; | ||
1245 | 469 | } | ||
1246 | 470 | |||
1247 | 471 | void MenuItemMatcher::match( | ||
1248 | 472 | MatchResult& matchResult, | ||
1249 | 473 | const vector<unsigned int>& parentLocation, | ||
1250 | 474 | const shared_ptr<GMenuModel>& menu, | ||
1251 | 475 | map<string, shared_ptr<GActionGroup>>& actions, | ||
1252 | 476 | unsigned int index) const | ||
1253 | 477 | { | ||
1254 | 478 | shared_ptr<GMenuItem> menuItem(g_menu_item_new_from_model(menu.get(), index), &g_object_deleter); | ||
1255 | 479 | |||
1256 | 480 | vector<unsigned int> location(parentLocation); | ||
1257 | 481 | location.emplace_back(index); | ||
1258 | 482 | |||
1259 | 483 | string action = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_ACTION); | ||
1260 | 484 | |||
1261 | 485 | bool isCheckbox = false; | ||
1262 | 486 | bool isRadio = false; | ||
1263 | 487 | bool isToggled = false; | ||
1264 | 488 | |||
1265 | 489 | pair<string, string> idPair; | ||
1266 | 490 | shared_ptr<GActionGroup> actionGroup; | ||
1267 | 491 | shared_ptr<GVariant> state; | ||
1268 | 492 | |||
1269 | 493 | if (!action.empty()) | ||
1270 | 494 | { | ||
1271 | 495 | idPair = split_action(action); | ||
1272 | 496 | actionGroup = actions[idPair.first]; | ||
1273 | 497 | state = shared_ptr<GVariant>(g_action_group_get_action_state(actionGroup.get(), | ||
1274 | 498 | idPair.second.c_str()), | ||
1275 | 499 | &gvariant_deleter); | ||
1276 | 500 | auto attributeTarget = get_attribute(menuItem, G_MENU_ATTRIBUTE_TARGET); | ||
1277 | 501 | |||
1278 | 502 | if (attributeTarget && state) | ||
1279 | 503 | { | ||
1280 | 504 | isToggled = g_variant_equal(state.get(), attributeTarget.get()); | ||
1281 | 505 | isRadio = true; | ||
1282 | 506 | } | ||
1283 | 507 | else if (state | ||
1284 | 508 | && g_variant_is_of_type(state.get(), G_VARIANT_TYPE_BOOLEAN)) | ||
1285 | 509 | { | ||
1286 | 510 | isToggled = g_variant_get_boolean(state.get()); | ||
1287 | 511 | isCheckbox = true; | ||
1288 | 512 | } | ||
1289 | 513 | } | ||
1290 | 514 | |||
1291 | 515 | Type actualType = Type::plain; | ||
1292 | 516 | if (isCheckbox) | ||
1293 | 517 | { | ||
1294 | 518 | actualType = Type::checkbox; | ||
1295 | 519 | } | ||
1296 | 520 | else if (isRadio) | ||
1297 | 521 | { | ||
1298 | 522 | actualType = Type::radio; | ||
1299 | 523 | } | ||
1300 | 524 | |||
1301 | 525 | if (actualType != p->m_type) | ||
1302 | 526 | { | ||
1303 | 527 | matchResult.failure( | ||
1304 | 528 | location, | ||
1305 | 529 | "Expected " + type_to_string(p->m_type) + ", found " | ||
1306 | 530 | + type_to_string(actualType)); | ||
1307 | 531 | } | ||
1308 | 532 | |||
1309 | 533 | // check themed icons | ||
1310 | 534 | map<string, vector<string>>::iterator iter; | ||
1311 | 535 | for (iter = p->m_themed_icons.begin(); iter != p->m_themed_icons.end(); ++iter) | ||
1312 | 536 | { | ||
1313 | 537 | auto icon_val = g_menu_item_get_attribute_value(menuItem.get(), (*iter).first.c_str(), nullptr); | ||
1314 | 538 | if (!icon_val) | ||
1315 | 539 | { | ||
1316 | 540 | matchResult.failure( | ||
1317 | 541 | location, | ||
1318 | 542 | "Expected themed icon " + (*iter).first + " was not found"); | ||
1319 | 543 | } | ||
1320 | 544 | |||
1321 | 545 | auto gicon = g_icon_deserialize(icon_val); | ||
1322 | 546 | if (!gicon || !G_IS_THEMED_ICON(gicon)) | ||
1323 | 547 | { | ||
1324 | 548 | matchResult.failure( | ||
1325 | 549 | location, | ||
1326 | 550 | "Expected attribute " + (*iter).first + " is not a themed icon"); | ||
1327 | 551 | } | ||
1328 | 552 | else | ||
1329 | 553 | { | ||
1330 | 554 | auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon)); | ||
1331 | 555 | int nb_icons = 0; | ||
1332 | 556 | while(iconNames[nb_icons]) | ||
1333 | 557 | { | ||
1334 | 558 | ++nb_icons; | ||
1335 | 559 | } | ||
1336 | 560 | |||
1337 | 561 | if (nb_icons != (*iter).second.size()) | ||
1338 | 562 | { | ||
1339 | 563 | matchResult.failure( | ||
1340 | 564 | location, | ||
1341 | 565 | "Expected " + to_string((*iter).second.size()) + | ||
1342 | 566 | " icons for themed icon [" + (*iter).first + | ||
1343 | 567 | "], but " + to_string(nb_icons) + " were found."); | ||
1344 | 568 | } | ||
1345 | 569 | else | ||
1346 | 570 | { | ||
1347 | 571 | // now compare all the icons | ||
1348 | 572 | for (int i = 0; i < nb_icons; ++i) | ||
1349 | 573 | { | ||
1350 | 574 | if ((*iter).second[i] != iconNames[i]) | ||
1351 | 575 | { | ||
1352 | 576 | matchResult.failure( | ||
1353 | 577 | location, | ||
1354 | 578 | "Icon at position " + to_string(i) + | ||
1355 | 579 | " for themed icon [" + (*iter).first + | ||
1356 | 580 | "], mismatchs. Expected: " + iconNames[i] + " but found " + (*iter).second[i]); | ||
1357 | 581 | } | ||
1358 | 582 | } | ||
1359 | 583 | } | ||
1360 | 584 | } | ||
1361 | 585 | g_object_unref(gicon); | ||
1362 | 586 | } | ||
1363 | 587 | |||
1364 | 588 | string label = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_LABEL); | ||
1365 | 589 | if (p->m_label && (*p->m_label) != label) | ||
1366 | 590 | { | ||
1367 | 591 | matchResult.failure( | ||
1368 | 592 | location, | ||
1369 | 593 | "Expected label '" + *p->m_label + "', but found '" + label | ||
1370 | 594 | + "'"); | ||
1371 | 595 | } | ||
1372 | 596 | |||
1373 | 597 | string icon = get_string_attribute(menuItem, G_MENU_ATTRIBUTE_ICON); | ||
1374 | 598 | if (p->m_icon && (*p->m_icon) != icon) | ||
1375 | 599 | { | ||
1376 | 600 | matchResult.failure( | ||
1377 | 601 | location, | ||
1378 | 602 | "Expected icon '" + *p->m_icon + "', but found '" + icon + "'"); | ||
1379 | 603 | } | ||
1380 | 604 | |||
1381 | 605 | if (p->m_action && (*p->m_action) != action) | ||
1382 | 606 | { | ||
1383 | 607 | matchResult.failure( | ||
1384 | 608 | location, | ||
1385 | 609 | "Expected action '" + *p->m_action + "', but found '" + action | ||
1386 | 610 | + "'"); | ||
1387 | 611 | } | ||
1388 | 612 | |||
1389 | 613 | if (!p->m_state_icons.empty() && !state) | ||
1390 | 614 | { | ||
1391 | 615 | matchResult.failure( | ||
1392 | 616 | location, | ||
1393 | 617 | "Expected state icons but no state was found"); | ||
1394 | 618 | } | ||
1395 | 619 | else if (!p->m_state_icons.empty() && state && | ||
1396 | 620 | !g_variant_is_of_type(state.get(), G_VARIANT_TYPE_VARDICT)) | ||
1397 | 621 | { | ||
1398 | 622 | matchResult.failure( | ||
1399 | 623 | location, | ||
1400 | 624 | "Expected state icons vardict, found " | ||
1401 | 625 | + type_to_string(actualType)); | ||
1402 | 626 | } | ||
1403 | 627 | else if (!p->m_state_icons.empty() && state && | ||
1404 | 628 | g_variant_is_of_type(state.get(), G_VARIANT_TYPE_VARDICT)) | ||
1405 | 629 | { | ||
1406 | 630 | std::vector<std::string> actual_state_icons; | ||
1407 | 631 | GVariantIter it; | ||
1408 | 632 | gchar* key; | ||
1409 | 633 | GVariant* value; | ||
1410 | 634 | |||
1411 | 635 | g_variant_iter_init(&it, state.get()); | ||
1412 | 636 | while (g_variant_iter_loop(&it, "{sv}", &key, &value)) | ||
1413 | 637 | { | ||
1414 | 638 | if (std::string(key) == "icon") { | ||
1415 | 639 | auto gicon = g_icon_deserialize(value); | ||
1416 | 640 | if (G_IS_THEMED_ICON(gicon)) | ||
1417 | 641 | { | ||
1418 | 642 | auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon)); | ||
1419 | 643 | // Just take the first icon in the list (there is only ever one) | ||
1420 | 644 | actual_state_icons.push_back(iconNames[0]); | ||
1421 | 645 | } | ||
1422 | 646 | g_object_unref(gicon); | ||
1423 | 647 | } | ||
1424 | 648 | else if (std::string(key) == "icons" && g_variant_is_of_type(value, G_VARIANT_TYPE("av"))) | ||
1425 | 649 | { | ||
1426 | 650 | // If we find "icons" in the map, clear any icons we may have found in "icon", | ||
1427 | 651 | // then break from the loop as we have found all icons now. | ||
1428 | 652 | actual_state_icons.clear(); | ||
1429 | 653 | GVariantIter icon_it; | ||
1430 | 654 | GVariant* icon_value; | ||
1431 | 655 | |||
1432 | 656 | g_variant_iter_init(&icon_it, value); | ||
1433 | 657 | while (g_variant_iter_loop(&icon_it, "v", &icon_value)) | ||
1434 | 658 | { | ||
1435 | 659 | auto gicon = g_icon_deserialize(icon_value); | ||
1436 | 660 | if (G_IS_THEMED_ICON(gicon)) | ||
1437 | 661 | { | ||
1438 | 662 | auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(gicon)); | ||
1439 | 663 | // Just take the first icon in the list (there is only ever one) | ||
1440 | 664 | actual_state_icons.push_back(iconNames[0]); | ||
1441 | 665 | } | ||
1442 | 666 | g_object_unref(gicon); | ||
1443 | 667 | } | ||
1444 | 668 | // We're breaking out of g_variant_iter_loop here so clean up | ||
1445 | 669 | g_variant_unref(value); | ||
1446 | 670 | g_free(key); | ||
1447 | 671 | break; | ||
1448 | 672 | } | ||
1449 | 673 | } | ||
1450 | 674 | |||
1451 | 675 | if (p->m_state_icons != actual_state_icons) | ||
1452 | 676 | { | ||
1453 | 677 | std::string expected_icons; | ||
1454 | 678 | for (unsigned int i = 0; i < p->m_state_icons.size(); ++i) | ||
1455 | 679 | { | ||
1456 | 680 | expected_icons += i == 0 ? p->m_state_icons[i] : ", " + p->m_state_icons[i]; | ||
1457 | 681 | } | ||
1458 | 682 | std::string actual_icons; | ||
1459 | 683 | for (unsigned int i = 0; i < actual_state_icons.size(); ++i) | ||
1460 | 684 | { | ||
1461 | 685 | actual_icons += i == 0 ? actual_state_icons[i] : ", " + actual_state_icons[i]; | ||
1462 | 686 | } | ||
1463 | 687 | matchResult.failure( | ||
1464 | 688 | location, | ||
1465 | 689 | "Expected state_icons == {" + expected_icons | ||
1466 | 690 | + "} but found {" + actual_icons + "}"); | ||
1467 | 691 | } | ||
1468 | 692 | } | ||
1469 | 693 | |||
1470 | 694 | for (const auto& e: p->m_pass_through_attributes) | ||
1471 | 695 | { | ||
1472 | 696 | string actionName = get_string_attribute(menuItem, e.first.c_str()); | ||
1473 | 697 | if (actionName.empty()) | ||
1474 | 698 | { | ||
1475 | 699 | matchResult.failure( | ||
1476 | 700 | location, | ||
1477 | 701 | "Could not find action name '" + e.first + "'"); | ||
1478 | 702 | } | ||
1479 | 703 | else | ||
1480 | 704 | { | ||
1481 | 705 | auto passThroughIdPair = split_action(actionName); | ||
1482 | 706 | auto actionGroup = actions[passThroughIdPair.first]; | ||
1483 | 707 | if (actionGroup) | ||
1484 | 708 | { | ||
1485 | 709 | auto value = get_action_group_attribute( | ||
1486 | 710 | actionGroup, passThroughIdPair.second.c_str()); | ||
1487 | 711 | if (!value) | ||
1488 | 712 | { | ||
1489 | 713 | matchResult.failure( | ||
1490 | 714 | location, | ||
1491 | 715 | "Expected pass-through attribute '" + e.first | ||
1492 | 716 | + "' was not present"); | ||
1493 | 717 | } | ||
1494 | 718 | else if (!g_variant_is_of_type(e.second.get(), g_variant_get_type(value.get()))) | ||
1495 | 719 | { | ||
1496 | 720 | std::string expectedType = g_variant_get_type_string(e.second.get()); | ||
1497 | 721 | std::string actualType = g_variant_get_type_string(value.get()); | ||
1498 | 722 | matchResult.failure( | ||
1499 | 723 | location, | ||
1500 | 724 | "Expected pass-through attribute type '" + expectedType | ||
1501 | 725 | + "' but found '" + actualType + "'"); | ||
1502 | 726 | } | ||
1503 | 727 | else if (g_variant_compare(e.second.get(), value.get())) | ||
1504 | 728 | { | ||
1505 | 729 | bool reportMismatch = true; | ||
1506 | 730 | if (g_strcmp0(g_variant_get_type_string(value.get()),"d") == 0 && p->m_maxDifference != 0.0) | ||
1507 | 731 | { | ||
1508 | 732 | auto actualDouble = g_variant_get_double(value.get()); | ||
1509 | 733 | auto expectedDouble = g_variant_get_double(e.second.get()); | ||
1510 | 734 | auto difference = actualDouble-expectedDouble; | ||
1511 | 735 | if (difference < 0) difference = difference * -1.0; | ||
1512 | 736 | if (difference <= p->m_maxDifference) | ||
1513 | 737 | { | ||
1514 | 738 | reportMismatch = false; | ||
1515 | 739 | } | ||
1516 | 740 | } | ||
1517 | 741 | if (reportMismatch) | ||
1518 | 742 | { | ||
1519 | 743 | gchar* expectedString = g_variant_print(e.second.get(), true); | ||
1520 | 744 | gchar* actualString = g_variant_print(value.get(), true); | ||
1521 | 745 | matchResult.failure( | ||
1522 | 746 | location, | ||
1523 | 747 | "Expected pass-through attribute '" + e.first | ||
1524 | 748 | + "' == " + expectedString + " but found " | ||
1525 | 749 | + actualString); | ||
1526 | 750 | |||
1527 | 751 | g_free(expectedString); | ||
1528 | 752 | g_free(actualString); | ||
1529 | 753 | } | ||
1530 | 754 | } | ||
1531 | 755 | } | ||
1532 | 756 | else | ||
1533 | 757 | { | ||
1534 | 758 | matchResult.failure(location, "Could not find action group for ID '" + passThroughIdPair.first + "'"); | ||
1535 | 759 | } | ||
1536 | 760 | } | ||
1537 | 761 | } | ||
1538 | 762 | |||
1539 | 763 | for (const auto& e: p->m_attributes) | ||
1540 | 764 | { | ||
1541 | 765 | auto value = get_attribute(menuItem, e.first.c_str()); | ||
1542 | 766 | if (!value) | ||
1543 | 767 | { | ||
1544 | 768 | matchResult.failure(location, | ||
1545 | 769 | "Expected attribute '" + e.first | ||
1546 | 770 | + "' could not be found"); | ||
1547 | 771 | } | ||
1548 | 772 | else if (!g_variant_is_of_type(e.second.get(), g_variant_get_type(value.get()))) | ||
1549 | 773 | { | ||
1550 | 774 | std::string expectedType = g_variant_get_type_string(e.second.get()); | ||
1551 | 775 | std::string actualType = g_variant_get_type_string(value.get()); | ||
1552 | 776 | matchResult.failure( | ||
1553 | 777 | location, | ||
1554 | 778 | "Expected attribute type '" + expectedType | ||
1555 | 779 | + "' but found '" + actualType + "'"); | ||
1556 | 780 | } | ||
1557 | 781 | else if (g_variant_compare(e.second.get(), value.get())) | ||
1558 | 782 | { | ||
1559 | 783 | gchar* expectedString = g_variant_print(e.second.get(), true); | ||
1560 | 784 | gchar* actualString = g_variant_print(value.get(), true); | ||
1561 | 785 | matchResult.failure( | ||
1562 | 786 | location, | ||
1563 | 787 | "Expected attribute '" + e.first + "' == " + expectedString | ||
1564 | 788 | + ", but found " + actualString); | ||
1565 | 789 | g_free(expectedString); | ||
1566 | 790 | g_free(actualString); | ||
1567 | 791 | } | ||
1568 | 792 | } | ||
1569 | 793 | |||
1570 | 794 | for (const auto& e: p->m_not_exist_attributes) | ||
1571 | 795 | { | ||
1572 | 796 | auto value = get_attribute(menuItem, e.c_str()); | ||
1573 | 797 | if (value) | ||
1574 | 798 | { | ||
1575 | 799 | matchResult.failure(location, | ||
1576 | 800 | "Not expected attribute '" + e | ||
1577 | 801 | + "' was found"); | ||
1578 | 802 | } | ||
1579 | 803 | } | ||
1580 | 804 | |||
1581 | 805 | if (p->m_isToggled && (*p->m_isToggled) != isToggled) | ||
1582 | 806 | { | ||
1583 | 807 | matchResult.failure( | ||
1584 | 808 | location, | ||
1585 | 809 | "Expected toggled = " + bool_to_string(*p->m_isToggled) | ||
1586 | 810 | + ", but found " + bool_to_string(isToggled)); | ||
1587 | 811 | } | ||
1588 | 812 | |||
1589 | 813 | if (!matchResult.success()) | ||
1590 | 814 | { | ||
1591 | 815 | return; | ||
1592 | 816 | } | ||
1593 | 817 | |||
1594 | 818 | if (!p->m_items.empty() || p->m_expectedSize) | ||
1595 | 819 | { | ||
1596 | 820 | shared_ptr<GMenuModel> link; | ||
1597 | 821 | |||
1598 | 822 | switch (p->m_linkType) | ||
1599 | 823 | { | ||
1600 | 824 | case LinkType::any: | ||
1601 | 825 | { | ||
1602 | 826 | link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SUBMENU), &g_object_deleter); | ||
1603 | 827 | if (!link) | ||
1604 | 828 | { | ||
1605 | 829 | link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SECTION), &g_object_deleter); | ||
1606 | 830 | } | ||
1607 | 831 | break; | ||
1608 | 832 | } | ||
1609 | 833 | case LinkType::submenu: | ||
1610 | 834 | { | ||
1611 | 835 | link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SUBMENU), &g_object_deleter); | ||
1612 | 836 | break; | ||
1613 | 837 | } | ||
1614 | 838 | case LinkType::section: | ||
1615 | 839 | { | ||
1616 | 840 | link.reset(g_menu_model_get_item_link(menu.get(), (int) index, G_MENU_LINK_SECTION), &g_object_deleter); | ||
1617 | 841 | break; | ||
1618 | 842 | } | ||
1619 | 843 | } | ||
1620 | 844 | |||
1621 | 845 | |||
1622 | 846 | if (!link) | ||
1623 | 847 | { | ||
1624 | 848 | if (p->m_expectedSize) | ||
1625 | 849 | { | ||
1626 | 850 | matchResult.failure( | ||
1627 | 851 | location, | ||
1628 | 852 | "Expected " + to_string(*p->m_expectedSize) | ||
1629 | 853 | + " children, but found none"); | ||
1630 | 854 | } | ||
1631 | 855 | else | ||
1632 | 856 | { | ||
1633 | 857 | matchResult.failure( | ||
1634 | 858 | location, | ||
1635 | 859 | "Expected " + to_string(p->m_items.size()) | ||
1636 | 860 | + " children, but found none"); | ||
1637 | 861 | } | ||
1638 | 862 | return; | ||
1639 | 863 | } | ||
1640 | 864 | else | ||
1641 | 865 | { | ||
1642 | 866 | while (true) | ||
1643 | 867 | { | ||
1644 | 868 | MatchResult childMatchResult(matchResult.createChild()); | ||
1645 | 869 | |||
1646 | 870 | if (p->m_expectedSize | ||
1647 | 871 | && *p->m_expectedSize | ||
1648 | 872 | != (unsigned int) g_menu_model_get_n_items( | ||
1649 | 873 | link.get())) | ||
1650 | 874 | { | ||
1651 | 875 | childMatchResult.failure( | ||
1652 | 876 | location, | ||
1653 | 877 | "Expected " + to_string(*p->m_expectedSize) | ||
1654 | 878 | + " child items, but found " | ||
1655 | 879 | + to_string( | ||
1656 | 880 | g_menu_model_get_n_items( | ||
1657 | 881 | link.get()))); | ||
1658 | 882 | } | ||
1659 | 883 | else if (!p->m_items.empty()) | ||
1660 | 884 | { | ||
1661 | 885 | switch (p->m_mode) | ||
1662 | 886 | { | ||
1663 | 887 | case Mode::all: | ||
1664 | 888 | p->all(childMatchResult, location, link, actions); | ||
1665 | 889 | break; | ||
1666 | 890 | case Mode::starts_with: | ||
1667 | 891 | p->startsWith(childMatchResult, location, link, actions); | ||
1668 | 892 | break; | ||
1669 | 893 | case Mode::ends_with: | ||
1670 | 894 | p->endsWith(childMatchResult, location, link, actions); | ||
1671 | 895 | break; | ||
1672 | 896 | } | ||
1673 | 897 | } | ||
1674 | 898 | |||
1675 | 899 | if (childMatchResult.success()) | ||
1676 | 900 | { | ||
1677 | 901 | matchResult.merge(childMatchResult); | ||
1678 | 902 | break; | ||
1679 | 903 | } | ||
1680 | 904 | else | ||
1681 | 905 | { | ||
1682 | 906 | if (matchResult.hasTimedOut()) | ||
1683 | 907 | { | ||
1684 | 908 | matchResult.merge(childMatchResult); | ||
1685 | 909 | break; | ||
1686 | 910 | } | ||
1687 | 911 | menuWaitForItems(link); | ||
1688 | 912 | } | ||
1689 | 913 | } | ||
1690 | 914 | } | ||
1691 | 915 | } | ||
1692 | 916 | |||
1693 | 917 | |||
1694 | 918 | for (const auto& a: p->m_setActionStates) | ||
1695 | 919 | { | ||
1696 | 920 | auto stateAction = action; | ||
1697 | 921 | auto stateIdPair = idPair; | ||
1698 | 922 | auto stateActionGroup = actionGroup; | ||
1699 | 923 | if (!a.first.empty()) | ||
1700 | 924 | { | ||
1701 | 925 | stateAction = get_string_attribute(menuItem, a.first.c_str());; | ||
1702 | 926 | stateIdPair = split_action(stateAction); | ||
1703 | 927 | stateActionGroup = actions[stateIdPair.first]; | ||
1704 | 928 | } | ||
1705 | 929 | |||
1706 | 930 | if (stateAction.empty()) | ||
1707 | 931 | { | ||
1708 | 932 | matchResult.failure( | ||
1709 | 933 | location, | ||
1710 | 934 | "Tried to set action state, but no action was found"); | ||
1711 | 935 | } | ||
1712 | 936 | else if(!stateActionGroup) | ||
1713 | 937 | { | ||
1714 | 938 | matchResult.failure( | ||
1715 | 939 | location, | ||
1716 | 940 | "Tried to set action state for action group '" + stateIdPair.first | ||
1717 | 941 | + "', but action group wasn't found"); | ||
1718 | 942 | } | ||
1719 | 943 | else if (!g_action_group_has_action(stateActionGroup.get(), stateIdPair.second.c_str())) | ||
1720 | 944 | { | ||
1721 | 945 | matchResult.failure( | ||
1722 | 946 | location, | ||
1723 | 947 | "Tried to set action state for action '" + stateAction | ||
1724 | 948 | + "', but action was not found"); | ||
1725 | 949 | } | ||
1726 | 950 | else | ||
1727 | 951 | { | ||
1728 | 952 | g_action_group_change_action_state(stateActionGroup.get(), stateIdPair.second.c_str(), | ||
1729 | 953 | g_variant_ref(a.second.get())); | ||
1730 | 954 | } | ||
1731 | 955 | |||
1732 | 956 | // FIXME this is a dodgy way to ensure the action state change gets dispatched | ||
1733 | 957 | menuWaitForItems(menu, 100); | ||
1734 | 958 | } | ||
1735 | 959 | |||
1736 | 960 | for (const auto& a: p->m_activations) | ||
1737 | 961 | { | ||
1738 | 962 | string tmpAction = action; | ||
1739 | 963 | auto tmpIdPair = idPair; | ||
1740 | 964 | auto tmpActionGroup = actionGroup; | ||
1741 | 965 | if (!a.first.empty()) | ||
1742 | 966 | { | ||
1743 | 967 | tmpAction = get_string_attribute(menuItem, a.first.c_str()); | ||
1744 | 968 | tmpIdPair = split_action(tmpAction); | ||
1745 | 969 | tmpActionGroup = actions[tmpIdPair.first]; | ||
1746 | 970 | } | ||
1747 | 971 | |||
1748 | 972 | if (tmpAction.empty()) | ||
1749 | 973 | { | ||
1750 | 974 | matchResult.failure( | ||
1751 | 975 | location, | ||
1752 | 976 | "Tried to activate action, but no action was found"); | ||
1753 | 977 | } | ||
1754 | 978 | else if(!tmpActionGroup) | ||
1755 | 979 | { | ||
1756 | 980 | matchResult.failure( | ||
1757 | 981 | location, | ||
1758 | 982 | "Tried to activate action group '" + tmpIdPair.first | ||
1759 | 983 | + "', but action group wasn't found"); | ||
1760 | 984 | } | ||
1761 | 985 | else if (!g_action_group_has_action(tmpActionGroup.get(), tmpIdPair.second.c_str())) | ||
1762 | 986 | { | ||
1763 | 987 | matchResult.failure( | ||
1764 | 988 | location, | ||
1765 | 989 | "Tried to activate action '" + tmpAction + "', but action was not found"); | ||
1766 | 990 | } | ||
1767 | 991 | else | ||
1768 | 992 | { | ||
1769 | 993 | if (a.second) | ||
1770 | 994 | { | ||
1771 | 995 | g_action_group_activate_action(tmpActionGroup.get(), tmpIdPair.second.c_str(), | ||
1772 | 996 | g_variant_ref(a.second.get())); | ||
1773 | 997 | } | ||
1774 | 998 | else | ||
1775 | 999 | { | ||
1776 | 1000 | g_action_group_activate_action(tmpActionGroup.get(), tmpIdPair.second.c_str(), nullptr); | ||
1777 | 1001 | } | ||
1778 | 1002 | |||
1779 | 1003 | // FIXME this is a dodgy way to ensure the activation gets dispatched | ||
1780 | 1004 | menuWaitForItems(menu, 100); | ||
1781 | 1005 | } | ||
1782 | 1006 | } | ||
1783 | 1007 | } | ||
1784 | 1008 | |||
1785 | 1009 | } // namepsace gmenuharness | ||
1786 | 1010 | |||
1787 | 1011 | } // namespace unity | ||
1788 | 0 | 1012 | ||
1789 | === added file 'src/gmenuharness/MenuMatcher.cpp' | |||
1790 | --- src/gmenuharness/MenuMatcher.cpp 1970-01-01 00:00:00 +0000 | |||
1791 | +++ src/gmenuharness/MenuMatcher.cpp 2015-10-28 09:41:22 +0000 | |||
1792 | @@ -0,0 +1,208 @@ | |||
1793 | 1 | /* | ||
1794 | 2 | * Copyright © 2014 Canonical Ltd. | ||
1795 | 3 | * | ||
1796 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1797 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
1798 | 6 | * as published by the Free Software Foundation. | ||
1799 | 7 | * | ||
1800 | 8 | * This program is distributed in the hope that it will be useful, | ||
1801 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1802 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1803 | 11 | * GNU Lesser General Public License for more details. | ||
1804 | 12 | * | ||
1805 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
1806 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1807 | 15 | * | ||
1808 | 16 | * Authored by: Pete Woods <pete.woods@canonical.com> | ||
1809 | 17 | */ | ||
1810 | 18 | |||
1811 | 19 | #include <unity/gmenuharness/MenuMatcher.h> | ||
1812 | 20 | #include <unity/gmenuharness/MatchUtils.h> | ||
1813 | 21 | |||
1814 | 22 | #include <iostream> | ||
1815 | 23 | |||
1816 | 24 | #include <gio/gio.h> | ||
1817 | 25 | |||
1818 | 26 | using namespace std; | ||
1819 | 27 | |||
1820 | 28 | namespace unity | ||
1821 | 29 | { | ||
1822 | 30 | |||
1823 | 31 | namespace gmenuharness | ||
1824 | 32 | { | ||
1825 | 33 | |||
1826 | 34 | namespace | ||
1827 | 35 | { | ||
1828 | 36 | |||
1829 | 37 | static void gdbus_connection_deleter(GDBusConnection* connection) | ||
1830 | 38 | { | ||
1831 | 39 | // if (!g_dbus_connection_is_closed(connection)) | ||
1832 | 40 | // { | ||
1833 | 41 | // g_dbus_connection_close_sync(connection, nullptr, nullptr); | ||
1834 | 42 | // } | ||
1835 | 43 | g_clear_object(&connection); | ||
1836 | 44 | } | ||
1837 | 45 | } | ||
1838 | 46 | |||
1839 | 47 | struct MenuMatcher::Parameters::Priv | ||
1840 | 48 | { | ||
1841 | 49 | string m_busName; | ||
1842 | 50 | |||
1843 | 51 | vector<pair<string, string>> m_actions; | ||
1844 | 52 | |||
1845 | 53 | string m_menuObjectPath; | ||
1846 | 54 | }; | ||
1847 | 55 | |||
1848 | 56 | MenuMatcher::Parameters::Parameters(const string& busName, | ||
1849 | 57 | const vector<pair<string, string>>& actions, | ||
1850 | 58 | const string& menuObjectPath) : | ||
1851 | 59 | p(new Priv) | ||
1852 | 60 | { | ||
1853 | 61 | p->m_busName = busName; | ||
1854 | 62 | p->m_actions = actions; | ||
1855 | 63 | p->m_menuObjectPath = menuObjectPath; | ||
1856 | 64 | } | ||
1857 | 65 | |||
1858 | 66 | MenuMatcher::Parameters::~Parameters() | ||
1859 | 67 | { | ||
1860 | 68 | } | ||
1861 | 69 | |||
1862 | 70 | MenuMatcher::Parameters::Parameters(const Parameters& other) : | ||
1863 | 71 | p(new Priv) | ||
1864 | 72 | { | ||
1865 | 73 | *this = other; | ||
1866 | 74 | } | ||
1867 | 75 | |||
1868 | 76 | MenuMatcher::Parameters::Parameters(Parameters&& other) | ||
1869 | 77 | { | ||
1870 | 78 | *this = move(other); | ||
1871 | 79 | } | ||
1872 | 80 | |||
1873 | 81 | MenuMatcher::Parameters& MenuMatcher::Parameters::operator=(const Parameters& other) | ||
1874 | 82 | { | ||
1875 | 83 | p->m_busName = other.p->m_busName; | ||
1876 | 84 | p->m_actions = other.p->m_actions; | ||
1877 | 85 | p->m_menuObjectPath = other.p->m_menuObjectPath; | ||
1878 | 86 | return *this; | ||
1879 | 87 | } | ||
1880 | 88 | |||
1881 | 89 | MenuMatcher::Parameters& MenuMatcher::Parameters::operator=(Parameters&& other) | ||
1882 | 90 | { | ||
1883 | 91 | p = move(other.p); | ||
1884 | 92 | return *this; | ||
1885 | 93 | } | ||
1886 | 94 | |||
1887 | 95 | struct MenuMatcher::Priv | ||
1888 | 96 | { | ||
1889 | 97 | Priv(const Parameters& parameters) : | ||
1890 | 98 | m_parameters(parameters) | ||
1891 | 99 | { | ||
1892 | 100 | } | ||
1893 | 101 | |||
1894 | 102 | Parameters m_parameters; | ||
1895 | 103 | |||
1896 | 104 | vector<MenuItemMatcher> m_items; | ||
1897 | 105 | |||
1898 | 106 | shared_ptr<GDBusConnection> m_system; | ||
1899 | 107 | |||
1900 | 108 | shared_ptr<GDBusConnection> m_session; | ||
1901 | 109 | |||
1902 | 110 | shared_ptr<GMenuModel> m_menu; | ||
1903 | 111 | |||
1904 | 112 | map<string, shared_ptr<GActionGroup>> m_actions; | ||
1905 | 113 | }; | ||
1906 | 114 | |||
1907 | 115 | MenuMatcher::MenuMatcher(const Parameters& parameters) : | ||
1908 | 116 | p(new Priv(parameters)) | ||
1909 | 117 | { | ||
1910 | 118 | p->m_system.reset(g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr), | ||
1911 | 119 | &gdbus_connection_deleter); | ||
1912 | 120 | g_dbus_connection_set_exit_on_close(p->m_system.get(), false); | ||
1913 | 121 | |||
1914 | 122 | p->m_session.reset(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr), | ||
1915 | 123 | &gdbus_connection_deleter); | ||
1916 | 124 | g_dbus_connection_set_exit_on_close(p->m_session.get(), false); | ||
1917 | 125 | |||
1918 | 126 | p->m_menu.reset( | ||
1919 | 127 | G_MENU_MODEL( | ||
1920 | 128 | g_dbus_menu_model_get( | ||
1921 | 129 | p->m_session.get(), | ||
1922 | 130 | p->m_parameters.p->m_busName.c_str(), | ||
1923 | 131 | p->m_parameters.p->m_menuObjectPath.c_str())), | ||
1924 | 132 | &g_object_deleter); | ||
1925 | 133 | |||
1926 | 134 | for (const auto& action : p->m_parameters.p->m_actions) | ||
1927 | 135 | { | ||
1928 | 136 | shared_ptr<GActionGroup> actionGroup( | ||
1929 | 137 | G_ACTION_GROUP( | ||
1930 | 138 | g_dbus_action_group_get( | ||
1931 | 139 | p->m_session.get(), | ||
1932 | 140 | p->m_parameters.p->m_busName.c_str(), | ||
1933 | 141 | action.second.c_str())), | ||
1934 | 142 | &g_object_deleter); | ||
1935 | 143 | p->m_actions[action.first] = actionGroup; | ||
1936 | 144 | } | ||
1937 | 145 | } | ||
1938 | 146 | |||
1939 | 147 | MenuMatcher::~MenuMatcher() | ||
1940 | 148 | { | ||
1941 | 149 | } | ||
1942 | 150 | |||
1943 | 151 | MenuMatcher& MenuMatcher::item(const MenuItemMatcher& item) | ||
1944 | 152 | { | ||
1945 | 153 | p->m_items.emplace_back(item); | ||
1946 | 154 | return *this; | ||
1947 | 155 | } | ||
1948 | 156 | |||
1949 | 157 | void MenuMatcher::match(MatchResult& matchResult) const | ||
1950 | 158 | { | ||
1951 | 159 | vector<unsigned int> location; | ||
1952 | 160 | |||
1953 | 161 | while (true) | ||
1954 | 162 | { | ||
1955 | 163 | MatchResult childMatchResult(matchResult.createChild()); | ||
1956 | 164 | |||
1957 | 165 | int menuSize = g_menu_model_get_n_items(p->m_menu.get()); | ||
1958 | 166 | if (p->m_items.size() > (unsigned int) menuSize) | ||
1959 | 167 | { | ||
1960 | 168 | childMatchResult.failure( | ||
1961 | 169 | location, | ||
1962 | 170 | "Row count mismatch, expected " + to_string(p->m_items.size()) | ||
1963 | 171 | + " but found " + to_string(menuSize)); | ||
1964 | 172 | } | ||
1965 | 173 | else | ||
1966 | 174 | { | ||
1967 | 175 | for (size_t i = 0; i < p->m_items.size(); ++i) | ||
1968 | 176 | { | ||
1969 | 177 | const auto& matcher = p->m_items.at(i); | ||
1970 | 178 | matcher.match(childMatchResult, location, p->m_menu, p->m_actions, i); | ||
1971 | 179 | } | ||
1972 | 180 | } | ||
1973 | 181 | |||
1974 | 182 | if (childMatchResult.success()) | ||
1975 | 183 | { | ||
1976 | 184 | matchResult.merge(childMatchResult); | ||
1977 | 185 | break; | ||
1978 | 186 | } | ||
1979 | 187 | else | ||
1980 | 188 | { | ||
1981 | 189 | if (matchResult.hasTimedOut()) | ||
1982 | 190 | { | ||
1983 | 191 | matchResult.merge(childMatchResult); | ||
1984 | 192 | break; | ||
1985 | 193 | } | ||
1986 | 194 | menuWaitForItems(p->m_menu); | ||
1987 | 195 | } | ||
1988 | 196 | } | ||
1989 | 197 | } | ||
1990 | 198 | |||
1991 | 199 | MatchResult MenuMatcher::match() const | ||
1992 | 200 | { | ||
1993 | 201 | MatchResult matchResult; | ||
1994 | 202 | match(matchResult); | ||
1995 | 203 | return matchResult; | ||
1996 | 204 | } | ||
1997 | 205 | |||
1998 | 206 | } // namespace gmenuharness | ||
1999 | 207 | |||
2000 | 208 | } // namespace unity | ||
2001 | 0 | 209 | ||
2002 | === modified file 'src/service.vala' | |||
2003 | --- src/service.vala 2015-10-02 14:07:44 +0000 | |||
2004 | +++ src/service.vala 2015-10-28 09:41:22 +0000 | |||
2005 | @@ -43,7 +43,7 @@ | |||
2006 | 43 | warn_notification.closed.connect((n) => { n.clear_actions(); }); | 43 | warn_notification.closed.connect((n) => { n.clear_actions(); }); |
2007 | 44 | BusWatcher.watch_namespace (GLib.BusType.SESSION, | 44 | BusWatcher.watch_namespace (GLib.BusType.SESSION, |
2008 | 45 | "org.freedesktop.Notifications", | 45 | "org.freedesktop.Notifications", |
2010 | 46 | () => { debug("Notifications name appeared"); notify_server_caps_checked = false; }, | 46 | () => { debug("Notifications name appeared"); }, |
2011 | 47 | () => { debug("Notifications name vanshed"); notify_server_caps_checked = false; }); | 47 | () => { debug("Notifications name vanshed"); notify_server_caps_checked = false; }); |
2012 | 48 | 48 | ||
2013 | 49 | this.settings = new Settings ("com.canonical.indicator.sound"); | 49 | this.settings = new Settings ("com.canonical.indicator.sound"); |
2014 | @@ -52,6 +52,8 @@ | |||
2015 | 52 | this.notify["visible"].connect ( () => this.update_root_icon () ); | 52 | this.notify["visible"].connect ( () => this.update_root_icon () ); |
2016 | 53 | 53 | ||
2017 | 54 | this.volume_control = volume; | 54 | this.volume_control = volume; |
2018 | 55 | this.volume_control.active_output_changed.connect (this.update_root_icon); | ||
2019 | 56 | this.volume_control.active_output_changed.connect (this.update_notification); | ||
2020 | 55 | 57 | ||
2021 | 56 | this.accounts_service = accounts; | 58 | this.accounts_service = accounts; |
2022 | 57 | /* If we're on the greeter, don't export */ | 59 | /* If we're on the greeter, don't export */ |
2023 | @@ -90,6 +92,10 @@ | |||
2024 | 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); |
2025 | 91 | }); | 93 | }); |
2026 | 92 | 94 | ||
2027 | 95 | this.menus.@foreach ( (profile, menu) => { | ||
2028 | 96 | this.volume_control.active_output_changed.connect (menu.update_volume_slider); | ||
2029 | 97 | }); | ||
2030 | 98 | |||
2031 | 93 | this.sync_preferred_players (); | 99 | this.sync_preferred_players (); |
2032 | 94 | this.settings.changed["interested-media-players"].connect ( () => { | 100 | this.settings.changed["interested-media-players"].connect ( () => { |
2033 | 95 | this.sync_preferred_players (); | 101 | this.sync_preferred_players (); |
2034 | @@ -245,17 +251,7 @@ | |||
2035 | 245 | 251 | ||
2036 | 246 | void update_root_icon () { | 252 | void update_root_icon () { |
2037 | 247 | double volume = this.volume_control.volume.volume; | 253 | double volume = this.volume_control.volume.volume; |
2049 | 248 | string icon; | 254 | string icon = get_volume_root_icon (volume, this.volume_control.mute, volume_control.active_output); |
2039 | 249 | if (this.volume_control.mute || volume <= 0.0) | ||
2040 | 250 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; | ||
2041 | 251 | else if (this.accounts_service != null && this.accounts_service.silentMode) | ||
2042 | 252 | icon = "audio-volume-muted-panel"; | ||
2043 | 253 | else if (volume <= 0.3) | ||
2044 | 254 | icon = "audio-volume-low-panel"; | ||
2045 | 255 | else if (volume <= 0.7) | ||
2046 | 256 | icon = "audio-volume-medium-panel"; | ||
2047 | 257 | else | ||
2048 | 258 | icon = "audio-volume-high-panel"; | ||
2050 | 259 | 255 | ||
2051 | 260 | string accessible_name; | 256 | string accessible_name; |
2052 | 261 | if (this.volume_control.mute) { | 257 | if (this.volume_control.mute) { |
2053 | @@ -281,6 +277,293 @@ | |||
2054 | 281 | private bool notify_server_supports_actions = false; | 277 | private bool notify_server_supports_actions = false; |
2055 | 282 | private bool notify_server_supports_sync = false; | 278 | private bool notify_server_supports_sync = false; |
2056 | 283 | private bool block_info_notifications = false; | 279 | private bool block_info_notifications = false; |
2057 | 280 | private bool waiting_user_approve_warn = false; | ||
2058 | 281 | |||
2059 | 282 | private string get_volume_icon (double volume, VolumeControl.ActiveOutput active_output) | ||
2060 | 283 | { | ||
2061 | 284 | string icon = ""; | ||
2062 | 285 | switch (active_output) | ||
2063 | 286 | { | ||
2064 | 287 | case VolumeControl.ActiveOutput.SPEAKERS: | ||
2065 | 288 | if (volume <= 0.0) | ||
2066 | 289 | icon = "audio-volume-muted"; | ||
2067 | 290 | else if (volume <= 0.3) | ||
2068 | 291 | icon = "audio-volume-low"; | ||
2069 | 292 | else if (volume <= 0.7) | ||
2070 | 293 | icon = "audio-volume-medium"; | ||
2071 | 294 | else | ||
2072 | 295 | icon = "audio-volume-high"; | ||
2073 | 296 | break; | ||
2074 | 297 | case VolumeControl.ActiveOutput.HEADPHONES: | ||
2075 | 298 | if (volume <= 0.0) | ||
2076 | 299 | icon = "audio-volume-muted"; | ||
2077 | 300 | else if (volume <= 0.3) | ||
2078 | 301 | icon = "audio-volume-low"; | ||
2079 | 302 | else if (volume <= 0.7) | ||
2080 | 303 | icon = "audio-volume-medium"; | ||
2081 | 304 | else | ||
2082 | 305 | icon = "audio-volume-high"; | ||
2083 | 306 | break; | ||
2084 | 307 | case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: | ||
2085 | 308 | if (volume <= 0.0) | ||
2086 | 309 | icon = "audio-volume-muted"; | ||
2087 | 310 | else if (volume <= 0.3) | ||
2088 | 311 | icon = "audio-volume-low"; | ||
2089 | 312 | else if (volume <= 0.7) | ||
2090 | 313 | icon = "audio-volume-medium"; | ||
2091 | 314 | else | ||
2092 | 315 | icon = "audio-volume-high"; | ||
2093 | 316 | break; | ||
2094 | 317 | case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: | ||
2095 | 318 | if (volume <= 0.0) | ||
2096 | 319 | icon = "audio-volume-muted"; | ||
2097 | 320 | else if (volume <= 0.3) | ||
2098 | 321 | icon = "audio-volume-low"; | ||
2099 | 322 | else if (volume <= 0.7) | ||
2100 | 323 | icon = "audio-volume-medium"; | ||
2101 | 324 | else | ||
2102 | 325 | icon = "audio-volume-high"; | ||
2103 | 326 | break; | ||
2104 | 327 | case VolumeControl.ActiveOutput.USB_SPEAKER: | ||
2105 | 328 | if (volume <= 0.0) | ||
2106 | 329 | icon = "audio-volume-muted"; | ||
2107 | 330 | else if (volume <= 0.3) | ||
2108 | 331 | icon = "audio-volume-low"; | ||
2109 | 332 | else if (volume <= 0.7) | ||
2110 | 333 | icon = "audio-volume-medium"; | ||
2111 | 334 | else | ||
2112 | 335 | icon = "audio-volume-high"; | ||
2113 | 336 | break; | ||
2114 | 337 | case VolumeControl.ActiveOutput.USB_HEADPHONES: | ||
2115 | 338 | if (volume <= 0.0) | ||
2116 | 339 | icon = "audio-volume-muted"; | ||
2117 | 340 | else if (volume <= 0.3) | ||
2118 | 341 | icon = "audio-volume-low"; | ||
2119 | 342 | else if (volume <= 0.7) | ||
2120 | 343 | icon = "audio-volume-medium"; | ||
2121 | 344 | else | ||
2122 | 345 | icon = "audio-volume-high"; | ||
2123 | 346 | break; | ||
2124 | 347 | case VolumeControl.ActiveOutput.HDMI_SPEAKER: | ||
2125 | 348 | if (volume <= 0.0) | ||
2126 | 349 | icon = "audio-volume-muted"; | ||
2127 | 350 | else if (volume <= 0.3) | ||
2128 | 351 | icon = "audio-volume-low"; | ||
2129 | 352 | else if (volume <= 0.7) | ||
2130 | 353 | icon = "audio-volume-medium"; | ||
2131 | 354 | else | ||
2132 | 355 | icon = "audio-volume-high"; | ||
2133 | 356 | break; | ||
2134 | 357 | case VolumeControl.ActiveOutput.HDMI_HEADPHONES: | ||
2135 | 358 | if (volume <= 0.0) | ||
2136 | 359 | icon = "audio-volume-muted"; | ||
2137 | 360 | else if (volume <= 0.3) | ||
2138 | 361 | icon = "audio-volume-low"; | ||
2139 | 362 | else if (volume <= 0.7) | ||
2140 | 363 | icon = "audio-volume-medium"; | ||
2141 | 364 | else | ||
2142 | 365 | icon = "audio-volume-high"; | ||
2143 | 366 | break; | ||
2144 | 367 | } | ||
2145 | 368 | return icon; | ||
2146 | 369 | } | ||
2147 | 370 | |||
2148 | 371 | private string get_volume_root_icon_by_volume (double volume, VolumeControl.ActiveOutput active_output) | ||
2149 | 372 | { | ||
2150 | 373 | string icon = ""; | ||
2151 | 374 | switch (active_output) | ||
2152 | 375 | { | ||
2153 | 376 | case VolumeControl.ActiveOutput.SPEAKERS: | ||
2154 | 377 | if (volume <= 0.0) | ||
2155 | 378 | icon = "audio-volume-muted-panel"; | ||
2156 | 379 | else if (volume <= 0.3) | ||
2157 | 380 | icon = "audio-volume-low-panel"; | ||
2158 | 381 | else if (volume <= 0.7) | ||
2159 | 382 | icon = "audio-volume-medium-panel"; | ||
2160 | 383 | else | ||
2161 | 384 | icon = "audio-volume-high-panel"; | ||
2162 | 385 | break; | ||
2163 | 386 | case VolumeControl.ActiveOutput.HEADPHONES: | ||
2164 | 387 | if (volume <= 0.0) | ||
2165 | 388 | icon = "audio-volume-muted-panel"; | ||
2166 | 389 | else if (volume <= 0.3) | ||
2167 | 390 | icon = "audio-volume-low-panel"; | ||
2168 | 391 | else if (volume <= 0.7) | ||
2169 | 392 | icon = "audio-volume-medium-panel"; | ||
2170 | 393 | else | ||
2171 | 394 | icon = "audio-volume-high-panel"; | ||
2172 | 395 | break; | ||
2173 | 396 | case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: | ||
2174 | 397 | if (volume <= 0.0) | ||
2175 | 398 | icon = "audio-volume-muted-panel"; | ||
2176 | 399 | else if (volume <= 0.3) | ||
2177 | 400 | icon = "audio-volume-low-panel"; | ||
2178 | 401 | else if (volume <= 0.7) | ||
2179 | 402 | icon = "audio-volume-medium-panel"; | ||
2180 | 403 | else | ||
2181 | 404 | icon = "audio-volume-high-panel"; | ||
2182 | 405 | break; | ||
2183 | 406 | case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: | ||
2184 | 407 | if (volume <= 0.0) | ||
2185 | 408 | icon = "audio-volume-muted-panel"; | ||
2186 | 409 | else if (volume <= 0.3) | ||
2187 | 410 | icon = "audio-volume-low-panel"; | ||
2188 | 411 | else if (volume <= 0.7) | ||
2189 | 412 | icon = "audio-volume-medium-panel"; | ||
2190 | 413 | else | ||
2191 | 414 | icon = "audio-volume-high-panel"; | ||
2192 | 415 | break; | ||
2193 | 416 | case VolumeControl.ActiveOutput.USB_SPEAKER: | ||
2194 | 417 | if (volume <= 0.0) | ||
2195 | 418 | icon = "audio-volume-muted-panel"; | ||
2196 | 419 | else if (volume <= 0.3) | ||
2197 | 420 | icon = "audio-volume-low-panel"; | ||
2198 | 421 | else if (volume <= 0.7) | ||
2199 | 422 | icon = "audio-volume-medium-panel"; | ||
2200 | 423 | else | ||
2201 | 424 | icon = "audio-volume-high-panel"; | ||
2202 | 425 | break; | ||
2203 | 426 | case VolumeControl.ActiveOutput.USB_HEADPHONES: | ||
2204 | 427 | if (volume <= 0.0) | ||
2205 | 428 | icon = "audio-volume-muted-panel"; | ||
2206 | 429 | else if (volume <= 0.3) | ||
2207 | 430 | icon = "audio-volume-low-panel"; | ||
2208 | 431 | else if (volume <= 0.7) | ||
2209 | 432 | icon = "audio-volume-medium-panel"; | ||
2210 | 433 | else | ||
2211 | 434 | icon = "audio-volume-high-panel"; | ||
2212 | 435 | break; | ||
2213 | 436 | case VolumeControl.ActiveOutput.HDMI_SPEAKER: | ||
2214 | 437 | if (volume <= 0.0) | ||
2215 | 438 | icon = "audio-volume-muted-panel"; | ||
2216 | 439 | else if (volume <= 0.3) | ||
2217 | 440 | icon = "audio-volume-low-panel"; | ||
2218 | 441 | else if (volume <= 0.7) | ||
2219 | 442 | icon = "audio-volume-medium-panel"; | ||
2220 | 443 | else | ||
2221 | 444 | icon = "audio-volume-high-panel"; | ||
2222 | 445 | break; | ||
2223 | 446 | case VolumeControl.ActiveOutput.HDMI_HEADPHONES: | ||
2224 | 447 | if (volume <= 0.0) | ||
2225 | 448 | icon = "audio-volume-muted-panel"; | ||
2226 | 449 | else if (volume <= 0.3) | ||
2227 | 450 | icon = "audio-volume-low-panel"; | ||
2228 | 451 | else if (volume <= 0.7) | ||
2229 | 452 | icon = "audio-volume-medium-panel"; | ||
2230 | 453 | else | ||
2231 | 454 | icon = "audio-volume-high-panel"; | ||
2232 | 455 | break; | ||
2233 | 456 | } | ||
2234 | 457 | return icon; | ||
2235 | 458 | } | ||
2236 | 459 | |||
2237 | 460 | private string get_volume_notification_icon (double volume, bool loud, VolumeControl.ActiveOutput active_output) { | ||
2238 | 461 | string icon = ""; | ||
2239 | 462 | if (loud) { | ||
2240 | 463 | switch (active_output) | ||
2241 | 464 | { | ||
2242 | 465 | case VolumeControl.ActiveOutput.SPEAKERS: | ||
2243 | 466 | icon = "audio-volume-high"; | ||
2244 | 467 | break; | ||
2245 | 468 | case VolumeControl.ActiveOutput.HEADPHONES: | ||
2246 | 469 | icon = "audio-volume-high"; | ||
2247 | 470 | break; | ||
2248 | 471 | case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: | ||
2249 | 472 | icon = "audio-volume-high"; | ||
2250 | 473 | break; | ||
2251 | 474 | case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: | ||
2252 | 475 | icon = "audio-volume-high"; | ||
2253 | 476 | break; | ||
2254 | 477 | case VolumeControl.ActiveOutput.USB_SPEAKER: | ||
2255 | 478 | icon = "audio-volume-high"; | ||
2256 | 479 | break; | ||
2257 | 480 | case VolumeControl.ActiveOutput.USB_HEADPHONES: | ||
2258 | 481 | icon = "audio-volume-high"; | ||
2259 | 482 | break; | ||
2260 | 483 | case VolumeControl.ActiveOutput.HDMI_SPEAKER: | ||
2261 | 484 | icon = "audio-volume-high"; | ||
2262 | 485 | break; | ||
2263 | 486 | case VolumeControl.ActiveOutput.HDMI_HEADPHONES: | ||
2264 | 487 | icon = "audio-volume-high"; | ||
2265 | 488 | break; | ||
2266 | 489 | } | ||
2267 | 490 | } else { | ||
2268 | 491 | icon = get_volume_icon (volume, active_output); | ||
2269 | 492 | } | ||
2270 | 493 | return icon; | ||
2271 | 494 | } | ||
2272 | 495 | |||
2273 | 496 | private string get_volume_root_icon (double volume, bool mute, VolumeControl.ActiveOutput active_output) { | ||
2274 | 497 | string icon = ""; | ||
2275 | 498 | switch (active_output) | ||
2276 | 499 | { | ||
2277 | 500 | case VolumeControl.ActiveOutput.SPEAKERS: | ||
2278 | 501 | if (mute || volume <= 0.0) | ||
2279 | 502 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; | ||
2280 | 503 | else if (this.accounts_service != null && this.accounts_service.silentMode) | ||
2281 | 504 | icon = "audio-volume-muted-panel"; | ||
2282 | 505 | else | ||
2283 | 506 | icon = get_volume_root_icon_by_volume (volume, active_output); | ||
2284 | 507 | break; | ||
2285 | 508 | case VolumeControl.ActiveOutput.HEADPHONES: | ||
2286 | 509 | if (mute || volume <= 0.0) | ||
2287 | 510 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; | ||
2288 | 511 | else if (this.accounts_service != null && this.accounts_service.silentMode) | ||
2289 | 512 | icon = "audio-volume-muted-panel"; | ||
2290 | 513 | else | ||
2291 | 514 | icon = get_volume_root_icon_by_volume (volume, active_output); | ||
2292 | 515 | break; | ||
2293 | 516 | case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: | ||
2294 | 517 | if (mute || volume <= 0.0) | ||
2295 | 518 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; | ||
2296 | 519 | else if (this.accounts_service != null && this.accounts_service.silentMode) | ||
2297 | 520 | icon = "audio-volume-muted-panel"; | ||
2298 | 521 | else | ||
2299 | 522 | icon = get_volume_root_icon_by_volume (volume, active_output); | ||
2300 | 523 | break; | ||
2301 | 524 | case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: | ||
2302 | 525 | if (mute || volume <= 0.0) | ||
2303 | 526 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; | ||
2304 | 527 | else if (this.accounts_service != null && this.accounts_service.silentMode) | ||
2305 | 528 | icon = "audio-volume-muted-panel"; | ||
2306 | 529 | else | ||
2307 | 530 | icon = get_volume_root_icon_by_volume (volume, active_output); | ||
2308 | 531 | break; | ||
2309 | 532 | case VolumeControl.ActiveOutput.USB_SPEAKER: | ||
2310 | 533 | if (mute || volume <= 0.0) | ||
2311 | 534 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; | ||
2312 | 535 | else if (this.accounts_service != null && this.accounts_service.silentMode) | ||
2313 | 536 | icon = "audio-volume-muted-panel"; | ||
2314 | 537 | else | ||
2315 | 538 | icon = get_volume_root_icon_by_volume (volume, active_output); | ||
2316 | 539 | break; | ||
2317 | 540 | case VolumeControl.ActiveOutput.USB_HEADPHONES: | ||
2318 | 541 | if (mute || volume <= 0.0) | ||
2319 | 542 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; | ||
2320 | 543 | else if (this.accounts_service != null && this.accounts_service.silentMode) | ||
2321 | 544 | icon = "audio-volume-muted-panel"; | ||
2322 | 545 | else | ||
2323 | 546 | icon = get_volume_root_icon_by_volume (volume, active_output); | ||
2324 | 547 | break; | ||
2325 | 548 | case VolumeControl.ActiveOutput.HDMI_SPEAKER: | ||
2326 | 549 | if (mute || volume <= 0.0) | ||
2327 | 550 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; | ||
2328 | 551 | else if (this.accounts_service != null && this.accounts_service.silentMode) | ||
2329 | 552 | icon = "audio-volume-muted-panel"; | ||
2330 | 553 | else | ||
2331 | 554 | icon = get_volume_root_icon_by_volume (volume, active_output); | ||
2332 | 555 | break; | ||
2333 | 556 | case VolumeControl.ActiveOutput.HDMI_HEADPHONES: | ||
2334 | 557 | if (mute || volume <= 0.0) | ||
2335 | 558 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; | ||
2336 | 559 | else if (this.accounts_service != null && this.accounts_service.silentMode) | ||
2337 | 560 | icon = "audio-volume-muted-panel"; | ||
2338 | 561 | else | ||
2339 | 562 | icon = get_volume_root_icon_by_volume (volume, active_output); | ||
2340 | 563 | break; | ||
2341 | 564 | } | ||
2342 | 565 | return icon; | ||
2343 | 566 | } | ||
2344 | 284 | 567 | ||
2345 | 285 | private void update_notification () { | 568 | private void update_notification () { |
2346 | 286 | 569 | ||
2347 | @@ -312,47 +595,72 @@ | |||
2348 | 312 | _pre_warn_volume = null; | 595 | _pre_warn_volume = null; |
2349 | 313 | volume_control.volume = tmp; | 596 | volume_control.volume = tmp; |
2350 | 314 | } | 597 | } |
2351 | 598 | waiting_user_approve_warn = false; | ||
2352 | 315 | }); | 599 | }); |
2353 | 316 | warn_notification.add_action ("cancel", _("Cancel"), (n, a) => { | 600 | warn_notification.add_action ("cancel", _("Cancel"), (n, a) => { |
2354 | 317 | _pre_warn_volume = null; | 601 | _pre_warn_volume = null; |
2355 | 602 | waiting_user_approve_warn = false; | ||
2356 | 318 | }); | 603 | }); |
2357 | 604 | waiting_user_approve_warn = true; | ||
2358 | 319 | show_notification(warn_notification); | 605 | show_notification(warn_notification); |
2359 | 320 | } else { | 606 | } else { |
2384 | 321 | close_notification(warn_notification); | 607 | if (!waiting_user_approve_warn) { |
2385 | 322 | 608 | close_notification(warn_notification); | |
2386 | 323 | if (notify_server_supports_sync && !block_info_notifications) { | 609 | |
2387 | 324 | 610 | if (notify_server_supports_sync && !block_info_notifications) { | |
2388 | 325 | /* Determine Label */ | 611 | |
2389 | 326 | unowned string volume_label = loud | 612 | /* Determine Label */ |
2390 | 327 | ? _("High volume can damage your hearing.") | 613 | string volume_label = loud |
2391 | 328 | : ""; | 614 | ? _("High volume can damage your hearing.") |
2392 | 329 | 615 | : ""; | |
2393 | 330 | /* Choose an icon */ | 616 | |
2394 | 331 | unowned string icon; | 617 | if (volume_label == "") { |
2395 | 332 | if (loud) { | 618 | if (volume_control.active_output == VolumeControl.ActiveOutput.SPEAKERS) { |
2396 | 333 | icon = "audio-volume-high"; | 619 | volume_label = _("Speakers"); |
2397 | 334 | } else { | 620 | } |
2398 | 335 | var volume = volume_control.volume.volume; | 621 | |
2399 | 336 | 622 | if (volume_control.active_output == VolumeControl.ActiveOutput.HEADPHONES) { | |
2400 | 337 | if (volume <= 0.0) | 623 | volume_label = _("Headphones"); |
2401 | 338 | icon = "audio-volume-muted"; | 624 | } |
2402 | 339 | else if (volume <= 0.3) | 625 | |
2403 | 340 | icon = "audio-volume-low"; | 626 | if (volume_control.active_output == VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES) { |
2404 | 341 | else if (volume <= 0.7) | 627 | volume_label = _("Bluetooth headphones"); |
2405 | 342 | icon = "audio-volume-medium"; | 628 | } |
2406 | 343 | else | 629 | |
2407 | 344 | icon = "audio-volume-high"; | 630 | if (volume_control.active_output == VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER) { |
2408 | 631 | volume_label = _("Bluetooth speaker"); | ||
2409 | 632 | } | ||
2410 | 633 | |||
2411 | 634 | if (volume_control.active_output == VolumeControl.ActiveOutput.USB_SPEAKER) { | ||
2412 | 635 | volume_label = _("Usb speaker"); | ||
2413 | 636 | } | ||
2414 | 637 | |||
2415 | 638 | if (volume_control.active_output == VolumeControl.ActiveOutput.USB_HEADPHONES) { | ||
2416 | 639 | volume_label = _("Usb headphones"); | ||
2417 | 640 | } | ||
2418 | 641 | |||
2419 | 642 | if (volume_control.active_output == VolumeControl.ActiveOutput.HDMI_SPEAKER) { | ||
2420 | 643 | volume_label = _("HDMI speaker"); | ||
2421 | 644 | } | ||
2422 | 645 | |||
2423 | 646 | if (volume_control.active_output == VolumeControl.ActiveOutput.HDMI_HEADPHONES) { | ||
2424 | 647 | volume_label = _("HDMI headphones"); | ||
2425 | 648 | } | ||
2426 | 649 | } | ||
2427 | 650 | |||
2428 | 651 | /* Choose an icon */ | ||
2429 | 652 | string icon = get_volume_notification_icon (volume_control.volume.volume, loud, volume_control.active_output); | ||
2430 | 653 | |||
2431 | 654 | /* Reset the notification */ | ||
2432 | 655 | var n = this.info_notification; | ||
2433 | 656 | n.update (_("Volume"), volume_label, icon); | ||
2434 | 657 | n.clear_hints(); | ||
2435 | 658 | n.set_hint ("x-canonical-non-shaped-icon", "true"); | ||
2436 | 659 | n.set_hint ("x-canonical-private-synchronous", "true"); | ||
2437 | 660 | n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false"); | ||
2438 | 661 | n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0)); | ||
2439 | 662 | show_notification(n); | ||
2440 | 345 | } | 663 | } |
2441 | 346 | |||
2442 | 347 | /* Reset the notification */ | ||
2443 | 348 | var n = this.info_notification; | ||
2444 | 349 | n.update (_("Volume"), volume_label, icon); | ||
2445 | 350 | n.clear_hints(); | ||
2446 | 351 | n.set_hint ("x-canonical-non-shaped-icon", "true"); | ||
2447 | 352 | n.set_hint ("x-canonical-private-synchronous", "true"); | ||
2448 | 353 | n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false"); | ||
2449 | 354 | n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0)); | ||
2450 | 355 | show_notification(n); | ||
2451 | 356 | } | 664 | } |
2452 | 357 | } | 665 | } |
2453 | 358 | } | 666 | } |
2454 | 359 | 667 | ||
2455 | === modified file 'src/sound-menu.vala' | |||
2456 | --- src/sound-menu.vala 2015-09-15 12:16:10 +0000 | |||
2457 | +++ src/sound-menu.vala 2015-10-28 09:41:22 +0000 | |||
2458 | @@ -71,6 +71,7 @@ | |||
2459 | 71 | this.notify_handlers = new HashTable<MediaPlayer, ulong> (direct_hash, direct_equal); | 71 | this.notify_handlers = new HashTable<MediaPlayer, ulong> (direct_hash, direct_equal); |
2460 | 72 | 72 | ||
2461 | 73 | this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0; | 73 | this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0; |
2462 | 74 | |||
2463 | 74 | } | 75 | } |
2464 | 75 | 76 | ||
2465 | 76 | ~SoundMenu () { | 77 | ~SoundMenu () { |
2466 | @@ -193,6 +194,43 @@ | |||
2467 | 193 | this.notify_handlers.remove (player); | 194 | this.notify_handlers.remove (player); |
2468 | 194 | } | 195 | } |
2469 | 195 | 196 | ||
2470 | 197 | public void update_volume_slider (VolumeControl.ActiveOutput active_output) { | ||
2471 | 198 | int index = find_action (this.volume_section, "indicator.volume"); | ||
2472 | 199 | if (index != -1) { | ||
2473 | 200 | string label = "Volume"; | ||
2474 | 201 | switch (active_output) { | ||
2475 | 202 | case VolumeControl.ActiveOutput.SPEAKERS: | ||
2476 | 203 | label = _("Volume"); | ||
2477 | 204 | break; | ||
2478 | 205 | case VolumeControl.ActiveOutput.HEADPHONES: | ||
2479 | 206 | label = _("Volume (Headphones)"); | ||
2480 | 207 | break; | ||
2481 | 208 | case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: | ||
2482 | 209 | label = _("Volume (Bluetooth)"); | ||
2483 | 210 | break; | ||
2484 | 211 | case VolumeControl.ActiveOutput.USB_SPEAKER: | ||
2485 | 212 | label = _("Volume (Usb)"); | ||
2486 | 213 | break; | ||
2487 | 214 | case VolumeControl.ActiveOutput.HDMI_SPEAKER: | ||
2488 | 215 | label = _("Volume (HDMI)"); | ||
2489 | 216 | break; | ||
2490 | 217 | case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: | ||
2491 | 218 | label = _("Volume (Bluetooth headphones)"); | ||
2492 | 219 | break; | ||
2493 | 220 | case VolumeControl.ActiveOutput.USB_HEADPHONES: | ||
2494 | 221 | label = _("Volume (Usb headphones)"); | ||
2495 | 222 | break; | ||
2496 | 223 | case VolumeControl.ActiveOutput.HDMI_HEADPHONES: | ||
2497 | 224 | label = _("Volume (HDMI headphones)"); | ||
2498 | 225 | break; | ||
2499 | 226 | } | ||
2500 | 227 | this.volume_section.remove (index); | ||
2501 | 228 | this.volume_section.insert_item (index, this.create_slider_menu_item (_(label), "indicator.volume(0)", 0.0, 1.0, 0.01, | ||
2502 | 229 | "audio-volume-low-zero-panel", | ||
2503 | 230 | "audio-volume-high-panel")); | ||
2504 | 231 | } | ||
2505 | 232 | } | ||
2506 | 233 | |||
2507 | 196 | public Menu root; | 234 | public Menu root; |
2508 | 197 | public Menu menu; | 235 | public Menu menu; |
2509 | 198 | Menu volume_section; | 236 | Menu volume_section; |
2510 | 199 | 237 | ||
2511 | === modified file 'src/volume-control-pulse.vala' | |||
2512 | --- src/volume-control-pulse.vala 2015-08-12 18:16:40 +0000 | |||
2513 | +++ src/volume-control-pulse.vala 2015-10-28 09:41:22 +0000 | |||
2514 | @@ -87,6 +87,7 @@ | |||
2515 | 87 | private bool _send_next_local_volume = false; | 87 | private bool _send_next_local_volume = false; |
2516 | 88 | private double _account_service_volume = 0.0; | 88 | private double _account_service_volume = 0.0; |
2517 | 89 | private bool _active_port_headphone = false; | 89 | private bool _active_port_headphone = false; |
2518 | 90 | private VolumeControl.ActiveOutput _active_output = VolumeControl.ActiveOutput.SPEAKERS; | ||
2519 | 90 | 91 | ||
2520 | 91 | /** true when connected to the pulse server */ | 92 | /** true when connected to the pulse server */ |
2521 | 92 | public override bool ready { get; private set; } | 93 | public override bool ready { get; private set; } |
2522 | @@ -135,6 +136,49 @@ | |||
2523 | 135 | stop_high_volume_approved_timer(); | 136 | stop_high_volume_approved_timer(); |
2524 | 136 | } | 137 | } |
2525 | 137 | 138 | ||
2526 | 139 | private VolumeControl.ActiveOutput calculate_active_output (SinkInfo? sink) { | ||
2527 | 140 | |||
2528 | 141 | VolumeControl.ActiveOutput ret_output = VolumeControl.ActiveOutput.SPEAKERS; | ||
2529 | 142 | /* Check if the current active port is headset/headphone */ | ||
2530 | 143 | /* There is not easy way to check if the port is a headset/headphone besides | ||
2531 | 144 | * checking for the port name. On touch (with the pulseaudio droid element) | ||
2532 | 145 | * the headset/headphone port is called 'output-headset' and 'output-headphone'. | ||
2533 | 146 | * On the desktop this is usually called 'analog-output-headphones' */ | ||
2534 | 147 | // look if it's a headset/headphones | ||
2535 | 148 | if (sink.name == "indicator_sound_test_headphones" || | ||
2536 | 149 | (sink.active_port != null && | ||
2537 | 150 | (sink.active_port.name.contains("headset") || | ||
2538 | 151 | sink.active_port.name.contains("headphone")))) { | ||
2539 | 152 | _active_port_headphone = true; | ||
2540 | 153 | // check if it's a bluetooth device | ||
2541 | 154 | var device_bus = sink.proplist.gets ("device.bus"); | ||
2542 | 155 | if (device_bus != null && device_bus == "bluetooth") { | ||
2543 | 156 | ret_output = VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES; | ||
2544 | 157 | } else if (device_bus != null && device_bus == "usb") { | ||
2545 | 158 | ret_output = VolumeControl.ActiveOutput.USB_HEADPHONES; | ||
2546 | 159 | } else if (device_bus != null && device_bus == "hdmi") { | ||
2547 | 160 | ret_output = VolumeControl.ActiveOutput.HDMI_HEADPHONES; | ||
2548 | 161 | } else { | ||
2549 | 162 | ret_output = VolumeControl.ActiveOutput.HEADPHONES; | ||
2550 | 163 | } | ||
2551 | 164 | } else { | ||
2552 | 165 | // speaker | ||
2553 | 166 | _active_port_headphone = false; | ||
2554 | 167 | var device_bus = sink.proplist.gets ("device.bus"); | ||
2555 | 168 | if (device_bus != null && device_bus == "bluetooth") { | ||
2556 | 169 | ret_output = VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER; | ||
2557 | 170 | } else if (device_bus != null && device_bus == "usb") { | ||
2558 | 171 | ret_output = VolumeControl.ActiveOutput.USB_SPEAKER; | ||
2559 | 172 | } else if (device_bus != null && device_bus == "hdmi") { | ||
2560 | 173 | ret_output = VolumeControl.ActiveOutput.HDMI_SPEAKER; | ||
2561 | 174 | } else { | ||
2562 | 175 | ret_output = VolumeControl.ActiveOutput.SPEAKERS; | ||
2563 | 176 | } | ||
2564 | 177 | } | ||
2565 | 178 | |||
2566 | 179 | return ret_output; | ||
2567 | 180 | } | ||
2568 | 181 | |||
2569 | 138 | /* PulseAudio logic*/ | 182 | /* PulseAudio logic*/ |
2570 | 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) |
2571 | 140 | { | 184 | { |
2572 | @@ -201,18 +245,20 @@ | |||
2573 | 201 | this.notify_property ("is-playing"); | 245 | this.notify_property ("is-playing"); |
2574 | 202 | } | 246 | } |
2575 | 203 | 247 | ||
2588 | 204 | /* Check if the current active port is headset/headphone */ | 248 | // store the current status of the active output |
2589 | 205 | /* There is not easy way to check if the port is a headset/headphone besides | 249 | VolumeControl.ActiveOutput active_output_before = active_output; |
2590 | 206 | * checking for the port name. On touch (with the pulseaudio droid element) | 250 | |
2591 | 207 | * the headset/headphone port is called 'output-headset' and 'output-headphone'. | 251 | // calculate the output |
2592 | 208 | * On the desktop this is usually called 'analog-output-headphones' */ | 252 | _active_output = calculate_active_output (i); |
2593 | 209 | if (i.active_port != null && | 253 | |
2594 | 210 | (i.active_port.name == "output-wired_headset" || | 254 | // check if the output has changed, if so... emit a signal |
2595 | 211 | i.active_port.name == "output-wired_headphone" || | 255 | VolumeControl.ActiveOutput active_output_now = active_output; |
2596 | 212 | i.active_port.name == "analog-output-headphones")) { | 256 | if (active_output_now != active_output_before) { |
2597 | 213 | _active_port_headphone = true; | 257 | this.active_output_changed (active_output_now); |
2598 | 214 | } else { | 258 | if (active_output_now == VolumeControl.ActiveOutput.SPEAKERS) { |
2599 | 215 | _active_port_headphone = false; | 259 | _high_volume_approved = false; |
2600 | 260 | } | ||
2601 | 261 | update_high_volume(); | ||
2602 | 216 | } | 262 | } |
2603 | 217 | 263 | ||
2604 | 218 | if (_pulse_use_stream_restore == false && | 264 | if (_pulse_use_stream_restore == false && |
2605 | @@ -478,7 +524,8 @@ | |||
2606 | 478 | this.context = new PulseAudio.Context (loop.get_api(), null, props); | 524 | this.context = new PulseAudio.Context (loop.get_api(), null, props); |
2607 | 479 | this.context.set_state_callback (context_state_callback); | 525 | this.context.set_state_callback (context_state_callback); |
2608 | 480 | 526 | ||
2610 | 481 | if (context.connect(null, Context.Flags.NOFAIL, null) < 0) | 527 | var server_string = Environment.get_variable("PULSE_SERVER"); |
2611 | 528 | if (context.connect(server_string, Context.Flags.NOFAIL, null) < 0) | ||
2612 | 482 | warning( "pa_context_connect() failed: %s\n", PulseAudio.strerror(context.errno())); | 529 | warning( "pa_context_connect() failed: %s\n", PulseAudio.strerror(context.errno())); |
2613 | 483 | } | 530 | } |
2614 | 484 | 531 | ||
2615 | @@ -535,6 +582,14 @@ | |||
2616 | 535 | } | 582 | } |
2617 | 536 | } | 583 | } |
2618 | 537 | 584 | ||
2619 | 585 | public override VolumeControl.ActiveOutput active_output | ||
2620 | 586 | { | ||
2621 | 587 | get | ||
2622 | 588 | { | ||
2623 | 589 | return _active_output; | ||
2624 | 590 | } | ||
2625 | 591 | } | ||
2626 | 592 | |||
2627 | 538 | /* Volume operations */ | 593 | /* Volume operations */ |
2628 | 539 | private static PulseAudio.Volume double_to_volume (double vol) | 594 | private static PulseAudio.Volume double_to_volume (double vol) |
2629 | 540 | { | 595 | { |
2630 | @@ -710,7 +765,7 @@ | |||
2631 | 710 | private bool calculate_high_volume_from_volume(double volume) { | 765 | private bool calculate_high_volume_from_volume(double volume) { |
2632 | 711 | return _active_port_headphone | 766 | return _active_port_headphone |
2633 | 712 | && _warning_volume_enabled | 767 | && _warning_volume_enabled |
2635 | 713 | && volume >= _warning_volume_norms | 768 | && volume > _warning_volume_norms |
2636 | 714 | && (stream == "multimedia"); | 769 | && (stream == "multimedia"); |
2637 | 715 | } | 770 | } |
2638 | 716 | 771 | ||
2639 | 717 | 772 | ||
2640 | === modified file 'src/volume-control.vala' | |||
2641 | --- src/volume-control.vala 2015-08-12 14:00:56 +0000 | |||
2642 | +++ src/volume-control.vala 2015-10-28 09:41:22 +0000 | |||
2643 | @@ -28,6 +28,17 @@ | |||
2644 | 28 | VOLUME_STREAM_CHANGE | 28 | VOLUME_STREAM_CHANGE |
2645 | 29 | } | 29 | } |
2646 | 30 | 30 | ||
2647 | 31 | public enum ActiveOutput { | ||
2648 | 32 | SPEAKERS, | ||
2649 | 33 | HEADPHONES, | ||
2650 | 34 | BLUETOOTH_HEADPHONES, | ||
2651 | 35 | BLUETOOTH_SPEAKER, | ||
2652 | 36 | USB_SPEAKER, | ||
2653 | 37 | USB_HEADPHONES, | ||
2654 | 38 | HDMI_SPEAKER, | ||
2655 | 39 | HDMI_HEADPHONES | ||
2656 | 40 | } | ||
2657 | 41 | |||
2658 | 31 | public class Volume : Object { | 42 | public class Volume : Object { |
2659 | 32 | public double volume; | 43 | public double volume; |
2660 | 33 | public VolumeReasons reason; | 44 | public VolumeReasons reason; |
2661 | @@ -39,6 +50,7 @@ | |||
2662 | 39 | public virtual bool high_volume { get { return false; } protected set { } } | 50 | public virtual bool high_volume { get { return false; } protected set { } } |
2663 | 40 | public virtual bool mute { get { return false; } } | 51 | public virtual bool mute { get { return false; } } |
2664 | 41 | public virtual bool is_playing { get { return false; } } | 52 | public virtual bool is_playing { get { return false; } } |
2665 | 53 | public virtual VolumeControl.ActiveOutput active_output { get { return VolumeControl.ActiveOutput.SPEAKERS; } } | ||
2666 | 42 | private Volume _volume; | 54 | private Volume _volume; |
2667 | 43 | public virtual Volume volume { get { return _volume; } set { } } | 55 | public virtual Volume volume { get { return _volume; } set { } } |
2668 | 44 | public virtual double mic_volume { get { return 0.0; } set { } } | 56 | public virtual double mic_volume { get { return 0.0; } set { } } |
2669 | @@ -56,4 +68,6 @@ | |||
2670 | 56 | v.reason = reason; | 68 | v.reason = reason; |
2671 | 57 | this.volume = v; | 69 | this.volume = v; |
2672 | 58 | } | 70 | } |
2673 | 71 | |||
2674 | 72 | public signal void active_output_changed (VolumeControl.ActiveOutput active_output); | ||
2675 | 59 | } | 73 | } |
2676 | 60 | 74 | ||
2677 | === modified file 'tests/CMakeLists.txt' | |||
2678 | --- tests/CMakeLists.txt 2015-08-11 21:42:20 +0000 | |||
2679 | +++ tests/CMakeLists.txt 2015-10-28 09:41:22 +0000 | |||
2680 | @@ -5,10 +5,10 @@ | |||
2681 | 5 | 5 | ||
2682 | 6 | include_directories(${GTEST_INCLUDE_DIR}) | 6 | include_directories(${GTEST_INCLUDE_DIR}) |
2683 | 7 | 7 | ||
2685 | 8 | add_library (gtest STATIC | 8 | add_library (gtest-static STATIC |
2686 | 9 | ${GTEST_SOURCE_DIR}/gtest-all.cc | 9 | ${GTEST_SOURCE_DIR}/gtest-all.cc |
2687 | 10 | ${GTEST_SOURCE_DIR}/gtest_main.cc) | 10 | ${GTEST_SOURCE_DIR}/gtest_main.cc) |
2689 | 11 | target_link_libraries(gtest ${GTEST_LIBS}) | 11 | target_link_libraries(gtest-static ${GTEST_LIBS}) |
2690 | 12 | 12 | ||
2691 | 13 | ########################### | 13 | ########################### |
2692 | 14 | # GSettings Schema | 14 | # GSettings Schema |
2693 | @@ -22,7 +22,8 @@ | |||
2694 | 22 | # GSettings: | 22 | # GSettings: |
2695 | 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, |
2696 | 24 | # and help the tests to find that file by setting -DSCHEMA_DIR | 24 | # and help the tests to find that file by setting -DSCHEMA_DIR |
2698 | 25 | set (SCHEMA_DIR "${CMAKE_CURRENT_BINARY_DIR}/gsettings-schemas") | 25 | set (XDG_DATA_DIRS "${CMAKE_CURRENT_BINARY_DIR}/gsettings-schemas") |
2699 | 26 | set (SCHEMA_DIR "${XDG_DATA_DIRS}/glib-2.0/schemas") | ||
2700 | 26 | add_definitions(-DSCHEMA_DIR="${SCHEMA_DIR}") | 27 | add_definitions(-DSCHEMA_DIR="${SCHEMA_DIR}") |
2701 | 27 | execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas | 28 | execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas |
2702 | 28 | OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE | 29 | OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE |
2703 | @@ -31,6 +32,7 @@ | |||
2704 | 31 | DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.sound.gschema.xml | 32 | DEPENDS ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.sound.gschema.xml |
2705 | 32 | COMMAND mkdir -p ${SCHEMA_DIR} | 33 | COMMAND mkdir -p ${SCHEMA_DIR} |
2706 | 33 | COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR} | 34 | COMMAND cp -f ${CMAKE_SOURCE_DIR}/data/*gschema.xml ${SCHEMA_DIR} |
2707 | 35 | COMMAND cp -f /usr/share/glib-2.0/schemas/com.ubuntu.sound.gschema.xml ${SCHEMA_DIR} | ||
2708 | 34 | COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR}) | 36 | COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR}) |
2709 | 35 | 37 | ||
2710 | 36 | ########################### | 38 | ########################### |
2711 | @@ -41,74 +43,74 @@ | |||
2712 | 41 | set(VALA_MOCKS_SYMBOLS_PATH "${CMAKE_CURRENT_BINARY_DIR}/vala-mocks.def") | 43 | set(VALA_MOCKS_SYMBOLS_PATH "${CMAKE_CURRENT_BINARY_DIR}/vala-mocks.def") |
2713 | 42 | 44 | ||
2714 | 43 | vala_init(vala-mocks | 45 | vala_init(vala-mocks |
2745 | 44 | DEPENDS | 46 | DEPENDS |
2746 | 45 | indicator-sound-service-lib | 47 | indicator-sound-service-lib |
2747 | 46 | PACKAGES | 48 | PACKAGES |
2748 | 47 | config | 49 | config |
2749 | 48 | gio-2.0 | 50 | gio-2.0 |
2750 | 49 | gio-unix-2.0 | 51 | gio-unix-2.0 |
2751 | 50 | libxml-2.0 | 52 | libxml-2.0 |
2752 | 51 | libpulse | 53 | libpulse |
2753 | 52 | libpulse-mainloop-glib | 54 | libpulse-mainloop-glib |
2754 | 53 | libnotify | 55 | libnotify |
2755 | 54 | accounts-service | 56 | accounts-service |
2756 | 55 | indicator-sound-service | 57 | indicator-sound-service |
2757 | 56 | OPTIONS | 58 | OPTIONS |
2758 | 57 | --ccode | 59 | --ccode |
2759 | 58 | --thread | 60 | --thread |
2760 | 59 | --vapidir=${CMAKE_BINARY_DIR}/src/ | 61 | --vapidir=${CMAKE_BINARY_DIR}/src/ |
2761 | 60 | --vapidir=${CMAKE_SOURCE_DIR}/vapi/ | 62 | --vapidir=${CMAKE_SOURCE_DIR}/vapi/ |
2762 | 61 | --vapidir=. | 63 | --vapidir=. |
2763 | 62 | ) | 64 | ) |
2764 | 63 | 65 | ||
2765 | 64 | vala_add(vala-mocks | 66 | vala_add(vala-mocks |
2766 | 65 | media-player-mock.vala | 67 | media-player-mock.vala |
2767 | 66 | ) | 68 | ) |
2768 | 67 | 69 | ||
2769 | 68 | vala_add(vala-mocks | 70 | vala_add(vala-mocks |
2770 | 69 | media-player-list-mock.vala | 71 | media-player-list-mock.vala |
2771 | 70 | ) | 72 | ) |
2772 | 71 | 73 | ||
2773 | 72 | vala_add(vala-mocks | 74 | vala_add(vala-mocks |
2774 | 73 | volume-control-mock.vala | 75 | volume-control-mock.vala |
2775 | 74 | ) | 76 | ) |
2776 | 75 | 77 | ||
2777 | 76 | vala_finish(vala-mocks | 78 | vala_finish(vala-mocks |
2786 | 77 | SOURCES | 79 | SOURCES |
2787 | 78 | vala_mocks_VALA_SOURCES | 80 | vala_mocks_VALA_SOURCES |
2788 | 79 | OUTPUTS | 81 | OUTPUTS |
2789 | 80 | vala_mocks_VALA_C | 82 | vala_mocks_VALA_C |
2790 | 81 | GENERATE_HEADER | 83 | GENERATE_HEADER |
2791 | 82 | ${VALA_MOCKS_HEADER_PATH} | 84 | ${VALA_MOCKS_HEADER_PATH} |
2792 | 83 | GENERATE_SYMBOLS | 85 | GENERATE_SYMBOLS |
2793 | 84 | ${VALA_MOCKS_SYMBOLS_PATH} | 86 | ${VALA_MOCKS_SYMBOLS_PATH} |
2794 | 85 | ) | 87 | ) |
2795 | 86 | 88 | ||
2796 | 87 | set_source_files_properties( | 89 | set_source_files_properties( |
2800 | 88 | ${vala_mocks_VALA_SOURCES} | 90 | ${vala_mocks_VALA_SOURCES} |
2801 | 89 | PROPERTIES | 91 | PROPERTIES |
2802 | 90 | HEADER_FILE_ONLY TRUE | 92 | HEADER_FILE_ONLY TRUE |
2803 | 91 | ) | 93 | ) |
2804 | 92 | 94 | ||
2805 | 93 | set( | 95 | set( |
2810 | 94 | VALA_MOCKS_SOURCES | 96 | VALA_MOCKS_SOURCES |
2811 | 95 | ${vala_mocks_VALA_SOURCES} | 97 | ${vala_mocks_VALA_SOURCES} |
2812 | 96 | ${vala_mocks_VALA_C} | 98 | ${vala_mocks_VALA_C} |
2813 | 97 | ${VALA_MOCKS_SYMBOLS_PATH} | 99 | ${VALA_MOCKS_SYMBOLS_PATH} |
2814 | 98 | ) | 100 | ) |
2815 | 99 | 101 | ||
2816 | 100 | add_definitions( | 102 | add_definitions( |
2818 | 101 | -Wno-unused-but-set-variable | 103 | -Wno-unused-but-set-variable |
2819 | 102 | ) | 104 | ) |
2820 | 103 | 105 | ||
2821 | 104 | add_library( | 106 | add_library( |
2824 | 105 | vala-mocks-lib STATIC | 107 | vala-mocks-lib STATIC |
2825 | 106 | ${VALA_MOCKS_SOURCES} | 108 | ${VALA_MOCKS_SOURCES} |
2826 | 107 | ) | 109 | ) |
2827 | 108 | 110 | ||
2828 | 109 | target_link_libraries( | 111 | target_link_libraries( |
2831 | 110 | vala-mocks-lib | 112 | vala-mocks-lib |
2832 | 111 | indicator-sound-service-lib | 113 | indicator-sound-service-lib |
2833 | 112 | ) | 114 | ) |
2834 | 113 | 115 | ||
2835 | 114 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) | 116 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) |
2836 | @@ -118,9 +120,9 @@ | |||
2837 | 118 | ########################### | 120 | ########################### |
2838 | 119 | 121 | ||
2839 | 120 | add_library( | 122 | add_library( |
2843 | 121 | pulse-mock | 123 | pulse-mock |
2844 | 122 | SHARED | 124 | SHARED |
2845 | 123 | pa-mock.cpp | 125 | pa-mock.cpp |
2846 | 124 | ) | 126 | ) |
2847 | 125 | 127 | ||
2848 | 126 | target_link_libraries (pulse-mock ${PULSEAUDIO_LIBRARIES}) | 128 | target_link_libraries (pulse-mock ${PULSEAUDIO_LIBRARIES}) |
2849 | @@ -131,7 +133,7 @@ | |||
2850 | 131 | 133 | ||
2851 | 132 | include_directories(${CMAKE_SOURCE_DIR}/src) | 134 | include_directories(${CMAKE_SOURCE_DIR}/src) |
2852 | 133 | add_executable (name-watch-test name-watch-test.cc ${CMAKE_SOURCE_DIR}/src/bus-watch-namespace.c) | 135 | add_executable (name-watch-test name-watch-test.cc ${CMAKE_SOURCE_DIR}/src/bus-watch-namespace.c) |
2854 | 134 | target_link_libraries (name-watch-test gtest ${SOUNDSERVICE_LIBRARIES}) | 136 | target_link_libraries (name-watch-test gtest-static ${SOUNDSERVICE_LIBRARIES}) |
2855 | 135 | add_test(name-watch-test name-watch-test) | 137 | add_test(name-watch-test name-watch-test) |
2856 | 136 | 138 | ||
2857 | 137 | ########################### | 139 | ########################### |
2858 | @@ -141,21 +143,21 @@ | |||
2859 | 141 | include_directories(${CMAKE_SOURCE_DIR}/src) | 143 | include_directories(${CMAKE_SOURCE_DIR}/src) |
2860 | 142 | add_executable (accounts-service-user-test accounts-service-user.cc) | 144 | add_executable (accounts-service-user-test accounts-service-user.cc) |
2861 | 143 | target_link_libraries ( | 145 | target_link_libraries ( |
2868 | 144 | accounts-service-user-test | 146 | accounts-service-user-test |
2869 | 145 | indicator-sound-service-lib | 147 | indicator-sound-service-lib |
2870 | 146 | vala-mocks-lib | 148 | vala-mocks-lib |
2871 | 147 | gtest | 149 | gtest-static |
2872 | 148 | ${SOUNDSERVICE_LIBRARIES} | 150 | ${SOUNDSERVICE_LIBRARIES} |
2873 | 149 | ${TEST_LIBRARIES} | 151 | ${TEST_LIBRARIES} |
2874 | 150 | ) | 152 | ) |
2875 | 151 | 153 | ||
2876 | 152 | # Split tests to work around libaccountservice sucking | 154 | # Split tests to work around libaccountservice sucking |
2877 | 153 | add_test(accounts-service-user-test-basic | 155 | add_test(accounts-service-user-test-basic |
2879 | 154 | accounts-service-user-test --gtest_filter=AccountsServiceUserTest.BasicObject | 156 | accounts-service-user-test --gtest_filter=AccountsServiceUserTest.BasicObject |
2880 | 155 | ) | 157 | ) |
2881 | 156 | 158 | ||
2882 | 157 | add_test(accounts-service-user-test-player | 159 | add_test(accounts-service-user-test-player |
2884 | 158 | accounts-service-user-test --gtest_filter=AccountsServiceUserTest.SetMediaPlayer | 160 | accounts-service-user-test --gtest_filter=AccountsServiceUserTest.SetMediaPlayer |
2885 | 159 | ) | 161 | ) |
2886 | 160 | 162 | ||
2887 | 161 | ########################### | 163 | ########################### |
2888 | @@ -165,11 +167,11 @@ | |||
2889 | 165 | include_directories(${CMAKE_SOURCE_DIR}/src) | 167 | include_directories(${CMAKE_SOURCE_DIR}/src) |
2890 | 166 | add_executable (volume-control-test volume-control-test.cc gschemas.compiled) | 168 | add_executable (volume-control-test volume-control-test.cc gschemas.compiled) |
2891 | 167 | target_link_libraries ( | 169 | target_link_libraries ( |
2897 | 168 | volume-control-test | 170 | volume-control-test |
2898 | 169 | indicator-sound-service-lib | 171 | indicator-sound-service-lib |
2899 | 170 | pulse-mock | 172 | pulse-mock |
2900 | 171 | gtest | 173 | gtest-static |
2901 | 172 | ${TEST_LIBRARIES} | 174 | ${TEST_LIBRARIES} |
2902 | 173 | ) | 175 | ) |
2903 | 174 | 176 | ||
2904 | 175 | add_test(volume-control-test volume-control-test) | 177 | add_test(volume-control-test volume-control-test) |
2905 | @@ -181,12 +183,12 @@ | |||
2906 | 181 | include_directories(${CMAKE_SOURCE_DIR}/src) | 183 | include_directories(${CMAKE_SOURCE_DIR}/src) |
2907 | 182 | add_executable (sound-menu-test sound-menu.cc) | 184 | add_executable (sound-menu-test sound-menu.cc) |
2908 | 183 | target_link_libraries ( | 185 | target_link_libraries ( |
2915 | 184 | sound-menu-test | 186 | sound-menu-test |
2916 | 185 | indicator-sound-service-lib | 187 | indicator-sound-service-lib |
2917 | 186 | vala-mocks-lib | 188 | vala-mocks-lib |
2918 | 187 | gtest | 189 | gtest-static |
2919 | 188 | ${SOUNDSERVICE_LIBRARIES} | 190 | ${SOUNDSERVICE_LIBRARIES} |
2920 | 189 | ${TEST_LIBRARIES} | 191 | ${TEST_LIBRARIES} |
2921 | 190 | ) | 192 | ) |
2922 | 191 | 193 | ||
2923 | 192 | add_test(sound-menu-test sound-menu-test) | 194 | add_test(sound-menu-test sound-menu-test) |
2924 | @@ -198,13 +200,13 @@ | |||
2925 | 198 | include_directories(${CMAKE_SOURCE_DIR}/src) | 200 | include_directories(${CMAKE_SOURCE_DIR}/src) |
2926 | 199 | add_executable (notifications-test notifications-test.cc) | 201 | add_executable (notifications-test notifications-test.cc) |
2927 | 200 | target_link_libraries ( | 202 | target_link_libraries ( |
2935 | 201 | notifications-test | 203 | notifications-test |
2936 | 202 | indicator-sound-service-lib | 204 | indicator-sound-service-lib |
2937 | 203 | vala-mocks-lib | 205 | vala-mocks-lib |
2938 | 204 | pulse-mock | 206 | pulse-mock |
2939 | 205 | gtest | 207 | gtest-static |
2940 | 206 | ${SOUNDSERVICE_LIBRARIES} | 208 | ${SOUNDSERVICE_LIBRARIES} |
2941 | 207 | ${TEST_LIBRARIES} | 209 | ${TEST_LIBRARIES} |
2942 | 208 | ) | 210 | ) |
2943 | 209 | 211 | ||
2944 | 210 | add_test(notifications-test notifications-test) | 212 | add_test(notifications-test notifications-test) |
2945 | @@ -216,23 +218,23 @@ | |||
2946 | 216 | include_directories(${CMAKE_SOURCE_DIR}/src) | 218 | include_directories(${CMAKE_SOURCE_DIR}/src) |
2947 | 217 | add_executable (media-player-user-test media-player-user.cc) | 219 | add_executable (media-player-user-test media-player-user.cc) |
2948 | 218 | target_link_libraries ( | 220 | target_link_libraries ( |
2955 | 219 | media-player-user-test | 221 | media-player-user-test |
2956 | 220 | indicator-sound-service-lib | 222 | indicator-sound-service-lib |
2957 | 221 | vala-mocks-lib | 223 | vala-mocks-lib |
2958 | 222 | gtest | 224 | gtest-static |
2959 | 223 | ${SOUNDSERVICE_LIBRARIES} | 225 | ${SOUNDSERVICE_LIBRARIES} |
2960 | 224 | ${TEST_LIBRARIES} | 226 | ${TEST_LIBRARIES} |
2961 | 225 | ) | 227 | ) |
2962 | 226 | 228 | ||
2963 | 227 | # Split tests to work around libaccountservice sucking | 229 | # Split tests to work around libaccountservice sucking |
2964 | 228 | add_test(media-player-user-test-basic | 230 | add_test(media-player-user-test-basic |
2966 | 229 | media-player-user-test --gtest_filter=MediaPlayerUserTest.BasicObject | 231 | media-player-user-test --gtest_filter=MediaPlayerUserTest.BasicObject |
2967 | 230 | ) | 232 | ) |
2968 | 231 | add_test(media-player-user-test-dataset | 233 | add_test(media-player-user-test-dataset |
2970 | 232 | media-player-user-test --gtest_filter=MediaPlayerUserTest.DataSet | 234 | media-player-user-test --gtest_filter=MediaPlayerUserTest.DataSet |
2971 | 233 | ) | 235 | ) |
2972 | 234 | add_test(media-player-user-test-timeout | 236 | add_test(media-player-user-test-timeout |
2974 | 235 | media-player-user-test --gtest_filter=MediaPlayerUserTest.TimeoutTest | 237 | media-player-user-test --gtest_filter=MediaPlayerUserTest.TimeoutTest |
2975 | 236 | ) | 238 | ) |
2976 | 237 | 239 | ||
2977 | 238 | ########################### | 240 | ########################### |
2978 | @@ -242,20 +244,20 @@ | |||
2979 | 242 | include_directories(${CMAKE_SOURCE_DIR}/src) | 244 | include_directories(${CMAKE_SOURCE_DIR}/src) |
2980 | 243 | add_executable (greeter-list-test greeter-list.cc) | 245 | add_executable (greeter-list-test greeter-list.cc) |
2981 | 244 | target_link_libraries ( | 246 | target_link_libraries ( |
2988 | 245 | greeter-list-test | 247 | greeter-list-test |
2989 | 246 | indicator-sound-service-lib | 248 | indicator-sound-service-lib |
2990 | 247 | vala-mocks-lib | 249 | vala-mocks-lib |
2991 | 248 | gtest | 250 | gtest-static |
2992 | 249 | ${SOUNDSERVICE_LIBRARIES} | 251 | ${SOUNDSERVICE_LIBRARIES} |
2993 | 250 | ${TEST_LIBRARIES} | 252 | ${TEST_LIBRARIES} |
2994 | 251 | ) | 253 | ) |
2995 | 252 | 254 | ||
2996 | 253 | # Split tests to work around libaccountservice sucking | 255 | # Split tests to work around libaccountservice sucking |
2997 | 254 | add_test(greeter-list-test-basic | 256 | add_test(greeter-list-test-basic |
2999 | 255 | greeter-list-test --gtest_filter=GreeterListTest.BasicObject | 257 | greeter-list-test --gtest_filter=GreeterListTest.BasicObject |
3000 | 256 | ) | 258 | ) |
3001 | 257 | add_test(greeter-list-test-iterator | 259 | add_test(greeter-list-test-iterator |
3003 | 258 | greeter-list-test --gtest_filter=GreeterListTest.BasicIterator | 260 | greeter-list-test --gtest_filter=GreeterListTest.BasicIterator |
3004 | 259 | ) | 261 | ) |
3005 | 260 | 262 | ||
3006 | 261 | ########################### | 263 | ########################### |
3007 | @@ -263,18 +265,22 @@ | |||
3008 | 263 | ########################### | 265 | ########################### |
3009 | 264 | 266 | ||
3010 | 265 | add_definitions( | 267 | add_definitions( |
3013 | 266 | -DINDICATOR_SOUND_SERVICE_BINARY="${CMAKE_BINARY_DIR}/src/indicator-sound-service" | 268 | -DINDICATOR_SOUND_SERVICE_BINARY="${CMAKE_BINARY_DIR}/src/indicator-sound-service" |
3014 | 267 | -DPA_MOCK_LIB="${CMAKE_CURRENT_BINARY_DIR}/libpulse-mock.so" | 269 | -DPA_MOCK_LIB="${CMAKE_CURRENT_BINARY_DIR}/libpulse-mock.so" |
3015 | 268 | ) | 270 | ) |
3016 | 269 | add_executable (indicator-test indicator-test.cc gschemas.compiled) | 271 | add_executable (indicator-test indicator-test.cc gschemas.compiled) |
3017 | 270 | target_link_libraries ( | 272 | target_link_libraries ( |
3022 | 271 | indicator-test | 273 | indicator-test |
3023 | 272 | gtest | 274 | gtest-static |
3024 | 273 | ${SOUNDSERVICE_LIBRARIES} | 275 | ${SOUNDSERVICE_LIBRARIES} |
3025 | 274 | ${TEST_LIBRARIES} | 276 | ${TEST_LIBRARIES} |
3026 | 275 | ) | 277 | ) |
3027 | 276 | 278 | ||
3028 | 277 | # Split tests to work around libaccountservice sucking | 279 | # Split tests to work around libaccountservice sucking |
3029 | 278 | add_test(indcator-test | 280 | add_test(indcator-test |
3031 | 279 | indicator-test | 281 | indicator-test |
3032 | 280 | ) | 282 | ) |
3033 | 283 | |||
3034 | 284 | add_subdirectory(integration) | ||
3035 | 285 | add_subdirectory(dbus-types) | ||
3036 | 286 | add_subdirectory(service-mocks) | ||
3037 | 281 | \ No newline at end of file | 287 | \ No newline at end of file |
3038 | 282 | 288 | ||
3039 | === added directory 'tests/dbus-types' | |||
3040 | === added file 'tests/dbus-types/CMakeLists.txt' | |||
3041 | --- tests/dbus-types/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
3042 | +++ tests/dbus-types/CMakeLists.txt 2015-10-28 09:41:22 +0000 | |||
3043 | @@ -0,0 +1,53 @@ | |||
3044 | 1 | set(CMAKE_AUTOMOC ON) | ||
3045 | 2 | set(CMAKE_INCLUDE_CURRENT_DIR ON) | ||
3046 | 3 | |||
3047 | 4 | find_package(Qt5DBus REQUIRED) | ||
3048 | 5 | include_directories(${Qt5DBus_INCLUDE_DIRS}) | ||
3049 | 6 | |||
3050 | 7 | add_definitions(-DQT_NO_KEYWORDS=1) | ||
3051 | 8 | |||
3052 | 9 | set(dbusinterface_streamrestore_xml "org.PulseAudio.Ext.StreamRestore1.xml") | ||
3053 | 10 | set_source_files_properties(${dbusinterface_streamrestore_xml} PROPERTIES | ||
3054 | 11 | CLASSNAME StreamRestoreInterface) | ||
3055 | 12 | |||
3056 | 13 | set(dbusinterface_accounts_xml "org.freedesktop.Accounts.xml") | ||
3057 | 14 | set_source_files_properties(${dbusinterface_accounts_xml} PROPERTIES | ||
3058 | 15 | CLASSNAME AccountsInterface) | ||
3059 | 16 | |||
3060 | 17 | set(dbusinterface_accountssound_xml "com.ubuntu.AccountsService.Sound.xml") | ||
3061 | 18 | set_source_files_properties(${dbusinterface_accountssound_xml} PROPERTIES | ||
3062 | 19 | CLASSNAME AccountsSoundInterface) | ||
3063 | 20 | |||
3064 | 21 | set(dbusinterface_properties_xml "org.freedesktop.DBus.Properties.xml") | ||
3065 | 22 | set_source_files_properties(${dbusinterface_properties_xml} PROPERTIES | ||
3066 | 23 | CLASSNAME DBusPropertiesInterface | ||
3067 | 24 | NO_NAMESPACE YES | ||
3068 | 25 | INCLUDE "dbus-types.h") | ||
3069 | 26 | |||
3070 | 27 | set(dbusinterface_actions_xml "org.gtk.Actions.xml") | ||
3071 | 28 | set_source_files_properties(${dbusinterface_actions_xml} PROPERTIES | ||
3072 | 29 | CLASSNAME MenusInterface) | ||
3073 | 30 | |||
3074 | 31 | set(dbusinterface_notifications_xml "org.freedesktop.Notifications.xml") | ||
3075 | 32 | set_source_files_properties(${dbusinterface_notifications_xml} PROPERTIES | ||
3076 | 33 | CLASSNAME NotificationsInterface) | ||
3077 | 34 | |||
3078 | 35 | qt5_add_dbus_interface(interface_files ${dbusinterface_streamrestore_xml} stream_restore_interface) | ||
3079 | 36 | qt5_add_dbus_interface(interface_files ${dbusinterface_properties_xml} dbus_properties_interface) | ||
3080 | 37 | qt5_add_dbus_interface(interface_files ${dbusinterface_accounts_xml} dbus_accounts_interface) | ||
3081 | 38 | qt5_add_dbus_interface(interface_files ${dbusinterface_accountssound_xml} dbus_accountssound_interface) | ||
3082 | 39 | qt5_add_dbus_interface(interface_files ${dbusinterface_actions_xml} dbus_menus_interface) | ||
3083 | 40 | qt5_add_dbus_interface(interface_files ${dbusinterface_notifications_xml} dbus_notifications_interface) | ||
3084 | 41 | |||
3085 | 42 | add_library( | ||
3086 | 43 | sound-indicator-dbus-interfaces | ||
3087 | 44 | STATIC | ||
3088 | 45 | ${interface_files} | ||
3089 | 46 | pulseaudio-volume.cpp | ||
3090 | 47 | ) | ||
3091 | 48 | |||
3092 | 49 | qt5_use_modules( | ||
3093 | 50 | sound-indicator-dbus-interfaces | ||
3094 | 51 | Core | ||
3095 | 52 | DBus | ||
3096 | 53 | ) | ||
3097 | 0 | 54 | ||
3098 | === added file 'tests/dbus-types/com.ubuntu.AccountsService.Sound.xml' | |||
3099 | --- tests/dbus-types/com.ubuntu.AccountsService.Sound.xml 1970-01-01 00:00:00 +0000 | |||
3100 | +++ tests/dbus-types/com.ubuntu.AccountsService.Sound.xml 2015-10-28 09:41:22 +0000 | |||
3101 | @@ -0,0 +1,9 @@ | |||
3102 | 1 | <node> | ||
3103 | 2 | <interface name="com.ubuntu.AccountsService.Sound"> | ||
3104 | 3 | <method name="Set"> | ||
3105 | 4 | <arg direction="in" type="s" name="interface" /> | ||
3106 | 5 | <arg direction="in" type="s" name="property" /> | ||
3107 | 6 | <arg direction="out" type="o" name="path" /> | ||
3108 | 7 | </method> | ||
3109 | 8 | </interface> | ||
3110 | 9 | </node> | ||
3111 | 0 | 10 | ||
3112 | === added file 'tests/dbus-types/dbus-types.h' | |||
3113 | --- tests/dbus-types/dbus-types.h 1970-01-01 00:00:00 +0000 | |||
3114 | +++ tests/dbus-types/dbus-types.h 2015-10-28 09:41:22 +0000 | |||
3115 | @@ -0,0 +1,48 @@ | |||
3116 | 1 | /* | ||
3117 | 2 | * Copyright (C) 2015 Canonical, Ltd. | ||
3118 | 3 | * | ||
3119 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3120 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
3121 | 6 | * by the Free Software Foundation. | ||
3122 | 7 | * | ||
3123 | 8 | * This program is distributed in the hope that it will be useful, but | ||
3124 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
3125 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
3126 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
3127 | 12 | * | ||
3128 | 13 | * You should have received a copy of the GNU General Public License along | ||
3129 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3130 | 15 | * | ||
3131 | 16 | * Author: Xavi Garcia <xavi.garcia.mena@canonical.com> | ||
3132 | 17 | */ | ||
3133 | 18 | #pragma once | ||
3134 | 19 | |||
3135 | 20 | #include <QDBusMetaType> | ||
3136 | 21 | #include "pulseaudio-volume.h" | ||
3137 | 22 | |||
3138 | 23 | namespace DBusTypes | ||
3139 | 24 | { | ||
3140 | 25 | inline void registerMetaTypes() | ||
3141 | 26 | { | ||
3142 | 27 | PulseaudioVolume::registerMetaType(); | ||
3143 | 28 | PulseaudioVolumeArray::registerMetaType(); | ||
3144 | 29 | } | ||
3145 | 30 | |||
3146 | 31 | static constexpr char const* DBUS_NAME = "com.canonical.indicator.sound"; | ||
3147 | 32 | |||
3148 | 33 | static constexpr char const* DBUS_PULSE = "org.PulseAudio1"; | ||
3149 | 34 | |||
3150 | 35 | static constexpr char const* ACCOUNTS_SERVICE = "org.freedesktop.Accounts"; | ||
3151 | 36 | |||
3152 | 37 | static constexpr char const* STREAM_RESTORE_NAME = "org.PulseAudio.Ext.StreamRestore1"; | ||
3153 | 38 | |||
3154 | 39 | static constexpr char const* STREAM_RESTORE_PATH = "/org/pulseaudio/stream_restore1"; | ||
3155 | 40 | |||
3156 | 41 | static constexpr char const* STREAM_RESTORE_ENTRY_NAME = "org.PulseAudio.Ext.StreamRestore1.RestoreEntry"; | ||
3157 | 42 | |||
3158 | 43 | static constexpr char const* MAIN_SERVICE_PATH = "/com/canonical/indicator/sound"; | ||
3159 | 44 | |||
3160 | 45 | static constexpr char const* ACTIONS_INTERFACE = "org.gtk.Actions"; | ||
3161 | 46 | |||
3162 | 47 | } // namespace DBusTypes | ||
3163 | 48 | |||
3164 | 0 | 49 | ||
3165 | === added file 'tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml' | |||
3166 | --- tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml 1970-01-01 00:00:00 +0000 | |||
3167 | +++ tests/dbus-types/org.PulseAudio.Ext.StreamRestore1.xml 2015-10-28 09:41:22 +0000 | |||
3168 | @@ -0,0 +1,7 @@ | |||
3169 | 1 | <node> | ||
3170 | 2 | <interface name="org.PulseAudio.Ext.StreamRestore1"> | ||
3171 | 3 | <method name="GetEntryByName"> | ||
3172 | 4 | <arg direction="in" type="s" name="entry" /> | ||
3173 | 5 | <arg direction="out" type="o" name="value" /> | ||
3174 | 6 | </interface> | ||
3175 | 7 | </node> | ||
3176 | 0 | 8 | ||
3177 | === added file 'tests/dbus-types/org.freedesktop.Accounts.xml' | |||
3178 | --- tests/dbus-types/org.freedesktop.Accounts.xml 1970-01-01 00:00:00 +0000 | |||
3179 | +++ tests/dbus-types/org.freedesktop.Accounts.xml 2015-10-28 09:41:22 +0000 | |||
3180 | @@ -0,0 +1,8 @@ | |||
3181 | 1 | <node> | ||
3182 | 2 | <interface name="org.freedesktop.Accounts"> | ||
3183 | 3 | <method name="FindUserByName"> | ||
3184 | 4 | <arg direction="in" type="s" name="user" /> | ||
3185 | 5 | <arg direction="out" type="o" name="path" /> | ||
3186 | 6 | </method> | ||
3187 | 7 | </interface> | ||
3188 | 8 | </node> | ||
3189 | 0 | 9 | ||
3190 | === added file 'tests/dbus-types/org.freedesktop.DBus.Properties.xml' | |||
3191 | --- tests/dbus-types/org.freedesktop.DBus.Properties.xml 1970-01-01 00:00:00 +0000 | |||
3192 | +++ tests/dbus-types/org.freedesktop.DBus.Properties.xml 2015-10-28 09:41:22 +0000 | |||
3193 | @@ -0,0 +1,22 @@ | |||
3194 | 1 | <node> | ||
3195 | 2 | <interface name="org.freedesktop.DBus.Properties"> | ||
3196 | 3 | <method name="Set"> | ||
3197 | 4 | <arg direction="in" type="s" name="entry" /> | ||
3198 | 5 | <arg direction="in" type="s" name="property" /> | ||
3199 | 6 | <arg direction="in" type="v" name="value" /> | ||
3200 | 7 | </method> | ||
3201 | 8 | |||
3202 | 9 | <method name="Get"> | ||
3203 | 10 | <arg direction="in" type="s" name="entry" /> | ||
3204 | 11 | <arg direction="in" type="s" name="property" /> | ||
3205 | 12 | <arg direction="out" type="v" name="value" /> | ||
3206 | 13 | </method> | ||
3207 | 14 | |||
3208 | 15 | <signal name="PropertiesChanged"> | ||
3209 | 16 | <arg type="s" name="interface_name"/> | ||
3210 | 17 | <arg type="a{sv}" name="changed_properties"/> | ||
3211 | 18 | <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/> | ||
3212 | 19 | <arg type="as" name="invalidated_properties"/> | ||
3213 | 20 | </signal> | ||
3214 | 21 | </interface> | ||
3215 | 22 | </node> | ||
3216 | 0 | 23 | ||
3217 | === added file 'tests/dbus-types/org.freedesktop.Notifications.xml' | |||
3218 | --- tests/dbus-types/org.freedesktop.Notifications.xml 1970-01-01 00:00:00 +0000 | |||
3219 | +++ tests/dbus-types/org.freedesktop.Notifications.xml 2015-10-28 09:41:22 +0000 | |||
3220 | @@ -0,0 +1,47 @@ | |||
3221 | 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
3222 | 2 | "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
3223 | 3 | <node> | ||
3224 | 4 | <interface name="org.freedesktop.Notifications"> | ||
3225 | 5 | <signal name="NotificationClosed"> | ||
3226 | 6 | <arg name="id" type="u" direction="out"/> | ||
3227 | 7 | <arg name="reason" type="u" direction="out"/> | ||
3228 | 8 | </signal> | ||
3229 | 9 | <signal name="ActionInvoked"> | ||
3230 | 10 | <arg name="id" type="u" direction="out"/> | ||
3231 | 11 | <arg name="action_key" type="s" direction="out"/> | ||
3232 | 12 | </signal> | ||
3233 | 13 | <signal name="dataChanged"> | ||
3234 | 14 | <arg name="id" type="u" direction="out"/> | ||
3235 | 15 | </signal> | ||
3236 | 16 | <method name="CloseNotification"> | ||
3237 | 17 | <arg name="id" type="u" direction="in"/> | ||
3238 | 18 | </method> | ||
3239 | 19 | <method name="GetServerInformation"> | ||
3240 | 20 | <arg name="name" type="s" direction="out"/> | ||
3241 | 21 | <arg name="vendor" type="s" direction="out"/> | ||
3242 | 22 | <arg name="version" type="s" direction="out"/> | ||
3243 | 23 | <arg name="specVersion" type="s" direction="out"/> | ||
3244 | 24 | </method> | ||
3245 | 25 | <method name="GetCapabilities"> | ||
3246 | 26 | <arg type="as" direction="out"/> | ||
3247 | 27 | </method> | ||
3248 | 28 | <method name="Notify"> | ||
3249 | 29 | <arg type="u" direction="out"/> | ||
3250 | 30 | <arg name="app_name" type="s" direction="in"/> | ||
3251 | 31 | <arg name="replaces_id" type="u" direction="in"/> | ||
3252 | 32 | <arg name="app_icon" type="s" direction="in"/> | ||
3253 | 33 | <arg name="summary" type="s" direction="in"/> | ||
3254 | 34 | <arg name="body" type="s" direction="in"/> | ||
3255 | 35 | <arg name="actions" type="as" direction="in"/> | ||
3256 | 36 | <arg name="hints" type="a{sv}" direction="in"/> | ||
3257 | 37 | <annotation name="org.qtproject.QtDBus.QtTypeName.In6" value="QVariantMap"/> | ||
3258 | 38 | <arg name="expire_timeout" type="i" direction="in"/> | ||
3259 | 39 | </method> | ||
3260 | 40 | <method name="onDataChanged"> | ||
3261 | 41 | <arg name="id" type="u" direction="in"/> | ||
3262 | 42 | </method> | ||
3263 | 43 | <method name="onCompleted"> | ||
3264 | 44 | <arg name="id" type="u" direction="in"/> | ||
3265 | 45 | </method> | ||
3266 | 46 | </interface> | ||
3267 | 47 | </node> | ||
3268 | 0 | 48 | ||
3269 | === added file 'tests/dbus-types/org.gtk.Actions.xml' | |||
3270 | --- tests/dbus-types/org.gtk.Actions.xml 1970-01-01 00:00:00 +0000 | |||
3271 | +++ tests/dbus-types/org.gtk.Actions.xml 2015-10-28 09:41:22 +0000 | |||
3272 | @@ -0,0 +1,13 @@ | |||
3273 | 1 | <node> | ||
3274 | 2 | <interface name="org.gtk.Actions"> | ||
3275 | 3 | <signal name="Changed"> | ||
3276 | 4 | <arg name="removedActions" type="as" direction="in" /> | ||
3277 | 5 | <arg name="actionsFlags" type="a{sb}" direction="in" /> | ||
3278 | 6 | <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/> | ||
3279 | 7 | <arg name="actionsChanged" type="a{sv}" direction="in" /> | ||
3280 | 8 | <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/> | ||
3281 | 9 | <arg name="actionsAdded" type="a{s(bgav)}" direction="in" /> | ||
3282 | 10 | <annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QVariantMap"/> | ||
3283 | 11 | </signal> | ||
3284 | 12 | </interface> | ||
3285 | 13 | </node> | ||
3286 | 0 | 14 | ||
3287 | === added file 'tests/dbus-types/pulseaudio-volume.cpp' | |||
3288 | --- tests/dbus-types/pulseaudio-volume.cpp 1970-01-01 00:00:00 +0000 | |||
3289 | +++ tests/dbus-types/pulseaudio-volume.cpp 2015-10-28 09:41:22 +0000 | |||
3290 | @@ -0,0 +1,156 @@ | |||
3291 | 1 | /* | ||
3292 | 2 | * Copyright (C) 2015 Canonical, Ltd. | ||
3293 | 3 | * | ||
3294 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3295 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
3296 | 6 | * by the Free Software Foundation. | ||
3297 | 7 | * | ||
3298 | 8 | * This program is distributed in the hope that it will be useful, but | ||
3299 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
3300 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
3301 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
3302 | 12 | * | ||
3303 | 13 | * You should have received a copy of the GNU General Public License along | ||
3304 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3305 | 15 | * | ||
3306 | 16 | * Author: Xavi Garcia <xavi.garcia.mena@canonical.com> | ||
3307 | 17 | */ | ||
3308 | 18 | #include "dbus-types.h" | ||
3309 | 19 | |||
3310 | 20 | PulseaudioVolume::PulseaudioVolume() : | ||
3311 | 21 | type_(0), | ||
3312 | 22 | volume_(10) | ||
3313 | 23 | { | ||
3314 | 24 | } | ||
3315 | 25 | |||
3316 | 26 | PulseaudioVolume::PulseaudioVolume(unsigned int type, unsigned int volume) : | ||
3317 | 27 | type_(type) | ||
3318 | 28 | , volume_(volume) | ||
3319 | 29 | { | ||
3320 | 30 | } | ||
3321 | 31 | |||
3322 | 32 | PulseaudioVolume::PulseaudioVolume(const PulseaudioVolume &other) : | ||
3323 | 33 | type_(other.type_), | ||
3324 | 34 | volume_(other.volume_) | ||
3325 | 35 | { | ||
3326 | 36 | } | ||
3327 | 37 | |||
3328 | 38 | PulseaudioVolume& PulseaudioVolume::operator=(const PulseaudioVolume &other) | ||
3329 | 39 | { | ||
3330 | 40 | type_ = other.type_; | ||
3331 | 41 | volume_ = other.volume_; | ||
3332 | 42 | |||
3333 | 43 | return *this; | ||
3334 | 44 | } | ||
3335 | 45 | |||
3336 | 46 | PulseaudioVolume::~PulseaudioVolume() | ||
3337 | 47 | { | ||
3338 | 48 | } | ||
3339 | 49 | |||
3340 | 50 | unsigned int PulseaudioVolume::getType() const | ||
3341 | 51 | { | ||
3342 | 52 | return type_; | ||
3343 | 53 | } | ||
3344 | 54 | |||
3345 | 55 | unsigned int PulseaudioVolume::getVolume() const | ||
3346 | 56 | { | ||
3347 | 57 | return volume_; | ||
3348 | 58 | } | ||
3349 | 59 | |||
3350 | 60 | void PulseaudioVolume::registerMetaType() | ||
3351 | 61 | { | ||
3352 | 62 | qRegisterMetaType<PulseaudioVolume>("PulseaudioVolume"); | ||
3353 | 63 | |||
3354 | 64 | qDBusRegisterMetaType<PulseaudioVolume>(); | ||
3355 | 65 | } | ||
3356 | 66 | |||
3357 | 67 | QDBusArgument &operator<<(QDBusArgument &argument, const PulseaudioVolume& volume) | ||
3358 | 68 | { | ||
3359 | 69 | argument.beginStructure(); | ||
3360 | 70 | argument << volume.type_; | ||
3361 | 71 | argument << volume.volume_; | ||
3362 | 72 | argument.endStructure(); | ||
3363 | 73 | |||
3364 | 74 | return argument; | ||
3365 | 75 | } | ||
3366 | 76 | |||
3367 | 77 | const QDBusArgument &operator>>(const QDBusArgument &argument, PulseaudioVolume &volume) | ||
3368 | 78 | { | ||
3369 | 79 | argument.beginStructure(); | ||
3370 | 80 | argument >> volume.type_; | ||
3371 | 81 | argument >> volume.volume_; | ||
3372 | 82 | argument.endStructure(); | ||
3373 | 83 | |||
3374 | 84 | return argument; | ||
3375 | 85 | } | ||
3376 | 86 | |||
3377 | 87 | PulseaudioVolumeArray::PulseaudioVolumeArray() | ||
3378 | 88 | { | ||
3379 | 89 | } | ||
3380 | 90 | |||
3381 | 91 | PulseaudioVolumeArray::PulseaudioVolumeArray(const PulseaudioVolumeArray &other) : | ||
3382 | 92 | volume_array_(other.volume_array_) | ||
3383 | 93 | { | ||
3384 | 94 | } | ||
3385 | 95 | |||
3386 | 96 | PulseaudioVolumeArray& PulseaudioVolumeArray::operator=(const PulseaudioVolumeArray &other) | ||
3387 | 97 | { | ||
3388 | 98 | volume_array_ = other.volume_array_; | ||
3389 | 99 | |||
3390 | 100 | return *this; | ||
3391 | 101 | } | ||
3392 | 102 | |||
3393 | 103 | PulseaudioVolumeArray::~PulseaudioVolumeArray() | ||
3394 | 104 | { | ||
3395 | 105 | } | ||
3396 | 106 | |||
3397 | 107 | int PulseaudioVolumeArray::getNumItems() const | ||
3398 | 108 | { | ||
3399 | 109 | return volume_array_.size(); | ||
3400 | 110 | } | ||
3401 | 111 | |||
3402 | 112 | PulseaudioVolume PulseaudioVolumeArray::getItem(int i) const | ||
3403 | 113 | { | ||
3404 | 114 | if (i < volume_array_.size()) | ||
3405 | 115 | { | ||
3406 | 116 | return volume_array_[i]; | ||
3407 | 117 | } | ||
3408 | 118 | return PulseaudioVolume(); | ||
3409 | 119 | } | ||
3410 | 120 | |||
3411 | 121 | void PulseaudioVolumeArray::addItem(PulseaudioVolume const &item) | ||
3412 | 122 | { | ||
3413 | 123 | volume_array_.push_back(item); | ||
3414 | 124 | } | ||
3415 | 125 | |||
3416 | 126 | void PulseaudioVolumeArray::registerMetaType() | ||
3417 | 127 | { | ||
3418 | 128 | qRegisterMetaType<PulseaudioVolumeArray>("PulseaudioVolumeArray"); | ||
3419 | 129 | |||
3420 | 130 | qDBusRegisterMetaType<PulseaudioVolumeArray>(); | ||
3421 | 131 | } | ||
3422 | 132 | |||
3423 | 133 | QDBusArgument &operator<<(QDBusArgument &argument, const PulseaudioVolumeArray& volume) | ||
3424 | 134 | { | ||
3425 | 135 | argument.beginArray( qMetaTypeId<PulseaudioVolume>() ); | ||
3426 | 136 | for (int i = 0; i < volume.volume_array_.size(); ++ i) | ||
3427 | 137 | { | ||
3428 | 138 | PulseaudioVolume item = volume.getItem(i); | ||
3429 | 139 | argument << item; | ||
3430 | 140 | } | ||
3431 | 141 | argument.endArray(); | ||
3432 | 142 | return argument; | ||
3433 | 143 | } | ||
3434 | 144 | |||
3435 | 145 | const QDBusArgument &operator>>(const QDBusArgument &argument, PulseaudioVolumeArray &volume) | ||
3436 | 146 | { | ||
3437 | 147 | argument.beginArray(); | ||
3438 | 148 | while ( !argument.atEnd() ) { | ||
3439 | 149 | PulseaudioVolume item; | ||
3440 | 150 | argument >> item; | ||
3441 | 151 | volume.volume_array_.push_back(item); | ||
3442 | 152 | } | ||
3443 | 153 | argument.endArray(); | ||
3444 | 154 | |||
3445 | 155 | return argument; | ||
3446 | 156 | } | ||
3447 | 0 | 157 | ||
3448 | === added file 'tests/dbus-types/pulseaudio-volume.h' | |||
3449 | --- tests/dbus-types/pulseaudio-volume.h 1970-01-01 00:00:00 +0000 | |||
3450 | +++ tests/dbus-types/pulseaudio-volume.h 2015-10-28 09:41:22 +0000 | |||
3451 | @@ -0,0 +1,69 @@ | |||
3452 | 1 | /* | ||
3453 | 2 | * Copyright (C) 2015 Canonical, Ltd. | ||
3454 | 3 | * | ||
3455 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3456 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
3457 | 6 | * by the Free Software Foundation. | ||
3458 | 7 | * | ||
3459 | 8 | * This program is distributed in the hope that it will be useful, but | ||
3460 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
3461 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
3462 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
3463 | 12 | * | ||
3464 | 13 | * You should have received a copy of the GNU General Public License along | ||
3465 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3466 | 15 | * | ||
3467 | 16 | * Author: Xavi Garcia <xavi.garcia.mena@canonical.com> | ||
3468 | 17 | */ | ||
3469 | 18 | #pragma once | ||
3470 | 19 | |||
3471 | 20 | #include <QtDBus> | ||
3472 | 21 | |||
3473 | 22 | class PulseaudioVolume | ||
3474 | 23 | { | ||
3475 | 24 | public: | ||
3476 | 25 | PulseaudioVolume(); | ||
3477 | 26 | PulseaudioVolume(unsigned int type, unsigned int volume); | ||
3478 | 27 | PulseaudioVolume(const PulseaudioVolume &other); | ||
3479 | 28 | PulseaudioVolume& operator=(const PulseaudioVolume &other); | ||
3480 | 29 | ~PulseaudioVolume(); | ||
3481 | 30 | |||
3482 | 31 | friend QDBusArgument &operator<<(QDBusArgument &argument, PulseaudioVolume const & volume); | ||
3483 | 32 | friend const QDBusArgument &operator>>(QDBusArgument const & argument, PulseaudioVolume &volume); | ||
3484 | 33 | |||
3485 | 34 | unsigned int getType() const; | ||
3486 | 35 | unsigned int getVolume() const; | ||
3487 | 36 | |||
3488 | 37 | //register Message with the Qt type system | ||
3489 | 38 | static void registerMetaType(); | ||
3490 | 39 | |||
3491 | 40 | private: | ||
3492 | 41 | unsigned int type_; | ||
3493 | 42 | unsigned int volume_; | ||
3494 | 43 | }; | ||
3495 | 44 | |||
3496 | 45 | class PulseaudioVolumeArray | ||
3497 | 46 | { | ||
3498 | 47 | public: | ||
3499 | 48 | PulseaudioVolumeArray(); | ||
3500 | 49 | PulseaudioVolumeArray(QString const &interface, QString const &property, QDBusVariant const& value); | ||
3501 | 50 | PulseaudioVolumeArray(const PulseaudioVolumeArray &other); | ||
3502 | 51 | PulseaudioVolumeArray& operator=(const PulseaudioVolumeArray &other); | ||
3503 | 52 | ~PulseaudioVolumeArray(); | ||
3504 | 53 | |||
3505 | 54 | friend QDBusArgument &operator<<(QDBusArgument &argument, PulseaudioVolumeArray const & volume); | ||
3506 | 55 | friend const QDBusArgument &operator>>(QDBusArgument const & argument, PulseaudioVolumeArray &volume); | ||
3507 | 56 | |||
3508 | 57 | int getNumItems() const; | ||
3509 | 58 | PulseaudioVolume getItem(int i) const; | ||
3510 | 59 | void addItem(PulseaudioVolume const &item); | ||
3511 | 60 | |||
3512 | 61 | //register Message with the Qt type system | ||
3513 | 62 | static void registerMetaType(); | ||
3514 | 63 | |||
3515 | 64 | private: | ||
3516 | 65 | QVector<PulseaudioVolume> volume_array_; | ||
3517 | 66 | }; | ||
3518 | 67 | |||
3519 | 68 | Q_DECLARE_METATYPE(PulseaudioVolume) | ||
3520 | 69 | Q_DECLARE_METATYPE(PulseaudioVolumeArray) | ||
3521 | 0 | 70 | ||
3522 | === added directory 'tests/integration' | |||
3523 | === added file 'tests/integration/CMakeLists.txt' | |||
3524 | --- tests/integration/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
3525 | +++ tests/integration/CMakeLists.txt 2015-10-28 09:41:22 +0000 | |||
3526 | @@ -0,0 +1,132 @@ | |||
3527 | 1 | set(CMAKE_AUTOMOC ON) | ||
3528 | 2 | set(CMAKE_INCLUDE_CURRENT_DIR ON) | ||
3529 | 3 | |||
3530 | 4 | include(FindGMock) | ||
3531 | 5 | |||
3532 | 6 | #pkg_check_modules(GMENUHARNESS REQUIRED libgmenuharness REQUIRED) | ||
3533 | 7 | #include_directories(${GMENUHARNESS_INCLUDE_DIRS}) | ||
3534 | 8 | include_directories("${CMAKE_SOURCE_DIR}/include") | ||
3535 | 9 | |||
3536 | 10 | pkg_check_modules(QTDBUSTEST REQUIRED libqtdbustest-1 REQUIRED) | ||
3537 | 11 | include_directories(${QTDBUSTEST_INCLUDE_DIRS}) | ||
3538 | 12 | |||
3539 | 13 | pkg_check_modules(QTDBUSMOCK REQUIRED libqtdbusmock-1 REQUIRED) | ||
3540 | 14 | include_directories(${QTDBUSMOCK_INCLUDE_DIRS}) | ||
3541 | 15 | |||
3542 | 16 | find_package(Qt5Test REQUIRED) | ||
3543 | 17 | include_directories(${Qt5Test_INCLUDE_DIRS}) | ||
3544 | 18 | |||
3545 | 19 | find_package(Qt5DBus REQUIRED) | ||
3546 | 20 | include_directories(${Qt5DBus_INCLUDE_DIRS}) | ||
3547 | 21 | |||
3548 | 22 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) | ||
3549 | 23 | include_directories(${GMOCK_INCLUDE_DIRS}) | ||
3550 | 24 | include_directories(${GTEST_INCLUDE_DIRS}) | ||
3551 | 25 | |||
3552 | 26 | include_directories("${CMAKE_SOURCE_DIR}/tests/dbus-types") | ||
3553 | 27 | include_directories("${CMAKE_BINARY_DIR}/tests/dbus-types") | ||
3554 | 28 | |||
3555 | 29 | add_definitions(-DSOUND_SERVICE_BIN="${CMAKE_BINARY_DIR}/src/indicator-sound-service" | ||
3556 | 30 | -DSTREAM_RESTORE_TABLE="${CMAKE_SOURCE_DIR}/tests/integration/touch-stream-restore.table" | ||
3557 | 31 | -DVOLUME_SET_BIN="${CMAKE_BINARY_DIR}/tests/integration/set-volume" | ||
3558 | 32 | -DACCOUNTS_SERVICE_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/accounts-mock/accounts-service-sound" | ||
3559 | 33 | -DMEDIA_PLAYER_MPRIS_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/media-player-mpris-mock/media-player-mpris-mock" | ||
3560 | 34 | -DMEDIA_PLAYER_MPRIS_UPDATE_BIN="${CMAKE_BINARY_DIR}/tests/service-mocks/media-player-mpris-mock/media-player-mpris-mock-update" | ||
3561 | 35 | -DTEST_SOUND="${CMAKE_SOURCE_DIR}/tests/integration/test-sound.wav" | ||
3562 | 36 | -DQT_NO_KEYWORDS=1 | ||
3563 | 37 | -DXDG_DATA_DIRS="${XDG_DATA_DIRS}" | ||
3564 | 38 | ) | ||
3565 | 39 | |||
3566 | 40 | set(GLIB_REQUIRED_VERSION 2.26) | ||
3567 | 41 | |||
3568 | 42 | pkg_check_modules( | ||
3569 | 43 | GLIB REQUIRED | ||
3570 | 44 | glib-2.0>=${GLIB_REQUIRED_VERSION} | ||
3571 | 45 | gio-2.0>=${GLIB_REQUIRED_VERSION} | ||
3572 | 46 | ) | ||
3573 | 47 | include_directories(${GLIB_INCLUDE_DIRS}) | ||
3574 | 48 | |||
3575 | 49 | set( | ||
3576 | 50 | INTEGRATION_TESTS_SRC | ||
3577 | 51 | indicator-sound-test-base.cpp | ||
3578 | 52 | test-indicator.cpp | ||
3579 | 53 | utils/dbus-pulse-volume.cpp | ||
3580 | 54 | main.cpp | ||
3581 | 55 | ) | ||
3582 | 56 | |||
3583 | 57 | add_executable( | ||
3584 | 58 | integration-tests | ||
3585 | 59 | ${INTEGRATION_TESTS_SRC} | ||
3586 | 60 | ) | ||
3587 | 61 | |||
3588 | 62 | qt5_use_modules( | ||
3589 | 63 | integration-tests | ||
3590 | 64 | Core | ||
3591 | 65 | DBus | ||
3592 | 66 | Test | ||
3593 | 67 | ) | ||
3594 | 68 | |||
3595 | 69 | target_link_libraries( | ||
3596 | 70 | integration-tests | ||
3597 | 71 | sound-indicator-dbus-interfaces | ||
3598 | 72 | ${QTDBUSMOCK_LDFLAGS} | ||
3599 | 73 | ${QTDBUSTEST_LDFLAGS} | ||
3600 | 74 | ${GTEST_LIBRARIES} | ||
3601 | 75 | ${GMOCK_LIBRARIES} | ||
3602 | 76 | # ${GMENUHARNESS_LDFLAGS} | ||
3603 | 77 | ${GLIB_LDFLAGS} | ||
3604 | 78 | gmenuharness-shared | ||
3605 | 79 | ) | ||
3606 | 80 | |||
3607 | 81 | add_test( | ||
3608 | 82 | integration-tests | ||
3609 | 83 | integration-tests | ||
3610 | 84 | ) | ||
3611 | 85 | |||
3612 | 86 | set( | ||
3613 | 87 | SET-VOLUME-SRC | ||
3614 | 88 | utils/dbus-pulse-volume.cpp | ||
3615 | 89 | utils/set-volume.cpp | ||
3616 | 90 | ) | ||
3617 | 91 | |||
3618 | 92 | set( | ||
3619 | 93 | GET-VOLUME-SRC | ||
3620 | 94 | utils/dbus-pulse-volume.cpp | ||
3621 | 95 | utils/get-volume.cpp | ||
3622 | 96 | ) | ||
3623 | 97 | |||
3624 | 98 | add_executable( | ||
3625 | 99 | set-volume | ||
3626 | 100 | ${SET-VOLUME-SRC} | ||
3627 | 101 | ) | ||
3628 | 102 | |||
3629 | 103 | add_executable( | ||
3630 | 104 | get-volume | ||
3631 | 105 | ${GET-VOLUME-SRC} | ||
3632 | 106 | ) | ||
3633 | 107 | |||
3634 | 108 | qt5_use_modules( | ||
3635 | 109 | set-volume | ||
3636 | 110 | Core | ||
3637 | 111 | DBus | ||
3638 | 112 | Test | ||
3639 | 113 | ) | ||
3640 | 114 | |||
3641 | 115 | qt5_use_modules( | ||
3642 | 116 | get-volume | ||
3643 | 117 | Core | ||
3644 | 118 | DBus | ||
3645 | 119 | Test | ||
3646 | 120 | ) | ||
3647 | 121 | |||
3648 | 122 | target_link_libraries( | ||
3649 | 123 | get-volume | ||
3650 | 124 | sound-indicator-dbus-interfaces | ||
3651 | 125 | ) | ||
3652 | 126 | |||
3653 | 127 | target_link_libraries( | ||
3654 | 128 | set-volume | ||
3655 | 129 | sound-indicator-dbus-interfaces | ||
3656 | 130 | ) | ||
3657 | 131 | |||
3658 | 132 | #add_subdirectory(utils) | ||
3659 | 0 | \ No newline at end of file | 133 | \ No newline at end of file |
3660 | 1 | 134 | ||
3661 | === added file 'tests/integration/Copy of test-sound.wav' | |||
3662 | 2 | Binary files tests/integration/Copy of test-sound.wav 1970-01-01 00:00:00 +0000 and tests/integration/Copy of test-sound.wav 2015-10-28 09:41:22 +0000 differ | 135 | Binary files tests/integration/Copy of test-sound.wav 1970-01-01 00:00:00 +0000 and tests/integration/Copy of test-sound.wav 2015-10-28 09:41:22 +0000 differ |
3663 | === added file 'tests/integration/indicator-sound-test-base.cpp' | |||
3664 | --- tests/integration/indicator-sound-test-base.cpp 1970-01-01 00:00:00 +0000 | |||
3665 | +++ tests/integration/indicator-sound-test-base.cpp 2015-10-28 09:41:22 +0000 | |||
3666 | @@ -0,0 +1,807 @@ | |||
3667 | 1 | /* | ||
3668 | 2 | * Copyright (C) 2015 Canonical, Ltd. | ||
3669 | 3 | * | ||
3670 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3671 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
3672 | 6 | * by the Free Software Foundation. | ||
3673 | 7 | * | ||
3674 | 8 | * This program is distributed in the hope that it will be useful, but | ||
3675 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
3676 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
3677 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
3678 | 12 | * | ||
3679 | 13 | * You should have received a copy of the GNU General Public License along | ||
3680 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3681 | 15 | * | ||
3682 | 16 | * Author: Xavi Garcia <xavi.garcia.mena@canonical.com> | ||
3683 | 17 | */ | ||
3684 | 18 | |||
3685 | 19 | #include "indicator-sound-test-base.h" | ||
3686 | 20 | |||
3687 | 21 | #include "dbus_menus_interface.h" | ||
3688 | 22 | #include "dbus_properties_interface.h" | ||
3689 | 23 | #include "dbus_accounts_interface.h" | ||
3690 | 24 | #include "dbus_accountssound_interface.h" | ||
3691 | 25 | #include "dbus_notifications_interface.h" | ||
3692 | 26 | #include "dbus-types.h" | ||
3693 | 27 | |||
3694 | 28 | #include <gio/gio.h> | ||
3695 | 29 | #include <chrono> | ||
3696 | 30 | #include <thread> | ||
3697 | 31 | |||
3698 | 32 | #include <QSignalSpy> | ||
3699 | 33 | #include "utils/dbus-pulse-volume.h" | ||
3700 | 34 | |||
3701 | 35 | using namespace QtDBusTest; | ||
3702 | 36 | using namespace QtDBusMock; | ||
3703 | 37 | using namespace std; | ||
3704 | 38 | using namespace testing; | ||
3705 | 39 | namespace mh = unity::gmenuharness; | ||
3706 | 40 | |||
3707 | 41 | namespace | ||
3708 | 42 | { | ||
3709 | 43 | const int MAX_TIME_WAITING_FOR_NOTIFICATIONS = 2000; | ||
3710 | 44 | } | ||
3711 | 45 | |||
3712 | 46 | IndicatorSoundTestBase::IndicatorSoundTestBase() : | ||
3713 | 47 | dbusMock(dbusTestRunner) | ||
3714 | 48 | { | ||
3715 | 49 | } | ||
3716 | 50 | |||
3717 | 51 | IndicatorSoundTestBase::~IndicatorSoundTestBase() | ||
3718 | 52 | { | ||
3719 | 53 | } | ||
3720 | 54 | |||
3721 | 55 | void IndicatorSoundTestBase::SetUp() | ||
3722 | 56 | { | ||
3723 | 57 | setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, true); | ||
3724 | 58 | setenv("DBUS_SYSTEM_BUS_ADDRESS", dbusTestRunner.systemBus().toStdString().c_str(), true); | ||
3725 | 59 | setenv("DBUS_SESSION_BUS_ADDRESS", dbusTestRunner.sessionBus().toStdString().c_str(), true); | ||
3726 | 60 | dbusMock.registerNotificationDaemon(); | ||
3727 | 61 | |||
3728 | 62 | dbusTestRunner.startServices(); | ||
3729 | 63 | |||
3730 | 64 | auto& notifications = notificationsMockInterface(); | ||
3731 | 65 | notifications.AddMethod("org.freedesktop.Notifications", | ||
3732 | 66 | "GetCapabilities", | ||
3733 | 67 | "", | ||
3734 | 68 | "as", | ||
3735 | 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']" | ||
3736 | 70 | ).waitForFinished(); | ||
3737 | 71 | |||
3738 | 72 | int waitedTime = 0; | ||
3739 | 73 | while (!dbusTestRunner.sessionConnection().interface()->isServiceRegistered("org.freedesktop.Notifications") && waitedTime < MAX_TIME_WAITING_FOR_NOTIFICATIONS) | ||
3740 | 74 | { | ||
3741 | 75 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||
3742 | 76 | waitedTime += 10; | ||
3743 | 77 | } | ||
3744 | 78 | } | ||
3745 | 79 | |||
3746 | 80 | void IndicatorSoundTestBase::TearDown() | ||
3747 | 81 | { | ||
3748 | 82 | unsetenv("XDG_DATA_DIRS"); | ||
3749 | 83 | unsetenv("PULSE_SERVER"); | ||
3750 | 84 | unsetenv("DBUS_SYSTEM_BUS_ADDRESS"); | ||
3751 | 85 | } | ||
3752 | 86 | |||
3753 | 87 | void gvariant_deleter(GVariant* varptr) | ||
3754 | 88 | { | ||
3755 | 89 | if (varptr != nullptr) | ||
3756 | 90 | { | ||
3757 | 91 | g_variant_unref(varptr); | ||
3758 | 92 | } | ||
3759 | 93 | } | ||
3760 | 94 | |||
3761 | 95 | std::shared_ptr<GVariant> IndicatorSoundTestBase::volume_variant(double volume) | ||
3762 | 96 | { | ||
3763 | 97 | GVariantBuilder builder; | ||
3764 | 98 | |||
3765 | 99 | g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); | ||
3766 | 100 | g_variant_builder_add(&builder, | ||
3767 | 101 | "{sv}", | ||
3768 | 102 | "title", | ||
3769 | 103 | g_variant_new_string("_Sound")); | ||
3770 | 104 | |||
3771 | 105 | g_variant_builder_add(&builder, | ||
3772 | 106 | "{sv}", | ||
3773 | 107 | "accessible-desc", | ||
3774 | 108 | g_variant_new_string("_Sound")); | ||
3775 | 109 | |||
3776 | 110 | auto icon = g_themed_icon_new("icon"); | ||
3777 | 111 | g_variant_builder_add(&builder, | ||
3778 | 112 | "{sv}", | ||
3779 | 113 | "icon", | ||
3780 | 114 | g_icon_serialize(icon)); | ||
3781 | 115 | |||
3782 | 116 | g_variant_builder_add(&builder, | ||
3783 | 117 | "{sv}", | ||
3784 | 118 | "visible", | ||
3785 | 119 | g_variant_new_boolean(true)); | ||
3786 | 120 | return shared_ptr<GVariant>(g_variant_builder_end(&builder), &gvariant_deleter); | ||
3787 | 121 | } | ||
3788 | 122 | |||
3789 | 123 | bool IndicatorSoundTestBase::setStreamRestoreVolume(QString const &role, double volume) | ||
3790 | 124 | { | ||
3791 | 125 | QProcess setVolume; | ||
3792 | 126 | setVolume.start(VOLUME_SET_BIN, QStringList() | ||
3793 | 127 | << role | ||
3794 | 128 | << QString("%1").arg(volume)); | ||
3795 | 129 | if (!setVolume.waitForStarted()) | ||
3796 | 130 | return false; | ||
3797 | 131 | |||
3798 | 132 | if (!setVolume.waitForFinished()) | ||
3799 | 133 | return false; | ||
3800 | 134 | |||
3801 | 135 | return setVolume.exitCode() == 0; | ||
3802 | 136 | } | ||
3803 | 137 | |||
3804 | 138 | bool IndicatorSoundTestBase::setSinkVolume(double volume) | ||
3805 | 139 | { | ||
3806 | 140 | QString volume_percentage = QString("%1\%").arg(volume*100); | ||
3807 | 141 | QProcess setVolume; | ||
3808 | 142 | setVolume.start("pactl", QStringList() | ||
3809 | 143 | << "-s" | ||
3810 | 144 | << "127.0.0.1" | ||
3811 | 145 | << "set-sink-volume" | ||
3812 | 146 | << "0" | ||
3813 | 147 | << volume_percentage); | ||
3814 | 148 | if (!setVolume.waitForStarted()) | ||
3815 | 149 | return false; | ||
3816 | 150 | |||
3817 | 151 | if (!setVolume.waitForFinished()) | ||
3818 | 152 | return false; | ||
3819 | 153 | |||
3820 | 154 | return setVolume.exitCode() == 0; | ||
3821 | 155 | } | ||
3822 | 156 | |||
3823 | 157 | bool IndicatorSoundTestBase::clearGSettingsPlayers() | ||
3824 | 158 | { | ||
3825 | 159 | QProcess clearPlayers; | ||
3826 | 160 | |||
3827 | 161 | clearPlayers.start("gsettings", QStringList() | ||
3828 | 162 | << "set" | ||
3829 | 163 | << "com.canonical.indicator.sound" | ||
3830 | 164 | << "interested-media-players" | ||
3831 | 165 | << "[]"); | ||
3832 | 166 | if (!clearPlayers.waitForStarted()) | ||
3833 | 167 | return false; | ||
3834 | 168 | |||
3835 | 169 | if (!clearPlayers.waitForFinished()) | ||
3836 | 170 | return false; | ||
3837 | 171 | |||
3838 | 172 | return clearPlayers.exitCode() == 0; | ||
3839 | 173 | } | ||
3840 | 174 | |||
3841 | 175 | bool IndicatorSoundTestBase::startTestMprisPlayer(QString const& playerName) | ||
3842 | 176 | { | ||
3843 | 177 | testPlayer1.terminate(); | ||
3844 | 178 | testPlayer1.start(MEDIA_PLAYER_MPRIS_BIN, QStringList() | ||
3845 | 179 | << playerName); | ||
3846 | 180 | if (!testPlayer1.waitForStarted()) | ||
3847 | 181 | return false; | ||
3848 | 182 | |||
3849 | 183 | |||
3850 | 184 | return true; | ||
3851 | 185 | } | ||
3852 | 186 | |||
3853 | 187 | bool IndicatorSoundTestBase::setTestMprisPlayerProperty(QString const &testPlayer, QString const &property, bool value) | ||
3854 | 188 | { | ||
3855 | 189 | QProcess setProperty; | ||
3856 | 190 | QString strValue; | ||
3857 | 191 | strValue = value ? "true" : "false"; | ||
3858 | 192 | |||
3859 | 193 | setProperty.start(MEDIA_PLAYER_MPRIS_UPDATE_BIN, QStringList() | ||
3860 | 194 | << testPlayer | ||
3861 | 195 | << property | ||
3862 | 196 | << strValue); | ||
3863 | 197 | if (!setProperty.waitForStarted()) | ||
3864 | 198 | return false; | ||
3865 | 199 | |||
3866 | 200 | if (!setProperty.waitForFinished()) | ||
3867 | 201 | return false; | ||
3868 | 202 | |||
3869 | 203 | return setProperty.exitCode() == 0; | ||
3870 | 204 | } | ||
3871 | 205 | |||
3872 | 206 | bool IndicatorSoundTestBase::startTestSound(QString const &role) | ||
3873 | 207 | { | ||
3874 | 208 | testSoundProcess.terminate(); | ||
3875 | 209 | testSoundProcess.start("paplay", QStringList() | ||
3876 | 210 | << "-s" | ||
3877 | 211 | << "127.0.0.1" | ||
3878 | 212 | << TEST_SOUND | ||
3879 | 213 | << QString("--property=media.role=%1").arg(role)); | ||
3880 | 214 | |||
3881 | 215 | if (!testSoundProcess.waitForStarted()) | ||
3882 | 216 | return false; | ||
3883 | 217 | |||
3884 | 218 | return true; | ||
3885 | 219 | } | ||
3886 | 220 | |||
3887 | 221 | void IndicatorSoundTestBase::stopTestSound() | ||
3888 | 222 | { | ||
3889 | 223 | testSoundProcess.terminate(); | ||
3890 | 224 | } | ||
3891 | 225 | |||
3892 | 226 | void IndicatorSoundTestBase::startPulseDesktop(DevicePortType speakerPort, DevicePortType headphonesPort) | ||
3893 | 227 | { | ||
3894 | 228 | try | ||
3895 | 229 | { | ||
3896 | 230 | pulseaudio.reset( | ||
3897 | 231 | new QProcessDBusService(DBusTypes::DBUS_PULSE, | ||
3898 | 232 | QDBusConnection::SessionBus, | ||
3899 | 233 | "pulseaudio", | ||
3900 | 234 | QStringList() << "--start" | ||
3901 | 235 | << "-vvvv" | ||
3902 | 236 | << "--disable-shm=true" | ||
3903 | 237 | << "--daemonize=false" | ||
3904 | 238 | << "--use-pid-file=false" | ||
3905 | 239 | << "--system=false" | ||
3906 | 240 | << "--exit-idle-time=-1" | ||
3907 | 241 | << "-n" | ||
3908 | 242 | << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort)) | ||
3909 | 243 | << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort)) | ||
3910 | 244 | << "--log-target=file:/tmp/pulse-daemon.log" | ||
3911 | 245 | << "--load=module-dbus-protocol" | ||
3912 | 246 | << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1" | ||
3913 | 247 | )); | ||
3914 | 248 | pulseaudio->start(dbusTestRunner.sessionConnection()); | ||
3915 | 249 | } | ||
3916 | 250 | catch (exception const& e) | ||
3917 | 251 | { | ||
3918 | 252 | cout << "pulseaudio(): " << e.what() << endl; | ||
3919 | 253 | throw; | ||
3920 | 254 | } | ||
3921 | 255 | } | ||
3922 | 256 | |||
3923 | 257 | void IndicatorSoundTestBase::startPulsePhone(DevicePortType speakerPort, DevicePortType headphonesPort) | ||
3924 | 258 | { | ||
3925 | 259 | try | ||
3926 | 260 | { | ||
3927 | 261 | pulseaudio.reset( | ||
3928 | 262 | new QProcessDBusService(DBusTypes::DBUS_PULSE, | ||
3929 | 263 | QDBusConnection::SessionBus, | ||
3930 | 264 | "pulseaudio", | ||
3931 | 265 | QStringList() << "--start" | ||
3932 | 266 | << "-vvvv" | ||
3933 | 267 | << "--disable-shm=true" | ||
3934 | 268 | << "--daemonize=false" | ||
3935 | 269 | << "--use-pid-file=false" | ||
3936 | 270 | << "--system=false" | ||
3937 | 271 | << "--exit-idle-time=-1" | ||
3938 | 272 | << "-n" | ||
3939 | 273 | << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort)) | ||
3940 | 274 | << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort)) | ||
3941 | 275 | << "--log-target=file:/tmp/pulse-daemon.log" | ||
3942 | 276 | << QString("--load=module-stream-restore restore_device=false restore_muted=false fallback_table=%1").arg(STREAM_RESTORE_TABLE) | ||
3943 | 277 | << "--load=module-dbus-protocol" | ||
3944 | 278 | << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1" | ||
3945 | 279 | )); | ||
3946 | 280 | pulseaudio->start(dbusTestRunner.sessionConnection()); | ||
3947 | 281 | } | ||
3948 | 282 | catch (exception const& e) | ||
3949 | 283 | { | ||
3950 | 284 | cout << "pulseaudio(): " << e.what() << endl; | ||
3951 | 285 | throw; | ||
3952 | 286 | } | ||
3953 | 287 | } | ||
3954 | 288 | |||
3955 | 289 | void IndicatorSoundTestBase::startAccountsService() | ||
3956 | 290 | { | ||
3957 | 291 | try | ||
3958 | 292 | { | ||
3959 | 293 | accountsService.reset( | ||
3960 | 294 | new QProcessDBusService(DBusTypes::ACCOUNTS_SERVICE, | ||
3961 | 295 | QDBusConnection::SystemBus, | ||
3962 | 296 | ACCOUNTS_SERVICE_BIN, | ||
3963 | 297 | QStringList())); | ||
3964 | 298 | accountsService->start(dbusTestRunner.systemConnection()); | ||
3965 | 299 | |||
3966 | 300 | initializeAccountsInterface(); | ||
3967 | 301 | } | ||
3968 | 302 | catch (exception const& e) | ||
3969 | 303 | { | ||
3970 | 304 | cout << "accountsService(): " << e.what() << endl; | ||
3971 | 305 | throw; | ||
3972 | 306 | } | ||
3973 | 307 | } | ||
3974 | 308 | |||
3975 | 309 | void IndicatorSoundTestBase::startIndicator() | ||
3976 | 310 | { | ||
3977 | 311 | try | ||
3978 | 312 | { | ||
3979 | 313 | setenv("PULSE_SERVER", "127.0.0.1", true); | ||
3980 | 314 | indicator.reset( | ||
3981 | 315 | new QProcessDBusService(DBusTypes::DBUS_NAME, | ||
3982 | 316 | QDBusConnection::SessionBus, | ||
3983 | 317 | SOUND_SERVICE_BIN, | ||
3984 | 318 | QStringList())); | ||
3985 | 319 | indicator->start(dbusTestRunner.sessionConnection()); | ||
3986 | 320 | } | ||
3987 | 321 | catch (exception const& e) | ||
3988 | 322 | { | ||
3989 | 323 | cout << "startIndicator(): " << e.what() << endl; | ||
3990 | 324 | throw; | ||
3991 | 325 | } | ||
3992 | 326 | } | ||
3993 | 327 | |||
3994 | 328 | mh::MenuMatcher::Parameters IndicatorSoundTestBase::desktopParameters() | ||
3995 | 329 | { | ||
3996 | 330 | return mh::MenuMatcher::Parameters( | ||
3997 | 331 | "com.canonical.indicator.sound", | ||
3998 | 332 | { { "indicator", "/com/canonical/indicator/sound" } }, | ||
3999 | 333 | "/com/canonical/indicator/sound/desktop"); | ||
4000 | 334 | } | ||
4001 | 335 | |||
4002 | 336 | mh::MenuMatcher::Parameters IndicatorSoundTestBase::phoneParameters() | ||
4003 | 337 | { | ||
4004 | 338 | return mh::MenuMatcher::Parameters( | ||
4005 | 339 | "com.canonical.indicator.sound", | ||
4006 | 340 | { { "indicator", "/com/canonical/indicator/sound" } }, | ||
4007 | 341 | "/com/canonical/indicator/sound/phone"); | ||
4008 | 342 | } | ||
4009 | 343 | |||
4010 | 344 | unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::volumeSlider(double volume, QString const &label) | ||
4011 | 345 | { | ||
4012 | 346 | return mh::MenuItemMatcher().radio() | ||
4013 | 347 | .label(label.toStdString()) | ||
4014 | 348 | .round_doubles(0.1) | ||
4015 | 349 | .int32_attribute("target", 0) | ||
4016 | 350 | .double_attribute("min-value", 0.0) | ||
4017 | 351 | .double_attribute("max-value", 1.0) | ||
4018 | 352 | .double_attribute("step", 0.01) | ||
4019 | 353 | .string_attribute("x-canonical-type", "com.canonical.unity.slider") | ||
4020 | 354 | .themed_icon("max-icon", {"audio-volume-high-panel", "audio-volume-high", "audio-volume", "audio"}) | ||
4021 | 355 | .themed_icon("min-icon", {"audio-volume-low-zero-panel", "audio-volume-low-zero", "audio-volume-low", "audio-volume", "audio"}) | ||
4022 | 356 | .pass_through_double_attribute("action", volume); | ||
4023 | 357 | } | ||
4024 | 358 | |||
4025 | 359 | unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::silentModeSwitch(bool toggled) | ||
4026 | 360 | { | ||
4027 | 361 | return mh::MenuItemMatcher::checkbox() | ||
4028 | 362 | .label("Silent Mode") | ||
4029 | 363 | .action("indicator.silent-mode") | ||
4030 | 364 | .toggled(toggled); | ||
4031 | 365 | } | ||
4032 | 366 | |||
4033 | 367 | bool IndicatorSoundTestBase::waitMenuChange() | ||
4034 | 368 | { | ||
4035 | 369 | if (signal_spy_menu_changed_) | ||
4036 | 370 | { | ||
4037 | 371 | return signal_spy_menu_changed_->wait(); | ||
4038 | 372 | } | ||
4039 | 373 | return false; | ||
4040 | 374 | } | ||
4041 | 375 | |||
4042 | 376 | bool IndicatorSoundTestBase::initializeMenuChangedSignal() | ||
4043 | 377 | { | ||
4044 | 378 | if (!menu_interface_) | ||
4045 | 379 | { | ||
4046 | 380 | menu_interface_.reset(new MenusInterface("com.canonical.indicator.sound", | ||
4047 | 381 | "/com/canonical/indicator/sound", | ||
4048 | 382 | dbusTestRunner.sessionConnection(), 0)); | ||
4049 | 383 | } | ||
4050 | 384 | if (menu_interface_) | ||
4051 | 385 | { | ||
4052 | 386 | qDebug() << "Waiting for signal"; | ||
4053 | 387 | signal_spy_menu_changed_.reset(new QSignalSpy(menu_interface_.get(), &MenusInterface::Changed)); | ||
4054 | 388 | } | ||
4055 | 389 | if (!menu_interface_ || !signal_spy_menu_changed_) | ||
4056 | 390 | { | ||
4057 | 391 | return false; | ||
4058 | 392 | } | ||
4059 | 393 | return true; | ||
4060 | 394 | } | ||
4061 | 395 | |||
4062 | 396 | bool IndicatorSoundTestBase::waitVolumeChangedInIndicator() | ||
4063 | 397 | { | ||
4064 | 398 | if (signal_spy_volume_changed_) | ||
4065 | 399 | { | ||
4066 | 400 | return signal_spy_volume_changed_->wait(); | ||
4067 | 401 | } | ||
4068 | 402 | return false; | ||
4069 | 403 | } | ||
4070 | 404 | |||
4071 | 405 | void IndicatorSoundTestBase::initializeAccountsInterface() | ||
4072 | 406 | { | ||
4073 | 407 | auto username = qgetenv("USER"); | ||
4074 | 408 | if (username != "") | ||
4075 | 409 | { | ||
4076 | 410 | std::unique_ptr<AccountsInterface> accountsInterface(new AccountsInterface("org.freedesktop.Accounts", | ||
4077 | 411 | "/org/freedesktop/Accounts", | ||
4078 | 412 | dbusTestRunner.systemConnection(), 0)); | ||
4079 | 413 | |||
4080 | 414 | QDBusReply<QDBusObjectPath> userResp = accountsInterface->call(QLatin1String("FindUserByName"), | ||
4081 | 415 | QLatin1String(username)); | ||
4082 | 416 | |||
4083 | 417 | if (!userResp.isValid()) | ||
4084 | 418 | { | ||
4085 | 419 | qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << userResp.error().message(); | ||
4086 | 420 | } | ||
4087 | 421 | |||
4088 | 422 | auto userPath = userResp.value().path(); | ||
4089 | 423 | if (userPath != "") | ||
4090 | 424 | { | ||
4091 | 425 | std::unique_ptr<AccountsSoundInterface> soundInterface(new AccountsSoundInterface("org.freedesktop.Accounts", | ||
4092 | 426 | userPath, | ||
4093 | 427 | dbusTestRunner.systemConnection(), 0)); | ||
4094 | 428 | |||
4095 | 429 | accounts_interface_.reset(new DBusPropertiesInterface("org.freedesktop.Accounts", | ||
4096 | 430 | userPath, | ||
4097 | 431 | dbusTestRunner.systemConnection(), 0)); | ||
4098 | 432 | if (!accounts_interface_->isValid()) | ||
4099 | 433 | { | ||
4100 | 434 | qWarning() << "SetVolume::initializeAccountsInterface(): D-Bus error: " << accounts_interface_->lastError().message(); | ||
4101 | 435 | } | ||
4102 | 436 | signal_spy_volume_changed_.reset(new QSignalSpy(accounts_interface_.get(),&DBusPropertiesInterface::PropertiesChanged)); | ||
4103 | 437 | } | ||
4104 | 438 | } | ||
4105 | 439 | } | ||
4106 | 440 | |||
4107 | 441 | OrgFreedesktopDBusMockInterface& IndicatorSoundTestBase::notificationsMockInterface() | ||
4108 | 442 | { | ||
4109 | 443 | return dbusMock.mockInterface("org.freedesktop.Notifications", | ||
4110 | 444 | "/org/freedesktop/Notifications", | ||
4111 | 445 | "org.freedesktop.Notifications", | ||
4112 | 446 | QDBusConnection::SessionBus); | ||
4113 | 447 | } | ||
4114 | 448 | |||
4115 | 449 | bool IndicatorSoundTestBase::setActionValue(const QString & action, QVariant value) | ||
4116 | 450 | { | ||
4117 | 451 | QDBusInterface actionsInterface(DBusTypes::DBUS_NAME, | ||
4118 | 452 | DBusTypes::MAIN_SERVICE_PATH, | ||
4119 | 453 | DBusTypes::ACTIONS_INTERFACE, | ||
4120 | 454 | dbusTestRunner.sessionConnection()); | ||
4121 | 455 | |||
4122 | 456 | QDBusVariant dbusVar(value); | ||
4123 | 457 | auto resp = actionsInterface.call("SetState", | ||
4124 | 458 | action, | ||
4125 | 459 | QVariant::fromValue(dbusVar), | ||
4126 | 460 | QVariant::fromValue(QVariantMap())); | ||
4127 | 461 | |||
4128 | 462 | if (resp.type() == QDBusMessage::ErrorMessage) | ||
4129 | 463 | { | ||
4130 | 464 | qCritical() << "IndicatorSoundTestBase::setActionValue(): Failed to set value for action " | ||
4131 | 465 | << action | ||
4132 | 466 | << " " | ||
4133 | 467 | << resp.errorMessage(); | ||
4134 | 468 | return false; | ||
4135 | 469 | } | ||
4136 | 470 | else | ||
4137 | 471 | { | ||
4138 | 472 | return true; | ||
4139 | 473 | } | ||
4140 | 474 | } | ||
4141 | 475 | |||
4142 | 476 | bool IndicatorSoundTestBase::pressNotificationButton(int id, const QString & button) | ||
4143 | 477 | { | ||
4144 | 478 | OrgFreedesktopDBusMockInterface actionsInterface("org.freedesktop.Notifications", | ||
4145 | 479 | "/org/freedesktop/Notifications", | ||
4146 | 480 | dbusTestRunner.sessionConnection()); | ||
4147 | 481 | |||
4148 | 482 | actionsInterface.EmitSignal( | ||
4149 | 483 | "org.freedesktop.Notifications", | ||
4150 | 484 | "ActionInvoked", "us", QVariantList() << id << button); | ||
4151 | 485 | |||
4152 | 486 | return true; | ||
4153 | 487 | } | ||
4154 | 488 | |||
4155 | 489 | bool IndicatorSoundTestBase::qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) | ||
4156 | 490 | { | ||
4157 | 491 | if (variant.canConvert<QDBusArgument>()) | ||
4158 | 492 | { | ||
4159 | 493 | QDBusArgument value(variant.value<QDBusArgument>()); | ||
4160 | 494 | if (value.currentType() == QDBusArgument::MapType) | ||
4161 | 495 | { | ||
4162 | 496 | value >> map; | ||
4163 | 497 | return true; | ||
4164 | 498 | } | ||
4165 | 499 | } | ||
4166 | 500 | return false; | ||
4167 | 501 | } | ||
4168 | 502 | |||
4169 | 503 | void IndicatorSoundTestBase::checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call) | ||
4170 | 504 | { | ||
4171 | 505 | QString icon; | ||
4172 | 506 | if (volume <= 0.0) | ||
4173 | 507 | { | ||
4174 | 508 | icon = "audio-volume-muted"; | ||
4175 | 509 | } | ||
4176 | 510 | else if (volume <= 0.3) | ||
4177 | 511 | { | ||
4178 | 512 | icon = "audio-volume-low"; | ||
4179 | 513 | } | ||
4180 | 514 | else if (volume <= 0.7) | ||
4181 | 515 | { | ||
4182 | 516 | icon = "audio-volume-medium"; | ||
4183 | 517 | } | ||
4184 | 518 | else | ||
4185 | 519 | { | ||
4186 | 520 | icon = "audio-volume-high"; | ||
4187 | 521 | } | ||
4188 | 522 | |||
4189 | 523 | ASSERT_NE(call.size(), 0); | ||
4190 | 524 | EXPECT_EQ("Notify", call.at(0)); | ||
4191 | 525 | |||
4192 | 526 | QVariantList const& args(call.at(1).toList()); | ||
4193 | 527 | ASSERT_EQ(8, args.size()); | ||
4194 | 528 | EXPECT_EQ("indicator-sound", args.at(0)); | ||
4195 | 529 | EXPECT_EQ(icon, args.at(2)); | ||
4196 | 530 | EXPECT_EQ("Volume", args.at(3)); | ||
4197 | 531 | EXPECT_EQ(label, args.at(4)); | ||
4198 | 532 | EXPECT_EQ(QStringList(), args.at(5)); | ||
4199 | 533 | |||
4200 | 534 | QVariantMap hints; | ||
4201 | 535 | ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); | ||
4202 | 536 | ASSERT_TRUE(hints.contains("value")); | ||
4203 | 537 | ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); | ||
4204 | 538 | ASSERT_TRUE(hints.contains("x-canonical-value-bar-tint")); | ||
4205 | 539 | ASSERT_TRUE(hints.contains("x-canonical-private-synchronous")); | ||
4206 | 540 | |||
4207 | 541 | EXPECT_EQ(volume*100, hints["value"]); | ||
4208 | 542 | EXPECT_EQ(true, hints["x-canonical-non-shaped-icon"]); | ||
4209 | 543 | EXPECT_EQ(isLoud, hints["x-canonical-value-bar-tint"]); | ||
4210 | 544 | EXPECT_EQ(true, hints["x-canonical-private-synchronous"]); | ||
4211 | 545 | } | ||
4212 | 546 | |||
4213 | 547 | void IndicatorSoundTestBase::checkHighVolumeNotification(QVariantList call) | ||
4214 | 548 | { | ||
4215 | 549 | ASSERT_NE(call.size(), 0); | ||
4216 | 550 | EXPECT_EQ("Notify", call.at(0)); | ||
4217 | 551 | |||
4218 | 552 | QVariantList const& args(call.at(1).toList()); | ||
4219 | 553 | ASSERT_EQ(8, args.size()); | ||
4220 | 554 | EXPECT_EQ("indicator-sound", args.at(0)); | ||
4221 | 555 | EXPECT_EQ("Volume", args.at(3)); | ||
4222 | 556 | } | ||
4223 | 557 | |||
4224 | 558 | void IndicatorSoundTestBase::checkCloseNotification(int id, QVariantList call) | ||
4225 | 559 | { | ||
4226 | 560 | EXPECT_EQ("CloseNotification", call.at(0)); | ||
4227 | 561 | QVariantList const& args(call.at(1).toList()); | ||
4228 | 562 | ASSERT_EQ(1, args.size()); | ||
4229 | 563 | } | ||
4230 | 564 | |||
4231 | 565 | void IndicatorSoundTestBase::checkNotificationWithNoArgs(QString const& method, QVariantList call) | ||
4232 | 566 | { | ||
4233 | 567 | EXPECT_EQ(method, call.at(0)); | ||
4234 | 568 | QVariantList const& args(call.at(1).toList()); | ||
4235 | 569 | ASSERT_EQ(0, args.size()); | ||
4236 | 570 | } | ||
4237 | 571 | |||
4238 | 572 | int IndicatorSoundTestBase::getNotificationID(QVariantList call) | ||
4239 | 573 | { | ||
4240 | 574 | if (call.size() == 0) | ||
4241 | 575 | { | ||
4242 | 576 | return -1; | ||
4243 | 577 | } | ||
4244 | 578 | QVariantList const& args(call.at(1).toList()); | ||
4245 | 579 | if (args.size() != 8) | ||
4246 | 580 | { | ||
4247 | 581 | return -1; | ||
4248 | 582 | } | ||
4249 | 583 | if (args.at(0) != "indicator-sound") | ||
4250 | 584 | { | ||
4251 | 585 | return -1; | ||
4252 | 586 | } | ||
4253 | 587 | |||
4254 | 588 | bool isInt; | ||
4255 | 589 | int id = args.at(1).toInt(&isInt); | ||
4256 | 590 | if (!isInt) | ||
4257 | 591 | { | ||
4258 | 592 | return -1; | ||
4259 | 593 | } | ||
4260 | 594 | return id; | ||
4261 | 595 | } | ||
4262 | 596 | |||
4263 | 597 | bool IndicatorSoundTestBase::activateHeadphones(bool headphonesActive) | ||
4264 | 598 | { | ||
4265 | 599 | QProcess pacltProcess; | ||
4266 | 600 | |||
4267 | 601 | QString defaultSinkName = "indicator_sound_test_speaker"; | ||
4268 | 602 | QString suspendedSinkName = "indicator_sound_test_headphones"; | ||
4269 | 603 | if (headphonesActive) | ||
4270 | 604 | { | ||
4271 | 605 | defaultSinkName = "indicator_sound_test_headphones"; | ||
4272 | 606 | suspendedSinkName = "indicator_sound_test_speaker"; | ||
4273 | 607 | } | ||
4274 | 608 | |||
4275 | 609 | pacltProcess.start("pactl", QStringList() << "-s" | ||
4276 | 610 | << "127.0.0.1" | ||
4277 | 611 | << "set-default-sink" | ||
4278 | 612 | << defaultSinkName); | ||
4279 | 613 | if (!pacltProcess.waitForStarted()) | ||
4280 | 614 | return false; | ||
4281 | 615 | |||
4282 | 616 | if (!pacltProcess.waitForFinished()) | ||
4283 | 617 | return false; | ||
4284 | 618 | |||
4285 | 619 | pacltProcess.start("pactl", QStringList() << "-s" | ||
4286 | 620 | << "127.0.0.1" | ||
4287 | 621 | << "suspend-sink" | ||
4288 | 622 | << defaultSinkName | ||
4289 | 623 | << "0"); | ||
4290 | 624 | if (!pacltProcess.waitForStarted()) | ||
4291 | 625 | return false; | ||
4292 | 626 | |||
4293 | 627 | if (!pacltProcess.waitForFinished()) | ||
4294 | 628 | return false; | ||
4295 | 629 | |||
4296 | 630 | pacltProcess.start("pactl", QStringList() << "-s" | ||
4297 | 631 | << "127.0.0.1" | ||
4298 | 632 | << "suspend-sink" | ||
4299 | 633 | << suspendedSinkName | ||
4300 | 634 | << "1"); | ||
4301 | 635 | if (!pacltProcess.waitForStarted()) | ||
4302 | 636 | return false; | ||
4303 | 637 | |||
4304 | 638 | if (!pacltProcess.waitForFinished()) | ||
4305 | 639 | return false; | ||
4306 | 640 | |||
4307 | 641 | return pacltProcess.exitCode() == 0; | ||
4308 | 642 | } | ||
4309 | 643 | |||
4310 | 644 | QString IndicatorSoundTestBase::getDevicePortString(DevicePortType port) | ||
4311 | 645 | { | ||
4312 | 646 | QString portString; | ||
4313 | 647 | |||
4314 | 648 | switch (port) | ||
4315 | 649 | { | ||
4316 | 650 | case WIRED: | ||
4317 | 651 | portString = "wired"; | ||
4318 | 652 | break; | ||
4319 | 653 | case BLUETOOTH: | ||
4320 | 654 | portString = "bluetooth"; | ||
4321 | 655 | break; | ||
4322 | 656 | case USB: | ||
4323 | 657 | portString = "usb"; | ||
4324 | 658 | break; | ||
4325 | 659 | case HDMI: | ||
4326 | 660 | portString = "hdmi"; | ||
4327 | 661 | break; | ||
4328 | 662 | default: | ||
4329 | 663 | portString = "not_defined"; | ||
4330 | 664 | break; | ||
4331 | 665 | } | ||
4332 | 666 | |||
4333 | 667 | return portString; | ||
4334 | 668 | } | ||
4335 | 669 | |||
4336 | 670 | void IndicatorSoundTestBase::checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort) | ||
4337 | 671 | { | ||
4338 | 672 | double INITIAL_VOLUME = 0.0; | ||
4339 | 673 | |||
4340 | 674 | QString speakerString; | ||
4341 | 675 | QString speakerStringMenu; | ||
4342 | 676 | switch(speakerPort) | ||
4343 | 677 | { | ||
4344 | 678 | case WIRED: | ||
4345 | 679 | speakerString = "Speakers"; | ||
4346 | 680 | speakerStringMenu = "Volume"; | ||
4347 | 681 | break; | ||
4348 | 682 | case BLUETOOTH: | ||
4349 | 683 | speakerString = "Bluetooth speaker"; | ||
4350 | 684 | speakerStringMenu = "Volume (Bluetooth)"; | ||
4351 | 685 | break; | ||
4352 | 686 | case USB: | ||
4353 | 687 | speakerString = "Usb speaker"; | ||
4354 | 688 | speakerStringMenu = "Volume (Usb)"; | ||
4355 | 689 | break; | ||
4356 | 690 | case HDMI: | ||
4357 | 691 | speakerString = "HDMI speaker"; | ||
4358 | 692 | speakerStringMenu = "Volume (HDMI)"; | ||
4359 | 693 | break; | ||
4360 | 694 | } | ||
4361 | 695 | |||
4362 | 696 | QString headphonesString; | ||
4363 | 697 | QString headphonesStringMenu; | ||
4364 | 698 | switch(headphonesPort) | ||
4365 | 699 | { | ||
4366 | 700 | case WIRED: | ||
4367 | 701 | headphonesString = "Headphones"; | ||
4368 | 702 | headphonesStringMenu = "Volume (Headphones)"; | ||
4369 | 703 | break; | ||
4370 | 704 | case BLUETOOTH: | ||
4371 | 705 | headphonesString = "Bluetooth headphones"; | ||
4372 | 706 | headphonesStringMenu = "Volume (Bluetooth headphones)"; | ||
4373 | 707 | break; | ||
4374 | 708 | case USB: | ||
4375 | 709 | headphonesString = "Usb headphones"; | ||
4376 | 710 | headphonesStringMenu = "Volume (Usb headphones)"; | ||
4377 | 711 | break; | ||
4378 | 712 | case HDMI: | ||
4379 | 713 | headphonesString = "HDMI headphones"; | ||
4380 | 714 | headphonesStringMenu = "Volume (HDMI headphones)"; | ||
4381 | 715 | break; | ||
4382 | 716 | } | ||
4383 | 717 | |||
4384 | 718 | QSignalSpy notificationsSpy(¬ificationsMockInterface(), | ||
4385 | 719 | SIGNAL(MethodCalled(const QString &, const QVariantList &))); | ||
4386 | 720 | |||
4387 | 721 | ASSERT_NO_THROW(startAccountsService()); | ||
4388 | 722 | ASSERT_NO_THROW(startPulsePhone(speakerPort, headphonesPort)); | ||
4389 | 723 | |||
4390 | 724 | // initialize volumes in pulseaudio | ||
4391 | 725 | EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); | ||
4392 | 726 | EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); | ||
4393 | 727 | |||
4394 | 728 | // start now the indicator, so it picks the new volumes | ||
4395 | 729 | ASSERT_NO_THROW(startIndicator()); | ||
4396 | 730 | |||
4397 | 731 | // if the speaker is the normal one it does not emit any notification, as that's | ||
4398 | 732 | // the default one. | ||
4399 | 733 | // for the rest it notifies the output | ||
4400 | 734 | if (speakerPort != WIRED) | ||
4401 | 735 | { | ||
4402 | 736 | WAIT_FOR_SIGNALS(notificationsSpy, 3); | ||
4403 | 737 | |||
4404 | 738 | // the first time we also have the calls to | ||
4405 | 739 | // GetServerInformation and GetCapabilities | ||
4406 | 740 | checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); | ||
4407 | 741 | checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); | ||
4408 | 742 | checkVolumeNotification(INITIAL_VOLUME, speakerString, false, notificationsSpy.at(2)); | ||
4409 | 743 | notificationsSpy.clear(); | ||
4410 | 744 | } | ||
4411 | 745 | |||
4412 | 746 | // activate the headphones | ||
4413 | 747 | EXPECT_TRUE(activateHeadphones(true)); | ||
4414 | 748 | |||
4415 | 749 | if (speakerPort == WIRED) | ||
4416 | 750 | { | ||
4417 | 751 | WAIT_FOR_SIGNALS(notificationsSpy, 3); | ||
4418 | 752 | |||
4419 | 753 | // the first time we also have the calls to | ||
4420 | 754 | // GetServerInformation and GetCapabilities | ||
4421 | 755 | checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); | ||
4422 | 756 | checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); | ||
4423 | 757 | checkVolumeNotification(INITIAL_VOLUME, headphonesString, false, notificationsSpy.at(2)); | ||
4424 | 758 | notificationsSpy.clear(); | ||
4425 | 759 | } | ||
4426 | 760 | else | ||
4427 | 761 | { | ||
4428 | 762 | WAIT_FOR_SIGNALS(notificationsSpy, 1); | ||
4429 | 763 | checkVolumeNotification(INITIAL_VOLUME, headphonesString, false, notificationsSpy.at(0)); | ||
4430 | 764 | notificationsSpy.clear(); | ||
4431 | 765 | } | ||
4432 | 766 | |||
4433 | 767 | // check the label in the menu | ||
4434 | 768 | EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) | ||
4435 | 769 | .item(mh::MenuItemMatcher() | ||
4436 | 770 | .action("indicator.root") | ||
4437 | 771 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4438 | 772 | .string_attribute("x-canonical-scroll-action", "indicator.scroll") | ||
4439 | 773 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4440 | 774 | .string_attribute("submenu-action", "indicator.indicator-shown") | ||
4441 | 775 | .mode(mh::MenuItemMatcher::Mode::starts_with) | ||
4442 | 776 | .submenu() | ||
4443 | 777 | .item(mh::MenuItemMatcher() | ||
4444 | 778 | .section() | ||
4445 | 779 | .item(silentModeSwitch(false)) | ||
4446 | 780 | .item(volumeSlider(INITIAL_VOLUME, headphonesStringMenu)) | ||
4447 | 781 | ) | ||
4448 | 782 | ).match()); | ||
4449 | 783 | |||
4450 | 784 | // deactivate the headphones | ||
4451 | 785 | EXPECT_TRUE(activateHeadphones(false)); | ||
4452 | 786 | |||
4453 | 787 | WAIT_FOR_SIGNALS(notificationsSpy, 1); | ||
4454 | 788 | checkVolumeNotification(INITIAL_VOLUME, speakerString, false, notificationsSpy.at(0)); | ||
4455 | 789 | notificationsSpy.clear(); | ||
4456 | 790 | |||
4457 | 791 | // check the label in the menu | ||
4458 | 792 | EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) | ||
4459 | 793 | .item(mh::MenuItemMatcher() | ||
4460 | 794 | .action("indicator.root") | ||
4461 | 795 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4462 | 796 | .string_attribute("x-canonical-scroll-action", "indicator.scroll") | ||
4463 | 797 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4464 | 798 | .string_attribute("submenu-action", "indicator.indicator-shown") | ||
4465 | 799 | .mode(mh::MenuItemMatcher::Mode::starts_with) | ||
4466 | 800 | .submenu() | ||
4467 | 801 | .item(mh::MenuItemMatcher() | ||
4468 | 802 | .section() | ||
4469 | 803 | .item(silentModeSwitch(false)) | ||
4470 | 804 | .item(volumeSlider(INITIAL_VOLUME, speakerStringMenu)) | ||
4471 | 805 | ) | ||
4472 | 806 | ).match()); | ||
4473 | 807 | } | ||
4474 | 0 | 808 | ||
4475 | === added file 'tests/integration/indicator-sound-test-base.h' | |||
4476 | --- tests/integration/indicator-sound-test-base.h 1970-01-01 00:00:00 +0000 | |||
4477 | +++ tests/integration/indicator-sound-test-base.h 2015-10-28 09:41:22 +0000 | |||
4478 | @@ -0,0 +1,146 @@ | |||
4479 | 1 | /* | ||
4480 | 2 | * Copyright (C) 2015 Canonical, Ltd. | ||
4481 | 3 | * | ||
4482 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
4483 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
4484 | 6 | * by the Free Software Foundation. | ||
4485 | 7 | * | ||
4486 | 8 | * This program is distributed in the hope that it will be useful, but | ||
4487 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
4488 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
4489 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
4490 | 12 | * | ||
4491 | 13 | * You should have received a copy of the GNU General Public License along | ||
4492 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
4493 | 15 | * | ||
4494 | 16 | * Author: Xavi Garcia <xavi.garcia.mena@canonical.com> | ||
4495 | 17 | */ | ||
4496 | 18 | |||
4497 | 19 | #pragma once | ||
4498 | 20 | |||
4499 | 21 | #include <libqtdbustest/DBusTestRunner.h> | ||
4500 | 22 | #include <libqtdbustest/QProcessDBusService.h> | ||
4501 | 23 | #include <libqtdbusmock/DBusMock.h> | ||
4502 | 24 | |||
4503 | 25 | #include <unity/gmenuharness/MatchUtils.h> | ||
4504 | 26 | #include <unity/gmenuharness/MenuMatcher.h> | ||
4505 | 27 | |||
4506 | 28 | #include <gmock/gmock.h> | ||
4507 | 29 | #include <gtest/gtest.h> | ||
4508 | 30 | |||
4509 | 31 | class MenusInterface; | ||
4510 | 32 | class DBusPulseVolume; | ||
4511 | 33 | class DBusPropertiesInterface; | ||
4512 | 34 | class QSignalSpy; | ||
4513 | 35 | |||
4514 | 36 | #define WAIT_FOR_SIGNALS(signalSpy, signalsExpected)\ | ||
4515 | 37 | {\ | ||
4516 | 38 | while (signalSpy.size() < signalsExpected)\ | ||
4517 | 39 | {\ | ||
4518 | 40 | ASSERT_TRUE(signalSpy.wait());\ | ||
4519 | 41 | }\ | ||
4520 | 42 | ASSERT_EQ(signalsExpected, signalSpy.size());\ | ||
4521 | 43 | } | ||
4522 | 44 | |||
4523 | 45 | class IndicatorSoundTestBase: public testing::Test | ||
4524 | 46 | { | ||
4525 | 47 | public: | ||
4526 | 48 | IndicatorSoundTestBase(); | ||
4527 | 49 | |||
4528 | 50 | ~IndicatorSoundTestBase(); | ||
4529 | 51 | |||
4530 | 52 | enum DevicePortType | ||
4531 | 53 | { | ||
4532 | 54 | WIRED, | ||
4533 | 55 | BLUETOOTH, | ||
4534 | 56 | USB, | ||
4535 | 57 | HDMI | ||
4536 | 58 | }; | ||
4537 | 59 | |||
4538 | 60 | protected: | ||
4539 | 61 | void SetUp() override; | ||
4540 | 62 | void TearDown() override; | ||
4541 | 63 | |||
4542 | 64 | void startIndicator(); | ||
4543 | 65 | void startPulseDesktop(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED); | ||
4544 | 66 | void startPulsePhone(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED); | ||
4545 | 67 | void startAccountsService(); | ||
4546 | 68 | |||
4547 | 69 | bool clearGSettingsPlayers(); | ||
4548 | 70 | |||
4549 | 71 | bool startTestMprisPlayer(QString const& playerName); | ||
4550 | 72 | |||
4551 | 73 | bool setTestMprisPlayerProperty(QString const &testPlayer, QString const &property, bool value); | ||
4552 | 74 | |||
4553 | 75 | bool setStreamRestoreVolume(QString const &role, double volume); | ||
4554 | 76 | |||
4555 | 77 | bool setSinkVolume(double volume); | ||
4556 | 78 | |||
4557 | 79 | bool startTestSound(QString const &role); | ||
4558 | 80 | |||
4559 | 81 | void stopTestSound(); | ||
4560 | 82 | |||
4561 | 83 | static std::shared_ptr<GVariant> volume_variant(double volume); | ||
4562 | 84 | |||
4563 | 85 | static unity::gmenuharness::MenuMatcher::Parameters desktopParameters(); | ||
4564 | 86 | |||
4565 | 87 | static unity::gmenuharness::MenuMatcher::Parameters phoneParameters(); | ||
4566 | 88 | |||
4567 | 89 | static unity::gmenuharness::MenuItemMatcher volumeSlider(double volume, QString const &label); | ||
4568 | 90 | |||
4569 | 91 | static unity::gmenuharness::MenuItemMatcher silentModeSwitch(bool toggled); | ||
4570 | 92 | |||
4571 | 93 | bool waitMenuChange(); | ||
4572 | 94 | |||
4573 | 95 | bool initializeMenuChangedSignal(); | ||
4574 | 96 | |||
4575 | 97 | bool waitVolumeChangedInIndicator(); | ||
4576 | 98 | |||
4577 | 99 | void initializeAccountsInterface(); | ||
4578 | 100 | |||
4579 | 101 | OrgFreedesktopDBusMockInterface& notificationsMockInterface(); | ||
4580 | 102 | |||
4581 | 103 | bool setActionValue(const QString & action, QVariant value); | ||
4582 | 104 | |||
4583 | 105 | bool pressNotificationButton(int id, const QString & button); | ||
4584 | 106 | |||
4585 | 107 | bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map); | ||
4586 | 108 | |||
4587 | 109 | void checkVolumeNotification(double volume, QString const& label, bool isLoud, QVariantList call); | ||
4588 | 110 | |||
4589 | 111 | void checkHighVolumeNotification(QVariantList call); | ||
4590 | 112 | |||
4591 | 113 | void checkCloseNotification(int id, QVariantList call); | ||
4592 | 114 | |||
4593 | 115 | void checkNotificationWithNoArgs(QString const& method, QVariantList call); | ||
4594 | 116 | |||
4595 | 117 | int getNotificationID(QVariantList call); | ||
4596 | 118 | |||
4597 | 119 | bool activateHeadphones(bool headphonesActive); | ||
4598 | 120 | |||
4599 | 121 | QString getDevicePortString(DevicePortType port); | ||
4600 | 122 | |||
4601 | 123 | void checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort); | ||
4602 | 124 | |||
4603 | 125 | QtDBusTest::DBusTestRunner dbusTestRunner; | ||
4604 | 126 | |||
4605 | 127 | QtDBusMock::DBusMock dbusMock; | ||
4606 | 128 | |||
4607 | 129 | QtDBusTest::DBusServicePtr indicator; | ||
4608 | 130 | |||
4609 | 131 | QtDBusTest::DBusServicePtr pulseaudio; | ||
4610 | 132 | |||
4611 | 133 | QtDBusTest::DBusServicePtr accountsService; | ||
4612 | 134 | |||
4613 | 135 | QProcess testSoundProcess; | ||
4614 | 136 | |||
4615 | 137 | QProcess testPlayer1; | ||
4616 | 138 | |||
4617 | 139 | std::unique_ptr<MenusInterface> menu_interface_; | ||
4618 | 140 | |||
4619 | 141 | std::unique_ptr<DBusPropertiesInterface> accounts_interface_; | ||
4620 | 142 | |||
4621 | 143 | std::unique_ptr<QSignalSpy> signal_spy_volume_changed_; | ||
4622 | 144 | |||
4623 | 145 | std::unique_ptr<QSignalSpy> signal_spy_menu_changed_; | ||
4624 | 146 | }; | ||
4625 | 0 | 147 | ||
4626 | === added file 'tests/integration/main.cpp' | |||
4627 | --- tests/integration/main.cpp 1970-01-01 00:00:00 +0000 | |||
4628 | +++ tests/integration/main.cpp 2015-10-28 09:41:22 +0000 | |||
4629 | @@ -0,0 +1,58 @@ | |||
4630 | 1 | /* | ||
4631 | 2 | * Copyright © 2014 Canonical Ltd. | ||
4632 | 3 | * | ||
4633 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
4634 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
4635 | 6 | * as published by the Free Software Foundation. | ||
4636 | 7 | * | ||
4637 | 8 | * This program is distributed in the hope that it will be useful, | ||
4638 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4639 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4640 | 11 | * GNU Lesser General Public License for more details. | ||
4641 | 12 | * | ||
4642 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
4643 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
4644 | 15 | * | ||
4645 | 16 | * Authors: | ||
4646 | 17 | * Pete Woods <pete.woods@canonical.com> | ||
4647 | 18 | */ | ||
4648 | 19 | |||
4649 | 20 | //#include <config.h> | ||
4650 | 21 | |||
4651 | 22 | #include <QCoreApplication> | ||
4652 | 23 | #include <QTimer> | ||
4653 | 24 | #include <gtest/gtest.h> | ||
4654 | 25 | |||
4655 | 26 | #include <libqtdbusmock/DBusMock.h> | ||
4656 | 27 | |||
4657 | 28 | #include "dbus-types.h" | ||
4658 | 29 | |||
4659 | 30 | using namespace QtDBusMock; | ||
4660 | 31 | |||
4661 | 32 | class Runner: public QObject | ||
4662 | 33 | { | ||
4663 | 34 | Q_OBJECT | ||
4664 | 35 | public Q_SLOTS: | ||
4665 | 36 | void run() | ||
4666 | 37 | { | ||
4667 | 38 | QCoreApplication::exit(RUN_ALL_TESTS()); | ||
4668 | 39 | } | ||
4669 | 40 | }; | ||
4670 | 41 | |||
4671 | 42 | int main(int argc, char **argv) | ||
4672 | 43 | { | ||
4673 | 44 | qputenv("LANG", "C.UTF-8"); | ||
4674 | 45 | unsetenv("LC_ALL"); | ||
4675 | 46 | |||
4676 | 47 | QCoreApplication application(argc, argv); | ||
4677 | 48 | DBusMock::registerMetaTypes(); | ||
4678 | 49 | DBusTypes::registerMetaTypes(); | ||
4679 | 50 | ::testing::InitGoogleTest(&argc, argv); | ||
4680 | 51 | |||
4681 | 52 | Runner runner; | ||
4682 | 53 | QTimer::singleShot(0, &runner, SLOT(run())); | ||
4683 | 54 | |||
4684 | 55 | return application.exec(); | ||
4685 | 56 | } | ||
4686 | 57 | |||
4687 | 58 | #include "main.moc" | ||
4688 | 0 | 59 | ||
4689 | === added file 'tests/integration/test-indicator.cpp' | |||
4690 | --- tests/integration/test-indicator.cpp 1970-01-01 00:00:00 +0000 | |||
4691 | +++ tests/integration/test-indicator.cpp 2015-10-28 09:41:22 +0000 | |||
4692 | @@ -0,0 +1,963 @@ | |||
4693 | 1 | /* | ||
4694 | 2 | * Copyright (C) 2015 Canonical, Ltd. | ||
4695 | 3 | * | ||
4696 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
4697 | 5 | * under the terms of the GNU General Public License version 3, as published | ||
4698 | 6 | * by the Free Software Foundation. | ||
4699 | 7 | * | ||
4700 | 8 | * This program is distributed in the hope that it will be useful, but | ||
4701 | 9 | * WITHOUT ANY WARRANTY; without even the implied warranties of | ||
4702 | 10 | * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
4703 | 11 | * PURPOSE. See the GNU General Public License for more details. | ||
4704 | 12 | * | ||
4705 | 13 | * You should have received a copy of the GNU General Public License along | ||
4706 | 14 | * with this program. If not, see <http://www.gnu.org/licenses/>. | ||
4707 | 15 | * | ||
4708 | 16 | * Author: Xavi Garcia <xavi.garcia.mena@canonical.com> | ||
4709 | 17 | */ | ||
4710 | 18 | |||
4711 | 19 | #include <indicator-sound-test-base.h> | ||
4712 | 20 | |||
4713 | 21 | #include <QDebug> | ||
4714 | 22 | #include <QTestEventLoop> | ||
4715 | 23 | #include <QSignalSpy> | ||
4716 | 24 | |||
4717 | 25 | using namespace std; | ||
4718 | 26 | using namespace testing; | ||
4719 | 27 | namespace mh = unity::gmenuharness; | ||
4720 | 28 | namespace | ||
4721 | 29 | { | ||
4722 | 30 | |||
4723 | 31 | class TestIndicator: public IndicatorSoundTestBase | ||
4724 | 32 | { | ||
4725 | 33 | }; | ||
4726 | 34 | |||
4727 | 35 | TEST_F(TestIndicator, PhoneChangeRoleVolume) | ||
4728 | 36 | { | ||
4729 | 37 | double INITIAL_VOLUME = 0.0; | ||
4730 | 38 | |||
4731 | 39 | ASSERT_NO_THROW(startAccountsService()); | ||
4732 | 40 | ASSERT_NO_THROW(startPulsePhone()); | ||
4733 | 41 | |||
4734 | 42 | // initialize volumes in pulseaudio | ||
4735 | 43 | EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); | ||
4736 | 44 | EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); | ||
4737 | 45 | |||
4738 | 46 | // start now the indicator, so it picks the new volumes | ||
4739 | 47 | ASSERT_NO_THROW(startIndicator()); | ||
4740 | 48 | |||
4741 | 49 | // Generate a random volume | ||
4742 | 50 | QTime now = QTime::currentTime(); | ||
4743 | 51 | qsrand(now.msec()); | ||
4744 | 52 | int randInt = qrand() % 100; | ||
4745 | 53 | double randomVolume = randInt / 100.0; | ||
4746 | 54 | |||
4747 | 55 | QSignalSpy &userAccountsSpy = *signal_spy_volume_changed_; | ||
4748 | 56 | // set an initial volume to the alert role | ||
4749 | 57 | userAccountsSpy.clear(); | ||
4750 | 58 | setStreamRestoreVolume("alert", 1.0); | ||
4751 | 59 | WAIT_FOR_SIGNALS(userAccountsSpy, 2); | ||
4752 | 60 | |||
4753 | 61 | userAccountsSpy.clear(); | ||
4754 | 62 | // play a test sound, it should change the role in the indicator | ||
4755 | 63 | EXPECT_TRUE(startTestSound("multimedia")); | ||
4756 | 64 | |||
4757 | 65 | // this time we only expect 1 signal as it's only the indicator | ||
4758 | 66 | // updating the value | ||
4759 | 67 | WAIT_FOR_SIGNALS(userAccountsSpy, 1); | ||
4760 | 68 | //EXPECT_TRUE(waitVolumeChangedInIndicator()); | ||
4761 | 69 | |||
4762 | 70 | userAccountsSpy.clear(); | ||
4763 | 71 | // set the random volume to the multimedia role | ||
4764 | 72 | EXPECT_TRUE(setStreamRestoreVolume("multimedia", randomVolume)); | ||
4765 | 73 | if (randomVolume != INITIAL_VOLUME) | ||
4766 | 74 | { | ||
4767 | 75 | WAIT_FOR_SIGNALS(userAccountsSpy, 1); | ||
4768 | 76 | } | ||
4769 | 77 | |||
4770 | 78 | // check the indicator | ||
4771 | 79 | EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) | ||
4772 | 80 | .item(mh::MenuItemMatcher() | ||
4773 | 81 | .action("indicator.root") | ||
4774 | 82 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4775 | 83 | .string_attribute("x-canonical-scroll-action", "indicator.scroll") | ||
4776 | 84 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4777 | 85 | .string_attribute("submenu-action", "indicator.indicator-shown") | ||
4778 | 86 | .mode(mh::MenuItemMatcher::Mode::starts_with) | ||
4779 | 87 | .submenu() | ||
4780 | 88 | .item(mh::MenuItemMatcher() | ||
4781 | 89 | .section() | ||
4782 | 90 | .item(silentModeSwitch(false)) | ||
4783 | 91 | .item(volumeSlider(randomVolume, "Volume")) | ||
4784 | 92 | ) | ||
4785 | 93 | ).match()); | ||
4786 | 94 | |||
4787 | 95 | // check that the last item is Sound Settings | ||
4788 | 96 | EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) | ||
4789 | 97 | .item(mh::MenuItemMatcher() | ||
4790 | 98 | .action("indicator.root") | ||
4791 | 99 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4792 | 100 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4793 | 101 | .mode(mh::MenuItemMatcher::Mode::ends_with) | ||
4794 | 102 | .submenu() | ||
4795 | 103 | .item(mh::MenuItemMatcher() | ||
4796 | 104 | .label("Sound Settings…") | ||
4797 | 105 | .action("indicator.phone-settings") | ||
4798 | 106 | ) | ||
4799 | 107 | ).match()); | ||
4800 | 108 | |||
4801 | 109 | userAccountsSpy.clear(); | ||
4802 | 110 | // stop the test sound, the role should change again to alert | ||
4803 | 111 | stopTestSound(); | ||
4804 | 112 | if (randomVolume != 1.0) | ||
4805 | 113 | { | ||
4806 | 114 | // we only wait if the volume in the alert and the | ||
4807 | 115 | // one set in the multimedia roles differ | ||
4808 | 116 | WAIT_FOR_SIGNALS(userAccountsSpy, 1); | ||
4809 | 117 | } | ||
4810 | 118 | |||
4811 | 119 | // check the initial volume for the alert role | ||
4812 | 120 | EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) | ||
4813 | 121 | .item(mh::MenuItemMatcher() | ||
4814 | 122 | .action("indicator.root") | ||
4815 | 123 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4816 | 124 | .string_attribute("x-canonical-scroll-action", "indicator.scroll") | ||
4817 | 125 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4818 | 126 | .string_attribute("submenu-action", "indicator.indicator-shown") | ||
4819 | 127 | .mode(mh::MenuItemMatcher::Mode::starts_with) | ||
4820 | 128 | .submenu() | ||
4821 | 129 | .item(mh::MenuItemMatcher() | ||
4822 | 130 | .section() | ||
4823 | 131 | .item(silentModeSwitch(false)) | ||
4824 | 132 | .item(volumeSlider(1.0, "Volume")) | ||
4825 | 133 | ) | ||
4826 | 134 | ).match()); | ||
4827 | 135 | |||
4828 | 136 | // check that the last item is Sound Settings | ||
4829 | 137 | EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) | ||
4830 | 138 | .item(mh::MenuItemMatcher() | ||
4831 | 139 | .action("indicator.root") | ||
4832 | 140 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4833 | 141 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4834 | 142 | .mode(mh::MenuItemMatcher::Mode::ends_with) | ||
4835 | 143 | .submenu() | ||
4836 | 144 | .item(mh::MenuItemMatcher() | ||
4837 | 145 | .label("Sound Settings…") | ||
4838 | 146 | .action("indicator.phone-settings") | ||
4839 | 147 | ) | ||
4840 | 148 | ).match()); | ||
4841 | 149 | } | ||
4842 | 150 | |||
4843 | 151 | TEST_F(TestIndicator, PhoneBasicInitialVolume) | ||
4844 | 152 | { | ||
4845 | 153 | double INITIAL_VOLUME = 0.0; | ||
4846 | 154 | |||
4847 | 155 | ASSERT_NO_THROW(startAccountsService()); | ||
4848 | 156 | EXPECT_TRUE(clearGSettingsPlayers()); | ||
4849 | 157 | ASSERT_NO_THROW(startPulsePhone()); | ||
4850 | 158 | |||
4851 | 159 | // initialize volumes in pulseaudio | ||
4852 | 160 | EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); | ||
4853 | 161 | |||
4854 | 162 | // start now the indicator, so it picks the new volumes | ||
4855 | 163 | ASSERT_NO_THROW(startIndicator()); | ||
4856 | 164 | |||
4857 | 165 | EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) | ||
4858 | 166 | .item(mh::MenuItemMatcher() | ||
4859 | 167 | .action("indicator.root") | ||
4860 | 168 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4861 | 169 | .string_attribute("x-canonical-scroll-action", "indicator.scroll") | ||
4862 | 170 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4863 | 171 | .string_attribute("submenu-action", "indicator.indicator-shown") | ||
4864 | 172 | .mode(mh::MenuItemMatcher::Mode::all) | ||
4865 | 173 | .submenu() | ||
4866 | 174 | .item(mh::MenuItemMatcher() | ||
4867 | 175 | .section() | ||
4868 | 176 | .item(silentModeSwitch(false)) | ||
4869 | 177 | .item(volumeSlider(INITIAL_VOLUME, "Volume")) | ||
4870 | 178 | ) | ||
4871 | 179 | .item(mh::MenuItemMatcher() | ||
4872 | 180 | .label("Sound Settings…") | ||
4873 | 181 | .action("indicator.phone-settings") | ||
4874 | 182 | ) | ||
4875 | 183 | ).match()); | ||
4876 | 184 | } | ||
4877 | 185 | |||
4878 | 186 | TEST_F(TestIndicator, PhoneAddMprisPlayer) | ||
4879 | 187 | { | ||
4880 | 188 | double INITIAL_VOLUME = 0.0; | ||
4881 | 189 | |||
4882 | 190 | ASSERT_NO_THROW(startAccountsService()); | ||
4883 | 191 | EXPECT_TRUE(clearGSettingsPlayers()); | ||
4884 | 192 | ASSERT_NO_THROW(startPulsePhone()); | ||
4885 | 193 | |||
4886 | 194 | // initialize volumes in pulseaudio | ||
4887 | 195 | EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); | ||
4888 | 196 | |||
4889 | 197 | // start now the indicator, so it picks the new volumes | ||
4890 | 198 | ASSERT_NO_THROW(startIndicator()); | ||
4891 | 199 | |||
4892 | 200 | EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) | ||
4893 | 201 | .item(mh::MenuItemMatcher() | ||
4894 | 202 | .action("indicator.root") | ||
4895 | 203 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4896 | 204 | .string_attribute("x-canonical-scroll-action", "indicator.scroll") | ||
4897 | 205 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4898 | 206 | .string_attribute("submenu-action", "indicator.indicator-shown") | ||
4899 | 207 | .mode(mh::MenuItemMatcher::Mode::all) | ||
4900 | 208 | .submenu() | ||
4901 | 209 | .item(mh::MenuItemMatcher() | ||
4902 | 210 | .section() | ||
4903 | 211 | .item(silentModeSwitch(false)) | ||
4904 | 212 | .item(volumeSlider(INITIAL_VOLUME, "Volume")) | ||
4905 | 213 | ) | ||
4906 | 214 | .item(mh::MenuItemMatcher() | ||
4907 | 215 | .label("Sound Settings…") | ||
4908 | 216 | .action("indicator.phone-settings") | ||
4909 | 217 | ) | ||
4910 | 218 | ).match()); | ||
4911 | 219 | |||
4912 | 220 | // initialize the signal spy | ||
4913 | 221 | EXPECT_TRUE(initializeMenuChangedSignal()); | ||
4914 | 222 | |||
4915 | 223 | // start the test player | ||
4916 | 224 | EXPECT_TRUE(startTestMprisPlayer("testplayer1")); | ||
4917 | 225 | |||
4918 | 226 | // wait for the menu change | ||
4919 | 227 | EXPECT_TRUE(waitMenuChange()); | ||
4920 | 228 | |||
4921 | 229 | // finally verify that the player is added | ||
4922 | 230 | EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) | ||
4923 | 231 | .item(mh::MenuItemMatcher() | ||
4924 | 232 | .action("indicator.root") | ||
4925 | 233 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4926 | 234 | .string_attribute("x-canonical-scroll-action", "indicator.scroll") | ||
4927 | 235 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4928 | 236 | .string_attribute("submenu-action", "indicator.indicator-shown") | ||
4929 | 237 | .mode(mh::MenuItemMatcher::Mode::all) | ||
4930 | 238 | .submenu() | ||
4931 | 239 | .item(mh::MenuItemMatcher() | ||
4932 | 240 | .section() | ||
4933 | 241 | .item(silentModeSwitch(false)) | ||
4934 | 242 | .item(volumeSlider(INITIAL_VOLUME, "Volume")) | ||
4935 | 243 | ) | ||
4936 | 244 | .item(mh::MenuItemMatcher() | ||
4937 | 245 | .section() | ||
4938 | 246 | .item(mh::MenuItemMatcher() | ||
4939 | 247 | .action("indicator.testplayer1.desktop") | ||
4940 | 248 | .label("TestPlayer1") | ||
4941 | 249 | .themed_icon("icon", {"testplayer"}) | ||
4942 | 250 | .string_attribute("x-canonical-type", "com.canonical.unity.media-player") | ||
4943 | 251 | ) | ||
4944 | 252 | .item(mh::MenuItemMatcher() | ||
4945 | 253 | .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") | ||
4946 | 254 | .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") | ||
4947 | 255 | .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop") | ||
4948 | 256 | .string_attribute("x-canonical-type","com.canonical.unity.playback-item") | ||
4949 | 257 | ) | ||
4950 | 258 | ) | ||
4951 | 259 | .item(mh::MenuItemMatcher() | ||
4952 | 260 | .label("Sound Settings…") | ||
4953 | 261 | .action("indicator.phone-settings") | ||
4954 | 262 | ) | ||
4955 | 263 | ).match()); | ||
4956 | 264 | } | ||
4957 | 265 | |||
4958 | 266 | TEST_F(TestIndicator, DesktopBasicInitialVolume) | ||
4959 | 267 | { | ||
4960 | 268 | double INITIAL_VOLUME = 0.0; | ||
4961 | 269 | |||
4962 | 270 | ASSERT_NO_THROW(startAccountsService()); | ||
4963 | 271 | EXPECT_TRUE(clearGSettingsPlayers()); | ||
4964 | 272 | ASSERT_NO_THROW(startPulseDesktop()); | ||
4965 | 273 | |||
4966 | 274 | // initialize volumes in pulseaudio | ||
4967 | 275 | EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); | ||
4968 | 276 | EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME)); | ||
4969 | 277 | |||
4970 | 278 | // start the test player | ||
4971 | 279 | EXPECT_TRUE(startTestMprisPlayer("testplayer1")); | ||
4972 | 280 | |||
4973 | 281 | // start now the indicator, so it picks the new volumes | ||
4974 | 282 | ASSERT_NO_THROW(startIndicator()); | ||
4975 | 283 | |||
4976 | 284 | EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters()) | ||
4977 | 285 | .item(mh::MenuItemMatcher() | ||
4978 | 286 | .action("indicator.root") | ||
4979 | 287 | .string_attribute("x-canonical-type", "com.canonical.indicator.root") | ||
4980 | 288 | .string_attribute("x-canonical-secondary-action", "indicator.mute") | ||
4981 | 289 | .mode(mh::MenuItemMatcher::Mode::all) | ||
4982 | 290 | .submenu() | ||
4983 | 291 | .item(mh::MenuItemMatcher() | ||
4984 | 292 | .section() | ||
4985 | 293 | .item(mh::MenuItemMatcher().checkbox() | ||
4986 | 294 | .label("Mute") | ||
4987 | 295 | ) | ||
4988 | 296 | .item(volumeSlider(INITIAL_VOLUME, "Volume")) | ||
4989 | 297 | ) | ||
4990 | 298 | .item(mh::MenuItemMatcher() | ||
4991 | 299 | .section() | ||
4992 | 300 | .item(mh::MenuItemMatcher() | ||
4993 | 301 | .action("indicator.testplayer1.desktop") | ||
4994 | 302 | .label("TestPlayer1") | ||
4995 | 303 | .themed_icon("icon", {"testplayer"}) | ||
4996 | 304 | .string_attribute("x-canonical-type", "com.canonical.unity.media-player") | ||
4997 | 305 | ) | ||
4998 | 306 | .item(mh::MenuItemMatcher() | ||
4999 | 307 | .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop") | ||
5000 | 308 | .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop") |
PASSED: Continuous integration, rev:511 jenkins. qa.ubuntu. com/job/ indicator- sound-ci/ 266/ jenkins. qa.ubuntu. com/job/ indicator- sound-wily- amd64-ci/ 42 jenkins. qa.ubuntu. com/job/ indicator- sound-wily- armhf-ci/ 42 jenkins. qa.ubuntu. com/job/ indicator- sound-wily- armhf-ci/ 42/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/indicator- sound-ci/ 266/rebuild
http://