openmw/scripts/data/integration_tests/test_lua_api/test.lua

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

364 lines
14 KiB
Lua
Raw Normal View History

2024-06-09 03:13:27 +08:00
local testing = require('testing_util')
2021-07-10 13:43:53 +02:00
local core = require('openmw.core')
local async = require('openmw.async')
local util = require('openmw.util')
2024-03-25 13:46:23 +00:00
local types = require('openmw.types')
local vfs = require('openmw.vfs')
2024-03-22 19:13:39 -05:00
local world = require('openmw.world')
local I = require('openmw.interfaces')
2021-07-10 13:43:53 +02:00
local function testTimers()
testing.expectAlmostEqual(core.getGameTimeScale(), 30, 'incorrect getGameTimeScale() result')
testing.expectAlmostEqual(core.getSimulationTimeScale(), 1, 'incorrect getSimulationTimeScale result')
local startGameTime = core.getGameTime()
local startSimulationTime = core.getSimulationTime()
local ts1, ts2, th1, th2
local cb = async:registerTimerCallback("tfunc", function(arg)
if arg == 'g' then
th1 = core.getGameTime() - startGameTime
else
ts1 = core.getSimulationTime() - startSimulationTime
end
end)
async:newGameTimer(36, cb, 'g')
async:newSimulationTimer(0.5, cb, 's')
async:newUnsavableGameTimer(72, function()
th2 = core.getGameTime() - startGameTime
end)
async:newUnsavableSimulationTimer(1, function()
ts2 = core.getSimulationTime() - startSimulationTime
end)
2024-06-22 02:52:02 +02:00
while not (ts1 and ts2 and th1 and th2) do
coroutine.yield()
end
2021-07-10 13:43:53 +02:00
2022-06-06 01:10:33 +02:00
testing.expectGreaterOrEqual(th1, 36, 'async:newGameTimer failed')
testing.expectGreaterOrEqual(ts1, 0.5, 'async:newSimulationTimer failed')
testing.expectGreaterOrEqual(th2, 72, 'async:newUnsavableGameTimer failed')
testing.expectGreaterOrEqual(ts2, 1, 'async:newUnsavableSimulationTimer failed')
2021-07-10 13:43:53 +02:00
end
local function testTeleport()
player:teleport('', util.vector3(100, 50, 500), util.transform.rotateZ(math.rad(90)))
2021-07-10 13:43:53 +02:00
coroutine.yield()
testing.expect(player.cell.isExterior, 'teleport to exterior failed')
testing.expectEqualWithDelta(player.position.x, 100, 1, 'incorrect position after teleporting')
testing.expectEqualWithDelta(player.position.y, 50, 1, 'incorrect position after teleporting')
testing.expectEqualWithDelta(player.position.z, 500, 1, 'incorrect position after teleporting')
testing.expectEqualWithDelta(player.rotation:getYaw(), math.rad(90), 0.05, 'incorrect yaw rotation after teleporting')
testing.expectEqualWithDelta(player.rotation:getPitch(), math.rad(0), 0.05, 'incorrect pitch rotation after teleporting')
local rotationX1, rotationZ1 = player.rotation:getAnglesXZ()
testing.expectEqualWithDelta(rotationX1, math.rad(0), 0.05, 'incorrect x rotation from getAnglesXZ after teleporting')
testing.expectEqualWithDelta(rotationZ1, math.rad(90), 0.05, 'incorrect z rotation from getAnglesXZ after teleporting')
local rotationZ2, rotationY2, rotationX2 = player.rotation:getAnglesZYX()
testing.expectEqualWithDelta(rotationZ2, math.rad(90), 0.05, 'incorrect z rotation from getAnglesZYX after teleporting')
testing.expectEqualWithDelta(rotationY2, math.rad(0), 0.05, 'incorrect y rotation from getAnglesZYX after teleporting')
testing.expectEqualWithDelta(rotationX2, math.rad(0), 0.05, 'incorrect x rotation from getAnglesZYX after teleporting')
player:teleport('', player.position, {rotation=util.transform.rotateZ(math.rad(-90)), onGround=true})
coroutine.yield()
testing.expectEqualWithDelta(player.rotation:getYaw(), math.rad(-90), 0.05, 'options.rotation is not working')
testing.expectLessOrEqual(player.position.z, 400, 'options.onGround is not working')
2021-07-10 13:43:53 +02:00
player:teleport('', util.vector3(50, -100, 0))
coroutine.yield()
testing.expect(player.cell.isExterior, 'teleport to exterior failed')
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')
2021-07-10 13:43:53 +02:00
end
local function testGetGMST()
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
2024-03-22 19:13:39 -05:00
local function testMWScript()
local variableStoreCount = 18
local variableStore = world.mwscript.getGlobalVariables(player)
2024-03-23 13:27:53 -05:00
testing.expectEqual(variableStoreCount, #variableStore)
2024-11-15 01:53:53 +01:00
2024-03-23 13:27:53 -05:00
variableStore.year = 5
testing.expectEqual(5, variableStore.year)
2024-03-22 19:13:39 -05:00
variableStore.year = 1
local indexCheck = 0
for index, value in ipairs(variableStore) do
2024-03-23 13:27:53 -05:00
testing.expectEqual(variableStore[index], value)
2024-03-22 19:13:39 -05:00
indexCheck = indexCheck + 1
end
2024-03-23 13:27:53 -05:00
testing.expectEqual(variableStoreCount, indexCheck)
2024-03-22 19:13:39 -05:00
indexCheck = 0
for index, value in pairs(variableStore) do
2024-03-23 13:27:53 -05:00
testing.expectEqual(variableStore[index], value)
2024-03-22 19:13:39 -05:00
indexCheck = indexCheck + 1
end
2024-03-23 13:27:53 -05:00
testing.expectEqual(variableStoreCount, indexCheck)
2024-03-22 19:13:39 -05:00
end
2024-06-22 02:52:02 +02:00
local function testRecordStore(store, storeName, skipPairs)
2024-03-25 13:46:23 +00:00
testing.expect(store.records)
local firstRecord = store.records[1]
2024-06-22 02:52:02 +02:00
if not firstRecord then
return
end
testing.expectEqual(firstRecord.id, store.records[firstRecord.id].id)
2024-03-25 13:46:23 +00:00
local status, _ = pcall(function()
2024-06-22 02:52:02 +02:00
for index, value in ipairs(store.records) do
if value.id == firstRecord.id then
testing.expectEqual(index, 1, storeName)
break
end
end
2024-03-25 13:46:23 +00:00
end)
2024-06-22 02:52:02 +02:00
testing.expectEqual(status, true, storeName)
2024-03-25 13:46:23 +00:00
end
local function testRecordStores()
for key, type in pairs(types) do
if type.records then
2024-06-22 02:52:02 +02:00
testRecordStore(type, key)
2024-03-25 13:46:23 +00:00
end
end
2024-06-22 02:52:02 +02:00
testRecordStore(core.magic.enchantments, "enchantments")
testRecordStore(core.magic.effects, "effects", true)
testRecordStore(core.magic.spells, "spells")
2024-03-25 13:46:23 +00:00
2024-06-22 02:52:02 +02:00
testRecordStore(core.stats.Attribute, "Attribute")
testRecordStore(core.stats.Skill, "Skill")
2024-03-25 13:46:23 +00:00
2024-06-22 02:52:02 +02:00
testRecordStore(core.sound, "sound")
testRecordStore(core.factions, "factions")
2024-03-25 13:46:23 +00:00
2024-06-22 02:52:02 +02:00
testRecordStore(types.NPC.classes, "classes")
testRecordStore(types.NPC.races, "races")
testRecordStore(types.Player.birthSigns, "birthSigns")
2024-03-25 13:46:23 +00:00
end
local function testRecordCreation()
local newLight = {
isCarriable = true,
isDynamic = true,
isFire =false,
isFlicker = false,
isFlickerSlow = false,
isNegative = false,
isOffByDefault = false,
isPulse = false,
weight = 1,
value = 10,
duration = 12,
radius = 30,
color = 5,
name = "TestLight",
model = "meshes/marker_door.dae"
}
local draft = types.Light.createRecordDraft(newLight)
local record = world.createRecord(draft)
for key, value in pairs(newLight) do
2024-06-22 02:52:02 +02:00
testing.expectEqual(record[key], value)
end
end
local function testUTF8Chars()
testing.expectEqual(utf8.codepoint("😀"), 0x1F600)
2024-06-09 03:13:27 +08:00
local chars = {}
for codepoint = 0, 0x10FFFF do
local char = utf8.char(codepoint)
local charSize = string.len(char)
testing.expect(not chars[char], nil, "Duplicate UTF-8 character: " .. char)
chars[char] = true
if codepoint <= 0x7F then
testing.expectEqual(charSize, 1)
elseif codepoint <= 0x7FF then
testing.expectEqual(charSize, 2)
elseif codepoint <= 0xFFFF then
testing.expectEqual(charSize, 3)
elseif codepoint <= 0x10FFFF then
testing.expectEqual(charSize, 4)
end
testing.expectEqual(utf8.codepoint(char), codepoint)
testing.expectEqual(utf8.len(char), 1)
end
end
local function testUTF8Strings()
local utf8str = "Hello, 你好, 🌎!"
2024-06-09 03:13:27 +08:00
local str = ""
for utf_char in utf8str:gmatch(utf8.charpattern) do
str = str .. utf_char
end
testing.expectEqual(str, utf8str)
testing.expectEqual(utf8.len(utf8str), 13)
testing.expectEqual(utf8.offset(utf8str, 9), 11)
end
2024-08-03 12:55:24 +02:00
local function testMemoryLimit()
local ok, err = pcall(function()
local t = {}
local n = 1
while true do
t[n] = n
n = n + 1
end
end)
testing.expectEqual(ok, false, 'Script reaching memory limit should fail')
testing.expectEqual(err, 'not enough memory')
end
2022-06-06 01:10:33 +02:00
local function initPlayer()
player:teleport('', util.vector3(4096, 4096, 1745), util.transform.identity)
2022-06-06 01:10:33 +02:00
coroutine.yield()
end
local function testVFS()
local file = 'test_vfs_dir/lines.txt'
testing.expectEqual(vfs.fileExists(file), true, 'lines.txt should exist')
testing.expectEqual(vfs.fileExists('test_vfs_dir/nosuchfile'), false, 'nosuchfile should not exist')
local getLine = vfs.lines(file)
for _,v in pairs({ '1', '2', '', '4' }) do
testing.expectEqual(getLine(), v)
end
testing.expectEqual(getLine(), nil, 'All lines should have been read')
local ok = pcall(function()
vfs.lines('test_vfs_dir/nosuchfile')
end)
testing.expectEqual(ok, false, 'Should not be able to read lines from nonexistent file')
local getPath = vfs.pathsWithPrefix('test_vfs_dir/')
testing.expectEqual(getPath(), file)
testing.expectEqual(getPath(), nil, 'All paths should have been read')
local handle = vfs.open(file)
testing.expectEqual(vfs.type(handle), 'file', 'File should be open')
testing.expectEqual(handle.fileName, file)
local n1, n2, _, l3, l4 = handle:read("*n", "*number", "*l", "*line", "*l")
testing.expectEqual(n1, 1)
testing.expectEqual(n2, 2)
testing.expectEqual(l3, '')
testing.expectEqual(l4, '4')
testing.expectEqual(handle:seek('set', 0), 0, 'Reading should happen from the start of the file')
testing.expectEqual(handle:read("*a"), '1\n2\n\n4')
testing.expectEqual(handle:close(), true, 'File should be closeable')
testing.expectEqual(vfs.type(handle), 'closed file', 'File should be closed')
end
local function testCommitCrime()
initPlayer()
local player = world.players[1]
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')
-- Reset crime level to have a clean slate
2024-11-15 01:53:53 +01:00
types.Player.setCrimeLevel(player, 0)
testing.expectEqual(I.Crimes.commitCrime(player, { type = types.Player.OFFENSE_TYPE.Theft, victim = player, arg = 100}).wasCrimeSeen, false, "Running the crime with the player as the victim should not result in a seen crime")
testing.expectEqual(I.Crimes.commitCrime(player, { type = types.Player.OFFENSE_TYPE.Theft, arg = 50 }).wasCrimeSeen, false, "Running the crime with no victim and a type shouldn't raise errors")
testing.expectEqual(I.Crimes.commitCrime(player, { type = types.Player.OFFENSE_TYPE.Murder }).wasCrimeSeen, false, "Running a murder crime should work even without a victim")
-- Create a mockup target for crimes
local victim = world.createObject(types.NPC.record(player).id)
victim:teleport(player.cell, player.position + util.vector3(0, 300, 0))
coroutine.yield()
-- Reset crime level for testing with a valid victim
2024-11-15 01:53:53 +01:00
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
2024-11-15 01:53:53 +01:00
local function testRecordModelProperty()
initPlayer()
local player = world.players[1]
testing.expectEqual(types.NPC.record(player).model, 'meshes/basicplayer.dae')
2024-11-15 01:53:53 +01:00
end
2021-07-10 13:43:53 +02:00
tests = {
{'timers', testTimers},
{'rotating player with controls.yawChange should change rotation', function()
initPlayer()
testing.runLocalTest(player, 'playerYawRotation')
end},
{'rotating player with controls.pitchChange should change rotation', function()
initPlayer()
testing.runLocalTest(player, 'playerPitchRotation')
end},
{'rotating player with controls.pitchChange and controls.yawChange should change rotation', function()
2022-06-06 01:10:33 +02:00
initPlayer()
testing.runLocalTest(player, 'playerPitchAndYawRotation')
2022-06-06 01:10:33 +02:00
end},
{'rotating player should not lead to nan rotation', function()
initPlayer()
testing.runLocalTest(player, 'playerRotation')
end},
2022-06-06 01:10:33 +02:00
{'playerForwardRunning', function()
initPlayer()
testing.runLocalTest(player, 'playerForwardRunning')
end},
{'playerDiagonalWalking', function()
initPlayer()
testing.runLocalTest(player, 'playerDiagonalWalking')
end},
{'findPath', function()
initPlayer()
testing.runLocalTest(player, 'findPath')
end},
{'findRandomPointAroundCircle', function()
initPlayer()
testing.runLocalTest(player, 'findRandomPointAroundCircle')
end},
{'castNavigationRay', function()
initPlayer()
testing.runLocalTest(player, 'castNavigationRay')
end},
{'findNearestNavMeshPosition', function()
initPlayer()
testing.runLocalTest(player, 'findNearestNavMeshPosition')
end},
2021-07-10 13:43:53 +02:00
{'teleport', testTeleport},
{'getGMST', testGetGMST},
2024-03-25 13:46:23 +00:00
{'recordStores', testRecordStores},
{'recordCreation', testRecordCreation},
{'utf8Chars', testUTF8Chars},
{'utf8Strings', testUTF8Strings},
2024-03-22 19:13:39 -05:00
{'mwscript', testMWScript},
2024-08-03 12:55:24 +02:00
{'testMemoryLimit', testMemoryLimit},
{'playerMemoryLimit', function()
initPlayer()
testing.runLocalTest(player, 'playerMemoryLimit')
2024-06-25 01:13:58 +02:00
end},
{'player with equipped weapon on attack should damage health of other actors', function()
initPlayer()
world.createObject('basic_dagger1h', 1):moveInto(player)
testing.runLocalTest(player, 'playerWeaponAttack')
end},
{'vfs', testVFS},
2024-11-15 01:53:53 +01:00
{'testCommitCrime', testCommitCrime},
{'recordModelProperty', testRecordModelProperty},
2021-07-10 13:43:53 +02:00
}
return {
engineHandlers = {
onUpdate = testing.testRunner(tests),
onPlayerAdded = function(p) player = p end,
},
eventHandlers = testing.eventHandlers,
2024-03-22 19:14:28 -05:00
}