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 | ||
c91413e6 MW |
35 | /* Here's the overall flow for a testing session. |
36 | * | |
37 | * @tvec_begin@ | |
38 | * -> output @bsession@ | |
39 | * @tvec_read@ | |
40 | * -> output @bgroup@ | |
41 | * -> env @setup@ | |
42 | * one or more tests | |
43 | * -> type @init@ (input and output) | |
44 | * -> type @parse@ (input) | |
45 | * -> output @btest@ | |
46 | * -> env @before@ | |
47 | * -> @tvec_skipgroup@ | |
48 | * -> output @skipgroup@ | |
49 | * -> env @run@ | |
50 | * -> @tvec_skip@ | |
51 | * -> output @skip@ | |
52 | * -> test @fn@ | |
53 | * -> @tvec_checkregs@ | |
54 | * -> type @eq@ | |
55 | * -> @tvec_fail@ | |
56 | * -> output @fail@ | |
57 | * -> @tvec_mismatch@ | |
58 | * -> output @dumpreg@ | |
59 | * -> type @dump@ | |
c91413e6 | 60 | * -> output @etest@ |
31d0247c | 61 | * -> env @after@ |
c91413e6 MW |
62 | * finally |
63 | * -> output @egroup@ | |
64 | * -> env @teardown@ | |
65 | * | |
66 | * @tvec_adhoc@ | |
67 | * @tvec_begingroup@ | |
68 | * -> output @bgroup@ | |
69 | * -> env @setup@ | |
70 | * @tvec_begintest@ | |
71 | * -> output @btest@ | |
72 | * @tvec_skip@ | |
73 | * -> output @skip@ | |
74 | * @tvec_claimeq@ | |
75 | * -> @tvec_fail@ | |
76 | * -> output @fail@ | |
77 | * -> @tvec_mismatch@ | |
78 | * -> output @dumpreg@ | |
79 | * -> type @dump@ | |
80 | * @tvec_endtest@ | |
81 | * -> output @etest@ | |
82 | * or @tvec_skipgroup@ | |
83 | * -> output @skipgroup@ | |
84 | * @tvec_endgroup@ | |
85 | * -> output @egroup@ | |
86 | * | |
87 | * @tvec_end@ | |
88 | * -> output @esession@ | |
89 | * -> output @destroy@ | |
90 | * | |
91 | * @tvec_benchrun@ | |
92 | * -> type @dump@ (compact style) | |
93 | * -> output @bbench@ | |
94 | * -> subenv @run@ | |
95 | * -> test @fn@ | |
96 | * -> output @ebench@ | |
97 | * -> @tvec_benchreport@ | |
98 | * | |
99 | * The output functions @error@ and @notice@ can be called at arbitrary | |
100 | * times. | |
101 | */ | |
102 | ||
b64eb60f MW |
103 | /*----- Header files ------------------------------------------------------*/ |
104 | ||
105 | #include <stdarg.h> | |
106 | #include <stddef.h> | |
107 | #include <stdio.h> | |
108 | #include <string.h> | |
109 | ||
110 | #ifndef MLIB_BUF_H | |
111 | # include "buf.h" | |
112 | #endif | |
113 | ||
114 | #ifndef MLIB_CONTROL_H | |
115 | # include "control.h" | |
116 | #endif | |
117 | ||
118 | #ifndef MLIB_BUF_H | |
119 | # include "dstr.h" | |
120 | #endif | |
121 | ||
e63124bc MW |
122 | #ifndef MLIB_GPRINTF_H |
123 | # include "gprintf.h" | |
124 | #endif | |
125 | ||
c91413e6 MW |
126 | #ifndef MLIB_LBUF_H |
127 | # include "lbuf.h" | |
128 | #endif | |
129 | ||
b64eb60f MW |
130 | #ifndef MLIB_MACROS_H |
131 | # include "macros.h" | |
132 | #endif | |
133 | ||
134 | /*----- Miscellaneous values ----------------------------------------------*/ | |
135 | ||
136 | /* These are attached to structures which represent extension points, as a | |
137 | * way to pass an opaque parameter to whatever things are hooked onto them. | |
138 | */ | |
139 | ||
140 | #define TVEC_MISCSLOTS(_) \ | |
141 | _(PTR, const void *, p) /* arbitrary pointer */ \ | |
142 | _(INT, long, i) /* signed integer */ \ | |
e63124bc MW |
143 | _(UINT, unsigned long, u) /* signed integer */ \ |
144 | _(FLT, double, f) /* floating point */ | |
b64eb60f MW |
145 | |
146 | union tvec_misc { | |
147 | #define TVEC_DEFSLOT(tag, ty, slot) ty slot; | |
148 | TVEC_MISCSLOTS(TVEC_DEFSLOT) | |
149 | #undef TVEC_DEFSLOT | |
150 | }; | |
151 | enum { | |
152 | #define TVEC_DEFCONST(tag, ty, slot) TVMISC_##tag, | |
153 | TVEC_MISCSLOTS(TVEC_DEFCONST) | |
154 | TVMISC_LIMIT | |
155 | }; | |
156 | ||
157 | /*----- Register values ---------------------------------------------------*/ | |
158 | ||
159 | /* The framework doesn't have a preconceived idea about what's in a register | |
160 | * value: it just allocates them and accesses them through the register type | |
161 | * functions. It doesn't even have a baked-in idea of how big a register | |
162 | * value is: instead, it gets that via the `regsz' slot in `struct | |
163 | * tvec_testinfo'. So, as far as the framework is concerned, it's safe to | |
164 | * add new slots to this union, even if they make the overall union larger. | |
165 | * This can be done by defining the preprocessor macro `TVEC_REGSLOTS' to be | |
166 | * a `union' fragment defining any additional union members. | |
167 | * | |
168 | * This creates a distinction between code which does and doesn't know the | |
169 | * size of a register value. Code which does, which typically means the test | |
170 | * functions, benchmarking setup and teardown functions, and tightly-bound | |
171 | * runner functions, is free to index the register vectors directly. Code | |
172 | * which doesn't, which means the framework core itself and output formatting | |
173 | * machinery, must use the `TVEC_REG' macro (or its more general `TVEC_GREG' | |
174 | * companion) for indexing register vectors. (In principle, register type | |
175 | * handlers also fit into this category, but they have no business peering | |
176 | * into register values other than the one's they're given.) | |
177 | */ | |
178 | ||
179 | union tvec_regval { | |
31d0247c MW |
180 | /* The actual register value. This is what the type handler sees. |
181 | * Additional members can be added by setting `TVEC_REGSLOTS' before | |
182 | * including this file. | |
183 | * | |
184 | * A register value can be /initialized/, which simply means that its | |
185 | * contents represent a valid value according to its type -- the register | |
186 | * can be compared, dumped, serialized, parsed into, etc. You can't do | |
187 | * anything safely to an uninitialized register value other than initialize | |
188 | * it. | |
189 | */ | |
b64eb60f MW |
190 | |
191 | long i; /* signed integer */ | |
192 | unsigned long u; /* unsigned integer */ | |
193 | void *p; /* pointer */ | |
e63124bc | 194 | double f; /* floating point */ |
c81c35df | 195 | struct { char *p; size_t sz; } text; /* text string */ |
d056fbdf | 196 | struct { unsigned char *p; size_t sz; } bytes; /* binary string of bytes */ |
adec5584 MW |
197 | struct { /* buffer */ |
198 | unsigned char *p; size_t sz; /* binary string */ | |
199 | size_t a, m; /* residue and modulus */ | |
200 | size_t off; /* offset into full buffer */ | |
201 | } buf; | |
b64eb60f MW |
202 | #ifdef TVEC_REGSLOTS |
203 | TVEC_REGSLOTS | |
204 | #endif | |
205 | }; | |
206 | ||
207 | struct tvec_reg { | |
31d0247c MW |
208 | /* A register. |
209 | * | |
210 | * Note that all of the registers listed as being used by a particular test | |
211 | * group are initialized at all times[1] while that test group is being | |
212 | * processed. (The other register slots don't even have types associated | |
213 | * with them, so there's nothing useful we could do with them.) | |
214 | * | |
215 | * The `TVRF_LIVE' flag indicates that the register was assigned a value by | |
216 | * the test vector file: it's the right thing to use to check whether an | |
217 | * optional register is actually present. Even `dead' registers are still | |
218 | * initialized, though. | |
219 | * | |
220 | * [1] This isn't quite true. Between individual tests, the registers are | |
221 | * released and reinitialized in order to reset them to known values | |
222 | * ready for the next test. But you won't see them at this point. | |
223 | */ | |
b64eb60f MW |
224 | |
225 | unsigned f; /* flags */ | |
c4ccbbf9 MW |
226 | #define TVRF_SEEN 1u /* assignment seen in file */ |
227 | #define TVRF_LIVE 2u /* used in current test */ | |
b64eb60f MW |
228 | union tvec_regval v; /* register value */ |
229 | }; | |
230 | ||
231 | struct tvec_regdef { | |
31d0247c MW |
232 | /* A register definition. Register definitions list the registers which |
233 | * are used by a particular test group (see `struct tvec_test' below). | |
234 | * | |
235 | * A vector of register definitions is terminated by a definition whose | |
236 | * `name' slot is null. | |
237 | */ | |
b64eb60f MW |
238 | |
239 | const char *name; /* register name (for input files) */ | |
b64eb60f | 240 | const struct tvec_regty *ty; /* register type descriptor */ |
d056fbdf | 241 | unsigned i; /* register index */ |
b64eb60f | 242 | unsigned f; /* flags */ |
c4ccbbf9 MW |
243 | #define TVRF_UNSET 1u /* register may be marked unset */ |
244 | #define TVRF_OPT 2u /* register need not be assigned */ | |
245 | #define TVRF_ID 4u /* part of test identity */ | |
b64eb60f MW |
246 | union tvec_misc arg; /* extra detail for the type */ |
247 | }; | |
db2bf411 | 248 | #define TVEC_ENDREGS { 0, 0, 0, 0, { 0 } } |
b64eb60f | 249 | |
e63124bc MW |
250 | /* @TVEC_GREG(vec, i, regsz)@ |
251 | * | |
252 | * If @vec@ is a data pointer which happens to contain the address of a | |
253 | * vector of @struct tvec_reg@ objects, @i@ is an integer, and @regsz@ is the | |
254 | * size of a @struct tvec_reg@, then this evaluates to the address of the | |
255 | * @i@th element of the vector. | |
256 | * | |
257 | * This is the general tool you need for accessing register vectors when you | |
258 | * don't have absolute knowledge of the size of a @union tvec_regval@. | |
259 | * Usually you want to access one of the register vectors in a @struct | |
260 | * tvec_state@, and @TVEC_REG@ will be more convenient. | |
261 | */ | |
262 | #define TVEC_GREG(vec, i, regsz) \ | |
263 | ((struct tvec_reg *)((unsigned char *)(vec) + (i)*(regsz))) | |
264 | ||
c91413e6 MW |
265 | /*----- Register types ----------------------------------------------------*/ |
266 | ||
267 | struct tvec_state; /* forward declaration */ | |
268 | ||
269 | struct tvec_regty { | |
270 | /* A register type. */ | |
271 | ||
272 | void (*init)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/); | |
31d0247c | 273 | /* Initialize the value in @*rv@. This will be called before any other |
c4ccbbf9 MW |
274 | * function acting on the value, including @release@. Following @init@, |
275 | * the register value must be valid to use for all other type entry | |
276 | * points. | |
31d0247c | 277 | */ |
c91413e6 MW |
278 | |
279 | void (*release)(union tvec_regval */*rv*/, | |
280 | const struct tvec_regdef */*rd*/); | |
c4ccbbf9 MW |
281 | /* Release any resources associated with the value in @*rv@. The |
282 | * register value may be left in an invalid state. | |
283 | */ | |
c91413e6 MW |
284 | |
285 | int (*eq)(const union tvec_regval */*rv0*/, | |
286 | const union tvec_regval */*rv1*/, | |
287 | const struct tvec_regdef */*rd*/); | |
31d0247c MW |
288 | /* Return nonzero if @*rv0@ and @*rv1@ are equal values. Asymmetric |
289 | * criteria are permitted: @tvec_checkregs@ calls @eq@ with the input | |
290 | * register as @rv0@ and the output as @rv1@. | |
291 | */ | |
c91413e6 MW |
292 | |
293 | int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/, | |
294 | const struct tvec_regdef */*rd*/); | |
31d0247c MW |
295 | /* Serialize the value @*rv@, writing the result to @b@. Return zero on |
296 | * success, or %$-1$% on error. | |
297 | */ | |
c91413e6 MW |
298 | |
299 | int (*frombuf)(buf */*b*/, union tvec_regval */*rv*/, | |
300 | const struct tvec_regdef */*rd*/); | |
31d0247c MW |
301 | /* Deserialize a value from @b@, storing it in @*rv@. Return zero on |
302 | * success, or %$-1$% on error. | |
303 | */ | |
c91413e6 MW |
304 | |
305 | int (*parse)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/, | |
306 | struct tvec_state */*tv*/); | |
31d0247c MW |
307 | /* Parse a value from @tv->fp@, storing it in @*rv@. Return zero on |
308 | * success, or %$-1$% on error, having reported one or more errors via | |
309 | * @tvec_error@ or @tvec_syntax@. A successful return should leave the | |
310 | * input position at the start of the next line; the caller will flush | |
311 | * the remainder of the line itself. | |
312 | */ | |
c91413e6 MW |
313 | |
314 | void (*dump)(const union tvec_regval */*rv*/, | |
315 | const struct tvec_regdef */*rd*/, | |
316 | unsigned /*style*/, | |
317 | const struct gprintf_ops */*gops*/, void */*go*/); | |
318 | #define TVSF_COMPACT 1u | |
31d0247c MW |
319 | /* Write a human-readable representation of the value @*rv@ using |
320 | * @gprintf@ on @gops@ and @go@. The @style@ is a collection of flags: | |
321 | * if @TVSF_COMPACT@ is set, then output should be minimal, and must fit | |
322 | * on a single line; otherwise, output may consist of multiple lines and | |
323 | * may contain redundant information if that is likely to be useful to a | |
324 | * human reader. | |
325 | */ | |
c91413e6 MW |
326 | }; |
327 | ||
328 | /*----- Test descriptions -------------------------------------------------*/ | |
329 | ||
d056fbdf MW |
330 | struct tvec_env; |
331 | ||
c91413e6 MW |
332 | typedef void tvec_testfn(const struct tvec_reg */*in*/, |
333 | struct tvec_reg */*out*/, | |
334 | void */*ctx*/); | |
335 | /* A test function. It should read inputs from @in@ and write outputs to | |
336 | * @out@. The @TVRF_LIVE@ is set on inputs which are actually present, and | |
337 | * on outputs which are wanted to test. A test function can set additional | |
338 | * `gratuitous outputs' by setting @TVRF_LIVE@ on them; clearing | |
339 | * @TVRF_LIVE@ on a wanted output causes a mismatch. | |
340 | * | |
341 | * A test function may be called zero or more times by the environment. In | |
342 | * particular, it may be called multiple times, though usually by prior | |
343 | * arrangement with the environment. | |
344 | * | |
345 | * The @ctx@ is supplied by the environment's @run@ function (see below). | |
346 | * The default environment calls the test function once, with a null | |
347 | * @ctx@. There is no expectation that the environment's context has | |
348 | * anything to do with the test function's context. | |
349 | */ | |
350 | ||
814e42ff MW |
351 | typedef int tvec_setvarfn(struct tvec_state */*tv*/, const char */*var*/, |
352 | const union tvec_regval */*rv*/, void */*ctx*/); | |
353 | /* Called after a variable is read. Return zero on success or %$-1$% on | |
354 | * error. This function is never called if the test group is skipped. | |
355 | */ | |
356 | ||
357 | struct tvec_vardef { | |
358 | size_t regsz; /* (minimum) register size */ | |
359 | tvec_setvarfn *setvar; /* function to set variable */ | |
360 | struct tvec_regdef def; /* register definition */ | |
361 | }; | |
362 | ||
c91413e6 MW |
363 | typedef void tvec_envsetupfn(struct tvec_state */*tv*/, |
364 | const struct tvec_env */*env*/, | |
365 | void */*pctx*/, void */*ctx*/); | |
366 | /* Initialize the context; called at the start of a test group; @pctx@ is | |
367 | * null for environments called by the core, but may be non-null for | |
368 | * subordinate environments. If setup fails, the function should call | |
31d0247c MW |
369 | * @tvec_skipgroup@ with a suitable excuse. The @set@, @after@, and |
370 | * @teardown@ entry points will still be called, but @before@ and @run@ | |
371 | * will not. | |
c91413e6 MW |
372 | */ |
373 | ||
814e42ff MW |
374 | typedef const struct tvec_vardef *tvec_envfindvarfn |
375 | (struct tvec_state */*tv*/, const char */*name*/, | |
376 | void **/*ctx_out*/, void */*ctx*/); | |
377 | /* Called when the parser finds a %|@var|%' special variable. If a | |
378 | * suitable variable was found, set @*ctx_out@ to a suitable context and | |
379 | * return the variable definition; the context will be passed to the | |
380 | * variable definition's @setvar@ function. If no suitable variable was | |
381 | * found, then return null. | |
c91413e6 MW |
382 | */ |
383 | ||
384 | typedef void tvec_envbeforefn(struct tvec_state */*tv*/, void */*ctx*/); | |
385 | /* Called prior to running a test. This is the right place to act on any | |
386 | * `%|@var|%' settings. If preparation fails, the function should call | |
387 | * @tvec_skip@ with a suitable excuse. This function is never called if | |
814e42ff MW |
388 | * the test group is skipped. It %%\emph{is}%% called if the test will be |
389 | * skipped due to erroneous test data. It should check the @TVSF_ACTIVE@ | |
390 | * flag if necessary. | |
c91413e6 MW |
391 | */ |
392 | ||
393 | typedef void tvec_envrunfn(struct tvec_state */*tv*/, | |
394 | tvec_testfn */*fn*/, void */*ctx*/); | |
395 | /* Run the test. It should either call @tvec_skip@, or run @fn@ one or | |
396 | * more times. In the latter case, it is responsible for checking the | |
397 | * outputs, and calling @tvec_fail@ as necessary; @tvec_checkregs@ will | |
398 | * check the register values against the supplied test vector, while | |
399 | * @tvec_check@ does pretty much everything necessary. This function is | |
400 | * never called if the test group is skipped. | |
401 | */ | |
402 | ||
403 | typedef void tvec_envafterfn(struct tvec_state */*tv*/, void */*ctx*/); | |
404 | /* Called after running or skipping a test. Typical actions involve | |
31d0247c | 405 | * resetting whatever things were established by @set@. This function |
814e42ff MW |
406 | * %%\emph{is}%% called if the test group is skipped or the test data is |
407 | * erroneous, so that the test environment can reset variables set by the | |
408 | * @set@ entry point. It should check the @TVSF_SKIP@ flag if necessary. | |
c91413e6 MW |
409 | */ |
410 | ||
411 | typedef void tvec_envteardownfn(struct tvec_state */*tv*/, void */*ctx*/); | |
412 | /* Tear down the environment: called at the end of a test group. */ | |
413 | ||
414 | ||
415 | struct tvec_env { | |
416 | /* A test environment sets things up for and arranges to run the test. | |
417 | * | |
418 | * The caller is responsible for allocating storage for the environment's | |
419 | * context, based on the @ctxsz@ slot, and freeing it later; this space is | |
420 | * passed in as the @ctx@ parameter to the remaining functions; if @ctxsz@ | |
421 | * is zero then @ctx@ is null. | |
422 | */ | |
423 | ||
424 | size_t ctxsz; /* environment context size */ | |
425 | ||
426 | tvec_envsetupfn *setup; /* setup for group */ | |
814e42ff | 427 | tvec_envfindvarfn *findvar; /* find variable */ |
c91413e6 MW |
428 | tvec_envbeforefn *before; /* prepare for test */ |
429 | tvec_envrunfn *run; /* run test function */ | |
430 | tvec_envafterfn *after; /* clean up after test */ | |
431 | tvec_envteardownfn *teardown; /* tear down after group */ | |
432 | }; | |
433 | ||
434 | struct tvec_test { | |
435 | /* A test description. */ | |
436 | ||
437 | const char *name; /* name of the test */ | |
438 | const struct tvec_regdef *regs; /* descriptions of the registers */ | |
439 | const struct tvec_env *env; /* environment to run test in */ | |
440 | tvec_testfn *fn; /* test function */ | |
441 | }; | |
442 | #define TVEC_ENDTESTS { 0, 0, 0, 0 } | |
443 | ||
b64eb60f MW |
444 | /*----- Test state --------------------------------------------------------*/ |
445 | ||
3efcfd2d MW |
446 | enum { |
447 | /* Possible test outcomes. */ | |
448 | ||
449 | TVOUT_LOSE, /* test failed */ | |
450 | TVOUT_SKIP, /* test skipped */ | |
451 | TVOUT_WIN, /* test passed */ | |
20ba6b0b | 452 | TVOUT_XFAIL, /* test passed, but shouldn't have */ |
3efcfd2d MW |
453 | TVOUT_LIMIT /* (number of possible outcomes) */ |
454 | }; | |
b64eb60f | 455 | |
d056fbdf MW |
456 | struct tvec_config { |
457 | /* An overall test configuration. */ | |
458 | ||
459 | const struct tvec_test *tests; /* the tests to be performed */ | |
460 | unsigned nrout, nreg; /* number of output/total regs */ | |
461 | size_t regsz; /* size of a register */ | |
462 | }; | |
463 | ||
b64eb60f | 464 | struct tvec_state { |
e63124bc MW |
465 | /* The primary state structure for the test vector machinery. */ |
466 | ||
d056fbdf | 467 | /* Flags. Read-only for all callers. */ |
b64eb60f | 468 | unsigned f; /* flags */ |
20ba6b0b MW |
469 | #define TVSF_SKIP 0x0001u /* skip this test group */ |
470 | #define TVSF_OPEN 0x0002u /* test is open */ | |
471 | #define TVSF_ACTIVE 0x0004u /* test is active */ | |
472 | #define TVSF_ERROR 0x0008u /* an error occurred */ | |
473 | #define TVSF_OUTMASK 0x00f0u /* test outcome (@TVOUT_...@) */ | |
3efcfd2d | 474 | #define TVSF_OUTSHIFT 4 /* shift applied to outcome */ |
20ba6b0b | 475 | #define TVSF_XFAIL 0x0100u /* test expected to fail */ |
31d0247c | 476 | #define TVSF_MUFFLE 0x0200u /* muffle errors */ |
e63124bc | 477 | |
d056fbdf MW |
478 | /* Test configuration. Read-only for all callers. */ |
479 | struct tvec_config cfg; /* test configuration */ | |
480 | ||
481 | /* Registers. Available to execution environments, which may modify the | |
482 | * contents of the active registers, as defined by the current test group, | |
483 | * but not the vector pointers themselves or inactive registers. | |
484 | */ | |
b64eb60f | 485 | struct tvec_reg *in, *out; /* register vectors */ |
e63124bc | 486 | |
d056fbdf MW |
487 | /* Test group state. Read-only for all callers. */ |
488 | const struct tvec_test *test; /* current test */ | |
e63124bc MW |
489 | |
490 | /* Test scoreboard. Available to output formatters. */ | |
b64eb60f | 491 | unsigned curr[TVOUT_LIMIT], all[TVOUT_LIMIT], grps[TVOUT_LIMIT]; |
e63124bc MW |
492 | |
493 | /* Output machinery. */ | |
b64eb60f | 494 | struct tvec_output *output; /* output formatter */ |
e63124bc MW |
495 | |
496 | /* Input machinery. Available to type parsers. */ | |
b64eb60f MW |
497 | const char *infile; unsigned lno, test_lno; /* input file name, line */ |
498 | FILE *fp; /* input file stream */ | |
499 | }; | |
500 | ||
e63124bc MW |
501 | /* @TVEC_REG(tv, vec, i)@ |
502 | * | |
503 | * If @tv@ is a pointer to a @struct tvec_state@, @vec@ is either @in@ or | |
504 | * @out@, and @i@ is an integer, then this evaluates to the address of the | |
505 | * @i@th register in the selected vector. | |
506 | */ | |
d056fbdf | 507 | #define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->cfg.regsz) |
67b5031e MW |
508 | |
509 | /*----- Output formatting -------------------------------------------------*/ | |
510 | ||
511 | struct tvec_output { | |
512 | /* An output formatter. */ | |
513 | const struct tvec_outops *ops; /* pointer to operations */ | |
514 | }; | |
515 | ||
d056fbdf MW |
516 | enum { |
517 | /* Register output dispositions. */ | |
518 | ||
519 | TVRD_INPUT, /* input-only register */ | |
520 | TVRD_OUTPUT, /* output-only (input is dead) */ | |
521 | TVRD_MATCH, /* matching (equal) registers */ | |
522 | TVRD_FOUND, /* mismatching output register */ | |
523 | TVRD_EXPECT, /* mismatching input register */ | |
524 | TVRD_LIMIT /* (number of dispositions) */ | |
525 | }; | |
526 | ||
527 | #define TVEC_LEVELS(_) \ | |
528 | _(NOTE, "notice", 4) \ | |
529 | _(ERR, "ERROR", 8) | |
530 | enum { | |
531 | #define TVEC_DEFLEVEL(tag, name, val) TVLEV_##tag = val, | |
532 | TVEC_LEVELS(TVEC_DEFLEVEL) | |
533 | #undef TVEC_DEFLEVEL | |
534 | TVLEV_LIMIT | |
535 | }; | |
536 | ||
67b5031e MW |
537 | /* Benchmarking details. */ |
538 | enum { | |
539 | TVBU_OP, /* counting operations of some kind */ | |
c91413e6 MW |
540 | TVBU_BYTE, /* counting bytes (@rbuf >= 0@) */ |
541 | TVBU_LIMIT /* (number of units) */ | |
67b5031e | 542 | }; |
adec5584 | 543 | struct bench_timing; /* include <mLib/bench.h> for the real definition */ |
67b5031e MW |
544 | |
545 | struct tvec_outops { | |
546 | /* Output operations. */ | |
547 | ||
548 | void (*bsession)(struct tvec_output */*o*/, struct tvec_state */*tv*/); | |
31d0247c MW |
549 | /* Begin a test session. The output driver will probably want to |
550 | * save @tv@, because this isn't provided to any other methods. | |
551 | */ | |
67b5031e MW |
552 | |
553 | int (*esession)(struct tvec_output */*o*/); | |
31d0247c | 554 | /* End a session, and return the suggested exit code. */ |
67b5031e MW |
555 | |
556 | void (*bgroup)(struct tvec_output */*o*/); | |
31d0247c | 557 | /* Begin a test group. The test group description is @tv->test@. */ |
67b5031e MW |
558 | |
559 | void (*skipgroup)(struct tvec_output */*o*/, | |
560 | const char */*excuse*/, va_list */*ap*/); | |
31d0247c MW |
561 | /* The group is being skipped; @excuse@ may be null or a format |
562 | * string explaining why. The @egroup@ method will not be called | |
563 | * separately. | |
564 | */ | |
67b5031e MW |
565 | |
566 | void (*egroup)(struct tvec_output */*o*/); | |
31d0247c MW |
567 | /* End a test group. At least one test was attempted or @skipgroup@ |
568 | * would have been called instead. If @tv->curr[TVOUT_LOSE]@ is nonzero | |
569 | * then the test group as a whole failed; otherwise it passed. | |
570 | */ | |
67b5031e MW |
571 | |
572 | void (*btest)(struct tvec_output */*o*/); | |
31d0247c | 573 | /* Begin a test case. */ |
67b5031e MW |
574 | |
575 | void (*skip)(struct tvec_output */*o*/, | |
576 | const char */*excuse*/, va_list */*ap*/); | |
31d0247c MW |
577 | /* The test case is being skipped; @excuse@ may be null or a format |
578 | * string explaining why. The @etest@ function will still be called (so | |
579 | * this works differently from @skipgroup@ and @egroup@). A test case | |
580 | * can be skipped at most once, and, if skipped, it cannot fail. | |
581 | */ | |
67b5031e MW |
582 | |
583 | void (*fail)(struct tvec_output */*o*/, | |
584 | const char */*detail*/, va_list */*ap*/); | |
31d0247c MW |
585 | /* The test case failed. |
586 | * | |
587 | * The output driver should preferably report the filename (@infile@) and | |
588 | * line number (@test_lno@, not @lno@) for the failing test. | |
589 | * | |
590 | * The @detail@ may be null or a format string describing detail about | |
591 | * how the failing test was run which can't be determined from the | |
592 | * registers; a @detail@ is usually provided when (and only when) the | |
593 | * test environment potentially invokes the test function more than once. | |
594 | * | |
595 | * A single test case can fail multiple times! | |
596 | */ | |
67b5031e MW |
597 | |
598 | void (*dumpreg)(struct tvec_output */*o*/, | |
599 | unsigned /*disp*/, const union tvec_regval */*rv*/, | |
600 | const struct tvec_regdef */*rd*/); | |
31d0247c MW |
601 | /* Dump a register. The `disposition' @disp@ explains what condition the |
602 | * register is in; see @tvec_dumpreg@ and the @TVRD_...@ codes. The | |
603 | * register value is at @rv@, and its definition, including its type, at | |
604 | * @rd@. Note that this function may be called on virtual registers | |
605 | * which aren't in either of the register vectors or mentioned by the | |
606 | * test description. It may also be called with @rv@ null, indicating | |
607 | * that the register is not live. | |
608 | */ | |
67b5031e MW |
609 | |
610 | void (*etest)(struct tvec_output */*o*/, unsigned /*outcome*/); | |
31d0247c MW |
611 | /* The test case concluded with the given @outcome@ (one of the |
612 | * @TVOUT_...@ codes. | |
613 | */ | |
67b5031e MW |
614 | |
615 | void (*bbench)(struct tvec_output */*o*/, | |
616 | const char */*ident*/, unsigned /*unit*/); | |
31d0247c MW |
617 | /* Begin a benchmark. The @ident@ is a formatted string identifying the |
618 | * benchmark based on the values of the input registered marked | |
619 | * @TVRF_ID@; the output driver is free to use this or come up with its | |
620 | * own way to identify the test, e.g., by inspecting the register values | |
621 | * for itself. The @unit@ is one of the @TVBU_...@ constants explaining | |
622 | * what sort of thing is being measured. | |
623 | */ | |
67b5031e MW |
624 | |
625 | void (*ebench)(struct tvec_output */*o*/, | |
626 | const char */*ident*/, unsigned /*unit*/, | |
627 | const struct bench_timing */*tm*/); | |
31d0247c MW |
628 | /* End a benchmark. The @ident@ and @unit@ arguments are as for |
629 | * @bbench@. If @tm@ is zero then the measurement failed; otherwise | |
630 | * @tm->n@ counts total number of things (operations or bytes, as | |
631 | * indicated by @unit@) processed in the indicated time. | |
632 | */ | |
67b5031e | 633 | |
c91413e6 | 634 | void (*report)(struct tvec_output */*o*/, unsigned /*level*/, |
67b5031e | 635 | const char */*msg*/, va_list */*ap*/); |
31d0247c MW |
636 | /* Report a message. The driver should ideally report the filename |
637 | * (@infile@) and line number (@lno@) prompting the error. | |
638 | */ | |
67b5031e MW |
639 | |
640 | void (*destroy)(struct tvec_output */*o*/); | |
31d0247c | 641 | /* Release any resources acquired by the driver. */ |
67b5031e MW |
642 | }; |
643 | ||
67b5031e MW |
644 | /*----- Session lifecycle -------------------------------------------------*/ |
645 | ||
67b5031e MW |
646 | /* --- @tvec_begin@ --- * |
647 | * | |
648 | * Arguments: @struct tvec_state *tv_out@ = state structure to fill in | |
649 | * @const struct tvec_config *config@ = test configuration | |
650 | * @struct tvec_output *o@ = output driver | |
651 | * | |
652 | * Returns: --- | |
653 | * | |
654 | * Use: Initialize a state structure ready to do some testing. | |
655 | */ | |
656 | ||
657 | extern void tvec_begin(struct tvec_state */*tv_out*/, | |
658 | const struct tvec_config */*config*/, | |
659 | struct tvec_output */*o*/); | |
660 | ||
661 | /* --- @tvec_end@ --- * | |
662 | * | |
663 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
664 | * | |
665 | * Returns: A proposed exit code. | |
666 | * | |
667 | * Use: Conclude testing and suggests an exit code to be returned to | |
668 | * the calling program. (The exit code comes from the output | |
669 | * driver's @esession@ method.) | |
670 | */ | |
671 | ||
672 | extern int tvec_end(struct tvec_state */*tv*/); | |
673 | ||
674 | /* --- @tvec_read@ --- * | |
675 | * | |
676 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
677 | * @const char *infile@ = the name of the input file | |
678 | * @FILE *fp@ = stream to read from | |
679 | * | |
31d0247c | 680 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
681 | * |
682 | * Use: Read test vector data from @fp@ and exercise test functions. | |
683 | * THe return code doesn't indicate test failures: it's only | |
684 | * concerned with whether there were problems with the input | |
685 | * file or with actually running the tests. | |
686 | */ | |
687 | ||
688 | extern int tvec_read(struct tvec_state */*tv*/, | |
689 | const char */*infile*/, FILE */*fp*/); | |
690 | ||
691 | /*----- Command-line interface --------------------------------------------*/ | |
692 | ||
693 | extern const struct tvec_config tvec_adhocconfig; | |
20ba6b0b MW |
694 | /* A special @struct tvec_config@ to use for programs which perform ad-hoc |
695 | * testing. | |
696 | */ | |
67b5031e MW |
697 | |
698 | /* --- @tvec_parseargs@ --- * | |
699 | * | |
700 | * Arguments: @int argc@ = number of command-line arguments | |
701 | * @char *argv[]@ = vector of argument strings | |
702 | * @struct tvec_state *tv_out@ = test vector state to initialize | |
703 | * @int *argpos_out@ = where to leave unread argument index | |
704 | * @const struct tvec_config *cofig@ = test vector configuration | |
705 | * | |
706 | * Returns: --- | |
707 | * | |
708 | * Use: Parse arguments and set up the test vector state @*tv_out@. | |
709 | * If errors occur, print messages to standard error and exit | |
710 | * with status 2. | |
711 | */ | |
712 | ||
713 | extern void tvec_parseargs(int /*argc*/, char */*argv*/[], | |
714 | struct tvec_state */*tv_out*/, | |
715 | int */*argpos_out*/, | |
716 | const struct tvec_config */*config*/); | |
717 | ||
718 | /* --- @tvec_readstdin@, @tvec_readfile@, @tvec_readarg@ --- * | |
719 | * | |
720 | * Arguments: @struct tvec_state *tv@ = test vector state | |
721 | * @const char *file@ = pathname of file to read | |
722 | * @const char *arg@ = argument to interpret | |
723 | * | |
31d0247c | 724 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
725 | * |
726 | * Use: Read test vector data from stdin or a named file. The | |
727 | * @tvec_readarg@ function reads from stdin if @arg@ is `%|-|%', | |
728 | * and from the named file otherwise. | |
729 | */ | |
730 | ||
731 | extern int tvec_readstdin(struct tvec_state */*tv*/); | |
732 | extern int tvec_readfile(struct tvec_state */*tv*/, const char */*file*/); | |
733 | extern int tvec_readarg(struct tvec_state */*tv*/, const char */*arg*/); | |
734 | ||
735 | /* --- @tvec_readdflt@ --- * | |
736 | * | |
737 | * Arguments: @struct tvec_state *tv@ = test vector state | |
738 | * @const char *dflt@ = defsault filename or null | |
739 | * | |
31d0247c | 740 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
741 | * |
742 | * Use: Reads from the default test vector data. If @file@ is null, | |
743 | * then read from standard input, unless that's a terminal; if | |
744 | * @file@ is not null, then read the named file, looking in the | |
745 | * directory named by the `%|srcdir|%' environment variable if | |
746 | * that's set, or otherwise in the current directory. | |
747 | */ | |
748 | ||
749 | extern int tvec_readdflt(struct tvec_state */*tv*/, const char */*file*/); | |
750 | ||
751 | /* --- @tvec_readargs@ --- * | |
752 | * | |
753 | * Arguments: @int argc@ = number of command-line arguments | |
754 | * @char *argv[]@ = vector of argument strings | |
755 | * @struct tvec_state *tv@ = test vector state | |
756 | * @int *argpos_inout@ = current argument position (updated) | |
757 | * @const char *dflt@ = default filename or null | |
758 | * | |
31d0247c | 759 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
760 | * |
761 | * Use: Reads from the sources indicated by the command-line | |
762 | * arguments, in order, interpreting each as for @tvec_readarg@; | |
763 | * if no arguments are given then read from @dflt@ as for | |
764 | * @tvec_readdflt@. | |
765 | */ | |
766 | ||
767 | extern int tvec_readargs(int /*argc*/, char */*argv*/[], | |
768 | struct tvec_state */*tv*/, | |
769 | int */*argpos_inout*/, const char */*dflt*/); | |
770 | ||
771 | /* --- @tvec_main@ --- * | |
772 | * | |
773 | * Arguments: @int argc@ = number of command-line arguments | |
774 | * @char *argv[]@ = vector of argument strings | |
775 | * @const struct tvec_config *cofig@ = test vector configuration | |
776 | * @const char *dflt@ = default filename or null | |
777 | * | |
778 | * Returns: Exit code. | |
779 | * | |
780 | * Use: All-in-one test vector front-end. Parse options from the | |
781 | * command-line as for @tvec_parseargs@, and then process the | |
782 | * remaining positional arguments as for @tvec_readargs@. The | |
783 | * function constructs and disposes of a test vector state. | |
784 | */ | |
785 | ||
786 | extern int tvec_main(int /*argc*/, char */*argv*/[], | |
787 | const struct tvec_config */*config*/, | |
788 | const char */*dflt*/); | |
789 | ||
790 | /*----- Test processing ---------------------------------------------------*/ | |
791 | ||
792 | /* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- * | |
793 | * | |
794 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
795 | * @const char *excuse@, @va_list *ap@ = reason why skipped | |
796 | * | |
797 | * Returns: --- | |
798 | * | |
799 | * Use: Skip the current group. This should only be called from a | |
800 | * test environment @setup@ function; a similar effect occurs if | |
801 | * the @setup@ function fails. | |
802 | */ | |
803 | ||
31d0247c MW |
804 | extern PRINTF_LIKE(2, 3) |
805 | void tvec_skipgroup(struct tvec_state */*tv*/, | |
806 | const char */*excuse*/, ...); | |
67b5031e MW |
807 | extern void tvec_skipgroup_v(struct tvec_state */*tv*/, |
808 | const char */*excuse*/, va_list */*ap*/); | |
809 | ||
810 | /* --- @tvec_skip@, @tvec_skip_v@ --- * | |
811 | * | |
812 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
813 | * @const char *excuse@, @va_list *ap@ = reason why test skipped | |
814 | * | |
815 | * Returns: --- | |
816 | * | |
817 | * Use: Skip the current test. This should only be called from a | |
818 | * test environment @run@ function; a similar effect occurs if | |
819 | * the @before@ function fails. | |
820 | */ | |
821 | ||
31d0247c MW |
822 | extern PRINTF_LIKE(2, 3) |
823 | void tvec_skip(struct tvec_state */*tv*/, const char */*excuse*/, ...); | |
67b5031e MW |
824 | extern void tvec_skip_v(struct tvec_state */*tv*/, |
825 | const char */*excuse*/, va_list */*ap*/); | |
826 | ||
e63124bc MW |
827 | /* --- @tvec_fail@, @tvec_fail_v@ --- * |
828 | * | |
829 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
3efcfd2d | 830 | * @const char *detail@, @va_list *ap@ = description of test |
e63124bc MW |
831 | * |
832 | * Returns: --- | |
833 | * | |
834 | * Use: Report the current test as a failure. This function can be | |
835 | * called multiple times for a single test, e.g., if the test | |
836 | * environment's @run@ function invokes the test function | |
837 | * repeatedly; but a single test that fails repeatedly still | |
838 | * only counts as a single failure in the statistics. The | |
839 | * @detail@ string and its format parameters can be used to | |
840 | * distinguish which of several invocations failed; it can | |
841 | * safely be left null if the test function is run only once. | |
842 | */ | |
843 | ||
31d0247c MW |
844 | extern PRINTF_LIKE(2, 3) |
845 | void tvec_fail(struct tvec_state */*tv*/, const char */*detail*/, ...); | |
e63124bc MW |
846 | extern void tvec_fail_v(struct tvec_state */*tv*/, |
847 | const char */*detail*/, va_list */*ap*/); | |
848 | ||
849 | /* --- @tvec_dumpreg@ --- * | |
850 | * | |
851 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
852 | * @unsigned disp@ = the register disposition (@TVRD_...@) | |
c91413e6 | 853 | * @const union tvec_regval *tv@ = register value, or null |
e63124bc MW |
854 | * @const struct tvec_regdef *rd@ = register definition |
855 | * | |
856 | * Returns: --- | |
857 | * | |
858 | * Use: Dump a register value to the output. This is the lowest- | |
859 | * level function for dumping registers, and calls the output | |
860 | * formatter directly. | |
861 | * | |
862 | * Usually @tvec_mismatch@ is much more convenient. Low-level | |
863 | * access is required for reporting `virtual' registers | |
864 | * corresponding to test environment settings. | |
865 | */ | |
866 | ||
867 | extern void tvec_dumpreg(struct tvec_state */*tv*/, | |
868 | unsigned /*disp*/, const union tvec_regval */*rv*/, | |
869 | const struct tvec_regdef */*rd*/); | |
870 | ||
31d0247c MW |
871 | /* --- @tvec_initregs@, @tvec_releaseregs@ --- * |
872 | * | |
873 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
874 | * | |
875 | * Returns: --- | |
876 | * | |
877 | * Use: Initialize or release, respectively, the registers required | |
878 | * by the current test. All of the registers, both input and | |
879 | * output, are effected. Initialized registers are not marked | |
880 | * live. | |
881 | */ | |
882 | ||
883 | extern void tvec_initregs(struct tvec_state */*tv*/); | |
884 | extern void tvec_releaseregs(struct tvec_state */*tv*/); | |
885 | ||
886 | /* --- @tvec_resetoutputs@ --- * | |
887 | * | |
888 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
889 | * | |
890 | * Returns: --- | |
891 | * | |
892 | * Use: Reset (releases and reinitializes) the output registers in | |
893 | * the test state. This is mostly of use to test environment | |
894 | * @run@ functions, between invocations of the test function. | |
895 | * Output registers are marked live if and only if the | |
896 | * corresponding input register is live. | |
897 | */ | |
898 | ||
899 | extern void tvec_resetoutputs(struct tvec_state */*tv*/); | |
900 | ||
901 | /* --- @tvec_checkregs@ --- * | |
902 | * | |
903 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
904 | * | |
905 | * Returns: Zero on success, %$-1$% on mismatch. | |
906 | * | |
907 | * Use: Compare the active output registers (according to the current | |
908 | * test group definition) with the corresponding input register | |
909 | * values. A mismatch occurs if the two values differ | |
910 | * (according to the register type's @eq@ method), or if the | |
911 | * input is live but the output is dead. | |
912 | * | |
913 | * This function only checks for a mismatch and returns the | |
914 | * result; it takes no other action. In particular, it doesn't | |
915 | * report a failure, or dump register values. | |
916 | */ | |
917 | ||
918 | extern int tvec_checkregs(struct tvec_state */*tv*/); | |
919 | ||
e63124bc MW |
920 | /* --- @tvec_mismatch@ --- * |
921 | * | |
922 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
923 | * @unsigned f@ = flags (@TVMF_...@) | |
924 | * | |
925 | * Returns: --- | |
926 | * | |
927 | * Use: Dumps registers suitably to report a mismatch. The flag bits | |
928 | * @TVMF_IN@ and @TVF_OUT@ select input-only and output | |
929 | * registers. If both are reset then nothing happens. | |
930 | * Suppressing the output registers may be useful, e.g., if the | |
931 | * test function crashed rather than returning outputs. | |
932 | */ | |
933 | ||
934 | #define TVMF_IN 1u | |
935 | #define TVMF_OUT 2u | |
936 | extern void tvec_mismatch(struct tvec_state */*tv*/, unsigned /*f*/); | |
937 | ||
938 | /* --- @tvec_check@, @tvec_check_v@ --- * | |
939 | * | |
940 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
3efcfd2d | 941 | * @const char *detail@, @va_list *ap@ = description of test |
e63124bc MW |
942 | * |
943 | * Returns: --- | |
944 | * | |
945 | * Use: Check the register values, reporting a failure and dumping | |
946 | * the registers in the event of a mismatch. This just wraps up | |
947 | * @tvec_checkregs@, @tvec_fail@ and @tvec_mismatch@ in the | |
948 | * obvious way. | |
949 | */ | |
950 | ||
31d0247c MW |
951 | extern PRINTF_LIKE(2, 3) |
952 | void tvec_check(struct tvec_state */*tv*/, const char */*detail*/, ...); | |
e63124bc MW |
953 | extern void tvec_check_v(struct tvec_state */*tv*/, |
954 | const char */*detail*/, va_list */*ap*/); | |
b64eb60f | 955 | |
67b5031e | 956 | /*----- Ad-hoc testing ----------------------------------------------------*/ |
b64eb60f | 957 | |
67b5031e | 958 | /* --- @tvec_adhoc@ --- * |
c5e0e403 | 959 | * |
67b5031e MW |
960 | * Arguments: @struct tvec_state *tv@ = test-vector state |
961 | * @struct tvec_test *t@ = space for a test definition | |
c5e0e403 MW |
962 | * |
963 | * Returns: --- | |
964 | * | |
67b5031e MW |
965 | * Use: Begin ad-hoc testing, i.e., without reading a file of |
966 | * test-vector data. | |
967 | * | |
968 | * The structure at @t@ will be used to record information about | |
969 | * the tests underway, which would normally come from a static | |
970 | * test definition. The other functions in this section assume | |
971 | * that @tvec_adhoc@ has been called. | |
c5e0e403 | 972 | */ |
b64eb60f | 973 | |
67b5031e | 974 | extern void tvec_adhoc(struct tvec_state */*tv*/, struct tvec_test */*t*/); |
b64eb60f | 975 | |
67b5031e | 976 | /* --- @tvec_begingroup@, @TVEC_BEGINGROUP@ --- * |
c5e0e403 MW |
977 | * |
978 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
67b5031e MW |
979 | * @const char *name@ = name for this test group |
980 | * @const char *file@, @unsigned @lno@ = calling file and line | |
c5e0e403 | 981 | * |
67b5031e | 982 | * Returns: --- |
c5e0e403 | 983 | * |
67b5031e MW |
984 | * Use: Begin an ad-hoc test group with the given name. The @file@ |
985 | * and @lno@ can be anything, but it's usually best if they | |
986 | * refer to the source code performing the test: the macro | |
987 | * @TVEC_BEGINGROUP@ does this automatically. | |
c5e0e403 | 988 | */ |
b64eb60f | 989 | |
67b5031e MW |
990 | extern void tvec_begingroup(struct tvec_state */*tv*/, const char */*name*/, |
991 | const char */*file*/, unsigned /*lno*/); | |
992 | #define TVEC_BEGINGROUP(tv, name) \ | |
993 | do tvec_begingroup(tv, name, __FILE__, __LINE__); while (0) | |
b64eb60f | 994 | |
67b5031e | 995 | /* --- @tvec_endgroup@ --- * |
c5e0e403 MW |
996 | * |
997 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
c5e0e403 | 998 | * |
67b5031e | 999 | * Returns: --- |
c5e0e403 | 1000 | * |
67b5031e MW |
1001 | * Use: End an ad-hoc test group. The statistics are updated and the |
1002 | * outcome is reported to the output formatter. | |
3efcfd2d MW |
1003 | */ |
1004 | ||
1005 | extern void tvec_endgroup(struct tvec_state */*tv*/); | |
1006 | ||
1007 | /* --- @TVEC_TESTGROUP@, @TVEC_TESTGROUP_TAG@ --- * | |
1008 | * | |
1009 | * Arguments: @tag@ = label-disambiguation tag | |
1010 | * @const struct tvec_state *tv = test-vector state | |
1011 | * @const char *name@ = test-group name | |
1012 | * | |
1013 | * Returns: --- | |
1014 | * | |
1015 | * Use: Control-structure macro: @TVEC_TESTGROUP(tv, name) stmt@ | |
1016 | * establishes a test group with the given @name@ (attributing | |
1017 | * it to the source file and lie number), executes @stmt@, and | |
1018 | * ends the test group. If @stmt@ invokes @break@ then the test | |
1019 | * group is skipped. @TVEC_TESTGROUP_TAG@ is the same, with an | |
1020 | * additional @tag@ argument for use in higher-level macros. | |
1021 | */ | |
1022 | ||
1023 | #define TVEC_TESTGROUP_TAG(tag, tv, name) \ | |
1024 | MC_WRAP(tag##__around, \ | |
1025 | { TVEC_BEGINGROUP(tv, name); }, \ | |
1026 | { tvec_endgroup(tv); }, \ | |
1027 | { if (!((tv)->f&TVSF_SKIP)) tvec_skipgroup(tv, 0); \ | |
1028 | tvec_endgroup(tv); }) | |
1029 | #define TVEC_TESTGROUP(tv, name) TVEC_TESTGROUP_TAG(grp, tv, name) | |
1030 | ||
1031 | /* --- @tvec_begintest@, @TVEC_BEGINTEST@ --- * | |
1032 | * | |
1033 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1034 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1035 | * | |
1036 | * Returns: --- | |
1037 | * | |
1038 | * Use: Begin an ad-hoc test case. The @file@ and @lno@ can be | |
1039 | * anything, but it's usually best if they refer to the source | |
1040 | * code performing the test: the macro @TVEC_BEGINGROUP@ does | |
1041 | * this automatically. | |
1042 | */ | |
1043 | ||
1044 | extern void tvec_begintest(struct tvec_state */*tv*/, | |
1045 | const char */*file*/, unsigned /*lno*/); | |
1046 | #define TVEC_BEGINTEST(tv) \ | |
1047 | do tvec_begintest(tv, __FILE__, __LINE__); while (0) | |
1048 | ||
31d0247c | 1049 | /* --- @tvec_endtest@ --- * |
3efcfd2d MW |
1050 | * |
1051 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1052 | * | |
1053 | * Returns: --- | |
1054 | * | |
31d0247c | 1055 | * Use: End an ad-hoc test case, The statistics are updated and the |
3efcfd2d MW |
1056 | * outcome is reported to the output formatter. |
1057 | */ | |
1058 | ||
1059 | extern void tvec_endtest(struct tvec_state */*tv*/); | |
1060 | ||
1061 | /* --- @TVEC_TEST@, @TVEC_TEST_TAG@ --- * | |
1062 | * | |
1063 | * Arguments: @tag@ = label-disambiguation tag | |
1064 | * @struct tvec_test *t@ = space for a test definition | |
1065 | * | |
1066 | * Returns: --- | |
1067 | * | |
1068 | * Use: Control-structure macro: @TVEC_TEST(tv) stmt@ begins a test | |
1069 | * case, executes @stmt@, and ends the test case. If @stmt@ | |
1070 | * invokes @break@ then the test case is skipped. | |
1071 | * @TVEC_TEST_TAG@ is the same, with an additional @tag@ argumet | |
1072 | * for use in higher-level macros. | |
1073 | */ | |
1074 | ||
1075 | #define TVEC_TEST_TAG(tag, tv) \ | |
1076 | MC_WRAP(tag##__around, \ | |
1077 | { TVEC_BEGINTEST(tv); }, \ | |
1078 | { tvec_endtest(tv); }, \ | |
1079 | { if ((tv)->f&TVSF_ACTIVE) tvec_skip((tv), 0); \ | |
1080 | tvec_endtest(tv); }) | |
1081 | #define TVEC_TEST(tv) TVEC_TEST_TAG(test, tv) | |
1082 | ||
1083 | /* --- @tvec_claim@, @tvec_claim_v@, @TVEC_CLAIM@ --- * | |
1084 | * | |
1085 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1086 | * @int ok@ = a flag | |
1087 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1088 | * @const char *msg@, @va_list *ap@ = message to report on | |
1089 | * failure | |
1090 | * | |
1091 | * Returns: The value @ok@. | |
1092 | * | |
1093 | * Use: Check that a claimed condition holds, as (part of) a test. | |
1094 | * If no test case is underway (i.e., if @TVSF_OPEN@ is reset in | |
1095 | * @tv->f@), then a new test case is begun and ended. The | |
1096 | * @file@ and @lno@ are passed to the output formatter to be | |
1097 | * reported in case of a failure. If @ok@ is nonzero, then | |
1098 | * nothing else happens; so, in particular, if @tvec_claim@ | |
1099 | * established a new test case, then the test case succeeds. If | |
1100 | * @ok@ is zero, then a failure is reported, quoting @msg@. | |
1101 | * | |
1102 | * The @TVEC_CLAIM@ macro is similar, only it (a) identifies the | |
1103 | * file and line number of the call site automatically, and (b) | |
1104 | * implicitly quotes the source text of the @ok@ condition in | |
1105 | * the failure message. | |
1106 | */ | |
1107 | ||
31d0247c MW |
1108 | extern PRINTF_LIKE(5, 6) |
1109 | int tvec_claim(struct tvec_state */*tv*/, int /*ok*/, | |
1110 | const char */*file*/, unsigned /*lno*/, | |
1111 | const char */*msg*/, ...); | |
3efcfd2d MW |
1112 | extern int tvec_claim_v(struct tvec_state */*tv*/, int /*ok*/, |
1113 | const char */*file*/, unsigned /*lno*/, | |
1114 | const char */*msg*/, va_list */*ap*/); | |
1115 | #define TVEC_CLAIM(tv, cond) \ | |
1116 | (tvec_claim(tv, !!(cond), __FILE__, __LINE__, "%s untrue", #cond)) | |
1117 | ||
1118 | /* --- @tvec_claimeq@ --- * | |
1119 | * | |
1120 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1121 | * @const struct tvec_regty *ty@ = register type | |
1122 | * @const union tvec_misc *arg@ = register type argument | |
1123 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1124 | * @const char *expr@ = the expression to quote on failure | |
1125 | * | |
1126 | * Returns: Nonzero if the input and output values of register 0 are | |
1127 | * equal, zero if they differ. | |
1128 | * | |
1129 | * Use: Check that the input and output values of register 0 are | |
1130 | * equal (according to the register type @ty@). As for | |
1131 | * @tvec_claim@ above, a test case is automatically begun and | |
1132 | * ended if none is already underway. If the values are | |
1133 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
1134 | * mismatched values are dumped. | |
1135 | * | |
1136 | * This function is not expected to be called directly, but | |
1137 | * through type-specific wrapper functions or macros such as | |
1138 | * @TVEC_CLAIMEQ_INT@. | |
1139 | */ | |
1140 | ||
1141 | extern int tvec_claimeq(struct tvec_state */*tv*/, | |
1142 | const struct tvec_regty */*ty*/, | |
1143 | const union tvec_misc */*arg*/, | |
1144 | const char */*file*/, unsigned /*lno*/, | |
1145 | const char */*expr*/); | |
1146 | ||
67b5031e | 1147 | /*----- Benchmarking ------------------------------------------------------*/ |
b64eb60f | 1148 | |
c91413e6 | 1149 | struct tvec_benchenv { |
67b5031e MW |
1150 | struct tvec_env _env; /* benchmarking is an environment */ |
1151 | struct bench_state **bst; /* benchmark state anchor or null */ | |
1152 | unsigned long niter; /* iterations done per unit */ | |
1153 | int riter, rbuf; /* iterations and buffer registers */ | |
1154 | const struct tvec_env *env; /* subordinate environment */ | |
b64eb60f MW |
1155 | }; |
1156 | ||
67b5031e | 1157 | struct tvec_benchctx { |
c91413e6 MW |
1158 | const struct tvec_benchenv *be; /* environment configuration */ |
1159 | struct bench_state *bst; /* benchmark state */ | |
1160 | double dflt_target; /* default time in seconds */ | |
31d0247c MW |
1161 | unsigned f; /* flags */ |
1162 | #define TVBF_SETTRG 1u /* set `@target' */ | |
1163 | #define TVBF_SETMASK (TVBF_SETTRG)) /* mask of @TVBF_SET...@ */ | |
c91413e6 | 1164 | void *subctx; /* subsidiary environment context */ |
3efcfd2d | 1165 | }; |
c5e0e403 | 1166 | |
67b5031e | 1167 | extern struct bench_state *tvec_benchstate; |
3efcfd2d | 1168 | |
c91413e6 | 1169 | /* --- Environment implementation --- * |
67b5031e | 1170 | * |
c91413e6 | 1171 | * The following special variables are supported. |
67b5031e | 1172 | * |
c91413e6 MW |
1173 | * * %|@target|% is the (approximate) number of seconds to run the |
1174 | * benchmark. | |
67b5031e | 1175 | * |
c91413e6 MW |
1176 | * Unrecognized variables are passed to the subordinate environment, if there |
1177 | * is one. Other events are passed through to the subsidiary environment. | |
67b5031e | 1178 | */ |
b64eb60f | 1179 | |
c91413e6 | 1180 | extern tvec_envsetupfn tvec_benchsetup; |
814e42ff | 1181 | extern tvec_envfindvarfn tvec_benchfindvar; |
c91413e6 MW |
1182 | extern tvec_envbeforefn tvec_benchbefore; |
1183 | extern tvec_envrunfn tvec_benchrun; | |
1184 | extern tvec_envafterfn tvec_benchafter; | |
1185 | extern tvec_envteardownfn tvec_benchteardown; | |
b64eb60f | 1186 | |
c91413e6 MW |
1187 | #define TVEC_BENCHENV \ |
1188 | { sizeof(struct tvec_benchctx), \ | |
1189 | tvec_benchsetup, \ | |
814e42ff | 1190 | tvec_benchfindvar, \ |
c91413e6 MW |
1191 | tvec_benchbefore, \ |
1192 | tvec_benchrun, \ | |
1193 | tvec_benchafter, \ | |
1194 | tvec_benchteardown } | |
1195 | #define TVEC_BENCHINIT TVEC_BENCHENV, &tvec_benchstate | |
3efcfd2d | 1196 | |
67b5031e MW |
1197 | /* --- @tvec_benchreport@ --- * |
1198 | * | |
1199 | * Arguments: @const struct gprintf_ops *gops@ = print operations | |
1200 | * @void *go@ = print destination | |
1201 | * @unsigned unit@ = the unit being measured (~TVBU_...@) | |
1202 | * @const struct bench_timing *tm@ = the benchmark result | |
1203 | * | |
1204 | * Returns: --- | |
1205 | * | |
1206 | * Use: Formats a report about the benchmark performance. This | |
6e683a79 MW |
1207 | * function is intended to be called on by an output @ebench@ |
1208 | * function. | |
67b5031e | 1209 | */ |
3efcfd2d | 1210 | |
67b5031e MW |
1211 | extern void tvec_benchreport |
1212 | (const struct gprintf_ops */*gops*/, void */*go*/, | |
1213 | unsigned /*unit*/, const struct bench_timing */*tm*/); | |
b64eb60f | 1214 | |
c91413e6 MW |
1215 | /*----- Remote execution --------------------------------------------------*/ |
1216 | ||
d056fbdf MW |
1217 | struct tvec_remoteenv; |
1218 | ||
c91413e6 MW |
1219 | struct tvec_remotecomms { |
1220 | int infd, outfd; /* input and output descriptors */ | |
31d0247c MW |
1221 | dbuf bout; /* output buffer */ |
1222 | unsigned char *bin; /* input buffer */ | |
1223 | size_t binoff, binlen, binsz; /* input offset, length, and size */ | |
1224 | size_t t; /* temporary offset */ | |
c91413e6 MW |
1225 | unsigned f; /* flags */ |
1226 | #define TVRF_BROKEN 0x0001u /* communications have failed */ | |
c91413e6 | 1227 | }; |
31d0247c | 1228 | #define TVEC_REMOTECOMMS_INIT { -1, -1, DBUF_INIT, 0, 0, 0, 0, 0, 0 } |
c91413e6 MW |
1229 | |
1230 | struct tvec_remotectx { | |
1231 | struct tvec_state *tv; /* test vector state */ | |
1232 | struct tvec_remotecomms rc; /* communication state */ | |
1233 | const struct tvec_remoteenv *re; /* environment configuration */ | |
814e42ff MW |
1234 | void *subctx; /* subenvironment context */ |
1235 | struct tvec_vardef vd; /* temporary variable definition */ | |
c91413e6 MW |
1236 | unsigned ver; /* protocol version */ |
1237 | pid_t kid; /* child process id */ | |
1238 | int errfd; /* child stderr descriptor */ | |
1239 | lbuf errbuf; /* child stderr line buffer */ | |
1240 | dstr prgwant, progress; /* progress: wanted/reported */ | |
1241 | unsigned exwant, exit; /* exit status wanted/reported */ | |
1242 | #define TVRF_RCNMASK 0x0300u /* reconnection behaviour: */ | |
814e42ff MW |
1243 | #define TVRCN_DEMAND 0x0000u /* connect on demand */ |
1244 | #define TVRCN_SKIP 0x0100u /* skip unless connected */ | |
c91413e6 MW |
1245 | #define TVRCN_FORCE 0x0200u /* force reconnection */ |
1246 | #define TVRF_MUFFLE 0x0400u /* muffle child stderr */ | |
31d0247c MW |
1247 | #define TVRF_SETEXIT 0x0800u /* set `@exit' */ |
1248 | #define TVRF_SETPRG 0x1000u /* set `@progress' */ | |
1249 | #define TVRF_SETRCN 0x2000u /* set `@reconnect' */ | |
1250 | #define TVRF_SETMASK (TVRF_SETEXIT | TVRF_SETPRG | TVRF_SETRCN) | |
1251 | /* mask of @TVTF_SET...@ */ | |
c91413e6 MW |
1252 | }; |
1253 | ||
1254 | typedef int tvec_connectfn(pid_t */*kid_out*/, int */*infd_out*/, | |
1255 | int */*outfd_out*/, int */*errfd_out*/, | |
1256 | struct tvec_state */*tv*/, | |
1257 | const struct tvec_remoteenv */*env*/); | |
c81c35df MW |
1258 | /* A connection function. On entry, @tv@ holds the test-vector state, and |
1259 | * @env@ is the test group's remote environment structure, which will | |
1260 | * typically really be some subclass of @struct tvec_remoteenv@ containing | |
1261 | * additional parameters for establishing the child process. | |
1262 | * | |
1263 | * On successful completion, the function stores input and output | |
1264 | * descriptors (which need not be distinct) in @*infd_out@ and | |
1265 | * @*outfd_out@, and returns zero; if it creates a child process, it should | |
1266 | * additionally store the child's process-id in @*kid_out@ and store in | |
1267 | * @*errfd_out@ a descriptor from which the child's error output can be | |
1268 | * read. On error, the function should report an appropriate message via | |
1269 | * @tvec_error@ and return %$-1$%. | |
1270 | */ | |
c91413e6 MW |
1271 | |
1272 | struct tvec_remoteenv_slots { | |
1273 | tvec_connectfn *connect; /* connection function */ | |
1274 | const struct tvec_env *env; /* subsidiary environment */ | |
814e42ff | 1275 | unsigned dflt_reconn; /* default reconnection */ |
c91413e6 MW |
1276 | }; |
1277 | ||
1278 | struct tvec_remoteenv { | |
1279 | struct tvec_env _env; | |
1280 | struct tvec_remoteenv_slots r; | |
1281 | }; | |
1282 | ||
1283 | struct tvec_remotefork_slots { | |
1284 | const struct tvec_test *tests; /* child tests (or null) */ | |
1285 | }; | |
1286 | ||
1287 | struct tvec_remotefork { | |
1288 | struct tvec_env _env; | |
1289 | struct tvec_remoteenv_slots r; | |
1290 | struct tvec_remotefork_slots f; | |
1291 | }; | |
1292 | ||
1293 | struct tvec_remoteexec_slots { | |
1294 | const char *const *args; /* command line to execute */ | |
1295 | }; | |
1296 | ||
1297 | struct tvec_remoteexec { | |
1298 | struct tvec_env _env; | |
1299 | struct tvec_remoteenv_slots r; | |
1300 | struct tvec_remoteexec_slots x; | |
1301 | }; | |
1302 | ||
1303 | union tvec_remoteenv_subclass_kludge { | |
1304 | struct tvec_env _env; | |
1305 | struct tvec_remoteenv renv; | |
1306 | struct tvec_remotefork fork; | |
1307 | struct tvec_remoteexec exec; | |
1308 | }; | |
1309 | ||
c81c35df MW |
1310 | /* Exit status. |
1311 | * | |
1312 | * We don't use the conventional encoding returned by the @wait@(2) family of | |
1313 | * system calls because it's too hard for our flags type to decode. Instead, | |
1314 | * we use our own encoding. | |
1315 | * | |
1316 | * The exit code or signal number ends up in the `value' field in the low 12 | |
1317 | * bits; bit 12 is set if the value field holds a signal, and it if holds an | |
1318 | * exit code. Bits 13--15 hold a code which describes the status of a child | |
1319 | * process or connection. | |
1320 | */ | |
1321 | #define TVXF_VALMASK 0x0fffu /* value (exit code or signal) */ | |
1322 | #define TVXF_SIG 0x1000u /* value is signal, not exit code */ | |
1323 | #define TVXF_CAUSEMASK 0xe000u /* mask for cause bits */ | |
1324 | #define TVXST_RUN 0x0000u /* still running */ | |
1325 | #define TVXST_EXIT 0x2000u /* child exited */ | |
1326 | #define TVXST_KILL 0x4000u /* child killed by signal */ | |
1327 | #define TVXST_CONT 0x6000u /* child continued (?) */ | |
1328 | #define TVXST_STOP 0x8000u /* child stopped (?) */ | |
1329 | #define TVXST_DISCONN 0xa000u /* disconnected */ | |
1330 | #define TVXST_UNK 0xc000u /* unknown */ | |
1331 | #define TVXST_ERR 0xe000u /* local error prevented diagnosis */ | |
1332 | ||
1333 | /* Remote environment. */ | |
c91413e6 | 1334 | extern tvec_envsetupfn tvec_remotesetup; |
814e42ff MW |
1335 | extern tvec_envfindvarfn tvec_remotefindvar; |
1336 | extern tvec_envbeforefn tvec_remotebefore; | |
c91413e6 MW |
1337 | extern tvec_envrunfn tvec_remoterun; |
1338 | extern tvec_envafterfn tvec_remoteafter; | |
1339 | extern tvec_envteardownfn tvec_remoteteardown; | |
1340 | #define TVEC_REMOTEENV \ | |
1341 | { sizeof(struct tvec_remotectx), \ | |
1342 | tvec_remotesetup, \ | |
814e42ff MW |
1343 | tvec_remotefindvar, \ |
1344 | tvec_remotebefore, \ | |
c91413e6 MW |
1345 | tvec_remoterun, \ |
1346 | tvec_remoteafter, \ | |
1347 | tvec_remoteteardown } | |
1348 | ||
c81c35df MW |
1349 | /* --- @tvec_setprogress@, @tvec_setprogress_v@ --- * |
1350 | * | |
1351 | * Arguments: @const char *status@ = progress status token format | |
1352 | * @va_list ap@ = argument tail | |
1353 | * | |
1354 | * Returns: --- | |
1355 | * | |
1356 | * Use: Reports the progress of a test execution to the client. | |
1357 | * | |
1358 | * The framework makes use of tokens beginning with %|%|%: | |
1359 | * | |
1360 | * * %|%IDLE|%: during the top-level server code; | |
1361 | * | |
1362 | * * %|%SETUP|%: during the enclosing environment's @before@ | |
1363 | * function; | |
1364 | * | |
1365 | * * %|%RUN|%: during the environment's @run@ function, or the | |
1366 | * test function; and | |
1367 | * | |
1368 | * * %|%DONE|%: during the enclosing environment's @after@ | |
1369 | * function. | |
1370 | * | |
1371 | * The intent is that a test can use the progress token to check | |
1372 | * that a function which is expected to crash does so at the | |
1373 | * correct point, so it's expected that more complex test | |
1374 | * functions and/or environments will set their own progress | |
1375 | * tokens to reflect what's going on. | |
1376 | */ | |
1377 | ||
31d0247c MW |
1378 | extern PRINTF_LIKE(1, 2) int tvec_setprogress(const char */*status*/, ...); |
1379 | extern int tvec_setprogress_v(const char */*status*/, va_list */*ap*/); | |
c91413e6 | 1380 | |
c81c35df MW |
1381 | /* --- @tvec_remoteserver@ --- * |
1382 | * | |
1383 | * Arguments: @int infd@, @int outfd@ = input and output file descriptors | |
1384 | * @const struct tvec_config *config@ = test configuration | |
1385 | * | |
1386 | * Returns: Suggested exit code. | |
1387 | * | |
1388 | * Use: Run a test server, reading packets from @infd@ and writing | |
1389 | * responses and notifications to @outfd@, and invoking tests as | |
1390 | * described by @config@. | |
1391 | * | |
1392 | * This function is not particularly general purpose. It | |
1393 | * expects to `take over' the process, and makes use of private | |
1394 | * global variables. | |
1395 | */ | |
1396 | ||
c91413e6 MW |
1397 | extern int tvec_remoteserver(int /*infd*/, int /*outfd*/, |
1398 | const struct tvec_config */*config*/); | |
1399 | ||
1400 | extern tvec_connectfn tvec_fork, tvec_exec; | |
1401 | ||
31d0247c | 1402 | #define TVEC_REMOTEFORK(subenv, tests) \ |
c91413e6 MW |
1403 | TVEC_REMOTEENV, { tvec_fork, subenv }, { tests } |
1404 | ||
1405 | #define TVEC_REMOTEEXEC(subenv, args) \ | |
1406 | TVEC_REMOTEENV, { tvec_exec, subenv }, { args } | |
1407 | ||
31d0247c MW |
1408 | /*----- Timeouts ----------------------------------------------------------*/ |
1409 | ||
1410 | struct tvec_timeoutenv { | |
1411 | struct tvec_env _env; | |
d056fbdf | 1412 | int timer; /* the timer (@ITIMER_...@) */ |
c81c35df MW |
1413 | double t; /* time to wait (in seconds) */ |
1414 | const struct tvec_env *env; /* subsidiary environment */ | |
31d0247c MW |
1415 | }; |
1416 | ||
1417 | struct tvec_timeoutctx { | |
c81c35df | 1418 | const struct tvec_timeoutenv *te; /* saved environment description */ |
814e42ff | 1419 | int timer; /* timer code (as overridden) */ |
c81c35df | 1420 | double t; /* time to wait (as overridden) */ |
31d0247c MW |
1421 | unsigned f; /* flags */ |
1422 | #define TVTF_SETTMO 1u /* set `@timeout' */ | |
814e42ff MW |
1423 | #define TVTF_SETTMR 2u /* set `@timer' */ |
1424 | #define TVTF_SETMASK (TVTF_SETTMO | TVTF_SETTMR) | |
1425 | /* mask of @TVTF_SET...@ */ | |
31d0247c MW |
1426 | void *subctx; |
1427 | }; | |
1428 | ||
1429 | extern tvec_envsetupfn tvec_timeoutsetup; | |
814e42ff | 1430 | extern tvec_envfindvarfn tvec_timeoutfindvar; |
31d0247c MW |
1431 | extern tvec_envbeforefn tvec_timeoutbefore; |
1432 | extern tvec_envrunfn tvec_timeoutrun; | |
1433 | extern tvec_envafterfn tvec_timeoutafter; | |
1434 | extern tvec_envteardownfn tvec_timeoutteardown; | |
1435 | #define TVEC_TIMEOUTENV \ | |
1436 | { sizeof(struct tvec_timeoutctx), \ | |
1437 | tvec_timeoutsetup, \ | |
814e42ff | 1438 | tvec_timeoutfindvar, \ |
31d0247c MW |
1439 | tvec_timeoutbefore, \ |
1440 | tvec_timeoutrun, \ | |
1441 | tvec_timeoutafter, \ | |
1442 | tvec_timeoutteardown } | |
1443 | #define TVEC_TIMEOUTINIT(timer, t) TVEC_TIMEOUTENV, timer, t | |
1444 | ||
67b5031e | 1445 | /*----- Output functions --------------------------------------------------*/ |
b64eb60f | 1446 | |
31d0247c MW |
1447 | /* --- @tvec_strlevel@ --- * |
1448 | * | |
1449 | * Arguments: @unsigned level@ = level code | |
1450 | * | |
1451 | * Returns: A human-readable description. | |
1452 | * | |
1453 | * Use: Converts a level code into something that you can print in a | |
1454 | * message. | |
1455 | */ | |
1456 | ||
d056fbdf | 1457 | extern const char *tvec_strlevel(unsigned /*level*/); |
31d0247c | 1458 | |
c91413e6 | 1459 | /* --- @tvec_report@, @tvec_report_v@ --- * |
3efcfd2d MW |
1460 | * |
1461 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1462 | * @const char *msg@, @va_list ap@ = error message | |
1463 | * | |
c91413e6 | 1464 | * Returns: --- |
3efcfd2d | 1465 | * |
c91413e6 MW |
1466 | * Use: Report an message with a given severity. Messages with level |
1467 | * @TVLEV_ERR@ or higher force a nonzero exit code. | |
3efcfd2d MW |
1468 | */ |
1469 | ||
31d0247c MW |
1470 | extern PRINTF_LIKE(3, 4) |
1471 | void tvec_report(struct tvec_state */*tv*/, unsigned /*level*/, | |
1472 | const char */*msg*/, ...); | |
c91413e6 MW |
1473 | extern void tvec_report_v(struct tvec_state */*tv*/, unsigned /*level*/, |
1474 | const char */*msg*/, va_list */*ap*/); | |
b64eb60f | 1475 | |
c91413e6 | 1476 | /* --- @tvec_error@, @tvec_notice@ --- * |
3efcfd2d MW |
1477 | * |
1478 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
c91413e6 | 1479 | * @const char *msg@, @va_list ap@ = error message |
3efcfd2d | 1480 | * |
31d0247c | 1481 | * Returns: The @tvec_error@ function returns %$-1$% as a trivial |
c91413e6 | 1482 | * convenience; @tvec_notice@ does not return a value. |
3efcfd2d | 1483 | * |
c91413e6 MW |
1484 | * Use: Report an error or a notice. Errors are distinct from test |
1485 | * failures, and indicate that a problem was encountered which | |
1486 | * compromised the activity of testing. Notices are important | |
1487 | * information which doesn't fit into any other obvious | |
1488 | * category. | |
3efcfd2d MW |
1489 | */ |
1490 | ||
31d0247c MW |
1491 | extern PRINTF_LIKE(2, 3) |
1492 | int tvec_error(struct tvec_state */*tv*/, const char */*msg*/, ...); | |
1493 | extern PRINTF_LIKE(2, 3) | |
1494 | void tvec_notice(struct tvec_state */*tv*/, const char */*msg*/, ...); | |
b64eb60f | 1495 | |
6e683a79 | 1496 | /* --- @tvec_unkregerr@ --- * |
d056fbdf MW |
1497 | * |
1498 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1499 | * @const char *name@ = register or pseudoregister name | |
1500 | * | |
1501 | * Returns: %$-1$%. | |
1502 | * | |
1503 | * Use: Reports an error that the register or pseudoregister is | |
1504 | * unrecognized. | |
1505 | */ | |
1506 | ||
6e683a79 | 1507 | extern int tvec_unkregerr(struct tvec_state */*tv*/, const char */*name*/); |
d056fbdf | 1508 | |
6e683a79 | 1509 | /* --- @tvec_dupregerr@ --- * |
d056fbdf MW |
1510 | * |
1511 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1512 | * @const char *name@ = register or pseudoregister name | |
1513 | * | |
1514 | * Returns: %$-1$%. | |
1515 | * | |
1516 | * Use: Reports an error that the register or pseudoregister has been | |
1517 | * assigned already in the current test. | |
1518 | */ | |
1519 | ||
6e683a79 | 1520 | extern int tvec_dupregerr(struct tvec_state */*tv*/, const char */*name*/); |
d056fbdf | 1521 | |
3efcfd2d MW |
1522 | /* --- @tvec_humanoutput@ --- * |
1523 | * | |
1524 | * Arguments: @FILE *fp@ = output file to write on | |
1525 | * | |
1526 | * Returns: An output formatter. | |
1527 | * | |
1528 | * Use: Return an output formatter which writes on @fp@ with the | |
1529 | * expectation that a human will be watching and interpreting | |
1530 | * the output. If @fp@ denotes a terminal, the display shows a | |
1531 | * `scoreboard' indicating the outcome of each test case | |
1532 | * attempted, and may in addition use colour and other | |
1533 | * highlighting. | |
1534 | */ | |
1535 | ||
e63124bc | 1536 | extern struct tvec_output *tvec_humanoutput(FILE */*fp*/); |
3efcfd2d MW |
1537 | |
1538 | /* --- @tvec_tapoutput@ --- * | |
1539 | * | |
1540 | * Arguments: @FILE *fp@ = output file to write on | |
1541 | * | |
1542 | * Returns: An output formatter. | |
1543 | * | |
1544 | * Use: Return an output formatter which writes on @fp@ in `TAP' | |
1545 | * (`Test Anything Protocol') format. | |
1546 | * | |
1547 | * TAP comes from the Perl community, but has spread rather | |
6e683a79 MW |
1548 | * further. This driver currently produces TAP version 14, but |
1549 | * pretends to be version 13. The driver produces a TAP `test | |
1550 | * point' -- i.e., a result reported as `ok' or `not ok' -- for | |
1551 | * each input test group. Failure reports and register dumps | |
1552 | * are produced as diagnostic messages before the final group | |
1553 | * result. (TAP permits structuerd YAML data after the | |
1554 | * test-point result, which could be used to report details, but | |
1555 | * (a) postponing the details until after the report is | |
1556 | * inconvenient, and (b) there is no standardization for the | |
1557 | * YAML anyway, so in practice it's no more useful than the | |
1558 | * unstructured diagnostics. | |
3efcfd2d MW |
1559 | */ |
1560 | ||
e63124bc | 1561 | extern struct tvec_output *tvec_tapoutput(FILE */*fp*/); |
3efcfd2d MW |
1562 | |
1563 | /* --- @tvec_dfltoutput@ --- * | |
1564 | * | |
1565 | * Arguments: @FILE *fp@ = output file to write on | |
1566 | * | |
1567 | * Returns: An output formatter. | |
1568 | * | |
1569 | * Use: Selects and instantiates an output formatter suitable for | |
1570 | * writing on @fp@. The policy is subject to change, but | |
1571 | * currently the `human' output format is selected if @fp@ is | |
1572 | * interactive (i.e., if @isatty(fileno(fp))@ is true), and | |
1573 | * otherwise the `tap' format is used. | |
1574 | */ | |
1575 | ||
e63124bc | 1576 | extern struct tvec_output *tvec_dfltout(FILE */*fp*/); |
b64eb60f | 1577 | |
67b5031e | 1578 | /*------ Serialization utilities ------------------------------------------*/ |
b64eb60f | 1579 | |
c91413e6 MW |
1580 | /* Serialization format. |
1581 | * | |
1582 | * The `candidate register definitions' are those entries @r@ in the @regs@ | |
1583 | * vector whose index @r.i@ is strictly less than @nr@. The `selected | |
1584 | * register definitions' are those candidate register definitions @r@ for | |
1585 | * which the indicated register @rv[r.i]@ has the @TVRF_LIVE@ flag set. The | |
1586 | * serialized output begins with a header bitmap: if there are %$n$% | |
1587 | * candidate register definitions then the header bitmap consists of %$\lceil | |
1588 | * n/8 \rceil$% bytes. Bits are ordered starting from the least significant | |
1589 | * bit of the first byte, end ending at the most significant bit of the final | |
1590 | * byte. The bit corresponding to a candidate register definition is set if | |
1591 | * and only if that register defintion is selected. The header bitmap is | |
1592 | * then followed by the serializations of the selected registers -- i.e., for | |
1593 | * each selected register definition @r@, the serialized value of register | |
1594 | * @rv[r.i]@ -- simply concatenated together, with no padding or alignment. | |
1595 | */ | |
1596 | ||
67b5031e MW |
1597 | /* --- @tvec_serialize@ --- * |
1598 | * | |
1599 | * Arguments: @const struct tvec_reg *rv@ = vector of registers | |
1600 | * @buf *b@ = buffer to write on | |
1601 | * @const struct tvec_regdef *regs@ = vector of register | |
1602 | * descriptions, terminated by an entry with a null | |
1603 | * @name@ slot | |
1604 | * @unsigned nr@ = number of entries in the @rv@ vector | |
1605 | * @size_t regsz@ = true size of each element of @rv@ | |
1606 | * | |
31d0247c | 1607 | * Returns: Zero on success, %$-1$% on failure. |
67b5031e MW |
1608 | * |
1609 | * Use: Serialize a collection of register values. | |
1610 | * | |
67b5031e MW |
1611 | * The serialized output is written to the buffer @b@. Failure |
1612 | * can be caused by running out of buffer space, or a failing | |
1613 | * type handler. | |
1614 | */ | |
3efcfd2d | 1615 | |
67b5031e MW |
1616 | extern int tvec_serialize(const struct tvec_reg */*rv*/, buf */*b*/, |
1617 | const struct tvec_regdef */*regs*/, | |
1618 | unsigned /*nr*/, size_t /*regsz*/); | |
3efcfd2d | 1619 | |
67b5031e MW |
1620 | /* --- @tvec_deserialize@ --- * |
1621 | * | |
1622 | * Arguments: @struct tvec_reg *rv@ = vector of registers | |
1623 | * @buf *b@ = buffer to write on | |
1624 | * @const struct tvec_regdef *regs@ = vector of register | |
1625 | * descriptions, terminated by an entry with a null | |
1626 | * @name@ slot | |
1627 | * @unsigned nr@ = number of entries in the @rv@ vector | |
1628 | * @size_t regsz@ = true size of each element of @rv@ | |
1629 | * | |
31d0247c | 1630 | * Returns: Zero on success, %$-1$% on failure. |
67b5031e MW |
1631 | * |
1632 | * Use: Deserialize a collection of register values. | |
1633 | * | |
1634 | * The size of the register vector @nr@ and the register | |
1635 | * definitions @regs@ must match those used when producing the | |
1636 | * serialization. For each serialized register value, | |
1637 | * deserialize and store the value into the appropriate register | |
1638 | * slot, and set the @TVRF_LIVE@ flag on the register. See | |
1639 | * @tvec_serialize@ for a description of the format. | |
1640 | * | |
c91413e6 | 1641 | * Failure results only from a failing register type handler. |
67b5031e | 1642 | */ |
3efcfd2d | 1643 | |
67b5031e MW |
1644 | extern int tvec_deserialize(struct tvec_reg */*rv*/, buf */*b*/, |
1645 | const struct tvec_regdef */*regs*/, | |
1646 | unsigned /*nr*/, size_t /*regsz*/); | |
3efcfd2d | 1647 | |
67b5031e MW |
1648 | /*----- Input utilities ---------------------------------------------------*/ |
1649 | ||
1650 | /* These are provided by the core for the benefit of type @parse@ methods, | |
1651 | * and test-environment @set@ functions, which get to read from the test | |
1652 | * input file. The latter are usually best implemented by calling on the | |
1653 | * former. | |
1654 | * | |
1655 | * The two main rules are as follows. | |
1656 | * | |
1657 | * * Leave the file position at the beginning of the line following | |
1658 | * whatever it was that you read. | |
1659 | * | |
1660 | * * When you read and consume a newline (which you do at least once, by | |
1661 | * the previous rule), then increment @tv->lno@ to keep track of the | |
1662 | * current line number. | |
1663 | */ | |
1664 | ||
67b5031e MW |
1665 | /* --- @tvec_syntax@, @tvec_syntax_v@ --- * |
1666 | * | |
1667 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1668 | * @int ch@ = the character found (in @fgetc@ format) | |
1669 | * @const char *expect@, @va_list ap@ = what was expected | |
1670 | * | |
31d0247c | 1671 | * Returns: %$-1$%. |
67b5031e MW |
1672 | * |
1673 | * Use: Report a syntax error quoting @ch@ and @expect@. If @ch@ is | |
1674 | * a newline, then back up so that it can be read again (e.g., | |
1675 | * by @tvec_flushtoeol@ or @tvec_nexttoken@, which will also | |
1676 | * advance the line number). | |
1677 | */ | |
1678 | ||
31d0247c MW |
1679 | extern PRINTF_LIKE(3, 4) |
1680 | int tvec_syntax(struct tvec_state */*tv*/, int /*ch*/, | |
1681 | const char */*expect*/, ...); | |
67b5031e MW |
1682 | extern int tvec_syntax_v(struct tvec_state */*tv*/, int /*ch*/, |
1683 | const char */*expect*/, va_list */*ap*/); | |
1684 | ||
31d0247c MW |
1685 | /* --- @tvec_skipspc@ --- * |
1686 | * | |
1687 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1688 | * | |
1689 | * Returns: --- | |
1690 | * | |
1691 | * Use: Advance over any whitespace characters other than newlines. | |
1692 | * This will stop at `;', end-of-file, or any other kind of | |
1693 | * non-whitespace; and it won't consume a newline. | |
1694 | */ | |
1695 | ||
1696 | extern void tvec_skipspc(struct tvec_state */*tv*/); | |
1697 | ||
67b5031e MW |
1698 | /* --- @tvec_flushtoeol@ --- * |
1699 | * | |
1700 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1701 | * @unsigned f@ = flags (@TVFF_...@) | |
1702 | * | |
31d0247c | 1703 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
1704 | * |
1705 | * Use: Advance to the start of the next line, consuming the | |
1706 | * preceding newline. | |
1707 | * | |
1708 | * A syntax error is reported if no newline character is found, | |
1709 | * i.e., the file ends in mid-line. A syntax error is also | |
1710 | * reported if material other than whitespace or a comment is | |
1711 | * found before the end of the line end, and @TVFF_ALLOWANY@ is | |
1712 | * not set in @f@. The line number count is updated | |
1713 | * appropriately. | |
1714 | */ | |
1715 | ||
1716 | #define TVFF_ALLOWANY 1u | |
1717 | extern int tvec_flushtoeol(struct tvec_state */*tv*/, unsigned /*f*/); | |
1718 | ||
1719 | /* --- @tvec_nexttoken@ --- * | |
1720 | * | |
1721 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1722 | * | |
31d0247c | 1723 | * Returns: Zero if there is a next token which can be read; %$-1$% if no |
67b5031e MW |
1724 | * token is available. |
1725 | * | |
1726 | * Use: Advance to the next whitespace-separated token, which may be | |
1727 | * on the next line. | |
1728 | * | |
1729 | * Tokens are separated by non-newline whitespace, comments, and | |
1730 | * newlines followed by whitespace; a newline /not/ followed by | |
1731 | * whitespace instead begins the next assignment, and two | |
1732 | * newlines separated only by whitespace terminate the data for | |
1733 | * a test. | |
1734 | * | |
1735 | * If this function returns zero, then the next character in the | |
1736 | * file begins a suitable token which can be read and | |
31d0247c | 1737 | * processed. If it returns %$-1$% then there is no such token, |
67b5031e MW |
1738 | * and the file position is left correctly. The line number |
1739 | * count is updated appropriately. | |
1740 | */ | |
3efcfd2d | 1741 | |
67b5031e | 1742 | extern int tvec_nexttoken(struct tvec_state */*tv*/); |
3efcfd2d | 1743 | |
31d0247c | 1744 | /* --- @tvec_readword@, @tvec_readword_v@ --- * |
67b5031e MW |
1745 | * |
1746 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1747 | * @dstr *d@ = string to append the word to | |
adec5584 | 1748 | * @const char **p_inout@ = pointer into string, updated |
67b5031e MW |
1749 | * @const char *delims@ = additional delimiters to stop at |
1750 | * @const char *expect@, @va_list ap@ = what was expected | |
1751 | * | |
31d0247c | 1752 | * Returns: Zero on success, %$-1$% on failure. |
67b5031e MW |
1753 | * |
1754 | * Use: A `word' consists of characters other than whitespace, null | |
1755 | * characters, and other than those listed in @delims@; | |
1756 | * furthermore, a word does not begin with a `;'. (If you want | |
1757 | * reading to stop at comments not preceded by whitespace, then | |
1758 | * include `;' in @delims@. This is a common behaviour.) | |
1759 | * | |
1760 | * If there is no word beginning at the current file position, | |
31d0247c MW |
1761 | * then return %$-1$%; furthermore, if @expect@ is not null, |
1762 | * then report an appropriate error via @tvec_syntax@. | |
67b5031e MW |
1763 | * |
1764 | * Otherwise, the word is accumulated in @d@ and zero is | |
1765 | * returned; if @d@ was not empty at the start of the call, the | |
1766 | * newly read word is separated from the existing material by a | |
1767 | * single space character. Since null bytes are never valid | |
1768 | * word constituents, a null terminator is written to @d@, and | |
1769 | * it is safe to treat the string in @d@ as being null- | |
1770 | * terminated. | |
adec5584 MW |
1771 | * |
1772 | * If @p_inout@ is not null, then @*p_inout@ must be a pointer | |
1773 | * into @d->buf@, which will be adjusted so that it will | |
1774 | * continue to point at the same position even if the buffer is | |
1775 | * reallocated. As a subtle tweak, if @*p_inout@ initially | |
1776 | * points at the end of the buffer, then it will be adjusted to | |
1777 | * point at the beginning of the next word, rather than at the | |
1778 | * additional intervening space. | |
67b5031e | 1779 | */ |
3efcfd2d | 1780 | |
adec5584 | 1781 | extern PRINTF_LIKE(5, 6) |
31d0247c | 1782 | int tvec_readword(struct tvec_state */*tv*/, dstr */*d*/, |
adec5584 MW |
1783 | const char **/*p_inout*/, const char */*delims*/, |
1784 | const char */*expect*/, ...); | |
67b5031e | 1785 | extern int tvec_readword_v(struct tvec_state */*tv*/, dstr */*d*/, |
adec5584 MW |
1786 | const char **/*p_inout*/, const char */*delims*/, |
1787 | const char */*expect*/, va_list */*ap*/); | |
b64eb60f | 1788 | |
67b5031e | 1789 | /*----- Integer types: signed and unsigned --------------------------------*/ |
3efcfd2d MW |
1790 | |
1791 | /* Integers may be input in decimal, hex, binary, or octal, following | |
1792 | * approximately usual conventions. | |
1793 | * | |
1794 | * * Signed integers may be preceded with a `+' or `-' sign. | |
1795 | * | |
1796 | * * Decimal integers are just a sequence of decimal digits `0' ... `9'. | |
1797 | * | |
1798 | * * Octal integers are a sequence of digits `0' ... `7', preceded by `0o' | |
1799 | * or `0O'. | |
1800 | * | |
1801 | * * Hexadecimal integers are a sequence of digits `0' ... `9', `a' | |
1802 | * ... `f', or `A' ... `F', preceded by `0x' or `0X'. | |
1803 | * | |
1804 | * * Radix-B integers are a sequence of digits `0' ... `9', `a' ... `f', or | |
1805 | * `A' ... `F', each with value less than B, preceded by `Br' or `BR', | |
1806 | * where 0 < B < 36 is expressed in decimal without any leading `0' or | |
1807 | * internal underscores `_'. | |
1808 | * | |
1809 | * * A digit sequence may contain internal underscore `_' separators, but | |
1810 | * not before or after all of the digits; and two consecutive `_' | |
1811 | * characters are not permitted. | |
1812 | */ | |
1813 | ||
b64eb60f | 1814 | extern const struct tvec_regty tvty_int, tvty_uint; |
3efcfd2d MW |
1815 | |
1816 | /* The @arg.p@ slot may be null or a pointer to @struct tvec_irange@ or | |
1817 | * @struct tvec_urange@ as appropriate. The bounds are inclusive; use, e.g., | |
1818 | * @LONG_MAX@ explicitly if one or the other bound is logically inapplicable. | |
1819 | */ | |
b64eb60f MW |
1820 | struct tvec_irange { long min, max; }; |
1821 | struct tvec_urange { unsigned long min, max; }; | |
1822 | ||
3efcfd2d | 1823 | /* Bounds corresponding to common integer types. */ |
b64eb60f MW |
1824 | extern const struct tvec_irange |
1825 | tvrange_schar, tvrange_short, tvrange_int, tvrange_long, | |
1826 | tvrange_sbyte, tvrange_i16, tvrange_i32; | |
1827 | extern const struct tvec_urange | |
1828 | tvrange_uchar, tvrange_ushort, tvrange_uint, tvrange_ulong, tvrange_size, | |
1829 | tvrange_byte, tvrange_u16, tvrange_u32; | |
3efcfd2d | 1830 | |
67b5031e | 1831 | /* --- @tvec_claimeq_int@, @TVEC_CLAIMEQ_INT@ --- * |
3efcfd2d MW |
1832 | * |
1833 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1834 | * @long i0, i1@ = two signed integers | |
1835 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1836 | * @const char *expr@ = the expression to quote on failure | |
1837 | * | |
1838 | * Returns: Nonzero if @i0@ and @i1@ are equal, otherwise zero. | |
1839 | * | |
1840 | * Use: Check that values of @i0@ and @i1@ are equal. As for | |
1841 | * @tvec_claim@ above, a test case is automatically begun and | |
1842 | * ended if none is already underway. If the values are | |
1843 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
1844 | * mismatched values are dumped: @i0@ is printed as the output | |
1845 | * value and @i1@ is printed as the input reference. | |
1846 | * | |
1847 | * The @TVEC_CLAIM_INT@ macro is similar, only it (a) identifies | |
1848 | * the file and line number of the call site automatically, and | |
1849 | * (b) implicitly quotes the source text of the @i0@ and @i1@ | |
1850 | * arguments in the failure message. | |
1851 | */ | |
b64eb60f MW |
1852 | |
1853 | extern int tvec_claimeq_int(struct tvec_state */*tv*/, | |
1854 | long /*i0*/, long /*i1*/, | |
1855 | const char */*file*/, unsigned /*lno*/, | |
1856 | const char */*expr*/); | |
3efcfd2d MW |
1857 | #define TVEC_CLAIMEQ_INT(tv, i0, i1) \ |
1858 | (tvec_claimeq_int(tv, i0, i1, __FILE__, __LINE__, #i0 " /= " #i1)) | |
1859 | ||
1860 | /* --- @tvec_claimeq_uint@, @TVEC_CLAIMEQ_UINT@ --- * | |
1861 | * | |
1862 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1863 | * @unsigned long u0, u1@ = two unsigned integers | |
1864 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1865 | * @const char *expr@ = the expression to quote on failure | |
1866 | * | |
1867 | * Returns: Nonzero if @u0@ and @u1@ are equal, otherwise zero. | |
1868 | * | |
1869 | * Use: Check that values of @u0@ and @u1@ are equal. As for | |
1870 | * @tvec_claim@ above, a test case is automatically begun and | |
1871 | * ended if none is already underway. If the values are | |
1872 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
1873 | * mismatched values are dumped: @u0@ is printed as the output | |
1874 | * value and @u1@ is printed as the input reference. | |
1875 | * | |
1876 | * The @TVEC_CLAIM_UINT@ macro is similar, only it (a) | |
1877 | * identifies the file and line number of the call site | |
1878 | * automatically, and (b) implicitly quotes the source text of | |
1879 | * the @u0@ and @u1@ arguments in the failure message. | |
1880 | */ | |
1881 | ||
b64eb60f MW |
1882 | extern int tvec_claimeq_uint(struct tvec_state */*tv*/, |
1883 | unsigned long /*u0*/, unsigned long /*u1*/, | |
1884 | const char */*file*/, unsigned /*lno*/, | |
1885 | const char */*expr*/); | |
b64eb60f MW |
1886 | #define TVEC_CLAIMEQ_UINT(tv, u0, u1) \ |
1887 | (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1)) | |
1888 | ||
c4ccbbf9 MW |
1889 | /*----- Size type ---------------------------------------------------------*/ |
1890 | ||
1891 | /* A size is an unsigned integer followed by an optional unit specifier | |
1892 | * consisting of an SI unit prefix and (optionally) the letter `B'. | |
1893 | */ | |
1894 | ||
1895 | extern const struct tvec_regty tvty_size; | |
1896 | ||
1897 | /* --- @tvec_claimeq_size@ --- * | |
1898 | * | |
1899 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1900 | * @unsigned long sz0, sz1@ = two sizes | |
1901 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1902 | * @const char *expr@ = the expression to quote on failure | |
1903 | * | |
1904 | * Returns: Nonzero if @sz0@ and @sz1@ are equal, otherwise zero. | |
1905 | * | |
1906 | * Use: Check that values of @u0@ and @u1@ are equal. As for | |
1907 | * @tvec_claim@ above, a test case is automatically begun and | |
1908 | * ended if none is already underway. If the values are | |
1909 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
1910 | * mismatched values are dumped: @u0@ is printed as the output | |
1911 | * value and @u1@ is printed as the input reference. | |
1912 | * | |
1913 | * The @TVEC_CLAIM_SIZE@ macro is similar, only it (a) | |
1914 | * identifies the file and line number of the call site | |
1915 | * automatically, and (b) implicitly quotes the source text of | |
1916 | * the @u0@ and @u1@ arguments in the failure message. | |
1917 | */ | |
1918 | ||
1919 | int tvec_claimeq_size(struct tvec_state *tv, | |
1920 | unsigned long sz0, unsigned long sz1, | |
1921 | const char *file, unsigned lno, const char *expr); | |
1922 | #define TVEC_CLAIMEQ_UINT(tv, u0, u1) \ | |
1923 | (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1)) | |
1924 | ||
3efcfd2d MW |
1925 | /*----- Floating-point type -----------------------------------------------*/ |
1926 | ||
c81c35df MW |
1927 | /* Floating-point values are either NaN (%|#nan|%, if supported by the |
1928 | * platform); positive or negative infinity (%|#inf|%, %|+#inf|%, or | |
1929 | * %|#+inf|% (preferring the last), and %|-#inf|% or %|#-inf|% (preferring | |
1930 | * the latter), if supported by the platform); or a number in strtod(3) | |
1931 | * syntax. | |
3efcfd2d MW |
1932 | * |
1933 | * The comparison rules for floating-point numbers are complex: see | |
1934 | * @tvec_claimeqish_float@ for details. | |
1935 | */ | |
1936 | ||
e63124bc | 1937 | extern const struct tvec_regty tvty_float; |
3efcfd2d | 1938 | |
e63124bc | 1939 | struct tvec_floatinfo { |
3efcfd2d MW |
1940 | /* Details about acceptable floating-point values. */ |
1941 | ||
1942 | unsigned f; /* flags (@TVFF_...@ bits) */ | |
1943 | #define TVFF_NOMIN 1u /* ignore @min@ (allow -inf) */ | |
1944 | #define TVFF_NOMAX 2u /* ignore @max@ (allow +inf) */ | |
1945 | #define TVFF_NANOK 4u /* permit NaN */ | |
1946 | #define TVFF_EQMASK 0xf0 /* how to compare */ | |
1947 | #define TVFF_EXACT 0x00 /* must equal exactly */ | |
1948 | #define TVFF_ABSDELTA 0x10 /* must be within @delta@ */ | |
1949 | #define TVFF_RELDELTA 0x20 /* diff < @delta@ fraction */ | |
1950 | double min, max; /* smallest/largest value allowed */ | |
1951 | double delta; /* maximum tolerable difference */ | |
e63124bc MW |
1952 | }; |
1953 | ||
d056fbdf MW |
1954 | extern const struct tvec_floatinfo tvflt_finite, tvflt_nonneg; |
1955 | ||
3efcfd2d MW |
1956 | /* --- @tvec_claimeqish_float@, @TVEC_CLAIMEQISH_FLOAT@ --- * |
1957 | * | |
1958 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1959 | * @double f0, f1@ = two floating-point numbers | |
1960 | * @unsigned f@ = flags (@TVFF_...@) | |
1961 | * @double delta@ = maximum tolerable difference | |
1962 | * @const char *file@, @unsigned @lno@ = calling file and line | |
1963 | * @const char *expr@ = the expression to quote on failure | |
1964 | * | |
c4ccbbf9 | 1965 | * Returns: Nonzero if @f0@ and @f1@ are sufficiently close, otherwise |
3efcfd2d MW |
1966 | * zero. |
1967 | * | |
1968 | * Use: Check that values of @f0@ and @f1@ are sufficiently close. | |
1969 | * As for @tvec_claim@ above, a test case is automatically begun | |
1970 | * and ended if none is already underway. If the values are | |
1971 | * too far apart, then @tvec_fail@ is called, quoting @expr@, | |
1972 | * and the mismatched values are dumped: @f0@ is printed as the | |
1973 | * output value and @f1@ is printed as the input reference. | |
1974 | * | |
1975 | * The details for the comparison are as follows. | |
1976 | * | |
1977 | * * A NaN value matches any other NaN, and nothing else. | |
1978 | * | |
1979 | * * An infinity matches another infinity of the same sign, | |
1980 | * and nothing else. | |
1981 | * | |
1982 | * * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any | |
1983 | * representable number matches only itself: in particular, | |
1984 | * positive and negative zero are considered distinct. | |
1985 | * (This allows tests to check that they land on the correct | |
1986 | * side of branch cuts, for example.) | |
1987 | * | |
1988 | * * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches | |
1989 | * %$y$% when %$|x - y| < \delta$%. | |
1990 | * | |
1991 | * * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches | |
c4ccbbf9 MW |
1992 | * %$y$% when %$|1 - x/y| < \delta$%. (Note that this |
1993 | * criterion is asymmetric. Write %$x \approx_\delta y$% | |
1994 | * if and only if %$|1 - x/y < \delta$%. Then, for example, | |
1995 | * if %$y/(1 + \delta) < x < y (1 - \delta)$%, then | |
1996 | * %$x \approx_\delta y$%, but %$y \not\approx_\delta x$%.) | |
3efcfd2d MW |
1997 | * |
1998 | * The @TVEC_CLAIM_FLOAT@ macro is similar, only it (a) | |
1999 | * identifies the file and line number of the call site | |
2000 | * automatically, and (b) implicitly quotes the source text of | |
2001 | * the @f0@ and @f1@ arguments (and @delta@) in the failure | |
2002 | * message. | |
2003 | */ | |
2004 | ||
e63124bc MW |
2005 | extern int tvec_claimeqish_float(struct tvec_state */*tv*/, |
2006 | double /*f0*/, double /*f1*/, | |
2007 | unsigned /*f*/, double /*delta*/, | |
2008 | const char */*file*/, unsigned /*lno*/, | |
2009 | const char */*expr*/); | |
3efcfd2d | 2010 | #define TVEC_CLAIMEQISH_FLOAT(tv, f0, f1, f, delta) \ |
c4ccbbf9 | 2011 | (tvec_claimeqish_float(tv, f0, f1, f, delta, __FILE__, __LINE__, \ |
3efcfd2d MW |
2012 | #f0 " /= " #f1 " (+/- " #delta ")")) |
2013 | ||
2014 | /* --- @tvec_claimeq_float@, @TVEC_CLAIMEQ_FLOAT@ --- * | |
2015 | * | |
2016 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
2017 | * @double f0, f1@ = two floating-point numbers | |
2018 | * @const char *file@, @unsigned @lno@ = calling file and line | |
2019 | * @const char *expr@ = the expression to quote on failure | |
2020 | * | |
2021 | * Returns: Nonzero if @f0@ and @u1@ are identical, otherwise zero. | |
2022 | * | |
2023 | * Use: Check that values of @f0@ and @f1@ are identical. The | |
2024 | * function is exactly equivalent to @tvec_claimeqish_float@ | |
2025 | * with @f == TVFF_EXACT@; the macro is similarly like | |
2026 | * @TVEC_CLAIMEQISH_FLOAT@ with @f == TVFF_EXACT@, except that | |
2027 | * it doesn't bother to quote a delta. | |
2028 | */ | |
2029 | ||
e63124bc MW |
2030 | extern int tvec_claimeq_float(struct tvec_state */*tv*/, |
2031 | double /*f0*/, double /*f1*/, | |
2032 | const char */*file*/, unsigned /*lno*/, | |
2033 | const char */*expr*/); | |
e63124bc MW |
2034 | #define TVEC_CLAIMEQ_FLOAT(tv, f0, f1) \ |
2035 | (tvec_claimeq_float(tv, f0, f1, __FILE__, __LINE__, #f0 " /= " #f1)) | |
2036 | ||
814e42ff MW |
2037 | /*----- Durations ---------------------------------------------------------*/ |
2038 | ||
2039 | /* A duration measures a time interval in seconds. The input format consists | |
2040 | * of a nonnegative decimal floating-point number in @strtod@ format followed | |
2041 | * by an optional unit specification. | |
814e42ff MW |
2042 | */ |
2043 | ||
2044 | extern const struct tvec_regty tvty_duration; | |
2045 | ||
13ee7406 MW |
2046 | /* --- @tvec_parsedurunit@ --- * |
2047 | * | |
2048 | * Arguments: @double *scale_out@ = where to leave the scale | |
2049 | * @const char **p_inout@ = input unit string, updated | |
2050 | * | |
2051 | * Returns: Zero on success, %$-1$% on error. | |
2052 | * | |
2053 | * Use: If @*p_inout@ begins with a unit string followed by the end | |
2054 | * of the string or some non-alphanumeric character, then store | |
2055 | * the corresponding scale factor in @*scale_out@, advance | |
2056 | * @*p_inout@ past the unit string, and return zero. Otherwise, | |
2057 | * return %$-1$%. | |
2058 | */ | |
2059 | ||
2060 | extern int tvec_parsedurunit(double */*scale_out*/, | |
2061 | const char **/*p_inout*/); | |
2062 | ||
c4ccbbf9 MW |
2063 | /* --- @tvec_claimeqish_duration@, @TVEC_CLAIMEQISH_DURATION@ --- * |
2064 | * | |
2065 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
2066 | * @double to, t1@ = two durations | |
2067 | * @unsigned f@ = flags (@TVFF_...@) | |
2068 | * @double delta@ = maximum tolerable difference | |
2069 | * @const char *file@, @unsigned @lno@ = calling file and line | |
2070 | * @const char *expr@ = the expression to quote on failure | |
2071 | * | |
2072 | * Returns: Nonzero if @t0@ and @t1@ are sufficiently close, otherwise | |
2073 | * zero. | |
2074 | * | |
2075 | * Use: Check that values of @t0@ and @t1@ are sufficiently close. | |
2076 | * This is essentially the same as @tvec_claimeqish_float@, only | |
2077 | * it dumps the values as durations on a mismatch. | |
2078 | * | |
2079 | * The @TVEC_CLAIM_FLOAT@ macro is similar, only it (a) | |
2080 | * identifies the file and line number of the call site | |
2081 | * automatically, and (b) implicitly quotes the source text of | |
2082 | * the @t0@ and @t1@ arguments (and @delta@) in the failure | |
2083 | * message. | |
2084 | */ | |
2085 | ||
2086 | extern int tvec_claimeqish_duration(struct tvec_state */*tv*/, | |
2087 | double /*t0*/, double /*t1*/, | |
2088 | unsigned /*f*/, double /*delta*/, | |
2089 | const char */*file*/, unsigned /*lno*/, | |
2090 | const char */*expr*/); | |
2091 | #define TVEC_CLAIMEQISH_DURATION(tv, t0, t1, f, delta) \ | |
2092 | (tvec_claimeqish_duration(tv, t0, t1, f, delta, __FILE__, __LINE__, \ | |
2093 | #t0 " /= " #t1 " (+/- " #delta ")")) | |
2094 | ||
2095 | /* --- @tvec_claimeq_duration@, @TVEC_CLAIMEQ_DURATION@ --- * | |
2096 | * | |
2097 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
2098 | * @double t0, t1@ = two durations | |
2099 | * @const char *file@, @unsigned @lno@ = calling file and line | |
2100 | * @const char *expr@ = the expression to quote on failure | |
2101 | * | |
2102 | * Returns: Nonzero if @t0@ and @t1@ are identical, otherwise zero. | |
2103 | * | |
2104 | * Use: Check that values of @t0@ and @t1@ are identical. The | |
2105 | * function is exactly equivalent to @tvec_claimeqish_duration@ | |
2106 | * with @f == TVFF_EXACT@; the macro is similarly like | |
2107 | * @TVEC_CLAIMEQISH_DURATION@ with @f == TVFF_EXACT@, except | |
2108 | * that it doesn't bother to quote a delta. | |
2109 | */ | |
2110 | ||
2111 | int tvec_claimeq_duration(struct tvec_state */*tv*/, | |
2112 | double /*t0*/, double /*t1*/, | |
2113 | const char */*file*/, unsigned /*lno*/, | |
2114 | const char */*expr*/); | |
2115 | #define TVEC_CLAIMEQ_DURATION(tv, t0, t1) \ | |
2116 | (tvec_claimeq_float(tv, t0, t1, __FILE__, __LINE__, #t0 " /= " #t1)) | |
2117 | ||
3efcfd2d MW |
2118 | /*----- Enumerated types --------------------------------------------------*/ |
2119 | ||
67b5031e MW |
2120 | /* An enumeration describes a set of values of some underlying type, each of |
2121 | * which has a symbolic name. Values outside of the defined set can occur -- | |
2122 | * on output, because of bugs in the tested code, or on input to test | |
2123 | * handling of unexpected values. | |
2124 | * | |
2125 | * There is a distinct enumerated type for each of the branches of | |
3efcfd2d MW |
2126 | * @tvec_misc@. In the following, we write @t@ for the type code, which is |
2127 | * @i@ for signed integer, @u@ for unsigned integer, @f@ for floating-point, | |
2128 | * and @p@ for pointer. | |
67b5031e MW |
2129 | * |
2130 | * On input, an enumerated value may be given by name or as a literal value. | |
2131 | * For enumerations based on numeric types, the literal values can be written | |
2132 | * in the same syntax as the underlying values. For enumerations based on | |
c81c35df | 2133 | * pointers, the only permitted literal is %|#nil|%, which denotes a null |
67b5031e MW |
2134 | * pointer. On output, names are preferred (with the underlying value given |
2135 | * in a comment). | |
3efcfd2d | 2136 | */ |
b64eb60f | 2137 | |
3efcfd2d MW |
2138 | #define DEFENUMTY(tag, ty, slot) \ |
2139 | extern const struct tvec_regty tvty_##slot##enum; | |
2140 | TVEC_MISCSLOTS(DEFENUMTY) | |
2141 | #undef DEFENUMTY | |
2142 | ||
2143 | /* A @struct tvec_tassoc@ associates a string tag with a value. */ | |
b64eb60f MW |
2144 | #define DEFASSOC(tag_, ty, slot) \ |
2145 | struct tvec_##slot##assoc { const char *tag; ty slot; }; | |
2146 | TVEC_MISCSLOTS(DEFASSOC) | |
2147 | #undef DEFASSOC | |
2148 | ||
67b5031e MW |
2149 | #define TVEC_ENDENUM { 0, 0 } |
2150 | ||
3efcfd2d MW |
2151 | /* Information about an enumerated type. */ |
2152 | #define DEFINFO(tag, ty, slot) \ | |
2153 | struct tvec_##slot##enuminfo { \ | |
2154 | const char *name; /* type name for diagnostics */ \ | |
2155 | const struct tvec_##slot##assoc *av; /* name/value mappings */ \ | |
2156 | EXTRA_##tag##_INFOSLOTS /* type-specific extra info */ \ | |
2157 | }; | |
2158 | ||
2159 | #define EXTRA_INT_INFOSLOTS \ | |
2160 | const struct tvec_irange *ir; /* allowed range of raw values */ | |
2161 | ||
2162 | #define EXTRA_UINT_INFOSLOTS \ | |
2163 | const struct tvec_urange *ur; /* allowed range of raw values */ | |
2164 | ||
2165 | #define EXTRA_FLT_INFOSLOTS \ | |
2166 | const struct tvec_floatinfo *fi; /* range and matching policy */ | |
2167 | ||
2168 | #define EXTRA_PTR_INFOSLOTS /* (nothing) */ | |
b64eb60f | 2169 | |
3efcfd2d MW |
2170 | TVEC_MISCSLOTS(DEFINFO) |
2171 | ||
2172 | #undef EXTRA_INT_INFOSLOTS | |
2173 | #undef EXTRA_UINT_INFOSLOTS | |
2174 | #undef EXTRA_FLT_INFOSLOTS | |
2175 | #undef EXTRA_PTR_INFOSLOTS | |
2176 | ||
2177 | #undef DEFINFO | |
2178 | ||
2179 | /* Standard enumerations. */ | |
20ba6b0b MW |
2180 | extern const struct tvec_ienuminfo tvenum_bool; |
2181 | extern const struct tvec_ienuminfo tvenum_cmp; | |
e63124bc | 2182 | |
3efcfd2d MW |
2183 | /* --- @tvec_claimeq_tenum@, @TVEC_CLAIMEQ_TENUM@ --- * |
2184 | * | |
2185 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
67b5031e | 2186 | * @const struct tvec_typeenuminfo *ei@ = enumeration type info |
3efcfd2d MW |
2187 | * @ty t0, t1@ = two values |
2188 | * @const char *file@, @unsigned @lno@ = calling file and line | |
2189 | * @const char *expr@ = the expression to quote on failure | |
2190 | * | |
2191 | * Returns: Nonzero if @t0@ and @t1@ are equal, otherwise zero. | |
2192 | * | |
2193 | * Use: Check that values of @t0@ and @t1@ are equal. As for | |
2194 | * @tvec_claim@ above, a test case is automatically begun and | |
2195 | * ended if none is already underway. If the values are | |
2196 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
67b5031e MW |
2197 | * mismatched values are dumped: @t0@ is printed as the output |
2198 | * value and @t1@ is printed as the input reference. | |
3efcfd2d MW |
2199 | * |
2200 | * The @TVEC_CLAIM_TENUM@ macro is similar, only it (a) | |
2201 | * identifies the file and line number of the call site | |
2202 | * automatically, and (b) implicitly quotes the source text of | |
67b5031e | 2203 | * the @t0@ and @t1@ arguments in the failure message. |
3efcfd2d MW |
2204 | */ |
2205 | ||
b64eb60f MW |
2206 | #define DECLCLAIM(tag, ty, slot) \ |
2207 | extern int tvec_claimeq_##slot##enum \ | |
2208 | (struct tvec_state */*tv*/, \ | |
e63124bc | 2209 | const struct tvec_##slot##enuminfo */*ei*/, \ |
3efcfd2d | 2210 | ty /*t0*/, ty /*t1*/, \ |
b64eb60f MW |
2211 | const char */*file*/, unsigned /*lno*/, const char */*expr*/); |
2212 | TVEC_MISCSLOTS(DECLCLAIM) | |
2213 | #undef DECLCLAIM | |
3efcfd2d MW |
2214 | #define TVEC_CLAIMEQ_IENUM(tv, ei, i0, i1) \ |
2215 | (tvec_claimeq_ienum(tv, ei, i0, i1, \ | |
2216 | __FILE__, __LINE__, #i0 " /= " #i1)) | |
2217 | #define TVEC_CLAIMEQ_UENUM(tv, ei, u0, u1) \ | |
2218 | (tvec_claimeq_uenum(tv, ei, u0, u1, \ | |
2219 | __FILE__, __LINE__, #u0 " /= " #u1)) | |
2220 | #define TVEC_CLAIMEQ_FENUM(tv, ei, f0, f1) \ | |
2221 | (tvec_claimeq_fenum(tv, ei, f0, f1, \ | |
2222 | __FILE__, __LINE__, #f0 " /= " #f1)) | |
2223 | #define TVEC_CLAIMEQ_PENUM(tv, ei, p0, p1) \ | |
2224 | (tvec_claimeq_penum(tv, ei, p0, p1, \ | |
2225 | __FILE__, __LINE__, #p0 " /= " #p1)) | |
2226 | ||
67b5031e MW |
2227 | /*----- Flags type --------------------------------------------------------*/ |
2228 | ||
2229 | /* A flags value packs a number of fields into a single nonnegative integer. | |
2230 | * Symbolic names are associated with the possible values of the various | |
2231 | * fields; more precisely, each name is associated with a value and a | |
2232 | * covering bitmask. | |
2233 | * | |
c81c35df MW |
2234 | * The input syntax is a sequence of items separated by `%|||%' signs. Each |
2235 | * item may be the symbolic name of a field value, or a literal unsigned | |
2236 | * integer. The masks associated with the given symbolic names must be | |
2237 | * disjoint. The resulting numerical value is simply the bitwise OR of the | |
2238 | * given values. | |
67b5031e MW |
2239 | * |
2240 | * On output, the table of symbolic names and their associated values and | |
2241 | * masks is repeatedly scanned, in order, to find disjoint matches -- i.e., | |
2242 | * entries whose value matches the target value in the bit positions | |
2243 | * indicated by the mask, and whose mask doesn't overlap with any previously | |
c81c35df MW |
2244 | * found matches; the names are then output, separated by `%|||%'. Any |
2245 | * remaining nonzero bits not covered by any of the matching masks are output | |
2246 | * as a single literal integer, in hex. | |
67b5031e | 2247 | */ |
b64eb60f MW |
2248 | |
2249 | extern const struct tvec_regty tvty_flags; | |
3efcfd2d MW |
2250 | |
2251 | struct tvec_flag { | |
2252 | /* Definition of a single flag or bitfield value. | |
2253 | * | |
2254 | * Each named setting comes with a value @v@ and a mask @m@; the mask | |
67b5031e | 2255 | * should cover all of the value bits, i.e., @(v&~m) == 0@. |
3efcfd2d MW |
2256 | */ |
2257 | ||
2258 | const char *tag; /* name */ | |
2259 | unsigned long m, v; /* mask and value */ | |
2260 | }; | |
2261 | ||
67b5031e MW |
2262 | #define TVEC_ENDFLAGS { 0, 0, 0 } |
2263 | ||
b64eb60f | 2264 | struct tvec_flaginfo { |
67b5031e MW |
2265 | /* Information about a flags type. */ |
2266 | ||
2267 | const char *name; /* type name for diagnostics */ | |
2268 | const struct tvec_flag *fv; /* name/mask/value mappings */ | |
2269 | const struct tvec_urange *range; /* permitted range for literals */ | |
b64eb60f MW |
2270 | }; |
2271 | ||
67b5031e MW |
2272 | /* --- @tvec_claimeq_flags@, @TVEC_CLAIMEQ_FLAGS@ --- * |
2273 | * | |
2274 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
2275 | * @const struct tvec_flaginfo *fi@ = flags type info | |
2276 | * @unsigned long f0, f1@ = two values | |
2277 | * @const char *file@, @unsigned @lno@ = calling file and line | |
2278 | * @const char *expr@ = the expression to quote on failure | |
2279 | * | |
2280 | * Returns: Nonzero if @f0@ and @f1@ are equal, otherwise zero. | |
2281 | * | |
2282 | * Use: Check that values of @f0@ and @f1@ are equal. As for | |
2283 | * @tvec_claim@ above, a test case is automatically begun and | |
2284 | * ended if none is already underway. If the values are | |
2285 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
2286 | * mismatched values are dumped: @f0@ is printed as the output | |
2287 | * value and @f1@ is printed as the input reference. | |
2288 | * | |
2289 | * The @TVEC_CLAIM_FLAGS@ macro is similar, only it (a) | |
2290 | * identifies the file and line number of the call site | |
2291 | * automatically, and (b) implicitly quotes the source text of | |
2292 | * the @f0@ and @f1@ arguments in the failure message. | |
2293 | */ | |
2294 | ||
b64eb60f MW |
2295 | extern int tvec_claimeq_flags(struct tvec_state */*tv*/, |
2296 | const struct tvec_flaginfo */*fi*/, | |
2297 | unsigned long /*f0*/, unsigned long /*f1*/, | |
2298 | const char */*file*/, unsigned /*lno*/, | |
2299 | const char */*expr*/); | |
2300 | #define TVEC_CLAIMEQ_FLAGS(tv, fi, f0, f1) \ | |
2301 | (tvec_claimeq_flags(tv, fi, f0, f1, \ | |
2302 | __FILE__, __LINE__, #f0 " /= " #f1)) | |
2303 | ||
67b5031e MW |
2304 | /*----- Character type ----------------------------------------------------*/ |
2305 | ||
2306 | /* A character value holds a character, as read by @fgetc@. The special | |
2307 | * @EOF@ value can also be represented. | |
2308 | * | |
c81c35df MW |
2309 | * On input, a character value can be given by symbolic name, with a leading |
2310 | * `%|#|%'; or a character or `%|\|%'-escape sequence, optionally in single | |
2311 | * quotes. | |
67b5031e MW |
2312 | * |
2313 | * The following escape sequences and character names are recognized. | |
2314 | * | |
2315 | * * `%|#eof|%' is the special end-of-file marker. | |
2316 | * | |
2317 | * * `%|#nul|%' is the NUL character, sometimes used to terminate strings. | |
2318 | * | |
2319 | * * `%|bell|%', `%|bel|%', `%|ding|%', or `%|\a|%' is the BEL character | |
2320 | * used to ring the terminal bell (or do some other thing to attract the | |
2321 | * user's attention). | |
2322 | * | |
2323 | * * %|#backspace|%, %|#bs|%, or %|\b|% is the backspace character, used to | |
2324 | * move the cursor backwords by one cell. | |
2325 | * | |
2326 | * * %|#escape|% %|#esc|%, or%|\e|% is the escape character, used to | |
2327 | * introduce special terminal commands. | |
2328 | * | |
2329 | * * %|#formfeed|%, %|#ff|%, or %|\f|% is the formfeed character, used to | |
2330 | * separate pages of text. | |
2331 | * | |
2332 | * * %|#newline|%, %|#linefeed|%, %|#lf|%, %|#nl|%, or %|\n|% is the | |
2333 | * newline character, used to terminate lines of text or advance the | |
2334 | * cursor to the next line (perhaps without returning it to the start of | |
2335 | * the line). | |
2336 | * | |
2337 | * * %|#return|%, %|#carriage-return|%, %|#cr|%, or %|\r|% is the | |
2338 | * carriage-return character, used to return the cursor to the start of | |
2339 | * the line. | |
2340 | * | |
2341 | * * %|#tab|%, %|#horizontal-tab|%, %|#ht|%, or %|\t|% is the tab | |
2342 | * character, used to advance the cursor to the next tab stop on the | |
2343 | * current line. | |
2344 | * | |
2345 | * * %|#vertical-tab|%, %|#vt|%, %|\v|% is the vertical tab character. | |
2346 | * | |
2347 | * * %|#space|%, %|#spc|% is the space character. | |
2348 | * | |
2349 | * * %|#delete|%, %|#del|% is the delete character, used to erase the most | |
2350 | * recent character. | |
2351 | * | |
2352 | * * %|\'|% is the single-quote character. | |
2353 | * | |
2354 | * * %|\\|% is the backslash character. | |
2355 | * | |
2356 | * * %|\"|% is the double-quote character. | |
2357 | * | |
2358 | * * %|\NNN|% or %|\{NNN}|% is the character with code NNN in octal. The | |
2359 | * NNN may be up to three digits long. | |
2360 | * | |
2361 | * * %|\xNN|% or %|\x{NN}|% is the character with code NNN in hexadecimal. | |
2362 | */ | |
2363 | ||
e63124bc | 2364 | extern const struct tvec_regty tvty_char; |
67b5031e MW |
2365 | |
2366 | /* --- @tvec_claimeq_char@, @TVEC_CLAIMEQ_CHAR@ --- * | |
2367 | * | |
2368 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
2369 | * @int ch0, ch1@ = two character codes | |
2370 | * @const char *file@, @unsigned @lno@ = calling file and line | |
2371 | * @const char *expr@ = the expression to quote on failure | |
2372 | * | |
2373 | * Returns: Nonzero if @ch0@ and @ch1@ are equal, otherwise zero. | |
2374 | * | |
2375 | * Use: Check that values of @ch0@ and @ch1@ are equal. As for | |
2376 | * @tvec_claim@ above, a test case is automatically begun and | |
2377 | * ended if none is already underway. If the values are | |
2378 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
2379 | * mismatched values are dumped: @ch0@ is printed as the output | |
2380 | * value and @ch1@ is printed as the input reference. | |
2381 | * | |
2382 | * The @TVEC_CLAIM_CHAR@ macro is similar, only it (a) | |
2383 | * identifies the file and line number of the call site | |
2384 | * automatically, and (b) implicitly quotes the source text of | |
2385 | * the @ch0@ and @ch1@ arguments in the failure message. | |
2386 | */ | |
2387 | ||
e63124bc MW |
2388 | extern int tvec_claimeq_char(struct tvec_state */*tv*/, |
2389 | int /*ch0*/, int /*ch1*/, | |
2390 | const char */*file*/, unsigned /*lno*/, | |
2391 | const char */*expr*/); | |
2392 | #define TVEC_CLAIMEQ_CHAR(tv, c0, c1) \ | |
2393 | (tvec_claimeq_char(tv, c0, c1, __FILE__, __LINE__, #c0 " /= " #c1)) | |
2394 | ||
67b5031e MW |
2395 | /*----- Text and binary string types --------------------------------------*/ |
2396 | ||
2397 | /* A string is a sequence of octets. Text and binary strings differ | |
2398 | * primarily in presentation: text strings are shown as raw characters where | |
2399 | * possible; binary strings are shown as hex dumps with an auxiliary text | |
2400 | * display. | |
2401 | * | |
2402 | * The input format for both kinds of strings is basically the same: a | |
2403 | * `compound string', consisting of | |
2404 | * | |
2405 | * * single-quoted strings, which are interpreted entirely literally, but | |
2406 | * can't contain single quotes or newlines; | |
2407 | * | |
2408 | * * double-quoted strings, in which `%|\|%'-escapes are interpreted as for | |
2409 | * characters; | |
2410 | * | |
2411 | * * character names, marked by an initial `%|#|%' sign; | |
2412 | * | |
2413 | * * special tokens marked by an initial `%|!|%' sign; or | |
2414 | * | |
2415 | * * barewords interpreted according to the current coding scheme. | |
2416 | * | |
2417 | * The special tokens are | |
2418 | * | |
2419 | * * `%|!bare|%', which causes subsequent sequences of barewords to be | |
2420 | * treated as plain text; | |
2421 | * | |
2422 | * * `%|!hex|%', `%|!base32|%', `%|!base64|%', which cause subsequent | |
2423 | * barewords to be decoded in the requested manner. | |
2424 | * | |
2425 | * * `%|!repeat|% %$n$% %|{|% %%\textit{string}%% %|}|%', which includes | |
2426 | * %$n$% copies of the (compound) string. | |
2427 | * | |
c81c35df MW |
2428 | * The only difference between text and binary strings is that the initial |
2429 | * coding scheme is %|bare|% for text strings and %|hex|% for binary strings. | |
2430 | * | |
67b5031e MW |
2431 | * Either kind of string can contain internal nul characters. A trailing nul |
2432 | * is appended -- beyond the stated input length -- to input strings as a | |
2433 | * convenience to test functions. Test functions may include such a nul | |
2434 | * character on output but this is not checked by the equality test. | |
2435 | * | |
2436 | * A @struct tvec_urange@ may be supplied as an argument: the length of the | |
2437 | * string (in bytes) will be checked against the permitted range. | |
2438 | */ | |
2439 | ||
c81c35df | 2440 | extern const struct tvec_regty tvty_text, tvty_bytes; |
b64eb60f | 2441 | |
d056fbdf MW |
2442 | /* --- @tvec_alloctext@, @tvec_allocbytes@ --- * |
2443 | * | |
2444 | * Arguments: @union tvec_regval *rv@ = register value | |
2445 | * @size_t sz@ = required size | |
2446 | * | |
2447 | * Returns: --- | |
2448 | * | |
2449 | * Use: Allocated space in a text or binary string register. If the | |
2450 | * current register size is sufficient, its buffer is left | |
2451 | * alone; otherwise, the old buffer, if any, is freed and a | |
2452 | * fresh buffer allocated. These functions are not intended to | |
2453 | * be used to adjust a buffer repeatedly, e.g., while building | |
2454 | * output incrementally: (a) they will perform badly, and (b) | |
2455 | * the old buffer contents are simply discarded if reallocation | |
2456 | * is necessary. Instead, use a @dbuf@ or @dstr@. | |
2457 | * | |
2458 | * The @tvec_alloctext@ function sneakily allocates an extra | |
2459 | * byte for a terminating zero. The @tvec_allocbytes@ function | |
2460 | * doesn't do this. | |
2461 | */ | |
2462 | ||
2463 | extern void tvec_alloctext(union tvec_regval */*rv*/, size_t /*sz*/); | |
2464 | extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/); | |
2465 | ||
c81c35df | 2466 | /* --- @tvec_claimeq_text@, @TVEC_CLAIMEQ_TEXT@ --- * |
67b5031e MW |
2467 | * |
2468 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
2469 | * @const char *p0@, @size_t sz0@ = first string with length | |
2470 | * @const char *p1@, @size_t sz1@ = second string with length | |
2471 | * @const char *file@, @unsigned @lno@ = calling file and line | |
2472 | * @const char *expr@ = the expression to quote on failure | |
2473 | * | |
2474 | * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise | |
2475 | * zero. | |
2476 | * | |
2477 | * Use: Check that strings at @p0@ and @p1@ are equal. As for | |
2478 | * @tvec_claim@ above, a test case is automatically begun and | |
2479 | * ended if none is already underway. If the values are | |
2480 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
2481 | * mismatched values are dumped: @p0@ is printed as the output | |
2482 | * value and @p1@ is printed as the input reference. | |
2483 | * | |
c81c35df | 2484 | * The @TVEC_CLAIM_TEXT@ macro is similar, only it (a) |
67b5031e MW |
2485 | * identifies the file and line number of the call site |
2486 | * automatically, and (b) implicitly quotes the source text of | |
2487 | * the @ch0@ and @ch1@ arguments in the failure message. | |
2488 | */ | |
2489 | ||
c81c35df MW |
2490 | extern int tvec_claimeq_text(struct tvec_state */*tv*/, |
2491 | const char */*p0*/, size_t /*sz0*/, | |
2492 | const char */*p1*/, size_t /*sz1*/, | |
2493 | const char */*file*/, unsigned /*lno*/, | |
2494 | const char */*expr*/); | |
2495 | #define TVEC_CLAIMEQ_TEXT(tv, p0, sz0, p1, sz1) \ | |
2496 | (tvec_claimeq_text(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \ | |
2497 | #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]")) | |
67b5031e | 2498 | |
c81c35df | 2499 | /* --- @tvec_claimeq_textz@, @TVEC_CLAIMEQ_TEXTZ@ --- * |
67b5031e MW |
2500 | * |
2501 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
2502 | * @const char *p0, *p1@ = two strings to compare | |
2503 | * @const char *file@, @unsigned @lno@ = calling file and line | |
2504 | * @const char *expr@ = the expression to quote on failure | |
2505 | * | |
2506 | * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise | |
2507 | * zero. | |
2508 | * | |
2509 | * Use: Check that strings at @p0@ and @p1@ are equal, as for | |
2510 | * @tvec_claimeq_string@, except that the strings are assumed | |
2511 | * null-terminated, so their lengths don't need to be supplied | |
c81c35df | 2512 | * explicitly. The macro is similarly like @TVEC_CLAIMEQ_TEXT@. |
67b5031e MW |
2513 | */ |
2514 | ||
c81c35df MW |
2515 | extern int tvec_claimeq_textz(struct tvec_state */*tv*/, |
2516 | const char */*p0*/, const char */*p1*/, | |
2517 | const char */*file*/, unsigned /*lno*/, | |
2518 | const char */*expr*/); | |
2519 | #define TVEC_CLAIMEQ_TEXTZ(tv, p0, p1) \ | |
2520 | (tvec_claimeq_textz(tv, p0, p1, __FILE__, __LINE__, #p0 " /= " #p1)) | |
67b5031e MW |
2521 | |
2522 | /* --- @tvec_claimeq_bytes@, @TVEC_CLAIMEQ_BYTES@ --- * | |
2523 | * | |
2524 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
2525 | * @const void *p0@, @size_t sz0@ = first string with length | |
2526 | * @const void *p1@, @size_t sz1@ = second string with length | |
2527 | * @const char *file@, @unsigned @lno@ = calling file and line | |
2528 | * @const char *expr@ = the expression to quote on failure | |
2529 | * | |
2530 | * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise | |
2531 | * zero. | |
2532 | * | |
2533 | * Use: Check that binary strings at @p0@ and @p1@ are equal. As for | |
2534 | * @tvec_claim@ above, a test case is automatically begun and | |
2535 | * ended if none is already underway. If the values are | |
2536 | * unequal, then @tvec_fail@ is called, quoting @expr@, and the | |
2537 | * mismatched values are dumped: @p0@ is printed as the output | |
2538 | * value and @p1@ is printed as the input reference. | |
2539 | * | |
2540 | * The @TVEC_CLAIM_STRING@ macro is similar, only it (a) | |
2541 | * identifies the file and line number of the call site | |
2542 | * automatically, and (b) implicitly quotes the source text of | |
2543 | * the @ch0@ and @ch1@ arguments in the failure message. | |
2544 | */ | |
2545 | ||
b64eb60f MW |
2546 | extern int tvec_claimeq_bytes(struct tvec_state */*tv*/, |
2547 | const void */*p0*/, size_t /*sz0*/, | |
2548 | const void */*p1*/, size_t /*sz1*/, | |
2549 | const char */*file*/, unsigned /*lno*/, | |
2550 | const char */*expr*/); | |
b64eb60f MW |
2551 | #define TVEC_CLAIMEQ_BYTES(tv, p0, sz0, p1, sz1) \ |
2552 | (tvec_claimeq(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \ | |
2553 | #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]")) | |
2554 | ||
67b5031e | 2555 | /*----- Buffer type -------------------------------------------------------*/ |
c5e0e403 | 2556 | |
67b5031e | 2557 | /* Buffer registers are primarily used for benchmarking. Only a buffer's |
adec5584 MW |
2558 | * allocation parameters are significant: its contents are ignored on |
2559 | * comparison and output, and unspecified on input. | |
2560 | * | |
2561 | * The input format gives the buffer's size, and an optional alignment | |
2562 | * specification, in the form %|SZ [`@' M [`+' A]]|%. Each of %|SZ|%, %|M|% | |
2563 | * and %|A|% are sizes, as an integer, optionally suffixed with a unit `kB', | |
2564 | * `MB', `GB', `TB', `PB', `EB', `ZB', `YB' (with or without the `B') | |
2565 | * denoting a power of 1024. The %|SZ|% gives the (effective) buffer size. | |
2566 | * %|M|% is the `alignment quantum' and %|A|% is the `alignment offset'; both | |
2567 | * default to zero, but if %|M|% is nonzero then the start of the buffer is | |
2568 | * aligned such that it is %|A|% more than a multiple of %|M|% bytes. Note | |
2569 | * that %|M|% need not be a power of two, though this is common. | |
67b5031e | 2570 | * |
adec5584 MW |
2571 | * Units other than `B' are used on output only when the size would be |
2572 | * expressed exactly. | |
2573 | * | |
2574 | * Buffers are %%\emph{not}%% allocated by default. In benchmarks, this is | |
2575 | * best done in a @before@ function. | |
c81c35df MW |
2576 | * |
2577 | * No @claimeq@ functions or macros are provided for buffers because they | |
2578 | * don't seem very useful. | |
67b5031e | 2579 | */ |
c5e0e403 | 2580 | |
67b5031e | 2581 | extern const struct tvec_regty tvty_buffer; |
c5e0e403 | 2582 | |
adec5584 MW |
2583 | /* --- @tvec_initbuffer@ --- * |
2584 | * | |
2585 | * Arguments: @union tvec_regval *rv@ = register value | |
d056fbdf | 2586 | * @const union tvec_regval *ref@ = reference buffer |
adec5584 MW |
2587 | * @size_t sz@ = size to allocate |
2588 | * | |
2589 | * Returns: --- | |
2590 | * | |
d056fbdf | 2591 | * Use: Initialize the alignment parameters in @rv@ to match @ref@, |
adec5584 MW |
2592 | * and the size to @sz@. |
2593 | */ | |
2594 | ||
2595 | extern void tvec_initbuffer(union tvec_regval */*rv*/, | |
d056fbdf | 2596 | const union tvec_regval */*ref*/, size_t /*sz*/); |
adec5584 MW |
2597 | |
2598 | /* --- @tvec_allocbuffer@ --- * | |
2599 | * | |
2600 | * Arguments: @union tvec_regval *rv@ = register value | |
2601 | * | |
2602 | * Returns: --- | |
2603 | * | |
2604 | * Use: Allocate @sz@ bytes to the buffer and fill the space with a | |
2605 | * distinctive pattern. | |
2606 | */ | |
2607 | ||
2608 | extern void tvec_allocbuffer(union tvec_regval */*rv*/); | |
2609 | ||
b64eb60f MW |
2610 | /*----- That's all, folks -------------------------------------------------*/ |
2611 | ||
2612 | #ifdef __cplusplus | |
2613 | } | |
2614 | #endif | |
2615 | ||
2616 | #endif |