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);
155 if (!_xbt_test_suites)
156 _xbt_test_suites = xbt_dynar_new(sizeof(xbt_test_suite_t),xbt_test_suite_free);
159 vasprintf(&suite->title,fmt, ap);
160 suite->units = xbt_dynar_new(sizeof(xbt_test_unit_t), &xbt_test_unit_free);
165 xbt_dynar_push(_xbt_test_suites,&suite);
170 /** @brief retrieve a testsuite from name, or create a new one */
171 xbt_test_suite_t xbt_test_suite_by_name(const char *name,const char *fmt, ...) {
172 xbt_test_suite_t suite;
173 unsigned int it_suite;
178 if (_xbt_test_suites)
179 xbt_dynar_foreach(_xbt_test_suites, it_suite, suite)
180 if (!strcmp(suite->name,name))
184 vasprintf(&bufname,fmt, ap);
186 suite = xbt_test_suite_new(name,bufname,NULL);
192 void xbt_test_suite_dump(xbt_test_suite_t suite) {
194 xbt_test_unit_t unit;
195 unsigned int it_unit;
196 fprintf(stderr,"TESTSUITE %s: %s (%s)\n",
197 suite->name, suite->title,
198 suite->enabled?"enabled":"disabled");
200 xbt_dynar_foreach(suite->units,it_unit,unit)
201 xbt_test_unit_dump(unit);
203 fprintf(stderr,"TESTSUITE IS NULL!\n");
207 /* add test case to test suite */
208 void xbt_test_suite_push(xbt_test_suite_t suite, const char *name, ts_test_cb_t func, const char *fmt, ...) {
209 xbt_test_unit_t unit;
216 unit = xbt_new0(struct s_xbt_test_unit,1);
218 vasprintf(&unit->title, fmt, ap);
220 unit->name = (char*)name;
225 unit->tests = xbt_dynar_new(sizeof(xbt_test_test_t), xbt_test_test_free);
227 xbt_dynar_push(suite->units, &unit);
231 /* run test one suite */
232 static int xbt_test_suite_run(xbt_test_suite_t suite) {
233 xbt_test_unit_t unit;
234 xbt_test_test_t test;
240 unsigned int it_unit,it_test,it_log;
242 int first=1; /* for result pretty printing */
247 /* suite title pretty-printing */
249 char suite_title[80];
250 int suite_len=strlen(suite->title);
253 xbt_assert2(suite_len<68,"suite title \"%s\" too long (%d should be less than 68",
254 suite->title,suite_len);
259 suite_title[i++]='\n';
260 suite_title[79]='\0';
262 sprintf(suite_title + 40 - (suite_len+4)/2, "[ %s ]", suite->title);
263 suite_title[40 + (suite_len+5)/2] = '=';
265 sprintf(suite_title+ 70," DISABLED ");
266 fprintf(stderr, "\n%s\n",suite_title);
269 if (suite->enabled) {
270 /* iterate through all tests */
271 xbt_dynar_foreach(suite->units, it_unit, unit) {
272 /* init unit case counters */
274 unit->test_ignore = 0;
275 unit->test_failed = 0;
276 unit->test_expect = 0;
278 /* display unit title */
279 asprintf(&cp," Unit: %s ......................................"
280 "......................................", unit->title);
282 fprintf(stderr, "%s", cp);
285 /* run the test case function */
286 _xbt_test_current_unit = unit;
290 /* iterate through all performed tests to determine status */
291 xbt_dynar_foreach(unit->tests,it_test, test) {
297 if ( test->failed && !test->expected_failure) unit->test_failed++;
298 if (!test->failed && test->expected_failure) unit->test_failed++;
299 if (test->expected_failure)
305 /* Display whether this unit went well */
306 if (unit->test_failed > 0 || unit->test_expect) {
307 /* some tests failed (or were supposed to), so do detailed reporting of test case */
308 if (unit->test_failed > 0) {
309 fprintf(stderr, ".. failed\n");
310 } else if (unit->nb_tests) {
311 fprintf(stderr, "...... ok\n"); /* successful, but show about expected */
313 fprintf(stderr, ".... skip\n"); /* shouldn't happen, but I'm a bit lost with this logic */
315 xbt_dynar_foreach(unit->tests,it_test, test) {
316 file = (test->file != NULL ? test->file : unit->file);
317 line = (test->line != 0 ? test->line : unit->line);
318 fprintf(stderr, " %s: %s [%s:%d]\n",
319 (test->ignored?" SKIP":(test->expected_failure?(test->failed?"EFAIL":"EPASS"):
320 (test->failed?" FAIL":" PASS"))),
321 test->title, file, line);
323 if ( (test->expected_failure && !test->failed) || (!test->expected_failure && test->failed) ) {
324 xbt_dynar_foreach(test->logs,it_log,log) {
325 file = (log->file != NULL ? log->file : file);
326 line = (log->line != 0 ? log->line : line);
327 fprintf(stderr, " %s:%d: %s\n",
328 file, line,log->text);
333 fprintf(stderr, " Summary: %d of %d tests failed",unit->test_failed, unit->nb_tests);
334 if (unit->test_ignore) {
335 fprintf(stderr," (%d tests ignored)\n",unit->test_ignore);
337 fprintf(stderr,"\n");
340 } else if (!unit->enabled) {
341 fprintf(stderr, " disabled\n"); /* no test were run */
342 } else if (unit->nb_tests) {
343 fprintf(stderr, "...... ok\n"); /* successful */
345 fprintf(stderr, ".... skip\n"); /* no test were run */
348 /* Accumulate test counts into the suite */
349 suite->nb_tests += unit->nb_tests;
350 suite->test_failed += unit->test_failed;
351 suite->test_ignore += unit->test_ignore;
352 suite->test_expect += unit->test_expect;
354 _xbt_test_nb_tests += unit->nb_tests;
355 _xbt_test_test_failed += unit->test_failed;
356 _xbt_test_test_ignore += unit->test_ignore;
357 _xbt_test_test_expect += unit->test_expect;
359 /* What's the conclusion of this test anyway? */
360 if (unit->nb_tests) {
362 if (unit->test_failed)
363 suite->unit_failed++;
364 } else if (!unit->enabled) {
365 suite->unit_disabled++;
367 suite->unit_ignore++;
371 _xbt_test_nb_units += suite->nb_units;
372 _xbt_test_unit_failed += suite->unit_failed;
373 _xbt_test_unit_ignore += suite->unit_ignore;
374 _xbt_test_unit_disabled += suite->unit_disabled;
376 if (suite->nb_units) {
377 _xbt_test_nb_suites++;
378 if (suite->test_failed)
379 _xbt_test_suite_failed++;
380 } else if (!suite->enabled) {
381 _xbt_test_suite_disabled++;
383 _xbt_test_suite_ignore++;
387 /* print test suite summary */
388 if (suite->enabled) {
391 " =====================================================================%s\n",
392 (suite->nb_units ? (suite->unit_failed ? "== FAILED":"====== OK")
393 : (suite->unit_disabled ? " DISABLED":"==== SKIP")));
394 fprintf(stderr, " Summary: Units: %.0f%% ok (%d units: ",
395 suite->nb_units?((1-(double)suite->unit_failed/(double)suite->nb_units)*100.0):100.0,
398 if (suite->nb_units != suite->unit_failed) {
399 fprintf(stderr, "%s%d ok",(first?"":", "),suite->nb_units - suite->unit_failed);
402 if (suite->unit_failed) {
403 fprintf(stderr, "%s%d failed",(first?"":", "),suite->unit_failed);
406 if (suite->unit_ignore) {
407 fprintf(stderr, "%s%d ignored",(first?"":", "),suite->unit_ignore);
410 if (suite->unit_disabled) {
411 fprintf(stderr, "%s%d disabled",(first?"":", "),suite->unit_disabled);
414 fprintf(stderr,")\n Tests: %.0f%% ok (%d tests: ",
415 suite->nb_tests?((1-(double)suite->test_failed/(double)suite->nb_tests)*100.0):100.0,
419 if (suite->nb_tests != suite->test_failed) {
420 fprintf(stderr, "%s%d ok",(first?"":", "),suite->nb_tests - suite->test_failed);
423 if (suite->test_failed) {
424 fprintf(stderr, "%s%d failed",(first?"":", "),suite->test_failed);
427 if (suite->test_ignore) {
428 fprintf(stderr, "%s%d ignored",(first?"":"; "),suite->test_ignore);
431 if (suite->test_expect) {
432 fprintf(stderr, "%s%d expected to fail",(first?"":"; "),suite->test_expect);
435 fprintf(stderr,")\n");
437 return suite->unit_failed;
440 static void apply_selection(char *selection) {
441 /* for the parsing */
445 char dir[1024]; /* the directive */
447 unsigned int it_suite;
448 xbt_test_suite_t suite;
449 xbt_test_unit_t unit;
450 unsigned int it_unit;
455 if (!selection || selection[0] == '\0')
458 /*printf("Test selection: %s\n", selection);*/
460 /* First apply the selection */
466 strncpy(dir,sel,p-sel);
476 memmove(dir,dir+1,strlen(dir));
480 memmove(dir,dir+1,strlen(dir));
485 strcpy(unitname,p+1);
486 strncpy(suitename,dir,p-dir);
487 suitename[p-dir]='\0';
489 strcpy(suitename,dir);
492 /*fprintf(stderr,"Seen %s (%s; suite=%s; unit=%s)\n",
493 dir,enabling?"enabling":"disabling", suitename, unitname);*/
495 /* Deal with the specific case of 'all' pseudo serie */
496 if (!strcmp("all",suitename)) {
497 if (unitname[0]!='\0') {
498 fprintf(stderr,"The 'all' pseudo-suite does not accept any unit specification\n");
502 xbt_dynar_foreach(_xbt_test_suites,it_suite,suite) {
503 xbt_dynar_foreach(suite->units,it_unit,unit) {
504 unit->enabled = enabling;
506 suite->enabled = enabling;
510 for (it=0; it< xbt_dynar_length(_xbt_test_suites); it++) {
511 xbt_test_suite_t thissuite=xbt_dynar_get_as(_xbt_test_suites,it,xbt_test_suite_t);
512 if (!strcmp(suitename,thissuite->name)) {
513 /* Do not disable the whole suite when we just want to disable a child */
514 if (enabling || (unitname[0]=='\0'))
515 thissuite->enabled = enabling;
517 if (unitname[0]=='\0') {
518 xbt_dynar_foreach(thissuite->units,it_unit,unit){
519 unit->enabled = enabling;
521 } else { /* act on one child only */
522 unsigned int it2_unit;
523 /* search it, first (we won't reuse it for external loop which gets broken) */
524 for (it2_unit=0; it2_unit< xbt_dynar_length(thissuite->units); it2_unit++) {
525 xbt_test_unit_t thisunit=xbt_dynar_get_as(thissuite->units,it2_unit,xbt_test_unit_t);
526 if (!strcmp(thisunit->name,unitname)) {
527 thisunit->enabled = enabling;
530 } /* search relevant unit */
531 if (it2_unit==xbt_dynar_length(thissuite->units)) {
532 fprintf(stderr,"Suite '%s' has no unit of name '%s'. Cannot apply the selection\n",
536 } /* act on childs (either all or one) */
538 break;/* found the relevant serie. We are happy */
540 } /* search relevant series */
541 if (it==xbt_dynar_length(_xbt_test_suites)) {
542 fprintf(stderr,"No suite of name '%s' found. Cannot apply the selection\n",suitename);
550 void xbt_test_dump(char *selection) {
551 apply_selection(selection);
553 if (_xbt_test_suites) {
554 unsigned int it_suite;
555 xbt_test_suite_t suite;
557 xbt_dynar_foreach(_xbt_test_suites,it_suite,suite)
558 xbt_test_suite_dump(suite);
560 printf(" No suite defined.");
564 int xbt_test_run(char *selection) {
565 apply_selection(selection);
567 if (_xbt_test_suites) {
568 unsigned int it_suite;
569 xbt_test_suite_t suite;
572 /* Run all the suites */
573 xbt_dynar_foreach(_xbt_test_suites,it_suite,suite)
574 xbt_test_suite_run(suite);
576 /* Display some more statistics */
577 fprintf(stderr,"\n\n TOTAL: Suites: %.0f%% ok (%d suites: ",
579 ? ((1-(double)_xbt_test_suite_failed/(double)_xbt_test_nb_suites)*100.0)
581 _xbt_test_nb_suites);
582 if (_xbt_test_nb_suites != _xbt_test_suite_failed) {
583 fprintf(stderr, "%d ok",_xbt_test_nb_suites - _xbt_test_suite_failed);
586 if (_xbt_test_suite_failed) {
587 fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_suite_failed);
591 if (_xbt_test_suite_ignore) {
592 fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_suite_ignore);
595 fprintf(stderr,")\n Units: %.0f%% ok (%d units: ",
596 _xbt_test_nb_units?((1-(double)_xbt_test_unit_failed/(double)_xbt_test_nb_units)*100.0):100.0,
599 if (_xbt_test_nb_units != _xbt_test_unit_failed) {
600 fprintf(stderr, "%s%d ok",(first?"":", "),_xbt_test_nb_units - _xbt_test_unit_failed);
603 if (_xbt_test_unit_failed) {
604 fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_unit_failed);
607 if (_xbt_test_unit_ignore) {
608 fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_unit_ignore);
611 fprintf(stderr,")\n Tests: %.0f%% ok (%d tests: ",
612 _xbt_test_nb_tests?((1-(double)_xbt_test_test_failed/(double)_xbt_test_nb_tests)*100.0):100.0,
615 if (_xbt_test_nb_tests != _xbt_test_test_failed) {
616 fprintf(stderr, "%s%d ok",(first?"":", "),_xbt_test_nb_tests - _xbt_test_test_failed);
619 if (_xbt_test_test_failed) {
620 fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_test_failed);
623 if (_xbt_test_test_ignore) {
624 fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_test_ignore);
627 if (_xbt_test_test_expect) {
628 fprintf(stderr, "%s%d expected to fail",(first?"":", "),_xbt_test_test_expect);
631 fprintf(stderr,")\n");
633 fprintf(stderr,"No unit to run!\n");
634 _xbt_test_unit_failed++;
636 return _xbt_test_unit_failed;
638 void xbt_test_exit(void) {
639 xbt_dynar_free(&_xbt_test_suites);
642 /* annotate test case with test */
643 void _xbt_test_add(const char*file,int line, const char *fmt, ...) {
644 xbt_test_unit_t unit=_xbt_test_current_unit;
645 xbt_test_test_t test;
651 test = xbt_new0(struct s_xbt_test_test,1);
653 vasprintf(&test->title, fmt, ap);
656 test->expected_failure = 0;
660 test->logs = xbt_dynar_new(sizeof(xbt_test_log_t),xbt_test_log_free);
661 xbt_dynar_push(unit->tests,&test);
665 /* annotate test case with log message and failure */
666 void _xbt_test_fail(const char*file,int line,const char *fmt, ...) {
667 xbt_test_unit_t unit = _xbt_test_current_unit;
668 xbt_test_test_t test;
675 xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
676 "Test failed even before being declared (broken unit: %s)",
679 log = xbt_new(struct s_xbt_test_log,1);
681 vasprintf(&log->text,fmt, ap);
686 test = xbt_dynar_getlast_as(unit->tests, xbt_test_test_t);
687 xbt_dynar_push(test->logs, &log);
692 void xbt_test_exception(xbt_ex_t e) {
693 _xbt_test_fail(e.file,e.line,"Exception %s raised: %s",xbt_ex_catname(e.category),e.msg);
696 void xbt_test_expect_failure(void) {
697 xbt_test_test_t test;
698 xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
699 "Cannot expect the failure of a test before declaring it (broken unit: %s)",
700 _xbt_test_current_unit->title);
701 test = xbt_dynar_getlast_as(_xbt_test_current_unit->tests,xbt_test_test_t);
702 test->expected_failure = 1;
704 void xbt_test_skip(void) {
705 xbt_test_test_t test;
707 xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
708 "Test skiped even before being declared (broken unit: %s)",
709 _xbt_test_current_unit->title);
711 test = xbt_dynar_getlast_as(_xbt_test_current_unit->tests,
716 /* annotate test case with log message only */
717 void _xbt_test_log(const char*file,int line,const char *fmt, ...) {
718 xbt_test_unit_t unit=_xbt_test_current_unit;
719 xbt_test_test_t test;
726 xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
727 "Test logged into even before being declared (broken test unit: %s)",unit->title);
729 log = xbt_new(struct s_xbt_test_log,1);
731 vasprintf(&log->text, fmt, ap);
736 test = xbt_dynar_getlast_as(unit->tests,xbt_test_test_t);
737 xbt_dynar_push(test->logs, &log);
745 XBT_TEST_SUITE("cunit","Testsuite mechanism autotest");
747 XBT_TEST_UNIT("expect",test_expected_failure,"expected failures") {
748 xbt_test_add0("Skipped test");
751 xbt_test_add2("%s %s","EXPECTED","FAILURE");
752 xbt_test_expect_failure();
753 xbt_test_log2("%s %s","Test","log");
754 xbt_test_fail0("EXPECTED FAILURE");
757 #endif /* SIMGRID_TEST */