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"
21 #include <sys/types.h>
32 static void buff_empty(buff_t *b) {
37 static buff_t *buff_new(void) {
38 buff_t *res=malloc(sizeof(buff_t));
39 res->data=malloc(512);
44 static void buff_free(buff_t *b) {
51 static void buff_append(buff_t *b, char *toadd) {
52 int addlen=strlen(toadd);
53 int needed_space=b->used+addlen+1;
55 if (needed_space > b->size) {
56 b->data = realloc(b->data, needed_space);
57 b->size = needed_space;
59 strcpy(b->data+b->used, toadd);
62 static void buff_chomp(buff_t *b) {
63 while (b->data[b->used] == '\n') {
64 b->data[b->used] = '\0';
72 int timeout_value = 5; /* child timeout value */
73 int expected_signal=0; /* !=0 if the following command should raise a signal */
74 int expected_return=0; /* the exepeted return code of following command */
75 int verbose=0; /* wheather we should wine on problems */
78 ** Dealing with timeouts
81 static void timeout_handler(int sig) {
85 ** Dealing with timeouts
88 static void pipe_handler(int sig) {
96 buff_t *output_wanted;
99 static void check_output() {
100 if (output_wanted->used==0
101 && output_got->used==0)
103 buff_chomp(output_got);
104 buff_chomp(output_wanted);
106 if ( output_got->used != output_wanted->used
107 || strcmp(output_got->data, output_wanted->data)) {
108 fprintf(stderr,"Output don't match expectations\n");
109 fprintf(stderr,">>>>> Expected %d chars:\n%s\n<<<<< Expected\n",
110 output_wanted->used,output_wanted->data);
111 fprintf(stderr,">>>>> Got %d chars:\n%s\n<<<<< Got\n",
112 output_got->used,output_got->data);
115 buff_empty(output_wanted);
116 buff_empty(output_got);
120 static void exec_cmd(char *cmd) {
124 if (pipe(child_stdin) || pipe(child_stdout)) {
125 perror("Cannot open the pipes");
131 perror("Cannot fork the command");
135 if (pid) { /* father */
139 close(child_stdin[0]);
140 fcntl(child_stdin[1], F_SETFL, O_NONBLOCK);
141 close(child_stdout[1]);
142 fcntl(child_stdout[0], F_SETFL, O_NONBLOCK);
145 for (posw=0; posw<input->used && !brokenpipe; ) {
147 // fprintf(stderr,"Still %d chars to write\n",input->used-posw);
148 got=write(child_stdin[1],input->data+posw,input->used-posw);
151 if (got<0 && errno!=EINTR && errno!=EAGAIN && errno!=EPIPE) {
152 perror("Error while writing input to child");
155 // fprintf(stderr,"written %d chars so far\n",posw);
157 posr=read(child_stdout[0],&buffout,4096);
158 // fprintf(stderr,"got %d chars\n",posr);
159 if (posr<0 && errno!=EINTR && errno!=EAGAIN) {
160 perror("Error while reading output of child");
165 buff_append(output_got,buffout);
168 if (got <= 0 && posr <= 0)
173 close(child_stdin[1]);
176 alarm(timeout_value);
178 posr=read(child_stdout[0],&buffout,4096);
179 if (posr<0 && errno!=EINTR && errno!=EAGAIN) {
180 perror("Error while reading output of child");
185 buff_append(output_got,buffout);
189 } while (!timeouted && posr!=0);
191 /* Check for broken pipe */
192 if (brokenpipe && verbose) {
193 fprintf(stderr,"Warning: Child did not consume all its input (I got broken pipe)\n");
196 /* Check for timeouts */
198 fprintf(stderr,"Child timeouted (waited %d sec)\n",timeout_value);
204 /* Wait for child, and check why it terminated */
207 if (WIFSIGNALED(status) && WTERMSIG(status) != expected_signal) {
208 fprintf(stderr,"Child got signal %d instead of signal %d\n",
209 WTERMSIG(status), expected_signal);
210 exit(WTERMSIG(status)+4);
212 if (!WIFSIGNALED(status) && expected_signal != 0) {
213 fprintf(stderr,"Child didn't got expected signal %d\n",
218 if (WIFEXITED(status) && WEXITSTATUS(status) != expected_return ) {
220 fprintf(stderr,"Child returned code %d instead of %d\n",
221 WEXITSTATUS(status), expected_return);
223 fprintf(stderr,"Child returned code %d\n", WEXITSTATUS(status));
224 exit(40+WEXITSTATUS(status));
226 expected_return = expected_signal = 0;
230 close(child_stdin[1]);
231 close(child_stdout[0]);
232 dup2(child_stdin[0],0);
233 close(child_stdin[0]);
234 dup2(child_stdout[1],1);
235 dup2(child_stdout[1],2);
236 close(child_stdout[1]);
238 execlp ("/bin/sh", "sh", "-c", cmd, NULL);
242 static void run_cmd(char *cmd) {
243 if (cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' ') {
247 while (cmd[pos] == '\n' || cmd[pos] == ' ' || cmd[pos] == '\t')
249 /* search begining */
251 while (cmd[pos++] == ' ');
253 // fprintf(stderr,"Saw cd '%s'\n",cmd+pos);
254 if (chdir(cmd+pos)) {
255 perror("Chdir failed");
264 static void handle_line(int nl, char *line) {
266 // printf("%d: %s",nl,line); fflush(stdout);
270 check_output(); /* Check that last command ran well */
272 printf("[%d] %s",nl,line);
278 buff_append(input,line+2);
282 buff_append(output_wanted,line+2);
286 if (!strncmp(line+2,"set timeout ",strlen("set timeout "))) {
287 timeout_value=atoi(line+2+strlen("set timeout"));
288 printf("[%d] (new timeout value: %d)\n",
291 } else if (!strncmp(line+2,"expect signal ",strlen("expect signal "))) {
292 expected_signal = atoi(line+2+strlen("expect signal "));
293 printf("[%d] (next command must raise signal %d)\n",
294 nl, expected_signal);
296 } else if (!strncmp(line+2,"expect return ",strlen("expect return "))) {
297 expected_return = atoi(line+2+strlen("expect return "));
298 printf("[%d] (next command must return code %d)\n",
299 nl, expected_return);
301 } else if (!strncmp(line+2,"verbose on",strlen("verbose on"))) {
303 printf("[%d] (increase verbosity)\n", nl);
305 } else if (!strncmp(line+2,"verbose off",strlen("verbose off"))) {
307 printf("[%d] (decrease verbosity)\n", nl);
310 fprintf(stderr,"%d: Malformed metacommand: %s",nl,line);
316 printf("[%d] %s",nl,line+2);
320 fprintf(stderr,"Syntax error line %d: %s",nl, line);
326 static int handle_suite(FILE* IN) {
331 buff_t *buff=buff_new();
334 while (getline(&line,(size_t*) &len, IN) != -1) {
337 /* Count the line length while checking wheather it's blank */
340 while (line[linelen] != '\0') {
341 if (line[linelen] != ' ' && line[linelen] != '\t' && line[linelen]!='\n')
349 /* Deal with \ at the end of the line, and call handle_line on result */
350 int to_be_continued = 0;
351 if (linelen>1 && line[linelen-2]=='\\') {
352 if (linelen>2 && line[linelen-3] == '\\') {
353 /* Damn. Escaped \ */
354 line[linelen-2] = '\n';
355 line[linelen-1] = '\0';
358 line[linelen-2] = '\0';
361 buffbegin = line_num;
365 if (buff->used || to_be_continued) {
366 buff_append(buff,line);
368 if (!to_be_continued) {
369 handle_line(buffbegin, buff->data);
374 handle_line(line_num,line);
377 check_output(); /* Check that last command ran well */
386 int main(int argc,char *argv[]) {
388 /* Setup the signal handlers */
389 struct sigaction newact,oldact;
390 memset(&newact,0,sizeof(newact));
391 newact.sa_handler=timeout_handler;
392 sigaction(SIGALRM,&newact,&oldact);
394 newact.sa_handler=pipe_handler;
395 sigaction(SIGPIPE,&newact,&oldact);
397 /* Setup the input/output buffers */
399 output_wanted=buff_new();
400 output_got=buff_new();
402 /* Find the description file */
406 printf("Test suite from stdin\n");fflush(stdout);
408 fprintf(stderr,"Test suite from stdin OK\n");
413 for (i=1; i<argc; i++) {
414 printf("Test suite `%s'\n",argv[i]);fflush(stdout);
415 IN=fopen(argv[i], "r");
417 perror(bprintf("Impossible to open the suite file `%s'",argv[i]));
422 fprintf(stderr,"Test suite `%s' OK\n",argv[i]);
427 buff_free(output_wanted);
428 buff_free(output_got);