/* the dlist of tesh include directories */
extern vector_t
-includes;
+include_dirs;
extern int
interrupted;
-extern int
+/*extern int
exit_code;
+*/
extern int
want_silent;
extern directory_t
root_directory;
+extern int
+want_detail_summary;
+
+extern int
+exit_code;
+
+extern pid_t
+pid;
+
+
#ifdef __cplusplus
}
#endif
-#define INDEFINITE ((int)-1)
-#define INDEFINITE_SIGNAL NULL
+#define INDEFINITE ((int)-1)
+#define INDEFINITE_SIGNAL NULL
-#define DEFAULT_FSTREAMS_CAPACITY ((int)128)
-#define DEFAULT_INCLUDES_CAPACITY DEFAULT_FSTREAMS_CAPACITY
-#define DEFAULT_UNITS_CAPACITY ((int)64)
-#define DEFAULT_COMMANDS_CAPACITY ((int)1024)
-#define DEFAULT_SUITES_CAPACITY ((int)128)
+#define DEFAULT_FSTREAMS_CAPACITY ((int)128)
+#define DEFAULT_INCLUDE_DIRS_CAPACITY DEFAULT_FSTREAMS_CAPACITY
+#define DEFAULT_UNITS_CAPACITY ((int)64)
+#define DEFAULT_INCLUDES ((int)8)
+#define DEFAULT_COMMANDS_CAPACITY ((int)512)
+#define DEFAULT_SUITES_CAPACITY ((int)32)
-#define MAX_SUFFIX ((unsigned int)9)
+#define MAX_SUFFIX ((unsigned int)9)
#ifdef __cplusplus
}
int
directories_free(void** directoriesptr);
-directory_t
-directories_get_back(directories_t directories);
+/*directory_t
+directories_get_back(directories_t directories);*/
-directory_t
-directories_search_fstream_directory(directories_t directories, const char* name);
int
directories_get_size(directories_t directories);
int
-directories_has_directories_to_load(directories_t directories);
+directories_is_empty(directories_t directories);
#ifdef __cplusplus
}
#endif
directory_t
-directory_new(const char* name, int load);
+directory_new(const char* name);
int
directory_open(directory_t directory);
#include <errno.h>
-#define EREAD ((int)2000) /* a read pipe operation failed */
-#define EREADPIPE ((int)2001) /* the pipe used to read from the stdout of the command is broken */
-#define ETIMEOUT ((int)2002) /* the command is timeouted */
-#define EWRITE ((int)2003) /* a write operation failed */
-#define EWRITEPIPE ((int)2004) /* the pipe used to write to the stdin of the command is broken */
-#define EEXEC ((int)2005) /* can't execute the command */
-#define EWAIT ((int)2006) /* the wait function failed */
-#define ECMDNOTFOUND ((int)2007) /* the command is not found */
-#define EEXITCODENOTMATCH ((int)2008) /* the exit codes don't match */
-#define EOUTPUTNOTMATCH ((int)2009) /* the outputs don't match */
-#define ESIGNOTMATCH ((int)2010) /* the signals don't match */
-#define EUNEXPECTEDSIG ((int)2011) /* unexpected signal caught */
-#define ESIGNOTRECEIPT ((int)2012) /* the expected signal is not receipt */
-#define EFILENOTFOUND ((int)2013) /* the specified tesh file is not found */
-#define EGETCWD ((int)2014) /* this is a system error : the getcwd() function failed (impossible) */
-#define EDIRNOTFOUND ((int)2015) /* the specified directory is not found */
-#define ECHDIR ((int)2016) /* this is a system error : the chdir() function failed (impossible) */
-#define EPROCESSCMDLINE ((int)2017) /* this is an internal error : the process_command_line() function failed */
-#define EARGNOTSPEC ((int)2018) /* a none optional argument is not specified in the command line */
-#define ENOTPOSITIVENUM ((int)2019) /* the argument of the option is not strictly positive */
-#define ESYNTAX ((int)2020) /* syntax error */
-#define EINVALIDTIMEOUT ((int)2021) /* the timeout value specified by the metacommand is invalid */
-#define EINVALIDEXITCODE ((int)2022) /* the expected exit code value specified by the metacommand is invalid */
-#define ESIGNOTSUPP ((int)2023) /* the signal specified by the metacommand is not supported */
-#define ELEADTIME ((int)2024) /* global timeout */
-#define EREADMENOTFOUND ((int)2025) /* unable to locate the README.txt file */
-#define EINCLUDENOTFOUND ((int)2026) /* the include file specified by a metacommand is not found */
-#define ESUFFIXTOOLONG ((int)2027) /* the suffix is too long */
-#define EFILENOTINSPECDIR ((int)2028) /* file not found in the specified directories */
-#define EFILENOTINCURDIR ((int)2029) /* file not found in the current directory */
+#define EREAD ((int)100) /* a read pipe operation failed */
+#define EREADPIPE ((int)101) /* the pipe used to read from the stdout of the command is broken */
+#define ETIMEOUT ((int)102) /* the command is timeouted */
+#define EWRITE ((int)103) /* a write operation failed */
+#define EWRITEPIPE ((int)104) /* the pipe used to write to the stdin of the command is broken */
+#define EEXEC ((int)105) /* can't execute the command */
+#define EWAIT ((int)106) /* the wait function failed */
+#define ECMDNOTFOUND ((int)107) /* the command is not found */
+#define EEXITCODENOTMATCH ((int)108) /* the exit codes don't match */
+#define EOUTPUTNOTMATCH ((int)109) /* the outputs don't match */
+#define ESIGNOTMATCH ((int)110) /* the signals don't match */
+#define EUNEXPECTEDSIG ((int)111) /* unexpected signal caught */
+#define ESIGNOTRECEIPT ((int)112) /* the expected signal is not receipt */
+#define EFILENOTFOUND ((int)113) /* the specified tesh file is not found */
+#define EGETCWD ((int)114) /* this is a system error : the getcwd() function failed (impossible) */
+#define EDIRNOTFOUND ((int)115) /* the specified directory is not found */
+#define ECHDIR ((int)116) /* this is a system error : the chdir() function failed (impossible) */
+#define EPROCCMDLINE ((int)117) /* this is an internal error : the process_command_line() function failed */
+#define ENOARG ((int)118) /* a none optional argument is not specified in the command line */
+#define ENOTPOSITIVENUM ((int)119) /* the argument of the option is not strictly positive */
+#define ESYNTAX ((int)120) /* syntax error */
+#define EINVALIDTIMEOUT ((int)121) /* the timeout value specified by the metacommand is invalid */
+#define EINVALIDEXITCODE ((int)122) /* the expected exit code value specified by the metacommand is invalid */
+#define ESIGNOTSUPP ((int)123) /* the signal specified by the metacommand is not supported */
+#define ELEADTIME ((int)124) /* global timeout */
+#define EREADMENOTFOUND ((int)125) /* unable to locate the README.txt file */
+#define EINCLUDENOTFOUND ((int)126) /* the include file specified by a metacommand is not found */
+#define ESUFFIXTOOLONG ((int)127) /* the suffix is too long */
+#define EFILENOTINSPECDIR ((int)128) /* file not found in the specified directories */
+#define EFILENOTINCURDIR ((int)129) /* file not found in the current directory */
+
+
+const char*
+error_get_at(int pos, int* code);
const char*
-error_to_string(int error);
+error_to_string(int errcode);
#ifdef __cplusplus
}
fstream_free(void** fstreamptr);
void
-fstream_parse(fstream_t fstream, unit_t unit);
+fstream_parse(fstream_t fstream, unit_t unit, xbt_os_mutex_t mutex);
#ifdef __cplusplus
}
/* the dlist of tesh include directories */
extern vector_t
-includes;
+include_dirs;
extern int
interrupted;
struct s_reader;
struct s_timer;
struct s_context;
+struct s_command;
+struct s_variable;
+struct s_variables;
+
/*
csr_pipe_function_failed = 16 /* the function pipe() or CreatePipe() fails */
}cs_reason_t;
+typedef struct s_variable
+{
+ char* name;
+ char* val;
+ int used:1;
+ int env:1;
+ int err:1;
+}s_variable_t,* variable_t;
-struct s_command;
-struct s_variable;
-struct s_variables;
-
+typedef struct s_variables
+{
+ dictionary_t items;
+}s_variables_t,* variables_t;
/*
* declaration of the tesh timer type
struct s_command* command; /* the command of the reader */
int failed; /* if 1, the reader failed */
int broken_pipe; /* if 1, the pipe used by the reader is broken */
+ int done;
xbt_os_sem_t started;
}s_reader_t,* reader_t;
struct s_command* command; /* the command of the writer */
int failed; /* if 1, the writer failed */
int broken_pipe; /* if 1, the pipe used by the writer is broken */
- xbt_os_sem_t started;
+ xbt_os_sem_t written;
+ xbt_os_sem_t can_write;
+ int done;
}s_writer_t,* writer_t;
+
typedef struct s_units
{
vector_t items; /* used to store the units */
*/
typedef struct s_unit
{
+ char* description;
+ int is_suite;
struct s_fstream* fstream;
struct s_runner* runner; /* the runner of the unit */
vector_t commands;
int number_of_failed_commands; /* number of failed commands of the unit */
int number_of_successeded_commands; /* number of successeded commands of the unit */
int number_of_terminated_commands; /* number of ended commands */
+ int number_of_waiting_commands;
xbt_os_thread_t thread; /* all the units run in its own thread */
xbt_os_sem_t sem; /* used by the commands of the unit to signal the end of the unit */
xbt_os_mutex_t mutex; /* used to synchronously access to the properties of the runner */
int parsed; /* if 1, the tesh file of the unit is parsed */
int released;
int parsing_include_file;
- struct s_suite* owner; /* the suite containing the unit if any */
+ struct s_unit* owner; /* the unit containing the unit if any */
+ struct s_unit* root;
vector_t suites;
int number; /* the number of suites */
int capacity; /* the number of suites that the unit can contain */
- int running_suite; /* if 1, the suite running a suite */
+ int running_suite; /* if 1, the suite running a suite */
+ vector_t includes;
}s_unit_t,* unit_t;
+
+
/*
* declaration of tesh suite type
*/
typedef struct s_runner
{
- /*vector_t units;*/ /* the vector containing all the units launched by the runner */
+ /*vector_t units;*/ /* the vector containing all the units launched by the runner */
struct s_units* units;
int timeouted; /* if 1, the runner is timeouted */
int timeout; /* the timeout of the runner */
int number_of_ended_units; /* the number of ended units */
int waiting; /* if 1, the runner is waiting the end of all the units */
xbt_os_thread_t thread; /* the timer thread */
+ vector_t variables;
+
+ int total_of_tests;
+ int total_of_successeded_tests;
+ int total_of_failed_tests;
+ int total_of_interrupted_tests;
+
+ int total_of_units;
+ int total_of_successeded_units;
+ int total_of_failed_units;
+ int total_of_interrupted_units;
+
+ int total_of_suites;
+ int total_of_successeded_suites;
+ int total_of_failed_suites;
+ int total_of_interrupted_suites;
}s_runner_t,* runner_t;
{
char* name;
DIR* stream;
- int load;
}s_directory_t,* directory_t;
typedef struct s_directories
int async; /* if 1, the command is asynchronous */
}s_context_t,* context_t;
+
+typedef void (*fn_sig_io_handler_t)(int);
/*
* declaration of the tesh command type
*/
#ifndef WIN32
int killed; /* if 1, the command was killed */
#endif
+
+ fn_sig_io_handler_t fn_sig_io_handler;
}s_command_t,* command_t;
#ifdef _cplusplus
#endif
unit_t
-unit_new(runner_t runner, suite_t owner, fstream_t fstream);
+unit_new(runner_t runner, unit_t root, unit_t owner, fstream_t fstream);
int
unit_free(void** unitptr);
unit_pushline(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* filepos, char kind, char *line);
void
-unit_handle_include(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name);
+unit_handle_include(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name, const char* description);
void
unit_parse(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name, FILE* stream);
void
unit_handle_suite(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* description);
+void
+display_title(const char* description);
+
#ifdef __cplusplus
}
#endif
#ifndef __VARIABLE_H
#define __VARIABLE_H
-#include <global.h>
+#include <com.h>
#ifdef __cplusplus
extern "C" {
#endif
+variable_t
+variable_new(const char* name, const char* val);
+
+int
+variable_free(variable_t* variableptr);
+
+int
+variable_is_used(variable_t variable);
+
+int
+variable_set_used(variable_t variable);
#ifdef __cplusplus
#endif
-#endif /*!__VARIABLE_H */
\ No newline at end of file
+#endif /*!__VARIABLE_H */
+
#ifndef __VARIABLES_H
#define __VARIABLES_H
-#include <global.h>
+#include <com.h>
#ifdef __cplusplus
extern "C" {
#endif
+variables_t
+variables_new(void);
+int
+variables_free(variables_t* variablesptr);
#ifdef __cplusplus
/* first block allocation */
- if((errno = resize(allocator)))
+ if(resize(allocator))
{
free(allocator);
return NULL;
int pos, node_size;
fn_finalize_t fn_finalize;
void* type;
+ int rv;
if(!(*allocator_ptr))
return EINVAL;
/* apply the fn_finalize function to the first type */
- if((errno = (*fn_finalize)(&type)))
- return errno;
+ if((rv = (*fn_finalize)(&type)))
+ return rv;
}
/*clear all the other types */
/* apply the fn_finalize function to the first type */
- if((errno = (*fn_finalize)(&type)))
- return errno;
+ if((rv = (*fn_finalize)(&type)))
+ return rv;
}
}
int
allocator_dealloc(allocator_t allocator, void* block)
{
+ int rv;
allocator_node_t node;
if(!allocator || !block)
if(allocator->fn_finalize)
{
- if((errno = (*(allocator->fn_finalize))(&block)))
- return errno;
+ if((rv = (*(allocator->fn_finalize))(&block)))
+ return rv;
memset(block, 0, allocator->type_size);
node->is_allocated = 0;
fn_finalize_t fn_finalize;
void* type;
register int pos;
+ int rv;
if(!allocator)
/* apply the fn_finalize function to the first type */
- if((errno = (*fn_finalize)(&type)))
- return errno;
+ if((rv = (*fn_finalize)(&type)))
+ return rv;
memset(type, 0, type_size);
node->is_allocated = 0;
/* apply the fn_finalize function to the first type */
- if((errno = (*fn_finalize)(&type)))
- return errno;
+ if((rv = (*fn_finalize)(&type)))
+ return rv;
memset(type, 0, type_size);
node->is_allocated = 0;
#include "../include/_signal.h"
+
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
+static void sig_io_handler(int status)
+{
+ INFO0("*************************Got a SIGIO**************************************");
+}
+
static void*
command_start(void* p);
-
command_t
command_new(unit_t unit, context_t context, xbt_os_mutex_t mutex)
{
command->stat_val = -1;
/* set the unit of the command */
- command->unit = unit;
+ command->unit = unit->root ? unit->root : unit;
/* all the commands are runned in a thread */
command->thread = NULL;
/* register the command */
xbt_os_mutex_acquire(mutex);
- /*unit->commands[(unit->number_of_commands)++] = command;*/
+
vector_push_back(unit->commands, command);
- (unit->number_of_commands)++;
+
+ (command->unit->number_of_commands)++;
+
xbt_os_mutex_release(mutex);
+ command->fn_sig_io_handler = sig_io_handler;
+
#ifndef WIN32
command->killed = 0;
#endif
+
+
return command;
}
}
command->pid= fork();
-
+
if(command->pid < 0)
{
close(child_stdin_fd[0]);
{
if(command->pid)
{/* father */
+
+
close(child_stdin_fd[0]);
close(child_stdout_fd[1]);
command->stdin_fd = child_stdin_fd[1];
+
command->stdout_fd = child_stdout_fd[0];
-
+
+ /* on indique que c'est le processus parent qui doit recevoir le signal */
+ /*fcntl(command->stdin_fd,F_SETOWN, pid);*/
+
if(command->reader)
{
/* launch the reader */
}
else
{/* child */
-
+
+
close(child_stdin_fd[1]);
+ close(child_stdout_fd[0]);
- dup2(child_stdin_fd[0],0);
+ if(dup2(child_stdin_fd[0],STDIN_FILENO/*0*/) < 0)
+ {
+ exit_code = EEXEC;
+ ERROR1("dup2() failed (%d)",errno);
+ command_handle_failure(command,csr_exec_failure);
+ }
- close(child_stdin_fd[0]);
+ /*close(child_stdin_fd[0]);
+ */
- close(child_stdout_fd[0]);
+ if(dup2(child_stdout_fd[1],STDOUT_FILENO/*1*/) < 0)
+ {
+ exit_code = EEXEC;
+ ERROR1("dup2() failed (%d)",errno);
+ command_handle_failure(command,csr_exec_failure);
+ }
- dup2(child_stdout_fd[1],1);
+ if(dup2(child_stdout_fd[1], STDERR_FILENO/*2*/) < 0)
+ {
+ exit_code = EEXEC;
+ ERROR1("dup2() failed (%d)",errno);
+ command_handle_failure(command,csr_exec_failure);
+ }
- dup2(child_stdout_fd[1],2);
+ fcntl(command->stdin_fd, F_SETFL, fcntl(command->stdin_fd, F_GETFL) | O_NONBLOCK);
+
+
+ if(command->writer)
+ xbt_os_sem_release(command->writer->can_write);
- close(child_stdout_fd[1]);
+ /*close(child_stdout_fd[1]);*/
if(command->reader)
xbt_os_sem_acquire(command->reader->started);
if(command->writer)
- xbt_os_sem_acquire(command->writer->started);
+ xbt_os_sem_acquire(command->writer->written);
if(command->timer)
- xbt_os_sem_acquire(command->timer->started);
+ xbt_os_sem_acquire(command->timer->started);
- execlp ("/bin/sh", "sh", "-c", command->context->command_line, NULL);
+ if(execlp("/bin/sh", "sh", "-c", command->context->command_line, NULL) < 0)
+ {
+ exit_code = EEXEC;
+ ERROR1("execlp() failed (%d)",errno);
+ command_handle_failure(command,csr_exec_failure);
+ }
}
}
}
/* let this thread wait for the child so that the main thread can detect the timeout without blocking on the wait */
+
+ xbt_os_mutex_acquire(command->unit->mutex);
+ command->unit->number_of_waiting_commands++;
+ xbt_os_mutex_release(command->unit->mutex);
+
pid = waitpid(command->pid, &(command->stat_val), 0);
+
+ xbt_os_mutex_acquire(command->unit->mutex);
+ command->unit->number_of_waiting_commands--;
+ xbt_os_mutex_release(command->unit->mutex);
+
/*printf("The %p command ended\n",command);*/
if(pid != command->pid)
{
if(WIFEXITED(command->stat_val))
command->exit_code = WEXITSTATUS(command->stat_val);
}
+
+
}
#endif
if(WIFSIGNALED(command->stat_val))
{
command->signal = strdup(signal_name(WTERMSIG(command->stat_val),command->context->signal));
- INFO3("the command -PID %d %s receive the signal : %s",command->pid, command->context->command_line, command->signal);
+ /*INFO3("the command -PID %d %s receive the signal : %s",command->pid, command->context->command_line, command->signal);*/
}
/* we have a signal and not signal is expected */
if(success && oh_check == command->context->output_handling && command->reader)
{
/* make sure the reader done */
- while(!command->reader->broken_pipe)
+ while(!command->reader->done)
xbt_os_thread_yield();
+
+ close(command->stdout_fd);
+ command->stdout_fd = INDEFINITE_FD;
- xbt_strbuff_chomp(command->output);
- xbt_strbuff_chomp(command->context->output);
- xbt_strbuff_trim(command->output);
- xbt_strbuff_trim(command->context->output);
+ xbt_strbuff_chomp(command->output);
+ xbt_strbuff_chomp(command->context->output);
+ xbt_strbuff_trim(command->output);
+ xbt_strbuff_trim(command->context->output);
- if(command->output->used != command->context->output->used || strcmp(command->output->data, command->context->output->data))
- {
- success = 0;
- exit_code = EOUTPUTNOTMATCH;
- reason = csr_outputs_dont_match;
- }
+ if(command->output->used != command->context->output->used || strcmp(command->output->data, command->context->output->data))
+ {
+ success = 0;
+ exit_code = EOUTPUTNOTMATCH;
+ reason = csr_outputs_dont_match;
+ }
}
if(success)
void
command_display_status(command_t command)
{
- #ifdef WIN32
- printf("\nCommand : PID - %p\n%s\n",command->pid,command->context->command_line);
- #else
- printf("\nCommand : PID - %d\n%s\n",command->pid,command->context->command_line);
- #endif
- printf("Status informations :\n");
- printf(" position in the tesh file : %s\n",command->context->line);
- /* the command successeded */
- if(cs_successeded == command->status)
- {
- /* status */
- printf(" status : success\n");
- }
- else
+ /*printf("\033[1m");*/
+
+ if(cs_successeded != command->status)
{
-
- /* display if the command is interrupted, failed or in a unknown status */
- if(cs_interrupted == command->status)
- printf(" status : interrupted\n");
- else if(cs_failed == command->status)
- printf(" status : failed\n");
- else
- printf(" status : unknown\n");
#ifndef WIN32
if(command->killed)
- printf(" <killed command>\n");
+ printf(" <killed command>\n");
#endif
/* display the reason of the status of the command */
{
/* the function pipe or CreatePipe() fails */
case csr_pipe_function_failed :
- printf(" reason : pipe() or CreatePipe() function failed (system error)\n");
+ printf(" reason : pipe() or CreatePipe() function failed (system error)\n");
break;
/* reader failure reasons*/
case csr_read_pipe_broken :
- printf(" reason : command read pipe broken\n");
+ printf(" reason : command read pipe broken\n");
break;
case csr_read_failure :
- printf(" reason : command stdout read failed\n");
+ printf(" reason : command stdout read failed\n");
break;
/* writer failure reasons */
case csr_write_failure :
- printf(" reason : command stdin write failed\n");
+ printf(" reason : command stdin write failed\n");
break;
case csr_write_pipe_broken :
- printf(" reason : command write pipe broken\n");
+ printf(" reason : command write pipe broken\n");
break;
/* timer reason */
case csr_timeout :
- printf(" reason : command timeouted\n");
+ printf(" reason : command timeouted\n");
break;
/* command failure reason */
case csr_command_not_found :
- printf(" reason : command not found\n");
+ printf(" reason : command not found\n");
break;
/* context failure reasons */
case csr_exit_codes_dont_match :
- printf(" reason : exit codes don't match\n");
+ printf(" reason : exit codes don't match\n");
break;
case csr_outputs_dont_match :
{
char *diff;
- printf(" reason : ouputs don't match\n");
+ printf(" reason : ouputs don't match\n");
diff = xbt_str_diff(command->context->output->data,command->output->data);
- printf(" output diff :\n%s\n",diff);
+ printf(" output diff :\n%s\n",diff);
free(diff);
}
break;
case csr_signals_dont_match :
- printf(" reason : signals don't match\n");
+ printf(" reason : signals don't match\n");
break;
case csr_unexpected_signal_caught:
- printf(" reason : unexpected signal caught\n");
+ printf(" reason : unexpected signal caught\n");
break;
case csr_expected_signal_not_receipt :
- printf(" reason : expected signal not receipt\n");
+ printf(" reason : expected signal not receipt\n");
break;
/* system failure reasons */
case csr_exec_failure :
- printf(" reason : can't excute the command\n");
+ printf(" reason : can't excute the command\n");
break;
case csr_wait_failure :
- printf(" reason : wait command failure\n");
+ printf(" reason : wait command failure\n");
break;
/* global/local interruption */
case csr_interruption_request :
- printf(" reason : the command receive a interruption request\n");
+ printf(" reason : the command receive a interruption request\n");
break;
/* unknown ? */
case csr_unknown :
- printf(" reason : unknown \n");
+ printf(" reason : unknown \n");
}
}
{
if(INDEFINITE != command->exit_code)
/* the command exit code */
- printf(" exit code : %d\n",command->exit_code);
+ printf(" exit code : %d\n",command->exit_code);
/* if an expected exit code was specified display it */
if(INDEFINITE != command->context->exit_code)
- printf(" expected exit code : %d\n",command->context->exit_code);
+ printf(" expected exit code : %d\n",command->context->exit_code);
else
- printf(" no expected exit code specified\n");
+ printf(" no expected exit code specified\n");
/* if an expected exit code was specified display it */
if(NULL == command->context->signal)
- printf(" no expected signal specified\n");
+ printf(" no expected signal specified\n");
else
{
if(NULL != command->signal)
- printf(" signal : %s\n",command->signal);
+ printf(" signal : %s\n",command->signal);
- printf(" expected signal : %s\n",command->context->signal);
+ printf(" expected signal : %s\n",command->context->signal);
}
/* if the command has out put and the metacommand display output is specified display it */
xbt_dynar_t a = xbt_str_split(command->output->data, "\n");
char *out = xbt_str_join(a,"\n||");
xbt_dynar_free(&a);
- printf(" output :\n||%s",out);
+ printf(" output :\n||%s",out);
free(out);
}
}
printf("\n");
+
+ /*printf("\033[0m");*/
}
+
+
+
void
command_handle_failure(command_t command, cs_reason_t reason)
{
int
dictionary_free(dictionary_t* dictionaryptr)
{
+ int rv;
+
if(!(*dictionaryptr))
return EINVAL;
- if((errno = htable_free(&((*dictionaryptr)->htable))))
- return errno;
+ if((rv = htable_free(&((*dictionaryptr)->htable))))
+ return rv;
free(*dictionaryptr);
*dictionaryptr = NULL;
return vector_get_size(directories->items);
}
+int
+directories_is_empty(directories_t directories)
+{
+ if(!directories)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return vector_is_empty(directories->items);
+}
+
int
directories_add(directories_t directories, directory_t directory)
{
return 0;
}
-directory_t
-directories_search_fstream_directory(directories_t directories, const char* name)
-{
-
- struct stat buffer = {0};
- char* prev;
- directory_t directory;
-
- if(!directories)
- {
- errno = EINVAL;
- return NULL;
- }
-
- prev = getcwd(NULL, 0);
-
- vector_rewind(directories->items);
-
- while((directory = vector_get(directories->items)))
- {
- chdir(directory->name);
-
- if(!stat(name, &buffer) || S_ISREG(buffer.st_mode))
- {
- chdir(prev);
- free(prev);
- return directory;
- }
-
- vector_move_next(directories->items);
- }
-
- chdir(prev);
- free(prev);
- errno = ESRCH;
- return NULL;
-}
-
int
directories_load(directories_t directories, fstreams_t fstreams, lstrings_t suffixes)
{
directory_t directory;
+ int rv;
if(!directories || !fstreams || !suffixes)
return EINVAL;
while((directory = vector_get(directories->items)))
{
- if(directory->load)
- {
- if((errno = directory_open(directory)))
- return errno;
-
- chdir(directory->name);
-
- if((errno = directory_load(directory, fstreams, suffixes)))
- return errno;
-
- if((errno = directory_close(directory)))
- return errno;
+ if((rv = directory_open(directory)))
+ return rv;
+
+ if((rv = directory_load(directory, fstreams, suffixes)))
+ return rv;
- chdir(root_directory->name);
- }
+ if((rv = directory_close(directory)))
+ return rv;
vector_move_next(directories->items);
return 0;
}
-int
-directories_has_directories_to_load(directories_t directories)
-{
- directory_t directory;
-
- if(!directories)
- {
- errno = EINVAL;
- return 0;
- }
-
- vector_rewind(directories->items);
-
- while((directory = vector_get(directories->items)))
- {
- if(directory->load)
- return 1;
-
- vector_move_next(directories->items);
-
- }
-
- return 0;
-}
-
-directory_t
-directories_get_back(directories_t directories)
-{
- if(!directories)
- {
- errno = EINVAL;
- return NULL;
- }
-
- return vector_get_back(directories->items);
-}
-
int
directories_free(void** directoriesptr)
{
+ int rv;
+
if(!(*directoriesptr))
return EINVAL;
- if((errno = vector_free(&((*((directories_t*)directoriesptr))->items))))
- return errno;
+ if((rv = vector_free(&((*((directories_t*)directoriesptr))->items))))
+ return rv;
free(*directoriesptr);
*directoriesptr = NULL;
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
directory_t
-directory_new(const char* name, int load)
+directory_new(const char* name)
{
directory_t directory;
- struct stat buffer = {0};
if(!name)
{
return NULL;
}
- if(stat(name, &buffer))
- return NULL;
-
- if(!S_ISDIR(buffer.st_mode))
- {
- errno = ENOTDIR;
- return NULL;
- }
-
directory = xbt_new0(s_directory_t, 1);
- if(!strcmp(".",name))
- {
- directory->name = getcwd(NULL, 0);
- }
- else if(!strcmp("..",name))
- {
- char* buffer = getcwd(NULL, 0);
- chdir(name);
- directory->name = getcwd(NULL, 0);
- chdir(buffer);
- free(buffer);
- }
- else
- {
- directory->name = strdup(name);
- }
-
+ directory->name = strdup(name);
+
+
directory->stream = NULL;
- directory->load = load;
return directory;
}
const char* suffix;
int has_valid_suffix;
int is_empty = 1;
+ int rv;
if(!directory || !fstreams)
return EINVAL;
}
/* add the fstream to the list of file streams to run */
- if((errno = fstreams_add(fstreams, fstream_new(directory->name, entry->d_name))))
+ if((rv = fstreams_add(fstreams, fstream_new(directory->name, entry->d_name))))
{
INFO0("fstreams_add() failed");
free(sfstream.directory);
free(sfstream.name);
- return errno;
+ return rv;
}
is_empty = 0;
-#include <error.h>\r
-\r
-typedef struct s_entry\r
-{\r
- int code;\r
- const char* string;\r
-}entry_t;\r
-\r
-static const\r
-entry_t err[] =\r
-{\r
- {EREAD, "a read pipe operation failed"},\r
- {EREADPIPE, "a pipe used to read from the stdout of a command is broken"},\r
- {ETIMEOUT, "a command is timeouted"},\r
- {EWRITE, "a write operation failed"},\r
- {EWRITEPIPE, "a pipe used to write to the stdin of a command is broken"},\r
- {EEXEC, "can't execute a command"},\r
- {EWAIT, "wait function failed"},\r
- {ECMDNOTFOUND, "command is not found"},\r
- {EEXITCODENOTMATCH, "exit codes don't match"},\r
- {EOUTPUTNOTMATCH, "outputs don't match"},\r
- {ESIGNOTMATCH, "signals don't match"},\r
- {EUNEXPECTEDSIG, "unexpected signal caught"},\r
- {ESIGNOTRECEIPT, "expected signal not receipt"},\r
- {EFILENOTFOUND, "specified tesh file not found"},\r
- {EGETCWD, "system error : the getcwd() function failed"},\r
- {EDIRNOTFOUND, "specified directory not found"},\r
- {ECHDIR, "system error : the chdir() function failed"},\r
- {EPROCESSCMDLINE, "internal error : the process_command_line() function failed"},\r
- {EARGNOTSPEC, "none optional argument not specified in the command line"},\r
- {ENOTPOSITIVENUM, "argument option not strictly positive"},\r
- {ESYNTAX, "syntax error"},\r
- {EINVALIDTIMEOUT, "timeout value specified by metacommand invalid"},\r
- {EINVALIDEXITCODE, "expected exit code value specified by the metacommand invalid"},\r
- {ESIGNOTSUPP, "signal specified by the metacommand not supported (Windows specific)"},\r
- {ELEADTIME, "lead time"},\r
- {EREADMENOTFOUND, "unable to locate the README.txt file"},\r
- {EINCLUDENOTFOUND, "include file specified by a metacommand is not found"},\r
- {ESUFFIXTOOLONG, "suffix is too long"},\r
- {EFILENOTINSPECDIR,"file not found in the specified directories"},\r
- {EFILENOTINCURDIR,"file not found in the current directory"},\r
- {-1, "unknown"}\r
-};\r
-\r
-const char*\r
-error_to_string(int error)\r
-{\r
- int i;\r
- \r
- for(i = 0; error >= 0 && err[i].code != -1; i++)\r
- {\r
- if(err[i].code == error)\r
- return err[i].string;\r
- } \r
- \r
- return "unknow error"; \r
-}\r
+#include <error.h>
+
+typedef struct s_entry
+{
+ const char* name;
+ int code;
+ const char* string;
+}entry_t;
+
+
+static const
+entry_t err[] =
+{
+ {"ENOENT", ENOENT, "No such file of directory."},
+ {"ENOMEM", ENOMEM,"Insufficient memory is available."},
+ {"EACCES", EACCES, "Read or search permission was denied for a component of the pathname."},
+ {"ENOTDIR", ENOTDIR, "Not a directory."},
+ {"EREAD", EREAD, "a read pipe operation failed"},
+ {"EREADPIPE", EREADPIPE, "a pipe used to read from the stdout of a command is broken"},
+ {"ETIMEOUT", ETIMEOUT, "a command is timeouted"},
+ {"EWRITE", EWRITE, "a write operation failed"},
+ {"EWRITEPIPE", EWRITEPIPE, "a pipe used to write to the stdin of a command is broken"},
+ {"EEXEC", EEXEC, "can't execute a command"},
+ {"EWAIT", EWAIT, "wait function failed"},
+ {"ECMDNOTFOUND", ECMDNOTFOUND, "command is not found"},
+ {"EEXITCODENOTMATCH", EEXITCODENOTMATCH, "exit codes don't match"},
+ {"EOUTPUTNOTMATCH", EOUTPUTNOTMATCH, "outputs don't match"},
+ {"ESIGNOTMATCH", ESIGNOTMATCH, "signals don't match"},
+ {"EUNEXPECTEDSIG", EUNEXPECTEDSIG, "unexpected signal caught"},
+ {"ESIGNOTRECEIPT", ESIGNOTRECEIPT, "expected signal not receipt"},
+ {"EFILENOTFOUND", EFILENOTFOUND, "specified tesh file not found"},
+ {"EGETCWD", EGETCWD, "system error : the getcwd() function failed"},
+ {"EDIRNOTFOUND", EDIRNOTFOUND, "specified directory not found"},
+ {"ECHDIR", ECHDIR, "system error : the chdir() function failed"},
+ {"EPROCCMDLINE", EPROCCMDLINE, "process_command_line() failed : internal error"},
+ {"ENOARG", ENOARG, "none optional argument not specified"},
+ {"ENOTPOSITIVENUM", ENOTPOSITIVENUM, "argument option not strictly positive"},
+ {"ESYNTAX", ESYNTAX, "syntax error"},
+ {"EINVALIDTIMEOUT", EINVALIDTIMEOUT, "timeout value specified by metacommand invalid"},
+ {"EINVALIDEXITCODE", EINVALIDEXITCODE, "expected exit code value specified by the metacommand invalid"},
+ {"ESIGNOTSUPP", ESIGNOTSUPP, "signal specified by the metacommand not supported (Windows specific)"},
+ {"ELEADTIME", ELEADTIME, "lead time"},
+ {"EREADMENOTFOUND", EREADMENOTFOUND, "unable to locate the README.txt file"},
+ {"EINCLUDENOTFOUND", EINCLUDENOTFOUND, "include file specified by a metacommand is not found"},
+ {"ESUFFIXTOOLONG", ESUFFIXTOOLONG, "suffix is too long"},
+ {"EFILENOTINSPECDIR", EFILENOTINSPECDIR,"file not found in the specified directories"},
+ {"EFILENOTINCURDIR", EFILENOTINCURDIR,"file not found in the current directory"},
+ {"unkwown", -1, "unknown"}
+};
+
+#include <stdio.h>
+
+const char*
+error_to_string(int errcode)
+{
+ int i;
+
+ for(i = 0; err[i].code != -1; i++)
+ if(err[i].code == errcode)
+ return err[i].string;
+
+ return "unknow error";
+}
+
+const char*
+error_get_at(int pos, int* code)
+{
+ if(pos < 0 || (pos > (sizeof(err)/sizeof(entry_t)) - 2))
+ {
+ errno = ERANGE;
+ return NULL;
+ }
+
+ *code = err[pos].code;
+ return err[pos].name;
+}
int
excludes_free(void** excludesptr)
{
+ int rv;
+
if(!(*excludesptr))
return EINVAL;
- if((errno =vector_free((&(*((excludes_t*)excludesptr))->items))))
- return errno;
+ if((rv =vector_free((&(*((excludes_t*)excludesptr))->items))))
+ return rv;
free(*excludesptr);
*excludesptr = NULL;
fstream_new(const char* directory, const char* name)
{
fstream_t fstream;
- /*struct stat buffer = {0};*/
-
+
if(!name)
{
errno = EINVAL;
return NULL;
}
- /*if(stat(name, &buffer))
- return NULL;
-
- if(!S_ISREG(buffer.st_mode))
- {
- errno = ENOENT;
- return NULL;
- }*/
-
fstream = xbt_new0(s_fstream_t, 1);
fstream->name = strdup(name);
int
fstream_open(fstream_t fstream)
{
+ char path[MAX_PATH] = {0};
+
if(!fstream || fstream->stream)
return EINVAL;
return 0;
}
- if(!(fstream->stream = fopen(fstream->name, "r")))
+ sprintf(path,"%s/%s",fstream->directory, fstream->name);
+
+ if(!(fstream->stream = fopen(path, "r")))
return errno;
return 0;
}
void
-fstream_parse( fstream_t fstream, unit_t unit)
+fstream_parse(fstream_t fstream, unit_t unit, xbt_os_mutex_t mutex)
{
size_t len;
char * line = NULL;
xbt_strbuff_t buff;
int buffbegin = 0;
context_t context;
- xbt_os_mutex_t mutex = unit->mutex;
/* Count the line length while checking wheather it's blank */
int blankline;
buff=xbt_strbuff_new();
context = context_new();
- while(!unit->interrupted && getline(&line, &len, fstream->stream) != -1)
+ while(!(unit->root->interrupted) && getline(&line, &len, fstream->stream) != -1)
{
+
blankline=1;
linelen = 0;
to_be_continued = 0;
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)", fstream->name);
- else
- ERROR2("Unit `%s' inclued in `%s' : NOK (syntax error)", fstream->name, fstream->name);
+ ERROR1("Unit `%s': NOK (syntax error)", fstream->name);
+
exit_code = ESYNTAX;
unit_handle_failure(unit);
break;
}
+ else if(unit->running_suite)
+ unit->running_suite = 0;
+
if(context->command_line)
{
{
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] == '\\')
+ if(linelen>2 && line[linelen-3] == '\\')
{
/* Damn. Escaped \ */
line[linelen-2] = '\n';
}
}
+
+
/* Check that last command of the file ran well */
if(context->command_line)
{
context_reset(context);
}
+
+
/* Clear buffers */
if (line)
free(line);
xbt_strbuff_free(buff);
+ context_free(&context);
}
fstreams_new(int capacity, fn_finalize_t fn_finalize)
{
fstreams_t fstreams;
- fstreams = xbt_new0(s_fstreams_t, 1);
+
+ if(!(fstreams = (fstreams_t) calloc(1, sizeof(s_fstreams_t))))
+ return NULL;
if(!(fstreams->items = vector_new(capacity, fn_finalize)))
{
if(!(to_erase = vector_new(8, NULL)))
return errno;
- INFO0("excluding file streams");
-
/* collecte the file streams to exclude */
vector_rewind(fstreams->items);
fstreams_load(fstreams_t fstreams)
{
register fstream_t fstream;
- const char* directory = NULL;
-
if(!fstreams )
return EINVAL;
while((fstream = vector_get(fstreams->items)))
{
- chdir(root_directory->name);
-
- if(!directory || strcmp(directory, fstream->directory))
- {
- directory = fstream->directory;
-
- if(!dont_want_display_directory)
- INFO1("entering directory \"%s\"",directory);
-
- }
-
- chdir(fstream->directory);
-
fstream_open(fstream);
-
vector_move_next(fstreams->items);
}
+
return 0;
}
int
fstreams_free(void** fstreamsptr)
{
+ int rv;
+
if(!(* fstreamsptr))
return EINVAL;
-
- if((errno = vector_free(&((*((fstreams_t*)fstreamsptr))->items))))
- return errno;
+ if(EAGAIN != (rv = vector_free(&((*((fstreams_t*)fstreamsptr))->items))))
+ return rv;
free(*fstreamsptr);
*prev = hassoc->next;
val = (void*)hassoc->val;
- if((errno = allocator_dealloc(htable->allocator,hassoc)))
+ if(allocator_dealloc(htable->allocator,hassoc))
return NULL;
return val;
hassoc_t* prev;
fn_cmp_key_t fn_cmp_key;
void* val;
+ int rv;
if(!htable || !key)
return EINVAL;
*prev = hassoc->next;
val = (void*)hassoc->val;
- if((errno = allocator_dealloc(htable->allocator,hassoc)))
- return errno;
+ if((rv = allocator_dealloc(htable->allocator,hassoc)))
+ return rv;
if(htable->fn_finalize)
{
- if((errno = (*(htable->fn_finalize))(&val)))
- return errno;
+ if((rv = (*(htable->fn_finalize))(&val)))
+ return rv;
}
return 0;
htable_free(htable_t* htableptr)
{
htable_t htable;
+ int rv;
if(!(*htableptr))
return EINVAL;
for(hassoc = content[pos]; hassoc; hassoc = hassoc->next)
{
val = (void*)hassoc->val;
- if((errno = (*(htable->fn_finalize))(&val)))
- return errno;
+ if((rv = (*(htable->fn_finalize))(&val)))
+ return rv;
}
}
}
free(htable->content);
- if((errno = allocator_free(&(htable->allocator))))
- return errno;
+ if((rv = allocator_free(&(htable->allocator))))
+ return rv;
free(*htableptr);
*htableptr = NULL;
register int pos;
int size;
void* val;
+ int rv;
if(!htable)
return EINVAL;
for(hassoc = content[pos]; hassoc; hassoc = hassoc->next)
{
val = (void*)hassoc->val;
- if((errno = (*(htable->fn_finalize))(&val)))
- return errno;
+ if((rv = (*(htable->fn_finalize))(&val)))
+ return rv;
}
content[pos] = NULL;
return EINVAL;
if(!lstrings->size)
- return EAGAIN;
+ return 0;
- while(lstrings_pop_back(lstrings));
+ while(lstrings->size)
+ lstrings_pop_back(lstrings);
return 0;
}
#include <error.h>
#include <getopt.h>
+#include <getpath.h>
/*
* entry used to define the parameter of a tesh option.
directory_t
root_directory = NULL;
-int
+/*int
exit_code = 0;
+*/
+
+int
+want_detail_summary = 0;
/* the current version of tesh */
static const char*
/* the include directories : see the !i metacommand */
vector_t
-includes = NULL;
+include_dirs = NULL;
/* the list of tesh files to run */
static fstreams_t
static int
want_check_syntax = 0;
-/* if 1, all the tesh file of the current directory
- * are runned
- */
-static int
-want_load_directory = 0;
-
/* if 1, the status of all the units is display at
* the end.
*/
units_sem = NULL;
static int
-prepared = 0;
-
+loaded = 0;
int
-interrupted = 0;
+interrupted = 0;
+
+int
+exit_code = 0;
+
+pid_t
+pid =0;
/* the table of the entries of the options */
static const struct s_optentry opt_entries[] =
{
- { 'C', string, (byte*)&directories, 0, "directory" },
+ { 'C', string, (byte*)NULL, 0, "directory" },
{ 'x', string, (byte*)&suffixes, 0, "suffix" },
{ 'e', flag, (byte*)&env_overrides, 0, "environment-overrides", },
{ 'f', string, (byte*)&fstreams, 0, "file" },
{ 'h', flag, (byte*)&want_display_usage, 0, "help" },
{ 'a', flag, (byte*)&want_display_semantic, 0, "semantic" },
{ 'i', flag, (byte*)&want_keep_going_unit, 0, "keep-going-unit" },
- { 'I', string, (byte*)&includes, 0, "include-dir" },
+ { 'I', string, (byte*)&include_dirs, 0, "include-dir" },
{ 'j', number, (byte*)&number_of_jobs, (byte*) &optional_number_of_jobs, "jobs" },
{ 'k', flag, (byte*)&want_keep_going, 0, "keep-going" },
+ { 'm', flag, (byte*)&want_detail_summary, 0, "detail-summary" },
{ 'c', flag, (byte*)&want_just_display, 0, "just-display" },
{ 'd', flag, (byte*)&display_data_base, 0,"display-data-base" },
{ 'q', flag, (byte*)&question, 0, "question" },
{ 'n', flag, (byte*)&want_dry_run, 0, "dry-run"},
{ 't', number, (byte*)&timeout, 0, "timeout" },
{ 'S', flag, (byte*)&want_check_syntax, 0, "check-syntax"},
- { 'r', flag, (byte*)&want_load_directory, 0, "load-directory"},
+ { 'r', string, (byte*)&directories, 0, "load-directory"},
{ 'v', flag, (byte*)&want_verbose, 0, "verbose"},
{ 'F', string,(byte*)&excludes, 0, "exclude"},
{ 'l', string,(byte*)&logs,0,"log"},
" -b, --build-file Build a tesh file.\n",
" -r, --load-directory Run all the tesh files located in the directories specified by the option --directory.\n",
" -v, --verbose Display the status of the commands.\n",
+ " -m, --detail-summary Detail the summary of the run.\n",
" -F file , --exclude=FILE Ignore the tesh file FILE.\n",
" -l format, --log Format of the xbt logs.\n",
NULL
static int
process_command_line(int argc, char** argv);
-static int
+static void
load(void);
static void
-display_usage(int exit_code);
+display_usage(void);
static void
display_version(void);
int
main(int argc, char* argv[])
{
- init();
-
+ if(init() < 0)
+ finalize();
+
/* process the command line */
- if((exit_code = process_command_line(argc, argv)))
+ if(process_command_line(argc, argv) < 0)
finalize();
+
+ /* move to the root directory (the directory may change during the command line processing) */
+ chdir(root_directory->name);
/* initialize the xbt library
* for thread portability layer
*/
+ /* xbt initialization */
if(!lstrings_is_empty(logs))
{
int size = lstrings_get_size(logs);
finalize();
}
- if(!directories_has_directories_to_load(directories) && want_load_directory)
- WARN0("--load-directory specified but no directory specified");
+ /* load tesh files */
+ load();
- excludes_check(excludes, fstreams);
- /* load tesh */
- if((exit_code = load()))
- finalize();
-
- prepared = 1;
+ /* use by the finalize function to known if it must display the tesh usage */
+ loaded = 1;
if(-2 == number_of_jobs)
{/* --jobs is not specified (use the default value) */
if(number_of_jobs > fstreams_get_size(fstreams))
{/* --jobs = N is specified and N is more than the number of tesh files */
- WARN0("number of requested jobs exceed the number of files");
+ WARN0("Number of requested jobs exceed the number of files to run");
/* assume one job per file */
number_of_jobs = fstreams_get_size(fstreams);
}
- /* initialize the semaphore used to synchronize the jobs */
+ /* initialize the semaphore used to synchronize all the units */
jobs_sem = xbt_os_sem_init(number_of_jobs);
/* initialize the semaphore used by the runner to wait for the end of all units */
units_sem = xbt_os_sem_init(0);
/* initialize the runner */
- if((0 != (exit_code = runner_init(
- want_check_syntax,
- timeout,
- fstreams))))
- {
+ if(runner_init(want_check_syntax, timeout, fstreams))
finalize();
- }
-
+
if(want_just_display && want_silent)
want_silent = 0;
WARN0("mismatch in the syntax : --just-check-syntax and --just-display options at same time");
/* run all the units */
- runner_run();
+ runner_run();
+
/* show the result of the units */
if(want_verbose || want_dry_run)
runner_display_status();
-
/* all the test are runned, destroy the runner */
runner_destroy();
- /* then, finalize tesh */
+ /* then, finalize tesh (release all the allocated memory and exits) */
finalize();
#ifndef WIN32
}
+/* init -- initialize tesh : allocated all the objects needed by tesh to run
+ * the tesh files specified in the command line.
+ *
+ * return If successful the function returns zero. Otherwise the function returns
+ * -1 and sets the global variable errno to the appropriate error code.
+ */
+
+
+
static int
init(void)
{
- char* buffer = getcwd(NULL, 0);
+ char* buffer;
+
+
#ifdef WIN32
/* Windows specific : don't display the general-protection-fault message box and
prev_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
#endif
- /* used to store the file streams to run */
- fstreams = fstreams_new(DEFAULT_FSTREAMS_CAPACITY, fstream_free);
+ /* used to store the files to run */
+ if(!(fstreams = fstreams_new(DEFAULT_FSTREAMS_CAPACITY, fstream_free)))
+ {
+ ERROR0("Insufficient memory is available to initialize tesh : system error");
+ return -1;
+ }
+
+ /* register the current directory */
+ if(!(buffer = getcwd(NULL, 0)))
+ {
+ exit_code = errno;
+
+ if(EACCES == errno)
+ ERROR0("tesh initialization failed - Insufficient permission to read the current directory");
+ else
+ ERROR0("Insufficient memory is available to initialize tesh : system error");
+
+ return -1;
+ }
+
+ /* save the root directory */
+ if(!(root_directory = directory_new(buffer)))
+ {
+ ERROR0("Insufficient memory is available to initialize tesh : system error");
+ return -1;
+ }
- root_directory = directory_new(buffer,want_load_directory);
free(buffer);
- /* used to store the directories to loads */
- directories = directories_new();
- /* register the current directory */
- directories_add(directories, root_directory);
+ /* the directories to loads */
+ if(!(directories = directories_new()))
+ {
+ ERROR0("Insufficient memory is available to initialize tesh : system error");
+ return -1;
+ }
- /* used to store the includes directories */
- includes = vector_new(DEFAULT_INCLUDES_CAPACITY, directory_free);
+ /* the include directories */
+ if(!(include_dirs = vector_new(DEFAULT_INCLUDE_DIRS_CAPACITY, directory_free)))
+ {
+ ERROR0("Insufficient memory is available to initialize tesh : system error");
+ return -1;
+ }
- /* xbt logs */
- logs = lstrings_new();
+ /* xbt logs option */
+ if(!(logs = lstrings_new()))
+ {
+ ERROR0("Insufficient memory is available to initialize tesh : system error");
+ return -1;
+ }
- /* used to to store all the excluded file streams */
- excludes = excludes_new();
+ /* the excluded files */
+ if(!(excludes = excludes_new()))
+ {
+ ERROR0("Insufficient memory is available to initialize tesh : system error");
+ return -1;
+ }
- /* list of file streams suffixes */
- suffixes = lstrings_new();
+ /* the suffixes */
+ if(!(suffixes = lstrings_new()))
+ {
+ ERROR0("Insufficient memory is available to initialize tesh : system error");
+ return -1;
+ }
- lstrings_push_back(suffixes,".tesh");
+ /* register the default suffix ".tesh" */
+ if(lstrings_push_back(suffixes,".tesh"))
+ {
+ ERROR0("Insufficient memory is available to initialize tesh : system error");
+ return -1;
+ }
+
+ pid = getpid();
return 0;
}
-static int
+/* load -- load the tesh files to run */
+static void
load(void)
{
- chdir(directory_get_name(root_directory));
- if(want_load_directory)
+ /* if the directories object is not empty load all the tesh files contained in
+ * the directories specified in the command line (this tesh files must have the
+ * a suffix specified in the suffixes object.
+ */
+ if(!directories_is_empty(directories))
directories_load(directories, fstreams, suffixes);
- /* on a aucun fichier specifie dans la ligne de commande
- * l'option --run-current-directory n'a pas ete specifie ou aucun fichier ne se trouve dans le repertoire a charger
+ /* if no tesh file has been specified in the command line try to load the default tesh file
+ * teshfile from the current directory
*/
if(fstreams_is_empty(fstreams))
{
struct stat buffer = {0};
- /* add the default tesh file if it exists */
+ /* add the default tesh file if it exists in the current directory */
if(!stat("teshfile", &buffer) && S_ISREG(buffer.st_mode))
fstreams_add(fstreams, fstream_new(getcwd(NULL, 0), "teshfile"));
}
+ /* excludes the files specified in the command line and stored in the excludes object */
if(!excludes_is_empty(excludes) && !fstreams_is_empty(fstreams))
+ {
+ /* check the files to excludes before */
+ excludes_check(excludes, fstreams);
+
+ /* exclude the specified tesh files */
fstreams_exclude(fstreams, excludes);
+ }
+ /* if the fstreams object is empty use the stdin */
if(fstreams_is_empty(fstreams))
fstreams_add(fstreams, fstream_new(NULL, "stdin"));
+ /* load the tesh files (open them) */
fstreams_load(fstreams);
- return 0;
}
+/* finalize -- cleanup all the allocated objects and display the tesh usage if needed */
static void
finalize(void)
{
- if((!exit_code && want_display_usage) || (!exit_code && !prepared))
- display_usage(exit_code);
+ /* if there is not an error and the user wants display the usage or
+ * if there is an error and all the files to load are loaded, display the usage
+ */
+ if((!exit_code && want_display_usage) || (!exit_code && !loaded))
+ display_usage();
+ /* delete the fstreams object */
if(fstreams)
fstreams_free((void**)&fstreams);
+ /* delete the excludes object */
if(excludes)
excludes_free((void**)&excludes);
+ /* delete the directories object */
if(directories)
directories_free((void**)&directories);
-
- if(includes)
- vector_free(&includes);
+ /* delete the root directory object */
+ if(root_directory)
+ directory_free((void**)&root_directory);
+
+ /* delete the include directories object */
+ if(include_dirs)
+ vector_free(&include_dirs);
+
+ /* delete the list of tesh files suffixes */
if(suffixes)
lstrings_free(&suffixes);
+ /* delete the xbt log options list */
if(logs)
lstrings_free(&logs);
+
- /* destroy the semaphore used to synchronize the jobs */
+ /* destroy the semaphore used to synchronize the units */
if(jobs_sem)
xbt_os_sem_destroy(jobs_sem);
-
+
+ /* destroy the semaphore used by the runner used to wait for the end of the units */
if(units_sem)
xbt_os_sem_destroy(units_sem);
/* exit from the xbt framework */
xbt_exit();
+ /* Windows specific (restore the previouse error mode */
#ifdef WIN32
SetErrorMode(prev_error_mode);
#endif
if(!want_verbose && !want_dry_run && !want_silent && !want_just_display)
INFO2("tesh terminated with exit code %d : %s",exit_code, (!exit_code ? "success" : error_to_string(exit_code)));
+ /* exit with the last error code */
exit(exit_code);
}
+/* init_options -- initialize the options string */
static void
init_options (void)
{
char *p;
unsigned int i;
+ /* the function has been already called */
if(optstring[0] != '\0')
- /* déjà traité. */
return;
p = optstring;
- /* Return switch and non-switch args in order, regardless of
- POSIXLY_CORRECT. Non-switch args are returned as option 1. */
- /* le premier caractère de la chaîne d'options vaut -.
- * les arguments ne correspondant pas à une option sont
- * manipulés comme s'ils étaient des arguments d'une option
- * dont le caractère est le caractère de code 1
- */
*p++ = '-';
for (i = 0; opt_entries[i].c != '\0'; ++i)
{
- /* initialize le nom de l'option longue*/
+ /* initialize the long name of the option*/
longopts[i].name = (opt_entries[i].long_name == 0 ? "" : opt_entries[i].long_name);
- /* getopt_long() retourne la valeur de val */
+ /* getopt_long returns the value of val */
longopts[i].flag = 0;
- /* la valeur de l'option courte est le caractère spécifié dans opt_entries[i].c */
+ /* the short option */
longopts[i].val = opt_entries[i].c;
- /* on l'ajoute à la chaine des optstring */
+ /* add the short option in the options string */
*p++ = opt_entries[i].c;
switch (opt_entries[i].type)
{
- /* si c'est une option qui sert a positionner un flag ou que l'on doit ignorée, elle n'a pas d'argument */
+ /* if this option is used to set a flag or if the argument must be ignored
+ * the option has no argument
+ */
case flag:
longopts[i].has_arg = no_argument;
break;
- /* c'est une option qui attent un argument :
- * une chaine de caractères, un nombre flottant,
- * ou un entier positif
- */
+ /* the option has an argument */
case string:
case number:
longopts[i].name = 0;
}
+/* process_command_line -- process the command line
+ *
+ * param argc the number of the arguments contained by the command line.
+ * param The array of C strings containing all the arguments of the command
+ * line.
+ *
+ * return If successful, the function returns 0. Otherwise -1 is returned
+ * and sets the global variable errno to indicate the error.
+ *
+ * errors [ENOENT] A file name specified in the command line does not exist
+ */
+
static int
process_command_line(int argc, char** argv)
{
directory_t directory;
fstream_t fstream;
- /* initialize the options table of tesh */
+ /* initialize the options string of tesh */
init_options();
- /* display the errors of the function getopt_long() */
+ /* let the function getopt_long display the errors if any */
opterr = 1;
+ /* set option index to zero */
optind = 0;
while (optind < argc)
{
- c = getopt_long (argc, argv, optstring, longopts, (int *) 0);
+ c = getopt_long(argc, argv, optstring, longopts, (int *) 0);
if(c == EOF)
{
}
else if (c == 1)
{
- /* the argument of the command line is not an option (no "-"), assume it's a tesh file */
- /*struct stat buffer = {0};
- char* prev = getcwd(NULL, 0);
+ /* no option specified, assume it's a tesh file to run */
+ char* path;
+ char* delimiter;
- directory = directories_get_back(directories);
-
- chdir(directory->name);
-
- if(stat(optarg, &buffer) || !S_ISREG(buffer.st_mode))
+ /* getpath returns -1 when the file to get the path doesn't exist */
+ if(getpath(optarg, &path) < 0)
{
- chdir(prev);
- free(prev);
- ERROR1("file %s not found", optarg);
- return EFILENOTFOUND;
+ exit_code = errno;
+
+ if(ENOENT == errno)
+ ERROR1("File %s does not exist", optarg);
+ else
+ ERROR0("Insufficient memory is available to parse the command line : system error");
+
+ return -1;
}
- chdir(prev);
- free(prev);*/
+ /* get to the last / (if any) to get the short name of the file */
+ delimiter = strrchr(optarg,'/');
- directory = directories_search_fstream_directory(directories, optarg);
+ /* create a new file stream which represents the tesh file to run */
+ fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);
- if(!directory)
- {
- if(1 == directories_get_size(directories))
- {
- ERROR1("file %s not found in the current directory",optarg);
- return EFILENOTINCURDIR;
- }
- else
- {
- ERROR1("file %s not found in the specified directories",optarg);
- return EFILENOTINSPECDIR;
- }
- }
+ free(path);
- if(!(fstream = fstream_new(directory_get_name(directory), optarg)))
+ /* if the list of all tesh files to run already contains this file
+ * destroy it and display a warning, otherwise add it in the list.
+ */
+ if(fstreams_contains(fstreams, fstream))
{
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
+ fstream_free((void**)&fstream);
+ WARN1("File %s already specified to be run", optarg);
}
else
- {
- if(fstreams_contains(fstreams, fstream))
- {
- fstream_free((void**)&fstream);
- WARN1("file %s already specified", optarg);
- }
- else
- {
- if((errno = fstreams_add(fstreams, fstream)))
- {
- fstream_free((void**)&fstream);
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
- }
- }
- }
+ fstreams_add(fstreams, fstream);
+
+
+
+
}
else if (c == '?')
{
- /* unknown option, getopt_long() displays the error */
- return 1;
+ /* unknown option, let getopt_long() displays the error */
+ return -1;
}
else
{
- for (entry = opt_entries; entry->c != '\0'; ++entry)
+ for(entry = opt_entries; entry->c != '\0'; ++entry)
if(c == entry->c)
{
{
/* impossible */
default:
- ERROR0("command line processing failed : internal error");
- return EPROCESSCMDLINE;
+ ERROR0("Command line processing failed : internal error");
+ exit_code = EPROCCMDLINE;
+ return -1;
/* flag options */
else if (*optarg == '\0')
{
/* a non optional argument is not specified */
- ERROR2("the option %c \"%s\"requires an argument",entry->c,entry->long_name);
- return EARGNOTSPEC;
+ ERROR2("Option %c \"%s\"requires an argument",entry->c,entry->long_name);
+ exit_code = ENOARG;
+ return -1;
}
- /* --directory option */
- if(!strcmp(entry->long_name,"directory"))
+ /* --load-directory option */
+ if(!strcmp(entry->long_name,"load-directory"))
{
- if(!(directory = directory_new(optarg, want_load_directory)))
+ char* path;
+
+ if(translatepath(optarg, &path) < 0)
{
+ exit_code = errno;
+
if(ENOTDIR == errno)
- {
- ERROR1("directory %s not found",optarg);
- return EDIRNOTFOUND;
- }
+ ERROR1("%s is not a directory",optarg);
else
- {
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
- }
+ ERROR0("Insufficient memory is available to process the command line - system error");
+
+ return -1;
+
}
else
{
+ directory = directory_new(path);
+ free(path);
+
if(directories_contains(directories, directory))
{
directory_free((void**)&directory);
- WARN1("directory %s already specified",optarg);
+ WARN1("Directory %s already specified to be load",optarg);
}
else
- {
- if((errno = directories_add(directories, directory)))
- {
- directory_free((void**)&directory);
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
- }
- }
+ directories_add(directories, directory);
+
+
}
}
+ else if(!strcmp(entry->long_name,"directory"))
+ {
+ char* path ;
+
+ if(translatepath(optarg, &path) < 0)
+ {
+ exit_code = errno;
+
+ if(ENOTDIR == errno)
+ ERROR1("%s is not a directory",optarg);
+ else
+ ERROR0("Insufficient memory is available to process the command line - system error");
+
+ return -1;
+ }
+ else
+ {
+ if(!dont_want_display_directory)
+ INFO1("Entering directory \"%s\"",path);
+
+ chdir(path);
+ free(path);
+
+
+ }
+ }
+
/* --suffix option */
else if(!strcmp(entry->long_name,"suffix"))
{
if(strlen(optarg) > MAX_SUFFIX)
{
- ERROR1("suffix %s too long",optarg);
- return ESUFFIXTOOLONG;
+ ERROR1("Suffix %s too long",optarg);
+ exit_code = ESUFFIXTOOLONG;
+ return -1;
}
if(optarg[0] == '.')
sprintf(suffix,".%s",optarg);
if(lstrings_contains(suffixes, suffix))
- WARN1("suffix %s already specified", optarg);
+ WARN1("Suffix %s already specified to be used", optarg);
else
lstrings_push_back(suffixes, suffix);
}
else
{
if(lstrings_contains(suffixes, optarg))
- WARN1("suffix %s already specified", optarg);
+ WARN1("Suffix %s already specified to be used", optarg);
else
lstrings_push_back(suffixes, optarg);
}
/* --file option */
else if(!strcmp(entry->long_name,"file"))
{
+ char* path;
+ char* delimiter;
- /* the argument of the command line is not an option (no "-"), assume it's a tesh file */
- /*struct stat buffer = {0};
- char* prev = getcwd(NULL, 0);
-
- directory = directories_get_back(directories);
-
- chdir(directory->name);
-
- if(stat(optarg, &buffer) || !S_ISREG(buffer.st_mode))
+ if(getpath(optarg, &path) < 0)
{
- chdir(prev);
- free(prev);
- ERROR1("file %s not found", optarg);
- return EFILENOTFOUND;
+ exit_code = errno;
+
+ if(ENOENT == errno)
+ ERROR1("File %s does not exist", optarg);
+ else
+ ERROR0("Insufficient memory is available to process the command line - system error");
+
+ return -1;
}
- chdir(prev);
- free(prev);*/
+ delimiter = strrchr(optarg,'/');
- directory = directories_search_fstream_directory(directories, optarg);
-
- if(!directory)
- {
- if(1 == directories_get_size(directories))
- {
- ERROR1("file %s not found in the current directory",optarg);
- return EFILENOTINCURDIR;
- }
- else
- {
- ERROR1("file %s not found in the specified directories",optarg);
- return EFILENOTINSPECDIR;
- }
- }
+ fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);
- if(!(fstream = fstream_new(directory_get_name(directory),optarg)))
+ free(path);
+
+ if(fstreams_contains(fstreams, fstream))
{
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
+ fstream_free((void**)&fstream);
+ WARN1("File %s already specified to run", optarg);
}
else
- {
- if(fstreams_contains(fstreams, fstream))
- {
- fstream_free((void**)&fstream);
- WARN1("file %s already specified", optarg);
- }
- else
- {
- if((errno = fstreams_add(fstreams, fstream)))
- {
- fstream_free((void**)&fstream);
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
- }
- }
- }
+ fstreams_add(fstreams, fstream);
}
/* --include-dir option */
else if(!strcmp(entry->long_name,"include-dir"))
{
- if(!(directory = directory_new(optarg, want_load_directory)))
+
+ char* path ;
+
+ if(translatepath(optarg, &path) < 0)
{
+ exit_code = errno;
+
if(ENOTDIR == errno)
- {
ERROR1("%s is not a directory",optarg);
- return EDIRNOTFOUND;
- }
else
- {
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
- }
+ ERROR0("Insufficient memory is available to process the command line - system error");
+
+ return -1;
}
else
{
- if(vector_contains(includes, directory))
+
+ directory = directory_new(path);
+ free(path);
+
+ if(vector_contains(include_dirs, directory))
{
directory_free((void**)&directory);
- WARN1("include directory %s already specified",optarg);
+ WARN1("Include directory %s already specified to be used",optarg);
}
else
- {
- if((errno = vector_push_back(includes, directory)))
- {
- directory_free((void**)&directory);
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
- }
- }
+ vector_push_back(include_dirs, directory);
}
}
/* --exclude option */
else if(!strcmp(entry->long_name,"exclude"))
{
- directory = directories_get_back(directories);
- if(!(fstream = fstream_new(directory_get_name(directory), optarg)))
+ char* path;
+ char* delimiter;
+
+ if(getpath(optarg, &path) < 0)
{
+ exit_code = errno;
+
if(ENOENT == errno)
- {
- ERROR1("file to exclude %s not found", optarg);
- return EFILENOTFOUND;
- }
+ ERROR1("file %s does not exist", optarg);
else
- {
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
- }
+ ERROR0("Insufficient memory is available to process the command line - system error");
+
+ return -1;
}
- else
+
+ delimiter = strrchr(optarg,'/');
+
+ fstream = fstream_new(path, delimiter ? delimiter + 1 : optarg);
+ free(path);
+
+ if(excludes_contains(excludes, fstream))
{
- if(excludes_contains(excludes, fstream))
- {
- fstream_free((void**)&fstream);
- WARN1("file to exclude %s already specified", optarg);
- }
- else
- {
- if((errno = excludes_add(excludes, fstream)))
- {
- fstream_free((void**)&fstream);
- ERROR1("command line processing failed with the error code %d", errno);
- return EPROCESSCMDLINE;
- }
- }
- }
+ fstream_free((void**)&fstream);
+ WARN1("File %s already specified to be exclude", optarg);
+ }
+ else
+ excludes_add(excludes, fstream);
+
}
/* --log option */
else if(!strcmp(entry->long_name,"log"))
}
else
{
- /* TODO */
+ INFO1("Unexpected option %s", optarg);
+ return -1;
}
if (i < 1 || cp[0] != '\0')
{
- ERROR2("option %c \"%s\" requires an strictly positive integer as argument",entry->c, entry->long_name);
- return ENOTPOSITIVENUM;
+ ERROR2("Option %c \"%s\" requires an strictly positive integer as argument",entry->c, entry->long_name);
+ exit_code = ENOTPOSITIVENUM;
+ return -1;
}
else
*(int*)entry->value = i;
}
static void
-display_usage(int exit_code)
+display_usage(void)
{
const char **cpp;
FILE* stream;
reader->command = command;
reader->broken_pipe = 0;
reader->failed = 0;
+ reader->done = 0;
reader->started = xbt_os_sem_init(0);
command_handle_failure(command, csr_read_failure);
}
- reader->broken_pipe = 1;
-
- /*printf("the reader of the command %p is ended\n",command);*/
+ reader->done = 1;
return NULL;
}
#include <runner.h>
#include <units.h>
+#include <unit.h>
#include <error.h>
+#include <variable.h>
#include <errno.h> /* for error code */
#include <stdlib.h> /* for calloc() */
-#include <stdio.h>
+#include <stdio.h>
+
+#include <sys/resource.h>
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
+extern char**
+environ;
+
/* the unique tesh runner */
static runner_t
runner = NULL;
* the want_check_syntax is specified. Returns
* 0 if the syntax is clean.
*/
-static int
+static void
check_syntax(void);
#ifdef WIN32
runner_init(int want_check_syntax, int timeout, fstreams_t fstreams)
{
- if(runner)
- {
- ERROR0("Runner is already initialized");
- return EEXIST;
- }
-
+ int i;
+ char* val;
+ char buffer[MAX_PATH + 1] = {0};
+ int code;
+ const char* cstr;
+ variable_t variable;
- runner = xbt_new0(s_runner_t, 1);
+ if(!(runner = (runner_t)calloc(1, sizeof(s_runner_t))))
+ return errno;
+
if(!(runner->units = units_new(runner, fstreams)))
{
- ERROR0("Runner initialization failed");
-
free(runner);
runner = NULL;
return errno;
runner->number_of_runned_units = 0;
runner->waiting = 0;
- if(want_check_syntax)
+ runner->total_of_tests = 0;
+ runner->total_of_successeded_tests = 0;
+ runner->total_of_failed_tests = 0;
+ runner->total_of_interrupted_tests = 0;
+
+ runner->total_of_units = 0;
+ runner->total_of_successeded_units = 0;
+ runner->total_of_failed_units = 0;
+ runner->total_of_interrupted_units = 0;
+
+ runner->total_of_suites = 0;
+ runner->total_of_successeded_suites = 0;
+ runner->total_of_failed_suites = 0;
+ runner->total_of_interrupted_suites = 0;
+
+ /* initialize the vector of variables */
+ runner->variables = vector_new(32, (fn_finalize_t)variable_free);
+
+
+ for(i = 0; environ[i] != NULL; i++)
+ {
+ val = strchr(environ[i], '=');
+
+ if(val)
+ {
+ val++;
+
+ if(val[0] != '\0')
+ strncpy(buffer, environ[i], (val - environ[i] -1));
+
+ variable = variable_new(buffer, val);
+ variable->env = 1;
+
+ /*printf("Add the environment variable %s %s\n", variable->name, variable->val);*/
+
+ vector_push_back(runner->variables, variable);
+ }
+ }
+
+ i = 0;
+
+ while((cstr = error_get_at(i++, &code)))
{
- if((errno = check_syntax()))
- return errno;
+ sprintf(buffer,"%d",code);
+ variable = variable_new(cstr, buffer);
+ variable->err = 1;
+ vector_push_back(runner->variables, variable);
}
+
+
+ if(want_check_syntax)
+ check_syntax();
- return 0;
+ return exit_code;
}
runner_destroy(void)
{
units_free((void**)(&(runner->units)));
+ vector_free(&runner->variables);
+
#ifdef WIN32
CloseHandle(timer_handle);
void
runner_display_status(void)
{
+
if(!want_dry_run)
{
+ struct rusage r_usage;
- /*unit_t unit;*/
-
- printf("Runner\n");
- printf("Status informations :\n");
-
- printf(" number of units %d\n",units_get_size(runner->units));
- printf(" exit code %d (%s)\n",exit_code, exit_code ? error_to_string(exit_code) : "success");
+ /*printf("\033[1m");*/
+ printf("\n TEst SHell utility - mini shell specialized in running test units.\n");
+ printf(" =============================================================================\n");
+ /*printf("\033[0m");*/
units_verbose(runner->units);
+
+ /*printf("\033[1m");*/
+ printf(" =====================================================================%s\n",
+ runner->total_of_failed_tests ? "== FAILED": (runner->total_of_interrupted_tests || runner->total_of_interrupted_units) ? "==== INTR" : "====== OK");
+
+ printf(" TOTAL : Suite(s): %.0f%% ok (%d suite(s): %d ok",
+ (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),
+ runner->total_of_suites, runner->total_of_successeded_suites);
+
+ if(runner->total_of_failed_suites > 0)
+ printf(", %d failed", runner->total_of_failed_suites);
+
+ if(runner->total_of_interrupted_suites > 0)
+ printf(", %d interrupted)", runner->total_of_interrupted_suites);
+
+ printf(")\n");
+
+ printf(" Unit(s): %.0f%% ok (%d unit(s): %d ok",
+ (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),
+ runner->total_of_units, runner->total_of_successeded_units);
+
+ if(runner->total_of_failed_units > 0)
+ printf(", %d failed", runner->total_of_failed_units);
+
+ if(runner->total_of_interrupted_units > 0)
+ printf(", %d interrupted)", runner->total_of_interrupted_units);
+
+ printf(")\n");
+
+ printf(" Test(s): %.0f%% ok (%d test(s): %d ok",
+ (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),
+ runner->total_of_tests, runner->total_of_successeded_tests);
+
+ if(runner->total_of_failed_tests > 0)
+ printf(", %d failed", runner->total_of_failed_tests);
+
+ if(runner->total_of_interrupted_tests > 0)
+ printf(", %d interrupted)", runner->total_of_interrupted_tests);
+
+ printf(")\n\n");
+
+
+ if(!getrusage(RUSAGE_SELF, &r_usage))
+ {
+
+ 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);
+ 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);
+
+ if(!getrusage(RUSAGE_CHILDREN, &r_usage))
+ {
+ 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);
+ 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);
+
+ }
+ }
+
+ /*printf("\033[0m");*/
}
else
{
if(exit_code)
ERROR0("Syntax error detected");
- else if(exit_code == 0)
+ else if(!exit_code)
INFO0("Syntax 0K");
}
}
-static int
+static void
check_syntax(void)
{
if(!want_dry_run)
want_dry_run = 0;
- if(0 == exit_code)
+ if(!exit_code)
{
if(!want_silent)
INFO0("syntax checked (OK)");
WARN0("mismatch in the syntax : --just-check-syntax and --check-syntax options at same time");
}
- return exit_code;
}
#include <command.h>
#include <context.h>
#include <fstream.h>
-
-
+#include <variable.h>
+#include <str_replace.h>
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
+static void
+replace_variables(unit_t unit, char** line)
+{
+ variable_t variable;
+ char name[MAX_PATH + 1] = {0};
+
+ /* check if some commands have setted some environment variables */
+ /* TODO */
+
+ /*printf("repalce all the variables of the line %s\n", *line);*/
+
+
+ xbt_os_mutex_acquire(unit->mutex);
+
+ vector_rewind(unit->runner->variables);
+
+ while((variable = vector_get(unit->runner->variables)))
+ {
+ sprintf(name, "$%s", variable->name);
+ /*printf("try to replace all the variable %s\n",name);*/
+ str_replace_all(line, name, variable->val);
+
+ vector_move_next(unit->runner->variables);
+ memset(name, 0, MAX_PATH + 1);
+ }
+
+ xbt_os_mutex_release(unit->mutex);
+
+ /*printf("line after the variables replacement %s\n",*line);*/
+
+}
/* 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_new(runner_t runner, unit_t root, unit_t owner, fstream_t fstream)
{
unit_t unit = xbt_new0(s_unit_t, 1);
unit->sem = NULL;
unit->commands = vector_new(DEFAULT_COMMANDS_CAPACITY, (fn_finalize_t)command_free);
+ unit->includes = vector_new(DEFAULT_INCLUDES, (fn_finalize_t)unit_free);
unit->thread = NULL;
unit->number_of_failed_commands = 0;
unit->number_of_successeded_commands = 0;
unit->number_of_terminated_commands = 0;
+ unit->number_of_waiting_commands = 0;
unit->interrupted = 0;
unit->failed = 0;
unit->successeded = 0;
unit->owner = owner;
+
+ unit->root = root ? root : unit;
+
+
unit->number = 0;
- unit->suites = vector_new(DEFAULT_COMMANDS_CAPACITY, (fn_finalize_t)suite_free);
+ unit->suites = vector_new(DEFAULT_SUITES_CAPACITY, (fn_finalize_t)unit_free);
unit->owner = owner;
unit->running_suite = 0;
+ unit->is_suite = 0;
+ unit->description = NULL;
return unit;
}
-void
+/*void
unit_add_suite(unit_t unit, suite_t suite)
{
vector_push_back(unit->suites, suite);
-}
+}*/
int
unit_free(void** unitptr)
vector_free(&((*__unitptr)->commands));
+ vector_free(&((*__unitptr)->includes));
+
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);
-
+ if((*__unitptr)->description)
+ free((*__unitptr)->description);
+
free((*__unitptr)->suites);
free(*__unitptr);
xbt_os_thread_t thread;
xbt_os_mutex_t mutex;
- context_t context;
- int i;
+ /*context_t context;*/
+ int i, j;
unit_t unit = (unit_t)p;
+ unit_t include;
xbt_os_mutex_acquire(unit->mutex);
unit->runner->number_of_runned_units++;
xbt_os_sem_acquire(jobs_sem);
mutex = xbt_os_mutex_init();
- context = context_new();
+ /*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);
+ /*unit_parse(unit, context, mutex, unit->fstream->name, unit->fstream->stream);*/
+
+ fstream_parse(unit->fstream, unit, mutex);
+
/* 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;
* 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)))
+ {
+ /*INFO1("the unit %s is released", unit->fstream->name);*/
xbt_os_sem_release(unit->sem);
+ }
+ else
+ {
+
+ INFO1("the unit %s is not released", unit->fstream->name);
+ INFO1("number of started commands %d", unit->number_of_started_commands);
+ INFO1("number of failed commands %d", unit->number_of_failed_commands);
+ INFO1("number of interrupted commands %d", unit->number_of_interrupted_commands);
+ INFO1("number of successeded commands %d", unit->number_of_successeded_commands);
+ INFO1("number of waiting commands %d", unit->number_of_waiting_commands);
+
+
+ if(unit->number_of_waiting_commands)
+ {
+ command_t command;
+ int i, j;
+
+ for(i = 0; i < vector_get_size(unit->includes) ; i++)
+ {
+ include = vector_get_at(unit->includes, i);
+
+ for(j = 0; j < vector_get_size(include->commands); j++)
+ {
+ command = vector_get_at(include->commands, j);
+
+ if(command->status == cs_in_progress)
+ {
+ INFO2("the command %s PID %d is in process", command->context->command_line, command->pid);
+
+ if(command->writer->done)
+ INFO2("the writer of the command %s PID %d done", command->context->command_line, command->pid);
+ else
+ INFO2("the writer of the command %s PID %d doesn't done", command->context->command_line, command->pid);
+ }
+ }
+
+ }
+ }
+ }
}
/* 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++)
+ for(i = 0; i < vector_get_size(unit->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);
}
+
+ for(i = 0; i < vector_get_size(unit->includes); i++)
+ {
+ include = vector_get_at(unit->includes, i);
+
+ for(j = 0; j < vector_get_size(include->commands); j++)
+ {
+ command = vector_get_at(include->commands, j);
+
+ if(command->status == cs_in_progress)
+ command_interrupt(command);
+ }
+ }
+
}
-
/* wait the end of the threads */
- for(i = 0; i < unit->number_of_commands; i++)
+ for(i = 0; i < vector_get_size(unit->commands); i++)
{
- /*thread = unit->commands[i]->thread;*/
-
command_t command = vector_get_at(unit->commands, i);
thread = command->thread;
xbt_os_thread_join(thread,NULL);
}
- context_free(&context);
-
+ for(i = 0; i < vector_get_size(unit->includes); i++)
+ {
+ include = vector_get_at(unit->includes, i);
+
+ for(j = 0; j < vector_get_size(include->commands); j++)
+ {
+ command_t command = vector_get_at(include->commands, j);
+ 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 */
/* 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);
unit_handle_failure(unit);
break;
}
+ else if(unit->running_suite)
+ {
+ /* TODO */
+ }
+
if(context->command_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)
{
+ char* line2;
/* Search end */
xbt_str_rtrim(line+2,"\n");
- switch (line[0])
+ line2 = strdup(line);
+
+ replace_variables(unit, &line2);
+
+ switch (line2[0])
{
case '#':
break;
case '$':
case '&':
- context->async = (line[0] == '&');
+ context->async = (line2[0] == '&');
/* further trim useless chars which are significant for in/output */
- xbt_str_rtrim(line+2," \t");
+ xbt_str_rtrim(line2+2," \t");
/* Deal with CD commands here, not in rctx */
- if (!strncmp("cd ",line+2,3))
+ if(!strncmp("cd ",line2 + 2, 3))
{
- char *dir=line+4;
-
- if (context->command_line)
+ /*char *dir=line2+4; */
+ char* dir = strdup(line2 + 4);
+
+ if(context->command_line)
{
if(!want_dry_run)
{
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));
exit_code = ECHDIR;
unit_handle_failure(unit);
}
+
+
}
break;
} /* else, pushline */
else
{
- unit_pushline(unit, context, mutex, filepos, line[0], line+2 /* pass '$ ' stuff*/);
+ unit_pushline(unit, context, mutex, filepos, line2[0], line2+2 /* pass '$ ' stuff*/);
break;
}
case '<':
case '>':
case '!':
- unit_pushline(unit, context, mutex, filepos, line[0], line+2 /* pass '$ ' stuff*/);
+ unit_pushline(unit, context, mutex, filepos, line2[0], line2+2 /* pass '$ ' stuff*/);
break;
case 'p':
if(!want_dry_run)
- INFO2("[%s] %s",filepos,line+2);
+ INFO2("[%s] %s",filepos,line2+2);
break;
case 'P':
if(!want_dry_run)
- CRITICAL2("[%s] %s",filepos,line+2);
+ CRITICAL2("[%s] %s",filepos,line2+2);
+ break;
+
+ case 'D':
+ if(unit->description)
+ WARN2("description already specified %s %s", filepos, line2);
+ else
+ unit->description = strdup(line2 + 2);
break;
default:
- ERROR2("[%s] Syntax error: %s",filepos, line);
+ ERROR2("[%s] Syntax error: %s",filepos, line2);
ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name);
exit_code = ESYNTAX;
unit_handle_failure(unit);
break;
}
+
+ free(line2);
}
void
VERB1("[%s] (ignore output of next command)", filepos);
}
- else if(!strncmp(line,"include", strlen("include")))
+ else if(!strncmp(line,"include ", strlen("include ")))
{
- unit_handle_include(unit, context, mutex, line + strlen("include "));
+ char* p1;
+ char* p2;
+
+ p1 = line + strlen("include");
+
+ while(*p1 == ' ' || *p1 == '\t')
+ p1++;
+
+
+ if(p1[0] == '\0')
+ {
+
+ exit_code = ESYNTAX;
+ ERROR1("include file not specified %s ", filepos);
+ unit_handle_failure(unit);
+ }
+ else
+ {
+ char file_name[MAX_PATH + 1] = {0};
+
+ /*INFO1("p1 is %s",p1);*/
+
+ p2 = p1;
+
+ while(*p2 != '\0' && *p2 != ' ' && *p2 != '\t')
+ {
+ /*INFO1("p2 is %s",p2);*/
+ p2++;
+
+ }
+ /*INFO1("p2 is %s",p2);*/
+
+ strncpy(file_name, p1, p2 - p1);
+
+ /*INFO1("filename is %s", file_name);*/
+
+ if(p2[0] != '\0')
+ while(*p2 == ' ' || *p2 == '\t')
+ p2++;
+
+ unit_handle_include(unit, context, mutex, file_name, p2[0] != '\0' ? p2 : NULL);
+
+ }
}
-
- else if(!strncmp(line,"suite", strlen("suite")))
+ else if(!strncmp(line,"suite ", strlen("suite ")))
{
unit_handle_suite(unit, context, mutex, line + strlen("suite "));
}
- else
+ else if(!strncmp(line,"unsetenv ", strlen("unsetenv ")))
+ {
+ int i;
+ int number_of_variables;
+ int exists = 0;
+ int env;
+ variable_t variable;
+ char* name = line + strlen("unsetenv ");
+
+ xbt_os_mutex_acquire(unit->mutex);
+
+ number_of_variables = vector_get_size(unit->runner->variables);
+
+ for(i = 0; i < number_of_variables; i++)
+ {
+ variable = vector_get_at(unit->runner->variables, i);
+
+ if(!strcmp(variable->name, name))
+ {
+ env = variable->env;
+ exists = 1;
+ break;
+ }
+ }
+
+ if(env)
+ {
+ if(exists)
+ vector_erase_at(unit->runner->variables, i);
+ else
+ WARN3("environment variable %s not found %s %s", name, line, filepos);
+ }
+ else
+ {
+ if(exists)
+ WARN3("%s is an not environment variable use unset metacommand to delete it %s %s", name, line, filepos);
+ else
+ WARN3("%s environment variable not found %s %s", name, line, filepos);
+ }
+
+ xbt_os_mutex_release(unit->mutex);
+
+
+ }
+ else if(!strncmp(line,"setenv ", strlen("setenv ")))
+ {
+ char* val;
+ char name[MAX_PATH + 1] = {0};
+ char* p;
+
+ p = line + strlen("setenv ");
+
+ val = strchr(p, '=');
+
+ if(val)
+ {
+ variable_t variable;
+ int exists = 0;
+ int env = 0;
+ val++;
+
+ /* syntax error */
+ if(val[0] == '\0')
+ {
+
+ exit_code = ESYNTAX;
+ ERROR2("indefinite variable value %s %s", line, filepos);
+ unit_handle_failure(unit);
+ }
+
+
+
+ strncpy(name, p, (val - p -1));
+
+ /* test if the variable is already registred */
+
+ xbt_os_mutex_acquire(unit->mutex);
+
+ vector_rewind(unit->runner->variables);
+
+ while((variable = vector_get(unit->runner->variables)))
+ {
+
+ if(!strcmp(variable->name, name))
+ {
+ variable->env = 1;
+ exists = 1;
+ break;
+ }
+
+ vector_move_next(unit->runner->variables);
+ }
+
+ /* if the variable is already registred, update its value;
+ * otherwise register it.
+ */
+ if(exists)
+ {
+ if(env)
+ {
+ free(variable->val);
+ variable->val = strdup(val);
+ setenv(variable->name, variable->val, 1);
+ }
+ else
+ WARN3("%s variable already exists %s %s", name, line, filepos);
+ }
+ else
+ {
+ variable = variable_new(name, val);
+ variable->env = 1;
+
+ vector_push_back(unit->runner->variables, variable);
+
+ setenv(variable->name, variable->val, 0);
+ }
+
+ xbt_os_mutex_release(unit->mutex);
+
+ }
+ }
+ else if(!strncmp(line,"unset ", strlen("unset ")))
{
- 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;
+ int i;
+ int number_of_variables;
+ int exists = 0;
+ int env;
+ int err;
+ variable_t variable;
+ char* name = line + strlen("unset ");
+
+ xbt_os_mutex_acquire(unit->mutex);
+
+ number_of_variables = vector_get_size(unit->runner->variables);
+
+ for(i = 0; i < number_of_variables; i++)
+ {
+ variable = vector_get_at(unit->runner->variables, i);
+
+ if(!strcmp(variable->name, name))
+ {
+ env = variable->env;
+ err = variable->err;
+ exists = 1;
+ break;
+ }
+ }
+
+ if(!env)
+ {
+ if(exists)
+ vector_erase_at(unit->runner->variables, i);
+ else
+ WARN3("variable %s not found %s %s", name, line, filepos);
+ }
+ else if(env)
+ {
+ WARN3("%s is an environment variable use unsetenv metacommand to delete it %s %s", name, line, filepos);
+ }
+ else
+ {
+ WARN3("%s is an error variable : you are not allowed to delete it %s %s", name, line, filepos);
+ }
+ xbt_os_mutex_release(unit->mutex);
+
}
+ else
+ {
+ /* may be a variable */
+ char* val;
+ char name[MAX_PATH + 1] = {0};
+
+ val = strchr(line, '=');
+
+ if(val)
+ {
+ variable_t variable;
+ int exists = 0;
+ val++;
+
+ /* syntax error */
+ if(val[0] == '\0')
+ {
+
+ exit_code = ESYNTAX;
+ ERROR2("indefinite variable value %s %s", line, filepos);
+ unit_handle_failure(unit);
+ }
+
+
+ /* assume it's a varibale */
+ strncpy(name, line, (val - line -1));
+
+ xbt_os_mutex_acquire(unit->mutex);
+
+ /* test if the variable is already registred */
+
+ vector_rewind(unit->runner->variables);
+
+ while((variable = vector_get(unit->runner->variables)))
+ {
+
+ if(!strcmp(variable->name, name))
+ {
+ exists = 1;
+ break;
+ }
+
+ vector_move_next(unit->runner->variables);
+ }
+
+ /* if the variable is already registred, update its value;
+ * otherwise register it.
+ */
+ if(exists)
+ {
+ free(variable->val);
+ variable->val = strdup(val);
+ }
+ else
+ vector_push_back(unit->runner->variables,variable_new(name, val));
+
+ xbt_os_mutex_release(unit->mutex);
+
+ }
+ 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)
+ unit_t root = unit->root ? unit->root : unit;
+
+ if(!root->interrupted)
{
/* the unit interrupted (exit for the loop) */
- unit->interrupted = 1;
+ root->interrupted = 1;
/* release the unit */
- xbt_os_sem_release(unit->sem);
+ xbt_os_sem_release(root->sem);
}
/* if the --keep-going option is not specified */
xbt_os_sem_release(unit->sem);
}
+void
+display_title(const char* description)
+{
+ int i;
+ char title[80];
+ int len = strlen(description);
+
+ title[0]=' ';
+
+ for (i = 1; i < 79; i++)
+ title[i]='=';
+
+ title[i++]='\n';
+ title[79]='\0';
+
+ sprintf(title + 40 - (len + 4)/2, "[ %s ]",description);
+ title[40 + (len + 5 ) / 2] = '=';
+
+ printf("\n%s\n",title);
+}
+
void
unit_verbose(unit_t unit)
{
- int i, test_count;
+ int i, j, k;
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)");
+ unit_t include;
+ unit_t suite;
+ char* p;
+ char title[MAX_PATH + 1] = {0};
+
+ int number_of_tests = 0; /* number of tests of a unit contained by this unit */
+ int number_of_failed_tests = 0; /* number of failed test of a unit contained by this unit */
+ int number_of_successeded_tests = 0; /* number of successeded tests of a unit contained by this unit */
+ int number_of_interrupted_tests = 0; /* number of interrupted tests of a unit contained by this unit */
+
+ int number_of_tests_of_suite = 0; /* number of tests of a suite contained by this unit */
+ int number_of_interrupted_tests_of_suite = 0; /* number of interrupted tests of a suite contained by this unit */
+ int number_of_failed_tests_of_suite = 0; /* number of failed tests of a suite contained by this unit */
+ int number_of_successeded_tests_of_suite = 0; /* number of successeded tests of a suite contained by this */
+
+ int number_of_units = 0; /* number of units contained by a suite */
+ int number_of_failed_units = 0; /* number of failed units contained by a suite */
+ int number_of_successeded_units = 0; /* number of successeded units contained by a suite */
+ int number_of_interrupted_units = 0; /* number of interrupted units contained by a suite */
+
+ int total_of_tests = 0; /* total of the tests contained by this unit */
+ int total_of_failed_tests = 0; /* total of failed tests contained by this unit */
+ int total_of_successeded_tests = 0; /* total of successeded tests contained by this unit */
+ int total_of_interrupted_tests = 0; /* total of interrupted tests contained by this unit */
+
+ int total_of_units = 0; /* total of units contained by this unit */
+ int total_of_failed_units = 0; /* total of failed units contained by this unit */
+ int total_of_successeded_units = 0; /* total of successeded units contained by this unit */
+ int total_of_interrupted_units = 0; /* total of interrutped units contained by this unit */
+
+ int total_of_suites = 0; /* total of suites contained by this unit */
+ int total_of_failed_suites = 0; /* total of failed suites contained by this unit */
+ int total_of_successeded_suites = 0; /* total of successeded suites contained by this unit */
+ int total_of_interrupted_suites = 0; /* total of interrupted suites contained by this unit */
+
+
+ if(unit->description)
+ strcpy(title, unit->description);
else
+ sprintf(title, "file : %s",unit->fstream->name);
+
+ if(unit->interrupted)
+ strcat(title, " (interrupted)");
+
+ display_title(title);
+
+ number_of_tests = vector_get_size(unit->commands);
+
+
+ /* tests */
+ for(i = 0; i < number_of_tests; i++)
{
- if(unit->interrupted)
- printf(" - (interruped)");
- else
- printf(" - (failed)");
+ command = vector_get_at(unit->commands, i);
+
+ if(command->status == cs_interrupted)
+ number_of_interrupted_tests++;
+ else if(command->status == cs_failed)
+ number_of_failed_tests++;
+ else if(command->status == cs_successeded)
+ number_of_successeded_tests++;
+
}
- 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);
+ if(number_of_tests)
+ {
+ asprintf(&p," Test(s): .........................................................................");
+
+ p[70] = '\0';
+ printf("%s", p);
+ free(p);
+
+ if(number_of_failed_tests > 0)
+ printf(".. failed\n");
+ else if(number_of_interrupted_tests > 0)
+ printf("interrupt\n");
+ else
+ printf(".... ..ok\n");
+
+
+ for(i = 0; i < number_of_tests; i++)
+ {
+ command = vector_get_at(unit->commands, i);
+
+ printf(" %s: %s [%s]\n",
+ command->status == cs_interrupted ? "INTR "
+ : command->status == cs_failed ? "FAILED"
+ : command->status == cs_successeded ? "PASS "
+ : "UNKNWN",
+ command->context->command_line,
+ command->context->line);
+
+ if(want_detail_summary)
+ command_display_status(command);
+
+ }
+
+ printf(" =====================================================================%s\n",
+ number_of_failed_tests ? "== FAILED": number_of_interrupted_tests ? "==== INTR" : "====== OK");
+
+ printf(" Summary: Test(s): %.0f%% ok (%d test(s): %d ok",
+ ((1-((double)number_of_failed_tests + (double)number_of_interrupted_tests)/(double)number_of_tests)*100.0),
+ number_of_tests, number_of_successeded_tests);
+
+ if(number_of_failed_tests > 0)
+ printf(", %d failed", number_of_failed_tests);
+
+ if(number_of_interrupted_tests > 0)
+ printf(", %d interrupted)", number_of_interrupted_tests);
+
+ printf(")\n\n");
+
+ total_of_tests = number_of_tests;
+ total_of_failed_tests = number_of_failed_tests;
+ total_of_interrupted_tests = number_of_interrupted_tests;
+ total_of_successeded_tests = number_of_successeded_tests;
+ }
- test_count = unit->number_of_commands;
- for(i = 0; i < test_count; i++)
+ /* includes */
+
+ total_of_failed_units = total_of_interrupted_units = total_of_successeded_units = 0;
+
+ number_of_failed_units = number_of_successeded_units = number_of_interrupted_units = 0;
+
+ number_of_units = vector_get_size(unit->includes);
+
+ for(i = 0; i < number_of_units ; i++)
{
- command = vector_get_at(unit->commands, i);
+ include = vector_get_at(unit->includes, i);
+
+ number_of_interrupted_tests = number_of_failed_tests = number_of_successeded_tests = 0;
+
+ number_of_tests = vector_get_size(include->commands);
+
+ for(j = 0; j < number_of_tests; j++)
+ {
+ command = vector_get_at(include->commands, j);
+
+ if(command->status == cs_interrupted)
+ number_of_interrupted_tests++;
+ else if(command->status == cs_failed)
+ number_of_failed_tests++;
+ else if(command->status == cs_successeded)
+ number_of_successeded_tests++;
+ }
+
+ asprintf(&p," Unit: %s ............................................................................", include->description ? include->description : include->fstream->name);
+
+ p[70] = '\0';
+ printf("%s", p);
+ free(p);
+
+
+ if(number_of_failed_tests > 0)
+ {
+ total_of_failed_units++;
+ printf(".. failed\n");
+ }
+ else if(number_of_interrupted_tests > 0)
+ {
+ total_of_interrupted_units++;
+ printf("interrupt\n");
+ }
+ else
+ {
+ total_of_successeded_units++;
+ printf(".... ..ok\n");
+ }
+
+ if(want_detail_summary)
+ {
+
+ for(j = 0; j < vector_get_size(include->commands); j++)
+ {
+ command = vector_get_at(include->commands, j);
+
+ printf(" %s: %s [%s]\n",
+ command->status == cs_interrupted ? "INTR "
+ : command->status == cs_failed ? "FAILED"
+ : command->status == cs_successeded ? "PASS "
+ : "UNKNWN",
+ command->context->command_line,
+ command->context->line);
+
+ command_display_status(command);
+ }
+
+
+ }
- /*command_display_status(unit->commands[i]);*/
- command_display_status(command);
+ printf(" =====================================================================%s\n",
+ number_of_failed_tests ? "== FAILED": number_of_interrupted_tests ? "==== INTR" : "====== OK");
+
+
+ printf(" Summary: Test(s): %.0f%% ok (%d test(s): %d ok",
+ (number_of_tests ? (1-((double)number_of_failed_tests + (double)number_of_interrupted_tests)/(double)number_of_tests)*100.0 : 100.0),
+ number_of_tests, number_of_successeded_tests);
+
+ if(number_of_failed_tests > 0)
+ printf(", %d failed", number_of_failed_tests);
+
+ if(number_of_interrupted_tests > 0)
+ printf(", %d interrupted)", number_of_interrupted_tests);
+
+ printf(")\n\n");
+
+
+ total_of_tests += number_of_tests;
+ total_of_failed_tests += number_of_failed_tests;
+ total_of_interrupted_tests += number_of_interrupted_tests;
+ total_of_successeded_tests += number_of_successeded_tests;
+ }
+
+ /* suites */
+
+ total_of_units = number_of_units;
+
+ total_of_failed_suites = total_of_successeded_suites = total_of_interrupted_suites = 0;
+
+ total_of_suites = vector_get_size(unit->suites);
+
+ for(k = 0; k < total_of_suites; k++)
+ {
+ suite = vector_get_at(unit->suites, k);
+
+ display_title(suite->description);
+
+ number_of_tests_of_suite = number_of_interrupted_tests_of_suite = number_of_failed_tests_of_suite = number_of_successeded_tests_of_suite = 0;
+
+ number_of_interrupted_units = number_of_failed_units = number_of_successeded_units = 0;
+
+ number_of_units = vector_get_size(suite->includes);
+
+ for(i = 0; i < number_of_units; i++)
+ {
+ number_of_interrupted_tests = number_of_failed_tests = number_of_successeded_tests = 0;
+
+ number_of_tests = vector_get_size(include->commands);
+
+ for(j = 0; j < vector_get_size(include->commands); j++)
+ {
+ command = vector_get_at(include->commands, j);
+
+ if(command->status == cs_interrupted)
+ number_of_interrupted_tests++;
+ else if(command->status == cs_failed)
+ number_of_failed_tests++;
+ else if(command->status == cs_successeded)
+ number_of_successeded_tests++;
+
+ }
+
+
+ include = vector_get_at(suite->includes, i);
+ asprintf(&p," Unit: %s ............................................................................", include->description ? include->description : include->fstream->name);
+
+ p[70] = '\0';
+ printf("%s", p);
+ free(p);
+
+ if(number_of_failed_tests > 0)
+ {
+ number_of_failed_units++;
+ printf(".. failed\n");
+ }
+ else if(number_of_interrupted_tests > 0)
+ {
+ number_of_interrupted_units++;
+ printf("interrupt\n");
+ }
+ else
+ {
+ number_of_successeded_units++;
+ printf(".... ..ok\n");
+ }
+
+ number_of_interrupted_tests_of_suite += number_of_interrupted_tests;
+ number_of_failed_tests_of_suite += number_of_failed_tests;
+ number_of_successeded_tests_of_suite += number_of_successeded_tests;
+
+ number_of_tests_of_suite += number_of_tests;
+
+ total_of_tests += number_of_tests;
+ total_of_failed_tests += number_of_failed_tests;
+ total_of_interrupted_tests += number_of_interrupted_tests;
+ total_of_successeded_tests += number_of_successeded_tests;
+
+ if(want_detail_summary)
+ {
+ for(j = 0; j < vector_get_size(include->commands); j++)
+ {
+ command = vector_get_at(include->commands, j);
+
+ printf(" %s: %s [%s]\n",
+ command->status == cs_interrupted ? "INTR "
+ : command->status == cs_failed ? "FAILED"
+ : command->status == cs_successeded ? "PASS "
+ : "UNKNWN",
+ command->context->command_line,
+ command->context->line);
+
+ command_display_status(command);
+
+ }
+
+
+ }
+
+ }
+
+
+
+ printf(" =====================================================================%s\n",
+ number_of_failed_tests_of_suite ? "== FAILED": number_of_interrupted_tests_of_suite ? "==== INTR" : "====== OK");
+
+ if(number_of_failed_tests_of_suite > 0)
+ total_of_failed_suites++;
+ else if(number_of_interrupted_tests_of_suite)
+ total_of_interrupted_suites++;
+ else
+ total_of_successeded_suites++;
+
+ total_of_failed_units += number_of_failed_units;
+ total_of_interrupted_units += number_of_interrupted_units;
+ total_of_successeded_units += number_of_successeded_units;
+
+ total_of_units += number_of_units;
+
+ printf(" Summary: Unit(s): %.0f%% ok (%d unit(s): %d ok",
+ (number_of_units ? (1-((double)number_of_failed_units + (double)number_of_interrupted_units)/(double)number_of_units)*100.0 : 100.0),
+ number_of_units, number_of_successeded_units);
+
+ if(number_of_failed_units > 0)
+ printf(", %d failed", number_of_failed_units);
+
+ if(number_of_interrupted_units > 0)
+ printf(", %d interrupted)", number_of_interrupted_units);
+
+ printf(")\n");
+
+ printf(" Test(s): %.0f%% ok (%d test(s): %d ok",
+ (number_of_tests_of_suite ? (1-((double)number_of_failed_tests_of_suite + (double)number_of_interrupted_tests_of_suite)/(double)number_of_tests_of_suite)*100.0 : 100.0),
+ number_of_tests_of_suite, number_of_successeded_tests_of_suite);
+
+ if(number_of_failed_tests_of_suite > 0)
+ printf(", %d failed", number_of_failed_tests_of_suite);
+
+ if(number_of_interrupted_tests_of_suite > 0)
+ printf(", %d interrupted)", number_of_interrupted_tests_of_suite);
+
+ printf(")\n\n");
}
+
+ printf(" TOTAL : Suite(s): %.0f%% ok (%d suite(s): %d ok",
+ (total_of_suites ? (1-((double)total_of_failed_suites + (double)total_of_interrupted_suites)/(double)total_of_suites)*100.0 : 100.0),
+ total_of_suites, total_of_successeded_suites);
+
+ if(total_of_failed_suites > 0)
+ printf(", %d failed", total_of_failed_suites);
+
+ if(total_of_interrupted_suites > 0)
+ printf(", %d interrupted)", total_of_interrupted_suites);
+
+ printf(")\n");
+
+ printf(" Unit(s): %.0f%% ok (%d unit(s): %d ok",
+ (total_of_units ? (1-((double)total_of_failed_units + (double)total_of_interrupted_units)/(double)total_of_units)*100.0 : 100.0),
+ total_of_units, total_of_successeded_units);
+
+ if(total_of_failed_units > 0)
+ printf(", %d failed", total_of_failed_units);
+
+ if(total_of_interrupted_units > 0)
+ printf(", %d interrupted)", total_of_interrupted_units);
+
+ printf(")\n");
+
+ printf(" Test(s): %.0f%% ok (%d test(s): %d ok",
+ (total_of_tests ? (1-((double)total_of_failed_tests + (double)total_of_interrupted_tests)/(double)total_of_tests)*100.0 : 100.0),
+ total_of_tests, total_of_successeded_tests);
+
+ if(total_of_failed_tests > 0)
+ printf(", %d failed", total_of_failed_tests);
+
+ if(total_of_interrupted_tests > 0)
+ printf(", %d interrupted)", total_of_interrupted_tests);
+
+ printf(")\n\n");
+
+
+ if(unit->interrupted)
+ unit->runner->total_of_interrupted_units++;
+ else if(total_of_failed_tests > 0)
+ unit->runner->total_of_failed_units++;
+ else
+ unit->runner->total_of_successeded_units++;
+
+
+ unit->runner->total_of_tests += total_of_tests;
+ unit->runner->total_of_failed_tests += total_of_failed_tests;
+ unit->runner->total_of_successeded_tests += total_of_successeded_tests;
+ unit->runner->total_of_interrupted_tests += total_of_interrupted_tests;
+
+
+ unit->runner->total_of_units += total_of_units + 1;
+ unit->runner->total_of_successeded_units += total_of_successeded_units;
+ unit->runner->total_of_failed_units += total_of_failed_units;
+ unit->runner->total_of_interrupted_units += total_of_interrupted_units;
+
+
+ unit->runner->total_of_suites += total_of_suites;
+ unit->runner->total_of_successeded_suites += total_of_successeded_suites;
+ unit->runner->total_of_failed_suites += total_of_failed_suites;
+ unit->runner->total_of_interrupted_suites += total_of_interrupted_suites;
+
+
+
+}
+
-}
void
-unit_handle_include(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name)
+unit_handle_include(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name, const char* description)
{
- directory_t include;
+ directory_t dir;
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);
+ /* the file is in the current directory */
fstream = fstream_new(getcwd(NULL, 0), file_name);
fstream_open(fstream);
{
prev_directory = getcwd(NULL, 0);
- vector_rewind(includes);
+ vector_rewind(include_dirs);
- while((include = vector_get(includes)))
+ while((dir = vector_get(include_dirs)))
{
- chdir(include->name);
+ chdir(dir->name);
if(!stat(file_name, &buffer) && S_ISREG(buffer.st_mode))
{
- fstream = fstream_new(include->name, file_name);
+ fstream = fstream_new(dir->name, file_name);
fstream_open(fstream);
break;
}
- vector_move_next(includes);
+ vector_move_next(include_dirs);
}
chdir(prev_directory);
}
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 */
+ if(!unit->running_suite)
+ {/* it's the unit of a suite */
+ unit_t include = unit_new(unit->runner,unit->root, unit, fstream);
+
+ include->mutex = unit->root->mutex;
- /*xbt_os_mutex_acquire(unit->mutex);
- vector_push_back(__unit->runner->units->items, __unit);
- xbt_os_mutex_release(unit->mutex);*/
+ if(description)
+ include->description = strdup(description);
+ vector_push_back(unit->includes, include);
- if(want_dry_run)
- INFO2("checking unit %s including in %s...",fstream->name, unit->fstream->name);
+ fstream_parse(fstream, include, mutex);
+ }
+ else
+ {/* it's a include */
+ unit_t owner = vector_get_back(unit->suites);
+ unit_t include = unit_new(unit->runner,unit->root, owner, fstream);
+
+ include->mutex = unit->root->mutex;
+
+ if(description)
+ include->description = strdup(description);
- unit_parse(unit, context_new(), mutex, fstream->name, fstream->stream);
+ vector_push_back(owner->includes, include);
- fstream_free((void**)&fstream);
- unit->parsing_include_file = 0;
+ fstream_parse(fstream, include, mutex);
+ }
}
}
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;
+ if(unit->running_suite)
+ {
+ exit_code = ESYNTAX;
+ unit_handle_failure(unit);
+ }
+ else
+ {
+ unit_t suite = unit_new(unit->runner, unit->root, unit, NULL);
+ suite->is_suite = 1;
+ suite->description = strdup(description);
+ vector_push_back(unit->suites, suite);
+ unit->running_suite = 1;
+ }
}
int
while((fstream = vector_get(fstreams->items)))
{
- if((errno = vector_push_back(units->items, unit_new(runner, NULL, fstream))))
+ if(vector_push_back(units->items, unit_new(runner, NULL, NULL, fstream)))
{
vector_free(&(units->items));
free(units);
int
units_free(void** unitsptr)
{
+ int rv;
+
if(!(*unitsptr))
return EINVAL;
- if((errno = vector_free(&((*((units_t*)unitsptr))->items))))
- return errno;
+ if((rv = vector_free(&((*((units_t*)unitsptr))->items))))
+ return rv;
free(*unitsptr);
*unitsptr = NULL;
-#include <variable.h>\r
-\r
-typedef struct s_variable\r
-{\r
- char* name;\r
- char* val;\r
- int used;\r
-}s_variable_t;
\ No newline at end of file
+#include <variable.h>
+
+XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
+
+variable_t
+variable_new(const char* name, const char* val)
+{
+ variable_t variable;
+
+ if(!name || !val)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if(!(variable = (variable_t)calloc(1, sizeof(s_variable_t))))
+ return NULL;
+
+ variable->name = strdup(name);
+ variable->val = strdup(val);
+ variable->used = 0;
+ variable->env = 0;
+ variable->err = 0;
+
+ return variable;
+
+}
+
+int
+variable_free(variable_t* variableptr)
+{
+ if(!(*variableptr))
+ return EINVAL;
+
+ free((*((variable_t*)(variableptr)))->name);
+ free((*((variable_t*)(variableptr)))->val);
+
+ free(*variableptr);
+
+ *variableptr = NULL;
+ return 0;
+}
+
+
+int
+variable_is_used(variable_t variable)
+{
+ if(!variable)
+ {
+ errno = EINVAL;
+ return 0;
+ }
+
+ return variable->used;
+}
+
+
+int
+variable_set_used(variable_t variable)
+{
+ if(!variable)
+ return EINVAL;
+
+ variable->used = 1;
+
+ return 0;
+}
-#include <variables.h>\r
-\r
-typedef struct s_variables\r
-{\r
- dictionary_t items;\r
-}s_variables_t;
\ No newline at end of file
+#include <com.h>
\ No newline at end of file
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
+
#ifdef _MSC_VER
#define inline _inline
#endif
int
vector_clear(vector_t vector)
{
+ int rv;
+
if(!vector)
return EINVAL;
if(!vector->size)
- return EAGAIN;
+ return 0;
if(vector->fn_finalize)
{
for(pos = 0; pos < size; pos++)
{
- if((errno = (*(fn_finalize))(&(items[pos]))))
- return errno;
+ if((rv = (*(fn_finalize))(&(items[pos]))))
+ return rv;
else
vector->size--;
}
int
vector_free(vector_t* vector_ptr)
{
+ int rv;
+
if(!(*vector_ptr))
return EINVAL;
- if((errno = vector_clear(*vector_ptr)))
- return errno;
+ if((rv = vector_clear(*vector_ptr)))
+ return rv;
free(*vector_ptr);
*vector_ptr = NULL;
int
vector_push_back(vector_t vector, void* item)
{
+ int rv;
+
if(!vector || !item)
return EINVAL;
-
/* if all capacity is used, resize the vector */
if(vector->capacity <= vector->size)
{
- if(!resize(vector))
- return errno;
+ if((rv = resize(vector)))
+ return rv;
}
/* increment the item count and push the new item at the end of the vector */
vector->pos = -1;
+
return 0;
}
int
vector_insert(vector_t vector, int pos, void* item)
{
+ int rv;
+
if(!vector)
return EINVAL;
if(vector->size >= vector->capacity)
{
- if(!resize(vector))
- return errno;
+ if((rv = resize(vector)))
+ return rv;
}
if(vector->size)
int
vector_erase_at(vector_t vector, int pos)
{
+ int rv;
+
if(!vector)
return EINVAL;
if(vector->fn_finalize)
{
- if((errno = (*(vector->fn_finalize))(&(vector->items[pos]))))
- return errno;
+ if((rv = (*(vector->fn_finalize))(&(vector->items[pos]))))
+ return rv;
}
if(pos != (vector->size - 1))
int
vector_erase(vector_t vector, void* item)
{
- int pos;
+ int pos, rv;
if(!vector || !item)
return EINVAL;
if(vector->fn_finalize)
{
- if((errno = (*(vector->fn_finalize))(&item)))
- return errno;
+ if((rv = (*(vector->fn_finalize))(&item)))
+ return rv;
}
if(pos != (vector->size - 1))
vector_erase_range(vector_t vector, int first, int last)
{
register int width;
-
+ int rv;
+
if(!vector || first >= last)
return EINVAL;
while(width--)
{
- if((errno = vector_erase_at(vector,first)))
- return errno;
+ if((rv = vector_erase_at(vector,first)))
+ return rv;
}
return 0;
register int pos;
int size;
void** items;
+ int rv;
if(!dst || !src ||(dst == src))
/* if the destination vector has not enough capacity resize it */
if(size > dst->capacity)
{
- if((errno = vector_reserve(dst, size - dst->capacity)))
- return errno;
+ if((rv = vector_reserve(dst, size - dst->capacity)))
+ return rv;
}
/* clear the destination vector */
- if((errno = vector_clear(dst)))
- return errno;
+ if((rv = vector_clear(dst)))
+ return rv;
dst->fn_finalize = NULL;
/* file the destination vector */
for(pos = 0; pos < size; pos++)
- if((errno = vector_push_back(dst,items[pos])))
- return errno;
+ if((rv = vector_push_back(dst,items[pos])))
+ return rv;
dst->pos = -1;
if(!(items = (void**)realloc(vector->items, size * sizeof(void*))))
return errno;
+
vector->capacity = size;
vector->items = items;
writer->thread = NULL;
writer->command = command;
- writer->started = xbt_os_sem_init(0);
+ writer->written = xbt_os_sem_init(0);
+ writer->can_write = xbt_os_sem_init(0);
+
+ writer->done = 0;
return writer;
}
void
writer_free(writer_t* writer)
{
+
+ /*xbt_os_sem_destroy((*writer)->started);
+ xbt_os_sem_destroy((*writer)->can_write);*/
+
free(*writer);
*writer = NULL;
}
{
writer_t writer = (writer_t)p;
command_t command = writer->command;
- long number_of_bytes_to_write = command->context->input->used;
+ int number_of_bytes_to_write = command->context->input->used;
char* input = (char*)(command->context->input->data);
int got;
+ int released = 0;
+
+
+ xbt_os_sem_acquire(writer->can_write);
- xbt_os_sem_release(writer->started);
while(!command->failed && !command->interrupted && !command->successeded && number_of_bytes_to_write > 0)
{
- got = number_of_bytes_to_write > SSIZE_MAX ? SSIZE_MAX : number_of_bytes_to_write;
- got = write( writer->command->stdin_fd, input, got );
-
+ got = number_of_bytes_to_write > PIPE_BUF ? PIPE_BUF : number_of_bytes_to_write;
+ got = write(writer->command->stdin_fd, input, got );
+
if(got < 0)
{
- if(EINTR == errno || EAGAIN == errno)
- {
+ if(EINTR == errno)
+ continue;
+
+ else if(EAGAIN == errno)
+ {/* the pipe is full */
+ if(!released)
+ {
+ xbt_os_sem_release(writer->written);
+ released = 1;
+ }
+
continue;
}
else if(EPIPE == errno)
input += got;
if(got == 0)
- usleep(100);
+ xbt_os_thread_yield();
+
+ }
+
+ if(!released)
+ {
+ xbt_os_sem_release(writer->written);
+ released = 1;
}
+
+ close(command->stdin_fd);
+ command->stdin_fd = INDEFINITE_FD;
+
command->context->input->data[0]='\0';
command->context->input->used=0;
command_handle_failure(command, csr_write_pipe_broken);
}
-
- close(command->stdin_fd);
- command->stdin_fd = INDEFINITE_FD;
+ writer->done = 1;
return NULL;