2
Copyright 2012 Canonical Ltd.
5
Charles Kerr <charles.kerr@canonical.com>
7
This program is free software: you can redistribute it and/or modify it
8
under the terms of the GNU General Public License version 3, as published
9
by the Free Software Foundation.
11
This program is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranties of
13
MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14
PURPOSE. See the GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License along
17
with this program. If not, see <http://www.gnu.org/licenses/>.
20
#ifndef INDICATOR_SERVICE_TEST_H
21
#define INDICATOR_SERVICE_TEST_H
29
#include <gtest/gtest.h>
30
#include <libdbustest/dbus-test.h>
31
#include <libdbusmenu-glib/client.h>
38
* Convenience class for looking at a DbusmenuClient's items for testing
42
* // confirm that there are N menuitems of type T
43
* TEST_EQ (helper.count_type(T), N);
45
* // confirm that there are N visible menuitems
46
* TEST_EQ (helper.count_property_bool(DBUSMENU_MENUITEM_PROP_VISIBLE,true), N);
48
* // get a sorted list of all the menuitems of type T
49
* std::vector<DbusmenuMenuitem*> items = helper.find_type(T);
51
class DbusmenuClientHelper
55
DbusmenuClientHelper (DbusmenuClient * client_): client(client_) {
56
g_object_ref (G_OBJECT(client));
58
~DbusmenuClientHelper() {
59
g_object_unref(G_OBJECT(client));
65
static void foreach_accumulate_func (DbusmenuMenuitem * mi, gpointer gset) {
66
static_cast<std::vector<DbusmenuMenuitem*>*>(gset)->push_back (mi);
71
std::vector<DbusmenuMenuitem*>
72
get_all_menuitems () const
74
std::vector<DbusmenuMenuitem*> items;
76
DbusmenuMenuitem * root = dbusmenu_client_get_root (client);
78
dbusmenu_menuitem_foreach (root, foreach_accumulate_func, &items);
85
template<typename value_type> class PropertyPredicate:
86
public std::unary_function<DbusmenuMenuitem*,bool> {
88
const std::string _key;
89
const value_type _value;
90
virtual value_type get_value(DbusmenuMenuitem * mi) const = 0;
92
PropertyPredicate (const char * propertyName, value_type propertyValue):
93
_key(propertyName), _value(propertyValue) { }
94
bool operator()(const DbusmenuMenuitem* cmi) const {
95
// FIXME: remove const_cast after the dbusmenu_menuitem_propperty_get*() functions are constified
96
DbusmenuMenuitem * mi = const_cast<DbusmenuMenuitem*>(cmi);
97
return dbusmenu_menuitem_property_exist (mi, _key.c_str()) && (_value == get_value(mi));
101
class StringPropertyPredicate: public PropertyPredicate<std::string> {
103
virtual std::string get_value (DbusmenuMenuitem * mi) const {
104
return dbusmenu_menuitem_property_get(mi, _key.c_str());
107
StringPropertyPredicate (const char * propName, const char * propValue):
108
PropertyPredicate (propName, propValue) {}
111
class IntPropertyPredicate: public PropertyPredicate<int> {
113
virtual int get_value (DbusmenuMenuitem * mi) const {
114
return dbusmenu_menuitem_property_get_int(mi, _key.c_str());
117
IntPropertyPredicate (const char * propName, int propValue):
118
PropertyPredicate (propName, propValue) {}
121
class BoolPropertyPredicate: public PropertyPredicate<bool> {
123
virtual bool get_value (DbusmenuMenuitem * mi) const {
124
return dbusmenu_menuitem_property_get_bool(mi, _key.c_str());
127
BoolPropertyPredicate (const char * propName, bool propValue):
128
PropertyPredicate (propName, propValue) {}
133
typedef std::vector<DbusmenuMenuitem*> menuitems_t;
136
match_property (menuitems_t& items, const char * key, const char * value) const
138
const StringPropertyPredicate pred (key, value);
139
items.erase (std::remove_if (items.begin(), items.end(), std::not1(pred)), items.end());
143
match_property_int (menuitems_t& items, const char * key, int value) const
145
const IntPropertyPredicate pred (key, value);
146
items.erase (std::remove_if (items.begin(), items.end(), std::not1(pred)), items.end());
150
match_property_bool (menuitems_t& items, const char * key, bool value) const
152
const BoolPropertyPredicate pred (key, value);
153
items.erase (std::remove_if (items.begin(), items.end(), std::not1(pred)), items.end());
156
menuitems_t find_property (const char * prop_name, const char * prop_value) const
159
g_return_val_if_fail (prop_name!=NULL, items);
160
g_return_val_if_fail (prop_value!=NULL, items);
162
items = get_all_menuitems ();
163
match_property (items, prop_name, prop_value);
167
menuitems_t find_property_int (const char * prop_name, int prop_value) const
169
std::vector<DbusmenuMenuitem*> items;
170
g_return_val_if_fail (prop_name!=NULL, items);
172
items = get_all_menuitems ();
173
match_property_int (items, prop_name, prop_value);
177
menuitems_t find_property_bool (const char * prop_name, bool prop_value) const
179
std::vector<DbusmenuMenuitem*> items;
180
g_return_val_if_fail (prop_name!=NULL, items);
182
items = get_all_menuitems ();
183
match_property_bool (items, prop_name, prop_value);
187
menuitems_t find_type (const char * type) const
189
return find_property (DBUSMENU_MENUITEM_PROP_TYPE, type);
192
int count_property (const char * propName, const char * propValue) const
194
return find_property (propName, propValue).size();
197
int count_type (const char * type) const
199
return count_property (DBUSMENU_MENUITEM_PROP_TYPE, type);
202
int count_property_int (const char * propName, int propValue) const
204
return find_property_int (propName, propValue).size();
207
int count_property_bool (const char * propName, bool propValue) const
209
return find_property_bool (propName, propValue).size();
214
DbusmenuClient * client;
218
* Fixture class for using Google Test on an indicator-service's
219
* com.canonical.dbusmenu interface.
221
* The SetUp() function starts the service up, waits for it to
222
* be visible on the bus, then creates a DbusmenuClient and waits
223
* for its layout-changed signal. This way the test function
224
* is reached after the menu is available and populated.
226
* TearDown() cleans up the DBus scaffolding and stops the service.
228
class IndicatorServiceTest : public ::testing::Test
232
IndicatorServiceTest(const char * service_name_,
233
const char * menu_object_path_,
234
const char * executable_):
238
indicator_service_proxy(0),
240
executable(executable_),
241
service_name(service_name_),
242
menu_object_path(menu_object_path_)
244
// glib one-time init stuff
246
g_assert (g_thread_supported());
247
mainloop = g_main_loop_new (NULL, FALSE);
253
on_layout_updated_static (DbusmenuClient * client, IndicatorServiceTest * self)
255
g_debug ("LAYOUT UPDATED");
256
self->on_layout_updated (client);
259
on_layout_updated (DbusmenuClient * client)
261
ASSERT_EQ (client, menu_client);
262
ASSERT_NE (handler_id, 0);
263
ASSERT_TRUE (g_signal_handler_is_connected (client, handler_id));
265
// stop listening for this event
266
g_signal_handler_disconnect (client, handler_id);
275
on_timeout_static (gpointer self)
277
static_cast<IndicatorServiceTest*>(self)->on_timeout();
283
ASSERT_NE (handler_id, 0ul);
284
g_source_remove (handler_id);
294
ASSERT_EQ (NULL, test_service);
295
ASSERT_EQ (NULL, menu_helper);
296
ASSERT_EQ (NULL, indicator_service_proxy);
298
test_service = dbus_test_service_new (NULL);
300
// Start the executable and wait until it shows up on the bus.
301
// Unset the NO_WATCHERS env var to ensure that the service
302
// will shut down when there are no watchers... otherwise
303
// this task will never finish
304
g_unsetenv("INDICATOR_ALLOW_NO_WATCHERS");
305
DbusTestProcess * indicator_service_task = dbus_test_process_new (executable.c_str());
306
dbus_test_service_add_task (test_service, DBUS_TEST_TASK(indicator_service_task));
307
g_object_unref (G_OBJECT(indicator_service_task));
309
// create a menu task that waits for our service before it runs
310
DbusTestTask * wait_task = dbus_test_task_new ();
311
dbus_test_task_set_wait_for (wait_task, service_name.c_str());
312
dbus_test_service_add_task (test_service, wait_task);
313
g_object_unref (G_OBJECT(wait_task));
315
g_debug ("starting tasks");
316
dbus_test_service_start_tasks(test_service);
318
// at this point the indicator service is running, let's Watch it
319
// to ensure it stays alive for the duration of the test
320
GError * error = NULL;
321
GDBusConnection * bus_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
322
indicator_service_proxy = g_dbus_proxy_new_sync (bus_connection,
323
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
325
service_name.c_str(),
326
"/org/ayatana/indicator/service",
327
"org.ayatana.indicator.service",
330
GVariant * result = g_dbus_proxy_call_sync (indicator_service_proxy, "Watch",
331
NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
334
g_variant_get (result, "(uu)", &a, &b);
335
g_debug ("Sending 'Watch' to proxy %p yielded %u %u", indicator_service_proxy, a, b);
336
g_variant_unref (result);
337
EXPECT_EQ(NULL, error);
339
g_message ("%s Unable to Watch indicator-service : %s", G_STRFUNC, error->message);
340
g_clear_error (&error);
342
g_object_unref (G_OBJECT(bus_connection));
344
menu_client = dbusmenu_client_new (service_name.c_str(), menu_object_path.c_str());
345
menu_helper = new DbusmenuClientHelper (menu_client);
347
// wait for a "layout-updated" signal before we let SetUp finish
348
wait_for_layout_update();
354
ASSERT_EQ (handler_id, 0);
356
// tear down the mainloop
357
ASSERT_TRUE (mainloop != NULL);
358
g_main_loop_unref (mainloop);
361
// tear down the menu client
362
if (menu_helper != NULL) {
366
if (menu_client != NULL) {
367
g_object_unref(G_OBJECT(menu_client));
371
// tear down the indicator proxy
372
EXPECT_TRUE (G_IS_DBUS_PROXY(indicator_service_proxy));
373
g_object_unref (G_OBJECT(indicator_service_proxy));
374
indicator_service_proxy = NULL;
377
void wait_for_layout_update()
379
ASSERT_EQ (handler_id, 0ul);
380
handler_id = g_signal_connect (menu_client,
381
DBUSMENU_CLIENT_SIGNAL_LAYOUT_UPDATED,
382
G_CALLBACK(on_layout_updated_static),
384
g_debug ("waiting for layout update...");
385
g_main_loop_run (mainloop);
388
void wait_seconds (int seconds)
390
ASSERT_EQ (handler_id, 0ul);
391
handler_id = g_timeout_add_seconds (seconds, on_timeout_static, this);
392
g_debug ("waiting %d seconds...", seconds);
393
g_main_loop_run (mainloop);
398
DbusmenuClient * menu_client;
399
DbusmenuClientHelper * menu_helper;
405
g_debug("done waiting");
406
g_main_loop_quit (mainloop);
409
GMainLoop * mainloop;
410
DbusTestService * test_service;
411
GDBusProxy * indicator_service_proxy;
413
const std::string executable;
414
const std::string service_name;
415
const std::string menu_object_path;
418
#endif // #ifndef INDICATOR_SERVICE_TEST_H