Commit | Line | Data |
---|---|---|
b64eb60f MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Test vector processing framework | |
4 | * | |
5 | * (c) 2023 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of the mLib utilities library. | |
11 | * | |
12 | * mLib is free software: you can redistribute it and/or modify it under | |
13 | * the terms of the GNU Library General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or (at | |
15 | * your option) any later version. | |
16 | * | |
17 | * mLib is distributed in the hope that it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | |
20 | * License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU Library General Public | |
23 | * License along with mLib. If not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |
25 | * USA. | |
26 | */ | |
27 | ||
28 | #ifndef MLIB_TVEC_H | |
29 | #define MLIB_TVEC_H | |
30 | ||
31 | #ifdef __cplusplus | |
32 | extern "C" { | |
33 | #endif | |
34 | ||
35 | /*----- Header files ------------------------------------------------------*/ | |
36 | ||
37 | #include <stdarg.h> | |
38 | #include <stddef.h> | |
39 | #include <stdio.h> | |
40 | #include <string.h> | |
41 | ||
42 | #ifndef MLIB_BUF_H | |
43 | # include "buf.h" | |
44 | #endif | |
45 | ||
46 | #ifndef MLIB_CONTROL_H | |
47 | # include "control.h" | |
48 | #endif | |
49 | ||
50 | #ifndef MLIB_BUF_H | |
51 | # include "dstr.h" | |
52 | #endif | |
53 | ||
e63124bc MW |
54 | #ifndef MLIB_GPRINTF_H |
55 | # include "gprintf.h" | |
56 | #endif | |
57 | ||
b64eb60f MW |
58 | #ifndef MLIB_MACROS_H |
59 | # include "macros.h" | |
60 | #endif | |
61 | ||
62 | /*----- Miscellaneous values ----------------------------------------------*/ | |
63 | ||
64 | /* These are attached to structures which represent extension points, as a | |
65 | * way to pass an opaque parameter to whatever things are hooked onto them. | |
66 | */ | |
67 | ||
68 | #define TVEC_MISCSLOTS(_) \ | |
69 | _(PTR, const void *, p) /* arbitrary pointer */ \ | |
70 | _(INT, long, i) /* signed integer */ \ | |
e63124bc MW |
71 | _(UINT, unsigned long, u) /* signed integer */ \ |
72 | _(FLT, double, f) /* floating point */ | |
b64eb60f MW |
73 | |
74 | union tvec_misc { | |
75 | #define TVEC_DEFSLOT(tag, ty, slot) ty slot; | |
76 | TVEC_MISCSLOTS(TVEC_DEFSLOT) | |
77 | #undef TVEC_DEFSLOT | |
78 | }; | |
79 | enum { | |
80 | #define TVEC_DEFCONST(tag, ty, slot) TVMISC_##tag, | |
81 | TVEC_MISCSLOTS(TVEC_DEFCONST) | |
82 | TVMISC_LIMIT | |
83 | }; | |
84 | ||
85 | /*----- Register values ---------------------------------------------------*/ | |
86 | ||
87 | /* The framework doesn't have a preconceived idea about what's in a register | |
88 | * value: it just allocates them and accesses them through the register type | |
89 | * functions. It doesn't even have a baked-in idea of how big a register | |
90 | * value is: instead, it gets that via the `regsz' slot in `struct | |
91 | * tvec_testinfo'. So, as far as the framework is concerned, it's safe to | |
92 | * add new slots to this union, even if they make the overall union larger. | |
93 | * This can be done by defining the preprocessor macro `TVEC_REGSLOTS' to be | |
94 | * a `union' fragment defining any additional union members. | |
95 | * | |
96 | * This creates a distinction between code which does and doesn't know the | |
97 | * size of a register value. Code which does, which typically means the test | |
98 | * functions, benchmarking setup and teardown functions, and tightly-bound | |
99 | * runner functions, is free to index the register vectors directly. Code | |
100 | * which doesn't, which means the framework core itself and output formatting | |
101 | * machinery, must use the `TVEC_REG' macro (or its more general `TVEC_GREG' | |
102 | * companion) for indexing register vectors. (In principle, register type | |
103 | * handlers also fit into this category, but they have no business peering | |
104 | * into register values other than the one's they're given.) | |
105 | */ | |
106 | ||
107 | union tvec_regval { | |
108 | /* The actual register value. This is what the type handler sees. | |
109 | * Additional members can be added by setting `TVEC_REGSLOTS' before | |
110 | * including this file. | |
111 | * | |
112 | * A register value can be /initialized/, which simply means that its | |
113 | * contents represent a valid value according to its type -- the | |
114 | * register can be compared, dumped, serialized, parsed into, etc. | |
115 | * You can't do anything safely to an uninitialized register value | |
116 | * other than initialize it. | |
117 | */ | |
118 | ||
119 | long i; /* signed integer */ | |
120 | unsigned long u; /* unsigned integer */ | |
121 | void *p; /* pointer */ | |
e63124bc | 122 | double f; /* floating point */ |
b64eb60f MW |
123 | struct { unsigned char *p; size_t sz; } bytes; /* binary string of bytes */ |
124 | struct { char *p; size_t sz; } str; /* text string */ | |
125 | #ifdef TVEC_REGSLOTS | |
126 | TVEC_REGSLOTS | |
127 | #endif | |
128 | }; | |
129 | ||
130 | struct tvec_reg { | |
131 | /* A register. | |
132 | * | |
133 | * Note that all of the registers listed as being used by a | |
134 | * particular test group are initialized at all times[1] while that | |
135 | * test group is being processed. (The other register slots don't | |
136 | * even have types associated with them, so there's nothing useful we | |
137 | * could do with them.) | |
138 | * | |
139 | * The `TVRF_LIVE' flag indicates that the register was assigned a | |
140 | * value by the test vector file: it's the right thing to use to | |
141 | * check whether an optional register is actually present. Even | |
142 | * `dead' registers are still initialized, though. | |
143 | * | |
144 | * [1] This isn't quite true. Between individual tests, the | |
145 | * registers are released and reinitialized in order to reset | |
146 | * them to known values ready for the next test. But you won't | |
147 | * see them at this point. | |
148 | */ | |
149 | ||
150 | unsigned f; /* flags */ | |
151 | #define TVRF_LIVE 1u /* used in current test */ | |
152 | union tvec_regval v; /* register value */ | |
153 | }; | |
154 | ||
155 | struct tvec_regdef { | |
156 | /* A register definition. Register definitions list the registers | |
157 | * which are used by a particular test group (see `struct tvec_test' | |
158 | * below). | |
159 | * | |
160 | * A vector of register definitions is terminated by a definition | |
161 | * whose `name' slot is null. | |
162 | */ | |
163 | ||
164 | const char *name; /* register name (for input files) */ | |
165 | unsigned i; /* register index */ | |
166 | const struct tvec_regty *ty; /* register type descriptor */ | |
167 | unsigned f; /* flags */ | |
168 | #define TVRF_OPT 1u /* optional register */ | |
169 | #define TVRF_ID 2u /* part of test identity */ | |
170 | union tvec_misc arg; /* extra detail for the type */ | |
171 | }; | |
172 | ||
e63124bc MW |
173 | /* @TVEC_GREG(vec, i, regsz)@ |
174 | * | |
175 | * If @vec@ is a data pointer which happens to contain the address of a | |
176 | * vector of @struct tvec_reg@ objects, @i@ is an integer, and @regsz@ is the | |
177 | * size of a @struct tvec_reg@, then this evaluates to the address of the | |
178 | * @i@th element of the vector. | |
179 | * | |
180 | * This is the general tool you need for accessing register vectors when you | |
181 | * don't have absolute knowledge of the size of a @union tvec_regval@. | |
182 | * Usually you want to access one of the register vectors in a @struct | |
183 | * tvec_state@, and @TVEC_REG@ will be more convenient. | |
184 | */ | |
185 | #define TVEC_GREG(vec, i, regsz) \ | |
186 | ((struct tvec_reg *)((unsigned char *)(vec) + (i)*(regsz))) | |
187 | ||
188 | /*------ Serialization utilities ------------------------------------------*/ | |
189 | ||
190 | /* --- @tvec_serialize@ --- * | |
191 | * | |
192 | * Arguments: @const struct tvec_reg *rv@ = vector of registers | |
193 | * @buf *b@ = buffer to write on | |
194 | * @const struct tvec_regdef *regs@ = vector of register | |
195 | * descriptions, terminated by an entry with a null | |
196 | * @name@ slot | |
197 | * @unsigned nr@ = number of entries in the @rv@ vector | |
198 | * @size_t regsz@ = true size of each element of @rv@ | |
199 | * | |
200 | * Returns: Zero on success, @-1@ on failure. | |
201 | * | |
202 | * Use: Serialize a collection of register values. | |
203 | * | |
204 | * The `candidate register definitions' are those entries @r@ in | |
205 | * the @regs@ vector whose index @r.i@ is strictly less than | |
206 | * @nr@. The `selected register definitions' are those | |
207 | * candidate register definitions @r@ for which the indicated | |
208 | * register @rv[r.i]@ has the @TVRF_LIVE@ flag set. The | |
209 | * serialized output begins with a header bitmap: if there are | |
210 | * %$n$% candidate register definitions then the header bitmap | |
211 | * consists of %$\lceil n/8 \rceil$% bytes. Bits are ordered | |
212 | * starting from the least significant bit of the first byte, | |
213 | * end ending at the most significant bit of the final byte. | |
214 | * The bit corresponding to a candidate register definition is | |
215 | * set if and only if that register defintion is selected. The | |
216 | * header bitmap is then followed by the serializations of the | |
217 | * selected registers -- i.e., for each selected register | |
218 | * definition @r@, the serialized value of register @rv[r.i]@ -- | |
219 | * simply concatenated together, with no padding or alignment. | |
220 | * | |
221 | * The serialized output is written to the buffer @b@. Failure | |
222 | * can be caused by running out of buffer space, or a failing | |
223 | * type handler. | |
224 | */ | |
225 | ||
226 | extern int tvec_serialize(const struct tvec_reg */*rv*/, buf */*b*/, | |
b64eb60f | 227 | const struct tvec_regdef */*regs*/, |
e63124bc | 228 | unsigned /*nr*/, size_t /*regsz*/); |
b64eb60f | 229 | |
e63124bc MW |
230 | /* --- @tvec_deserialize@ --- * |
231 | * | |
232 | * Arguments: @struct tvec_reg *rv@ = vector of registers | |
233 | * @buf *b@ = buffer to write on | |
234 | * @const struct tvec_regdef *regs@ = vector of register | |
235 | * descriptions, terminated by an entry with a null | |
236 | * @name@ slot | |
237 | * @unsigned nr@ = number of entries in the @rv@ vector | |
238 | * @size_t regsz@ = true size of each element of @rv@ | |
239 | * | |
240 | * Returns: Zero on success, @-1@ on failure. | |
241 | * | |
242 | * Use: Deserialize a collection of register values. | |
243 | * | |
244 | * The size of the register vector @nr@ and the register | |
245 | * definitions @regs@ must match those used when producing the | |
246 | * serialization. For each serialized register value, | |
247 | * deserialize and store the value into the appropriate register | |
248 | * slot, and set the @TVRF_LIVE@ flag on the register. See | |
249 | * @tvec_serialize@ for a description of the format. | |
250 | * | |
251 | * On successful completion, store the address of the first byte | |
252 | * after the serialized data in @*end_out@ if @end_out@ is not | |
253 | * null. Failure results only from a failing register type | |
254 | * handler. | |
255 | */ | |
256 | ||
257 | extern int tvec_deserialize(struct tvec_reg */*rv*/, buf */*b*/, | |
b64eb60f | 258 | const struct tvec_regdef */*regs*/, |
e63124bc | 259 | unsigned /*nr*/, size_t /*regsz*/); |
b64eb60f MW |
260 | |
261 | /*----- Test state --------------------------------------------------------*/ | |
262 | ||
3efcfd2d MW |
263 | enum { |
264 | /* Possible test outcomes. */ | |
265 | ||
266 | TVOUT_LOSE, /* test failed */ | |
267 | TVOUT_SKIP, /* test skipped */ | |
268 | TVOUT_WIN, /* test passed */ | |
269 | TVOUT_LIMIT /* (number of possible outcomes) */ | |
270 | }; | |
b64eb60f MW |
271 | |
272 | struct tvec_state { | |
e63124bc MW |
273 | /* The primary state structure for the test vector machinery. */ |
274 | ||
b64eb60f MW |
275 | unsigned f; /* flags */ |
276 | #define TVSF_SKIP 1u /* skip this test group */ | |
277 | #define TVSF_OPEN 2u /* test is open */ | |
278 | #define TVSF_ACTIVE 4u /* test is active */ | |
882a39c1 | 279 | #define TVSF_ERROR 8u /* an error occurred */ |
3efcfd2d MW |
280 | #define TVSF_OUTMASK 0xf0 /* test outcome (@TVOUT_...@) */ |
281 | #define TVSF_OUTSHIFT 4 /* shift applied to outcome */ | |
e63124bc MW |
282 | |
283 | /* Registers. Available to execution environments. */ | |
b64eb60f MW |
284 | unsigned nrout, nreg; /* number of output/total registers */ |
285 | size_t regsz; /* size of register entry */ | |
286 | struct tvec_reg *in, *out; /* register vectors */ | |
e63124bc MW |
287 | |
288 | /* Test groups state. Available to output formatters. */ | |
b64eb60f | 289 | const struct tvec_test *tests, *test; /* all tests and current test */ |
e63124bc MW |
290 | |
291 | /* Test scoreboard. Available to output formatters. */ | |
b64eb60f | 292 | unsigned curr[TVOUT_LIMIT], all[TVOUT_LIMIT], grps[TVOUT_LIMIT]; |
e63124bc MW |
293 | |
294 | /* Output machinery. */ | |
b64eb60f | 295 | struct tvec_output *output; /* output formatter */ |
e63124bc MW |
296 | |
297 | /* Input machinery. Available to type parsers. */ | |
b64eb60f MW |
298 | const char *infile; unsigned lno, test_lno; /* input file name, line */ |
299 | FILE *fp; /* input file stream */ | |
300 | }; | |
301 | ||
e63124bc MW |
302 | /* @TVEC_REG(tv, vec, i)@ |
303 | * | |
304 | * If @tv@ is a pointer to a @struct tvec_state@, @vec@ is either @in@ or | |
305 | * @out@, and @i@ is an integer, then this evaluates to the address of the | |
306 | * @i@th register in the selected vector. | |
307 | */ | |
b64eb60f MW |
308 | #define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->regsz) |
309 | ||
310 | /*----- Test descriptions -------------------------------------------------*/ | |
311 | ||
b64eb60f MW |
312 | typedef void tvec_testfn(const struct tvec_reg */*in*/, |
313 | struct tvec_reg */*out*/, | |
314 | void */*ctx*/); | |
e63124bc MW |
315 | /* A test function. It should read inputs from @in@ and write outputs to |
316 | * @out@. The @TVRF_LIVE@ is set on inputs which are actually present, and | |
317 | * on outputs which are wanted to test. A test function can set additional | |
318 | * `gratuitous outputs' by setting @TVRF_LIVE@ on them; clearing | |
319 | * @TVRF_LIVE@ on a wanted output causes a mismatch. | |
320 | * | |
321 | * A test function may be called zero or more times by the environment. In | |
322 | * particular, it may be called multiple times, though usually by prior | |
323 | * arrangement with the environment. | |
324 | * | |
325 | * The @ctx@ is supplied by the environment's @run@ function (see below). | |
326 | * The default environment calls the test function once, with a null | |
327 | * @ctx@. There is no expectation that the environment's context has | |
328 | * anything to do with the test function's context. | |
329 | */ | |
330 | ||
331 | struct tvec_env { | |
332 | /* A test environment sets things up for and arranges to run the test. | |
333 | * | |
334 | * The caller is responsible for allocating storage for the environment's | |
335 | * context, based on the @ctxsz@ slot, and freeing it later; this space is | |
336 | * passed in as the @ctx@ parameter to the remaining functions; if @ctxsz@ | |
337 | * is zero then @ctx@ is null. | |
338 | */ | |
339 | ||
340 | size_t ctxsz; /* environment context size */ | |
341 | ||
342 | int (*setup)(struct tvec_state */*tv*/, const struct tvec_env */*env*/, | |
343 | void */*pctx*/, void */*ctx*/); | |
344 | /* Initialize the context; called at the start of a test group. Return | |
345 | * zero on success, or @-1@ on failure. If setup fails, the context is | |
346 | * freed, and the test group is skipped. | |
347 | */ | |
348 | ||
349 | int (*set)(struct tvec_state */*tv*/, const char */*var*/, | |
350 | const struct tvec_env */*env*/, void */*ctx*/); | |
351 | /* Called when the parser finds a %|@var|%' setting to parse and store | |
352 | * the value. If @setup@ failed, this is still called (so as to skip the | |
353 | * value), but @ctx@ is null. | |
354 | */ | |
355 | ||
356 | int (*before)(struct tvec_state */*tv*/, void */*ctx*/); | |
357 | /* Called prior to running a test. This is the right place to act on any | |
358 | * `%|@var|%' settings. Return zero on success or @-1@ on failure (which | |
359 | * causes the test to be skipped). This function is never called if the | |
360 | * test group is skipped. | |
361 | */ | |
362 | ||
363 | void (*run)(struct tvec_state */*tv*/, tvec_testfn */*fn*/, void */*ctx*/); | |
364 | /* Run the test. It should either call @tvec_skip@, or run @fn@ one or | |
365 | * more times. In the latter case, it is responsible for checking the | |
366 | * outputs, and calling @tvec_fail@ as necessary; @tvec_checkregs@ will | |
367 | * check the register values against the supplied test vector, while | |
368 | * @tvec_check@ does pretty much everything necessary. This function is | |
369 | * never called if the test group is skipped. | |
370 | */ | |
371 | ||
372 | void (*after)(struct tvec_state */*tv*/, void */*ctx*/); | |
373 | /* Called after running or skipping a test. Typical actions involve | |
374 | * resetting whatever things were established by @set@. This function is | |
375 | * never called if the test group is skipped. | |
376 | */ | |
377 | ||
378 | void (*teardown)(struct tvec_state */*tv*/, void */*ctx*/); | |
379 | /* Tear down the environment: called at the end of a test group. If the | |
380 | * setup failed, then this function is still called, with a null @ctx@. | |
381 | */ | |
382 | }; | |
b64eb60f MW |
383 | |
384 | struct tvec_test { | |
e63124bc MW |
385 | /* A test description. */ |
386 | ||
b64eb60f MW |
387 | const char *name; /* name of the test */ |
388 | const struct tvec_regdef *regs; /* descriptions of the registers */ | |
e63124bc | 389 | const struct tvec_env *env; /* environment to run test in */ |
b64eb60f | 390 | tvec_testfn *fn; /* test function */ |
b64eb60f MW |
391 | }; |
392 | ||
e63124bc MW |
393 | |
394 | enum { | |
395 | /* Register output dispositions. */ | |
396 | ||
397 | TVRD_INPUT, /* input-only register */ | |
398 | TVRD_OUTPUT, /* output-only (input is dead) */ | |
399 | TVRD_MATCH, /* matching (equal) registers */ | |
400 | TVRD_FOUND, /* mismatching output register */ | |
401 | TVRD_EXPECT /* mismatching input register */ | |
402 | }; | |
403 | ||
404 | /* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- * | |
405 | * | |
406 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
3efcfd2d | 407 | * @const char *excuse@, @va_list *ap@ = reason why skipped |
e63124bc MW |
408 | * |
409 | * Returns: --- | |
410 | * | |
411 | * Use: Skip the current group. This should only be called from a | |
412 | * test environment @setup@ function; a similar effect occurs if | |
413 | * the @setup@ function fails. | |
414 | */ | |
415 | ||
b64eb60f | 416 | extern void PRINTF_LIKE(2, 3) |
e63124bc MW |
417 | tvec_skipgroup(struct tvec_state */*tv*/, const char */*excuse*/, ...); |
418 | extern void tvec_skipgroup_v(struct tvec_state */*tv*/, | |
419 | const char */*excuse*/, va_list */*ap*/); | |
b64eb60f | 420 | |
e63124bc MW |
421 | /* --- @tvec_skip@, @tvec_skip_v@ --- * |
422 | * | |
423 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
3efcfd2d | 424 | * @const char *excuse@, @va_list *ap@ = reason why test skipped |
e63124bc MW |
425 | * |
426 | * Returns: --- | |
427 | * | |
428 | * Use: Skip the current test. This should only be called from a | |
429 | * test environment @run@ function; a similar effect occurs if | |
430 | * the @before@ function fails. | |
431 | */ | |
b64eb60f | 432 | |
e63124bc MW |
433 | extern void PRINTF_LIKE(2, 3) |
434 | tvec_skip(struct tvec_state */*tv*/, const char */*excuse*/, ...); | |
435 | extern void tvec_skip_v(struct tvec_state */*tv*/, | |
436 | const char */*excuse*/, va_list */*ap*/); | |
b64eb60f | 437 | |
e63124bc MW |
438 | /* --- @tvec_resetoutputs@ --- * |
439 | * | |
440 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
441 | * | |
442 | * Returns: --- | |
443 | * | |
444 | * Use: Reset (releases and reinitializes) the output registers in | |
445 | * the test state. This is mostly of use to test environment | |
446 | * @run@ functions, between invocations of the test function. | |
447 | */ | |
b64eb60f | 448 | |
e63124bc | 449 | extern void tvec_resetoutputs(struct tvec_state */*tv*/); |
b64eb60f | 450 | |
e63124bc MW |
451 | /* --- @tvec_checkregs@ --- * |
452 | * | |
453 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
454 | * | |
455 | * Returns: Zero on success, @-1@ on mismatch. | |
456 | * | |
457 | * Use: Compare the active output registers (according to the current | |
458 | * test group definition) with the corresponding input register | |
459 | * values. A mismatch occurs if the two values differ | |
460 | * (according to the register type's @eq@ method), or if the | |
461 | * input is live but the output is dead. | |
462 | * | |
463 | * This function only checks for a mismatch and returns the | |
464 | * result; it takes no other action. In particular, it doesn't | |
465 | * report a failure, or dump register values. | |
466 | */ | |
b64eb60f | 467 | |
e63124bc MW |
468 | extern int tvec_checkregs(struct tvec_state */*tv*/); |
469 | ||
470 | /* --- @tvec_fail@, @tvec_fail_v@ --- * | |
471 | * | |
472 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
3efcfd2d | 473 | * @const char *detail@, @va_list *ap@ = description of test |
e63124bc MW |
474 | * |
475 | * Returns: --- | |
476 | * | |
477 | * Use: Report the current test as a failure. This function can be | |
478 | * called multiple times for a single test, e.g., if the test | |
479 | * environment's @run@ function invokes the test function | |
480 | * repeatedly; but a single test that fails repeatedly still | |
481 | * only counts as a single failure in the statistics. The | |
482 | * @detail@ string and its format parameters can be used to | |
483 | * distinguish which of several invocations failed; it can | |
484 | * safely be left null if the test function is run only once. | |
485 | */ | |
486 | ||
487 | extern void PRINTF_LIKE(2, 3) | |
488 | tvec_fail(struct tvec_state */*tv*/, const char */*detail*/, ...); | |
489 | extern void tvec_fail_v(struct tvec_state */*tv*/, | |
490 | const char */*detail*/, va_list */*ap*/); | |
491 | ||
492 | /* --- @tvec_dumpreg@ --- * | |
493 | * | |
494 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
495 | * @unsigned disp@ = the register disposition (@TVRD_...@) | |
496 | * @const union tvec_regval *tv@ = register value | |
497 | * @const struct tvec_regdef *rd@ = register definition | |
498 | * | |
499 | * Returns: --- | |
500 | * | |
501 | * Use: Dump a register value to the output. This is the lowest- | |
502 | * level function for dumping registers, and calls the output | |
503 | * formatter directly. | |
504 | * | |
505 | * Usually @tvec_mismatch@ is much more convenient. Low-level | |
506 | * access is required for reporting `virtual' registers | |
507 | * corresponding to test environment settings. | |
508 | */ | |
509 | ||
510 | extern void tvec_dumpreg(struct tvec_state */*tv*/, | |
511 | unsigned /*disp*/, const union tvec_regval */*rv*/, | |
512 | const struct tvec_regdef */*rd*/); | |
513 | ||
514 | /* --- @tvec_mismatch@ --- * | |
515 | * | |
516 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
517 | * @unsigned f@ = flags (@TVMF_...@) | |
518 | * | |
519 | * Returns: --- | |
520 | * | |
521 | * Use: Dumps registers suitably to report a mismatch. The flag bits | |
522 | * @TVMF_IN@ and @TVF_OUT@ select input-only and output | |
523 | * registers. If both are reset then nothing happens. | |
524 | * Suppressing the output registers may be useful, e.g., if the | |
525 | * test function crashed rather than returning outputs. | |
526 | */ | |
527 | ||
528 | #define TVMF_IN 1u | |
529 | #define TVMF_OUT 2u | |
530 | extern void tvec_mismatch(struct tvec_state */*tv*/, unsigned /*f*/); | |
531 | ||
532 | /* --- @tvec_check@, @tvec_check_v@ --- * | |
533 | * | |
534 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
3efcfd2d | 535 | * @const char *detail@, @va_list *ap@ = description of test |
e63124bc MW |
536 | * |
537 | * Returns: --- | |
538 | * | |
539 | * Use: Check the register values, reporting a failure and dumping | |
540 | * the registers in the event of a mismatch. This just wraps up | |
541 | * @tvec_checkregs@, @tvec_fail@ and @tvec_mismatch@ in the | |
542 | * obvious way. | |
543 | */ | |
544 | ||
545 | extern void PRINTF_LIKE(2, 3) | |
546 | tvec_check(struct tvec_state */*tv*/, const char */*detail*/, ...); | |
547 | extern void tvec_check_v(struct tvec_state */*tv*/, | |
548 | const char */*detail*/, va_list */*ap*/); | |
b64eb60f MW |
549 | |
550 | /*----- Session lifecycle -------------------------------------------------*/ | |
551 | ||
c5e0e403 MW |
552 | struct tvec_config { |
553 | /* An overall test configuration. */ | |
e63124bc | 554 | |
c5e0e403 MW |
555 | const struct tvec_test *tests; /* the tests to be performed */ |
556 | unsigned nrout, nreg; /* number of output/total regs */ | |
557 | size_t regsz; /* size of a register */ | |
b64eb60f MW |
558 | }; |
559 | ||
c5e0e403 MW |
560 | /* --- @tvec_begin@ --- * |
561 | * | |
562 | * Arguments: @struct tvec_state *tv_out@ = state structure to fill in | |
563 | * @const struct tvec_config *config@ = test configuration | |
564 | * @struct tvec_output *o@ = output driver | |
565 | * | |
566 | * Returns: --- | |
567 | * | |
568 | * Use: Initialize a state structure ready to do some testing. | |
569 | */ | |
b64eb60f | 570 | |
c5e0e403 MW |
571 | extern void tvec_begin(struct tvec_state */*tv_out*/, |
572 | const struct tvec_config */*config*/, | |
573 | struct tvec_output */*o*/); | |
b64eb60f | 574 | |
c5e0e403 MW |
575 | /* --- @tvec_end@ --- * |
576 | * | |
577 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
578 | * | |
579 | * Returns: A proposed exit code. | |
580 | * | |
581 | * Use: Conclude testing and suggests an exit code to be returned to | |
582 | * the calling program. (The exit code comes from the output | |
583 | * driver's @esession@ method.) | |
584 | */ | |
b64eb60f | 585 | |
c5e0e403 | 586 | extern int tvec_end(struct tvec_state */*tv*/); |
b64eb60f | 587 | |
c5e0e403 MW |
588 | /* --- @tvec_read@ --- * |
589 | * | |
590 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
591 | * @const char *infile@ = the name of the input file | |
592 | * @FILE *fp@ = stream to read from | |
593 | * | |
594 | * Returns: Zero on success, @-1@ on error. | |
595 | * | |
596 | * Use: Read test vector data from @fp@ and exercise test functions. | |
597 | * THe return code doesn't indicate test failures: it's only | |
598 | * concerned with whether there were problems with the input | |
599 | * file or with actually running the tests. | |
600 | */ | |
b64eb60f | 601 | |
c5e0e403 MW |
602 | extern int tvec_read(struct tvec_state */*tv*/, |
603 | const char */*infile*/, FILE */*fp*/); | |
b64eb60f | 604 | |
c5e0e403 | 605 | /*----- Input utilities ---------------------------------------------------*/ |
b64eb60f | 606 | |
c5e0e403 MW |
607 | /* These are provided by the core for the benefit of type @parse@ methods, |
608 | * and test-environment @set@ functions, which get to read from the test | |
609 | * input file. The latter are usually best implemented by calling on the | |
610 | * former. | |
611 | * | |
612 | * The two main rules are as follows. | |
613 | * | |
614 | * * Leave the file position at the beginning of the line following | |
615 | * whatever it was that you read. | |
616 | * | |
617 | * * When you read and consume a newline (which you do at least once, by | |
618 | * the previous rule), then increment @tv->lno@ to keep track of the | |
619 | * current line number. | |
620 | */ | |
b64eb60f | 621 | |
c5e0e403 MW |
622 | /* --- @tvec_skipspc@ --- * |
623 | * | |
624 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
625 | * | |
626 | * Returns: --- | |
627 | * | |
628 | * Use: Advance over any whitespace characters other than newlines. | |
629 | * This will stop at `;', end-of-file, or any other kind of | |
630 | * non-whitespace; and it won't consume a newline. | |
631 | */ | |
b64eb60f | 632 | |
c5e0e403 | 633 | extern void tvec_skipspc(struct tvec_state */*tv*/); |
b64eb60f | 634 | |
c5e0e403 MW |
635 | /* --- @tvec_syntax@, @tvec_syntax_v@ --- * |
636 | * | |
637 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
638 | * @int ch@ = the character found (in @fgetc@ format) | |
639 | * @const char *expect@, @va_list ap@ = what was expected | |
640 | * | |
641 | * Returns: @-1@ | |
642 | * | |
643 | * Use: Report a syntax error quoting @ch@ and @expect@. If @ch@ is | |
644 | * a newline, then back up so that it can be read again (e.g., | |
645 | * by @tvec_flushtoeol@ or @tvec_nexttoken@, which will also | |
646 | * advance the line number). | |
647 | */ | |
b64eb60f | 648 | |
c5e0e403 MW |
649 | extern int PRINTF_LIKE(3, 4) |
650 | tvec_syntax(struct tvec_state */*tv*/, int /*ch*/, | |
651 | const char */*expect*/, ...); | |
652 | extern int tvec_syntax_v(struct tvec_state */*tv*/, int /*ch*/, | |
653 | const char */*expect*/, va_list */*ap*/); | |
b64eb60f | 654 | |
c5e0e403 MW |
655 | /* --- @tvec_flushtoeol@ --- * |
656 | * | |
657 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
658 | * @unsigned f@ = flags (@TVFF_...@) | |
659 | * | |
660 | * Returns: Zero on success, @-1@ on error. | |
661 | * | |
662 | * Use: Advance to the start of the next line, consuming the | |
663 | * preceding newline. | |
664 | * | |
665 | * A syntax error is reported if no newline character is found, | |
666 | * i.e., the file ends in mid-line. A syntax error is also | |
667 | * reported if material other than whitespace or a comment is | |
668 | * found before the end of the line end, and @TVFF_ALLOWANY@ is | |
669 | * not set in @f@. The line number count is updated | |
670 | * appropriately. | |
671 | */ | |
b64eb60f | 672 | |
c5e0e403 MW |
673 | #define TVFF_ALLOWANY 1u |
674 | extern int tvec_flushtoeol(struct tvec_state */*tv*/, unsigned /*f*/); | |
b64eb60f | 675 | |
c5e0e403 MW |
676 | /* --- @tvec_nexttoken@ --- * |
677 | * | |
678 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
679 | * | |
680 | * Returns: Zero if there is a next token which can be read; @-1@ if no | |
681 | * token is available. | |
682 | * | |
683 | * Use: Advance to the next whitespace-separated token, which may be | |
684 | * on the next line. | |
685 | * | |
686 | * Tokens are separated by non-newline whitespace, comments, and | |
687 | * newlines followed by whitespace; a newline /not/ followed by | |
688 | * whitespace instead begins the next assignment, and two | |
689 | * newlines separated only by whitespace terminate the data for | |
690 | * a test. | |
691 | * | |
692 | * If this function returns zero, then the next character in the | |
693 | * file begins a suitable token which can be read and | |
694 | * processed. If it returns @-1@ then there is no such token, | |
695 | * and the file position is left correctly. The line number | |
696 | * count is updated appropriately. | |
697 | */ | |
b64eb60f | 698 | |
c5e0e403 | 699 | extern int tvec_nexttoken(struct tvec_state */*tv*/); |
b64eb60f | 700 | |
c5e0e403 MW |
701 | /* --- @tvec_readword@ --- * |
702 | * | |
703 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
704 | * @dstr *d@ = string to append the word to | |
705 | * @const char *delims@ = additional delimiters to stop at | |
706 | * @const char *expect@, @va_list ap@ = what was expected | |
707 | * | |
708 | * Returns: Zero on success, @-1@ on failure. | |
709 | * | |
710 | * Use: A `word' consists of characters other than whitespace, null | |
711 | * characters, and other than those listed in @delims@; | |
712 | * furthermore, a word does not begin with a `;'. (If you want | |
713 | * reading to stop at comments not preceded by whitespace, then | |
714 | * include `;' in @delims@. This is a common behaviour.) | |
715 | * | |
716 | * If there is no word beginning at the current file position, | |
717 | * then return @-1@; furthermore, if @expect@ is not null, then | |
718 | * report an appropriate error via @tvec_syntax@. | |
719 | * | |
720 | * Otherwise, the word is accumulated in @d@ and zero is | |
721 | * returned; if @d@ was not empty at the start of the call, the | |
722 | * newly read word is separated from the existing material by a | |
723 | * single space character. Since null bytes are never valid | |
724 | * word constituents, a null terminator is written to @d@, and | |
725 | * it is safe to treat the string in @d@ as being null- | |
726 | * terminated. | |
727 | */ | |
b64eb60f | 728 | |
c5e0e403 MW |
729 | extern int PRINTF_LIKE(4, 5) |
730 | tvec_readword(struct tvec_state */*tv*/, dstr */*d*/, | |
731 | const char */*delims*/, const char */*expect*/, ...); | |
732 | extern int tvec_readword_v(struct tvec_state */*tv*/, dstr */*d*/, | |
733 | const char */*delims*/, const char */*expect*/, | |
734 | va_list */*ap*/); | |
b64eb60f | 735 | |
3efcfd2d MW |
736 | /*----- Ad-hoc testing ----------------------------------------------------*/ |
737 | ||
738 | /* --- @tvec_adhoc@ --- * | |
739 | * | |
740 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
741 | * @struct tvec_test *t@ = space for a test definition | |
742 | * | |
743 | * Returns: --- | |
744 | * | |
745 | * Use: Begin ad-hoc testing, i.e., without reading a file of | |
746 | * test-vector data. | |
747 | * | |
748 | * The structure at @t@ will be used to record information about | |
749 | * the tests underway, which would normally come from a static | |
750 | * test definition. The other functions in this section assume | |
751 | * that @tvec_adhoc@ has been called. | |
752 | */ | |
753 | ||
754 | extern void tvec_adhoc(struct tvec_state */*tv*/, struct tvec_test */*t*/); | |
755 | ||
756 | /* --- @tvec_begingroup@, @TVEC_BEGINGROUP@ --- * | |
757 | * | |
758 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
759 | * @const char *name@ = name for this test group | |
760 | * @const char *file@, @unsigned @lno@ = calling file and line | |
761 | * | |
762 | * Returns: --- | |
763 | * | |
764 | * Use: Begin an ad-hoc test group with the given name. The @file@ | |
765 | * and @lno@ can be anything, but it's usually best if they | |
766 | * refer to the source code performing the test: the macro | |
767 | * @TVEC_BEGINGROUP@ does this automatically. | |
768 | */ | |
769 | ||
770 | extern void tvec_begingroup(struct tvec_state */*tv*/, const char */*name*/, | |
771 | const char */*file*/, unsigned /*lno*/); | |
772 | #define TVEC_BEGINGROUP(tv, name) \ | |
773 | do tvec_begingroup(tv, name, __FILE__, __LINE__); while (0) | |
774 | ||
775 | /* --- @tvec_endgroup@ --- * | |
776 | * | |
777 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
778 | * | |
779 | * Returns: --- | |
780 | * | |
781 | * Use: End an ad-hoc test group. The statistics are updated and the | |
782 | * outcome is reported to the output formatter. | |
783 | */ | |
784 | ||
785 | extern void tvec_endgroup(struct tvec_state */*tv*/); | |
786 | ||
787 | /* --- @TVEC_TESTGROUP@, @TVEC_TESTGROUP_TAG@ --- * | |
788 | * | |
789 | * Arguments: @tag@ = label-disambiguation tag | |
790 | * @const struct tvec_state *tv = test-vector state | |
791 | * @const char *name@ = test-group name | |
792 | * | |
793 | * Returns: --- | |
794 | * | |
795 | * Use: Control-structure macro: @TVEC_TESTGROUP(tv, name) stmt@ | |
796 | * establishes a test group with the given @name@ (attributing | |
797 | * it to the source file and lie number), executes @stmt@, and | |
798 | * ends the test group. If @stmt@ invokes @break@ then the test | |
799 | * group is skipped. @TVEC_TESTGROUP_TAG@ is the same, with an | |
800 | * additional @tag@ argument for use in higher-level macros. | |
801 | */ | |
802 | ||
803 | #define TVEC_TESTGROUP_TAG(tag, tv, name) \ | |
804 | MC_WRAP(tag##__around, \ | |
805 | { TVEC_BEGINGROUP(tv, name); }, \ | |
806 | { tvec_endgroup(tv); }, \ | |
807 | { if (!((tv)->f&TVSF_SKIP)) tvec_skipgroup(tv, 0); \ | |
808 | tvec_endgroup(tv); }) | |
809 | #define TVEC_TESTGROUP(tv, name) TVEC_TESTGROUP_TAG(grp, tv, name) | |
810 | ||
811 | /* --- @tvec_begintest@, @TVEC_BEGINTEST@ --- * | |
812 | * | |
813 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
814 | * @const char *file@, @unsigned @lno@ = calling file and line | |
815 | * | |
816 | * Returns: --- | |
817 | * | |
818 | * Use: Begin an ad-hoc test case. The @file@ and @lno@ can be | |
819 | * anything, but it's usually best if they refer to the source | |
820 | * code performing the test: the macro @TVEC_BEGINGROUP@ does | |
821 | * this automatically. | |
822 | */ | |
823 | ||
824 | extern void tvec_begintest(struct tvec_state */*tv*/, | |
825 | const char */*file*/, unsigned /*lno*/); | |
826 | #define TVEC_BEGINTEST(tv) \ | |
827 | do tvec_begintest(tv, __FILE__, __LINE__); while (0) | |
828 | ||
829 | /* --- *tvec_endtest@ --- * | |
830 | * | |
831 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
832 | * | |
833 | * Returns: --- | |
834 | * | |
835 | * Use: End a ad-hoc test case, The statistics are updated and the | |
836 | * outcome is reported to the output formatter. | |
837 | */ | |
838 | ||
839 | extern void tvec_endtest(struct tvec_state */*tv*/); | |
840 | ||
841 | /* --- @TVEC_TEST@, @TVEC_TEST_TAG@ --- * | |
842 | * | |
843 | * Arguments: @tag@ = label-disambiguation tag | |
844 | * @struct tvec_test *t@ = space for a test definition | |
845 | * | |
846 | * Returns: --- | |
847 | * | |
848 | * Use: Control-structure macro: @TVEC_TEST(tv) stmt@ begins a test | |
849 | * case, executes @stmt@, and ends the test case. If @stmt@ | |
850 | * invokes @break@ then the test case is skipped. | |
851 | * @TVEC_TEST_TAG@ is the same, with an additional @tag@ argumet | |
852 | * for use in higher-level macros. | |
853 | */ | |
854 | ||
855 | #define TVEC_TEST_TAG(tag, tv) \ | |
856 | MC_WRAP(tag##__around, \ | |
857 | { TVEC_BEGINTEST(tv); }, \ | |
858 | { tvec_endtest(tv); }, \ | |
859 | { if ((tv)->f&TVSF_ACTIVE) tvec_skip((tv), 0); \ | |
860 | tvec_endtest(tv); }) | |
861 | #define TVEC_TEST(tv) TVEC_TEST_TAG(test, tv) | |
862 | ||
863 | /* --- @tvec_claim@, @tvec_claim_v@, @TVEC_CLAIM@ --- * | |
864 | * | |
865 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
866 | * @int ok@ = a flag | |
867 | * @const char *file@, @unsigned @lno@ = calling file and line | |
868 | * @const char *msg@, @va_list *ap@ = message to report on | |
869 | * failure | |
870 | * | |
871 | * Returns: The value @ok@. | |
872 | * | |
873 | * Use: Check that a claimed condition holds, as (part of) a test. | |
874 | * If no test case is underway (i.e., if @TVSF_OPEN@ is reset in | |
875 | * @tv->f@), then a new test case is begun and ended. The | |
876 | * @file@ and @lno@ are passed to the output formatter to be | |
877 | * reported in case of a failure. If @ok@ is nonzero, then | |
878 | * nothing else happens; so, in particular, if @tvec_claim@ | |
879 | * established a new test case, then the test case succeeds. If | |
880 | * @ok@ is zero, then a failure is reported, quoting @msg@. | |
881 | * | |
882 | * The @TVEC_CLAIM@ macro is similar, only it (a) identifies the | |
883 | * file and line number of the call site automatically, and (b) | |
884 | * implicitly quotes the source text of the @ok@ condition in | |
885 | * the failure message. | |
886 | */ | |
887 | ||
888 | extern int PRINTF_LIKE(5, 6) | |
889 | tvec_claim(struct tvec_state */*tv*/, int /*ok*/, | |
890 | const char */*file*/, unsigned /*lno*/, | |
891 | const char */*msg*/, ...); | |
892 | extern int tvec_claim_v(struct tvec_state */*tv*/, int /*ok*/, | |
893 | const char */*file*/, unsigned /*lno*/, | |
894 | const char */*msg*/, va_list */*ap*/); | |
895 | #define TVEC_CLAIM(tv, cond) \ | |
896 | (tvec_claim(tv, !!(cond), __FILE__, __LINE__, "%s untrue", #cond)) | |
897 | ||
898 | /* --- @tvec_claimeq@ --- * | |
899 | * | |
900 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
901 | * @const struct tvec_regty *ty@ = register type | |
902 | * @const union tvec_misc *arg@ = register type argument | |
903 | * @const char *file@, @unsigned @lno@ = calling file and line | |
904 | * @const char *expr@ = the expression to quote on failure | |
905 | * | |
906 | * Returns: Nonzero if the input and output values of register 0 are | |
907 | * equal, zero if they differ. | |
908 | * | |
909 | * Use: Check that the input and output values of register 0 are | |
910 | * equal (according to the register type @ty@). As for | |
911 | * @tvec_claim@ above, a test case is automatically begun and | |
912 | * ended if none is already underway. If the values are | |
913 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
914 | * mismatched values are dumped. | |
915 | * | |
916 | * This function is not expected to be called directly, but | |
917 | * through type-specific wrapper functions or macros such as | |
918 | * @TVEC_CLAIMEQ_INT@. | |
919 | */ | |
920 | ||
921 | extern int tvec_claimeq(struct tvec_state */*tv*/, | |
922 | const struct tvec_regty */*ty*/, | |
923 | const union tvec_misc */*arg*/, | |
924 | const char */*file*/, unsigned /*lno*/, | |
925 | const char */*expr*/); | |
926 | ||
b64eb60f MW |
927 | /*----- Output formatting -------------------------------------------------*/ |
928 | ||
929 | struct tvec_output { | |
3efcfd2d MW |
930 | /* An output formatter. */ |
931 | const struct tvec_outops *ops; /* pointer to operations */ | |
b64eb60f MW |
932 | }; |
933 | ||
3efcfd2d MW |
934 | /* Benchmarking details. */ |
935 | enum { | |
936 | TVBU_OP, /* counting operations of some kind */ | |
937 | TVBU_BYTE /* counting bytes (@RBUF >= 0@) */ | |
938 | }; | |
939 | struct bench_timing; /* forward declaration */ | |
c5e0e403 | 940 | |
b64eb60f | 941 | struct tvec_outops { |
3efcfd2d MW |
942 | /* Output operations. */ |
943 | ||
944 | void (*bsession)(struct tvec_output */*o*/, struct tvec_state */*tv*/); | |
945 | /* Begin a test session. The output driver will probably want to | |
946 | * save @tv@, because this isn't provided to any other methods. | |
947 | */ | |
b64eb60f | 948 | |
b64eb60f | 949 | int (*esession)(struct tvec_output */*o*/); |
3efcfd2d | 950 | /* End a session, and return the suggested exit code. */ |
b64eb60f MW |
951 | |
952 | void (*bgroup)(struct tvec_output */*o*/); | |
3efcfd2d MW |
953 | /* Begin a test group. The test group description is @tv->test@. */ |
954 | ||
b64eb60f MW |
955 | void (*skipgroup)(struct tvec_output */*o*/, |
956 | const char */*excuse*/, va_list */*ap*/); | |
3efcfd2d MW |
957 | /* The group is being skipped; @excuse@ may be null or a format |
958 | * string explaining why. The @egroup@ method will not be called | |
959 | * separately. | |
960 | */ | |
961 | ||
962 | void (*egroup)(struct tvec_output */*o*/); | |
963 | /* End a test group. At least one test was attempted or @skipgroup@ | |
964 | * would have been called instead. If @tv->curr[TVOUT_LOSE]@ is | |
965 | * nonzero then the test group as a whole failed; otherwise it | |
966 | * passed. | |
967 | */ | |
b64eb60f MW |
968 | |
969 | void (*btest)(struct tvec_output */*o*/); | |
3efcfd2d MW |
970 | /* Begin a test case. */ |
971 | ||
b64eb60f MW |
972 | void (*skip)(struct tvec_output */*o*/, |
973 | const char */*excuse*/, va_list */*ap*/); | |
3efcfd2d MW |
974 | /* The test case is being skipped; @excuse@ may be null or a format |
975 | * string explaining why. The @etest@ function will still be called | |
976 | * (so this works differently from @skipgroup@ and @egroup@). A test | |
977 | * case can be skipped at most once, and, if skipped, it cannot fail. | |
978 | */ | |
979 | ||
b64eb60f MW |
980 | void (*fail)(struct tvec_output */*o*/, |
981 | const char */*detail*/, va_list */*ap*/); | |
3efcfd2d MW |
982 | /* The test case failed. |
983 | * | |
984 | * The output driver should preferably report the filename (@infile@) | |
985 | * and line number (@test_lno@, not @lno@) for the failing test. | |
986 | * | |
987 | * The @detail@ may be null or a format string describing detail | |
988 | * about how the failing test was run which can't be determined from | |
989 | * the registers; a @detail@ is usually provided when (and only when) | |
990 | * the test environment potentially invokes the test function more | |
991 | * than once. | |
992 | * | |
993 | * A single test case can fail multiple times! | |
994 | */ | |
995 | ||
e63124bc MW |
996 | void (*dumpreg)(struct tvec_output */*o*/, |
997 | unsigned /*disp*/, const union tvec_regval */*rv*/, | |
998 | const struct tvec_regdef */*rd*/); | |
3efcfd2d MW |
999 | /* Dump a register. The `disposition' @disp@ explains what condition |
1000 | * the register is in; see @tvec_dumpreg@ and the @TVRD_...@ codes. | |
1001 | * The register value is at @rv@, and its definition, including its | |
1002 | * type, at @rd@. Note that this function may be called on virtual | |
1003 | * registers which aren't in either of the register vectors or | |
1004 | * mentioned by the test description. | |
1005 | */ | |
1006 | ||
b64eb60f | 1007 | void (*etest)(struct tvec_output */*o*/, unsigned /*outcome*/); |
3efcfd2d MW |
1008 | /* The test case concluded with the given @outcome@ (one of the |
1009 | * @TVOUT_...@ codes. | |
1010 | */ | |
b64eb60f | 1011 | |
e63124bc MW |
1012 | void (*bbench)(struct tvec_output */*o*/, |
1013 | const char */*ident*/, unsigned /*unit*/); | |
3efcfd2d MW |
1014 | /* Begin a benchmark. The @ident@ is a formatted string identifying |
1015 | * the benchmark based on the values of the input registered marked | |
1016 | * @TVRF_ID@; the output driver is free to use this or come up with | |
1017 | * its own way to identify the test, e.g., by inspecting the register | |
1018 | * values for itself. The @unit@ is one of the @TVBU_...@ constants | |
1019 | * explaining what sort of thing is being measured. | |
1020 | */ | |
1021 | ||
b64eb60f | 1022 | void (*ebench)(struct tvec_output */*o*/, |
e63124bc | 1023 | const char */*ident*/, unsigned /*unit*/, |
b64eb60f | 1024 | const struct bench_timing */*tm*/); |
3efcfd2d MW |
1025 | /* End a benchmark. The @ident@ and @unit@ arguments are as for |
1026 | * @bbench@. If @tm@ is zero then the measurement failed; otherwise | |
1027 | * @tm->n@ counts total number of things (operations or bytes, as | |
1028 | * indicated by @unit@) processed in the indicated time. | |
1029 | */ | |
1030 | ||
1031 | void (*error)(struct tvec_output */*o*/, | |
1032 | const char */*msg*/, va_list */*ap*/); | |
1033 | /* Report an error. The driver should ideally report the filename | |
1034 | * (@infile@) and line number (@lno@) prompting the error. | |
1035 | */ | |
1036 | ||
1037 | void (*notice)(struct tvec_output */*o*/, | |
1038 | const char */*msg*/, va_list */*ap*/); | |
1039 | /* Report a miscellaneous message. The driver should ideally report | |
1040 | * the filename (@infile@) and line number (@lno@) prompting the | |
1041 | * error. | |
1042 | */ | |
b64eb60f MW |
1043 | |
1044 | void (*destroy)(struct tvec_output */*o*/); | |
3efcfd2d | 1045 | /* Release any resources acquired by the driver. */ |
b64eb60f MW |
1046 | }; |
1047 | ||
3efcfd2d MW |
1048 | /* --- @tvec_error@, @tvec_error_v@ --- * |
1049 | * | |
1050 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1051 | * @const char *msg@, @va_list ap@ = error message | |
1052 | * | |
1053 | * Returns: @-1@. | |
1054 | * | |
1055 | * Use: Report an error. Errors are distinct from test failures, | |
1056 | * and indicate that a problem was encountered which compromised | |
1057 | * the activity of testing. | |
1058 | */ | |
1059 | ||
882a39c1 | 1060 | extern int PRINTF_LIKE(2, 3) |
b64eb60f | 1061 | tvec_error(struct tvec_state */*tv*/, const char */*msg*/, ...); |
882a39c1 MW |
1062 | extern int tvec_error_v(struct tvec_state */*tv*/, |
1063 | const char */*msg*/, va_list */*ap*/); | |
b64eb60f | 1064 | |
3efcfd2d MW |
1065 | /* --- @tvec_notice@, @tvec_notice_v@ --- * |
1066 | * | |
1067 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1068 | * @const char *msg@, @va_list ap@ = message | |
1069 | * | |
1070 | * Returns: --- | |
1071 | * | |
1072 | * Use: Output a notice: essentially, some important information | |
1073 | * which doesn't fit into any of the existing categories. | |
1074 | */ | |
1075 | ||
b64eb60f MW |
1076 | extern void PRINTF_LIKE(2, 3) |
1077 | tvec_notice(struct tvec_state */*tv*/, const char */*msg*/, ...); | |
1078 | extern void tvec_notice_v(struct tvec_state */*tv*/, | |
1079 | const char */*msg*/, va_list */*ap*/); | |
1080 | ||
3efcfd2d MW |
1081 | /* --- @tvec_humanoutput@ --- * |
1082 | * | |
1083 | * Arguments: @FILE *fp@ = output file to write on | |
1084 | * | |
1085 | * Returns: An output formatter. | |
1086 | * | |
1087 | * Use: Return an output formatter which writes on @fp@ with the | |
1088 | * expectation that a human will be watching and interpreting | |
1089 | * the output. If @fp@ denotes a terminal, the display shows a | |
1090 | * `scoreboard' indicating the outcome of each test case | |
1091 | * attempted, and may in addition use colour and other | |
1092 | * highlighting. | |
1093 | */ | |
1094 | ||
e63124bc | 1095 | extern struct tvec_output *tvec_humanoutput(FILE */*fp*/); |
3efcfd2d MW |
1096 | |
1097 | /* --- @tvec_tapoutput@ --- * | |
1098 | * | |
1099 | * Arguments: @FILE *fp@ = output file to write on | |
1100 | * | |
1101 | * Returns: An output formatter. | |
1102 | * | |
1103 | * Use: Return an output formatter which writes on @fp@ in `TAP' | |
1104 | * (`Test Anything Protocol') format. | |
1105 | * | |
1106 | * TAP comes from the Perl community, but has spread rather | |
1107 | * further. This driver produces TAP version 13. The driver | |
1108 | * produces a TAP `test point' -- i.e., a result reported as | |
1109 | * `ok' or `not ok' -- for each input test group. Failure | |
1110 | * reports and register dumps are produced as diagnostic | |
1111 | * messages before the final group result. (TAP permits | |
1112 | * structuerd YAML data after the test-point result, which could | |
1113 | * be used to report details, but (a) postponing the details | |
1114 | * until after the report is inconvenient, and (b) there is no | |
1115 | * standardization for the YAML anyway, so in practice it's no | |
1116 | * more useful than the unstructured diagnostics. | |
1117 | */ | |
1118 | ||
e63124bc | 1119 | extern struct tvec_output *tvec_tapoutput(FILE */*fp*/); |
3efcfd2d MW |
1120 | |
1121 | /* --- @tvec_dfltoutput@ --- * | |
1122 | * | |
1123 | * Arguments: @FILE *fp@ = output file to write on | |
1124 | * | |
1125 | * Returns: An output formatter. | |
1126 | * | |
1127 | * Use: Selects and instantiates an output formatter suitable for | |
1128 | * writing on @fp@. The policy is subject to change, but | |
1129 | * currently the `human' output format is selected if @fp@ is | |
1130 | * interactive (i.e., if @isatty(fileno(fp))@ is true), and | |
1131 | * otherwise the `tap' format is used. | |
1132 | */ | |
1133 | ||
e63124bc | 1134 | extern struct tvec_output *tvec_dfltout(FILE */*fp*/); |
b64eb60f | 1135 | |
b64eb60f MW |
1136 | /*----- Register types ----------------------------------------------------*/ |
1137 | ||
1138 | struct tvec_regty { | |
3efcfd2d MW |
1139 | /* A register type. */ |
1140 | ||
b64eb60f | 1141 | void (*init)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/); |
3efcfd2d MW |
1142 | /* Initialize the value in @*rv@. This will be called before any |
1143 | * other function acting on the value, including @release@. | |
1144 | */ | |
1145 | ||
b64eb60f MW |
1146 | void (*release)(union tvec_regval */*rv*/, |
1147 | const struct tvec_regdef */*rd*/); | |
3efcfd2d MW |
1148 | /* Release any resources associated with the value in @*rv@. */ |
1149 | ||
b64eb60f MW |
1150 | int (*eq)(const union tvec_regval */*rv0*/, |
1151 | const union tvec_regval */*rv1*/, | |
1152 | const struct tvec_regdef */*rd*/); | |
3efcfd2d MW |
1153 | /* Return nonzero if @*rv0@ and @*rv1@ are equal values. |
1154 | * Asymmetric criteria are permitted: @tvec_checkregs@ calls @eq@ | |
1155 | * with the input register as @rv0@ and the output as @rv1@. | |
1156 | */ | |
1157 | ||
b64eb60f MW |
1158 | int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/, |
1159 | const struct tvec_regdef */*rd*/); | |
3efcfd2d MW |
1160 | /* Serialize the value @*rv@, writing the result to @b@. Return |
1161 | * zero on success, or @-1@ on error. | |
1162 | */ | |
1163 | ||
b64eb60f MW |
1164 | int (*frombuf)(buf */*b*/, union tvec_regval */*rv*/, |
1165 | const struct tvec_regdef */*rd*/); | |
3efcfd2d MW |
1166 | /* Deserialize a value from @b@, storing it in @*rv@. Return zero on |
1167 | * success, or @-1@ on error. | |
1168 | */ | |
1169 | ||
882a39c1 MW |
1170 | int (*parse)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/, |
1171 | struct tvec_state */*tv*/); | |
3efcfd2d MW |
1172 | /* Parse a value from @tv->fp@, storing it in @*rv@. Return zero on |
1173 | * success, or @-1@ on error, having reported one or more errors via | |
1174 | * @tvec_error@ or @tvec_syntax@. A successful return should leave | |
1175 | * the input position at the start of the next line; the caller will | |
1176 | * flush the remainder of the line itself. | |
1177 | */ | |
1178 | ||
b64eb60f MW |
1179 | void (*dump)(const union tvec_regval */*rv*/, |
1180 | const struct tvec_regdef */*rd*/, | |
e63124bc MW |
1181 | unsigned /*style*/, |
1182 | const struct gprintf_ops */*gops*/, void */*go*/); | |
b64eb60f | 1183 | #define TVSF_COMPACT 1u |
3efcfd2d MW |
1184 | /* Write a human-readable representation of the value @*rv@ using |
1185 | * @gprintf@ on @gops@ and @go@. The @style@ is a collection of | |
1186 | * flags: if @TVSF_COMPACT@ is set, then output should be minimal, | |
1187 | * and must fit on a single line; otherwise, output may consist of | |
1188 | * multiple lines and may contain redundant information if that is | |
1189 | * likely to be useful to a human reader. | |
1190 | */ | |
b64eb60f MW |
1191 | }; |
1192 | ||
3efcfd2d MW |
1193 | /*----- Integer types: signed and unsigned ------------------------------- */ |
1194 | ||
1195 | /* Integers may be input in decimal, hex, binary, or octal, following | |
1196 | * approximately usual conventions. | |
1197 | * | |
1198 | * * Signed integers may be preceded with a `+' or `-' sign. | |
1199 | * | |
1200 | * * Decimal integers are just a sequence of decimal digits `0' ... `9'. | |
1201 | * | |
1202 | * * Octal integers are a sequence of digits `0' ... `7', preceded by `0o' | |
1203 | * or `0O'. | |
1204 | * | |
1205 | * * Hexadecimal integers are a sequence of digits `0' ... `9', `a' | |
1206 | * ... `f', or `A' ... `F', preceded by `0x' or `0X'. | |
1207 | * | |
1208 | * * Radix-B integers are a sequence of digits `0' ... `9', `a' ... `f', or | |
1209 | * `A' ... `F', each with value less than B, preceded by `Br' or `BR', | |
1210 | * where 0 < B < 36 is expressed in decimal without any leading `0' or | |
1211 | * internal underscores `_'. | |
1212 | * | |
1213 | * * A digit sequence may contain internal underscore `_' separators, but | |
1214 | * not before or after all of the digits; and two consecutive `_' | |
1215 | * characters are not permitted. | |
1216 | */ | |
1217 | ||
b64eb60f | 1218 | extern const struct tvec_regty tvty_int, tvty_uint; |
3efcfd2d MW |
1219 | |
1220 | /* The @arg.p@ slot may be null or a pointer to @struct tvec_irange@ or | |
1221 | * @struct tvec_urange@ as appropriate. The bounds are inclusive; use, e.g., | |
1222 | * @LONG_MAX@ explicitly if one or the other bound is logically inapplicable. | |
1223 | */ | |
b64eb60f MW |
1224 | struct tvec_irange { long min, max; }; |
1225 | struct tvec_urange { unsigned long min, max; }; | |
1226 | ||
3efcfd2d | 1227 | /* Bounds corresponding to common integer types. */ |
b64eb60f MW |
1228 | extern const struct tvec_irange |
1229 | tvrange_schar, tvrange_short, tvrange_int, tvrange_long, | |
1230 | tvrange_sbyte, tvrange_i16, tvrange_i32; | |
1231 | extern const struct tvec_urange | |
1232 | tvrange_uchar, tvrange_ushort, tvrange_uint, tvrange_ulong, tvrange_size, | |
1233 | tvrange_byte, tvrange_u16, tvrange_u32; | |
3efcfd2d MW |
1234 | |
1235 | /* --- tvec_claimeq_int@, @TVEC_CLAIMEQ_INT@ --- * | |
1236 | * | |
1237 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1238 | * @long i0, i1@ = two signed integers | |
1239 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1240 | * @const char *expr@ = the expression to quote on failure | |
1241 | * | |
1242 | * Returns: Nonzero if @i0@ and @i1@ are equal, otherwise zero. | |
1243 | * | |
1244 | * Use: Check that values of @i0@ and @i1@ are equal. As for | |
1245 | * @tvec_claim@ above, a test case is automatically begun and | |
1246 | * ended if none is already underway. If the values are | |
1247 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
1248 | * mismatched values are dumped: @i0@ is printed as the output | |
1249 | * value and @i1@ is printed as the input reference. | |
1250 | * | |
1251 | * The @TVEC_CLAIM_INT@ macro is similar, only it (a) identifies | |
1252 | * the file and line number of the call site automatically, and | |
1253 | * (b) implicitly quotes the source text of the @i0@ and @i1@ | |
1254 | * arguments in the failure message. | |
1255 | */ | |
b64eb60f MW |
1256 | |
1257 | extern int tvec_claimeq_int(struct tvec_state */*tv*/, | |
1258 | long /*i0*/, long /*i1*/, | |
1259 | const char */*file*/, unsigned /*lno*/, | |
1260 | const char */*expr*/); | |
3efcfd2d MW |
1261 | #define TVEC_CLAIMEQ_INT(tv, i0, i1) \ |
1262 | (tvec_claimeq_int(tv, i0, i1, __FILE__, __LINE__, #i0 " /= " #i1)) | |
1263 | ||
1264 | /* --- @tvec_claimeq_uint@, @TVEC_CLAIMEQ_UINT@ --- * | |
1265 | * | |
1266 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1267 | * @unsigned long u0, u1@ = two unsigned integers | |
1268 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1269 | * @const char *expr@ = the expression to quote on failure | |
1270 | * | |
1271 | * Returns: Nonzero if @u0@ and @u1@ are equal, otherwise zero. | |
1272 | * | |
1273 | * Use: Check that values of @u0@ and @u1@ are equal. As for | |
1274 | * @tvec_claim@ above, a test case is automatically begun and | |
1275 | * ended if none is already underway. If the values are | |
1276 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
1277 | * mismatched values are dumped: @u0@ is printed as the output | |
1278 | * value and @u1@ is printed as the input reference. | |
1279 | * | |
1280 | * The @TVEC_CLAIM_UINT@ macro is similar, only it (a) | |
1281 | * identifies the file and line number of the call site | |
1282 | * automatically, and (b) implicitly quotes the source text of | |
1283 | * the @u0@ and @u1@ arguments in the failure message. | |
1284 | */ | |
1285 | ||
b64eb60f MW |
1286 | extern int tvec_claimeq_uint(struct tvec_state */*tv*/, |
1287 | unsigned long /*u0*/, unsigned long /*u1*/, | |
1288 | const char */*file*/, unsigned /*lno*/, | |
1289 | const char */*expr*/); | |
b64eb60f MW |
1290 | #define TVEC_CLAIMEQ_UINT(tv, u0, u1) \ |
1291 | (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1)) | |
1292 | ||
3efcfd2d MW |
1293 | /*----- Floating-point type -----------------------------------------------*/ |
1294 | ||
1295 | /* Floating-point are either NaN (`#nan', if supported by the platform); | |
1296 | * positive or negative infinity (`#inf', `+#inf', or, preferred, `#+inf', | |
1297 | * and `-#inf' or, preferred, `#-inf', if supported by the platform), or a | |
1298 | * number in strtod(3) syntax. | |
1299 | * | |
1300 | * The comparison rules for floating-point numbers are complex: see | |
1301 | * @tvec_claimeqish_float@ for details. | |
1302 | */ | |
1303 | ||
e63124bc | 1304 | extern const struct tvec_regty tvty_float; |
3efcfd2d | 1305 | |
e63124bc | 1306 | struct tvec_floatinfo { |
3efcfd2d MW |
1307 | /* Details about acceptable floating-point values. */ |
1308 | ||
1309 | unsigned f; /* flags (@TVFF_...@ bits) */ | |
1310 | #define TVFF_NOMIN 1u /* ignore @min@ (allow -inf) */ | |
1311 | #define TVFF_NOMAX 2u /* ignore @max@ (allow +inf) */ | |
1312 | #define TVFF_NANOK 4u /* permit NaN */ | |
1313 | #define TVFF_EQMASK 0xf0 /* how to compare */ | |
1314 | #define TVFF_EXACT 0x00 /* must equal exactly */ | |
1315 | #define TVFF_ABSDELTA 0x10 /* must be within @delta@ */ | |
1316 | #define TVFF_RELDELTA 0x20 /* diff < @delta@ fraction */ | |
1317 | double min, max; /* smallest/largest value allowed */ | |
1318 | double delta; /* maximum tolerable difference */ | |
e63124bc MW |
1319 | }; |
1320 | ||
3efcfd2d MW |
1321 | /* --- @tvec_claimeqish_float@, @TVEC_CLAIMEQISH_FLOAT@ --- * |
1322 | * | |
1323 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1324 | * @double f0, f1@ = two floating-point numbers | |
1325 | * @unsigned f@ = flags (@TVFF_...@) | |
1326 | * @double delta@ = maximum tolerable difference | |
1327 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1328 | * @const char *expr@ = the expression to quote on failure | |
1329 | * | |
1330 | * Returns: Nonzero if @f0@ and @u1@ are sufficiently close, otherwise | |
1331 | * zero. | |
1332 | * | |
1333 | * Use: Check that values of @f0@ and @f1@ are sufficiently close. | |
1334 | * As for @tvec_claim@ above, a test case is automatically begun | |
1335 | * and ended if none is already underway. If the values are | |
1336 | * too far apart, then @tvec_fail@ is called, quoting @expr@, | |
1337 | * and the mismatched values are dumped: @f0@ is printed as the | |
1338 | * output value and @f1@ is printed as the input reference. | |
1339 | * | |
1340 | * The details for the comparison are as follows. | |
1341 | * | |
1342 | * * A NaN value matches any other NaN, and nothing else. | |
1343 | * | |
1344 | * * An infinity matches another infinity of the same sign, | |
1345 | * and nothing else. | |
1346 | * | |
1347 | * * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any | |
1348 | * representable number matches only itself: in particular, | |
1349 | * positive and negative zero are considered distinct. | |
1350 | * (This allows tests to check that they land on the correct | |
1351 | * side of branch cuts, for example.) | |
1352 | * | |
1353 | * * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches | |
1354 | * %$y$% when %$|x - y| < \delta$%. | |
1355 | * | |
1356 | * * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches | |
1357 | * %$y$% when %$|1 - y/x| < \delta$%. (Note that this | |
1358 | * criterion is asymmetric | |
1359 | * | |
1360 | * The @TVEC_CLAIM_FLOAT@ macro is similar, only it (a) | |
1361 | * identifies the file and line number of the call site | |
1362 | * automatically, and (b) implicitly quotes the source text of | |
1363 | * the @f0@ and @f1@ arguments (and @delta@) in the failure | |
1364 | * message. | |
1365 | */ | |
1366 | ||
e63124bc MW |
1367 | extern int tvec_claimeqish_float(struct tvec_state */*tv*/, |
1368 | double /*f0*/, double /*f1*/, | |
1369 | unsigned /*f*/, double /*delta*/, | |
1370 | const char */*file*/, unsigned /*lno*/, | |
1371 | const char */*expr*/); | |
3efcfd2d MW |
1372 | #define TVEC_CLAIMEQISH_FLOAT(tv, f0, f1, f, delta) \ |
1373 | (tvec_claimeqish_float(tv, f0, f1, f, delta, , __FILE__, __LINE__, \ | |
1374 | #f0 " /= " #f1 " (+/- " #delta ")")) | |
1375 | ||
1376 | /* --- @tvec_claimeq_float@, @TVEC_CLAIMEQ_FLOAT@ --- * | |
1377 | * | |
1378 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1379 | * @double f0, f1@ = two floating-point numbers | |
1380 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1381 | * @const char *expr@ = the expression to quote on failure | |
1382 | * | |
1383 | * Returns: Nonzero if @f0@ and @u1@ are identical, otherwise zero. | |
1384 | * | |
1385 | * Use: Check that values of @f0@ and @f1@ are identical. The | |
1386 | * function is exactly equivalent to @tvec_claimeqish_float@ | |
1387 | * with @f == TVFF_EXACT@; the macro is similarly like | |
1388 | * @TVEC_CLAIMEQISH_FLOAT@ with @f == TVFF_EXACT@, except that | |
1389 | * it doesn't bother to quote a delta. | |
1390 | */ | |
1391 | ||
e63124bc MW |
1392 | extern int tvec_claimeq_float(struct tvec_state */*tv*/, |
1393 | double /*f0*/, double /*f1*/, | |
1394 | const char */*file*/, unsigned /*lno*/, | |
1395 | const char */*expr*/); | |
e63124bc MW |
1396 | #define TVEC_CLAIMEQ_FLOAT(tv, f0, f1) \ |
1397 | (tvec_claimeq_float(tv, f0, f1, __FILE__, __LINE__, #f0 " /= " #f1)) | |
1398 | ||
3efcfd2d MW |
1399 | /*----- Enumerated types --------------------------------------------------*/ |
1400 | ||
1401 | /* There is a distinct enumerated type for each of the branches of | |
1402 | * @tvec_misc@. In the following, we write @t@ for the type code, which is | |
1403 | * @i@ for signed integer, @u@ for unsigned integer, @f@ for floating-point, | |
1404 | * and @p@ for pointer. | |
1405 | */ | |
b64eb60f | 1406 | |
3efcfd2d MW |
1407 | #define DEFENUMTY(tag, ty, slot) \ |
1408 | extern const struct tvec_regty tvty_##slot##enum; | |
1409 | TVEC_MISCSLOTS(DEFENUMTY) | |
1410 | #undef DEFENUMTY | |
1411 | ||
1412 | /* A @struct tvec_tassoc@ associates a string tag with a value. */ | |
b64eb60f MW |
1413 | #define DEFASSOC(tag_, ty, slot) \ |
1414 | struct tvec_##slot##assoc { const char *tag; ty slot; }; | |
1415 | TVEC_MISCSLOTS(DEFASSOC) | |
1416 | #undef DEFASSOC | |
1417 | ||
3efcfd2d MW |
1418 | /* Information about an enumerated type. */ |
1419 | #define DEFINFO(tag, ty, slot) \ | |
1420 | struct tvec_##slot##enuminfo { \ | |
1421 | const char *name; /* type name for diagnostics */ \ | |
1422 | const struct tvec_##slot##assoc *av; /* name/value mappings */ \ | |
1423 | EXTRA_##tag##_INFOSLOTS /* type-specific extra info */ \ | |
1424 | }; | |
1425 | ||
1426 | #define EXTRA_INT_INFOSLOTS \ | |
1427 | const struct tvec_irange *ir; /* allowed range of raw values */ | |
1428 | ||
1429 | #define EXTRA_UINT_INFOSLOTS \ | |
1430 | const struct tvec_urange *ur; /* allowed range of raw values */ | |
1431 | ||
1432 | #define EXTRA_FLT_INFOSLOTS \ | |
1433 | const struct tvec_floatinfo *fi; /* range and matching policy */ | |
1434 | ||
1435 | #define EXTRA_PTR_INFOSLOTS /* (nothing) */ | |
b64eb60f | 1436 | |
3efcfd2d MW |
1437 | TVEC_MISCSLOTS(DEFINFO) |
1438 | ||
1439 | #undef EXTRA_INT_INFOSLOTS | |
1440 | #undef EXTRA_UINT_INFOSLOTS | |
1441 | #undef EXTRA_FLT_INFOSLOTS | |
1442 | #undef EXTRA_PTR_INFOSLOTS | |
1443 | ||
1444 | #undef DEFINFO | |
1445 | ||
1446 | /* Standard enumerations. */ | |
e63124bc MW |
1447 | const struct tvec_ienuminfo tvenum_bool; |
1448 | ||
3efcfd2d MW |
1449 | /* --- @tvec_claimeq_tenum@, @TVEC_CLAIMEQ_TENUM@ --- * |
1450 | * | |
1451 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1452 | * @ty t0, t1@ = two values | |
1453 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1454 | * @const char *expr@ = the expression to quote on failure | |
1455 | * | |
1456 | * Returns: Nonzero if @t0@ and @t1@ are equal, otherwise zero. | |
1457 | * | |
1458 | * Use: Check that values of @t0@ and @t1@ are equal. As for | |
1459 | * @tvec_claim@ above, a test case is automatically begun and | |
1460 | * ended if none is already underway. If the values are | |
1461 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
1462 | * mismatched values are dumped: @u0@ is printed as the output | |
1463 | * value and @u1@ is printed as the input reference. | |
1464 | * | |
1465 | * The @TVEC_CLAIM_TENUM@ macro is similar, only it (a) | |
1466 | * identifies the file and line number of the call site | |
1467 | * automatically, and (b) implicitly quotes the source text of | |
1468 | * the @u0@ and @u1@ arguments in the failure message. | |
1469 | */ | |
1470 | ||
b64eb60f MW |
1471 | #define DECLCLAIM(tag, ty, slot) \ |
1472 | extern int tvec_claimeq_##slot##enum \ | |
1473 | (struct tvec_state */*tv*/, \ | |
e63124bc | 1474 | const struct tvec_##slot##enuminfo */*ei*/, \ |
3efcfd2d | 1475 | ty /*t0*/, ty /*t1*/, \ |
b64eb60f MW |
1476 | const char */*file*/, unsigned /*lno*/, const char */*expr*/); |
1477 | TVEC_MISCSLOTS(DECLCLAIM) | |
1478 | #undef DECLCLAIM | |
3efcfd2d MW |
1479 | #define TVEC_CLAIMEQ_IENUM(tv, ei, i0, i1) \ |
1480 | (tvec_claimeq_ienum(tv, ei, i0, i1, \ | |
1481 | __FILE__, __LINE__, #i0 " /= " #i1)) | |
1482 | #define TVEC_CLAIMEQ_UENUM(tv, ei, u0, u1) \ | |
1483 | (tvec_claimeq_uenum(tv, ei, u0, u1, \ | |
1484 | __FILE__, __LINE__, #u0 " /= " #u1)) | |
1485 | #define TVEC_CLAIMEQ_FENUM(tv, ei, f0, f1) \ | |
1486 | (tvec_claimeq_fenum(tv, ei, f0, f1, \ | |
1487 | __FILE__, __LINE__, #f0 " /= " #f1)) | |
1488 | #define TVEC_CLAIMEQ_PENUM(tv, ei, p0, p1) \ | |
1489 | (tvec_claimeq_penum(tv, ei, p0, p1, \ | |
1490 | __FILE__, __LINE__, #p0 " /= " #p1)) | |
1491 | ||
1492 | /*----- Flags type -------------------------------------------------------*/ | |
b64eb60f MW |
1493 | |
1494 | extern const struct tvec_regty tvty_flags; | |
3efcfd2d MW |
1495 | |
1496 | struct tvec_flag { | |
1497 | /* Definition of a single flag or bitfield value. | |
1498 | * | |
1499 | * Each named setting comes with a value @v@ and a mask @m@; the mask | |
1500 | * should cover all of the value bits, i.e., @(v&~m) == 0@. On input, | |
1501 | * exactly | |
1502 | */ | |
1503 | ||
1504 | const char *tag; /* name */ | |
1505 | unsigned long m, v; /* mask and value */ | |
1506 | }; | |
1507 | ||
b64eb60f MW |
1508 | struct tvec_flaginfo { |
1509 | const char *name; | |
1510 | const struct tvec_flag *fv; | |
1511 | const struct tvec_urange *range; | |
1512 | }; | |
1513 | ||
1514 | extern int tvec_claimeq_flags(struct tvec_state */*tv*/, | |
1515 | const struct tvec_flaginfo */*fi*/, | |
1516 | unsigned long /*f0*/, unsigned long /*f1*/, | |
1517 | const char */*file*/, unsigned /*lno*/, | |
1518 | const char */*expr*/); | |
1519 | #define TVEC_CLAIMEQ_FLAGS(tv, fi, f0, f1) \ | |
1520 | (tvec_claimeq_flags(tv, fi, f0, f1, \ | |
1521 | __FILE__, __LINE__, #f0 " /= " #f1)) | |
1522 | ||
e63124bc MW |
1523 | extern const struct tvec_regty tvty_char; |
1524 | extern int tvec_claimeq_char(struct tvec_state */*tv*/, | |
1525 | int /*ch0*/, int /*ch1*/, | |
1526 | const char */*file*/, unsigned /*lno*/, | |
1527 | const char */*expr*/); | |
1528 | #define TVEC_CLAIMEQ_CHAR(tv, c0, c1) \ | |
1529 | (tvec_claimeq_char(tv, c0, c1, __FILE__, __LINE__, #c0 " /= " #c1)) | |
1530 | ||
b64eb60f MW |
1531 | extern const struct tvec_regty tvty_string, tvty_bytes; |
1532 | ||
1533 | extern int tvec_claimeq_string(struct tvec_state */*tv*/, | |
1534 | const char */*p0*/, size_t /*sz0*/, | |
1535 | const char */*p1*/, size_t /*sz1*/, | |
1536 | const char */*file*/, unsigned /*lno*/, | |
1537 | const char */*expr*/); | |
1538 | extern int tvec_claimeq_strz(struct tvec_state */*tv*/, | |
1539 | const char */*p0*/, const char */*p1*/, | |
1540 | const char */*file*/, unsigned /*lno*/, | |
1541 | const char */*expr*/); | |
1542 | extern int tvec_claimeq_bytes(struct tvec_state */*tv*/, | |
1543 | const void */*p0*/, size_t /*sz0*/, | |
1544 | const void */*p1*/, size_t /*sz1*/, | |
1545 | const char */*file*/, unsigned /*lno*/, | |
1546 | const char */*expr*/); | |
b64eb60f MW |
1547 | #define TVEC_CLAIMEQ_STRING(tv, p0, sz0, p1, sz1) \ |
1548 | (tvec_claimeq_string(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \ | |
1549 | #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]")) | |
1550 | #define TVEC_CLAIMEQ_STRZ(tv, p0, p1) \ | |
1551 | (tvec_claimeq_strz(tv, p0, p1, __FILE__, __LINE__, #p0 " /= " #p1)) | |
1552 | #define TVEC_CLAIMEQ_BYTES(tv, p0, sz0, p1, sz1) \ | |
1553 | (tvec_claimeq(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \ | |
1554 | #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]")) | |
1555 | ||
1556 | extern const struct tvec_regty tvty_buffer; | |
1557 | ||
1558 | extern void tvec_allocstring(union tvec_regval */*rv*/, size_t /*sz*/); | |
1559 | extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/); | |
1560 | ||
c5e0e403 MW |
1561 | /*----- Benchmarking ------------------------------------------------------*/ |
1562 | ||
1563 | struct tvec_bench { | |
1564 | struct tvec_env _env; /* benchmarking is an environment */ | |
1565 | struct bench_state **bst; /* benchmark state anchor or null */ | |
1566 | unsigned long niter; /* iterations done per unit */ | |
1567 | int riter, rbuf; /* iterations and buffer registers */ | |
1568 | const struct tvec_env *env; /* environment (per test, not grp) */ | |
1569 | }; | |
1570 | #define TVEC_BENCHENV \ | |
1571 | { sizeof(struct tvec_benchctx), \ | |
1572 | tvec_benchsetup, \ | |
1573 | tvec_benchset, \ | |
1574 | tvec_benchbefore, \ | |
1575 | tvec_benchrun, \ | |
1576 | tvec_benchafter, \ | |
1577 | tvec_benchteardown } | |
1578 | #define TVEC_BENCHINIT TVEC_BENCHENV, &tvec_benchstate | |
1579 | ||
1580 | struct tvec_benchctx { | |
1581 | const struct tvec_bench *b; | |
1582 | struct bench_state *bst; | |
1583 | double dflt_target; | |
1584 | void *subctx; | |
1585 | }; | |
1586 | ||
1587 | extern struct bench_state *tvec_benchstate; | |
1588 | ||
1589 | extern int tvec_benchsetup(struct tvec_state */*tv*/, | |
1590 | const struct tvec_env */*env*/, | |
1591 | void */*pctx*/, void */*ctx*/); | |
1592 | extern int tvec_benchset(struct tvec_state */*tv*/, const char */*var*/, | |
1593 | const struct tvec_env */*env*/, void */*ctx*/); | |
1594 | extern int tvec_benchbefore(struct tvec_state */*tv*/, void */*ctx*/); | |
1595 | extern void tvec_benchrun(struct tvec_state */*tv*/, | |
1596 | tvec_testfn */*fn*/, void */*ctx*/); | |
1597 | extern void tvec_benchafter(struct tvec_state */*tv*/, void */*ctx*/); | |
1598 | extern void tvec_benchteardown(struct tvec_state */*tv*/, void */*ctx*/); | |
1599 | ||
1600 | extern void tvec_benchreport | |
1601 | (const struct gprintf_ops */*gops*/, void */*go*/, | |
1602 | unsigned /*unit*/, const struct bench_timing */*tm*/); | |
1603 | ||
1604 | /*----- Command-line interface --------------------------------------------*/ | |
1605 | ||
1606 | extern const struct tvec_config tvec_adhocconfig; | |
1607 | ||
1608 | extern void tvec_parseargs(int /*argc*/, char */*argv*/[], | |
1609 | struct tvec_state */*tv_out*/, | |
1610 | int */*argpos_out*/, | |
1611 | const struct tvec_config */*config*/); | |
1612 | ||
1613 | extern int tvec_readstdin(struct tvec_state */*tv*/); | |
1614 | extern int tvec_readfile(struct tvec_state */*tv*/, const char */*file*/); | |
1615 | extern int tvec_readdflt(struct tvec_state */*tv*/, const char */*file*/); | |
1616 | extern int tvec_readarg(struct tvec_state */*tv*/, const char */*arg*/); | |
1617 | ||
1618 | extern int tvec_readargs(int /*argc*/, char */*argv*/[], | |
1619 | struct tvec_state */*tv*/, | |
1620 | int */*argpos_inout*/, const char */*dflt*/); | |
1621 | ||
1622 | extern int tvec_main(int /*argc*/, char */*argv*/[], | |
1623 | const struct tvec_config */*config*/, | |
1624 | const char */*dflt*/); | |
1625 | ||
b64eb60f MW |
1626 | /*----- That's all, folks -------------------------------------------------*/ |
1627 | ||
1628 | #ifdef __cplusplus | |
1629 | } | |
1630 | #endif | |
1631 | ||
1632 | #endif |