Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
tesh version 2
[simgrid.git] / tools / tesh2 / src / unit.c
diff --git a/tools/tesh2/src/unit.c b/tools/tesh2/src/unit.c
new file mode 100644 (file)
index 0000000..1f540d6
--- /dev/null
@@ -0,0 +1,757 @@
+#include <unit.h>
+#include <suite.h>
+#include <command.h>
+#include <context.h>
+#include <fstream.h>
+
+
+
+XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
+
+
+/* the unit thread start routine */
+static void*
+unit_start(void* p);
+
+unit_t
+unit_new(runner_t runner, suite_t owner, fstream_t fstream)
+{
+       unit_t unit = xbt_new0(s_unit_t, 1);
+
+       /* set the owner of the unit */
+       unit->runner = runner;
+       
+       unit->fstream = fstream;
+
+       unit->sem = NULL;
+
+       unit->commands = vector_new(DEFAULT_COMMANDS_CAPACITY, (fn_finalize_t)command_free);
+       
+       unit->thread = NULL;
+
+       unit->number_of_started_commands = 0;
+       unit->number_of_interrupted_commands = 0;
+       unit->number_of_failed_commands = 0;
+       unit->number_of_successeded_commands = 0;
+       unit->number_of_terminated_commands = 0;
+       unit->interrupted = 0;
+       unit->failed = 0;
+       unit->successeded = 0;
+       unit->parsed = 0;
+       unit->released = 0;
+       unit->parsing_include_file = 0;
+       
+       
+       unit->owner = owner;
+       unit->number = 0;
+       unit->suites = vector_new(DEFAULT_COMMANDS_CAPACITY, (fn_finalize_t)suite_free);
+       unit->owner = owner;
+       
+       unit->running_suite = 0;
+
+       return unit;
+
+}
+
+void
+unit_add_suite(unit_t unit, suite_t suite)
+{
+       vector_push_back(unit->suites, suite);
+}
+
+int
+unit_free(void** unitptr)
+{
+       unit_t* __unitptr = (unit_t*)unitptr;
+       
+       vector_free(&((*__unitptr)->commands));
+       
+       vector_free(&((*__unitptr)->suites));
+       
+       /* if the unit is interrupted during its run, the semaphore is NULL */
+       if((*__unitptr)->sem)
+               xbt_os_sem_destroy((*__unitptr)->sem);
+               
+               
+       free((*__unitptr)->suites);
+
+       free(*__unitptr);
+       
+       *__unitptr = NULL;
+       
+       return 0;
+}
+
+static void*
+unit_start(void* p) 
+{
+       
+       xbt_os_thread_t thread;
+       xbt_os_mutex_t mutex;
+       context_t context;
+       int i;
+
+       unit_t unit = (unit_t)p;
+       
+       xbt_os_mutex_acquire(unit->mutex);
+       unit->runner->number_of_runned_units++;
+       xbt_os_mutex_release(unit->mutex);
+
+       /* try to acquire the jobs semaphore to start */
+       xbt_os_sem_acquire(jobs_sem);
+       
+       mutex = xbt_os_mutex_init();
+       context = context_new();
+       
+       if(want_dry_run)
+               INFO1("checking unit %s...",unit->fstream->name); 
+       
+       /* parse the file */
+       unit_parse(unit, context, mutex, unit->fstream->name, unit->fstream->stream);
+       
+       /* if the unit is not interrupted and not failed the unit, all the file is parsed
+        * so all the command are launched
+        */
+       if(!unit->interrupted)
+       {
+               unit->parsed = 1;
+               
+               /* all the commands have terminate before the end of the parsing of the tesh file
+                * so the unit release the semaphore itself
+                */
+               if(!unit->released && (unit->number_of_started_commands == (unit->number_of_failed_commands + unit->number_of_interrupted_commands + unit->number_of_successeded_commands)))
+                       xbt_os_sem_release(unit->sem);  
+               
+       }
+       
+       /* wait the end of all the commands or a command failure or an interruption */
+       
+       
+       xbt_os_sem_acquire(unit->sem);
+       
+       if(unit->interrupted)
+       {
+               command_t command;
+
+               /* interrupt all the running commands of the unit */ 
+               for(i = 0; i < unit->number_of_commands; i++)
+               {
+                       /*command = unit->commands[i];*/
+                       command = vector_get_at(unit->commands, i);
+
+                       if(command->status == cs_in_progress)
+                               /*command_interrupt(unit->commands[i]);*/
+                               command_interrupt(command);
+               }
+       }
+       
+
+       /* wait the end of the threads */
+       for(i = 0; i < unit->number_of_commands; i++)
+       {
+               /*thread = unit->commands[i]->thread;*/
+               
+               command_t command = vector_get_at(unit->commands, i);
+               thread = command->thread;
+               
+               if(thread)
+                       xbt_os_thread_join(thread,NULL);
+       }
+       
+       context_free(&context);
+
+       xbt_os_mutex_destroy(mutex);
+       
+       xbt_os_mutex_acquire(unit->mutex);
+
+       /* increment the number of ended units */
+       unit->runner->number_of_ended_units++;
+       
+       /* it's the last unit, release the runner */
+       if(/*!unit->interrupted &&*/ (unit->runner->number_of_runned_units == unit->runner->number_of_ended_units))
+       {
+               if(unit->number_of_successeded_commands == unit->number_of_commands)
+                       unit->successeded = 1;
+
+               /* first release the mutex */
+               xbt_os_mutex_release(unit->mutex);
+               xbt_os_sem_release(units_sem);
+       }
+       else
+               xbt_os_mutex_release(unit->mutex);
+
+       /* release the jobs semaphore, then the next unit can start */
+       xbt_os_sem_release(jobs_sem);
+       
+       return NULL;
+
+}
+
+void
+unit_parse(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name, FILE* stream)
+{
+       size_t len;
+       char * line = NULL;
+       int line_num=0;
+       char file_pos[256];
+       xbt_strbuff_t buff;
+       int buffbegin = 0; 
+       
+       /* Count the line length while checking wheather it's blank */
+       int blankline;
+       int linelen;    
+       /* Deal with \ at the end of the line, and call handle_line on result */
+       int to_be_continued;
+       
+       buff=xbt_strbuff_new();
+       
+       while(!unit->interrupted  && getline(&line, &len, stream) != -1)
+       {
+               blankline=1;
+               linelen = 0;    
+               to_be_continued = 0;
+
+               line_num++;
+               
+               while(line[linelen] != '\0') 
+               {
+                       if (line[linelen] != ' ' && line[linelen] != '\t' && line[linelen]!='\n' && line[linelen]!='\r')
+                               blankline = 0;
+                       
+                       linelen++;
+               }
+       
+               if(blankline) 
+               {
+                       if(!context->command_line && (context->input->used || context->output->used))
+                       {
+                               ERROR1("[%d] Error: no command found in this chunk of lines.",buffbegin);
+                               
+                               if(unit->parsing_include_file)
+                                       ERROR1("Unit `%s': NOK (syntax error)", unit->fstream->name);
+                               else
+                                       ERROR2("Unit `%s' inclued in `%s' : NOK (syntax error)", file_name, unit->fstream->name);       
+                               
+                               exit_code = ESYNTAX;
+                               unit_handle_failure(unit);
+                               break;
+                       }
+                       
+                       if(context->command_line)
+                       {
+                               if(!want_dry_run)
+                               {
+                                       command_t command = command_new(unit, context, mutex);
+                                       command_run(command);
+                               }
+                               
+                               context_reset(context);
+                       }
+               
+               
+                       continue;
+                       
+               }
+               
+               if(linelen>1 && line[linelen-2]=='\\') 
+               {
+                       if (linelen>2 && line[linelen-3] == '\\') 
+                       {
+                               /* Damn. Escaped \ */
+                               line[linelen-2] = '\n';
+                               line[linelen-1] = '\0';
+                       } 
+                       else 
+                       {
+                               to_be_continued = 1;
+                               line[linelen-2] = '\0';
+                               linelen -= 2;  
+                               
+                               if (!buff->used)
+                                       buffbegin = line_num;
+                       }
+               }
+       
+               if(buff->used || to_be_continued) 
+               { 
+                       xbt_strbuff_append(buff,line);
+       
+                       if (!to_be_continued) 
+                       {
+                               snprintf(file_pos,256,"%s:%d",file_name,buffbegin);
+                               unit_handle_line(unit, context, mutex, file_pos, buff->data);    
+                               xbt_strbuff_empty(buff);
+                       }
+               } 
+               else 
+               {
+                       snprintf(file_pos,256,"%s:%d",file_name,line_num);
+                       unit_handle_line(unit, context, mutex, file_pos, line);      
+               }
+       }
+       
+       /* Check that last command of the file ran well */
+       if(context->command_line)
+       {
+               if(!want_dry_run)
+               {
+                       command_t command = command_new(unit, context, mutex);
+                       command_run(command);
+               }
+               
+               context_reset(context);
+       }
+
+       /* Clear buffers */
+       if (line)
+               free(line);
+               
+       xbt_strbuff_free(buff); 
+}
+
+void 
+unit_handle_line(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char * filepos, char *line) 
+{
+       /* Search end */
+       xbt_str_rtrim(line+2,"\n");
+       
+       switch (line[0]) 
+       {
+               case '#': 
+               break;
+               
+               case '$':
+               case '&':
+                       
+               context->async = (line[0] == '&');
+               
+               /* further trim useless chars which are significant for in/output */
+               xbt_str_rtrim(line+2," \t");
+               
+               /* Deal with CD commands here, not in rctx */
+               if (!strncmp("cd ",line+2,3)) 
+               {
+                       char *dir=line+4;
+               
+                       if (context->command_line)
+                       {
+                               if(!want_dry_run)
+                               {
+                                       command_t command = command_new(unit, context, mutex);
+                                       command_run(command);
+                               }
+                               
+                               context_reset(context);
+                       }
+               
+                       /* search begining */
+                       while (*(dir++) == ' ');
+                       
+                       dir--;
+                       
+                       VERB1("Saw cd '%s'",dir);
+                       
+                       /*if(want_dry_run)
+                       {
+                               unit->number_of_successeded_commands++;
+                       }*/
+                       if(!want_dry_run)
+                       {
+                               if(chdir(dir))
+                               {
+                                       ERROR2("Chdir to %s failed: %s",dir,strerror(errno));
+                                       ERROR1("Test suite `%s': NOK (system error)", unit->fstream->name);
+                                       exit_code = ECHDIR;
+                                       unit_handle_failure(unit);
+                               }
+                       }
+                       
+                       break;
+               } /* else, pushline */
+               else
+               {
+                       unit_pushline(unit, context, mutex, filepos, line[0], line+2 /* pass '$ ' stuff*/);
+                       break;
+               }
+               
+               case '<':
+               case '>':
+               case '!':
+               unit_pushline(unit, context, mutex, filepos, line[0], line+2 /* pass '$ ' stuff*/);    
+               break;
+               
+               case 'p':
+               if(!want_dry_run)
+                       INFO2("[%s] %s",filepos,line+2);
+               break;
+               
+               case 'P':
+               if(!want_dry_run)
+                       CRITICAL2("[%s] %s",filepos,line+2);
+               break;
+               
+               default:
+               ERROR2("[%s] Syntax error: %s",filepos, line);
+               ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name);
+               exit_code = ESYNTAX;
+               unit_handle_failure(unit);
+               break;
+       }
+}
+
+void 
+unit_pushline(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* filepos, char kind, char *line) 
+{
+       switch (kind) 
+       {
+               case '$':
+               case '&':
+               
+               if(context->command_line) 
+               {
+                       command_t command;
+                       
+                       if(context->output->used || context->input->used) 
+                       {
+                               ERROR2("[%s] More than one command in this chunk of lines (previous: %s).\nDunno which input/output belongs to which command.",filepos,context->command_line);
+                               ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name);
+                               exit_code = ESYNTAX;
+                               unit_handle_failure(unit);
+                               return;
+                       }
+                       
+                       if(!want_dry_run)
+                       {
+                               command = command_new(unit, context, mutex);
+                               command_run(command);
+                       }
+                       
+                       context_reset(context);
+                       
+                       VERB1("[%s] More than one command in this chunk of lines",filepos);
+               }
+               
+               context->command_line = strdup(line);
+               
+               
+               context->line = strdup(filepos);
+               /*INFO2("[%s] %s",filepos,context->command_line);*/
+               
+               break;
+               
+               case '<':
+               xbt_strbuff_append(context->input,line);
+               xbt_strbuff_append(context->input,"\n");
+               break;
+               
+               case '>':
+               xbt_strbuff_append(context->output,line);
+               xbt_strbuff_append(context->output,"\n");
+               break;
+               
+               case '!':
+               
+               if(context->command_line)
+               {
+                       if(!want_dry_run)
+                       {
+                               command_t command = command_new(unit, context, mutex);
+                               command_run(command);
+                       }
+                       
+                       context_reset(context);
+               }
+               
+               if(!strncmp(line,"timeout no",strlen("timeout no"))) 
+               {
+                       VERB1("[%s] (disable timeout)", filepos);
+                       context->timeout = INDEFINITE;
+               } 
+               else if(!strncmp(line,"timeout ",strlen("timeout "))) 
+               {
+                       int i = 0;
+                       char* p = line + strlen("timeout ");
+       
+                       while(p[i] != '\0')
+                       {
+                               if(!isdigit(p[i]))
+                               {
+                                       exit_code = ESYNTAX;
+                                       ERROR2("Invalid timeout value `%s' at %s ", line + strlen("timeout "), filepos);
+                                       unit_handle_failure(unit);
+                               }
+
+                               i++;
+                       }
+                       
+                       context->timeout = atoi(line + strlen("timeout"));
+                       VERB2("[%s] (new timeout value: %d)",filepos,context->timeout);
+               
+               } 
+               else if (!strncmp(line,"expect signal ",strlen("expect signal "))) 
+               {
+                       context->signal = strdup(line + strlen("expect signal "));
+                       
+                       #ifdef WIN32
+                       if(!strstr(context->signal,"SIGSEGVSIGTRAPSIGBUSSIGFPESIGILL"))
+                       {
+                               exit_code = ESYNTAX;
+                               /*ERROR2("Signal `%s' not supported at %s", line + strlen("expect signal "), filepos);*/
+                               unit_handle_failure(unit);
+                       }
+                       #endif
+
+                       xbt_str_trim(context->signal," \n");
+                       VERB2("[%s] (next command must raise signal %s)", filepos, context->signal);
+               
+               } 
+               else if (!strncmp(line,"expect return ",strlen("expect return "))) 
+               {
+
+                       int i = 0;
+                       char* p = line + strlen("expect return ");
+                       
+                       
+                       while(p[i] != '\0')
+                       {
+                               if(!isdigit(p[i]))
+                               {
+                                       exit_code = ESYNTAX;
+                                       ERROR2("Invalid exit code value `%s' at %s ", line + strlen("expect return "), filepos);
+                                       unit_handle_failure(unit);
+                               }
+
+                               i++;
+                       }
+
+                       context->exit_code = atoi(line+strlen("expect return "));
+                       VERB2("[%s] (next command must return code %d)",filepos, context->exit_code);
+               
+               } 
+               else if (!strncmp(line,"output ignore",strlen("output ignore"))) 
+               {
+                       context->output_handling = oh_ignore;
+                       VERB1("[%s] (ignore output of next command)", filepos);
+               
+               } 
+               else if (!strncmp(line,"output display",strlen("output display"))) 
+               {
+                       context->output_handling = oh_display;
+                       VERB1("[%s] (ignore output of next command)", filepos);
+               
+               } 
+               else if(!strncmp(line,"include", strlen("include")))
+               {
+                       unit_handle_include(unit, context, mutex, line + strlen("include "));
+               }
+               
+               else if(!strncmp(line,"suite", strlen("suite")))
+               {
+                       unit_handle_suite(unit, context, mutex, line + strlen("suite "));
+               }
+               else 
+               {
+                       ERROR2("%s: Malformed metacommand: %s",filepos,line);
+                       ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name);
+                       exit_code = ESYNTAX;
+                       unit_handle_failure(unit);
+                       return;
+               }
+               
+               break;
+       }
+}
+
+
+void
+unit_handle_failure(unit_t unit)
+{
+       if(!want_keep_going_unit)
+       {
+               if(!unit->interrupted)
+               {
+                       /* the unit interrupted (exit for the loop) */
+                       unit->interrupted = 1;
+
+                       /* release the unit */
+                       xbt_os_sem_release(unit->sem);
+               }
+
+               /* if the --keep-going option is not specified */
+               if(!want_keep_going)
+               {
+                       if(!interrupted)
+                       {
+                               /* request an global interruption by the runner */
+                               interrupted = 1;
+
+                               /* release the runner */
+                               xbt_os_sem_release(units_sem);
+                       }
+               }
+       }
+}
+
+void
+unit_run(unit_t unit, xbt_os_mutex_t mutex)
+{
+       if(!interrupted)
+       {
+               unit->mutex = mutex;
+               
+               unit->sem = xbt_os_sem_init(0);
+
+               /* start the unit */
+               unit->thread = xbt_os_thread_create("", unit_start, unit);
+       }
+       else
+               /* the unit is interrupted by the runner before its starting 
+                * in this case the unit semaphore is NULL take care of that
+                * in the function unit_free()
+                */
+               unit->interrupted = 1;
+
+       
+}
+
+void
+unit_interrupt(unit_t unit)
+{
+       /* interrupt the loop */
+       unit->interrupted = 1;
+       xbt_os_sem_release(unit->sem);
+}
+
+void
+unit_verbose(unit_t unit)
+{
+       int i, test_count;
+       command_t command;
+
+       printf("\nUnit : %s (%s)\n", unit->fstream->name, unit->fstream->directory);
+       printf("Status informations :");
+
+       if(unit->parsed && unit->number_of_successeded_commands == unit->number_of_started_commands)
+               printf(" - (success)"); 
+       else
+       {
+               if(unit->interrupted)
+                       printf(" - (interruped)");
+               else
+                       printf(" - (failed)");
+       }
+       
+       printf("\n");
+
+       printf("    number of commands               : %d\n", unit->number_of_commands);
+       printf("    number of runned commands        : %d\n", unit->number_of_started_commands);
+       printf("    number of successeded commands   : %d\n", unit->number_of_successeded_commands);
+       printf("    number of failed commands        : %d\n", unit->number_of_failed_commands);
+       printf("    number of interrupted commands   : %d\n", unit->number_of_interrupted_commands);
+       
+       test_count = unit->number_of_commands;
+       
+       
+       for(i = 0; i < test_count; i++)
+       {
+               command = vector_get_at(unit->commands, i);
+               
+               /*command_display_status(unit->commands[i]);*/
+               command_display_status(command);
+       }
+
+
+}
+
+void
+unit_handle_include(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name)
+{
+       directory_t include;
+       char* prev_directory = NULL;
+       fstream_t fstream = NULL;
+       struct stat buffer = {0};
+       
+       
+       if(!stat(file_name, &buffer) && S_ISREG(buffer.st_mode))
+       {
+               INFO1("the file stream %s is in the current directory", file_name);
+               
+               fstream = fstream_new(getcwd(NULL, 0), file_name);
+               fstream_open(fstream);
+       }
+       /* the file to include is not in the current directory, check if it is in a include directory */
+       else
+       {
+               prev_directory = getcwd(NULL, 0);
+               
+               vector_rewind(includes);
+               
+               while((include = vector_get(includes)))
+               {
+                       chdir(include->name);
+                       
+                       if(!stat(file_name, &buffer) && S_ISREG(buffer.st_mode))
+                       {
+                               fstream = fstream_new(include->name, file_name);
+                               fstream_open(fstream);
+                               break;
+                       }
+                       
+                       
+                       vector_move_next(includes);
+               }
+               
+               chdir(prev_directory);
+               free(prev_directory);
+       }
+       
+       
+       
+       /* the file to include is not found handle the failure */
+       if(!fstream)
+       {
+               exit_code = EINCLUDENOTFOUND;
+               ERROR1("Include file %s not found",file_name);
+               unit_handle_failure(unit);
+       }
+       else
+       {
+               /* parse the include file */
+               
+               /*unit_t __unit = unit_new(unit->runner, NULL, fstream);*/
+               unit->parsing_include_file = 1;
+               
+               /* add the unit to the list of the runned units */
+               
+               /*xbt_os_mutex_acquire(unit->mutex);
+               vector_push_back(__unit->runner->units->items, __unit);
+               xbt_os_mutex_release(unit->mutex);*/
+               
+               
+               if(want_dry_run)
+                       INFO2("checking unit %s including in %s...",fstream->name, unit->fstream->name); 
+               
+               unit_parse(unit, context_new(), mutex, fstream->name, fstream->stream);
+               
+               fstream_free((void**)&fstream);
+               unit->parsing_include_file = 0;
+       }
+}
+
+void
+unit_handle_suite(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* description)
+{
+       suite_t suite = suite_new(unit, description);
+       unit_add_suite(unit, suite);
+       unit->running_suite = 1;
+}
+
+int
+unit_reset(unit_t unit)
+{
+       fseek(unit->fstream->stream,0L, SEEK_SET);
+       unit->parsed = 0;
+       unit->number_of_commands = 0;
+       
+       return 0;
+}