Merge lp:~ted/indicator-messages/sanity-testing into lp:indicator-messages/15.04

Proposed by Ted Gould
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
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.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
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.

Revision history for this message
Ted Gould (ted) wrote :
Download full text (3.4 KiB)

On Tue, 2015-03-10 at 18:42 +0000, Charles Kerr wrote:

> > + -DSCHEMA_DIR="\"$(abs_builddir)/gsettings-schemas-compiled/\"" \
>
> Might make sense to define this directory as its own variable, so that it can be used below in schemas-compiled.stamp?
>
> Also, we're using $(abs_builddir)/gsettings-schema-compiled here but $(builddir)/gsettings-schema-compiled below... I think they're the same in this MP but it's probably better to use abs_builddir everywhere?

Makes sense. r460

> > $(top_builddir)/common/indicator-messages-service.h \
> > $(top_srcdir)/src/gactionmuxer.c \
> > - $(top_srcdir)/src/gactionmuxer.h
> > + $(top_srcdir)/src/gactionmuxer.h \
>
> How did this work before?
>
> > $(top_srcdir)/src/dbus-data.h

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_ptr<GMenuModel> _menu;
> > + std::shared_ptr<GActionGroup> _actions;
> > + DbusTestService * _session_service;
> > + DbusTestService * _system_service;
> > + DbusTestTask * _test_indicator;
> > + DbusTestTask * _test_dummy;
> > + GDBusConnection * _session;
> > + GDBusConnection * _system;
> > +
>
> http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier

Eh, sure, one underscore seems like it doesn't break the rules. Not
perfect for sure.

> > + std::for_each(mocks.begin(), mocks.end(), [this](std::shared_ptr<DbusTestTask> task) {
>
> "for (auto task : mocks) {" would be clearer
>
> > + if (dbus_test_task_get_bus(task.get()) == DBUS_TEST_SERVICE_BUS_SYSTEM) {
> > + dbus_test_service_add_task(_system_service, task.get());
> > + } else {
> > + dbus_test_service_add_task(_session_service, task.get());
> > + }
> > + });

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
> > +messageReplyActivate (GObject * obj, gchar * name, GVariant * value, gpointer user_data) {
> > + std::string * res = reinterpret_cast<std::string *>(user_data);
>
> "auto res =" would avoid redundancy
>
> > + *res = g_variant_get_string(value, nullptr);
> > +}

You just want me to give into your auto everywhere world! r462

> > + EXPECT_EVENTUALLY_ACTION_ENABLED("remove-all", false);
> > +}
>
> 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....

Read more...

460. By Ted Gould

Making the schema compilation directory into its own variable

461. By Ted Gould

Remove useless for_each call

462. By Ted Gould

Pedantic use of auto

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

LGTM.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile.am'
--- Makefile.am 2013-08-20 10:38:10 +0000
+++ Makefile.am 2015-03-10 21:21:53 +0000
@@ -12,10 +12,10 @@
1212
13if BUILD_TESTS13if BUILD_TESTS
14SUBDIRS += \14SUBDIRS += \
15 test15 tests
1616
17# build src first17# build src first
18test: src18tests: src libmessaging-menu
1919
20endif20endif
2121
2222
=== modified file 'configure.ac'
--- configure.ac 2014-09-17 20:13:13 +0000
+++ configure.ac 2015-03-10 21:21:53 +0000
@@ -48,6 +48,8 @@
4848
49PKG_CHECK_MODULES(GIO, gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION)49PKG_CHECK_MODULES(GIO, gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION)
5050
51PKG_CHECK_MODULES(DBUSTEST, dbustest-1)
52
51AC_SUBST(APPLET_CFLAGS)53AC_SUBST(APPLET_CFLAGS)
52AC_SUBST(APPLET_LIBS)54AC_SUBST(APPLET_LIBS)
5355
@@ -172,7 +174,7 @@
172data/icons/scalable/categories/Makefile174data/icons/scalable/categories/Makefile
173data/upstart/Makefile175data/upstart/Makefile
174po/Makefile.in176po/Makefile.in
175test/Makefile177tests/Makefile
176libmessaging-menu/Makefile178libmessaging-menu/Makefile
177libmessaging-menu/messaging-menu.pc179libmessaging-menu/messaging-menu.pc
178doc/Makefile180doc/Makefile
179181
=== modified file 'debian/control'
--- debian/control 2014-10-08 14:45:43 +0000
+++ debian/control 2015-03-10 21:21:53 +0000
@@ -11,6 +11,7 @@
11 gtk-doc-tools,11 gtk-doc-tools,
12 intltool,12 intltool,
13 libaccountsservice-dev,13 libaccountsservice-dev,
14 libdbustest1-dev,
14 libgirepository1.0-dev (>= 0.9.12),15 libgirepository1.0-dev (>= 0.9.12),
15 libgtest-dev,16 libgtest-dev,
16 python3-dbusmock,17 python3-dbusmock,
1718
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2013-10-01 10:29:49 +0000
+++ po/POTFILES.in 2015-03-10 21:21:53 +0000
@@ -1,5 +1,4 @@
1[encoding: UTF-8]1[encoding: UTF-8]
2test/indicator-messages-service-activate.c
3src/im-phone-menu.c2src/im-phone-menu.c
4src/messages-service.c3src/messages-service.c
5src/im-desktop-menu.c4src/im-desktop-menu.c
65
=== renamed directory 'test' => 'tests'
=== removed directory 'tests'
=== modified file 'tests/Makefile.am'
--- test/Makefile.am 2013-08-28 10:23:31 +0000
+++ tests/Makefile.am 2015-03-10 21:21:53 +0000
@@ -1,5 +1,6 @@
11
2check_LIBRARIES = libgtest.a2CLEANFILES=
3check_LTLIBRARIES = libgtest.la
3check_PROGRAMS = test-gactionmuxer4check_PROGRAMS = test-gactionmuxer
45
5TESTS = $(check_PROGRAMS)6TESTS = $(check_PROGRAMS)
@@ -7,15 +8,22 @@
7AM_CPPFLAGS = $(GTEST_CPPFLAGS) \8AM_CPPFLAGS = $(GTEST_CPPFLAGS) \
8 -I${top_srcdir}/src9 -I${top_srcdir}/src
910
10nodist_libgtest_a_SOURCES = \11######################################
12# Google Test
13######################################
14
15nodist_libgtest_la_SOURCES = \
11 $(GTEST_SOURCE)/src/gtest-all.cc \16 $(GTEST_SOURCE)/src/gtest-all.cc \
12 $(GTEST_SOURCE)/src/gtest_main.cc17 $(GTEST_SOURCE)/src/gtest_main.cc
13libgtest_a_CPPFLAGS = \18libgtest_la_CPPFLAGS = \
14 $(GTEST_CPPFLAGS) -w \19 $(GTEST_CPPFLAGS) -w \
15 $(AM_CPPFLAGS)20 $(AM_CPPFLAGS)
16libgtest_a_CXXFLAGS = \21libgtest_la_CXXFLAGS = \
17 $(AM_CXXFLAGS)22 $(AM_CXXFLAGS)
1823
24######################################
25# GActionMixer
26######################################
1927
20test_gactionmuxer_SOURCES = \28test_gactionmuxer_SOURCES = \
21 test-gactionmuxer.cpp29 test-gactionmuxer.cpp
@@ -27,8 +35,46 @@
27test_gactionmuxer_LDADD = \35test_gactionmuxer_LDADD = \
28 $(APPLET_LIBS) \36 $(APPLET_LIBS) \
29 libindicator-messages-service.la \37 libindicator-messages-service.la \
30 libgtest.a38 libgtest.la
3139
40######################################
41# Indicator Test
42######################################
43
44SCHEMA_COMPILED_DIR="$(abs_builddir)/gsettings-schemas-compiled/"
45
46indicator_test_SOURCES = \
47 indicator-test.cpp
48
49indicator_test_CPPFLAGS = \
50 -DINDICATOR_MESSAGES_SERVICE_BINARY="\"$(abs_top_builddir)/src/indicator-messages-service\"" \
51 -DSCHEMA_DIR="\"$(SCHEMA_COMPILED_DIR)\"" \
52 -DXDG_DATA_DIRS="\"$(abs_srcdir)/\"" \
53 -I$(top_srcdir)/libmessaging-menu \
54 -std=c++11 \
55 $(APPLET_CFLAGS) \
56 $(DBUSTEST_CFLAGS) \
57 $(AM_CPPFLAGS)
58
59indicator_test_LDADD = \
60 $(APPLET_LIBS) \
61 $(DBUSTEST_LIBS) \
62 $(top_builddir)/libmessaging-menu/libmessaging-menu.la \
63 libgtest.la \
64 -lc -lpthread
65
66indicator-test.cpp: schemas-compiled.stamp
67
68schemas-compiled.stamp: $(top_srcdir)/data/*gschema.xml
69 @rm -rf $(SCHEMA_COMPILED_DIR)
70 @mkdir -p $(SCHEMA_COMPILED_DIR)
71 @cp -f $(top_srcdir)/data/*gschema.xml $(SCHEMA_COMPILED_DIR)
72 @`pkg-config gio-2.0 --variable glib_compile_schemas` $(SCHEMA_COMPILED_DIR)
73 @touch schemas-compiled.stamp
74
75CLEANFILES += schemas-compiled.stamp
76TESTS += indicator-test
77check_PROGRAMS += indicator-test
3278
33######################################79######################################
34# Lib containing code under test80# Lib containing code under test
@@ -41,7 +87,7 @@
41 $(top_builddir)/common/indicator-messages-service.c \87 $(top_builddir)/common/indicator-messages-service.c \
42 $(top_builddir)/common/indicator-messages-service.h \88 $(top_builddir)/common/indicator-messages-service.h \
43 $(top_srcdir)/src/gactionmuxer.c \89 $(top_srcdir)/src/gactionmuxer.c \
44 $(top_srcdir)/src/gactionmuxer.h90 $(top_srcdir)/src/gactionmuxer.h \
45 $(top_srcdir)/src/dbus-data.h91 $(top_srcdir)/src/dbus-data.h
4692
47libindicator_messages_service_ladir = \93libindicator_messages_service_ladir = \
4894
=== added file 'tests/accounts-service-mock.h'
--- tests/accounts-service-mock.h 1970-01-01 00:00:00 +0000
+++ tests/accounts-service-mock.h 2015-03-10 21:21:53 +0000
@@ -0,0 +1,134 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted@canonical.com>
18 */
19
20#include <memory>
21#include <libdbustest/dbus-test.h>
22
23class AccountsServiceMock
24{
25 DbusTestDbusMock * mock = nullptr;
26 DbusTestDbusMockObject * soundobj = nullptr;
27 DbusTestDbusMockObject * userobj = nullptr;
28 DbusTestDbusMockObject * syssoundobj = nullptr;
29 DbusTestDbusMockObject * privacyobj = nullptr;
30
31 public:
32 AccountsServiceMock () {
33 mock = dbus_test_dbus_mock_new("org.freedesktop.Accounts");
34
35 dbus_test_task_set_bus(DBUS_TEST_TASK(mock), DBUS_TEST_SERVICE_BUS_SYSTEM);
36
37 DbusTestDbusMockObject * baseobj = dbus_test_dbus_mock_get_object(mock, "/org/freedesktop/Accounts", "org.freedesktop.Accounts", NULL);
38
39 dbus_test_dbus_mock_object_add_method(mock, baseobj,
40 "CacheUser", G_VARIANT_TYPE_STRING, G_VARIANT_TYPE_OBJECT_PATH,
41 "ret = dbus.ObjectPath('/user')\n", NULL);
42 dbus_test_dbus_mock_object_add_method(mock, baseobj,
43 "FindUserById", G_VARIANT_TYPE_INT64, G_VARIANT_TYPE_OBJECT_PATH,
44 "ret = dbus.ObjectPath('/user')\n", NULL);
45 dbus_test_dbus_mock_object_add_method(mock, baseobj,
46 "FindUserByName", G_VARIANT_TYPE_STRING, G_VARIANT_TYPE_OBJECT_PATH,
47 "ret = dbus.ObjectPath('/user')\n", NULL);
48 dbus_test_dbus_mock_object_add_method(mock, baseobj,
49 "ListCachedUsers", NULL, G_VARIANT_TYPE_OBJECT_PATH_ARRAY,
50 "ret = [ dbus.ObjectPath('/user') ]\n", NULL);
51 dbus_test_dbus_mock_object_add_method(mock, baseobj,
52 "UncacheUser", G_VARIANT_TYPE_STRING, NULL,
53 "", NULL);
54
55 userobj = dbus_test_dbus_mock_get_object(mock, "/user", "org.freedesktop.Accounts.User", NULL);
56 dbus_test_dbus_mock_object_add_property(mock, userobj,
57 "UserName", G_VARIANT_TYPE_STRING,
58 g_variant_new_string(g_get_user_name()), NULL);
59 dbus_test_dbus_mock_object_add_method(mock, baseobj,
60 "SetXHasMessages", G_VARIANT_TYPE_BOOLEAN, nullptr,
61 "", NULL);
62
63 soundobj = dbus_test_dbus_mock_get_object(mock, "/user", "com.canonical.indicator.sound.AccountsService", NULL);
64 dbus_test_dbus_mock_object_add_property(mock, soundobj,
65 "Timestamp", G_VARIANT_TYPE_UINT64,
66 g_variant_new_uint64(0), NULL);
67 dbus_test_dbus_mock_object_add_property(mock, soundobj,
68 "PlayerName", G_VARIANT_TYPE_STRING,
69 g_variant_new_string(""), NULL);
70 dbus_test_dbus_mock_object_add_property(mock, soundobj,
71 "PlayerIcon", G_VARIANT_TYPE_VARIANT,
72 g_variant_new_variant(g_variant_new_string("")), NULL);
73 dbus_test_dbus_mock_object_add_property(mock, soundobj,
74 "Running", G_VARIANT_TYPE_BOOLEAN,
75 g_variant_new_boolean(FALSE), NULL);
76 dbus_test_dbus_mock_object_add_property(mock, soundobj,
77 "State", G_VARIANT_TYPE_STRING,
78 g_variant_new_string(""), NULL);
79 dbus_test_dbus_mock_object_add_property(mock, soundobj,
80 "Title", G_VARIANT_TYPE_STRING,
81 g_variant_new_string(""), NULL);
82 dbus_test_dbus_mock_object_add_property(mock, soundobj,
83 "Artist", G_VARIANT_TYPE_STRING,
84 g_variant_new_string(""), NULL);
85 dbus_test_dbus_mock_object_add_property(mock, soundobj,
86 "Album", G_VARIANT_TYPE_STRING,
87 g_variant_new_string(""), NULL);
88 dbus_test_dbus_mock_object_add_property(mock, soundobj,
89 "ArtUrl", G_VARIANT_TYPE_STRING,
90 g_variant_new_string(""), NULL);
91
92 syssoundobj = dbus_test_dbus_mock_get_object(mock, "/user", "com.ubuntu.touch.AccountsService.Sound", NULL);
93 dbus_test_dbus_mock_object_add_property(mock, syssoundobj,
94 "SilentMode", G_VARIANT_TYPE_BOOLEAN,
95 g_variant_new_boolean(FALSE), NULL);
96
97 privacyobj = dbus_test_dbus_mock_get_object(mock, "/user", "com.ubuntu.touch.AccountsService.SecurityPrivacy", NULL);
98 dbus_test_dbus_mock_object_add_property(mock, privacyobj,
99 "MessagesWelcomeScreen", G_VARIANT_TYPE_BOOLEAN,
100 g_variant_new_boolean(true), NULL);
101 dbus_test_dbus_mock_object_add_property(mock, privacyobj,
102 "StatsWelcomeScreen", G_VARIANT_TYPE_BOOLEAN,
103 g_variant_new_boolean(true), NULL);
104 }
105
106 ~AccountsServiceMock () {
107 g_debug("Destroying the Accounts Service Mock");
108 g_clear_object(&mock);
109 }
110
111 void setSilentMode (bool modeValue) {
112 dbus_test_dbus_mock_object_update_property(mock, syssoundobj,
113 "SilentMode", g_variant_new_boolean(modeValue ? TRUE : FALSE),
114 NULL);
115 }
116
117 operator std::shared_ptr<DbusTestTask> () {
118 return std::shared_ptr<DbusTestTask>(
119 DBUS_TEST_TASK(g_object_ref(mock)),
120 [](DbusTestTask * task) { g_clear_object(&task); });
121 }
122
123 operator DbusTestTask* () {
124 return DBUS_TEST_TASK(mock);
125 }
126
127 operator DbusTestDbusMock* () {
128 return mock;
129 }
130
131 DbusTestDbusMockObject * get_sound () {
132 return soundobj;
133 }
134};
0135
=== modified file 'tests/applications/test.desktop'
--- test/applications/test.desktop 2013-02-27 00:34:14 +0000
+++ tests/applications/test.desktop 2015-03-10 21:21:53 +0000
@@ -1,2 +1,5 @@
1[Desktop Entry]1[Desktop Entry]
2Type=Application2Type=Application
3Name=Test
4Exec=test
5Icon=test-app
36
=== added file 'tests/applications/test2.desktop'
--- tests/applications/test2.desktop 1970-01-01 00:00:00 +0000
+++ tests/applications/test2.desktop 2015-03-10 21:21:53 +0000
@@ -0,0 +1,5 @@
1[Desktop Entry]
2Type=Application
3Name=Test
4Exec=test
5Icon=test-app
06
=== added file 'tests/indicator-fixture.h'
--- tests/indicator-fixture.h 1970-01-01 00:00:00 +0000
+++ tests/indicator-fixture.h 2015-03-10 21:21:53 +0000
@@ -0,0 +1,688 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted@canonical.com>
18 */
19
20#include <memory>
21#include <algorithm>
22#include <string>
23#include <functional>
24#include <future>
25
26#include <gtest/gtest.h>
27#include <gio/gio.h>
28#include <libdbustest/dbus-test.h>
29
30class IndicatorFixture : public ::testing::Test
31{
32 private:
33 std::string _indicatorPath;
34 std::string _indicatorAddress;
35 std::vector<std::shared_ptr<DbusTestTask>> _mocks;
36 protected:
37 std::chrono::milliseconds _eventuallyTime;
38
39 private:
40 class PerRunData {
41 public:
42 /* We're private in the fixture but other than that we don't care,
43 we don't leak out. This object's purpose isn't to hide data it is
44 to make the lifecycle of the items more clear. */
45 std::shared_ptr<GMenuModel> _menu;
46 std::shared_ptr<GActionGroup> _actions;
47 DbusTestService * _session_service;
48 DbusTestService * _system_service;
49 DbusTestTask * _test_indicator;
50 DbusTestTask * _test_dummy;
51 GDBusConnection * _session;
52 GDBusConnection * _system;
53
54 PerRunData (const std::string& indicatorPath, const std::string& indicatorAddress, std::vector<std::shared_ptr<DbusTestTask>>& mocks)
55 : _menu(nullptr)
56 , _session(nullptr)
57 {
58 _session_service = dbus_test_service_new(nullptr);
59 dbus_test_service_set_bus(_session_service, DBUS_TEST_SERVICE_BUS_SESSION);
60
61 _system_service = dbus_test_service_new(nullptr);
62 dbus_test_service_set_bus(_system_service, DBUS_TEST_SERVICE_BUS_SYSTEM);
63
64 _test_indicator = DBUS_TEST_TASK(dbus_test_process_new(indicatorPath.c_str()));
65 dbus_test_task_set_name(_test_indicator, "Indicator");
66 dbus_test_service_add_task(_session_service, _test_indicator);
67
68 _test_dummy = dbus_test_task_new();
69 dbus_test_task_set_wait_for(_test_dummy, indicatorAddress.c_str());
70 dbus_test_task_set_name(_test_dummy, "Dummy");
71 dbus_test_service_add_task(_session_service, _test_dummy);
72
73 for(auto task : mocks) {
74 if (dbus_test_task_get_bus(task.get()) == DBUS_TEST_SERVICE_BUS_SYSTEM) {
75 dbus_test_service_add_task(_system_service, task.get());
76 } else {
77 dbus_test_service_add_task(_session_service, task.get());
78 }
79 }
80
81 g_debug("Starting System Bus");
82 dbus_test_service_start_tasks(_system_service);
83 _system = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, nullptr);
84 g_dbus_connection_set_exit_on_close(_system, FALSE);
85
86 g_debug("Starting Session Bus");
87 dbus_test_service_start_tasks(_session_service);
88 _session = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
89 g_dbus_connection_set_exit_on_close(_session, FALSE);
90 }
91
92 virtual ~PerRunData (void) {
93 _menu.reset();
94 _actions.reset();
95
96 /* D-Bus Test Stuff */
97 g_clear_object(&_test_dummy);
98 g_clear_object(&_test_indicator);
99 g_clear_object(&_session_service);
100 g_clear_object(&_system_service);
101
102 /* Wait for D-Bus session bus to go */
103 if (!g_dbus_connection_is_closed(_session)) {
104 g_dbus_connection_close_sync(_session, nullptr, nullptr);
105 }
106 g_clear_object(&_session);
107
108 if (!g_dbus_connection_is_closed(_system)) {
109 g_dbus_connection_close_sync(_system, nullptr, nullptr);
110 }
111 g_clear_object(&_system);
112 }
113 };
114
115 std::shared_ptr<PerRunData> run;
116
117 public:
118 virtual ~IndicatorFixture() = default;
119
120 IndicatorFixture (const std::string& path,
121 const std::string& addr)
122 : _indicatorPath(path)
123 , _indicatorAddress(addr)
124 , _eventuallyTime(std::chrono::seconds(5))
125 {
126 };
127
128
129 protected:
130 virtual void SetUp() override
131 {
132 run = std::make_shared<PerRunData>(_indicatorPath, _indicatorAddress, _mocks);
133
134 _mocks.clear();
135 }
136
137 virtual void TearDown() override
138 {
139 run.reset();
140 }
141
142 void addMock (std::shared_ptr<DbusTestTask> mock)
143 {
144 _mocks.push_back(mock);
145 }
146
147 std::shared_ptr<DbusTestTask> buildBustleMock (const std::string& filename, DbusTestServiceBus bus = DBUS_TEST_SERVICE_BUS_BOTH)
148 {
149 return std::shared_ptr<DbusTestTask>([filename, bus]() {
150 DbusTestTask * bustle = DBUS_TEST_TASK(dbus_test_bustle_new(filename.c_str()));
151 dbus_test_task_set_name(bustle, "Bustle");
152 dbus_test_task_set_bus(bustle, bus);
153 return bustle;
154 }(), [](DbusTestTask * bustle) {
155 g_clear_object(&bustle);
156 });
157 }
158
159 private:
160 void waitForCore (GObject * obj, const gchar * signalname) {
161 auto loop = g_main_loop_new(nullptr, FALSE);
162
163 /* Our two exit criteria */
164 gulong signal = g_signal_connect_swapped(obj, signalname, G_CALLBACK(g_main_loop_quit), loop);
165 guint timer = g_timeout_add_seconds(5, [](gpointer user_data) -> gboolean {
166 g_warning("Menu Timeout");
167 g_main_loop_quit((GMainLoop *)user_data);
168 return G_SOURCE_CONTINUE;
169 }, loop);
170
171 /* Wait for sync */
172 g_main_loop_run(loop);
173
174 /* Clean up */
175 g_source_remove(timer);
176 g_signal_handler_disconnect(obj, signal);
177
178 g_main_loop_unref(loop);
179 }
180
181 void menuWaitForItems (const std::shared_ptr<GMenuModel>& menu) {
182 auto count = g_menu_model_get_n_items(menu.get());
183
184 if (count != 0)
185 return;
186
187 waitForCore(G_OBJECT(menu.get()), "items-changed");
188 }
189
190 void agWaitForActions (const std::shared_ptr<GActionGroup>& group) {
191 auto list = std::shared_ptr<gchar *>(
192 g_action_group_list_actions(group.get()),
193 [](gchar ** list) {
194 g_strfreev(list);
195 });
196
197 if (g_strv_length(list.get()) != 0) {
198 return;
199 }
200
201 waitForCore(G_OBJECT(group.get()), "action-added");
202 }
203
204 testing::AssertionResult expectEventually (std::function<testing::AssertionResult(void)> &testfunc) {
205 auto loop = std::shared_ptr<GMainLoop>(g_main_loop_new(nullptr, FALSE), [](GMainLoop * loop) { if (loop != nullptr) g_main_loop_unref(loop); });
206
207 std::promise<testing::AssertionResult> retpromise;
208 auto retfuture = retpromise.get_future();
209 auto start = std::chrono::steady_clock::now();
210
211 /* The core of the idle function as an object so we can use the C++-isms
212 of attaching the variables and make this code reasonably readable */
213 std::function<void(void)> idlefunc = [&loop, &retpromise, &testfunc, &start, this]() -> void {
214 auto result = testfunc();
215
216 if (result == false && _eventuallyTime > (std::chrono::steady_clock::now() - start)) {
217 return;
218 }
219
220 retpromise.set_value(result);
221 g_main_loop_quit(loop.get());
222 };
223
224 auto idlesrc = g_idle_add([](gpointer data) -> gboolean {
225 auto func = reinterpret_cast<std::function<void(void)> *>(data);
226 (*func)();
227 return G_SOURCE_CONTINUE;
228 }, &idlefunc);
229
230 g_main_loop_run(loop.get());
231 g_source_remove(idlesrc);
232
233 return retfuture.get();
234 }
235
236 protected:
237 void setMenu (const std::string& path) {
238 run->_menu.reset();
239
240 g_debug("Getting Menu: %s:%s", _indicatorAddress.c_str(), path.c_str());
241 run->_menu = std::shared_ptr<GMenuModel>(G_MENU_MODEL(g_dbus_menu_model_get(run->_session, _indicatorAddress.c_str(), path.c_str())), [](GMenuModel * modelptr) {
242 g_clear_object(&modelptr);
243 });
244
245 menuWaitForItems(run->_menu);
246 }
247
248 void setActions (const std::string& path) {
249 run->_actions.reset();
250
251 run->_actions = std::shared_ptr<GActionGroup>(G_ACTION_GROUP(g_dbus_action_group_get(run->_session, _indicatorAddress.c_str(), path.c_str())), [](GActionGroup * groupptr) {
252 g_clear_object(&groupptr);
253 });
254
255 agWaitForActions(run->_actions);
256 }
257
258 void activateAction (const std::string &name, std::shared_ptr<GVariant> &parameter) {
259 g_action_group_activate_action(run->_actions.get(), name.c_str(), parameter.get());
260 }
261
262 void activateAction (const std::string &name, GVariant * parameter = nullptr) {
263 std::shared_ptr<GVariant> param;
264
265 if (parameter != nullptr)
266 param = std::shared_ptr<GVariant>(g_variant_ref_sink(parameter), [](GVariant * var) {
267 g_variant_unref(var);
268 });
269
270 return activateAction(name, param);
271 }
272
273 testing::AssertionResult expectActionExists (const gchar * nameStr, const std::string& name) {
274 bool hasit = g_action_group_has_action(run->_actions.get(), name.c_str());
275
276 if (!hasit) {
277 auto result = testing::AssertionFailure();
278 result <<
279 " Action: " << nameStr << std::endl <<
280 " Expected: " << "Exists" << std::endl <<
281 " Actual: " << "No action found" << std::endl;
282
283 return result;
284 }
285
286 auto result = testing::AssertionSuccess();
287 return result;
288 }
289
290 template <typename... Args> testing::AssertionResult expectEventuallyActionExists (Args&& ... args) {
291 std::function<testing::AssertionResult(void)> func = [&]() {
292 return expectActionExists(std::forward<Args>(args)...);
293 };
294 return expectEventually(func);
295 }
296
297 testing::AssertionResult expectActionDoesNotExist (const gchar * nameStr, const std::string& name) {
298 bool hasit = g_action_group_has_action(run->_actions.get(), name.c_str());
299
300 if (hasit) {
301 auto result = testing::AssertionFailure();
302 result <<
303 " Action: " << nameStr << std::endl <<
304 " Expected: " << "No action found" << std::endl <<
305 " Actual: " << "Exists" << std::endl;
306
307 return result;
308 }
309
310 auto result = testing::AssertionSuccess();
311 return result;
312 }
313
314 template <typename... Args> testing::AssertionResult expectEventuallyActionDoesNotExist (Args&& ... args) {
315 std::function<testing::AssertionResult(void)> func = [&]() {
316 return expectActionDoesNotExist(std::forward<Args>(args)...);
317 };
318 return expectEventually(func);
319 }
320
321 testing::AssertionResult expectActionEnabled (const char * nameStr, const char * typeStr, const std::string& name, bool enabled) {
322 auto aenabled = g_action_group_get_action_enabled(run->_actions.get(), name.c_str());
323
324 if (enabled != aenabled) {
325 auto result = testing::AssertionFailure();
326 result <<
327 " Action: " << nameStr << std::endl <<
328 " Expected: " << enabled << std::endl <<
329 " Actual: " << aenabled << std::endl;
330
331 return result;
332 }
333
334 auto result = testing::AssertionSuccess();
335 return result;
336 }
337
338 template <typename... Args> testing::AssertionResult expectEventuallyActionEnabled (Args&& ... args) {
339 std::function<testing::AssertionResult(void)> func = [&]() {
340 return expectActionEnabled(std::forward<Args>(args)...);
341 };
342 return expectEventually(func);
343 }
344
345 testing::AssertionResult expectActionStateType (const char * nameStr, const char * typeStr, const std::string& name, const GVariantType * type) {
346 auto atype = g_action_group_get_action_state_type(run->_actions.get(), name.c_str());
347 bool same = false;
348
349 if (atype != nullptr) {
350 same = g_variant_type_equal(atype, type);
351 }
352
353 if (!same) {
354 auto result = testing::AssertionFailure();
355 result <<
356 " Action: " << nameStr << std::endl <<
357 " Expected: " << typeStr << std::endl <<
358 " Actual: " << (atype == nullptr ? "(null)" : g_variant_type_peek_string(atype)) << std::endl;
359
360 return result;
361 }
362
363 auto result = testing::AssertionSuccess();
364 return result;
365 }
366
367 template <typename... Args> testing::AssertionResult expectEventuallyActionStateType (Args&& ... args) {
368 std::function<testing::AssertionResult(void)> func = [&]() {
369 return expectActionStateType(std::forward<Args>(args)...);
370 };
371 return expectEventually(func);
372 }
373
374 testing::AssertionResult expectActionActivationType (const char * nameStr, const char * typeStr, const std::string& name, const GVariantType * type) {
375 auto atype = g_action_group_get_action_parameter_type(run->_actions.get(), name.c_str());
376 bool same = false;
377
378 if (atype != nullptr) {
379 same = g_variant_type_equal(atype, type);
380 }
381
382 if (!same) {
383 auto result = testing::AssertionFailure();
384 result <<
385 " Action: " << nameStr << std::endl <<
386 " Expected: " << typeStr << std::endl <<
387 " Actual: " << (atype == nullptr ? "(null)" : g_variant_type_peek_string(atype)) << std::endl;
388
389 return result;
390 }
391
392 auto result = testing::AssertionSuccess();
393 return result;
394 }
395
396 template <typename... Args> testing::AssertionResult expectEventuallyActionActivationType (Args&& ... args) {
397 std::function<testing::AssertionResult(void)> func = [&]() {
398 return expectActionActivationType(std::forward<Args>(args)...);
399 };
400 return expectEventually(func);
401 }
402
403 testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, std::shared_ptr<GVariant> varref) {
404 auto aval = std::shared_ptr<GVariant>(g_action_group_get_action_state(run->_actions.get(), name.c_str()), [] (GVariant * varptr) {
405 if (varptr != nullptr)
406 g_variant_unref(varptr);
407 });
408 bool match = false;
409
410 if (aval != nullptr) {
411 match = g_variant_equal(aval.get(), varref.get());
412 }
413
414 if (!match) {
415 gchar * attstr = nullptr;
416
417 if (aval != nullptr) {
418 attstr = g_variant_print(aval.get(), TRUE);
419 } else {
420 attstr = g_strdup("nullptr");
421 }
422
423 auto result = testing::AssertionFailure();
424 result <<
425 " Action: " << nameStr << std::endl <<
426 " Expected: " << valueStr << std::endl <<
427 " Actual: " << attstr << std::endl;
428
429 g_free(attstr);
430
431 return result;
432 } else {
433 auto result = testing::AssertionSuccess();
434 return result;
435 }
436 }
437
438 testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, GVariant * value) {
439 auto varref = std::shared_ptr<GVariant>(g_variant_ref_sink(value), [](GVariant * varptr) {
440 if (varptr != nullptr)
441 g_variant_unref(varptr);
442 });
443 return expectActionStateIs(nameStr, valueStr, name, varref);
444 }
445
446 testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, bool value) {
447 GVariant * var = g_variant_new_boolean(value);
448 return expectActionStateIs(nameStr, valueStr, name, var);
449 }
450
451 testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, std::string value) {
452 GVariant * var = g_variant_new_string(value.c_str());
453 return expectActionStateIs(nameStr, valueStr, name, var);
454 }
455
456 testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, const char * value) {
457 GVariant * var = g_variant_new_string(value);
458 return expectActionStateIs(nameStr, valueStr, name, var);
459 }
460
461 testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, double value) {
462 GVariant * var = g_variant_new_double(value);
463 return expectActionStateIs(nameStr, valueStr, name, var);
464 }
465
466 testing::AssertionResult expectActionStateIs (const char * nameStr, const char * valueStr, const std::string& name, float value) {
467 GVariant * var = g_variant_new_double(value);
468 return expectActionStateIs(nameStr, valueStr, name, var);
469 }
470
471 template <typename... Args> testing::AssertionResult expectEventuallyActionStateIs (Args&& ... args) {
472 std::function<testing::AssertionResult(void)> func = [&]() {
473 return expectActionStateIs(std::forward<Args>(args)...);
474 };
475 return expectEventually(func);
476 }
477
478
479 private:
480 std::shared_ptr<GVariant> getMenuAttributeVal (int location, std::shared_ptr<GMenuModel>& menu, const std::string& attribute, std::shared_ptr<GVariant>& value) {
481 if (!(location < g_menu_model_get_n_items(menu.get()))) {
482 return nullptr;
483 }
484
485 if (location >= g_menu_model_get_n_items(menu.get()))
486 return nullptr;
487
488 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) {
489 if (varptr != nullptr)
490 g_variant_unref(varptr);
491 });
492
493 return menuval;
494 }
495
496 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) {
497 if (menuLocation == menuEnd)
498 return nullptr;
499
500 if (menuLocation + 1 == menuEnd)
501 return getMenuAttributeVal(*menuLocation, menu, attribute, value);
502
503 auto clearfunc = [](GMenuModel * modelptr) {
504 g_clear_object(&modelptr);
505 };
506
507 auto submenu = std::shared_ptr<GMenuModel>(g_menu_model_get_item_link(menu.get(), *menuLocation, G_MENU_LINK_SUBMENU), clearfunc);
508
509 if (submenu == nullptr)
510 submenu = std::shared_ptr<GMenuModel>(g_menu_model_get_item_link(menu.get(), *menuLocation, G_MENU_LINK_SECTION), clearfunc);
511
512 if (submenu == nullptr)
513 return nullptr;
514
515 menuWaitForItems(submenu);
516
517 return getMenuAttributeRecurse(menuLocation + 1, menuEnd, attribute, value, submenu);
518 }
519
520 protected:
521 testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const char * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, GVariant * value) {
522 auto varref = std::shared_ptr<GVariant>(g_variant_ref_sink(value), [](GVariant * varptr) {
523 if (varptr != nullptr)
524 g_variant_unref(varptr);
525 });
526
527 auto attrib = getMenuAttributeRecurse(menuLocation.cbegin(), menuLocation.cend(), attribute, varref, run->_menu);
528 bool same = false;
529
530 if (attrib != nullptr && varref != nullptr) {
531 same = g_variant_equal(attrib.get(), varref.get());
532 }
533
534 if (!same) {
535 gchar * attstr = nullptr;
536
537 if (attrib != nullptr) {
538 attstr = g_variant_print(attrib.get(), TRUE);
539 } else {
540 attstr = g_strdup("nullptr");
541 }
542
543 auto result = testing::AssertionFailure();
544 result <<
545 " Menu: " << menuLocationStr << std::endl <<
546 " Attribute: " << attributeStr << std::endl <<
547 " Expected: " << valueStr << std::endl <<
548 " Actual: " << attstr << std::endl;
549
550 g_free(attstr);
551
552 return result;
553 } else {
554 auto result = testing::AssertionSuccess();
555 return result;
556 }
557 }
558
559 testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const char * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, bool value) {
560 GVariant * var = g_variant_new_boolean(value);
561 return expectMenuAttribute(menuLocationStr, attributeStr, valueStr, menuLocation, attribute, var);
562 }
563
564 testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const char * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, std::string value) {
565 GVariant * var = g_variant_new_string(value.c_str());
566 return expectMenuAttribute(menuLocationStr, attributeStr, valueStr, menuLocation, attribute, var);
567 }
568
569 testing::AssertionResult expectMenuAttribute (const char * menuLocationStr, const char * attributeStr, const char * valueStr, const std::vector<int> menuLocation, const std::string& attribute, const char * value) {
570 GVariant * var = g_variant_new_string(value);
571 return expectMenuAttribute(menuLocationStr, attributeStr, valueStr, menuLocation, attribute, var);
572 }
573
574 template <typename... Args> testing::AssertionResult expectEventuallyMenuAttribute (Args&& ... args) {
575 std::function<testing::AssertionResult(void)> func = [&]() {
576 return expectMenuAttribute(std::forward<Args>(args)...);
577 };
578 return expectEventually(func);
579 }
580
581 /* Eventually Helpers */
582 #define _EVENTUALLY_HELPER(oper) \
583 template <typename... Args> testing::AssertionResult expectEventually##oper (Args&& ... args) { \
584 std::function<testing::AssertionResult(void)> func = [&]() { \
585 return testing::internal::CmpHelper##oper(std::forward<Args>(args)...); \
586 }; \
587 return expectEventually(func); \
588 }
589
590 _EVENTUALLY_HELPER(EQ);
591 _EVENTUALLY_HELPER(NE);
592 _EVENTUALLY_HELPER(LT);
593 _EVENTUALLY_HELPER(GT);
594 _EVENTUALLY_HELPER(STREQ);
595 _EVENTUALLY_HELPER(STRNE);
596
597 #undef _EVENTUALLY_HELPER
598};
599
600/* Menu Attrib */
601#define ASSERT_MENU_ATTRIB(menu, attrib, value) \
602 ASSERT_PRED_FORMAT3(IndicatorFixture::expectMenuAttribute, menu, attrib, value)
603
604#define EXPECT_MENU_ATTRIB(menu, attrib, value) \
605 EXPECT_PRED_FORMAT3(IndicatorFixture::expectMenuAttribute, menu, attrib, value)
606
607#define EXPECT_EVENTUALLY_MENU_ATTRIB(menu, attrib, value) \
608 EXPECT_PRED_FORMAT3(IndicatorFixture::expectEventuallyMenuAttribute, menu, attrib, value)
609
610/* Action Exists */
611#define ASSERT_ACTION_EXISTS(action) \
612 ASSERT_PRED_FORMAT1(IndicatorFixture::expectActionExists, action)
613
614#define EXPECT_ACTION_EXISTS(action) \
615 EXPECT_PRED_FORMAT1(IndicatorFixture::expectActionExists, action)
616
617#define EXPECT_EVENTUALLY_ACTION_EXISTS(action) \
618 EXPECT_PRED_FORMAT1(IndicatorFixture::expectEventuallyActionExists, action)
619
620/* Action Does Not Exist */
621#define ASSERT_ACTION_DOES_NOT_EXIST(action) \
622 ASSERT_PRED_FORMAT1(IndicatorFixture::expectActionDoesNotExist, action)
623
624#define EXPECT_ACTION_DOES_NOT_EXIST(action) \
625 EXPECT_PRED_FORMAT1(IndicatorFixture::expectActionDoesNotExist, action)
626
627#define EXPECT_EVENTUALLY_ACTION_DOES_NOT_EXIST(action) \
628 EXPECT_PRED_FORMAT1(IndicatorFixture::expectEventuallyActionDoesNotExist, action)
629
630/* Action Enabled */
631#define ASSERT_ACTION_ENABLED(action, state) \
632 ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionEnabled, action, state)
633
634#define EXPECT_ACTION_ENABLED(action, state) \
635 EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionEnabled, action, state)
636
637#define EXPECT_EVENTUALLY_ACTION_ENABLED(action, state) \
638 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyActionEnabled, action, state)
639
640/* Action State */
641#define ASSERT_ACTION_STATE(action, value) \
642 ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionStateIs, action, value)
643
644#define EXPECT_ACTION_STATE(action, value) \
645 EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionStateIs, action, value)
646
647#define EXPECT_EVENTUALLY_ACTION_STATE(action, value) \
648 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyActionStateIs, action, value)
649
650/* Action State Type */
651#define ASSERT_ACTION_STATE_TYPE(action, type) \
652 ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionStateType, action, type)
653
654#define EXPECT_ACTION_STATE_TYPE(action, type) \
655 EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionStateType, action, type)
656
657#define EXPECT_EVENTUALLY_ACTION_STATE_TYPE(action, type) \
658 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyActionStateType, action, type)
659
660/* Action Activation Type */
661#define ASSERT_ACTION_ACTIVATION_TYPE(action, type) \
662 ASSERT_PRED_FORMAT2(IndicatorFixture::expectActionActivationType, action, type)
663
664#define EXPECT_ACTION_ACTIVATION_TYPE(action, type) \
665 EXPECT_PRED_FORMAT2(IndicatorFixture::expectActionActivationType, action, type)
666
667#define EXPECT_EVENTUALLY_ACTION_ACTIVATION_TYPE(action, type) \
668 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyActionActivationType, action, type)
669
670/* Helpers */
671
672#define EXPECT_EVENTUALLY_EQ(expected, actual) \
673 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyEQ, expected, actual)
674
675#define EXPECT_EVENTUALLY_NE(expected, actual) \
676 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyNE, expected, actual)
677
678#define EXPECT_EVENTUALLY_LT(expected, actual) \
679 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyLT, expected, actual)
680
681#define EXPECT_EVENTUALLY_GT(expected, actual) \
682 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallyGT, expected, actual)
683
684#define EXPECT_EVENTUALLY_STREQ(expected, actual) \
685 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallySTREQ, expected, actual)
686
687#define EXPECT_EVENTUALLY_STRNE(expected, actual) \
688 EXPECT_PRED_FORMAT2(IndicatorFixture::expectEventuallySTRNE, expected, actual)
0689
=== added file 'tests/indicator-test.cpp'
--- tests/indicator-test.cpp 1970-01-01 00:00:00 +0000
+++ tests/indicator-test.cpp 2015-03-10 21:21:53 +0000
@@ -0,0 +1,209 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Ted Gould <ted@canonical.com>
18 */
19
20#include <gtest/gtest.h>
21#include <gio/gio.h>
22
23#include "indicator-fixture.h"
24#include "accounts-service-mock.h"
25
26#include "messaging-menu-app.h"
27#include "messaging-menu-message.h"
28
29class IndicatorTest : public IndicatorFixture
30{
31protected:
32 IndicatorTest (void) :
33 IndicatorFixture(INDICATOR_MESSAGES_SERVICE_BINARY, "com.canonical.indicator.messages")
34 {
35 }
36
37 std::shared_ptr<AccountsServiceMock> as;
38
39 virtual void SetUp() override
40 {
41 g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE);
42 g_setenv("GSETTINGS_BACKEND", "memory", TRUE);
43
44 g_setenv("XDG_DATA_DIRS", XDG_DATA_DIRS, TRUE);
45
46 as = std::make_shared<AccountsServiceMock>();
47 addMock(*as);
48
49 IndicatorFixture::SetUp();
50 }
51
52 virtual void TearDown() override
53 {
54 as.reset();
55
56 IndicatorFixture::TearDown();
57 }
58
59};
60
61
62TEST_F(IndicatorTest, RootAction) {
63 setActions("/com/canonical/indicator/messages");
64
65 EXPECT_EVENTUALLY_ACTION_EXISTS("messages");
66 EXPECT_ACTION_STATE_TYPE("messages", G_VARIANT_TYPE("a{sv}"));
67 EXPECT_ACTION_STATE("messages", g_variant_new_parsed("{'icon': <('themed', <['indicator-messages-offline', 'indicator-messages', 'indicator']>)>, 'title': <'Notifications'>, 'accessible-desc': <'Messages'>, 'visible': <false>}"));
68}
69
70TEST_F(IndicatorTest, SingleMessage) {
71 setActions("/com/canonical/indicator/messages");
72
73 auto app = std::shared_ptr<MessagingMenuApp>(messaging_menu_app_new("test.desktop"), [](MessagingMenuApp * app) { g_clear_object(&app); });
74 ASSERT_NE(nullptr, app);
75 messaging_menu_app_register(app.get());
76
77 EXPECT_EVENTUALLY_ACTION_EXISTS("test.launch");
78
79 auto msg = std::shared_ptr<MessagingMenuMessage>(messaging_menu_message_new(
80 "testid",
81 nullptr, /* no icon */
82 "Test Title",
83 "A subtitle too",
84 "You only like me for my body",
85 0), [](MessagingMenuMessage * msg) { g_clear_object(&msg); });
86 messaging_menu_app_append_message(app.get(), msg.get(), nullptr, FALSE);
87
88 EXPECT_EVENTUALLY_ACTION_EXISTS("test.msg.testid");
89
90 setMenu("/com/canonical/indicator/messages/phone");
91
92 EXPECT_EVENTUALLY_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-type", "com.canonical.indicator.messages.messageitem");
93 EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "label", "Test Title");
94 EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-message-id", "testid");
95 EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-subtitle", "A subtitle too");
96 EXPECT_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-text", "You only like me for my body");
97}
98
99static void
100messageReplyActivate (GObject * obj, gchar * name, GVariant * value, gpointer user_data) {
101 auto res = reinterpret_cast<std::string *>(user_data);
102 *res = g_variant_get_string(value, nullptr);
103}
104
105TEST_F(IndicatorTest, MessageReply) {
106 setActions("/com/canonical/indicator/messages");
107
108 auto app = std::shared_ptr<MessagingMenuApp>(messaging_menu_app_new("test.desktop"), [](MessagingMenuApp * app) { g_clear_object(&app); });
109 ASSERT_NE(nullptr, app);
110 messaging_menu_app_register(app.get());
111
112 EXPECT_EVENTUALLY_ACTION_EXISTS("test.launch");
113
114 auto msg = std::shared_ptr<MessagingMenuMessage>(messaging_menu_message_new(
115 "messageid",
116 nullptr, /* no icon */
117 "Reply Message",
118 "A message to reply to",
119 "In-app replies are for wimps, reply here to save yourself time and be cool.",
120 0), [](MessagingMenuMessage * msg) { g_clear_object(&msg); });
121 messaging_menu_message_add_action(msg.get(),
122 "replyid",
123 "Reply",
124 G_VARIANT_TYPE_STRING,
125 nullptr);
126 messaging_menu_app_append_message(app.get(), msg.get(), nullptr, FALSE);
127
128 EXPECT_EVENTUALLY_ACTION_EXISTS("test.msg.messageid");
129 EXPECT_EVENTUALLY_ACTION_EXISTS("test.msg-actions.messageid.replyid");
130 EXPECT_ACTION_ACTIVATION_TYPE("test.msg-actions.messageid.replyid", G_VARIANT_TYPE_STRING);
131
132 EXPECT_ACTION_ENABLED("remove-all", true);
133
134 setMenu("/com/canonical/indicator/messages/phone");
135
136 EXPECT_EVENTUALLY_MENU_ATTRIB(std::vector<int>({0, 0, 0}), "x-canonical-type", "com.canonical.indicator.messages.messageitem");
137
138 std::string activateResponse;
139 g_signal_connect(msg.get(), "activate", G_CALLBACK(messageReplyActivate), &activateResponse);
140
141 activateAction("test.msg-actions.messageid.replyid", g_variant_new_string("Reply to me"));
142
143 EXPECT_EVENTUALLY_EQ("Reply to me", activateResponse);
144
145 EXPECT_EVENTUALLY_ACTION_ENABLED("remove-all", false);
146}
147
148TEST_F(IndicatorTest, IconNotification) {
149 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); });
150 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); });
151
152 setActions("/com/canonical/indicator/messages");
153
154 auto app = std::shared_ptr<MessagingMenuApp>(messaging_menu_app_new("test.desktop"), [](MessagingMenuApp * app) { g_clear_object(&app); });
155 ASSERT_NE(nullptr, app);
156 messaging_menu_app_register(app.get());
157
158 EXPECT_EVENTUALLY_ACTION_EXISTS("test.launch");
159
160 EXPECT_ACTION_STATE("messages", normalicon);
161
162 auto app2 = std::shared_ptr<MessagingMenuApp>(messaging_menu_app_new("test2.desktop"), [](MessagingMenuApp * app) { g_clear_object(&app); });
163 ASSERT_NE(nullptr, app2);
164 messaging_menu_app_register(app2.get());
165
166 EXPECT_EVENTUALLY_ACTION_EXISTS("test2.launch");
167
168 messaging_menu_app_append_source_with_count(app2.get(),
169 "countsource",
170 nullptr,
171 "Count Source",
172 500);
173 messaging_menu_app_draw_attention(app2.get(), "countsource");
174
175 EXPECT_EVENTUALLY_ACTION_STATE("messages", blueicon);
176
177 auto msg = std::shared_ptr<MessagingMenuMessage>(messaging_menu_message_new(
178 "messageid",
179 nullptr, /* no icon */
180 "Message",
181 "A secret message",
182 "asdfa;lkweraoweprijas;dvlknasvdoiewur;aslkd",
183 0), [](MessagingMenuMessage * msg) { g_clear_object(&msg); });
184 messaging_menu_message_set_draws_attention(msg.get(), true);
185 messaging_menu_app_append_message(app.get(), msg.get(), nullptr, FALSE);
186
187 EXPECT_EVENTUALLY_ACTION_EXISTS("test.msg.messageid");
188 EXPECT_ACTION_STATE("messages", blueicon);
189
190 messaging_menu_app_unregister(app2.get());
191 app2.reset();
192
193 EXPECT_EVENTUALLY_ACTION_DOES_NOT_EXIST("test2.msg.countsource");
194 EXPECT_ACTION_STATE("messages", blueicon);
195
196 messaging_menu_app_remove_message(app.get(), msg.get());
197
198 EXPECT_EVENTUALLY_ACTION_STATE("messages", normalicon);
199 EXPECT_ACTION_ENABLED("remove-all", false);
200
201 messaging_menu_app_append_message(app.get(), msg.get(), nullptr, FALSE);
202
203 EXPECT_EVENTUALLY_ACTION_STATE("messages", blueicon);
204 EXPECT_ACTION_ENABLED("remove-all", true);
205
206 activateAction("remove-all");
207
208 EXPECT_EVENTUALLY_ACTION_STATE("messages", normalicon);
209}
0210
=== renamed file 'tests/manual' => 'tests/manual'

Subscribers

People subscribed via source and target branches