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(_XBT_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
97 /* the environment, as specified by the opengroup, used to initialize the process properties */
\r
98 # define environ **wenviron;
\r
100 extern char **environ;
\r
109 /* the unique tesh runner */
\r
113 /* wait for the tesh runner terminaison */
\r
118 runner_start_routine(void* p);
\r
121 /* check the syntax of the tesh files if
\r
122 * the check_syntax_flag is specified. Returns
\r
123 * 0 if the syntax is clean.
\r
126 check_syntax(void);*/
\r
131 timer_handle = NULL;
\r
135 runner_start_routine(void* p)
\r
140 li.QuadPart=- runner->timeout * 10000000; /* 10000000 = 10 000 000 * 100 nanoseconds = 1 second */
\r
142 /* create the waitable timer */
\r
143 timer_handle = CreateWaitableTimer(NULL, TRUE, NULL);
\r
145 /* set a timer to wait for timeout seconds */
\r
146 SetWaitableTimer(timer_handle, &li, 0, NULL, NULL, 0);
\r
148 /* wait for the timer */
\r
149 WaitForSingleObject(timer_handle, INFINITE);
\r
151 if(runner->waiting)
\r
153 exit_code = ELEADTIME;
\r
155 runner->timeouted = 1;
\r
156 xbt_os_sem_release(units_sem);
\r
164 runner_start_routine(void* p)
\r
166 struct timespec ts;
\r
167 int timeout = runner->timeout;
\r
170 while(timeout-- && runner->waiting)
\r
177 nanosleep(&ts, &ts);
\r
178 }while(EINTR == errno);
\r
183 /* TODO process the error */
\r
187 if(runner->waiting)
\r
189 exit_code = ELEADTIME;
\r
191 runner->timeouted = 1;
\r
192 xbt_os_sem_release(units_sem);
\r
202 runner_init(/*int check_syntax_flag, */int timeout, fstreams_t fstreams)
\r
207 char buffer[PATH_MAX + 1] = {0};
\r
211 variable_t variable;
\r
213 #if (defined(__CHKCMD) && defined(__BUILTIN) && !defined(_XBT_WIN32))
\r
224 ERROR0("The runner is already initialized");
\r
228 runner = xbt_new0(s_runner_t, 1);
\r
230 runner->path = NULL;
\r
231 runner->builtin = NULL;
\r
233 if(!(runner->units = units_new(runner, fstreams)))
\r
240 runner->timeout = timeout;
\r
241 runner->timeouted = 0;
\r
242 runner->interrupted = 0;
\r
243 runner->number_of_ended_units = 0;
\r
244 runner->number_of_runned_units = 0;
\r
245 runner->waiting = 0;
\r
247 runner->total_of_tests = 0;
\r
248 runner->total_of_successeded_tests = 0;
\r
249 runner->total_of_failed_tests = 0;
\r
250 runner->total_of_interrupted_tests = 0;
\r
252 runner->total_of_units = 0;
\r
253 runner->total_of_successeded_units = 0;
\r
254 runner->total_of_failed_units = 0;
\r
255 runner->total_of_interrupted_units = 0;
\r
257 runner->total_of_suites = 0;
\r
258 runner->total_of_successeded_suites = 0;
\r
259 runner->total_of_failed_suites = 0;
\r
260 runner->total_of_interrupted_suites = 0;
\r
262 /* initialize the vector of variables */
\r
263 runner->variables = xbt_dynar_new(sizeof(variable_t), (void_f_pvoid_t)variable_free);
\r
265 /* add the environment variables in the vector */
\r
266 for(i = 0; environ[i] != NULL; i++)
\r
268 val = strchr(environ[i], '=');
\r
275 strncpy(buffer, environ[i], (val - environ[i] -1));
\r
277 if(!strcmp("TESH_PPID", buffer))
\r
280 variable = variable_new(buffer, val);
\r
282 xbt_dynar_push(runner->variables, &variable);
\r
285 if(!strcmp("PATH", buffer))
\r
287 if(!strcmp("Path", buffer) || !strcmp("PATH", buffer))
\r
293 /* get the list of paths */
\r
296 runner->path = explode(';', val);
\r
298 runner->path = explode(':', val);
\r
301 /* remove spaces and backslahes at the end of the path */
\r
302 for (k = 0; runner->path[k] != NULL; k++)
\r
304 p = runner->path[k];
\r
309 for(j = len - 1; p[j] == '/' || p[j] == ' '; j--)
\r
311 for(j = len - 1; p[j] == '\\' || p[j] == ' '; j--)
\r
318 memset(buffer, 0, PATH_MAX + 1);
\r
324 char* tesh_dir = getcwd(NULL, 0);
\r
326 sprintf(buffer,"%d",getpid());
\r
329 setenv("TESH_PPID", buffer, 0);
\r
330 setenv("TESH_DIR", tesh_dir, 0);
\r
332 SetEnvironmentVariable("TESH_PPID", buffer);
\r
333 SetEnvironmentVariable("TESH_DIR", tesh_dir);
\r
336 variable = variable_new("TESH_PPID", buffer);
\r
339 xbt_dynar_push(runner->variables, &variable);
\r
341 variable = variable_new("TESH_DIR", tesh_dir);
\r
344 xbt_dynar_push(runner->variables, &variable);
\r
349 variable = variable_new("EXIT_SUCCESS", "0");
\r
352 xbt_dynar_push(runner->variables, &variable);
\r
354 variable = variable_new("EXIT_FAILURE", "1");
\r
357 xbt_dynar_push(runner->variables, &variable);
\r
359 variable = variable_new("TRUE", "0");
\r
362 xbt_dynar_push(runner->variables, &variable);
\r
364 variable = variable_new("FALSE", "1");
\r
367 xbt_dynar_push(runner->variables, &variable);
\r
371 /* add the errors variables */
\r
372 while((cstr = error_get_at(i++, &code)))
\r
374 sprintf(buffer,"%d",code);
\r
375 variable = variable_new(cstr, buffer);
\r
377 xbt_dynar_push(runner->variables, &variable);
\r
380 /* if the user want check the syntax, check it */
\r
381 /*if(check_syntax_flag)
\r
385 #if (!defined(_XBT_WIN32) && defined(__CHKCMD))
\r
386 #if defined(__BUILTIN)
\r
390 /* compute the full path the builtin.def file */
\r
391 sprintf(buffer,"%s/builtin.def",getenv("TESH_DIR"));
\r
393 if(!(s = fopen(buffer, "r")))
\r
395 ERROR1("File `(%s)' not found", buffer);
\r
402 if(!(s = fopen("builtin.def", "r")))
\r
404 ERROR0("File `(builtin.def)' not found");
\r
413 fgetpos(s, &begin);
\r
415 while(readline(s, &line, &len) != -1)
\r
421 while(line[i] != '\0')
\r
423 if (line[i] != ' ' && line[i] != '\t' && line[i]!='\n' && line[i]!='\r')
\r
436 fsetpos(s, &begin);
\r
444 runner->builtin = xbt_new0(char*, n + 1); /* (char**) calloc(n + 1, sizeof(char*));*/
\r
448 while(readline(s, &line, &len) != -1)
\r
453 while(line[i] != '\0')
\r
455 if (line[i] != ' ' && line[i] != '\t' && line[i]!='\n' && line[i]!='\r')
\r
468 l[strlen(l) - 1] = '\0';
\r
470 (runner->builtin)[n++] = l;
\r
478 WARN0("The file `(builtin.def)' is empty");
\r
479 free(runner->builtin);
\r
480 runner->builtin = NULL;
\r
492 runner->builtin = xbt_new0(char*, __BUILTIN_MAX + 1); /* (char**) calloc(__BUILTIN_MAX + 1, sizeof(char*));*/
\r
494 for(i = 0; i < __BUILTIN_MAX; i++)
\r
495 runner->builtin[i] = strdup(builtin[i]);
\r
499 return exit_code ? -1 : 0;
\r
503 runner_destroy(void)
\r
508 units_free((void**)(&(runner->units)));
\r
510 if(runner->variables)
\r
511 xbt_dynar_free(&runner->variables);
\r
514 CloseHandle(timer_handle);
\r
518 xbt_os_thread_join(runner->thread, NULL);
\r
522 for (i = 0; runner->path[i] != NULL; i++)
\r
523 free(runner->path[i]);
\r
525 free(runner->path);
\r
528 if(runner->builtin)
\r
530 for (i = 0; runner->builtin[i] != NULL; i++)
\r
531 free(runner->builtin[i]);
\r
533 free(runner->builtin);
\r
545 /* allocate the mutex used by the units to asynchronously access
\r
546 * to the properties of the runner.
\r
548 xbt_os_mutex_t mutex = xbt_os_mutex_init();
\r
550 /* run all the units */
\r
551 units_run_all(runner->units, mutex);
\r
558 /* if the runner is timeouted or receive a interruption request
\r
559 * , interrupt all the active units.
\r
561 if(runner->timeouted || interrupted)
\r
562 runner_interrupt();
\r
564 /* joins all the units */
\r
565 units_join_all(runner->units);
\r
567 /* release the mutex resource */
\r
568 xbt_os_mutex_destroy(mutex);
\r
575 if(runner->timeout > 0)
\r
576 runner->thread = xbt_os_thread_create("", runner_start_routine, NULL);
\r
578 /* signal that the runner is waiting */
\r
579 runner->waiting = 1;
\r
581 /* wait for the end of all the units */
\r
582 xbt_os_sem_acquire(units_sem);
\r
585 runner->waiting = 0;
\r
591 * interrupt all the active units.
\r
592 * this function is called when the lead time of the execution is reached
\r
593 * or when a failed unit requests an interruption of the execution.
\r
596 runner_interrupt(void)
\r
598 units_interrupt_all(runner->units);
\r
602 runner_summarize(void)
\r
608 struct rusage r_usage;
\r
610 FILETIME start_time;
\r
611 FILETIME exit_time;
\r
612 FILETIME kernel_time;
\r
613 FILETIME user_time;
\r
617 printf("\n TEst SHell utility - mini shell specialized in running test units.\n");
\r
618 printf(" =============================================================================\n");
\r
620 units_summuarize(runner->units);
\r
622 printf(" =====================================================================%s\n",
\r
623 runner->total_of_failed_tests ? "== FAILED": (runner->total_of_interrupted_tests || runner->total_of_interrupted_units) ? "==== INTR" : "====== OK");
\r
625 printf(" TOTAL : Suite(s): %.0f%% ok (%d suite(s): %d ok",
\r
626 (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
627 runner->total_of_suites, runner->total_of_successeded_suites);
\r
629 if(runner->total_of_failed_suites > 0)
\r
630 printf(", %d failed", runner->total_of_failed_suites);
\r
632 if(runner->total_of_interrupted_suites > 0)
\r
633 printf(", %d interrupted)", runner->total_of_interrupted_suites);
\r
637 printf(" Unit(s): %.0f%% ok (%d unit(s): %d ok",
\r
638 (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
639 runner->total_of_units, runner->total_of_successeded_units);
\r
641 if(runner->total_of_failed_units > 0)
\r
642 printf(", %d failed", runner->total_of_failed_units);
\r
644 if(runner->total_of_interrupted_units > 0)
\r
645 printf(", %d interrupted)", runner->total_of_interrupted_units);
\r
649 printf(" Test(s): %.0f%% ok (%d test(s): %d ok",
\r
650 (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
651 runner->total_of_tests, runner->total_of_successeded_tests);
\r
653 if(runner->total_of_failed_tests > 0)
\r
654 printf(", %d failed", runner->total_of_failed_tests);
\r
656 if(runner->total_of_interrupted_tests > 0)
\r
657 printf(", %d interrupted)", runner->total_of_interrupted_tests);
\r
662 if(!getrusage(RUSAGE_SELF, &r_usage))
\r
665 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
666 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
668 if(!getrusage(RUSAGE_CHILDREN, &r_usage))
\r
670 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
671 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
677 if(GetProcessTimes(GetCurrentProcess(), &start_time, &exit_time, &kernel_time, &user_time))
\r
679 FileTimeToSystemTime(&user_time, &si);
\r
681 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
683 FileTimeToSystemTime(&kernel_time, &si);
\r
685 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
695 ERROR0("Syntax NOK");
\r
696 else if(!exit_code)
\r
697 INFO0("Syntax 0K");
\r
702 runner_is_timedout(void)
\r
704 return runner->timeouted;
\r