1 /*
2 * Check: a unit test framework for C
3 * Copyright (C) 2001, 2002 Arien Malec
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 * MA 02110-1301, USA.
19 */
20
21 #include "libcompat/libcompat.h"
22
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <math.h>
28
29 #include "internal-check.h"
30 #include "check_error.h"
31 #include "check_list.h"
32 #include "check_impl.h"
33 #include "check_msg.h"
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h> /* for _POSIX_VERSION */
37 #endif
38
39 #ifndef DEFAULT_TIMEOUT
40 #define DEFAULT_TIMEOUT 4
41 #endif
42
43 /*
44 * When a process exits either normally, with exit(), or
45 * by an uncaught signal, The lower 0x377 bits are passed
46 * to the parent. Of those, only the lower 8 bits are
47 * returned by the WEXITSTATUS() macro.
48 */
49 #define WEXITSTATUS_MASK 0xFF
50
51 int check_major_version = CHECK_MAJOR_VERSION;
52 int check_minor_version = CHECK_MINOR_VERSION;
53 int check_micro_version = CHECK_MICRO_VERSION;
54
55 const char *current_test_name = NULL;
56
57 static int non_pass (int val);
58 static Fixture *fixture_create (SFun fun, int ischecked);
59 static void tcase_add_fixture (TCase * tc, SFun setup, SFun teardown,
60 int ischecked);
61 static void tr_init (TestResult * tr);
62 static void suite_free (Suite * s);
63 static void tcase_free (TCase * tc);
64
65 Suite *
suite_create(const char * name)66 suite_create (const char *name)
67 {
68 Suite *s;
69
70 s = (Suite *) emalloc (sizeof (Suite)); /* freed in suite_free */
71 if (name == NULL)
72 s->name = "";
73 else
74 s->name = name;
75 s->tclst = check_list_create ();
76 return s;
77 }
78
79 int
suite_tcase(Suite * s,const char * tcname)80 suite_tcase (Suite * s, const char *tcname)
81 {
82 List *l;
83 TCase *tc;
84
85 if (s == NULL)
86 return 0;
87
88 l = s->tclst;
89 for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) {
90 tc = (TCase *) check_list_val (l);
91 if (strcmp (tcname, tc->name) == 0)
92 return 1;
93 }
94
95 return 0;
96 }
97
98 static void
suite_free(Suite * s)99 suite_free (Suite * s)
100 {
101 List *l;
102
103 if (s == NULL)
104 return;
105 l = s->tclst;
106 for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) {
107 tcase_free ((TCase *) check_list_val (l));
108 }
109 check_list_free (s->tclst);
110 free (s);
111 }
112
113
114 TCase *
tcase_create(const char * name)115 tcase_create (const char *name)
116 {
117 char *env;
118 double timeout_sec = DEFAULT_TIMEOUT;
119
120 TCase *tc = (TCase *) emalloc (sizeof (TCase)); /*freed in tcase_free */
121
122 if (name == NULL)
123 tc->name = "";
124 else
125 tc->name = name;
126
127 env = getenv ("CK_DEFAULT_TIMEOUT");
128 if (env != NULL) {
129 char *endptr = NULL;
130 double tmp = strtod (env, &endptr);
131
132 if (tmp >= 0 && endptr != env && (*endptr) == '\0') {
133 timeout_sec = tmp;
134 }
135 }
136
137 env = getenv ("CK_TIMEOUT_MULTIPLIER");
138 if (env != NULL) {
139 char *endptr = NULL;
140 double tmp = strtod (env, &endptr);
141
142 if (tmp >= 0 && endptr != env && (*endptr) == '\0') {
143 timeout_sec = timeout_sec * tmp;
144 }
145 }
146
147 tc->timeout.tv_sec = (time_t) floor (timeout_sec);
148 tc->timeout.tv_nsec =
149 (long) ((timeout_sec - floor (timeout_sec)) * (double) NANOS_PER_SECONDS);
150
151 tc->tflst = check_list_create ();
152 tc->unch_sflst = check_list_create ();
153 tc->ch_sflst = check_list_create ();
154 tc->unch_tflst = check_list_create ();
155 tc->ch_tflst = check_list_create ();
156 tc->tags = check_list_create ();
157
158 return tc;
159 }
160
161 /*
162 * Helper function to create a list of tags from
163 * a space separated string.
164 */
165 List *
tag_string_to_list(const char * tags_string)166 tag_string_to_list (const char *tags_string)
167 {
168 List *list;
169 char *tags;
170 char *tag;
171
172 list = check_list_create ();
173
174 if (NULL == tags_string) {
175 return list;
176 }
177
178 tags = strdup (tags_string);
179 tag = strtok (tags, " ");
180 while (tag) {
181 check_list_add_end (list, strdup (tag));
182 tag = strtok (NULL, " ");
183 }
184 free (tags);
185 return list;
186 }
187
188 void
tcase_set_tags(TCase * tc,const char * tags_orig)189 tcase_set_tags (TCase * tc, const char *tags_orig)
190 {
191 /* replace any pre-existing list */
192 if (tc->tags) {
193 check_list_apply (tc->tags, free);
194 check_list_free (tc->tags);
195 }
196 tc->tags = tag_string_to_list (tags_orig);
197 }
198
199 static void
tcase_free(TCase * tc)200 tcase_free (TCase * tc)
201 {
202 check_list_apply (tc->tflst, free);
203 check_list_apply (tc->unch_sflst, free);
204 check_list_apply (tc->ch_sflst, free);
205 check_list_apply (tc->unch_tflst, free);
206 check_list_apply (tc->ch_tflst, free);
207 check_list_apply (tc->tags, free);
208 check_list_free (tc->tflst);
209 check_list_free (tc->unch_sflst);
210 check_list_free (tc->ch_sflst);
211 check_list_free (tc->unch_tflst);
212 check_list_free (tc->ch_tflst);
213 check_list_free (tc->tags);
214 free (tc);
215 }
216
217 unsigned int
tcase_matching_tag(TCase * tc,List * check_for)218 tcase_matching_tag (TCase * tc, List * check_for)
219 {
220
221 if (NULL == check_for) {
222 return 0;
223 }
224
225 for (check_list_front (check_for); !check_list_at_end (check_for);
226 check_list_advance (check_for)) {
227 for (check_list_front (tc->tags); !check_list_at_end (tc->tags);
228 check_list_advance (tc->tags)) {
229 if (0 == strcmp ((const char *) check_list_val (tc->tags),
230 (const char *) check_list_val (check_for))) {
231 return 1;
232 }
233 }
234 }
235 return 0;
236 }
237
238 void
suite_add_tcase(Suite * s,TCase * tc)239 suite_add_tcase (Suite * s, TCase * tc)
240 {
241 if (s == NULL || tc == NULL || check_list_contains (s->tclst, tc)) {
242 return;
243 }
244
245 check_list_add_end (s->tclst, tc);
246 }
247
248 void
_tcase_add_test(TCase * tc,TFun fn,const char * name,int _signal,int allowed_exit_value,int start,int end)249 _tcase_add_test (TCase * tc, TFun fn, const char *name, int _signal,
250 int allowed_exit_value, int start, int end)
251 {
252 TF *tf;
253
254 if (tc == NULL || fn == NULL || name == NULL)
255 return;
256 tf = (TF *) emalloc (sizeof (TF)); /* freed in tcase_free */
257 tf->fn = fn;
258 tf->loop_start = start;
259 tf->loop_end = end;
260 tf->signal = _signal; /* 0 means no signal expected */
261 tf->allowed_exit_value = (WEXITSTATUS_MASK & allowed_exit_value); /* 0 is default successful exit */
262 tf->name = name;
263 check_list_add_end (tc->tflst, tf);
264 }
265
266 static Fixture *
fixture_create(SFun fun,int ischecked)267 fixture_create (SFun fun, int ischecked)
268 {
269 Fixture *f;
270
271 f = (Fixture *) emalloc (sizeof (Fixture));
272 f->fun = fun;
273 f->ischecked = ischecked;
274
275 return f;
276 }
277
278 void
tcase_add_unchecked_fixture(TCase * tc,SFun setup,SFun teardown)279 tcase_add_unchecked_fixture (TCase * tc, SFun setup, SFun teardown)
280 {
281 tcase_add_fixture (tc, setup, teardown, 0);
282 }
283
284 void
tcase_add_checked_fixture(TCase * tc,SFun setup,SFun teardown)285 tcase_add_checked_fixture (TCase * tc, SFun setup, SFun teardown)
286 {
287 tcase_add_fixture (tc, setup, teardown, 1);
288 }
289
290 static void
tcase_add_fixture(TCase * tc,SFun setup,SFun teardown,int ischecked)291 tcase_add_fixture (TCase * tc, SFun setup, SFun teardown, int ischecked)
292 {
293 if (setup) {
294 if (ischecked)
295 check_list_add_end (tc->ch_sflst, fixture_create (setup, ischecked));
296 else
297 check_list_add_end (tc->unch_sflst, fixture_create (setup, ischecked));
298 }
299
300 /* Add teardowns at front so they are run in reverse order. */
301 if (teardown) {
302 if (ischecked)
303 check_list_add_front (tc->ch_tflst, fixture_create (teardown, ischecked));
304 else
305 check_list_add_front (tc->unch_tflst,
306 fixture_create (teardown, ischecked));
307 }
308 }
309
310 void
tcase_set_timeout(TCase * tc,double timeout)311 tcase_set_timeout (TCase * tc, double timeout)
312 {
313 #if defined(HAVE_FORK)
314 if (timeout >= 0) {
315 char *env = getenv ("CK_TIMEOUT_MULTIPLIER");
316
317 if (env != NULL) {
318 char *endptr = NULL;
319 double tmp = strtod (env, &endptr);
320
321 if (tmp >= 0 && endptr != env && (*endptr) == '\0') {
322 timeout = timeout * tmp;
323 }
324 }
325
326 tc->timeout.tv_sec = (time_t) floor (timeout);
327 tc->timeout.tv_nsec =
328 (long) ((timeout - floor (timeout)) * (double) NANOS_PER_SECONDS);
329 }
330 #else
331 (void) tc;
332 (void) timeout;
333 eprintf
334 ("This version does not support timeouts, as fork is not supported",
335 __FILE__, __LINE__);
336 /* Ignoring, as Check is not compiled with fork support. */
337 #endif /* HAVE_FORK */
338 }
339
340 void
tcase_fn_start(const char * fname,const char * file,int line)341 tcase_fn_start (const char *fname, const char *file, int line)
342 {
343 send_ctx_info (CK_CTX_TEST);
344 send_loc_info (file, line);
345
346 current_test_name = fname;
347 }
348
349 const char *
tcase_name()350 tcase_name ()
351 {
352 return current_test_name;
353 }
354
355 void
_mark_point(const char * file,int line)356 _mark_point (const char *file, int line)
357 {
358 send_loc_info (file, line);
359 }
360
361 void
_ck_assert_failed(const char * file,int line,const char * expr,...)362 _ck_assert_failed (const char *file, int line, const char *expr, ...)
363 {
364 const char *msg;
365 va_list ap;
366 char buf[BUFSIZ];
367 const char *to_send;
368
369 send_loc_info (file, line);
370
371 va_start (ap, expr);
372 msg = (const char *) va_arg (ap, char *);
373
374 /*
375 * If a message was passed, format it with vsnprintf.
376 * Otherwise, print the expression as is.
377 */
378 if (msg != NULL) {
379 vsnprintf (buf, BUFSIZ, msg, ap);
380 to_send = buf;
381 } else {
382 to_send = expr;
383 }
384
385 va_end (ap);
386 send_failure_info (to_send);
387 if (cur_fork_status () == CK_FORK) {
388 #if defined(HAVE_FORK) && HAVE_FORK==1
389 _exit (1);
390 #endif /* HAVE_FORK */
391 } else {
392 longjmp (error_jmp_buffer, 1);
393 }
394 }
395
396 SRunner *
srunner_create(Suite * s)397 srunner_create (Suite * s)
398 {
399 SRunner *sr = (SRunner *) emalloc (sizeof (SRunner)); /* freed in srunner_free */
400
401 sr->slst = check_list_create ();
402 if (s != NULL)
403 check_list_add_end (sr->slst, s);
404 sr->stats = (TestStats *) emalloc (sizeof (TestStats)); /* freed in srunner_free */
405 sr->stats->n_checked = sr->stats->n_failed = sr->stats->n_errors = 0;
406 sr->resultlst = check_list_create ();
407 sr->log_fname = NULL;
408 sr->xml_fname = NULL;
409 sr->tap_fname = NULL;
410 sr->loglst = NULL;
411
412 #if defined(HAVE_FORK)
413 sr->fstat = CK_FORK_GETENV;
414 #else
415 /*
416 * Overriding the default of running tests in fork mode,
417 * as this system does not have fork()
418 */
419 sr->fstat = CK_NOFORK;
420 #endif /* HAVE_FORK */
421
422 return sr;
423 }
424
425 void
srunner_add_suite(SRunner * sr,Suite * s)426 srunner_add_suite (SRunner * sr, Suite * s)
427 {
428 if (s == NULL)
429 return;
430
431 check_list_add_end (sr->slst, s);
432 }
433
434 void
srunner_free(SRunner * sr)435 srunner_free (SRunner * sr)
436 {
437 List *l;
438 TestResult *tr;
439
440 if (sr == NULL)
441 return;
442
443 free (sr->stats);
444 l = sr->slst;
445 for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) {
446 suite_free ((Suite *) check_list_val (l));
447 }
448 check_list_free (sr->slst);
449
450 l = sr->resultlst;
451 for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) {
452 tr = (TestResult *) check_list_val (l);
453 tr_free (tr);
454 }
455 check_list_free (sr->resultlst);
456
457 free (sr);
458 }
459
460 int
srunner_ntests_failed(SRunner * sr)461 srunner_ntests_failed (SRunner * sr)
462 {
463 return sr->stats->n_failed + sr->stats->n_errors;
464 }
465
466 int
srunner_ntests_run(SRunner * sr)467 srunner_ntests_run (SRunner * sr)
468 {
469 return sr->stats->n_checked;
470 }
471
472 TestResult **
srunner_failures(SRunner * sr)473 srunner_failures (SRunner * sr)
474 {
475 int i = 0;
476 TestResult **trarray;
477 List *rlst;
478
479 trarray =
480 (TestResult **) emalloc (sizeof (trarray[0]) *
481 srunner_ntests_failed (sr));
482
483 rlst = sr->resultlst;
484 for (check_list_front (rlst); !check_list_at_end (rlst);
485 check_list_advance (rlst)) {
486 TestResult *tr = (TestResult *) check_list_val (rlst);
487
488 if (non_pass (tr->rtype))
489 trarray[i++] = tr;
490
491 }
492 return trarray;
493 }
494
495 TestResult **
srunner_results(SRunner * sr)496 srunner_results (SRunner * sr)
497 {
498 int i = 0;
499 TestResult **trarray;
500 List *rlst;
501
502 trarray =
503 (TestResult **) emalloc (sizeof (trarray[0]) * srunner_ntests_run (sr));
504
505 rlst = sr->resultlst;
506 for (check_list_front (rlst); !check_list_at_end (rlst);
507 check_list_advance (rlst)) {
508 trarray[i++] = (TestResult *) check_list_val (rlst);
509 }
510 return trarray;
511 }
512
513 static int
non_pass(int val)514 non_pass (int val)
515 {
516 return val != CK_PASS;
517 }
518
519 TestResult *
tr_create(void)520 tr_create (void)
521 {
522 TestResult *tr;
523
524 tr = (TestResult *) emalloc (sizeof (TestResult));
525 tr_init (tr);
526 return tr;
527 }
528
529 static void
tr_init(TestResult * tr)530 tr_init (TestResult * tr)
531 {
532 tr->ctx = CK_CTX_INVALID;
533 tr->line = -1;
534 tr->rtype = CK_TEST_RESULT_INVALID;
535 tr->msg = NULL;
536 tr->file = NULL;
537 tr->tcname = NULL;
538 tr->tname = NULL;
539 tr->duration = -1;
540 }
541
542 void
tr_free(TestResult * tr)543 tr_free (TestResult * tr)
544 {
545 free (tr->file);
546 free (tr->msg);
547 free (tr);
548 }
549
550
551 const char *
tr_msg(TestResult * tr)552 tr_msg (TestResult * tr)
553 {
554 return tr->msg;
555 }
556
557 int
tr_lno(TestResult * tr)558 tr_lno (TestResult * tr)
559 {
560 return tr->line;
561 }
562
563 const char *
tr_lfile(TestResult * tr)564 tr_lfile (TestResult * tr)
565 {
566 return tr->file;
567 }
568
569 int
tr_rtype(TestResult * tr)570 tr_rtype (TestResult * tr)
571 {
572 return tr->rtype;
573 }
574
575 enum ck_result_ctx
tr_ctx(TestResult * tr)576 tr_ctx (TestResult * tr)
577 {
578 return tr->ctx;
579 }
580
581 const char *
tr_tcname(TestResult * tr)582 tr_tcname (TestResult * tr)
583 {
584 return tr->tcname;
585 }
586
587 static enum fork_status _fstat = CK_FORK;
588
589 void
set_fork_status(enum fork_status fstat)590 set_fork_status (enum fork_status fstat)
591 {
592 if (fstat == CK_FORK || fstat == CK_NOFORK || fstat == CK_FORK_GETENV)
593 _fstat = fstat;
594 else
595 eprintf ("Bad status in set_fork_status", __FILE__, __LINE__);
596 }
597
598 enum fork_status
cur_fork_status(void)599 cur_fork_status (void)
600 {
601 return _fstat;
602 }
603
604 /**
605 * Not all systems support the same clockid_t's. This call checks
606 * if the CLOCK_MONOTONIC clockid_t is valid. If so, that is returned,
607 * otherwise, CLOCK_REALTIME is returned.
608 *
609 * The clockid_t that was found to work on the first call is
610 * cached for subsequent calls.
611 */
612 clockid_t
check_get_clockid()613 check_get_clockid ()
614 {
615 static clockid_t clockid = -1;
616
617 if (clockid == -1) {
618 /*
619 * Only check if we have librt available. Otherwise, the clockid
620 * will be ignored anyway, as the clock_gettime() and
621 * timer_create() functions will be re-implemented in libcompat.
622 * Worse, if librt and alarm() are unavailable, this check
623 * will result in an assert(0).
624 */
625 #if defined(HAVE_POSIX_TIMERS) && defined(HAVE_MONOTONIC_CLOCK)
626 timer_t timerid;
627
628 if (timer_create (CLOCK_MONOTONIC, NULL, &timerid) == 0) {
629 timer_delete (timerid);
630 clockid = CLOCK_MONOTONIC;
631 } else {
632 clockid = CLOCK_REALTIME;
633 }
634 #else
635 clockid = CLOCK_MONOTONIC;
636 #endif
637 }
638
639 return clockid;
640 }
641