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