mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-03-22 02:11:19 +00:00
[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:
parent
920c5fd99b
commit
bf604b99b3
5 changed files with 326 additions and 9 deletions
|
@ -1,3 +1,5 @@
|
|||
include libs/gamecode/test/Makemodule.am
|
||||
|
||||
gc_deps=libs/util/libQFutil.la
|
||||
|
||||
noinst_LTLIBRARIES += libs/gamecode/libQFgamecode.la
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
17
libs/gamecode/test/Makemodule.am
Normal file
17
libs/gamecode/test/Makemodule.am
Normal 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
166
libs/gamecode/test/main.c
Normal 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;
|
||||
}
|
132
libs/gamecode/test/test-store.c
Normal file
132
libs/gamecode/test/test-store.c
Normal 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"
|
Loading…
Reference in a new issue