/** * Copyright (c) 2026 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "dusktest.h" #include "console/console.h" #include "script/scriptmanager.h" #include "ui/ui.h" #include "util/memory.h" static int ui_setup(void **state) { consoleInit(); memoryZero(UI_ELEMENTS, sizeof(UI_ELEMENTS)); memoryZero(&UI, sizeof(UI)); for(uint8_t i = 0; i < UI_ROOT_MAX; i++) UI.root[i] = UI_ELEMENT_ID_INVALID; for(uint8_t i = 0; i < UI_OVERLAY_ROOT_MAX; i++) { UI.overlayRoot[i] = UI_ELEMENT_ID_INVALID; } errorret_t ret = scriptManagerInit(); if(errorIsNotOk(ret)) { errorCatch(ret); return -1; } return 0; } static int ui_teardown(void **state) { errorret_t ret = scriptManagerDispose(); if(errorIsNotOk(ret)) errorCatch(ret); consoleDispose(); return 0; } static void exec(const char_t *script) { jerry_value_t result; errorret_t ret = scriptManagerExec(script, &result); if(errorIsNotOk(ret)) { errorCatch(errorPrint(ret)); fail_msg("Script execution failed (see printed error above)"); } assert_true(errorIsOk(ret)); jerry_value_free(result); } static void test_ui_add_requires_an_argument(void **state) { errorret_t ret = scriptManagerExec("UI.add();", NULL); assert_true(errorIsNotOk(ret)); errorCatch(ret); assert_int_equal(memoryGetAllocatedCount(), 0); } static void test_ui_add_requires_an_object(void **state) { errorret_t ret = scriptManagerExec("UI.add('button');", NULL); assert_true(errorIsNotOk(ret)); errorCatch(ret); assert_int_equal(memoryGetAllocatedCount(), 0); } static void test_ui_add_requires_a_uielement(void **state) { errorret_t ret = scriptManagerExec("UI.add({});", NULL); assert_true(errorIsNotOk(ret)); errorCatch(ret); assert_int_equal(memoryGetAllocatedCount(), 0); } static void test_ui_remove_of_untracked_element_is_a_noop(void **state) { jerry_value_t result; errorret_t ret = scriptManagerExec( "UI.remove(new UIElement());", &result ); assert_true(errorIsOk(ret)); jerry_value_free(result); } static void test_ui_add_overlay_and_remove_overlay(void **state) { assert_true(UI.overlayRoot[0] == UI_ELEMENT_ID_INVALID); exec( "globalThis.el = new UIElement();" "el.x = 3;" "UI.addOverlay(el);" ); assert_true(UI.overlayRoot[0] != UI_ELEMENT_ID_INVALID); assert_true(UI_ELEMENTS[UI.overlayRoot[0]].type == UI_ELEMENT_TYPE_SCRIPTED); assert_float_equal(UI_ELEMENTS[UI.overlayRoot[0]].x, 3.0f, 0.001f); exec("UI.removeOverlay(el);"); assert_true(UI.overlayRoot[0] == UI_ELEMENT_ID_INVALID); } static void test_ui_add_overlay_detaches_from_parent(void **state) { exec( "globalThis.parent = new UIElement();" "globalThis.child = new UIElement();" "parent.add(child);" "if(parent.childCount !== 1) throw new Error('expected 1 child');" "UI.addOverlay(child);" "if(parent.childCount !== 0) {" " throw new Error('expected 0 children after UI.addOverlay');" "}" "if(child.parent !== undefined) throw new Error('child.parent should be undefined');" ); } static void test_ui_dispose_removes_overlay_root(void **state) { exec( "globalThis.el = new UIElement();" "UI.addOverlay(el);" ); assert_true(UI.overlayRoot[0] != UI_ELEMENT_ID_INVALID); exec("el.dispose();"); assert_true(UI.overlayRoot[0] == UI_ELEMENT_ID_INVALID); } static void test_ui_overlay_root_is_independent_of_root(void **state) { // A normal root and an overlay root are two separate arrays -- adding // to one must never touch the other. exec( "globalThis.a = new UIElement();" "globalThis.b = new UIElement();" "UI.add(a);" "UI.addOverlay(b);" ); assert_true(UI.root[0] != UI_ELEMENT_ID_INVALID); assert_true(UI.overlayRoot[0] != UI_ELEMENT_ID_INVALID); assert_true(UI.root[0] != UI.overlayRoot[0]); } int main(void) { assertInit(); const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown( test_ui_add_requires_an_argument, ui_setup, ui_teardown ), cmocka_unit_test_setup_teardown( test_ui_add_requires_an_object, ui_setup, ui_teardown ), cmocka_unit_test_setup_teardown( test_ui_add_requires_a_uielement, ui_setup, ui_teardown ), cmocka_unit_test_setup_teardown( test_ui_remove_of_untracked_element_is_a_noop, ui_setup, ui_teardown ), cmocka_unit_test_setup_teardown( test_ui_add_overlay_and_remove_overlay, ui_setup, ui_teardown ), cmocka_unit_test_setup_teardown( test_ui_add_overlay_detaches_from_parent, ui_setup, ui_teardown ), cmocka_unit_test_setup_teardown( test_ui_dispose_removes_overlay_root, ui_setup, ui_teardown ), cmocka_unit_test_setup_teardown( test_ui_overlay_root_is_independent_of_root, ui_setup, ui_teardown ), }; return cmocka_run_group_tests(tests, NULL, NULL); }