• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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