Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include "dbus.h"
10 :
11 : #include <stdio.h> // printf() & co
12 : #include <time.h> // time()
13 :
14 : #include <tests.h>
15 : #include <tests_plugin.h>
16 :
17 : typedef struct
18 : {
19 : char * lookupSignalName;
20 : char * receivedKeyName;
21 :
22 : DBusConnection * connection;
23 : int stop;
24 : } TestContext;
25 :
26 : #define TEST_TIMEOUT 1
27 : #define TEST_DISPATCH_TIMEOUT 200
28 :
29 : /** D-Bus bus type used by tests */
30 : DBusBusType testBusType;
31 :
32 : /** key namespace to use for tests */
33 : char * testKeyNamespace;
34 :
35 : /**
36 : * @internal
37 : * Process D-Bus messages and check for expected message.
38 : *
39 : * @param connection D-Bus connection
40 : * @param message received D-Bus message
41 : * @param data test context
42 : * @return message handler result
43 : */
44 0 : static DBusHandlerResult receiveMessageHandler (DBusConnection * connection ELEKTRA_UNUSED, DBusMessage * message, void * data)
45 : {
46 0 : TestContext * context = (TestContext *) data;
47 :
48 0 : char * interface = "org.libelektra";
49 0 : if (dbus_message_is_signal (message, interface, context->lookupSignalName))
50 : {
51 : char * keyName;
52 : DBusError error;
53 0 : dbus_error_init (&error);
54 :
55 : // read key name from message
56 0 : dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &keyName, DBUS_TYPE_INVALID);
57 0 : if (dbus_error_is_set (&error))
58 : {
59 : ELEKTRA_LOG_WARNING ("Failed to read message: %s", error.message);
60 : }
61 : else
62 : {
63 : // Message received, stop dispatching
64 0 : context->receivedKeyName = keyName;
65 0 : context->stop = 1;
66 : }
67 :
68 0 : dbus_error_free (&error);
69 : return DBUS_HANDLER_RESULT_HANDLED;
70 : }
71 : return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
72 : }
73 :
74 : /**
75 : * @internal
76 : * Dispatch messages and declares timeout if dispatching is not stopped within
77 : * TEST_TIMEOUT.
78 : *
79 : * @param context test context
80 : */
81 0 : static void runDispatch (TestContext * context)
82 : {
83 : time_t now;
84 0 : time_t start = time (NULL);
85 0 : context->stop = 0;
86 0 : while (!context->stop && dbus_connection_read_write_dispatch (context->connection, TEST_DISPATCH_TIMEOUT))
87 : {
88 0 : now = time (NULL);
89 : // Stop dispatching after one second
90 0 : if (now - start > TEST_TIMEOUT)
91 : {
92 0 : succeed_if (0, "timeout exceeded; test failed");
93 : break;
94 : }
95 : }
96 0 : }
97 :
98 : /**
99 : * @internal
100 : * Create new test context.
101 : *
102 : * @param connection D-Bus connection
103 : * @param signalName Expected signal name
104 : * @return Context
105 : */
106 0 : static TestContext * createTestContext (DBusConnection * connection, char * signalName)
107 : {
108 0 : TestContext * context = elektraMalloc (sizeof *context);
109 0 : exit_if_fail (context, "malloc failed");
110 :
111 0 : context->lookupSignalName = signalName;
112 0 : context->connection = connection;
113 0 : context->receivedKeyName = "";
114 0 : return context;
115 : }
116 :
117 : /**
118 : * @internal
119 : * Get and setup D-Bus connection.
120 : *
121 : * @param type D-Bus bus type
122 : * @return D-Bus connection or NULL on error
123 : */
124 0 : static DBusConnection * getDbusConnection (DBusBusType type)
125 : {
126 : DBusError error;
127 0 : dbus_error_init (&error);
128 :
129 0 : DBusConnection * connection = dbus_bus_get (type, &error);
130 0 : if (connection == NULL)
131 : {
132 0 : printf ("connect: Failed to open connection to %s message bus: %s\n", (type == DBUS_BUS_SYSTEM) ? "system" : "session",
133 : error.message);
134 0 : dbus_error_free (&error);
135 0 : dbus_shutdown ();
136 0 : return NULL;
137 : }
138 0 : dbus_error_free (&error);
139 :
140 0 : dbus_connection_set_exit_on_disconnect (connection, FALSE);
141 :
142 0 : return connection;
143 : }
144 :
145 0 : static int test_prerequisites (void)
146 : {
147 0 : printf ("testing prerequisites\n");
148 0 : printf ("detecting available bus types - please ignore single error messages prefixed with \"connect:\"\n");
149 :
150 0 : DBusConnection * systemBus = getDbusConnection (DBUS_BUS_SYSTEM);
151 0 : DBusConnection * sessionBus = getDbusConnection (DBUS_BUS_SESSION);
152 :
153 0 : int success = 0;
154 0 : if (systemBus != NULL || sessionBus != NULL)
155 : {
156 : // Set bus type for tests
157 : // NOTE brew dbus on MacOs supports session by out of the box while session
158 : // bus is not available without further configuration on Linux
159 0 : if (systemBus)
160 : {
161 0 : testBusType = DBUS_BUS_SYSTEM;
162 0 : testKeyNamespace = "system";
163 : }
164 0 : else if (sessionBus)
165 : {
166 0 : testBusType = DBUS_BUS_SESSION;
167 0 : testKeyNamespace = "user";
168 : }
169 :
170 : success = 1;
171 : }
172 :
173 0 : if (systemBus) dbus_connection_unref (systemBus);
174 0 : if (sessionBus) dbus_connection_unref (sessionBus);
175 :
176 0 : return success;
177 : }
178 :
179 0 : static void test_keyAdded (void)
180 : {
181 0 : printf ("test adding keys\n");
182 :
183 : // (namespace)/tests/foo
184 0 : Key * parentKey = keyNew (testKeyNamespace, KEY_END);
185 0 : keyAddName (parentKey, "tests/foo");
186 :
187 : // (namespace)/tests/foo/bar
188 0 : Key * toAdd = keyDup (parentKey);
189 0 : keyAddName (toAdd, "bar");
190 0 : keySetString (toAdd, "test");
191 :
192 0 : KeySet * ks = ksNew (0, KS_END);
193 :
194 0 : KeySet * conf = ksNew (0, KS_END);
195 0 : PLUGIN_OPEN ("dbus");
196 :
197 : // initial get to save current state
198 0 : plugin->kdbGet (plugin, ks, parentKey);
199 :
200 : // add key to keyset
201 0 : ksAppendKey (ks, toAdd);
202 :
203 0 : DBusConnection * connection = getDbusConnection (testBusType);
204 0 : TestContext * context = createTestContext (connection, "KeyAdded");
205 0 : elektraDbusSetupReceiveMessage (connection, receiveMessageHandler, (void *) context);
206 :
207 0 : plugin->kdbSet (plugin, ks, parentKey);
208 0 : runDispatch (context);
209 :
210 0 : succeed_if_same_string (keyName (toAdd), context->receivedKeyName);
211 :
212 0 : elektraFree (context);
213 0 : elektraDbusTeardownReceiveMessage (connection, receiveMessageHandler, (void *) context);
214 0 : dbus_connection_unref (connection);
215 0 : ksDel (ks);
216 0 : keyDel (parentKey);
217 0 : PLUGIN_CLOSE ();
218 0 : }
219 :
220 0 : static void test_keyChanged (void)
221 : {
222 0 : printf ("test changing keys\n");
223 :
224 : // All keys created by keyNew have the KEY_FLAG_SYNC set and will be
225 : // detected as changed by the dbus plugin
226 : // This flag is only cleared after kdbSet or when keys come from a backend.
227 :
228 : // (namespace)/tests/foo
229 0 : Key * parentKey = keyNew (testKeyNamespace, KEY_END);
230 0 : keyAddName (parentKey, "tests/foo");
231 :
232 : // (namespace)/tests/foo/bar
233 0 : Key * toChange = keyDup (parentKey);
234 0 : keyAddName (toChange, "bar");
235 0 : keySetString (toChange, "test");
236 :
237 0 : KeySet * ks = ksNew (2, toChange, KS_END);
238 :
239 0 : KeySet * conf = ksNew (0, KS_END);
240 0 : PLUGIN_OPEN ("dbus");
241 :
242 : // initial get to save current state
243 0 : plugin->kdbGet (plugin, ks, parentKey);
244 :
245 : // change key in keyset
246 0 : keySetString (toChange, "new value");
247 :
248 0 : DBusConnection * connection = getDbusConnection (testBusType);
249 0 : TestContext * context = createTestContext (connection, "KeyChanged");
250 0 : elektraDbusSetupReceiveMessage (connection, receiveMessageHandler, (void *) context);
251 :
252 0 : plugin->kdbSet (plugin, ks, parentKey);
253 0 : runDispatch (context);
254 :
255 0 : succeed_if_same_string (keyName (toChange), context->receivedKeyName);
256 :
257 0 : elektraFree (context);
258 0 : elektraDbusTeardownReceiveMessage (connection, receiveMessageHandler, (void *) context);
259 0 : dbus_connection_unref (connection);
260 0 : ksDel (ks);
261 0 : keyDel (parentKey);
262 0 : PLUGIN_CLOSE ();
263 0 : }
264 :
265 0 : static void test_keyDeleted (void)
266 : {
267 0 : printf ("test deleting keys\n");
268 :
269 : // (namespace)/tests/foo
270 0 : Key * parentKey = keyNew (testKeyNamespace, KEY_END);
271 0 : keyAddName (parentKey, "tests/foo");
272 :
273 : // (namespace)/tests/foo/bar
274 0 : Key * toDelete = keyDup (parentKey);
275 0 : keyAddName (toDelete, "bar");
276 0 : keySetString (toDelete, "test");
277 :
278 0 : KeySet * ks = ksNew (1, keyDup (toDelete), KS_END);
279 :
280 0 : KeySet * conf = ksNew (0, KS_END);
281 0 : PLUGIN_OPEN ("dbus");
282 :
283 : // initial get to save current state
284 0 : plugin->kdbGet (plugin, ks, parentKey);
285 :
286 : // remove key from keyset
287 0 : Key * deleted = ksLookup (ks, toDelete, KDB_O_POP);
288 0 : succeed_if (deleted != NULL, "key was not found");
289 :
290 0 : DBusConnection * connection = getDbusConnection (testBusType);
291 0 : TestContext * context = createTestContext (connection, "KeyDeleted");
292 0 : elektraDbusSetupReceiveMessage (connection, receiveMessageHandler, (void *) context);
293 :
294 0 : plugin->kdbSet (plugin, ks, parentKey);
295 0 : runDispatch (context);
296 :
297 0 : succeed_if_same_string (keyName (toDelete), context->receivedKeyName);
298 :
299 0 : elektraFree (context);
300 0 : elektraDbusTeardownReceiveMessage (connection, receiveMessageHandler, (void *) context);
301 0 : dbus_connection_unref (connection);
302 0 : keyDel (toDelete);
303 0 : ksDel (ks);
304 0 : keyDel (parentKey);
305 0 : PLUGIN_CLOSE ();
306 0 : }
307 :
308 0 : static void test_announceOnce (void)
309 : {
310 0 : printf ("test announce once\n");
311 :
312 : // (namespace)/tests/foo
313 0 : Key * parentKey = keyNew (testKeyNamespace, KEY_END);
314 0 : keyAddName (parentKey, "tests/foo");
315 :
316 : // (namespace)/tests/foo/bar/#0
317 0 : Key * toAdd1 = keyDup (parentKey);
318 0 : keyAddName (toAdd1, "bar/#0");
319 0 : keySetString (toAdd1, "test");
320 :
321 : // (namespace)/tests/foo/bar/#1
322 0 : Key * toAdd2 = keyDup (toAdd1);
323 0 : keySetBaseName (toAdd2, "#1");
324 :
325 : // (namespace)/tests/foo/bar
326 0 : Key * toChange = keyDup (parentKey);
327 0 : keyAddName (toChange, "bar");
328 0 : keySetString (toChange, "test");
329 :
330 0 : KeySet * ks = ksNew (1, toChange, KS_END);
331 :
332 0 : KeySet * conf = ksNew (1, keyNew ("/announce", KEY_VALUE, "once", KEY_END), KS_END);
333 0 : PLUGIN_OPEN ("dbus");
334 :
335 : // initial get to save current state
336 0 : plugin->kdbGet (plugin, ks, parentKey);
337 :
338 : // modify keyset
339 0 : ksAppendKey (ks, toAdd1);
340 0 : ksAppendKey (ks, toAdd2);
341 0 : keySetString (toChange, "new value");
342 :
343 0 : DBusConnection * connection = getDbusConnection (testBusType);
344 0 : TestContext * context = createTestContext (connection, "Commit");
345 0 : elektraDbusSetupReceiveMessage (connection, receiveMessageHandler, (void *) context);
346 :
347 0 : plugin->kdbSet (plugin, ks, parentKey);
348 0 : runDispatch (context);
349 :
350 0 : succeed_if_same_string (keyName (parentKey), context->receivedKeyName);
351 :
352 0 : elektraFree (context);
353 0 : elektraDbusTeardownReceiveMessage (connection, receiveMessageHandler, (void *) context);
354 0 : dbus_connection_unref (connection);
355 0 : ksDel (ks);
356 0 : keyDel (parentKey);
357 0 : PLUGIN_CLOSE ();
358 0 : }
359 :
360 0 : static void test_cascadedChangeNotification (void)
361 : {
362 0 : printf ("test change notification with cascaded parent key\n");
363 :
364 0 : Key * parentKey = keyNew ("/tests/foo", KEY_END);
365 :
366 : // (namespace)/tests/foo
367 0 : Key * completeParentKey = keyNew (testKeyNamespace, KEY_END);
368 0 : keyAddName (completeParentKey, "tests/foo");
369 :
370 : // (namespace)/tests/foo/bar
371 0 : Key * toAdd = keyDup (completeParentKey);
372 0 : keyAddName (toAdd, "bar");
373 0 : keySetString (toAdd, "test");
374 :
375 0 : KeySet * ks = ksNew (1, completeParentKey, KS_END);
376 :
377 0 : KeySet * conf = ksNew (0, KS_END);
378 0 : PLUGIN_OPEN ("dbus");
379 :
380 : // initial get to save current state
381 0 : plugin->kdbGet (plugin, ks, parentKey);
382 :
383 : // add key to keyset
384 0 : ksAppendKey (ks, toAdd);
385 :
386 0 : DBusConnection * connection = getDbusConnection (testBusType);
387 0 : TestContext * context = createTestContext (connection, "KeyAdded");
388 0 : elektraDbusSetupReceiveMessage (connection, receiveMessageHandler, (void *) context);
389 :
390 0 : plugin->kdbSet (plugin, ks, parentKey);
391 0 : runDispatch (context);
392 :
393 0 : succeed_if_same_string (keyName (toAdd), context->receivedKeyName);
394 :
395 0 : elektraFree (context);
396 0 : elektraDbusTeardownReceiveMessage (connection, receiveMessageHandler, (void *) context);
397 0 : dbus_connection_unref (connection);
398 0 : ksDel (ks);
399 0 : keyDel (parentKey);
400 0 : PLUGIN_CLOSE ();
401 0 : }
402 :
403 0 : static void test_cascadedAnnounceOnce (void)
404 : {
405 0 : printf ("test announce once with cascaded parent key\n");
406 :
407 0 : Key * parentKey = keyNew ("/tests/foo", KEY_END);
408 :
409 : // (namespace)/tests/foo
410 0 : Key * completeParentKey = keyNew (testKeyNamespace, KEY_END);
411 0 : keyAddName (completeParentKey, "tests/foo");
412 :
413 : // (namespace)/tests/foo/bar
414 0 : Key * toAdd = keyDup (completeParentKey);
415 0 : keyAddName (toAdd, "bar");
416 0 : keySetString (toAdd, "test");
417 :
418 0 : KeySet * ks = ksNew (1, completeParentKey, KS_END);
419 :
420 0 : KeySet * conf = ksNew (1, keyNew ("/announce", KEY_VALUE, "once", KEY_END), KS_END);
421 0 : PLUGIN_OPEN ("dbus");
422 :
423 : // initial get to save current state
424 0 : plugin->kdbGet (plugin, ks, parentKey);
425 :
426 : // add key to keyset
427 0 : ksAppendKey (ks, toAdd);
428 :
429 0 : DBusConnection * connection = getDbusConnection (testBusType);
430 0 : TestContext * context = createTestContext (connection, "Commit");
431 0 : elektraDbusSetupReceiveMessage (connection, receiveMessageHandler, (void *) context);
432 :
433 0 : plugin->kdbSet (plugin, ks, parentKey);
434 0 : runDispatch (context);
435 :
436 0 : succeed_if_same_string (keyName (completeParentKey), context->receivedKeyName);
437 :
438 0 : elektraFree (context);
439 0 : elektraDbusTeardownReceiveMessage (connection, receiveMessageHandler, (void *) context);
440 0 : dbus_connection_unref (connection);
441 0 : ksDel (ks);
442 0 : keyDel (parentKey);
443 0 : PLUGIN_CLOSE ();
444 0 : }
445 :
446 0 : int main (int argc, char ** argv)
447 : {
448 0 : printf ("DBUS TESTS\n");
449 0 : printf ("==========\n\n");
450 :
451 0 : init (argc, argv);
452 :
453 : // Test if dbus is available
454 0 : if (test_prerequisites ())
455 : {
456 : // Test added, changed & deleted
457 0 : test_keyAdded ();
458 0 : test_keyChanged ();
459 0 : test_keyDeleted ();
460 :
461 0 : test_announceOnce ();
462 :
463 0 : test_cascadedAnnounceOnce ();
464 0 : test_cascadedChangeNotification ();
465 : }
466 : else
467 : {
468 0 : printf ("warning no dbus daemon available; skipping tests that require dbus\n");
469 : }
470 :
471 0 : print_result ("testmod_dbus");
472 :
473 0 : dbus_shutdown ();
474 :
475 0 : return nbError;
476 : }
|