~phurley/indicator-session/lp-957563

« back to all changes in this revision

Viewing changes to tests/gtest-dbus-helper.h

  • Committer: Tarmac
  • Author(s): Charles Kerr
  • Date: 2012-11-16 20:00:08 UTC
  • mfrom: (369.2.8 lp-1074065)
  • Revision ID: tarmac-20121116200008-9rust1xv7z7vey3l
Reimplement test-service s.t. we actually test activating the service via dbus. Drop unneeded Xorg baggage. Fixes: https://bugs.launchpad.net/bugs/1074065.

Approved by PS Jenkins bot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
Copyright 2012 Canonical Ltd.
3
 
 
4
 
Authors:
5
 
    Charles Kerr <charles.kerr@canonical.com>
6
 
 
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.
10
 
 
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.
15
 
 
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/>.
18
 
*/
19
 
 
20
 
#ifndef INDICATOR_SERVICE_TEST_H
21
 
#define INDICATOR_SERVICE_TEST_H
22
 
 
23
 
#include <algorithm>
24
 
#include <functional>
25
 
#include <string>
26
 
#include <vector>
27
 
 
28
 
#include <gio/gio.h>
29
 
#include <gtest/gtest.h>
30
 
#include <libdbustest/dbus-test.h>
31
 
#include <libdbusmenu-glib/client.h>
32
 
 
33
 
/***
34
 
****
35
 
***/
36
 
 
37
 
/**
38
 
 * Convenience class for looking at a DbusmenuClient's items for testing
39
 
 *
40
 
 * Examples:
41
 
 *
42
 
 * // confirm that there are N menuitems of type T
43
 
 * TEST_EQ (helper.count_type(T), N);
44
 
 *
45
 
 * // confirm that there are N visible menuitems
46
 
 * TEST_EQ (helper.count_property_bool(DBUSMENU_MENUITEM_PROP_VISIBLE,true), N);
47
 
 *
48
 
 * // get a sorted list of all the menuitems of type T
49
 
 * std::vector<DbusmenuMenuitem*> items = helper.find_type(T);
50
 
 */
51
 
class DbusmenuClientHelper
52
 
{
53
 
  public:
54
 
 
55
 
    DbusmenuClientHelper (DbusmenuClient * client_): client(client_) {
56
 
      g_object_ref (G_OBJECT(client));
57
 
    }
58
 
    ~DbusmenuClientHelper() {
59
 
      g_object_unref(G_OBJECT(client));
60
 
      client = NULL;
61
 
    }
62
 
 
63
 
  private:
64
 
 
65
 
    static void foreach_accumulate_func (DbusmenuMenuitem * mi, gpointer gset) {
66
 
      static_cast<std::vector<DbusmenuMenuitem*>*>(gset)->push_back (mi);
67
 
    }
68
 
 
69
 
  public:
70
 
 
71
 
    std::vector<DbusmenuMenuitem*>
72
 
    get_all_menuitems () const
73
 
    {
74
 
      std::vector<DbusmenuMenuitem*> items;
75
 
 
76
 
      DbusmenuMenuitem * root = dbusmenu_client_get_root (client);
77
 
      if (root != NULL)
78
 
        dbusmenu_menuitem_foreach (root, foreach_accumulate_func, &items);
79
 
 
80
 
      return items;
81
 
    }
82
 
 
83
 
  private:
84
 
 
85
 
    template<typename value_type> class PropertyPredicate:
86
 
        public std::unary_function<DbusmenuMenuitem*,bool> {
87
 
      protected:
88
 
        const std::string _key;
89
 
        const value_type _value;
90
 
        virtual value_type get_value(DbusmenuMenuitem * mi) const = 0;
91
 
      public:
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));
98
 
        }
99
 
    };
100
 
 
101
 
    class StringPropertyPredicate: public PropertyPredicate<std::string> {
102
 
      protected:
103
 
        virtual std::string get_value (DbusmenuMenuitem * mi) const {
104
 
          return dbusmenu_menuitem_property_get(mi, _key.c_str());
105
 
        }
106
 
      public:
107
 
        StringPropertyPredicate (const char * propName, const char * propValue):
108
 
          PropertyPredicate (propName, propValue) {}
109
 
    };
110
 
 
111
 
    class IntPropertyPredicate: public PropertyPredicate<int> {
112
 
      protected:
113
 
        virtual int get_value (DbusmenuMenuitem * mi) const {
114
 
          return dbusmenu_menuitem_property_get_int(mi, _key.c_str());
115
 
        }
116
 
      public:
117
 
        IntPropertyPredicate (const char * propName, int propValue):
118
 
          PropertyPredicate (propName, propValue) {}
119
 
    };
120
 
 
121
 
    class BoolPropertyPredicate: public PropertyPredicate<bool> {
122
 
      protected:
123
 
        virtual bool get_value (DbusmenuMenuitem * mi) const {
124
 
          return dbusmenu_menuitem_property_get_bool(mi, _key.c_str());
125
 
        }
126
 
      public:
127
 
        BoolPropertyPredicate (const char * propName, bool propValue):
128
 
          PropertyPredicate (propName, propValue) {}
129
 
    };
130
 
 
131
 
  public:
132
 
 
133
 
    typedef std::vector<DbusmenuMenuitem*> menuitems_t;
134
 
 
135
 
    void
136
 
    match_property (menuitems_t& items, const char * key, const char * value) const
137
 
    {
138
 
      const StringPropertyPredicate pred (key, value);
139
 
      items.erase (std::remove_if (items.begin(), items.end(), std::not1(pred)), items.end());
140
 
    }
141
 
 
142
 
    void
143
 
    match_property_int (menuitems_t& items, const char * key, int value) const
144
 
    {
145
 
      const IntPropertyPredicate pred (key, value);
146
 
      items.erase (std::remove_if (items.begin(), items.end(), std::not1(pred)), items.end());
147
 
    }
148
 
 
149
 
    void
150
 
    match_property_bool (menuitems_t& items, const char * key, bool value) const
151
 
    {
152
 
      const BoolPropertyPredicate pred (key, value);
153
 
      items.erase (std::remove_if (items.begin(), items.end(), std::not1(pred)), items.end());
154
 
    }
155
 
      
156
 
    menuitems_t find_property (const char * prop_name, const char * prop_value) const
157
 
    {
158
 
      menuitems_t items;
159
 
      g_return_val_if_fail (prop_name!=NULL, items);
160
 
      g_return_val_if_fail (prop_value!=NULL, items);
161
 
 
162
 
      items = get_all_menuitems ();
163
 
      match_property (items, prop_name, prop_value);
164
 
      return items;
165
 
    }
166
 
 
167
 
    menuitems_t find_property_int (const char * prop_name, int prop_value) const
168
 
    {
169
 
      std::vector<DbusmenuMenuitem*> items;
170
 
      g_return_val_if_fail (prop_name!=NULL, items);
171
 
 
172
 
      items = get_all_menuitems ();
173
 
      match_property_int (items, prop_name, prop_value);
174
 
      return items;
175
 
    }
176
 
 
177
 
    menuitems_t find_property_bool (const char * prop_name, bool prop_value) const
178
 
    {
179
 
      std::vector<DbusmenuMenuitem*> items;
180
 
      g_return_val_if_fail (prop_name!=NULL, items);
181
 
 
182
 
      items = get_all_menuitems ();
183
 
      match_property_bool (items, prop_name, prop_value);
184
 
      return items;
185
 
    }
186
 
 
187
 
    menuitems_t find_type (const char * type) const
188
 
    {
189
 
      return find_property (DBUSMENU_MENUITEM_PROP_TYPE, type);
190
 
    }
191
 
 
192
 
    int count_property (const char * propName, const char * propValue) const
193
 
    {
194
 
      return find_property (propName, propValue).size();
195
 
    }
196
 
 
197
 
    int count_type (const char * type) const
198
 
    {
199
 
      return count_property (DBUSMENU_MENUITEM_PROP_TYPE, type);
200
 
    }
201
 
 
202
 
    int count_property_int (const char * propName, int propValue) const
203
 
    {
204
 
      return find_property_int (propName, propValue).size();
205
 
    }
206
 
 
207
 
    int count_property_bool (const char * propName, bool propValue) const
208
 
    {
209
 
      return find_property_bool (propName, propValue).size();
210
 
    }
211
 
 
212
 
  private:
213
 
 
214
 
    DbusmenuClient * client;
215
 
};
216
 
 
217
 
/**
218
 
 * Fixture class for using Google Test on an indicator-service's
219
 
 * com.canonical.dbusmenu interface.
220
 
 *
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.
225
 
 *
226
 
 * TearDown() cleans up the DBus scaffolding and stops the service.
227
 
 */
228
 
class IndicatorServiceTest : public ::testing::Test
229
 
{
230
 
  public:
231
 
 
232
 
    IndicatorServiceTest(const char * service_name_,
233
 
                         const char * menu_object_path_,
234
 
                         const char * executable_):
235
 
      menu_client(0),
236
 
      menu_helper(0),
237
 
      test_service(0),
238
 
      indicator_service_proxy(0),
239
 
      handler_id(0),
240
 
      executable(executable_),
241
 
      service_name(service_name_),
242
 
      menu_object_path(menu_object_path_)
243
 
    {
244
 
      // glib one-time init stuff
245
 
      g_type_init();
246
 
      g_assert (g_thread_supported());
247
 
      mainloop = g_main_loop_new (NULL, FALSE);
248
 
    }
249
 
 
250
 
  private:
251
 
 
252
 
    static void
253
 
    on_layout_updated_static (DbusmenuClient * client, IndicatorServiceTest * self)
254
 
    {
255
 
      g_debug ("LAYOUT UPDATED");
256
 
      self->on_layout_updated (client);
257
 
    }
258
 
    void
259
 
    on_layout_updated (DbusmenuClient * client)
260
 
    {
261
 
      ASSERT_EQ (client, menu_client);
262
 
      ASSERT_NE (handler_id, 0);
263
 
      ASSERT_TRUE (g_signal_handler_is_connected (client, handler_id));
264
 
 
265
 
      // stop listening for this event
266
 
      g_signal_handler_disconnect (client, handler_id);
267
 
      handler_id = 0;
268
 
 
269
 
      ready();
270
 
    }
271
 
 
272
 
  private:
273
 
 
274
 
    static gboolean
275
 
    on_timeout_static (gpointer self)
276
 
    {
277
 
      static_cast<IndicatorServiceTest*>(self)->on_timeout();
278
 
      return false;
279
 
    }
280
 
    void
281
 
    on_timeout()
282
 
    {
283
 
      ASSERT_NE (handler_id, 0ul);
284
 
      g_source_remove (handler_id);
285
 
      handler_id = 0;
286
 
      ready();
287
 
    }
288
 
 
289
 
  protected:
290
 
 
291
 
    virtual void
292
 
    SetUp()
293
 
    {
294
 
      ASSERT_EQ (NULL, test_service);
295
 
      ASSERT_EQ (NULL, menu_helper);
296
 
      ASSERT_EQ (NULL, indicator_service_proxy);
297
 
 
298
 
      test_service = dbus_test_service_new (NULL);
299
 
 
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));
308
 
 
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));
314
 
 
315
 
      g_debug ("starting tasks");
316
 
      dbus_test_service_start_tasks(test_service);
317
 
 
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,
324
 
                                                       NULL,
325
 
                                                       service_name.c_str(),
326
 
                                                       "/org/ayatana/indicator/service",
327
 
                                                       "org.ayatana.indicator.service",
328
 
                                                       NULL,
329
 
                                                       &error);
330
 
      GVariant * result = g_dbus_proxy_call_sync (indicator_service_proxy, "Watch",
331
 
                                                  NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
332
 
                                                  -1, NULL, &error);
333
 
      guint a, b;
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);
338
 
      if (error != NULL) {
339
 
        g_message ("%s Unable to Watch indicator-service : %s", G_STRFUNC, error->message);
340
 
        g_clear_error (&error);
341
 
      }
342
 
      g_object_unref (G_OBJECT(bus_connection));
343
 
 
344
 
      menu_client = dbusmenu_client_new (service_name.c_str(), menu_object_path.c_str());
345
 
      menu_helper = new DbusmenuClientHelper (menu_client);
346
 
 
347
 
      // wait for a "layout-updated" signal before we let SetUp finish
348
 
      wait_for_layout_update();
349
 
    }
350
 
 
351
 
    virtual void
352
 
    TearDown()
353
 
    {
354
 
      ASSERT_EQ (handler_id, 0);
355
 
 
356
 
      // tear down the mainloop
357
 
      ASSERT_TRUE (mainloop != NULL);
358
 
      g_main_loop_unref (mainloop);
359
 
      mainloop = NULL;
360
 
 
361
 
      // tear down the menu client
362
 
      if (menu_helper != NULL) {
363
 
        delete menu_helper;
364
 
        menu_helper = NULL;
365
 
      }
366
 
      if (menu_client != NULL) {
367
 
        g_object_unref(G_OBJECT(menu_client));
368
 
        menu_client = NULL;
369
 
      }
370
 
 
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;
375
 
    }
376
 
 
377
 
    void wait_for_layout_update()
378
 
    {
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),
383
 
                                     this);
384
 
      g_debug ("waiting for layout update...");
385
 
      g_main_loop_run (mainloop);
386
 
    }
387
 
 
388
 
    void wait_seconds (int seconds)
389
 
    {
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);
394
 
    }
395
 
 
396
 
  protected:
397
 
 
398
 
    DbusmenuClient * menu_client;
399
 
    DbusmenuClientHelper * menu_helper;
400
 
 
401
 
  private:
402
 
 
403
 
    void ready()
404
 
    {
405
 
      g_debug("done waiting");
406
 
      g_main_loop_quit (mainloop);
407
 
    }
408
 
 
409
 
    GMainLoop * mainloop;
410
 
    DbusTestService * test_service;
411
 
    GDBusProxy * indicator_service_proxy;
412
 
    gulong handler_id;
413
 
    const std::string executable;
414
 
    const std::string service_name;
415
 
    const std::string menu_object_path;
416
 
};
417
 
 
418
 
#endif // #ifndef INDICATOR_SERVICE_TEST_H