mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-28 21:07:59 +03:00
Merge branch 'lua_save_load_test' into 'master'
Some checks failed
Build and test / MacOS (push) Has been cancelled
Build and test / Read .env file and expose it as output (push) Has been cancelled
Build and test / Ubuntu (push) Has been cancelled
Build and test / Windows (2019) (push) Has been cancelled
Build and test / Windows (2022) (push) Has been cancelled
Some checks failed
Build and test / MacOS (push) Has been cancelled
Build and test / Read .env file and expose it as output (push) Has been cancelled
Build and test / Ubuntu (push) Has been cancelled
Build and test / Windows (2019) (push) Has been cancelled
Build and test / Windows (2022) (push) Has been cancelled
Add Lua integration tests for loading and saving See merge request OpenMW/openmw!4604
This commit is contained in:
commit
72aefbf191
6 changed files with 324 additions and 105 deletions
|
@ -326,6 +326,24 @@ testing.registerGlobalTest('player weapon attack', function()
|
|||
testing.runLocalTest(player, 'player weapon attack')
|
||||
end)
|
||||
|
||||
testing.registerGlobalTest('load while teleporting - init player', function()
|
||||
local player = world.players[1]
|
||||
player:teleport('Museum of Wonders', util.vector3(0, -1500, 111), util.transform.rotateZ(math.rad(180)))
|
||||
end)
|
||||
|
||||
testing.registerGlobalTest('load while teleporting - teleport', function()
|
||||
local player = world.players[1]
|
||||
local landracer = world.createObject('landracer')
|
||||
landracer:teleport(player.cell, player.position + util.vector3(0, 500, 0))
|
||||
coroutine.yield()
|
||||
|
||||
local door = world.getObjectByFormId(core.getFormId('the_hub.omwaddon', 26))
|
||||
door:activateBy(player)
|
||||
coroutine.yield()
|
||||
|
||||
landracer:teleport(player.cell, player.position)
|
||||
end)
|
||||
|
||||
return {
|
||||
engineHandlers = {
|
||||
onUpdate = testing.updateGlobal,
|
||||
|
|
|
@ -1,6 +1,57 @@
|
|||
local testing = require('testing_util')
|
||||
local matchers = require('matchers')
|
||||
local menu = require('openmw.menu')
|
||||
|
||||
testing.registerMenuTest('save and load', function()
|
||||
menu.newGame()
|
||||
coroutine.yield()
|
||||
menu.saveGame('save and load')
|
||||
coroutine.yield()
|
||||
|
||||
local directorySaves = {}
|
||||
directorySaves['save_and_load.omwsave'] = {
|
||||
playerName = '',
|
||||
playerLevel = 1,
|
||||
timePlayed = 0,
|
||||
description = 'save and load',
|
||||
contentFiles = {
|
||||
'builtin.omwscripts',
|
||||
'template.omwgame',
|
||||
'landracer.omwaddon',
|
||||
'the_hub.omwaddon',
|
||||
'test_lua_api.omwscripts',
|
||||
},
|
||||
creationTime = matchers.isAny(),
|
||||
}
|
||||
local expectedAllSaves = {}
|
||||
expectedAllSaves[' - 1'] = directorySaves
|
||||
|
||||
testing.expectThat(menu.getAllSaves(), matchers.equalTo(expectedAllSaves))
|
||||
|
||||
menu.loadGame(' - 1', 'save_and_load.omwsave')
|
||||
coroutine.yield()
|
||||
|
||||
menu.deleteGame(' - 1', 'save_and_load.omwsave')
|
||||
testing.expectThat(menu.getAllSaves(), matchers.equalTo({}))
|
||||
end)
|
||||
|
||||
testing.registerMenuTest('load while teleporting', function()
|
||||
menu.newGame()
|
||||
coroutine.yield()
|
||||
|
||||
testing.runGlobalTest('load while teleporting - init player')
|
||||
|
||||
menu.saveGame('load while teleporting')
|
||||
coroutine.yield()
|
||||
|
||||
testing.runGlobalTest('load while teleporting - teleport')
|
||||
|
||||
menu.loadGame(' - 1', 'load_while_teleporting.omwsave')
|
||||
coroutine.yield()
|
||||
|
||||
menu.deleteGame(' - 1', 'load_while_teleporting.omwsave')
|
||||
end)
|
||||
|
||||
local function registerGlobalTest(name, description)
|
||||
testing.registerMenuTest(description or name, function()
|
||||
menu.newGame()
|
||||
|
|
|
@ -6,6 +6,7 @@ local input = require('openmw.input')
|
|||
local types = require('openmw.types')
|
||||
local nearby = require('openmw.nearby')
|
||||
local camera = require('openmw.camera')
|
||||
local matchers = require('matchers')
|
||||
|
||||
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Controls, false)
|
||||
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Fighting, false)
|
||||
|
@ -113,13 +114,13 @@ testing.registerLocalTest('player rotation',
|
|||
coroutine.yield()
|
||||
|
||||
local alpha1, gamma1 = self.rotation:getAnglesXZ()
|
||||
testing.expectThat(alpha1, testing.isNotNan(), 'Alpha rotation in XZ convention is nan')
|
||||
testing.expectThat(gamma1, testing.isNotNan(), 'Gamma rotation in XZ convention is nan')
|
||||
testing.expectThat(alpha1, matchers.isNotNan(), 'Alpha rotation in XZ convention is nan')
|
||||
testing.expectThat(gamma1, matchers.isNotNan(), 'Gamma rotation in XZ convention is nan')
|
||||
|
||||
local alpha2, beta2, gamma2 = self.rotation:getAnglesZYX()
|
||||
testing.expectThat(alpha2, testing.isNotNan(), 'Alpha rotation in ZYX convention is nan')
|
||||
testing.expectThat(beta2, testing.isNotNan(), 'Beta rotation in ZYX convention is nan')
|
||||
testing.expectThat(gamma2, testing.isNotNan(), 'Gamma rotation in ZYX convention is nan')
|
||||
testing.expectThat(alpha2, matchers.isNotNan(), 'Alpha rotation in ZYX convention is nan')
|
||||
testing.expectThat(beta2, matchers.isNotNan(), 'Beta rotation in ZYX convention is nan')
|
||||
testing.expectThat(gamma2, matchers.isNotNan(), 'Gamma rotation in ZYX convention is nan')
|
||||
end
|
||||
end)
|
||||
|
||||
|
|
218
scripts/data/integration_tests/testing_util/matchers.lua
Normal file
218
scripts/data/integration_tests/testing_util/matchers.lua
Normal file
|
@ -0,0 +1,218 @@
|
|||
local module = {}
|
||||
|
||||
---
|
||||
-- Matcher verifying that distance between given value and expected is not greater than maxDistance.
|
||||
-- @function elementsAreArray
|
||||
-- @param expected#vector.
|
||||
-- @usage
|
||||
-- expectThat(util.vector2(0, 0), closeToVector(util.vector2(0, 1), 1))
|
||||
function module.closeToVector(expected, maxDistance)
|
||||
return function(actual)
|
||||
local distance = (expected - actual):length()
|
||||
if distance <= maxDistance then
|
||||
return ''
|
||||
end
|
||||
return string.format('%s is too far from expected %s: %s > %s', actual, expected, distance, maxDistance)
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Matcher verifying that given value is an array each element of which matches elements of expected.
|
||||
-- @function elementsAreArray
|
||||
-- @param expected#array of values or matcher functions.
|
||||
-- @usage
|
||||
-- local t = {42, 13}
|
||||
-- local matcher = function(actual)
|
||||
-- if actual ~= 42 then
|
||||
-- return string.format('%s is not 42', actual)
|
||||
-- end
|
||||
-- return ''
|
||||
-- end
|
||||
-- expectThat({42, 13}, elementsAreArray({matcher, 13}))
|
||||
function module.elementsAreArray(expected)
|
||||
local expected_matchers = {}
|
||||
for i, v in ipairs(expected) do
|
||||
if type(v) == 'function' then
|
||||
expected_matchers[i] = v
|
||||
else
|
||||
expected_matchers[i] = function (other)
|
||||
if expected[i].__eq(expected[i], other) then
|
||||
return ''
|
||||
end
|
||||
return string.format('%s element %s does no match expected: %s', i, other, expected[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
return function(actual)
|
||||
if #actual < #expected_matchers then
|
||||
return string.format('number of elements is less than expected: %s < %s', #actual, #expected_matchers)
|
||||
end
|
||||
local message = ''
|
||||
for i, v in ipairs(actual) do
|
||||
if i > #expected_matchers then
|
||||
message = string.format('%s\n%s element is out of expected range: %s', message, i, #expected_matchers)
|
||||
break
|
||||
end
|
||||
local match_message = expected_matchers[i](v)
|
||||
if match_message ~= '' then
|
||||
message = string.format('%s\n%s', message, match_message)
|
||||
end
|
||||
end
|
||||
return message
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Matcher verifying that given number is not a nan.
|
||||
-- @function isNotNan
|
||||
-- @usage
|
||||
-- expectThat(value, isNotNan())
|
||||
function module.isNotNan()
|
||||
return function(actual)
|
||||
if actual ~= actual then
|
||||
return 'actual value is nan, expected to be not nan'
|
||||
end
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Matcher accepting any value.
|
||||
-- @function isAny
|
||||
-- @usage
|
||||
-- expectThat(value, isAny())
|
||||
function module.isAny()
|
||||
return function(actual)
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
local function serializeArray(a)
|
||||
local result = nil
|
||||
for _, v in ipairs(a) do
|
||||
if result == nil then
|
||||
result = string.format('{%s', serialize(v))
|
||||
else
|
||||
result = string.format('%s, %s', result, serialize(v))
|
||||
end
|
||||
end
|
||||
if result == nil then
|
||||
return '{}'
|
||||
end
|
||||
return string.format('%s}', result)
|
||||
end
|
||||
|
||||
local function serializeTable(t)
|
||||
local result = nil
|
||||
for k, v in pairs(t) do
|
||||
if result == nil then
|
||||
result = string.format('{%q = %s', k, serialize(v))
|
||||
else
|
||||
result = string.format('%s, %q = %s', result, k, serialize(v))
|
||||
end
|
||||
end
|
||||
if result == nil then
|
||||
return '{}'
|
||||
end
|
||||
return string.format('%s}', result)
|
||||
end
|
||||
|
||||
local function isArray(t)
|
||||
local i = 1
|
||||
for _ in pairs(t) do
|
||||
if t[i] == nil then
|
||||
return false
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function serialize(v)
|
||||
local t = type(v)
|
||||
if t == 'string' then
|
||||
return string.format('%q', v)
|
||||
elseif t == 'table' then
|
||||
if isArray(v) then
|
||||
return serializeArray(v)
|
||||
end
|
||||
return serializeTable(v)
|
||||
end
|
||||
return string.format('%s', v)
|
||||
end
|
||||
|
||||
local function compareScalars(v1, v2)
|
||||
if v1 == v2 then
|
||||
return ''
|
||||
end
|
||||
if type(v1) == 'string' then
|
||||
return string.format('%q ~= %q', v1, v2)
|
||||
end
|
||||
return string.format('%s ~= %s', v1, v2)
|
||||
end
|
||||
|
||||
local function collectKeys(t)
|
||||
local result = {}
|
||||
for key in pairs(t) do
|
||||
table.insert(result, key)
|
||||
end
|
||||
table.sort(result)
|
||||
return result
|
||||
end
|
||||
|
||||
local function compareTables(t1, t2)
|
||||
local keys1 = collectKeys(t1)
|
||||
local keys2 = collectKeys(t2)
|
||||
if #keys1 ~= #keys2 then
|
||||
return string.format('table size mismatch: %d ~= %d', #keys1, #keys2)
|
||||
end
|
||||
for i = 1, #keys1 do
|
||||
local key1 = keys1[i]
|
||||
local key2 = keys2[i]
|
||||
if key1 ~= key2 then
|
||||
return string.format('table keys mismatch: %q ~= %q', key1, key2)
|
||||
end
|
||||
local d = compare(t1[key1], t2[key2])
|
||||
if d ~= '' then
|
||||
return string.format('table values mismatch at key %s: %s', serialize(key1), d)
|
||||
end
|
||||
end
|
||||
return ''
|
||||
end
|
||||
|
||||
function compare(v1, v2)
|
||||
local type1 = type(v1)
|
||||
local type2 = type(v2)
|
||||
if type2 == 'function' then
|
||||
return v2(v1)
|
||||
end
|
||||
if type1 ~= type2 then
|
||||
return string.format('types mismatch: %s ~= %s', type1, type2)
|
||||
end
|
||||
if type1 == 'nil' then
|
||||
return ''
|
||||
elseif type1 == 'table' then
|
||||
return compareTables(v1, v2)
|
||||
elseif type1 == 'nil' or type1 == 'boolean' or type1 == 'number' or type1 == 'string' then
|
||||
return compareScalars(v1, v2)
|
||||
end
|
||||
error('unsupported type: %s', type1)
|
||||
end
|
||||
|
||||
---
|
||||
-- Matcher verifying that given value is equal to expected. Accepts nil, boolean, number, string and table or matcher
|
||||
-- function.
|
||||
-- @function equalTo
|
||||
-- @usage
|
||||
-- expectThat({a = {42, 'foo', {b = true}}}, equalTo({a = {42, 'foo', {b = true}}}))
|
||||
function module.equalTo(expected)
|
||||
return function(actual)
|
||||
local diff = compare(actual, expected)
|
||||
if diff == '' then
|
||||
return ''
|
||||
end
|
||||
return string.format('%s; actual: %s; expected: %s', diff, serialize(actual, ''), serialize(expected, ''))
|
||||
end
|
||||
end
|
||||
|
||||
return module
|
|
@ -158,76 +158,6 @@ function M.expectNotEqual(v1, v2, msg)
|
|||
end
|
||||
end
|
||||
|
||||
function M.closeToVector(expected, maxDistance)
|
||||
return function(actual)
|
||||
local distance = (expected - actual):length()
|
||||
if distance <= maxDistance then
|
||||
return ''
|
||||
end
|
||||
return string.format('%s is too far from expected %s: %s > %s', actual, expected, distance, maxDistance)
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Matcher verifying that given value is an array each element of which matches elements of expected.
|
||||
-- @function elementsAreArray
|
||||
-- @param expected#array of values or matcher functions.
|
||||
-- @usage
|
||||
-- local t = {42, 13}
|
||||
-- local matcher = function(actual)
|
||||
-- if actual ~= 42 then
|
||||
-- return string.format('%s is not 42', actual)
|
||||
-- end
|
||||
-- return ''
|
||||
-- end
|
||||
-- expectThat({42, 13}, elementsAreArray({matcher, 13}))
|
||||
function M.elementsAreArray(expected)
|
||||
local expected_matchers = {}
|
||||
for i, v in ipairs(expected) do
|
||||
if type(v) == 'function' then
|
||||
expected_matchers[i] = v
|
||||
else
|
||||
expected_matchers[i] = function (other)
|
||||
if expected[i].__eq(expected[i], other) then
|
||||
return ''
|
||||
end
|
||||
return string.format('%s element %s does no match expected: %s', i, other, expected[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
return function(actual)
|
||||
if #actual < #expected_matchers then
|
||||
return string.format('number of elements is less than expected: %s < %s', #actual, #expected_matchers)
|
||||
end
|
||||
local message = ''
|
||||
for i, v in ipairs(actual) do
|
||||
if i > #expected_matchers then
|
||||
message = string.format('%s\n%s element is out of expected range: %s', message, i, #expected_matchers)
|
||||
break
|
||||
end
|
||||
local match_message = expected_matchers[i](v)
|
||||
if match_message ~= '' then
|
||||
message = string.format('%s\n%s', message, match_message)
|
||||
end
|
||||
end
|
||||
return message
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Matcher verifying that given number is not a nan.
|
||||
-- @function isNotNan
|
||||
-- @usage
|
||||
-- expectThat(value, isNotNan())
|
||||
function M.isNotNan(expected)
|
||||
return function(actual)
|
||||
if actual ~= actual then
|
||||
return 'actual value is nan, expected to be not nan'
|
||||
end
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Verifies that given value matches provided matcher.
|
||||
-- @function expectThat
|
||||
|
|
|
@ -5,6 +5,7 @@ local testing = require('testing_util')
|
|||
local util = require('openmw.util')
|
||||
local types = require('openmw.types')
|
||||
local nearby = require('openmw.nearby')
|
||||
local matchers = require('matchers')
|
||||
|
||||
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Fighting, false)
|
||||
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Jumping, false)
|
||||
|
@ -45,33 +46,33 @@ testing.registerLocalTest('Guard in Imperial Prison Ship should find path (#7241
|
|||
testing.expectLessOrEqual((util.vector2(path[#path].x, path[#path].y) - util.vector2(dst.x, dst.y)):length(), 1, 'Last path point x, y')
|
||||
testing.expectLessOrEqual(path[#path].z - dst.z, 20, 'Last path point z')
|
||||
if agentBounds.shapeType == nearby.COLLISION_SHAPE_TYPE.Aabb then
|
||||
testing.expectThat(path, testing.elementsAreArray({
|
||||
testing.closeToVector(util.vector3(34.29737091064453125, 806.3817138671875, 112.76610565185546875), 1e-1),
|
||||
testing.closeToVector(util.vector3(15, 1102, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(-112, 1110, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(-118, 1393, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(-67.99993896484375, 1421.2000732421875, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(-33.999935150146484375, 1414.4000244140625, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(-6.79993534088134765625, 1380.4000244140625, 85.094573974609375), 1e-1),
|
||||
testing.closeToVector(util.vector3(79, 724, -104.83390045166015625), 1e-1),
|
||||
testing.closeToVector(util.vector3(84, 290.000030517578125, -104.83390045166015625), 1e-1),
|
||||
testing.closeToVector(util.vector3(83.552001953125, 42.26399993896484375, -104.58989715576171875), 1e-1),
|
||||
testing.closeToVector(util.vector3(89, -105, -98.72841644287109375), 1e-1),
|
||||
testing.closeToVector(util.vector3(90, -90, -99.7056884765625), 1e-1),
|
||||
testing.expectThat(path, matchers.elementsAreArray({
|
||||
matchers.closeToVector(util.vector3(34.29737091064453125, 806.3817138671875, 112.76610565185546875), 1e-1),
|
||||
matchers.closeToVector(util.vector3(15, 1102, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-112, 1110, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-118, 1393, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-67.99993896484375, 1421.2000732421875, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-33.999935150146484375, 1414.4000244140625, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-6.79993534088134765625, 1380.4000244140625, 85.094573974609375), 1e-1),
|
||||
matchers.closeToVector(util.vector3(79, 724, -104.83390045166015625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(84, 290.000030517578125, -104.83390045166015625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(83.552001953125, 42.26399993896484375, -104.58989715576171875), 1e-1),
|
||||
matchers.closeToVector(util.vector3(89, -105, -98.72841644287109375), 1e-1),
|
||||
matchers.closeToVector(util.vector3(90, -90, -99.7056884765625), 1e-1),
|
||||
}))
|
||||
elseif agentBounds.shapeType == nearby.COLLISION_SHAPE_TYPE.Cylinder then
|
||||
testing.expectThat(path, testing.elementsAreArray({
|
||||
testing.closeToVector(util.vector3(34.29737091064453125, 806.3817138671875, 112.76610565185546875), 1e-1),
|
||||
testing.closeToVector(util.vector3(-13.5999355316162109375, 1060.800048828125, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(-27.1999359130859375, 1081.2000732421875, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(-81.59993743896484375, 1128.800048828125, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(-101.99993896484375, 1156.0001220703125, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(-118, 1393, 112.2945709228515625), 1e-1),
|
||||
testing.closeToVector(util.vector3(7, 1470, 114.73973846435546875), 1e-1),
|
||||
testing.closeToVector(util.vector3(79, 724, -104.83390045166015625), 1e-1),
|
||||
testing.closeToVector(util.vector3(84, 290.000030517578125, -104.83390045166015625), 1e-1),
|
||||
testing.closeToVector(util.vector3(95, 27, -104.83390045166015625), 1e-1),
|
||||
testing.closeToVector(util.vector3(90, -90, -104.83390045166015625), 1e-1),
|
||||
testing.expectThat(path, matchers.elementsAreArray({
|
||||
matchers.closeToVector(util.vector3(34.29737091064453125, 806.3817138671875, 112.76610565185546875), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-13.5999355316162109375, 1060.800048828125, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-27.1999359130859375, 1081.2000732421875, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-81.59993743896484375, 1128.800048828125, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-101.99993896484375, 1156.0001220703125, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(-118, 1393, 112.2945709228515625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(7, 1470, 114.73973846435546875), 1e-1),
|
||||
matchers.closeToVector(util.vector3(79, 724, -104.83390045166015625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(84, 290.000030517578125, -104.83390045166015625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(95, 27, -104.83390045166015625), 1e-1),
|
||||
matchers.closeToVector(util.vector3(90, -90, -104.83390045166015625), 1e-1),
|
||||
}))
|
||||
end
|
||||
end)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue