From 2198b318bb14086078c519832245ff29b986bfcb Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Tue, 14 Apr 2026 11:36:57 -0600 Subject: [PATCH] gen(hand): integration tests return-table shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructures the 20 files under luaui/Tests/, luaui/TestsExamples/, plus the headless-only common/testing/infologtest.lua, from bare- global hook declarations to a return-table shape. Updates the dbg_test_runner widget to read test hooks from the returned table. Motivation: the pre-existing shape required the test files to run under setfenv(chunk, testEnvironment) and define `function test()`, `function setup()`, etc. as bare module-level globals that setfenv redirected into the environment. That works at runtime but emmylua can't model the sandboxing — it sees 20+ files declaring project- wide globals like `test`, `setup`, `skip`, `cleanup`. To keep emmylua happy, .emmyrc.json had to blacklist both test directories under workspace.ignoreDir — a kludge on clearly-ours code. Lives on its own leaf so the convention change can be discussed in isolation. Minimal shape change per file — just prepend `local` to each top- level `function` declaration, and append a final `return { ... }` block listing whichever lifecycle hooks (skip/setup/test/cleanup) that file actually defines. Original indentation and formatting preserved (no stylua reformatting noise — the fmt transform runs after this one in the mig pipeline). Runner patch — luaui/Widgets/dbg_test_runner.lua, loadTestFromFile: - capture the return value of pcall(chunk) - require it to be a table - merge its keys into testEnvironment so runTestInternal still reads bare `skip`/`setup`/`test`/`cleanup` under setfenv --- common/testing/infologtest.lua | 6 ++++-- .../test_cmd_blueprint_filter.lua | 10 +++++---- .../cmd_blueprint/test_cmd_blueprint_line.lua | 10 +++++---- .../test_cmd_blueprint_single.lua | 8 ++++--- .../cmd_stop_selfd/test_cmd_stop_selfd.lua | 10 +++++---- luaui/Tests/critters/test_critters.lua | 12 ++++++----- .../test_gui_selfd_icons_armpw.lua | 10 +++++---- .../test_gui_selfd_icons_armvp.lua | 10 +++++---- .../Tests/mex-building/pregame_mex_queue.lua | 10 +++++---- luaui/Tests/mex-building/pregame_mex_snap.lua | 10 +++++---- luaui/Tests/select_api/compare_to_spring.lua | 10 +++++---- luaui/Tests/selftests/test_assertions.lua | 20 ++++++++++-------- luaui/Tests/selftests/test_callins.lua | 14 +++++++------ luaui/Tests/weapondefs/test_flighttime.lua | 12 ++++++----- .../balance/test_arm_vs_cor_fighters.lua | 10 +++++---- .../balance/test_grunts_vs_pawns.lua | 10 +++++---- .../test_gui_battle_resource_tracker.lua | 10 +++++---- .../test_utilities/test_mock.lua | 4 +++- .../test_utilities/test_wait.lua | 8 ++++--- luaui/Widgets/dbg_test_runner.lua | 21 ++++++++++++++++--- 20 files changed, 134 insertions(+), 81 deletions(-) diff --git a/common/testing/infologtest.lua b/common/testing/infologtest.lua index a2a56892cd9..e69ebe3d006 100644 --- a/common/testing/infologtest.lua +++ b/common/testing/infologtest.lua @@ -2,7 +2,7 @@ local maxErrors = 10 -function skip() +local function skip() -- TODO: re-enable. disabled 2025-10-01 in order to get CICD working return true end @@ -48,9 +48,11 @@ local function infologTest() return errors end -function test() +local function test() local errors = infologTest() if #errors > 0 then error(table.concat(errors, "\n"), 0) end end + +return { skip = skip, test = test } diff --git a/luaui/Tests/cmd_blueprint/test_cmd_blueprint_filter.lua b/luaui/Tests/cmd_blueprint/test_cmd_blueprint_filter.lua index 6d2a2ecf8a2..a579bf000dc 100644 --- a/luaui/Tests/cmd_blueprint/test_cmd_blueprint_filter.lua +++ b/luaui/Tests/cmd_blueprint/test_cmd_blueprint_filter.lua @@ -1,12 +1,12 @@ local widgetName = "Blueprint" -function skip() +local function skip() -- TODO re-enable and debug. Disabled 2025-09-30 to unblock CICD -- return Spring.GetGameFrame() <= 0 return true end -function setup() +local function setup() assert(widgetHandler.knownWidgets[widgetName] ~= nil) Test.clearMap() @@ -20,14 +20,14 @@ function setup() }) end -function cleanup() +local function cleanup() Test.clearMap() Spring.SetCameraState(initialCameraState) end local delay = 5 -function test() +local function test() assert(widget) mock_saveBlueprintsToFile = Test.mock(widget, "saveBlueprintsToFile") @@ -81,3 +81,5 @@ function test() widget.handleBlueprintDeleteAction() assert(widget.selectedBlueprintIndex == nil, widget.selectedBlueprintIndex) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/cmd_blueprint/test_cmd_blueprint_line.lua b/luaui/Tests/cmd_blueprint/test_cmd_blueprint_line.lua index 96da9d02704..48ce155df52 100644 --- a/luaui/Tests/cmd_blueprint/test_cmd_blueprint_line.lua +++ b/luaui/Tests/cmd_blueprint/test_cmd_blueprint_line.lua @@ -1,10 +1,10 @@ local widgetName = "Blueprint" -function skip() +local function skip() return not Platform.gl end -function setup() +local function setup() assert(widgetHandler.knownWidgets[widgetName] ~= nil) Test.clearMap() @@ -21,14 +21,14 @@ function setup() }) end -function cleanup() +local function cleanup() Test.clearMap() Spring.SetCameraState(initialCameraState) end local delay = 5 -function test() +local function test() widget = widgetHandler:FindWidget(widgetName) assert(widget) @@ -105,3 +105,5 @@ function test() assert(#builderQueue == bpCount, #builderQueue) assertEqual(builderQueue[1].id, -blueprintUnitDefID) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/cmd_blueprint/test_cmd_blueprint_single.lua b/luaui/Tests/cmd_blueprint/test_cmd_blueprint_single.lua index f107d10afb6..3d0a8bc4cd4 100644 --- a/luaui/Tests/cmd_blueprint/test_cmd_blueprint_single.lua +++ b/luaui/Tests/cmd_blueprint/test_cmd_blueprint_single.lua @@ -1,6 +1,6 @@ local widgetName = "Blueprint" -function setup() +local function setup() assert(widgetHandler.knownWidgets[widgetName] ~= nil) Test.clearMap() @@ -16,14 +16,14 @@ function setup() }) end -function cleanup() +local function cleanup() Test.clearMap() Spring.SetCameraState(initialCameraState) end local delay = 5 -function test() +local function test() widget = widgetHandler:FindWidget(widgetName) assert(widget) @@ -82,3 +82,5 @@ function test() assertEqual(#builderQueue, 1) assertEqual(builderQueue[1].id, -blueprintUnitDefID) end + +return { setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/cmd_stop_selfd/test_cmd_stop_selfd.lua b/luaui/Tests/cmd_stop_selfd/test_cmd_stop_selfd.lua index 1d31c4067af..be10dbba7b9 100644 --- a/luaui/Tests/cmd_stop_selfd/test_cmd_stop_selfd.lua +++ b/luaui/Tests/cmd_stop_selfd/test_cmd_stop_selfd.lua @@ -1,18 +1,18 @@ -function skip() +local function skip() return Spring.GetGameFrame() <= 0 end -function setup() +local function setup() Test.clearMap() -- Enable UnitCommand callin for tests Test.expectCallin("UnitCommand") end -function cleanup() +local function cleanup() Test.clearMap() end -function test() +local function test() widget = widgetHandler:FindWidget("Stop means Stop") assert(widget, "Stop means Stop widget not found via FindWidget") @@ -45,3 +45,5 @@ function test() assertEqual(Spring.GetUnitSelfDTime(unitID), 0, string.format("GetUnitSelfDTime after stop of queued selfd: expected 0, got %d", Spring.GetUnitSelfDTime(unitID))) assertEqual(Spring.GetUnitCommandCount(unitID), 0, string.format("GetUnitCommandCount after stop of queued selfd: expected 0, got %d", Spring.GetUnitCommandCount(unitID))) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/critters/test_critters.lua b/luaui/Tests/critters/test_critters.lua index 81f560036df..b542107e129 100644 --- a/luaui/Tests/critters/test_critters.lua +++ b/luaui/Tests/critters/test_critters.lua @@ -1,9 +1,9 @@ -function skip() +local function skip() -- TODO re-enable and debug. Disabled 2025-12-22 to unblock CICD return true end -function setup() +local function setup() Test.clearMap() Test.levelHeightMap() @@ -11,14 +11,14 @@ function setup() Spring.SendCommands("setspeed 5") end -function cleanup() +local function cleanup() Spring.SendCommands("globallos") Spring.SendCommands("setspeed 1") Test.clearMap() end -function runCritterTest() +local function runCritterTest() local WAIT_FRAMES = 204 -- enough to trigger critter cleanup/restoring by gaia_critters local unitName = "armpw" local critterName = "critter_crab" @@ -188,6 +188,8 @@ function runCritterTest() assert(countAliveCritters() == 36) end -function test() +local function test() runCritterTest() end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/gui_selfd_icons/test_gui_selfd_icons_armpw.lua b/luaui/Tests/gui_selfd_icons/test_gui_selfd_icons_armpw.lua index 95cfb19d2ae..661fb613a66 100644 --- a/luaui/Tests/gui_selfd_icons/test_gui_selfd_icons_armpw.lua +++ b/luaui/Tests/gui_selfd_icons/test_gui_selfd_icons_armpw.lua @@ -1,21 +1,21 @@ local widgetName = "Self-Destruct Icons" -function skip() +local function skip() return Spring.GetGameFrame() <= 0 end -function setup() +local function setup() Test.clearMap() Test.prepareWidget(widgetName) Test.expectCallin("UnitCommand") end -function cleanup() +local function cleanup() Test.clearMap() end -function test() +local function test() widget = widgetHandler:FindWidget(widgetName) assert(widget) @@ -54,3 +54,5 @@ function test() return table.count(widget.activeSelfD) == 1 and table.count(widget.queuedSelfD) == 0 end, 10) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/gui_selfd_icons/test_gui_selfd_icons_armvp.lua b/luaui/Tests/gui_selfd_icons/test_gui_selfd_icons_armvp.lua index e255235f040..740299dd381 100644 --- a/luaui/Tests/gui_selfd_icons/test_gui_selfd_icons_armvp.lua +++ b/luaui/Tests/gui_selfd_icons/test_gui_selfd_icons_armvp.lua @@ -1,21 +1,21 @@ local widgetName = "Self-Destruct Icons" -function skip() +local function skip() return Spring.GetGameFrame() <= 0 end -function setup() +local function setup() Test.clearMap() Test.prepareWidget(widgetName) Test.expectCallin("UnitCommand") end -function cleanup() +local function cleanup() Test.clearMap() end -function test() +local function test() widget = widgetHandler:FindWidget(widgetName) assert(widget) @@ -49,3 +49,5 @@ function test() --assert(table.count(widget.activeSelfD) == 0) --assert(table.count(widget.queuedSelfD) == 0) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/mex-building/pregame_mex_queue.lua b/luaui/Tests/mex-building/pregame_mex_queue.lua index 20114da4f38..f2bc7c22229 100644 --- a/luaui/Tests/mex-building/pregame_mex_queue.lua +++ b/luaui/Tests/mex-building/pregame_mex_queue.lua @@ -1,9 +1,9 @@ -function skip() +local function skip() return Spring.GetGameFrame() > 0 end -- Test whether mexes are able to clear queued buildings by shift-clicking -function setup() +local function setup() Test.clearMap() local widget_cmd_extractor_snap = widgetHandler:FindWidget("Extractor Snap (mex/geo)") @@ -25,7 +25,7 @@ function setup() Test.waitTime(10) end -function cleanup() +local function cleanup() Test.clearMap() WG["pregame-build"].setBuildQueue({}) @@ -35,7 +35,7 @@ function cleanup() end -- tests both pregame mex snap behavior, as well as basic queue and blueprint handling -function test() +local function test() local mexUnitDefId = UnitDefNames["armmex"].id local metalSpots = WG["resource_spot_finder"].metalSpotsList @@ -104,3 +104,5 @@ function test() local buildQueue = WG["pregame-build"].getBuildQueue() assert(#buildQueue == 0, "Build queue should be empty") end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/mex-building/pregame_mex_snap.lua b/luaui/Tests/mex-building/pregame_mex_snap.lua index 8b89e4b4337..e496cfb8e3d 100644 --- a/luaui/Tests/mex-building/pregame_mex_snap.lua +++ b/luaui/Tests/mex-building/pregame_mex_snap.lua @@ -1,8 +1,8 @@ -function skip() +local function skip() return Spring.GetGameFrame() > 0 end -function setup() +local function setup() Test.clearMap() widget_cmd_extractor_snap = widgetHandler:FindWidget("Extractor Snap (mex/geo)") @@ -24,7 +24,7 @@ function setup() Test.waitTime(10) end -function cleanup() +local function cleanup() Test.clearMap() WG["pregame-build"].setBuildQueue({}) @@ -33,7 +33,7 @@ function cleanup() Spring.SetCameraState(initialCameraState) end -function test() +local function test() mexUnitDefId = UnitDefNames["armmex"].id metalSpots = WG["resource_spot_finder"].metalSpotsList @@ -81,3 +81,5 @@ function test() 0, }, 0.1) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/select_api/compare_to_spring.lua b/luaui/Tests/select_api/compare_to_spring.lua index 3d04047b68c..71b0c34f005 100644 --- a/luaui/Tests/select_api/compare_to_spring.lua +++ b/luaui/Tests/select_api/compare_to_spring.lua @@ -4,15 +4,15 @@ local selectApi = VFS.Include("luaui/Include/select_api.lua") local nameLookup = {} local passed = true -function skip() +local function skip() return Spring.GetGameFrame() <= 0 or not Platform.gl end -function setup() +local function setup() Test.clearMap() end -function cleanup() +local function cleanup() Spring.SendCommands("setspeed " .. 1) end @@ -227,7 +227,7 @@ end -- for each filter, the sum of {{filter}} and Not_{{filter}} always equals 537. -- this means 6 units are being created but then not included in the tests -- could be 'dbg_sphere' 'dbg_sphere_fullmetal' 'pbr_cube' -function test() +local function test() passed = true local uids = createUnits() local halfSize = math.floor(#uids / 2) @@ -342,3 +342,5 @@ function test() end assert(passed, "read errors above") end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/selftests/test_assertions.lua b/luaui/Tests/selftests/test_assertions.lua index b474bf2161f..e0f246c55e2 100644 --- a/luaui/Tests/selftests/test_assertions.lua +++ b/luaui/Tests/selftests/test_assertions.lua @@ -1,4 +1,4 @@ -function sanityChecks() +local function sanityChecks() -- Just to make sure some standard methods used here work as expected. Spring.GiveOrderToUnit(2, CMD.FIRE_STATE, { 0 }, {}) SyncedProxy.Spring.ValidUnitID(20) @@ -8,7 +8,7 @@ function sanityChecks() assert(err ~= "attempt to yield across metamethod/C-call boundary") end -function failingTests() +local function failingTests() -- All of these fail due to error "attempt to yield across metamethod/C-call boundary" -- This is something to do with how the test system is structured. local res, err = pcall(function() @@ -63,7 +63,7 @@ function failingTests() -- end -function failingWhileSucceedingTests() +local function failingWhileSucceedingTests() -- these ones are actually failing even when they don't throw exceptions, -- it's because assertThrows is catching the exception, it's just not -- the one we want. @@ -78,13 +78,13 @@ function failingWhileSucceedingTests() end) end -function testWaitUntil() +local function testWaitUntil() Test.waitUntil(function() return true end) end -function testAssertSuccessBefore() +local function testAssertSuccessBefore() -- test the method succeeding assertSuccessBefore(1, 10, function() return true @@ -102,7 +102,7 @@ function testAssertSuccessBefore() end, "error") end -function testAssertThrows() +local function testAssertThrows() -- test detecting an exception assertThrows(function() error("error") @@ -123,7 +123,7 @@ function testAssertThrows() end) end -function testAssertThrowsMessage() +local function testAssertThrowsMessage() -- test throwing a specific message assertThrowsMessage(function() error("error") @@ -157,7 +157,7 @@ function testAssertThrowsMessage() end, "error") end -function testAssertEqual() +local function testAssertEqual() -- test numeric mismatch assertThrowsMessage(function() assertEqual(1, 2) @@ -179,7 +179,7 @@ function testAssertEqual() assertEqual(nil, nil) end -function test() +local function test() sanityChecks() testWaitUntil() testAssertThrows() @@ -189,3 +189,5 @@ function test() --failingTests() failingWhileSucceedingTests() end + +return { test = test } diff --git a/luaui/Tests/selftests/test_callins.lua b/luaui/Tests/selftests/test_callins.lua index 2bb532bac7b..648711d844f 100644 --- a/luaui/Tests/selftests/test_callins.lua +++ b/luaui/Tests/selftests/test_callins.lua @@ -1,16 +1,16 @@ -function skip() +local function skip() return Spring.GetGameFrame() <= 0 end -function setup() +local function setup() Test.clearMap() end -function cleanup() +local function cleanup() Test.clearMap() end -function runBaseTests() +local function runBaseTests() -- double expect should throw Test.expectCallin("UnitCommand") @@ -39,7 +39,7 @@ function runBaseTests() Test.clearCallins() end -function runWaitUntil(countOnly, reallyCountOnly, wait, expect, clear) +local function runWaitUntil(countOnly, reallyCountOnly, wait, expect, clear) -- test waitUntilCallinArgs with and without expectCallin preregister local myTeamID = Spring.GetMyTeamID() if expect then @@ -71,7 +71,7 @@ function runWaitUntil(countOnly, reallyCountOnly, wait, expect, clear) end end -function test() +local function test() local FULL = false local COUNT = true local EXPECT = true @@ -98,3 +98,5 @@ function test() runWaitUntil(FULL, FULL, 0, not EXPECT, CLEAR) Test.setUnsafeCallins(false) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Tests/weapondefs/test_flighttime.lua b/luaui/Tests/weapondefs/test_flighttime.lua index f7f98599503..c1c8e7be371 100644 --- a/luaui/Tests/weapondefs/test_flighttime.lua +++ b/luaui/Tests/weapondefs/test_flighttime.lua @@ -1,9 +1,9 @@ -function skip() +local function skip() -- TODO re-enable and debug. Disabled 2025-09-30 to unblock CICD return true end -function setup() +local function setup() Test.clearMap() Spring.SendCommands("editdefs 1") @@ -11,7 +11,7 @@ function setup() Spring.SendCommands("setspeed 5") end -function cleanup() +local function cleanup() Test.clearMap() Spring.SendCommands("globallos") @@ -19,7 +19,7 @@ function cleanup() Spring.SendCommands("editdefs 0") end -function runDistanceTest(flightTime, shouldAlive) +local function runDistanceTest(flightTime, shouldAlive) SyncedRun(function(locals) local flightTime = locals.flightTime for weaponDefID, weaponDef in pairs(WeaponDefs) do @@ -88,8 +88,10 @@ function runDistanceTest(flightTime, shouldAlive) assertEqual(isAlive2, shouldAlive) end -function test() +local function test() runDistanceTest(30, true) Test.clearMap() runDistanceTest(0, false) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/TestsExamples/balance/test_arm_vs_cor_fighters.lua b/luaui/TestsExamples/balance/test_arm_vs_cor_fighters.lua index 6ecb36a4fed..6d8d6d806e7 100644 --- a/luaui/TestsExamples/balance/test_arm_vs_cor_fighters.lua +++ b/luaui/TestsExamples/balance/test_arm_vs_cor_fighters.lua @@ -1,18 +1,18 @@ -function skip() +local function skip() return Spring.GetGameFrame() <= 0 end -function setup() +local function setup() Test.clearMap() end -function cleanup() +local function cleanup() Test.clearMap() Spring.SendCommands("setspeed " .. 1) end -function test() +local function test() local units = { [0] = "armfig", [1] = "corveng", @@ -94,3 +94,5 @@ function test() -- cor fighters should win assert(winner == 1) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/TestsExamples/balance/test_grunts_vs_pawns.lua b/luaui/TestsExamples/balance/test_grunts_vs_pawns.lua index 881ebc567f1..eac4e8c4242 100644 --- a/luaui/TestsExamples/balance/test_grunts_vs_pawns.lua +++ b/luaui/TestsExamples/balance/test_grunts_vs_pawns.lua @@ -1,18 +1,18 @@ -function skip() +local function skip() return Spring.GetGameFrame() <= 0 end -function setup() +local function setup() Test.clearMap() end -function cleanup() +local function cleanup() Test.clearMap() Spring.SendCommands("setspeed " .. 1) end -function test() +local function test() local units = { [0] = "armpw", [1] = "corak", @@ -82,3 +82,5 @@ function test() -- pawns should win assert(winner == 0) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/TestsExamples/gui_battle_resource_tracker/test_gui_battle_resource_tracker.lua b/luaui/TestsExamples/gui_battle_resource_tracker/test_gui_battle_resource_tracker.lua index a0171c3ea27..4c54a5ef90e 100644 --- a/luaui/TestsExamples/gui_battle_resource_tracker/test_gui_battle_resource_tracker.lua +++ b/luaui/TestsExamples/gui_battle_resource_tracker/test_gui_battle_resource_tracker.lua @@ -1,10 +1,10 @@ local widgetName = "Battle Resource Tracker" -function skip() +local function skip() return Spring.GetGameFrame() <= 0 end -function setup() +local function setup() assert(widgetHandler.knownWidgets[widgetName] ~= nil) Test.clearMap() @@ -12,11 +12,11 @@ function setup() Test.prepareWidget(widgetName) end -function cleanup() +local function cleanup() Test.clearMap() end -function test() +local function test() widget = widgetHandler:FindWidget(widgetName) assert(widget) @@ -60,3 +60,5 @@ function test() end assert(totalE == n * unitE) end + +return { skip = skip, setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/TestsExamples/test_utilities/test_mock.lua b/luaui/TestsExamples/test_utilities/test_mock.lua index 32a895b5184..73180f42753 100644 --- a/luaui/TestsExamples/test_utilities/test_mock.lua +++ b/luaui/TestsExamples/test_utilities/test_mock.lua @@ -1,4 +1,4 @@ -function test() +local function test() mock_SpringGetModKeyState = Test.mock(Spring, "GetModKeyState", function() return true, false, true, false end) @@ -7,3 +7,5 @@ function test() assert(#mock_SpringGetModKeyState.calls == 1) end + +return { test = test } diff --git a/luaui/TestsExamples/test_utilities/test_wait.lua b/luaui/TestsExamples/test_utilities/test_wait.lua index 78140bea0c3..0b0d36410b3 100644 --- a/luaui/TestsExamples/test_utilities/test_wait.lua +++ b/luaui/TestsExamples/test_utilities/test_wait.lua @@ -1,13 +1,13 @@ -function setup() +local function setup() Test.clearMap() Test.expectCallin("UnitCreated") end -function cleanup() +local function cleanup() Test.clearMap() end -function test() +local function test() Spring.Echo("[test_wait] waiting 5 frames") Test.waitFrames(5) @@ -34,3 +34,5 @@ function test() Spring.Echo("[test_wait] waiting 1000 ms") Test.waitTime(1000) end + +return { setup = setup, test = test, cleanup = cleanup } diff --git a/luaui/Widgets/dbg_test_runner.lua b/luaui/Widgets/dbg_test_runner.lua index cac2e59c60e..bce1b646aad 100644 --- a/luaui/Widgets/dbg_test_runner.lua +++ b/luaui/Widgets/dbg_test_runner.lua @@ -855,6 +855,11 @@ end local function runTestInternal() log(LOG.DEBUG, "[runTestInternal]") + -- Hooks are setfenv-injected into the test env; capture as locals so the + -- analyzer/LLM don't rewrite the bare reads (`_G.cleanup` crashes at runtime). + ---@diagnostic disable-next-line: undefined-global + local skip, setup, test, cleanup = skip, setup, test, cleanup + if testRunState.filesIndex == 1 then TestExtraUtils.startTests() end @@ -1041,13 +1046,23 @@ local function loadTestFromFile(filename) setfenv(chunk, testEnvironment) - local success, err = pcall(chunk) + local success, result = pcall(chunk) if not success then - return false, err + return false, result + end + + -- Test files return a table { skip?, setup?, test, cleanup? }; merge its + -- hooks into testEnvironment so runTestInternal (which reads them as bare + -- globals under setfenv) finds them. + if type(result) ~= "table" then + return false, filename .. " did not return a test table { skip, setup, test, cleanup }" + end + for k, v in pairs(result) do + testEnvironment[k] = v end if testEnvironment.test == nil then - return false, "no test() function" + return false, filename .. " returned a table without a test() function" end setfenv(testEnvironment.__runTestInternal, testEnvironment)