2 * src/runner.c - type representing the runner.
\r
4 * Copyright 2008,2009 Martin Quinson, Malek Cherier All right reserved.
\r
6 * This program is free software; you can redistribute it and/or modify it
\r
7 * under the terms of the license (GNU LGPL) which comes with this package.
\r
10 * This file contains all the definitions of the functions related with
\r
11 * the tesh runner type.
\r
18 #include <variable.h>
\r
20 #include <errno.h> /* for error code */
\r
21 #include <stdlib.h> /* for calloc() */
\r
24 #include <readline.h>
\r
25 #include <explode.h>
\r
28 #include <sys/resource.h>
\r
31 #define _RUNNER_HASHCODE 0xFEFEAAAA
\r
32 \rXBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
34 #if (!defined(__BUILTIN) && defined(__CHKCMD) && !defined(_XBT_WIN32))
\r
35 static const char *builtin[] =
\r
36 {
\r"alias",
\r"bind",
\r"builtin",
\r"caller",
\r"cd",
\r"command",
37 \r"compgen",
\r"complete",
\r"declare",
\r"disown",
\r"echo",
\r"enable",
\r"eval",
\r"exec",
\r"export",
38 \r"false",
\r"fc",
\r"function",
\r"getopts",
\r"hash",
\r"history",
\r"jobs",
\r"let",
\r"logout",
39 \r"printf",
\r"pwd",
\r"readonly",
\r"shift",
\r"shopt",
\r"source",
\r"suspend",
\r"test",
\r"time",
40 \r"times",
\r"trap",
\r"true",
\r"type",
\r"typeset",
\r"ulimit",
\r"umask",
\r"unalias",
\r"unset",
45 #define __BUILTIN_MAX ((size_t)42)
\r
49 /* under darwin, the environment gets added to the process at startup time. So, it's not defined at library link time, forcing us to extra tricks */
\r
50 # include <crt_externs.h>
\r
51 # define environ (*_NSGetEnviron())
\r
54 /* the environment, as specified by the opengroup, used to initialize the process properties */
\r
55 # define environ **wenviron;
\r
57 extern char **environ;
63 extern char **
\r environ;
67 /* the unique tesh runner */
\r
68 static runner_t
\r runner = NULL;
70 /* wait for the tesh runner terminaison */
\r
71 static void
\r runner_wait(void);
72 \r\rstatic void *
\r runner_start_routine(void *p);
74 /* check the syntax of the tesh files if
\r
75 * the check_syntax_flag is specified. Returns
\r
76 * 0 if the syntax is clean.
\r
79 check_syntax(void);*/
\r
82 \rstatic HANDLE
\r timer_handle = NULL;
83 \r\r\rstatic void *
\r runner_start_routine(void *p)
\r
86 \r\rli.QuadPart = -runner->timeout * 10000000; /* 10000000 = 10 000 000 * 100 nanoseconds = 1 second */
88 /* create the waitable timer */
\r
89 timer_handle = CreateWaitableTimer(NULL, TRUE, NULL);
91 /* set a timer to wait for timeout seconds */
\r
92 SetWaitableTimer(timer_handle, &li, 0, NULL, NULL, 0);
94 /* wait for the timer */
\r
95 WaitForSingleObject(timer_handle, INFINITE);
96 \r\rif (runner->waiting)
98 \rexit_code = ELEADTIME;
100 \rrunner->timeouted = 1;
101 \rxbt_os_sem_release(units_sem);
108 static void *
\r runner_start_routine(void *p)
\r
110 \rstruct timespec ts;
111 \rint timeout = runner->timeout;
112 \r\r\rwhile (timeout-- && runner->waiting)
119 \rnanosleep(&ts, &ts);
120 \r} while (EINTR == errno);
125 /* TODO process the error */
\r
130 \rif (runner->waiting)
132 \rexit_code = ELEADTIME;
134 \rrunner->timeouted = 1;
135 \rxbt_os_sem_release(units_sem);
144 runner_init( /*int check_syntax_flag, */ int timeout,
145 fstreams_t fstreams)
\r
149 \rchar buffer[PATH_MAX + 1] = { 0 };
152 \rvariable_t variable;
154 #if (defined(__CHKCMD) && defined(__BUILTIN) && !defined(_XBT_WIN32))
\r
164 \rERROR0("The runner is already initialized");
167 \r\rrunner = xbt_new0(s_runner_t, 1);
168 \r\rrunner->path = NULL;
169 \rrunner->builtin = NULL;
170 \r\rif (!(runner->units = units_new(runner, fstreams)))
176 \r\rrunner->timeout = timeout;
177 \rrunner->timeouted = 0;
178 \rrunner->interrupted = 0;
179 \rrunner->number_of_ended_units = 0;
180 \rrunner->number_of_runned_units = 0;
181 \rrunner->waiting = 0;
182 \r\rrunner->total_of_tests = 0;
183 \rrunner->total_of_successeded_tests = 0;
184 \rrunner->total_of_failed_tests = 0;
185 \rrunner->total_of_interrupted_tests = 0;
186 \r\rrunner->total_of_units = 0;
187 \rrunner->total_of_successeded_units = 0;
188 \rrunner->total_of_failed_units = 0;
189 \rrunner->total_of_interrupted_units = 0;
190 \r\rrunner->total_of_suites = 0;
191 \rrunner->total_of_successeded_suites = 0;
192 \rrunner->total_of_failed_suites = 0;
193 \rrunner->total_of_interrupted_suites = 0;
195 /* initialize the vector of variables */
\r
197 xbt_dynar_new(sizeof(variable_t), (void_f_pvoid_t) variable_free);
199 /* add the environment variables in the vector */
\r
200 for (i = 0; environ[i] != NULL; i++)
202 \rval = strchr(environ[i], '=');
206 \r\rif (val[0] != '\0')
207 \rstrncpy(buffer, environ[i], (val - environ[i] - 1));
208 \r\rif (!strcmp("TESH_PPID", buffer))
210 \r\rvariable = variable_new(buffer, val);
212 \rxbt_dynar_push(runner->variables, &variable);
215 if (!strcmp("PATH", buffer))
218 if (!strcmp("Path", buffer) || !strcmp("PATH", buffer))
225 /* get the list of paths */
\r
228 runner->path = explode(';', val);
231 runner->path = explode(':', val);
235 /* remove spaces and backslahes at the end of the path */
\r
236 for (k = 0; runner->path[k] != NULL; k++)
238 \rp = runner->path[k];
242 for (j = len - 1; p[j] == '/' || p[j] == ' '; j--)
245 for (j = len - 1; p[j] == '\\' || p[j] == ' '; j--)
251 \r\r\rmemset(buffer, 0, PATH_MAX + 1);
254 \r\rif (is_tesh_root)
256 \rchar *tesh_dir = getcwd(NULL, 0);
257 \r\rsprintf(buffer, "%d", getpid());
260 setenv("TESH_PPID", buffer, 0);
261 \rsetenv("TESH_DIR", tesh_dir, 0);
264 SetEnvironmentVariable("TESH_PPID", buffer);
265 \rSetEnvironmentVariable("TESH_DIR", tesh_dir);
268 \rvariable = variable_new("TESH_PPID", buffer);
270 \r\rxbt_dynar_push(runner->variables, &variable);
271 \r\rvariable = variable_new("TESH_DIR", tesh_dir);
273 \r\rxbt_dynar_push(runner->variables, &variable);
276 \r\rvariable = variable_new("EXIT_SUCCESS", "0");
278 \r\rxbt_dynar_push(runner->variables, &variable);
279 \r\rvariable = variable_new("EXIT_FAILURE", "1");
281 \r\rxbt_dynar_push(runner->variables, &variable);
282 \r\rvariable = variable_new("TRUE", "0");
284 \r\rxbt_dynar_push(runner->variables, &variable);
285 \r\rvariable = variable_new("FALSE", "1");
287 \r\rxbt_dynar_push(runner->variables, &variable);
290 /* add the errors variables */
\r
291 while ((cstr = error_get_at(i++, &code)))
293 \rsprintf(buffer, "%d", code);
294 \rvariable = variable_new(cstr, buffer);
296 \rxbt_dynar_push(runner->variables, &variable);
299 /* if the user want check the syntax, check it */
\r
300 /*if(check_syntax_flag)
\r
304 #if (!defined(_XBT_WIN32) && defined(__CHKCMD))
\r
305 #if defined(__BUILTIN)
\r
309 /* compute the full path the builtin.def file */
\r
310 sprintf(buffer, "%s/builtin.def", getenv("TESH_DIR"));
311 \r\rif (!(s = fopen(buffer, "r")))
313 \rERROR1("File `(%s)' not found", buffer);
320 \rif (!(s = fopen("builtin.def", "r")))
322 \rERROR0("File `(builtin.def)' not found");
329 \r\rfgetpos(s, &begin);
330 \r\rwhile (readline(s, &line, &len) != -1)
334 \r\r\rwhile (line[i] != '\0')
336 \rif (line[i] != ' ' && line[i] != '\t' && line[i] != '\n'
347 \r\rfsetpos(s, &begin);
353 \r\rrunner->builtin = xbt_new0(char *, n + 1); /* (char**) calloc(n + 1, sizeof(char*)); */
355 \r\rwhile (readline(s, &line, &len) != -1)
359 \r\rwhile (line[i] != '\0')
361 \rif (line[i] != ' ' && line[i] != '\t' && line[i] != '\n'
372 \r\rl[strlen(l) - 1] = '\0';
373 \r\r(runner->builtin)[n++] = l;
380 \rWARN0("The file `(builtin.def)' is empty");
381 \rfree(runner->builtin);
382 \rrunner->builtin = NULL;
390 runner->builtin = xbt_new0(char *, __BUILTIN_MAX + 1); /* (char**) calloc(__BUILTIN_MAX + 1, sizeof(char*)); */
391 \r\rfor (i = 0; i < __BUILTIN_MAX; i++)
392 \rrunner->builtin[i] = strdup(builtin[i]);
396 \rreturn exit_code ? -1 : 0;
399 \r\rvoid
\r runner_destroy(void)
\r
402 \r\rif (runner->units)
403 \runits_free((void **) (&(runner->units)));
404 \r\rif (runner->variables)
405 \rxbt_dynar_free(&runner->variables);
408 CloseHandle(timer_handle);
411 \rif (runner->thread)
412 \rxbt_os_thread_join(runner->thread, NULL);
413 \r\rif (runner->path)
415 \rfor (i = 0; runner->path[i] != NULL; i++)
416 \rfree(runner->path[i]);
417 \r\rfree(runner->path);
419 \r\rif (runner->builtin)
421 \rfor (i = 0; runner->builtin[i] != NULL; i++)
422 \rfree(runner->builtin[i]);
423 \r\rfree(runner->builtin);
429 \r\rvoid
\r runner_run(void)
\r
432 /* allocate the mutex used by the units to asynchronously access
\r
433 * to the properties of the runner.
\r
435 xbt_os_mutex_t mutex = xbt_os_mutex_init();
437 /* run all the units */
\r
438 units_run_all(runner->units, mutex);
439 \r\r\rif (!interrupted)
442 /* if the runner is timeouted or receive a interruption request
\r
443 * , interrupt all the active units.
\r
445 if (runner->timeouted || interrupted)
446 \rrunner_interrupt();
448 /* joins all the units */
\r
449 units_join_all(runner->units);
451 /* release the mutex resource */
\r
452 xbt_os_mutex_destroy(mutex);
455 \r\rstatic void
\r runner_wait(void)
\r
457 \rif (runner->timeout > 0)
458 \rrunner->thread = xbt_os_thread_create("", runner_start_routine, NULL);
460 /* signal that the runner is waiting */
\r
463 /* wait for the end of all the units */
\r
464 xbt_os_sem_acquire(units_sem);
465 \r\r\rrunner->waiting = 0;
470 * interrupt all the active units.
\r
471 * this function is called when the lead time of the execution is reached
\r
472 * or when a failed unit requests an interruption of the execution.
\r
474 void
\r runner_interrupt(void)
\r
476 \runits_interrupt_all(runner->units);
477 \r}
\r\rvoid
\r runner_summarize(void)
\r
479 \r\rif (!dry_run_flag)
483 struct rusage r_usage;
487 \rFILETIME exit_time;
488 \rFILETIME kernel_time;
489 \rFILETIME user_time;
494 ("\n TEst SHell utility - mini shell specialized in running test units.\n");
496 (" =============================================================================\n");
497 \r\runits_summuarize(runner->units);
499 (" =====================================================================%s\n",
500 \rrunner->total_of_failed_tests ? "== FAILED" : (runner->
501 total_of_interrupted_tests
503 total_of_interrupted_units)
504 ? "==== INTR" : "====== OK");
505 \r\rprintf(" TOTAL : Suite(s): %.0f%% ok (%d suite(s): %d ok",
507 total_of_suites ? (1 -
509 total_of_failed_suites +
511 total_of_interrupted_suites) /
512 (double) runner->total_of_suites) *
513 100.0 : 100.0),
\rrunner->total_of_suites,
514 runner->total_of_successeded_suites);
515 \r\rif (runner->total_of_failed_suites > 0)
516 \rprintf(", %d failed", runner->total_of_failed_suites);
517 \r\rif (runner->total_of_interrupted_suites > 0)
518 \rprintf(", %d interrupted)", runner->total_of_interrupted_suites);
520 \r\rprintf(" Unit(s): %.0f%% ok (%d unit(s): %d ok",
\r
522 total_of_units ? (1 -
523 ((double) runner->total_of_failed_units +
525 total_of_interrupted_units) /
526 (double) runner->total_of_units) *
527 100.0 : 100.0),
\rrunner->total_of_units,
528 runner->total_of_successeded_units);
529 \r\rif (runner->total_of_failed_units > 0)
530 \rprintf(", %d failed", runner->total_of_failed_units);
531 \r\rif (runner->total_of_interrupted_units > 0)
532 \rprintf(", %d interrupted)", runner->total_of_interrupted_units);
534 \r\rprintf(" Test(s): %.0f%% ok (%d test(s): %d ok",
\r
536 total_of_tests ? (1 -
537 ((double) runner->total_of_failed_tests +
539 total_of_interrupted_tests) /
540 (double) runner->total_of_tests) *
541 100.0 : 100.0),
\rrunner->total_of_tests,
542 runner->total_of_successeded_tests);
543 \r\rif (runner->total_of_failed_tests > 0)
544 \rprintf(", %d failed", runner->total_of_failed_tests);
545 \r\rif (runner->total_of_interrupted_tests > 0)
546 \rprintf(", %d interrupted)", runner->total_of_interrupted_tests);
550 if (!getrusage(RUSAGE_SELF, &r_usage))
553 (" Total tesh user time used: %ld second(s) %ld microsecond(s)\n",
554 r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec);
556 (" Total tesh system time used: %ld second(s) %ld microsecond(s)\n\n",
557 r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec);
558 \r\rif (!getrusage(RUSAGE_CHILDREN, &r_usage))
561 (" Total children user time used: %ld second(s) %ld microsecond(s)\n",
562 r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec);
564 (" Total children system time used: %ld second(s) %ld microsecond(s)\n\n",
565 r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec);
570 \rif (GetProcessTimes
571 (GetCurrentProcess(), &start_time, &exit_time, &kernel_time,
574 \rFileTimeToSystemTime(&user_time, &si);
576 (" User time used: %2u Hour(s) %2u Minute(s) %2u Second(s) %3u Millisecond(s)\n",
577 si.wHour, si.wMinute, si.wSecond, si.wMilliseconds);
578 \r\rFileTimeToSystemTime(&kernel_time, &si);
580 (" Kernel time used: %2u Hour(s) %2u Minute(s) %2u Second(s) %3u Millisecond(s)\n",
581 si.wHour, si.wMinute, si.wSecond, si.wMilliseconds);
590 \rERROR0("Syntax NOK");
593 \rINFO0("Syntax 0K");
597 \r\rint
\r runner_is_timedout(void)
\r
599 \rreturn runner->timeouted;