From 981ca957c1755fd59bfbdd3ef95d74ea790e15a5 Mon Sep 17 00:00:00 2001 From: elsid Date: Wed, 26 Feb 2025 23:12:17 +0100 Subject: [PATCH] Register global tests to run them --- .../integration_tests/test_lua_api/test.lua | 135 +++++++----------- .../testing_util/testing_util.lua | 51 ++++--- scripts/data/morrowind_tests/global.lua | 23 +-- .../data/morrowind_tests/global_dialogues.lua | 66 ++++----- .../data/morrowind_tests/global_issues.lua | 66 ++++----- .../data/morrowind_tests/global_mwscript.lua | 64 ++++----- 6 files changed, 181 insertions(+), 224 deletions(-) diff --git a/scripts/data/integration_tests/test_lua_api/test.lua b/scripts/data/integration_tests/test_lua_api/test.lua index 5686065698..f55c44df63 100644 --- a/scripts/data/integration_tests/test_lua_api/test.lua +++ b/scripts/data/integration_tests/test_lua_api/test.lua @@ -7,7 +7,7 @@ local vfs = require('openmw.vfs') local world = require('openmw.world') local I = require('openmw.interfaces') -local function testTimers() +testing.registerGlobalTest('testTimers', function() testing.expectAlmostEqual(core.getGameTimeScale(), 30, 'incorrect getGameTimeScale() result') testing.expectAlmostEqual(core.getSimulationTimeScale(), 1, 'incorrect getSimulationTimeScale result') @@ -39,9 +39,9 @@ local function testTimers() testing.expectGreaterOrEqual(ts1, 0.5, 'async:newSimulationTimer failed') testing.expectGreaterOrEqual(th2, 72, 'async:newUnsavableGameTimer failed') testing.expectGreaterOrEqual(ts2, 1, 'async:newUnsavableSimulationTimer failed') -end +end) -local function testTeleport() +testing.registerGlobalTest('testTeleport', function() local player = world.players[1] player:teleport('', util.vector3(100, 50, 500), util.transform.rotateZ(math.rad(90))) coroutine.yield() @@ -72,16 +72,16 @@ local function testTeleport() testing.expectEqualWithDelta(player.position.x, 50, 1, 'incorrect position after teleporting') testing.expectEqualWithDelta(player.position.y, -100, 1, 'incorrect position after teleporting') testing.expectEqualWithDelta(player.rotation:getYaw(), math.rad(-90), 0.05, 'teleporting changes rotation') -end +end) -local function testGetGMST() +testing.registerGlobalTest('testGetGMST', function() testing.expectEqual(core.getGMST('non-existed gmst'), nil) testing.expectEqual(core.getGMST('Water_RippleFrameCount'), 4) testing.expectEqual(core.getGMST('Inventory_DirectionalDiffuseR'), 0.5) testing.expectEqual(core.getGMST('Level_Up_Level2'), 'something') -end +end) -local function testMWScript() +testing.registerGlobalTest('testMWScript', function() local variableStoreCount = 18 local variableStore = world.mwscript.getGlobalVariables(player) testing.expectEqual(variableStoreCount, #variableStore) @@ -101,7 +101,7 @@ local function testMWScript() indexCheck = indexCheck + 1 end testing.expectEqual(variableStoreCount, indexCheck) -end +end) local function testRecordStore(store, storeName, skipPairs) testing.expect(store.records) @@ -122,7 +122,7 @@ local function testRecordStore(store, storeName, skipPairs) testing.expectEqual(status, true, storeName) end -local function testRecordStores() +testing.registerGlobalTest('testRecordStores', function() for key, type in pairs(types) do if type.records then testRecordStore(type, key) @@ -141,9 +141,9 @@ local function testRecordStores() testRecordStore(types.NPC.classes, "classes") testRecordStore(types.NPC.races, "races") testRecordStore(types.Player.birthSigns, "birthSigns") -end +end) -local function testRecordCreation() +testing.registerGlobalTest('testRecordCreation', function() local newLight = { isCarriable = true, isDynamic = true, @@ -166,9 +166,9 @@ local function testRecordCreation() for key, value in pairs(newLight) do testing.expectEqual(record[key], value) end -end +end) -local function testUTF8Chars() +testing.registerGlobalTest('testUTF8Chars', function() testing.expectEqual(utf8.codepoint("😀"), 0x1F600) local chars = {} @@ -193,9 +193,9 @@ local function testUTF8Chars() testing.expectEqual(utf8.codepoint(char), codepoint) testing.expectEqual(utf8.len(char), 1) end -end +end) -local function testUTF8Strings() +testing.registerGlobalTest('testUTF8Strings', function() local utf8str = "Hello, 你好, 🌎!" local str = "" @@ -206,9 +206,9 @@ local function testUTF8Strings() testing.expectEqual(utf8.len(utf8str), 13) testing.expectEqual(utf8.offset(utf8str, 9), 11) -end +end) -local function testMemoryLimit() +testing.registerGlobalTest('testMemoryLimit', function() local ok, err = pcall(function() local t = {} local n = 1 @@ -219,7 +219,7 @@ local function testMemoryLimit() end) testing.expectEqual(ok, false, 'Script reaching memory limit should fail') testing.expectEqual(err, 'not enough memory') -end +end) local function initPlayer() local player = world.players[1] @@ -228,7 +228,7 @@ local function initPlayer() return player end -local function testVFS() +testing.registerGlobalTest('testVFS', function() local file = 'test_vfs_dir/lines.txt' local nosuchfile = 'test_vfs_dir/nosuchfile' testing.expectEqual(vfs.fileExists(file), true, 'lines.txt should exist') @@ -272,9 +272,9 @@ local function testVFS() for _,v in pairs(expectedLines) do testing.expectEqual(getLine(), v) end -end +end) -local function testCommitCrime() +testing.registerGlobalTest('testCommitCrime', function() local player = initPlayer() testing.expectEqual(player == nil, false, 'A viable player reference should exist to run `testCommitCrime`') testing.expectEqual(I.Crimes == nil, false, 'Crimes interface should be available in global contexts') @@ -294,80 +294,41 @@ local function testCommitCrime() types.Player.setCrimeLevel(player, 0) testing.expectEqual(I.Crimes.commitCrime(player, { victim = victim, type = types.Player.OFFENSE_TYPE.Theft, arg = 50 }).wasCrimeSeen, true, "Running a crime with a valid victim should notify them when the player is not sneaking, even if it's not explicitly passed in") testing.expectEqual(types.Player.getCrimeLevel(player), 0, "Crime level should not change if the victim's alarm value is low and there's no other witnesses") -end +end) -local function testRecordModelProperty() +testing.registerGlobalTest('testRecordModelProperty', function() local player = initPlayer() testing.expectEqual(types.NPC.record(player).model, 'meshes/basicplayer.dae') +end) + +local function registerPlayerTest(name) + testing.registerGlobalTest(name, function() + local player = initPlayer() + testing.runLocalTest(player, name) + end) end -tests = { - {'timers', testTimers}, - {'rotating player with controls.yawChange should change rotation', function() - local player = initPlayer() - testing.runLocalTest(player, 'playerYawRotation') - end}, - {'rotating player with controls.pitchChange should change rotation', function() - local player = initPlayer() - testing.runLocalTest(player, 'playerPitchRotation') - end}, - {'rotating player with controls.pitchChange and controls.yawChange should change rotation', function() - local player = initPlayer() - testing.runLocalTest(player, 'playerPitchAndYawRotation') - end}, - {'rotating player should not lead to nan rotation', function() - local player = initPlayer() - testing.runLocalTest(player, 'playerRotation') - end}, - {'playerForwardRunning', function() - local player = initPlayer() - testing.runLocalTest(player, 'playerForwardRunning') - end}, - {'playerDiagonalWalking', function() - local player = initPlayer() - testing.runLocalTest(player, 'playerDiagonalWalking') - end}, - {'findPath', function() - local player = initPlayer() - testing.runLocalTest(player, 'findPath') - end}, - {'findRandomPointAroundCircle', function() - local player = initPlayer() - testing.runLocalTest(player, 'findRandomPointAroundCircle') - end}, - {'castNavigationRay', function() - local player = initPlayer() - testing.runLocalTest(player, 'castNavigationRay') - end}, - {'findNearestNavMeshPosition', function() - local player = initPlayer() - testing.runLocalTest(player, 'findNearestNavMeshPosition') - end}, - {'teleport', testTeleport}, - {'getGMST', testGetGMST}, - {'recordStores', testRecordStores}, - {'recordCreation', testRecordCreation}, - {'utf8Chars', testUTF8Chars}, - {'utf8Strings', testUTF8Strings}, - {'mwscript', testMWScript}, - {'testMemoryLimit', testMemoryLimit}, - {'playerMemoryLimit', function() - local player = initPlayer() - testing.runLocalTest(player, 'playerMemoryLimit') - end}, - {'player with equipped weapon on attack should damage health of other actors', function() - local player = initPlayer() - world.createObject('basic_dagger1h', 1):moveInto(player) - testing.runLocalTest(player, 'playerWeaponAttack') - end}, - {'vfs', testVFS}, - {'testCommitCrime', testCommitCrime}, - {'recordModelProperty', testRecordModelProperty}, -} +registerPlayerTest('playerYawRotation') +registerPlayerTest('playerPitchRotation') +registerPlayerTest('playerPitchAndYawRotation') +registerPlayerTest('playerRotation') +registerPlayerTest('playerForwardRunning') +registerPlayerTest('playerDiagonalWalking') +registerPlayerTest('findPath') +registerPlayerTest('findRandomPointAroundCircle') +registerPlayerTest('castNavigationRay') +registerPlayerTest('findNearestNavMeshPosition') +registerPlayerTest('playerMemoryLimit') + +testing.registerGlobalTest('playerWeaponAttack', function() + local player = initPlayer() + world.createObject('basic_dagger1h', 1):moveInto(player) + testing.runLocalTest(player, 'playerWeaponAttack') +end) return { engineHandlers = { - onUpdate = testing.testRunner(tests), + onUpdate = testing.makeUpdateGlobal(), }, eventHandlers = testing.eventHandlers, } diff --git a/scripts/data/integration_tests/testing_util/testing_util.lua b/scripts/data/integration_tests/testing_util/testing_util.lua index 7b886636ed..2d67c6ca8a 100644 --- a/scripts/data/integration_tests/testing_util/testing_util.lua +++ b/scripts/data/integration_tests/testing_util/testing_util.lua @@ -2,12 +2,19 @@ local core = require('openmw.core') local util = require('openmw.util') local M = {} + +local globalTestsOrder = {} +local globalTests = {} +local globalTestRunner = nil + +local localTests = {} +local localTestRunner = nil local currentLocalTest = nil local currentLocalTestError = nil -function M.testRunner(tests) +function M.makeUpdateGlobal() local fn = function() - for i, test in ipairs(tests) do + for i, test in ipairs(globalTestsOrder) do local name, fn = unpack(test) print('TEST_START', i, name) local status, err = pcall(fn) @@ -27,6 +34,11 @@ function M.testRunner(tests) end end +function M.registerGlobalTest(name, fn) + globalTests[name] = fn + table.insert(globalTestsOrder, {name, fn}) +end + function M.runLocalTest(obj, name) currentLocalTest = name currentLocalTestError = nil @@ -39,7 +51,21 @@ function M.runLocalTest(obj, name) end end -function M.expect(cond, delta, msg) +function M.registerLocalTest(name, fn) + localTests[name] = fn +end + +function M.updateLocal() + if localTestRunner and coroutine.status(localTestRunner) ~= 'dead' then + if not core.isWorldPaused() then + coroutine.resume(localTestRunner) + end + else + localTestRunner = nil + end +end + +function M.expect(cond, msg) if not cond then error(msg or '"true" expected', 2) end @@ -182,28 +208,11 @@ function M.formatActualExpected(actual, expected) return string.format('actual: %s, expected: %s', actual, expected) end -local localTests = {} -local localTestRunner = nil - -function M.registerLocalTest(name, fn) - localTests[name] = fn -end - -function M.updateLocal() - if localTestRunner and coroutine.status(localTestRunner) ~= 'dead' then - if not core.isWorldPaused() then - coroutine.resume(localTestRunner) - end - else - localTestRunner = nil - end -end - M.eventHandlers = { runLocalTest = function(name) -- used only in local scripts fn = localTests[name] if not fn then - core.sendGlobalEvent('localTestFinished', {name=name, errMsg='Test not found'}) + core.sendGlobalEvent('localTestFinished', {name=name, errMsg='Local test is not found'}) return end localTestRunner = coroutine.create(function() diff --git a/scripts/data/morrowind_tests/global.lua b/scripts/data/morrowind_tests/global.lua index fb7113d1b1..a78d90e408 100644 --- a/scripts/data/morrowind_tests/global.lua +++ b/scripts/data/morrowind_tests/global.lua @@ -8,28 +8,13 @@ if not core.contentFiles.has('Morrowind.esm') then error('This test requires Morrowind.esm') end -function makeTests(modules) - local tests = {} - - for _, moduleName in ipairs(modules) do - local module = require(moduleName) - for _, v in ipairs(module) do - table.insert(tests, {string.format('[%s] %s', moduleName, v[1]), v[2]}) - end - end - - return tests -end - -local testModules = { - 'global_issues', - 'global_dialogues', - 'global_mwscript', -} +require('global_issues') +require('global_dialogues') +require('global_mwscript') return { engineHandlers = { - onUpdate = testing.testRunner(makeTests(testModules)), + onUpdate = testing.makeUpdateGlobal(), }, eventHandlers = testing.eventHandlers, } diff --git a/scripts/data/morrowind_tests/global_dialogues.lua b/scripts/data/morrowind_tests/global_dialogues.lua index 397eb8461c..68f4f4a747 100644 --- a/scripts/data/morrowind_tests/global_dialogues.lua +++ b/scripts/data/morrowind_tests/global_dialogues.lua @@ -13,35 +13,37 @@ function iterateOverRecords(records) return firstRecordId, lastRecordId, count end -return { - {'Should support iteration over journal dialogues', function() - local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.journal.records) - testing.expectEqual(firstRecordId, '11111 test journal') - testing.expectEqual(lastRecordId, 'va_vamprich') - testing.expectEqual(count, 632) - end}, - {'Should support iteration over topic dialogues', function() - local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.topic.records) - testing.expectEqual(firstRecordId, '1000-drake pledge') - testing.expectEqual(lastRecordId, 'zenithar') - testing.expectEqual(count, 1698) - end}, - {'Should support iteration over greeting dialogues', function() - local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.greeting.records) - testing.expectEqual(firstRecordId, 'greeting 0') - testing.expectEqual(lastRecordId, 'greeting 9') - testing.expectEqual(count, 10) - end}, - {'Should support iteration over persuasion dialogues', function() - local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.persuasion.records) - testing.expectEqual(firstRecordId, 'admire fail') - testing.expectEqual(lastRecordId, 'taunt success') - testing.expectEqual(count, 10) - end}, - {'Should support iteration over voice dialogues', function() - local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.voice.records) - testing.expectEqual(firstRecordId, 'alarm') - testing.expectEqual(lastRecordId, 'thief') - testing.expectEqual(count, 8) - end}, -} +testing.registerGlobalTest('[dialogues] Should support iteration over journal dialogues', function() + local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.journal.records) + testing.expectEqual(firstRecordId, '11111 test journal') + testing.expectEqual(lastRecordId, 'va_vamprich') + testing.expectEqual(count, 632) +end) + +testing.registerGlobalTest('[dialogues] Should support iteration over topic dialogues', function() + local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.topic.records) + testing.expectEqual(firstRecordId, '1000-drake pledge') + testing.expectEqual(lastRecordId, 'zenithar') + testing.expectEqual(count, 1698) +end) + +testing.registerGlobalTest('[dialogues] Should support iteration over greeting dialogues', function() + local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.greeting.records) + testing.expectEqual(firstRecordId, 'greeting 0') + testing.expectEqual(lastRecordId, 'greeting 9') + testing.expectEqual(count, 10) +end) + +testing.registerGlobalTest('[dialogues] Should support iteration over persuasion dialogues', function() + local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.persuasion.records) + testing.expectEqual(firstRecordId, 'admire fail') + testing.expectEqual(lastRecordId, 'taunt success') + testing.expectEqual(count, 10) +end) + +testing.registerGlobalTest('[dialogues] Should support iteration over voice dialogues', function() + local firstRecordId, lastRecordId, count = iterateOverRecords(core.dialogue.voice.records) + testing.expectEqual(firstRecordId, 'alarm') + testing.expectEqual(lastRecordId, 'thief') + testing.expectEqual(count, 8) +end) diff --git a/scripts/data/morrowind_tests/global_issues.lua b/scripts/data/morrowind_tests/global_issues.lua index 2afad085b0..a3400da87c 100644 --- a/scripts/data/morrowind_tests/global_issues.lua +++ b/scripts/data/morrowind_tests/global_issues.lua @@ -4,37 +4,37 @@ local world = require('openmw.world') local core = require('openmw.core') local types = require('openmw.types') -return { - {'Player should be able to walk up stairs in Ebonheart docks (#4247)', function() - world.players[1]:teleport('', util.vector3(19867, -102180, -79), util.transform.rotateZ(math.rad(91))) - coroutine.yield() - testing.runLocalTest(world.players[1], 'Player should be able to walk up stairs in Ebonheart docks (#4247)') - end}, - {'Guard in Imperial Prison Ship should find path (#7241)', function() - world.players[1]:teleport('Imperial Prison Ship', util.vector3(61, -135, -105), util.transform.rotateZ(math.rad(-20))) - coroutine.yield() - testing.runLocalTest(world.players[1], 'Guard in Imperial Prison Ship should find path (#7241)') - end}, - {'Should keep reference to an object moved into container (#7663)', function() - world.players[1]:teleport('ToddTest', util.vector3(2176, 3648, -191), util.transform.rotateZ(math.rad(0))) - coroutine.yield() - local barrel = world.createObject('barrel_01', 1) - local fargothRing = world.createObject('ring_keley', 1) - coroutine.yield() - testing.expectEqual(types.Container.inventory(barrel):find('ring_keley'), nil) - fargothRing:moveInto(types.Container.inventory(barrel)) - coroutine.yield() - testing.expectEqual(fargothRing.recordId, 'ring_keley') - local isFargothRing = function(actual) - if actual == nil then - return 'ring_keley is not found' - end - if actual.id ~= fargothRing.id then - return 'found ring_keley id does not match expected: actual=' .. tostring(actual.id) - .. ', expected=' .. tostring(fargothRing.id) - end - return '' +testing.registerGlobalTest('[issues] Player should be able to walk up stairs in Ebonheart docks (#4247)', function() + world.players[1]:teleport('', util.vector3(19867, -102180, -79), util.transform.rotateZ(math.rad(91))) + coroutine.yield() + testing.runLocalTest(world.players[1], 'Player should be able to walk up stairs in Ebonheart docks (#4247)') +end) + +testing.registerGlobalTest('[issues] Guard in Imperial Prison Ship should find path (#7241)', function() + world.players[1]:teleport('Imperial Prison Ship', util.vector3(61, -135, -105), util.transform.rotateZ(math.rad(-20))) + coroutine.yield() + testing.runLocalTest(world.players[1], 'Guard in Imperial Prison Ship should find path (#7241)') +end) + +testing.registerGlobalTest('[issues] Should keep reference to an object moved into container (#7663)', function() + world.players[1]:teleport('ToddTest', util.vector3(2176, 3648, -191), util.transform.rotateZ(math.rad(0))) + coroutine.yield() + local barrel = world.createObject('barrel_01', 1) + local fargothRing = world.createObject('ring_keley', 1) + coroutine.yield() + testing.expectEqual(types.Container.inventory(barrel):find('ring_keley'), nil) + fargothRing:moveInto(types.Container.inventory(barrel)) + coroutine.yield() + testing.expectEqual(fargothRing.recordId, 'ring_keley') + local isFargothRing = function(actual) + if actual == nil then + return 'ring_keley is not found' end - testing.expectThat(types.Container.inventory(barrel):find('ring_keley'), isFargothRing) - end}, -} + if actual.id ~= fargothRing.id then + return 'found ring_keley id does not match expected: actual=' .. tostring(actual.id) + .. ', expected=' .. tostring(fargothRing.id) + end + return '' + end + testing.expectThat(types.Container.inventory(barrel):find('ring_keley'), isFargothRing) +end) diff --git a/scripts/data/morrowind_tests/global_mwscript.lua b/scripts/data/morrowind_tests/global_mwscript.lua index a4347ea66e..fec9fcdba5 100644 --- a/scripts/data/morrowind_tests/global_mwscript.lua +++ b/scripts/data/morrowind_tests/global_mwscript.lua @@ -14,38 +14,38 @@ function iterateOverVariables(variables) return first, last, count end -return { - {'Should support iteration over an empty set of script variables', function() - local mainVars = world.mwscript.getGlobalScript('main').variables - local first, last, count = iterateOverVariables(mainVars) - testing.expectEqual(first, nil) - testing.expectEqual(last, nil) - testing.expectEqual(count, 0) - testing.expectEqual(count, #mainVars) - end}, - {'Should support iteration of script variables', function() - local jiub = world.getObjectByFormId(core.getFormId('Morrowind.esm', 172867)) - local jiubVars = world.mwscript.getLocalScript(jiub).variables - local first, last, count = iterateOverVariables(jiubVars) +testing.registerGlobalTest('[mwscript] Should support iteration over an empty set of script variables', function() + local mainVars = world.mwscript.getGlobalScript('main').variables + local first, last, count = iterateOverVariables(mainVars) + testing.expectEqual(first, nil) + testing.expectEqual(last, nil) + testing.expectEqual(count, 0) + testing.expectEqual(count, #mainVars) +end) - testing.expectEqual(first, 'state') - testing.expectEqual(last, 'timer') - testing.expectEqual(count, 3) - testing.expectEqual(count, #jiubVars) - end}, - {'Should support numeric and string indices for getting and setting', function() - local jiub = world.getObjectByFormId(core.getFormId('Morrowind.esm', 172867)) - local jiubVars = world.mwscript.getLocalScript(jiub).variables +testing.registerGlobalTest('[mwscript] Should support iteration of script variables', function() + local jiub = world.getObjectByFormId(core.getFormId('Morrowind.esm', 172867)) + local jiubVars = world.mwscript.getLocalScript(jiub).variables + local first, last, count = iterateOverVariables(jiubVars) - testing.expectEqual(jiubVars[1], jiubVars.state) - testing.expectEqual(jiubVars[2], jiubVars.wandering) - testing.expectEqual(jiubVars[3], jiubVars.timer) + testing.expectEqual(first, 'state') + testing.expectEqual(last, 'timer') + testing.expectEqual(count, 3) + testing.expectEqual(count, #jiubVars) +end) - jiubVars[1] = 123; - testing.expectEqual(jiubVars.state, 123) - jiubVars.wandering = 42; - testing.expectEqual(jiubVars[2], 42) - jiubVars[3] = 1.25; - testing.expectEqual(jiubVars.timer, 1.25) - end}, -} +testing.registerGlobalTest('[mwscript] Should support numeric and string indices for getting and setting', function() + local jiub = world.getObjectByFormId(core.getFormId('Morrowind.esm', 172867)) + local jiubVars = world.mwscript.getLocalScript(jiub).variables + + testing.expectEqual(jiubVars[1], jiubVars.state) + testing.expectEqual(jiubVars[2], jiubVars.wandering) + testing.expectEqual(jiubVars[3], jiubVars.timer) + + jiubVars[1] = 123; + testing.expectEqual(jiubVars.state, 123) + jiubVars.wandering = 42; + testing.expectEqual(jiubVars[2], 42) + jiubVars[3] = 1.25; + testing.expectEqual(jiubVars.timer, 1.25) +end)