Merge lp:~xavi-garcia-mena/indicator-sound/bug-1213907-active-players-playback-control-unit-tests into lp:indicator-sound/15.10

Proposed by Xavi Garcia
Status: Needs review
Proposed branch: lp:~xavi-garcia-mena/indicator-sound/bug-1213907-active-players-playback-control-unit-tests
Merge into: lp:indicator-sound/15.10
Diff against target: 750 lines (+682/-0)
7 files modified
tests/integration/CMakeLists.txt (+1/-0)
tests/integration/indicator-sound-test-base.cpp (+5/-0)
tests/integration/indicator-sound-test-base.h (+1/-0)
tests/integration/test-indicator.cpp (+274/-0)
tests/integration/utils/gobj_memory.h (+200/-0)
tests/integration/utils/gsettings.cpp (+149/-0)
tests/integration/utils/gsettings.h (+52/-0)
To merge this branch: bzr merge lp:~xavi-garcia-mena/indicator-sound/bug-1213907-active-players-playback-control-unit-tests
Reviewer Review Type Date Requested Status
Charles Kerr (community) Needs Information
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+286183@code.launchpad.net

Commit message

This branch adds extra integration tests to the recently landed branch https://code.launchpad.net/~xavi-garcia-mena/indicator-sound/bug-1213907-active-players-playback-control

It adds GSettings tests and checks the last-running-player property.

Description of the change

This branch adds extra integration tests to the recently landed branch https://code.launchpad.net/~xavi-garcia-mena/indicator-sound/bug-1213907-active-players-playback-control

It adds GSettings tests and checks the last-running-player property.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
529. By Xavi Garcia

Integration test moved

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

Comments inline

review: Needs Information

Unmerged revisions

529. By Xavi Garcia

Integration test moved

528. By Xavi Garcia

Added integration tests

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/integration/CMakeLists.txt'
2--- tests/integration/CMakeLists.txt 2015-12-23 13:35:46 +0000
3+++ tests/integration/CMakeLists.txt 2016-02-17 09:23:14 +0000
4@@ -51,6 +51,7 @@
5 indicator-sound-test-base.cpp
6 test-indicator.cpp
7 utils/dbus-pulse-volume.cpp
8+ utils/gsettings.cpp
9 main.cpp
10 )
11
12
13=== modified file 'tests/integration/indicator-sound-test-base.cpp'
14--- tests/integration/indicator-sound-test-base.cpp 2016-01-29 11:16:34 +0000
15+++ tests/integration/indicator-sound-test-base.cpp 2016-02-17 09:23:14 +0000
16@@ -380,6 +380,11 @@
17 }
18 }
19
20+void IndicatorSoundTestBase::stopIndicator()
21+{
22+ indicator.reset();
23+}
24+
25 mh::MenuMatcher::Parameters IndicatorSoundTestBase::desktopParameters()
26 {
27 return mh::MenuMatcher::Parameters(
28
29=== modified file 'tests/integration/indicator-sound-test-base.h'
30--- tests/integration/indicator-sound-test-base.h 2016-01-29 11:16:34 +0000
31+++ tests/integration/indicator-sound-test-base.h 2016-02-17 09:23:14 +0000
32@@ -72,6 +72,7 @@
33 void TearDown() override;
34
35 void startIndicator();
36+ void stopIndicator();
37 void startPulseDesktop(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED);
38 void startPulsePhone(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED);
39 void startAccountsService();
40
41=== modified file 'tests/integration/test-indicator.cpp'
42--- tests/integration/test-indicator.cpp 2016-02-10 13:08:49 +0000
43+++ tests/integration/test-indicator.cpp 2016-02-17 09:23:14 +0000
44@@ -17,11 +17,15 @@
45 */
46
47 #include <indicator-sound-test-base.h>
48+#include <utils/gsettings.h>
49
50 #include <QDebug>
51 #include <QTestEventLoop>
52 #include <QSignalSpy>
53
54+#include <chrono>
55+#include <thread>
56+
57 using namespace std;
58 using namespace testing;
59 namespace mh = unity::gmenuharness;
60@@ -1553,4 +1557,274 @@
61 checkPortDevicesLabels(HDMI, HDMI);
62 }
63
64+TEST_F(TestIndicator, DesktopMprisPlayersPlaybackControlsActive)
65+{
66+ double INITIAL_VOLUME = 0.0;
67+
68+ ASSERT_NO_THROW(startAccountsService());
69+ EXPECT_TRUE(clearGSettingsPlayers());
70+ ASSERT_NO_THROW(startPulseDesktop());
71+
72+ // initialize volumes in pulseaudio
73+ EXPECT_FALSE(setStreamRestoreVolume("alert", INITIAL_VOLUME));
74+ EXPECT_TRUE(setSinkVolume(INITIAL_VOLUME));
75+
76+ // start the test player
77+ EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
78+
79+ // start now the indicator, so it picks the new volumes
80+ ASSERT_NO_THROW(startIndicator());
81+
82+ // check that the player is added
83+ EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
84+ .item(mh::MenuItemMatcher()
85+ .action("indicator.root")
86+ .string_attribute("x-canonical-type", "com.canonical.indicator.root")
87+ .string_attribute("x-canonical-secondary-action", "indicator.mute")
88+ .mode(mh::MenuItemMatcher::Mode::all)
89+ .submenu()
90+ .item(mh::MenuItemMatcher()
91+ .section()
92+ .item(mh::MenuItemMatcher().checkbox()
93+ .label("Mute")
94+ )
95+ .item(volumeSlider(INITIAL_VOLUME, "Volume"))
96+ )
97+ .item(mh::MenuItemMatcher()
98+ .section()
99+ .item(mh::MenuItemMatcher()
100+ .action("indicator.testplayer1.desktop")
101+ .label("TestPlayer1")
102+ .themed_icon("icon", {"testplayer"})
103+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
104+ )
105+ .item(mh::MenuItemMatcher()
106+ .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop")
107+ .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop")
108+ .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop")
109+ .string_attribute("x-canonical-type","com.canonical.unity.playback-item")
110+ )
111+ )
112+ .item(mh::MenuItemMatcher()
113+ .label("Sound Settings…")
114+ )
115+ ).match());
116+
117+ // start the second test player
118+ EXPECT_TRUE(startTestMprisPlayer("testplayer2"));
119+
120+ // check that the player is added
121+ EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
122+ .item(mh::MenuItemMatcher()
123+ .action("indicator.root")
124+ .string_attribute("x-canonical-type", "com.canonical.indicator.root")
125+ .string_attribute("x-canonical-secondary-action", "indicator.mute")
126+ .mode(mh::MenuItemMatcher::Mode::all)
127+ .submenu()
128+ .item(mh::MenuItemMatcher()
129+ .section()
130+ .item(mh::MenuItemMatcher().checkbox()
131+ .label("Mute")
132+ )
133+ .item(volumeSlider(INITIAL_VOLUME, "Volume"))
134+ )
135+ .item(mh::MenuItemMatcher()
136+ .section()
137+ .item(mh::MenuItemMatcher()
138+ .action("indicator.testplayer1.desktop")
139+ .label("TestPlayer1")
140+ .themed_icon("icon", {"testplayer"})
141+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
142+ )
143+ .item(mh::MenuItemMatcher()
144+ .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop")
145+ .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop")
146+ .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop")
147+ .string_attribute("x-canonical-type","com.canonical.unity.playback-item")
148+ )
149+ )
150+ .item(mh::MenuItemMatcher()
151+ .section()
152+ .item(mh::MenuItemMatcher()
153+ .action("indicator.testplayer2.desktop")
154+ .label("TestPlayer2")
155+ .themed_icon("icon", {"testplayer"})
156+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
157+ )
158+ .item(mh::MenuItemMatcher()
159+ .string_attribute("x-canonical-previous-action","indicator.previous.testplayer2.desktop")
160+ .string_attribute("x-canonical-play-action","indicator.play.testplayer2.desktop")
161+ .string_attribute("x-canonical-next-action","indicator.next.testplayer2.desktop")
162+ .string_attribute("x-canonical-type","com.canonical.unity.playback-item")
163+ )
164+ )
165+ .item(mh::MenuItemMatcher()
166+ .label("Sound Settings…")
167+ )
168+ ).match());
169+
170+ // stop the first player
171+ EXPECT_TRUE(stopTestMprisPlayer("testplayer1"));
172+
173+ // check that the player's playback controls are removed
174+ EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
175+ .item(mh::MenuItemMatcher()
176+ .action("indicator.root")
177+ .string_attribute("x-canonical-type", "com.canonical.indicator.root")
178+ .string_attribute("x-canonical-secondary-action", "indicator.mute")
179+ .mode(mh::MenuItemMatcher::Mode::all)
180+ .submenu()
181+ .item(mh::MenuItemMatcher()
182+ .section()
183+ .item(mh::MenuItemMatcher().checkbox()
184+ .label("Mute")
185+ )
186+ .item(volumeSlider(INITIAL_VOLUME, "Volume"))
187+ )
188+ .item(mh::MenuItemMatcher()
189+ .section()
190+ .item(mh::MenuItemMatcher()
191+ .action("indicator.testplayer1.desktop")
192+ .label("TestPlayer1")
193+ .themed_icon("icon", {"testplayer"})
194+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
195+ )
196+ )
197+ .item(mh::MenuItemMatcher()
198+ .section()
199+ .item(mh::MenuItemMatcher()
200+ .action("indicator.testplayer2.desktop")
201+ .label("TestPlayer2")
202+ .themed_icon("icon", {"testplayer"})
203+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
204+ )
205+ .item(mh::MenuItemMatcher()
206+ .string_attribute("x-canonical-previous-action","indicator.previous.testplayer2.desktop")
207+ .string_attribute("x-canonical-play-action","indicator.play.testplayer2.desktop")
208+ .string_attribute("x-canonical-next-action","indicator.next.testplayer2.desktop")
209+ .string_attribute("x-canonical-type","com.canonical.unity.playback-item")
210+ )
211+ )
212+ .item(mh::MenuItemMatcher()
213+ .label("Sound Settings…")
214+ )
215+ ).match());
216+
217+ // stop the second player (it should be the last running one)
218+ EXPECT_TRUE(stopTestMprisPlayer("testplayer2"));
219+
220+ // verify the players with gsettings
221+ Settings settings;
222+
223+ int timeout = 1000;
224+ while (settings.get_last_running_player() != "testplayer2.desktop" && timeout)
225+ {
226+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
227+ timeout -= 500;
228+ }
229+ EXPECT_EQ("testplayer2.desktop", settings.get_last_running_player());
230+
231+ timeout = 1000;
232+ while (settings.get_interested_media_players() != "[[ testplayer1.desktop ][ testplayer2.desktop ]]" && timeout)
233+ {
234+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
235+ timeout -= 500;
236+ }
237+ EXPECT_EQ("[[ testplayer1.desktop ][ testplayer2.desktop ]]", settings.get_interested_media_players());
238+
239+ // restart the indicator
240+ // we should get the player 2 as the one with playback constrols.
241+ // In this case we only have the play button as the player is not running yet.
242+ startIndicator();
243+
244+ // check that the player 2 has the playback controls with play set.
245+ EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
246+ .item(mh::MenuItemMatcher()
247+ .action("indicator.root")
248+ .string_attribute("x-canonical-type", "com.canonical.indicator.root")
249+ .string_attribute("x-canonical-secondary-action", "indicator.mute")
250+ .mode(mh::MenuItemMatcher::Mode::all)
251+ .submenu()
252+ .item(mh::MenuItemMatcher()
253+ .section()
254+ .item(mh::MenuItemMatcher().checkbox()
255+ .label("Mute")
256+ )
257+ .item(volumeSlider(INITIAL_VOLUME, "Volume"))
258+ )
259+ .item(mh::MenuItemMatcher()
260+ .section()
261+ .item(mh::MenuItemMatcher()
262+ .action("indicator.testplayer1.desktop")
263+ .label("TestPlayer1")
264+ .themed_icon("icon", {"testplayer"})
265+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
266+ )
267+ )
268+ .item(mh::MenuItemMatcher()
269+ .section()
270+ .item(mh::MenuItemMatcher()
271+ .action("indicator.testplayer2.desktop")
272+ .label("TestPlayer2")
273+ .themed_icon("icon", {"testplayer"})
274+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
275+ )
276+ .item(mh::MenuItemMatcher()
277+ .string_attribute("x-canonical-play-action","indicator.play.testplayer2.desktop")
278+ .string_attribute("x-canonical-type","com.canonical.unity.playback-item")
279+ )
280+ )
281+ .item(mh::MenuItemMatcher()
282+ .label("Sound Settings…")
283+ )
284+ ).match());
285+
286+ // now start the other player.
287+ // that should change the playback controls shown from player 2 to player 1
288+ EXPECT_TRUE(startTestMprisPlayer("testplayer1"));
289+
290+ EXPECT_MATCHRESULT(mh::MenuMatcher(desktopParameters())
291+ .item(mh::MenuItemMatcher()
292+ .action("indicator.root")
293+ .string_attribute("x-canonical-type", "com.canonical.indicator.root")
294+ .string_attribute("x-canonical-secondary-action", "indicator.mute")
295+ .mode(mh::MenuItemMatcher::Mode::all)
296+ .submenu()
297+ .item(mh::MenuItemMatcher()
298+ .section()
299+ .item(mh::MenuItemMatcher().checkbox()
300+ .label("Mute")
301+ )
302+ .item(volumeSlider(INITIAL_VOLUME, "Volume"))
303+ )
304+ .item(mh::MenuItemMatcher()
305+ .section()
306+ .item(mh::MenuItemMatcher()
307+ .action("indicator.testplayer1.desktop")
308+ .label("TestPlayer1")
309+ .themed_icon("icon", {"testplayer"})
310+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
311+ )
312+ .item(mh::MenuItemMatcher()
313+ .string_attribute("x-canonical-previous-action","indicator.previous.testplayer1.desktop")
314+ .string_attribute("x-canonical-play-action","indicator.play.testplayer1.desktop")
315+ .string_attribute("x-canonical-next-action","indicator.next.testplayer1.desktop")
316+ .string_attribute("x-canonical-type","com.canonical.unity.playback-item")
317+ )
318+ )
319+ .item(mh::MenuItemMatcher()
320+ .section()
321+ .item(mh::MenuItemMatcher()
322+ .action("indicator.testplayer2.desktop")
323+ .label("TestPlayer2")
324+ .themed_icon("icon", {"testplayer"})
325+ .string_attribute("x-canonical-type", "com.canonical.unity.media-player")
326+ )
327+ )
328+ .item(mh::MenuItemMatcher()
329+ .label("Sound Settings…")
330+ )
331+ ).match());
332+}
333+
334 } // namespace
335
336=== added file 'tests/integration/utils/gobj_memory.h'
337--- tests/integration/utils/gobj_memory.h 1970-01-01 00:00:00 +0000
338+++ tests/integration/utils/gobj_memory.h 2016-02-17 09:23:14 +0000
339@@ -0,0 +1,200 @@
340+/*
341+ * Copyright (C) 2013 Canonical Ltd.
342+ *
343+ * This program is free software: you can redistribute it and/or modify
344+ * it under the terms of the GNU Lesser General Public License version 3 as
345+ * published by the Free Software Foundation.
346+ *
347+ * This program is distributed in the hope that it will be useful,
348+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
349+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
350+ * GNU Lesser General Public License for more details.
351+ *
352+ * You should have received a copy of the GNU Lesser General Public License
353+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
354+ *
355+ * Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com>
356+ */
357+
358+#pragma once
359+
360+#include <stdexcept>
361+
362+#pragma GCC diagnostic push
363+#pragma GCC diagnostic ignored "-Wold-style-cast"
364+#pragma GCC diagnostic ignored "-Wcast-qual"
365+#include <glib-object.h>
366+
367+#include <iostream>
368+
369+/**
370+ * This class is meant for automatically managing the lifetime of C objects derived
371+ * from gobject. Its API perfectly mirrors the API of unique_ptr except that you
372+ * can't define your own deleter function as it is always g_object_unref.
373+ *
374+ * API/ABI stability is not guaranteed. If you need to pass the object across an ABI
375+ * boundary, pass the plain gobject.
376+ *
377+ * This is how you would use gobj_ptr 99% of the time:
378+ *
379+ * gobj_ptr<GSomeType> o(g_some_type_new(...));
380+ *
381+ * More specifically, the object will decrement the gobject reference count
382+ * of the object it points to when it goes out of scope. It will never increment it.
383+ * Thus you should only assign to it when already holding a reference. gobj_ptr
384+ * will then take ownership of that particular reference.
385+ *
386+ * Floating gobjects can not be put in this container as they are meant to be put
387+ * into native gobject aware containers immediately upon construction. Trying to insert
388+ * a floating gobject into a gobj_ptr will throw an invalid_argument exception. To
389+ * prevent accidental memory leaks, the floating gobject is unreffed in this case.
390+ */
391+template <typename T>
392+class gobj_ptr final
393+{
394+private:
395+ T* u;
396+
397+ void validate_float(T* t)
398+ {
399+ if (t != nullptr && g_object_is_floating(G_OBJECT(t)))
400+ {
401+ // LCOV_EXCL_START // False negative from gcovr.
402+ throw std::invalid_argument("Tried to add a floating gobject into a gobj_ptr.");
403+ // LCOV_EXCL_STOP
404+ }
405+ }
406+
407+public:
408+ typedef T element_type;
409+ typedef T* pointer;
410+ typedef decltype(g_object_unref) deleter_type;
411+
412+ constexpr gobj_ptr() noexcept : u(nullptr)
413+ {
414+ }
415+ explicit gobj_ptr(T* t)
416+ : u(t)
417+ {
418+ // What should we do if validate throws? Unreffing unknown objs
419+ // is dodgy but not unreffing runs the risk of
420+ // memory leaks. Currently unrefs as u is destroyed
421+ // when this exception is thrown.
422+ validate_float(t);
423+ }
424+ constexpr gobj_ptr(std::nullptr_t) noexcept : u(nullptr){};
425+ gobj_ptr(gobj_ptr&& o) noexcept
426+ {
427+ u = o.u;
428+ o.u = nullptr;
429+ }
430+ gobj_ptr(const gobj_ptr& o)
431+ : u(nullptr)
432+ {
433+ *this = o;
434+ }
435+ gobj_ptr& operator=(const gobj_ptr& o)
436+ {
437+ if (o.u != nullptr)
438+ {
439+ g_object_ref(o.u);
440+ }
441+ reset(o.u);
442+ return *this;
443+ }
444+ ~gobj_ptr()
445+ {
446+ reset();
447+ }
448+
449+ deleter_type& get_deleter() noexcept
450+ {
451+ return g_object_unref;
452+ }
453+ deleter_type& get_deleter() const noexcept
454+ {
455+ return g_object_unref;
456+ }
457+
458+ void swap(gobj_ptr<T>& o) noexcept
459+ {
460+ T* tmp = u;
461+ u = o.u;
462+ o.u = tmp;
463+ }
464+ void reset(pointer p = pointer())
465+ {
466+ if (u != nullptr)
467+ {
468+ g_object_unref(G_OBJECT(u));
469+ u = nullptr;
470+ }
471+ // Same throw dilemma as in pointer constructor.
472+ u = p;
473+ validate_float(p);
474+ }
475+
476+ T* release() noexcept
477+ {
478+ T* r = u;
479+ u = nullptr;
480+ return r;
481+ }
482+ T* get() const noexcept
483+ {
484+ return u;
485+ }
486+
487+ T& operator*() const
488+ {
489+ return *u;
490+ }
491+ T* operator->() const noexcept
492+ {
493+ return u;
494+ }
495+ explicit operator bool() const noexcept
496+ {
497+ return u != nullptr;
498+ }
499+
500+ gobj_ptr& operator=(gobj_ptr&& o) noexcept
501+ {
502+ reset();
503+ u = o.u;
504+ o.u = nullptr;
505+ return *this;
506+ }
507+ gobj_ptr& operator=(std::nullptr_t) noexcept
508+ {
509+ reset();
510+ return *this;
511+ }
512+ bool operator==(const gobj_ptr<T>& o) const noexcept
513+ {
514+ return u == o.u;
515+ }
516+ bool operator!=(const gobj_ptr<T>& o) const noexcept
517+ {
518+ return u != o.u;
519+ }
520+ bool operator<(const gobj_ptr<T>& o) const noexcept
521+ {
522+ return u < o.u;
523+ }
524+ bool operator<=(const gobj_ptr<T>& o) const noexcept
525+ {
526+ return u <= o.u;
527+ }
528+ bool operator>(const gobj_ptr<T>& o) const noexcept
529+ {
530+ return u > o.u;
531+ }
532+ bool operator>=(const gobj_ptr<T>& o) const noexcept
533+ {
534+ return u >= o.u;
535+ }
536+};
537+
538+#pragma GCC diagnostic pop
539+
540
541=== added file 'tests/integration/utils/gsettings.cpp'
542--- tests/integration/utils/gsettings.cpp 1970-01-01 00:00:00 +0000
543+++ tests/integration/utils/gsettings.cpp 2016-02-17 09:23:14 +0000
544@@ -0,0 +1,149 @@
545+/*
546+ * Copyright (C) 2016 Canonical Ltd.
547+ *
548+ * This program is free software: you can redistribute it and/or modify
549+ * it under the terms of the GNU General Public License version 3 as
550+ * published by the Free Software Foundation.
551+ *
552+ * This program is distributed in the hope that it will be useful,
553+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
554+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
555+ * GNU General Public License for more details.
556+ *
557+ * You should have received a copy of the GNU General Public License
558+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
559+ *
560+ * Authored by: Xavi Garcia <xavi.garcia.mena@canonical.com>
561+ */
562+
563+#include "gsettings.h"
564+
565+//#include "settings-defaults.h"
566+
567+#pragma GCC diagnostic push
568+#pragma GCC diagnostic ignored "-Wold-style-cast"
569+#pragma GCC diagnostic ignored "-Wcast-qual"
570+#include <gio/gio.h>
571+#pragma GCC diagnostic pop
572+#include <QDebug>
573+
574+#include <memory>
575+
576+using namespace std;
577+
578+Settings::Settings()
579+ : Settings("com.canonical.indicator.sound")
580+{
581+}
582+
583+Settings::Settings(string const& schema_name)
584+ : schema_(nullptr, &g_settings_schema_unref)
585+ , schema_name_(schema_name)
586+{
587+ GSettingsSchemaSource* src = g_settings_schema_source_get_default();
588+ schema_.reset(g_settings_schema_source_lookup(src, schema_name.c_str(), true));
589+ if (schema_)
590+ {
591+ settings_.reset(g_settings_new(schema_name.c_str()));
592+ }
593+ else
594+ {
595+ qCritical() << "The schema" << schema_name.c_str() << "is missing";
596+ }
597+}
598+
599+Settings::~Settings() = default;
600+
601+string Settings::get_last_running_player() const
602+{
603+ return get_string("last-running-player", "");
604+}
605+
606+string Settings::get_interested_media_players() const
607+{
608+ return get_string_array_as_string("interested-media-players", "");
609+}
610+
611+string Settings::get_string(char const* key, string const& default_value) const
612+{
613+ if (!settings_ || !g_settings_schema_has_key(schema_.get(), key))
614+ {
615+ return default_value;
616+ }
617+
618+ char *value = g_settings_get_string(settings_.get(), key);
619+ if (value)
620+ {
621+ string result = value;
622+ g_free(value);
623+ return result;
624+ }
625+ return default_value; // LCOV_EXCL_LINE
626+}
627+
628+int Settings::get_positive_int(char const* key, int default_value) const
629+{
630+ int i = get_int(key, default_value);
631+ if (i <= 0)
632+ {
633+ throw domain_error(string("Settings::get_positive_int(): invalid zero or negative value for ")
634+ + key + ": " + to_string(i) + " in schema " + schema_name_);
635+ }
636+ return i;
637+}
638+
639+int Settings::get_positive_or_zero_int(char const* key, int default_value) const
640+{
641+ int i = get_int(key, default_value);
642+ if (i < 0)
643+ {
644+ throw domain_error(string("Settings::get_positive_or_zero_int(): invalid negative value for ")
645+ + key + ": " + to_string(i) + " in schema " + schema_name_);
646+ }
647+ return i;
648+}
649+
650+int Settings::get_int(char const* key, int default_value) const
651+{
652+ if (!settings_ || !g_settings_schema_has_key(schema_.get(), key))
653+ {
654+ return default_value;
655+ }
656+
657+ return g_settings_get_int(settings_.get(), key);
658+}
659+
660+bool Settings::get_bool(char const* key, bool default_value) const
661+{
662+ if (!settings_ || !g_settings_schema_has_key(schema_.get(), key))
663+ {
664+ return default_value;
665+ }
666+
667+ return g_settings_get_boolean(settings_.get(), key);
668+}
669+
670+std::string Settings::get_string_array_as_string(char const* key, std::string const& default_value) const
671+{
672+ if (!settings_ || !g_settings_schema_has_key(schema_.get(), key))
673+ {
674+ return default_value;
675+ }
676+ gchar **values;
677+ values = g_settings_get_strv(settings_.get(), key);
678+ if (!values)
679+ {
680+ return default_value;
681+ }
682+ int length = 0;
683+ std::string res = "[";
684+ while (((gpointer*) values)[length])
685+ {
686+ std::string test(values[length]);
687+ length++;
688+ res += "[ " + test + " ]";
689+ }
690+ res += "]";
691+
692+ return res;
693+}
694
695=== added file 'tests/integration/utils/gsettings.h'
696--- tests/integration/utils/gsettings.h 1970-01-01 00:00:00 +0000
697+++ tests/integration/utils/gsettings.h 2016-02-17 09:23:14 +0000
698@@ -0,0 +1,52 @@
699+/*
700+ * Copyright (C) 2016 Canonical Ltd.
701+ *
702+ * This program is free software: you can redistribute it and/or modify
703+ * it under the terms of the GNU General Public License version 3 as
704+ * published by the Free Software Foundation.
705+ *
706+ * This program is distributed in the hope that it will be useful,
707+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
708+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
709+ * GNU General Public License for more details.
710+ *
711+ * You should have received a copy of the GNU General Public License
712+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
713+ *
714+ * Authored by: Xavi Garcia <xavi.garcia.mena@canonical.com>
715+ */
716+
717+#pragma once
718+
719+#include "gobj_memory.h"
720+
721+#include <memory>
722+#include <string>
723+
724+typedef struct _GSettings GSettings;
725+typedef struct _GSettingsSchema GSettingsSchema;
726+
727+class Settings
728+{
729+public:
730+ Settings();
731+ // This constructor is only for use in the tests.
732+ explicit Settings(std::string const& schema_name);
733+ ~Settings();
734+
735+ std::string get_last_running_player() const;
736+
737+ std::string get_interested_media_players() const;
738+
739+private:
740+ std::string get_string(char const* key, std::string const& default_value) const;
741+ int get_positive_int(char const* key, int default_value) const;
742+ int get_positive_or_zero_int(char const* key, int default_value) const;
743+ int get_int(char const* key, int default_value) const;
744+ bool get_bool(char const* key, bool default_value) const;
745+ std::string get_string_array_as_string(char const* key, std::string const& default_value) const;
746+
747+ std::unique_ptr<GSettingsSchema, void(*)(GSettingsSchema*)> schema_;
748+ std::string schema_name_;
749+ gobj_ptr<GSettings> settings_;
750+};

Subscribers

People subscribed via source and target branches