Corrected the displayed status of skipped tests from 'skip' to 'dnr' (short for 'did not run'). This change ensures better clarity in the test summary by accurately reflecting the status of each test. The modification impacts the visual representation of skipped tests, enhancing the overall readability of the test results.
703 lines
22 KiB
Lua
703 lines
22 KiB
Lua
local tests = {}
|
|
local tests_by_mod = {}
|
|
|
|
local TESTS_STATE_ENUM = {
|
|
NOT_STARTED = "not_started",
|
|
STARTED = "started",
|
|
STARTED_PLAYERS = "started_players",
|
|
DONE = "done"
|
|
}
|
|
local tests_state = TESTS_STATE_ENUM.NOT_STARTED
|
|
|
|
local tests_context = {}
|
|
|
|
local failed = 0
|
|
|
|
test_harness.set_context = function(mod, version_string)
|
|
table.insert(tests_context, { mod = mod, version_string = version_string})
|
|
end
|
|
|
|
local register_test = function(mod, name, func, opts)
|
|
local modnames = minetest.get_modnames()
|
|
local is_mod = false
|
|
for i=1,#modnames do
|
|
if modnames[i]==mod then
|
|
is_mod=true
|
|
break
|
|
end
|
|
end
|
|
assert(is_mod)
|
|
assert(type(name) == "string")
|
|
assert(func == nil or type(func) == "function")
|
|
if not opts then
|
|
opts = {}
|
|
else
|
|
opts = test_harness.table_copy(opts)
|
|
end
|
|
opts.mod = mod
|
|
opts.name = name
|
|
opts.func = func
|
|
table.insert(tests, opts)
|
|
local mod_test_list = tests_by_mod[mod] or {}
|
|
tests_by_mod[mod] = mod_test_list
|
|
table.insert(mod_test_list, opts)
|
|
end
|
|
|
|
test_harness.get_test_registrator = function(mod)
|
|
local modnames = minetest.get_modnames()
|
|
local is_mod = false
|
|
for i=1,#modnames do
|
|
if modnames[i]==mod then
|
|
is_mod=true
|
|
break
|
|
end
|
|
end
|
|
if not is_mod then error("get_test_registrator given mod "..mod.." is not a mod.") end
|
|
return function(name, func, opts)
|
|
register_test(mod, name, func, opts)
|
|
end
|
|
end
|
|
|
|
|
|
---------------------
|
|
-- Helpers
|
|
---------------------
|
|
local vec = vector.new
|
|
local vecw = function(axis, n, base)
|
|
local ret = vec(base)
|
|
ret[axis] = n
|
|
return ret
|
|
end
|
|
local pos2str = minetest.pos_to_string
|
|
local get_node = minetest.get_node
|
|
local set_node = minetest.set_node
|
|
|
|
local charset = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"
|
|
math.randomseed(os.clock())
|
|
function test_harness.randomString(length)
|
|
local ret = {}
|
|
local r
|
|
for _ = 1, length do
|
|
r = math.random(1, #charset)
|
|
table.insert(ret, charset:sub(r, r))
|
|
end
|
|
return table.concat(ret)
|
|
end
|
|
|
|
function test_harness.table_copy(t)
|
|
local t2 = {}
|
|
for k, v in pairs(t) do
|
|
if type(v) == "table" then
|
|
t2[k] = test_harness.table_copy(v)
|
|
else
|
|
t2[k] = v
|
|
end
|
|
end
|
|
return t2
|
|
end
|
|
|
|
--- Copies and modifies positions `pos1` and `pos2` so that each component of
|
|
-- `pos1` is less than or equal to the corresponding component of `pos2`.
|
|
-- Returns the new positions.
|
|
function test_harness.sort_pos(pos1, pos2)
|
|
pos1 = vector.copy(pos1)
|
|
pos2 = vector.copy(pos2)
|
|
if pos1.x > pos2.x then
|
|
pos2.x, pos1.x = pos1.x, pos2.x
|
|
end
|
|
if pos1.y > pos2.y then
|
|
pos2.y, pos1.y = pos1.y, pos2.y
|
|
end
|
|
if pos1.z > pos2.z then
|
|
pos2.z, pos1.z = pos1.z, pos2.z
|
|
end
|
|
return pos1, pos2
|
|
end
|
|
|
|
--- Determines the volume of the region defined by positions `pos1` and `pos2`.
|
|
-- @return The volume.
|
|
function test_harness.volume(pos1, pos2)
|
|
local pos1, pos2 = test_harness.sort_pos(pos1, pos2)
|
|
return (pos2.x - pos1.x + 1) *
|
|
(pos2.y - pos1.y + 1) *
|
|
(pos2.z - pos1.z + 1)
|
|
end
|
|
|
|
---------------------
|
|
-- Nodes
|
|
---------------------
|
|
local air = "air"
|
|
rawset(_G, "testnode1", "")
|
|
rawset(_G, "testnode2", "")
|
|
rawset(_G, "testnode3", "")
|
|
-- Loads nodenames to use for tests
|
|
local function init_nodes()
|
|
testnode1 = minetest.registered_aliases["mapgen_stone"]
|
|
testnode2 = minetest.registered_aliases["mapgen_dirt"]
|
|
testnode3 = minetest.registered_aliases["mapgen_cobble"] or minetest.registered_aliases["mapgen_dirt_with_grass"]
|
|
assert(testnode1 and testnode2 and testnode3)
|
|
end
|
|
-- Writes repeating pattern into given area
|
|
rawset(_G, "place_pattern", function(pos1, pos2, pattern)
|
|
local pos = vec()
|
|
local node = { name = "" }
|
|
local i = 1
|
|
for z = pos1.z, pos2.z do
|
|
pos.z = z
|
|
for y = pos1.y, pos2.y do
|
|
pos.y = y
|
|
for x = pos1.x, pos2.x do
|
|
pos.x = x
|
|
node.name = pattern[i]
|
|
set_node(pos, node)
|
|
i = i % #pattern + 1
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
|
|
---------------------
|
|
-- Area management
|
|
---------------------
|
|
assert(minetest.get_mapgen_setting("mg_name") == "singlenode")
|
|
rawset(_G, "area", {})
|
|
do
|
|
local areamin, areamax
|
|
local off
|
|
local c_air = minetest.get_content_id(air)
|
|
local vbuffer = {}
|
|
-- Assign a new area for use, will emerge and then call ready()
|
|
area.assign = function(min, max, ready)
|
|
areamin = min
|
|
areamax = max
|
|
minetest.emerge_area(min, max, function(bpos, action, remaining)
|
|
assert(action ~= minetest.EMERGE_ERRORED)
|
|
if remaining > 0 then return end
|
|
minetest.after(0, function()
|
|
area.clear()
|
|
ready()
|
|
end)
|
|
end)
|
|
end
|
|
-- Reset area contents and state
|
|
area.clear = function()
|
|
if off and vector.equals(off, vec(0, 0, 0)) then
|
|
return
|
|
end
|
|
local vmanip = minetest.get_voxel_manip(areamin, areamax)
|
|
local vpos1, vpos2 = vmanip:get_emerged_area()
|
|
local vcount = (vpos2.x - vpos1.x + 1) * (vpos2.y - vpos1.y + 1) * (vpos2.z - vpos1.z + 1)
|
|
if #vbuffer ~= vcount then
|
|
vbuffer = {}
|
|
for i = 1, vcount do
|
|
vbuffer[i] = c_air
|
|
end
|
|
end
|
|
vmanip:set_data(vbuffer)
|
|
vmanip:write_to_map()
|
|
off = vec(0, 0, 0)
|
|
end
|
|
-- Returns an usable area [pos1, pos2] that does not overlap previous ones
|
|
area.get = function(sizex, sizey, sizez)
|
|
local size
|
|
if sizey == nil and sizez == nil then
|
|
size = vec(sizex, sizex, sizex)
|
|
else
|
|
size = vec(sizex, sizey, sizez)
|
|
end
|
|
local pos1 = vector.add(areamin, off)
|
|
local pos2 = vector.subtract(vector.add(pos1, size), 1)
|
|
if pos2.x > areamax.x or pos2.y > areamax.y or pos2.z > areamax.z then
|
|
error("Internal failure: out of space")
|
|
end
|
|
off = vector.add(off, size)
|
|
return pos1, pos2
|
|
end
|
|
-- Returns an axis and count (= n) relative to the last-requested area that is unoccupied
|
|
area.dir = function(n)
|
|
local pos1 = vector.add(areamin, off)
|
|
if pos1.x + n <= areamax.x then
|
|
off.x = off.x + n
|
|
return "x", n
|
|
elseif pos1.x + n <= areamax.y then
|
|
off.y = off.y + n
|
|
return "y", n
|
|
elseif pos1.z + n <= areamax.z then
|
|
off.z = off.z + n
|
|
return "z", n
|
|
end
|
|
error("Internal failure: out of space")
|
|
end
|
|
-- Returns [XYZ] margin (list of pos pairs) of n around last-requested area
|
|
-- (may actually be larger but doesn't matter)
|
|
area.margin = function(n)
|
|
local pos1, pos2 = area.get(n)
|
|
return {
|
|
{ vec(areamin.x, areamin.y, pos1.z), pos2 }, -- X/Y
|
|
{ vec(areamin.x, pos1.y, areamin.z), pos2 }, -- X/Z
|
|
{ vec(pos1.x, areamin.y, areamin.z), pos2 }, -- Y/Z
|
|
}
|
|
end
|
|
end
|
|
-- Split an existing area into two non-overlapping [pos1, half1], [half2, pos2] parts; returns half1, half2
|
|
area.split = function(pos1, pos2)
|
|
local axis
|
|
if pos2.x - pos1.x >= 1 then
|
|
axis = "x"
|
|
elseif pos2.y - pos1.y >= 1 then
|
|
axis = "y"
|
|
elseif pos2.z - pos1.z >= 1 then
|
|
axis = "z"
|
|
else
|
|
error("Internal failure: area too small to split")
|
|
end
|
|
local hspan = math.floor((pos2[axis] - pos1[axis] + 1) / 2)
|
|
local half1 = vecw(axis, pos1[axis] + hspan - 1, pos2)
|
|
local half2 = vecw(axis, pos1[axis] + hspan, pos2)
|
|
return half1, half2
|
|
end
|
|
|
|
|
|
---------------------
|
|
-- Checks
|
|
---------------------
|
|
rawset(_G, "check", {})
|
|
-- Check that all nodes in [pos1, pos2] are the node(s) specified
|
|
check.filled = function(pos1, pos2, nodes)
|
|
if type(nodes) == "string" then
|
|
nodes = { nodes }
|
|
end
|
|
local _, counts = minetest.find_nodes_in_area(pos1, pos2, nodes)
|
|
local total = test_harness.volume(pos1, pos2)
|
|
local sum = 0
|
|
for _, n in pairs(counts) do
|
|
sum = sum + n
|
|
end
|
|
if sum ~= total then
|
|
error((total - sum) .. " " .. table.concat(nodes, ",") .. " nodes missing in " ..
|
|
pos2str(pos1) .. " -> " .. pos2str(pos2))
|
|
end
|
|
end
|
|
-- Check that none of the nodes in [pos1, pos2] are the node(s) specified
|
|
check.not_filled = function(pos1, pos2, nodes)
|
|
if type(nodes) == "string" then
|
|
nodes = { nodes }
|
|
end
|
|
local _, counts = minetest.find_nodes_in_area(pos1, pos2, nodes)
|
|
for nodename, n in pairs(counts) do
|
|
if n ~= 0 then
|
|
error(counts[nodename] .. " " .. nodename .. " nodes found in " ..
|
|
pos2str(pos1) .. " -> " .. pos2str(pos2))
|
|
end
|
|
end
|
|
end
|
|
-- Check that all of the areas are only made of node(s) specified
|
|
check.filled2 = function(list, nodes)
|
|
for _, pos in ipairs(list) do
|
|
check.filled(pos[1], pos[2], nodes)
|
|
end
|
|
end
|
|
-- Check that none of the areas contain the node(s) specified
|
|
check.not_filled2 = function(list, nodes)
|
|
for _, pos in ipairs(list) do
|
|
check.not_filled(pos[1], pos[2], nodes)
|
|
end
|
|
end
|
|
-- Checks presence of a repeating pattern in [pos1, po2] (cf. place_pattern)
|
|
check.pattern = function(pos1, pos2, pattern)
|
|
local pos = vec()
|
|
local i = 1
|
|
for z = pos1.z, pos2.z do
|
|
pos.z = z
|
|
for y = pos1.y, pos2.y do
|
|
pos.y = y
|
|
for x = pos1.x, pos2.x do
|
|
pos.x = x
|
|
local node = get_node(pos)
|
|
if node.name ~= pattern[i] then
|
|
error(pattern[i] .. " not found at " .. pos2str(pos) .. " (i=" .. i .. ")")
|
|
end
|
|
i = i % #pattern + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---------------------------------------
|
|
--- Colors Management
|
|
--- from https://github.com/ldrumm/lua-chroma
|
|
--------------------------------------
|
|
rawset(_G, "pprint", setmetatable({
|
|
escapes = {
|
|
clear = "\027[0m",
|
|
red = "\027[31m",
|
|
green = "\027[32m",
|
|
orange = "\027[33m",
|
|
navy = "\027[34m",
|
|
magenta = "\027[35m",
|
|
cyan = "\027[36m",
|
|
gray = "\027[90m",
|
|
grey = "\027[90m",
|
|
light_gray = "\027[37m",
|
|
light_grey = "\027[37m",
|
|
peach = "\027[91m",
|
|
light_green = "\027[92m",
|
|
yellow = "\027[93m",
|
|
blue = "\027[94m",
|
|
pink = "\027[95m",
|
|
baby_blue = "\027[96m",
|
|
|
|
highlight = {
|
|
red = "\027[41m",
|
|
green = "\027[42m",
|
|
orange = "\027[43m",
|
|
navy = "\027[44m",
|
|
magenta = "\027[45m",
|
|
cyan = "\027[46m",
|
|
gray = "\027[47m",
|
|
grey = "\027[47m",
|
|
light_gray = "\027[100m",
|
|
light_grey = "\027[100m",
|
|
peach = "\027[101m",
|
|
light_green = "\027[102m",
|
|
yellow = "\027[103m",
|
|
blue = "\027[104m",
|
|
pink = "\027[105m",
|
|
baby_blue = "\027[106m",
|
|
},
|
|
|
|
strikethrough = "\027[9m",
|
|
underline = "\027[4m",
|
|
bold = "\027[1m",
|
|
},
|
|
_sequence = '',
|
|
_highlight = false,
|
|
print = io.write
|
|
},
|
|
{
|
|
__call = function(self, ...) return io.write(...) end,
|
|
|
|
__index = function(self, index)
|
|
local esc = self._highlight and rawget(self, 'escapes').highlight[index]
|
|
or rawget(self, 'escapes')[index]
|
|
self._highlight = index == 'highlight'
|
|
if esc ~= nil then
|
|
if type(esc) == 'string' then
|
|
self._sequence = self._sequence .. esc
|
|
end
|
|
return setmetatable({}, {
|
|
__call = function(proxy, ...)
|
|
if self._sequence then io.write(self._sequence) end
|
|
self.print(...)
|
|
self._sequence = ''
|
|
io.write(rawget(self,'escapes').clear)
|
|
return self
|
|
end,
|
|
__index = function(proxy, k)
|
|
return self[k]
|
|
end,
|
|
})
|
|
else
|
|
return rawget(self, index)
|
|
end
|
|
end,
|
|
}))
|
|
|
|
|
|
|
|
--------------------------------------
|
|
local request_shutdown = function()
|
|
if failed == 0 then
|
|
io.close(io.open(minetest.get_worldpath() .. "/tests_ok", "w"))
|
|
end
|
|
print("Requesting server shutdown")
|
|
minetest.request_shutdown()
|
|
end
|
|
|
|
|
|
local all_in_table = function(names, names_list)
|
|
for _, n in ipairs(names) do
|
|
if not names_list[n] then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local print_result_line = function(test)
|
|
local s = ":"..test.mod..":"
|
|
local rest = s .. test.name
|
|
pprint.light_gray(s)
|
|
pprint(" ")
|
|
pprint(test.name)
|
|
pprint(string.rep(" ", 80 - #rest))
|
|
if test.result.ok then pprint.green("pass") else pprint.red("FAIL") end
|
|
pprint("\n")
|
|
if not test.result.ok and test.result.err then
|
|
pprint.yellow(" " .. test.result.err .. "\n")
|
|
end
|
|
end
|
|
|
|
local display_tests_summary = function()
|
|
|
|
print(string.rep("-",80))
|
|
print("----"..string.rep(" ",72).."----")
|
|
pprint("----"..string.rep(" ",27))
|
|
pprint.bold.underline.orange("TESTS RUN SUMMARY")
|
|
print(string.rep(" ",28).."----")
|
|
print("----"..string.rep(" ",72).."----")
|
|
print(string.rep("-",80))
|
|
|
|
print("All tests done, " .. failed .. " tests failed.")
|
|
print()
|
|
|
|
for mod, tests_list in pairs(tests_by_mod) do
|
|
pprint.baby_blue(string.format("%#80s\n", mod))
|
|
for _, test in ipairs(tests_list) do
|
|
if test.func == nil then
|
|
local s = ":".. test.mod ..":---- " .. test.name
|
|
pprint.light_gray(":".. test.mod ..":").blue("---- " .. test.name)
|
|
pprint.blue(string.rep("-", 80 - #s).."\n")
|
|
elseif test.result ~= nil then
|
|
print_result_line(test)
|
|
else
|
|
local s = ":"..test.mod..":"
|
|
local rest = s .. test.name
|
|
pprint.light_gray(s.." "..test.name..string.rep(" ", 80 - #rest).."dnr\n")
|
|
end
|
|
end
|
|
pprint.baby_blue(string.rep("-",80),"\n")
|
|
end
|
|
print(string.rep("-",80))
|
|
pprint.bold("Tests done, ")
|
|
if failed == 0 then pprint.bold.green(failed) else pprint.bold.red(failed) end
|
|
pprint.bold(" tests failed.\n")
|
|
print(string.rep("-",80))
|
|
end
|
|
|
|
test_harness.dump = function(o)
|
|
if type(o) == 'table' then
|
|
local s = '{ '
|
|
for k, v in pairs(o) do
|
|
if type(k) ~= 'number' then k = '"' .. k .. '"' end
|
|
s = s .. '[' .. k .. '] = ' .. test_harness.dump(v) .. ','
|
|
end
|
|
return s .. '} '
|
|
else
|
|
return tostring(o)
|
|
end
|
|
end
|
|
|
|
test_harness.save_players = function(players)
|
|
local players_data = {}
|
|
for _, p in ipairs(players) do
|
|
local player_obj = nil
|
|
local player_name = nil
|
|
if type(p) == "string" then
|
|
player_obj = minetest.get_player_by_name(p)
|
|
player_name = p
|
|
else
|
|
player_obj = p
|
|
player_name = p:get_player_name()
|
|
end
|
|
players_data[player_name] = {
|
|
position = player_obj:get_pos(),
|
|
privs = test_harness.table_copy(minetest.get_player_privs(player_name)),
|
|
inventory = test_harness.table_copy(player_obj:get_inventory():get_lists())
|
|
}
|
|
end
|
|
return players_data
|
|
end
|
|
|
|
test_harness.restore_players = function(players_data)
|
|
for player_name, data in pairs(players_data) do
|
|
local player = minetest.get_player_by_name(player_name)
|
|
player:set_pos(data.position)
|
|
minetest.set_player_privs(player_name, data.privs)
|
|
player:get_inventory():set_lists(data.inventory)
|
|
end
|
|
end
|
|
|
|
|
|
local get_connected_player_names = function()
|
|
local connected_player_names = {}
|
|
for _, p in ipairs(minetest.get_connected_players()) do
|
|
connected_player_names[p:get_player_name()] = true
|
|
end
|
|
return connected_player_names
|
|
end
|
|
|
|
test_harness.run_player_tests = function(list_player_tests, area)
|
|
|
|
for _, test in ipairs(list_player_tests) do
|
|
local connected_player_names = get_connected_player_names()
|
|
if not test.result and
|
|
test.func and
|
|
test.players and
|
|
next(test.players) and
|
|
all_in_table(test.players, connected_player_names)
|
|
then
|
|
local player_data = test_harness.save_players(test.players)
|
|
area.clear()
|
|
local ok, err = pcall(test.func)
|
|
test.result = { ok = ok, err = err }
|
|
print_result_line(test)
|
|
test_harness.restore_players(player_data)
|
|
if not ok then
|
|
failed = failed + 1
|
|
if minetest.settings:get_bool("test_harness_failfast", false) then
|
|
tests_state = TESTS_STATE_ENUM.DONE
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local remaining_tests = {}
|
|
if tests_state ~= TESTS_STATE_ENUM.DONE then
|
|
for _, test in ipairs(list_player_tests) do
|
|
if test.func ~= nil and test.result == nil then
|
|
table.insert(remaining_tests,test)
|
|
end
|
|
end
|
|
if #remaining_tests == 0 then
|
|
tests_state = TESTS_STATE_ENUM.DONE
|
|
end
|
|
end
|
|
|
|
if tests_state == TESTS_STATE_ENUM.DONE then
|
|
display_tests_summary()
|
|
|
|
if minetest.settings:get_bool("test_harness_stop_server", true) then
|
|
request_shutdown()
|
|
end
|
|
else
|
|
-- reschedule
|
|
minetest.after(1,test_harness.run_player_tests,remaining_tests, area)
|
|
end
|
|
end
|
|
|
|
|
|
---------------------
|
|
-- Main function
|
|
---------------------
|
|
local run_tests = function()
|
|
tests_state = TESTS_STATE_ENUM.STARTED
|
|
local simple_tests = {}
|
|
local players_tests = {}
|
|
do
|
|
local nb_tests = 0
|
|
local current_title = {}
|
|
for _, test in ipairs(tests) do
|
|
if not test.func then
|
|
nb_tests = nb_tests + 1
|
|
table.insert(current_title, test)
|
|
elseif test.players and next(test.players) then
|
|
for _, t in ipairs(current_title) do
|
|
table.insert(players_tests, t)
|
|
end
|
|
current_title = {}
|
|
table.insert(players_tests, test)
|
|
else
|
|
for _, t in ipairs(current_title) do
|
|
table.insert(simple_tests, t)
|
|
end
|
|
current_title = {}
|
|
table.insert(simple_tests, test)
|
|
end
|
|
end
|
|
local v = minetest.get_version()
|
|
|
|
|
|
print("Running " .. nb_tests .. " tests for:")
|
|
for _,context in ipairs(tests_context) do
|
|
print(" - "..context.mod .." - "..context.version_string)
|
|
end
|
|
print("on " .. v.project .. " " .. (v.hash or v.string))
|
|
end
|
|
|
|
init_nodes()
|
|
|
|
-- emerge area from (0,0,0) ~ (56,56,56) and keep it loaded
|
|
-- Note: making this area smaller speeds up tests
|
|
local wanted = vec(56, 56, 56)
|
|
for x = 0, math.floor(wanted.x / 16) do
|
|
for y = 0, math.floor(wanted.y / 16) do
|
|
for z = 0, math.floor(wanted.z / 16) do
|
|
assert(minetest.forceload_block(vec(x * 16, y * 16, z * 16), true, -1))
|
|
end
|
|
end
|
|
end
|
|
failed = 0
|
|
area.assign(vec(0, 0, 0), wanted, function()
|
|
-- run the simple tests
|
|
for _, test in ipairs(simple_tests) do
|
|
if not test.func then
|
|
local s = ":"..test.mod..":---- " .. test.name .. " "
|
|
print(s .. string.rep("-", 60 - #s))
|
|
test.result = { ok = true, err = "" }
|
|
else
|
|
area.clear()
|
|
local ok, err = pcall(test.func)
|
|
test.result = { ok = ok, err = err }
|
|
print_result_line(test)
|
|
if not ok then
|
|
failed = failed + 1
|
|
if minetest.settings:get_bool("test_harness_failfast", false) then
|
|
tests_state = TESTS_STATE_ENUM.DONE
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
print("Server tests done, " .. failed .. " tests failed.")
|
|
|
|
if next(players_tests) == nil then
|
|
tests_state = TESTS_STATE_ENUM.DONE
|
|
end
|
|
if tests_state == TESTS_STATE_ENUM.DONE then
|
|
display_tests_summary()
|
|
|
|
if minetest.settings:get_bool("test_harness_stop_server", true) then
|
|
request_shutdown()
|
|
end
|
|
end
|
|
|
|
-- list of needed players
|
|
local players_table = {}
|
|
for _, t in ipairs(players_tests) do
|
|
if t.players and next(t.players) then
|
|
for _, p in ipairs(t.players) do
|
|
players_table[p] = true
|
|
end
|
|
end
|
|
end
|
|
local needed_playernames = {}
|
|
for k, _ in pairs(players_table) do table.insert(needed_playernames, k) end
|
|
|
|
for _, player_name in ipairs(needed_playernames) do
|
|
print("registering player " .. player_name)
|
|
minetest.set_player_password(player_name,
|
|
minetest.get_password_hash(player_name,
|
|
minetest.settings:get("test_harness_test_players_password") or "test"))
|
|
end
|
|
|
|
-- launch test
|
|
test_harness.run_player_tests(players_tests, area)
|
|
end)
|
|
end
|
|
|
|
|
|
-- for debug purposes
|
|
minetest.register_on_joinplayer(function(player)
|
|
minetest.set_player_privs(player:get_player_name(),
|
|
minetest.string_to_privs("fly,fast,noclip,basic_debug,debug,interact"))
|
|
end)
|
|
minetest.register_on_punchnode(function(pos, node, puncher)
|
|
minetest.chat_send_player(puncher:get_player_name(), pos2str(pos))
|
|
end)
|
|
|
|
minetest.after(0, run_tests)
|