chiark / gitweb /
New version number.
[mLib] / testrig.c
1 /* -*-c-*-
2  *
3  * $Id: testrig.c,v 1.4 1999/05/19 19:02:17 mdw Exp $
4  *
5  * Generic test driver
6  *
7  * (c) 1998 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the mLib utilities library.
13  *
14  * mLib is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * mLib is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with mLib; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: testrig.c,v $
33  * Revision 1.4  1999/05/19 19:02:17  mdw
34  * Aesthetic changes: fix spelling of `initialize'; use uppercase token
35  * constants; abandon old double-underscore convention for internal
36  * functions and variables.
37  *
38  * Revision 1.3  1999/05/06 19:51:35  mdw
39  * Reformatted the LGPL notice a little bit.
40  *
41  * Revision 1.2  1999/05/05 18:50:31  mdw
42  * Change licensing conditions to LGPL.
43  *
44  * Revision 1.1.1.1  1998/06/17 23:44:42  mdw
45  * Initial version of mLib
46  *
47  */
48
49 /*----- Header files ------------------------------------------------------*/
50
51 #include <ctype.h>
52 #include <errno.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56
57 #include "dstr.h"
58 #include "report.h"
59 #include "quis.h"
60 #include "testrig.h"
61
62 /*----- Static variables --------------------------------------------------*/
63
64 static dstr tok;
65
66 enum {
67   TOK_EOF = 0x100,
68   TOK_WORD
69 };
70
71 /*----- Main code ---------------------------------------------------------*/
72
73 /* --- @decode@ --- *
74  *
75  * Arguments:   @int tok@ = token type to decode
76  *
77  * Returns:     Pointer to a textual representation of the token.
78  *
79  * Use:         Produces a readable representation of a token.
80  */
81
82 static const char *decode(int t)
83 {
84   static char buf[4];
85
86   switch (t) {
87     case TOK_EOF:
88       return ("<eof>");
89     case TOK_WORD:
90       return (tok.buf);
91     default:
92       buf[0] = t;
93       buf[1] = 0;
94       return (buf);
95   }
96   return ("<buggy-program>");
97 }
98
99 /* --- @gettok@ --- *
100  *
101  * Arguments:   @FILE *fp@ = file handle to read from
102  *
103  * Returns:     Type of token read.
104  *
105  * Use:         Reads a token from the input stream.
106  */
107
108 static int gettok(FILE *fp)
109 {
110   int ch;
111
112   /* --- Clear the token accumulator --- */
113
114   dstr_reset(&tok);
115
116   /* --- Prime the lookahead character --- */
117
118 again:
119   ch = getc(fp);
120
121   /* --- Skip leading whitespace --- */
122
123   while (isspace((unsigned char)ch))
124     ch = getc(fp);
125
126   /* --- Trap some special characters --- */
127
128   switch (ch) {
129
130     /* --- Comments --- */
131
132     case '#':
133       do ch = getc(fp); while (ch != EOF && ch != '\n');
134       goto again;
135
136     /* --- End of file --- */
137       
138     case EOF:
139       return (TOK_EOF);
140
141     /* --- Quote characters --- */
142
143     case '`':
144       ch = '\'';
145     case '\'':
146     case '\"': {
147       int quote = ch;
148
149       for (;;) {
150         ch = getc(fp);
151         if (ch == EOF || ch == quote)
152           break;
153         if (ch == '\\') {
154           ch = getc(fp);
155           if (ch == EOF)
156             ch = '\\';
157         }
158         DPUTC(&tok, ch);
159       }
160       DPUTZ(&tok);
161       return (TOK_WORD);
162     }
163
164     /* --- Anything else is either a word or self-delimiting --- */
165
166     default:
167       if (isalnum((unsigned char)ch)) {
168         for (;;) {
169           DPUTC(&tok, ch);
170           ch = getc(fp);
171           if (ch == EOF ||
172               ch == ';' ||
173               ch == '\"' || ch == '\'' || ch == '`' ||
174               isspace((unsigned char)ch))
175             break;
176           if (ch == '\\') {
177             ch = getc(fp);
178             if (ch == EOF)
179               ch = '\\';
180           }
181         }
182         ungetc(ch, fp);
183         DPUTZ(&tok);
184         return (TOK_WORD);
185       } else
186         return (ch);
187   }
188 }
189
190 /* --- @type_hex@ --- */
191
192 static void cvt_hex(const char *s, dstr *d)
193 {
194   while (s[0] && s[1]) {
195     int x = s[0], y = s[1];
196     if ('0' <= x && x <= '9') x -= '0';
197     else if ('A' <= x && x <= 'F') x -= 'A' - 10;
198     else if ('a' <= x && x <= 'f') x -= 'a' - 10;
199     else x = 0;
200     if ('0' <= y && y <= '9') y -= '0';
201     else if ('A' <= y && y <= 'F') y -= 'A' - 10;
202     else if ('a' <= y && y <= 'f') y -= 'a' - 10;
203     else y = 0;
204     DPUTC(d, (x << 4) + y);
205     s += 2;
206   }
207 }
208
209 static void dump_hex(dstr *d, FILE *fp)
210 {
211   const char *p, *q;
212   for (p = d->buf, q = p + d->len; p < q; p++)
213     fprintf(fp, "%02x", *(unsigned char *)p);
214 }
215
216 test_type type_hex = { cvt_hex, dump_hex };
217
218 /* --- @type_string@ --- */
219
220 static void cvt_string(const char *s, dstr *d)
221 {
222   DPUTS(d, s);
223 }
224
225 static void dump_string(dstr *d, FILE *fp)
226 {
227   dstr_write(d, fp);
228 }
229
230 test_type type_string = { cvt_string, dump_string };
231
232 /* --- @type_int@ --- */
233
234 static void cvt_int(const char *s, dstr *d)
235 {
236   DENSURE(d, sizeof(long));
237   sscanf(s, "%i", (int *)d->buf + d->len);
238 }
239
240 static void dump_int(dstr *d, FILE *fp)
241 {
242   fprintf(fp, "%i", *(int *)(d->buf));
243 }
244
245 test_type type_int = { cvt_int, dump_int };
246
247 /* --- @test_run@ --- *
248  *
249  * Arguments:   @int argc@ = number of command line arguments
250  *              @char *argv[]@ = pointer to command line arguments
251  *              @const test_chunk chunk[]@ = pointer to chunk definitions
252  *              @const char *vec@ = name of default test vector file
253  *
254  * Returns:     Doesn't.
255  *
256  * Use:         Runs a set of test vectors to ensure that a component is
257  *              working properly.
258  */
259
260 void test_run(int argc, char *argv[],
261               const test_chunk chunk[],
262               const char *vec)
263 {
264   FILE *fp;
265   int i;
266   const test_chunk *cch;
267   dstr dv[TEST_FIELDMAX];
268   int fail = 0, ok = 1;
269   int sofar = 0;
270
271   /* --- Silly bits of initialization --- */
272
273   ego(argv[0]);
274
275   for (i = 0; i < TEST_FIELDMAX; i++)
276     dstr_create(&dv[i]);
277
278   /* --- Parse command line arguments --- */
279
280   {
281     const char *p = 0;
282
283     i = 0;
284     for (;;) {
285       if (!p || !*p) {
286         if (i >= argc - 1)
287           break;
288         p = argv[++i];
289         if (strcmp(p, "--") == 0) {
290           i++;
291           break;
292         }
293         if (p[0] != '-' || p[1] == 0)
294           break;
295         p++;
296       }
297       switch (*p++) {
298         case 'h':
299           printf("%s test driver\n"
300                  "Usage: %s [-f FILENAME]\n", QUIS, QUIS);
301           exit(0);
302         case 'f':
303           if (!*p) {
304             if (i >= argc - 1)
305               die(1, "option `-f' expects an argument");
306             p = argv[++i];
307           }
308           vec = p;
309           p = 0;
310           break;
311         default:
312           die(1, "option `-%c' unknown", p[-1]);
313           break;
314       }
315     }
316   }
317
318   /* --- Start parsing from the file --- */
319
320   if ((fp = fopen(vec, "r")) == 0)
321     die(1, "couldn't open test vector file `%s': %s", vec, strerror(errno));
322
323   for (;;) {
324     int t = gettok(fp);
325
326     /* --- This is a reasonable place to stop --- */
327
328     if (t == TOK_EOF)
329       break;
330
331     /* --- Pick out the chunk name --- */
332
333     if (t != TOK_WORD)
334       die(1, "expected <word>; found `%s'", decode(t));
335
336     /* --- Find the right chunk block --- */
337
338     for (cch = chunk; ; cch++) {
339       if (!cch->name)
340         goto skip_chunk;
341       if (strcmp(tok.buf, cch->name) == 0)
342         break;
343     }
344
345     /* --- Past the open brace to the first chunk --- */
346
347     if ((t = gettok(fp)) != '{')
348       die(1, "expected '{'; found `%s'", decode(t));
349
350     /* --- Start on the test data now --- */
351
352     printf("%s: ", cch->name);
353     fflush(stdout);
354     sofar = 0;
355     ok = 1;
356
357     for (;;) {
358       t = gettok(fp);
359
360       /* --- Accept a close brace --- */
361
362       if (t == '}')
363         break;
364
365       /* --- Otherwise I expect a list of words --- */
366
367       for (i = 0; cch->f[i]; i++) {
368         dstr_reset(&dv[i]);
369         if (t != TOK_WORD)
370           die(1, "expected <word>; found `%s'", decode(t));
371         cch->f[i]->cvt(tok.buf, &dv[i]);
372         t = gettok(fp);
373       }
374
375       /* --- And a terminating semicolon --- */
376
377       if (t != ';')
378         die(1, "expected `;'; found `%s'", decode(t));
379
380       /* --- Run the test code --- */
381
382       if (!cch->test(dv)) {
383         printf("%s: ", cch->name);
384         for (i = 0; i < sofar; i++) putchar('.');
385         fail = 1; ok = 0;
386       }
387       sofar++;
388       putchar('.');
389       fflush(stdout);
390     }
391
392     puts(ok ? " ok" : " failed");
393     fflush(stdout);
394     continue;
395
396   skip_chunk:
397     if ((t = gettok(fp)) != '{')
398       die(1, "expected '{'; found `%s'", decode(t));
399     for (;;) {
400       t = gettok(fp);
401       if (t == '}')
402         break;
403       while (t == TOK_WORD)
404         t = gettok(fp);
405       if (t != ';')
406         die(1, "expected `;'; found `%s'", decode(t));
407     }      
408   }
409
410   exit(fail);
411 }
412
413 /*----- That's all, folks -------------------------------------------------*/