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 | ||
b1a20bee MW |
110 | #ifndef MLIB_ARENA_H |
111 | # include "arena.h" | |
b64eb60f MW |
112 | #endif |
113 | ||
b1a20bee MW |
114 | #ifndef MLIB_BUF_H |
115 | # include "buf.h" | |
b64eb60f MW |
116 | #endif |
117 | ||
b1a20bee | 118 | #ifndef MLIB_DSTR_H |
b64eb60f MW |
119 | # include "dstr.h" |
120 | #endif | |
121 | ||
e63124bc MW |
122 | #ifndef MLIB_GPRINTF_H |
123 | # include "gprintf.h" | |
124 | #endif | |
125 | ||
b64eb60f MW |
126 | #ifndef MLIB_MACROS_H |
127 | # include "macros.h" | |
128 | #endif | |
129 | ||
130 | /*----- Miscellaneous values ----------------------------------------------*/ | |
131 | ||
132 | /* These are attached to structures which represent extension points, as a | |
133 | * way to pass an opaque parameter to whatever things are hooked onto them. | |
134 | */ | |
135 | ||
136 | #define TVEC_MISCSLOTS(_) \ | |
137 | _(PTR, const void *, p) /* arbitrary pointer */ \ | |
138 | _(INT, long, i) /* signed integer */ \ | |
e63124bc MW |
139 | _(UINT, unsigned long, u) /* signed integer */ \ |
140 | _(FLT, double, f) /* floating point */ | |
b64eb60f MW |
141 | |
142 | union tvec_misc { | |
143 | #define TVEC_DEFSLOT(tag, ty, slot) ty slot; | |
144 | TVEC_MISCSLOTS(TVEC_DEFSLOT) | |
145 | #undef TVEC_DEFSLOT | |
146 | }; | |
147 | enum { | |
148 | #define TVEC_DEFCONST(tag, ty, slot) TVMISC_##tag, | |
149 | TVEC_MISCSLOTS(TVEC_DEFCONST) | |
150 | TVMISC_LIMIT | |
151 | }; | |
152 | ||
153 | /*----- Register values ---------------------------------------------------*/ | |
154 | ||
155 | /* The framework doesn't have a preconceived idea about what's in a register | |
156 | * value: it just allocates them and accesses them through the register type | |
157 | * functions. It doesn't even have a baked-in idea of how big a register | |
158 | * value is: instead, it gets that via the `regsz' slot in `struct | |
159 | * tvec_testinfo'. So, as far as the framework is concerned, it's safe to | |
160 | * add new slots to this union, even if they make the overall union larger. | |
161 | * This can be done by defining the preprocessor macro `TVEC_REGSLOTS' to be | |
162 | * a `union' fragment defining any additional union members. | |
163 | * | |
164 | * This creates a distinction between code which does and doesn't know the | |
165 | * size of a register value. Code which does, which typically means the test | |
166 | * functions, benchmarking setup and teardown functions, and tightly-bound | |
167 | * runner functions, is free to index the register vectors directly. Code | |
168 | * which doesn't, which means the framework core itself and output formatting | |
169 | * machinery, must use the `TVEC_REG' macro (or its more general `TVEC_GREG' | |
170 | * companion) for indexing register vectors. (In principle, register type | |
171 | * handlers also fit into this category, but they have no business peering | |
172 | * into register values other than the one's they're given.) | |
173 | */ | |
174 | ||
175 | union tvec_regval { | |
31d0247c MW |
176 | /* The actual register value. This is what the type handler sees. |
177 | * Additional members can be added by setting `TVEC_REGSLOTS' before | |
178 | * including this file. | |
179 | * | |
180 | * A register value can be /initialized/, which simply means that its | |
181 | * contents represent a valid value according to its type -- the register | |
182 | * can be compared, dumped, serialized, parsed into, etc. You can't do | |
183 | * anything safely to an uninitialized register value other than initialize | |
184 | * it. | |
185 | */ | |
b64eb60f MW |
186 | |
187 | long i; /* signed integer */ | |
188 | unsigned long u; /* unsigned integer */ | |
189 | void *p; /* pointer */ | |
e63124bc | 190 | double f; /* floating point */ |
c81c35df | 191 | struct { char *p; size_t sz; } text; /* text string */ |
d056fbdf | 192 | struct { unsigned char *p; size_t sz; } bytes; /* binary string of bytes */ |
adec5584 MW |
193 | struct { /* buffer */ |
194 | unsigned char *p; size_t sz; /* binary string */ | |
195 | size_t a, m; /* residue and modulus */ | |
196 | size_t off; /* offset into full buffer */ | |
197 | } buf; | |
b64eb60f MW |
198 | #ifdef TVEC_REGSLOTS |
199 | TVEC_REGSLOTS | |
200 | #endif | |
201 | }; | |
202 | ||
203 | struct tvec_reg { | |
31d0247c MW |
204 | /* A register. |
205 | * | |
206 | * Note that all of the registers listed as being used by a particular test | |
207 | * group are initialized at all times[1] while that test group is being | |
208 | * processed. (The other register slots don't even have types associated | |
209 | * with them, so there's nothing useful we could do with them.) | |
210 | * | |
211 | * The `TVRF_LIVE' flag indicates that the register was assigned a value by | |
212 | * the test vector file: it's the right thing to use to check whether an | |
213 | * optional register is actually present. Even `dead' registers are still | |
214 | * initialized, though. | |
215 | * | |
216 | * [1] This isn't quite true. Between individual tests, the registers are | |
217 | * released and reinitialized in order to reset them to known values | |
218 | * ready for the next test. But you won't see them at this point. | |
219 | */ | |
b64eb60f MW |
220 | |
221 | unsigned f; /* flags */ | |
c4ccbbf9 MW |
222 | #define TVRF_SEEN 1u /* assignment seen in file */ |
223 | #define TVRF_LIVE 2u /* used in current test */ | |
b64eb60f MW |
224 | union tvec_regval v; /* register value */ |
225 | }; | |
226 | ||
227 | struct tvec_regdef { | |
31d0247c MW |
228 | /* A register definition. Register definitions list the registers which |
229 | * are used by a particular test group (see `struct tvec_test' below). | |
230 | * | |
231 | * A vector of register definitions is terminated by a definition whose | |
232 | * `name' slot is null. | |
233 | */ | |
b64eb60f MW |
234 | |
235 | const char *name; /* register name (for input files) */ | |
b64eb60f | 236 | const struct tvec_regty *ty; /* register type descriptor */ |
d056fbdf | 237 | unsigned i; /* register index */ |
b64eb60f | 238 | unsigned f; /* flags */ |
c4ccbbf9 MW |
239 | #define TVRF_UNSET 1u /* register may be marked unset */ |
240 | #define TVRF_OPT 2u /* register need not be assigned */ | |
241 | #define TVRF_ID 4u /* part of test identity */ | |
b64eb60f MW |
242 | union tvec_misc arg; /* extra detail for the type */ |
243 | }; | |
db2bf411 | 244 | #define TVEC_ENDREGS { 0, 0, 0, 0, { 0 } } |
b64eb60f | 245 | |
e63124bc MW |
246 | /* @TVEC_GREG(vec, i, regsz)@ |
247 | * | |
248 | * If @vec@ is a data pointer which happens to contain the address of a | |
249 | * vector of @struct tvec_reg@ objects, @i@ is an integer, and @regsz@ is the | |
250 | * size of a @struct tvec_reg@, then this evaluates to the address of the | |
251 | * @i@th element of the vector. | |
252 | * | |
253 | * This is the general tool you need for accessing register vectors when you | |
254 | * don't have absolute knowledge of the size of a @union tvec_regval@. | |
255 | * Usually you want to access one of the register vectors in a @struct | |
256 | * tvec_state@, and @TVEC_REG@ will be more convenient. | |
257 | */ | |
258 | #define TVEC_GREG(vec, i, regsz) \ | |
259 | ((struct tvec_reg *)((unsigned char *)(vec) + (i)*(regsz))) | |
260 | ||
c91413e6 MW |
261 | /*----- Register types ----------------------------------------------------*/ |
262 | ||
263 | struct tvec_state; /* forward declaration */ | |
264 | ||
265 | struct tvec_regty { | |
266 | /* A register type. */ | |
267 | ||
268 | void (*init)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/); | |
31d0247c | 269 | /* Initialize the value in @*rv@. This will be called before any other |
c4ccbbf9 MW |
270 | * function acting on the value, including @release@. Following @init@, |
271 | * the register value must be valid to use for all other type entry | |
272 | * points. | |
31d0247c | 273 | */ |
c91413e6 MW |
274 | |
275 | void (*release)(union tvec_regval */*rv*/, | |
276 | const struct tvec_regdef */*rd*/); | |
c4ccbbf9 MW |
277 | /* Release any resources associated with the value in @*rv@. The |
278 | * register value may be left in an invalid state. | |
279 | */ | |
c91413e6 MW |
280 | |
281 | int (*eq)(const union tvec_regval */*rv0*/, | |
282 | const union tvec_regval */*rv1*/, | |
283 | const struct tvec_regdef */*rd*/); | |
31d0247c MW |
284 | /* Return nonzero if @*rv0@ and @*rv1@ are equal values. Asymmetric |
285 | * criteria are permitted: @tvec_checkregs@ calls @eq@ with the input | |
286 | * register as @rv0@ and the output as @rv1@. | |
287 | */ | |
c91413e6 MW |
288 | |
289 | int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/, | |
290 | const struct tvec_regdef */*rd*/); | |
31d0247c MW |
291 | /* Serialize the value @*rv@, writing the result to @b@. Return zero on |
292 | * success, or %$-1$% on error. | |
293 | */ | |
c91413e6 MW |
294 | |
295 | int (*frombuf)(buf */*b*/, union tvec_regval */*rv*/, | |
296 | const struct tvec_regdef */*rd*/); | |
31d0247c MW |
297 | /* Deserialize a value from @b@, storing it in @*rv@. Return zero on |
298 | * success, or %$-1$% on error. | |
299 | */ | |
c91413e6 MW |
300 | |
301 | int (*parse)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/, | |
302 | struct tvec_state */*tv*/); | |
31d0247c MW |
303 | /* Parse a value from @tv->fp@, storing it in @*rv@. Return zero on |
304 | * success, or %$-1$% on error, having reported one or more errors via | |
305 | * @tvec_error@ or @tvec_syntax@. A successful return should leave the | |
306 | * input position at the start of the next line; the caller will flush | |
307 | * the remainder of the line itself. | |
308 | */ | |
c91413e6 MW |
309 | |
310 | void (*dump)(const union tvec_regval */*rv*/, | |
311 | const struct tvec_regdef */*rd*/, | |
312 | unsigned /*style*/, | |
313 | const struct gprintf_ops */*gops*/, void */*go*/); | |
314 | #define TVSF_COMPACT 1u | |
5c0f2e08 | 315 | #define TVSF_RAW 2u |
31d0247c MW |
316 | /* Write a human-readable representation of the value @*rv@ using |
317 | * @gprintf@ on @gops@ and @go@. The @style@ is a collection of flags: | |
318 | * if @TVSF_COMPACT@ is set, then output should be minimal, and must fit | |
319 | * on a single line; otherwise, output may consist of multiple lines and | |
320 | * may contain redundant information if that is likely to be useful to a | |
5c0f2e08 MW |
321 | * human reader. If @TVSF_RAW@ is set, then output should prefer |
322 | * machine-readability over human-readability. | |
31d0247c | 323 | */ |
c91413e6 MW |
324 | }; |
325 | ||
326 | /*----- Test descriptions -------------------------------------------------*/ | |
327 | ||
d056fbdf MW |
328 | struct tvec_env; |
329 | ||
c91413e6 MW |
330 | typedef void tvec_testfn(const struct tvec_reg */*in*/, |
331 | struct tvec_reg */*out*/, | |
332 | void */*ctx*/); | |
333 | /* A test function. It should read inputs from @in@ and write outputs to | |
334 | * @out@. The @TVRF_LIVE@ is set on inputs which are actually present, and | |
335 | * on outputs which are wanted to test. A test function can set additional | |
336 | * `gratuitous outputs' by setting @TVRF_LIVE@ on them; clearing | |
337 | * @TVRF_LIVE@ on a wanted output causes a mismatch. | |
338 | * | |
339 | * A test function may be called zero or more times by the environment. In | |
340 | * particular, it may be called multiple times, though usually by prior | |
341 | * arrangement with the environment. | |
342 | * | |
343 | * The @ctx@ is supplied by the environment's @run@ function (see below). | |
344 | * The default environment calls the test function once, with a null | |
345 | * @ctx@. There is no expectation that the environment's context has | |
346 | * anything to do with the test function's context. | |
347 | */ | |
348 | ||
814e42ff MW |
349 | typedef int tvec_setvarfn(struct tvec_state */*tv*/, const char */*var*/, |
350 | const union tvec_regval */*rv*/, void */*ctx*/); | |
351 | /* Called after a variable is read. Return zero on success or %$-1$% on | |
352 | * error. This function is never called if the test group is skipped. | |
353 | */ | |
354 | ||
355 | struct tvec_vardef { | |
356 | size_t regsz; /* (minimum) register size */ | |
357 | tvec_setvarfn *setvar; /* function to set variable */ | |
358 | struct tvec_regdef def; /* register definition */ | |
359 | }; | |
360 | ||
c91413e6 MW |
361 | typedef void tvec_envsetupfn(struct tvec_state */*tv*/, |
362 | const struct tvec_env */*env*/, | |
363 | void */*pctx*/, void */*ctx*/); | |
364 | /* Initialize the context; called at the start of a test group; @pctx@ is | |
365 | * null for environments called by the core, but may be non-null for | |
366 | * subordinate environments. If setup fails, the function should call | |
31d0247c MW |
367 | * @tvec_skipgroup@ with a suitable excuse. The @set@, @after@, and |
368 | * @teardown@ entry points will still be called, but @before@ and @run@ | |
369 | * will not. | |
c91413e6 MW |
370 | */ |
371 | ||
814e42ff MW |
372 | typedef const struct tvec_vardef *tvec_envfindvarfn |
373 | (struct tvec_state */*tv*/, const char */*name*/, | |
374 | void **/*ctx_out*/, void */*ctx*/); | |
375 | /* Called when the parser finds a %|@var|%' special variable. If a | |
376 | * suitable variable was found, set @*ctx_out@ to a suitable context and | |
377 | * return the variable definition; the context will be passed to the | |
378 | * variable definition's @setvar@ function. If no suitable variable was | |
379 | * found, then return null. | |
c91413e6 MW |
380 | */ |
381 | ||
382 | typedef void tvec_envbeforefn(struct tvec_state */*tv*/, void */*ctx*/); | |
383 | /* Called prior to running a test. This is the right place to act on any | |
384 | * `%|@var|%' settings. If preparation fails, the function should call | |
385 | * @tvec_skip@ with a suitable excuse. This function is never called if | |
814e42ff MW |
386 | * the test group is skipped. It %%\emph{is}%% called if the test will be |
387 | * skipped due to erroneous test data. It should check the @TVSF_ACTIVE@ | |
388 | * flag if necessary. | |
c91413e6 MW |
389 | */ |
390 | ||
391 | typedef void tvec_envrunfn(struct tvec_state */*tv*/, | |
392 | tvec_testfn */*fn*/, void */*ctx*/); | |
393 | /* Run the test. It should either call @tvec_skip@, or run @fn@ one or | |
394 | * more times. In the latter case, it is responsible for checking the | |
395 | * outputs, and calling @tvec_fail@ as necessary; @tvec_checkregs@ will | |
396 | * check the register values against the supplied test vector, while | |
397 | * @tvec_check@ does pretty much everything necessary. This function is | |
398 | * never called if the test group is skipped. | |
399 | */ | |
400 | ||
401 | typedef void tvec_envafterfn(struct tvec_state */*tv*/, void */*ctx*/); | |
402 | /* Called after running or skipping a test. Typical actions involve | |
31d0247c | 403 | * resetting whatever things were established by @set@. This function |
814e42ff MW |
404 | * %%\emph{is}%% called if the test group is skipped or the test data is |
405 | * erroneous, so that the test environment can reset variables set by the | |
406 | * @set@ entry point. It should check the @TVSF_SKIP@ flag if necessary. | |
c91413e6 MW |
407 | */ |
408 | ||
409 | typedef void tvec_envteardownfn(struct tvec_state */*tv*/, void */*ctx*/); | |
410 | /* Tear down the environment: called at the end of a test group. */ | |
411 | ||
412 | ||
413 | struct tvec_env { | |
414 | /* A test environment sets things up for and arranges to run the test. | |
415 | * | |
416 | * The caller is responsible for allocating storage for the environment's | |
417 | * context, based on the @ctxsz@ slot, and freeing it later; this space is | |
418 | * passed in as the @ctx@ parameter to the remaining functions; if @ctxsz@ | |
419 | * is zero then @ctx@ is null. | |
420 | */ | |
421 | ||
422 | size_t ctxsz; /* environment context size */ | |
423 | ||
424 | tvec_envsetupfn *setup; /* setup for group */ | |
814e42ff | 425 | tvec_envfindvarfn *findvar; /* find variable */ |
c91413e6 MW |
426 | tvec_envbeforefn *before; /* prepare for test */ |
427 | tvec_envrunfn *run; /* run test function */ | |
428 | tvec_envafterfn *after; /* clean up after test */ | |
429 | tvec_envteardownfn *teardown; /* tear down after group */ | |
430 | }; | |
431 | ||
432 | struct tvec_test { | |
433 | /* A test description. */ | |
434 | ||
435 | const char *name; /* name of the test */ | |
436 | const struct tvec_regdef *regs; /* descriptions of the registers */ | |
437 | const struct tvec_env *env; /* environment to run test in */ | |
438 | tvec_testfn *fn; /* test function */ | |
439 | }; | |
c91413e6 | 440 | |
b64eb60f MW |
441 | /*----- Test state --------------------------------------------------------*/ |
442 | ||
b1a20bee MW |
443 | struct tvec_output; |
444 | ||
3efcfd2d MW |
445 | enum { |
446 | /* Possible test outcomes. */ | |
447 | ||
448 | TVOUT_LOSE, /* test failed */ | |
449 | TVOUT_SKIP, /* test skipped */ | |
20ba6b0b | 450 | TVOUT_XFAIL, /* test passed, but shouldn't have */ |
5c0f2e08 | 451 | TVOUT_WIN, /* test passed */ |
3efcfd2d MW |
452 | TVOUT_LIMIT /* (number of possible outcomes) */ |
453 | }; | |
b64eb60f | 454 | |
d056fbdf MW |
455 | struct tvec_config { |
456 | /* An overall test configuration. */ | |
457 | ||
b1a20bee | 458 | const struct tvec_test *const *tests; /* the tests to be performed */ |
d056fbdf MW |
459 | unsigned nrout, nreg; /* number of output/total regs */ |
460 | size_t regsz; /* size of a register */ | |
461 | }; | |
462 | ||
b64eb60f | 463 | struct tvec_state { |
e63124bc MW |
464 | /* The primary state structure for the test vector machinery. */ |
465 | ||
d056fbdf | 466 | /* Flags. Read-only for all callers. */ |
b64eb60f | 467 | unsigned f; /* flags */ |
20ba6b0b MW |
468 | #define TVSF_SKIP 0x0001u /* skip this test group */ |
469 | #define TVSF_OPEN 0x0002u /* test is open */ | |
470 | #define TVSF_ACTIVE 0x0004u /* test is active */ | |
471 | #define TVSF_ERROR 0x0008u /* an error occurred */ | |
472 | #define TVSF_OUTMASK 0x00f0u /* test outcome (@TVOUT_...@) */ | |
3efcfd2d | 473 | #define TVSF_OUTSHIFT 4 /* shift applied to outcome */ |
20ba6b0b | 474 | #define TVSF_XFAIL 0x0100u /* test expected to fail */ |
31d0247c | 475 | #define TVSF_MUFFLE 0x0200u /* muffle errors */ |
e63124bc | 476 | |
b1a20bee MW |
477 | /* Memory allocation. Read-only for all callers. */ |
478 | arena *a; | |
479 | ||
d056fbdf MW |
480 | /* Test configuration. Read-only for all callers. */ |
481 | struct tvec_config cfg; /* test configuration */ | |
482 | ||
483 | /* Registers. Available to execution environments, which may modify the | |
484 | * contents of the active registers, as defined by the current test group, | |
485 | * but not the vector pointers themselves or inactive registers. | |
486 | */ | |
b64eb60f | 487 | struct tvec_reg *in, *out; /* register vectors */ |
e63124bc | 488 | |
d056fbdf MW |
489 | /* Test group state. Read-only for all callers. */ |
490 | const struct tvec_test *test; /* current test */ | |
e63124bc MW |
491 | |
492 | /* Test scoreboard. Available to output formatters. */ | |
b64eb60f | 493 | unsigned curr[TVOUT_LIMIT], all[TVOUT_LIMIT], grps[TVOUT_LIMIT]; |
e63124bc | 494 | |
b1a20bee | 495 | /* Output machinery. Read-only for environments. */ |
b64eb60f | 496 | struct tvec_output *output; /* output formatter */ |
e63124bc MW |
497 | |
498 | /* Input machinery. Available to type parsers. */ | |
b64eb60f MW |
499 | const char *infile; unsigned lno, test_lno; /* input file name, line */ |
500 | FILE *fp; /* input file stream */ | |
b1a20bee MW |
501 | |
502 | /* Adhoc testing state. Private. */ | |
503 | struct tvec_test adhoc_test; | |
504 | const struct tvec_test *adhoc_tests[]; | |
b64eb60f MW |
505 | }; |
506 | ||
e63124bc MW |
507 | /* @TVEC_REG(tv, vec, i)@ |
508 | * | |
509 | * If @tv@ is a pointer to a @struct tvec_state@, @vec@ is either @in@ or | |
510 | * @out@, and @i@ is an integer, then this evaluates to the address of the | |
511 | * @i@th register in the selected vector. | |
512 | */ | |
d056fbdf | 513 | #define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->cfg.regsz) |
67b5031e | 514 | |
67b5031e MW |
515 | /*----- Session lifecycle -------------------------------------------------*/ |
516 | ||
67b5031e MW |
517 | /* --- @tvec_begin@ --- * |
518 | * | |
519 | * Arguments: @struct tvec_state *tv_out@ = state structure to fill in | |
520 | * @const struct tvec_config *config@ = test configuration | |
521 | * @struct tvec_output *o@ = output driver | |
522 | * | |
523 | * Returns: --- | |
524 | * | |
525 | * Use: Initialize a state structure ready to do some testing. | |
526 | */ | |
527 | ||
528 | extern void tvec_begin(struct tvec_state */*tv_out*/, | |
529 | const struct tvec_config */*config*/, | |
530 | struct tvec_output */*o*/); | |
531 | ||
532 | /* --- @tvec_end@ --- * | |
533 | * | |
534 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
535 | * | |
536 | * Returns: A proposed exit code. | |
537 | * | |
538 | * Use: Conclude testing and suggests an exit code to be returned to | |
539 | * the calling program. (The exit code comes from the output | |
540 | * driver's @esession@ method.) | |
541 | */ | |
542 | ||
543 | extern int tvec_end(struct tvec_state */*tv*/); | |
544 | ||
545 | /* --- @tvec_read@ --- * | |
546 | * | |
547 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
548 | * @const char *infile@ = the name of the input file | |
549 | * @FILE *fp@ = stream to read from | |
550 | * | |
31d0247c | 551 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
552 | * |
553 | * Use: Read test vector data from @fp@ and exercise test functions. | |
554 | * THe return code doesn't indicate test failures: it's only | |
555 | * concerned with whether there were problems with the input | |
556 | * file or with actually running the tests. | |
557 | */ | |
558 | ||
559 | extern int tvec_read(struct tvec_state */*tv*/, | |
560 | const char */*infile*/, FILE */*fp*/); | |
561 | ||
562 | /*----- Command-line interface --------------------------------------------*/ | |
563 | ||
67b5031e MW |
564 | /* --- @tvec_parseargs@ --- * |
565 | * | |
566 | * Arguments: @int argc@ = number of command-line arguments | |
567 | * @char *argv[]@ = vector of argument strings | |
568 | * @struct tvec_state *tv_out@ = test vector state to initialize | |
569 | * @int *argpos_out@ = where to leave unread argument index | |
570 | * @const struct tvec_config *cofig@ = test vector configuration | |
571 | * | |
572 | * Returns: --- | |
573 | * | |
574 | * Use: Parse arguments and set up the test vector state @*tv_out@. | |
575 | * If errors occur, print messages to standard error and exit | |
576 | * with status 2. | |
577 | */ | |
578 | ||
579 | extern void tvec_parseargs(int /*argc*/, char */*argv*/[], | |
580 | struct tvec_state */*tv_out*/, | |
581 | int */*argpos_out*/, | |
582 | const struct tvec_config */*config*/); | |
583 | ||
584 | /* --- @tvec_readstdin@, @tvec_readfile@, @tvec_readarg@ --- * | |
585 | * | |
586 | * Arguments: @struct tvec_state *tv@ = test vector state | |
587 | * @const char *file@ = pathname of file to read | |
588 | * @const char *arg@ = argument to interpret | |
589 | * | |
31d0247c | 590 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
591 | * |
592 | * Use: Read test vector data from stdin or a named file. The | |
593 | * @tvec_readarg@ function reads from stdin if @arg@ is `%|-|%', | |
594 | * and from the named file otherwise. | |
595 | */ | |
596 | ||
597 | extern int tvec_readstdin(struct tvec_state */*tv*/); | |
598 | extern int tvec_readfile(struct tvec_state */*tv*/, const char */*file*/); | |
599 | extern int tvec_readarg(struct tvec_state */*tv*/, const char */*arg*/); | |
600 | ||
601 | /* --- @tvec_readdflt@ --- * | |
602 | * | |
603 | * Arguments: @struct tvec_state *tv@ = test vector state | |
604 | * @const char *dflt@ = defsault filename or null | |
605 | * | |
31d0247c | 606 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
607 | * |
608 | * Use: Reads from the default test vector data. If @file@ is null, | |
609 | * then read from standard input, unless that's a terminal; if | |
610 | * @file@ is not null, then read the named file, looking in the | |
611 | * directory named by the `%|srcdir|%' environment variable if | |
612 | * that's set, or otherwise in the current directory. | |
613 | */ | |
614 | ||
615 | extern int tvec_readdflt(struct tvec_state */*tv*/, const char */*file*/); | |
616 | ||
617 | /* --- @tvec_readargs@ --- * | |
618 | * | |
619 | * Arguments: @int argc@ = number of command-line arguments | |
620 | * @char *argv[]@ = vector of argument strings | |
621 | * @struct tvec_state *tv@ = test vector state | |
622 | * @int *argpos_inout@ = current argument position (updated) | |
623 | * @const char *dflt@ = default filename or null | |
624 | * | |
31d0247c | 625 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
626 | * |
627 | * Use: Reads from the sources indicated by the command-line | |
628 | * arguments, in order, interpreting each as for @tvec_readarg@; | |
629 | * if no arguments are given then read from @dflt@ as for | |
630 | * @tvec_readdflt@. | |
631 | */ | |
632 | ||
633 | extern int tvec_readargs(int /*argc*/, char */*argv*/[], | |
634 | struct tvec_state */*tv*/, | |
635 | int */*argpos_inout*/, const char */*dflt*/); | |
636 | ||
637 | /* --- @tvec_main@ --- * | |
638 | * | |
639 | * Arguments: @int argc@ = number of command-line arguments | |
640 | * @char *argv[]@ = vector of argument strings | |
641 | * @const struct tvec_config *cofig@ = test vector configuration | |
642 | * @const char *dflt@ = default filename or null | |
643 | * | |
644 | * Returns: Exit code. | |
645 | * | |
646 | * Use: All-in-one test vector front-end. Parse options from the | |
647 | * command-line as for @tvec_parseargs@, and then process the | |
648 | * remaining positional arguments as for @tvec_readargs@. The | |
649 | * function constructs and disposes of a test vector state. | |
650 | */ | |
651 | ||
652 | extern int tvec_main(int /*argc*/, char */*argv*/[], | |
653 | const struct tvec_config */*config*/, | |
654 | const char */*dflt*/); | |
655 | ||
656 | /*----- Test processing ---------------------------------------------------*/ | |
657 | ||
658 | /* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- * | |
659 | * | |
660 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
661 | * @const char *excuse@, @va_list *ap@ = reason why skipped | |
662 | * | |
663 | * Returns: --- | |
664 | * | |
665 | * Use: Skip the current group. This should only be called from a | |
666 | * test environment @setup@ function; a similar effect occurs if | |
667 | * the @setup@ function fails. | |
668 | */ | |
669 | ||
31d0247c MW |
670 | extern PRINTF_LIKE(2, 3) |
671 | void tvec_skipgroup(struct tvec_state */*tv*/, | |
672 | const char */*excuse*/, ...); | |
67b5031e MW |
673 | extern void tvec_skipgroup_v(struct tvec_state */*tv*/, |
674 | const char */*excuse*/, va_list */*ap*/); | |
675 | ||
676 | /* --- @tvec_skip@, @tvec_skip_v@ --- * | |
677 | * | |
678 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
679 | * @const char *excuse@, @va_list *ap@ = reason why test skipped | |
680 | * | |
681 | * Returns: --- | |
682 | * | |
683 | * Use: Skip the current test. This should only be called from a | |
684 | * test environment @run@ function; a similar effect occurs if | |
685 | * the @before@ function fails. | |
686 | */ | |
687 | ||
31d0247c MW |
688 | extern PRINTF_LIKE(2, 3) |
689 | void tvec_skip(struct tvec_state */*tv*/, const char */*excuse*/, ...); | |
67b5031e MW |
690 | extern void tvec_skip_v(struct tvec_state */*tv*/, |
691 | const char */*excuse*/, va_list */*ap*/); | |
692 | ||
e63124bc MW |
693 | /* --- @tvec_fail@, @tvec_fail_v@ --- * |
694 | * | |
695 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
3efcfd2d | 696 | * @const char *detail@, @va_list *ap@ = description of test |
e63124bc MW |
697 | * |
698 | * Returns: --- | |
699 | * | |
700 | * Use: Report the current test as a failure. This function can be | |
701 | * called multiple times for a single test, e.g., if the test | |
702 | * environment's @run@ function invokes the test function | |
703 | * repeatedly; but a single test that fails repeatedly still | |
704 | * only counts as a single failure in the statistics. The | |
705 | * @detail@ string and its format parameters can be used to | |
706 | * distinguish which of several invocations failed; it can | |
707 | * safely be left null if the test function is run only once. | |
708 | */ | |
709 | ||
31d0247c MW |
710 | extern PRINTF_LIKE(2, 3) |
711 | void tvec_fail(struct tvec_state */*tv*/, const char */*detail*/, ...); | |
e63124bc MW |
712 | extern void tvec_fail_v(struct tvec_state */*tv*/, |
713 | const char */*detail*/, va_list */*ap*/); | |
714 | ||
715 | /* --- @tvec_dumpreg@ --- * | |
716 | * | |
717 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
718 | * @unsigned disp@ = the register disposition (@TVRD_...@) | |
c91413e6 | 719 | * @const union tvec_regval *tv@ = register value, or null |
e63124bc MW |
720 | * @const struct tvec_regdef *rd@ = register definition |
721 | * | |
722 | * Returns: --- | |
723 | * | |
724 | * Use: Dump a register value to the output. This is the lowest- | |
725 | * level function for dumping registers, and calls the output | |
726 | * formatter directly. | |
727 | * | |
728 | * Usually @tvec_mismatch@ is much more convenient. Low-level | |
729 | * access is required for reporting `virtual' registers | |
730 | * corresponding to test environment settings. | |
731 | */ | |
732 | ||
733 | extern void tvec_dumpreg(struct tvec_state */*tv*/, | |
734 | unsigned /*disp*/, const union tvec_regval */*rv*/, | |
735 | const struct tvec_regdef */*rd*/); | |
736 | ||
31d0247c MW |
737 | /* --- @tvec_initregs@, @tvec_releaseregs@ --- * |
738 | * | |
739 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
740 | * | |
741 | * Returns: --- | |
742 | * | |
743 | * Use: Initialize or release, respectively, the registers required | |
744 | * by the current test. All of the registers, both input and | |
745 | * output, are effected. Initialized registers are not marked | |
746 | * live. | |
747 | */ | |
748 | ||
749 | extern void tvec_initregs(struct tvec_state */*tv*/); | |
750 | extern void tvec_releaseregs(struct tvec_state */*tv*/); | |
751 | ||
752 | /* --- @tvec_resetoutputs@ --- * | |
753 | * | |
754 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
755 | * | |
756 | * Returns: --- | |
757 | * | |
758 | * Use: Reset (releases and reinitializes) the output registers in | |
759 | * the test state. This is mostly of use to test environment | |
760 | * @run@ functions, between invocations of the test function. | |
761 | * Output registers are marked live if and only if the | |
762 | * corresponding input register is live. | |
763 | */ | |
764 | ||
765 | extern void tvec_resetoutputs(struct tvec_state */*tv*/); | |
766 | ||
767 | /* --- @tvec_checkregs@ --- * | |
768 | * | |
769 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
770 | * | |
771 | * Returns: Zero on success, %$-1$% on mismatch. | |
772 | * | |
773 | * Use: Compare the active output registers (according to the current | |
774 | * test group definition) with the corresponding input register | |
775 | * values. A mismatch occurs if the two values differ | |
776 | * (according to the register type's @eq@ method), or if the | |
777 | * input is live but the output is dead. | |
778 | * | |
779 | * This function only checks for a mismatch and returns the | |
780 | * result; it takes no other action. In particular, it doesn't | |
781 | * report a failure, or dump register values. | |
782 | */ | |
783 | ||
784 | extern int tvec_checkregs(struct tvec_state */*tv*/); | |
785 | ||
e63124bc MW |
786 | /* --- @tvec_mismatch@ --- * |
787 | * | |
788 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
789 | * @unsigned f@ = flags (@TVMF_...@) | |
790 | * | |
791 | * Returns: --- | |
792 | * | |
793 | * Use: Dumps registers suitably to report a mismatch. The flag bits | |
794 | * @TVMF_IN@ and @TVF_OUT@ select input-only and output | |
795 | * registers. If both are reset then nothing happens. | |
796 | * Suppressing the output registers may be useful, e.g., if the | |
797 | * test function crashed rather than returning outputs. | |
798 | */ | |
799 | ||
800 | #define TVMF_IN 1u | |
801 | #define TVMF_OUT 2u | |
802 | extern void tvec_mismatch(struct tvec_state */*tv*/, unsigned /*f*/); | |
803 | ||
804 | /* --- @tvec_check@, @tvec_check_v@ --- * | |
805 | * | |
806 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
3efcfd2d | 807 | * @const char *detail@, @va_list *ap@ = description of test |
e63124bc MW |
808 | * |
809 | * Returns: --- | |
810 | * | |
811 | * Use: Check the register values, reporting a failure and dumping | |
812 | * the registers in the event of a mismatch. This just wraps up | |
813 | * @tvec_checkregs@, @tvec_fail@ and @tvec_mismatch@ in the | |
814 | * obvious way. | |
815 | */ | |
816 | ||
31d0247c MW |
817 | extern PRINTF_LIKE(2, 3) |
818 | void tvec_check(struct tvec_state */*tv*/, const char */*detail*/, ...); | |
e63124bc MW |
819 | extern void tvec_check_v(struct tvec_state */*tv*/, |
820 | const char */*detail*/, va_list */*ap*/); | |
b64eb60f | 821 | |
67b5031e | 822 | /*----- Output functions --------------------------------------------------*/ |
b64eb60f | 823 | |
31d0247c MW |
824 | /* --- @tvec_strlevel@ --- * |
825 | * | |
826 | * Arguments: @unsigned level@ = level code | |
827 | * | |
828 | * Returns: A human-readable description. | |
829 | * | |
830 | * Use: Converts a level code into something that you can print in a | |
831 | * message. | |
832 | */ | |
833 | ||
d056fbdf | 834 | extern const char *tvec_strlevel(unsigned /*level*/); |
31d0247c | 835 | |
c91413e6 | 836 | /* --- @tvec_report@, @tvec_report_v@ --- * |
3efcfd2d MW |
837 | * |
838 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
839 | * @const char *msg@, @va_list ap@ = error message | |
840 | * | |
c91413e6 | 841 | * Returns: --- |
3efcfd2d | 842 | * |
c91413e6 MW |
843 | * Use: Report an message with a given severity. Messages with level |
844 | * @TVLEV_ERR@ or higher force a nonzero exit code. | |
3efcfd2d MW |
845 | */ |
846 | ||
b1a20bee MW |
847 | #define TVEC_LEVELS(_) \ |
848 | _(INFO, "info", 3) \ | |
849 | _(NOTE, "notice", 4) \ | |
850 | _(ERR, "ERROR", 8) | |
851 | enum { | |
852 | #define TVEC_DEFLEVEL(tag, name, val) TVLEV_##tag = val, | |
853 | TVEC_LEVELS(TVEC_DEFLEVEL) | |
854 | #undef TVEC_DEFLEVEL | |
855 | TVLEV_LIMIT | |
856 | }; | |
857 | ||
31d0247c MW |
858 | extern PRINTF_LIKE(3, 4) |
859 | void tvec_report(struct tvec_state */*tv*/, unsigned /*level*/, | |
860 | const char */*msg*/, ...); | |
c91413e6 MW |
861 | extern void tvec_report_v(struct tvec_state */*tv*/, unsigned /*level*/, |
862 | const char */*msg*/, va_list */*ap*/); | |
b64eb60f | 863 | |
b1a20bee | 864 | /* --- @tvec_error@, @tvec_notice@, @tvec_info@ --- * |
3efcfd2d MW |
865 | * |
866 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
c91413e6 | 867 | * @const char *msg@, @va_list ap@ = error message |
3efcfd2d | 868 | * |
31d0247c | 869 | * Returns: The @tvec_error@ function returns %$-1$% as a trivial |
c91413e6 | 870 | * convenience; @tvec_notice@ does not return a value. |
3efcfd2d | 871 | * |
b1a20bee | 872 | * Use: Report a message. Errors are distinct from test |
c91413e6 MW |
873 | * failures, and indicate that a problem was encountered which |
874 | * compromised the activity of testing. Notices are important | |
875 | * information which doesn't fit into any other obvious | |
b1a20bee MW |
876 | * category. Information is anything else, and is a reasonable |
877 | * fallback for writing unstructured information in the absence | |
878 | * of dedicated support in an output driver. | |
879 | * | |
880 | * These functions are simple convenience wrappers around | |
881 | * @tvec_report@. Use @tvec_report_v@ directly if you have a | |
882 | * captured @va_list@ of arguments to format. | |
3efcfd2d MW |
883 | */ |
884 | ||
31d0247c MW |
885 | extern PRINTF_LIKE(2, 3) |
886 | int tvec_error(struct tvec_state */*tv*/, const char */*msg*/, ...); | |
887 | extern PRINTF_LIKE(2, 3) | |
888 | void tvec_notice(struct tvec_state */*tv*/, const char */*msg*/, ...); | |
b1a20bee MW |
889 | extern PRINTF_LIKE(2, 3) |
890 | void tvec_info(struct tvec_state */*tv*/, const char */*msg*/, ...); | |
b64eb60f | 891 | |
6e683a79 | 892 | /* --- @tvec_unkregerr@ --- * |
d056fbdf MW |
893 | * |
894 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
895 | * @const char *name@ = register or pseudoregister name | |
896 | * | |
897 | * Returns: %$-1$%. | |
898 | * | |
899 | * Use: Reports an error that the register or pseudoregister is | |
900 | * unrecognized. | |
901 | */ | |
902 | ||
6e683a79 | 903 | extern int tvec_unkregerr(struct tvec_state */*tv*/, const char */*name*/); |
d056fbdf | 904 | |
6e683a79 | 905 | /* --- @tvec_dupregerr@ --- * |
d056fbdf MW |
906 | * |
907 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
908 | * @const char *name@ = register or pseudoregister name | |
909 | * | |
910 | * Returns: %$-1$%. | |
911 | * | |
912 | * Use: Reports an error that the register or pseudoregister has been | |
913 | * assigned already in the current test. | |
914 | */ | |
915 | ||
6e683a79 | 916 | extern int tvec_dupregerr(struct tvec_state */*tv*/, const char */*name*/); |
d056fbdf | 917 | |
3efcfd2d MW |
918 | /* --- @tvec_humanoutput@ --- * |
919 | * | |
920 | * Arguments: @FILE *fp@ = output file to write on | |
5c0f2e08 | 921 | * @unsigned style@ = output style (@TVSF_...@) |
3efcfd2d MW |
922 | * |
923 | * Returns: An output formatter. | |
924 | * | |
925 | * Use: Return an output formatter which writes on @fp@ with the | |
926 | * expectation that a human will be watching and interpreting | |
927 | * the output. If @fp@ denotes a terminal, the display shows a | |
928 | * `scoreboard' indicating the outcome of each test case | |
929 | * attempted, and may in addition use colour and other | |
930 | * highlighting. | |
931 | */ | |
932 | ||
e63124bc | 933 | extern struct tvec_output *tvec_humanoutput(FILE */*fp*/); |
3efcfd2d | 934 | |
5c0f2e08 MW |
935 | /* --- @tvec_machineoutput@ --- * |
936 | * | |
937 | * Arguments: @FILE *fp@ = output file to write on | |
938 | * | |
939 | * Returns: An output formatter. | |
940 | * | |
941 | * Use: Return an output formatter which writes on @fp@ in a | |
942 | * moderately simple machine-readable format. | |
943 | */ | |
944 | ||
945 | struct tvec_output *tvec_machineoutput(FILE *fp); | |
946 | ||
3efcfd2d MW |
947 | /* --- @tvec_tapoutput@ --- * |
948 | * | |
949 | * Arguments: @FILE *fp@ = output file to write on | |
5c0f2e08 | 950 | * @unsigned style@ = output style (@TVSF_...@) |
3efcfd2d MW |
951 | * |
952 | * Returns: An output formatter. | |
953 | * | |
954 | * Use: Return an output formatter which writes on @fp@ in `TAP' | |
955 | * (`Test Anything Protocol') format. | |
956 | * | |
957 | * TAP comes from the Perl community, but has spread rather | |
6e683a79 MW |
958 | * further. This driver currently produces TAP version 14, but |
959 | * pretends to be version 13. The driver produces a TAP `test | |
960 | * point' -- i.e., a result reported as `ok' or `not ok' -- for | |
961 | * each input test group. Failure reports and register dumps | |
962 | * are produced as diagnostic messages before the final group | |
963 | * result. (TAP permits structuerd YAML data after the | |
964 | * test-point result, which could be used to report details, but | |
965 | * (a) postponing the details until after the report is | |
966 | * inconvenient, and (b) there is no standardization for the | |
967 | * YAML anyway, so in practice it's no more useful than the | |
968 | * unstructured diagnostics. | |
3efcfd2d MW |
969 | */ |
970 | ||
e63124bc | 971 | extern struct tvec_output *tvec_tapoutput(FILE */*fp*/); |
3efcfd2d MW |
972 | |
973 | /* --- @tvec_dfltoutput@ --- * | |
974 | * | |
975 | * Arguments: @FILE *fp@ = output file to write on | |
976 | * | |
977 | * Returns: An output formatter. | |
978 | * | |
979 | * Use: Selects and instantiates an output formatter suitable for | |
980 | * writing on @fp@. The policy is subject to change, but | |
981 | * currently the `human' output format is selected if @fp@ is | |
982 | * interactive (i.e., if @isatty(fileno(fp))@ is true), and | |
b1a20bee | 983 | * otherwise the `machine' format is used. |
3efcfd2d MW |
984 | */ |
985 | ||
e63124bc | 986 | extern struct tvec_output *tvec_dfltout(FILE */*fp*/); |
b64eb60f | 987 | |
67b5031e | 988 | /*------ Serialization utilities ------------------------------------------*/ |
b64eb60f | 989 | |
c91413e6 MW |
990 | /* Serialization format. |
991 | * | |
992 | * The `candidate register definitions' are those entries @r@ in the @regs@ | |
b1a20bee MW |
993 | * vector whose index @r.i@ is strictly less than @nr@ and where |
994 | * @r.f&mask == want@* . The `selected register definitions' are those | |
995 | * candidate register definitions @r@ for which the indicated register | |
996 | * @rv[r.i]@ has the @TVRF_LIVE@ flag set. The serialized output begins with | |
997 | * a header bitmap: if there are %$n$% candidate register definitions then | |
998 | * the header bitmap consists of %$\lceil n/8 \rceil$% bytes. Bits are | |
999 | * ordered starting from the least significant bit of the first byte, end | |
1000 | * ending at the most significant bit of the final byte. The bit | |
1001 | * corresponding to a candidate register definition is set if and only if | |
1002 | * that register defintion is selected. The header bitmap is then followed | |
1003 | * by the serializations of the selected registers -- i.e., for each selected | |
1004 | * register definition @r@, the serialized value of register @rv[r.i]@ -- | |
1005 | * simply concatenated together, with no padding or alignment. | |
c91413e6 MW |
1006 | */ |
1007 | ||
67b5031e MW |
1008 | /* --- @tvec_serialize@ --- * |
1009 | * | |
1010 | * Arguments: @const struct tvec_reg *rv@ = vector of registers | |
1011 | * @buf *b@ = buffer to write on | |
1012 | * @const struct tvec_regdef *regs@ = vector of register | |
1013 | * descriptions, terminated by an entry with a null | |
1014 | * @name@ slot | |
b1a20bee | 1015 | * @unsigned mask, want@ = flag-based selection |
67b5031e MW |
1016 | * @unsigned nr@ = number of entries in the @rv@ vector |
1017 | * @size_t regsz@ = true size of each element of @rv@ | |
1018 | * | |
31d0247c | 1019 | * Returns: Zero on success, %$-1$% on failure. |
67b5031e MW |
1020 | * |
1021 | * Use: Serialize a collection of register values. | |
1022 | * | |
67b5031e MW |
1023 | * The serialized output is written to the buffer @b@. Failure |
1024 | * can be caused by running out of buffer space, or a failing | |
1025 | * type handler. | |
1026 | */ | |
3efcfd2d | 1027 | |
67b5031e MW |
1028 | extern int tvec_serialize(const struct tvec_reg */*rv*/, buf */*b*/, |
1029 | const struct tvec_regdef */*regs*/, | |
b1a20bee | 1030 | unsigned /*mask*/, unsigned /*want*/, |
67b5031e | 1031 | unsigned /*nr*/, size_t /*regsz*/); |
3efcfd2d | 1032 | |
67b5031e MW |
1033 | /* --- @tvec_deserialize@ --- * |
1034 | * | |
1035 | * Arguments: @struct tvec_reg *rv@ = vector of registers | |
1036 | * @buf *b@ = buffer to write on | |
1037 | * @const struct tvec_regdef *regs@ = vector of register | |
1038 | * descriptions, terminated by an entry with a null | |
1039 | * @name@ slot | |
b1a20bee | 1040 | * @unsigned mask, want@ = flag-based selection |
67b5031e MW |
1041 | * @unsigned nr@ = number of entries in the @rv@ vector |
1042 | * @size_t regsz@ = true size of each element of @rv@ | |
1043 | * | |
31d0247c | 1044 | * Returns: Zero on success, %$-1$% on failure. |
67b5031e MW |
1045 | * |
1046 | * Use: Deserialize a collection of register values. | |
1047 | * | |
1048 | * The size of the register vector @nr@ and the register | |
1049 | * definitions @regs@ must match those used when producing the | |
1050 | * serialization. For each serialized register value, | |
1051 | * deserialize and store the value into the appropriate register | |
1052 | * slot, and set the @TVRF_LIVE@ flag on the register. See | |
1053 | * @tvec_serialize@ for a description of the format. | |
1054 | * | |
b1a20bee MW |
1055 | * Failure results only from an input too small for the initial |
1056 | * bitmap or a failing register type handler. | |
67b5031e | 1057 | */ |
3efcfd2d | 1058 | |
67b5031e MW |
1059 | extern int tvec_deserialize(struct tvec_reg */*rv*/, buf */*b*/, |
1060 | const struct tvec_regdef */*regs*/, | |
b1a20bee | 1061 | unsigned /*mask*/, unsigned /*want*/, |
67b5031e | 1062 | unsigned /*nr*/, size_t /*regsz*/); |
3efcfd2d | 1063 | |
67b5031e MW |
1064 | /*----- Input utilities ---------------------------------------------------*/ |
1065 | ||
1066 | /* These are provided by the core for the benefit of type @parse@ methods, | |
1067 | * and test-environment @set@ functions, which get to read from the test | |
1068 | * input file. The latter are usually best implemented by calling on the | |
1069 | * former. | |
1070 | * | |
1071 | * The two main rules are as follows. | |
1072 | * | |
1073 | * * Leave the file position at the beginning of the line following | |
1074 | * whatever it was that you read. | |
1075 | * | |
1076 | * * When you read and consume a newline (which you do at least once, by | |
1077 | * the previous rule), then increment @tv->lno@ to keep track of the | |
1078 | * current line number. | |
1079 | */ | |
1080 | ||
67b5031e MW |
1081 | /* --- @tvec_syntax@, @tvec_syntax_v@ --- * |
1082 | * | |
1083 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1084 | * @int ch@ = the character found (in @fgetc@ format) | |
1085 | * @const char *expect@, @va_list ap@ = what was expected | |
1086 | * | |
31d0247c | 1087 | * Returns: %$-1$%. |
67b5031e MW |
1088 | * |
1089 | * Use: Report a syntax error quoting @ch@ and @expect@. If @ch@ is | |
1090 | * a newline, then back up so that it can be read again (e.g., | |
1091 | * by @tvec_flushtoeol@ or @tvec_nexttoken@, which will also | |
1092 | * advance the line number). | |
1093 | */ | |
1094 | ||
31d0247c MW |
1095 | extern PRINTF_LIKE(3, 4) |
1096 | int tvec_syntax(struct tvec_state */*tv*/, int /*ch*/, | |
1097 | const char */*expect*/, ...); | |
67b5031e MW |
1098 | extern int tvec_syntax_v(struct tvec_state */*tv*/, int /*ch*/, |
1099 | const char */*expect*/, va_list */*ap*/); | |
1100 | ||
31d0247c MW |
1101 | /* --- @tvec_skipspc@ --- * |
1102 | * | |
1103 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1104 | * | |
1105 | * Returns: --- | |
1106 | * | |
1107 | * Use: Advance over any whitespace characters other than newlines. | |
1108 | * This will stop at `;', end-of-file, or any other kind of | |
1109 | * non-whitespace; and it won't consume a newline. | |
1110 | */ | |
1111 | ||
1112 | extern void tvec_skipspc(struct tvec_state */*tv*/); | |
1113 | ||
67b5031e MW |
1114 | /* --- @tvec_flushtoeol@ --- * |
1115 | * | |
1116 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1117 | * @unsigned f@ = flags (@TVFF_...@) | |
1118 | * | |
31d0247c | 1119 | * Returns: Zero on success, %$-1$% on error. |
67b5031e MW |
1120 | * |
1121 | * Use: Advance to the start of the next line, consuming the | |
1122 | * preceding newline. | |
1123 | * | |
1124 | * A syntax error is reported if no newline character is found, | |
1125 | * i.e., the file ends in mid-line. A syntax error is also | |
1126 | * reported if material other than whitespace or a comment is | |
1127 | * found before the end of the line end, and @TVFF_ALLOWANY@ is | |
1128 | * not set in @f@. The line number count is updated | |
1129 | * appropriately. | |
1130 | */ | |
1131 | ||
1132 | #define TVFF_ALLOWANY 1u | |
1133 | extern int tvec_flushtoeol(struct tvec_state */*tv*/, unsigned /*f*/); | |
1134 | ||
1135 | /* --- @tvec_nexttoken@ --- * | |
1136 | * | |
1137 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1138 | * | |
31d0247c | 1139 | * Returns: Zero if there is a next token which can be read; %$-1$% if no |
67b5031e MW |
1140 | * token is available. |
1141 | * | |
1142 | * Use: Advance to the next whitespace-separated token, which may be | |
1143 | * on the next line. | |
1144 | * | |
1145 | * Tokens are separated by non-newline whitespace, comments, and | |
1146 | * newlines followed by whitespace; a newline /not/ followed by | |
1147 | * whitespace instead begins the next assignment, and two | |
1148 | * newlines separated only by whitespace terminate the data for | |
1149 | * a test. | |
1150 | * | |
1151 | * If this function returns zero, then the next character in the | |
1152 | * file begins a suitable token which can be read and | |
31d0247c | 1153 | * processed. If it returns %$-1$% then there is no such token, |
67b5031e MW |
1154 | * and the file position is left correctly. The line number |
1155 | * count is updated appropriately. | |
1156 | */ | |
3efcfd2d | 1157 | |
67b5031e | 1158 | extern int tvec_nexttoken(struct tvec_state */*tv*/); |
3efcfd2d | 1159 | |
31d0247c | 1160 | /* --- @tvec_readword@, @tvec_readword_v@ --- * |
67b5031e MW |
1161 | * |
1162 | * Arguments: @struct tvec_state *tv@ = test-vector state | |
1163 | * @dstr *d@ = string to append the word to | |
adec5584 | 1164 | * @const char **p_inout@ = pointer into string, updated |
67b5031e MW |
1165 | * @const char *delims@ = additional delimiters to stop at |
1166 | * @const char *expect@, @va_list ap@ = what was expected | |
1167 | * | |
31d0247c | 1168 | * Returns: Zero on success, %$-1$% on failure. |
67b5031e MW |
1169 | * |
1170 | * Use: A `word' consists of characters other than whitespace, null | |
1171 | * characters, and other than those listed in @delims@; | |
1172 | * furthermore, a word does not begin with a `;'. (If you want | |
1173 | * reading to stop at comments not preceded by whitespace, then | |
1174 | * include `;' in @delims@. This is a common behaviour.) | |
1175 | * | |
1176 | * If there is no word beginning at the current file position, | |
31d0247c MW |
1177 | * then return %$-1$%; furthermore, if @expect@ is not null, |
1178 | * then report an appropriate error via @tvec_syntax@. | |
67b5031e MW |
1179 | * |
1180 | * Otherwise, the word is accumulated in @d@ and zero is | |
1181 | * returned; if @d@ was not empty at the start of the call, the | |
1182 | * newly read word is separated from the existing material by a | |
1183 | * single space character. Since null bytes are never valid | |
1184 | * word constituents, a null terminator is written to @d@, and | |
1185 | * it is safe to treat the string in @d@ as being null- | |
1186 | * terminated. | |
adec5584 MW |
1187 | * |
1188 | * If @p_inout@ is not null, then @*p_inout@ must be a pointer | |
1189 | * into @d->buf@, which will be adjusted so that it will | |
1190 | * continue to point at the same position even if the buffer is | |
1191 | * reallocated. As a subtle tweak, if @*p_inout@ initially | |
1192 | * points at the end of the buffer, then it will be adjusted to | |
1193 | * point at the beginning of the next word, rather than at the | |
1194 | * additional intervening space. | |
67b5031e | 1195 | */ |
3efcfd2d | 1196 | |
adec5584 | 1197 | extern PRINTF_LIKE(5, 6) |
31d0247c | 1198 | int tvec_readword(struct tvec_state */*tv*/, dstr */*d*/, |
adec5584 MW |
1199 | const char **/*p_inout*/, const char */*delims*/, |
1200 | const char */*expect*/, ...); | |
67b5031e | 1201 | extern int tvec_readword_v(struct tvec_state */*tv*/, dstr */*d*/, |
adec5584 MW |
1202 | const char **/*p_inout*/, const char */*delims*/, |
1203 | const char */*expect*/, va_list */*ap*/); | |
b64eb60f | 1204 | |
b64eb60f MW |
1205 | /*----- That's all, folks -------------------------------------------------*/ |
1206 | ||
1207 | #ifdef __cplusplus | |
1208 | } | |
1209 | #endif | |
1210 | ||
1211 | #endif |