133 lines
4.2 KiB
C
133 lines
4.2 KiB
C
|
|
/**
|
||
|
|
* @file interop_test.c
|
||
|
|
* @brief Round-trip test for the galaga boundary, hoststruct.c-style.
|
||
|
|
*
|
||
|
|
* Links the real script.c and the real galaga.bas -- not copies -- so this
|
||
|
|
* fails the moment the boundary and the script disagree. The four claims it
|
||
|
|
* pins:
|
||
|
|
*
|
||
|
|
* 1. The script writes the engine's actor memory: a formation enemy's sway
|
||
|
|
* lands in akgl_Actor.x with no marshalling step.
|
||
|
|
* 2. The outbox works: a diving enemy above the player raises FIRE# and the
|
||
|
|
* C side reads it.
|
||
|
|
* 3. The boss flips actor state bit 13 at one hit point -- the boundary
|
||
|
|
* crossed engine-ward.
|
||
|
|
* 4. Sustained calling holds: 24000 calls through the per-call
|
||
|
|
* akbasic_environment_zero() regime, the load a 40-enemy wave puts on
|
||
|
|
* the runtime in ten seconds.
|
||
|
|
*
|
||
|
|
* Exit status equals the number of failed claims.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <math.h>
|
||
|
|
#include <stdbool.h>
|
||
|
|
#include <stddef.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
#include <akerror.h>
|
||
|
|
|
||
|
|
#include <akgl/actor.h>
|
||
|
|
|
||
|
|
#include "galaga.h"
|
||
|
|
|
||
|
|
#ifndef GALAGA_SCRIPT_PATH
|
||
|
|
#define GALAGA_SCRIPT_PATH "galaga.bas"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* script.c reads these; main.c usually defines them. This test is the host. */
|
||
|
|
galaga_Game galaga_game;
|
||
|
|
galaga_Shared galaga_shared;
|
||
|
|
|
||
|
|
static int FAILURES = 0;
|
||
|
|
|
||
|
|
#define CLAIM(__cond, __text) \
|
||
|
|
if ( !(__cond) ) { \
|
||
|
|
fprintf(stderr, "FAILED: %s\n", __text); \
|
||
|
|
FAILURES += 1; \
|
||
|
|
} else { \
|
||
|
|
printf("ok: %s\n", __text); \
|
||
|
|
}
|
||
|
|
|
||
|
|
static akerr_ErrorContext *run_claims(void)
|
||
|
|
{
|
||
|
|
galaga_Enemy enemy;
|
||
|
|
akgl_Actor actor;
|
||
|
|
int i = 0;
|
||
|
|
PREPARE_ERROR(errctx);
|
||
|
|
|
||
|
|
PASS(errctx, galaga_script_boot((char *)GALAGA_SCRIPT_PATH));
|
||
|
|
|
||
|
|
/* --- 1: formation sway lands in the actor ---------------------------- */
|
||
|
|
memset(&enemy, 0, sizeof(enemy));
|
||
|
|
memset(&actor, 0, sizeof(actor));
|
||
|
|
enemy.kind = GALAGA_ENEMY_BEE;
|
||
|
|
enemy.state = GALAGA_ES_FORMATION;
|
||
|
|
enemy.homex = 400.0f;
|
||
|
|
enemy.homey = 300.0f;
|
||
|
|
enemy.hp = 1;
|
||
|
|
actor.x = 0.0f;
|
||
|
|
actor.y = 0.0f;
|
||
|
|
PASS(errctx, galaga_script_update_enemy(&enemy, &actor, 0.016f));
|
||
|
|
CLAIM((fabsf(actor.x - enemy.homex) <= 16.5f) && (actor.y == enemy.homey),
|
||
|
|
"a formation bee's sway is written into akgl_Actor.x/y by the script");
|
||
|
|
|
||
|
|
/* --- 2: the fire outbox ---------------------------------------------- */
|
||
|
|
memset(&enemy, 0, sizeof(enemy));
|
||
|
|
memset(&actor, 0, sizeof(actor));
|
||
|
|
enemy.kind = GALAGA_ENEMY_BEE;
|
||
|
|
enemy.state = GALAGA_ES_DIVING;
|
||
|
|
enemy.rnd = 0.0f; /* 0.0 < DT% * 1.5: always willing */
|
||
|
|
actor.x = 600.0f;
|
||
|
|
actor.y = 200.0f;
|
||
|
|
galaga_shared.playerx = 610.0f; /* just off the shot's column */
|
||
|
|
galaga_shared.playery = 860.0f; /* well below */
|
||
|
|
PASS(errctx, galaga_script_update_enemy(&enemy, &actor, 0.016f));
|
||
|
|
CLAIM(enemy.fire == 1,
|
||
|
|
"a diving bee above the player raises FIRE# for the engine to consume");
|
||
|
|
|
||
|
|
/* --- 3: the boss's hurt bit ------------------------------------------ */
|
||
|
|
memset(&enemy, 0, sizeof(enemy));
|
||
|
|
memset(&actor, 0, sizeof(actor));
|
||
|
|
enemy.kind = GALAGA_ENEMY_BOSS;
|
||
|
|
enemy.state = GALAGA_ES_FORMATION;
|
||
|
|
enemy.homex = 500.0f;
|
||
|
|
enemy.homey = 120.0f;
|
||
|
|
enemy.hp = 1;
|
||
|
|
actor.state = AKGL_ACTOR_STATE_ALIVE;
|
||
|
|
PASS(errctx, galaga_script_update_enemy(&enemy, &actor, 0.016f));
|
||
|
|
CLAIM((actor.state & AKGL_ACTOR_STATE_UNDEFINED_13) != 0,
|
||
|
|
"a boss at one hit point raises actor state bit 13 from BASIC");
|
||
|
|
|
||
|
|
/* --- 4: sustained calling --------------------------------------------- */
|
||
|
|
memset(&enemy, 0, sizeof(enemy));
|
||
|
|
memset(&actor, 0, sizeof(actor));
|
||
|
|
enemy.kind = GALAGA_ENEMY_BEE;
|
||
|
|
enemy.state = GALAGA_ES_FORMATION;
|
||
|
|
enemy.homex = 400.0f;
|
||
|
|
enemy.homey = 300.0f;
|
||
|
|
for ( i = 0; i < 24000; i++ ) {
|
||
|
|
enemy.rnd = 0.9f; /* never dive: keep the state put */
|
||
|
|
PASS(errctx, galaga_script_update_enemy(&enemy, &actor, 0.016f));
|
||
|
|
}
|
||
|
|
CLAIM(galaga_game.script_errors == 0,
|
||
|
|
"24000 calls survive the per-call akbasic_environment_zero() regime");
|
||
|
|
SUCCEED_RETURN(errctx);
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void)
|
||
|
|
{
|
||
|
|
PREPARE_ERROR(errctx);
|
||
|
|
|
||
|
|
ATTEMPT {
|
||
|
|
CATCH(errctx, run_claims());
|
||
|
|
} CLEANUP {
|
||
|
|
} PROCESS(errctx) {
|
||
|
|
} HANDLE_DEFAULT(errctx) {
|
||
|
|
LOG_ERROR_WITH_MESSAGE(errctx, "the interop test could not run");
|
||
|
|
FAILURES += 1;
|
||
|
|
} FINISH_NORETURN(errctx);
|
||
|
|
|
||
|
|
return FAILURES;
|
||
|
|
}
|