Merge lp:~ted/indicator-messages/sanity-testing into lp:indicator-messages/15.04
- sanity-testing
- Merge into trunk.15.04
Status: | Merged |
---|---|
Approved by: | Charles Kerr |
Approved revision: | 462 |
Merged at revision: | 441 |
Proposed branch: | lp:~ted/indicator-messages/sanity-testing |
Merge into: | lp:indicator-messages/15.04 |
Diff against target: |
1226 lines (+1098/-11) 10 files modified
Makefile.am (+2/-2) configure.ac (+3/-1) debian/control (+1/-0) po/POTFILES.in (+0/-1) tests/Makefile.am (+53/-7) tests/accounts-service-mock.h (+134/-0) tests/applications/test.desktop (+3/-0) tests/applications/test2.desktop (+5/-0) tests/indicator-fixture.h (+688/-0) tests/indicator-test.cpp (+209/-0) |
To merge this branch: | bzr merge lp:~ted/indicator-messages/sanity-testing |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Charles Kerr (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+252197@code.launchpad.net |
Commit message
Sanity tests to test basic features of the messaging menu
Description of the change
Some basic testing so that we have some fallback from stupid mistakes, but not exhaustive by any means.
NOTE: A large part of this MR is framework code copied from indicator-sound. It should go into a shared library eventually, but it isn't there today.
PS Jenkins bot (ps-jenkins) wrote : | # |
Charles Kerr (charlesk) wrote : | # |
A few inline comments below, no showstoppers.
Overall I'm very impressed with the IndicatorFixture you've got here.
If I was the one who reviewed it before in i-sound, maybe it was before my time on autopilot and I didn't realize what I was seeing.
When you get back from Galveston, let's look into making a shared repo so that other indicators can use this.
Ted Gould (ted) wrote : | # |
On Tue, 2015-03-10 at 18:42 +0000, Charles Kerr wrote:
> > + -DSCHEMA_
>
> Might make sense to define this directory as its own variable, so that it can be used below in schemas-
>
> Also, we're using $(abs_builddir)
Makes sense. r460
> > $(top_builddir)
> > $(top_srcdir)
> > - $(top_srcdir)
> > + $(top_srcdir)
>
> How did this work before?
>
> > $(top_srcdir)
I think because it was header files and you don't *actually* need to
include them in the source list unless you are doing a make dist to
build your tarball. If you always use the Bazaar branch you'll never
notice them being missing.
> > + operator DbusTestDbusMock* () {
> > + return mock;
> > + }
>
> overloading operator() like this seems excessive.
I was trying to make the tests read nicely in that otherwise you end up
having to do all the GObject casts. The reality is that this one isn't
too useful, but the DbusTestTask gets passed to libdbustest all the
time. Not as much with this fixture, but in the other i-sound tests it
is used like that more (mock comes from i-sound)
> > + std::shared_
> > + std::shared_
> > + DbusTestService * _session_service;
> > + DbusTestService * _system_service;
> > + DbusTestTask * _test_indicator;
> > + DbusTestTask * _test_dummy;
> > + GDBusConnection * _session;
> > + GDBusConnection * _system;
> > +
>
> http://
Eh, sure, one underscore seems like it doesn't break the rules. Not
perfect for sure.
> > + std::for_
>
> "for (auto task : mocks) {" would be clearer
>
> > + if (dbus_test_
> > + dbus_test_
> > + } else {
> > + dbus_test_
> > + }
> > + });
Good point. r461
> > + virtual ~PerRunData (void) {
>
> dtor doesn't need a void argument?
The compiler at least seems to want the parameter list. The void is just
making it look less like line noise :-)
> > +static void
> > +messageReplyAc
> > + std::string * res = reinterpret_
>
> "auto res =" would avoid redundancy
>
> > + *res = g_variant_
> > +}
You just want me to give into your auto everywhere world! r462
> > + EXPECT_
> > +}
>
> EXPECT_ is used as the default in these tests, but most of the time followup tests don't make sense if the ones that preceded them failed & should use ASSERT_ instead
So, yes....
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:462
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'Makefile.am' |
2 | --- Makefile.am 2013-08-20 10:38:10 +0000 |
3 | +++ Makefile.am 2015-03-10 21:21:53 +0000 |
4 | @@ -12,10 +12,10 @@ |
5 | |
6 | if BUILD_TESTS |
7 | SUBDIRS += \ |
8 | - test |
9 | + tests |
10 | |
11 | # build src first |
12 | -test: src |
13 | +tests: src libmessaging-menu |
14 | |
15 | endif |
16 | |
17 | |
18 | === modified file 'configure.ac' |
19 | --- configure.ac 2014-09-17 20:13:13 +0000 |
20 | +++ configure.ac 2015-03-10 21:21:53 +0000 |
21 | @@ -48,6 +48,8 @@ |
22 | |
23 | PKG_CHECK_MODULES(GIO, gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION) |
24 | |
25 | +PKG_CHECK_MODULES(DBUSTEST, dbustest-1) |
26 | + |
27 | AC_SUBST(APPLET_CFLAGS) |
28 | AC_SUBST(APPLET_LIBS) |
29 | |
30 | @@ -172,7 +174,7 @@ |
31 | data/icons/scalable/categories/Makefile |
32 | data/upstart/Makefile |
33 | po/Makefile.in |
34 | -test/Makefile |
35 | +tests/Makefile |
36 | libmessaging-menu/Makefile |
37 | libmessaging-menu/messaging-menu.pc |
38 | doc/Makefile |
39 | |
40 | === modified file 'debian/control' |
41 | --- debian/control 2014-10-08 14:45:43 +0000 |
42 | +++ debian/control 2015-03-10 21:21:53 +0000 |
43 | @@ -11,6 +11,7 @@ |
44 | gtk-doc-tools, |
45 | intltool, |
46 | libaccountsservice-dev, |
47 | + libdbustest1-dev, |
48 | libgirepository1.0-dev (>= 0.9.12), |
49 | libgtest-dev, |
50 | python3-dbusmock, |
51 | |
52 | === modified file 'po/POTFILES.in' |
53 | --- po/POTFILES.in 2013-10-01 10:29:49 +0000 |
54 | +++ po/POTFILES.in 2015-03-10 21:21:53 +0000 |
55 | @@ -1,5 +1,4 @@ |
56 | [encoding: UTF-8] |
57 | -test/indicator-messages-service-activate.c |
58 | src/im-phone-menu.c |
59 | src/messages-service.c |
60 | src/im-desktop-menu.c |
61 | |
62 | === renamed directory 'test' => 'tests' |
63 | === removed directory 'tests' |
64 | === modified file 'tests/Makefile.am' |
65 | --- test/Makefile.am 2013-08-28 10:23:31 +0000 |
66 | +++ tests/Makefile.am 2015-03-10 21:21:53 +0000 |
67 | @@ -1,5 +1,6 @@ |
68 | |
69 | -check_LIBRARIES = libgtest.a |
70 | +CLEANFILES= |
71 | +check_LTLIBRARIES = libgtest.la |
72 | check_PROGRAMS = test-gactionmuxer |
73 | |
74 | TESTS = $(check_PROGRAMS) |
75 | @@ -7,15 +8,22 @@ |
76 | AM_CPPFLAGS = $(GTEST_CPPFLAGS) \ |
77 | -I${top_srcdir}/src |
78 | |
79 | -nodist_libgtest_a_SOURCES = \ |
80 | +###################################### |
81 | +# Google Test |
82 | +###################################### |
83 | + |
84 | +nodist_libgtest_la_SOURCES = \ |
85 | $(GTEST_SOURCE)/src/gtest-all.cc \ |
86 | $(GTEST_SOURCE)/src/gtest_main.cc |
87 | -libgtest_a_CPPFLAGS = \ |
88 | +libgtest_la_CPPFLAGS = \ |
89 | $(GTEST_CPPFLAGS) -w \ |
90 | $(AM_CPPFLAGS) |
91 | -libgtest_a_CXXFLAGS = \ |
92 | +libgtest_la_CXXFLAGS = \ |
93 | $(AM_CXXFLAGS) |
94 | |
95 | +###################################### |
96 | +# GActionMixer |
97 | +###################################### |
98 | |
99 | test_gactionmuxer_SOURCES = \ |
100 | test-gactionmuxer.cpp |
101 | @@ -27,8 +35,46 @@ |
102 | test_gactionmuxer_LDADD = \ |
103 | $(APPLET_LIBS) \ |
104 | libindicator-messages-service.la \ |
105 | - libgtest.a |
106 | - |
107 | + libgtest.la |
108 | + |
109 | +###################################### |
110 | +# Indicator Test |
111 | +###################################### |
112 | + |
113 | +SCHEMA_COMPILED_DIR="$(abs_builddir)/gsettings-schemas-compiled/" |
114 | + |
115 | +indicator_test_SOURCES = \ |
116 | + indicator-test.cpp |
117 | + |
118 | +indicator_test_CPPFLAGS = \ |
119 | + -DINDICATOR_MESSAGES_SERVICE_BINARY="\"$(abs_top_builddir)/src/indicator-messages-service\"" \ |
120 | + -DSCHEMA_DIR="\"$(SCHEMA_COMPILED_DIR)\"" \ |
121 | + -DXDG_DATA_DIRS="\"$(abs_srcdir)/\"" \ |
122 | + -I$(top_srcdir)/libmessaging-menu \ |
123 | + -std=c++11 \ |
124 | + $(APPLET_CFLAGS) \ |
125 | + $(DBUSTEST_CFLAGS) \ |
126 | + $(AM_CPPFLAGS) |
127 | + |
128 | +indicator_test_LDADD = \ |
129 | + $(APPLET_LIBS) \ |
130 | + $(DBUSTEST_LIBS) \ |
131 | + $(top_builddir)/libmessaging-menu/libmessaging-menu.la \ |
132 | + libgtest.la \ |
133 | + -lc -lpthread |
134 | + |
135 | +indicator-test.cpp: schemas-compiled.stamp |
136 | + |
137 | +schemas-compiled.stamp: $(top_srcdir)/data/*gschema.xml |
138 | + @rm -rf $(SCHEMA_COMPILED_DIR) |
139 | + @mkdir -p $(SCHEMA_COMPILED_DIR) |
140 | + @cp -f $(top_srcdir)/data/*gschema.xml $(SCHEMA_COMPILED_DIR) |
141 | + @`pkg-config gio-2.0 --variable glib_compile_schemas` $(SCHEMA_COMPILED_DIR) |
142 | + @touch schemas-compiled.stamp |
143 | + |
144 | +CLEANFILES += schemas-compiled.stamp |
145 | +TESTS += indicator-test |
146 | +check_PROGRAMS += indicator-test |
147 | |
148 | ###################################### |
149 | # Lib containing code under test |
150 | @@ -41,7 +87,7 @@ |
151 | $(top_builddir)/common/indicator-messages-service.c \ |
152 | $(top_builddir)/common/indicator-messages-service.h \ |
153 | $(top_srcdir)/src/gactionmuxer.c \ |
154 | - $(top_srcdir)/src/gactionmuxer.h |
155 | + $(top_srcdir)/src/gactionmuxer.h \ |
156 | $(top_srcdir)/src/dbus-data.h |
157 | |
158 | libindicator_messages_service_ladir = \ |
159 | |
160 | === added file 'tests/accounts-service-mock.h' |
161 | --- tests/accounts-service-mock.h 1970-01-01 00:00:00 +0000 |
162 | +++ tests/accounts-service-mock.h 2015-03-10 21:21:53 +0000 |
163 | @@ -0,0 +1,134 @@ |
164 | +/* |
165 | + * Copyright © 2014 Canonical Ltd. |
166 | + * |
167 | + * This program is free software; you can redistribute it and/or modify |
168 | + * it under the terms of the GNU General Public License as published by |
169 | + * the Free Software Foundation; version 3. |
170 | + * |
171 | + * This program is distributed in the hope that it will be useful, |
172 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
173 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
174 | + * GNU General Public License for more details. |
175 | + * |
176 | + * You should have received a copy of the GNU General Public License |
177 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
178 | + * |
179 | + * Authors: |
180 | + * Ted Gould <ted@canonical.com> |
181 | + */ |
182 | + |
183 | +#include <memory> |
184 | +#include <libdbustest/dbus-test.h> |
185 | + |
186 | +class AccountsServiceMock |
187 | +{ |
188 | + DbusTestDbusMock * mock = nullptr; |
189 | + DbusTestDbusMockObject * soundobj = nullptr; |
190 | + DbusTestDbusMockObject * userobj = nullptr; |
191 | + DbusTestDbusMockObject * syssoundobj = nullptr; |
192 | + DbusTestDbusMockObject * privacyobj = nullptr; |
193 | + |
194 | + public: |
195 | + AccountsServiceMock () { |
196 | + mock = dbus_test_dbus_mock_new("org.freedesktop.Accounts"); |
197 | + |
198 | + dbus_test_task_set_bus(DBUS_TEST_TASK(mock), DBUS_TEST_SERVICE_BUS_SYSTEM); |
199 | + |
200 | + DbusTestDbusMockObject * baseobj = dbus_test_dbus_mock_get_object(mock, "/org/freedesktop/Accounts", "org.freedesktop.Accounts", NULL); |
201 | + |
202 | + dbus_test_dbus_mock_object_add_method(mock, baseobj, |
203 | + "CacheUser", G_VARIANT_TYPE_STRING, G_VARIANT_TYPE_OBJECT_PATH, |
204 | + "ret = dbus.ObjectPath('/user')\n", NULL); |
205 | + dbus_test_dbus_mock_object_add_method(mock, baseobj, |
206 | + "FindUserById", G_VARIANT_TYPE_INT64, G_VARIANT_TYPE_OBJECT_PATH, |
207 | + "ret = dbus.ObjectPath('/user')\n", NULL); |
208 | + dbus_test_dbus_mock_object_add_method(mock, baseobj, |
209 | + "FindUserByName", G_VARIANT_TYPE_STRING, G_VARIANT_TYPE_OBJECT_PATH, |
210 | + "ret = dbus.ObjectPath('/user')\n", NULL); |
211 | + dbus_test_dbus_mock_object_add_method(mock, baseobj, |
212 | + "ListCachedUsers", NULL, G_VARIANT_TYPE_OBJECT_PATH_ARRAY, |
213 | + "ret = [ dbus.ObjectPath('/user') ]\n", NULL); |
214 | + dbus_test_dbus_mock_object_add_method(mock, baseobj, |
215 | + "UncacheUser", G_VARIANT_TYPE_STRING, NULL, |
216 | + "", NULL); |
217 | + |
218 | + userobj = dbus_test_dbus_mock_get_object(mock, "/user", "org.freedesktop.Accounts.User", NULL); |
219 | + dbus_test_dbus_mock_object_add_property(mock, userobj, |
220 | + "UserName", G_VARIANT_TYPE_STRING, |
221 | + g_variant_new_string(g_get_user_name()), NULL); |
222 | + dbus_test_dbus_mock_object_add_method(mock, baseobj, |
223 | + "SetXHasMessages", G_VARIANT_TYPE_BOOLEAN, nullptr, |
224 | + "", NULL); |
225 | + |
226 | + soundobj = dbus_test_dbus_mock_get_object(mock, "/user", "com.canonical.indicator.sound.AccountsService", NULL); |
227 | + dbus_test_dbus_mock_object_add_property(mock, soundobj, |
228 | + "Timestamp", G_VARIANT_TYPE_UINT64, |
229 | + g_variant_new_uint64(0), NULL); |
230 | + dbus_test_dbus_mock_object_add_property(mock, soundobj, |
231 | + "PlayerName", G_VARIANT_TYPE_STRING, |
232 | + g_variant_new_string(""), NULL); |
233 | + dbus_test_dbus_mock_object_add_property(mock, soundobj, |
234 | + "PlayerIcon", G_VARIANT_TYPE_VARIANT, |
235 | + g_variant_new_variant(g_variant_new_string("")), NULL); |
236 | + dbus_test_dbus_mock_object_add_property(mock, soundobj, |
237 | + "Running", G_VARIANT_TYPE_BOOLEAN, |
238 | + g_variant_new_boolean(FALSE), NULL); |
239 | + dbus_test_dbus_mock_object_add_property(mock, soundobj, |
240 | + "State", G_VARIANT_TYPE_STRING, |
241 | + g_variant_new_string(""), NULL); |
242 | + dbus_test_dbus_mock_object_add_property(mock, soundobj, |
243 | + "Title", G_VARIANT_TYPE_STRING, |
244 | + g_variant_new_string(""), NULL); |
245 | + dbus_test_dbus_mock_object_add_property(mock, soundobj, |
246 | + "Artist", G_VARIANT_TYPE_STRING, |
247 | + g_variant_new_string(""), NULL); |
248 | + dbus_test_dbus_mock_object_add_property(mock, soundobj, |
249 | + "Album", G_VARIANT_TYPE_STRING, |
250 | + g_variant_new_string(""), NULL); |
251 | + dbus_test_dbus_mock_object_add_property(mock, soundobj, |
252 | + "ArtUrl", G_VARIANT_TYPE_STRING, |
253 | + g_variant_new_string(""), NULL); |
254 | + |
255 | + syssoundobj = dbus_test_dbus_mock_get_object(mock, "/user", "com.ubuntu.touch.AccountsService.Sound", NULL); |
256 | + dbus_test_dbus_mock_object_add_property(mock, syssoundobj, |
257 | + "SilentMode", G_VARIANT_TYPE_BOOLEAN, |
258 | + g_variant_new_boolean(FALSE), NULL); |
259 | + |
260 | + privacyobj = dbus_test_dbus_mock_get_object(mock, "/user", "com.ubuntu.touch.AccountsService.SecurityPrivacy", NULL); |
261 | + dbus_test_dbus_mock_object_add_property(mock, privacyobj, |
262 | + "MessagesWelcomeScreen", G_VARIANT_TYPE_BOOLEAN, |
263 | + g_variant_new_boolean(true), NULL); |
264 | + dbus_test_dbus_mock_object_add_property(mock, privacyobj, |
265 | + "StatsWelcomeScreen", G_VARIANT_TYPE_BOOLEAN, |
266 | + g_variant_new_boolean(true), NULL); |
267 | + } |
268 | + |
269 | + ~AccountsServiceMock () { |
270 | + g_debug("Destroying the Accounts Service Mock"); |
271 | + g_clear_object(&mock); |
272 | + } |
273 | + |
274 | + void setSilentMode (bool modeValue) { |
275 | + dbus_test_dbus_mock_object_update_property(mock, syssoundobj, |
276 | + "SilentMode", g_variant_new_boolean(modeValue ? TRUE : FALSE), |
277 | + NULL); |
278 | + } |
279 | + |
280 | + operator std::shared_ptr<DbusTestTask> () { |
281 | + return std::shared_ptr<DbusTestTask>( |
282 | + DBUS_TEST_TASK(g_object_ref(mock)), |
283 | + [](DbusTestTask * task) { g_clear_object(&task); }); |
284 | + } |
285 | + |
286 | + operator DbusTestTask* () { |
287 | + return DBUS_TEST_TASK(mock); |
288 | + } |
289 | + |
290 | + operator DbusTestDbusMock* () { |
291 | + return mock; |
292 | + } |
293 | + |
294 | + DbusTestDbusMockObject * get_sound () { |
295 | + return soundobj; |
296 | + } |
297 | +}; |
298 | |
299 | === modified file 'tests/applications/test.desktop' |
300 | --- test/applications/test.desktop 2013-02-27 00:34:14 +0000 |
301 | +++ tests/applications/test.desktop 2015-03-10 21:21:53 +0000 |
302 | @@ -1,2 +1,5 @@ |
303 | [Desktop Entry] |
304 | Type=Application |
305 | +Name=Test |
306 | +Exec=test |
307 | +Icon=test-app |
308 | |
309 | === added file 'tests/applications/test2.desktop' |
310 | --- tests/applications/test2.desktop 1970-01-01 00:00:00 +0000 |
311 | +++ tests/applications/test2.desktop 2015-03-10 21:21:53 +0000 |
312 | @@ -0,0 +1,5 @@ |
313 | +[Desktop Entry] |
314 | +Type=Application |
315 | +Name=Test |
316 | +Exec=test |
317 | +Icon=test-app |
318 | |
319 | === added file 'tests/indicator-fixture.h' |
320 | --- tests/indicator-fixture.h 1970-01-01 00:00:00 +0000 |
321 | +++ tests/indicator-fixture.h 2015-03-10 21:21:53 +0000 |
322 | @@ -0,0 +1,688 @@ |
323 | +/* |
324 | + * Copyright © 2014 Canonical Ltd. |
325 | + * |
326 | + * This program is free software; you can redistribute it and/or modify |
327 | + * it under the terms of the GNU General Public License as published by |
328 | + * the Free Software Foundation; version 3. |
329 | + * |
330 | + * This program is distributed in the hope that it will be useful, |
331 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
332 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
333 | + * GNU General Public License for more details. |
334 | + * |
335 | + * You should have received a copy of the GNU General Public License |
336 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
337 | + * |
338 | + * Authors: |
339 | + * Ted Gould <ted@canonical.com> |
340 | + */ |
341 | + |
342 | +#include <memory> |
343 | +#include <algorithm> |
344 | +#include <string> |
345 | +#include <functional> |
346 | +#include <future> |
347 | + |
348 | +#include <gtest/gtest.h> |
349 | +#include <gio/gio.h> |
350 | +#include <libdbustest/dbus-test.h> |
351 | + |
352 | +class IndicatorFixture : public ::testing::Test |
353 | +{ |
354 | + private: |
355 | + std::string _indicatorPath; |
356 | + std::string _indicatorAddress; |
357 | + std::vector<std::shared_ptr<DbusTestTask>> _mocks; |
358 | + protected: |
359 | + std::chrono::milliseconds _eventuallyTime; |
360 | + |
361 | + private: |
362 | + class PerRunData { |
363 | + public: |
364 | + /* We're private in the fixture but other than that we don't care, |
365 | + we don't leak out. This object's purpose isn't to hide data it is |
366 | + to make the lifecycle of the items more clear. */ |
367 | + std::shared_ptr<GMenuModel> _menu; |
368 | + std::shared_ptr<GActionGroup> _actions; |
369 | + DbusTestService * _session_service; |
370 | + DbusTestService * _system_service; |
371 | + DbusTestTask * _test_indicator; |
372 | + DbusTestTask * _test_dummy; |
373 | + GDBusConnection * _session; |
374 | + GDBusConnection * _system; |
375 | + |
376 | + PerRunData (const std::string& indicatorPath, const std::string& indicatorAddress, std::vector<std::shared_ptr<DbusTestTask>>& mocks) |
377 | + : _menu(nullptr) |
378 | + , _session(nullptr) |
379 | + { |
380 | + _session_service = dbus_test_service_new(nullptr); |
381 | + dbus_test_service_set_bus(_session_service, DBUS_TEST_SERVICE_BUS_SESSION); |
382 | + |
383 | + _system_service = dbus_test_service_new(nullptr); |
384 | + dbus_test_service_set_bus(_system_service, DBUS_TEST_SERVICE_BUS_SYSTEM); |
385 | + |
386 | + _test_indicator = DBUS_TEST_TASK(dbus_test_process_new(indicatorPath.c_str())); |
387 | + dbus_test_task_set_name(_test_indicator, "Indicator"); |
388 | + dbus_test_service_add_task(_session_service, _test_indicator); |
389 | + |
390 | + _test_dummy = dbus_test_task_new(); |
391 | + dbus_test_task_set_wait_for(_test_dummy, indicatorAddress.c_str()); |
392 | + dbus_test_task_set_name(_test_dummy, "Dummy"); |
393 | + dbus_test_service_add_task(_session_service, _test_dummy); |
394 | + |
395 | + for(auto task : mocks) { |
396 | + if (dbus_test_task_get_bus(task.get()) == DBUS_TEST_SERVICE_BUS_SYSTEM) { |
397 | + dbus_test_service_add_task(_system_service, task.get()); |
398 | + } else { |
399 | + dbus_test_service_add_task(_session_service, task.get()); |
400 | + } |
401 | + } |
402 | + |
403 | + g_debug("Starting System Bus"); |
404 | + dbus_test_service_start_tasks(_system_service); |
405 | + _system = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr); |
406 | + g_dbus_connection_set_exit_on_close(_system, FALSE); |
407 | + |
408 | + g_debug("Starting Session Bus"); |
409 | + dbus_test_service_start_tasks(_session_service); |
410 | + _session = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr); |
411 | + g_dbus_connection_set_exit_on_close(_session, FALSE); |
412 | + } |
413 | + |
414 | + virtual ~PerRunData (void) { |
415 | + _menu.reset(); |
416 | + _actions.reset(); |
417 | + |
418 | + /* D-Bus Test Stuff */ |
419 | + g_clear_object(&_test_dummy); |
420 | + g_clear_object(&_test_indicator); |
421 | + g_clear_object(&_session_service); |
422 | + g_clear_object(&_system_service); |
423 | + |
424 | + /* Wait for D-Bus session bus to go */ |
425 | + if (!g_dbus_connection_is_closed(_session)) { |
426 | + g_dbus_connection_close_sync(_session, nullptr, nullptr); |
427 | + } |
428 | + g_clear_object(&_session); |
429 | + |
430 | + if (!g_dbus_connection_is_closed(_system)) { |
431 | + g_dbus_connection_close_sync(_system, nullptr, nullptr); |
432 | + } |
433 | + g_clear_object(&_system); |
434 | + } |
435 | + }; |
436 | + |
437 | + std::shared_ptr<PerRunData> run; |
438 | + |
439 | + public: |
440 | + virtual ~IndicatorFixture() = default; |
441 | + |
442 | + IndicatorFixture (const std::string& path, |
443 | + const std::string& addr) |
444 | + : _indicatorPath(path) |
445 | + , _indicatorAddress(addr) |
446 | + , _eventuallyTime(std::chrono::seconds(5)) |
447 | + { |
448 | + }; |
449 | + |
450 | + |
451 | + protected: |
452 | + virtual void SetUp() override |
453 | + { |
454 | + run = std::make_shared<PerRunData>(_indicatorPath, _indicatorAddress, _mocks); |
455 | + |
456 | + _mocks.clear(); |
457 | + } |
458 | + |
459 | + virtual void TearDown() override |
460 | + { |
461 | + run.reset(); |
462 | + } |
463 | + |
464 | + void addMock (std::shared_ptr<DbusTestTask> mock) |
465 | + { |
466 | + _mocks.push_back(mock); |
467 | + } |
468 | + |
469 | + std::shared_ptr<DbusTestTask> buildBustleMock (const std::string& filename, DbusTestServiceBus bus = DBUS_TEST_SERVICE_BUS_BOTH) |
470 | + { |
471 | + return std::shared_ptr<DbusTestTask>([filename, bus]() { |
472 | + DbusTestTask * bustle = DBUS_TEST_TASK(dbus_test_bustle_new(filename.c_str())); |
473 | + dbus_test_task_set_name(bustle, "Bustle"); |
474 | + dbus_test_task_set_bus(bustle, bus); |
475 | + return bustle; |
476 | + }(), [](DbusTestTask * bustle) { |
477 | + g_clear_object(&bustle); |
478 | + }); |
479 | + } |
480 | + |
481 | + private: |
482 | + void waitForCore (GObject * obj, const gchar * signalname) { |
483 | + auto loop = g_main_loop_new(nullptr, FALSE); |
484 | + |
485 | + /* Our two exit criteria */ |
486 | + gulong signal = g_signal_connect_swapped(obj, signalname, G_CALLBACK(g_main_loop_quit), loop); |
487 | + guint timer = g_timeout_add_seconds(5, [](gpointer user_data) -> gboolean { |
488 | + g_warning("Menu Timeout"); |
489 | + g_main_loop_quit((GMainLoop *)user_data); |
490 | + return G_SOURCE_CONTINUE; |
491 | + }, loop); |
492 | + |
493 | + /* Wait for sync */ |
494 | + g_main_loop_run(loop); |
495 | + |
496 | + /* Clean up */ |
497 | + g_source_remove(timer); |
498 | + g_signal_handler_disconnect(obj, signal); |
499 | + |
500 | + g_main_loop_unref(loop); |
501 | + } |
502 | + |
503 | + void menuWaitForItems (const std::shared_ptr<GMenuModel>& menu) { |
504 | + auto count = g_menu_model_get_n_items(menu.get()); |
505 | + |
506 | + if (count != 0) |
507 | + return; |
508 | + |
509 | + waitForCore(G_OBJECT(menu.get()), "items-changed"); |
510 | + } |
511 | + |
512 | + void agWaitForActions (const std::shared_ptr<GActionGroup>& group) { |
513 | + auto list = std::shared_ptr<gchar *>( |
514 | + g_action_group_list_actions(group.get()), |
515 | + [](gchar ** list) { |
516 | + g_strfreev(list); |
517 | + }); |
518 | + |
519 | + if (g_strv_length(list.get()) != 0) { |
520 | + return; |
521 | + } |
522 | + |
523 | + waitForCore(G_OBJECT(group.get()), "action-added"); |
524 | + } |
525 | + |
526 | + testing::AssertionResult expectEventually (std::function<testing::AssertionResult(void)> &testfunc) { |
527 | + auto loop = std::shared_ptr<GMainLoop>(g_main_loop_new(nullptr, FALSE), [](GMainLoop * loop) { if (loop != nullptr) g_main_loop_unref(loop); }); |
528 | + |
529 | + std::promise<testing::AssertionResult> retpromise; |
530 | + auto retfuture = retpromise.get_future(); |
531 | + auto start = std::chrono::steady_clock::now(); |
532 | + |
533 | + /* The core of the idle function as an object so we can use the C++-isms |
534 | + of attaching the variables and make this code reasonably readable */ |
535 | + std::function<void(void)> idlefunc = [&loop, &retpromise, &testfunc, &start, this]() -> void { |
536 | + auto result = testfunc(); |
537 | + |
538 | + if (result == false && _eventuallyTime > (std::chrono::steady_clock::now() - start)) { |
539 | + return; |
540 | + } |
541 | + |
542 | + retpromise.set_value(result); |
543 | + g_main_loop_quit(loop.get()); |
544 | + }; |
545 | + |
546 | + auto idlesrc = g_idle_add([](gpointer data) -> gboolean { |
547 | + auto func = reinterpret_cast<std::function<void(void)> *>(data); |
548 | + (*func)(); |
549 | + return G_SOURCE_CONTINUE; |
550 | + }, &idlefunc); |
551 | + |
552 | + g_main_loop_run(loop.get()); |
553 | + g_source_remove(idlesrc); |
554 | + |
555 | + return retfuture.get(); |
556 | + } |
557 | + |
558 | + protected: |
559 | + void setMenu (const std::string& path) { |
560 | + run->_menu.reset(); |
561 | + |
562 | + g_debug("Getting Menu: %s:%s", _indicatorAddress.c_str(), path.c_str()); |
563 | + run->_menu = std::shared_ptr<GMenuModel>(G_MENU_MODEL(g_dbus_menu_model_get(run->_session, _indicatorAddress.c_str(), path.c_str())), [](GMenuModel * modelptr) { |
564 | + g_clear_object(&modelptr); |
565 | + }); |
566 | + |
567 | + menuWaitForItems(run->_menu); |
568 | + } |
569 | + |
570 | + void setActions (const std::string& path) { |
571 | + run->_actions.reset(); |
572 | + |
573 | + run->_actions = std::shared_ptr<GActionGroup>(G_ACTION_GROUP(g_dbus_action_group_get(run->_session, _indicatorAddress.c_str(), path.c_str())), [](GActionGroup * groupptr) { |
574 | + g_clear_object(&groupptr); |
575 | + }); |
576 | + |
577 | + agWaitForActions(run->_actions); |
578 | + } |
579 | + |
580 | + void activateAction (const std::string &name, std::shared_ptr<GVariant> ¶meter) { |
581 | + g_action_group_activate_action(run->_actions.get(), name.c_str(), parameter.get()); |
582 | + } |
583 | + |
584 | + void activateAction (const std::string &name, GVariant * parameter = nullptr) { |
585 | + std::shared_ptr<GVariant> param; |
586 | + |
587 | + if (parameter != nullptr) |
588 | + param = std::shared_ptr<GVariant>(g_variant_ref_sink(parameter), [](GVariant * var) { |
589 | + g_variant_unref(var); |
590 | + }); |
591 | + |
592 | + return activateAction(name, param); |
593 | + } |
594 | + |
595 | + testing::AssertionResult expectActionExists (const gchar * nameStr, const std::string& name) { |
596 | + bool hasit = g_action_group_has_action(run->_actions.get(), name.c_str()); |
597 | + |
598 | + if (!hasit) { |
599 | + auto result = testing::AssertionFailure(); |
600 | + result << |
601 | + " Action: " << nameStr << std::endl << |
602 | + " Expected: " << "Exists" << std::endl << |
603 | + " Actual: " << "No action found" << std::endl; |
604 | + |
605 | + return result; |
606 | + } |
607 | + |
608 | + auto result = testing::AssertionSuccess(); |
609 | + return result; |
610 | + } |
611 | + |
612 | + template <typename... Args> testing::AssertionResult expectEventuallyActionExists (Args&& ... args) { |
613 | + std::function<testing::AssertionResult(void)> func = [&]() { |
614 | + return expectActionExists(std::forward<Args>(args)...); |
615 | + }; |
616 | + return expectEventually(func); |
617 | + } |
618 | + |
619 | + testing::AssertionResult expectActionDoesNotExist (const gchar * nameStr, const std::string& name) { |
620 | + bool hasit = g_action_group_has_action(run->_actions.get(), name.c_str()); |
621 | + |
622 | + if (hasit) { |
623 | + auto result = testing::AssertionFailure(); |
624 | + result << |
625 | + " Action: " << nameStr << std::endl << |
626 | + " Expected: " << "No action found" << std::endl << |
627 | + " Actual: " << "Exists" << std::endl; |
628 | + |
629 | + return result; |
630 | + } |
631 | + |
632 | + auto result = testing::AssertionSuccess(); |
633 | + return result; |
634 | + } |
635 | + |
636 | + template <typename... Args> testing::AssertionResult expectEventuallyActionDoesNotExist (Args&& ... args) { |
637 | + std::function<testing::AssertionResult(void)> func = [&]() { |
638 | + return expectActionDoesNotExist(std::forward<Args>(args)...); |
639 | + }; |
640 | + return expectEventually(func); |
641 | + } |
642 | + |
643 | + testing::AssertionResult expectActionEnabled (const char * nameStr, const char * typeStr, const std::string& name, bool enabled) { |
644 | + auto aenabled = g_action_group_get_action_enabled(run->_actions.get(), name.c_str()); |
645 | + |
646 | + if (enabled != aenabled) { |
647 | + auto result = testing::AssertionFailure(); |
648 | + result << |
649 | + " Action: " << nameStr << std::endl << |
650 | + " Expected: " << enabled << std::endl << |
651 | + " Actual: " << aenabled << std::endl; |
652 | + |
653 | + return result; |
654 | + } |
655 | + |
656 | + auto result = testing::AssertionSuccess(); |
657 | + return result; |
658 | + } |
659 | + |
660 | + template <typename... Args> testing::AssertionResult expectEventuallyActionEnabled (Args&& ... args) { |
661 | + std::function<testing::AssertionResult(void)> func = [&]() { |
662 | + return expectActionEnabled(std::forward<Args>(args)...); |
663 | + }; |
664 | + return expectEventually(func); |
665 | + } |
666 | + |
667 | + testing::AssertionResult expectActionStateType (const char * nameStr, const char * typeStr, const std::string& name, const GVariantType * type) { |
668 | + auto atype = g_action_group_get_action_state_type(run->_actions.get(), name.c_str()); |
669 | + bool same = false; |
670 | + |
671 | + if (atype != nullptr) { |
672 | + same = g_variant_type_equal(atype, type); |
673 | + } |
674 | + |
675 | + if (!same) { |
676 | + auto result = testing::AssertionFailure(); |
677 | + result << |
678 | + " Action: " << nameStr << std::endl << |
679 | + " Expected: " << typeStr << std::endl << |
680 | + " Actual: " << (atype == nullptr ? "(null)" : g_variant_type_peek_string(atype)) << std::endl; |
681 | + |
682 | + return result; |
683 | + } |
684 | + |
685 | + auto result = testing::AssertionSuccess(); |
686 | + return result; |
687 | + } |
688 | + |
689 | + template <typename... Args> testing::AssertionResult expectEventuallyActionStateType (Args&& ... args) { |
690 | + std::function<testing::AssertionResult(void)> func = [&]() { |
691 | + return expectActionStateType(std::forward<Args>(args)...); |
692 | + }; |
693 | + return expectEventually(func); |
694 | + } |
695 | + |
696 | + testing::AssertionResult expectActionActivationType (const char * nameStr, const char * typeStr, const std::string& name, const GVariantType * type) { |
697 | + auto atype = g_action_group_get_action_parameter_type(run->_actions.get(), name.c_str()); |
698 | + bool same = false; |
699 | + |
700 | + if (atype != nullptr) { |
701 | + same = g_variant_type_equal(atype, type); |
702 | + } |
703 | + |
704 | + if (!same) { |
705 | + auto result = testing::AssertionFailure(); |
706 | + result << |
707 | + " Action: " << nameStr << std::endl << |
708 | + " Expected: " << typeStr << std::endl << |
709 | + " Actual: " << (atype == nullptr ? "(null)" : g_variant_type_peek_string(atype)) << std::endl; |
710 | + |
711 | + return result; |
712 | + } |
713 | + |
714 | + auto result = testing::AssertionSuccess(); |
715 | + return result; |
716 | + } |
717 | + |
718 | + template <typename... Args> testing::AssertionResult expectEventuallyActionActivationType (Args&& ... args) { |
719 | + std::function<testing::AssertionResult(void)> func = [&]() { |
720 | + return expectActionActivationType(std::forward<Args>(args)...); |
721 | + }; |
722 | + return expectEventually(func); |
723 | + } |
724 | + |
725 | + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, std::shared_ptr<GVariant> varref) { |
726 | + auto aval = std::shared_ptr<GVariant>(g_action_group_get_action_state(run->_actions.get(), name.c_str()), [] (GVariant * varptr) { |
727 | + if (varptr != nullptr) |
728 | + g_variant_unref(varptr); |
729 | + }); |
730 | + bool match = false; |
731 | + |
732 | + if (aval != nullptr) { |
733 | + match = g_variant_equal(aval.get(), varref.get()); |
734 | + } |
735 | + |
736 | + if (!match) { |
737 | + gchar * attstr = nullptr; |
738 | + |
739 | + if (aval != nullptr) { |
740 | + attstr = g_variant_print(aval.get(), TRUE); |
741 | + } else { |
742 | + attstr = g_strdup("nullptr"); |
743 | + } |
744 | + |
745 | + auto result = testing::AssertionFailure(); |
746 | + result << |
747 | + " Action: " << nameStr << std::endl << |
748 | + " Expected: " << valueStr << std::endl << |
749 | + " Actual: " << attstr << std::endl; |
750 | + |
751 | + g_free(attstr); |
752 | + |
753 | + return result; |
754 | + } else { |
755 | + auto result = testing::AssertionSuccess(); |
756 | + return result; |
757 | + } |
758 | + } |
759 | + |
760 | + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, GVariant * value) { |
761 | + auto varref = std::shared_ptr<GVariant>(g_variant_ref_sink(value), [](GVariant * varptr) { |
762 | + if (varptr != nullptr) |
763 | + g_variant_unref(varptr); |
764 | + }); |
765 | + return expectActionStateIs(nameStr, valueStr, name, varref); |
766 | + } |
767 | + |
768 | + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, bool value) { |
769 | + GVariant * var = g_variant_new_boolean(value); |
770 | + return expectActionStateIs(nameStr, valueStr, name, var); |
771 | + } |
772 | + |
773 | + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, std::string value) { |
774 | + GVariant * var = g_variant_new_string(value.c_str()); |
775 | + return expectActionStateIs(nameStr, valueStr, name, var); |
776 | + } |
777 | + |
778 | + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, const char * value) { |
779 | + GVariant * var = g_variant_new_string(value); |
780 | + return expectActionStateIs(nameStr, valueStr, name, var); |
781 | + } |
782 | + |
783 | + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, double value) { |
784 | + GVariant * var = g_variant_new_double(value); |
785 | + return expectActionStateIs(nameStr, valueStr, name, var); |
786 | + } |
787 | + |
788 | + testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, float value) { |
789 | + GVariant * var = g_variant_new_double(value); |
790 | + return expectActionStateIs(nameStr, valueStr, name, var); |
791 | + } |
792 | + |
793 | + template <typename... Args> testing::AssertionResult expectEventuallyActionStateIs (Args&& ... args) { |
794 | + std::function<testing::AssertionResult(void)> func = [&]() { |
795 | + return expectActionStateIs(std::forward<Args>(args)...); |
796 | + }; |
797 | + return expectEventually(func); |
798 | + } |
799 | + |
800 | + |
801 | + private: |
802 | + std::shared_ptr<GVariant> getMenuAttributeVal (int location, std::shared_ptr<GMenuModel>& menu, const std::string& attribute, std::shared_ptr<GVariant>& value) { |
803 | + if (!(location < g_menu_model_get_n_items(menu.get()))) { |
804 | + return nullptr; |
805 | + } |
806 | + |
807 | + if (location >= g_menu_model_get_n_items(menu.get())) |
808 | + return nullptr; |
809 | + |
810 | + auto menuval = std::shared_ptr<GVariant>(g_menu_model_get_item_attribute_value(menu.get(), location, attribute.c_str(), g_variant_get_type(value.get())), [](GVariant * varptr) { |
811 | + if (varptr != nullptr) |
812 | + g_variant_unref(varptr); |
813 | + }); |
814 | + |
815 | + return menuval; |
816 | + } |
817 | + |
818 | + std::shared_ptr<GVariant> getMenuAttributeRecurse (std::vector<int>::const_iterator menuLocation, std::vector<int>::const_iterator menuEnd, const std::string& attribute, std::shared_ptr<GVariant>& value, std::shared_ptr<GMenuModel>& menu) { |
819 | + if (menuLocation == menuEnd) |
820 | + return nullptr; |
821 | + |
822 | + if (menuLocation + 1 == menuEnd) |
823 | + return getMenuAttributeVal(*menuLocation, menu, attribute, value); |
824 | + |
825 | + auto clearfunc = [](GMenuModel * modelptr) { |
826 | + g_clear_object(&modelptr); |
827 | + }; |
828 | + |
829 | + auto submenu = std::shared_ptr<GMenuModel>(g_menu_model_get_item_link(menu.get(), *menuLocation, G_MENU_LINK_SUBMENU), clearfunc); |
830 | + |
831 | + if (submenu == nullptr) |
832 | + submenu = std::shared_ptr<GMenuModel>(g_menu_model_get_item_link(menu.get(), *menuLocation, G_MENU_LINK_SECTION), clearfunc); |
833 | + |
834 | + if (submenu == nullptr) |
835 | + return nullptr; |
836 | + |
837 | + menuWaitForItems(submenu); |
838 | + |
839 | + return getMenuAttributeRecurse(menuLocation + 1, menuEnd, attribute, value, submenu); |
840 | + } |
841 | + |
842 | + protected: |
843 | + testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const char * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, GVariant * value) { |
844 | + auto varref = std::shared_ptr<GVariant>(g_variant_ref_sink(value), [](GVariant * varptr) { |
845 | + if (varptr != nullptr) |
846 | + g_variant_unref(varptr); |
847 | + }); |
848 | + |
849 | + auto attrib = getMenuAttributeRecurse(menuLocation.cbegin(), menuLocation.cend(), attribute, varref, run->_menu); |
850 | + bool same = false; |
851 | + |
852 | + if (attrib != nullptr && varref != nullptr) { |
853 | + same = g_variant_equal(attrib.get(), varref.get()); |
854 | + } |
855 | + |
856 | + if (!same) { |
857 | + gchar * attstr = nullptr; |
858 | + |
859 | + if (attrib != nullptr) { |
860 | + attstr = g_variant_print(attrib.get(), TRUE); |
861 | + } else { |
862 | + attstr = g_strdup("nullptr"); |
863 | + } |
864 | + |
865 | + auto result = testing::AssertionFailure(); |
866 | + result << |
867 | + " Menu: " << menuLocationStr << std::endl << |
868 | + " Attribute: " << attributeStr << std::endl << |
869 | + " Expected: " << valueStr << std::endl << |
870 | + " Actual: " << attstr << std::endl; |
871 | + |
872 | + g_free(attstr); |
873 | + |
874 | + return result; |
875 | + } else { |
876 | + auto result = testing::AssertionSuccess(); |
877 | + return result; |
878 | + } |
879 | + } |
880 | + |
881 | + testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const char * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, bool value) { |
882 | + GVariant * var = g_variant_new_boolean(value); |
883 | + return expectMenuAttribute(menuLocationStr, attributeStr, valueStr, menuLocation, attribute, var); |
884 | + } |
885 | + |
886 | + testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const char * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, std::string value) { |
887 | + GVariant * var = g_variant_new_string(value.c_str()); |
888 | + return expectMenuAttribute(menuLocationStr, attributeStr, valueStr, menuLocation, attribute, var); |
889 | + } |
890 | + |
891 | + testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const char * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, const char * value) { |
892 | + GVariant * var = g_variant_new_string(value); |
893 | + return expectMenuAttribute(menuLocationStr, attributeStr, valueStr, menuLocation, attribute, var); |
894 | + } |
895 | + |
896 | + template <typename... Args> testing::AssertionResult expectEventuallyMenuAttribute (Args&& ... args) { |
897 | + std::function<testing::AssertionResult(void)> func = [&]() { |
898 | + return expectMenuAttribute(std::forward<Args>(args)...); |
899 | + }; |
900 | + return expectEventually(func); |
901 | + } |
902 | + |
903 | + /* Eventually Helpers */ |
904 | + #define _EVENTUALLY_HELPER(oper) \ |
905 | + template <typename... Args> testing::AssertionResult expectEventually##oper (Args&& ... args) { \ |
906 | + std::function<testing::AssertionResult(void)> func = [&]() { \ |
907 | + return testing::internal::CmpHelper##oper(std::forward<Args>(args)...); \ |
908 | + }; \ |
909 | + return expectEventually(func); \ |
910 | + } |
911 | + |
912 | + _EVENTUALLY_HELPER(EQ); |
913 | + _EVENTUALLY_HELPER(NE); |
914 | + _EVENTUALLY_HELPER(LT); |
915 | + _EVENTUALLY_HELPER(GT); |
916 | + _EVENTUALLY_HELPER(STREQ); |
917 | + _EVENTUALLY_HELPER(STRNE); |
918 | + |
919 | + #undef _EVENTUALLY_HELPER |
920 | +}; |
921 | + |
922 | +/* Menu Attrib */ |
923 | +#define ASSERT_MENU_ATTRIB(menu, attrib, value) \ |
924 | + ASSERT_PRED_FORMAT3(IndicatorFixture::expectMenuAttribute, menu, attrib, value) |
925 | + |
926 | +#define EXPECT_MENU_ATTRIB(menu, attrib, value) \ |
927 | + EXPECT_PRED_FORMAT3(IndicatorFixture::expectMenuAttribute, menu, attrib, value) |
928 | + |
929 | +#define EXPECT_EVENTUALLY_MENU_ATTRIB(menu, attrib, value) \ |
930 | + EXPECT_PRED_FORMAT3(IndicatorFixture::expectEventuallyMenuAttribute, menu, attrib, value) |
931 | + |
932 | +/* Action Exists */ |
933 | +#define ASSERT_ACTION_EXISTS(action) \ |
934 | + ASSERT_PRED_FORMAT1(IndicatorFixture::expectActionExists, action) |
935 | + |
936 | +#define EXPECT_ACTION_EXISTS(action) \ |
937 | + EXPECT_PRED_FORMAT1(IndicatorFixture::expectActionExists, action) |
938 | + |
939 | +#define EXPECT_EVENTUALLY_ACTION_EXISTS(action) \ |
940 | + EXPECT_PRED_FORMAT1(IndicatorFixture::expectEventuallyActionExists, action) |
941 | + |
942 | +/* Action Does Not Exist */ |
943 | +#define ASSERT_ACTION_DOES_NOT_EXIST(action) \ |
944 | + ASSERT_PRED_FORMAT1(IndicatorFixture::expectActionDoesNotExist, action) |
945 | + |
946 | +#define EXPECT_ACTION_DOES_NOT_EXIST(action) \ |
947 | + EXPECT_PRED_FORMAT1(IndicatorFixture::expectActionDoesNotExist, action) |
948 | + |
949 | +#define EXPECT_EVENTUALLY_ACTION_DOES_NOT_EXIST(action) \ |
950 | + EXPECT_PRED_FORMAT1(IndicatorFixture::expectEventuallyActionDoesNotExist, action) |
951 | + |
952 | +/* Action Enabled */ |
953 | +#define ASSERT_ACTION_ENABLED(action, state) \ |
954 | + ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionEnabled, action, state) |
955 | + |
956 | +#define EXPECT_ACTION_ENABLED(action, state) \ |
957 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionEnabled, action, state) |
958 | + |
959 | +#define EXPECT_EVENTUALLY_ACTION_ENABLED(action, state) \ |
960 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyActionEnabled, action, state) |
961 | + |
962 | +/* Action State */ |
963 | +#define ASSERT_ACTION_STATE(action, value) \ |
964 | + ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionStateIs, action, value) |
965 | + |
966 | +#define EXPECT_ACTION_STATE(action, value) \ |
967 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionStateIs, action, value) |
968 | + |
969 | +#define EXPECT_EVENTUALLY_ACTION_STATE(action, value) \ |
970 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyActionStateIs, action, value) |
971 | + |
972 | +/* Action State Type */ |
973 | +#define ASSERT_ACTION_STATE_TYPE(action, type) \ |
974 | + ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionStateType, action, type) |
975 | + |
976 | +#define EXPECT_ACTION_STATE_TYPE(action, type) \ |
977 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionStateType, action, type) |
978 | + |
979 | +#define EXPECT_EVENTUALLY_ACTION_STATE_TYPE(action, type) \ |
980 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyActionStateType, action, type) |
981 | + |
982 | +/* Action Activation Type */ |
983 | +#define ASSERT_ACTION_ACTIVATION_TYPE(action, type) \ |
984 | + ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionActivationType, action, type) |
985 | + |
986 | +#define EXPECT_ACTION_ACTIVATION_TYPE(action, type) \ |
987 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionActivationType, action, type) |
988 | + |
989 | +#define EXPECT_EVENTUALLY_ACTION_ACTIVATION_TYPE(action, type) \ |
990 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyActionActivationType, action, type) |
991 | + |
992 | +/* Helpers */ |
993 | + |
994 | +#define EXPECT_EVENTUALLY_EQ(expected, actual) \ |
995 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyEQ, expected, actual) |
996 | + |
997 | +#define EXPECT_EVENTUALLY_NE(expected, actual) \ |
998 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyNE, expected, actual) |
999 | + |
1000 | +#define EXPECT_EVENTUALLY_LT(expected, actual) \ |
1001 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyLT, expected, actual) |
1002 | + |
1003 | +#define EXPECT_EVENTUALLY_GT(expected, actual) \ |
1004 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyGT, expected, actual) |
1005 | + |
1006 | +#define EXPECT_EVENTUALLY_STREQ(expected, actual) \ |
1007 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallySTREQ, expected, actual) |
1008 | + |
1009 | +#define EXPECT_EVENTUALLY_STRNE(expected, actual) \ |
1010 | + EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallySTRNE, expected, actual) |
1011 | |
1012 | === added file 'tests/indicator-test.cpp' |
1013 | --- tests/indicator-test.cpp 1970-01-01 00:00:00 +0000 |
1014 | +++ tests/indicator-test.cpp 2015-03-10 21:21:53 +0000 |
1015 | @@ -0,0 +1,209 @@ |
1016 | +/* |
1017 | + * Copyright © 2015 Canonical Ltd. |
1018 | + * |
1019 | + * This program is free software; you can redistribute it and/or modify |
1020 | + * it under the terms of the GNU General Public License as published by |
1021 | + * the Free Software Foundation; version 3. |
1022 | + * |
1023 | + * This program is distributed in the hope that it will be useful, |
1024 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1025 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1026 | + * GNU General Public License for more details. |
1027 | + * |
1028 | + * You should have received a copy of the GNU General Public License |
1029 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1030 | + * |
1031 | + * Authors: |
1032 | + * Ted Gould <ted@canonical.com> |
1033 | + */ |
1034 | + |
1035 | +#include <gtest/gtest.h> |
1036 | +#include <gio/gio.h> |
1037 | + |
1038 | +#include "indicator-fixture.h" |
1039 | +#include "accounts-service-mock.h" |
1040 | + |
1041 | +#include "messaging-menu-app.h" |
1042 | +#include "messaging-menu-message.h" |
1043 | + |
1044 | +class IndicatorTest : public IndicatorFixture |
1045 | +{ |
1046 | +protected: |
1047 | + IndicatorTest (void) : |
1048 | + IndicatorFixture(INDICATOR_MESSAGES_SERVICE_BINARY, "com.canonical.indicator.messages") |
1049 | + { |
1050 | + } |
1051 | + |
1052 | + std::shared_ptr<AccountsServiceMock> as; |
1053 | + |
1054 | + virtual void SetUp() override |
1055 | + { |
1056 | + g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE); |
1057 | + g_setenv("GSETTINGS_BACKEND", "memory", TRUE); |
1058 | + |
1059 | + g_setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, TRUE); |
1060 | + |
1061 | + as = std::make_shared<AccountsServiceMock>(); |
1062 | + addMock(*as); |
1063 | + |
1064 | + IndicatorFixture::SetUp(); |
1065 | + } |
1066 | + |
1067 | + virtual void TearDown() override |
1068 | + { |
1069 | + as.reset(); |
1070 | + |
1071 | + IndicatorFixture::TearDown(); |
1072 | + } |
1073 | + |
1074 | +}; |
1075 | + |
1076 | + |
1077 | +TEST_F(IndicatorTest, RootAction) { |
1078 | + setActions("/com/canonical/indicator/messages"); |
1079 | + |
1080 | + EXPECT_EVENTUALLY_ACTION_EXISTS("messages"); |
1081 | + EXPECT_ACTION_STATE_TYPE("messages", G_VARIANT_TYPE("a{sv}")); |
1082 | + EXPECT_ACTION_STATE("messages", g_variant_new_parsed("{'icon': <('themed', <['indicator-messages-offline', 'indicator-messages', 'indicator']>)>, 'title': <'Notifications'>, 'accessible-desc': <'Messages'>, 'visible': <false>}")); |
1083 | +} |
1084 | + |
1085 | +TEST_F(IndicatorTest, SingleMessage) { |
1086 | + setActions("/com/canonical/indicator/messages"); |
1087 | + |
1088 | + auto app = std::shared_ptr<MessagingMenuApp>(messaging_menu_app_new("test.desktop"), [](MessagingMenuApp * app) { g_clear_object(&app); }); |
1089 | + ASSERT_NE(nullptr, app); |
1090 | + messaging_menu_app_register(app.get()); |
1091 | + |
1092 | + EXPECT_EVENTUALLY_ACTION_EXISTS("test.launch"); |
1093 | + |
1094 | + auto msg = std::shared_ptr<MessagingMenuMessage>(messaging_menu_message_new( |
1095 | + "testid", |
1096 | + nullptr, /* no icon */ |
1097 | + "Test Title", |
1098 | + "A subtitle too", |
1099 | + "You only like me for my body", |
1100 | + 0), [](MessagingMenuMessage * msg) { g_clear_object(&msg); }); |
1101 | + messaging_menu_app_append_message(app.get(), msg.get(), nullptr, FALSE); |
1102 | + |
1103 | + EXPECT_EVENTUALLY_ACTION_EXISTS("test.msg.testid"); |
1104 | + |
1105 | + setMenu("/com/canonical/indicator/messages/phone"); |
1106 | + |
1107 | + EXPECT_EVENTUALLY_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-type", "com.canonical.indicator.messages.messageitem"); |
1108 | + EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "label", "Test Title"); |
1109 | + EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-message-id", "testid"); |
1110 | + EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-subtitle", "A subtitle too"); |
1111 | + EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-text", "You only like me for my body"); |
1112 | +} |
1113 | + |
1114 | +static void |
1115 | +messageReplyActivate (GObject * obj, gchar * name, GVariant * value, gpointer user_data) { |
1116 | + auto res = reinterpret_cast<std::string *>(user_data); |
1117 | + *res = g_variant_get_string(value, nullptr); |
1118 | +} |
1119 | + |
1120 | +TEST_F(IndicatorTest, MessageReply) { |
1121 | + setActions("/com/canonical/indicator/messages"); |
1122 | + |
1123 | + auto app = std::shared_ptr<MessagingMenuApp>(messaging_menu_app_new("test.desktop"), [](MessagingMenuApp * app) { g_clear_object(&app); }); |
1124 | + ASSERT_NE(nullptr, app); |
1125 | + messaging_menu_app_register(app.get()); |
1126 | + |
1127 | + EXPECT_EVENTUALLY_ACTION_EXISTS("test.launch"); |
1128 | + |
1129 | + auto msg = std::shared_ptr<MessagingMenuMessage>(messaging_menu_message_new( |
1130 | + "messageid", |
1131 | + nullptr, /* no icon */ |
1132 | + "Reply Message", |
1133 | + "A message to reply to", |
1134 | + "In-app replies are for wimps, reply here to save yourself time and be cool.", |
1135 | + 0), [](MessagingMenuMessage * msg) { g_clear_object(&msg); }); |
1136 | + messaging_menu_message_add_action(msg.get(), |
1137 | + "replyid", |
1138 | + "Reply", |
1139 | + G_VARIANT_TYPE_STRING, |
1140 | + nullptr); |
1141 | + messaging_menu_app_append_message(app.get(), msg.get(), nullptr, FALSE); |
1142 | + |
1143 | + EXPECT_EVENTUALLY_ACTION_EXISTS("test.msg.messageid"); |
1144 | + EXPECT_EVENTUALLY_ACTION_EXISTS("test.msg-actions.messageid.replyid"); |
1145 | + EXPECT_ACTION_ACTIVATION_TYPE("test.msg-actions.messageid.replyid", G_VARIANT_TYPE_STRING); |
1146 | + |
1147 | + EXPECT_ACTION_ENABLED("remove-all", true); |
1148 | + |
1149 | + setMenu("/com/canonical/indicator/messages/phone"); |
1150 | + |
1151 | + EXPECT_EVENTUALLY_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-type", "com.canonical.indicator.messages.messageitem"); |
1152 | + |
1153 | + std::string activateResponse; |
1154 | + g_signal_connect(msg.get(), "activate", G_CALLBACK(messageReplyActivate), &activateResponse); |
1155 | + |
1156 | + activateAction("test.msg-actions.messageid.replyid", g_variant_new_string("Reply to me")); |
1157 | + |
1158 | + EXPECT_EVENTUALLY_EQ("Reply to me", activateResponse); |
1159 | + |
1160 | + EXPECT_EVENTUALLY_ACTION_ENABLED("remove-all", false); |
1161 | +} |
1162 | + |
1163 | +TEST_F(IndicatorTest, IconNotification) { |
1164 | + auto normalicon = std::shared_ptr<GVariant>(g_variant_ref_sink(g_variant_new_parsed("{'icon': <('themed', <['indicator-messages-offline', 'indicator-messages', 'indicator']>)>, 'title': <'Notifications'>, 'accessible-desc': <'Messages'>, 'visible': <true>}")), [](GVariant *var) {if (var != nullptr) g_variant_unref(var); }); |
1165 | + auto blueicon = std::shared_ptr<GVariant>(g_variant_ref_sink(g_variant_new_parsed("{'icon': <('themed', <['indicator-messages-new-offline', 'indicator-messages-new', 'indicator-messages', 'indicator']>)>, 'title': <'Notifications'>, 'accessible-desc': <'New Messages'>, 'visible': <true>}")), [](GVariant *var) {if (var != nullptr) g_variant_unref(var); }); |
1166 | + |
1167 | + setActions("/com/canonical/indicator/messages"); |
1168 | + |
1169 | + auto app = std::shared_ptr<MessagingMenuApp>(messaging_menu_app_new("test.desktop"), [](MessagingMenuApp * app) { g_clear_object(&app); }); |
1170 | + ASSERT_NE(nullptr, app); |
1171 | + messaging_menu_app_register(app.get()); |
1172 | + |
1173 | + EXPECT_EVENTUALLY_ACTION_EXISTS("test.launch"); |
1174 | + |
1175 | + EXPECT_ACTION_STATE("messages", normalicon); |
1176 | + |
1177 | + auto app2 = std::shared_ptr<MessagingMenuApp>(messaging_menu_app_new("test2.desktop"), [](MessagingMenuApp * app) { g_clear_object(&app); }); |
1178 | + ASSERT_NE(nullptr, app2); |
1179 | + messaging_menu_app_register(app2.get()); |
1180 | + |
1181 | + EXPECT_EVENTUALLY_ACTION_EXISTS("test2.launch"); |
1182 | + |
1183 | + messaging_menu_app_append_source_with_count(app2.get(), |
1184 | + "countsource", |
1185 | + nullptr, |
1186 | + "Count Source", |
1187 | + 500); |
1188 | + messaging_menu_app_draw_attention(app2.get(), "countsource"); |
1189 | + |
1190 | + EXPECT_EVENTUALLY_ACTION_STATE("messages", blueicon); |
1191 | + |
1192 | + auto msg = std::shared_ptr<MessagingMenuMessage>(messaging_menu_message_new( |
1193 | + "messageid", |
1194 | + nullptr, /* no icon */ |
1195 | + "Message", |
1196 | + "A secret message", |
1197 | + "asdfa;lkweraoweprijas;dvlknasvdoiewur;aslkd", |
1198 | + 0), [](MessagingMenuMessage * msg) { g_clear_object(&msg); }); |
1199 | + messaging_menu_message_set_draws_attention(msg.get(), true); |
1200 | + messaging_menu_app_append_message(app.get(), msg.get(), nullptr, FALSE); |
1201 | + |
1202 | + EXPECT_EVENTUALLY_ACTION_EXISTS("test.msg.messageid"); |
1203 | + EXPECT_ACTION_STATE("messages", blueicon); |
1204 | + |
1205 | + messaging_menu_app_unregister(app2.get()); |
1206 | + app2.reset(); |
1207 | + |
1208 | + EXPECT_EVENTUALLY_ACTION_DOES_NOT_EXIST("test2.msg.countsource"); |
1209 | + EXPECT_ACTION_STATE("messages", blueicon); |
1210 | + |
1211 | + messaging_menu_app_remove_message(app.get(), msg.get()); |
1212 | + |
1213 | + EXPECT_EVENTUALLY_ACTION_STATE("messages", normalicon); |
1214 | + EXPECT_ACTION_ENABLED("remove-all", false); |
1215 | + |
1216 | + messaging_menu_app_append_message(app.get(), msg.get(), nullptr, FALSE); |
1217 | + |
1218 | + EXPECT_EVENTUALLY_ACTION_STATE("messages", blueicon); |
1219 | + EXPECT_ACTION_ENABLED("remove-all", true); |
1220 | + |
1221 | + activateAction("remove-all"); |
1222 | + |
1223 | + EXPECT_EVENTUALLY_ACTION_STATE("messages", normalicon); |
1224 | +} |
1225 | |
1226 | === renamed file 'tests/manual' => 'tests/manual' |
PASSED: Continuous integration, rev:459 jenkins. qa.ubuntu. com/job/ indicator- messages- ci/147/ jenkins. qa.ubuntu. com/job/ indicator- messages- vivid-amd64- ci/9 jenkins. qa.ubuntu. com/job/ indicator- messages- vivid-armhf- ci/9 jenkins. qa.ubuntu. com/job/ indicator- messages- vivid-armhf- ci/9/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/indicator- messages- ci/147/ rebuild
http://