Merge lp:~pete-woods/unity-api/add-signal-managers into lp:unity-api

Proposed by Pete Woods
Status: Merged
Approved by: Pete Woods
Approved revision: 299
Merged at revision: 282
Proposed branch: lp:~pete-woods/unity-api/add-signal-managers
Merge into: lp:unity-api
Prerequisite: lp:~pete-woods/unity-api/add-glib-assigner-class
Diff against target: 491 lines (+325/-0)
11 files modified
debian/control (+1/-0)
include/unity/util/GObjectMemory.h (+33/-0)
include/unity/util/GioMemory.h (+72/-0)
include/unity/util/GlibMemory.h (+28/-0)
include/unity/util/ResourcePtr.h (+10/-0)
test/gtest/unity/util/CMakeLists.txt (+1/-0)
test/gtest/unity/util/GObjectMemory/GObjectMemory_test.cpp (+34/-0)
test/gtest/unity/util/GioMemory/CMakeLists.txt (+30/-0)
test/gtest/unity/util/GioMemory/GioMemory_test.cpp (+114/-0)
test/headers/CMakeLists.txt (+1/-0)
test/headers/includechecker.py (+1/-0)
To merge this branch: bzr merge lp:~pete-woods/unity-api/add-signal-managers
Reviewer Review Type Date Requested Status
Unity8 CI Bot continuous-integration Needs Fixing
Michi Henning (community) Approve
Review via email: mp+320961@code.launchpad.net

Commit message

unity::util - Add glib signal managers

Description of the change

unity::util - Add glib signal managers

To post a comment you must log in.
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:295
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/165/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4698
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4726
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4549
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4549/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4549
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4549/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4549
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4549/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4549
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4549/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4549
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4549/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4549
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4549/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity-api-ci/165/rebuild

review: Approve (continuous-integration)
Revision history for this message
Michi Henning (michihenning) wrote :

This looks sensible to me. I'd prefer if someone with more glib mojo than me could top-approve though.

review: Approve
294. By Pete Woods

Fix null assignment behaviour in assigner classes

295. By Pete Woods

Tweak docs

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
296. By Pete Woods

Fix whitespace error

298. By Pete Woods

Fix up tests, use libqtdbustest for temporary test bus

299. By Pete Woods

Add docs

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2016-12-23 09:14:43 +0000
3+++ debian/control 2017-04-04 09:34:42 +0000
4@@ -14,6 +14,7 @@
5 graphviz,
6 libglib2.0-dev,
7 libgtest-dev,
8+ libqtdbustest1-dev,
9 pkg-config,
10 python3:any,
11 qt5-default,
12
13=== modified file 'include/unity/util/GObjectMemory.h'
14--- include/unity/util/GObjectMemory.h 2017-04-04 09:34:42 +0000
15+++ include/unity/util/GObjectMemory.h 2017-04-04 09:34:42 +0000
16@@ -24,6 +24,8 @@
17 #include <stdexcept>
18 #include <glib-object.h>
19
20+#include <unity/util/ResourcePtr.h>
21+
22 namespace unity
23 {
24
25@@ -109,6 +111,20 @@
26 SP& smart_ptr_;
27 };
28
29+template <typename T>
30+struct GObjectSignalUnsubscriber
31+{
32+ void operator()(gulong handle) noexcept
33+ {
34+ if (handle != 0 && G_IS_OBJECT(obj_.get()))
35+ {
36+ g_signal_handler_disconnect(obj_.get(), handle);
37+ }
38+ }
39+
40+ GObjectSPtr<T> obj_;
41+};
42+
43 }
44
45 /**
46@@ -187,6 +203,23 @@
47 return internal::GObjectAssigner<SP>(smart_ptr);
48 }
49
50+template<typename T>
51+using GObjectSignalConnection = ResourcePtr<gulong, internal::GObjectSignalUnsubscriber<T>>;
52+
53+/**
54+ \brief Simple wrapper to manage the lifecycle of GObject signal connections.
55+
56+ When 'nameConnection_' goes out of scope or is dealloc'ed, the source will be removed:
57+ \code{.cpp}
58+ GObjectSignalConnection<FooBar> nameConnection_;
59+ nameConnection_ = gobject_signal_connection(g_signal_connect(o.get(), "notify::name", G_CALLBACK(on_notify_name), this), o);
60+ \endcode
61+ */
62+template <typename T>
63+inline GObjectSignalConnection<T> gobject_signal_connection(gulong id, const GObjectSPtr<T>& obj)
64+{
65+ return GObjectSignalConnection<T>(id, internal::GObjectSignalUnsubscriber<T>{obj});
66+}
67
68 } // namespace until
69
70
71=== added file 'include/unity/util/GioMemory.h'
72--- include/unity/util/GioMemory.h 1970-01-01 00:00:00 +0000
73+++ include/unity/util/GioMemory.h 2017-04-04 09:34:42 +0000
74@@ -0,0 +1,72 @@
75+/*
76+ * Copyright (C) 2013-2017 Canonical Ltd.
77+ *
78+ * This program is free software: you can redistribute it and/or modify
79+ * it under the terms of the GNU Lesser General Public License version 3 as
80+ * published by the Free Software Foundation.
81+ *
82+ * This program is distributed in the hope that it will be useful,
83+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
84+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
85+ * GNU Lesser General Public License for more details.
86+ *
87+ * You should have received a copy of the GNU Lesser General Public License
88+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
89+ *
90+ * Authored by: Pete Woods <pete.woods@canonical.com>
91+ */
92+
93+#ifndef UNITY_UTIL_GIOMEMORY_H
94+#define UNITY_UTIL_GIOMEMORY_H
95+
96+#include <gio/gio.h>
97+
98+#include <unity/util/GObjectMemory.h>
99+
100+namespace unity
101+{
102+
103+namespace util
104+{
105+
106+namespace internal
107+{
108+struct GDBusSignalUnsubscriber
109+{
110+public:
111+ void operator()(guint handle) noexcept
112+ {
113+ if (handle != 0 && G_IS_OBJECT(bus_.get()))
114+ {
115+ g_dbus_connection_signal_unsubscribe(bus_.get(), handle);
116+ }
117+ }
118+
119+ GObjectSPtr<GDBusConnection> bus_;
120+};
121+
122+}
123+
124+typedef ResourcePtr<guint, internal::GDBusSignalUnsubscriber> GDBusSignalConnection;
125+
126+/**
127+ \brief Simple wrapper to manage the lifecycle of manual GDBus signal connections.
128+
129+ When 'signalConnection_' goes out of scope or is dealloc'ed, the connection will be removed:
130+ \code{.cpp}
131+ GDBusSignalConnection signalConnection_;
132+
133+ signalConnection_ = gdbus_signal_connection(
134+ g_dbus_connection_signal_subscribe(bus.get(), nullptr, "org.does.not.exist", nullptr, "/does/not/exist", nullptr, G_DBUS_SIGNAL_FLAGS_NONE, on_dbus_signal, this, nullptr), bus);
135+ \endcode
136+ */
137+inline GDBusSignalConnection gdbus_signal_connection(guint id, GObjectSPtr<GDBusConnection> bus) noexcept
138+{
139+ return GDBusSignalConnection(id, internal::GDBusSignalUnsubscriber{bus});
140+}
141+
142+} // namespace until
143+
144+} // namespace unity
145+
146+#endif
147
148=== modified file 'include/unity/util/GlibMemory.h'
149--- include/unity/util/GlibMemory.h 2017-04-04 09:34:42 +0000
150+++ include/unity/util/GlibMemory.h 2017-04-04 09:34:42 +0000
151@@ -24,6 +24,8 @@
152 #include <memory>
153 #include <glib.h>
154
155+#include <unity/util/ResourcePtr.h>
156+
157 namespace unity
158 {
159
160@@ -83,6 +85,17 @@
161 SP& smart_ptr_;
162 };
163
164+struct GSourceUnsubscriber
165+{
166+ void operator()(guint tag) noexcept
167+ {
168+ if (tag != 0)
169+ {
170+ g_source_remove(tag);
171+ }
172+ }
173+};
174+
175 }
176
177 #define UNITY_UTIL_DEFINE_GLIB_SMART_POINTERS(TypeName, func) \
178@@ -156,6 +169,21 @@
179 return internal::GlibAssigner<SP>(smart_ptr);
180 }
181
182+using GSourceManager = ResourcePtr<guint, internal::GSourceUnsubscriber>;
183+
184+/**
185+ \brief Simple wrapper to manage the lifecycle of sources.
186+
187+ When 'timer' goes out of scope or is dealloc'ed, the source will be removed:
188+ \code{.cpp}
189+ auto timer = g_source_manager(g_timeout_add(5000, on_timeout, nullptr));
190+ \endcode
191+ */
192+inline GSourceManager g_source_manager(guint id)
193+{
194+ return GSourceManager(id, internal::GSourceUnsubscriber());
195+}
196+
197 /**
198 * Below here is some hackery to extract the matching deleters for all built in glib types.
199 */
200
201=== modified file 'include/unity/util/ResourcePtr.h'
202--- include/unity/util/ResourcePtr.h 2014-06-25 23:46:22 +0000
203+++ include/unity/util/ResourcePtr.h 2017-04-04 09:34:42 +0000
204@@ -20,6 +20,7 @@
205 #define UNITY_UTIL_RESOURCEPTR_H
206
207 #include <mutex>
208+#include <type_traits>
209
210 namespace unity
211 {
212@@ -135,6 +136,7 @@
213 */
214 typedef D deleter_type;
215
216+ ResourcePtr();
217 explicit ResourcePtr(D d);
218 ResourcePtr(R r, D d);
219 ResourcePtr(ResourcePtr&& r);
220@@ -175,6 +177,14 @@
221 typedef LockAdopter<decltype(m_)> AdoptLock;
222 };
223
224+template<typename R, typename D>
225+ResourcePtr<R, D>::ResourcePtr()
226+ : initialized_(false)
227+{
228+ static_assert(!std::is_pointer<deleter_type>::value,
229+ "constructed with null function pointer deleter");
230+}
231+
232 /**
233 Constructs a ResourcePtr with the specified deleter. No resource is held, so a call to has_resource()
234 after constructing a ResourcePtr this way returns <code>false</code>.
235
236=== modified file 'test/gtest/unity/util/CMakeLists.txt'
237--- test/gtest/unity/util/CMakeLists.txt 2017-01-19 13:52:32 +0000
238+++ test/gtest/unity/util/CMakeLists.txt 2017-04-04 09:34:42 +0000
239@@ -1,6 +1,7 @@
240 add_subdirectory(Daemon)
241 add_subdirectory(DefinesPtrs)
242 add_subdirectory(FileIO)
243+add_subdirectory(GioMemory)
244 add_subdirectory(GlibMemory)
245 add_subdirectory(GObjectMemory)
246 add_subdirectory(IniParser)
247
248=== modified file 'test/gtest/unity/util/GObjectMemory/GObjectMemory_test.cpp'
249--- test/gtest/unity/util/GObjectMemory/GObjectMemory_test.cpp 2017-04-04 09:34:42 +0000
250+++ test/gtest/unity/util/GObjectMemory/GObjectMemory_test.cpp 2017-04-04 09:34:42 +0000
251@@ -54,6 +54,8 @@
252
253 void foo_bar_assigner_null(FooBar** in);
254
255+void foo_bar_set_name(FooBar* fooBar, const gchar* const name);
256+
257 G_END_DECLS
258
259 // private implementation
260@@ -197,6 +199,11 @@
261 }
262 }
263
264+void foo_bar_set_name(FooBar* fooBar, const gchar* const name)
265+{
266+ g_object_set(fooBar, "name", name, nullptr);
267+}
268+
269 //
270 // Test cases
271 //
272@@ -213,6 +220,22 @@
273 {
274 DELETED_OBJECTS.clear();
275 }
276+
277+ static void on_notify_name(GObject *object, GParamSpec *, gpointer user_data)
278+ {
279+ static_cast<GObjectMemoryTest*>(user_data)->onNotifyName(object);
280+ }
281+
282+ void onNotifyName(GObject *object)
283+ {
284+ gcharUPtr name;
285+ g_object_get(object, "name", assign_glib(name), nullptr);
286+ nameChanges_.emplace_back(name.get());
287+ }
288+
289+ list<string> nameChanges_;
290+
291+ GObjectSignalConnection<FooBar> nameConnection_;
292 };
293
294 TEST_F(GObjectMemoryTest, trivial)
295@@ -496,6 +519,17 @@
296 EXPECT_EQ(list<Deleted>({{"hi", 6}, {"bye", 7}}), DELETED_OBJECTS);
297 }
298
299+TEST_F(GObjectMemoryTest, signals)
300+{
301+ auto o(share_gobject(foo_bar_new_full("hi", 1)));
302+ nameConnection_ = gobject_signal_connection(g_signal_connect(o.get(), "notify::name", G_CALLBACK(on_notify_name), this), o);
303+ foo_bar_set_name(o.get(), "change1");
304+ nameConnection_.dealloc();
305+ foo_bar_set_name(o.get(), "change2");
306+
307+ EXPECT_EQ(list<string>{"change1"}, nameChanges_);
308+}
309+
310 typedef pair<const char*, guint> GObjectMemoryMakeSharedTestParam;
311
312 class GObjectMemoryMakeHelperMethodsTest: public testing::TestWithParam<GObjectMemoryMakeSharedTestParam>
313
314=== added directory 'test/gtest/unity/util/GioMemory'
315=== added file 'test/gtest/unity/util/GioMemory/CMakeLists.txt'
316--- test/gtest/unity/util/GioMemory/CMakeLists.txt 1970-01-01 00:00:00 +0000
317+++ test/gtest/unity/util/GioMemory/CMakeLists.txt 2017-04-04 09:34:42 +0000
318@@ -0,0 +1,30 @@
319+pkg_check_modules(GIO REQUIRED gio-2.0)
320+pkg_check_modules(QDBUSTEST REQUIRED libqtdbustest-1)
321+
322+find_package(Qt5Core REQUIRED)
323+find_package(Qt5DBus REQUIRED)
324+
325+include_directories(
326+ ${Qt5Core_INCLUDE_DIRS}
327+ ${Qt5DBus_INCLUDE_DIRS}
328+ ${GIO_INCLUDE_DIRS}
329+ ${QDBUSTEST_INCLUDE_DIRS}
330+ )
331+
332+add_definitions(
333+ -DQT_NO_KEYWORDS=1
334+ )
335+
336+add_executable(GioMemory_test
337+ GioMemory_test.cpp
338+ )
339+
340+target_link_libraries(GioMemory_test
341+ ${TESTLIBS}
342+ ${GIO_LDFLAGS}
343+ ${QDBUSTEST_LDFLAGS}
344+ Qt5::Core
345+ Qt5::DBus
346+ )
347+
348+add_test(GioMemory_test GioMemory_test)
349
350=== added file 'test/gtest/unity/util/GioMemory/GioMemory_test.cpp'
351--- test/gtest/unity/util/GioMemory/GioMemory_test.cpp 1970-01-01 00:00:00 +0000
352+++ test/gtest/unity/util/GioMemory/GioMemory_test.cpp 2017-04-04 09:34:42 +0000
353@@ -0,0 +1,114 @@
354+/*
355+ * Copyright (C) 2017 Canonical Ltd.
356+ *
357+ * This program is free software: you can redistribute it and/or modify
358+ * it under the terms of the GNU Lesser General Public License version 3 as
359+ * published by the Free Software Foundation.
360+ *
361+ * This program is distributed in the hope that it will be useful,
362+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
363+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
364+ * GNU Lesser General Public License for more details.
365+ *
366+ * You should have received a copy of the GNU Lesser General Public License
367+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
368+ *
369+ * Authored by: Pete Woods <pete.woods@canonical.com>
370+ */
371+
372+#include <unity/util/GioMemory.h>
373+#include <unity/util/GlibMemory.h>
374+#include <libqtdbustest/DBusTestRunner.h>
375+#include <gtest/gtest.h>
376+#include <list>
377+#include <string>
378+
379+using namespace std;
380+using namespace unity::util;
381+using namespace QtDBusTest;
382+
383+namespace
384+{
385+
386+class GioMemoryTest: public testing::Test
387+{
388+protected:
389+ static void SetUpTestCase()
390+ {
391+ g_log_set_always_fatal((GLogLevelFlags) (G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL));
392+ }
393+
394+ static void on_dbus_signal(GDBusConnection *, const gchar *, const gchar *, const gchar *, const gchar *signal_name, GVariant *, gpointer user_data)
395+ {
396+ static_cast<GioMemoryTest*>(user_data)->onDbusSignal(signal_name);
397+ }
398+
399+ void onDbusSignal(const gchar *signal_name)
400+ {
401+ signals_.emplace_back(signal_name);
402+ g_main_loop_quit(mainloop_.get());
403+ }
404+
405+ static gboolean on_timeout(gpointer user_data)
406+ {
407+ return static_cast<GioMemoryTest*>(user_data)->onTimeout();
408+ }
409+
410+ gboolean onTimeout()
411+ {
412+ g_main_loop_quit(mainloop_.get());
413+ return G_SOURCE_CONTINUE;
414+ }
415+
416+ static GObjectSPtr<GDBusConnection> getSessionBus()
417+ {
418+ auto address = unique_glib(g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, nullptr, nullptr));
419+
420+ auto bus = unique_gobject(
421+ g_dbus_connection_new_for_address_sync(address.get(), (GDBusConnectionFlags) (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION), nullptr,
422+ nullptr, nullptr));
423+
424+ g_dbus_connection_set_exit_on_close(bus.get(), FALSE);
425+
426+ return bus;
427+ }
428+
429+ DBusTestRunner dbusTestRunner;
430+
431+ GDBusSignalConnection signalConnection_;
432+
433+ GMainLoopSPtr mainloop_;
434+
435+ list<string> signals_;
436+};
437+
438+
439+TEST_F(GioMemoryTest, signals)
440+{
441+ mainloop_ = share_glib(g_main_loop_new(nullptr, false));
442+
443+ auto bus = getSessionBus();
444+ ASSERT_TRUE(bool(bus));
445+
446+ signalConnection_ = gdbus_signal_connection(
447+ g_dbus_connection_signal_subscribe(bus.get(), nullptr, "org.does.not.exist", nullptr, "/does/not/exist", nullptr, G_DBUS_SIGNAL_FLAGS_NONE, on_dbus_signal, this, nullptr), bus);
448+
449+ g_dbus_connection_emit_signal(bus.get(), nullptr, "/does/not/exist", "org.does.not.exist", "hello", nullptr, nullptr);
450+ {
451+ auto timer = g_source_manager(g_timeout_add(5000, on_timeout, this));
452+ g_main_loop_run(mainloop_.get());
453+ EXPECT_EQ(list<string>{"hello"}, signals_);
454+ }
455+ signals_.clear();
456+
457+ signalConnection_.dealloc();
458+
459+ g_dbus_connection_emit_signal(bus.get(), nullptr, "/does/not/exist", "org.does.not.exist", "hello", nullptr, nullptr);
460+ {
461+ auto timer = g_source_manager(g_timeout_add(5000, on_timeout, this));
462+ g_main_loop_run(mainloop_.get());
463+ EXPECT_TRUE(signals_.empty());
464+ }
465+}
466+
467+}
468
469=== modified file 'test/headers/CMakeLists.txt'
470--- test/headers/CMakeLists.txt 2017-01-19 13:52:32 +0000
471+++ test/headers/CMakeLists.txt 2017-04-04 09:34:42 +0000
472@@ -11,6 +11,7 @@
473 )
474
475 set(exclusions
476+ "GioMemory.h"
477 "GlibMemory.h"
478 "GObjectMemory.h"
479 )
480
481=== modified file 'test/headers/includechecker.py'
482--- test/headers/includechecker.py 2017-01-19 13:52:32 +0000
483+++ test/headers/includechecker.py 2017-04-04 09:34:42 +0000
484@@ -38,6 +38,7 @@
485 'unity/shell': { 'Qt' }, # Anything under unity/shell can include anything starting with Qt
486 'unity/util/GObjectMemory': { 'glib' }, # The unity/util/GObjectMemory header can include anything starting with glib
487 'unity/util/GlibMemory': { 'glib' }, # The unity/util/GlibMemory header can include anything starting with glib
488+ 'unity/util/GioMemory': { 'glib' }, # The unity/util/GioMemory header can include anything starting with glib
489 }
490
491 def check_file(filename, permitted_includes):

Subscribers

People subscribed via source and target branches

to all changes: