chiark / gitweb /
Skip past trailing whitespace in str_getword.
[mLib] / testrig.c
1 /* -*-c-*-
2  *
3  * $Id: testrig.c,v 1.8 1999/12/10 23:41:37 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.8  1999/12/10 23:41:37  mdw
34  * Support for different sizes and types of integers.
35  *
36  * Revision 1.7  1999/11/21 13:01:39  mdw
37  * Allow more characters to start words in test vector files.
38  *
39  * Revision 1.6  1999/11/16 15:03:23  mdw
40  * Mark test types as constant.
41  *
42  * Revision 1.5  1999/05/21 22:14:30  mdw
43  * Take advantage of the new dynamic string macros.
44  *
45  * Revision 1.4  1999/05/19 19:02:17  mdw
46  * Aesthetic changes: fix spelling of `initialize'; use uppercase token
47  * constants; abandon old double-underscore convention for internal
48  * functions and variables.
49  *
50  * Revision 1.3  1999/05/06 19:51:35  mdw
51  * Reformatted the LGPL notice a little bit.
52  *
53  * Revision 1.2  1999/05/05 18:50:31  mdw
54  * Change licensing conditions to LGPL.
55  *
56  * Revision 1.1.1.1  1998/06/17 23:44:42  mdw
57  * Initial version of mLib
58  *
59  */
60
61 /*----- Header files ------------------------------------------------------*/
62
63 #include <ctype.h>
64 #include <errno.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68
69 #include "dstr.h"
70 #include "report.h"
71 #include "quis.h"
72 #include "testrig.h"
73
74 /*----- Static variables --------------------------------------------------*/
75
76 static dstr tok;
77
78 enum {
79   TOK_EOF = 0x100,
80   TOK_WORD
81 };
82
83 /*----- Main code ---------------------------------------------------------*/
84
85 /* --- @decode@ --- *
86  *
87  * Arguments:   @int tok@ = token type to decode
88  *
89  * Returns:     Pointer to a textual representation of the token.
90  *
91  * Use:         Produces a readable representation of a token.
92  */
93
94 static const char *decode(int t)
95 {
96   static char buf[4];
97
98   switch (t) {
99     case TOK_EOF:
100       return ("<eof>");
101     case TOK_WORD:
102       return (tok.buf);
103     default:
104       buf[0] = t;
105       buf[1] = 0;
106       return (buf);
107   }
108   return ("<buggy-program>");
109 }
110
111 /* --- @gettok@ --- *
112  *
113  * Arguments:   @FILE *fp@ = file handle to read from
114  *
115  * Returns:     Type of token read.
116  *
117  * Use:         Reads a token from the input stream.
118  */
119
120 static int gettok(FILE *fp)
121 {
122   int ch;
123
124   /* --- Clear the token accumulator --- */
125
126   DRESET(&tok);
127
128   /* --- Prime the lookahead character --- */
129
130 again:
131   ch = getc(fp);
132
133   /* --- Skip leading whitespace --- */
134
135   while (isspace((unsigned char)ch))
136     ch = getc(fp);
137
138   /* --- Trap some special characters --- */
139
140   switch (ch) {
141
142     /* --- Comments --- */
143
144     case '#':
145       do ch = getc(fp); while (ch != EOF && ch != '\n');
146       goto again;
147
148     /* --- End of file --- */
149       
150     case EOF:
151       return (TOK_EOF);
152
153     /* --- Quote characters --- */
154
155     case '`':
156       ch = '\'';
157     case '\'':
158     case '\"': {
159       int quote = ch;
160
161       for (;;) {
162         ch = getc(fp);
163         if (ch == EOF || ch == quote)
164           break;
165         if (ch == '\\') {
166           ch = getc(fp);
167           if (ch == EOF)
168             ch = '\\';
169         }
170         DPUTC(&tok, ch);
171       }
172       DPUTZ(&tok);
173       return (TOK_WORD);
174     }
175
176     /* --- Self-delimiting things --- */
177
178     case ';':
179     case '{':
180     case '}':
181       return (ch);
182
183     /* --- Anything else is a word --- */
184
185     default:
186       for (;;) {
187         DPUTC(&tok, ch);
188         ch = getc(fp);
189         switch (ch) {
190           case EOF:
191           case ';':
192           case '{':
193           case '}':
194           case '\"':
195           case '\'':
196           case '`':
197             goto done;
198           default:
199             if (isspace((unsigned char)ch))
200               goto done;
201         }
202         if (ch == '\\') {
203           ch = getc(fp);
204           if (ch == EOF)
205             ch = '\\';
206         }
207       }
208     done:
209       ungetc(ch, fp);
210       DPUTZ(&tok);
211       return (TOK_WORD);
212   }
213 }
214
215 /* --- @type_hex@ --- */
216
217 static void cvt_hex(const char *s, dstr *d)
218 {
219   while (s[0] && s[1]) {
220     int x = s[0], y = s[1];
221     if ('0' <= x && x <= '9') x -= '0';
222     else if ('A' <= x && x <= 'F') x -= 'A' - 10;
223     else if ('a' <= x && x <= 'f') x -= 'a' - 10;
224     else x = 0;
225     if ('0' <= y && y <= '9') y -= '0';
226     else if ('A' <= y && y <= 'F') y -= 'A' - 10;
227     else if ('a' <= y && y <= 'f') y -= 'a' - 10;
228     else y = 0;
229     DPUTC(d, (x << 4) + y);
230     s += 2;
231   }
232 }
233
234 static void dump_hex(dstr *d, FILE *fp)
235 {
236   const char *p, *q;
237   for (p = d->buf, q = p + d->len; p < q; p++)
238     fprintf(fp, "%02x", *(unsigned char *)p);
239 }
240
241 const test_type type_hex = { cvt_hex, dump_hex };
242
243 /* --- @type_string@ --- */
244
245 static void cvt_string(const char *s, dstr *d)
246 {
247   DPUTS(d, s);
248 }
249
250 static void dump_string(dstr *d, FILE *fp)
251 {
252   DWRITE(d, fp);
253 }
254
255 const test_type type_string = { cvt_string, dump_string };
256
257 /* --- @type_int@ --- */
258
259 static void cvt_int(const char *s, dstr *d)
260 {
261   DENSURE(d, sizeof(int));
262   sscanf(s, "%i", (int *)d->buf);
263 }
264
265 static void dump_int(dstr *d, FILE *fp)
266 {
267   fprintf(fp, "%i", *(int *)d->buf);
268 }
269
270 const test_type type_int = { cvt_int, dump_int };
271
272 /* --- @type_long@ --- */
273
274 static void cvt_long(const char *s, dstr *d)
275 {
276   DENSURE(d, sizeof(long));
277   *(long *)d->buf = strtol(s, 0, 0);
278 }
279
280 static void dump_long(dstr *d, FILE *fp)
281 {
282   fprintf(fp, "%li", *(long *)d->buf);
283 }
284
285 const test_type type_long = { cvt_long, dump_long };
286
287 /* --- @type_ulong@ --- */
288
289 static void cvt_ulong(const char *s, dstr *d)
290 {
291   DENSURE(d, sizeof(unsigned long));
292   *(unsigned long *)d->buf = strtoul(s, 0, 0);
293 }
294
295 static void dump_ulong(dstr *d, FILE *fp)
296 {
297   fprintf(fp, "%lu", *(unsigned long *)d->buf);
298 }
299
300 const test_type type_ulong = { cvt_ulong, dump_ulong };
301
302 /* --- @type_uint32@ --- */
303
304 static void cvt_uint32(const char *buf, dstr *d)
305 {
306   DENSURE(d, sizeof(uint32));
307   *(uint32 *)d->buf = strtoul(buf, 0, 0);
308 }
309
310 static void dump_uint32(dstr *d, FILE *fp)
311 {
312   fprintf(fp, "%lu\n", (unsigned long)*(uint32 *)d->buf);
313 }
314
315 const test_type type_uint32 = { cvt_uint32, dump_uint32 };
316
317 /* --- @test_run@ --- *
318  *
319  * Arguments:   @int argc@ = number of command line arguments
320  *              @char *argv[]@ = pointer to command line arguments
321  *              @const test_chunk chunk[]@ = pointer to chunk definitions
322  *              @const char *vec@ = name of default test vector file
323  *
324  * Returns:     Doesn't.
325  *
326  * Use:         Runs a set of test vectors to ensure that a component is
327  *              working properly.
328  */
329
330 void test_run(int argc, char *argv[],
331               const test_chunk chunk[],
332               const char *vec)
333 {
334   FILE *fp;
335   int i;
336   const test_chunk *cch;
337   dstr dv[TEST_FIELDMAX];
338   int fail = 0, ok = 1;
339   int sofar = 0;
340
341   /* --- Silly bits of initialization --- */
342
343   ego(argv[0]);
344
345   for (i = 0; i < TEST_FIELDMAX; i++)
346     DCREATE(&dv[i]);
347
348   /* --- Parse command line arguments --- */
349
350   {
351     const char *p = 0;
352
353     i = 0;
354     for (;;) {
355       if (!p || !*p) {
356         if (i >= argc - 1)
357           break;
358         p = argv[++i];
359         if (strcmp(p, "--") == 0) {
360           i++;
361           break;
362         }
363         if (p[0] != '-' || p[1] == 0)
364           break;
365         p++;
366       }
367       switch (*p++) {
368         case 'h':
369           printf("%s test driver\n"
370                  "Usage: %s [-f FILENAME]\n", QUIS, QUIS);
371           exit(0);
372         case 'f':
373           if (!*p) {
374             if (i >= argc - 1)
375               die(1, "option `-f' expects an argument");
376             p = argv[++i];
377           }
378           vec = p;
379           p = 0;
380           break;
381         default:
382           die(1, "option `-%c' unknown", p[-1]);
383           break;
384       }
385     }
386   }
387
388   /* --- Start parsing from the file --- */
389
390   if ((fp = fopen(vec, "r")) == 0)
391     die(1, "couldn't open test vector file `%s': %s", vec, strerror(errno));
392
393   for (;;) {
394     int t = gettok(fp);
395
396     /* --- This is a reasonable place to stop --- */
397
398     if (t == TOK_EOF)
399       break;
400
401     /* --- Pick out the chunk name --- */
402
403     if (t != TOK_WORD)
404       die(1, "expected <word>; found `%s'", decode(t));
405
406     /* --- Find the right chunk block --- */
407
408     for (cch = chunk; ; cch++) {
409       if (!cch->name)
410         goto skip_chunk;
411       if (strcmp(tok.buf, cch->name) == 0)
412         break;
413     }
414
415     /* --- Past the open brace to the first chunk --- */
416
417     if ((t = gettok(fp)) != '{')
418       die(1, "expected `{'; found `%s'", decode(t));
419
420     /* --- Start on the test data now --- */
421
422     printf("%s: ", cch->name);
423     fflush(stdout);
424     sofar = 0;
425     ok = 1;
426
427     for (;;) {
428       t = gettok(fp);
429
430       /* --- Accept a close brace --- */
431
432       if (t == '}')
433         break;
434
435       /* --- Otherwise I expect a list of words --- */
436
437       for (i = 0; cch->f[i]; i++) {
438         DRESET(&dv[i]);
439         if (t != TOK_WORD)
440           die(1, "expected <word>; found `%s'", decode(t));
441         cch->f[i]->cvt(tok.buf, &dv[i]);
442         t = gettok(fp);
443       }
444
445       /* --- And a terminating semicolon --- */
446
447       if (t != ';')
448         die(1, "expected `;'; found `%s'", decode(t));
449
450       /* --- Run the test code --- */
451
452       if (!cch->test(dv)) {
453         printf("%s: ", cch->name);
454         for (i = 0; i < sofar; i++) putchar('.');
455         fail = 1; ok = 0;
456       }
457       sofar++;
458       putchar('.');
459       fflush(stdout);
460     }
461
462     puts(ok ? " ok" : " failed");
463     fflush(stdout);
464     continue;
465
466   skip_chunk:
467     if ((t = gettok(fp)) != '{')
468       die(1, "expected '{'; found `%s'", decode(t));
469     for (;;) {
470       t = gettok(fp);
471       if (t == '}')
472         break;
473       while (t == TOK_WORD)
474         t = gettok(fp);
475       if (t != ';')
476         die(1, "expected `;'; found `%s'", decode(t));
477     }      
478   }
479
480   exit(fail);
481 }
482
483 /*----- That's all, folks -------------------------------------------------*/