X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/d0f6612f4d1ff68fd89ea26930b81618077da742..bea7d471e30c245e71b6276dcdcd420f8e9d088f:/tools/tesh2/src/unit.c diff --git a/tools/tesh2/src/unit.c b/tools/tesh2/src/unit.c new file mode 100644 index 0000000000..1f540d65f5 --- /dev/null +++ b/tools/tesh2/src/unit.c @@ -0,0 +1,757 @@ +#include +#include +#include +#include +#include + + + +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; +}