Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
tesh version 2
[simgrid.git] / tools / tesh2 / src / unit.c
1 #include <unit.h>
2 #include <suite.h>
3 #include <command.h>
4 #include <context.h>
5 #include <fstream.h>
6
7
8
9 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(tesh);
10
11
12 /* the unit thread start routine */
13 static void*
14 unit_start(void* p);
15
16 unit_t
17 unit_new(runner_t runner, suite_t owner, fstream_t fstream)
18 {
19         unit_t unit = xbt_new0(s_unit_t, 1);
20
21         /* set the owner of the unit */
22         unit->runner = runner;
23         
24         unit->fstream = fstream;
25
26         unit->sem = NULL;
27
28         unit->commands = vector_new(DEFAULT_COMMANDS_CAPACITY, (fn_finalize_t)command_free);
29         
30         unit->thread = NULL;
31
32         unit->number_of_started_commands = 0;
33         unit->number_of_interrupted_commands = 0;
34         unit->number_of_failed_commands = 0;
35         unit->number_of_successeded_commands = 0;
36         unit->number_of_terminated_commands = 0;
37         unit->interrupted = 0;
38         unit->failed = 0;
39         unit->successeded = 0;
40         unit->parsed = 0;
41         unit->released = 0;
42         unit->parsing_include_file = 0;
43         
44         
45         unit->owner = owner;
46         unit->number = 0;
47         unit->suites = vector_new(DEFAULT_COMMANDS_CAPACITY, (fn_finalize_t)suite_free);
48         unit->owner = owner;
49         
50         unit->running_suite = 0;
51
52         return unit;
53
54 }
55
56 void
57 unit_add_suite(unit_t unit, suite_t suite)
58 {
59         vector_push_back(unit->suites, suite);
60 }
61
62 int
63 unit_free(void** unitptr)
64 {
65         unit_t* __unitptr = (unit_t*)unitptr;
66         
67         vector_free(&((*__unitptr)->commands));
68         
69         vector_free(&((*__unitptr)->suites));
70         
71         /* if the unit is interrupted during its run, the semaphore is NULL */
72         if((*__unitptr)->sem)
73                 xbt_os_sem_destroy((*__unitptr)->sem);
74                 
75                 
76         free((*__unitptr)->suites);
77
78         free(*__unitptr);
79         
80         *__unitptr = NULL;
81         
82         return 0;
83 }
84
85 static void*
86 unit_start(void* p) 
87 {
88         
89         xbt_os_thread_t thread;
90         xbt_os_mutex_t mutex;
91         context_t context;
92         int i;
93
94         unit_t unit = (unit_t)p;
95         
96         xbt_os_mutex_acquire(unit->mutex);
97         unit->runner->number_of_runned_units++;
98         xbt_os_mutex_release(unit->mutex);
99
100         /* try to acquire the jobs semaphore to start */
101         xbt_os_sem_acquire(jobs_sem);
102         
103         mutex = xbt_os_mutex_init();
104         context = context_new();
105         
106         if(want_dry_run)
107                 INFO1("checking unit %s...",unit->fstream->name); 
108         
109         /* parse the file */
110         unit_parse(unit, context, mutex, unit->fstream->name, unit->fstream->stream);
111         
112         /* if the unit is not interrupted and not failed the unit, all the file is parsed
113          * so all the command are launched
114          */
115         if(!unit->interrupted)
116         {
117                 unit->parsed = 1;
118                 
119                 /* all the commands have terminate before the end of the parsing of the tesh file
120                  * so the unit release the semaphore itself
121                  */
122                 if(!unit->released && (unit->number_of_started_commands == (unit->number_of_failed_commands + unit->number_of_interrupted_commands + unit->number_of_successeded_commands)))
123                         xbt_os_sem_release(unit->sem);  
124                 
125         }
126         
127         /* wait the end of all the commands or a command failure or an interruption */
128         
129         
130         xbt_os_sem_acquire(unit->sem);
131         
132         if(unit->interrupted)
133         {
134                 command_t command;
135
136                 /* interrupt all the running commands of the unit */ 
137                 for(i = 0; i < unit->number_of_commands; i++)
138                 {
139                         /*command = unit->commands[i];*/
140                         command = vector_get_at(unit->commands, i);
141
142                         if(command->status == cs_in_progress)
143                                 /*command_interrupt(unit->commands[i]);*/
144                                 command_interrupt(command);
145                 }
146         }
147         
148
149         /* wait the end of the threads */
150         for(i = 0; i < unit->number_of_commands; i++)
151         {
152                 /*thread = unit->commands[i]->thread;*/
153                 
154                 command_t command = vector_get_at(unit->commands, i);
155                 thread = command->thread;
156                 
157                 if(thread)
158                         xbt_os_thread_join(thread,NULL);
159         }
160         
161         context_free(&context);
162
163         xbt_os_mutex_destroy(mutex);
164         
165         xbt_os_mutex_acquire(unit->mutex);
166
167         /* increment the number of ended units */
168         unit->runner->number_of_ended_units++;
169         
170         /* it's the last unit, release the runner */
171         if(/*!unit->interrupted &&*/ (unit->runner->number_of_runned_units == unit->runner->number_of_ended_units))
172         {
173                 if(unit->number_of_successeded_commands == unit->number_of_commands)
174                         unit->successeded = 1;
175
176                 /* first release the mutex */
177                 xbt_os_mutex_release(unit->mutex);
178                 xbt_os_sem_release(units_sem);
179         }
180         else
181                 xbt_os_mutex_release(unit->mutex);
182
183         /* release the jobs semaphore, then the next unit can start */
184         xbt_os_sem_release(jobs_sem);
185         
186         return NULL;
187
188 }
189
190 void
191 unit_parse(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name, FILE* stream)
192 {
193         size_t len;
194         char * line = NULL;
195         int line_num=0;
196         char file_pos[256];
197         xbt_strbuff_t buff;
198         int buffbegin = 0; 
199         
200         /* Count the line length while checking wheather it's blank */
201         int blankline;
202         int linelen;    
203         /* Deal with \ at the end of the line, and call handle_line on result */
204         int to_be_continued;
205         
206         buff=xbt_strbuff_new();
207         
208         while(!unit->interrupted  && getline(&line, &len, stream) != -1)
209         {
210                 blankline=1;
211                 linelen = 0;    
212                 to_be_continued = 0;
213
214                 line_num++;
215                 
216                 while(line[linelen] != '\0') 
217                 {
218                         if (line[linelen] != ' ' && line[linelen] != '\t' && line[linelen]!='\n' && line[linelen]!='\r')
219                                 blankline = 0;
220                         
221                         linelen++;
222                 }
223         
224                 if(blankline) 
225                 {
226                         if(!context->command_line && (context->input->used || context->output->used))
227                         {
228                                 ERROR1("[%d] Error: no command found in this chunk of lines.",buffbegin);
229                                 
230                                 if(unit->parsing_include_file)
231                                         ERROR1("Unit `%s': NOK (syntax error)", unit->fstream->name);
232                                 else
233                                         ERROR2("Unit `%s' inclued in `%s' : NOK (syntax error)", file_name, unit->fstream->name);       
234                                 
235                                 exit_code = ESYNTAX;
236                                 unit_handle_failure(unit);
237                                 break;
238                         }
239                         
240                         if(context->command_line)
241                         {
242                                 if(!want_dry_run)
243                                 {
244                                         command_t command = command_new(unit, context, mutex);
245                                         command_run(command);
246                                 }
247                                 
248                                 context_reset(context);
249                         }
250                 
251                 
252                         continue;
253                         
254                 }
255                 
256                 if(linelen>1 && line[linelen-2]=='\\') 
257                 {
258                         if (linelen>2 && line[linelen-3] == '\\') 
259                         {
260                                 /* Damn. Escaped \ */
261                                 line[linelen-2] = '\n';
262                                 line[linelen-1] = '\0';
263                         } 
264                         else 
265                         {
266                                 to_be_continued = 1;
267                                 line[linelen-2] = '\0';
268                                 linelen -= 2;  
269                                 
270                                 if (!buff->used)
271                                         buffbegin = line_num;
272                         }
273                 }
274         
275                 if(buff->used || to_be_continued) 
276                 { 
277                         xbt_strbuff_append(buff,line);
278         
279                         if (!to_be_continued) 
280                         {
281                                 snprintf(file_pos,256,"%s:%d",file_name,buffbegin);
282                                 unit_handle_line(unit, context, mutex, file_pos, buff->data);    
283                                 xbt_strbuff_empty(buff);
284                         }
285                 } 
286                 else 
287                 {
288                         snprintf(file_pos,256,"%s:%d",file_name,line_num);
289                         unit_handle_line(unit, context, mutex, file_pos, line);      
290                 }
291         }
292         
293         /* Check that last command of the file ran well */
294         if(context->command_line)
295         {
296                 if(!want_dry_run)
297                 {
298                         command_t command = command_new(unit, context, mutex);
299                         command_run(command);
300                 }
301                 
302                 context_reset(context);
303         }
304
305         /* Clear buffers */
306         if (line)
307                 free(line);
308                 
309         xbt_strbuff_free(buff); 
310 }
311
312 void 
313 unit_handle_line(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char * filepos, char *line) 
314 {
315         /* Search end */
316         xbt_str_rtrim(line+2,"\n");
317         
318         switch (line[0]) 
319         {
320                 case '#': 
321                 break;
322                 
323                 case '$':
324                 case '&':
325                         
326                 context->async = (line[0] == '&');
327                 
328                 /* further trim useless chars which are significant for in/output */
329                 xbt_str_rtrim(line+2," \t");
330                 
331                 /* Deal with CD commands here, not in rctx */
332                 if (!strncmp("cd ",line+2,3)) 
333                 {
334                         char *dir=line+4;
335                 
336                         if (context->command_line)
337                         {
338                                 if(!want_dry_run)
339                                 {
340                                         command_t command = command_new(unit, context, mutex);
341                                         command_run(command);
342                                 }
343                                 
344                                 context_reset(context);
345                         }
346                 
347                         /* search begining */
348                         while (*(dir++) == ' ');
349                         
350                         dir--;
351                         
352                         VERB1("Saw cd '%s'",dir);
353                         
354                         /*if(want_dry_run)
355                         {
356                                 unit->number_of_successeded_commands++;
357                         }*/
358                         if(!want_dry_run)
359                         {
360                                 if(chdir(dir))
361                                 {
362                                         ERROR2("Chdir to %s failed: %s",dir,strerror(errno));
363                                         ERROR1("Test suite `%s': NOK (system error)", unit->fstream->name);
364                                         exit_code = ECHDIR;
365                                         unit_handle_failure(unit);
366                                 }
367                         }
368                         
369                         break;
370                 } /* else, pushline */
371                 else
372                 {
373                         unit_pushline(unit, context, mutex, filepos, line[0], line+2 /* pass '$ ' stuff*/);
374                         break;
375                 }
376                 
377                 case '<':
378                 case '>':
379                 case '!':
380                 unit_pushline(unit, context, mutex, filepos, line[0], line+2 /* pass '$ ' stuff*/);    
381                 break;
382                 
383                 case 'p':
384                 if(!want_dry_run)
385                         INFO2("[%s] %s",filepos,line+2);
386                 break;
387                 
388                 case 'P':
389                 if(!want_dry_run)
390                         CRITICAL2("[%s] %s",filepos,line+2);
391                 break;
392                 
393                 default:
394                 ERROR2("[%s] Syntax error: %s",filepos, line);
395                 ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name);
396                 exit_code = ESYNTAX;
397                 unit_handle_failure(unit);
398                 break;
399         }
400 }
401
402 void 
403 unit_pushline(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* filepos, char kind, char *line) 
404 {
405         switch (kind) 
406         {
407                 case '$':
408                 case '&':
409                 
410                 if(context->command_line) 
411                 {
412                         command_t command;
413                         
414                         if(context->output->used || context->input->used) 
415                         {
416                                 ERROR2("[%s] More than one command in this chunk of lines (previous: %s).\nDunno which input/output belongs to which command.",filepos,context->command_line);
417                                 ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name);
418                                 exit_code = ESYNTAX;
419                                 unit_handle_failure(unit);
420                                 return;
421                         }
422                         
423                         if(!want_dry_run)
424                         {
425                                 command = command_new(unit, context, mutex);
426                                 command_run(command);
427                         }
428                         
429                         context_reset(context);
430                         
431                         VERB1("[%s] More than one command in this chunk of lines",filepos);
432                 }
433                 
434                 context->command_line = strdup(line);
435                 
436                 
437                 context->line = strdup(filepos);
438                 /*INFO2("[%s] %s",filepos,context->command_line);*/
439                 
440                 break;
441                 
442                 case '<':
443                 xbt_strbuff_append(context->input,line);
444                 xbt_strbuff_append(context->input,"\n");
445                 break;
446                 
447                 case '>':
448                 xbt_strbuff_append(context->output,line);
449                 xbt_strbuff_append(context->output,"\n");
450                 break;
451                 
452                 case '!':
453                 
454                 if(context->command_line)
455                 {
456                         if(!want_dry_run)
457                         {
458                                 command_t command = command_new(unit, context, mutex);
459                                 command_run(command);
460                         }
461                         
462                         context_reset(context);
463                 }
464                 
465                 if(!strncmp(line,"timeout no",strlen("timeout no"))) 
466                 {
467                         VERB1("[%s] (disable timeout)", filepos);
468                         context->timeout = INDEFINITE;
469                 } 
470                 else if(!strncmp(line,"timeout ",strlen("timeout "))) 
471                 {
472                         int i = 0;
473                         char* p = line + strlen("timeout ");
474         
475                         while(p[i] != '\0')
476                         {
477                                 if(!isdigit(p[i]))
478                                 {
479                                         exit_code = ESYNTAX;
480                                         ERROR2("Invalid timeout value `%s' at %s ", line + strlen("timeout "), filepos);
481                                         unit_handle_failure(unit);
482                                 }
483
484                                 i++;
485                         }
486                         
487                         context->timeout = atoi(line + strlen("timeout"));
488                         VERB2("[%s] (new timeout value: %d)",filepos,context->timeout);
489                 
490                 } 
491                 else if (!strncmp(line,"expect signal ",strlen("expect signal "))) 
492                 {
493                         context->signal = strdup(line + strlen("expect signal "));
494                         
495                         #ifdef WIN32
496                         if(!strstr(context->signal,"SIGSEGVSIGTRAPSIGBUSSIGFPESIGILL"))
497                         {
498                                 exit_code = ESYNTAX;
499                                 /*ERROR2("Signal `%s' not supported at %s", line + strlen("expect signal "), filepos);*/
500                                 unit_handle_failure(unit);
501                         }
502                         #endif
503
504                         xbt_str_trim(context->signal," \n");
505                         VERB2("[%s] (next command must raise signal %s)", filepos, context->signal);
506                 
507                 } 
508                 else if (!strncmp(line,"expect return ",strlen("expect return "))) 
509                 {
510
511                         int i = 0;
512                         char* p = line + strlen("expect return ");
513                         
514                         
515                         while(p[i] != '\0')
516                         {
517                                 if(!isdigit(p[i]))
518                                 {
519                                         exit_code = ESYNTAX;
520                                         ERROR2("Invalid exit code value `%s' at %s ", line + strlen("expect return "), filepos);
521                                         unit_handle_failure(unit);
522                                 }
523
524                                 i++;
525                         }
526
527                         context->exit_code = atoi(line+strlen("expect return "));
528                         VERB2("[%s] (next command must return code %d)",filepos, context->exit_code);
529                 
530                 } 
531                 else if (!strncmp(line,"output ignore",strlen("output ignore"))) 
532                 {
533                         context->output_handling = oh_ignore;
534                         VERB1("[%s] (ignore output of next command)", filepos);
535                 
536                 } 
537                 else if (!strncmp(line,"output display",strlen("output display"))) 
538                 {
539                         context->output_handling = oh_display;
540                         VERB1("[%s] (ignore output of next command)", filepos);
541                 
542                 } 
543                 else if(!strncmp(line,"include", strlen("include")))
544                 {
545                         unit_handle_include(unit, context, mutex, line + strlen("include "));
546                 }
547                 
548                 else if(!strncmp(line,"suite", strlen("suite")))
549                 {
550                         unit_handle_suite(unit, context, mutex, line + strlen("suite "));
551                 }
552                 else 
553                 {
554                         ERROR2("%s: Malformed metacommand: %s",filepos,line);
555                         ERROR1("Test suite `%s': NOK (syntax error)",unit->fstream->name);
556                         exit_code = ESYNTAX;
557                         unit_handle_failure(unit);
558                         return;
559                 }
560                 
561                 break;
562         }
563 }
564
565
566 void
567 unit_handle_failure(unit_t unit)
568 {
569         if(!want_keep_going_unit)
570         {
571                 if(!unit->interrupted)
572                 {
573                         /* the unit interrupted (exit for the loop) */
574                         unit->interrupted = 1;
575
576                         /* release the unit */
577                         xbt_os_sem_release(unit->sem);
578                 }
579
580                 /* if the --keep-going option is not specified */
581                 if(!want_keep_going)
582                 {
583                         if(!interrupted)
584                         {
585                                 /* request an global interruption by the runner */
586                                 interrupted = 1;
587
588                                 /* release the runner */
589                                 xbt_os_sem_release(units_sem);
590                         }
591                 }
592         }
593 }
594
595 void
596 unit_run(unit_t unit, xbt_os_mutex_t mutex)
597 {
598         if(!interrupted)
599         {
600                 unit->mutex = mutex;
601                 
602                 unit->sem = xbt_os_sem_init(0);
603
604                 /* start the unit */
605                 unit->thread = xbt_os_thread_create("", unit_start, unit);
606         }
607         else
608                 /* the unit is interrupted by the runner before its starting 
609                  * in this case the unit semaphore is NULL take care of that
610                  * in the function unit_free()
611                  */
612                 unit->interrupted = 1;
613
614         
615 }
616
617 void
618 unit_interrupt(unit_t unit)
619 {
620         /* interrupt the loop */
621         unit->interrupted = 1;
622         xbt_os_sem_release(unit->sem);
623 }
624
625 void
626 unit_verbose(unit_t unit)
627 {
628         int i, test_count;
629         command_t command;
630
631         printf("\nUnit : %s (%s)\n", unit->fstream->name, unit->fstream->directory);
632         printf("Status informations :");
633
634         if(unit->parsed && unit->number_of_successeded_commands == unit->number_of_started_commands)
635                 printf(" - (success)"); 
636         else
637         {
638                 if(unit->interrupted)
639                         printf(" - (interruped)");
640                 else
641                         printf(" - (failed)");
642         }
643         
644         printf("\n");
645
646         printf("    number of commands               : %d\n", unit->number_of_commands);
647         printf("    number of runned commands        : %d\n", unit->number_of_started_commands);
648         printf("    number of successeded commands   : %d\n", unit->number_of_successeded_commands);
649         printf("    number of failed commands        : %d\n", unit->number_of_failed_commands);
650         printf("    number of interrupted commands   : %d\n", unit->number_of_interrupted_commands);
651         
652         test_count = unit->number_of_commands;
653         
654         
655         for(i = 0; i < test_count; i++)
656         {
657                 command = vector_get_at(unit->commands, i);
658                 
659                 /*command_display_status(unit->commands[i]);*/
660                 command_display_status(command);
661         }
662
663
664 }
665
666 void
667 unit_handle_include(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* file_name)
668 {
669         directory_t include;
670         char* prev_directory = NULL;
671         fstream_t fstream = NULL;
672         struct stat buffer = {0};
673         
674         
675         if(!stat(file_name, &buffer) && S_ISREG(buffer.st_mode))
676         {
677                 INFO1("the file stream %s is in the current directory", file_name);
678                 
679                 fstream = fstream_new(getcwd(NULL, 0), file_name);
680                 fstream_open(fstream);
681         }
682         /* the file to include is not in the current directory, check if it is in a include directory */
683         else
684         {
685                 prev_directory = getcwd(NULL, 0);
686                 
687                 vector_rewind(includes);
688                 
689                 while((include = vector_get(includes)))
690                 {
691                         chdir(include->name);
692                         
693                         if(!stat(file_name, &buffer) && S_ISREG(buffer.st_mode))
694                         {
695                                 fstream = fstream_new(include->name, file_name);
696                                 fstream_open(fstream);
697                                 break;
698                         }
699                         
700                         
701                         vector_move_next(includes);
702                 }
703                 
704                 chdir(prev_directory);
705                 free(prev_directory);
706         }
707         
708         
709         
710         /* the file to include is not found handle the failure */
711         if(!fstream)
712         {
713                 exit_code = EINCLUDENOTFOUND;
714                 ERROR1("Include file %s not found",file_name);
715                 unit_handle_failure(unit);
716         }
717         else
718         {
719                 /* parse the include file */
720                 
721                 /*unit_t __unit = unit_new(unit->runner, NULL, fstream);*/
722                 unit->parsing_include_file = 1;
723                 
724                 /* add the unit to the list of the runned units */
725                 
726                 /*xbt_os_mutex_acquire(unit->mutex);
727                 vector_push_back(__unit->runner->units->items, __unit);
728                 xbt_os_mutex_release(unit->mutex);*/
729                 
730                 
731                 if(want_dry_run)
732                         INFO2("checking unit %s including in %s...",fstream->name, unit->fstream->name); 
733                 
734                 unit_parse(unit, context_new(), mutex, fstream->name, fstream->stream);
735                 
736                 fstream_free((void**)&fstream);
737                 unit->parsing_include_file = 0;
738         }
739 }
740
741 void
742 unit_handle_suite(unit_t unit, context_t context, xbt_os_mutex_t mutex, const char* description)
743 {
744         suite_t suite = suite_new(unit, description);
745         unit_add_suite(unit, suite);
746         unit->running_suite = 1;
747 }
748
749 int
750 unit_reset(unit_t unit)
751 {
752         fseek(unit->fstream->stream,0L, SEEK_SET);
753         unit->parsed = 0;
754         unit->number_of_commands = 0;
755         
756         return 0;
757 }