/** * @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; }