1 /* ------------------------------------------------------------------------
2 @NAME       : simple_test.c
3 @INPUT      :
4 @OUTPUT     :
5 @RETURNS    :
6 @DESCRIPTION: Run some basic tests on some simple data.  The .bib files
7               processed here are all free of errors, and use just the basic
8               BibTeX syntax.  This is just to make sure the parser and
9               library are working in the crudest sense; more elaborate
10               tests will someday performed elsewhere (I hope).
11 @GLOBALS    :
12 @CALLS      :
13 @CALLERS    :
14 @CREATED    : 1997/07/29, Greg Ward
15 @MODIFIED   :
16 @VERSION    : $Id: simple_test.c 714 2003-10-09 02:37:15Z greg $
17 @COPYRIGHT  : Copyright (c) 1996-97 by Gregory P. Ward.  All rights reserved.
18 
19               This file is part of the btparse distribution (but not part
20               of the library itself).  This is free software; you can
21               redistribute it and/or modify it under the terms of the GNU
22               General Public License as published by the Free Software
23               Foundation; either version 2 of the License, or (at your
24               option) any later version.
25 -------------------------------------------------------------------------- */
26 
27 #include "bt_config.h"
28 #include <stdlib.h>
29 #include <string.h>
30 #include "btparse.h"
31 #include "testlib.h"
32 #include "my_dmalloc.h"
33 
34 int test_num = 0;
35 
36 
37 typedef enum { single, multiple, wholefile } test_mode;
38 
39 typedef struct
40 {
41    int    num_fields;
42    char **fields;
43    int    num_values;
44    char **values;
45    bt_nodetype * ntypes;
46 } test_data;
47 
48 typedef boolean (*tester) (AST *, test_data *);
49 
50 typedef struct
51 {
52    char *      desc;
53    char *      filename;
54    ushort      options;
55    tester      test_func;
56    test_data * data;
57 } test;
58 
59 /* prototypes needed for defining the tests[] array */
60 boolean eviltest_regular (AST *entry, test_data *data);
61 boolean eviltest_macro (AST *entry, test_data *data);
62 boolean eviltest_comment (AST *entry, test_data *data);
63 boolean eviltest_preamble (AST *entry, test_data *data);
64 boolean goodtest_regular (AST *entry, test_data *data);
65 boolean goodtest_macro (AST *entry, test_data *data);
66 boolean goodtest_comment (AST *entry, test_data *data);
67 boolean goodtest_preamble (AST *entry, test_data *data);
68 
69 /* and prototypes to keep "gcc -Wall" from whining */
70 boolean test_multiple (FILE *, char *, ushort, ushort, int, test *);
71 boolean test_wholefile (char *, ushort, ushort, int, test *);
72 
73 
74 /* a priori knowledge about the entry in "regular.bib" (used for both tests) */
75 char * regular_fields[] = { "title", "editor", "publisher", "year" };
76 char * regular_values[] =
77   { "A ", "Book", "  John Q.  Random", "junk", "Foo Bar \\& Sons", "1922" };
78 char * regular_values_proc[] =
79   { "A Book", "John Q. Random", "Foo Bar \\& Sons", "1922" };
80 bt_nodetype regular_ntypes[] =
81   { BTAST_STRING, BTAST_STRING, BTAST_STRING, BTAST_MACRO, BTAST_STRING, BTAST_NUMBER };
82 bt_nodetype regular_ntypes_proc[] =
83   { BTAST_STRING, BTAST_STRING, BTAST_STRING, BTAST_STRING };
84 
85 /* likewise for "macro.bib" */
86 char * macro_macros[] = { "macro", "foo" };
87 char * macro_values[] =
88   { "macro  text ", "blah blah  ", " ding dong " };
89 char * macro_values_proc[] =
90   { "macro text", "blah blah ding dong" };
91 bt_nodetype macro_ntypes[] = { BTAST_STRING, BTAST_STRING, BTAST_STRING };
92 
93 /* and for "comment.bib" */
94 char * comment_value = "this is a comment entry, anything at all can go in it (as long          as parentheses are balanced), even {braces}";
95 char * comment_value_proc = "this is a comment entry, anything at all can go in it (as long as parentheses are balanced), even {braces}";
96 
97 /* and for "preamble.bib" */
98 char * preamble_values[] =
99    { " This is   a preamble",
100      "---the concatenation of several strings" };
101 char * preamble_value_proc =
102    "This is a preamble---the concatenation of several strings";
103 
104 test_data regular_unproc_data =
105    { 4, regular_fields, 6, regular_values, regular_ntypes };
106 test_data regular_proc_data =
107    { 4, regular_fields, 4, regular_values_proc, regular_ntypes_proc };
108 test_data macro_unproc_data =
109    { 2, macro_macros, 3, macro_values, macro_ntypes };
110 test_data macro_proc_data =
111    { 2, macro_macros, 2, macro_values_proc, macro_ntypes };
112 test_data comment_unproc_data =
113    { 0, NULL, 1, &comment_value, NULL };
114 test_data comment_proc_data =
115    { 0, NULL, 1, &comment_value_proc, NULL };
116 test_data preamble_unproc_data =
117    { 0, NULL, 2, preamble_values, NULL };
118 test_data preamble_proc_data =
119    { 0, NULL, 1, &preamble_value_proc, NULL };
120 
121 
122 test tests[] =
123 {
124    { "regular entry (unprocessed, low-level scan)",
125      "regular.bib", BTO_MINIMAL,
126      eviltest_regular, &regular_unproc_data
127    },
128    { "macro entry (unprocessed, low-level scan)",
129      "macro.bib", BTO_MINIMAL,
130      eviltest_macro, &macro_unproc_data
131    },
132    { "comment entry (unprocessed, low-level scan)",
133      "comment.bib", BTO_MINIMAL,
134      eviltest_comment, &comment_unproc_data
135    },
136    { "preamble entry (unprocessed, low-level scan)",
137      "preamble.bib", BTO_MINIMAL,
138      eviltest_preamble, &preamble_unproc_data
139    },
140    { "regular entry (unprocessed, high-level scan)",
141      "regular.bib", BTO_MINIMAL,
142      goodtest_regular, &regular_unproc_data
143    },
144    { "macro entry (unprocessed, high-level scan)",
145      "macro.bib", BTO_MINIMAL,
146      goodtest_macro, &macro_unproc_data
147    },
148    { "comment entry (unprocessed, high-level scan)",
149      "comment.bib", BTO_MINIMAL,
150      goodtest_comment, &comment_unproc_data
151    },
152    { "preamble entry (unprocessed, high-level scan)",
153      "preamble.bib", BTO_MINIMAL,
154      goodtest_preamble, &preamble_unproc_data
155    },
156    { "regular entry (processed, low-level scan)",
157      "regular.bib", BTO_FULL,
158      eviltest_regular, &regular_proc_data
159    },
160    { "macro entry (processed, low-level scan)",
161      "macro.bib", BTO_FULL,
162      eviltest_macro, &macro_proc_data
163    },
164    { "comment entry (processed, low-level scan)",
165      "comment.bib", BTO_FULL,
166      eviltest_comment, &comment_proc_data
167    },
168    { "preamble entry (processed, low-level scan)",
169      "preamble.bib", BTO_FULL,
170      eviltest_preamble, &preamble_proc_data
171    },
172    { "regular entry (processed, high-level scan)",
173      "regular.bib", BTO_FULL,
174      goodtest_regular, &regular_proc_data
175    },
176    { "macro entry (processed, high-level scan)",
177      "macro.bib", BTO_FULL,
178      goodtest_macro, &macro_proc_data
179    },
180    { "comment entry (processed, high-level scan)",
181      "comment.bib", BTO_FULL,
182      goodtest_comment, &comment_proc_data
183    },
184    { "preamble entry (processed, high-level scan)",
185      "preamble.bib", BTO_FULL,
186      goodtest_preamble, &preamble_proc_data
187    },
188 };
189 
190 
191 #define NUM_TESTS sizeof (tests) / sizeof (tests[0])
192 
193 
eviltest_regular(AST * entry,test_data * data)194 boolean eviltest_regular (AST * entry, test_data * data)
195 {
196    boolean ok = TRUE;
197    AST *   key;
198    AST *   field;
199    AST *   value;
200    int     field_num;
201    int     value_num;
202 
203    CHECK_ESCAPE (entry != NULL, return FALSE, "entry")
204    CHECK (entry->nodetype == BTAST_ENTRY)
205    CHECK (entry->metatype == BTE_REGULAR)
206    CHECK (strcmp (entry->text, "book") == 0)
207 
208    key = entry->down;
209    CHECK_ESCAPE (key != NULL, return FALSE, "entry")
210    CHECK (key->nodetype == BTAST_KEY)
211    CHECK (key->metatype == BTE_UNKNOWN)
212    CHECK (strcmp (key->text, "abook") == 0)
213 
214    field = key;
215    field_num = 0;
216    value_num = 0;
217 
218    while ((field = field->right))
219    {
220       CHECK_ESCAPE (field_num < data->num_fields, break, "entry")
221       CHECK (field->nodetype == BTAST_FIELD)
222       CHECK (field->metatype == BTE_UNKNOWN)
223       CHECK (strcmp (field->text, data->fields[field_num++]) == 0)
224 
225       value = field->down;
226       while (value)
227       {
228          CHECK_ESCAPE (value_num < data->num_values, break, "field")
229          CHECK (value->nodetype == data->ntypes[value_num])
230          CHECK (strcmp (value->text, data->values[value_num]) == 0)
231          value = value->right;
232          value_num++;
233       }
234    }
235    CHECK (field_num == data->num_fields)
236    CHECK (value_num == data->num_values)
237 
238    return ok;
239 
240 } /* eviltest_regular () */
241 
242 
eviltest_macro(AST * entry,test_data * data)243 boolean eviltest_macro (AST * entry, test_data * data)
244 {
245    boolean ok = TRUE;
246    AST *   macro;
247    AST *   value;
248    int     macro_num;
249    int     value_num;
250 
251    CHECK (entry != NULL)
252    CHECK (entry->nodetype == BTAST_ENTRY)
253    CHECK (entry->metatype == BTE_MACRODEF)
254    CHECK (strcmp (entry->text, "string") == 0)
255 
256    macro_num = 0;
257    value_num = 0;
258    macro = entry->down;
259 
260    while (macro)
261    {
262       CHECK_ESCAPE (macro_num < data->num_fields, break, "entry")
263       CHECK (macro->nodetype == BTAST_FIELD)
264       CHECK (macro->metatype == BTE_UNKNOWN)
265       CHECK (strcmp (macro->text, data->fields[macro_num++]) == 0)
266 
267       value = macro->down;
268       while (value)
269       {
270          CHECK_ESCAPE (value_num < data->num_values, break, "macro")
271          CHECK (value->nodetype == data->ntypes[value_num])
272          CHECK (strcmp (value->text, data->values[value_num]) == 0)
273          value = value->right;
274          value_num++;
275       }
276       macro = macro->right;
277    }
278    CHECK (macro_num == data->num_fields)
279    CHECK (value_num == data->num_values)
280 
281    return ok;
282 
283 } /* eviltest_macro () */
284 
285 
eviltest_comment(AST * entry,test_data * data)286 boolean eviltest_comment (AST * entry, test_data * data)
287 {
288    boolean ok = TRUE;
289    AST *   value;
290 
291    CHECK_ESCAPE (entry != NULL, return FALSE, "entry");
292    CHECK (strcmp (entry->text, "comment") == 0);
293 
294    value = entry->down;
295    CHECK_ESCAPE (value != NULL, return FALSE, "entry");
296    CHECK (strcmp (value->text, data->values[0]) == 0);
297    CHECK (value->right == NULL);
298    CHECK (value->down == NULL);
299 
300    return ok;
301 } /* eviltest_comment () */
302 
303 
eviltest_preamble(AST * entry,test_data * data)304 boolean eviltest_preamble (AST * entry, test_data * data)
305 {
306    boolean ok = TRUE;
307    AST *   value;
308    int     value_num;
309 
310    CHECK_ESCAPE (entry != NULL, return FALSE, "entry");
311    CHECK (strcmp (entry->text, "preamble") == 0);
312 
313    value_num = 0;
314    value = entry->down;
315    while (value)
316    {
317       CHECK_ESCAPE (value_num < data->num_values, break, "entry");
318       CHECK (value->nodetype == BTAST_STRING);
319       CHECK (strcmp (value->text, data->values[value_num]) == 0);
320 
321       value = value->right;
322       value_num++;
323    }
324 
325    CHECK (value_num == data->num_values);
326    return ok;
327 
328 } /* eviltest_preamble () */
329 
330 
goodtest_regular(AST * entry,test_data * data)331 boolean goodtest_regular (AST * entry, test_data * data)
332 {
333    boolean ok = TRUE;
334    AST *   field;
335    AST *   value;
336    char *  field_name;
337    char *  value_text;
338    bt_nodetype
339            value_nodetype;
340    int     field_num;
341    int     value_num;
342 
343    CHECK (bt_entry_metatype (entry) == BTE_REGULAR);
344    CHECK (strcmp (bt_entry_type (entry), "book") == 0);
345    CHECK (strcmp (bt_entry_key (entry), "abook") == 0);
346 
347    field = NULL;
348    field_num = 0;
349    value_num = 0;
350 
351    while ((field = bt_next_field (entry, field, &field_name)))
352    {
353       CHECK_ESCAPE (field_num < data->num_fields, break, "entry");
354       CHECK (strcmp (field_name, data->fields[field_num++]) == 0);
355 
356       value = NULL;
357       while ((value = bt_next_value (field,value,&value_nodetype,&value_text)))
358       {
359          CHECK_ESCAPE (value_num < data->num_values, break, "field");
360          CHECK (value_nodetype == data->ntypes[value_num]);
361          CHECK (strcmp (value_text, data->values[value_num++]) == 0);
362       }
363    }
364 
365    CHECK (field_num == data->num_fields);
366    CHECK (value_num == data->num_values);
367 
368    return ok;
369 
370 }
371 
372 
goodtest_macro(AST * entry,test_data * data)373 boolean goodtest_macro (AST * entry, test_data * data)
374 {
375    boolean ok = TRUE;
376    AST *   macro;
377    AST *   value;
378    char *  macro_name;
379    char *  value_text;
380    bt_nodetype
381            value_nodetype;
382    int     macro_num;
383    int     value_num;
384 
385    CHECK (bt_entry_metatype (entry) == BTE_MACRODEF);
386    CHECK (strcmp (bt_entry_type (entry), "string") == 0);
387    CHECK (bt_entry_key (entry) == NULL);
388 
389    macro = NULL;
390    macro_num = 0;
391    value_num = 0;
392 
393    while ((macro = bt_next_macro (entry, macro, &macro_name)))
394    {
395       CHECK_ESCAPE (macro_num < data->num_fields, break, "entry");
396       CHECK (strcmp (macro_name, data->fields[macro_num++]) == 0);
397 
398       value = NULL;
399       while ((value = bt_next_value (macro,value,&value_nodetype,&value_text)))
400       {
401          CHECK_ESCAPE (value_num < data->num_values, break, "macro");
402          CHECK (value_nodetype == data->ntypes[value_num]);
403          CHECK (strcmp (value_text, data->values[value_num++]) == 0);
404       }
405    }
406 
407    CHECK (macro_num == data->num_fields);
408    CHECK (value_num == data->num_values);
409 
410    return ok;
411 
412 }
413 
414 
goodtest_comment(AST * entry,test_data * data)415 boolean goodtest_comment (AST * entry, test_data * data)
416 {
417    boolean ok = TRUE;
418    AST *   value;
419    char *  text;
420 
421    CHECK (bt_entry_metatype (entry) == BTE_COMMENT);
422    CHECK (strcmp (bt_entry_type (entry), "comment") == 0);
423 
424    value = bt_next_value (entry, NULL, NULL, &text);
425    CHECK (strcmp (text, data->values[0]) == 0);
426 
427    return ok;
428 }
429 
430 
goodtest_preamble(AST * entry,test_data * data)431 boolean goodtest_preamble (AST * entry, test_data * data)
432 {
433    boolean ok = TRUE;
434    AST *   value;
435    char *  value_text;
436    bt_nodetype
437            value_nodetype;
438    int     value_num;
439 
440    CHECK (bt_entry_metatype (entry) == BTE_PREAMBLE);
441    CHECK (strcmp (bt_entry_type (entry), "preamble") == 0);
442 
443    value = NULL;
444    value_num = 0;
445    while ((value = bt_next_value (entry, value, &value_nodetype, &value_text)))
446    {
447       CHECK_ESCAPE (value_num < data->num_values, break, "entry");
448       CHECK (value_nodetype == BTAST_STRING);
449       CHECK (strcmp (value_text, data->values[value_num++]) == 0);
450    }
451 
452    CHECK (value_num == data->num_values);
453    return ok;
454 }
455 
456 
test_multiple(FILE * file,char * filename,ushort string_opts,ushort other_opts,int num_entries,test * tests)457 boolean test_multiple (FILE * file,
458                        char * filename,
459                        ushort string_opts,
460                        ushort other_opts,
461                        int    num_entries,
462                        test * tests)
463 {
464    boolean entry_ok;
465    boolean ok;
466    int     entry_num;
467    AST *   entry;
468 
469    ok = TRUE;
470    entry_num = 0;
471 
472    printf ("multiple entries in one file, read individually:\n");
473    set_all_stringopts (string_opts);
474 
475    while (1)
476    {
477       entry = bt_parse_entry (file, filename, other_opts, &entry_ok);
478       if (!entry) break;                /* at eof? */
479 
480       CHECK_ESCAPE (entry_num < num_entries, break, "file");
481 
482       entry_ok &= tests[entry_num].test_func (entry, tests[entry_num].data);
483       printf ("  %s: %s\n",
484               tests[entry_num].desc,
485               entry_ok ? "ok" : "not ok");
486       entry_num++;
487       bt_free_ast (entry);
488 
489       ok &= entry_ok;
490    }
491 
492    CHECK (entry_num == num_entries);
493    printf ("...%s\n", ok ? "all ok" : "not all ok");
494    return ok;
495 
496 }
497 
498 
test_wholefile(char * filename,ushort string_opts,ushort other_opts,int num_entries,test * tests)499 boolean test_wholefile (char * filename,
500                         ushort string_opts,
501                         ushort other_opts,
502                         int    num_entries,
503                         test * tests)
504 {
505    boolean entry_ok;
506    boolean ok;
507    int     entry_num;
508    AST *   entries,
509        *   entry;
510 
511    ok = TRUE;
512    entry_num = 0;
513 
514    printf ("multiple entries in one file, read together:\n");
515    set_all_stringopts (string_opts);
516    entries = bt_parse_file (filename, other_opts, &entry_ok);
517    CHECK (entry_ok);
518 
519    entry = NULL;
520    while ((entry = bt_next_entry (entries, entry)))
521    {
522       CHECK_ESCAPE (entry_num < num_entries, break, "file");
523 
524       entry_ok = tests[entry_num].test_func (entry, tests[entry_num].data);
525       printf ("  %s: %s\n",
526               tests[entry_num].desc,
527               entry_ok ? "ok" : "not ok");
528       entry_num++;
529 
530       ok &= entry_ok;
531    }
532 
533    CHECK (entry_num == num_entries);
534    bt_free_ast (entries);
535    printf ("...%s\n", ok ? "all ok" : "not all ok");
536    return ok;
537 
538 }
539 
540 
main(void)541 int main (void)
542 {
543    unsigned i;
544    char    filename[256];
545    FILE *  infile;
546    AST *   entry;
547    ushort  options = 0;                 /* use default non-string options */
548    boolean ok;
549    int     num_failures = 0;
550 
551    bt_initialize ();
552 
553    for (i = 0; i < NUM_TESTS; i++)
554    {
555       infile = open_file (tests[i].filename, DATA_DIR, filename);
556 
557       /* Override string-processing options for all entry metatypes */
558       set_all_stringopts (tests[i].options);
559 
560       entry = bt_parse_entry (infile, filename, options, &ok);
561       ok &= tests[i].test_func (entry, tests[i].data);
562       bt_free_ast (entry);
563       entry = bt_parse_entry (infile, filename, options, NULL);
564       CHECK ((entry == NULL));
565       CHECK (feof (infile));
566       fclose (infile);
567 
568       printf ("%s: %s\n", tests[i].desc, ok ? "ok" : "not ok");
569       if (!ok) num_failures++;
570    } /* for i */
571 
572    infile = open_file ("simple.bib", DATA_DIR, filename);
573    if (! test_multiple (infile, filename, BTO_MINIMAL, options, 4, tests+4))
574       num_failures++;
575    rewind (infile);
576    if (! test_multiple (infile, filename, BTO_FULL, options, 4, tests+12))
577       num_failures++;
578 
579    fclose (infile);
580 
581    if (! test_wholefile (DATA_DIR "/" "simple.bib",
582                          BTO_MINIMAL, options, 4, tests+4))
583       num_failures++;
584    if (! test_wholefile (DATA_DIR "/" "simple.bib",
585                          BTO_FULL, options, 4, tests+12))
586       num_failures++;
587 
588    bt_cleanup ();
589 
590    if (num_failures == 0)
591       printf ("All tests successful\n");
592    else
593       printf ("%d failed tests\n", num_failures);
594 
595    return (num_failures > 0);
596 }
597