--- /dev/null
+#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;
+}