[gamecode] Add automated tests for store ops

They even found a bug in the addressing mode functions :) (I'd forgotten
that I wanted signed offsets from the pointer and thus forgot to cast
st->b to short in order to get the sign extension)
This commit is contained in:
Bill Currie 2022-01-03 14:41:29 +09:00
parent 920c5fd99b
commit bf604b99b3
5 changed files with 326 additions and 9 deletions

View file

@ -1,3 +1,5 @@
include libs/gamecode/test/Makemodule.am
gc_deps=libs/util/libQFutil.la
noinst_LTLIBRARIES += libs/gamecode/libQFgamecode.la

View file

@ -1753,11 +1753,11 @@ pr_entity_mode (progs_t *pr, const dstatement_t *st, int shift)
mm_offs = OPA(uint);
break;
case 2:
// constant indexed pointer: *a + b
mm_offs = OPA(uint) + st->b;
// constant indexed pointer: *a + b (supports -ve offset)
mm_offs = OPA(uint) + (short) st->b;
break;
case 3:
// verible indexed pointer: *a + *b (supports -ve offset)
// variable indexed pointer: *a + *b (supports -ve offset)
mm_offs = OPA(uint) + OPB(int);
break;
}
@ -1782,11 +1782,11 @@ pr_address_mode (progs_t *pr, const dstatement_t *st, int shift)
mm_offs = OPA(uint);
break;
case 2:
// constant indexed pointer: *a + b
mm_offs = OPA(uint) + st->b;
// constant indexed pointer: *a + b (supports -ve offset)
mm_offs = OPA(uint) + (short) st->b;
break;
case 3:
// verible indexed pointer: *a + *b (supports -ve offset)
// variable indexed pointer: *a + *b (supports -ve offset)
mm_offs = OPA(uint) + OPB(int);
break;
}
@ -1811,11 +1811,11 @@ pr_jump_mode (progs_t *pr, const dstatement_t *st)
jump_offs = OPA(uint);
break;
case 2:
// constant indexed pointer: *a + b
jump_offs = OPA(uint) + st->b;
// constant indexed pointer: *a + b (supports -ve offset)
jump_offs = OPA(uint) + (short) st->b;
break;
case 3:
// verible indexed pointer: *a + *b (supports -ve offset)
// variable indexed pointer: *a + *b (supports -ve offset)
jump_offs = OPA(uint) + OPB(int);
break;
}

View file

@ -0,0 +1,17 @@
libs_gamecode_tests = \
libs/gamecode/test/test-store
TESTS += $(libs_gamecode_tests)
check_PROGRAMS += $(libs_gamecode_tests)
EXTRA_DIST += main.c
test_gamecode_libs= \
libs/gamecode/libQFgamecode.la \
libs/util/libQFutil.la
libs_gamecode_test_test_store_SOURCES= \
libs/gamecode/test/test-store.c
libs_gamecode_test_test_store_LDADD= $(test_gamecode_libs)
libs_gamecode_test_test_store_DEPENDENCIES= $(test_gamecode_libs)

166
libs/gamecode/test/main.c Normal file
View file

@ -0,0 +1,166 @@
#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "QF/va.h"
#define num_tests (sizeof (tests) / sizeof (tests[0]))
static int test_enabled[num_tests] = { 0 };
#include "getopt.h"
#include "QF/cmd.h"
#include "QF/cvar.h"
static bfunction_t test_functions[] = {
{}, // null function
{ .first_statement = 0 }
};
static dprograms_t test_progs = {
.version = PROG_VERSION,
};
static progs_t test_pr;
static jmp_buf jump_buffer;
static void
test_debug_handler (prdebug_t event, void *param, void *data)
{
switch (event) {
case prd_breakpoint:
if (verbose > 0) {
printf ("debug: %s\n", prdebug_names[event]);
}
longjmp (jump_buffer, 1);
case prd_subenter:
if (verbose > 0) {
printf ("debug: subenter %d\n", *(func_t *) param);
}
case prd_subexit:
break;
case prd_trace:
dstatement_t *st = test_pr.pr_statements + test_pr.pr_xstatement;
if (verbose > 0) {
printf ("debug: trace %05x %04x %04x %04x %04x\n",
test_pr.pr_xstatement, st->op, st->a, st->b, st->c);
}
break;
case prd_runerror:
printf ("debug: %s: %s\n", prdebug_names[event], (char *)param);
longjmp (jump_buffer, 3);
case prd_watchpoint:
case prd_begin:
case prd_terminate:
case prd_error:
case prd_none:
printf ("debug: unexpected:%s %p\n", prdebug_names[event], param);
longjmp (jump_buffer, 2);
}
}
static void
setup_test (test_t *test)
{
memset (&test_pr, 0, sizeof (test_pr));
test_pr.progs = &test_progs;
test_pr.debug_handler = test_debug_handler;
test_pr.pr_trace = 1;
test_pr.pr_trace_depth = -1;
test_pr.function_table = test_functions;
test_pr.globals_size = test->num_globals;
pr_uint_t num_globals = test->num_globals + test->extra_globals;
test_pr.pr_globals = malloc (num_globals * sizeof (pr_type_t));
memcpy (test_pr.pr_globals, test->init_globals,
test->num_globals * sizeof (pr_type_t));
memset (test_pr.pr_globals + test->num_globals, 0,
test->extra_globals * sizeof (pr_type_t));
test_pr.pr_statements
= malloc ((test->num_statements + 1) * sizeof (dstatement_t));
memcpy (test_pr.pr_statements, test->statements,
(test->num_statements + 1) * sizeof (dstatement_t));
test_pr.pr_statements[test->num_statements] =
(dstatement_t) { OP_BREAK, 0, 0, 0 };
}
static int
run_test (test_t *test)
{
int jump_ret;
int ret = 0;
setup_test (test);
if (!(jump_ret = setjmp (jump_buffer))) {
PR_ExecuteProgram (&test_pr, 1);
printf ("returned from progs\n");
}
if (jump_ret == 1) {
if (memcmp (test_pr.pr_globals, test->expect_globals,
test->num_globals * sizeof (pr_int_t)) == 0) {
ret = 1;
printf ("test #%zd: %s: OK\n", test - tests, test->desc);
} else {
printf ("test #%zd: %s: bytes differ\n", test - tests, test->desc);
}
} else {
printf ("test #%zd: %s: critical failure\n", test - tests, test->desc);
}
free (test_pr.pr_globals);
free (test_pr.pr_statements);
return ret;
}
int
main (int argc, char **argv)
{
int c;
size_t i, test;
int pass = 1;
Cmd_Init_Hash ();
Cvar_Init_Hash ();
Cmd_Init ();
Cvar_Init ();
PR_Init_Cvars ();
while ((c = getopt (argc, argv, "qvt:")) != EOF) {
switch (c) {
case 'q':
verbose--;
break;
case 'v':
verbose++;
break;
case 't':
test = atoi (optarg);
if (test < num_tests) {
test_enabled[test] = 1;
} else {
fprintf (stderr, "Bad test number (0 - %zd)\n", num_tests);
return 1;
}
break;
default:
fprintf (stderr, "-q (quiet) -v (verbose) and/or -t TEST "
"(test number)\n");
return 1;
}
}
for (i = 0; i < num_tests; i++)
if (test_enabled[i])
break;
if (i == num_tests) {
for (i = 0; i < num_tests; i++)
test_enabled[i] = 1;
}
for (i = 0; i < num_tests; i++) {
if (!test_enabled[i])
continue;
pass &= run_test (&tests[i]);
}
return !pass;
}

View file

@ -0,0 +1,132 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "QF/progs.h"
static int verbose = 0;
// both calculates the number of globals in the test, and ensures that both
// init and expect are the same size (will product a "void value not ignored"
// error if the sizes differ)
#define num_globals(init, expect) \
__builtin_choose_expr ( \
sizeof (init) == sizeof (expect), sizeof (init) / sizeof (init[0]), \
(void) 0\
)
// calculate the numver of statements in the test
#define num_statements(statements) \
(sizeof (statements) / sizeof (statements[0]))
typedef struct {
const char *desc;
pr_uint_t extra_globals;
pr_uint_t num_globals;
pr_uint_t num_statements;
dstatement_t *statements;
pr_int_t *init_globals;
pr_int_t *expect_globals;
} test_t;
static pr_int_t test_globals_init[] = {
// pointers
24, 26, 28, 29,
32, -4, -2, 0,
1, 4, 0xdeadbeef, 0xfeedf00d,
// source data
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
// destination data
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
};
static pr_int_t test_globals_expect[] = {
// pointers
24, 26, 28, 29,
32, -4, -2, 0,
1, 4, 0xdeadbeef, 0xfeedf00d,
// source data
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
// destination data
11, 12, 9, 10,
8, 5, 6, 7,
1, 2, 3, 4,
};
#define BASE(b, base) (((base) & 3) << OP_##b##_SHIFT)
#define OP(a, b, c, op) ((op) | BASE(A, a) | BASE(B, b) | BASE(C, c))
static dstatement_t store_A_statements[] = {
{OP(0, 0, 0, OP_STORE_A_4), 32, 0, 12},
{OP(0, 0, 0, OP_STORE_A_3), 29, 0, 16},
{OP(0, 0, 0, OP_STORE_A_1), 28, 0, 19},
{OP(0, 0, 0, OP_STORE_A_2), 26, 0, 20},
{OP(0, 0, 0, OP_STORE_A_2), 24, 0, 22},
};
static dstatement_t store_B_statements[] = {
{OP(0, 0, 0, OP_STORE_B_4), 4, 0, 12},
{OP(0, 0, 0, OP_STORE_B_3), 3, 0, 16},
{OP(0, 0, 0, OP_STORE_B_1), 2, 0, 19},
{OP(0, 0, 0, OP_STORE_B_2), 1, 0, 20},
{OP(0, 0, 0, OP_STORE_B_2), 0, 0, 22},
};
static dstatement_t store_C_statements[] = {
{OP(0, 0, 0, OP_STORE_C_4), 2, 4, 12},
{OP(0, 0, 0, OP_STORE_C_3), 2, 1, 16},
{OP(0, 0, 0, OP_STORE_C_1), 2, 0, 19},
{OP(0, 0, 0, OP_STORE_C_2), 2, -2, 20},
{OP(0, 0, 0, OP_STORE_C_2), 2, -4, 22},
};
static dstatement_t store_D_statements[] = {
{OP(0, 0, 0, OP_STORE_D_4), 2, 9, 12},
{OP(0, 0, 0, OP_STORE_D_3), 2, 8, 16},
{OP(0, 0, 0, OP_STORE_D_1), 2, 7, 19},
{OP(0, 0, 0, OP_STORE_D_2), 2, 6, 20},
{OP(0, 0, 0, OP_STORE_D_2), 2, 5, 22},
};
test_t tests[] = {
{
.desc = "store A",
.num_globals = num_globals (test_globals_init, test_globals_expect),
.num_statements = num_statements (store_A_statements),
.statements = store_A_statements,
.init_globals = test_globals_init,
.expect_globals = test_globals_expect,
},
{
.desc = "store B",
.num_globals = num_globals (test_globals_init, test_globals_expect),
.num_statements = num_statements (store_B_statements),
.statements = store_B_statements,
.init_globals = test_globals_init,
.expect_globals = test_globals_expect,
},
{
.desc = "store C",
.num_globals = num_globals (test_globals_init, test_globals_expect),
.num_statements = num_statements (store_C_statements),
.statements = store_C_statements,
.init_globals = test_globals_init,
.expect_globals = test_globals_expect,
},
{
.desc = "store D",
.num_globals = num_globals (test_globals_init, test_globals_expect),
.num_statements = num_statements (store_D_statements),
.statements = store_D_statements,
.init_globals = test_globals_init,
.expect_globals = test_globals_expect,
},
};
#include "main.c"