Merge lp:~charlesk/indicator-session/add-dbus-test-harness into lp:indicator-session/12.10
- add-dbus-test-harness
- Merge into trunk.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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
jenkins (community) | continuous-integration | Needs Fixing | |
Allan LeSage | Approve | ||
Review via email: mp+121063@code.launchpad.net |
Commit message
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://
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 | /*** |
PASSED: Continuous integration, rev:350 jenkins. qa.ubuntu. com/job/ indicator- session- ci/7/ jenkins. qa.ubuntu. com/job/ indicator- session- ci/./label= quantal/ 7/console
http://
Executed test runs:
SUCCESS: http://