/** * Copyright (c) 2026 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ // nftw()/FTW_DEPTH/FTW_PHYS are glibc extensions gated behind this. #define _XOPEN_SOURCE 700 #include "savetestfixture.h" #include "assert/assert.h" #include "asset/asset.h" #include "asset/loader/assetloader.h" #include "asset/loader/assetentry.h" #include "util/memory.h" #include "util/string.h" #include #include #include #include #include static char_t SAVE_TEST_FIXTURE_HOME[PATH_MAX]; static char_t SAVE_TEST_FIXTURE_REAL_HOME[PATH_MAX]; static bool_t SAVE_TEST_FIXTURE_HAD_REAL_HOME; static const char_t *SAVE_TEST_FIXTURE_PARTY_JSON = "{\"members\":[{" "\"attack\":10,\"defense\":5,\"magic\":0,\"speed\":10,\"luck\":0," "\"healthMax\":30,\"mpMax\":10" "}]}"; static yyjson_doc *SAVE_TEST_FIXTURE_PARTY_DOC = NULL; errorret_t saveTestFixturePartyJsonLoadSyncStub(assetloading_t *loading) { loading->entry->data.json = SAVE_TEST_FIXTURE_PARTY_DOC; loading->entry->state = ASSET_ENTRY_STATE_LOADED; errorOk(); } errorret_t saveTestFixturePartyJsonDisposeStub(assetentry_t *entry) { errorOk(); } void saveTestFixtureStubPartyAsset(void) { memoryZero(&ASSET, sizeof(ASSET)); for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { threadMutexInit(&ASSET.loading[i].mutex); } if(SAVE_TEST_FIXTURE_PARTY_DOC == NULL) { SAVE_TEST_FIXTURE_PARTY_DOC = yyjson_read( SAVE_TEST_FIXTURE_PARTY_JSON, strlen(SAVE_TEST_FIXTURE_PARTY_JSON), 0 ); assertNotNull( SAVE_TEST_FIXTURE_PARTY_DOC, "Failed to parse party JSON fixture" ); } ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_JSON].loadSync = saveTestFixturePartyJsonLoadSyncStub; ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_JSON].dispose = saveTestFixturePartyJsonDisposeStub; } void saveTestFixtureUnstubPartyAsset(void) { if(SAVE_TEST_FIXTURE_PARTY_DOC == NULL) return; yyjson_doc_free(SAVE_TEST_FIXTURE_PARTY_DOC); SAVE_TEST_FIXTURE_PARTY_DOC = NULL; } static int_t saveTestFixtureRemoveEntry( const char *path, const struct stat *statBuffer, int typeFlag, struct FTW *ftwBuffer ) { return remove(path); } int saveTestFixtureSetup(void **state) { const char_t *realHome = getenv("HOME"); SAVE_TEST_FIXTURE_HAD_REAL_HOME = realHome != NULL; if(realHome != NULL) { stringCopy( SAVE_TEST_FIXTURE_REAL_HOME, realHome, sizeof(SAVE_TEST_FIXTURE_REAL_HOME) - 1 ); } stringCopy( SAVE_TEST_FIXTURE_HOME, "/tmp/dusk-save-test-XXXXXX", sizeof(SAVE_TEST_FIXTURE_HOME) - 1 ); assertNotNull(mkdtemp(SAVE_TEST_FIXTURE_HOME), "Failed to create temp $HOME"); assertTrue(setenv("HOME", SAVE_TEST_FIXTURE_HOME, 1) == 0, "setenv failed"); saveTestFixtureStubPartyAsset(); errorret_t ret = saveInit(); assertTrue(errorIsOk(ret), "saveInit failed in test fixture"); return 0; } int saveTestFixtureTeardown(void **state) { saveTestFixtureUnstubPartyAsset(); nftw( SAVE_TEST_FIXTURE_HOME, saveTestFixtureRemoveEntry, 16, FTW_DEPTH | FTW_PHYS ); if(SAVE_TEST_FIXTURE_HAD_REAL_HOME) { setenv("HOME", SAVE_TEST_FIXTURE_REAL_HOME, 1); } else { unsetenv("HOME"); } memoryZero(&SAVE, sizeof(SAVE)); return 0; } void saveTestFixtureBlockSaveDirectory(void) { const char_t *home = getenv("HOME"); assertNotNull(home, "Fixture $HOME must be set before blocking it"); char_t path[PATH_MAX]; stringFormat(path, sizeof(path), "%s/.dusk", home); FILE *file = fopen(path, "w"); assertNotNull(file, "Failed to create blocking file"); fclose(file); }