/** * @file verbs_table.c * @brief Tests the dispatch table's invariants. * * The sorting assertion is the important one: bsearch on a mis-sorted table * silently fails to find a verb rather than producing a compile error, and the * symptom would be "Unknown command PRINT" a long way from the cause. */ #include #include #include #include "testutil.h" int main(void) { const akbasic_Verb *table = NULL; const akbasic_Verb *found = NULL; int count = 0; int i = 0; TEST_REQUIRE_OK(akbasic_error_register()); table = akbasic_verb_table(&count); TEST_REQUIRE(table != NULL, "the verb table must exist"); TEST_REQUIRE(count > 0, "the verb table must not be empty"); /* Sorted, strictly: bsearch requires it and a duplicate name is a bug. */ for ( i = 1; i < count; i++ ) { TEST_REQUIRE(strcmp(table[i - 1].name, table[i].name) < 0, "table is not sorted at index %d: \"%s\" then \"%s\"", i, table[i - 1].name, table[i].name); } /* Every row is reachable by its own name. */ for ( i = 0; i < count; i++ ) { TEST_REQUIRE_OK(akbasic_verb_lookup(table[i].name, &found)); TEST_REQUIRE(found == &table[i], "row \"%s\" is not findable", table[i].name); } /* Verbs and function names are case-insensitive; variable names are not. */ TEST_REQUIRE_OK(akbasic_verb_lookup("print", &found)); TEST_REQUIRE(found != NULL && strcmp(found->name, "PRINT") == 0, "lowercase print missed"); TEST_REQUIRE_OK(akbasic_verb_lookup("PrInT", &found)); TEST_REQUIRE(found != NULL && strcmp(found->name, "PRINT") == 0, "mixed-case print missed"); /* A miss is a NULL, not an error. */ TEST_REQUIRE_OK(akbasic_verb_lookup("NOTAVERB", &found)); TEST_REQUIRE(found == NULL, "an unknown name must miss"); TEST_REQUIRE_OK(akbasic_verb_lookup("", &found)); TEST_REQUIRE(found == NULL, "an empty name must miss"); /* Every function row carries a real arity and a handler. */ for ( i = 0; i < count; i++ ) { if ( table[i].tokentype != AKBASIC_TOK_FUNCTION ) { continue; } TEST_REQUIRE(table[i].arity >= 1, "function \"%s\" has arity %d", table[i].name, table[i].arity); TEST_REQUIRE(table[i].exec != NULL, "function \"%s\" has no exec handler", table[i].name); } /* * The four rows with no exec handler are consumed by another verb's parse * path and are never evaluated on their own. Any other missing handler is a * verb that would report "Unknown command" at runtime. */ for ( i = 0; i < count; i++ ) { if ( table[i].exec != NULL ) { continue; } TEST_REQUIRE(strcmp(table[i].name, "AND") == 0 || strcmp(table[i].name, "ELSE") == 0 || strcmp(table[i].name, "NOT") == 0 || strcmp(table[i].name, "OR") == 0 || strcmp(table[i].name, "REM") == 0 || strcmp(table[i].name, "STEP") == 0 || strcmp(table[i].name, "THEN") == 0 || strcmp(table[i].name, "TO") == 0, "verb \"%s\" has no exec handler and is not a known passive token", table[i].name); } TEST_REQUIRE_STATUS(akbasic_verb_lookup(NULL, &found), AKERR_NULLPOINTER); TEST_REQUIRE_STATUS(akbasic_verb_lookup("PRINT", NULL), AKERR_NULLPOINTER); return akbasic_test_failures; }