Merge lp:~charlesk/indicator-session/add-dbus-test-harness into lp:indicator-session/12.10

Proposed by Charles Kerr
Status: Merged
Merge reported by: Charles Kerr
Merged at revision: not available
Proposed branch: lp:~charlesk/indicator-session/add-dbus-test-harness
Merge into: lp:indicator-session/12.10
Diff against target: 436 lines (+419/-2)
2 files modified
tests/gtest-dbus-helper.h (+418/-0)
tests/test-service.cc (+1/-2)
To merge this branch: bzr merge lp:~charlesk/indicator-session/add-dbus-test-harness
Reviewer Review Type Date Requested Status
jenkins (community) continuous-integration Needs Fixing
Allan LeSage Approve
Review via email: mp+121063@code.launchpad.net

Description of the change

Add a Google Test + libdbustest harness for bug #1040678

To post a comment you must log in.
Revision history for this message
jenkins (martin-mrazik+qa) wrote :
review: Approve (continuous-integration)
Revision history for this message
Allan LeSage (allanlesage) wrote :

Discussed with charlesk, he suggested revisiting this with glib-native DBus testing framework; confirm Jenkins' finding that this builds.

review: Approve
Revision history for this message
jenkins (martin-mrazik+qa) wrote :

FAILED: Autolanding.
No commit message was specified.
http://jenkins.qa.ubuntu.com/job/indicator-session-autolanding/6/

review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'tests/gtest-dbus-helper.h'
2--- tests/gtest-dbus-helper.h 1970-01-01 00:00:00 +0000
3+++ tests/gtest-dbus-helper.h 2012-08-23 18:06:22 +0000
4@@ -0,0 +1,418 @@
5+/*
6+Copyright 2012 Canonical Ltd.
7+
8+Authors:
9+ Charles Kerr <charles.kerr@canonical.com>
10+
11+This program is free software: you can redistribute it and/or modify it
12+under the terms of the GNU General Public License version 3, as published
13+by the Free Software Foundation.
14+
15+This program is distributed in the hope that it will be useful, but
16+WITHOUT ANY WARRANTY; without even the implied warranties of
17+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
18+PURPOSE. See the GNU General Public License for more details.
19+
20+You should have received a copy of the GNU General Public License along
21+with this program. If not, see <http://www.gnu.org/licenses/>.
22+*/
23+
24+#ifndef INDICATOR_SERVICE_TEST_H
25+#define INDICATOR_SERVICE_TEST_H
26+
27+#include <algorithm>
28+#include <functional>
29+#include <string>
30+#include <vector>
31+
32+#include <gio/gio.h>
33+#include <gtest/gtest.h>
34+#include <libdbustest/dbus-test.h>
35+#include <libdbusmenu-glib/client.h>
36+
37+/***
38+****
39+***/
40+
41+/**
42+ * Convenience class for looking at a DbusmenuClient's items for testing
43+ *
44+ * Examples:
45+ *
46+ * // confirm that there are N menuitems of type T
47+ * TEST_EQ (helper.count_type(T), N);
48+ *
49+ * // confirm that there are N visible menuitems
50+ * TEST_EQ (helper.count_property_bool(DBUSMENU_MENUITEM_PROP_VISIBLE,true), N);
51+ *
52+ * // get a sorted list of all the menuitems of type T
53+ * std::vector<DbusmenuMenuitem*> items = helper.find_type(T);
54+ */
55+class DbusmenuClientHelper
56+{
57+ public:
58+
59+ DbusmenuClientHelper (DbusmenuClient * client_): client(client_) {
60+ g_object_ref (G_OBJECT(client));
61+ }
62+ ~DbusmenuClientHelper() {
63+ g_object_unref(G_OBJECT(client));
64+ client = NULL;
65+ }
66+
67+ private:
68+
69+ static void foreach_accumulate_func (DbusmenuMenuitem * mi, gpointer gset) {
70+ static_cast<std::vector<DbusmenuMenuitem*>*>(gset)->push_back (mi);
71+ }
72+
73+ public:
74+
75+ std::vector<DbusmenuMenuitem*>
76+ get_all_menuitems () const
77+ {
78+ std::vector<DbusmenuMenuitem*> items;
79+
80+ DbusmenuMenuitem * root = dbusmenu_client_get_root (client);
81+ if (root != NULL)
82+ dbusmenu_menuitem_foreach (root, foreach_accumulate_func, &items);
83+
84+ return items;
85+ }
86+
87+ private:
88+
89+ template<typename value_type> class PropertyPredicate:
90+ public std::unary_function<DbusmenuMenuitem*,bool> {
91+ protected:
92+ const std::string _key;
93+ const value_type _value;
94+ virtual value_type get_value(DbusmenuMenuitem * mi) const = 0;
95+ public:
96+ PropertyPredicate (const char * propertyName, value_type propertyValue):
97+ _key(propertyName), _value(propertyValue) { }
98+ bool operator()(const DbusmenuMenuitem* cmi) const {
99+ // FIXME: remove const_cast after the dbusmenu_menuitem_propperty_get*() functions are constified
100+ DbusmenuMenuitem * mi = const_cast<DbusmenuMenuitem*>(cmi);
101+ return dbusmenu_menuitem_property_exist (mi, _key.c_str()) && (_value == get_value(mi));
102+ }
103+ };
104+
105+ class StringPropertyPredicate: public PropertyPredicate<std::string> {
106+ protected:
107+ virtual std::string get_value (DbusmenuMenuitem * mi) const {
108+ return dbusmenu_menuitem_property_get(mi, _key.c_str());
109+ }
110+ public:
111+ StringPropertyPredicate (const char * propName, const char * propValue):
112+ PropertyPredicate (propName, propValue) {}
113+ };
114+
115+ class IntPropertyPredicate: public PropertyPredicate<int> {
116+ protected:
117+ virtual int get_value (DbusmenuMenuitem * mi) const {
118+ return dbusmenu_menuitem_property_get_int(mi, _key.c_str());
119+ }
120+ public:
121+ IntPropertyPredicate (const char * propName, int propValue):
122+ PropertyPredicate (propName, propValue) {}
123+ };
124+
125+ class BoolPropertyPredicate: public PropertyPredicate<bool> {
126+ protected:
127+ virtual bool get_value (DbusmenuMenuitem * mi) const {
128+ return dbusmenu_menuitem_property_get_bool(mi, _key.c_str());
129+ }
130+ public:
131+ BoolPropertyPredicate (const char * propName, bool propValue):
132+ PropertyPredicate (propName, propValue) {}
133+ };
134+
135+ public:
136+
137+ typedef std::vector<DbusmenuMenuitem*> menuitems_t;
138+
139+ void
140+ match_property (menuitems_t& items, const char * key, const char * value) const
141+ {
142+ const StringPropertyPredicate pred (key, value);
143+ items.erase (std::remove_if (items.begin(), items.end(), std::not1(pred)), items.end());
144+ }
145+
146+ void
147+ match_property_int (menuitems_t& items, const char * key, int value) const
148+ {
149+ const IntPropertyPredicate pred (key, value);
150+ items.erase (std::remove_if (items.begin(), items.end(), std::not1(pred)), items.end());
151+ }
152+
153+ void
154+ match_property_bool (menuitems_t& items, const char * key, bool value) const
155+ {
156+ const BoolPropertyPredicate pred (key, value);
157+ items.erase (std::remove_if (items.begin(), items.end(), std::not1(pred)), items.end());
158+ }
159+
160+ menuitems_t find_property (const char * prop_name, const char * prop_value) const
161+ {
162+ menuitems_t items;
163+ g_return_val_if_fail (prop_name!=NULL, items);
164+ g_return_val_if_fail (prop_value!=NULL, items);
165+
166+ items = get_all_menuitems ();
167+ match_property (items, prop_name, prop_value);
168+ return items;
169+ }
170+
171+ menuitems_t find_property_int (const char * prop_name, int prop_value) const
172+ {
173+ std::vector<DbusmenuMenuitem*> items;
174+ g_return_val_if_fail (prop_name!=NULL, items);
175+
176+ items = get_all_menuitems ();
177+ match_property_int (items, prop_name, prop_value);
178+ return items;
179+ }
180+
181+ menuitems_t find_property_bool (const char * prop_name, bool prop_value) const
182+ {
183+ std::vector<DbusmenuMenuitem*> items;
184+ g_return_val_if_fail (prop_name!=NULL, items);
185+
186+ items = get_all_menuitems ();
187+ match_property_bool (items, prop_name, prop_value);
188+ return items;
189+ }
190+
191+ menuitems_t find_type (const char * type) const
192+ {
193+ return find_property (DBUSMENU_MENUITEM_PROP_TYPE, type);
194+ }
195+
196+ int count_property (const char * propName, const char * propValue) const
197+ {
198+ return find_property (propName, propValue).size();
199+ }
200+
201+ int count_type (const char * type) const
202+ {
203+ return count_property (DBUSMENU_MENUITEM_PROP_TYPE, type);
204+ }
205+
206+ int count_property_int (const char * propName, int propValue) const
207+ {
208+ return find_property_int (propName, propValue).size();
209+ }
210+
211+ int count_property_bool (const char * propName, bool propValue) const
212+ {
213+ return find_property_bool (propName, propValue).size();
214+ }
215+
216+ private:
217+
218+ DbusmenuClient * client;
219+};
220+
221+/**
222+ * Fixture class for using Google Test on an indicator-service's
223+ * com.canonical.dbusmenu interface.
224+ *
225+ * The SetUp() function starts the service up, waits for it to
226+ * be visible on the bus, then creates a DbusmenuClient and waits
227+ * for its layout-changed signal. This way the test function
228+ * is reached after the menu is available and populated.
229+ *
230+ * TearDown() cleans up the DBus scaffolding and stops the service.
231+ */
232+class IndicatorServiceTest : public ::testing::Test
233+{
234+ public:
235+
236+ IndicatorServiceTest(const char * service_name_,
237+ const char * menu_object_path_,
238+ const char * executable_):
239+ menu_client(0),
240+ menu_helper(0),
241+ test_service(0),
242+ indicator_service_proxy(0),
243+ handler_id(0),
244+ executable(executable_),
245+ service_name(service_name_),
246+ menu_object_path(menu_object_path_)
247+ {
248+ // glib one-time init stuff
249+ g_type_init();
250+ g_assert (g_thread_supported());
251+ mainloop = g_main_loop_new (NULL, FALSE);
252+ }
253+
254+ private:
255+
256+ static void
257+ on_layout_updated_static (DbusmenuClient * client, IndicatorServiceTest * self)
258+ {
259+ g_debug ("LAYOUT UPDATED");
260+ self->on_layout_updated (client);
261+ }
262+ void
263+ on_layout_updated (DbusmenuClient * client)
264+ {
265+ ASSERT_EQ (client, menu_client);
266+ ASSERT_NE (handler_id, 0);
267+ ASSERT_TRUE (g_signal_handler_is_connected (client, handler_id));
268+
269+ // stop listening for this event
270+ g_signal_handler_disconnect (client, handler_id);
271+ handler_id = 0;
272+
273+ ready();
274+ }
275+
276+ private:
277+
278+ static gboolean
279+ on_timeout_static (gpointer self)
280+ {
281+ static_cast<IndicatorServiceTest*>(self)->on_timeout();
282+ return false;
283+ }
284+ void
285+ on_timeout()
286+ {
287+ ASSERT_NE (handler_id, 0ul);
288+ g_source_remove (handler_id);
289+ handler_id = 0;
290+ ready();
291+ }
292+
293+ protected:
294+
295+ virtual void
296+ SetUp()
297+ {
298+ ASSERT_EQ (NULL, test_service);
299+ ASSERT_EQ (NULL, menu_helper);
300+ ASSERT_EQ (NULL, indicator_service_proxy);
301+
302+ test_service = dbus_test_service_new (NULL);
303+
304+ // Start the executable and wait until it shows up on the bus.
305+ // Unset the NO_WATCHERS env var to ensure that the service
306+ // will shut down when there are no watchers... otherwise
307+ // this task will never finish
308+ g_unsetenv("INDICATOR_ALLOW_NO_WATCHERS");
309+ DbusTestProcess * indicator_service_task = dbus_test_process_new (executable.c_str());
310+ dbus_test_service_add_task (test_service, DBUS_TEST_TASK(indicator_service_task));
311+ g_object_unref (G_OBJECT(indicator_service_task));
312+
313+ // create a menu task that waits for our service before it runs
314+ DbusTestTask * wait_task = dbus_test_task_new ();
315+ dbus_test_task_set_wait_for (wait_task, service_name.c_str());
316+ dbus_test_service_add_task (test_service, wait_task);
317+ g_object_unref (G_OBJECT(wait_task));
318+
319+ g_debug ("starting tasks");
320+ dbus_test_service_start_tasks(test_service);
321+
322+ // at this point the indicator service is running, let's Watch it
323+ // to ensure it stays alive for the duration of the test
324+ GError * error = NULL;
325+ GDBusConnection * bus_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
326+ indicator_service_proxy = g_dbus_proxy_new_sync (bus_connection,
327+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
328+ NULL,
329+ service_name.c_str(),
330+ "/org/ayatana/indicator/service",
331+ "org.ayatana.indicator.service",
332+ NULL,
333+ &error);
334+ GVariant * result = g_dbus_proxy_call_sync (indicator_service_proxy, "Watch",
335+ NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
336+ -1, NULL, &error);
337+ guint a, b;
338+ g_variant_get (result, "(uu)", &a, &b);
339+ g_debug ("Sending 'Watch' to proxy %p yielded %u %u", indicator_service_proxy, a, b);
340+ g_variant_unref (result);
341+ EXPECT_EQ(NULL, error);
342+ if (error != NULL) {
343+ g_message ("%s Unable to Watch indicator-service : %s", G_STRFUNC, error->message);
344+ g_clear_error (&error);
345+ }
346+ g_object_unref (G_OBJECT(bus_connection));
347+
348+ menu_client = dbusmenu_client_new (service_name.c_str(), menu_object_path.c_str());
349+ menu_helper = new DbusmenuClientHelper (menu_client);
350+
351+ // wait for a "layout-updated" signal before we let SetUp finish
352+ wait_for_layout_update();
353+ }
354+
355+ virtual void
356+ TearDown()
357+ {
358+ ASSERT_EQ (handler_id, 0);
359+
360+ // tear down the mainloop
361+ ASSERT_TRUE (mainloop != NULL);
362+ g_main_loop_unref (mainloop);
363+ mainloop = NULL;
364+
365+ // tear down the menu client
366+ if (menu_helper != NULL) {
367+ delete menu_helper;
368+ menu_helper = NULL;
369+ }
370+ if (menu_client != NULL) {
371+ g_object_unref(G_OBJECT(menu_client));
372+ menu_client = NULL;
373+ }
374+
375+ // tear down the indicator proxy
376+ EXPECT_TRUE (G_IS_DBUS_PROXY(indicator_service_proxy));
377+ g_object_unref (G_OBJECT(indicator_service_proxy));
378+ indicator_service_proxy = NULL;
379+ }
380+
381+ void wait_for_layout_update()
382+ {
383+ ASSERT_EQ (handler_id, 0ul);
384+ handler_id = g_signal_connect (menu_client,
385+ DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED,
386+ G_CALLBACK(on_layout_updated_static),
387+ this);
388+ g_debug ("waiting for layout update...");
389+ g_main_loop_run (mainloop);
390+ }
391+
392+ void wait_seconds (int seconds)
393+ {
394+ ASSERT_EQ (handler_id, 0ul);
395+ handler_id = g_timeout_add_seconds (seconds, on_timeout_static, this);
396+ g_debug ("waiting %d seconds...", seconds);
397+ g_main_loop_run (mainloop);
398+ }
399+
400+ protected:
401+
402+ DbusmenuClient * menu_client;
403+ DbusmenuClientHelper * menu_helper;
404+
405+ private:
406+
407+ void ready()
408+ {
409+ g_debug("done waiting");
410+ g_main_loop_quit (mainloop);
411+ }
412+
413+ GMainLoop * mainloop;
414+ DbusTestService * test_service;
415+ GDBusProxy * indicator_service_proxy;
416+ gulong handler_id;
417+ const std::string executable;
418+ const std::string service_name;
419+ const std::string menu_object_path;
420+};
421+
422+#endif // #ifndef INDICATOR_SERVICE_TEST_H
423
424=== modified file 'tests/test-service.cc'
425--- tests/test-service.cc 2012-07-11 14:51:42 +0000
426+++ tests/test-service.cc 2012-08-23 18:06:22 +0000
427@@ -17,8 +17,7 @@
428 with this program. If not, see <http://www.gnu.org/licenses/>.
429 */
430
431-#include <libindicator/indicator-service-test.h>
432-
433+#include "gtest-dbus-helper.h"
434 #include "shared-names.h"
435
436 /***

Subscribers

People subscribed via source and target branches