]> git.draconx.ca Git - cdecl99.git/blob - test/crossparse.c
Add better --help text to the test programs.
[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 <stdbool.h>
24 #include <getopt.h>
25 #include <cdecl.h>
26 #include "test.h"
27
28 #define PROGNAME "crossparse"
29 static const char *progname = PROGNAME;
30 static const char sopts[] = "ECVH";
31 static const struct option lopts[] = {
32         { "cdecl",   0, NULL, 'C' },
33         { "english", 0, NULL, 'E' },
34         { "version", 0, NULL, 'V' },
35         { "help",    0, NULL, 'H' },
36         { 0 }
37 };
38
39 static void print_usage(FILE *f)
40 {
41         fprintf(f, "Usage: %s [options]\n", progname);
42 }
43
44 static void print_help(void)
45 {
46         const struct option *opt;
47
48         print_usage(stdout);
49         puts("Test that libcdecl can parse its own output.\n");
50
51         puts("Options:");
52         for (opt = lopts; opt->val; opt++) {
53                 int w = print_option_start(opt, NULL);
54
55                 if (w)
56                         putchar('\n');
57         }
58 }
59
60 enum {
61         MODE_CDECL,
62         MODE_ENGLISH,
63 };
64
65 typedef struct cdecl *parse_func(const char *);
66 typedef size_t render_func(char *, size_t, struct cdecl *);
67
68 char *rerender(const char *str, const char *parse_name,  parse_func *parse,
69                                 const char *render_name, render_func *render)
70 {
71         struct cdecl *decl = NULL;
72         char *buf = NULL;
73         size_t len;
74
75         decl = parse(str);
76         if (!decl) {
77                 fprintf(stderr, "%s: %s: failed to parse input: %s\n",
78                                 progname, parse_name, cdecl_get_error()->str);
79                 goto err;
80         }
81
82         len = render(NULL, 0, decl);
83         buf = malloc_nofail(len+1);
84         if (render(buf, len+1, decl) != len) {
85                 fprintf(stderr, "%s: %s: inconsistent length returned\n",
86                                 progname, render_name);
87                 goto err;
88         }
89
90         cdecl_free(decl);
91         return buf;
92 err:
93         cdecl_free(decl);
94         free(buf);
95
96         fprintf(stderr, "%s: the failed input was: %s\n", progname, str);
97         return NULL;
98 }
99 #define rerender(str, p, r) (rerender(str, #p, p, #r, r))
100
101 static bool test_crossparse(const char *str, int mode)
102 {
103         char *buf1 = NULL, *buf2 = NULL, *buf3 = NULL;
104         bool ret = false;
105
106         if (mode == MODE_ENGLISH) {
107                 buf1 = rerender(str, cdecl_parse_english, cdecl_declare);
108                 if (!buf1)
109                         goto out;
110                 buf2 = rerender(buf1, cdecl_parse_decl, cdecl_explain);
111                 if (!buf2)
112                         goto out;
113                 buf3 = rerender(buf2, cdecl_parse_english, cdecl_declare);
114                 if (!buf3)
115                         goto out;
116         } else {
117                 buf1 = rerender(str, cdecl_parse_decl, cdecl_explain);
118                 if (!buf1)
119                         goto out;
120                 buf2 = rerender(buf1, cdecl_parse_english, cdecl_declare);
121                 if (!buf2)
122                         goto out;
123                 buf3 = rerender(buf2, cdecl_parse_decl, cdecl_explain);
124                 if (!buf3)
125                         goto out;
126         }
127
128         if (!strcmp(buf1, buf3))
129                 ret = true;
130 out:
131         free(buf1);
132         free(buf2);
133         free(buf3);
134         return ret;
135 }
136
137 int main(int argc, char **argv)
138 {
139         int opt, mode = MODE_CDECL;
140         int ret = EXIT_FAILURE;
141
142         if (argc > 0)
143                 progname = argv[0];
144
145         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
146                 switch (opt) {
147                 case 'C':
148                         mode = MODE_CDECL;
149                         break;
150                 case 'E':
151                         mode = MODE_ENGLISH;
152                         break;
153                 case 'V':
154                         test_print_version(PROGNAME);
155                         return EXIT_SUCCESS;
156                 case 'H':
157                         print_help();
158                         return EXIT_SUCCESS;
159                 default:
160                         print_usage(stderr);
161                         return EXIT_FAILURE;
162                 }
163         }
164
165         if (!argv[optind]) {
166                 print_usage(stderr);
167                 return EXIT_FAILURE;
168         }
169
170         for (int i = optind; i < argc; i++) {
171                 if (!test_crossparse(argv[i], mode)) {
172                         fprintf(stderr, "%s: failed cross-parse check of: %s\n",
173                                         progname, argv[i]);
174                         return EXIT_FAILURE;
175                 }
176         }
177
178         return EXIT_SUCCESS;
179 }