Add a test for g_dbus_proxy_get_cached_property_names
[platform/upstream/glib.git] / gio / tests / gdbus-proxy.c
1 /* GLib testing framework examples and tests
2  *
3  * Copyright (C) 2008-2010 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include <gio/gio.h>
24 #include <unistd.h>
25 #include <string.h>
26
27 #include "gdbus-tests.h"
28
29 /* all tests rely on a shared mainloop */
30 static GMainLoop *loop = NULL;
31
32 /* ---------------------------------------------------------------------------------------------------- */
33 /* Test that the method aspects of GDBusProxy works */
34 /* ---------------------------------------------------------------------------------------------------- */
35
36 static void
37 test_methods (GDBusProxy *proxy)
38 {
39   GVariant *result;
40   GError *error;
41   const gchar *str;
42   gchar *dbus_error_name;
43
44   /* check that we can invoke a method */
45   error = NULL;
46   result = g_dbus_proxy_call_sync (proxy,
47                                    "HelloWorld",
48                                    g_variant_new ("(s)", "Hey"),
49                                    G_DBUS_CALL_FLAGS_NONE,
50                                    -1,
51                                    NULL,
52                                    &error);
53   g_assert_no_error (error);
54   g_assert (result != NULL);
55   g_assert_cmpstr (g_variant_get_type_string (result), ==, "(s)");
56   g_variant_get (result, "(&s)", &str);
57   g_assert_cmpstr (str, ==, "You greeted me with 'Hey'. Thanks!");
58   g_variant_unref (result);
59
60   /* Check that we can completely recover the returned error */
61   result = g_dbus_proxy_call_sync (proxy,
62                                    "HelloWorld",
63                                    g_variant_new ("(s)", "Yo"),
64                                    G_DBUS_CALL_FLAGS_NONE,
65                                    -1,
66                                    NULL,
67                                    &error);
68   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
69   g_assert (g_dbus_error_is_remote_error (error));
70   g_assert (g_dbus_error_is_remote_error (error));
71   g_assert (result == NULL);
72   dbus_error_name = g_dbus_error_get_remote_error (error);
73   g_assert_cmpstr (dbus_error_name, ==, "com.example.TestException");
74   g_free (dbus_error_name);
75   g_assert (g_dbus_error_strip_remote_error (error));
76   g_assert_cmpstr (error->message, ==, "Yo is not a proper greeting");
77   g_clear_error (&error);
78
79   /* Check that we get a timeout if the method handling is taking longer than timeout */
80   error = NULL;
81   result = g_dbus_proxy_call_sync (proxy,
82                                    "Sleep",
83                                    g_variant_new ("(i)", 500 /* msec */),
84                                    G_DBUS_CALL_FLAGS_NONE,
85                                    100 /* msec */,
86                                    NULL,
87                                    &error);
88   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
89   g_assert (!g_dbus_error_is_remote_error (error));
90   g_assert (result == NULL);
91   g_clear_error (&error);
92
93   /* Check that proxy-default timeouts work. */
94   g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1);
95
96   /* the default timeout is 25000 msec so this should work */
97   result = g_dbus_proxy_call_sync (proxy,
98                                    "Sleep",
99                                    g_variant_new ("(i)", 500 /* msec */),
100                                    G_DBUS_CALL_FLAGS_NONE,
101                                    -1, /* use proxy default (e.g. -1 -> e.g. 25000 msec) */
102                                    NULL,
103                                    &error);
104   g_assert_no_error (error);
105   g_assert (result != NULL);
106   g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
107   g_variant_unref (result);
108
109   /* now set the proxy-default timeout to 250 msec and try the 500 msec call - this should FAIL */
110   g_dbus_proxy_set_default_timeout (proxy, 250);
111   g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, 250);
112   result = g_dbus_proxy_call_sync (proxy,
113                                    "Sleep",
114                                    g_variant_new ("(i)", 500 /* msec */),
115                                    G_DBUS_CALL_FLAGS_NONE,
116                                    -1, /* use proxy default (e.g. 250 msec) */
117                                    NULL,
118                                    &error);
119   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
120   g_assert (!g_dbus_error_is_remote_error (error));
121   g_assert (result == NULL);
122   g_clear_error (&error);
123
124   /* clean up after ourselves */
125   g_dbus_proxy_set_default_timeout (proxy, -1);
126 }
127
128 static gboolean
129 strv_equal (const gchar **strv, ...)
130 {
131   gint count;
132   va_list list;
133   const gchar *str;
134   gboolean res;
135
136   res = TRUE;
137   count = 0;
138   va_start (list, strv);
139   while (1)
140     {
141       str = va_arg (list, const gchar *);
142       if (str == NULL)
143         break;
144       if (g_strcmp0 (str, strv[count]) != 0)
145         {
146           res = FALSE;
147           break;
148         }
149       count++;
150     }
151   va_end (list);
152
153   if (res)
154     res = g_strv_length ((gchar**)strv) == count;
155
156   return res;
157 }
158
159 /* ---------------------------------------------------------------------------------------------------- */
160 /* Test that the property aspects of GDBusProxy works */
161 /* ---------------------------------------------------------------------------------------------------- */
162
163 static void
164 test_properties (GDBusProxy *proxy)
165 {
166   GError *error;
167   GVariant *variant;
168   GVariant *variant2;
169   GVariant *result;
170   gchar **names;
171
172   error = NULL;
173
174   /*
175    * Check that we can list all cached properties.
176    */
177   names = g_dbus_proxy_get_cached_property_names (proxy);
178
179   g_assert (strv_equal (names,
180                         "PropertyThatWillBeInvalidated",
181                         "ab",
182                         "ad",
183                         "ai",
184                         "an",
185                         "ao",
186                         "aq",
187                         "as",
188                         "at",
189                         "au",
190                         "ax",
191                         "ay",
192                         "b",
193                         "d",
194                         "foo",
195                         "i",
196                         "n",
197                         "o",
198                         "q",
199                         "s",
200                         "t",
201                         "u",
202                         "x",
203                         "y"));
204
205   g_strfreev (names);
206
207   /*
208    * Check that we can read cached properties.
209    *
210    * No need to test all properties - GVariant has already been tested
211    */
212   variant = g_dbus_proxy_get_cached_property (proxy, "y");
213   g_assert (variant != NULL);
214   g_assert_cmpint (g_variant_get_byte (variant), ==, 1);
215   g_variant_unref (variant);
216   variant = g_dbus_proxy_get_cached_property (proxy, "o");
217   g_assert (variant != NULL);
218   g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path");
219   g_variant_unref (variant);
220
221   /*
222    * Now ask the service to change a property and check that #GDBusProxy::g-property-changed
223    * is received. Also check that the cache is updated.
224    */
225   variant2 = g_variant_new_byte (42);
226   result = g_dbus_proxy_call_sync (proxy,
227                                    "FrobSetProperty",
228                                    g_variant_new ("(sv)",
229                                                   "y",
230                                                   variant2),
231                                    G_DBUS_CALL_FLAGS_NONE,
232                                    -1,
233                                    NULL,
234                                    &error);
235   g_assert_no_error (error);
236   g_assert (result != NULL);
237   g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
238   g_variant_unref (result);
239   _g_assert_signal_received (proxy, "g-properties-changed");
240   variant = g_dbus_proxy_get_cached_property (proxy, "y");
241   g_assert (variant != NULL);
242   g_assert_cmpint (g_variant_get_byte (variant), ==, 42);
243   g_variant_unref (variant);
244
245   g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (142));
246   variant = g_dbus_proxy_get_cached_property (proxy, "y");
247   g_assert (variant != NULL);
248   g_assert_cmpint (g_variant_get_byte (variant), ==, 142);
249   g_variant_unref (variant);
250
251   g_dbus_proxy_set_cached_property (proxy, "y", NULL);
252   variant = g_dbus_proxy_get_cached_property (proxy, "y");
253   g_assert (variant == NULL);
254
255   /* Check that the invalidation feature of the PropertiesChanged()
256    * signal works... First, check that we have a cached value of the
257    * property (from the initial GetAll() call)
258    */
259   variant = g_dbus_proxy_get_cached_property (proxy, "PropertyThatWillBeInvalidated");
260   g_assert (variant != NULL);
261   g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "InitialValue");
262   g_variant_unref (variant);
263   /* now ask to invalidate the property - this causes a
264    *
265    *   PropertiesChanaged("com.example.Frob",
266    *                      {},
267    *                      ["PropertyThatWillBeInvalidated")
268    *
269    * signal to be emitted. This is received before the method reply
270    * for FrobInvalidateProperty *but* since the proxy was created in
271    * the same thread as we're doing this synchronous call, we'll get
272    * the method reply before...
273    */
274   result = g_dbus_proxy_call_sync (proxy,
275                                    "FrobInvalidateProperty",
276                                    NULL,
277                                    G_DBUS_CALL_FLAGS_NONE,
278                                    -1,
279                                    NULL,
280                                    &error);
281   g_assert_no_error (error);
282   g_assert (result != NULL);
283   g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
284   g_variant_unref (result);
285   /* ... hence we wait for the g-properties-changed signal to be delivered */
286   _g_assert_signal_received (proxy, "g-properties-changed");
287   /* ... and now we finally, check that the cached value has been invalidated */
288   variant = g_dbus_proxy_get_cached_property (proxy, "PropertyThatWillBeInvalidated");
289   g_assert (variant == NULL);
290 }
291
292 /* ---------------------------------------------------------------------------------------------------- */
293 /* Test that the signal aspects of GDBusProxy works */
294 /* ---------------------------------------------------------------------------------------------------- */
295
296 static void
297 test_proxy_signals_on_signal (GDBusProxy  *proxy,
298                               const gchar *sender_name,
299                               const gchar *signal_name,
300                               GVariant    *parameters,
301                               gpointer     user_data)
302 {
303   GString *s = user_data;
304
305   g_assert_cmpstr (signal_name, ==, "TestSignal");
306   g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(sov)");
307
308   g_variant_print_string (parameters, s, TRUE);
309 }
310
311 typedef struct
312 {
313   GMainLoop *internal_loop;
314   GString *s;
315 } TestSignalData;
316
317 static void
318 test_proxy_signals_on_emit_signal_cb (GDBusProxy   *proxy,
319                                       GAsyncResult *res,
320                                       gpointer      user_data)
321 {
322   TestSignalData *data = user_data;
323   GError *error;
324   GVariant *result;
325
326   error = NULL;
327   result = g_dbus_proxy_call_finish (proxy,
328                                      res,
329                                      &error);
330   g_assert_no_error (error);
331   g_assert (result != NULL);
332   g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
333   g_variant_unref (result);
334
335   /* check that the signal was recieved before we got the method result */
336   g_assert (strlen (data->s->str) > 0);
337
338   /* break out of the loop */
339   g_main_loop_quit (data->internal_loop);
340 }
341
342 static void
343 test_signals (GDBusProxy *proxy)
344 {
345   GError *error;
346   GString *s;
347   gulong signal_handler_id;
348   TestSignalData data;
349   GVariant *result;
350
351   error = NULL;
352
353   /*
354    * Ask the service to emit a signal and check that we receive it.
355    *
356    * Note that blocking calls don't block in the mainloop so wait for the signal (which
357    * is dispatched before the method reply)
358    */
359   s = g_string_new (NULL);
360   signal_handler_id = g_signal_connect (proxy,
361                                         "g-signal",
362                                         G_CALLBACK (test_proxy_signals_on_signal),
363                                         s);
364
365   result = g_dbus_proxy_call_sync (proxy,
366                                    "EmitSignal",
367                                    g_variant_new ("(so)",
368                                                   "Accept the next proposition you hear",
369                                                   "/some/path"),
370                                    G_DBUS_CALL_FLAGS_NONE,
371                                    -1,
372                                    NULL,
373                                    &error);
374   g_assert_no_error (error);
375   g_assert (result != NULL);
376   g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
377   g_variant_unref (result);
378   /* check that we haven't received the signal just yet */
379   g_assert (strlen (s->str) == 0);
380   /* and now wait for the signal */
381   _g_assert_signal_received (proxy, "g-signal");
382   g_assert_cmpstr (s->str,
383                    ==,
384                    "('Accept the next proposition you hear .. in bed!', objectpath '/some/path/in/bed', <'a variant'>)");
385   g_signal_handler_disconnect (proxy, signal_handler_id);
386   g_string_free (s, TRUE);
387
388   /*
389    * Now do this async to check the signal is received before the method returns.
390    */
391   s = g_string_new (NULL);
392   data.internal_loop = g_main_loop_new (NULL, FALSE);
393   data.s = s;
394   signal_handler_id = g_signal_connect (proxy,
395                                         "g-signal",
396                                         G_CALLBACK (test_proxy_signals_on_signal),
397                                         s);
398   g_dbus_proxy_call (proxy,
399                      "EmitSignal",
400                      g_variant_new ("(so)",
401                                     "You will make a great programmer",
402                                     "/some/other/path"),
403                      G_DBUS_CALL_FLAGS_NONE,
404                      -1,
405                      NULL,
406                      (GAsyncReadyCallback) test_proxy_signals_on_emit_signal_cb,
407                      &data);
408   g_main_loop_run (data.internal_loop);
409   g_main_loop_unref (data.internal_loop);
410   g_assert_cmpstr (s->str,
411                    ==,
412                    "('You will make a great programmer .. in bed!', objectpath '/some/other/path/in/bed', <'a variant'>)");
413   g_signal_handler_disconnect (proxy, signal_handler_id);
414   g_string_free (s, TRUE);
415 }
416
417 static void
418 test_bogus_method_return (GDBusProxy *proxy)
419 {
420   GError *error = NULL;
421   GVariant *result;
422
423   result = g_dbus_proxy_call_sync (proxy,
424                                    "PairReturn",
425                                    NULL,
426                                    G_DBUS_CALL_FLAGS_NONE,
427                                    -1,
428                                    NULL,
429                                    &error);
430   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
431   g_assert (result == NULL);
432 }
433
434 /* ---------------------------------------------------------------------------------------------------- */
435
436 static const gchar *frob_dbus_interface_xml =
437   "<node>"
438   "  <interface name='com.example.Frob'>"
439   /* Deliberately different from gdbus-testserver.py's definition */
440   "    <method name='PairReturn'>"
441   "      <arg type='u' name='somenumber' direction='in'/>"
442   "      <arg type='s' name='somestring' direction='out'/>"
443   "    </method>"
444   "    <method name='HelloWorld'>"
445   "      <arg type='s' name='somestring' direction='in'/>"
446   "      <arg type='s' name='somestring' direction='out'/>"
447   "    </method>"
448   "    <method name='Sleep'>"
449   "      <arg type='i' name='timeout' direction='in'/>"
450   "    </method>"
451   "    <property name='y' type='y' access='readwrite'/>"
452   "  </interface>"
453   "</node>";
454 static GDBusInterfaceInfo *frob_dbus_interface_info;
455
456 static void
457 test_expected_interface (GDBusProxy *proxy)
458 {
459   /* This is obviously wrong but expected interface is not set so we don't fail... */
460   g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!"));
461   g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42));
462   g_dbus_proxy_set_cached_property (proxy, "does-not-exist", g_variant_new_string ("something"));
463   g_dbus_proxy_set_cached_property (proxy, "does-not-exist", NULL);
464
465   /* Now repeat the method tests, with an expected interface set */
466   g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info);
467   test_methods (proxy);
468
469   /* And now one more test where we deliberately set the expected
470    * interface definition incorrectly
471    */
472   test_bogus_method_return (proxy);
473
474   /* Also check that we complain if setting a cached property of the wrong type */
475   if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
476     {
477       g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!"));
478     }
479   g_test_trap_assert_stderr ("*Trying to set property y of type s but according to the expected interface the type is y*");
480   g_test_trap_assert_failed();
481
482   if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
483     {
484       g_dbus_proxy_set_cached_property (proxy, "does-not-exist", g_variant_new_string ("something"));
485     }
486   g_test_trap_assert_stderr ("*Trying to lookup property does-not-exist which isn't in expected interface com.example.Frob*");
487   g_test_trap_assert_failed();
488
489   /* this should work, however (since the type is correct) */
490   g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42));
491 }
492
493 static void
494 test_proxy (void)
495 {
496   GDBusProxy *proxy;
497   GDBusConnection *connection;
498   GError *error;
499
500   session_bus_up ();
501
502   /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return
503    * until one can connect to the bus but that's not how things work right now
504    */
505   usleep (500 * 1000);
506
507   error = NULL;
508   connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
509                                NULL,
510                                &error);
511   g_assert_no_error (error);
512   error = NULL;
513   proxy = g_dbus_proxy_new_sync (connection,
514                                  G_DBUS_PROXY_FLAGS_NONE,
515                                  NULL,                      /* GDBusInterfaceInfo */
516                                  "com.example.TestService", /* name */
517                                  "/com/example/TestObject", /* object path */
518                                  "com.example.Frob",        /* interface */
519                                  NULL, /* GCancellable */
520                                  &error);
521   g_assert_no_error (error);
522
523   /* this is safe; testserver will exit once the bus goes away */
524   g_assert (g_spawn_command_line_async (SRCDIR "/gdbus-testserver.py", NULL));
525
526   _g_assert_property_notify (proxy, "g-name-owner");
527
528   test_methods (proxy);
529   test_properties (proxy);
530   test_signals (proxy);
531   test_expected_interface (proxy);
532
533   g_object_unref (proxy);
534   g_object_unref (connection);
535 }
536
537 /* ---------------------------------------------------------------------------------------------------- */
538
539 int
540 main (int   argc,
541       char *argv[])
542 {
543   gint ret;
544   GDBusNodeInfo *introspection_data = NULL;
545
546   g_type_init ();
547   g_test_init (&argc, &argv, NULL);
548
549   introspection_data = g_dbus_node_info_new_for_xml (frob_dbus_interface_xml, NULL);
550   g_assert (introspection_data != NULL);
551   frob_dbus_interface_info = introspection_data->interfaces[0];
552
553   /* all the tests rely on a shared main loop */
554   loop = g_main_loop_new (NULL, FALSE);
555
556   /* all the tests use a session bus with a well-known address that we can bring up and down
557    * using session_bus_up() and session_bus_down().
558    */
559   g_unsetenv ("DISPLAY");
560   g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
561
562   g_test_add_func ("/gdbus/proxy", test_proxy);
563
564   ret = g_test_run();
565
566   g_dbus_node_info_unref (introspection_data);
567   return ret;
568 }