Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
15c0053770f240e23e0d839e723f7e79a360d073
[simgrid.git] / tools / tesh / tesh.c
1 /* TESH (Test Shell) -- mini shell specialized in running test units        */
2
3 /* Copyright (c) 2007, 2008, 2009, 2010. The SimGrid Team.
4  * All rights reserved.                                                     */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package. */
8
9 /* specific to Borland Compiler */
10 #ifdef __BORLANDDC__
11 #pragma hdrstop
12 #endif
13
14 #include "tesh.h"
15 #include "xbt.h"
16
17 XBT_LOG_NEW_DEFAULT_CATEGORY(tesh, "TEst SHell utility");
18
19 /*** Options ***/
20 int timeout_value = 5;          /* child timeout value */
21
22 char *testsuite_name;
23 static void handle_line(const char *filepos, char *line)
24 {
25   /* Search end */
26   xbt_str_rtrim(line + 2, "\n");
27
28   /*
29      DEBUG7("rctx={%s,in={%d,>>%10s<<},exp={%d,>>%10s<<},got={%d,>>%10s<<}}",
30      rctx->cmd,
31      rctx->input->used,        rctx->input->data,
32      rctx->output_wanted->used,rctx->output_wanted->data,
33      rctx->output_got->used,   rctx->output_got->data);
34    */
35   DEBUG2("[%s] %s", filepos, line);
36
37   switch (line[0]) {
38   case '#':
39     break;
40
41   case '$':
42     /* further trim useless chars which are significant for in/output */
43     xbt_str_rtrim(line + 2, " \t");
44
45     /* Deal with CD commands here, not in rctx */
46     if (!strncmp("cd ", line + 2, 3)) {
47       char *dir = line + 4;
48
49       if (rctx->cmd)
50         rctx_start();
51
52       /* search beginning */
53       while (*(dir++) == ' ');
54       dir--;
55       VERB1("Saw cd '%s'", dir);
56       if (chdir(dir)) {
57         ERROR2("Chdir to %s failed: %s", dir, strerror(errno));
58         ERROR1("Test suite `%s': NOK (system error)", testsuite_name);
59         rctx_armageddon(rctx, 4);
60       }
61       break;
62     }                           /* else, pushline */
63   case '&':
64   case '<':
65   case '>':
66   case '!':
67     rctx_pushline(filepos, line[0], line + 2 /* pass '$ ' stuff */ );
68     break;
69
70   case 'p':
71     INFO2("[%s] %s", filepos, line + 2);
72     break;
73   case 'P':
74     CRITICAL2("[%s] %s", filepos, line + 2);
75     break;
76
77   default:
78     ERROR2("[%s] Syntax error: %s", filepos, line);
79     ERROR1("Test suite `%s': NOK (syntax error)", testsuite_name);
80     rctx_armageddon(rctx, 1);
81     break;
82   }
83 }
84
85 static void handle_suite(const char *filename, FILE * IN)
86 {
87   size_t len;
88   char *line = NULL;
89   int line_num = 0;
90   char file_pos[256];
91
92   xbt_strbuff_t buff = xbt_strbuff_new();
93   int buffbegin = 0;
94
95   rctx = rctx_new();
96
97   while (getline(&line, &len, IN) != -1) {
98     line_num++;
99
100     /* Count the line length while checking wheather it's blank */
101     int blankline = 1;
102     int linelen = 0;
103     while (line[linelen] != '\0') {
104       if (line[linelen] != ' ' && line[linelen] != '\t'
105           && line[linelen] != '\n')
106         blankline = 0;
107       linelen++;
108     }
109
110     if (blankline) {
111       if (!rctx->cmd && !rctx->is_empty) {
112         ERROR1("[%d] Error: no command found in this chunk of lines.",
113                buffbegin);
114         ERROR1("Test suite `%s': NOK (syntax error)", testsuite_name);
115         rctx_armageddon(rctx, 1);
116       }
117       if (rctx->cmd)
118         rctx_start();
119
120       continue;
121     }
122
123     /* Deal with \ at the end of the line, and call handle_line on result */
124     int to_be_continued = 0;
125     if (linelen > 1 && line[linelen - 2] == '\\') {
126       if (linelen > 2 && line[linelen - 3] == '\\') {
127         /* Damn. Escaped \ */
128         line[linelen - 2] = '\n';
129         line[linelen - 1] = '\0';
130       } else {
131         to_be_continued = 1;
132         line[linelen - 2] = '\0';
133         linelen -= 2;
134         if (!buff->used)
135           buffbegin = line_num;
136       }
137     }
138
139     if (buff->used || to_be_continued) {
140       xbt_strbuff_append(buff, line);
141
142       if (!to_be_continued) {
143         snprintf(file_pos, 256, "%s:%d", filename, buffbegin);
144         handle_line(file_pos, buff->data);
145         xbt_strbuff_empty(buff);
146       }
147
148     } else {
149       snprintf(file_pos, 256, "%s:%d", filename, line_num);
150       handle_line(file_pos, line);
151     }
152   }
153   /* Check that last command of the file ran well */
154   if (rctx->cmd)
155     rctx_start();
156
157   /* Wait all background commands */
158
159   rctx_free(rctx);
160
161   /* Clear buffers */
162   if (line)
163     free(line);
164   xbt_strbuff_free(buff);
165
166 }
167
168 static void parse_environ()
169 {
170   char *p;
171   int i;
172   env = xbt_dict_new();
173   for (i = 0; environ[i]; i++) {
174     p = environ[i];
175     char *eq = strchr(p, '=');
176     char *key = bprintf("%.*s", (int) (eq - p), p);
177     xbt_dict_set(env, key, xbt_strdup(eq + 1), xbt_free_f);
178     free(key);
179   }
180 }
181
182 int main(int argc, char *argv[])
183 {
184
185   FILE *IN;
186   int i;
187
188   /* Ignore pipe issues.
189      They will show up when we try to send data to dead buddies,
190      but we will stop doing so when we're done with provided input */
191   struct sigaction newact;
192   memset(&newact, 0, sizeof(newact));
193   newact.sa_handler = SIG_IGN;
194   sigaction(SIGPIPE, &newact, NULL);
195
196   xbt_init(&argc, argv);
197   rctx_init();
198   parse_environ();
199
200   /* Get args */
201   for (i = 1; i < argc; i++) {
202     if (!strcmp(argv[i], "--cd")) {
203       if (i == argc - 1) {
204         ERROR0("--cd argument requires an argument");
205         exit(1);
206       }
207       if (chdir(argv[i + 1])) {
208         ERROR2("Cannot change directory to %s: %s", argv[i + 1],
209                strerror(errno));
210         exit(1);
211       }
212       INFO1("Change directory to %s", argv[i + 1]);
213       memmove(argv + i, argv + i + 2, (argc - i - 1)*sizeof(char*));
214       argc -= 2;
215       i -= 2;
216     }
217   }
218
219   /* Find the description file */
220   if (argc == 1) {
221     INFO0("Test suite from stdin");
222     testsuite_name = xbt_strdup("(stdin)");
223     handle_suite("stdin", stdin);
224     INFO0("Test suite from stdin OK");
225
226   } else {
227     for (i = 1; i < argc; i++) {
228       char *suitename = xbt_strdup(argv[i]);
229       if (!strcmp("./", suitename))
230         memmove(suitename, suitename + 2, strlen(suitename + 2));
231
232       if (!strcmp(".tesh", suitename + strlen(suitename) - 5))
233         suitename[strlen(suitename) - 5] = '\0';
234
235       INFO1("Test suite `%s'", suitename);
236       testsuite_name = suitename;
237       IN = fopen(argv[i], "r");
238       if (!IN) {
239         perror(bprintf("Impossible to open the suite file `%s'", argv[i]));
240         ERROR1("Test suite `%s': NOK (system error)", testsuite_name);
241         rctx_armageddon(rctx, 1);
242       }
243       handle_suite(suitename, IN);
244       rctx_wait_bg();
245       fclose(IN);
246       INFO1("Test suite `%s' OK", suitename);
247       free(suitename);
248     }
249   }
250
251   rctx_exit();
252   return 0;
253 }