1 #include "config.h"
2 
3 #include <glib-object.h>
4 #include <dbus/dbus.h>
5 #include <dbus/dbus-glib.h>
6 #include <dbus/dbus-glib-lowlevel.h>
7 
8 #include <telepathy-glib/dbus.h>
9 #include <telepathy-glib/dbus-properties-mixin.h>
10 #include <telepathy-glib/debug.h>
11 #include <telepathy-glib/proxy.h>
12 #include <telepathy-glib/svc-generic.h>
13 #include <telepathy-glib/util.h>
14 
15 #include "_gen/svc.h"
16 #include "tests/lib/util.h"
17 
18 #define WITH_PROPERTIES_IFACE "com.example.WithProperties"
19 
20 typedef struct _TestProperties {
21     GObject parent;
22 } TestProperties;
23 typedef struct _TestPropertiesClass {
24     GObjectClass parent;
25     TpDBusPropertiesMixinClass props;
26 } TestPropertiesClass;
27 
28 GType test_properties_get_type (void);
29 
30 #define TEST_TYPE_PROPERTIES \
31   (test_properties_get_type ())
32 #define TEST_PROPERTIES(obj) \
33   (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_PROPERTIES, \
34                                TestProperties))
35 #define TEST_PROPERTIES_CLASS(klass) \
36   (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_PROPERTIES, \
37                             TestPropertiesClass))
38 #define TEST_IS_PROPERTIES(obj) \
39   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_PROPERTIES))
40 #define TEST_IS_PROPERTIES_CLASS(klass) \
41   (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_PROPERTIES))
42 #define TEST_PROPERTIES_GET_CLASS(obj) \
43   (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_PROPERTIES, \
44                               TestPropertiesClass))
45 
46 G_DEFINE_TYPE_WITH_CODE (TestProperties,
47     test_properties,
48     G_TYPE_OBJECT,
49     G_IMPLEMENT_INTERFACE (TEST_TYPE_SVC_WITH_PROPERTIES, NULL);
50     G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
51       tp_dbus_properties_mixin_iface_init));
52 
53 static void
test_properties_init(TestProperties * self)54 test_properties_init (TestProperties *self)
55 {
56 }
57 
58 static void
prop_getter(GObject * object,GQuark interface,GQuark name,GValue * value,gpointer user_data)59 prop_getter (GObject *object,
60              GQuark interface,
61              GQuark name,
62              GValue *value,
63              gpointer user_data)
64 {
65   if (tp_strdiff (user_data, "read"))
66     g_assert_cmpstr (user_data, ==, "full-access");
67 
68   g_value_set_uint (value, 42);
69 }
70 
71 static gboolean
prop_setter(GObject * object,GQuark interface,GQuark name,const GValue * value,gpointer user_data,GError ** error)72 prop_setter (GObject *object,
73              GQuark interface,
74              GQuark name,
75              const GValue *value,
76              gpointer user_data,
77              GError **error)
78 {
79   g_assert (G_VALUE_HOLDS_UINT (value));
80 
81   if (tp_strdiff (user_data, "FULL ACCESS"))
82     g_assert_cmpstr (user_data, ==, "BLACK HOLE");
83 
84   g_assert_cmpuint (g_value_get_uint (value), ==, 57);
85   return TRUE;
86 }
87 
88 static void
test_properties_class_init(TestPropertiesClass * cls)89 test_properties_class_init (TestPropertiesClass *cls)
90 {
91   static TpDBusPropertiesMixinPropImpl with_properties_props[] = {
92         { "ReadOnly", "read", "READ" },
93         { "ReadWrite", "full-access", "FULL ACCESS" },
94         { "WriteOnly", "black-hole", "BLACK HOLE" },
95         { NULL }
96   };
97   static TpDBusPropertiesMixinIfaceImpl interfaces[] = {
98       { WITH_PROPERTIES_IFACE, prop_getter, prop_setter,
99         with_properties_props },
100       { NULL }
101   };
102 
103   cls->props.interfaces = interfaces;
104 
105   tp_dbus_properties_mixin_class_init (G_OBJECT_CLASS (cls),
106       G_STRUCT_OFFSET (TestPropertiesClass, props));
107 }
108 
109 static void
test_get(TpProxy * proxy)110 test_get (TpProxy *proxy)
111 {
112   GValue *value;
113 
114   g_assert (tp_cli_dbus_properties_run_get (proxy, -1,
115         WITH_PROPERTIES_IFACE, "ReadOnly", &value, NULL, NULL));
116   g_assert (G_VALUE_HOLDS_UINT (value));
117   g_assert_cmpuint (g_value_get_uint (value), ==, 42);
118 
119   g_boxed_free (G_TYPE_VALUE, value);
120 }
121 
122 static void
test_set(TpProxy * proxy)123 test_set (TpProxy *proxy)
124 {
125   GValue value = { 0, };
126 
127   g_value_init (&value, G_TYPE_UINT);
128   g_value_set_uint (&value, 57);
129 
130   g_assert (tp_cli_dbus_properties_run_set (proxy, -1,
131         WITH_PROPERTIES_IFACE, "ReadWrite", &value, NULL, NULL));
132   g_assert (tp_cli_dbus_properties_run_set (proxy, -1,
133         WITH_PROPERTIES_IFACE, "WriteOnly", &value, NULL, NULL));
134 
135   g_value_unset (&value);
136 }
137 
138 static void
test_get_all(TpProxy * proxy)139 test_get_all (TpProxy *proxy)
140 {
141   GHashTable *hash;
142   GValue *value;
143 
144   g_assert (tp_cli_dbus_properties_run_get_all (proxy, -1,
145         WITH_PROPERTIES_IFACE, &hash, NULL, NULL));
146   g_assert (hash != NULL);
147   tp_asv_dump (hash);
148   g_assert_cmpuint (g_hash_table_size (hash), ==, 2);
149 
150   value = g_hash_table_lookup (hash, "WriteOnly");
151   g_assert (value == NULL);
152 
153   value = g_hash_table_lookup (hash, "ReadOnly");
154   g_assert (value != NULL);
155   g_assert (G_VALUE_HOLDS_UINT (value));
156   g_assert_cmpuint (g_value_get_uint (value), ==, 42);
157 
158   value = g_hash_table_lookup (hash, "ReadWrite");
159   g_assert (value != NULL);
160   g_assert (G_VALUE_HOLDS_UINT (value));
161   g_assert_cmpuint (g_value_get_uint (value), ==, 42);
162 
163   g_hash_table_unref (hash);
164 }
165 
166 static void
properties_changed_cb(TpProxy * proxy,const gchar * interface_name,GHashTable * changed_properties,const gchar ** invalidated_properties,gpointer user_data,GObject * weak_object)167 properties_changed_cb (
168     TpProxy *proxy,
169     const gchar *interface_name,
170     GHashTable *changed_properties,
171     const gchar **invalidated_properties,
172     gpointer user_data,
173     GObject *weak_object)
174 {
175   GMainLoop *loop = user_data;
176   guint value;
177   gboolean valid;
178 
179   g_assert_cmpuint (g_hash_table_size (changed_properties), ==, 1);
180   value = tp_asv_get_uint32 (changed_properties, "ReadOnly", &valid);
181   g_assert (valid);
182   g_assert_cmpuint (value, ==, 42);
183 
184   g_assert_cmpuint (g_strv_length ((gchar **) invalidated_properties), ==, 1);
185   g_assert_cmpstr (invalidated_properties[0], ==, "ReadWrite");
186 
187   g_main_loop_quit (loop);
188 }
189 
190 typedef struct {
191     TestProperties *obj;
192     TpProxy *proxy;
193 } Context;
194 
195 static void
test_emit_changed(Context * ctx)196 test_emit_changed (Context *ctx)
197 {
198   GMainLoop *loop = g_main_loop_new (NULL, FALSE);
199   TpProxySignalConnection *signal_conn;
200   const gchar *properties[] = { "ReadOnly", "ReadWrite", NULL };
201   GError *error = NULL;
202 
203   signal_conn = tp_cli_dbus_properties_connect_to_properties_changed (
204       ctx->proxy, properties_changed_cb, loop, NULL, NULL, &error);
205   g_assert_no_error (error);
206   g_assert (signal_conn != NULL);
207 
208   tp_dbus_properties_mixin_emit_properties_changed (G_OBJECT (ctx->obj),
209       WITH_PROPERTIES_IFACE, properties);
210   g_main_loop_run (loop);
211 
212   tp_dbus_properties_mixin_emit_properties_changed_varargs (G_OBJECT (ctx->obj),
213       WITH_PROPERTIES_IFACE, "ReadOnly", "ReadWrite", NULL);
214   g_main_loop_run (loop);
215 
216   tp_proxy_signal_connection_disconnect (signal_conn);
217 }
218 
219 int
main(int argc,char ** argv)220 main (int argc, char **argv)
221 {
222   Context ctx;
223   TpDBusDaemon *dbus_daemon;
224 
225   tp_tests_init (&argc, &argv);
226 
227   dbus_daemon = tp_tests_dbus_daemon_dup_or_die ();
228   ctx.obj = tp_tests_object_new_static_class (TEST_TYPE_PROPERTIES, NULL);
229   tp_dbus_daemon_register_object (dbus_daemon, "/", ctx.obj);
230 
231   /* Open a D-Bus connection to myself */
232   ctx.proxy = TP_PROXY (tp_tests_object_new_static_class (TP_TYPE_PROXY,
233       "dbus-daemon", dbus_daemon,
234       "bus-name", tp_dbus_daemon_get_unique_name (dbus_daemon),
235       "object-path", "/",
236       NULL));
237 
238   g_assert (tp_proxy_has_interface (ctx.proxy, "org.freedesktop.DBus.Properties"));
239 
240   g_test_add_data_func ("/properties/get", ctx.proxy, (GTestDataFunc) test_get);
241   g_test_add_data_func ("/properties/set", ctx.proxy, (GTestDataFunc) test_set);
242   g_test_add_data_func ("/properties/get-all", ctx.proxy, (GTestDataFunc) test_get_all);
243 
244   g_test_add_data_func ("/properties/changed", &ctx, (GTestDataFunc) test_emit_changed);
245 
246   tp_tests_run_with_bus ();
247 
248   g_object_unref (ctx.obj);
249   g_object_unref (ctx.proxy);
250   g_object_unref (dbus_daemon);
251 
252   return 0;
253 }
254