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 <string.h>
10 : #include <tests.h>
11 :
12 : #ifdef HAVE_UNISTD_H
13 : #include <unistd.h>
14 : #endif
15 :
16 : #ifdef HAVE_TIME_H
17 : #include <time.h>
18 : #endif
19 :
20 : #ifdef HAVE_LOCALE_H
21 : #include <locale.h>
22 : #endif
23 :
24 : #ifdef USE_NFTW
25 : #include <ftw.h>
26 : #include <stdlib.h>
27 : #define NOPENFD 20
28 : #endif
29 :
30 : #include <regex.h>
31 :
32 : #include <kdbinternal.h>
33 :
34 : #ifdef __cplusplus
35 : extern "C" {
36 : #endif
37 :
38 : int nbError;
39 : int nbTest;
40 :
41 : char file[KDB_MAX_PATH_LENGTH];
42 : char srcdir[KDB_MAX_PATH_LENGTH + 1];
43 :
44 : char * tmpfilename;
45 : char * tempHome;
46 : int tempHomeLen;
47 : char * tempHomeConf;
48 :
49 : /**Does some useful startup.
50 : */
51 189 : int init (int argc, char ** argv)
52 : {
53 : char * tmpvar;
54 : int fd;
55 :
56 189 : if (argc > 1)
57 : {
58 189 : strncpy (srcdir, argv[1], sizeof (srcdir) - 1);
59 : }
60 : else
61 : {
62 0 : strncpy (srcdir, BUILTIN_DATA_FOLDER, sizeof (srcdir) - 1);
63 : }
64 :
65 189 : tmpvar = getenv ("TMPDIR");
66 189 : if (!tmpvar)
67 : {
68 189 : tmpvar = "/tmp";
69 : }
70 : // check tempvar for trailing slash /
71 189 : if (strlen (tmpvar) > 2)
72 : {
73 189 : if (tmpvar[strlen (tmpvar) - 1] == '/')
74 : {
75 0 : tmpvar[strlen (tmpvar) - 1] = '\0';
76 : }
77 : }
78 :
79 189 : tempHomeLen = strlen (tmpvar) + 1 + 13 + 6 + 1;
80 189 : tempHome = elektraMalloc (tempHomeLen);
81 189 : tempHomeConf = elektraMalloc (tempHomeLen + strlen (KDB_DB_USER) + 2);
82 189 : succeed_if (tempHome != 0, "elektraMalloc failed");
83 189 : snprintf (tempHome, tempHomeLen, "%s/elektra-test.XXXXXX", tmpvar);
84 189 : snprintf (tempHomeConf, tempHomeLen, "%s/elektra-test.XXXXXX/" KDB_DB_USER, tmpvar);
85 189 : succeed_if (mkdtemp (tempHome) != 0, "mkdtemp failed");
86 189 : setenv ("HOME", tempHome, 1);
87 :
88 189 : atexit (clean_temp_home);
89 :
90 189 : int tmpfilenameLen = tempHomeLen + 1 + 12 + 6 + 1;
91 189 : tmpfilename = elektraMalloc (tmpfilenameLen);
92 189 : succeed_if (tmpfilenameLen != 0, "elektraMalloc failed");
93 189 : snprintf (tmpfilename, tmpfilenameLen, "%s/elektra-tmp.XXXXXX", tempHome);
94 189 : fd = mkstemp (tmpfilename);
95 189 : succeed_if (fd != -1, "mkstemp failed");
96 189 : close (fd);
97 :
98 189 : return 0;
99 : }
100 :
101 : /**Create a root key for a backend.
102 : *
103 : * @return an allocated root key */
104 0 : Key * create_root_key (const char * backendName)
105 : {
106 0 : Key * root = keyNew ("user/tests", KEY_END);
107 : /*Make mountpoint beneath root, and do all tests here*/
108 0 : keyAddBaseName (root, backendName);
109 0 : keySetString (root, backendName);
110 0 : keySetString (root, backendName);
111 0 : return root;
112 : }
113 :
114 : /**Create a configuration keyset for a backend.
115 : *
116 : * @return an allocated configuration keyset for a backend*/
117 0 : KeySet * create_conf (const char * filename)
118 : {
119 0 : return ksNew (2, keyNew ("system/path", KEY_VALUE, filename, KEY_END), KS_END);
120 : }
121 :
122 :
123 125 : int compare_line_files_fun (const char * filename, const char * genfilename, int (*cmpFun) (const char *, const char *, size_t n))
124 : {
125 : FILE *forg, *fgen;
126 : char bufferorg[BUFFER_LENGTH + 1];
127 : char buffergen[BUFFER_LENGTH + 1];
128 125 : char * org = 0;
129 125 : char * gen = 0;
130 125 : int line = 0;
131 125 : int remainingBufferLength = BUFFER_LENGTH;
132 :
133 125 : forg = fopen (filename, "r");
134 125 : fgen = fopen (genfilename, "r");
135 :
136 125 : strncpy (bufferorg, "could not open file, orig: ", BUFFER_LENGTH);
137 125 : remainingBufferLength -= strlen ("could not open file, orig: ");
138 125 : strncat (bufferorg, filename, remainingBufferLength);
139 125 : remainingBufferLength -= strlen (filename);
140 125 : strncat (bufferorg, " gen: ", remainingBufferLength);
141 125 : remainingBufferLength -= strlen (" gen: ");
142 125 : strncat (bufferorg, genfilename, remainingBufferLength);
143 :
144 125 : exit_if_fail (forg && fgen, bufferorg);
145 :
146 2121 : while ((org = fgets (bufferorg, BUFFER_LENGTH, forg)) && (gen = fgets (buffergen, BUFFER_LENGTH, fgen)))
147 : {
148 1996 : line++;
149 1996 : if ((*cmpFun) (bufferorg, buffergen, BUFFER_LENGTH))
150 : {
151 0 : printf ("Compare <%s>, with <%s>\n", bufferorg, buffergen);
152 0 : printf ("in file %s, line %d.\n", filename, line);
153 0 : succeed_if (0, "comparing lines failed");
154 : goto error;
155 : }
156 : }
157 :
158 125 : if (org || fgets (buffergen, BUFFER_LENGTH, fgen))
159 : {
160 0 : printf ("The files do not have same number of lines (%d): %s.\n", line, filename);
161 0 : succeed_if (0, "comparing files failed");
162 : goto error;
163 : }
164 :
165 125 : fclose (forg);
166 125 : fclose (fgen);
167 125 : return 1;
168 :
169 : error:
170 0 : fclose (forg);
171 0 : fclose (fgen);
172 0 : return 0;
173 : }
174 :
175 :
176 : /**
177 : * @brief Compare two files line by line
178 : *
179 : * @param filename first file
180 : * @param genfilename file to compare with
181 : *
182 : * @retval 0 on errors (succeed_if already executed)
183 : * @retval 1 on success
184 : */
185 123 : int compare_line_files (const char * filename, const char * genfilename)
186 : {
187 123 : return compare_line_files_fun (filename, genfilename, &strncmp);
188 : }
189 :
190 :
191 : /**
192 : * @brief Compare regex in pattern to str
193 : *
194 : * @param pattern char * representing a regex pattern
195 : * @param str char * we compare the pattern to
196 : *
197 : * @retval 1 if pattern is invalid or does not match str
198 : * @retval 0 on success
199 : */
200 4 : int regexcmp (const char * pattern, const char * str, size_t n ELEKTRA_UNUSED)
201 : {
202 : int status;
203 : regex_t re;
204 :
205 4 : if (regcomp (&re, pattern, REG_EXTENDED | REG_NOSUB) != 0)
206 : {
207 : return (1);
208 : }
209 4 : status = regexec (&re, str, (size_t) 0, NULL, 0);
210 4 : regfree (&re);
211 4 : return status;
212 : }
213 :
214 : /**
215 : * @brief Compare two files line by line where the original file is made up of
216 : * regex
217 : *
218 : * @param filename first file, containing regex patterns
219 : * @param genfilename file to compare with
220 : *
221 : * @retval 0 on errors (succeed_if already executed)
222 : * @retval 1 on success
223 : */
224 2 : int compare_regex_to_line_files (const char * filename, const char * genfilename)
225 : {
226 2 : return compare_line_files_fun (filename, genfilename, ®excmp);
227 : }
228 :
229 :
230 : /**Compare two files line by line.
231 : *
232 : * Fails when there are any differences.
233 : *
234 : * The original file is passed as parameter.
235 : * It will be compared with the file -gen.
236 : *
237 : * file.xml -> file-gen.xml (xml comparator)
238 : * file.txt -> file-gen.txt (line comparator)
239 : * file.c -> file-gen.c (c comparator)
240 : *
241 : */
242 0 : int compare_files (const char * filename)
243 : {
244 : char genfilename[KDB_MAX_PATH_LENGTH];
245 0 : char * dot = strrchr (filename, '.');
246 :
247 0 : exit_if_fail (dot != 0, "could not find extension in file");
248 :
249 0 : strncpy (genfilename, filename, dot - filename);
250 : /* does not terminate string, but strcat need it, so: */
251 0 : genfilename[dot - filename] = 0;
252 0 : strcat (genfilename, "-gen");
253 0 : if (!strcmp (dot, ".xml"))
254 : {
255 0 : strcat (genfilename, ".xml");
256 : }
257 0 : else if (!strcmp (dot, ".txt"))
258 : {
259 0 : strcat (genfilename, ".txt");
260 : }
261 0 : else if (!strcmp (dot, ".c"))
262 : {
263 0 : strcat (genfilename, ".c");
264 : }
265 :
266 0 : return compare_line_files (filename, genfilename);
267 : }
268 :
269 :
270 : /* return file name in srcdir.
271 : * No bound checking on file size, may overflow. */
272 562 : char * srcdir_file (const char * fileName)
273 : {
274 562 : strcpy (file, srcdir);
275 562 : strcat (file, "/");
276 562 : strcat (file, fileName);
277 562 : return file;
278 : }
279 :
280 713 : const char * elektraFilename (void)
281 : {
282 713 : return tmpfilename;
283 : }
284 :
285 81 : void elektraUnlink (const char * filename)
286 : {
287 270 : unlink (filename);
288 81 : }
289 :
290 12 : void clear_sync (KeySet * ks)
291 : {
292 : Key * k;
293 12 : ksRewind (ks);
294 88 : while ((k = ksNext (ks)) != 0)
295 64 : keyClearSync (k);
296 12 : }
297 :
298 7 : void output_meta (Key * k)
299 : {
300 : const Key * meta;
301 :
302 7 : keyRewindMeta (k);
303 20 : while ((meta = keyNextMeta (k)) != 0)
304 : {
305 6 : printf (", %s: %s", keyName (meta), (const char *) keyValue (meta));
306 : }
307 7 : printf ("\n");
308 7 : }
309 :
310 7 : void output_key (Key * k)
311 : {
312 : // output_meta will print endline
313 7 : printf ("%p key: %s, string: %s", (void *) k, keyName (k), keyString (k));
314 7 : output_meta (k);
315 7 : }
316 :
317 3 : void output_keyset (KeySet * ks)
318 : {
319 : Key * k;
320 3 : ksRewind (ks);
321 13 : while ((k = ksNext (ks)) != 0)
322 : {
323 7 : output_key (k);
324 : }
325 3 : }
326 :
327 0 : void output_plugin (Plugin * plugin)
328 : {
329 0 : if (!plugin) return;
330 :
331 0 : printf ("Name: %s [%zu]\n", plugin->name, plugin->refcounter);
332 0 : output_keyset (plugin->config);
333 : }
334 :
335 0 : void output_backend (Backend * backend)
336 : {
337 0 : if (!backend) return;
338 :
339 0 : printf ("us: %zd, ss: %zd\n", backend->usersize, backend->systemsize);
340 0 : output_key (backend->mountpoint);
341 : }
342 :
343 0 : void output_trie (Trie * trie)
344 : {
345 : int i;
346 0 : for (i = 0; i < KDB_MAX_UCHAR; ++i)
347 : {
348 0 : if (trie->value[i])
349 : {
350 0 : printf ("output_trie: %p, mp: %s %s [%d]\n", (void *) trie->value[i], keyName (trie->value[i]->mountpoint),
351 0 : keyString (trie->value[i]->mountpoint), i);
352 : }
353 0 : if (trie->children[i]) output_trie (trie->children[i]);
354 : }
355 0 : if (trie->empty_value)
356 : {
357 0 : printf ("empty_value: %p, mp: %s %s\n", (void *) trie->empty_value, keyName (trie->empty_value->mountpoint),
358 0 : keyString (trie->empty_value->mountpoint));
359 : }
360 0 : }
361 :
362 0 : void output_split (Split * split)
363 : {
364 0 : printf ("Split - size: %zu, alloc: %zu\n", split->size, split->alloc);
365 0 : for (size_t i = 0; i < split->size; ++i)
366 : {
367 0 : if (split->handles[i])
368 : {
369 0 : printf ("split #%zu size: %zd, handle: %p, sync: %d, parent: %s (%s), spec: %zd, dir: %zd, user: %zd, system: "
370 : "%zd\n",
371 0 : i, ksGetSize (split->keysets[i]), (void *) split->handles[i], split->syncbits[i],
372 0 : keyName (split->parents[i]), keyString (split->parents[i]), split->handles[i]->specsize,
373 : split->handles[i]->dirsize, split->handles[i]->usersize, split->handles[i]->systemsize);
374 : }
375 : else
376 : {
377 0 : printf ("split #%zu, size: %zd, default split, sync: %d\n", i, ksGetSize (split->keysets[i]), split->syncbits[i]);
378 : }
379 : }
380 0 : }
381 :
382 0 : void generate_split (Split * split)
383 : {
384 0 : printf ("succeed_if (split->size == %zu, \"size of split not correct\");\n", split->size);
385 0 : for (size_t i = 0; i < split->size; ++i)
386 : {
387 0 : printf ("succeed_if (split->syncbits[%zu]== %d, \"size of split not correct\");\n", i, split->syncbits[i]);
388 0 : printf ("succeed_if (ksGetSize(split->keysets[%zu]) == %zd, \"wrong size\");\n", i, ksGetSize (split->keysets[i]));
389 : }
390 0 : }
391 :
392 : /**
393 : * @brief Output warnings if present
394 : *
395 : * To check for warnings use:
396 : * succeed_if(output_warnings(parentKey), "warning(s) found");
397 : *
398 : * @param warningKey the key to retrieve metadata from
399 : *
400 : * @see check_for_errors_and_warnings if you want errors to have a test case failed without output
401 : *
402 : * @retval 1 if no warnings (can be used within succeed_if)
403 : */
404 2586 : int output_warnings (Key * warningKey)
405 : {
406 : //! [warnings]
407 2586 : const Key * metaWarnings = keyGetMeta (warningKey, "warnings");
408 2586 : if (!metaWarnings) return 1; /* There are no current warnings */
409 :
410 34 : int nrWarnings = atoi (keyString (metaWarnings));
411 :
412 17 : printf ("There are %d warnings\n", nrWarnings + 1);
413 38 : for (int i = 0; i <= nrWarnings; ++i)
414 : {
415 21 : char buffer[] = "warnings/#00\0description";
416 21 : buffer[10] = i / 10 % 10 + '0';
417 21 : buffer[11] = i % 10 + '0';
418 21 : printf ("buffer is: %s\n", buffer);
419 21 : strncat (buffer, "/number", sizeof (buffer) - strlen (buffer) - 1);
420 21 : printf ("number: %s\n", keyString (keyGetMeta (warningKey, buffer)));
421 21 : buffer[12] = '\0';
422 21 : strncat (buffer, "/description", sizeof (buffer) - strlen (buffer) - 1);
423 21 : printf ("description: %s\n", keyString (keyGetMeta (warningKey, buffer)));
424 21 : buffer[12] = '\0';
425 21 : strncat (buffer, "/module", sizeof (buffer) - strlen (buffer) - 1);
426 21 : keyGetMeta (warningKey, buffer);
427 21 : printf ("module: %s\n", keyString (keyGetMeta (warningKey, buffer)));
428 21 : buffer[12] = '\0';
429 21 : strncat (buffer, "/file", sizeof (buffer) - strlen (buffer) - 1);
430 21 : keyGetMeta (warningKey, buffer);
431 21 : printf ("file: %s\n", keyString (keyGetMeta (warningKey, buffer)));
432 21 : buffer[12] = '\0';
433 21 : strncat (buffer, "/line", sizeof (buffer) - strlen (buffer) - 1);
434 21 : keyGetMeta (warningKey, buffer);
435 21 : printf ("line: %s\n", keyString (keyGetMeta (warningKey, buffer)));
436 21 : buffer[12] = '\0';
437 21 : strncat (buffer, "/reason", sizeof (buffer) - strlen (buffer) - 1);
438 21 : keyGetMeta (warningKey, buffer);
439 21 : printf ("reason: %s\n", keyString (keyGetMeta (warningKey, buffer)));
440 21 : buffer[12] = '\0';
441 21 : strncat (buffer, "/mountpoint", sizeof (buffer) - strlen (buffer) - 1);
442 21 : keyGetMeta (warningKey, buffer);
443 21 : printf ("reason: %s\n", keyString (keyGetMeta (warningKey, buffer)));
444 21 : buffer[12] = '\0';
445 21 : strncat (buffer, "/configfile", sizeof (buffer) - strlen (buffer) - 1);
446 21 : keyGetMeta (warningKey, buffer);
447 21 : printf ("reason: %s\n", keyString (keyGetMeta (warningKey, buffer)));
448 : }
449 : //! [warnings]
450 :
451 : return 0;
452 : }
453 :
454 : /**
455 : * @brief Output the error if present
456 : *
457 : * To check for error use:
458 : * succeed_if(output_error(parentKey), "error found");
459 : *
460 : * @param errorKey keys to retrieve errors from
461 : *
462 : * @retval 1 if no error (can be used within succeed_if)
463 : */
464 2590 : int output_error (Key * errorKey)
465 : {
466 : //! [error]
467 2590 : const Key * metaError = keyGetMeta (errorKey, "error");
468 2590 : if (!metaError) return 1; /* There is no current error */
469 :
470 10 : printf ("number: %s\n", keyString (keyGetMeta (errorKey, "error/number")));
471 10 : printf ("description: : %s\n", keyString (keyGetMeta (errorKey, "error/description")));
472 10 : printf ("module: : %s\n", keyString (keyGetMeta (errorKey, "error/module")));
473 10 : printf ("at: %s:%s\n", keyString (keyGetMeta (errorKey, "error/file")), keyString (keyGetMeta (errorKey, "error/line")));
474 10 : printf ("reason: : %s\n", keyString (keyGetMeta (errorKey, "error/reason")));
475 10 : printf ("mountpoint: : %s\n", keyString (keyGetMeta (errorKey, "error/mountpoint")));
476 10 : printf ("configfile: : %s\n", keyString (keyGetMeta (errorKey, "error/configfile")));
477 : //! [error]
478 :
479 10 : return 0;
480 : }
481 :
482 : #ifdef USE_NFTW
483 198 : static int rm_all (const char * fpath, const struct stat * sb ELEKTRA_UNUSED, int tflag, struct FTW * ftwbuf ELEKTRA_UNUSED)
484 : {
485 198 : if (tflag == FTW_F)
486 : {
487 6 : unlink (fpath);
488 : }
489 192 : else if (tflag == FTW_D || tflag == FTW_DP)
490 : {
491 192 : rmdir (fpath);
492 : }
493 : else
494 : {
495 : // not a file or dir we can delete
496 0 : printf ("unexpected flag: %d\n", tflag);
497 0 : return 1;
498 : }
499 : return 0;
500 : }
501 : #endif
502 :
503 189 : void clean_temp_home (void)
504 : {
505 189 : if (tmpfilename)
506 : {
507 378 : elektraUnlink (tmpfilename);
508 189 : elektraFree (tmpfilename);
509 189 : tmpfilename = NULL;
510 : }
511 :
512 189 : if (tempHomeConf)
513 : {
514 189 : rmdir (tempHomeConf);
515 189 : elektraFree (tempHomeConf);
516 189 : tempHomeConf = NULL;
517 : }
518 :
519 189 : if (tempHome)
520 : {
521 : #ifdef USE_NFTW
522 189 : int nftw_flags = FTW_DEPTH | FTW_PHYS;
523 189 : succeed_if (nftw (tempHome, rm_all, NOPENFD, nftw_flags) == 0, "Could not delete TMPHOME via nftw");
524 : #else
525 : size_t fileToCleanLen = tempHomeLen + 30;
526 : char * fileToClean = elektraMalloc (fileToCleanLen);
527 : snprintf (fileToClean, fileToCleanLen, "%s/.gnupg/random_seed", tempHome);
528 : unlink (fileToClean);
529 : snprintf (fileToClean, fileToCleanLen, "%s/.gnupg/trustdb.gpg", tempHome);
530 : unlink (fileToClean);
531 : snprintf (fileToClean, fileToCleanLen, "%s/.gnupg/pubring.kbx~", tempHome);
532 : unlink (fileToClean);
533 : snprintf (fileToClean, fileToCleanLen, "%s/.gnupg/pubring.kbx", tempHome);
534 : unlink (fileToClean);
535 : snprintf (fileToClean, fileToCleanLen, "%s/.gnupg", tempHome);
536 : rmdir (fileToClean);
537 : elektraFree (fileToClean);
538 :
539 : succeed_if (rmdir (tempHome) == 0, "Could not delete TMPHOME manually");
540 : #endif
541 189 : elektraFree (tempHome);
542 189 : tempHome = NULL;
543 189 : tempHomeLen = 0;
544 : }
545 189 : }
546 :
547 : #ifdef __cplusplus
548 : } // end extern "C"
549 : #endif
|