3 /* TESH (Test Shell) -- mini shell specialized in running test units */
5 /* Copyright (c) 2007 Martin Quinson. */
6 /* All rights reserved. */
8 /* This program is free software; you can redistribute it and/or modify it
9 * under the terms of the license (GNU LGPL) which comes with this package. */
11 /* specific to Borland Compiler */
17 #include "xbt/sysdep.h"
18 #include "xbt/function_types.h"
22 #include <sys/types.h>
33 static void buff_empty(buff_t *b) {
38 static buff_t *buff_new(void) {
39 buff_t *res=malloc(sizeof(buff_t));
40 res->data=malloc(512);
45 static void buff_free(buff_t *b) {
52 static void buff_append(buff_t *b, char *toadd) {
53 int addlen=strlen(toadd);
54 int needed_space=b->used+addlen+1;
56 if (needed_space > b->size) {
57 b->data = realloc(b->data, needed_space);
58 b->size = needed_space;
60 strcpy(b->data+b->used, toadd);
63 static void buff_chomp(buff_t *b) {
64 while (b->data[b->used] == '\n') {
65 b->data[b->used] = '\0';
71 static void buff_trim(buff_t* b)
74 b->used = strlen(b->data);
77 typedef struct s_signal_entry
81 }s_signal_entry_t,* signal_entry_t;
83 static const s_signal_entry_t signals[] =
119 #define SIGUNKNW SIGMAX + 1
121 /* returns the name of the signal from it number */
123 signal_name(unsigned int number);
128 int timeout_value = 5; /* child timeout value */
129 char* expected_signal=NULL; /* !=NULL if the following command should raise a signal */
130 int expected_return=0; /* the exepeted return code of following command */
131 int verbose=0; /* wheather we should wine on problems */
134 ** Dealing with timeouts
137 static void timeout_handler(int sig) {
141 ** Dealing with timeouts
144 static void pipe_handler(int sig) {
152 buff_t *output_wanted;
155 static void check_output() {
156 if (output_wanted->used==0
157 && output_got->used==0)
159 buff_chomp(output_got);
160 buff_chomp(output_wanted);
161 buff_trim(output_got);
162 buff_trim(output_wanted);
164 if ( output_got->used != output_wanted->used
165 || strcmp(output_got->data, output_wanted->data)) {
166 fprintf(stderr,"Output don't match expectations\n");
167 fprintf(stderr,">>>>> Expected %d chars:\n%s\n<<<<< Expected\n",
168 output_wanted->used,output_wanted->data);
169 fprintf(stderr,">>>>> Got %d chars:\n%s\n<<<<< Got\n",
170 output_got->used,output_got->data);
173 buff_empty(output_wanted);
174 buff_empty(output_got);
178 static void exec_cmd(char *cmd) {
182 if (pipe(child_stdin) || pipe(child_stdout)) {
183 perror("Cannot open the pipes");
189 perror("Cannot fork the command");
193 if (pid) { /* father */
197 close(child_stdin[0]);
198 fcntl(child_stdin[1], F_SETFL, O_NONBLOCK);
199 close(child_stdout[1]);
200 fcntl(child_stdout[0], F_SETFL, O_NONBLOCK);
203 for (posw=0; posw<input->used && !brokenpipe; ) {
205 // fprintf(stderr,"Still %d chars to write\n",input->used-posw);
206 got=write(child_stdin[1],input->data+posw,input->used-posw);
209 if (got<0 && errno!=EINTR && errno!=EAGAIN && errno!=EPIPE) {
210 perror("Error while writing input to child");
213 // fprintf(stderr,"written %d chars so far\n",posw);
215 posr=read(child_stdout[0],&buffout,4096);
216 // fprintf(stderr,"got %d chars\n",posr);
217 if (posr<0 && errno!=EINTR && errno!=EAGAIN) {
218 perror("Error while reading output of child");
223 buff_append(output_got,buffout);
226 if (got <= 0 && posr <= 0)
231 close(child_stdin[1]);
234 alarm(timeout_value);
236 posr=read(child_stdout[0],&buffout,4096);
237 if (posr<0 && errno!=EINTR && errno!=EAGAIN) {
238 perror("Error while reading output of child");
243 buff_append(output_got,buffout);
247 } while (!timeouted && posr!=0);
249 /* Check for broken pipe */
250 if (brokenpipe && verbose) {
251 fprintf(stderr,"Warning: Child did not consume all its input (I got broken pipe)\n");
254 /* Check for timeouts */
256 fprintf(stderr,"Child timeouted (waited %d sec)\n",timeout_value);
262 /* Wait for child, and check why it terminated */
265 if (WIFSIGNALED(status) && strcmp(signal_name(WTERMSIG(status)),expected_signal)) {
266 fprintf(stderr,"Child got signal %s instead of signal %s\n",signal_name(WTERMSIG(status)), expected_signal);
267 exit(WTERMSIG(status)+4);
270 if (!WIFSIGNALED(status) && expected_signal) {
271 fprintf(stderr,"Child didn't got expected signal %s\n",
276 if (WIFEXITED(status) && WEXITSTATUS(status) != expected_return ) {
278 fprintf(stderr,"Child returned code %d instead of %d\n",
279 WEXITSTATUS(status), expected_return);
281 fprintf(stderr,"Child returned code %d\n", WEXITSTATUS(status));
282 exit(40+WEXITSTATUS(status));
287 free(expected_signal);
288 expected_signal = NULL;
293 close(child_stdin[1]);
294 close(child_stdout[0]);
295 dup2(child_stdin[0],0);
296 close(child_stdin[0]);
297 dup2(child_stdout[1],1);
298 dup2(child_stdout[1],2);
299 close(child_stdout[1]);
301 execlp ("/bin/sh", "sh", "-c", cmd, NULL);
305 static void run_cmd(char *cmd) {
306 if (cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' ') {
310 while (cmd[pos] == '\n' || cmd[pos] == ' ' || cmd[pos] == '\t')
312 /* search begining */
314 while (cmd[pos++] == ' ');
316 // fprintf(stderr,"Saw cd '%s'\n",cmd+pos);
317 if (chdir(cmd+pos)) {
318 perror("Chdir failed");
327 static void handle_line(int nl, char *line) {
329 // printf("%d: %s",nl,line); fflush(stdout);
333 check_output(); /* Check that last command ran well */
335 printf("[%d] %s",nl,line);
341 buff_append(input,line+2);
345 buff_append(output_wanted,line+2);
349 if (!strncmp(line+2,"set timeout ",strlen("set timeout "))) {
350 timeout_value=atoi(line+2+strlen("set timeout"));
351 printf("[%d] (new timeout value: %d)\n",
354 } else if (!strncmp(line+2,"expect signal ",strlen("expect signal "))) {
355 expected_signal = strdup(line+2 + strlen("expect signal "));
356 trim(expected_signal," \n");
357 printf("[%d] (next command must raise signal %s)\n", nl, expected_signal);
359 } else if (!strncmp(line+2,"expect return ",strlen("expect return "))) {
360 expected_return = atoi(line+2+strlen("expect return "));
361 printf("[%d] (next command must return code %d)\n",
362 nl, expected_return);
364 } else if (!strncmp(line+2,"verbose on",strlen("verbose on"))) {
366 printf("[%d] (increase verbosity)\n", nl);
368 } else if (!strncmp(line+2,"verbose off",strlen("verbose off"))) {
370 printf("[%d] (decrease verbosity)\n", nl);
373 fprintf(stderr,"%d: Malformed metacommand: %s",nl,line);
379 printf("[%d] %s",nl,line+2);
383 fprintf(stderr,"Syntax error line %d: %s",nl, line);
389 static int handle_suite(FILE* IN) {
394 buff_t *buff=buff_new();
397 while (getline(&line,(size_t*) &len, IN) != -1) {
400 /* Count the line length while checking wheather it's blank */
403 while (line[linelen] != '\0') {
404 if (line[linelen] != ' ' && line[linelen] != '\t' && line[linelen]!='\n')
412 /* Deal with \ at the end of the line, and call handle_line on result */
413 int to_be_continued = 0;
414 if (linelen>1 && line[linelen-2]=='\\') {
415 if (linelen>2 && line[linelen-3] == '\\') {
416 /* Damn. Escaped \ */
417 line[linelen-2] = '\n';
418 line[linelen-1] = '\0';
421 line[linelen-2] = '\0';
424 buffbegin = line_num;
428 if (buff->used || to_be_continued) {
429 buff_append(buff,line);
431 if (!to_be_continued) {
432 handle_line(buffbegin, buff->data);
437 handle_line(line_num,line);
440 check_output(); /* Check that last command ran well */
449 int main(int argc,char *argv[]) {
451 /* Setup the signal handlers */
452 struct sigaction newact,oldact;
453 memset(&newact,0,sizeof(newact));
454 newact.sa_handler=timeout_handler;
455 sigaction(SIGALRM,&newact,&oldact);
457 newact.sa_handler=pipe_handler;
458 sigaction(SIGPIPE,&newact,&oldact);
460 /* Setup the input/output buffers */
462 output_wanted=buff_new();
463 output_got=buff_new();
465 /* Find the description file */
469 printf("Test suite from stdin\n");fflush(stdout);
471 fprintf(stderr,"Test suite from stdin OK\n");
476 for (i=1; i<argc; i++) {
477 printf("Test suite `%s'\n",argv[i]);fflush(stdout);
478 IN=fopen(argv[i], "r");
480 perror(bprintf("Impossible to open the suite file `%s'",argv[i]));
484 // fclose(IN); ->leads to segfault on amd64...
485 fprintf(stderr,"Test suite `%s' OK\n",argv[i]);
490 buff_free(output_wanted);
491 buff_free(output_got);
496 signal_name(unsigned int number)
501 /* special case of SIGSEGV and SIGBUS (be conditional of the implementation)*/
502 if((number == SIGBUS) && strcmp("SIGBUS",expected_signal))
505 return (signals[number - 1].name);