test.c

00001 /* 
00002  * Copyright (c) 2005-2012 by KoanLogic s.r.l.
00003  */
00004 
00005 #include <sys/types.h>
00006 #include <sys/time.h>
00007 #include <signal.h>
00008 #include <stddef.h>
00009 #include <stdio.h>
00010 #include <stdarg.h>
00011 #include <strings.h>
00012 #include <time.h>
00013 
00014 #include <toolbox/carpal.h>
00015 #include <toolbox/misc.h>
00016 #include <toolbox/test.h>
00017 
00018 #if defined(HAVE_WAIT3) || defined(HAVE_WAIT)
00019   #include <sys/wait.h>
00020 #endif  /* HAVE_WAIT3 || HAVE_WAIT */
00021 #ifdef HAVE_STRUCT_RUSAGE
00022   #include <sys/resource.h>
00023 #endif  /* HAVE_STRUCT_RUSAGE */
00024 #if defined(HAVE_FORK)
00025   #define U_TEST_SANDBOX_ENABLED  1
00026 #endif  /* HAVE_FORK */
00027 #ifdef HAVE_UNAME
00028   #include <sys/utsname.h>
00029 #endif  /* HAVE_UNAME */
00030 
00031 static char g_interrupted = 0;
00032 static char g_debug = 0;
00033 static char g_verbose = 0;   /* If true, be chatty */
00034 #define CHAT(...)   \
00035     do { if (g_verbose) { printf("[CHAT] "); printf(__VA_ARGS__); } } while (0)
00036 
00037 #define U_TEST_CASE_PID_INITIALIZER   -1
00038 
00039 typedef enum { U_TEST_CASE_T, U_TEST_SUITE_T } u_test_what_t;
00040 
00041 /* Generic test objects list header. */
00042 typedef struct 
00043 { 
00044     unsigned int currank, nchildren;
00045     LIST_HEAD(, u_test_obj_s) objs;
00046 } TO;
00047 
00048 /* Test dependencies list header. */
00049 typedef LIST_HEAD(, u_test_dep_s) TD;
00050 
00051 /* A test suite/case dependency (identified by its label). */
00052 struct u_test_dep_s
00053 {
00054     char id[U_TEST_ID_MAX];           /* Tag of the declared dependency */
00055     LIST_ENTRY(u_test_dep_s) links;   /* Sibling deps */
00056     struct u_test_obj_s *upref;       /* Reference to the resolved dependency */
00057 };
00058 
00059 typedef struct u_test_dep_s u_test_dep_t;
00060 
00061 /* Synoptical test results view. */
00062 struct u_test_syn_s
00063 {
00064     unsigned int total, pass, fail, abrt, skip;
00065 };
00066 
00067 typedef struct u_test_syn_s u_test_syn_t;
00068 
00069 /* Generic test data container.  The attributes shared by both test suites and
00070  * cases are stored here.  The parent container (be it suite or case depends 
00071  * on '.what' attribute value) is accessed via the T[SC]_HANDLE() macro. */
00072 struct u_test_obj_s
00073 {
00074     u_test_what_t what;             /* Kind of test object (suite or case) */
00075     TO *parent;                     /* Head of the list we're attached to */
00076     char sequenced;                 /* True if sequenced */
00077     unsigned int rank;              /* Scheduling rank: low has higher prio */
00078     char id[U_TEST_ID_MAX];         /* Test object identifier: MUST be unique */
00079     int status;                     /* Test exit code */
00080     time_t start, stop;             /* Test case/suite begin/end timestamps */
00081     LIST_ENTRY(u_test_obj_s) links; /* Sibling test cases/suites */
00082     TD deps;                        /* Test cases/suites we depend on */
00083 };
00084 
00085 typedef struct u_test_obj_s u_test_obj_t;
00086 
00087 /* A test case. */
00088 struct u_test_case_s
00089 {
00090     int (*func) (struct u_test_case_s *);   /* The unit test function */
00091     u_test_obj_t o;                         /* Test case attributes */
00092     pid_t pid;                              /* Proc Id when exec'd in subproc */
00093 #ifdef HAVE_STRUCT_RUSAGE
00094     struct rusage stats;                    /* Resources returned by wait3 */
00095 #endif  /* HAVE_STRUCT_RUSAGE */
00096     struct u_test_suite_s *pts;             /* Parent test suite */
00097 };
00098 
00099 /* A test suite. */
00100 struct u_test_suite_s
00101 {
00102     TO u_test_cases;        /* Head of TCs list */
00103     u_test_obj_t o;         /* Test suite attributes */
00104     u_test_syn_t syn;       /* Test cases' synoptical */
00105     struct u_test_s *pt;    /* Parent test */
00106 };
00107 
00108 /* Test report functions. */
00109 struct u_test_rep_s
00110 {
00111     u_test_rep_f t_cb;
00112     u_test_suite_rep_f ts_cb;
00113     u_test_case_rep_f tc_cb;
00114 };
00115 
00116 /* A test. */
00117 struct u_test_s
00118 {
00119     unsigned int currank;       /* Current TS rank when sequencing */
00120     char id[U_TEST_ID_MAX];     /* Test id */
00121     TO u_test_suites;           /* Head of TSs list */
00122     char outfn[4096];           /* Output file name */
00123     time_t start, stop;         /* Test begin/end timestamps */
00124     char sandboxed;             /* True if TC's exec'd in subproc */
00125     unsigned int max_parallel;  /* Max # of test cases executed in parallel */
00126     u_test_syn_t syn;           /* Test suites' synoptical */
00127     char host[1024];            /* Host info */
00128 
00129     /* Test report hook functions */
00130     struct u_test_rep_s reporters;
00131 };
00132 
00133 /* Get test suite handle by its '.o(bject)' field. */
00134 #define TS_HANDLE(optr)    \
00135     (u_test_suite_t *) ((char *) optr - offsetof(u_test_suite_t, o))
00136 
00137 /* Get test case handle by its '.o(bject)' field. */
00138 #define TC_HANDLE(optr) \
00139     (u_test_case_t *) ((char *) optr - offsetof(u_test_case_t, o))
00140 
00141 /* Test objects routines. */
00142 static int u_test_obj_dep_add (u_test_dep_t *td, u_test_obj_t *to);
00143 static int u_test_obj_depends_on (const char *id, const char *depid, 
00144         TO *parent);
00145 static int u_test_obj_evict_id (TO *h, const char *id);
00146 static int u_test_obj_init (const char *id, u_test_what_t what, 
00147         u_test_obj_t *to);
00148 static int u_test_obj_scheduler (TO *h, int (*sched)(u_test_obj_t *));
00149 static int u_test_obj_sequencer (TO *h);
00150 static u_test_obj_t *u_test_obj_pick_top (TO *h);
00151 static u_test_obj_t *u_test_obj_search (TO *h, const char *id);
00152 static void u_test_obj_free (u_test_obj_t *to);
00153 static void u_test_obj_print (char nindent, u_test_obj_t *to);
00154 
00155 /* Test dependencies routines. */
00156 static int u_test_dep_new (const char *id, u_test_dep_t **ptd);
00157 static u_test_dep_t *u_test_dep_search (TD *h, const char *id);
00158 static char u_test_dep_dry (TD *h);
00159 static char u_test_dep_failed (TD *h);
00160 static void u_test_dep_free (u_test_dep_t *td);
00161 static void u_test_dep_print (char nindent, u_test_dep_t *td);
00162 
00163 /* Test case routines. */
00164 static void u_test_case_print (u_test_case_t *tc);
00165 static int u_test_case_dep_add (u_test_dep_t *td, u_test_case_t *tc);
00166 static int u_test_case_scheduler (u_test_obj_t *to);
00167 static int u_test_case_report_txt (FILE *fp, u_test_case_t *tc);
00168 static int u_test_case_report_xml (FILE *fp, u_test_case_t *tc);
00169 static int u_test_case_exec (u_test_case_t *tc);
00170 
00171 /* Test cases routines. */
00172 static int u_test_cases_reap (TO *h);
00173 static u_test_case_t *u_test_cases_search_by_pid (TO *h, pid_t pid);
00174 
00175 /* Test suite routines. */
00176 static int u_test_suite_scheduler (u_test_obj_t *to);
00177 static void u_test_suite_print (u_test_suite_t *ts);
00178 static int u_test_suite_dep_add (u_test_dep_t *td, u_test_suite_t *ts);
00179 static int u_test_suite_report_txt (FILE *fp, u_test_suite_t *ts, 
00180         u_test_rep_tag_t tag);
00181 static int u_test_suite_report_xml (FILE *fp, u_test_suite_t *ts, 
00182         u_test_rep_tag_t tag);
00183 static int u_test_suite_update_all_status (u_test_suite_t *ts, int status);
00184 
00185 /* Test routines. */
00186 static int u_test_sequencer (u_test_t *t);
00187 static int u_test_scheduler (u_test_t *t);
00188 static void u_test_print (u_test_t *t);
00189 static int u_test_getopt (int ac, char *av[], u_test_t *t);
00190 static void u_test_usage (const char *prog, const char *msg);
00191 static int u_test_set_outfmt (u_test_t *t, const char *fmt);
00192 static int u_test_reporter (u_test_t *t);
00193 static int u_test_report_txt (FILE *fp, u_test_t *t, u_test_rep_tag_t tag);
00194 static int u_test_report_xml (FILE *fp, u_test_t *t, u_test_rep_tag_t tag);
00195 static int u_test_uname (u_test_t *t);
00196 
00197 /* Test synoptical info routines. */
00198 static int u_test_syn_update (u_test_syn_t *syn, TO *h);
00199 
00200 /* Misc routines. */
00201 static const char *u_test_status_str (int status);
00202 static int u_test_init (u_test_t *t);
00203 static int u_test_signals (void);
00204 static void u_test_interrupted (int signo);
00205 static void u_test_bail_out (TO *h);
00206 static int u_test_obj_ts_fmt (u_test_obj_t *to, char b[80], char e[80], 
00207         char d[80]);
00208 #ifdef HAVE_STRUCT_RUSAGE
00209 static int u_test_case_rusage_fmt (u_test_case_t *tc, char uti[80], 
00210         char sti[80]);
00211 static int __timeval_fmt (struct timeval *tv, char t[80]);
00212 #endif  /* HAVE_STRUCT_RUSAGE */
00213 
00214 /* Pre-cooked reporters. */
00215 struct u_test_rep_s xml_reps = {
00216     u_test_report_xml,
00217     u_test_suite_report_xml,
00218     u_test_case_report_xml
00219 };
00220 
00221 struct u_test_rep_s txt_reps = {
00222     u_test_report_txt,
00223     u_test_suite_report_txt,
00224     u_test_case_report_txt
00225 };
00226 
00319 int u_test_run (int ac, char *av[], u_test_t *t)
00320 {
00321     con_err_if (u_test_init(t));
00322     con_err_if (u_test_getopt(ac, av, t));
00323     con_err_if (u_test_sequencer(t));
00324 
00325     /* Take a look at dependencies after sequencing. */
00326     if (g_debug)
00327         u_test_print(t);  
00328 
00329     con_err_if (u_test_scheduler(t));
00330     con_err_if (u_test_reporter(t));
00331 
00332     return (t->syn.total == t->syn.pass) ? 0 : ~0;
00333 err:
00334     return ~0;
00335 }
00336 
00348 int u_test_suite_dep_register (const char *id, u_test_suite_t *ts)
00349 {
00350     u_test_dep_t *td = NULL;
00351 
00352     dbg_err_if (u_test_dep_new(id, &td));
00353     dbg_err_if (u_test_suite_dep_add(td, ts));
00354 
00355     return 0;
00356 err:
00357     u_test_dep_free(td);
00358     return ~0;
00359 }
00360 
00376 int u_test_case_depends_on (const char *tcid, const char *depid, 
00377         u_test_suite_t *ts)
00378 {
00379     return u_test_obj_depends_on(tcid, depid, &ts->u_test_cases);
00380 }
00381 
00397 int u_test_suite_depends_on (const char *tsid, const char *depid, u_test_t *t)
00398 {
00399     return u_test_obj_depends_on(tsid, depid, &t->u_test_suites);
00400 }
00401 
00413 int u_test_case_dep_register (const char *id, u_test_case_t *tc)
00414 {
00415     u_test_dep_t *td = NULL;
00416 
00417     dbg_err_if (u_test_dep_new(id, &td));
00418     dbg_err_if (u_test_case_dep_add(td, tc));
00419 
00420     return 0;
00421 err:
00422     u_test_dep_free(td);
00423     return ~0;
00424 }
00425 
00442 int u_test_case_register (const char *id, u_test_f func, u_test_suite_t *ts)
00443 {
00444     u_test_case_t *tc = NULL;
00445 
00446     dbg_err_if (u_test_case_new(id, func, &tc));
00447     dbg_err_if (u_test_case_add(tc, ts));
00448 
00449     return 0;
00450 err:
00451     u_test_case_free(tc);
00452     return ~0;
00453 }
00454 
00467 int u_test_case_printf (u_test_case_t *tc, const char *fmt, ...)
00468 {
00469     int rc;
00470     va_list ap;
00471 
00472     dbg_return_if (tc == NULL, -1);
00473 
00474     (void) printf("{%s} ", tc->o.id);
00475 
00476     va_start(ap, fmt);
00477     rc = vprintf(fmt, ap);
00478     va_end(ap);
00479 
00480     if (rc > 0)
00481         (void) printf("\n");
00482 
00483     return rc;
00484 }
00485 
00498 int u_test_suite_new (const char *id, u_test_suite_t **pts)
00499 {
00500     u_test_suite_t *ts = NULL;
00501 
00502     dbg_return_if (id == NULL, ~0);
00503     dbg_return_if (pts == NULL, ~0);
00504 
00505     dbg_err_sif ((ts = u_malloc(sizeof *ts)) == NULL);
00506     LIST_INIT(&ts->u_test_cases.objs);
00507     ts->u_test_cases.currank = 0;
00508     memset(&ts->syn, 0, sizeof ts->syn);
00509 
00510     dbg_err_if (u_test_obj_init(id, U_TEST_SUITE_T, &ts->o));
00511 
00512     *pts = ts;
00513 
00514     return 0;
00515 err:
00516     u_test_suite_free(ts);
00517     return ~0;
00518 }
00519 
00530 void u_test_suite_free (u_test_suite_t *ts)
00531 {
00532     u_test_case_t *tc;
00533     u_test_obj_t *to, *nto;
00534 
00535     if (ts == NULL)
00536         return;
00537 
00538     /* Free dependencies. */
00539     u_test_obj_free(&ts->o);
00540 
00541     /* Free test cases. */
00542     LIST_FOREACH_SAFE (to, &ts->u_test_cases.objs, links, nto)
00543     {
00544         tc = TC_HANDLE(to);
00545         u_test_case_free(tc);
00546     }
00547 
00548     u_free(ts);
00549 
00550     return;
00551 }
00552 
00563 void u_test_free (u_test_t *t)
00564 {
00565     u_test_suite_t *ts;
00566     u_test_obj_t *to, *nto;
00567 
00568     if (t == NULL)
00569         return;
00570 
00571     /* Free all children test suites. */
00572     LIST_FOREACH_SAFE (to, &t->u_test_suites.objs, links, nto)
00573     {
00574         ts = TS_HANDLE(to);
00575         u_test_suite_free(ts);
00576     } 
00577 
00578     /* Free the container. */
00579     u_free(t);
00580 
00581     return;
00582 }
00583 
00596 int u_test_set_u_test_rep (u_test_t *t, u_test_rep_f func)
00597 {
00598     dbg_return_if (t == NULL, ~0);
00599     dbg_return_if (func == NULL, ~0);
00600 
00601     t->reporters.t_cb = func;
00602 
00603     return 0;
00604 }
00605 
00618 int u_test_set_u_test_case_rep (u_test_t *t, u_test_case_rep_f func)
00619 {
00620     dbg_return_if (t == NULL, ~0);
00621     dbg_return_if (func == NULL, ~0);
00622 
00623     t->reporters.tc_cb = func;
00624 
00625     return 0;
00626 }
00627 
00640 int u_test_set_u_test_suite_rep (u_test_t *t, u_test_suite_rep_f func)
00641 {
00642     dbg_return_if (t == NULL, ~0);
00643     dbg_return_if (func == NULL, ~0);
00644 
00645     t->reporters.ts_cb = func;
00646 
00647     return 0;
00648 }
00649 
00661 int u_test_set_outfn (u_test_t *t, const char *outfn)
00662 {
00663     dbg_return_if (t == NULL, ~0);
00664     dbg_return_if (outfn == NULL, ~0);
00665 
00666     dbg_return_if (u_strlcpy(t->outfn, outfn, sizeof t->outfn), ~0);
00667 
00668     return 0;
00669 }
00670 
00683 int u_test_new (const char *id, u_test_t **pt)
00684 {
00685     u_test_t *t = NULL;
00686 
00687     dbg_return_if (pt == NULL, ~0);
00688 
00689     dbg_err_sif ((t = u_malloc(sizeof *t)) == NULL);
00690 
00691     LIST_INIT(&t->u_test_suites.objs);
00692     t->u_test_suites.currank = 0;
00693     dbg_err_if (u_strlcpy(t->id, id, sizeof t->id));
00694     t->currank = 0;
00695     t->start = t->stop = -1;
00696     (void) u_strlcpy(t->outfn, U_TEST_OUTFN_DFL, sizeof t->outfn);
00697 #ifdef U_TEST_SANDBOX_ENABLED
00698     /* The default is to spawn sub-processes to execute test cases - 
00699      * on systems where the fork(2) syscall is available. */
00700     t->sandboxed = 1;   
00701 #else   /* !U_TEST_SANDBOX_ENABLED */
00702     t->sandboxed = 0;
00703 #endif  /* U_TEST_SANDBOX_ENABLED */
00704     t->max_parallel = U_TEST_MAX_PARALLEL;
00705 
00706     /* Set default report routines (may be overwritten). */
00707     t->reporters = txt_reps;
00708 
00709     /* Zeroize synoptical test info. */
00710     memset(&t->syn, 0, sizeof t->syn);  
00711 
00712     *pt = t;
00713 
00714     return 0;
00715 err:
00716     u_free(t);
00717     return ~0;
00718 }
00719 
00730 void u_test_case_free (u_test_case_t *tc)
00731 {
00732     if (tc == NULL)
00733         return;
00734 
00735     /* Free the attached dependencies. */
00736     u_test_obj_free(&tc->o);
00737 
00738     /* Free the container. */
00739     u_free(tc);
00740 
00741     return;
00742 }
00743 
00758 int u_test_case_new (const char *id, u_test_f func, u_test_case_t **ptc)
00759 {
00760     u_test_case_t *tc = NULL;
00761 
00762     dbg_return_if (id == NULL, ~0);
00763     dbg_return_if (ptc == NULL, ~0);
00764 
00765     dbg_err_sif ((tc = u_malloc(sizeof *tc)) == NULL);
00766     dbg_err_if (u_test_obj_init(id, U_TEST_CASE_T, &tc->o));
00767 
00768     tc->func = func;
00769     tc->pid = U_TEST_CASE_PID_INITIALIZER;
00770 #ifdef HAVE_STRUCT_RUSAGE
00771     memset(&tc->stats, 0, sizeof tc->stats);
00772 #endif  /* HAVE_STRUCT_RUSAGE */
00773 
00774     *ptc = tc;
00775 
00776     return 0;
00777 err:
00778     u_test_case_free(tc);
00779     return ~0;
00780 }
00781 
00793 int u_test_case_add (u_test_case_t *tc, u_test_suite_t *ts)
00794 {
00795     dbg_return_if (tc == NULL, ~0);
00796     dbg_return_if (ts == NULL, ~0);
00797 
00798     LIST_INSERT_HEAD(&ts->u_test_cases.objs, &tc->o, links);
00799     tc->o.parent = &ts->u_test_cases;
00800     tc->pts = ts;
00801 
00802     return 0;
00803 }
00804 
00816 int u_test_suite_add (u_test_suite_t *ts, u_test_t *t)
00817 {
00818     dbg_return_if (t == NULL, ~0);
00819     dbg_return_if (ts == NULL, ~0);
00820 
00821     LIST_INSERT_HEAD(&t->u_test_suites.objs, &ts->o, links);
00822     ts->o.parent = &t->u_test_suites;
00823     ts->pt = t;
00824 
00825     return 0;
00826 }
00827 
00839 void u_test_print (u_test_t *t)
00840 {
00841     u_test_obj_t *to;
00842 
00843     dbg_return_if (t == NULL, );
00844 
00845     u_con("[test] %s", t->id);  
00846 
00847     LIST_FOREACH (to, &t->u_test_suites.objs, links)
00848         u_test_suite_print(TS_HANDLE(to));
00849 
00850     return;
00851 }
00852 
00857 static void u_test_suite_print (u_test_suite_t *ts)
00858 {
00859     u_test_dep_t *td;
00860     u_test_obj_t *to;
00861 
00862     dbg_return_if (ts == NULL, );
00863 
00864     u_test_obj_print(4, &ts->o);
00865 
00866     LIST_FOREACH (td, &ts->o.deps, links)
00867         u_test_dep_print(4, td);
00868 
00869     LIST_FOREACH (to, &ts->u_test_cases.objs, links)
00870         u_test_case_print(TC_HANDLE(to));
00871 
00872     return;
00873 }
00874 
00875 static void u_test_case_print (u_test_case_t *tc)
00876 {
00877     u_test_dep_t *td;
00878 
00879     dbg_return_if (tc == NULL, );
00880 
00881     u_test_obj_print(8, &tc->o);
00882 
00883     LIST_FOREACH (td, &tc->o.deps, links)
00884         u_test_dep_print(8, td);
00885 
00886     return;
00887 }
00888 
00889 static void u_test_obj_print (char nindent, u_test_obj_t *to)
00890 {
00891     dbg_return_if (to == NULL, );
00892 
00893     u_con("%*c=> [%s] %s", 
00894             nindent, ' ', to->what == U_TEST_CASE_T ? "case" : "suite", to->id);
00895 
00896     /* attributes */
00897     u_con("%*c    .rank = %u", nindent, ' ', to->rank);  
00898     u_con("%*c    .seq = %s", nindent, ' ', to->sequenced ? "true" : "false");
00899 
00900     return;
00901 }
00902 
00903 static void u_test_dep_print (char nindent, u_test_dep_t *td)
00904 {
00905     u_con("%*c    .<dep> = %s", nindent, ' ', td->id);
00906     return;
00907 }
00908 
00909 static int u_test_dep_new (const char *id, u_test_dep_t **ptd)
00910 {
00911     u_test_dep_t *td = NULL;
00912 
00913     dbg_return_if (id == NULL, ~0);
00914     dbg_return_if (ptd == NULL, ~0);
00915 
00916     dbg_err_sif ((td = u_malloc(sizeof *td)) == NULL);
00917     dbg_err_if (u_strlcpy(td->id, id, sizeof td->id));
00918     td->upref = NULL;   /* When non-NULL it is assumed as resolved. */
00919 
00920     *ptd = td;
00921 
00922     return 0;
00923 err:
00924     u_test_dep_free(td);
00925     return ~0;
00926 }
00927 
00928 static void u_test_dep_free (u_test_dep_t *td)
00929 {
00930     if (td)
00931         u_free(td);
00932 
00933     return;
00934 }
00935 
00936 static u_test_dep_t *u_test_dep_search (TD *h, const char *id)
00937 {
00938     u_test_dep_t *td;
00939 
00940     dbg_return_if (h == NULL, NULL);
00941     dbg_return_if (id == NULL, NULL);
00942 
00943     LIST_FOREACH (td, h, links)
00944     {
00945         if (!strcmp(td->id, id))
00946             return td;
00947     }
00948 
00949     return NULL;
00950 }
00951 
00952 static u_test_obj_t *u_test_obj_search (TO *h, const char *id)
00953 {
00954     u_test_obj_t *to;
00955 
00956     dbg_return_if (h == NULL, NULL);
00957     dbg_return_if (id == NULL, NULL);
00958 
00959     LIST_FOREACH (to, &h->objs, links)
00960     {
00961         if (!strcmp(to->id, id))
00962             return to;
00963     }
00964 
00965     return NULL;
00966 }
00967 
00968 static int u_test_suite_dep_add (u_test_dep_t *td, u_test_suite_t *ts)
00969 {
00970     return u_test_obj_dep_add(td, &ts->o);
00971 }
00972 
00973 static int u_test_case_dep_add (u_test_dep_t *td, u_test_case_t *tc)
00974 {
00975     return u_test_obj_dep_add(td, &tc->o);
00976 }
00977 
00978 /* It MUST be called AFTER the test case/suite on which it is established 
00979  * has been added. */
00980 static int u_test_obj_dep_add (u_test_dep_t *td, u_test_obj_t *to)
00981 {
00982     dbg_return_if (to == NULL, ~0);
00983     dbg_return_if (td == NULL, ~0);
00984 
00985     /* See if the dependency isn't already recorded, in which case
00986      * insert it into the dependency list for this test case/suite. */
00987     if (u_test_dep_search(&to->deps, to->id) == NULL)
00988     {
00989         LIST_INSERT_HEAD(&to->deps, td, links);
00990     }
00991     else
00992         u_free(td);
00993 
00994     return 0;
00995 }
00996 
00997 static void u_test_obj_free (u_test_obj_t *to)
00998 {
00999     u_test_dep_t *td, *ntd;
01000 
01001     /* Just free the attached deps list: expect 'to' being a pointer to a
01002      * test object in a bigger malloc'd chunk (suite or case) which will be
01003      * later free'd by its legitimate owner. */
01004     LIST_FOREACH_SAFE (td, &to->deps, links, ntd)
01005         u_test_dep_free(td);
01006 
01007     return;
01008 }
01009 
01010 static char u_test_dep_dry (TD *h)
01011 {
01012     u_test_dep_t *td;
01013 
01014     dbg_return_if (h == NULL, 1);
01015 
01016     LIST_FOREACH (td, h, links)
01017     {
01018         /* Search for any non-resolved dependency. */
01019         if (td->upref == NULL)
01020             return 0;
01021     }
01022 
01023     /* We get here also when the dependency list is empty. */
01024     return 1;
01025 }
01026 
01027 static char u_test_dep_failed (TD *h)
01028 {
01029     u_test_dep_t *td;
01030 
01031     dbg_return_if (h == NULL, 0);
01032 
01033     LIST_FOREACH (td, h, links)
01034     {
01035         if (td->upref && td->upref->status != U_TEST_SUCCESS)
01036             return 1;
01037     }
01038 
01039     return 0;
01040 }
01041 
01042 static u_test_obj_t *u_test_obj_pick_top (TO *h)
01043 {
01044     /* Top element is a (not already sequenced) test object with no deps. */
01045     u_test_obj_t *to;
01046 
01047     LIST_FOREACH (to, &h->objs, links)
01048     {
01049         if (!to->sequenced && u_test_dep_dry(&to->deps))
01050         {
01051             /* Record the reached depth: it will be used by the
01052              * next evicted element. */
01053             to->parent->currank = U_MAX(to->rank, to->parent->currank);
01054             return to;
01055         }
01056     }
01057 
01058     return NULL;
01059 }
01060 
01061 /* Do a (variation on) topological sorting over the partially ordered sets
01062  * of test suites/cases.  We are primarily concerned in equally ranking all
01063  * test objects which are at the same "dependency-depth" so that the scheduler
01064  * can exec tests in the same rank-pool in parallel.  We are also interested 
01065  * in creating a partial order for cases where the scheduler is forced to 
01066  * run sequentially (i.e. no system fork(2) or by explicit user request).
01067  * In this respect, the ranking algorithm creates a partition of the 
01068  * case/suite's sets into equivalency classes from where any element can be 
01069  * picked at will (until EC exhaustion) to create a suitable sequence.
01070  * NOTE that higher ranks mean lower priority */
01071 static int u_test_obj_sequencer (TO *h)
01072 {
01073     u_test_obj_t *to, *fto;
01074     u_test_suite_t *ts;
01075 
01076     dbg_return_if (h == NULL, ~0);
01077 
01078     /* Pick top test cases/suites one by one until they are all sequenced.*/
01079     while ((to = u_test_obj_pick_top(h)) != NULL)
01080     {
01081         dbg_err_if (u_test_obj_evict_id(h, to->id));
01082     }
01083 
01084     /* Test if we got out because of a cycle in deps, i.e. check if we 
01085      * still have any non-sequenced test case/suite. */
01086     LIST_FOREACH (to, &h->objs, links)
01087     {
01088         dbg_err_ifm (!to->sequenced, 
01089                 "%s not sequenced: dependency loop !", to->id);
01090     }
01091 
01092     /* A test suite need to recur into its test cases. */
01093     if ((fto = LIST_FIRST(&h->objs)) && fto->what == U_TEST_SUITE_T)
01094     {
01095         LIST_FOREACH (to, &h->objs, links)
01096         {
01097             ts = TS_HANDLE(to);
01098             dbg_err_if (u_test_obj_sequencer(&ts->u_test_cases));
01099         }
01100     }
01101 
01102     return 0;
01103 err:
01104     return ~0;
01105 }
01106 
01107 static int u_test_obj_evict_id (TO *h, const char *id)
01108 {
01109     u_test_obj_t *to;
01110     u_test_dep_t *td;
01111 
01112     dbg_return_if (id == NULL, ~0); 
01113 
01114     /* Delete from the deps list of every test suite, all the records
01115      * matching the given id. */
01116     LIST_FOREACH (to, &h->objs, links)
01117     {
01118         LIST_FOREACH (td, &to->deps, links)
01119         {
01120             if (!strcmp(td->id, id))
01121             {
01122                 /* Set the rank of the evicted object to the actual
01123                  * depth +1 in the sequencing array */
01124                 to->rank = to->parent->currank + 1;
01125                 /* Link the dependency to its resolved test object. */
01126                 td->upref = u_test_obj_search(to->parent, id);
01127                 break;
01128             }
01129         }
01130 
01131         /* Eviction consists in asserting the '.sequenced' attribute of
01132          * the chosen test object. */
01133         if (!strcmp(to->id, id))
01134             to->sequenced = 1;
01135     }
01136 
01137     return 0;
01138 }
01139 
01140 static int u_test_sequencer (u_test_t *t)
01141 {
01142     dbg_return_if (t == NULL, ~0);
01143 
01144     return u_test_obj_sequencer(&t->u_test_suites);
01145 }
01146 
01147 static int u_test_obj_init (const char *id, u_test_what_t what, 
01148         u_test_obj_t *to)
01149 {
01150     dbg_return_if (id == NULL, ~0);
01151     dbg_return_if (to == NULL, ~0);
01152 
01153     LIST_INIT(&to->deps);
01154     to->what = what;
01155     to->parent = NULL;
01156     to->sequenced = 0;
01157     to->rank = 0;
01158     to->status = U_TEST_SUCCESS;
01159     to->start = to->stop = -1;
01160     dbg_err_if (u_strlcpy(to->id, id, sizeof to->id)); 
01161 
01162     return 0;
01163 err:
01164     return ~0;
01165 }
01166 
01167 static int u_test_obj_depends_on (const char *id, const char *depid, TO *parent)
01168 {
01169     u_test_obj_t *to;
01170     u_test_dep_t *td = NULL;
01171 
01172     /* The object for which we are adding the dependency must be already
01173      * in place. */
01174     dbg_err_if ((to = u_test_obj_search(parent, id)) == NULL);
01175 
01176     /* Check if the same dependency is already recorded. */
01177     if ((td = u_test_dep_search(&to->deps, depid)))
01178         return 0;
01179 
01180     /* In case it is a new dependency, create and stick it to the object
01181      * deps list. */
01182     dbg_err_if (u_test_dep_new(depid, &td));
01183     dbg_err_if (u_test_obj_dep_add(td, to));
01184 
01185     return 0;
01186 err:
01187     u_test_dep_free(td);
01188     return ~0;
01189 }
01190 
01191 static int u_test_obj_scheduler (TO *h, int (*sched)(u_test_obj_t *))
01192 {
01193     char simple_sched = 0;
01194     unsigned int r, parallel = 0;
01195     u_test_obj_t *to, *fto, *tptr;
01196     u_test_case_t *ftc;
01197 
01198     dbg_return_if (h == NULL, ~0);
01199     dbg_return_if (sched == NULL, ~0);
01200 
01201     /* Pop the first element in list which will be used to reconnect to its 
01202      * upper layer relatives (i.e. test suite & test). */
01203     if ((fto = LIST_FIRST(&h->objs)) == NULL)
01204         return 0;   /* List empty, go out. */
01205 
01206     /* Initialize the spawned children (i.e. test cases) counter to 0.
01207      * It will be incremented on each u_test_case_exec() invocation, when 
01208      * in sandboxed mode. */
01209     h->nchildren = 0;
01210 
01211     /* See which kind of scheduler we need to use. 
01212      * Systems without fork(2) have the .sandboxed attribute hardcoded to 
01213      * 'false', hence any U_TEST_CASE_T will be assigned to the "simple"
01214      * scheduler. */
01215     if (fto->what == U_TEST_SUITE_T)
01216         simple_sched = 1;
01217     else
01218         simple_sched = (ftc = TC_HANDLE(fto), ftc->pts->pt->sandboxed) ? 0 : 1;
01219 
01220     /* Go through all the test objs and select those at the current scheduling 
01221      * rank: first lower rank TO's (i.e. highest priority). */
01222     for (r = 0; r <= h->currank && !g_interrupted; ++r)
01223     {
01224         /* TS's and non-sandboxed TC's are simply scheduled one-by-one. */
01225         if (simple_sched)
01226         {
01227             LIST_FOREACH (to, &h->objs, links)
01228             {
01229                 if (to->rank == r)
01230                 {
01231                     /* Check for any dependency failure. */
01232                     if (u_test_dep_failed(&to->deps))
01233                     {
01234                         CHAT("Skip %s due to dependency failure\n", to->id);
01235 
01236                         to->status = U_TEST_SKIPPED; 
01237 
01238                         /* Update test cases status. */
01239                         if (to->what == U_TEST_SUITE_T)
01240                         {
01241                             (void) u_test_suite_update_all_status(TS_HANDLE(to),
01242                                     U_TEST_SKIPPED);
01243                         }
01244 
01245                         continue;
01246                     }
01247 
01248                     (void) sched(to);
01249                 }
01250             }
01251         }
01252         else    
01253         {
01254             /* Sandboxed TC's scheduling logic is slightly more complex:
01255              * we run them in chunks of U_TEST_MAX_PARALLEL cardinality, then
01256              * we reap the whole chunk and start again with another one, until
01257              * the list has been traversed. 'tptr' is the pointer to the next 
01258              * TC to be run. */
01259             u_test_case_t *tc;
01260 
01261             tptr = LIST_FIRST(&h->objs);
01262             tc = TC_HANDLE(tptr);
01263 
01264         resched:
01265             for (to = tptr; to != NULL; to = LIST_NEXT(to, links))
01266             {
01267                 if (to->rank == r)
01268                 {
01269                     /* Avoid scheduling a TO that has failed dependencies. */
01270                     if (u_test_dep_failed(&to->deps))
01271                     {
01272                         CHAT("Skip %s due to dependency failure\n", to->id);
01273                         to->status = U_TEST_SKIPPED; 
01274                         continue;
01275                     }
01276 
01277                     /* If no dependency failed, we can schedule the TC at the 
01278                      * current rank. */
01279                     dbg_if (sched(to));
01280             
01281                     /* See if we reached this chunk's upper bound. */
01282                     if ((parallel += 1) == tc->pts->pt->max_parallel)
01283                     {
01284                         /* Save restart reference and jump to reap. */
01285                         tptr = LIST_NEXT(to, links);
01286                         goto reap;
01287                     }
01288                 }
01289                     
01290                 /* Make sure 'tptr' is unset, so that, in case we go out on 
01291                  * loop exhaustion, the reaper branch knows that we don't need
01292                  * to be restarted. */
01293                 tptr = NULL;
01294             }
01295 
01296         reap:
01297             dbg_err_if (u_test_cases_reap(h));
01298 
01299             /* If 'tptr' was set, it means that we need to restart. */
01300             if (tptr != NULL)
01301             {
01302                 parallel = 0;       /* Reset parallel counter. */
01303                 goto resched;       /* Go to sched again. */
01304             }
01305         }
01306     }
01307 
01308     return 0;
01309 err:
01310     return ~0;
01311 }
01312 
01313 static int u_test_cases_reap (TO *h)
01314 {
01315 #ifdef U_TEST_SANDBOX_ENABLED
01316     int status;
01317     pid_t child;
01318     u_test_case_t *tc;
01319 #ifdef HAVE_STRUCT_RUSAGE
01320     struct rusage rusage;
01321 #endif  /* HAVE_STRUCT_RUSAGE */
01322 
01323     dbg_return_if (h == NULL, ~0);
01324 
01325     while (h->nchildren > 0)
01326     {
01327 #ifdef HAVE_WAIT3
01328         if ((child = wait3(&status, 0, &rusage)) == -1)
01329 #else   /* i.e. HAVE_WAIT */
01330         if ((child = wait(&status)) == -1)
01331 #endif
01332         {
01333             /* Interrupted (should be by user interaction) or no more child 
01334              * processes */
01335             if (errno == EINTR || errno == ECHILD)
01336                 break;
01337             else    /* THIS IS BAD */
01338                 con_err_sifm (1, "wait3 failed badly, test aborted");
01339         }
01340 
01341         if ((tc = u_test_cases_search_by_pid(h, child)) == NULL)
01342         {
01343             CHAT("not a waited test case: %d\n", child);
01344             continue;
01345         }
01346 
01347         /* We assume that a test case which received a SIGSTOP
01348          * is going to be resumed (i.e. SIGCONT'inued) independently. */
01349         if (WIFSTOPPED(status))
01350             continue;
01351 
01352         /* Reset pid to the default. */
01353         tc->pid = U_TEST_CASE_PID_INITIALIZER;
01354  
01355         /* Update test case stats. */
01356         if (WIFEXITED(status))
01357         {
01358             /* The test case terminated normally, that is, by calling exit(3) 
01359              * or _exit(2), or by returning from main().  
01360              * We expect a compliant test case to return one of U_TEST_FAILURE
01361              * or U_TEST_SUCCESS. */
01362             tc->o.status = WEXITSTATUS(status);
01363             dbg_if (tc->o.status != U_TEST_SUCCESS && 
01364                     tc->o.status != U_TEST_FAILURE);
01365         }
01366         else if (WIFSIGNALED(status))
01367         {
01368             /* The test case was terminated by a signal. */
01369             tc->o.status = U_TEST_ABORTED;
01370             CHAT("test case %s terminated by signal %d\n", tc->o.id, 
01371                     WTERMSIG(status));
01372         }
01373 
01374 #ifdef HAVE_STRUCT_RUSAGE
01375         tc->stats = rusage;
01376 #endif  /* HAVE_STRUCT_RUSAGE */
01377 
01378         tc->o.stop = time(NULL);
01379 
01380         h->nchildren -= 1;
01381     }
01382 
01383     if (h->nchildren != 0)
01384     {
01385         CHAT("%u child(ren) still in wait !\n", h->nchildren);
01386         goto err;
01387     }
01388 
01389     return 0;
01390 err:
01391     return ~0;
01392 #else   /* !U_TEST_SANDBOX_ENABLED */
01393     u_con("What are you doing here ?");
01394     return ~0;
01395 #endif  /* U_TEST_SANDBOX_ENABLED */
01396 }
01397 
01398 static u_test_case_t *u_test_cases_search_by_pid (TO *h, pid_t pid)
01399 {
01400     u_test_obj_t *to;
01401     u_test_case_t *tc;
01402 
01403     LIST_FOREACH (to, &h->objs, links)
01404     {
01405         if (tc = TC_HANDLE(to), tc->pid == pid)
01406             return tc;
01407     }
01408 
01409     return NULL;
01410 }
01411 
01412 static int u_test_syn_update (u_test_syn_t *syn, TO *h)
01413 {
01414     u_test_obj_t *to;
01415 
01416     dbg_return_if (h == NULL, ~0);
01417     dbg_return_if (syn == NULL, ~0);
01418 
01419     LIST_FOREACH (to, &h->objs, links)
01420     {
01421         syn->total += 1;
01422 
01423         switch (to->status)
01424         {
01425             case U_TEST_SUCCESS:
01426                 syn->pass += 1;
01427                 break;
01428             case U_TEST_FAILURE:
01429                 syn->fail += 1;
01430                 break;
01431             case U_TEST_ABORTED:
01432                 syn->abrt += 1;
01433                 break;
01434             case U_TEST_SKIPPED:
01435                 syn->skip += 1;
01436                 break;
01437             default:
01438                 u_warn("unknown status %d in test obj %s", to->status, to->id);
01439         }
01440     }
01441 
01442     return 0;
01443 }
01444 
01445 static int u_test_scheduler (u_test_t *t)
01446 {
01447     int rc;
01448 
01449     dbg_return_if (t == NULL, ~0);
01450 
01451     /* Run the test scheduler. */ 
01452     rc = u_test_obj_scheduler(&t->u_test_suites, u_test_suite_scheduler);
01453 
01454     /* Collect synoptical stats. */
01455     (void) u_test_syn_update(&t->syn, &t->u_test_suites);
01456 
01457     return rc;
01458 }
01459 
01460 static int u_test_suite_scheduler (u_test_obj_t *to)
01461 {
01462     int rc;
01463     u_test_obj_t *tco;
01464     u_test_suite_t *ts;
01465 
01466     dbg_return_if (to == NULL || (ts = TS_HANDLE(to)) == NULL, ~0);
01467 
01468     CHAT("now scheduling test suite %s\n", ts->o.id);
01469 
01470     /* Record test suite begin timestamp. */
01471     ts->o.start = time(NULL);
01472 
01473     /* Go through children test cases. */
01474     rc = u_test_obj_scheduler(&ts->u_test_cases, u_test_case_scheduler);
01475 
01476     /* See if we've been interrupted, in which case we need to cleanup
01477      * as much as possible before exit'ing. */
01478     if (g_interrupted)
01479         u_test_bail_out(&ts->u_test_cases);
01480 
01481     /* Record test suite end timestamp. */
01482     ts->o.stop = time(NULL);
01483 
01484     /* Update stats for reporting. */
01485     LIST_FOREACH (tco, &ts->u_test_cases.objs, links)
01486     {
01487         /* If any of our test cases failed, set the overall status 
01488          * to FAILURE */
01489         if (tco->status != U_TEST_SUCCESS)
01490         {
01491             ts->o.status = U_TEST_FAILURE;
01492             break;
01493         }
01494     }
01495 
01496     /* Collect synoptical stats. */
01497     (void) u_test_syn_update(&ts->syn, &ts->u_test_cases);
01498 
01499     return rc;
01500 }
01501 
01502 static int u_test_suite_update_all_status (u_test_suite_t *ts, int status)
01503 {
01504     u_test_obj_t *to;
01505 
01506     dbg_return_if (ts == NULL, ~0);
01507 
01508     LIST_FOREACH (to, &ts->u_test_cases.objs, links)
01509         to->status = status;
01510 
01511     return 0;
01512 }
01513 
01514 static int u_test_case_scheduler (u_test_obj_t *to)
01515 {
01516     dbg_return_if (to == NULL, ~0);
01517 
01518     CHAT("now scheduling test case %s\n", to->id);
01519 
01520     return u_test_case_exec(TC_HANDLE(to));
01521 }
01522 
01523 static int u_test_case_exec (u_test_case_t *tc)
01524 {
01525     int rc;
01526     pid_t pid;
01527 
01528     dbg_return_if (tc == NULL, ~0);
01529 
01530     if (tc->func == NULL)
01531         return 0;
01532 
01533     /* Record test case begin timestamp. */
01534     tc->o.start = time(NULL);
01535 
01536     /* The following condition is always asserted on systems with no fork(2)
01537      * syscall.  See u_test_new(). */
01538     if (!tc->pts->pt->sandboxed)
01539     {
01540         dbg_if ((rc = tc->func(tc)) != U_TEST_SUCCESS && rc != U_TEST_FAILURE);
01541 
01542         /* Update test case stats. */
01543         tc->o.status = rc;
01544         tc->o.stop = time(NULL);
01545 
01546         return 0;
01547     }
01548 
01549 #ifdef U_TEST_SANDBOX_ENABLED
01550     switch (pid = fork())
01551     {
01552         case -1:
01553             u_warn("test case %s spawn failed: %s", tc->o.id, strerror(errno));
01554             return ~0;
01555         case 0:
01556             exit(tc->func(tc));
01557         default:
01558             break;
01559     }
01560 
01561     tc->pid = pid;                  /* Set test case process pid */
01562     tc->o.parent->nchildren += 1;   /* Tell the u_test_case's head we have one
01563                                        more child */
01564 
01565     CHAT("started test case %s with pid %d\n", tc->o.id, (int) tc->pid);
01566 #endif  /* U_TEST_SANDBOX_ENABLED */
01567 
01568     /* always return ok here, the reaper will know the test exit status */
01569     return 0;
01570 }
01571 
01572 static int u_test_reporter (u_test_t *t)
01573 {
01574     FILE *fp;
01575     u_test_obj_t *tso, *tco;
01576     u_test_suite_t *ts;
01577 
01578     dbg_return_if (t == NULL, ~0);
01579 
01580     /* Assume that each report function is set correctly (we trust the
01581      * setters having done a good job). */
01582 
01583     if (strcmp(t->outfn, "-"))
01584         dbg_err_sif ((fp = fopen(t->outfn, "w")) == NULL);
01585     else
01586         fp = stdout;
01587 
01588     dbg_err_if (t->reporters.t_cb(fp, t, U_TEST_REP_HEAD));
01589 
01590     LIST_FOREACH (tso, &t->u_test_suites.objs, links)
01591     {
01592         ts = TS_HANDLE(tso);
01593 
01594         dbg_err_if (t->reporters.ts_cb(fp, ts, U_TEST_REP_HEAD));
01595 
01596         LIST_FOREACH (tco, &ts->u_test_cases.objs, links)
01597             dbg_err_if (t->reporters.tc_cb(fp, TC_HANDLE(tco)));
01598 
01599         dbg_err_if (t->reporters.ts_cb(fp, ts, U_TEST_REP_TAIL));
01600     }
01601 
01602     dbg_err_if (t->reporters.t_cb(fp, t, U_TEST_REP_TAIL));
01603 
01604     (void) fclose(fp);
01605 
01606     return 0;
01607 err:
01608     return ~0;
01609 }
01610 
01611 static int u_test_report_txt (FILE *fp, u_test_t *t, u_test_rep_tag_t tag)
01612 {
01613     u_test_syn_t *syn = &t->syn;
01614 
01615     if (tag == U_TEST_REP_HEAD)
01616     {
01617         (void) fprintf(fp, "%s", t->id);
01618 
01619         /* Host info. */
01620         (void) fprintf(fp, " (%s)\n", t->host);
01621     }
01622 
01623     if (tag == U_TEST_REP_TAIL)
01624     {
01625         (void) fprintf(fp, "Number of test suites: %u\n", syn->total);
01626         (void) fprintf(fp, "               Passed: %u\n", syn->pass);
01627         (void) fprintf(fp, "               Failed: %u\n", syn->fail);
01628         (void) fprintf(fp, "              Aborted: %u\n", syn->abrt);
01629         (void) fprintf(fp, "              Skipped: %u\n", syn->skip);
01630     }
01631 
01632     return 0;
01633 }
01634 
01635 static int u_test_suite_report_txt (FILE *fp, u_test_suite_t *ts, 
01636         u_test_rep_tag_t tag)
01637 {
01638     int status;
01639     char b[80] = { '\0' }, e[80] = { '\0' }, d[80] = { '\0' };
01640     u_test_syn_t *syn = &ts->syn;
01641 
01642     dbg_return_if (fp == NULL, ~0);
01643     dbg_return_if (ts == NULL, ~0);
01644 
01645     if (tag == U_TEST_REP_HEAD)
01646     {
01647         status = ts->o.status;
01648 
01649         (void) fprintf(fp, "\t* [%s] %s\n", 
01650                 u_test_status_str(status), ts->o.id);
01651 
01652         /* Timing info is relevant only in case test succeeded. */ 
01653         if (status == U_TEST_SUCCESS)
01654         {
01655             (void) u_test_obj_ts_fmt(&ts->o, b, e, d);
01656             (void) fprintf(fp, "\t       begin: %s\n", b);
01657             (void) fprintf(fp, "\t         end: %s\n", e);
01658             (void) fprintf(fp, "\t     elapsed: %s\n", d);
01659         }
01660     }
01661 
01662     if (tag == U_TEST_REP_TAIL)
01663     {
01664         (void) fprintf(fp, "\tNumber of test cases: %u\n", syn->total);
01665         (void) fprintf(fp, "\t              Passed: %u\n", syn->pass);
01666         (void) fprintf(fp, "\t              Failed: %u\n", syn->fail);
01667         (void) fprintf(fp, "\t             Aborted: %u\n", syn->abrt);
01668         (void) fprintf(fp, "\t             Skipped: %u\n", syn->skip);
01669     }
01670 
01671     return 0;
01672 }
01673 
01674 static int u_test_case_report_txt (FILE *fp, u_test_case_t *tc)
01675 {
01676     int status;
01677 #ifdef HAVE_STRUCT_RUSAGE
01678     char s[80] = { '\0' }, u[80] = { '\0' };
01679 #endif  /* HAVE_STRUCT_RUSAGE */
01680     char d[80] = { '\0' };
01681 
01682     dbg_return_if (fp == NULL, ~0);
01683     dbg_return_if (tc == NULL, ~0);
01684 
01685     status = tc->o.status;
01686 
01687     (void) fprintf(fp, "\t\t* [%s] %s\n", u_test_status_str(status), tc->o.id);
01688 
01689     if (status == U_TEST_SUCCESS)
01690     {
01691 #ifdef HAVE_STRUCT_RUSAGE
01692         if (tc->pts->pt->sandboxed)
01693         {
01694             (void) u_test_case_rusage_fmt(tc, u, s);
01695             (void) fprintf(fp, "\t\t    sys time: %s\n", s);
01696             (void) fprintf(fp, "\t\t   user time: %s\n", u);
01697         }
01698         else
01699 #endif  /* HAVE_STRUCT_RUSAGE */
01700         {
01701             (void) u_test_obj_ts_fmt(&tc->o, NULL, NULL, d);
01702             (void) fprintf(fp, "\t\t     elapsed:%s\n", d);
01703         }
01704     }
01705 
01706     return 0;
01707 }
01708 
01709 static int u_test_report_xml (FILE *fp, u_test_t *t, u_test_rep_tag_t tag)
01710 {
01711     u_test_syn_t *syn = &t->syn;
01712 
01713     dbg_return_if (t == NULL, ~0);
01714     dbg_return_if (fp == NULL, ~0);
01715 
01716     if (tag == U_TEST_REP_HEAD)
01717     {
01718         (void) fprintf(fp, "<?xml version=\"1.0\"?>\n");
01719         (void) fprintf(fp, "<test id=\"%s\">\n", t->id);
01720 
01721         /* Add synoptical info. */
01722         (void) fprintf(fp, "\t<total>%u</total>\n", syn->total);
01723         (void) fprintf(fp, "\t<passed>%u</passed>\n", syn->pass);
01724         (void) fprintf(fp, "\t<failed>%u</failed>\n", syn->fail);
01725         (void) fprintf(fp, "\t<aborted>%u</aborted>\n", syn->abrt);
01726         (void) fprintf(fp, "\t<skipped>%u</skipped>\n", syn->skip);
01727 
01728         /* Host info. */
01729         (void) fprintf(fp, "\t<host>%s</host>\n", t->host);
01730     }
01731 
01732     if (tag == U_TEST_REP_TAIL)
01733         (void) fprintf(fp, "</test>\n");
01734 
01735     return 0;
01736 }
01737 
01738 static int u_test_suite_report_xml (FILE *fp, u_test_suite_t *ts, 
01739         u_test_rep_tag_t tag)
01740 {
01741     int status;
01742     u_test_syn_t *syn;
01743 
01744     dbg_return_if (fp == NULL, ~0);
01745     dbg_return_if (ts == NULL, ~0);
01746 
01747     syn = &ts->syn;
01748 
01749     if (tag == U_TEST_REP_HEAD)
01750     {
01751         status = ts->o.status;
01752 
01753         (void) fprintf(fp, "\t<test_suite id=\"%s\">\n", ts->o.id);
01754 
01755         /* Status first. */
01756         (void) fprintf(fp, "\t\t<status>%s</status>\n", 
01757                 u_test_status_str(status));
01758 
01759         /* Then timings information. */
01760         if (status == U_TEST_SUCCESS)
01761         {
01762             char b[80], d[80], e[80];
01763 
01764             (void) u_test_obj_ts_fmt(&ts->o, b, e, d);
01765 
01766             (void) fprintf(fp, "\t\t<begin>%s</begin>\n", b);
01767             (void) fprintf(fp, "\t\t<end>%s</end>\n", e);
01768             (void) fprintf(fp, "\t\t<elapsed>%s</elapsed>\n", d);
01769         }
01770 
01771         /* Add synoptical info. */
01772         (void) fprintf(fp, "\t\t<total>%u</total>\n", syn->total);
01773         (void) fprintf(fp, "\t\t<passed>%u</passed>\n", syn->pass);
01774         (void) fprintf(fp, "\t\t<failed>%u</failed>\n", syn->fail);
01775         (void) fprintf(fp, "\t\t<aborted>%u</aborted>\n", syn->abrt);
01776         (void) fprintf(fp, "\t\t<skipped>%u</skipped>\n", syn->skip);
01777     }
01778 
01779     if (tag == U_TEST_REP_TAIL)
01780         (void) fprintf(fp, "\t</test_suite>\n");
01781 
01782     return 0;
01783 }
01784 
01785 static int u_test_case_report_xml (FILE *fp, u_test_case_t *tc)
01786 {
01787     int status;
01788 
01789     dbg_return_if (fp == NULL, ~0);
01790     dbg_return_if (tc == NULL, ~0);
01791 
01792     status = tc->o.status;
01793 
01794     (void) fprintf(fp, "\t\t<test_case id=\"%s\">\n", tc->o.id);
01795 
01796     (void) fprintf(fp, "\t\t\t<status>%s</status>\n", 
01797             u_test_status_str(status));
01798 
01799     if (status == U_TEST_SUCCESS)
01800     {
01801         char d[80];
01802 #ifdef HAVE_STRUCT_RUSAGE
01803         char u[80], s[80];
01804 
01805         /* When sandboxed we have rusage info. */
01806         if (tc->pts->pt->sandboxed)
01807         {
01808             (void) u_test_case_rusage_fmt(tc, u, s);
01809             (void) fprintf(fp, "\t\t\t<sys_time>%s</sys_time>\n", s);
01810             (void) fprintf(fp, "\t\t\t<user_time>%s</user_time>\n", u);
01811         }
01812         else
01813 #endif  /* HAVE_STRUCT_RUSAGE */
01814         {
01815             (void) u_test_obj_ts_fmt(&tc->o, NULL, NULL, d);
01816             (void) fprintf(fp, "\t\t\t<elapsed>%s</elapsed>\n", d);
01817         }
01818     }
01819 
01820     (void) fprintf(fp, "\t\t</test_case>\n");
01821 
01822     return 0;
01823 }
01824 
01825 static const char *u_test_status_str (int status)
01826 {
01827     switch (status)
01828     {
01829         case U_TEST_SUCCESS:  return "PASS";
01830         case U_TEST_FAILURE:  return "FAIL";
01831         case U_TEST_ABORTED:  return "ABRT";
01832         case U_TEST_SKIPPED:  return "SKIP";
01833     }
01834 
01835     return "?";
01836 }
01837 
01838 static int u_test_getopt (int ac, char *av[], u_test_t *t)
01839 {
01840     int c, mp;
01841 
01842     dbg_return_if (t == NULL, ~0);
01843 
01844     while ((c = getopt(ac, av, "df:ho:p:sv")) != -1)
01845     {
01846         switch (c)
01847         {
01848             case 'o':   /* Report file. */
01849                 (void) u_strlcpy(t->outfn, optarg, sizeof t->outfn);
01850                 break;
01851             case 'f':   /* Report format. */
01852                 if (u_test_set_outfmt(t, optarg))
01853                     u_test_usage(av[0], "bad report format");
01854                 break;
01855             case 'v':   /* Increment verbosity. */
01856                 g_verbose = 1;
01857                 break;
01858             case 'd':   /* Print out some internal info. */
01859                 g_debug = 1;
01860                 break;
01861 #ifdef U_TEST_SANDBOX_ENABLED
01862             case 'p':   /* Max number of test cases running in parallel. */
01863                 if (u_atoi(optarg, &mp) || mp < 0)
01864                     u_test_usage(av[0], "bad max parallel");
01865                 t->max_parallel = (unsigned int) mp;
01866                 break;
01867             case 's':   /* Serialized (i.e. !sandboxed) test cases. */
01868                 t->sandboxed = 0;
01869                 break;
01870 #endif  /* U_TEST_SANDBOX_ENABLED */
01871             case 'h': default:
01872                 u_test_usage(av[0], NULL);
01873                 break;
01874         } 
01875     }
01876 
01877     return 0;
01878 }
01879 
01880 static void u_test_usage (const char *prog, const char *msg)
01881 {
01882     const char *us = 
01883         "                                                                   \n"
01884         "Synopsis: %s [options]                                             \n"
01885         "                                                                   \n"
01886         "   where \'options\' is a combination of the following:            \n"
01887         "                                                                   \n"
01888         "       -o <file>           Set the report output file              \n"
01889         "       -f <txt|xml>        Choose report output format             \n"
01890 #ifdef U_TEST_SANDBOX_ENABLED
01891         "       -p <number>         Set the max number of parallel tests    \n"
01892         "       -s                  Serialize test cases (non sandboxed)    \n"
01893 #endif  /* U_TEST_SANDBOX_ENABLED */
01894         "       -d                  Debug mode                              \n"
01895         "       -v                  Be chatty                               \n"
01896         "       -h                  Print this help                         \n"
01897         "                                                                   \n"
01898         ;
01899     
01900     if (msg)
01901         (void) fprintf(stderr, "\nError: %s\n", msg);
01902 
01903     (void) fprintf(stderr, us, prog ? prog : "unitest");
01904 
01905     exit(1);
01906 }
01907 
01908 static int u_test_set_outfmt (u_test_t *t, const char *fmt)
01909 {
01910     dbg_return_if (t == NULL, ~0);
01911     dbg_return_if (fmt == NULL, ~0);
01912 
01913     if (!strcasecmp(fmt, "txt"))
01914         t->reporters = txt_reps;
01915     else if (!strcasecmp(fmt, "xml"))
01916         t->reporters = xml_reps;
01917     else
01918         return ~0;
01919 
01920     return 0;
01921 }
01922 
01923 static int u_test_init (u_test_t *t)
01924 {
01925     dbg_return_if (t == NULL, ~0);
01926 
01927     dbg_err_if (u_test_signals());
01928     dbg_err_if (u_test_uname(t));
01929 
01930     return 0;
01931 err:
01932     return ~0;
01933 }
01934 
01935 static int u_test_uname (u_test_t *t)
01936 {
01937 #ifdef HAVE_UNAME
01938     struct utsname h;
01939 #endif  /* HAVE_UNAME */
01940 
01941     dbg_return_if (t == NULL, ~0);
01942 
01943 #ifdef HAVE_UNAME
01944     dbg_return_sif (uname(&h) == -1, ~0);
01945 
01946     (void) u_snprintf(t->host, sizeof t->host, "%s - %s %s %s", 
01947             h.nodename, h.sysname, h.release, h.machine);
01948 #else   
01949     /* TODO refine this ! */
01950     (void) u_strlcpy(t->host, "unknown host name", sizeof t->host);
01951 #endif  /* HAVE_UNAME */
01952 
01953     return 0; 
01954 }
01955 
01956 static int u_test_signals (void)
01957 {
01958 #ifdef HAVE_SIGACTION
01959     struct sigaction sa;
01960 
01961     sa.sa_handler = u_test_interrupted;
01962     sigemptyset(&sa.sa_mask);
01963     sa.sa_flags = 0;    /* Don't restart. */
01964         
01965     dbg_err_sif (sigaction(SIGINT, &sa, NULL) == -1);
01966     dbg_err_sif (sigaction(SIGTERM, &sa, NULL) == -1);
01967     dbg_err_sif (sigaction(SIGQUIT, &sa, NULL) == -1);
01968 #else   /* !HAVE_SIGACTION */
01969     dbg_err_sif (signal(SIGINT, u_test_interrupted) == SIG_ERR);
01970     dbg_err_sif (signal(SIGTERM, u_test_interrupted) == SIG_ERR);
01971 #endif  /* HAVE_SIGACTION */
01972 
01973     return 0;
01974 err:
01975     return ~0;
01976 }
01977 
01978 static void u_test_interrupted (int signo)
01979 {
01980     u_unused_args(signo);
01981     g_interrupted = 1;
01982 }
01983 
01984 #ifdef HAVE_STRUCT_RUSAGE
01985 static int u_test_case_rusage_fmt (u_test_case_t *tc, char uti[80], 
01986         char sti[80])
01987 {
01988     dbg_return_if (tc == NULL, ~0);
01989 
01990     if (uti)
01991         (void) __timeval_fmt(&tc->stats.ru_utime, uti);
01992 
01993     if (sti)
01994         (void) __timeval_fmt(&tc->stats.ru_stime, sti);
01995 
01996     /* TODO Other rusage fields depending on UNIX implementation. 
01997      * By POSIX we're only guaranteed about the existence of ru_utime and 
01998      * ru_stime fields. */
01999 
02000     return 0;
02001 }
02002 
02003 static int __timeval_fmt (struct timeval *tv, char t[80])
02004 {
02005     char _tv[80];
02006     time_t secs;
02007 
02008     dbg_return_if (t == NULL, ~0);
02009     dbg_return_if (tv == NULL, ~0);
02010 
02011     secs = tv->tv_sec;
02012 
02013     dbg_if (strftime(_tv, sizeof _tv, "%M:%S", localtime(&secs)) == 0);
02014     dbg_if (u_snprintf(t, 80, "%s.%06.6d", _tv, tv->tv_usec));
02015 
02016     return 0;
02017 }
02018 #endif  /* HAVE_STRUCT_RUSAGE */
02019 
02020 static int u_test_obj_ts_fmt (u_test_obj_t *to, char b[80], char e[80], 
02021         char d[80])
02022 {
02023     time_t elapsed;
02024    
02025     dbg_return_if (to == NULL, ~0);
02026 
02027     elapsed = to->stop - to->start;
02028 
02029     if (b)
02030     {
02031         dbg_if (strftime(b, 80, "%a %Y-%m-%d %H:%M:%S %Z", 
02032                     localtime(&to->start)) == 0);
02033     }
02034 
02035     if (e)
02036     {
02037         dbg_if (strftime(e, 80, "%a %Y-%m-%d %H:%M:%S %Z", 
02038                     localtime(&to->stop)) == 0);
02039     }
02040 
02041     if (d)
02042         dbg_if (strftime(d, 80, "%M:%S", localtime(&elapsed)) == 0);
02043 
02044     return 0;
02045 }
02046 
02047 static void u_test_bail_out (TO *h)
02048 {
02049     int status;
02050     u_test_obj_t *to;
02051     u_test_case_t *tc;
02052 
02053     CHAT("Bailing out, as requested by the user\n");
02054     
02055 #ifdef U_TEST_SANDBOX_ENABLED
02056     /* Assume 'h' is a test cases' list head. */
02057 
02058     /* Brutally kill all (possibly) running test cases. 
02059      * There may be a harmless race here, in case a child has already
02060      * called exit(2), but we've been interrupted before reaping it. */
02061     LIST_FOREACH (to, &h->objs, links)
02062     {
02063         tc = TC_HANDLE(to);
02064 
02065         if (tc->pid != U_TEST_CASE_PID_INITIALIZER)
02066         {
02067             CHAT("Killing test case %s [%d]\n", to->id, (int) tc->pid);
02068             dbg_if (kill(tc->pid, SIGKILL) == -1);
02069         }
02070     }
02071 
02072     while (waitpid(-1, &status, 0) != -1) ;
02073 
02074 #endif  /* U_TEST_SANDBOX_ENABLED */
02075 
02076     exit(1);
02077 }

←Products
© 2005-2012 - KoanLogic S.r.l. - All rights reserved