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
35 #define _RUNNER_HASHCODE 0xFEFEAAAA
\r
37 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
\r
39 #if (!defined(__BUILTIN) && defined(__CHKCMD) && !defined(WIN32))
\r
40 static const char* builtin[] =
\r
87 #define __BUILTIN_MAX ((size_t)42)
\r
92 /* 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
93 # include <crt_externs.h>
\r
94 # define environ (*_NSGetEnviron())
\r
95 # elif !defined(WIN32)
\r
96 /* the environment, as specified by the opengroup, used to initialize the process properties */
\r
97 extern char **environ;
\r
105 /* the unique tesh runner */
\r
109 /* wait for the tesh runner terminaison */
\r
114 runner_start_routine(void* p);
\r
117 /* check the syntax of the tesh files if
\r
118 * the check_syntax_flag is specified. Returns
\r
119 * 0 if the syntax is clean.
\r
122 check_syntax(void);*/
\r
127 timer_handle = NULL;
\r
131 runner_start_routine(void* p)
\r
136 li.QuadPart=- runner->timeout * 10000000; /* 10000000 = 10 000 000 * 100 nanoseconds = 1 second */
\r
138 /* create the waitable timer */
\r
139 timer_handle = CreateWaitableTimer(NULL, TRUE, NULL);
\r
141 /* set a timer to wait for timeout seconds */
\r
142 SetWaitableTimer(timer_handle, &li, 0, NULL, NULL, 0);
\r
144 /* wait for the timer */
\r
145 WaitForSingleObject(timer_handle, INFINITE);
\r
147 if(runner->waiting)
\r
149 exit_code = ELEADTIME;
\r
151 runner->timeouted = 1;
\r
152 xbt_os_sem_release(units_sem);
\r
160 runner_start_routine(void* p)
\r
162 struct timespec ts;
\r
163 int timeout = runner->timeout;
\r
166 while(timeout-- && runner->waiting)
\r
173 nanosleep(&ts, &ts);
\r
174 }while(EINTR == errno);
\r
179 /* TODO process the error */
\r
183 if(runner->waiting)
\r
185 exit_code = ELEADTIME;
\r
187 runner->timeouted = 1;
\r
188 xbt_os_sem_release(units_sem);
\r
198 runner_init(/*int check_syntax_flag, */int timeout, fstreams_t fstreams)
\r
203 char buffer[PATH_MAX + 1] = {0};
\r
207 variable_t variable;
\r
209 #if (defined(__CHKCMD) && defined(__BUILTIN) && !defined(WIN32))
\r
220 ERROR0("The runner is already initialized");
\r
224 runner = xbt_new0(s_runner_t, 1);
\r
226 runner->path = NULL;
\r
227 runner->builtin = NULL;
\r
229 if(!(runner->units = units_new(runner, fstreams)))
\r
236 runner->timeout = timeout;
\r
237 runner->timeouted = 0;
\r
238 runner->interrupted = 0;
\r
239 runner->number_of_ended_units = 0;
\r
240 runner->number_of_runned_units = 0;
\r
241 runner->waiting = 0;
\r
243 runner->total_of_tests = 0;
\r
244 runner->total_of_successeded_tests = 0;
\r
245 runner->total_of_failed_tests = 0;
\r
246 runner->total_of_interrupted_tests = 0;
\r
248 runner->total_of_units = 0;
\r
249 runner->total_of_successeded_units = 0;
\r
250 runner->total_of_failed_units = 0;
\r
251 runner->total_of_interrupted_units = 0;
\r
253 runner->total_of_suites = 0;
\r
254 runner->total_of_successeded_suites = 0;
\r
255 runner->total_of_failed_suites = 0;
\r
256 runner->total_of_interrupted_suites = 0;
\r
258 /* initialize the vector of variables */
\r
259 runner->variables = xbt_dynar_new(sizeof(variable_t), (void_f_pvoid_t)variable_free);
\r
261 /* add the environment variables in the vector */
\r
262 for(i = 0; environ[i] != NULL; i++)
\r
264 val = strchr(environ[i], '=');
\r
271 strncpy(buffer, environ[i], (val - environ[i] -1));
\r
273 if(!strcmp("TESH_PPID", buffer))
\r
276 variable = variable_new(buffer, val);
\r
278 xbt_dynar_push(runner->variables, &variable);
\r
281 if(!strcmp("PATH", buffer))
\r
283 if(!strcmp("Path", buffer) || !strcmp("PATH", buffer))
\r
289 /* get the list of paths */
\r
292 runner->path = explode(';', val);
\r
294 runner->path = explode(':', val);
\r
297 /* remove spaces and backslahes at the end of the path */
\r
298 for (k = 0; runner->path[k] != NULL; k++)
\r
300 p = runner->path[k];
\r
305 for(j = len - 1; p[j] == '/' || p[j] == ' '; j--)
\r
307 for(j = len - 1; p[j] == '\\' || p[j] == ' '; j--)
\r
314 memset(buffer, 0, PATH_MAX + 1);
\r
320 char* tesh_dir = getcwd(NULL, 0);
\r
322 sprintf(buffer,"%d",getpid());
\r
325 setenv("TESH_PPID", buffer, 0);
\r
326 setenv("TESH_DIR", tesh_dir, 0);
\r
328 SetEnvironmentVariable("TESH_PPID", buffer);
\r
329 SetEnvironmentVariable("TESH_DIR", tesh_dir);
\r
332 variable = variable_new("TESH_PPID", buffer);
\r
335 xbt_dynar_push(runner->variables, &variable);
\r
337 variable = variable_new("TESH_DIR", tesh_dir);
\r
340 xbt_dynar_push(runner->variables, &variable);
\r
345 variable = variable_new("EXIT_SUCCESS", "0");
\r
348 xbt_dynar_push(runner->variables, &variable);
\r
350 variable = variable_new("EXIT_FAILURE", "1");
\r
353 xbt_dynar_push(runner->variables, &variable);
\r
355 variable = variable_new("TRUE", "0");
\r
358 xbt_dynar_push(runner->variables, &variable);
\r
360 variable = variable_new("FALSE", "1");
\r
363 xbt_dynar_push(runner->variables, &variable);
\r
367 /* add the errors variables */
\r
368 while((cstr = error_get_at(i++, &code)))
\r
370 sprintf(buffer,"%d",code);
\r
371 variable = variable_new(cstr, buffer);
\r
373 xbt_dynar_push(runner->variables, &variable);
\r
376 /* if the user want check the syntax, check it */
\r
377 /*if(check_syntax_flag)
\r
381 #if (!defined(WIN32) && defined(__CHKCMD))
\r
382 #if defined(__BUILTIN)
\r
386 /* compute the full path the builtin.def file */
\r
387 sprintf(buffer,"%s/builtin.def",getenv("TESH_DIR"));
\r
389 if(!(s = fopen(buffer, "r")))
\r
391 ERROR1("File `(%s)' not found", buffer);
\r
398 if(!(s = fopen("builtin.def", "r")))
\r
400 ERROR0("File `(builtin.def)' not found");
\r
409 fgetpos(s, &begin);
\r
411 while(readline(s, &line, &len) != -1)
\r
417 while(line[i] != '\0')
\r
419 if (line[i] != ' ' && line[i] != '\t' && line[i]!='\n' && line[i]!='\r')
\r
432 fsetpos(s, &begin);
\r
440 runner->builtin = xbt_new0(char*, n + 1); /* (char**) calloc(n + 1, sizeof(char*));*/
\r
444 while(readline(s, &line, &len) != -1)
\r
449 while(line[i] != '\0')
\r
451 if (line[i] != ' ' && line[i] != '\t' && line[i]!='\n' && line[i]!='\r')
\r
464 l[strlen(l) - 1] = '\0';
\r
466 (runner->builtin)[n++] = l;
\r
474 WARN0("The file `(builtin.def)' is empty");
\r
475 free(runner->builtin);
\r
476 runner->builtin = NULL;
\r
488 runner->builtin = xbt_new0(char*, __BUILTIN_MAX + 1); /* (char**) calloc(__BUILTIN_MAX + 1, sizeof(char*));*/
\r
490 for(i = 0; i < __BUILTIN_MAX; i++)
\r
491 runner->builtin[i] = strdup(builtin[i]);
\r
495 return exit_code ? -1 : 0;
\r
499 runner_destroy(void)
\r
504 units_free((void**)(&(runner->units)));
\r
506 if(runner->variables)
\r
507 xbt_dynar_free(&runner->variables);
\r
510 CloseHandle(timer_handle);
\r
514 xbt_os_thread_join(runner->thread, NULL);
\r
518 for (i = 0; runner->path[i] != NULL; i++)
\r
519 free(runner->path[i]);
\r
521 free(runner->path);
\r
524 if(runner->builtin)
\r
526 for (i = 0; runner->builtin[i] != NULL; i++)
\r
527 free(runner->builtin[i]);
\r
529 free(runner->builtin);
\r
541 /* allocate the mutex used by the units to asynchronously access
\r
542 * to the properties of the runner.
\r
544 xbt_os_mutex_t mutex = xbt_os_mutex_init();
\r
546 /* run all the units */
\r
547 units_run_all(runner->units, mutex);
\r
554 /* if the runner is timeouted or receive a interruption request
\r
555 * , interrupt all the active units.
\r
557 if(runner->timeouted || interrupted)
\r
558 runner_interrupt();
\r
560 /* joins all the units */
\r
561 units_join_all(runner->units);
\r
563 /* release the mutex resource */
\r
564 xbt_os_mutex_destroy(mutex);
\r
571 if(runner->timeout > 0)
\r
572 runner->thread = xbt_os_thread_create("", runner_start_routine, NULL);
\r
574 /* signal that the runner is waiting */
\r
575 runner->waiting = 1;
\r
577 /* wait for the end of all the units */
\r
578 xbt_os_sem_acquire(units_sem);
\r
581 runner->waiting = 0;
\r
587 * interrupt all the active units.
\r
588 * this function is called when the lead time of the execution is reached
\r
589 * or when a failed unit requests an interruption of the execution.
\r
592 runner_interrupt(void)
\r
594 units_interrupt_all(runner->units);
\r
598 runner_summarize(void)
\r
604 struct rusage r_usage;
\r
606 FILETIME start_time;
\r
607 FILETIME exit_time;
\r
608 FILETIME kernel_time;
\r
609 FILETIME user_time;
\r
613 printf("\n TEst SHell utility - mini shell specialized in running test units.\n");
\r
614 printf(" =============================================================================\n");
\r
616 units_summuarize(runner->units);
\r
618 printf(" =====================================================================%s\n",
\r
619 runner->total_of_failed_tests ? "== FAILED": (runner->total_of_interrupted_tests || runner->total_of_interrupted_units) ? "==== INTR" : "====== OK");
\r
621 printf(" TOTAL : Suite(s): %.0f%% ok (%d suite(s): %d ok",
\r
622 (runner->total_of_suites ? (1-((double)runner->total_of_failed_suites + (double)runner->total_of_interrupted_suites)/(double)runner->total_of_suites)*100.0 : 100.0),
\r
623 runner->total_of_suites, runner->total_of_successeded_suites);
\r
625 if(runner->total_of_failed_suites > 0)
\r
626 printf(", %d failed", runner->total_of_failed_suites);
\r
628 if(runner->total_of_interrupted_suites > 0)
\r
629 printf(", %d interrupted)", runner->total_of_interrupted_suites);
\r
633 printf(" Unit(s): %.0f%% ok (%d unit(s): %d ok",
\r
634 (runner->total_of_units ? (1-((double)runner->total_of_failed_units + (double)runner->total_of_interrupted_units)/(double)runner->total_of_units)*100.0 : 100.0),
\r
635 runner->total_of_units, runner->total_of_successeded_units);
\r
637 if(runner->total_of_failed_units > 0)
\r
638 printf(", %d failed", runner->total_of_failed_units);
\r
640 if(runner->total_of_interrupted_units > 0)
\r
641 printf(", %d interrupted)", runner->total_of_interrupted_units);
\r
645 printf(" Test(s): %.0f%% ok (%d test(s): %d ok",
\r
646 (runner->total_of_tests ? (1-((double)runner->total_of_failed_tests + (double)runner->total_of_interrupted_tests)/(double)runner->total_of_tests)*100.0 : 100.0),
\r
647 runner->total_of_tests, runner->total_of_successeded_tests);
\r
649 if(runner->total_of_failed_tests > 0)
\r
650 printf(", %d failed", runner->total_of_failed_tests);
\r
652 if(runner->total_of_interrupted_tests > 0)
\r
653 printf(", %d interrupted)", runner->total_of_interrupted_tests);
\r
658 if(!getrusage(RUSAGE_SELF, &r_usage))
\r
661 printf(" Total tesh user time used: %ld second(s) %ld microsecond(s)\n", r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec);
\r
662 printf(" Total tesh system time used: %ld second(s) %ld microsecond(s)\n\n", r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec);
\r
664 if(!getrusage(RUSAGE_CHILDREN, &r_usage))
\r
666 printf(" Total children user time used: %ld second(s) %ld microsecond(s)\n", r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec);
\r
667 printf(" Total children system time used: %ld second(s) %ld microsecond(s)\n\n", r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec);
\r
673 if(GetProcessTimes(GetCurrentProcess(), &start_time, &exit_time, &kernel_time, &user_time))
\r
675 FileTimeToSystemTime(&user_time, &si);
\r
677 printf(" User time used: %2u Hour(s) %2u Minute(s) %2u Second(s) %3u Millisecond(s)\n", si.wHour, si.wMinute, si.wSecond, si.wMilliseconds );
\r
679 FileTimeToSystemTime(&kernel_time, &si);
\r
681 printf(" Kernel time used: %2u Hour(s) %2u Minute(s) %2u Second(s) %3u Millisecond(s)\n", si.wHour, si.wMinute, si.wSecond, si.wMilliseconds );
\r
691 ERROR0("Syntax NOK");
\r
692 else if(!exit_code)
\r
693 INFO0("Syntax 0K");
\r
698 runner_is_timedout(void)
\r
700 return runner->timeouted;
\r