101 lines
4.5 KiB
C
101 lines
4.5 KiB
C
|
|
/**
|
||
|
|
* @file environment_scope.c
|
||
|
|
* @brief Tests scoping, label creation, the wait mechanism and pool release.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "harness.h"
|
||
|
|
|
||
|
|
int main(void)
|
||
|
|
{
|
||
|
|
akbasic_Environment *root = NULL;
|
||
|
|
akbasic_Environment *child = NULL;
|
||
|
|
akbasic_Variable *variable = NULL;
|
||
|
|
akbasic_Variable *again = NULL;
|
||
|
|
akbasic_Value *value = NULL;
|
||
|
|
int64_t lineno = 0;
|
||
|
|
int i = 0;
|
||
|
|
|
||
|
|
TEST_REQUIRE_OK(harness_start(NULL));
|
||
|
|
root = HARNESS_RUNTIME.environment;
|
||
|
|
|
||
|
|
/* The active environment auto-creates on a miss. */
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_get(root, "A#", &variable));
|
||
|
|
TEST_REQUIRE(variable != NULL, "the active environment must auto-create a variable");
|
||
|
|
TEST_REQUIRE_INT(variable->valuetype, AKBASIC_TYPE_INTEGER);
|
||
|
|
|
||
|
|
/* And the second lookup finds the same one. */
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_get(root, "A#", &again));
|
||
|
|
TEST_REQUIRE(again == variable, "a repeat lookup must find the same variable");
|
||
|
|
|
||
|
|
/* A child sees its parent's variables. */
|
||
|
|
TEST_REQUIRE_OK(akbasic_runtime_new_environment(&HARNESS_RUNTIME));
|
||
|
|
child = HARNESS_RUNTIME.environment;
|
||
|
|
TEST_REQUIRE(child->parent == root, "the child must chain to its parent");
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_get(child, "A#", &again));
|
||
|
|
TEST_REQUIRE(again == variable, "the child must see the parent's variable");
|
||
|
|
|
||
|
|
/*
|
||
|
|
* A parent does not create variables on behalf of a child: looking up a
|
||
|
|
* missing name in a non-active environment yields NULL without error.
|
||
|
|
*/
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_get(root, "ZZ#", &again));
|
||
|
|
TEST_REQUIRE(again == NULL, "a non-active environment must not auto-create");
|
||
|
|
|
||
|
|
/* Labels are created only at the top level, and are visible from below. */
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_set_label(child, "LOOPTOP", 30));
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_get_label(child, "LOOPTOP", &lineno));
|
||
|
|
TEST_REQUIRE_INT(lineno, 30);
|
||
|
|
TEST_REQUIRE_STATUS(akbasic_environment_get_label(child, "MISSING", &lineno),
|
||
|
|
AKBASIC_ERR_UNDEFINED);
|
||
|
|
|
||
|
|
/* Popping releases the environment back to the pool. */
|
||
|
|
TEST_REQUIRE_OK(akbasic_runtime_prev_environment(&HARNESS_RUNTIME));
|
||
|
|
TEST_REQUIRE(HARNESS_RUNTIME.environment == root, "pop must restore the parent");
|
||
|
|
TEST_REQUIRE(!child->used, "pop must release the environment to the pool");
|
||
|
|
|
||
|
|
/* The root has no parent to pop to. */
|
||
|
|
TEST_REQUIRE_STATUS(akbasic_runtime_prev_environment(&HARNESS_RUNTIME),
|
||
|
|
AKBASIC_ERR_ENVIRONMENT);
|
||
|
|
|
||
|
|
/* The wait mechanism, including the parent-chain search. */
|
||
|
|
TEST_REQUIRE(!akbasic_environment_is_waiting_for_any(root), "a fresh environment waits for nothing");
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_wait_for_command(root, "NEXT"));
|
||
|
|
TEST_REQUIRE(akbasic_environment_is_waiting_for_any(root), "the wait must register");
|
||
|
|
TEST_REQUIRE(akbasic_environment_is_waiting_for(root, "NEXT"), "the wait must match by name");
|
||
|
|
TEST_REQUIRE(!akbasic_environment_is_waiting_for(root, "DATA"), "a different verb must not match");
|
||
|
|
|
||
|
|
/* Two pending waits in one environment is a hard error, not a panic. */
|
||
|
|
TEST_REQUIRE_STATUS(akbasic_environment_wait_for_command(root, "DATA"), AKBASIC_ERR_STATE);
|
||
|
|
|
||
|
|
TEST_REQUIRE_OK(akbasic_runtime_new_environment(&HARNESS_RUNTIME));
|
||
|
|
child = HARNESS_RUNTIME.environment;
|
||
|
|
TEST_REQUIRE(akbasic_environment_is_waiting_for(child, "NEXT"),
|
||
|
|
"a child must see its parent's wait");
|
||
|
|
TEST_REQUIRE_OK(akbasic_runtime_prev_environment(&HARNESS_RUNTIME));
|
||
|
|
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_stop_waiting(root, "NEXT"));
|
||
|
|
TEST_REQUIRE(!akbasic_environment_is_waiting_for_any(root), "stop_waiting must clear it");
|
||
|
|
|
||
|
|
/* The per-line value pool is bounded and says so. */
|
||
|
|
for ( i = 0; i < AKBASIC_MAX_VALUES; i++ ) {
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_new_value(root, &value));
|
||
|
|
}
|
||
|
|
TEST_REQUIRE_STATUS(akbasic_environment_new_value(root, &value), AKBASIC_ERR_BOUNDS);
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_zero(root));
|
||
|
|
TEST_REQUIRE_OK(akbasic_environment_new_value(root, &value));
|
||
|
|
|
||
|
|
/* So is the environment pool. */
|
||
|
|
for ( i = 0; i < AKBASIC_MAX_ENVIRONMENTS - 1; i++ ) {
|
||
|
|
TEST_REQUIRE_OK(akbasic_runtime_new_environment(&HARNESS_RUNTIME));
|
||
|
|
}
|
||
|
|
TEST_REQUIRE_STATUS(akbasic_runtime_new_environment(&HARNESS_RUNTIME),
|
||
|
|
AKBASIC_ERR_ENVIRONMENT);
|
||
|
|
for ( i = 0; i < AKBASIC_MAX_ENVIRONMENTS - 1; i++ ) {
|
||
|
|
TEST_REQUIRE_OK(akbasic_runtime_prev_environment(&HARNESS_RUNTIME));
|
||
|
|
}
|
||
|
|
TEST_REQUIRE(HARNESS_RUNTIME.environment == root, "the pool must unwind back to the root");
|
||
|
|
|
||
|
|
harness_stop();
|
||
|
|
return akbasic_test_failures;
|
||
|
|
}
|