]> git.draconx.ca Git - cdecl99.git/blob - test/crossparse.c
Port random crossparse test to Autotest.
[cdecl99.git] / test / crossparse.c
1 /*
2  * Test that libcdecl can parse its own output.
3  * Copyright © 2012, 2020 Nick Bowler
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <stdbool.h>
25 #include <getopt.h>
26 #include <cdecl.h>
27 #include "test.h"
28
29 #define PROGNAME "crossparse"
30 static const char *progname = PROGNAME;
31 static const char sopts[] = "f:ECVH";
32 static const struct option lopts[] = {
33         { "cdecl",   0, NULL, 'C' },
34         { "english", 0, NULL, 'E' },
35         { "file",    2, NULL, 'f' },
36         { "version", 0, NULL, 'V' },
37         { "help",    0, NULL, 'H' },
38         { 0 }
39 };
40
41 static void print_usage(FILE *f)
42 {
43         fprintf(f, "Usage: %s [options]\n", progname);
44 }
45
46 static void print_help(void)
47 {
48         const struct option *opt;
49
50         print_usage(stdout);
51         puts("Test that libcdecl can parse its own output.\n");
52
53         puts("Options:");
54         for (opt = lopts; opt->val; opt++) {
55                 int w = print_option_start(opt, NULL);
56
57                 if (w)
58                         putchar('\n');
59         }
60 }
61
62 enum {
63         MODE_CDECL,
64         MODE_ENGLISH,
65 };
66
67 typedef struct cdecl *parse_func(const char *);
68 typedef size_t render_func(char *, size_t, struct cdecl *);
69
70 char *rerender(const char *str, const char *parse_name,  parse_func *parse,
71                                 const char *render_name, render_func *render)
72 {
73         struct cdecl *decl = NULL;
74         char *buf = NULL;
75         size_t len;
76
77         decl = parse(str);
78         if (!decl) {
79                 fprintf(stderr, "%s: %s: failed to parse input: %s\n",
80                                 progname, parse_name, cdecl_get_error()->str);
81                 goto err;
82         }
83
84         len = render(NULL, 0, decl);
85         buf = malloc_nofail(len+1);
86         if (render(buf, len+1, decl) != len) {
87                 fprintf(stderr, "%s: %s: inconsistent length returned\n",
88                                 progname, render_name);
89                 goto err;
90         }
91
92         cdecl_free(decl);
93         return buf;
94 err:
95         cdecl_free(decl);
96         free(buf);
97
98         fprintf(stderr, "%s: the failed input was: %s\n", progname, str);
99         return NULL;
100 }
101 #define rerender(str, p, r) (rerender(str, #p, p, #r, r))
102
103 static bool test_crossparse(const char *str, int mode)
104 {
105         char *buf1 = NULL, *buf2 = NULL, *buf3 = NULL;
106         bool ret = false;
107
108         if (mode == MODE_ENGLISH) {
109                 buf1 = rerender(str, cdecl_parse_english, cdecl_declare);
110                 if (!buf1)
111                         goto out;
112                 buf2 = rerender(buf1, cdecl_parse_decl, cdecl_explain);
113                 if (!buf2)
114                         goto out;
115                 buf3 = rerender(buf2, cdecl_parse_english, cdecl_declare);
116                 if (!buf3)
117                         goto out;
118         } else {
119                 buf1 = rerender(str, cdecl_parse_decl, cdecl_explain);
120                 if (!buf1)
121                         goto out;
122                 buf2 = rerender(buf1, cdecl_parse_english, cdecl_declare);
123                 if (!buf2)
124                         goto out;
125                 buf3 = rerender(buf2, cdecl_parse_decl, cdecl_explain);
126                 if (!buf3)
127                         goto out;
128         }
129
130         if (!strcmp(buf1, buf3))
131                 ret = true;
132 out:
133         free(buf1);
134         free(buf2);
135         free(buf3);
136
137         if (!ret) {
138                 fprintf(stderr, "%s: failed cross-parse check of: %s\n",
139                                 progname, str);
140         }
141
142         return ret;
143 }
144
145 int main(int argc, char **argv)
146 {
147         int opt, mode = MODE_CDECL;
148         int ret = EXIT_SUCCESS;
149
150         const char *filename = NULL;
151         FILE *infile = NULL;
152
153         if (argc > 0)
154                 progname = argv[0];
155
156         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
157                 switch (opt) {
158                 case 'C':
159                         mode = MODE_CDECL;
160                         break;
161                 case 'E':
162                         mode = MODE_ENGLISH;
163                         break;
164                 case 'f':
165                         infile = stdin;
166                         filename = optarg;
167                         break;
168                 case 'V':
169                         test_print_version(PROGNAME);
170                         return EXIT_SUCCESS;
171                 case 'H':
172                         print_help();
173                         return EXIT_SUCCESS;
174                 default:
175                         print_usage(stderr);
176                         return EXIT_FAILURE;
177                 }
178         }
179
180         if (infile) {
181                 char *line = NULL;
182                 size_t n;
183
184                 if (filename) {
185                         infile = fopen(filename, "r");
186                         if (!infile) {
187                                 fprintf(stderr, "%s: %s: %s\n", progname,
188                                                 filename, strerror(errno));
189                                 return EXIT_FAILURE;
190                         }
191                 }
192
193                 while (getline(&line, &n, infile) >= 0) {
194                         char *c = strchr(line, '\n');
195                         if (c)
196                                 *c = '\0';
197                         if (!test_crossparse(line, mode))
198                                 ret = EXIT_FAILURE;
199                 }
200         } else if (argv[optind]) {
201                 for (int i = optind; i < argc; i++) {
202                         if (!test_crossparse(argv[i], mode))
203                                 ret = EXIT_FAILURE;
204                 }
205         } else {
206                 print_usage(stderr);
207                 return EXIT_FAILURE;
208         }
209
210         return ret;
211 }