1*658eb9e1SMichael Kruse /*
2*658eb9e1SMichael Kruse Name: imtest.c
3*658eb9e1SMichael Kruse Purpose: Test driver for imath library.
4*658eb9e1SMichael Kruse Author: M. J. Fromberger
5*658eb9e1SMichael Kruse
6*658eb9e1SMichael Kruse Copyright (C) 2002-2008 Michael J. Fromberger, All Rights Reserved.
7*658eb9e1SMichael Kruse
8*658eb9e1SMichael Kruse Permission is hereby granted, free of charge, to any person obtaining a copy
9*658eb9e1SMichael Kruse of this software and associated documentation files (the "Software"), to deal
10*658eb9e1SMichael Kruse in the Software without restriction, including without limitation the rights
11*658eb9e1SMichael Kruse to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12*658eb9e1SMichael Kruse copies of the Software, and to permit persons to whom the Software is
13*658eb9e1SMichael Kruse furnished to do so, subject to the following conditions:
14*658eb9e1SMichael Kruse
15*658eb9e1SMichael Kruse The above copyright notice and this permission notice shall be included in
16*658eb9e1SMichael Kruse all copies or substantial portions of the Software.
17*658eb9e1SMichael Kruse
18*658eb9e1SMichael Kruse THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19*658eb9e1SMichael Kruse IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20*658eb9e1SMichael Kruse FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21*658eb9e1SMichael Kruse AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22*658eb9e1SMichael Kruse LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23*658eb9e1SMichael Kruse OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24*658eb9e1SMichael Kruse SOFTWARE.
25*658eb9e1SMichael Kruse
26*658eb9e1SMichael Kruse Reads tests from input files or standard input, and runs them. Tests have
27*658eb9e1SMichael Kruse the form:
28*658eb9e1SMichael Kruse
29*658eb9e1SMichael Kruse code:inputs:outputs
30*658eb9e1SMichael Kruse
31*658eb9e1SMichael Kruse The 'code' is a string identifying the test to be performed. The inputs and
32*658eb9e1SMichael Kruse outputs are comma-separated sequences of values. The format of each input
33*658eb9e1SMichael Kruse is:
34*658eb9e1SMichael Kruse
35*658eb9e1SMichael Kruse 1005 number in decimal notation (signs ok)
36*658eb9e1SMichael Kruse #x-C0E number in hexadecimal notation
37*658eb9e1SMichael Kruse #b1011 number in binary notation
38*658eb9e1SMichael Kruse #o37750 number in octal notation
39*658eb9e1SMichael Kruse =k use register k for this input
40*658eb9e1SMichael Kruse
41*658eb9e1SMichael Kruse For rational tests, the following syntax is also legal:
42*658eb9e1SMichael Kruse @5.33 use decimal notation (for rationals only)
43*658eb9e1SMichael Kruse may be combined with radix notation, e.g. #x@A0.5C
44*658eb9e1SMichael Kruse
45*658eb9e1SMichael Kruse Each output is a string representing the value to which the corresponding
46*658eb9e1SMichael Kruse result is compared in order to pass the test. By default, tests are expected
47*658eb9e1SMichael Kruse to succeed (i.e., return MP_OK). To specify an alternate return value, use
48*658eb9e1SMichael Kruse the notation $RESULT, where RESULT is the name of an error (e.g., MP_MEMORY,
49*658eb9e1SMichael Kruse MP_UNDEF, etc.) or a numeric result denoted $#number (e.g., $#-5).
50*658eb9e1SMichael Kruse
51*658eb9e1SMichael Kruse Results are written to standard output in the following formats:
52*658eb9e1SMichael Kruse
53*658eb9e1SMichael Kruse filename<tab>line<tab>number<tab>result<eoln>
54*658eb9e1SMichael Kruse filename<tab>line<tab>number<tab>result<tab>message<eoln>
55*658eb9e1SMichael Kruse
56*658eb9e1SMichael Kruse The filename and line give the offset of the test in its input file, the
57*658eb9e1SMichael Kruse number is the numbet of the test among all inputs, starting from 1.
58*658eb9e1SMichael Kruse The result is a textual description of the result code returned by the
59*658eb9e1SMichael Kruse operation being tested.
60*658eb9e1SMichael Kruse
61*658eb9e1SMichael Kruse The exit status is 0 if all tests passed, 1 if one or more tests failed or
62*658eb9e1SMichael Kruse had errors.
63*658eb9e1SMichael Kruse
64*658eb9e1SMichael Kruse Note: There is currently a fixed limit on the length of lines by this test
65*658eb9e1SMichael Kruse ---- driver. You can increase it if you wish, but the code doesn't check;
66*658eb9e1SMichael Kruse lines over the length are truncated (split).
67*658eb9e1SMichael Kruse */
68*658eb9e1SMichael Kruse
69*658eb9e1SMichael Kruse #include <assert.h>
70*658eb9e1SMichael Kruse #include <ctype.h>
71*658eb9e1SMichael Kruse #include <errno.h>
72*658eb9e1SMichael Kruse #include <limits.h>
73*658eb9e1SMichael Kruse #include <stdio.h>
74*658eb9e1SMichael Kruse #include <stdlib.h>
75*658eb9e1SMichael Kruse #include <string.h>
76*658eb9e1SMichael Kruse #include <time.h>
77*658eb9e1SMichael Kruse
78*658eb9e1SMichael Kruse #include "imath.h"
79*658eb9e1SMichael Kruse #include "imdrover.h"
80*658eb9e1SMichael Kruse
81*658eb9e1SMichael Kruse #ifdef LINE_MAX
82*658eb9e1SMichael Kruse #undef LINE_MAX
83*658eb9e1SMichael Kruse #endif
84*658eb9e1SMichael Kruse
85*658eb9e1SMichael Kruse #define LINE_MAX 4096
86*658eb9e1SMichael Kruse
87*658eb9e1SMichael Kruse typedef struct {
88*658eb9e1SMichael Kruse char *code;
89*658eb9e1SMichael Kruse int num_inputs;
90*658eb9e1SMichael Kruse int num_outputs;
91*658eb9e1SMichael Kruse test_f call;
92*658eb9e1SMichael Kruse } test_t;
93*658eb9e1SMichael Kruse
94*658eb9e1SMichael Kruse test_t g_tests[] = {
95*658eb9e1SMichael Kruse /* What it does... */
96*658eb9e1SMichael Kruse {"initu", 2, 1, test_init}, /* r0 = uv(r1) */
97*658eb9e1SMichael Kruse {"initv", 2, 1, test_init}, /* r0 = v(r1) */
98*658eb9e1SMichael Kruse {"setu", 2, 1, test_set}, /* r0 = uv(r1) */
99*658eb9e1SMichael Kruse {"setv", 2, 1, test_set}, /* r0 = v(r1) */
100*658eb9e1SMichael Kruse {"neg", 2, 1, test_neg}, /* r1 = -r0 */
101*658eb9e1SMichael Kruse {"abs", 2, 1, test_abs}, /* r1 = |r0| */
102*658eb9e1SMichael Kruse {"add", 3, 1, test_add}, /* r3 = r1 + r2 */
103*658eb9e1SMichael Kruse {"addv", 3, 1, test_add}, /* r3 = r1 + v(r2) */
104*658eb9e1SMichael Kruse {"sub", 3, 1, test_sub}, /* r3 = r1 - r2 */
105*658eb9e1SMichael Kruse {"subv", 3, 1, test_sub}, /* r3 = r1 - v(r2) */
106*658eb9e1SMichael Kruse {"mul", 3, 1, test_mul}, /* r3 = r1 * r2 */
107*658eb9e1SMichael Kruse {"mulp2", 3, 1, test_mulp2}, /* r3 = r1 * 2^v(r2) */
108*658eb9e1SMichael Kruse {"mulv", 3, 1, test_mulv}, /* r3 = r1 * v(r2) */
109*658eb9e1SMichael Kruse {"sqr", 2, 1, test_sqr}, /* r2 = r1 * r1 */
110*658eb9e1SMichael Kruse {"div", 4, 2, test_div}, /* r2 = r1 / r2, r3 = r1 % r2 */
111*658eb9e1SMichael Kruse {"divp2", 4, 2, test_divp2}, /* r2 = r1 / 2^v(r2),r3 = r1 % 2^v(r2)*/
112*658eb9e1SMichael Kruse {"divv", 3, 2, test_divv}, /* r2 = r1 / v(r2), r3 = r1 % v(r2) */
113*658eb9e1SMichael Kruse {"expt", 3, 1, test_expt}, /* r3 = r1 ^ v(r2) */
114*658eb9e1SMichael Kruse {"exptv", 3, 1, test_exptv}, /* r3 = v(r1) ^ v(r2) */
115*658eb9e1SMichael Kruse {"exptf", 3, 1, test_exptf}, /* r3 = r1 ^ r2 */
116*658eb9e1SMichael Kruse {"mod", 3, 1, test_mod}, /* r3 = r1 % r2 */
117*658eb9e1SMichael Kruse {"gcd", 3, 1, test_gcd}, /* r3 = gcd(r1, r2) */
118*658eb9e1SMichael Kruse {"egcd", 5, 3, test_egcd}, /* r3 = gcd(r1, r2) = r1*r4 + r2*r5 */
119*658eb9e1SMichael Kruse {"lcm", 3, 1, test_lcm}, /* r3 = lcm(r1, r2) */
120*658eb9e1SMichael Kruse {"sqrt", 2, 1, test_sqrt}, /* r2 = sqrt(r1) */
121*658eb9e1SMichael Kruse {"root", 3, 1, test_root}, /* r3 = r1^(1/v(r2)) */
122*658eb9e1SMichael Kruse {"invmod", 3, 1, test_invmod}, /* r3 = r1^-1 mod r2 */
123*658eb9e1SMichael Kruse {"emod", 4, 1, test_exptmod}, /* r4 = r1^r2 mod r3 */
124*658eb9e1SMichael Kruse {"emodev", 4, 1, test_exptmod_ev}, /* r4 = r1^v(r2) mod r3 */
125*658eb9e1SMichael Kruse {"emodbv", 4, 1, test_exptmod_bv}, /* r4 = v(r1)^r2 mod r3 */
126*658eb9e1SMichael Kruse {"cmp", 2, 1, test_comp}, /* rtn = compare(r1, r2) */
127*658eb9e1SMichael Kruse {"cmpu", 2, 1, test_ucomp}, /* rtn = compare(|r1|, |r2|) */
128*658eb9e1SMichael Kruse {"cmpz", 1, 1, test_zcomp}, /* rtn = compare(r1, 0) */
129*658eb9e1SMichael Kruse {"cmpv", 2, 1, test_vcomp}, /* rtn = compare(r1, v(r2)) */
130*658eb9e1SMichael Kruse {"cmpuv", 2, 1, test_uvcomp}, /* rtn = compare(r1, v(r2)) */
131*658eb9e1SMichael Kruse {"tostr", 2, 1, test_tostr}, /* r1: value, r2: radix, o1: result */
132*658eb9e1SMichael Kruse {"tobin", 1, 1, test_tobin}, /* r1: value, o1: result binary */
133*658eb9e1SMichael Kruse {"readbin", 1, 1, test_read_binary}, /* r1: 2's comp, o1: result value */
134*658eb9e1SMichael Kruse {"to-uns", 1, 1, test_to_uns}, /* r1: value, o1: result binary */
135*658eb9e1SMichael Kruse {"readuns", 1, 1, test_read_uns}, /* r1: unsigned, o1: result value */
136*658eb9e1SMichael Kruse {"to-int", 1, 1, test_to_int}, /* r1: value, o1: result */
137*658eb9e1SMichael Kruse {"to-uint", 1, 1, test_to_uint}, /* r1: value, o1: result */
138*658eb9e1SMichael Kruse {"meta", -1, -1, test_meta},
139*658eb9e1SMichael Kruse {"qneg", 2, 1, test_qneg}, /* r2 = -r1 */
140*658eb9e1SMichael Kruse {"qrecip", 2, 1, test_qrecip}, /* r2 = 1 / r1 */
141*658eb9e1SMichael Kruse {"qabs", 2, 1, test_qabs}, /* r2 = |r1| */
142*658eb9e1SMichael Kruse {"qadd", 3, 1, test_qadd}, /* r3 = r1 + r2 */
143*658eb9e1SMichael Kruse {"qsub", 3, 1, test_qsub}, /* r3 = r1 - r2 */
144*658eb9e1SMichael Kruse {"qmul", 3, 1, test_qmul}, /* r3 = r1 * r2 */
145*658eb9e1SMichael Kruse {"qdiv", 3, 1, test_qdiv}, /* r3 = r1 / r2 */
146*658eb9e1SMichael Kruse {"qaddz", 3, 1, test_qaddz}, /* r3 = r1 + r2 */
147*658eb9e1SMichael Kruse {"qsubz", 3, 1, test_qsubz}, /* r3 = r1 - r2 */
148*658eb9e1SMichael Kruse {"qmulz", 3, 1, test_qmulz}, /* r3 = r1 * r2 */
149*658eb9e1SMichael Kruse {"qdivz", 3, 1, test_qdivz}, /* r3 = r1 / r2 */
150*658eb9e1SMichael Kruse {"qexpt", 3, 1, test_qexpt}, /* r3 = r1 ^ v(r2) */
151*658eb9e1SMichael Kruse {"qtostr", 2, 1, test_qtostr}, /* r1: value, r2: radix; o1: result */
152*658eb9e1SMichael Kruse {"qtodec", 4, 1, test_qtodec}, /* r1: val, r2: rdx, r3: prec,
153*658eb9e1SMichael Kruse r4: rounding mode; o1: res */
154*658eb9e1SMichael Kruse {"qrdec", 2, 1, test_qrdec}, /* r1: dec, r2: rdx; o1: result value */
155*658eb9e1SMichael Kruse {"isprime", 1, 1, test_is_prime}, /* rtn = prime(r1) ? MP_TRUE : MP_FALSE */
156*658eb9e1SMichael Kruse {NULL, 0, 0, NULL} /* end of list marker */
157*658eb9e1SMichael Kruse };
158*658eb9e1SMichael Kruse
159*658eb9e1SMichael Kruse char g_line[LINE_MAX];
160*658eb9e1SMichael Kruse
161*658eb9e1SMichael Kruse extern mp_result imath_errno;
162*658eb9e1SMichael Kruse extern char *imath_errmsg;
163*658eb9e1SMichael Kruse
164*658eb9e1SMichael Kruse const char *g_imath_strerr[] = {"MP_OK", "MP_TRUE", "MP_MEMORY", "MP_RANGE",
165*658eb9e1SMichael Kruse "MP_UNDEF", "MP_TRUNC", "MP_BADARG"};
166*658eb9e1SMichael Kruse
167*658eb9e1SMichael Kruse bool process_file(char *file_name, FILE *ifp, FILE *ofp);
168*658eb9e1SMichael Kruse int read_line(FILE *ifp, char *line, int limit);
169*658eb9e1SMichael Kruse void trim_line(char *line);
170*658eb9e1SMichael Kruse int is_blank(char *line);
171*658eb9e1SMichael Kruse int parse_line(char *line, testspec_t *t);
172*658eb9e1SMichael Kruse int count_fields(char *line, int delim);
173*658eb9e1SMichael Kruse void parse_fields(char *line, int delim, char **start);
174*658eb9e1SMichael Kruse int run_test(int test_num, testspec_t *t, FILE *ofp);
175*658eb9e1SMichael Kruse void free_test(testspec_t *t);
176*658eb9e1SMichael Kruse int find_test(char *code, test_t *info);
177*658eb9e1SMichael Kruse char *error_string(mp_result res);
178*658eb9e1SMichael Kruse
main(int argc,char * argv[])179*658eb9e1SMichael Kruse int main(int argc, char *argv[]) {
180*658eb9e1SMichael Kruse int exit_status = 0;
181*658eb9e1SMichael Kruse
182*658eb9e1SMichael Kruse init_testing();
183*658eb9e1SMichael Kruse
184*658eb9e1SMichael Kruse if (argc == 1) {
185*658eb9e1SMichael Kruse fprintf(stderr, "[reading from stdin]\n");
186*658eb9e1SMichael Kruse if (!process_file("-", stdin, stdout)) exit_status = 1;
187*658eb9e1SMichael Kruse } else {
188*658eb9e1SMichael Kruse FILE *ifp;
189*658eb9e1SMichael Kruse int i;
190*658eb9e1SMichael Kruse
191*658eb9e1SMichael Kruse for (i = 1; i < argc; ++i) {
192*658eb9e1SMichael Kruse if (strcmp(argv[i], "-") == 0) {
193*658eb9e1SMichael Kruse ifp = stdin;
194*658eb9e1SMichael Kruse } else if ((ifp = fopen(argv[i], "r")) == NULL) {
195*658eb9e1SMichael Kruse fprintf(stderr, "Cannot open '%s': %s\n", argv[i], strerror(errno));
196*658eb9e1SMichael Kruse return 1;
197*658eb9e1SMichael Kruse }
198*658eb9e1SMichael Kruse if (!process_file(argv[i], ifp, stdout)) exit_status = 1;
199*658eb9e1SMichael Kruse
200*658eb9e1SMichael Kruse fclose(ifp);
201*658eb9e1SMichael Kruse }
202*658eb9e1SMichael Kruse }
203*658eb9e1SMichael Kruse return exit_status;
204*658eb9e1SMichael Kruse }
205*658eb9e1SMichael Kruse
206*658eb9e1SMichael Kruse /** Reads and runs test cases from `ifp` and writes test results to `ofp`. The
207*658eb9e1SMichael Kruse given `file_name` is used for cosmetic attribution. The return value is
208*658eb9e1SMichael Kruse true if all tests passed, false if any tests failed or had errors. */
process_file(char * file_name,FILE * ifp,FILE * ofp)209*658eb9e1SMichael Kruse bool process_file(char *file_name, FILE *ifp, FILE *ofp) {
210*658eb9e1SMichael Kruse int res, line_num, test_num = 0, num_failed = 0, num_bogus = 0;
211*658eb9e1SMichael Kruse clock_t start, finish;
212*658eb9e1SMichael Kruse
213*658eb9e1SMichael Kruse start = clock();
214*658eb9e1SMichael Kruse while ((line_num = read_line(ifp, g_line, LINE_MAX)) != 0) {
215*658eb9e1SMichael Kruse testspec_t t;
216*658eb9e1SMichael Kruse t.line = line_num;
217*658eb9e1SMichael Kruse t.file = file_name;
218*658eb9e1SMichael Kruse if (parse_line(g_line, &t)) {
219*658eb9e1SMichael Kruse if ((res = run_test(++test_num, &t, ofp)) < 0) {
220*658eb9e1SMichael Kruse ++num_bogus;
221*658eb9e1SMichael Kruse } else if (res == 0) {
222*658eb9e1SMichael Kruse ++num_failed;
223*658eb9e1SMichael Kruse }
224*658eb9e1SMichael Kruse free_test(&t);
225*658eb9e1SMichael Kruse } else {
226*658eb9e1SMichael Kruse fprintf(stderr, "Line %d: Incorrect input syntax.\n", line_num);
227*658eb9e1SMichael Kruse ++num_bogus;
228*658eb9e1SMichael Kruse }
229*658eb9e1SMichael Kruse }
230*658eb9e1SMichael Kruse finish = clock();
231*658eb9e1SMichael Kruse
232*658eb9e1SMichael Kruse fprintf(ofp,
233*658eb9e1SMichael Kruse "# %s %d tests: %d passed, %d failed, %d errors. (%.2f seconds)\n",
234*658eb9e1SMichael Kruse file_name, test_num, (test_num - num_failed - num_bogus), num_failed,
235*658eb9e1SMichael Kruse num_bogus, ((double)(finish - start) / CLOCKS_PER_SEC));
236*658eb9e1SMichael Kruse
237*658eb9e1SMichael Kruse return num_failed == 0 && num_bogus == 0;
238*658eb9e1SMichael Kruse }
239*658eb9e1SMichael Kruse
read_line(FILE * ifp,char * line,int limit)240*658eb9e1SMichael Kruse int read_line(FILE *ifp, char *line, int limit) {
241*658eb9e1SMichael Kruse static FILE *current_fp = NULL;
242*658eb9e1SMichael Kruse static int current_line = 0;
243*658eb9e1SMichael Kruse
244*658eb9e1SMichael Kruse if (ifp != current_fp) {
245*658eb9e1SMichael Kruse current_fp = ifp;
246*658eb9e1SMichael Kruse current_line = 0;
247*658eb9e1SMichael Kruse }
248*658eb9e1SMichael Kruse
249*658eb9e1SMichael Kruse do {
250*658eb9e1SMichael Kruse if (fgets(line, limit, ifp) == NULL) return 0;
251*658eb9e1SMichael Kruse
252*658eb9e1SMichael Kruse ++current_line;
253*658eb9e1SMichael Kruse } while (is_blank(line));
254*658eb9e1SMichael Kruse
255*658eb9e1SMichael Kruse trim_line(line);
256*658eb9e1SMichael Kruse return current_line;
257*658eb9e1SMichael Kruse }
258*658eb9e1SMichael Kruse
259*658eb9e1SMichael Kruse /** Removes leading and trailing whitespace from a zero-terminated `line`. */
trim_line(char * line)260*658eb9e1SMichael Kruse void trim_line(char *line) {
261*658eb9e1SMichael Kruse int len;
262*658eb9e1SMichael Kruse char *fnw = line;
263*658eb9e1SMichael Kruse
264*658eb9e1SMichael Kruse /* Remove leading whitespace */
265*658eb9e1SMichael Kruse while (isspace((unsigned char)*fnw)) ++fnw;
266*658eb9e1SMichael Kruse
267*658eb9e1SMichael Kruse len = strlen(fnw);
268*658eb9e1SMichael Kruse memmove(line, fnw, len);
269*658eb9e1SMichael Kruse
270*658eb9e1SMichael Kruse /* Remove trailing whitespace (including linefeeds) */
271*658eb9e1SMichael Kruse fnw = line + len - 1;
272*658eb9e1SMichael Kruse while (fnw >= line && isspace((unsigned char)*fnw)) *fnw-- = '\0';
273*658eb9e1SMichael Kruse }
274*658eb9e1SMichael Kruse
275*658eb9e1SMichael Kruse /** Reports whether a zero-terminated `line` contains only whitespace after a
276*658eb9e1SMichael Kruse line-trailing comment (`# ...`) is removed. */
is_blank(char * line)277*658eb9e1SMichael Kruse int is_blank(char *line) {
278*658eb9e1SMichael Kruse while (*line && *line != '#' && isspace((unsigned char)*line)) ++line;
279*658eb9e1SMichael Kruse
280*658eb9e1SMichael Kruse return *line == '\0' || *line == '#';
281*658eb9e1SMichael Kruse }
282*658eb9e1SMichael Kruse
parse_line(char * line,testspec_t * t)283*658eb9e1SMichael Kruse int parse_line(char *line, testspec_t *t) {
284*658eb9e1SMichael Kruse char *code_brk, *in_brk;
285*658eb9e1SMichael Kruse int num_fields;
286*658eb9e1SMichael Kruse
287*658eb9e1SMichael Kruse if ((code_brk = strchr(line, ':')) == NULL) return 0;
288*658eb9e1SMichael Kruse if ((in_brk = strchr(code_brk + 1, ':')) == NULL) return 0;
289*658eb9e1SMichael Kruse
290*658eb9e1SMichael Kruse *code_brk = '\0';
291*658eb9e1SMichael Kruse t->code = line;
292*658eb9e1SMichael Kruse *in_brk = '\0';
293*658eb9e1SMichael Kruse
294*658eb9e1SMichael Kruse num_fields = count_fields(code_brk + 1, ',');
295*658eb9e1SMichael Kruse t->num_inputs = num_fields;
296*658eb9e1SMichael Kruse t->input = NULL;
297*658eb9e1SMichael Kruse
298*658eb9e1SMichael Kruse num_fields = count_fields(in_brk + 1, ',');
299*658eb9e1SMichael Kruse t->num_outputs = num_fields;
300*658eb9e1SMichael Kruse t->output = NULL;
301*658eb9e1SMichael Kruse
302*658eb9e1SMichael Kruse if (t->num_inputs > 0) {
303*658eb9e1SMichael Kruse t->input = calloc(t->num_inputs, sizeof(char *));
304*658eb9e1SMichael Kruse parse_fields(code_brk + 1, ',', t->input);
305*658eb9e1SMichael Kruse }
306*658eb9e1SMichael Kruse if (t->num_outputs > 0) {
307*658eb9e1SMichael Kruse t->output = calloc(t->num_outputs, sizeof(char *));
308*658eb9e1SMichael Kruse parse_fields(in_brk + 1, ',', t->output);
309*658eb9e1SMichael Kruse }
310*658eb9e1SMichael Kruse return 1;
311*658eb9e1SMichael Kruse }
312*658eb9e1SMichael Kruse
313*658eb9e1SMichael Kruse /** Returns the number of `delim` separated fields occur in `line`. */
count_fields(char * line,int delim)314*658eb9e1SMichael Kruse int count_fields(char *line, int delim) {
315*658eb9e1SMichael Kruse int count = 1;
316*658eb9e1SMichael Kruse
317*658eb9e1SMichael Kruse if (*line == '\0') return 0;
318*658eb9e1SMichael Kruse
319*658eb9e1SMichael Kruse while (*line) {
320*658eb9e1SMichael Kruse if (*line == (char)delim && *(line + 1) != '\0') ++count;
321*658eb9e1SMichael Kruse ++line;
322*658eb9e1SMichael Kruse }
323*658eb9e1SMichael Kruse return count;
324*658eb9e1SMichael Kruse }
325*658eb9e1SMichael Kruse
parse_fields(char * line,int delim,char ** start)326*658eb9e1SMichael Kruse void parse_fields(char *line, int delim, char **start) {
327*658eb9e1SMichael Kruse int pos = 0;
328*658eb9e1SMichael Kruse
329*658eb9e1SMichael Kruse start[pos++] = line;
330*658eb9e1SMichael Kruse while ((line = strchr(line, delim)) != NULL) {
331*658eb9e1SMichael Kruse *line++ = '\0';
332*658eb9e1SMichael Kruse start[pos++] = line;
333*658eb9e1SMichael Kruse }
334*658eb9e1SMichael Kruse }
335*658eb9e1SMichael Kruse
336*658eb9e1SMichael Kruse /** Runs the test cases specified by `t`, and writes its results to `ofp`. The
337*658eb9e1SMichael Kruse `test_num` is used in log output and should reflect the global ordering of
338*658eb9e1SMichael Kruse tests, but is not otherwise interpreted by this function.
339*658eb9e1SMichael Kruse
340*658eb9e1SMichael Kruse This function returns 0 if the test succeeds, 1 if the test fails, and -1
341*658eb9e1SMichael Kruse if the test is broken (e.g., its code is unknown). */
run_test(int test_num,testspec_t * t,FILE * ofp)342*658eb9e1SMichael Kruse int run_test(int test_num, testspec_t *t, FILE *ofp) {
343*658eb9e1SMichael Kruse test_t info;
344*658eb9e1SMichael Kruse
345*658eb9e1SMichael Kruse /* Look up and reality check test parameters */
346*658eb9e1SMichael Kruse if (find_test(t->code, &info) < 0) {
347*658eb9e1SMichael Kruse fprintf(stderr, "Line %d: Test code '%s' is unknown.\n", t->line, t->code);
348*658eb9e1SMichael Kruse return -1;
349*658eb9e1SMichael Kruse } else {
350*658eb9e1SMichael Kruse int errs = 0;
351*658eb9e1SMichael Kruse
352*658eb9e1SMichael Kruse if (info.num_inputs >= 0 && t->num_inputs != info.num_inputs) {
353*658eb9e1SMichael Kruse fprintf(stderr,
354*658eb9e1SMichael Kruse "Line %d: Wrong number of inputs to %s (want %d, have %d)\n",
355*658eb9e1SMichael Kruse t->line, t->code, info.num_inputs, t->num_inputs);
356*658eb9e1SMichael Kruse ++errs;
357*658eb9e1SMichael Kruse }
358*658eb9e1SMichael Kruse if (info.num_outputs >= 0 && t->num_outputs != info.num_outputs) {
359*658eb9e1SMichael Kruse fprintf(stderr,
360*658eb9e1SMichael Kruse "Line %d: Wrong number of outputs to %s (want %d, have %d)\n",
361*658eb9e1SMichael Kruse t->line, t->code, info.num_outputs, t->num_outputs);
362*658eb9e1SMichael Kruse ++errs;
363*658eb9e1SMichael Kruse }
364*658eb9e1SMichael Kruse if (errs) {
365*658eb9e1SMichael Kruse fprintf(stderr, "Line %d: %d error(s), skipping this test.\n", t->line,
366*658eb9e1SMichael Kruse errs);
367*658eb9e1SMichael Kruse return -1;
368*658eb9e1SMichael Kruse }
369*658eb9e1SMichael Kruse }
370*658eb9e1SMichael Kruse
371*658eb9e1SMichael Kruse /* If return value is true, just print a generic OK message;
372*658eb9e1SMichael Kruse otherwise, it is assumed that imath_errno has been set to
373*658eb9e1SMichael Kruse a value indicating the problem. */
374*658eb9e1SMichael Kruse if ((info.call)(t, ofp)) {
375*658eb9e1SMichael Kruse fprintf(ofp, "%s\t%d\t%d\tOK\n", t->file, t->line, test_num);
376*658eb9e1SMichael Kruse return 1;
377*658eb9e1SMichael Kruse } else if (imath_errno >= MP_BADARG) {
378*658eb9e1SMichael Kruse fprintf(ofp, "%s\t%d\t%d\t%s\n", t->file, t->line, test_num,
379*658eb9e1SMichael Kruse error_string(imath_errno));
380*658eb9e1SMichael Kruse } else {
381*658eb9e1SMichael Kruse fprintf(ofp, "%s\t%d\t%d\tFAILED\t%s\n", t->file, t->line, test_num,
382*658eb9e1SMichael Kruse imath_errmsg);
383*658eb9e1SMichael Kruse }
384*658eb9e1SMichael Kruse return 0;
385*658eb9e1SMichael Kruse }
386*658eb9e1SMichael Kruse
387*658eb9e1SMichael Kruse /** Locates the run instructions for the specified test `code`, and if they are
388*658eb9e1SMichael Kruse found populates `*info` with a copy. It returns -1 if `code` is unknown. */
find_test(char * code,test_t * info)389*658eb9e1SMichael Kruse int find_test(char *code, test_t *info) {
390*658eb9e1SMichael Kruse int i = 0;
391*658eb9e1SMichael Kruse
392*658eb9e1SMichael Kruse while (g_tests[i].code != NULL) {
393*658eb9e1SMichael Kruse if (strcmp(g_tests[i].code, code) == 0) {
394*658eb9e1SMichael Kruse *info = g_tests[i];
395*658eb9e1SMichael Kruse return i;
396*658eb9e1SMichael Kruse }
397*658eb9e1SMichael Kruse ++i;
398*658eb9e1SMichael Kruse }
399*658eb9e1SMichael Kruse return -1;
400*658eb9e1SMichael Kruse }
401*658eb9e1SMichael Kruse
402*658eb9e1SMichael Kruse /** Releases the memory occupied by a test case invocation. */
free_test(testspec_t * t)403*658eb9e1SMichael Kruse void free_test(testspec_t *t) {
404*658eb9e1SMichael Kruse assert(t != NULL);
405*658eb9e1SMichael Kruse
406*658eb9e1SMichael Kruse if (t->input != NULL) {
407*658eb9e1SMichael Kruse free(t->input);
408*658eb9e1SMichael Kruse t->input = NULL;
409*658eb9e1SMichael Kruse }
410*658eb9e1SMichael Kruse if (t->output != NULL) {
411*658eb9e1SMichael Kruse free(t->output);
412*658eb9e1SMichael Kruse t->output = NULL;
413*658eb9e1SMichael Kruse }
414*658eb9e1SMichael Kruse }
415*658eb9e1SMichael Kruse
416*658eb9e1SMichael Kruse /** Returns a static label string describing `res`. Note that this is not the
417*658eb9e1SMichael Kruse same as the error string returned by `mp_error_string`, but corresponds to
418*658eb9e1SMichael Kruse the spelling of the constant for its value. */
error_string(mp_result res)419*658eb9e1SMichael Kruse char *error_string(mp_result res) {
420*658eb9e1SMichael Kruse int v = abs(res);
421*658eb9e1SMichael Kruse
422*658eb9e1SMichael Kruse return (char *)g_imath_strerr[v];
423*658eb9e1SMichael Kruse }
424*658eb9e1SMichael Kruse
425*658eb9e1SMichael Kruse /* Here there be dragons */
426