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