rpg/ui/scene moved to archive/ earlier left a handful of otherwise-generic systems with dead references into them: - asset/loader/chunk existed solely to load rpg overworld map chunks (tile_t, worldpos_t) - deleted outright, along with its ASSET_LOADER_TYPE_CHUNK entry in assetloader.c/h. - save/slot wrapped party_t/storyflagval_t/map-name/cutscene-path, which is 100% rpg save data - deleted outright (save.c/h, savedevice.c/h and the Linux save backend keep their generic settings/device-discovery logic, just lose the slot read/write pieces that only existed for it). - locale/localemanager.c and test/CMakeLists.txt/test/save/* had a few stray includes and test cases that only existed for the now-gone code. Also restores assets/locale/en_US.po (copied back from archive/assets/) - locale init still needs a real locale file to boot into, even with the asset-moving/archiving done separately. Co-Authored-By: Claude Sonnet 5 <[email protected]>
307 lines
9.5 KiB
C
307 lines
9.5 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "dusktest.h"
|
|
#include "savetestfixture.h"
|
|
#include "save/savedevice.h"
|
|
#include "save/savedevicelinux.h"
|
|
#include "save/settings/savesettings.h"
|
|
#include "util/memory.h"
|
|
#include "util/string.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
// ============================================================
|
|
// Helper: drives a device through saveDeviceCheckAvailability +
|
|
// saveDeviceUpdate (the real public flow), returning once the callback has
|
|
// fired.
|
|
// ============================================================
|
|
|
|
static bool_t g_callbackFired;
|
|
static savedevice_t *g_callbackDevice;
|
|
|
|
static void checkAvailabilityCallback(savedevice_t *device, void *user) {
|
|
g_callbackFired = true;
|
|
g_callbackDevice = device;
|
|
}
|
|
|
|
static void checkAvailabilityAndDrain(savedevice_t *device) {
|
|
g_callbackFired = false;
|
|
g_callbackDevice = NULL;
|
|
saveDeviceCheckAvailability(device, checkAvailabilityCallback, NULL);
|
|
saveDeviceUpdate(device);
|
|
}
|
|
|
|
// ============================================================
|
|
// saveDeviceLinuxGetDirectory / GetSettingsPath / GetSlotPath
|
|
// ============================================================
|
|
|
|
static void test_getDirectory_success(void **state) {
|
|
char_t path[512];
|
|
errorret_t ret = saveDeviceLinuxGetDirectory(path, sizeof(path));
|
|
assert_true(errorIsOk(ret));
|
|
|
|
char_t expected[512];
|
|
stringFormat(expected, sizeof(expected), "%s/.dusk/saves", getenv("HOME"));
|
|
assert_true(stringEquals(path, expected));
|
|
}
|
|
|
|
static void test_getDirectory_homeUnset_errors(void **state) {
|
|
unsetenv("HOME");
|
|
|
|
char_t path[512];
|
|
errorret_t ret = saveDeviceLinuxGetDirectory(path, sizeof(path));
|
|
assert_true(errorIsNotOk(ret));
|
|
errorCatch(ret);
|
|
}
|
|
|
|
static void test_getDirectory_nullAsserts(void **state) {
|
|
expect_assert_failure(saveDeviceLinuxGetDirectory(NULL, 512));
|
|
}
|
|
|
|
static void test_getSettingsPath_success(void **state) {
|
|
char_t path[512];
|
|
errorret_t ret = saveDeviceLinuxGetSettingsPath(path, sizeof(path));
|
|
assert_true(errorIsOk(ret));
|
|
|
|
char_t expected[512];
|
|
stringFormat(
|
|
expected, sizeof(expected), "%s/.dusk/saves/settings.json", getenv("HOME")
|
|
);
|
|
assert_true(stringEquals(path, expected));
|
|
}
|
|
|
|
static void test_getSettingsPath_homeUnset_errors(void **state) {
|
|
unsetenv("HOME");
|
|
|
|
char_t path[512];
|
|
errorret_t ret = saveDeviceLinuxGetSettingsPath(path, sizeof(path));
|
|
assert_true(errorIsNotOk(ret));
|
|
errorCatch(ret);
|
|
}
|
|
|
|
// ============================================================
|
|
// saveDeviceLinuxCheckAvailability
|
|
// ============================================================
|
|
|
|
static void test_checkAvailability_wrongStateAsserts(void **state) {
|
|
savedevice_t device;
|
|
memoryZero(&device, sizeof(device));
|
|
device.state = SAVE_DEVICE_STATE_UNKNOWN;
|
|
|
|
expect_assert_failure(saveDeviceLinuxCheckAvailability(&device));
|
|
}
|
|
|
|
static void test_checkAvailability_nullAsserts(void **state) {
|
|
expect_assert_failure(saveDeviceLinuxCheckAvailability(NULL));
|
|
}
|
|
|
|
static void test_checkAvailability_success(void **state) {
|
|
savedevice_t device;
|
|
memoryZero(&device, sizeof(device));
|
|
device.state = SAVE_DEVICE_STATE_UNKNOWN;
|
|
|
|
checkAvailabilityAndDrain(&device);
|
|
|
|
assert_int_equal(device.state, SAVE_DEVICE_STATE_AVAILABLE);
|
|
assert_null(device.reasonKey);
|
|
assert_true(g_callbackFired);
|
|
assert_ptr_equal(g_callbackDevice, &device);
|
|
|
|
// The directory should now actually exist on disk (under the sandboxed
|
|
// $HOME the fixture set up).
|
|
char_t path[512];
|
|
errorret_t pathRet = saveDeviceLinuxGetDirectory(path, sizeof(path));
|
|
assert_true(errorIsOk(pathRet));
|
|
FILE *dir = fopen(path, "r");
|
|
// Directories can't be fopen'd for reading data, but a NULL here would
|
|
// mean the path doesn't exist at all - a non-NULL/EISDIR failure both
|
|
// indicate the directory exists.
|
|
if(dir != NULL) fclose(dir);
|
|
}
|
|
|
|
static void test_checkAvailability_homeUnset(void **state) {
|
|
unsetenv("HOME");
|
|
|
|
savedevice_t device;
|
|
memoryZero(&device, sizeof(device));
|
|
device.state = SAVE_DEVICE_STATE_UNKNOWN;
|
|
|
|
checkAvailabilityAndDrain(&device);
|
|
|
|
assert_int_equal(device.state, SAVE_DEVICE_STATE_UNAVAILABLE);
|
|
assert_true(stringEquals(device.reasonKey, "save.linux.no_home"));
|
|
assert_true(g_callbackFired);
|
|
}
|
|
|
|
static void test_checkAvailability_mkdirpFails(void **state) {
|
|
saveTestFixtureBlockSaveDirectory();
|
|
|
|
savedevice_t device;
|
|
memoryZero(&device, sizeof(device));
|
|
device.state = SAVE_DEVICE_STATE_UNKNOWN;
|
|
|
|
checkAvailabilityAndDrain(&device);
|
|
|
|
assert_int_equal(device.state, SAVE_DEVICE_STATE_UNAVAILABLE);
|
|
assert_true(stringEquals(device.reasonKey, "save.linux.mkdirp_failed"));
|
|
assert_true(g_callbackFired);
|
|
}
|
|
|
|
// ============================================================
|
|
// saveDeviceLinuxSettingsWrite / saveDeviceLinuxSettingsRead
|
|
// ============================================================
|
|
|
|
static void test_settingsWrite_nullAsserts(void **state) {
|
|
savedevice_t device;
|
|
savesettings_t settings;
|
|
saveSettingsInit(&settings);
|
|
|
|
expect_assert_failure(saveDeviceLinuxSettingsWrite(NULL, &settings));
|
|
expect_assert_failure(saveDeviceLinuxSettingsWrite(&device, NULL));
|
|
}
|
|
|
|
static void test_settingsRead_nullAsserts(void **state) {
|
|
savedevice_t device;
|
|
savesettings_t settings;
|
|
saveSettingsInit(&settings);
|
|
|
|
expect_assert_failure(saveDeviceLinuxSettingsRead(NULL, &settings));
|
|
expect_assert_failure(saveDeviceLinuxSettingsRead(&device, NULL));
|
|
}
|
|
|
|
static void test_settingsWriteRead_roundTrip(void **state) {
|
|
savedevice_t device;
|
|
memoryZero(&device, sizeof(device));
|
|
// The save directory must exist before a write can succeed - normally
|
|
// saveDeviceLinuxCheckAvailability does this via mkdirp.
|
|
checkAvailabilityAndDrain(&device);
|
|
|
|
savesettings_t written;
|
|
saveSettingsInit(&written);
|
|
written.audioMasterVolume = 3.25f;// exactly representable, safe to compare
|
|
|
|
errorret_t writeRet = saveDeviceLinuxSettingsWrite(&device, &written);
|
|
assert_true(errorIsOk(writeRet));
|
|
|
|
savesettings_t read;
|
|
saveSettingsInit(&read);
|
|
|
|
errorret_t readRet = saveDeviceLinuxSettingsRead(&device, &read);
|
|
assert_true(errorIsOk(readRet));
|
|
assert_true(read.audioMasterVolume == 3.25f);
|
|
}
|
|
|
|
static void test_settingsRead_noFileYet_leavesUntouched(void **state) {
|
|
savedevice_t device;
|
|
memoryZero(&device, sizeof(device));
|
|
|
|
savesettings_t settings;
|
|
saveSettingsInit(&settings);
|
|
settings.audioMasterVolume = 7.75f;
|
|
|
|
errorret_t ret = saveDeviceLinuxSettingsRead(&device, &settings);
|
|
assert_true(errorIsOk(ret));
|
|
assert_true(settings.audioMasterVolume == 7.75f);
|
|
}
|
|
|
|
static void test_settingsRead_corruptFile_errors(void **state) {
|
|
savedevice_t device;
|
|
memoryZero(&device, sizeof(device));
|
|
|
|
savedevice_t tmpDevice;
|
|
memoryZero(&tmpDevice, sizeof(tmpDevice));
|
|
tmpDevice.state = SAVE_DEVICE_STATE_CHECKING_AVAILABILITY;
|
|
saveDeviceLinuxCheckAvailability(&tmpDevice);
|
|
assert_int_equal(tmpDevice.state, SAVE_DEVICE_STATE_AVAILABLE);
|
|
|
|
char_t settingsPath[512];
|
|
errorret_t pathRet =
|
|
saveDeviceLinuxGetSettingsPath(settingsPath, sizeof(settingsPath));
|
|
assert_true(errorIsOk(pathRet));
|
|
|
|
FILE *file = fopen(settingsPath, "w");
|
|
assert_non_null(file);
|
|
fprintf(file, "not valid json {{{");
|
|
fclose(file);
|
|
|
|
savesettings_t settings;
|
|
saveSettingsInit(&settings);
|
|
|
|
errorret_t ret = saveDeviceLinuxSettingsRead(&device, &settings);
|
|
assert_true(errorIsNotOk(ret));
|
|
errorCatch(ret);
|
|
}
|
|
|
|
int main(void) {
|
|
assertInit();
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test_setup_teardown(
|
|
test_getDirectory_success, saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_getDirectory_homeUnset_errors,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_getDirectory_nullAsserts,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_getSettingsPath_success,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_getSettingsPath_homeUnset_errors,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_checkAvailability_wrongStateAsserts,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_checkAvailability_nullAsserts,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_checkAvailability_success,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_checkAvailability_homeUnset,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_checkAvailability_mkdirpFails,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
|
|
cmocka_unit_test_setup_teardown(
|
|
test_settingsWrite_nullAsserts,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_settingsRead_nullAsserts,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_settingsWriteRead_roundTrip,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_settingsRead_noFileYet_leavesUntouched,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
cmocka_unit_test_setup_teardown(
|
|
test_settingsRead_corruptFile_errors,
|
|
saveTestFixtureSetup, saveTestFixtureTeardown
|
|
),
|
|
};
|
|
|
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
}
|