Nothing ever wrote to it - it only ever passed its own address through to onComplete/CUTSCENE_CALLBACK/modal-option callbacks, so every call site now passes NULL directly and the 8KB buffer is gone. Co-Authored-By: Claude Sonnet 5 <[email protected]>
174 lines
4.8 KiB
C
174 lines
4.8 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "dusktest.h"
|
|
#include "rpg/cutscene/cutscenesystem.h"
|
|
#include "time/time.h"
|
|
#include "util/memory.h"
|
|
|
|
static void test_cutsceneWaitCompletesAfterItsDuration(void **state) {
|
|
|
|
cutsceneitem_t item = { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f };
|
|
cutsceneitemdata_t data;
|
|
memoryZero(&data, sizeof(data));
|
|
|
|
cutsceneWaitStart(&item, &data);
|
|
|
|
TIME.delta = 0.5f;
|
|
assert_false(cutsceneWaitUpdate(&item, &data));
|
|
|
|
TIME.delta = 0.6f;
|
|
assert_true(cutsceneWaitUpdate(&item, &data));
|
|
}
|
|
|
|
static void *lastUserData;
|
|
static uint8_t callbackCallCount;
|
|
|
|
static void recordCallback(void *userData) {
|
|
lastUserData = userData;
|
|
callbackCallCount++;
|
|
}
|
|
|
|
static void test_cutsceneCallbackFiresOnStartWithNullUserData(void **state) {
|
|
|
|
callbackCallCount = 0;
|
|
lastUserData = (void *)0x1;
|
|
|
|
cutsceneitem_t item =
|
|
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = recordCallback };
|
|
cutsceneitemdata_t data;
|
|
|
|
cutsceneCallbackStart(&item, &data);
|
|
assert_int_equal(callbackCallCount, 1);
|
|
assert_null(lastUserData);
|
|
|
|
// Callback items always complete immediately -- the effect already
|
|
// happened in Start, not Update.
|
|
assert_true(cutsceneCallbackUpdate(&item, &data));
|
|
assert_int_equal(callbackCallCount, 1);// Update doesn't fire it again
|
|
}
|
|
|
|
static void test_cutsceneCallbackNullIsNoop(void **state) {
|
|
|
|
callbackCallCount = 0;
|
|
cutsceneitem_t item =
|
|
{ .type = CUTSCENE_ITEM_TYPE_CALLBACK, .callback = NULL };
|
|
cutsceneitemdata_t data;
|
|
|
|
cutsceneCallbackStart(&item, &data);// should not crash
|
|
assert_int_equal(callbackCallCount, 0);
|
|
}
|
|
|
|
static void test_cutsceneSetPauseAppliesImmediately(void **state) {
|
|
|
|
CUTSCENE_SYSTEM.pause = CUTSCENE_PAUSE_NONE;
|
|
cutsceneitem_t item = {
|
|
.type = CUTSCENE_ITEM_TYPE_SET_PAUSE,
|
|
.setPause = CUTSCENE_PAUSE_NPC | CUTSCENE_PAUSE_BATTLE
|
|
};
|
|
cutsceneitemdata_t data;
|
|
|
|
cutsceneSetPauseStart(&item, &data);
|
|
assert_int_equal(
|
|
CUTSCENE_SYSTEM.pause, CUTSCENE_PAUSE_NPC | CUTSCENE_PAUSE_BATTLE
|
|
);
|
|
assert_true(cutsceneSetPauseUpdate(&item, &data));
|
|
}
|
|
|
|
static void test_cutsceneConcurrentCompletesOnceAllChildrenDo(void **state) {
|
|
|
|
cutsceneitem_t item = {
|
|
.type = CUTSCENE_ITEM_TYPE_CONCURRENT,
|
|
.concurrent = {
|
|
.items = (const cutsceneitem_t[]){
|
|
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 0.2f },
|
|
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 0.5f }
|
|
},
|
|
.count = 2
|
|
}
|
|
};
|
|
cutsceneitemdata_t data;
|
|
memoryZero(&data, sizeof(data));
|
|
|
|
cutsceneConcurrentStart(&item, &data);
|
|
|
|
TIME.delta = 0.3f;
|
|
// Child 0 (0.2s) elapses; child 1 (0.5s) still has 0.2s left.
|
|
assert_false(cutsceneConcurrentUpdate(&item, &data));
|
|
|
|
TIME.delta = 0.3f;
|
|
// Child 1 now elapses too.
|
|
assert_true(cutsceneConcurrentUpdate(&item, &data));
|
|
}
|
|
|
|
static void test_cutsceneConcurrentDoesNotReUpdateFinishedChildren(
|
|
void **state
|
|
) {
|
|
|
|
cutsceneitem_t item = {
|
|
.type = CUTSCENE_ITEM_TYPE_CONCURRENT,
|
|
.concurrent = {
|
|
.items = (const cutsceneitem_t[]){
|
|
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 0.1f },
|
|
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 10.0f }
|
|
},
|
|
.count = 2
|
|
}
|
|
};
|
|
cutsceneitemdata_t data;
|
|
memoryZero(&data, sizeof(data));
|
|
|
|
cutsceneConcurrentStart(&item, &data);
|
|
|
|
TIME.delta = 0.2f;
|
|
cutsceneConcurrentUpdate(&item, &data);// child 0 finishes
|
|
|
|
// If child 0 were re-updated, its stored wait value would keep dropping
|
|
// further below zero -- not observable directly, but the overall result
|
|
// must not falsely report done just because child 0 keeps completing.
|
|
TIME.delta = 0.2f;
|
|
assert_false(cutsceneConcurrentUpdate(&item, &data));
|
|
}
|
|
|
|
static void test_cutsceneConcurrentCannotNest(void **state) {
|
|
|
|
cutsceneitem_t outer = {
|
|
.type = CUTSCENE_ITEM_TYPE_CONCURRENT,
|
|
.concurrent = {
|
|
.items = (const cutsceneitem_t[]){
|
|
{
|
|
.type = CUTSCENE_ITEM_TYPE_CONCURRENT,
|
|
.concurrent = {
|
|
.items = (const cutsceneitem_t[]){
|
|
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f }
|
|
},
|
|
.count = 1
|
|
}
|
|
}
|
|
},
|
|
.count = 1
|
|
}
|
|
};
|
|
cutsceneitemdata_t data;
|
|
|
|
expect_assert_failure(cutsceneConcurrentStart(&outer, &data));
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test(test_cutsceneWaitCompletesAfterItsDuration),
|
|
cmocka_unit_test(test_cutsceneCallbackFiresOnStartWithNullUserData),
|
|
cmocka_unit_test(test_cutsceneCallbackNullIsNoop),
|
|
cmocka_unit_test(test_cutsceneSetPauseAppliesImmediately),
|
|
cmocka_unit_test(test_cutsceneConcurrentCompletesOnceAllChildrenDo),
|
|
cmocka_unit_test(test_cutsceneConcurrentDoesNotReUpdateFinishedChildren),
|
|
cmocka_unit_test(test_cutsceneConcurrentCannotNest),
|
|
};
|
|
|
|
return cmocka_run_group_tests(tests, NULL, NULL);
|
|
}
|