3 * Test vector processing framework
5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
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.
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.
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,
35 /*----- Header files ------------------------------------------------------*/
46 #ifndef MLIB_CONTROL_H
58 /*----- Miscellaneous values ----------------------------------------------*/
60 /* These are attached to structures which represent extension points, as a
61 * way to pass an opaque parameter to whatever things are hooked onto them.
64 #define TVEC_MISCSLOTS(_) \
65 _(PTR, const void *, p) /* arbitrary pointer */ \
66 _(INT, long, i) /* signed integer */ \
67 _(UINT, unsigned long, u) /* signed integer */
70 #define TVEC_DEFSLOT(tag, ty, slot) ty slot;
71 TVEC_MISCSLOTS(TVEC_DEFSLOT
)
75 #define TVEC_DEFCONST(tag, ty, slot) TVMISC_##tag,
76 TVEC_MISCSLOTS(TVEC_DEFCONST
)
80 /*----- Register values ---------------------------------------------------*/
82 /* The framework doesn't have a preconceived idea about what's in a register
83 * value: it just allocates them and accesses them through the register type
84 * functions. It doesn't even have a baked-in idea of how big a register
85 * value is: instead, it gets that via the `regsz' slot in `struct
86 * tvec_testinfo'. So, as far as the framework is concerned, it's safe to
87 * add new slots to this union, even if they make the overall union larger.
88 * This can be done by defining the preprocessor macro `TVEC_REGSLOTS' to be
89 * a `union' fragment defining any additional union members.
91 * This creates a distinction between code which does and doesn't know the
92 * size of a register value. Code which does, which typically means the test
93 * functions, benchmarking setup and teardown functions, and tightly-bound
94 * runner functions, is free to index the register vectors directly. Code
95 * which doesn't, which means the framework core itself and output formatting
96 * machinery, must use the `TVEC_REG' macro (or its more general `TVEC_GREG'
97 * companion) for indexing register vectors. (In principle, register type
98 * handlers also fit into this category, but they have no business peering
99 * into register values other than the one's they're given.)
103 /* The actual register value. This is what the type handler sees.
104 * Additional members can be added by setting `TVEC_REGSLOTS' before
105 * including this file.
107 * A register value can be /initialized/, which simply means that its
108 * contents represent a valid value according to its type -- the
109 * register can be compared, dumped, serialized, parsed into, etc.
110 * You can't do anything safely to an uninitialized register value
111 * other than initialize it.
114 long i
; /* signed integer */
115 unsigned long u
; /* unsigned integer */
116 void *p
; /* pointer */
117 struct { unsigned char *p
; size_t sz
; } bytes
; /* binary string of bytes */
118 struct { char *p
; size_t sz
; } str
; /* text string */
127 * Note that all of the registers listed as being used by a
128 * particular test group are initialized at all times[1] while that
129 * test group is being processed. (The other register slots don't
130 * even have types associated with them, so there's nothing useful we
131 * could do with them.)
133 * The `TVRF_LIVE' flag indicates that the register was assigned a
134 * value by the test vector file: it's the right thing to use to
135 * check whether an optional register is actually present. Even
136 * `dead' registers are still initialized, though.
138 * [1] This isn't quite true. Between individual tests, the
139 * registers are released and reinitialized in order to reset
140 * them to known values ready for the next test. But you won't
141 * see them at this point.
144 unsigned f
; /* flags */
145 #define TVRF_LIVE 1u /* used in current test */
146 union tvec_regval v
; /* register value */
150 /* A register definition. Register definitions list the registers
151 * which are used by a particular test group (see `struct tvec_test'
154 * A vector of register definitions is terminated by a definition
155 * whose `name' slot is null.
158 const char *name
; /* register name (for input files) */
159 unsigned i
; /* register index */
160 const struct tvec_regty
*ty
; /* register type descriptor */
161 unsigned f
; /* flags */
162 #define TVRF_OPT 1u /* optional register */
163 #define TVRF_ID 2u /* part of test identity */
164 union tvec_misc arg
; /* extra detail for the type */
167 extern int tvec_serialize(const struct tvec_reg */
*rv*/
,
168 const struct tvec_regdef */
*regs*/
,
169 unsigned /*nr*/, size_t /*regsz*/,
170 void **/
*p_out*/
, size_t */
*sz_out*/
);
172 extern int tvec_deserialize(struct tvec_reg */
*rv*/
,
173 const struct tvec_regdef */
*regs*/
,
174 unsigned /*nr*/, size_t /*regsz*/,
175 const void */
*p*/
, size_t /*sz*/);
177 /*----- Test state --------------------------------------------------------*/
179 enum { TVOUT_LOSE
, TVOUT_SKIP
, TVOUT_WIN
, TVOUT_LIMIT
};
182 unsigned f
; /* flags */
183 #define TVSF_SKIP 1u /* skip this test group */
184 #define TVSF_OPEN 2u /* test is open */
185 #define TVSF_ACTIVE 4u /* test is active */
186 #define TVSF_OUTMASK 0xf0 /* test outcome */
187 #define TVSF_OUTSHIFT 4
188 unsigned nrout
, nreg
; /* number of output/total registers */
189 size_t regsz
; /* size of register entry */
190 struct tvec_reg
*in
, *out
; /* register vectors */
191 char expst
, st
; /* progress status codes */
192 const struct tvec_test
*tests
, *test
; /* all tests and current test */
193 unsigned curr
[TVOUT_LIMIT
], all
[TVOUT_LIMIT
], grps
[TVOUT_LIMIT
];
194 struct tvec_output
*output
; /* output formatter */
195 const char *infile
; unsigned lno
, test_lno
; /* input file name, line */
196 FILE *fp
; /* input file stream */
199 #define TVEC_GREG(vec, i, regsz) \
200 ((struct tvec_reg *)((unsigned char *)(vec) + (i)*(regsz)))
201 #define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->regsz)
203 /*----- Test descriptions -------------------------------------------------*/
205 typedef void tvec_hookfn(struct tvec_state */
*tv*/
);
206 typedef void tvec_testfn(const struct tvec_reg */
*in*/
,
207 struct tvec_reg */
*out*/
,
211 const char *name
; /* name of the test */
212 const struct tvec_regdef
*regs
; /* descriptions of the registers */
213 tvec_hookfn
*preflight
; /* check before starting */
214 tvec_hookfn
*run
; /* test runner */
215 tvec_testfn
*fn
; /* test function */
216 union tvec_misc arg
; /* additional parameter to `run' */
219 extern void PRINTF_LIKE(2, 3)
220 tvec_check(struct tvec_state */
*tv*/
, const char */
*detail*/
, ...);
221 extern void tvec_check_v(struct tvec_state */
*tv*/
,
222 const char */
*detail*/
, va_list */
*ap*/
);
224 extern void tvec_runtest(struct tvec_state */
*tv*/
);
226 /*----- Input utilities ---------------------------------------------------*/
228 extern void tvec_skipspc(struct tvec_state */
*tv*/
);
230 #define TVFF_ALLOWANY 1u
231 extern void tvec_flushtoeol(struct tvec_state */
*tv*/
, unsigned /*f*/);
233 extern int PRINTF_LIKE(4, 5)
234 tvec_readword(struct tvec_state */
*tv*/
, dstr */
*d*/
,
235 const char */
*delims*/
, const char */
*expect*/
, ...);
236 extern int tvec_readword_v(struct tvec_state */
*tv*/
, dstr */
*d*/
,
237 const char */
*delims*/
, const char */
*expect*/
,
240 extern int tvec_nexttoken(struct tvec_state */
*tv*/
);
242 /*----- Session lifecycle -------------------------------------------------*/
245 const struct tvec_test
*tests
;
246 unsigned nrout
, nreg
;
250 extern void tvec_begin(struct tvec_state */
*tv_out*/
,
251 const struct tvec_info */
*info*/
,
252 struct tvec_output */
*o*/
);
253 extern int tvec_end(struct tvec_state */
*tv*/
);
255 extern void tvec_read(struct tvec_state */
*tv*/
,
256 const char */
*infile*/
, FILE */
*fp*/
);
259 /*----- Benchmarking ------------------------------------------------------*/
262 unsigned long niter
; /* iterations done per unit */
263 int riter
, rbuf
; /* iterations and buffer registers */
264 size_t ctxsz
; /* size of context */
265 int (*setup
)(const struct tvec_reg */
*in*/
, struct tvec_reg */
*out*/
,
266 const union tvec_misc */
*arg*/
, void */
*ctx*/
); /* setup fn */
267 void (*teardown
)(void */
*ctx*/
); /* teardown function, or null */
268 struct bench_state
**b
; /* benchmark state anchor or null */
269 union tvec_misc arg
; /* argument to setup */
272 extern struct bench_state
*tvec_benchstate
;
274 extern int tvec_ensurebench(struct tvec_state */
*tv*/
,
275 struct bench_state
**/
*b_out*/
);
276 extern void tvec_bench(struct tvec_state */
*tv*/
);
278 /*----- Ad-hoc testing ----------------------------------------------------*/
280 extern void tvec_adhoc(struct tvec_state */
*tv*/
, struct tvec_test */
*t*/
);
282 extern void tvec_begingroup(struct tvec_state */
*tv*/
, const char */
*name*/
,
283 const char */
*file*/
, unsigned /*lno*/);
284 extern void tvec_reportgroup(struct tvec_state */
*tv*/
);
285 extern void tvec_endgroup(struct tvec_state */
*tv*/
);
287 #define TVEC_BEGINGROUP(tv, name) \
288 do tvec_begingroup(tv, name, __FILE__, __LINE__); while (0)
290 #define TVEC_TESTGROUP(tag, tv, name) \
291 MC_WRAP(tag##__around, \
292 { TVEC_BEGINGROUP(tv, name); }, \
293 { tvec_endgroup(tv); }, \
294 { if (!((tv)->f&TVSF_SKIP)) tvec_skipgroup(tv, 0); \
295 tvec_endgroup(tv); })
297 extern void tvec_begintest(struct tvec_state */
*tv*/
,
298 const char */
*file*/
, unsigned /*lno*/);
299 extern void tvec_endtest(struct tvec_state */
*tv*/
);
301 #define TVEC_BEGINTEST(tv) \
302 do tvec_begintest(tv, __FILE__, __LINE__); while (0)
304 #define TVEC_TEST(tag, tv) \
305 MC_WRAP(tag##__around, \
306 { TVEC_BEGINTEST(tv); }, \
307 { tvec_endtest(tv); }, \
308 { if ((tv)->f&TVSF_ACTIVE) tvec_skipgroup((tv), 0); \
311 extern int PRINTF_LIKE(5, 6)
312 tvec_claim(struct tvec_state */
*tv*/
, int /*ok*/,
313 const char */
*file*/
, unsigned /*lno*/,
314 const char */
*expr*/
, ...);
316 #define TVEC_CLAIM(tv, cond) \
317 (tvec_claim(tv, !!(cond), __FILE__, __LINE__, #cond " untrue"))
319 extern int tvec_claimeq(struct tvec_state */
*tv*/
,
320 const struct tvec_regty */
*ty*/
,
321 const union tvec_misc */
*arg*/
,
322 const char */
*file*/
, unsigned /*lno*/,
323 const char */
*expr*/
);
325 /*----- Command-line interface --------------------------------------------*/
327 extern const struct tvec_info tvec_adhocinfo
;
329 extern void tvec_parseargs(int /*argc*/, char */
*argv*/
[],
330 struct tvec_state */
*tv_out*/
,
332 const struct tvec_info */
*info*/
);
334 extern void tvec_readstdin(struct tvec_state */
*tv*/
);
335 extern void tvec_readfile(struct tvec_state */
*tv*/
, const char */
*file*/
);
336 extern void tvec_readdflt(struct tvec_state */
*tv*/
, const char */
*file*/
);
337 extern void tvec_readarg(struct tvec_state */
*tv*/
, const char */
*arg*/
);
339 extern void tvec_readargs(int /*argc*/, char */
*argv*/
[],
340 struct tvec_state */
*tv*/
,
341 int */
*argpos_inout*/
, const char */
*dflt*/
);
343 extern int tvec_main(int /*argc*/, char */
*argv*/
[],
344 const struct tvec_info */
*info*/
,
345 const char */
*dflt*/
);
347 /*----- Output formatting -------------------------------------------------*/
350 const struct tvec_outops
*ops
;
351 struct tvec_state
*tv
;
357 void (*error
)(struct tvec_output */
*o*/
,
358 const char */
*msg*/
, va_list */
*ap*/
);
359 void (*notice
)(struct tvec_output */
*o*/
,
360 const char */
*msg*/
, va_list */
*ap*/
);
361 void (*write
)(struct tvec_output */
*o*/
, const char */
*p*/
, size_t /*sz*/);
363 void (*bsession
)(struct tvec_output */
*o*/
);
364 int (*esession
)(struct tvec_output */
*o*/
);
366 void (*bgroup
)(struct tvec_output */
*o*/
);
367 void (*egroup
)(struct tvec_output */
*o*/
, unsigned /*outcome*/);
368 void (*skipgroup
)(struct tvec_output */
*o*/
,
369 const char */
*excuse*/
, va_list */
*ap*/
);
371 void (*btest
)(struct tvec_output */
*o*/
);
372 void (*skip
)(struct tvec_output */
*o*/
,
373 const char */
*excuse*/
, va_list */
*ap*/
);
374 void (*fail
)(struct tvec_output */
*o*/
,
375 const char */
*detail*/
, va_list */
*ap*/
);
376 void (*mismatch
)(struct tvec_output */
*o*/
);
377 void (*etest
)(struct tvec_output */
*o*/
, unsigned /*outcome*/);
379 void (*bbench
)(struct tvec_output */
*o*/
);
380 void (*ebench
)(struct tvec_output */
*o*/
,
381 const struct bench_timing */
*tm*/
);
383 void (*destroy
)(struct tvec_output */
*o*/
);
386 extern void PRINTF_LIKE(2, 3) NORETURN
387 tvec_error(struct tvec_state */
*tv*/
, const char */
*msg*/
, ...);
388 extern void NORETURN
tvec_error_v(struct tvec_state */
*tv*/
,
389 const char */
*msg*/
, va_list */
*ap*/
);
391 extern void PRINTF_LIKE(2, 3)
392 tvec_notice(struct tvec_state */
*tv*/
, const char */
*msg*/
, ...);
393 extern void tvec_notice_v(struct tvec_state */
*tv*/
,
394 const char */
*msg*/
, va_list */
*ap*/
);
396 extern void PRINTF_LIKE(3, 4) NORETURN
397 tvec_syntax(struct tvec_state */
*tv*/
, int /*ch*/,
398 const char */
*expect*/
, ...);
399 extern void NORETURN
tvec_syntax_v(struct tvec_state */
*tv*/
, int /*ch*/,
400 const char */
*expect*/
, va_list */
*ap*/
);
402 extern void PRINTF_LIKE(2, 3)
403 tvec_skipgroup(struct tvec_state */
*tv*/
, const char */
*note*/
, ...);
404 extern void tvec_skipgroup_v(struct tvec_state */
*tv*/
,
405 const char */
*note*/
, va_list */
*ap*/
);
407 extern void PRINTF_LIKE(2, 3)
408 tvec_skip(struct tvec_state */
*tv*/
, const char */
*excuse*/
, ...);
409 extern void tvec_skip_v(struct tvec_state */
*tv*/
,
410 const char */
*excuse*/
, va_list */
*ap*/
);
412 extern void PRINTF_LIKE(2, 3)
413 tvec_fail(struct tvec_state */
*tv*/
, const char */
*detail*/
, ...);
414 extern void tvec_fail_v(struct tvec_state */
*tv*/
,
415 const char */
*detail*/
, va_list */
*ap*/
);
417 extern void tvec_mismatch(struct tvec_state */
*tv*/
);
419 extern void PRINTF_LIKE(2, 3)
420 tvec_write(struct tvec_state */
*tv*/
, const char */
*p*/
, ...);
421 extern void tvec_write_v(struct tvec_state */
*tv*/
,
422 const char */
*p*/
, va_list */
*ap*/
);
424 extern struct tvec_output
*tvec_humanoutput(FILE */
*fp*/
);
425 extern struct tvec_output
*tvec_tapoutput(FILE */
*fp*/
);
426 extern struct tvec_output
*tvec_dfltout(FILE */
*fp*/
);
428 /*----- Register types ----------------------------------------------------*/
431 void (*init
)(union tvec_regval */
*rv*/
, const struct tvec_regdef */
*rd*/
);
432 void (*release
)(union tvec_regval */
*rv*/
,
433 const struct tvec_regdef */
*rd*/
);
434 int (*eq
)(const union tvec_regval */
*rv0*/
,
435 const union tvec_regval */
*rv1*/
,
436 const struct tvec_regdef */
*rd*/
);
437 size_t (*measure
)(const union tvec_regval */
*rv*/
,
438 const struct tvec_regdef */
*rd*/
);
439 int (*tobuf
)(buf */
*b*/
, const union tvec_regval */
*rv*/
,
440 const struct tvec_regdef */
*rd*/
);
441 int (*frombuf
)(buf */
*b*/
, union tvec_regval */
*rv*/
,
442 const struct tvec_regdef */
*rd*/
);
443 void (*parse
)(union tvec_regval */
*rv*/
, const struct tvec_regdef */
*rd*/
,
444 struct tvec_state */
*tv*/
);
445 void (*dump
)(const union tvec_regval */
*rv*/
,
446 const struct tvec_regdef */
*rd*/
,
447 struct tvec_state */
*tv*/
, unsigned /*style*/);
448 #define TVSF_COMPACT 1u
451 extern const struct tvec_regty tvty_int
, tvty_uint
;
452 struct tvec_irange
{ long min
, max
; };
453 struct tvec_urange
{ unsigned long min
, max
; };
455 extern const struct tvec_irange
456 tvrange_schar
, tvrange_short
, tvrange_int
, tvrange_long
,
457 tvrange_sbyte
, tvrange_i16
, tvrange_i32
;
458 extern const struct tvec_urange
459 tvrange_uchar
, tvrange_ushort
, tvrange_uint
, tvrange_ulong
, tvrange_size
,
460 tvrange_byte
, tvrange_u16
, tvrange_u32
;
462 extern int tvec_claimeq_int(struct tvec_state */
*tv*/
,
463 long /*i0*/, long /*i1*/,
464 const char */
*file*/
, unsigned /*lno*/,
465 const char */
*expr*/
);
466 extern int tvec_claimeq_uint(struct tvec_state */
*tv*/
,
467 unsigned long /*u0*/, unsigned long /*u1*/,
468 const char */
*file*/
, unsigned /*lno*/,
469 const char */
*expr*/
);
470 #define TVEC_CLAIMEQ_INT(tv, i0, i1) \
471 (tvec_claimeq_int(tv, i0, i1, __FILE__, __LINE__, #i0 " /= " #i1))
472 #define TVEC_CLAIMEQ_UINT(tv, u0, u1) \
473 (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1))
475 extern const struct tvec_regty tvty_enum
;
477 #define DEFASSOC(tag_, ty, slot) \
478 struct tvec_##slot##assoc { const char *tag; ty slot; };
479 TVEC_MISCSLOTS(DEFASSOC
)
482 struct tvec_enuminfo
{
483 const char *name
; unsigned mv
;
485 #define DEFENUMINFO(tag, ty, slot) struct { \
486 const struct tvec_##slot##assoc *av; \
489 #define RANGESLOT_INT const struct tvec_irange *ir;
490 #define RANGESLOT_UINT const struct tvec_urange *ur;
491 #define RANGESLOT_PTR
492 TVEC_MISCSLOTS(DEFENUMINFO
)
495 #undef RANGESLOT_UINT
500 #define DECLCLAIM(tag, ty, slot) \
501 extern int tvec_claimeq_##slot##enum \
502 (struct tvec_state */*tv*/, \
503 const struct tvec_enuminfo */*ei*/, ty /*e0*/, ty /*e1*/, \
504 const char */*file*/, unsigned /*lno*/, const char */*expr*/);
505 TVEC_MISCSLOTS(DECLCLAIM
)
507 #define TVEC_CLAIMEQ_IENUM(tv, ei, e0, e1) \
508 (tvec_claimeq_ienum(tv, ei, e0, e1, \
509 __FILE__, __LINE__, #e0 " /= " #e1))
510 #define TVEC_CLAIMEQ_UENUM(tv, ei, e0, e1) \
511 (tvec_claimeq_uenum(tv, ei, e0, e1, \
512 __FILE__, __LINE__, #e0 " /= " #e1))
513 #define TVEC_CLAIMEQ_PENUM(tv, ei, e0, e1) \
514 (tvec_claimeq_penum(tv, ei, e0, e1, \
515 __FILE__, __LINE__, #e0 " /= " #e1))
517 extern const struct tvec_regty tvty_flags
;
518 struct tvec_flag
{ const char *tag
; unsigned long m
, v
; };
519 struct tvec_flaginfo
{
521 const struct tvec_flag
*fv
;
522 const struct tvec_urange
*range
;
525 extern int tvec_claimeq_flags(struct tvec_state */
*tv*/
,
526 const struct tvec_flaginfo */
*fi*/
,
527 unsigned long /*f0*/, unsigned long /*f1*/,
528 const char */
*file*/
, unsigned /*lno*/,
529 const char */
*expr*/
);
530 #define TVEC_CLAIMEQ_FLAGS(tv, fi, f0, f1) \
531 (tvec_claimeq_flags(tv, fi, f0, f1, \
532 __FILE__, __LINE__, #f0 " /= " #f1))
534 extern const struct tvec_regty tvty_string
, tvty_bytes
;
536 extern int tvec_claimeq_string(struct tvec_state */
*tv*/
,
537 const char */
*p0*/
, size_t /*sz0*/,
538 const char */
*p1*/
, size_t /*sz1*/,
539 const char */
*file*/
, unsigned /*lno*/,
540 const char */
*expr*/
);
541 extern int tvec_claimeq_strz(struct tvec_state */
*tv*/
,
542 const char */
*p0*/
, const char */
*p1*/
,
543 const char */
*file*/
, unsigned /*lno*/,
544 const char */
*expr*/
);
545 extern int tvec_claimeq_bytes(struct tvec_state */
*tv*/
,
546 const void */
*p0*/
, size_t /*sz0*/,
547 const void */
*p1*/
, size_t /*sz1*/,
548 const char */
*file*/
, unsigned /*lno*/,
549 const char */
*expr*/
);
551 #define TVEC_CLAIMEQ_STRING(tv, p0, sz0, p1, sz1) \
552 (tvec_claimeq_string(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \
553 #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]"))
554 #define TVEC_CLAIMEQ_STRZ(tv, p0, p1) \
555 (tvec_claimeq_strz(tv, p0, p1, __FILE__, __LINE__, #p0 " /= " #p1))
556 #define TVEC_CLAIMEQ_BYTES(tv, p0, sz0, p1, sz1) \
557 (tvec_claimeq(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \
558 #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]"))
560 extern const struct tvec_regty tvty_buffer
;
562 extern void tvec_allocstring(union tvec_regval */
*rv*/
, size_t /*sz*/);
563 extern void tvec_allocbytes(union tvec_regval */
*rv*/
, size_t /*sz*/);
565 /*----- That's all, folks -------------------------------------------------*/