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 */
18 #include <sys/types.h>
24 int timeout_value = 5; /* child timeout value */
25 char* expected_signal=NULL; /* !=NULL if the following command should raise a signal */
26 int expected_return=0; /* the exepeted return code of following command */
27 int verbose=0; /* wheather we should wine on problems */
30 ** Dealing with timeouts
33 static void timeout_handler(int sig) {
37 ** Dealing with timeouts
40 static void pipe_handler(int sig) {
48 buff_t *output_wanted;
51 static void check_output() {
52 if (output_wanted->used==0
53 && output_got->used==0)
55 buff_chomp(output_got);
56 buff_chomp(output_wanted);
57 buff_trim(output_got);
58 buff_trim(output_wanted);
60 if ( output_got->used != output_wanted->used
61 || strcmp(output_got->data, output_wanted->data)) {
62 fprintf(stderr,"Output don't match expectations\n");
63 fprintf(stderr,">>>>> Expected %d chars:\n%s\n<<<<< Expected\n",
64 output_wanted->used,output_wanted->data);
65 fprintf(stderr,">>>>> Got %d chars:\n%s\n<<<<< Got\n",
66 output_got->used,output_got->data);
69 buff_empty(output_wanted);
70 buff_empty(output_got);
74 static void exec_cmd(char *cmd) {
78 if (pipe(child_stdin) || pipe(child_stdout)) {
79 perror("Cannot open the pipes");
85 perror("Cannot fork the command");
89 if (pid) { /* father */
93 close(child_stdin[0]);
94 fcntl(child_stdin[1], F_SETFL, O_NONBLOCK);
95 close(child_stdout[1]);
96 fcntl(child_stdout[0], F_SETFL, O_NONBLOCK);
99 for (posw=0; posw<input->used && !brokenpipe; ) {
101 // fprintf(stderr,"Still %d chars to write\n",input->used-posw);
102 got=write(child_stdin[1],input->data+posw,input->used-posw);
105 if (got<0 && errno!=EINTR && errno!=EAGAIN && errno!=EPIPE) {
106 perror("Error while writing input to child");
109 // fprintf(stderr,"written %d chars so far\n",posw);
111 posr=read(child_stdout[0],&buffout,4096);
112 // fprintf(stderr,"got %d chars\n",posr);
113 if (posr<0 && errno!=EINTR && errno!=EAGAIN) {
114 perror("Error while reading output of child");
119 buff_append(output_got,buffout);
122 if (got <= 0 && posr <= 0)
127 close(child_stdin[1]);
130 alarm(timeout_value);
132 posr=read(child_stdout[0],&buffout,4096);
133 if (posr<0 && errno!=EINTR && errno!=EAGAIN) {
134 perror("Error while reading output of child");
139 buff_append(output_got,buffout);
143 } while (!timeouted && posr!=0);
145 /* Check for broken pipe */
146 if (brokenpipe && verbose) {
147 fprintf(stderr,"Warning: Child did not consume all its input (I got broken pipe)\n");
150 /* Check for timeouts */
152 fprintf(stderr,"Child timeouted (waited %d sec)\n",timeout_value);
158 /* Wait for child, and check why it terminated */
161 if (WIFSIGNALED(status) &&
162 strcmp(signal_name(WTERMSIG(status),expected_signal),
164 fprintf(stderr,"Child got signal %s instead of signal %s\n",
165 signal_name(WTERMSIG(status),expected_signal),
167 exit(WTERMSIG(status)+4);
170 if (!WIFSIGNALED(status) && expected_signal) {
171 fprintf(stderr,"Child didn't got expected signal %s\n",
176 if (WIFEXITED(status) && WEXITSTATUS(status) != expected_return ) {
178 fprintf(stderr,"Child returned code %d instead of %d\n",
179 WEXITSTATUS(status), expected_return);
181 fprintf(stderr,"Child returned code %d\n", WEXITSTATUS(status));
182 exit(40+WEXITSTATUS(status));
187 free(expected_signal);
188 expected_signal = NULL;
193 close(child_stdin[1]);
194 close(child_stdout[0]);
195 dup2(child_stdin[0],0);
196 close(child_stdin[0]);
197 dup2(child_stdout[1],1);
198 dup2(child_stdout[1],2);
199 close(child_stdout[1]);
201 execlp ("/bin/sh", "sh", "-c", cmd, NULL);
205 static void run_cmd(char *cmd) {
206 if (cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' ') {
210 while (cmd[pos] == '\n' || cmd[pos] == ' ' || cmd[pos] == '\t')
212 /* search begining */
214 while (cmd[pos++] == ' ');
216 // fprintf(stderr,"Saw cd '%s'\n",cmd+pos);
217 if (chdir(cmd+pos)) {
218 perror("Chdir failed");
227 static void handle_line(int nl, char *line) {
229 // printf("%d: %s",nl,line); fflush(stdout);
233 check_output(); /* Check that last command ran well */
235 printf("[%d] %s",nl,line);
241 buff_append(input,line+2);
245 buff_append(output_wanted,line+2);
249 if (!strncmp(line+2,"set timeout ",strlen("set timeout "))) {
250 timeout_value=atoi(line+2+strlen("set timeout"));
251 printf("[%d] (new timeout value: %d)\n",
254 } else if (!strncmp(line+2,"expect signal ",strlen("expect signal "))) {
255 expected_signal = strdup(line+2 + strlen("expect signal "));
256 xbt_str_trim(expected_signal," \n");
257 printf("[%d] (next command must raise signal %s)\n", nl, expected_signal);
259 } else if (!strncmp(line+2,"expect return ",strlen("expect return "))) {
260 expected_return = atoi(line+2+strlen("expect return "));
261 printf("[%d] (next command must return code %d)\n",
262 nl, expected_return);
264 } else if (!strncmp(line+2,"verbose on",strlen("verbose on"))) {
266 printf("[%d] (increase verbosity)\n", nl);
268 } else if (!strncmp(line+2,"verbose off",strlen("verbose off"))) {
270 printf("[%d] (decrease verbosity)\n", nl);
273 fprintf(stderr,"%d: Malformed metacommand: %s",nl,line);
279 printf("[%d] %s",nl,line+2);
283 fprintf(stderr,"Syntax error line %d: %s",nl, line);
289 static int handle_suite(FILE* IN) {
294 buff_t *buff=buff_new();
297 while (getline(&line,(size_t*) &len, IN) != -1) {
300 /* Count the line length while checking wheather it's blank */
303 while (line[linelen] != '\0') {
304 if (line[linelen] != ' ' && line[linelen] != '\t' && line[linelen]!='\n')
312 /* Deal with \ at the end of the line, and call handle_line on result */
313 int to_be_continued = 0;
314 if (linelen>1 && line[linelen-2]=='\\') {
315 if (linelen>2 && line[linelen-3] == '\\') {
316 /* Damn. Escaped \ */
317 line[linelen-2] = '\n';
318 line[linelen-1] = '\0';
321 line[linelen-2] = '\0';
324 buffbegin = line_num;
328 if (buff->used || to_be_continued) {
329 buff_append(buff,line);
331 if (!to_be_continued) {
332 handle_line(buffbegin, buff->data);
337 handle_line(line_num,line);
340 check_output(); /* Check that last command ran well */
349 int main(int argc,char *argv[]) {
351 /* Setup the signal handlers */
352 struct sigaction newact,oldact;
353 memset(&newact,0,sizeof(newact));
354 newact.sa_handler=timeout_handler;
355 sigaction(SIGALRM,&newact,&oldact);
357 newact.sa_handler=pipe_handler;
358 sigaction(SIGPIPE,&newact,&oldact);
360 /* Setup the input/output buffers */
362 output_wanted=buff_new();
363 output_got=buff_new();
365 /* Find the description file */
369 printf("Test suite from stdin\n");fflush(stdout);
371 fprintf(stderr,"Test suite from stdin OK\n");
376 for (i=1; i<argc; i++) {
377 printf("Test suite `%s'\n",argv[i]);fflush(stdout);
378 IN=fopen(argv[i], "r");
380 perror(bprintf("Impossible to open the suite file `%s'",argv[i]));
384 // fclose(IN); ->leads to segfault on amd64...
385 fprintf(stderr,"Test suite `%s' OK\n",argv[i]);
390 buff_free(output_wanted);
391 buff_free(output_got);