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 "dbusrecv.h"
10 :
11 : #include <stdio.h> // printf() & co
12 :
13 : #include <kdbio/uv.h>
14 : #include <kdbioplugin.h>
15 :
16 : #include <uv.h>
17 :
18 : #include <tests.h>
19 : #include <tests_plugin.h>
20 :
21 : #define TEST_TIMEOUT 1000
22 :
23 : Key * test_callbackKey;
24 : uv_loop_t * test_callbackLoop;
25 :
26 : /** D-Bus bus type used by tests */
27 : DBusBusType testBusType;
28 :
29 : /**
30 : * @internal
31 : * Get and setup D-Bus connection
32 : *
33 : * @param type D-Bus bus type
34 : * @return D-Bus connection or NULL on error
35 : */
36 0 : static DBusConnection * getDbusConnection (DBusBusType type)
37 : {
38 : DBusError error;
39 0 : dbus_error_init (&error);
40 :
41 0 : DBusConnection * connection = dbus_bus_get (type, &error);
42 0 : if (connection == NULL)
43 : {
44 0 : printf ("connect: Failed to open connection to %s message bus: %s\n", (type == DBUS_BUS_SYSTEM) ? "system" : "session",
45 : error.message);
46 0 : dbus_error_free (&error);
47 0 : return NULL;
48 : }
49 0 : dbus_error_free (&error);
50 :
51 0 : dbus_connection_set_exit_on_disconnect (connection, FALSE);
52 :
53 0 : return connection;
54 : }
55 :
56 : /**
57 : * @internal
58 : * Send Elektra's D-Bus message.
59 : *
60 : * @param signalName signal name
61 : * @param keyName key name
62 : */
63 0 : static void dbusSendMessage (const char * signalName, const char * keyName)
64 : {
65 : DBusMessage * message;
66 0 : const char * interface = "org.libelektra";
67 0 : const char * path = "/org/libelektra/configuration";
68 :
69 0 : DBusConnection * connection = getDbusConnection (testBusType);
70 0 : exit_if_fail (connection != NULL, "could not get bus connection");
71 :
72 0 : message = dbus_message_new_signal (path, interface, signalName);
73 0 : exit_if_fail (message, "could not allocate dbus message");
74 :
75 0 : exit_if_fail (dbus_message_append_args (message, DBUS_TYPE_STRING, &keyName, DBUS_TYPE_INVALID), "could not add message arguments");
76 :
77 0 : dbus_connection_send (connection, message, NULL);
78 :
79 0 : dbus_message_unref (message);
80 0 : dbus_connection_unref (connection);
81 :
82 0 : return;
83 : }
84 :
85 : /**
86 : * Timeout for tests.
87 : *
88 : * Creates a failure and stops the event loop
89 : *
90 : * @param timerOp timer operation
91 : */
92 0 : static void test_timerCallback (ElektraIoTimerOperation * timerOp ELEKTRA_UNUSED)
93 : {
94 0 : succeed_if (0, "timeout exceeded; test failed");
95 0 : uv_stop (test_callbackLoop);
96 0 : }
97 :
98 : /**
99 : * @internal
100 : * Called by plugin when a notification was received.
101 : * The key is saved to be evaluated by the current test and the event loop is
102 : * stopped.
103 : *
104 : * @see ElektraNotificationCallback (kdbnotificationinternal.h)
105 : *
106 : * @param key changed key
107 : * @param context notification callback context
108 : */
109 0 : static void test_notificationCallback (Key * key, ElektraNotificationCallbackContext * context ELEKTRA_UNUSED)
110 : {
111 0 : test_callbackKey = key;
112 0 : uv_stop (test_callbackLoop);
113 0 : }
114 :
115 0 : static int test_prerequisites (void)
116 : {
117 0 : printf ("testing prerequisites\n");
118 0 : printf ("detecting available bus types - please ignore single error messages prefixed with \"connect:\"\n");
119 :
120 0 : DBusConnection * systemBus = getDbusConnection (DBUS_BUS_SYSTEM);
121 0 : DBusConnection * sessionBus = getDbusConnection (DBUS_BUS_SESSION);
122 :
123 0 : int success = 0;
124 0 : if (systemBus != NULL || sessionBus != NULL)
125 : {
126 : // Set bus type for tests
127 : // NOTE brew dbus on MacOs supports session by out of the box while session
128 : // bus is not available without further configuration on Linux
129 0 : if (systemBus)
130 : {
131 0 : testBusType = DBUS_BUS_SYSTEM;
132 : }
133 0 : else if (sessionBus)
134 : {
135 0 : testBusType = DBUS_BUS_SESSION;
136 : }
137 : success = 1;
138 : }
139 :
140 0 : if (systemBus) dbus_connection_unref (systemBus);
141 0 : if (sessionBus) dbus_connection_unref (sessionBus);
142 :
143 0 : return success;
144 : }
145 :
146 0 : static void test_commit (uv_loop_t * loop, ElektraIoInterface * binding)
147 : {
148 0 : printf ("test commit\n");
149 :
150 0 : KeySet * conf = ksNew (1, keyNew ("user/announce", KEY_VALUE, "once", KEY_END), KS_END);
151 0 : PLUGIN_OPEN ("dbusrecv");
152 :
153 : // io binding is required for dispatching
154 0 : size_t func = elektraPluginGetFunction (plugin, "setIoBinding");
155 0 : exit_if_fail (func, "could not get function setIoBinding");
156 0 : KeySet * setIoBindingParams =
157 0 : ksNew (1, keyNew ("/ioBinding", KEY_BINARY, KEY_SIZE, sizeof (binding), KEY_VALUE, &binding, KEY_END), KS_END);
158 0 : ElektraIoPluginSetBinding setIoBinding = (ElektraIoPluginSetBinding) func;
159 0 : setIoBinding (plugin, setIoBindingParams);
160 0 : ksDel (setIoBindingParams);
161 :
162 : // open notification
163 0 : func = elektraPluginGetFunction (plugin, "openNotification");
164 0 : exit_if_fail (func, "could not get function openNotification");
165 0 : ElektraNotificationOpenNotification openNotification = (ElektraNotificationOpenNotification) func;
166 0 : KeySet * openNotificationParams = ksNew (2, keyNew ("/callback", KEY_FUNC, test_notificationCallback, KEY_END), KS_END);
167 0 : openNotification (plugin, openNotificationParams);
168 0 : ksDel (openNotificationParams);
169 :
170 0 : char * expectedKeyName = "system/tests/testmod_dbusrecv/changed";
171 0 : dbusSendMessage ("Commit", expectedKeyName);
172 :
173 0 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (TEST_TIMEOUT, 1, test_timerCallback, NULL);
174 0 : elektraIoBindingAddTimer (binding, timerOp);
175 :
176 0 : test_callbackKey = NULL;
177 0 : test_callbackLoop = loop;
178 0 : uv_run (loop, UV_RUN_DEFAULT);
179 :
180 0 : succeed_if_same_string (expectedKeyName, keyName (test_callbackKey));
181 :
182 : // close notification
183 0 : func = elektraPluginGetFunction (plugin, "closeNotification");
184 0 : exit_if_fail (func, "could not get function closeNotification");
185 0 : ElektraNotificationCloseNotification closeNotification = (ElektraNotificationCloseNotification) func;
186 0 : closeNotification (plugin, NULL);
187 :
188 0 : elektraIoBindingRemoveTimer (timerOp);
189 0 : elektraFree (timerOp);
190 0 : keyDel (test_callbackKey);
191 0 : PLUGIN_CLOSE ();
192 0 : }
193 :
194 0 : static void test_keyAdded (uv_loop_t * loop, ElektraIoInterface * binding)
195 : {
196 0 : printf ("test adding keys\n");
197 :
198 0 : KeySet * conf = ksNew (0, KS_END);
199 0 : PLUGIN_OPEN ("dbusrecv");
200 :
201 : // io binding is required for dispatching
202 0 : size_t func = elektraPluginGetFunction (plugin, "setIoBinding");
203 0 : exit_if_fail (func, "could not get function setIoBinding");
204 0 : KeySet * setIoBindingParams =
205 0 : ksNew (1, keyNew ("/ioBinding", KEY_BINARY, KEY_SIZE, sizeof (binding), KEY_VALUE, &binding, KEY_END), KS_END);
206 0 : ElektraIoPluginSetBinding setIoBinding = (ElektraIoPluginSetBinding) func;
207 0 : setIoBinding (plugin, setIoBindingParams);
208 0 : ksDel (setIoBindingParams);
209 :
210 : // open notification
211 0 : func = elektraPluginGetFunction (plugin, "openNotification");
212 0 : exit_if_fail (func, "could not get function openNotification");
213 0 : KeySet * openNotificationParams = ksNew (2, keyNew ("/callback", KEY_FUNC, test_notificationCallback, KEY_END), KS_END);
214 0 : ElektraNotificationOpenNotification openNotification = (ElektraNotificationOpenNotification) func;
215 0 : openNotification (plugin, openNotificationParams);
216 0 : ksDel (openNotificationParams);
217 :
218 0 : char * expectedKeyName = "system/tests/testmod_dbusrecv/added";
219 0 : dbusSendMessage ("KeyAdded", expectedKeyName);
220 :
221 0 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (TEST_TIMEOUT, 1, test_timerCallback, NULL);
222 0 : elektraIoBindingAddTimer (binding, timerOp);
223 :
224 0 : test_callbackKey = NULL;
225 0 : test_callbackLoop = loop;
226 0 : uv_run (loop, UV_RUN_DEFAULT);
227 :
228 0 : succeed_if_same_string (expectedKeyName, keyName (test_callbackKey));
229 :
230 : // close notification
231 0 : func = elektraPluginGetFunction (plugin, "closeNotification");
232 0 : exit_if_fail (func, "could not get function closeNotification");
233 0 : ElektraNotificationCloseNotification closeNotification = (ElektraNotificationCloseNotification) func;
234 0 : closeNotification (plugin, NULL);
235 :
236 0 : elektraIoBindingRemoveTimer (timerOp);
237 0 : elektraFree (timerOp);
238 0 : keyDel (test_callbackKey);
239 0 : PLUGIN_CLOSE ();
240 0 : }
241 :
242 0 : static void test_keyChanged (uv_loop_t * loop, ElektraIoInterface * binding)
243 : {
244 0 : printf ("test adding keys\n");
245 :
246 0 : KeySet * conf = ksNew (0, KS_END);
247 0 : PLUGIN_OPEN ("dbusrecv");
248 :
249 : // io binding is required for dispatching
250 0 : size_t func = elektraPluginGetFunction (plugin, "setIoBinding");
251 0 : exit_if_fail (func, "could not get function setIoBinding");
252 0 : KeySet * setIoBindingParams =
253 0 : ksNew (1, keyNew ("/ioBinding", KEY_BINARY, KEY_SIZE, sizeof (binding), KEY_VALUE, &binding, KEY_END), KS_END);
254 0 : ElektraIoPluginSetBinding setIoBinding = (ElektraIoPluginSetBinding) func;
255 0 : setIoBinding (plugin, setIoBindingParams);
256 0 : ksDel (setIoBindingParams);
257 :
258 : // open notification
259 0 : func = elektraPluginGetFunction (plugin, "openNotification");
260 0 : exit_if_fail (func, "could not get function openNotification");
261 0 : KeySet * openNotificationParams = ksNew (2, keyNew ("/callback", KEY_FUNC, test_notificationCallback, KEY_END), KS_END);
262 0 : ElektraNotificationOpenNotification openNotification = (ElektraNotificationOpenNotification) func;
263 0 : openNotification (plugin, openNotificationParams);
264 0 : ksDel (openNotificationParams);
265 :
266 0 : char * expectedKeyName = "system/tests/testmod_dbusrecv/changed";
267 0 : dbusSendMessage ("KeyChanged", expectedKeyName);
268 :
269 0 : ElektraIoTimerOperation * timerOp = elektraIoNewTimerOperation (TEST_TIMEOUT, 1, test_timerCallback, NULL);
270 0 : elektraIoBindingAddTimer (binding, timerOp);
271 :
272 0 : test_callbackKey = NULL;
273 0 : test_callbackLoop = loop;
274 0 : uv_run (loop, UV_RUN_DEFAULT);
275 :
276 0 : succeed_if_same_string (expectedKeyName, keyName (test_callbackKey));
277 :
278 : // close notification
279 0 : func = elektraPluginGetFunction (plugin, "closeNotification");
280 0 : exit_if_fail (func, "could not get function closeNotification");
281 0 : ElektraNotificationCloseNotification closeNotification = (ElektraNotificationCloseNotification) func;
282 0 : closeNotification (plugin, NULL);
283 :
284 0 : elektraIoBindingRemoveTimer (timerOp);
285 0 : elektraFree (timerOp);
286 0 : keyDel (test_callbackKey);
287 0 : PLUGIN_CLOSE ();
288 0 : }
289 :
290 0 : int main (int argc, char ** argv)
291 : {
292 0 : printf ("DBUSRECV TESTS\n");
293 0 : printf ("==============\n\n");
294 :
295 0 : init (argc, argv);
296 :
297 : // Test if dbus is available
298 0 : if (test_prerequisites ())
299 : {
300 :
301 0 : uv_loop_t * loop = uv_default_loop ();
302 0 : ElektraIoInterface * binding = elektraIoUvNew (loop);
303 :
304 : // Test change types
305 0 : test_commit (loop, binding);
306 0 : test_keyAdded (loop, binding);
307 0 : test_keyChanged (loop, binding);
308 :
309 0 : elektraIoBindingCleanup (binding);
310 0 : uv_run (loop, UV_RUN_ONCE);
311 : #ifdef HAVE_LIBUV1
312 0 : uv_loop_close (loop);
313 : #elif HAVE_LIBUV0
314 : uv_loop_delete (loop);
315 : #endif
316 : }
317 : else
318 : {
319 0 : printf ("warning: no dbus daemon available; skipping tests that require dbus\n");
320 : }
321 :
322 0 : print_result ("testmod_dbusrecv");
323 :
324 0 : dbus_shutdown ();
325 :
326 0 : return nbError;
327 : }
|