Splits saveslot_t/savesettings_t into a stable, version-independent in-memory struct (save/slot/, save/settings/) and a separate frozen per-schema-version struct (saveslotver1_t/savesettingsver1_t) used only for JSON serialization, converted between manually. Every write now stamps a "version" field (SAVE_SLOT_CURRENT/SAVE_SETTINGS_CURRENT), and every read requires it to match exactly, rejecting anything else - laying the groundwork for a real migration path instead of silently misreading old/incompatible save data.
173 lines
5.0 KiB
C
173 lines
5.0 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "dusktest.h"
|
|
#include "save/settings/savesettings.h"
|
|
#include "save/savejson.h"
|
|
#include "util/memory.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// ============================================================
|
|
// Helpers - wrap the writeInit/readInit macros (which can only be used
|
|
// inside a function returning errorret_t) so tests can call them plainly.
|
|
// ============================================================
|
|
|
|
static errorret_t settingsToJSON(
|
|
savesettings_t *settings,
|
|
char_t **outJson,
|
|
size_t *outLen
|
|
) {
|
|
writeInit();
|
|
|
|
errorret_t writeResult = saveSettingsWriteJSON(settings, doc, object);
|
|
if(errorIsNotOk(writeResult)) {
|
|
yyjson_mut_doc_free(doc);
|
|
errorChain(writeResult);
|
|
}
|
|
|
|
*outJson = yyjson_mut_write(doc, 0, outLen);
|
|
yyjson_mut_doc_free(doc);
|
|
assertNotNull(*outJson, "Failed to write settings JSON");
|
|
|
|
errorOk();
|
|
}
|
|
|
|
static errorret_t settingsFromJSON(
|
|
savesettings_t *settings, const char_t *json
|
|
) {
|
|
readInit(json, strlen(json));
|
|
errorret_t readResult = saveSettingsReadJSON(settings, object);
|
|
yyjson_doc_free(jsonDoc);
|
|
errorChain(readResult);
|
|
errorOk();
|
|
}
|
|
|
|
// ============================================================
|
|
// saveSettingsInit
|
|
// ============================================================
|
|
|
|
static void test_saveSettingsInit_resetsAudioMasterVolume(void **state) {
|
|
savesettings_t settings;
|
|
memoryZero(&settings, sizeof(settings));
|
|
settings.audioMasterVolume = 42.0f;
|
|
|
|
saveSettingsInit(&settings);
|
|
|
|
assert_true(settings.audioMasterVolume == 1.0f);
|
|
}
|
|
|
|
static void test_saveSettingsInit_nullAsserts(void **state) {
|
|
expect_assert_failure(saveSettingsInit(NULL));
|
|
}
|
|
|
|
// ============================================================
|
|
// saveSettingsWriteJSON / saveSettingsReadJSON
|
|
// ============================================================
|
|
|
|
static void test_saveSettingsWriteJSON_nullAsserts(void **state) {
|
|
savesettings_t settings;
|
|
saveSettingsInit(&settings);
|
|
writeInit();
|
|
|
|
expect_assert_failure(saveSettingsWriteJSON(NULL, doc, object));
|
|
expect_assert_failure(saveSettingsWriteJSON(&settings, NULL, object));
|
|
expect_assert_failure(saveSettingsWriteJSON(&settings, doc, NULL));
|
|
|
|
yyjson_mut_doc_free(doc);
|
|
}
|
|
|
|
static void test_saveSettingsReadJSON_nullAsserts(void **state) {
|
|
savesettings_t settings;
|
|
saveSettingsInit(&settings);
|
|
writeInit();
|
|
|
|
size_t len;
|
|
char_t *json = yyjson_mut_write(doc, 0, &len);
|
|
yyjson_mut_doc_free(doc);
|
|
assert_non_null(json);
|
|
|
|
yyjson_doc *readDoc = yyjson_read(json, len, 0);
|
|
yyjson_val *readObject = yyjson_doc_get_root(readDoc);
|
|
|
|
expect_assert_failure(saveSettingsReadJSON(NULL, readObject));
|
|
expect_assert_failure(saveSettingsReadJSON(&settings, NULL));
|
|
|
|
yyjson_doc_free(readDoc);
|
|
free(json);
|
|
}
|
|
|
|
static void test_saveSettingsWriteReadJSON_roundTrip(void **state) {
|
|
savesettings_t written;
|
|
saveSettingsInit(&written);
|
|
written.audioMasterVolume = 2.5f;
|
|
|
|
char_t *json;
|
|
size_t len;
|
|
errorret_t writeRet = settingsToJSON(&written, &json, &len);
|
|
assert_true(errorIsOk(writeRet));
|
|
|
|
savesettings_t read;
|
|
saveSettingsInit(&read);
|
|
read.audioMasterVolume = -1.0f;// sentinel, should be overwritten
|
|
|
|
errorret_t readRet = settingsFromJSON(&read, json);
|
|
assert_true(errorIsOk(readRet));
|
|
assert_true(read.audioMasterVolume == 2.5f);
|
|
|
|
free(json);
|
|
}
|
|
|
|
static void test_saveSettingsReadJSON_missingKeyUsesDefault(void **state) {
|
|
savesettings_t settings;
|
|
saveSettingsInit(&settings);
|
|
settings.audioMasterVolume = 999.0f;// sentinel
|
|
|
|
errorret_t ret = settingsFromJSON(&settings, "{\"version\":1}");
|
|
assert_true(errorIsOk(ret));
|
|
assert_true(settings.audioMasterVolume == 1.0f);
|
|
}
|
|
|
|
// ============================================================
|
|
// saveSettingsReadJSON - version enforcement
|
|
// ============================================================
|
|
|
|
static void test_saveSettingsReadJSON_versionMissing_errors(void **state) {
|
|
savesettings_t settings;
|
|
saveSettingsInit(&settings);
|
|
|
|
errorret_t ret = settingsFromJSON(&settings, "{}");
|
|
assert_true(errorIsNotOk(ret));
|
|
errorCatch(ret);
|
|
}
|
|
|
|
static void test_saveSettingsReadJSON_versionMismatch_errors(void **state) {
|
|
savesettings_t settings;
|
|
saveSettingsInit(&settings);
|
|
|
|
errorret_t ret = settingsFromJSON(&settings, "{\"version\":999}");
|
|
assert_true(errorIsNotOk(ret));
|
|
errorCatch(ret);
|
|
}
|
|
|
|
int main(void) {
|
|
assertInit();
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test(test_saveSettingsInit_resetsAudioMasterVolume),
|
|
cmocka_unit_test(test_saveSettingsInit_nullAsserts),
|
|
|
|
cmocka_unit_test(test_saveSettingsWriteJSON_nullAsserts),
|
|
cmocka_unit_test(test_saveSettingsReadJSON_nullAsserts),
|
|
cmocka_unit_test(test_saveSettingsWriteReadJSON_roundTrip),
|
|
cmocka_unit_test(test_saveSettingsReadJSON_missingKeyUsesDefault),
|
|
cmocka_unit_test(test_saveSettingsReadJSON_versionMissing_errors),
|
|
cmocka_unit_test(test_saveSettingsReadJSON_versionMismatch_errors),
|
|
};
|
|
|
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
}
|