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