Add some extra tests

This commit is contained in:
2026-06-01 13:48:29 -05:00
parent 98db62a4bc
commit eedb7769e6
2 changed files with 162 additions and 0 deletions
+160
View File
@@ -193,6 +193,136 @@ static void test_update_error_state(void **state) {
assert_int_equal(memoryGetAllocatedCount(), 0);
}
static void test_update_noop_on_empty_table(void **state) {
errorret_t ret = assetUpdate();
assert_true(errorIsOk(ret));
assert_int_equal(memoryGetAllocatedCount(), 0);
}
static void test_update_loaded_entry_not_redispatched(void **state) {
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetUpdate();
assetUpdate(); // slot freed
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
// Further updates must not re-dispatch or modify a LOADED entry.
assetUpdate();
assetUpdate();
assetUpdate();
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
assert_false(loading_slot_has_entry(entry));
assert_int_equal(memoryGetAllocatedCount(), 0);
}
static void test_update_overflow_queues_entries(void **state) {
// Create one more entry than there are loading slots.
const int TOTAL = ASSET_LOADING_COUNT_MAX + 1;
assetentry_t *entries[ASSET_LOADING_COUNT_MAX + 1];
for(int i = 0; i < TOTAL; i++) {
char_t name[ASSET_FILE_NAME_MAX];
snprintf(name, sizeof(name), "asset%d.locale", i);
entries[i] = assetGetEntry(name, ASSET_LOADER_TYPE_LOCALE, NULL);
}
// Update 1: fills all slots, first ASSET_LOADING_COUNT_MAX entries reach LOADED.
// The overflow entry has no slot yet and stays NOT_STARTED.
errorret_t ret = assetUpdate();
assert_true(errorIsOk(ret));
for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
assert_int_equal(entries[i]->state, ASSET_ENTRY_STATE_LOADED);
}
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED);
// Update 2: LOADED slots are freed. Overflow entry still NOT_STARTED because
// the dispatch phase ran before the slots were cleared this turn.
ret = assetUpdate();
assert_true(errorIsOk(ret));
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED);
// Update 3: now a slot is available; the overflow entry is dispatched and loaded.
ret = assetUpdate();
assert_true(errorIsOk(ret));
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_LOADED);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
static void test_update_error_slot_stays_occupied(void **state) {
ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_LOCALE].loadSync = stub_load_fail;
assetentry_t *entry = assetGetEntry("fail.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetUpdate();
assert_int_equal(entry->state, ASSET_ENTRY_STATE_ERROR);
// Unlike LOADED, the ERROR case does NOT clear the slot — it throws instead.
assert_true(loading_slot_has_entry(entry));
errorret_t ret = assetUpdate();
assert_true(errorIsNotOk(ret));
errorCatch(ret);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
// ============================================================
// assetGetEntry — dedup against non-NOT_STARTED entries
// ============================================================
static void test_getEntry_returns_loaded_entry(void **state) {
assetentry_t *a = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetUpdate();
assert_int_equal(a->state, ASSET_ENTRY_STATE_LOADED);
// A second request for the same name must return the same entry even though
// it is already LOADED rather than NOT_STARTED.
assetentry_t *b = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assert_ptr_equal(a, b);
assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
// ============================================================
// assetEntryDispose tests
// ============================================================
static void test_entry_dispose_clears_entry(void **state) {
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetUpdate();
assetUpdate(); // ensure loading slot is freed before disposing
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
errorret_t ret = assetEntryDispose(entry);
assert_true(errorIsOk(ret));
assert_int_equal(entry->type, ASSET_LOADER_TYPE_NULL);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
static void test_entry_dispose_slot_reusable(void **state) {
assetentry_t *a = assetGetEntry("a.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assetUpdate();
assetUpdate();
assert_false(loading_slot_has_entry(a));
assetEntryDispose(a);
assert_int_equal(a->type, ASSET_LOADER_TYPE_NULL);
// The freed slot should now accept a new entry.
assetentry_t *b = assetGetEntry("b.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
assert_non_null(b);
assert_int_equal(b->state, ASSET_ENTRY_STATE_NOT_STARTED);
errorret_t ret = assetUpdate();
assert_true(errorIsOk(ret));
assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
// ============================================================
// assetRequireLoaded tests
// ============================================================
@@ -221,22 +351,52 @@ static void test_requireLoaded_spins_to_loaded(void **state) {
assert_int_equal(memoryGetAllocatedCount(), 0);
}
static void test_requireLoaded_propagates_error(void **state) {
ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_LOCALE].loadSync = stub_load_fail;
assetentry_t *entry = assetGetEntry("fail.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
// requireLoaded spins assetUpdate until LOADED — but the loader always fails,
// so the second assetUpdate sees ERROR and throws, which errorChain propagates.
errorret_t ret = assetRequireLoaded(entry);
assert_true(errorIsNotOk(ret));
errorCatch(ret);
assert_int_equal(entry->state, ASSET_ENTRY_STATE_ERROR);
assert_int_equal(memoryGetAllocatedCount(), 0);
}
// ============================================================
// main
// ============================================================
int main(void) {
const struct CMUnitTest tests[] = {
// getEntry
cmocka_unit_test_setup_teardown(test_getEntry_creates_new, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_getEntry_dedup, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_getEntry_distinct_names, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_getEntry_returns_loaded_entry, asset_setup, asset_teardown),
// assetUpdate — state machine
cmocka_unit_test_setup_teardown(test_update_noop_on_empty_table, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_entry_reaches_loaded, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_slot_occupied_after_first_update, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_slot_cleared_after_second_update, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_four_slots_fill_independently, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_loaded_entry_not_redispatched, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_overflow_queues_entries, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_error_state, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_update_error_slot_stays_occupied, asset_setup, asset_teardown),
// assetEntryDispose
cmocka_unit_test_setup_teardown(test_entry_dispose_clears_entry, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_entry_dispose_slot_reusable, asset_setup, asset_teardown),
// assetRequireLoaded
cmocka_unit_test_setup_teardown(test_requireLoaded_already_loaded, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_requireLoaded_spins_to_loaded, asset_setup, asset_teardown),
cmocka_unit_test_setup_teardown(test_requireLoaded_propagates_error, asset_setup, asset_teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}