3 /* cunit - A little C Unit facility */
5 /* Copyright (c) 2005 Martin Quinson. All rights reserved. */
7 /* This program is free software; you can redistribute it and/or modify it
8 * under the terms of the license (GNU LGPL) which comes with this package. */
10 /* This is partially inspirated from the OSSP ts (Test Suite Library) */
14 #include "xbt/sysdep.h" /* vasprintf */
15 #include "xbt/cunit.h"
16 #include "xbt/dynar.h"
18 /* collection of all suites */
19 static xbt_dynar_t _xbt_test_suites = NULL;
20 /* global statistics */
21 static int _xbt_test_nb_tests = 0;
22 static int _xbt_test_test_failed = 0;
23 static int _xbt_test_test_ignore = 0;
24 static int _xbt_test_test_expect = 0;
26 static int _xbt_test_nb_units = 0;
27 static int _xbt_test_unit_failed = 0;
28 static int _xbt_test_unit_ignore = 0;
29 static int _xbt_test_unit_disabled = 0;
31 static int _xbt_test_nb_suites = 0;
32 static int _xbt_test_suite_failed = 0;
33 static int _xbt_test_suite_ignore = 0;
34 static int _xbt_test_suite_disabled = 0;
37 xbt_test_unit_t _xbt_test_current_unit = NULL;
40 /* test suite test log */
41 typedef struct s_xbt_test_log {
47 static void xbt_test_log_dump(xbt_test_log_t log) {
49 fprintf(stderr," log %p(%s:%d)=%s\n",log,log->file,log->line,log->text);
51 fprintf(stderr," log=NULL\n");
54 /* test suite test check */
55 typedef struct s_xbt_test_test {
65 static void xbt_test_test_dump(xbt_test_test_t test){
69 fprintf(stderr," test %p(%s:%d)=%s (%s)\n",
70 test,test->file,test->line,test->title,
71 test->failed?"failed":"not failed");
72 xbt_dynar_foreach(test->logs,it_log,log)
73 xbt_test_log_dump(log);
76 fprintf(stderr," test=NULL\n");
79 /* test suite test unit */
80 struct s_xbt_test_unit {
87 xbt_dynar_t tests; /* of xbt_test_test_t*/
90 int test_failed,test_ignore,test_expect;
93 static void xbt_test_unit_dump(xbt_test_unit_t unit) {
97 fprintf(stderr," UNIT %s: %s (%s)\n",
98 unit->name,unit->title,
99 (unit->enabled?"enabled":"disabled"));
101 xbt_dynar_foreach(unit->tests,it_test,test)
102 xbt_test_test_dump(test);
104 fprintf(stderr," unit=NULL\n");
109 struct s_xbt_test_suite {
113 xbt_dynar_t units; /* of xbt_test_unit_t */
115 int nb_tests,nb_units;
116 int test_failed,test_ignore,test_expect;
117 int unit_failed,unit_ignore,unit_disabled;
120 /* destroy test suite */
121 static void xbt_test_suite_free(void *s) {
122 xbt_test_suite_t suite = *(xbt_test_suite_t*) s;
126 xbt_dynar_free(&suite->units);
131 static void xbt_test_unit_free(void *unit) {
132 xbt_test_unit_t u = *(xbt_test_unit_t*)unit;
135 xbt_dynar_free(&u->tests);
138 static void xbt_test_test_free(void *test) {
139 xbt_test_test_t t = *(xbt_test_test_t*)test;
141 xbt_dynar_free(&(t->logs));
144 static void xbt_test_log_free(void *log) {
145 xbt_test_log_t l= *(xbt_test_log_t*) log;
150 /** @brief create test suite */
151 xbt_test_suite_t xbt_test_suite_new(const char *name, const char *fmt, ...) {
152 xbt_test_suite_t suite = xbt_new0(struct s_xbt_test_suite,1);
156 if (!_xbt_test_suites)
157 _xbt_test_suites = xbt_dynar_new(sizeof(xbt_test_suite_t),xbt_test_suite_free);
160 vres = vasprintf(&suite->title,fmt, ap);
161 suite->units = xbt_dynar_new(sizeof(xbt_test_unit_t), &xbt_test_unit_free);
166 xbt_dynar_push(_xbt_test_suites,&suite);
171 /** @brief retrieve a testsuite from name, or create a new one */
172 xbt_test_suite_t xbt_test_suite_by_name(const char *name,const char *fmt, ...) {
173 xbt_test_suite_t suite;
174 unsigned int it_suite;
180 if (_xbt_test_suites)
181 xbt_dynar_foreach(_xbt_test_suites, it_suite, suite)
182 if (!strcmp(suite->name,name))
186 vres = vasprintf(&bufname,fmt, ap);
188 suite = xbt_test_suite_new(name,bufname,NULL);
194 void xbt_test_suite_dump(xbt_test_suite_t suite) {
196 xbt_test_unit_t unit;
197 unsigned int it_unit;
198 fprintf(stderr,"TESTSUITE %s: %s (%s)\n",
199 suite->name, suite->title,
200 suite->enabled?"enabled":"disabled");
202 xbt_dynar_foreach(suite->units,it_unit,unit)
203 xbt_test_unit_dump(unit);
205 fprintf(stderr,"TESTSUITE IS NULL!\n");
209 /* add test case to test suite */
210 void xbt_test_suite_push(xbt_test_suite_t suite, const char *name, ts_test_cb_t func, const char *fmt, ...) {
211 xbt_test_unit_t unit;
219 unit = xbt_new0(struct s_xbt_test_unit,1);
221 vres = vasprintf(&unit->title, fmt, ap);
223 unit->name = (char*)name;
228 unit->tests = xbt_dynar_new(sizeof(xbt_test_test_t), xbt_test_test_free);
230 xbt_dynar_push(suite->units, &unit);
234 /* run test one suite */
235 static int xbt_test_suite_run(xbt_test_suite_t suite) {
236 xbt_test_unit_t unit;
237 xbt_test_test_t test;
243 unsigned int it_unit,it_test,it_log;
245 int first=1; /* for result pretty printing */
251 /* suite title pretty-printing */
253 char suite_title[80];
254 int suite_len=strlen(suite->title);
257 xbt_assert2(suite_len<68,"suite title \"%s\" too long (%d should be less than 68",
258 suite->title,suite_len);
263 suite_title[i++]='\n';
264 suite_title[79]='\0';
266 sprintf(suite_title + 40 - (suite_len+4)/2, "[ %s ]", suite->title);
267 suite_title[40 + (suite_len+5)/2] = '=';
269 sprintf(suite_title+ 70," DISABLED ");
270 fprintf(stderr, "\n%s\n",suite_title);
273 if (suite->enabled) {
274 /* iterate through all tests */
275 xbt_dynar_foreach(suite->units, it_unit, unit) {
276 /* init unit case counters */
278 unit->test_ignore = 0;
279 unit->test_failed = 0;
280 unit->test_expect = 0;
282 /* display unit title */
283 vres=asprintf(&cp," Unit: %s ......................................"
284 "......................................", unit->title);
286 fprintf(stderr, "%s", cp);
289 /* run the test case function */
290 _xbt_test_current_unit = unit;
294 /* iterate through all performed tests to determine status */
295 xbt_dynar_foreach(unit->tests,it_test, test) {
301 if ( test->failed && !test->expected_failure) unit->test_failed++;
302 if (!test->failed && test->expected_failure) unit->test_failed++;
303 if (test->expected_failure)
309 /* Display whether this unit went well */
310 if (unit->test_failed > 0 || unit->test_expect) {
311 /* some tests failed (or were supposed to), so do detailed reporting of test case */
312 if (unit->test_failed > 0) {
313 fprintf(stderr, ".. failed\n");
314 } else if (unit->nb_tests) {
315 fprintf(stderr, "...... ok\n"); /* successful, but show about expected */
317 fprintf(stderr, ".... skip\n"); /* shouldn't happen, but I'm a bit lost with this logic */
319 xbt_dynar_foreach(unit->tests,it_test, test) {
320 file = (test->file != NULL ? test->file : unit->file);
321 line = (test->line != 0 ? test->line : unit->line);
322 fprintf(stderr, " %s: %s [%s:%d]\n",
323 (test->ignored?" SKIP":(test->expected_failure?(test->failed?"EFAIL":"EPASS"):
324 (test->failed?" FAIL":" PASS"))),
325 test->title, file, line);
327 if ( (test->expected_failure && !test->failed) || (!test->expected_failure && test->failed) ) {
328 xbt_dynar_foreach(test->logs,it_log,log) {
329 file = (log->file != NULL ? log->file : file);
330 line = (log->line != 0 ? log->line : line);
331 fprintf(stderr, " %s:%d: %s\n",
332 file, line,log->text);
337 fprintf(stderr, " Summary: %d of %d tests failed",unit->test_failed, unit->nb_tests);
338 if (unit->test_ignore) {
339 fprintf(stderr," (%d tests ignored)\n",unit->test_ignore);
341 fprintf(stderr,"\n");
344 } else if (!unit->enabled) {
345 fprintf(stderr, " disabled\n"); /* no test were run */
346 } else if (unit->nb_tests) {
347 fprintf(stderr, "...... ok\n"); /* successful */
349 fprintf(stderr, ".... skip\n"); /* no test were run */
352 /* Accumulate test counts into the suite */
353 suite->nb_tests += unit->nb_tests;
354 suite->test_failed += unit->test_failed;
355 suite->test_ignore += unit->test_ignore;
356 suite->test_expect += unit->test_expect;
358 _xbt_test_nb_tests += unit->nb_tests;
359 _xbt_test_test_failed += unit->test_failed;
360 _xbt_test_test_ignore += unit->test_ignore;
361 _xbt_test_test_expect += unit->test_expect;
363 /* What's the conclusion of this test anyway? */
364 if (unit->nb_tests) {
366 if (unit->test_failed)
367 suite->unit_failed++;
368 } else if (!unit->enabled) {
369 suite->unit_disabled++;
371 suite->unit_ignore++;
375 _xbt_test_nb_units += suite->nb_units;
376 _xbt_test_unit_failed += suite->unit_failed;
377 _xbt_test_unit_ignore += suite->unit_ignore;
378 _xbt_test_unit_disabled += suite->unit_disabled;
380 if (suite->nb_units) {
381 _xbt_test_nb_suites++;
382 if (suite->test_failed)
383 _xbt_test_suite_failed++;
384 } else if (!suite->enabled) {
385 _xbt_test_suite_disabled++;
387 _xbt_test_suite_ignore++;
391 /* print test suite summary */
392 if (suite->enabled) {
395 " =====================================================================%s\n",
396 (suite->nb_units ? (suite->unit_failed ? "== FAILED":"====== OK")
397 : (suite->unit_disabled ? " DISABLED":"==== SKIP")));
398 fprintf(stderr, " Summary: Units: %.0f%% ok (%d units: ",
399 suite->nb_units?((1-(double)suite->unit_failed/(double)suite->nb_units)*100.0):100.0,
402 if (suite->nb_units != suite->unit_failed) {
403 fprintf(stderr, "%s%d ok",(first?"":", "),suite->nb_units - suite->unit_failed);
406 if (suite->unit_failed) {
407 fprintf(stderr, "%s%d failed",(first?"":", "),suite->unit_failed);
410 if (suite->unit_ignore) {
411 fprintf(stderr, "%s%d ignored",(first?"":", "),suite->unit_ignore);
414 if (suite->unit_disabled) {
415 fprintf(stderr, "%s%d disabled",(first?"":", "),suite->unit_disabled);
418 fprintf(stderr,")\n Tests: %.0f%% ok (%d tests: ",
419 suite->nb_tests?((1-(double)suite->test_failed/(double)suite->nb_tests)*100.0):100.0,
423 if (suite->nb_tests != suite->test_failed) {
424 fprintf(stderr, "%s%d ok",(first?"":", "),suite->nb_tests - suite->test_failed);
427 if (suite->test_failed) {
428 fprintf(stderr, "%s%d failed",(first?"":", "),suite->test_failed);
431 if (suite->test_ignore) {
432 fprintf(stderr, "%s%d ignored",(first?"":"; "),suite->test_ignore);
435 if (suite->test_expect) {
436 fprintf(stderr, "%s%d expected to fail",(first?"":"; "),suite->test_expect);
439 fprintf(stderr,")\n");
441 return suite->unit_failed;
444 static void apply_selection(char *selection) {
445 /* for the parsing */
449 char dir[1024]; /* the directive */
451 unsigned int it_suite;
452 xbt_test_suite_t suite;
453 xbt_test_unit_t unit;
454 unsigned int it_unit;
459 if (!selection || selection[0] == '\0')
462 /*printf("Test selection: %s\n", selection);*/
464 /* First apply the selection */
470 strncpy(dir,sel,p-sel);
480 memmove(dir,dir+1,strlen(dir));
484 memmove(dir,dir+1,strlen(dir));
489 strcpy(unitname,p+1);
490 strncpy(suitename,dir,p-dir);
491 suitename[p-dir]='\0';
493 strcpy(suitename,dir);
496 /*fprintf(stderr,"Seen %s (%s; suite=%s; unit=%s)\n",
497 dir,enabling?"enabling":"disabling", suitename, unitname);*/
499 /* Deal with the specific case of 'all' pseudo serie */
500 if (!strcmp("all",suitename)) {
501 if (unitname[0]!='\0') {
502 fprintf(stderr,"The 'all' pseudo-suite does not accept any unit specification\n");
506 xbt_dynar_foreach(_xbt_test_suites,it_suite,suite) {
507 xbt_dynar_foreach(suite->units,it_unit,unit) {
508 unit->enabled = enabling;
510 suite->enabled = enabling;
514 for (it=0; it< xbt_dynar_length(_xbt_test_suites); it++) {
515 xbt_test_suite_t thissuite=xbt_dynar_get_as(_xbt_test_suites,it,xbt_test_suite_t);
516 if (!strcmp(suitename,thissuite->name)) {
517 /* Do not disable the whole suite when we just want to disable a child */
518 if (enabling || (unitname[0]=='\0'))
519 thissuite->enabled = enabling;
521 if (unitname[0]=='\0') {
522 xbt_dynar_foreach(thissuite->units,it_unit,unit){
523 unit->enabled = enabling;
525 } else { /* act on one child only */
526 unsigned int it2_unit;
527 /* search it, first (we won't reuse it for external loop which gets broken) */
528 for (it2_unit=0; it2_unit< xbt_dynar_length(thissuite->units); it2_unit++) {
529 xbt_test_unit_t thisunit=xbt_dynar_get_as(thissuite->units,it2_unit,xbt_test_unit_t);
530 if (!strcmp(thisunit->name,unitname)) {
531 thisunit->enabled = enabling;
534 } /* search relevant unit */
535 if (it2_unit==xbt_dynar_length(thissuite->units)) {
536 fprintf(stderr,"Suite '%s' has no unit of name '%s'. Cannot apply the selection\n",
540 } /* act on childs (either all or one) */
542 break;/* found the relevant serie. We are happy */
544 } /* search relevant series */
545 if (it==xbt_dynar_length(_xbt_test_suites)) {
546 fprintf(stderr,"No suite of name '%s' found. Cannot apply the selection\n",suitename);
554 void xbt_test_dump(char *selection) {
555 apply_selection(selection);
557 if (_xbt_test_suites) {
558 unsigned int it_suite;
559 xbt_test_suite_t suite;
561 xbt_dynar_foreach(_xbt_test_suites,it_suite,suite)
562 xbt_test_suite_dump(suite);
564 printf(" No suite defined.");
568 int xbt_test_run(char *selection) {
569 apply_selection(selection);
571 if (_xbt_test_suites) {
572 unsigned int it_suite;
573 xbt_test_suite_t suite;
576 /* Run all the suites */
577 xbt_dynar_foreach(_xbt_test_suites,it_suite,suite)
578 xbt_test_suite_run(suite);
580 /* Display some more statistics */
581 fprintf(stderr,"\n\n TOTAL: Suites: %.0f%% ok (%d suites: ",
583 ? ((1-(double)_xbt_test_suite_failed/(double)_xbt_test_nb_suites)*100.0)
585 _xbt_test_nb_suites);
586 if (_xbt_test_nb_suites != _xbt_test_suite_failed) {
587 fprintf(stderr, "%d ok",_xbt_test_nb_suites - _xbt_test_suite_failed);
590 if (_xbt_test_suite_failed) {
591 fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_suite_failed);
595 if (_xbt_test_suite_ignore) {
596 fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_suite_ignore);
599 fprintf(stderr,")\n Units: %.0f%% ok (%d units: ",
600 _xbt_test_nb_units?((1-(double)_xbt_test_unit_failed/(double)_xbt_test_nb_units)*100.0):100.0,
603 if (_xbt_test_nb_units != _xbt_test_unit_failed) {
604 fprintf(stderr, "%s%d ok",(first?"":", "),_xbt_test_nb_units - _xbt_test_unit_failed);
607 if (_xbt_test_unit_failed) {
608 fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_unit_failed);
611 if (_xbt_test_unit_ignore) {
612 fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_unit_ignore);
615 fprintf(stderr,")\n Tests: %.0f%% ok (%d tests: ",
616 _xbt_test_nb_tests?((1-(double)_xbt_test_test_failed/(double)_xbt_test_nb_tests)*100.0):100.0,
619 if (_xbt_test_nb_tests != _xbt_test_test_failed) {
620 fprintf(stderr, "%s%d ok",(first?"":", "),_xbt_test_nb_tests - _xbt_test_test_failed);
623 if (_xbt_test_test_failed) {
624 fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_test_failed);
627 if (_xbt_test_test_ignore) {
628 fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_test_ignore);
631 if (_xbt_test_test_expect) {
632 fprintf(stderr, "%s%d expected to fail",(first?"":", "),_xbt_test_test_expect);
635 fprintf(stderr,")\n");
637 fprintf(stderr,"No unit to run!\n");
638 _xbt_test_unit_failed++;
640 return _xbt_test_unit_failed;
642 void xbt_test_exit(void) {
643 xbt_dynar_free(&_xbt_test_suites);
646 /* annotate test case with test */
647 void _xbt_test_add(const char*file,int line, const char *fmt, ...) {
648 xbt_test_unit_t unit=_xbt_test_current_unit;
649 xbt_test_test_t test;
656 test = xbt_new0(struct s_xbt_test_test,1);
658 vres=vasprintf(&test->title, fmt, ap);
661 test->expected_failure = 0;
665 test->logs = xbt_dynar_new(sizeof(xbt_test_log_t),xbt_test_log_free);
666 xbt_dynar_push(unit->tests,&test);
670 /* annotate test case with log message and failure */
671 void _xbt_test_fail(const char*file,int line,const char *fmt, ...) {
672 xbt_test_unit_t unit = _xbt_test_current_unit;
673 xbt_test_test_t test;
681 xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
682 "Test failed even before being declared (broken unit: %s)",
685 log = xbt_new(struct s_xbt_test_log,1);
687 vres=vasprintf(&log->text,fmt, ap);
692 test = xbt_dynar_getlast_as(unit->tests, xbt_test_test_t);
693 xbt_dynar_push(test->logs, &log);
698 void xbt_test_exception(xbt_ex_t e) {
699 _xbt_test_fail(e.file,e.line,"Exception %s raised: %s",xbt_ex_catname(e.category),e.msg);
702 void xbt_test_expect_failure(void) {
703 xbt_test_test_t test;
704 xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
705 "Cannot expect the failure of a test before declaring it (broken unit: %s)",
706 _xbt_test_current_unit->title);
707 test = xbt_dynar_getlast_as(_xbt_test_current_unit->tests,xbt_test_test_t);
708 test->expected_failure = 1;
710 void xbt_test_skip(void) {
711 xbt_test_test_t test;
713 xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
714 "Test skiped even before being declared (broken unit: %s)",
715 _xbt_test_current_unit->title);
717 test = xbt_dynar_getlast_as(_xbt_test_current_unit->tests,
722 /* annotate test case with log message only */
723 void _xbt_test_log(const char*file,int line,const char *fmt, ...) {
724 xbt_test_unit_t unit=_xbt_test_current_unit;
725 xbt_test_test_t test;
733 xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
734 "Test logged into even before being declared (broken test unit: %s)",unit->title);
736 log = xbt_new(struct s_xbt_test_log,1);
738 vres=vasprintf(&log->text, fmt, ap);
743 test = xbt_dynar_getlast_as(unit->tests,xbt_test_test_t);
744 xbt_dynar_push(test->logs, &log);
752 XBT_TEST_SUITE("cunit","Testsuite mechanism autotest");
754 XBT_TEST_UNIT("expect",test_expected_failure,"expected failures") {
755 xbt_test_add0("Skipped test");
758 xbt_test_add2("%s %s","EXPECTED","FAILURE");
759 xbt_test_expect_failure();
760 xbt_test_log2("%s %s","Test","log");
761 xbt_test_fail0("EXPECTED FAILURE");
764 #endif /* SIMGRID_TEST */