1581 lines
49 KiB
C
1581 lines
49 KiB
C
#include "ftest/ftest.h"
|
|
//#include <dirent.h>
|
|
|
|
/*
|
|
* by default display in millisecond
|
|
*/
|
|
/*
|
|
|
|
*/
|
|
|
|
#define NANOSECOND (timeunit[0]=='n')
|
|
#define SECOND (timeunit[0]=='s')
|
|
|
|
#define LOCK(mutex_var) pthread_mutex_lock(&mutex_var);
|
|
#define UNLOCK(mutex_var) pthread_mutex_unlock(&mutex_var);
|
|
|
|
#define INCREMENT(variable)\
|
|
do{\
|
|
if(is_parallel_nb){\
|
|
LOCK(mut_##variable); \
|
|
++variable;\
|
|
UNLOCK(mut_##variable);\
|
|
}\
|
|
else ++variable;\
|
|
}while(0);
|
|
|
|
|
|
|
|
/*
|
|
* struct to store tests failed
|
|
*/
|
|
|
|
struct failed_lists{
|
|
char *name;
|
|
struct failed_lists *next;
|
|
};
|
|
void clear_all_falied_list(struct failed_lists **flist);
|
|
|
|
#define default_ordered 0
|
|
#define default_unicolour 0
|
|
#define default_removelog 0
|
|
//#define default_parallel_nb 1
|
|
#define default_parallel_nb_opt 1
|
|
#define default_verb NORMAL
|
|
|
|
|
|
/*
|
|
* global variables
|
|
* if not exported, it only exists in ftest.c
|
|
*/
|
|
/*
|
|
* begin variable option
|
|
*/
|
|
long int verb=NORMAL;
|
|
|
|
bool some_thing_wrong = 0;
|
|
|
|
bool help=0;
|
|
bool only_usage=0;
|
|
bool ordered= 0;
|
|
bool gtestlike=0;
|
|
bool debug=0;
|
|
bool unicolour = 0;
|
|
bool removelog = 0;
|
|
char *timeunit="ms";
|
|
char *savelog=NULL;
|
|
char *default_timeunit="ms";
|
|
char *default_savelog="log_all_tests";
|
|
char *default_bar_progress=" c";
|
|
|
|
bool bar_progress_has_to_be_freed = 0;
|
|
bool default_bar_progress_has_to_be_freed = 0;
|
|
|
|
//size_t width = 80;
|
|
|
|
char *colors_f[]={DEFAULT_K, GREEN_K, RED_K, YELLOW_K, BLUE_K, ""};
|
|
int k_DEFAULT=0, k_GREEN=1, k_RED=2, k_YELLOW=3, k_BLUE=4, k_NOTHING=Dknothing;
|
|
|
|
char *tab_hk_f[]={ HK_EQ, HK_TR, HK_RN, HK_DN, HK_OK, HK_FL, HK_PS, HK_SK };
|
|
char *g_tab_hk_f[]={ gHK_EQ, gHK_TR, gHK_RN, gHK_DN, gHK_OK, gHK_FL, gHK_PS, gHK_SK };
|
|
int hk_EQ=0, hk_TR=1, hk_RN=2, hk_DN=3, hk_OK=4, hk_FL=5, hk_PS=6, hk_SK=7 ;
|
|
|
|
//char *varHK_EQ=HK_EQ, *varHK_TR=HK_TR, *varHK_RN=HK_RN, *varHK_DN=HK_DN, *varHK_OK=HK_OK, *varHK_FL=HK_FL, *varHK_PS=HK_PS, *varHK_SK=HK_SK;
|
|
|
|
bool some_tests_selected=0;
|
|
|
|
size_t *array_TYPE_SIZE_T=NULL; /* if active, size = count_tests */
|
|
char **array_TYPE_STRING=NULL;
|
|
|
|
size_t cur_array_TYPE_SIZE_T=0; /* < count_tests */
|
|
size_t cur_array_TYPE_STRING=0;
|
|
|
|
/*
|
|
* number of threads
|
|
*/
|
|
size_t parallel_nb = 0;
|
|
size_t parallel_nb_opt = 0; /* to solve debug option */
|
|
|
|
/*
|
|
* end variable option
|
|
*/
|
|
|
|
bool is_parallel_nb = 0;
|
|
bool is_parallel_nb_opt = 0;
|
|
bool is_verb = 0;
|
|
bool log_parallel = true;
|
|
bool progress = true; // false;
|
|
|
|
char *bar_progress = " c"; /*{fill_bar,fill_dot,colored} */
|
|
bool is_bar_progress = true;
|
|
size_t count_para_progress = 0;
|
|
|
|
FILE **f_ou_th;
|
|
|
|
size_t count_tests = 0;
|
|
|
|
size_t count_pass_global = 0;
|
|
size_t count_pass_local = 0;
|
|
|
|
|
|
size_t count_fail_global = 0;
|
|
size_t count_fail_local = 0;
|
|
|
|
/*
|
|
* count in local test
|
|
* using array [count_test] global variable
|
|
*/
|
|
size_t *count_pass_test = NULL;
|
|
size_t *count_fail_test = NULL;
|
|
|
|
/*
|
|
* count on each thread [PARALLEL]
|
|
*/
|
|
size_t *count_pass_thread = NULL;
|
|
size_t *count_fail_thread = NULL;
|
|
|
|
size_t *id_thread_self = NULL;
|
|
char **log_name_file_thrd = NULL;
|
|
/*
|
|
* the first instance of the func struct,
|
|
* it containis the first test
|
|
*/
|
|
|
|
struct func *f_beging = NULL;
|
|
/*
|
|
* current test : used by parallel tests
|
|
*/
|
|
struct func *current_fn = NULL;
|
|
|
|
/*
|
|
* list of all failed tests
|
|
*/
|
|
struct failed_lists *failed_l = NULL;
|
|
|
|
|
|
/*
|
|
* list of failed test on each thread
|
|
*/
|
|
struct failed_lists **thread_test_failed_l = NULL;
|
|
|
|
/*
|
|
* mutex to add global failed test
|
|
*/
|
|
pthread_mutex_t mut_global_list_fail;
|
|
/*
|
|
* mutex to have current test to do
|
|
*/
|
|
pthread_mutex_t mut_current_test;
|
|
|
|
|
|
pthread_mutex_t mut_count_pass_global;
|
|
pthread_mutex_t mut_count_fail_global;
|
|
pthread_mutex_t mut_count_pass_local;
|
|
pthread_mutex_t mut_count_fail_local;
|
|
|
|
pthread_mutex_t mut_count_para_progress;
|
|
pthread_cond_t cond_count_para_progress;
|
|
|
|
|
|
/*
|
|
* end of the global variables of test_t.c
|
|
*/
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
void append_failed_list(struct failed_lists **fn_failed_list ,const char *name_failed){
|
|
|
|
if(*fn_failed_list){
|
|
|
|
struct failed_lists *tmp_failed_l = *fn_failed_list, *rec_tmp;
|
|
|
|
while(tmp_failed_l){
|
|
rec_tmp = tmp_failed_l;
|
|
tmp_failed_l = tmp_failed_l->next;
|
|
}
|
|
tmp_failed_l = malloc(sizeof(struct failed_lists));
|
|
tmp_failed_l->name = malloc(strlen(name_failed)+1);
|
|
strcpy(tmp_failed_l->name, name_failed);
|
|
tmp_failed_l->next = NULL;
|
|
rec_tmp->next = tmp_failed_l;
|
|
|
|
}
|
|
else{
|
|
*fn_failed_list = malloc(sizeof(struct failed_lists));
|
|
(*fn_failed_list)->name = malloc(strlen(name_failed)+1);
|
|
strcpy((*fn_failed_list)->name, name_failed);
|
|
(*fn_failed_list)->next = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* match the id global (gives by OS) of the thread with the local (the program) id of thread
|
|
*/
|
|
long int id_of_thread_executed(const char * func_call_name){
|
|
size_t id_from_self = pthread_self();
|
|
for(long int i=0; i<= parallel_nb; ++i){
|
|
if(id_thread_self[i] == id_from_self)
|
|
return i;
|
|
}
|
|
/*if(id_thread_self){
|
|
for(long int i=0; i<= parallel_nb; ++i)
|
|
PRINT_DEBUG(" id_thread_self[%ld] = %ld \n", i, id_thread_self[i]);
|
|
}*/
|
|
/* no error, when pthread is call in a test, we will not have a match! */
|
|
PRINT_DEBUG("\n debug [%s]: called by <%s>, id_from_self: %ld\n",__func__, func_call_name,id_from_self);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* format name of TEST(name_f) is: 'TEST_name_f____NUM|',
|
|
* and name attribute is 'TEST(name_f): test N° NUM!' (! at the end is random):
|
|
* we extract NUM here
|
|
* to have hash_table of the count when parallel test!
|
|
*/
|
|
size_t extract_num__f(const char *name_f){
|
|
size_t len = strlen(name_f);
|
|
size_t val = 0, p = 1;
|
|
for(long i= len-1; i>=0; --i){
|
|
PRINT_DEBUG(" name_f(%s)[%ld] = %c\n",name_f,i,name_f[i]);
|
|
if(name_f[i]=='|') val = 0;
|
|
if(name_f[i] >= '0' && name_f[i] <= '9'){
|
|
val += p * (name_f[i]-'0');
|
|
p *= 10;
|
|
}
|
|
else if( name_f[i] == ' ' || name_f[i] == '_' || name_f[i] == '=' ) break;
|
|
}
|
|
return val;
|
|
}
|
|
/* TEST_funcname___NUM -> TEST(funcname) */
|
|
char* extract_func_edited_TEST_from_exec_func_name(char* func_name){
|
|
size_t len=strlen(func_name);
|
|
char *ret_name=malloc(len+1);
|
|
strcpy(ret_name, func_name);
|
|
char *pad="____";
|
|
size_t len_pad=strlen(pad);
|
|
ret_name[4]='(';
|
|
for(size_t i=5;i<len-len_pad; ++i){
|
|
if(0==strncmp(func_name+i, pad,len_pad)){
|
|
ret_name[i]=')';
|
|
ret_name[i+1]='\0';
|
|
}
|
|
}
|
|
return ret_name;
|
|
}
|
|
|
|
// ========================== =================================
|
|
void setup_variables_before_exec(){
|
|
if(unicolour){
|
|
k_DEFAULT=k_NOTHING;
|
|
k_GREEN=k_NOTHING;
|
|
k_RED=k_NOTHING;
|
|
k_YELLOW=k_NOTHING;
|
|
k_BLUE=k_NOTHING;
|
|
|
|
size_t len_bp = strlen(bar_progress);
|
|
size_t len_db = strlen(default_bar_progress);
|
|
if( len_bp >= len_db ){
|
|
char *tmp_bp=malloc(len_bp+1);
|
|
strcpy(tmp_bp,bar_progress);
|
|
tmp_bp[2]='u';
|
|
bar_progress=tmp_bp;
|
|
bar_progress_has_to_be_freed = 1;
|
|
}
|
|
else{
|
|
char *tmp_bp=malloc(len_db+1);
|
|
strcpy(tmp_bp,default_bar_progress);
|
|
tmp_bp[2]='u';
|
|
default_bar_progress=tmp_bp;
|
|
default_bar_progress_has_to_be_freed = 1;
|
|
}
|
|
}
|
|
|
|
if(gtestlike){
|
|
for(int i=0; i<=hk_SK; ++i)
|
|
tab_hk_f[i]=g_tab_hk_f[i];
|
|
}
|
|
|
|
parallel_nb = parallel_nb_opt;
|
|
is_parallel_nb = is_parallel_nb_opt;
|
|
|
|
/*if(savelog){
|
|
f_savelog=fopen(savelog, "w+");
|
|
}*/
|
|
}
|
|
|
|
|
|
// ===================================== begin options handle =======================================================
|
|
|
|
void usage(int argc, char **argv){
|
|
printf("usage: %s [OPTIONS] [<ARGS>] \n\n"
|
|
" or : %s [OPTIONS]=[<ARGS>]\n\n",argv[0],argv[0]);
|
|
printf("OPTIONS\n");
|
|
printf( "\t -h, --help \n"
|
|
"\t\tprint help, options variables\n\n");
|
|
printf( "\t -g, --gtestlike \n"
|
|
"\t\tto have gtest hook like!\n\n");
|
|
printf( "\t -p <NB>, --parallel <NB>, -p=<NB>, --parallel=<NB>\n"
|
|
"\t\tby default the program ran in sequantial all test, \n"
|
|
"\t\tif this option is set, the program run tests on NB threads.\n"
|
|
"\t\tEach thread pull up one test out the list of all test not yet executed,\n"
|
|
"\t\tand execute it, until the list is empty \n\n");
|
|
printf( "\t -t <unit>, --time <unit>, -t=<unit>, --time=<unit> \n"
|
|
"\t\tby default unit is millisecons ms, the other of unit are choices are second (or s), and nanosecond (or ns)\n"
|
|
"\t\tex: -t ns or -t=nanosecond or --time=n to set nanosecond unit\n\n");
|
|
printf( "\t -u , --unicolour\n"
|
|
"\t\tby default, the result is colored, if you choice this option, it prints with default color\n\n");
|
|
printf( "\t -r , --remove\n"
|
|
"\t\tif the option parallel is choosen the result on each thread is record in separate files,\n"
|
|
"\t\tthis option remove the file logs of each thread after all tests.\n\n");
|
|
printf( "\t -s <file>, --savelog <file>, -s=file, --savelog=file\n"
|
|
"\t\tthis option save the global ordered result in 'file',\n"
|
|
"\t\t \n\n");
|
|
|
|
printf( "\t -n=<NUM1>,<NUM2> <NUM3>... ,--numtests=<NUM1>,<NUM2>...\n"
|
|
"\t\tthis option allow to execute only the selected numbers of tests (in the order in file test)\n"
|
|
"\t\tex: -n=0,6,3 8 to execute the tests 0,3,6,8 (if the number is less than the count of all tests)\n\n");
|
|
printf( "\t -l=<NAME1>,<NAME2> <NAME3>... ,--listests=<NAME1>,<NAME2>...<NAMEn>\n"
|
|
"\t\tthis option allow to execute only the selected name of tests. It allows empty name by using '-l=,'\n"
|
|
"\t\tex: -l=name0,,name2 : execute only (if they exist): TEST(name0),TEST(),TEST(name2)\n\n");
|
|
printf( "\t -b <BPRGSS>, --bar_progress <BPRGSS>, -b=BPRGSS, --bar_progress=BPRGSS. Example: -b=\"#_c\"\n"
|
|
"\t\tthis option change progression bar if it is active. The first character (\'#\') fills the bar\n"
|
|
"\t\tthe second char (\'_\') fills the other part of bar. the bar is colored if the 3rd char is \'c\' and not if different.\n"
|
|
"\t\tby default the progress bar is active and the option is -b=\" c\", if need not colored, we can put -b=\" n\" option.\n\n");
|
|
printf( "\t -z=<option> \n"
|
|
"\t\tthis option is to set option=0,\n"
|
|
"\t\tfor example, -z=progress is to not load progress bar, it is need if we want to redirect (pipe) the result to file.\n"
|
|
"\t\tother option: -z=log_parallel (to avoid logs not ordered when parallel tests which is loged by default)\n\n");
|
|
printf( "\t -d, --debug \n"
|
|
"\t\tto print debug by using PRINT_DEBUG, by default PRINT_DEBUG is not print unless -d is set\n"
|
|
/*"\t\t-d need to be set at the end of all options if -p is use, to avoid sigfault because the parallel env is not yet set for debug print parallel\n\n"*/
|
|
);
|
|
printf( "\t -v, --verb = integer or macro: NORMAL or VERB or NOVERB \n"
|
|
"\t\tto chage test result printing: NORMAL=0, VERB=1, NOVERB=2 or integers not equal to 0 or 1 \n"
|
|
);
|
|
|
|
|
|
if(array_TYPE_SIZE_T){
|
|
for(int i=0; i< cur_array_TYPE_SIZE_T; ++i){
|
|
PRINT_DEBUG("array_TYPE_SIZE_T[%d]=%ld\n",i,array_TYPE_SIZE_T[i]);
|
|
}
|
|
}
|
|
if(array_TYPE_STRING) {
|
|
for(int i=0; i< cur_array_TYPE_STRING; ++i){
|
|
PRINT_DEBUG("array_TYPE_STRING[%d]=%s\n",i,array_TYPE_STRING[i]);
|
|
}
|
|
}
|
|
if(some_thing_wrong){
|
|
printf("invalid argument\n");
|
|
exit(0);
|
|
}
|
|
if(only_usage) exit(0);
|
|
|
|
}
|
|
|
|
|
|
const char* extract_string_after_equal_symbole_in_string(const char * in_str){
|
|
size_t len=strlen(in_str);
|
|
|
|
for(long i=0; i<len-1; ++i){
|
|
if(in_str[i]=='=')
|
|
return in_str+i+1;
|
|
}
|
|
return NULL; // check for '\0' or ' ' return !
|
|
}
|
|
|
|
|
|
|
|
#define COMPARE_STR_TO_DEFS(defined,in_str)\
|
|
/*LOG(" ===================== ======== %s vs %s =========== \n",in_str,#defined);*/\
|
|
if(strcmp(in_str, #defined)==0){\
|
|
return defined;\
|
|
}
|
|
|
|
long int extract_num_after_equal_symbole_in_string(char * in_str){
|
|
|
|
size_t len=strlen(in_str);
|
|
long int val=0, p=1;
|
|
int added=0;
|
|
for(long i=len-1; i>=0; --i){
|
|
PRINT_DEBUG("(%s)[%ld]=%c\n",in_str,i,in_str[i]);
|
|
if(in_str[i]=='=') {
|
|
if(added) return val;
|
|
|
|
COMPARE_STR_TO_DEFS(NORMAL,in_str+i+1);
|
|
COMPARE_STR_TO_DEFS(VERB,in_str+i+1);
|
|
COMPARE_STR_TO_DEFS(NOVERB,in_str+i+1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
if(in_str[i] >= '0' && in_str[i] <= '9' ){
|
|
val += p * (in_str[i]-'0');
|
|
p *= 10;
|
|
added=1;
|
|
}
|
|
}
|
|
if(added) return val;
|
|
return -1;
|
|
}
|
|
|
|
#define LOG_WRONG(option,arg,msg)\
|
|
some_thing_wrong=1;\
|
|
help=1;\
|
|
printf("incorrect %s option is interpreted as -%c, %s \n\n",arg,#option[0],msg);\
|
|
break;
|
|
|
|
#define IF_OPTION_WITH_ARG_NUM(option)\
|
|
if(argv[i][0]=='-'){\
|
|
j=1;\
|
|
while(argv[i][j]=='-') ++j;\
|
|
if(argv[i][j] == #option[0]){\
|
|
arg=argv[i];\
|
|
long ret_num=extract_num_after_equal_symbole_in_string(argv[i]);\
|
|
PRINT_DEBUG("option=%s, ret_num = %ld, argv[%d]=%s\n",#option,ret_num,i,argv[i]);\
|
|
is_##option = 1;\
|
|
if(ret_num > -1)\
|
|
option = ret_num;\
|
|
else{\
|
|
if(i<argc-1){\
|
|
if(argv[i+1][0]=='-'){\
|
|
option = default_##option;\
|
|
LOG_WRONG(option,arg, "wait for args")\
|
|
}\
|
|
else{\
|
|
ret_num=extract_num_after_equal_symbole_in_string(argv[++i]);\
|
|
if(ret_num>0)\
|
|
option = ret_num;\
|
|
else{ \
|
|
option = default_##option;\
|
|
LOG_WRONG(option,arg, "wait for args")\
|
|
}\
|
|
}\
|
|
}\
|
|
else{\
|
|
option = default_##option;\
|
|
LOG_WRONG(option,arg, "wait for args")\
|
|
}\
|
|
}\
|
|
PRINT_DEBUG("option %s activated, its value is %ld\n",#option,option);\
|
|
continue;\
|
|
}\
|
|
}\
|
|
|
|
#define IF_OPTION_WITH_ARG_STR(option)\
|
|
if(argv[i][0]=='-'){\
|
|
j=1;\
|
|
while(argv[i][j]=='-') ++j;/* to accept multiple -- */\
|
|
if(argv[i][j] == #option[0]){\
|
|
arg=argv[i];\
|
|
char* ret_str=(char*)extract_string_after_equal_symbole_in_string(argv[i]);\
|
|
PRINT_DEBUG("option=%s, ret_str = %s, argv[%d]=%s\n",#option,ret_str,i,argv[i]);\
|
|
if(ret_str ==NULL || strlen(ret_str)==0){\
|
|
if(i<argc-1){\
|
|
if(argv[i+1][0]=='-')\
|
|
help=1;\
|
|
else\
|
|
option = argv[++i];\
|
|
}\
|
|
else{\
|
|
help=1;\
|
|
}\
|
|
}\
|
|
else option = ret_str;\
|
|
continue;\
|
|
}\
|
|
}\
|
|
|
|
|
|
|
|
/*
|
|
* if the variable option is boolean
|
|
*/
|
|
|
|
#define IF_OPTION_NO_ARG(option)\
|
|
if(argv[i][0]=='-'){\
|
|
j=1;\
|
|
while(argv[i][j]=='-') ++j;\
|
|
if(argv[i][j] == #option[0]){\
|
|
option=1;\
|
|
if(0==strcmp(#option,"help")){ only_usage=1;break;}\
|
|
continue;\
|
|
}\
|
|
}\
|
|
|
|
|
|
#define IF_OPTION_TO_ZERO(option)\
|
|
if(argv[i][0]=='-'){\
|
|
j=1;\
|
|
while(argv[i][j]=='-') ++j;/* to accept multiple -- */\
|
|
if(argv[i][j] == 'z' ){\
|
|
arg=argv[i];\
|
|
char* ret_str=(char*)extract_string_after_equal_symbole_in_string(argv[i]);\
|
|
PRINT_DEBUG("to zero option={%s}, ret_str = {%s}, argv[%d]={%s}\n",#option,ret_str,i,argv[i]);\
|
|
if(ret_str == NULL || strlen(ret_str)==0){\
|
|
if(i<argc-1){\
|
|
if(argv[i+1][0]=='-')\
|
|
help=1;\
|
|
else if(0==strcmp(#option, argv[i+1])){ \
|
|
option=0;\
|
|
++i;\
|
|
continue;\
|
|
}\
|
|
}\
|
|
else{\
|
|
help=1;\
|
|
}\
|
|
}\
|
|
else if(0==strcmp(#option,ret_str)){ \
|
|
option = 0;\
|
|
continue;\
|
|
}\
|
|
}\
|
|
}\
|
|
|
|
|
|
void extract_to_array_TYPE_SIZE_T(char * in_str){
|
|
size_t len=strlen(in_str);
|
|
long int val=0, p=10;
|
|
int added=0;/* if the number is 0, without this var , the precedent algo did not work */
|
|
for(long i=0; i<len; ++i){
|
|
PRINT_DEBUG("(%s)[%ld]=%c\n",in_str,i,in_str[i]);
|
|
if(in_str[i]=='-'){/*we don't need the option name */
|
|
while((in_str[i] >= '0' && in_str[i] <= '9' ) || (in_str[i] >= 'A' && in_str[i] <= 'z' ) || (in_str[i]=='_')|| (in_str[i]=='-')){
|
|
++i;
|
|
}
|
|
}
|
|
else if(in_str[i] >= '0' && in_str[i] <= '9' ){
|
|
val = (p * val) + (in_str[i]-'0');
|
|
added=1;
|
|
}
|
|
else{
|
|
/* rec val in array */
|
|
PRINT_DEBUG("val=(%ld) \n",val);
|
|
array_TYPE_SIZE_T[cur_array_TYPE_SIZE_T++]=val;
|
|
val=0;
|
|
added=0;
|
|
}
|
|
}
|
|
PRINT_DEBUG("end val=(%ld) \n",val);
|
|
if(added)
|
|
array_TYPE_SIZE_T[cur_array_TYPE_SIZE_T++]=val;
|
|
|
|
}
|
|
|
|
void extract_to_array_TYPE_STRING(char * in_str){
|
|
size_t len=strlen(in_str);
|
|
char *val=malloc(len);
|
|
size_t cur_val=0;
|
|
for(long i=0; i<len; ++i){
|
|
if(in_str[i]=='-'){/*we don't need the option name */
|
|
while((in_str[i] >= '0' && in_str[i] <= '9' ) || (in_str[i] >= 'A' && in_str[i] <= 'z' ) || (in_str[i]=='_')|| (in_str[i]=='-')){
|
|
++i;
|
|
}
|
|
}
|
|
else if((in_str[i] >= '0' && in_str[i] <= '9' ) || (in_str[i] >= 'A' && in_str[i] <= 'z' ) || (in_str[i]=='_')){
|
|
val[cur_val++] = in_str[i];
|
|
}
|
|
else{
|
|
/* rec val in array */
|
|
val[cur_val++]='\0';
|
|
|
|
PRINT_DEBUG("val_str=(((%s) cur_val=[%ld]\n",val,cur_val);
|
|
array_TYPE_STRING[cur_array_TYPE_STRING]=malloc(strlen(val)+1);
|
|
strcpy(array_TYPE_STRING[cur_array_TYPE_STRING++],val);
|
|
cur_val=0;
|
|
}
|
|
}
|
|
if(cur_val){
|
|
val[cur_val++]='\0';
|
|
PRINT_DEBUG("val_str=(%s) cur_val=[%ld]\n",val,cur_val);
|
|
|
|
array_TYPE_STRING[cur_array_TYPE_STRING]=malloc(strlen(val)+1);
|
|
strcpy(array_TYPE_STRING[cur_array_TYPE_STRING++],val);
|
|
|
|
}
|
|
free(val);
|
|
}
|
|
|
|
|
|
|
|
#define IF_OPTION_WITH_MULTIPLE_ARG(option,type)\
|
|
if(argv[i][0]=='-'){\
|
|
j=1;\
|
|
while(argv[i][j]=='-') ++j;\
|
|
if(argv[i][j] == #option[0]){\
|
|
arg=argv[i];\
|
|
array_##type=malloc(sizeof(type)*count_tests);\
|
|
some_tests_selected= 1;\
|
|
do{\
|
|
extract_to_array_##type(argv[i]);\
|
|
PRINT_DEBUG("option=%s, cur = %ld, argv[%d]=%s\n",#option,cur_array_##type,i,argv[i]);\
|
|
}while(i<argc-1 && argv[++i][0] != '-');\
|
|
PRINT_DEBUG("after while option=%s, cur = %ld, argv[%d]=%s\n",#option,cur_array_##type,i,argv[i]);\
|
|
if(argv[i][0]=='-'){/* handle to allow next option */\
|
|
j=1;while(argv[i][j]=='-') ++j;\
|
|
if (argv[i][j] != #option[0]) --i;\
|
|
}\
|
|
PRINT_DEBUG("after if arc-1 option=%s, cur = %ld, i=%d, et argv[i+1]=%s\n",#option,cur_array_##type,i,argv[i+1]);\
|
|
continue;\
|
|
}\
|
|
}\
|
|
|
|
|
|
/*
|
|
* if no continue call, it means no match option!
|
|
*/
|
|
|
|
#define IF_NO_MATCH_DO_WRONG \
|
|
printf("option %s inconnu\n",argv[i]);\
|
|
help=1; some_thing_wrong=1; break;
|
|
|
|
|
|
void parse_options(int argc, char **argv){
|
|
int j;
|
|
char *arg;
|
|
for(int i=1; i<argc; ++i){
|
|
PRINT_DEBUG("argc=%d, argv[%d]=%s\n",argc,i,argv[i]);
|
|
IF_OPTION_NO_ARG(help)
|
|
IF_OPTION_NO_ARG(debug)
|
|
IF_OPTION_NO_ARG(gtestlike)
|
|
//IF_OPTION_WITH_ARG_NUM(parallel_nb)
|
|
IF_OPTION_WITH_ARG_NUM(parallel_nb_opt)
|
|
IF_OPTION_WITH_ARG_NUM(verb)
|
|
IF_OPTION_WITH_ARG_STR(savelog)
|
|
IF_OPTION_WITH_ARG_STR(timeunit)
|
|
IF_OPTION_WITH_ARG_STR(bar_progress)
|
|
IF_OPTION_NO_ARG(ordered)
|
|
IF_OPTION_NO_ARG(removelog)
|
|
IF_OPTION_NO_ARG(unicolour)
|
|
IF_OPTION_TO_ZERO(progress)
|
|
IF_OPTION_TO_ZERO(log_parallel)
|
|
IF_OPTION_WITH_MULTIPLE_ARG(numsuit,TYPE_SIZE_T)
|
|
IF_OPTION_WITH_MULTIPLE_ARG(listsuite,TYPE_STRING)
|
|
IF_NO_MATCH_DO_WRONG
|
|
}
|
|
|
|
setup_variables_before_exec();
|
|
}
|
|
|
|
// ==================================================== end handle option ================================
|
|
|
|
/*
|
|
* print all TESTs failed
|
|
*/
|
|
|
|
#define LISTE_ALL_FAILED_TEST_IN_F_OUT\
|
|
while(failed_lst){\
|
|
PRINT_HK_C(colors_f[k_RED], tab_hk_f[hk_FL]," %s\n",failed_lst->name);\
|
|
failed_lst = failed_lst->next;\
|
|
}
|
|
|
|
|
|
void list_failed_test(struct failed_lists *test_failed, const char * func_call_name){
|
|
struct failed_lists *failed_lst = test_failed;
|
|
if(is_parallel_nb){
|
|
long int id_thrd = id_of_thread_executed(func_call_name);
|
|
if(id_thrd < 0 || id_thrd == parallel_nb ){
|
|
LISTE_ALL_FAILED_TEST_IN_F_OUT
|
|
}else{
|
|
while(failed_lst){
|
|
PRINT_HK_C(colors_f[k_RED], tab_hk_f[hk_FL]," %s, on thread[%ld]\n",failed_lst->name,id_thrd);
|
|
failed_lst = failed_lst->next;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
LISTE_ALL_FAILED_TEST_IN_F_OUT
|
|
}
|
|
PRINT_HK_C(colors_f[k_DEFAULT], tab_hk_f[hk_EQ],"%s\n","");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define INCREMENT_EXPECT(expect,name)\
|
|
do{\
|
|
size_t num_test=extract_num__f(name);\
|
|
++count_## expect ##_test[num_test];\
|
|
PRINT_DEBUG("INCREMENT cout_%s_test[%ld] = %ld\n",#expect, num_test,count_## expect ##_test[num_test]); \
|
|
}while(0);
|
|
|
|
|
|
#define EXPECTED_EXPECT_F(expect/*, not_expect*/) \
|
|
\
|
|
bool expected_##expect##_f(bool val){ \
|
|
if(val == expect) { \
|
|
INCREMENT(count_pass_local); /*++count_pass_local*/ \
|
|
return true; \
|
|
}else { \
|
|
INCREMENT(count_fail_local); /*++count_fail_local*/ \
|
|
return false; \
|
|
} \
|
|
} \
|
|
bool expected_##expect##_f_name(bool val, const char * name){ \
|
|
if(val == expect) { \
|
|
INCREMENT_EXPECT(pass,name);\
|
|
return true; \
|
|
}else { \
|
|
INCREMENT_EXPECT(fail,name);\
|
|
return false; \
|
|
} \
|
|
} \
|
|
|
|
|
|
EXPECTED_EXPECT_F(true)
|
|
EXPECTED_EXPECT_F(false)
|
|
|
|
#define EXPECTED_OP_TYPE(OP,type) \
|
|
\
|
|
bool expected_##OP##_##type(type var1, type var2){ \
|
|
if(COMPARE_N_##type(&var1, &var2) OP 0){ \
|
|
INCREMENT(count_pass_local); /*++count_pass_local*/ \
|
|
return true; \
|
|
}else { \
|
|
INCREMENT(count_fail_local); /*++count_fail_local*/ \
|
|
return false; \
|
|
} \
|
|
} \
|
|
bool expected_##OP##_name_##type(type var1, type var2,const char * name){ \
|
|
if(COMPARE_N_##type(&var1, &var2) OP 0){ \
|
|
INCREMENT_EXPECT(pass,name);\
|
|
return true; \
|
|
}else { \
|
|
INCREMENT_EXPECT(fail,name);\
|
|
return false; \
|
|
} \
|
|
}\
|
|
bool expected_array_##OP##_##type(type *var1, long int sz1, type *var2, long int sz2){ \
|
|
if(sz1 OP sz2){\
|
|
if(sz1 == sz2){\
|
|
size_t count_ = 0;\
|
|
for(size_t i=0;i<sz1;++i){\
|
|
/*if(COMPARE_N_##type((void*)&var1[i],(void*)&var2[i]) OP 0)\
|
|
*/if(COMPARE_N_##type((void*)(var1 + i),(void*)(var2 + i)) OP 0)\
|
|
++count_;\
|
|
else{ \
|
|
INCREMENT(count_fail_local); /*++count_fail_local*/ \
|
|
return false;\
|
|
}\
|
|
}\
|
|
if(count_ == sz1){ \
|
|
INCREMENT(count_pass_local); /*++count_pass_local*/ \
|
|
return true; \
|
|
}\
|
|
}\
|
|
else{\
|
|
INCREMENT(count_pass_local); /*++count_pass_local*/ \
|
|
return true; \
|
|
}\
|
|
}\
|
|
INCREMENT(count_fail_local); /*++count_fail_local*/ \
|
|
return false; \
|
|
} \
|
|
bool expected_array_##OP##_name_##type(type *var1, long int sz1, type *var2, long int sz2, const char * name){ \
|
|
if(sz1 OP sz2){\
|
|
if(sz1 == sz2){\
|
|
size_t count_=0;\
|
|
for(size_t i=0;i<sz1;++i){\
|
|
if(COMPARE_N_##type((void*)&var1[i], (void*)&var2[i]) OP 0)\
|
|
++count_;\
|
|
else {\
|
|
INCREMENT_EXPECT(fail,name);\
|
|
return false;\
|
|
}\
|
|
}\
|
|
if(count_ == sz1){ \
|
|
INCREMENT_EXPECT(pass,name);\
|
|
return true; \
|
|
}\
|
|
}\
|
|
else{\
|
|
INCREMENT_EXPECT(pass,name);\
|
|
return true; \
|
|
}\
|
|
}\
|
|
INCREMENT_EXPECT(fail,name);\
|
|
return false; \
|
|
} \
|
|
|
|
|
|
EXPECTED_OP_TYPE(EQ,TYPE_CHAR)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_U_CHAR)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_INT)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_U_INT)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_L_INT)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_U_L_INT)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_SIZE_T)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_FLOAT)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_DOUBLE)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_L_DOUBLE)
|
|
EXPECTED_OP_TYPE(EQ,TYPE_STRING)
|
|
|
|
EXPECTED_OP_TYPE(LT,TYPE_CHAR)
|
|
EXPECTED_OP_TYPE(LT,TYPE_U_CHAR)
|
|
EXPECTED_OP_TYPE(LT,TYPE_INT)
|
|
EXPECTED_OP_TYPE(LT,TYPE_U_INT)
|
|
EXPECTED_OP_TYPE(LT,TYPE_L_INT)
|
|
EXPECTED_OP_TYPE(LT,TYPE_U_L_INT)
|
|
EXPECTED_OP_TYPE(LT,TYPE_SIZE_T)
|
|
EXPECTED_OP_TYPE(LT,TYPE_FLOAT)
|
|
EXPECTED_OP_TYPE(LT,TYPE_DOUBLE)
|
|
EXPECTED_OP_TYPE(LT,TYPE_L_DOUBLE)
|
|
EXPECTED_OP_TYPE(LT,TYPE_STRING)
|
|
|
|
EXPECTED_OP_TYPE(GT,TYPE_CHAR)
|
|
EXPECTED_OP_TYPE(GT,TYPE_U_CHAR)
|
|
EXPECTED_OP_TYPE(GT,TYPE_INT)
|
|
EXPECTED_OP_TYPE(GT,TYPE_U_INT)
|
|
EXPECTED_OP_TYPE(GT,TYPE_L_INT)
|
|
EXPECTED_OP_TYPE(GT,TYPE_U_L_INT)
|
|
EXPECTED_OP_TYPE(GT,TYPE_SIZE_T)
|
|
EXPECTED_OP_TYPE(GT,TYPE_FLOAT)
|
|
EXPECTED_OP_TYPE(GT,TYPE_DOUBLE)
|
|
EXPECTED_OP_TYPE(GT,TYPE_L_DOUBLE)
|
|
EXPECTED_OP_TYPE(GT,TYPE_STRING)
|
|
|
|
EXPECTED_OP_TYPE(LE,TYPE_CHAR)
|
|
EXPECTED_OP_TYPE(LE,TYPE_U_CHAR)
|
|
EXPECTED_OP_TYPE(LE,TYPE_INT)
|
|
EXPECTED_OP_TYPE(LE,TYPE_U_INT)
|
|
EXPECTED_OP_TYPE(LE,TYPE_L_INT)
|
|
EXPECTED_OP_TYPE(LE,TYPE_U_L_INT)
|
|
EXPECTED_OP_TYPE(LE,TYPE_SIZE_T)
|
|
EXPECTED_OP_TYPE(LE,TYPE_FLOAT)
|
|
EXPECTED_OP_TYPE(LE,TYPE_DOUBLE)
|
|
EXPECTED_OP_TYPE(LE,TYPE_L_DOUBLE)
|
|
EXPECTED_OP_TYPE(LE,TYPE_STRING)
|
|
|
|
EXPECTED_OP_TYPE(GE,TYPE_CHAR)
|
|
EXPECTED_OP_TYPE(GE,TYPE_U_CHAR)
|
|
EXPECTED_OP_TYPE(GE,TYPE_INT)
|
|
EXPECTED_OP_TYPE(GE,TYPE_U_INT)
|
|
EXPECTED_OP_TYPE(GE,TYPE_L_INT)
|
|
EXPECTED_OP_TYPE(GE,TYPE_U_L_INT)
|
|
EXPECTED_OP_TYPE(GE,TYPE_SIZE_T)
|
|
EXPECTED_OP_TYPE(GE,TYPE_FLOAT)
|
|
EXPECTED_OP_TYPE(GE,TYPE_DOUBLE)
|
|
EXPECTED_OP_TYPE(GE,TYPE_L_DOUBLE)
|
|
EXPECTED_OP_TYPE(GE,TYPE_STRING)
|
|
|
|
EXPECTED_OP_TYPE(NE,TYPE_CHAR)
|
|
EXPECTED_OP_TYPE(NE,TYPE_U_CHAR)
|
|
EXPECTED_OP_TYPE(NE,TYPE_INT)
|
|
EXPECTED_OP_TYPE(NE,TYPE_U_INT)
|
|
EXPECTED_OP_TYPE(NE,TYPE_L_INT)
|
|
EXPECTED_OP_TYPE(NE,TYPE_U_L_INT)
|
|
EXPECTED_OP_TYPE(NE,TYPE_SIZE_T)
|
|
EXPECTED_OP_TYPE(NE,TYPE_FLOAT)
|
|
EXPECTED_OP_TYPE(NE,TYPE_DOUBLE)
|
|
EXPECTED_OP_TYPE(NE,TYPE_L_DOUBLE)
|
|
EXPECTED_OP_TYPE(NE,TYPE_STRING)
|
|
|
|
// ====================== progress bar =================
|
|
|
|
|
|
unsigned sleep(unsigned x) { time_t t0=time(0); while (difftime(time(0),t0)<x); return 0; }
|
|
unsigned nnsleep(long long x) {
|
|
struct timespec time_stop;
|
|
struct timespec time_start;
|
|
clock_gettime(CLOCK_REALTIME, &time_start);
|
|
|
|
long long diff;
|
|
do{
|
|
clock_gettime(CLOCK_REALTIME, &time_stop);
|
|
|
|
diff = 1.0e9 * (time_stop.tv_sec - time_start.tv_sec) + (time_stop.tv_nsec - time_start.tv_nsec);
|
|
}while(diff < x);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void bar_progress_test_(){
|
|
bar_progress_start();
|
|
|
|
struct func *tmp;
|
|
size_t num_test=0;
|
|
size_t current_local_count_progress=0;
|
|
|
|
do{
|
|
pthread_mutex_lock(&mut_count_para_progress);
|
|
while(current_local_count_progress == count_para_progress && count_para_progress <= count_tests){
|
|
pthread_cond_wait(&cond_count_para_progress, &mut_count_para_progress);
|
|
}
|
|
current_local_count_progress = count_para_progress;
|
|
pthread_mutex_unlock(&mut_count_para_progress);
|
|
|
|
tmp = current_fn;
|
|
//UNLOCK(mut_current_test);
|
|
if(tmp)
|
|
num_test = extract_num__f(tmp->name);
|
|
if(strlen(bar_progress) < strlen(default_bar_progress))
|
|
bar_progress_step_msg(num_test, count_tests, "test N°", default_bar_progress[0],default_bar_progress[1],default_bar_progress[2]=='c');
|
|
else bar_progress_step_msg(num_test, count_tests, "test N°", bar_progress[0],bar_progress[1],bar_progress[2]=='c');
|
|
nnsleep(200000000);// 200 milliseconds
|
|
//nnsleep(2000000000);// 2000 milliseconds
|
|
}while(tmp);
|
|
|
|
|
|
|
|
bar_progress_stop();
|
|
|
|
}
|
|
|
|
void*
|
|
run_progress_tests(void *max_d)
|
|
{
|
|
/*int max_col = 80;*/ //*(int*)max_d;
|
|
//progress_test_(max_col);
|
|
bar_progress_test_();
|
|
return (void*)0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ====================== end funcs progress bar =======
|
|
|
|
void
|
|
append_func(void (*run)(void), char *name){
|
|
static struct func *f_static = NULL;
|
|
if(f_beging == NULL){
|
|
f_beging = malloc(sizeof(struct func));
|
|
f_static = f_beging;
|
|
f_static->name = malloc(strlen(name)+1);
|
|
strcpy(f_static->name,name);
|
|
f_static->run = run;
|
|
f_static->next = NULL;
|
|
}
|
|
else{
|
|
struct func *tmp = malloc(sizeof(struct func));
|
|
tmp->run = run;
|
|
tmp->name = malloc(strlen(name)+1);
|
|
strcpy(tmp->name,name);
|
|
tmp->next = NULL;
|
|
f_static->next = tmp;
|
|
f_static = tmp;
|
|
}
|
|
++count_tests;
|
|
}
|
|
|
|
void begin_execute_func(char *fun_ame, struct timespec *start_t){
|
|
clock_gettime(CLOCK_REALTIME, start_t);
|
|
PRINT_HK_C(colors_f[k_GREEN],tab_hk_f[hk_RN]," %s\n", fun_ame);
|
|
count_pass_local = 0;
|
|
count_fail_local = 0;
|
|
}
|
|
|
|
#define PRINT_TIMESTAMP_STAT(color)\
|
|
if(SECOND) PRINT_HK_C(color,tab_hk_f[hk_DN]," %lu tests passed from %s (%lf s)\n\n",count_pass_local,fun_ame, diff_timespec_seconds(end_t, start_t));\
|
|
else if(NANOSECOND) PRINT_HK_C(color,tab_hk_f[hk_DN]," %lu tests passed from %s (%ld ns)\n\n",count_pass_local,fun_ame, diff_timespec_nanoseconds(end_t, start_t));\
|
|
else PRINT_HK_C(color,tab_hk_f[hk_DN]," %lu tests passed from %s (%lf ms)\n\n",count_pass_local,fun_ame, diff_timespec_milliseconds(end_t, start_t));
|
|
|
|
void end_execute_func(char *fun_ame, struct timespec start_t){
|
|
struct timespec end_t; clock_gettime(CLOCK_REALTIME, &end_t);
|
|
if(count_fail_local){
|
|
INCREMENT(count_fail_global); /*++count_fail_global*/
|
|
append_failed_list(&failed_l, fun_ame);
|
|
PRINT_HK_C(colors_f[k_RED], tab_hk_f[hk_FL], " %lu tests failed from %s\n",count_fail_local,fun_ame);
|
|
PRINT_TIMESTAMP_STAT(colors_f[k_RED]);
|
|
}
|
|
else
|
|
{
|
|
INCREMENT(count_pass_global); /*++count_pass_global*/
|
|
PRINT_TIMESTAMP_STAT(colors_f[k_GREEN]);
|
|
}
|
|
}
|
|
/*
|
|
* print on the top of test
|
|
*/
|
|
void head_run(size_t nbtest, struct timespec *start_t){
|
|
clock_gettime(CLOCK_REALTIME, start_t);
|
|
if(cur_array_TYPE_SIZE_T || cur_array_TYPE_STRING) PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ],"%s"," Running tests.\n");
|
|
else PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," Running %lu tests.\n",nbtest);
|
|
}
|
|
|
|
/*
|
|
* printing on the end of test
|
|
*/
|
|
void
|
|
stat_end_run(size_t ntst, struct timespec start_t){
|
|
struct timespec end_t; clock_gettime(CLOCK_REALTIME, &end_t);
|
|
|
|
if(SECOND) PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," %lu tests ran. (%lf s total)\n",ntst, diff_timespec_seconds(end_t, start_t));
|
|
else if(NANOSECOND) PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," %lu tests ran. (%ld ns total)\n",ntst, diff_timespec_nanoseconds(end_t, start_t));
|
|
else PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," %lu tests ran. (%lf ms total)\n",ntst, diff_timespec_milliseconds(end_t, start_t));
|
|
|
|
PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_PS]," %lu tests\n", count_pass_global);
|
|
if(failed_l != NULL){
|
|
PRINT_HK_C(colors_f[k_RED], tab_hk_f[hk_FL]," %lu tests, listed below:\n",count_fail_global);
|
|
list_failed_test(failed_l, __func__);
|
|
PRINT_HK_C("","","\n%ld FAILED TESTS \n",count_fail_global);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* need to separate num type and ptr type because of (void*)
|
|
* in the 2 below macros
|
|
*/
|
|
|
|
#define GEN_IS_IN_ARRAY_PTR(type)\
|
|
bool is_in_array_##type(type *array, type val){\
|
|
bool found = false;\
|
|
for(size_t i = 0; i < cur_array_##type; ++i){\
|
|
if(debug){\
|
|
char * strarr = type##_TO_STR(array[i]), *strval = type##_TO_STR(val);\
|
|
PRINT_DEBUG("compare |%s| in array and val: |%s|\n",strarr, strval);\
|
|
if(strcmp(#type, "TYPE_STRING" ) != 0 ){ free(strarr);free(strval); }\
|
|
}/*PRINT_DEBUG("compare |%s| in array and val: |%s|\n",type##_TO_STR(array[i]), type##_TO_STR(val));*/\
|
|
if(COMPARE_N_##type((void*)(&array[i]),(void*)&val ) == 0 ){\
|
|
found = true;\
|
|
break;\
|
|
}\
|
|
}\
|
|
PRINT_DEBUG(" val return = %d \n",found);\
|
|
return found;\
|
|
}\
|
|
|
|
// no need anymore, combined with above!!
|
|
#define GEN_IS_IN_ARRAY_NUM(type)\
|
|
bool is_in_array_##type(type *array, type val){\
|
|
bool found = false;\
|
|
for(size_t i = 0; i < cur_array_##type; ++i){\
|
|
char * strarr = type##_TO_STR(array[i]), *strval = type##_TO_STR(val);\
|
|
PRINT_DEBUG("compare |%s| in array and val: |%s|\n",strarr, strval);\
|
|
free(strarr);free(strval);\
|
|
/*PRINT_DEBUG("compare |%s| in array and val: |%s|\n",type##_TO_STR(array[i]), type##_TO_STR(val));*/\
|
|
if(COMPARE_N_##type((void*)(&array[i]),(void*)&val ) == 0 ){\
|
|
found = true;\
|
|
break;\
|
|
}\
|
|
}\
|
|
PRINT_DEBUG(" val return = %d \n",found);\
|
|
return found;\
|
|
}\
|
|
|
|
|
|
GEN_IS_IN_ARRAY_PTR(TYPE_STRING)
|
|
GEN_IS_IN_ARRAY_PTR(TYPE_SIZE_T)
|
|
|
|
/*
|
|
* extract name test between () because the syntax is TEST(name_test)
|
|
*/
|
|
void extract_name_test_from_name(char *name_org, char **name_f){
|
|
size_t len=strlen(name_org);
|
|
long cur=-1;
|
|
char *name_test=malloc(len+1);
|
|
for(size_t i=0; i<len; ++i){
|
|
if(cur == -1 && name_org[i]=='(')
|
|
cur=0;
|
|
else if(cur >=0){
|
|
if(name_org[i] == ')')
|
|
break;
|
|
else
|
|
name_test[cur++]=name_org[i];
|
|
}
|
|
}
|
|
name_test[cur]='\0';
|
|
PRINT_DEBUG("name_test =%s\n",name_test);
|
|
*name_f = name_test;
|
|
|
|
}
|
|
|
|
#define CHECK_IF_SELECTED_TEST(name_f)\
|
|
exec_test=0;\
|
|
if(some_tests_selected == 0){\
|
|
exec_test=1; \
|
|
}\
|
|
else{\
|
|
if(cur_array_TYPE_SIZE_T){\
|
|
num_f=extract_num__f(name_f) ;\
|
|
exec_test = is_in_array_TYPE_SIZE_T(array_TYPE_SIZE_T, num_f);\
|
|
}\
|
|
if(exec_test == 0 && cur_array_TYPE_STRING){\
|
|
extract_name_test_from_name(name_f, &name_test);\
|
|
exec_test = is_in_array_TYPE_STRING(array_TYPE_STRING, name_test );\
|
|
free(name_test);\
|
|
}\
|
|
}\
|
|
|
|
|
|
|
|
void execute_all(struct func *fun){
|
|
struct func *tmp = fun;
|
|
struct timespec start_t;
|
|
size_t num_f;
|
|
char *name_test=NULL;
|
|
bool exec_test=0;
|
|
//PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," Running %lu tests.\n",count_tests);
|
|
while(tmp){
|
|
pthread_mutex_lock(&mut_count_para_progress);
|
|
++count_para_progress;
|
|
pthread_mutex_unlock(&mut_count_para_progress);
|
|
pthread_cond_signal(&cond_count_para_progress);
|
|
current_fn = tmp;
|
|
CHECK_IF_SELECTED_TEST(tmp->name)
|
|
if(exec_test){
|
|
begin_execute_func(tmp->name, &start_t);
|
|
tmp->run();
|
|
end_execute_func(tmp->name, start_t);
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
current_fn = tmp;
|
|
pthread_mutex_lock(&mut_count_para_progress);
|
|
++count_para_progress;
|
|
pthread_mutex_unlock(&mut_count_para_progress);
|
|
pthread_cond_signal(&cond_count_para_progress);
|
|
}
|
|
|
|
void _purge_t();
|
|
|
|
void
|
|
run_all_tests()
|
|
{
|
|
//progress = true;
|
|
#if 1
|
|
pthread_t thrd_progress;
|
|
if(progress) pthread_create(&thrd_progress, NULL, run_progress_tests, NULL);
|
|
//if(progress) pthread_create(&thrd_progress, NULL, run_progress_tests, (void*)&max_col);
|
|
#endif
|
|
|
|
struct timespec start_t;
|
|
head_run(count_tests, &start_t);
|
|
execute_all(f_beging);
|
|
//stat_end_run(count_tests, start_t);
|
|
stat_end_run(count_pass_global + count_fail_global, start_t);
|
|
|
|
if(progress) pthread_join(thrd_progress, NULL);
|
|
|
|
#if 0
|
|
_purge_t();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* begin end parallel tests
|
|
*/
|
|
/*
|
|
* print on the top of all test (parallel case)
|
|
*/
|
|
void head_all_parallel_run(struct timespec *start_t){
|
|
clock_gettime(CLOCK_REALTIME, start_t);
|
|
if (cur_array_TYPE_SIZE_T || cur_array_TYPE_STRING) PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," Running tests on %ld threads\n", parallel_nb);
|
|
else PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," Running %ld tests on %ld threads\n",count_tests, parallel_nb);
|
|
}
|
|
|
|
/*
|
|
* print on the top of test in parallel
|
|
*/
|
|
void head_parallel_run(struct timespec *start_t, size_t id_thrd){
|
|
sprintf(log_name_file_thrd[id_thrd],"log_thread_%ld_id_%ld",id_thrd,pthread_self());
|
|
f_ou_th[id_thrd] = fopen(log_name_file_thrd[id_thrd], "w+");
|
|
clock_gettime(CLOCK_REALTIME, start_t);
|
|
PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," Running tests on thread[%ld] ========== ==threadID== %ld \n", id_thrd,pthread_self());
|
|
}
|
|
|
|
/*
|
|
* printing stat of each thread tests
|
|
*/
|
|
void
|
|
stat_end_parallel_run(size_t ntst, struct timespec start_t, size_t id_thrd){
|
|
struct timespec end_t; clock_gettime(CLOCK_REALTIME, &end_t);
|
|
|
|
if(SECOND) PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," %lu tests ran on thread[%ld]. (%lf s total) \n",ntst, id_thrd, diff_timespec_seconds(end_t, start_t));
|
|
else if(NANOSECOND) PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," %lu tests ran on thread[%ld]. (%ld ns total)\n",ntst, id_thrd, diff_timespec_nanoseconds(end_t, start_t));
|
|
else PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," %lu tests ran on thread[%ld]. (%lf ms total)\n",ntst, id_thrd, diff_timespec_milliseconds(end_t, start_t));
|
|
|
|
PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_PS]," %lu tests passed on thread[%ld]\n", count_pass_thread[id_thrd], id_thrd);
|
|
if(thread_test_failed_l[id_thrd] != NULL){
|
|
PRINT_HK_C(colors_f[k_RED], tab_hk_f[hk_FL]," %lu tests failed on thread[%ld], listed below:\n",count_fail_thread[id_thrd],id_thrd);
|
|
list_failed_test(thread_test_failed_l[id_thrd], __func__);
|
|
}
|
|
}
|
|
/*
|
|
* stat of all tests on all threads
|
|
*/
|
|
|
|
void
|
|
stat_end_all_parallel_run(size_t ntst, struct timespec start_t){
|
|
struct timespec end_t; clock_gettime(CLOCK_REALTIME, &end_t);
|
|
|
|
//PRINT_HK_C(colors_f[k_DEFAULT], tab_hk_f[hk_EQ]," %s: all parallel tests done\n\n",__FILE__);
|
|
|
|
if(SECOND) PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," %lu tests ran on %ld threads. (%lf s total) \n",ntst, parallel_nb, diff_timespec_seconds(end_t, start_t));
|
|
else if(NANOSECOND) PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," %lu tests ran on %ld threads. (%ld ns total)\n",ntst, parallel_nb, diff_timespec_nanoseconds(end_t, start_t));
|
|
else PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_EQ]," %lu tests ran on %ld threads. (%lf ms total)\n",ntst, parallel_nb, diff_timespec_milliseconds(end_t, start_t));
|
|
|
|
PRINT_HK_C(colors_f[k_GREEN], tab_hk_f[hk_PS]," %lu tests\n", count_pass_global);
|
|
if(failed_l != NULL){
|
|
PRINT_HK_C(colors_f[k_RED], tab_hk_f[hk_FL]," %lu tests, listed below:\n",count_fail_global);
|
|
list_failed_test(failed_l, __func__);
|
|
PRINT_HK_C("","","\n%ld FAILED TESTS \n",count_fail_global);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void begin_execute_func_parallel(char *fun_ame, struct timespec *start_t, size_t id_thrd){
|
|
clock_gettime(CLOCK_REALTIME, start_t);
|
|
PRINT_HK_C(colors_f[k_GREEN],tab_hk_f[hk_RN]," %s on thread[%ld]\n", fun_ame, id_thrd);
|
|
}
|
|
|
|
#define PRINT_TIMESTAMP_STAT_PARALLEL(color)\
|
|
if(SECOND) PRINT_HK_C(color,tab_hk_f[hk_DN]," %lu tests passed from %s (%lf s), on thread[%ld]\n\n",count_pass_test[num_test],fun_ame, diff_timespec_seconds(end_t, start_t),id_thrd);\
|
|
else if(NANOSECOND) PRINT_HK_C(color,tab_hk_f[hk_DN]," %lu tests passed from %s (%ld ns), on thread[%ld]\n\n",count_pass_test[num_test],fun_ame, diff_timespec_nanoseconds(end_t, start_t),id_thrd);\
|
|
else PRINT_HK_C(color,tab_hk_f[hk_DN]," %lu tests passed from %s (%lf ms), on thread[%ld]\n\n",count_pass_test[num_test],fun_ame, diff_timespec_milliseconds(end_t, start_t),id_thrd);
|
|
|
|
void end_execute_func_parallel(char *fun_ame, struct timespec start_t, size_t id_thrd){
|
|
struct timespec end_t; clock_gettime(CLOCK_REALTIME, &end_t);
|
|
size_t num_test = extract_num__f(fun_ame);
|
|
PRINT_DEBUG(" ... thread[%ld], count_fail_test[%ld] = %ld ... %s\n", id_thrd, num_test, count_fail_test[num_test],fun_ame);
|
|
if(count_fail_test[num_test]){
|
|
INCREMENT(count_fail_global); /*++count_fail_global*/
|
|
append_failed_list(&thread_test_failed_l[id_thrd], fun_ame);
|
|
++(count_fail_thread[id_thrd]);
|
|
LOCK(mut_global_list_fail);
|
|
append_failed_list(&failed_l, fun_ame);
|
|
UNLOCK(mut_global_list_fail);
|
|
PRINT_HK_C(colors_f[k_RED], tab_hk_f[hk_FL], " %lu tests failed from %s on thread[%ld], %ld tests failed on thread[%ld]\n",count_fail_test[num_test],fun_ame, id_thrd,count_fail_thread[id_thrd],id_thrd);
|
|
PRINT_TIMESTAMP_STAT_PARALLEL(colors_f[k_RED]);
|
|
}
|
|
else
|
|
{
|
|
++(count_pass_thread[id_thrd]);
|
|
INCREMENT(count_pass_global); /*++count_pass_global*/
|
|
PRINT_TIMESTAMP_STAT_PARALLEL(colors_f[k_GREEN]);
|
|
}
|
|
}
|
|
|
|
void execute_test_parallel(size_t id_thrd){
|
|
|
|
struct timespec start_t;
|
|
struct func *tmp;
|
|
size_t num_f;
|
|
char *name_test=NULL;
|
|
bool exec_test=0;
|
|
|
|
do{
|
|
pthread_mutex_lock(&mut_count_para_progress);
|
|
++count_para_progress;
|
|
pthread_mutex_unlock(&mut_count_para_progress);
|
|
pthread_cond_signal(&cond_count_para_progress);
|
|
LOCK(mut_current_test);
|
|
tmp = current_fn;
|
|
if(tmp){
|
|
current_fn = tmp->next;
|
|
UNLOCK(mut_current_test);
|
|
CHECK_IF_SELECTED_TEST(tmp->name)
|
|
if(exec_test){
|
|
PRINT_DEBUG(" *** thread[%ld], func_name = %s *** \n", id_thrd, tmp->name);
|
|
begin_execute_func_parallel(tmp->name, &start_t, id_thrd);
|
|
tmp->run();
|
|
end_execute_func_parallel(tmp->name, start_t, id_thrd);
|
|
}
|
|
}
|
|
else{
|
|
UNLOCK(mut_current_test);
|
|
}
|
|
}while(tmp);
|
|
|
|
pthread_mutex_lock(&mut_count_para_progress);
|
|
++count_para_progress;
|
|
pthread_mutex_unlock(&mut_count_para_progress);
|
|
pthread_cond_signal(&cond_count_para_progress);
|
|
}
|
|
|
|
void*
|
|
run_parallel_tests(void *id)
|
|
{
|
|
|
|
size_t id_th=*(size_t*)id;
|
|
id_thread_self[id_th] = pthread_self();
|
|
struct timespec start_t;
|
|
head_parallel_run(&start_t, id_th);
|
|
execute_test_parallel(id_th);
|
|
stat_end_parallel_run(count_fail_thread[id_th]+count_pass_thread[id_th], start_t, id_th);
|
|
return (void*)0;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
void final_parallel_test_();
|
|
|
|
/*
|
|
* initialisation
|
|
*/
|
|
void
|
|
init_parallel_test_()
|
|
{
|
|
|
|
signal(SIGSEGV, final_parallel_test_); /* to clear logs files! */
|
|
|
|
|
|
is_parallel_nb = 1;
|
|
|
|
f_ou_th = malloc((parallel_nb + 1) *sizeof(FILE*));
|
|
log_name_file_thrd = malloc((parallel_nb + 1) *sizeof(char*));
|
|
for(size_t i=0; i<=parallel_nb; ++i){
|
|
log_name_file_thrd[i] = malloc((256) *sizeof(char));
|
|
}
|
|
|
|
/*
|
|
* on thread principale
|
|
*/
|
|
sprintf(log_name_file_thrd[parallel_nb],"log_principal_thread_%ld_id_%ld",parallel_nb,pthread_self());
|
|
f_ou_th[parallel_nb] = fopen(log_name_file_thrd[parallel_nb], "w+");
|
|
|
|
|
|
|
|
count_pass_test = malloc(count_tests * sizeof(size_t));
|
|
count_fail_test = malloc(count_tests * sizeof(size_t));
|
|
for(size_t i=0; i<count_tests; ++i){
|
|
count_pass_test[i]=0;
|
|
count_fail_test[i]=0;
|
|
}
|
|
|
|
thread_test_failed_l = malloc(parallel_nb * sizeof(struct failed_lists*));
|
|
count_pass_thread = malloc(parallel_nb * sizeof(size_t));
|
|
count_fail_thread = malloc(parallel_nb * sizeof(size_t));
|
|
id_thread_self = malloc((parallel_nb + 1) * sizeof(size_t));
|
|
|
|
id_thread_self[parallel_nb]= pthread_self();// main thread
|
|
|
|
for(size_t i=0; i<parallel_nb; ++i){
|
|
thread_test_failed_l[i] = NULL;
|
|
count_pass_thread[i] = 0;
|
|
count_fail_thread[i] = 0;
|
|
id_thread_self[i]=0; /* have to initialize because if some threads not yetr run we have warning with valgrind : non initialize value, beause real value is provide by each thread */
|
|
}
|
|
|
|
current_fn = f_beging;
|
|
|
|
pthread_mutex_init(&mut_global_list_fail, NULL);
|
|
pthread_mutex_init(&mut_current_test, NULL);
|
|
pthread_mutex_init(&mut_count_pass_global, NULL);
|
|
pthread_mutex_init(&mut_count_fail_global, NULL);
|
|
pthread_mutex_init(&mut_count_pass_local, NULL);
|
|
pthread_mutex_init(&mut_count_fail_local, NULL);
|
|
pthread_mutex_init(&mut_count_para_progress, NULL);
|
|
pthread_cond_init(&cond_count_para_progress, NULL);
|
|
}
|
|
/*
|
|
* finalisation, cleanup
|
|
*/
|
|
void
|
|
final_parallel_test_()
|
|
{
|
|
is_parallel_nb = 0; /* to avoid issue when print debug afer removing log_ of each thread if removelog is active */
|
|
|
|
free(count_pass_test);
|
|
free(count_fail_test);
|
|
free(count_pass_thread);
|
|
free(count_fail_thread);
|
|
|
|
free(id_thread_self);
|
|
//id_thread_self = NULL;
|
|
|
|
for(size_t i=0; i< parallel_nb; ++i)
|
|
clear_all_falied_list(&thread_test_failed_l[i]);
|
|
|
|
free(thread_test_failed_l);
|
|
|
|
|
|
pthread_mutex_destroy(&mut_global_list_fail);
|
|
pthread_mutex_destroy(&mut_current_test);
|
|
pthread_mutex_destroy(&mut_count_pass_global);
|
|
pthread_mutex_destroy(&mut_count_fail_global);
|
|
pthread_mutex_destroy(&mut_count_pass_local);
|
|
pthread_mutex_destroy(&mut_count_fail_local);
|
|
pthread_mutex_destroy(&mut_count_para_progress);
|
|
pthread_cond_destroy(&cond_count_para_progress);
|
|
|
|
|
|
char reader[256]="Here are the ordered results for each thread";
|
|
|
|
struct winsize w;
|
|
ioctl(1, TIOCGWINSZ, &w);
|
|
|
|
fprintf(F_OUT,"\n\n%0*d\n %*s \n%0*d\n\n",w.ws_col,0, (int)(w.ws_col+strlen(reader))/2, reader,w.ws_col,0 );
|
|
|
|
if(savelog){
|
|
FILE *f_savelog;
|
|
f_savelog=fopen(savelog, "a");
|
|
for(size_t id_thrd =0 ; id_thrd <= parallel_nb; ++id_thrd){
|
|
rewind(f_ou_th[id_thrd]); // put the file pointer to the begin of file;
|
|
while(fgets(reader, 255,f_ou_th[id_thrd] )){
|
|
fprintf(F_OUT,"%s",reader);
|
|
fprintf(f_savelog,"%s",reader);
|
|
}
|
|
fclose(f_ou_th[id_thrd]);
|
|
}
|
|
fclose(f_savelog);
|
|
}else{
|
|
for(size_t id_thrd =0 ; id_thrd <= parallel_nb; ++id_thrd){
|
|
rewind(f_ou_th[id_thrd]); // put the file pointer to the begin of file;
|
|
while(fgets(reader, 255,f_ou_th[id_thrd] ))
|
|
fprintf(F_OUT,"%s",reader);
|
|
fclose(f_ou_th[id_thrd]);
|
|
}
|
|
}
|
|
|
|
free(f_ou_th);
|
|
|
|
if(removelog){
|
|
for(size_t i=0; i<=parallel_nb; ++i){
|
|
remove(log_name_file_thrd[i]);
|
|
PRINT_DEBUG("file log of treard[%ld] removed\n",i);
|
|
}
|
|
}
|
|
|
|
for(size_t i=0; i<=parallel_nb; ++i)
|
|
free(log_name_file_thrd[i]);
|
|
|
|
free(log_name_file_thrd);
|
|
}
|
|
|
|
void run_all_tests_parallel(size_t parallel /*, int max_col*/)
|
|
{
|
|
parallel_nb = parallel; /* need to be here to initialise parallel_nb for init_parallel_test_ */
|
|
|
|
init_parallel_test_();
|
|
|
|
struct timespec start_t;
|
|
|
|
head_all_parallel_run(&start_t);
|
|
|
|
#if 1
|
|
pthread_t thrd_progress;
|
|
if(progress) pthread_create(&thrd_progress, NULL, run_progress_tests, NULL);
|
|
//if(progress) pthread_create(&thrd_progress, NULL, run_progress_tests, (void*)&max_col);
|
|
#endif
|
|
|
|
pthread_t *thrd = malloc(parallel_nb * sizeof(pthread_t));
|
|
size_t *id_th = malloc( parallel_nb * sizeof(size_t));
|
|
|
|
for(size_t i = 0; i < parallel_nb; ++i){
|
|
id_th[i]=i;
|
|
pthread_create(&thrd[i], NULL, run_parallel_tests, (void*)&id_th[i]);
|
|
}
|
|
|
|
for(size_t i=0; i<parallel_nb; ++i){
|
|
pthread_join(thrd[i], NULL);
|
|
}
|
|
|
|
if(progress) pthread_join(thrd_progress, NULL);
|
|
|
|
//stat_end_all_parallel_run(count_tests, start_t );
|
|
stat_end_all_parallel_run(count_pass_global + count_fail_global, start_t );
|
|
|
|
free(id_th);
|
|
free(thrd);
|
|
|
|
final_parallel_test_();
|
|
|
|
#if 0
|
|
_purge_t();
|
|
#endif
|
|
}
|
|
|
|
void run_all_tests_args(int argc, char **argv){
|
|
|
|
parse_options(argc,argv);
|
|
|
|
|
|
if(help) usage(argc,argv);
|
|
if(is_parallel_nb) run_all_tests_parallel(parallel_nb);
|
|
else run_all_tests();
|
|
}
|
|
void
|
|
clear_all_func(struct func **fun)
|
|
{
|
|
if(*fun){
|
|
struct func *tmp = *fun, *ttmp;
|
|
while(tmp != NULL){
|
|
free(tmp->name);
|
|
ttmp = tmp;
|
|
tmp = tmp->next;
|
|
free(ttmp);
|
|
}
|
|
*fun=NULL;
|
|
}
|
|
}
|
|
void
|
|
clear_all_falied_list(struct failed_lists **flist)
|
|
{
|
|
if(*flist){
|
|
struct failed_lists *tmp = *flist, *ttmp;
|
|
while(tmp != NULL){
|
|
free(tmp->name);
|
|
|
|
ttmp = tmp;
|
|
tmp = tmp->next;
|
|
free(ttmp);
|
|
}
|
|
*flist=NULL;
|
|
}
|
|
}
|
|
/*
|
|
* to purge func list!
|
|
* optionnal but good practice
|
|
*/
|
|
void
|
|
_purge_t()
|
|
{
|
|
|
|
#if 0
|
|
clear_all_func(&f_beging);
|
|
clear_all_falied_list(&failed_l);
|
|
|
|
#endif
|
|
if(array_TYPE_SIZE_T){
|
|
free(array_TYPE_SIZE_T);
|
|
array_TYPE_SIZE_T = NULL;
|
|
}
|
|
if(array_TYPE_STRING) {
|
|
for(int i=0; i< cur_array_TYPE_STRING; ++i){
|
|
free(array_TYPE_STRING[i]);
|
|
}
|
|
free(array_TYPE_STRING);
|
|
array_TYPE_STRING = NULL;
|
|
}
|
|
|
|
PRINT_DEBUG("%s\n","_purge_t done ");
|
|
}
|
|
|
|
|
|
|
|
__attribute__((destructor))
|
|
void
|
|
purge_tests()
|
|
{
|
|
|
|
if(bar_progress_has_to_be_freed) free(bar_progress);
|
|
if(default_bar_progress_has_to_be_freed) free(default_bar_progress);
|
|
#if 1
|
|
clear_all_func(&f_beging);
|
|
clear_all_falied_list(&failed_l);
|
|
#endif
|
|
if(array_TYPE_SIZE_T){
|
|
free(array_TYPE_SIZE_T);
|
|
}
|
|
if(array_TYPE_STRING) {
|
|
for(int i=0; i< cur_array_TYPE_STRING; ++i){
|
|
free(array_TYPE_STRING[i]);
|
|
}
|
|
free(array_TYPE_STRING);
|
|
}
|
|
|
|
PRINT_DEBUG("%s\n","purge done");
|
|
|
|
}
|
|
|