Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Tests for pluginprocess library.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include <stdio.h>
11 :
12 : #include <kdbpluginprocess.h>
13 :
14 : #include <kdb.h>
15 : #include <kdbplugin.h>
16 : #include <kdbprivate.h>
17 : #include <stdlib.h>
18 :
19 : #include <tests.h>
20 :
21 10 : static int elektraDummyOpen (Plugin * handle, Key * errorKey)
22 : {
23 10 : ElektraPluginProcess * pp = elektraPluginGetData (handle);
24 10 : if (pp == NULL)
25 : {
26 10 : if ((pp = elektraPluginProcessInit (errorKey)) == NULL) return ELEKTRA_PLUGIN_STATUS_ERROR;
27 10 : elektraPluginSetData (handle, pp);
28 : // pass dummy plugin data over to the child
29 10 : int * testData = (int *) malloc (sizeof (int));
30 10 : *testData = 42;
31 10 : elektraPluginProcessSetData (pp, testData);
32 10 : if (!elektraPluginProcessIsParent (pp)) elektraPluginProcessStart (handle, pp);
33 : }
34 10 : if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessOpen (pp, errorKey);
35 :
36 0 : keySetMeta (errorKey, "user/tests/pluginprocess/open", "");
37 0 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
38 : }
39 :
40 12 : static int elektraDummyClose (Plugin * handle, Key * errorKey)
41 : {
42 12 : ElektraPluginProcess * pp = elektraPluginGetData (handle);
43 12 : if (pp)
44 : {
45 12 : int * testData = (int *) elektraPluginProcessGetData (pp);
46 12 : elektraPluginSetData (handle, NULL);
47 12 : elektraFree (testData);
48 :
49 12 : if (elektraPluginProcessIsParent (pp))
50 : {
51 12 : ElektraPluginProcessCloseResult result = elektraPluginProcessClose (pp, errorKey);
52 12 : return result.result;
53 : }
54 : }
55 :
56 0 : keySetMeta (errorKey, "user/tests/pluginprocess/close", "");
57 0 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
58 : }
59 :
60 10 : static int elektraDummyGet (Plugin * handle, KeySet * returned, Key * parentKey)
61 : {
62 10 : ElektraPluginProcess * pp = elektraPluginGetData (handle);
63 10 : if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_GET, returned, parentKey);
64 :
65 0 : int * testData = (int *) elektraPluginProcessGetData (pp);
66 :
67 0 : keySetMeta (parentKey, "user/tests/pluginprocess/get", "");
68 : // Just check if the child can access its actual plugin data
69 0 : if (*testData == 42) keySetMeta (parentKey, "user/tests/pluginprocess/testdata", "");
70 0 : ksAppendKey (returned, keyNew ("user/tests/pluginprocess/get", KEY_END));
71 0 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
72 : }
73 :
74 6 : static int elektraDummySet (Plugin * handle, KeySet * returned, Key * parentKey)
75 : {
76 6 : ElektraPluginProcess * pp = elektraPluginGetData (handle);
77 6 : if (elektraPluginProcessIsParent (pp))
78 : {
79 6 : return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_SET, returned, parentKey);
80 : }
81 :
82 0 : keySetMeta (parentKey, "user/tests/pluginprocess/set", "value");
83 0 : ksAppendKey (returned, keyNew ("user/tests/pluginprocess/set", KEY_END));
84 0 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
85 : }
86 :
87 8 : static int elektraDummyError (Plugin * handle, KeySet * returned, Key * parentKey)
88 : {
89 8 : ElektraPluginProcess * pp = elektraPluginGetData (handle);
90 8 : if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_ERROR, returned, parentKey);
91 :
92 0 : keySetMeta (parentKey, "user/tests/pluginprocess/error", "");
93 0 : ksAppendKey (returned, keyNew ("user/tests/pluginprocess/error", KEY_END));
94 0 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
95 : }
96 :
97 14 : static Plugin * createDummyPlugin (KeySet * conf)
98 : {
99 14 : Plugin * plugin = malloc (sizeof (struct _Plugin));
100 14 : plugin->config = conf;
101 14 : plugin->kdbOpen = &elektraDummyOpen;
102 14 : plugin->kdbClose = &elektraDummyClose;
103 14 : plugin->kdbGet = &elektraDummyGet;
104 14 : plugin->kdbSet = &elektraDummySet;
105 14 : plugin->kdbError = &elektraDummyError;
106 14 : plugin->name = "dummy";
107 14 : plugin->refcounter = 1;
108 14 : plugin->data = NULL;
109 14 : return plugin;
110 : }
111 :
112 2 : static void test_communication (void)
113 : {
114 2 : printf ("test communication\n");
115 :
116 2 : Key * parentKey = keyNew ("user/tests/pluginprocess", KEY_END);
117 2 : keySetMeta (parentKey, "/hello/from/parent", "value");
118 2 : KeySet * conf = ksNew (0, KS_END);
119 2 : Plugin * plugin = createDummyPlugin (conf);
120 :
121 2 : KeySet * ks = ksNew (0, KS_END);
122 :
123 2 : succeed_if (plugin->kdbOpen (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbOpen was not successful");
124 2 : ElektraPluginProcess * pp = elektraPluginGetData (plugin);
125 2 : succeed_if (pp != NULL, "didn't store the pluginprocess struct in the plugin's data");
126 2 : if (pp)
127 : {
128 2 : succeed_if (keyGetMeta (parentKey, "user/tests/pluginprocess/open") != NULL,
129 : "child process didn't set the open metadata on the parent key");
130 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbSet was not successful");
131 2 : const Key * parentMeta = keyGetMeta (parentKey, "/hello/from/parent");
132 2 : succeed_if (parentMeta != NULL, "missing parent metadata on parent key") if (parentMeta != NULL)
133 : {
134 2 : succeed_if (elektraStrCmp (keyString (parentMeta), "value") == 0, "missing parent metadata value on parent key");
135 : }
136 2 : const Key * childMeta = keyGetMeta (parentKey, "user/tests/pluginprocess/set");
137 2 : succeed_if (childMeta != NULL, "missing child metadata on parent key");
138 2 : if (childMeta != NULL)
139 : {
140 2 : succeed_if (elektraStrCmp (keyString (childMeta), "value") == 0, "missing child metadata value on parent key");
141 : }
142 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbGet was not successful");
143 2 : succeed_if (keyGetMeta (parentKey, "user/tests/pluginprocess/get") != NULL,
144 : "child process didn't set the get metadata on the parent key");
145 2 : succeed_if (keyGetMeta (parentKey, "user/tests/pluginprocess/testdata") != NULL,
146 : "child process didn't receive the plugin data from the parent");
147 2 : succeed_if (plugin->kdbError (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS,
148 : "call to kdbError was not successful");
149 2 : succeed_if (keyGetMeta (parentKey, "user/tests/pluginprocess/error") != NULL,
150 : "child process didn't set the error metadata on the parent key");
151 : }
152 2 : succeed_if (plugin->kdbClose (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbClose was not successful");
153 2 : succeed_if (keyGetMeta (parentKey, "user/tests/pluginprocess/close") != NULL,
154 : "child process didn't set the close metadata on the parent key");
155 2 : succeed_if (elektraPluginGetData (plugin) == NULL, "didn't free the pluginprocess struct in the plugin's data");
156 2 : succeed_if (ksLookup (ks, parentKey, KDB_O_NONE) == NULL, "stored the parent key in the keyset");
157 :
158 2 : output_warnings (parentKey);
159 2 : output_error (parentKey);
160 :
161 2 : keyDel (parentKey);
162 2 : ksDel (ks);
163 2 : ksDel (conf);
164 2 : elektraFree (plugin);
165 2 : }
166 :
167 2 : static void test_emptyKeySet (void)
168 : {
169 2 : printf ("test emptyKeySet\n");
170 :
171 2 : Key * parentKey = keyNew ("user/tests/pluginprocess", KEY_END);
172 2 : KeySet * conf = ksNew (0, KS_END);
173 2 : Plugin * plugin = createDummyPlugin (conf);
174 :
175 2 : KeySet * ks = NULL;
176 :
177 2 : succeed_if (plugin->kdbOpen (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbOpen was not successful");
178 2 : ElektraPluginProcess * pp = elektraPluginGetData (plugin);
179 2 : if (pp)
180 : {
181 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR,
182 : "call to kdbSet with null keyset was successful");
183 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR,
184 : "call to kdbGet with null keyset was successful");
185 2 : succeed_if (plugin->kdbError (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR,
186 : "call to kdbError with null keyset was successful");
187 : }
188 2 : succeed_if (plugin->kdbClose (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbClose was not successful");
189 :
190 2 : output_warnings (parentKey);
191 2 : output_error (parentKey);
192 :
193 2 : keyDel (parentKey);
194 2 : ksDel (ks);
195 2 : ksDel (conf);
196 2 : elektraFree (plugin);
197 2 : }
198 :
199 2 : static void test_reservedParentKeyName (void)
200 : {
201 2 : printf ("test reservedParentKeyName\n");
202 :
203 2 : Key * parentKey = keyNew ("/pluginprocess/parent/name", KEY_VALUE, "invalid", KEY_END);
204 2 : KeySet * conf = ksNew (0, KS_END);
205 2 : Plugin * plugin = createDummyPlugin (conf);
206 :
207 2 : KeySet * ks = ksNew (6, KS_END);
208 2 : ksAppendKey (ks, keyNew ("/pluginprocess/parent", KEY_END));
209 2 : ksAppendKey (ks, keyNew ("/pluginprocess/parent/name", KEY_END));
210 2 : ksAppendKey (ks, keyNew ("/pluginprocess/command", KEY_END));
211 2 : ksAppendKey (ks, keyNew ("/pluginprocess/payload/exists", KEY_END));
212 2 : ksAppendKey (ks, keyNew ("/pluginprocess/version", KEY_END));
213 2 : ksAppendKey (ks, keyNew ("/pluginprocess/result", KEY_END));
214 :
215 2 : succeed_if (plugin->kdbOpen (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbOpen was not successful");
216 2 : ElektraPluginProcess * pp = elektraPluginGetData (plugin);
217 2 : if (pp)
218 : {
219 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbGet was not successful");
220 : }
221 2 : succeed_if (plugin->kdbClose (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbClose was not successful");
222 :
223 2 : output_warnings (parentKey);
224 2 : output_error (parentKey);
225 :
226 2 : keyDel (parentKey);
227 2 : ksDel (ks);
228 2 : ksDel (conf);
229 2 : elektraFree (plugin);
230 2 : }
231 :
232 2 : static void test_keysetContainingParentKey (void)
233 : {
234 2 : printf ("test keysetContainingParentKey\n");
235 :
236 2 : Key * parentKey = keyNew ("user/tests/pluginprocess", KEY_END);
237 2 : keySetMeta (parentKey, "/hello/from/parent", "value");
238 2 : KeySet * conf = ksNew (0, KS_END);
239 2 : Plugin * plugin = createDummyPlugin (conf);
240 :
241 2 : KeySet * ks = ksNew (1, KS_END);
242 2 : ksAppendKey (ks, parentKey);
243 :
244 2 : succeed_if (plugin->kdbOpen (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbOpen was not successful");
245 2 : ElektraPluginProcess * pp = elektraPluginGetData (plugin);
246 2 : if (pp)
247 : {
248 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbSet was not successful");
249 2 : const Key * parentMeta = keyGetMeta (parentKey, "/hello/from/parent");
250 2 : succeed_if (parentMeta != NULL, "missing parent metadata on parent key") if (parentMeta != NULL)
251 : {
252 2 : succeed_if (elektraStrCmp (keyString (parentMeta), "value") == 0, "missing parent metadata value on parent key");
253 : }
254 2 : const Key * childMeta = keyGetMeta (parentKey, "user/tests/pluginprocess/set");
255 2 : succeed_if (childMeta != NULL, "missing child metadata on parent key");
256 2 : if (childMeta != NULL)
257 : {
258 2 : succeed_if (elektraStrCmp (keyString (childMeta), "value") == 0, "missing child metadata value on parent key");
259 : }
260 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbGet was not successful");
261 2 : succeed_if (plugin->kdbError (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS,
262 : "call to kdbError was not successful");
263 : }
264 2 : succeed_if (plugin->kdbClose (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbClose was not successful");
265 2 : succeed_if (ksLookupByName (ks, "user/tests/pluginprocess", KDB_O_NONE) != NULL,
266 : "parent key got removed from the keyset by pluginprocess");
267 :
268 2 : output_warnings (parentKey);
269 2 : output_error (parentKey);
270 :
271 2 : ksDel (ks);
272 2 : ksDel (conf);
273 2 : elektraFree (plugin);
274 2 : }
275 :
276 2 : static int elektraDummySetAddingParentKey (Plugin * handle, KeySet * returned, Key * parentKey)
277 : {
278 2 : ElektraPluginProcess * pp = elektraPluginGetData (handle);
279 2 : if (elektraPluginProcessIsParent (pp))
280 : {
281 2 : return elektraPluginProcessSend (pp, ELEKTRA_PLUGINPROCESS_SET, returned, parentKey);
282 : }
283 :
284 0 : keySetMeta (parentKey, "user/tests/pluginprocess/set", "value");
285 0 : ksAppendKey (returned, keyNew ("user/tests/pluginprocess/set", KEY_END));
286 0 : ksAppendKey (returned, parentKey);
287 0 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
288 : }
289 :
290 2 : static void test_childAddingParentKey (void)
291 : {
292 2 : printf ("test childAddingParentKey\n");
293 :
294 2 : Key * parentKey = keyNew ("user/tests/pluginprocess", KEY_END);
295 2 : keySetMeta (parentKey, "/hello/from/parent", "value");
296 2 : KeySet * conf = ksNew (0, KS_END);
297 2 : Plugin * plugin = createDummyPlugin (conf);
298 2 : plugin->kdbSet = &elektraDummySetAddingParentKey;
299 :
300 2 : KeySet * ks = ksNew (0, KS_END);
301 :
302 2 : succeed_if (plugin->kdbOpen (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbOpen was not successful");
303 2 : ElektraPluginProcess * pp = elektraPluginGetData (plugin);
304 2 : if (pp)
305 : {
306 2 : succeed_if (plugin->kdbSet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbSet was not successful");
307 2 : Key * addedParentKey = ksLookup (ks, parentKey, KDB_O_NONE);
308 2 : succeed_if (addedParentKey != NULL, "parent key was not added to keyset");
309 2 : const Key * parentMeta = keyGetMeta (addedParentKey, "/hello/from/parent");
310 2 : succeed_if (parentMeta != NULL, "missing parent metadata on parent key") if (parentMeta != NULL)
311 : {
312 2 : succeed_if (elektraStrCmp (keyString (parentMeta), "value") == 0, "missing parent metadata value on parent key");
313 : }
314 2 : const Key * childMeta = keyGetMeta (addedParentKey, "user/tests/pluginprocess/set");
315 2 : succeed_if (childMeta != NULL, "missing child metadata on parent key");
316 2 : if (childMeta != NULL)
317 : {
318 2 : succeed_if (elektraStrCmp (keyString (childMeta), "value") == 0, "missing child metadata value on parent key");
319 : }
320 2 : succeed_if (plugin->kdbGet (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbGet was not successful");
321 2 : succeed_if (plugin->kdbError (plugin, ks, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS,
322 : "call to kdbError was not successful");
323 : }
324 2 : succeed_if (plugin->kdbClose (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbClose was not successful");
325 2 : succeed_if (ksLookupByName (ks, "user/tests/pluginprocess", KDB_O_NONE) != NULL,
326 : "parent key got removed from the keyset by pluginprocess");
327 :
328 2 : output_warnings (parentKey);
329 2 : output_error (parentKey);
330 :
331 2 : keyDel (parentKey);
332 2 : ksDel (ks);
333 2 : ksDel (conf);
334 2 : elektraFree (plugin);
335 2 : }
336 :
337 2 : static int elektraDummyOpenWithError (Plugin * handle, Key * errorKey)
338 : {
339 2 : ElektraPluginProcess * pp = elektraPluginGetData (handle);
340 2 : if (pp == NULL)
341 : {
342 2 : if ((pp = elektraPluginProcessInit (errorKey)) == NULL) return ELEKTRA_PLUGIN_STATUS_ERROR;
343 2 : elektraPluginSetData (handle, pp);
344 : // Assume some other initialization failed and thus close here without calling open
345 : // to free the resources but without sending the command
346 : // Note that init didn't fail thus we have forked already
347 : // so kill the child here and cleanup the parent
348 2 : if (elektraPluginProcessIsParent (pp))
349 : {
350 2 : elektraPluginProcessClose (pp, errorKey);
351 : }
352 : else
353 : {
354 0 : _Exit (EXIT_SUCCESS);
355 : }
356 : }
357 :
358 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
359 : }
360 :
361 2 : static void test_closeWithoutOpen (void)
362 : {
363 2 : printf ("test closeWithoutOpen\n");
364 :
365 2 : Key * parentKey = keyNew ("user/tests/pluginprocess", KEY_END);
366 2 : KeySet * conf = ksNew (0, KS_END);
367 2 : Plugin * plugin = createDummyPlugin (conf);
368 2 : plugin->kdbOpen = &elektraDummyOpenWithError;
369 2 : KeySet * ks = ksNew (0, KS_END);
370 :
371 2 : succeed_if (plugin->kdbOpen (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_SUCCESS, "call to kdbOpen was not successful");
372 :
373 2 : output_warnings (parentKey);
374 2 : output_error (parentKey);
375 :
376 2 : keyDel (parentKey);
377 2 : ksDel (ks);
378 2 : ksDel (conf);
379 2 : elektraFree (plugin);
380 2 : }
381 :
382 2 : static int elektraDummyOpenAndDie (Plugin * handle, Key * errorKey)
383 : {
384 2 : ElektraPluginProcess * pp = elektraPluginGetData (handle);
385 2 : if (pp == NULL)
386 : {
387 2 : if ((pp = elektraPluginProcessInit (errorKey)) == NULL) return ELEKTRA_PLUGIN_STATUS_ERROR;
388 2 : elektraPluginSetData (handle, pp);
389 2 : if (!elektraPluginProcessIsParent (pp)) elektraPluginProcessStart (handle, pp);
390 : }
391 2 : if (elektraPluginProcessIsParent (pp)) return elektraPluginProcessOpen (pp, errorKey);
392 :
393 : // simulate a dying child process to check if pipes get notified about it
394 0 : _Exit (0);
395 : }
396 :
397 2 : static void test_childDies (void)
398 : {
399 2 : printf ("test childDies\n");
400 :
401 2 : Key * parentKey = keyNew ("user/tests/pluginprocess", KEY_END);
402 2 : KeySet * conf = ksNew (0, KS_END);
403 2 : Plugin * plugin = createDummyPlugin (conf);
404 2 : plugin->kdbOpen = &elektraDummyOpenAndDie;
405 2 : KeySet * ks = ksNew (0, KS_END);
406 :
407 2 : succeed_if (plugin->kdbOpen (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR, "call to kdbOpen was successful");
408 : // Child died, we still have to call close to cleanup the resources
409 : // It is expected to fail as pluginprocess cannot communicate with the child anymore, but still cleans up resources
410 2 : succeed_if (plugin->kdbClose (plugin, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR, "call to kdbClose was successful");
411 :
412 2 : output_warnings (parentKey);
413 2 : output_error (parentKey);
414 :
415 2 : keyDel (parentKey);
416 2 : ksDel (ks);
417 2 : ksDel (conf);
418 2 : elektraFree (plugin);
419 2 : }
420 :
421 2 : int main (int argc, char ** argv)
422 : {
423 2 : init (argc, argv);
424 :
425 2 : test_communication ();
426 2 : test_emptyKeySet ();
427 2 : test_reservedParentKeyName ();
428 2 : test_keysetContainingParentKey ();
429 2 : test_closeWithoutOpen ();
430 2 : test_childAddingParentKey ();
431 2 : test_childDies ();
432 :
433 2 : print_result ("pluginprocess");
434 :
435 2 : return nbError;
436 : }
|