Initial commit

This commit is contained in:
2022-10-08 17:16:13 -04:00
commit 385638c5e1
1925 changed files with 872504 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
-- helper
core.register_async_dofile(core.get_modpath(core.get_current_modname()) ..
DIR_DELIM .. "inside_async_env.lua")
local function deepequal(a, b)
if type(a) == "function" then
return type(b) == "function"
elseif type(a) ~= "table" then
return a == b
elseif type(b) ~= "table" then
return false
end
for k, v in pairs(a) do
if not deepequal(v, b[k]) then
return false
end
end
for k, v in pairs(b) do
if not deepequal(a[k], v) then
return false
end
end
return true
end
-- Object Passing / Serialization
local test_object = {
name = "stairs:stair_glass",
type = "node",
groups = {oddly_breakable_by_hand = 3, cracky = 3, stair = 1},
description = "Glass Stair",
sounds = {
dig = {name = "default_glass_footstep", gain = 0.5},
footstep = {name = "default_glass_footstep", gain = 0.3},
dug = {name = "default_break_glass", gain = 1}
},
node_box = {
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
{-0.5, 0, 0, 0.5, 0.5, 0.5}
},
type = "fixed"
},
tiles = {
{name = "stairs_glass_split.png", backface_culling = true},
{name = "default_glass.png", backface_culling = true},
{name = "stairs_glass_stairside.png^[transformFX", backface_culling = true}
},
on_place = function(itemstack, placer)
return core.is_player(placer)
end,
sunlight_propagates = true,
is_ground_content = false,
light_source = 0,
}
local function test_object_passing()
local tmp = core.serialize_roundtrip(test_object)
assert(deepequal(test_object, tmp))
local circular_key = {"foo", "bar"}
circular_key[circular_key] = true
tmp = core.serialize_roundtrip(circular_key)
assert(tmp[1] == "foo")
assert(tmp[2] == "bar")
assert(tmp[tmp] == true)
local circular_value = {"foo"}
circular_value[2] = circular_value
tmp = core.serialize_roundtrip(circular_value)
assert(tmp[1] == "foo")
assert(tmp[2] == tmp)
-- Two-segment cycle
local cycle_seg_1, cycle_seg_2 = {}, {}
cycle_seg_1[1] = cycle_seg_2
cycle_seg_2[1] = cycle_seg_1
tmp = core.serialize_roundtrip(cycle_seg_1)
assert(tmp[1][1] == tmp)
-- Duplicated value without a cycle
local acyclic_dup_holder = {}
tmp = ItemStack("")
acyclic_dup_holder[tmp] = tmp
tmp = core.serialize_roundtrip(acyclic_dup_holder)
for k, v in pairs(tmp) do
assert(rawequal(k, v))
end
end
unittests.register("test_object_passing", test_object_passing)
local function test_userdata_passing(_, pos)
-- basic userdata passing
local obj = table.copy(test_object.tiles[1])
obj.test = ItemStack("default:cobble 99")
local tmp = core.serialize_roundtrip(obj)
assert(type(tmp.test) == "userdata")
assert(obj.test:to_string() == tmp.test:to_string())
-- object can't be passed, should error
obj = core.raycast(pos, pos)
assert(not pcall(core.serialize_roundtrip, obj))
-- VManip
local vm = core.get_voxel_manip(pos, pos)
local expect = vm:get_node_at(pos)
local vm2 = core.serialize_roundtrip(vm)
assert(deepequal(vm2:get_node_at(pos), expect))
end
unittests.register("test_userdata_passing", test_userdata_passing, {map=true})
-- Asynchronous jobs
local function test_handle_async(cb)
-- Basic test including mod name tracking and unittests.async_test()
-- which is defined inside_async_env.lua
local func = function(x)
return core.get_last_run_mod(), _VERSION, unittests[x]()
end
local expect = {core.get_last_run_mod(), _VERSION, true}
core.handle_async(func, function(...)
if not deepequal(expect, {...}) then
return cb("Values did not equal")
end
if core.get_last_run_mod() ~= expect[1] then
return cb("Mod name not tracked correctly")
end
-- Test passing of nil arguments and return values
core.handle_async(function(a, b)
return a, b
end, function(a, b)
if b ~= 123 then
return cb("Argument went missing")
end
cb()
end, nil, 123)
end, "async_test")
end
unittests.register("test_handle_async", test_handle_async, {async=true})
local function test_userdata_passing2(cb, _, pos)
-- VManip: check transfer into other env
local vm = core.get_voxel_manip(pos, pos)
local expect = vm:get_node_at(pos)
core.handle_async(function(vm_, pos_)
return vm_:get_node_at(pos_)
end, function(ret)
if not deepequal(expect, ret) then
return cb("Node data mismatch (one-way)")
end
-- VManip: test a roundtrip
core.handle_async(function(vm_)
return vm_
end, function(vm2)
if not deepequal(expect, vm2:get_node_at(pos)) then
return cb("Node data mismatch (roundtrip)")
end
cb()
end, vm)
end, vm, pos)
end
unittests.register("test_userdata_passing2", test_userdata_passing2, {map=true, async=true})

View File

@@ -0,0 +1,112 @@
dofile(core.get_modpath(core.get_current_modname()) .. "/crafting_prepare.lua")
-- Test minetest.clear_craft function
local function test_clear_craft()
-- Clearing by output
minetest.register_craft({
output = "foo",
recipe = {{"bar"}}
})
minetest.register_craft({
output = "foo 4",
recipe = {{"foo", "bar"}}
})
assert(#minetest.get_all_craft_recipes("foo") == 2)
minetest.clear_craft({output="foo"})
assert(minetest.get_all_craft_recipes("foo") == nil)
-- Clearing by input
minetest.register_craft({
output = "foo 4",
recipe = {{"foo", "bar"}}
})
assert(#minetest.get_all_craft_recipes("foo") == 1)
minetest.clear_craft({recipe={{"foo", "bar"}}})
assert(minetest.get_all_craft_recipes("foo") == nil)
end
unittests.register("test_clear_craft", test_clear_craft)
-- Test minetest.get_craft_result function
local function test_get_craft_result()
-- normal
local input = {
method = "normal",
width = 2,
items = {"", "unittests:coal_lump", "", "unittests:stick"}
}
minetest.log("info", "[unittests] torch crafting input: "..dump(input))
local output, decremented_input = minetest.get_craft_result(input)
minetest.log("info", "[unittests] torch crafting output: "..dump(output))
minetest.log("info", "[unittests] torch crafting decremented input: "..dump(decremented_input))
assert(output.item)
minetest.log("info", "[unittests] torch crafting output.item:to_table(): "..dump(output.item:to_table()))
assert(output.item:get_name() == "unittests:torch")
assert(output.item:get_count() == 4)
-- fuel
input = {
method = "fuel",
width = 1,
items = {"unittests:coal_lump"}
}
minetest.log("info", "[unittests] coal fuel input: "..dump(input))
output, decremented_input = minetest.get_craft_result(input)
minetest.log("info", "[unittests] coal fuel output: "..dump(output))
minetest.log("info", "[unittests] coal fuel decremented input: "..dump(decremented_input))
assert(output.time)
assert(output.time > 0)
-- cooking
input = {
method = "cooking",
width = 1,
items = {"unittests:iron_lump"}
}
minetest.log("info", "[unittests] iron lump cooking input: "..dump(output))
output, decremented_input = minetest.get_craft_result(input)
minetest.log("info", "[unittests] iron lump cooking output: "..dump(output))
minetest.log("info", "[unittests] iron lump cooking decremented input: "..dump(decremented_input))
assert(output.time)
assert(output.time > 0)
assert(output.item)
minetest.log("info", "[unittests] iron lump cooking output.item:to_table(): "..dump(output.item:to_table()))
assert(output.item:get_name() == "unittests:steel_ingot")
assert(output.item:get_count() == 1)
-- tool repair (repairable)
input = {
method = "normal",
width = 2,
-- Using a wear of 60000
items = {"unittests:repairable_tool 1 60000", "unittests:repairable_tool 1 60000"}
}
minetest.log("info", "[unittests] repairable tool crafting input: "..dump(input))
output, decremented_input = minetest.get_craft_result(input)
minetest.log("info", "[unittests] repairable tool crafting output: "..dump(output))
minetest.log("info", "[unittests] repairable tool crafting decremented input: "..dump(decremented_input))
assert(output.item)
minetest.log("info", "[unittests] repairable tool crafting output.item:to_table(): "..dump(output.item:to_table()))
assert(output.item:get_name() == "unittests:repairable_tool")
-- Test the wear value.
-- See src/craftdef.cpp in Minetest source code for the formula. The formula to calculate
-- the value 51187 is:
-- 65536 - ((65536-60000)+(65536-60000)) + floor(additonal_wear * 65536 + 0.5) = 51187
-- where additional_wear = 0.05
assert(output.item:get_wear() == 51187)
assert(output.item:get_count() == 1)
-- failing tool repair (unrepairable)
input = {
method = "normal",
width = 2,
items = {"unittests:unrepairable_tool 1 60000", "unittests:unrepairable_tool 1 60000"}
}
minetest.log("info", "[unittests] unrepairable tool crafting input: "..dump(input))
output, decremented_input = minetest.get_craft_result(input)
minetest.log("info", "[unittests] unrepairable tool crafting output: "..dump(output))
minetest.log("info", "[unittests] unrepairable tool crafting decremented input: "..dump(decremented_input))
assert(output.item)
minetest.log("info", "[unittests] unrepairable tool crafting output.item:to_table(): "..dump(output.item:to_table()))
-- unrepairable tool must not yield any output
assert(output.item:is_empty())
end
unittests.register("test_get_craft_result", test_get_craft_result)

View File

@@ -0,0 +1,94 @@
-- Registering some dummy items and recipes for the crafting tests
minetest.register_craftitem("unittests:torch", {
description = "Crafting Test Item: Torch",
inventory_image = "unittests_torch.png",
groups = { dummy = 1 },
})
minetest.register_craftitem("unittests:coal_lump", {
description = "Crafting Test Item: Coal Lump",
inventory_image = "unittests_coal_lump.png",
groups = { dummy = 1 },
})
minetest.register_craftitem("unittests:stick", {
description = "Crafting Test Item: Stick",
inventory_image = "unittests_stick.png",
groups = { dummy = 1 },
})
minetest.register_craftitem("unittests:iron_lump", {
description = "Crafting Test Item: Iron Lump",
inventory_image = "unittests_iron_lump.png",
groups = { dummy = 1 },
})
minetest.register_craftitem("unittests:steel_ingot", {
description = "Crafting Test Item: Steel Ingot",
inventory_image = "unittests_steel_ingot.png",
groups = { dummy = 1 },
})
-- Use aliases in recipes for more complete testing
minetest.register_alias("unittests:steel_ingot_alias", "unittests:steel_ingot")
minetest.register_alias("unittests:coal_lump_alias", "unittests:coal_lump")
minetest.register_alias("unittests:iron_lump_alias", "unittests:iron_lump")
-- Recipes for tests: Normal crafting, cooking and fuel
minetest.register_craft({
output = 'unittests:torch 4',
recipe = {
{'unittests:coal_lump_alias'},
{'unittests:stick'},
}
})
minetest.register_craft({
type = "cooking",
output = "unittests:steel_ingot_alias",
recipe = "unittests:iron_lump_alias",
})
minetest.register_craft({
type = "fuel",
recipe = "unittests:coal_lump_alias",
burntime = 40,
})
-- Test tool repair
minetest.register_craft({
type = "toolrepair",
additional_wear = -0.05,
})
-- Test the disable_repair=1 group
minetest.register_tool("unittests:unrepairable_tool", {
description = "Crafting Test Item: Unrepairable Tool",
inventory_image = "unittests_unrepairable_tool.png",
tool_capabilities = {
groupcaps = {
cracky = {
times = {3, 2, 1},
}
}
},
groups = { disable_repair = 1, dummy = 1 }
})
minetest.register_tool("unittests:repairable_tool", {
description = "Crafting Test Item: Repairable Tool",
inventory_image = "unittests_repairable_tool.png",
tool_capabilities = {
groupcaps = {
cracky = {
times = {3, 2, 1},
}
}
},
groups = { dummy = 1 },
})

View File

@@ -0,0 +1,132 @@
local log = {}
local function insert_log(...)
log[#log+1] = string.format(...)
end
local function objref_str(self, ref)
if ref and ref:is_player() then
return "player"
end
return self.object == ref and "self" or tostring(ref)
end
core.register_entity("unittests:callbacks", {
initial_properties = {
hp_max = 5,
visual = "upright_sprite",
textures = { "unittests_stick.png" },
static_save = false,
},
on_activate = function(self, staticdata, dtime_s)
self.object:set_armor_groups({test = 100})
assert(self.object:get_hp() == self.initial_properties.hp_max)
insert_log("on_activate(%d)", #staticdata)
end,
on_deactivate = function(self, removal)
insert_log("on_deactivate(%s)", tostring(removal))
end,
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
insert_log("on_punch(%s, %.1f, %d)", objref_str(self, puncher),
time_from_last_punch, damage)
end,
on_death = function(self, killer)
assert(self.object:get_hp() == 0)
insert_log("on_death(%s)", objref_str(self, killer))
end,
on_rightclick = function(self, clicker)
insert_log("on_rightclick(%s)", objref_str(self, clicker))
end,
on_attach_child = function(self, child)
insert_log("on_attach_child(%s)", objref_str(self, child))
end,
on_detach_child = function(self, child)
insert_log("on_detach_child(%s)", objref_str(self, child))
end,
on_detach = function(self, parent)
insert_log("on_detach(%s)", objref_str(self, parent))
end,
get_staticdata = function(self)
assert(false)
end,
})
--
local function check_log(expect)
if #expect ~= #log then
error("Log mismatch: " .. core.write_json(log))
end
for i, s in ipairs(expect) do
if log[i] ~= s then
error("Log mismatch at " .. i .. ": " .. core.write_json(log))
end
end
log = {} -- clear it for next time
end
local function test_entity_lifecycle(_, pos)
log = {}
-- with binary in staticdata
local obj = core.add_entity(pos, "unittests:callbacks", "abc\000def")
check_log({"on_activate(7)"})
obj:set_hp(0)
check_log({"on_death(nil)", "on_deactivate(true)"})
-- objectref must be invalid now
assert(obj:get_velocity() == nil)
end
unittests.register("test_entity_lifecycle", test_entity_lifecycle, {map=true})
local function test_entity_interact(_, pos)
log = {}
local obj = core.add_entity(pos, "unittests:callbacks")
check_log({"on_activate(0)"})
-- rightclick
obj:right_click(obj)
check_log({"on_rightclick(self)"})
-- useless punch
obj:punch(obj, 0.5, {})
check_log({"on_punch(self, 0.5, 0)"})
-- fatal punch
obj:punch(obj, 1.9, {
full_punch_interval = 1.0,
damage_groups = { test = 10 },
})
check_log({
-- does 10 damage even though we only have 5 hp
"on_punch(self, 1.9, 10)",
"on_death(self)",
"on_deactivate(true)"
})
end
unittests.register("test_entity_interact", test_entity_interact, {map=true})
local function test_entity_attach(player, pos)
log = {}
local obj = core.add_entity(pos, "unittests:callbacks")
check_log({"on_activate(0)"})
-- attach player to entity
player:set_attach(obj)
check_log({"on_attach_child(player)"})
player:set_detach()
check_log({"on_detach_child(player)"})
-- attach entity to player
obj:set_attach(player)
check_log({})
obj:set_detach()
check_log({"on_detach(player)"})
obj:remove()
end
unittests.register("test_entity_attach", test_entity_attach, {player=true, map=true})

View File

@@ -0,0 +1,202 @@
unittests = {}
unittests.list = {}
-- name: Name of the test
-- func:
-- for sync: function(player, pos), should error on failure
-- for async: function(callback, player, pos)
-- MUST call callback() or callback("error msg") in case of error once test is finished
-- this means you cannot use assert() in the test implementation
-- opts: {
-- player = false, -- Does test require a player?
-- map = false, -- Does test require map access?
-- async = false, -- Does the test run asynchronously? (read notes above!)
-- }
function unittests.register(name, func, opts)
local def = table.copy(opts or {})
def.name = name
def.func = func
table.insert(unittests.list, def)
end
function unittests.on_finished(all_passed)
-- free to override
end
-- Calls invoke with a callback as argument
-- Suspends coroutine until that callback is called
-- Return values are passed through
local function await(invoke)
local co = coroutine.running()
assert(co)
local called_early = true
invoke(function(...)
if called_early == true then
called_early = {...}
else
coroutine.resume(co, ...)
co = nil
end
end)
if called_early ~= true then
-- callback was already called before yielding
return unpack(called_early)
end
called_early = nil
return coroutine.yield()
end
function unittests.run_one(idx, counters, out_callback, player, pos)
local def = unittests.list[idx]
if not def.player then
player = nil
elseif player == nil then
out_callback(false)
return false
end
if not def.map then
pos = nil
elseif pos == nil then
out_callback(false)
return false
end
local tbegin = core.get_us_time()
local function done(status, err)
local tend = core.get_us_time()
local ms_taken = (tend - tbegin) / 1000
if not status then
core.log("error", err)
end
print(string.format("[%s] %s - %dms",
status and "PASS" or "FAIL", def.name, ms_taken))
counters.time = counters.time + ms_taken
counters.total = counters.total + 1
if status then
counters.passed = counters.passed + 1
end
end
if def.async then
core.log("info", "[unittest] running " .. def.name .. " (async)")
def.func(function(err)
done(err == nil, err)
out_callback(true)
end, player, pos)
else
core.log("info", "[unittest] running " .. def.name)
local status, err = pcall(def.func, player, pos)
done(status, err)
out_callback(true)
end
return true
end
local function wait_for_player(callback)
if #core.get_connected_players() > 0 then
return callback(core.get_connected_players()[1])
end
local first = true
core.register_on_joinplayer(function(player)
if first then
callback(player)
first = false
end
end)
end
local function wait_for_map(player, callback)
local check = function()
if core.get_node_or_nil(player:get_pos()) ~= nil then
callback()
else
core.after(0, check)
end
end
check()
end
function unittests.run_all()
-- This runs in a coroutine so it uses await().
local counters = { time = 0, total = 0, passed = 0 }
-- Run standalone tests first
for idx = 1, #unittests.list do
local def = unittests.list[idx]
def.done = await(function(cb)
unittests.run_one(idx, counters, cb, nil, nil)
end)
end
-- Wait for a player to join, run tests that require a player
local player = await(wait_for_player)
for idx = 1, #unittests.list do
local def = unittests.list[idx]
if not def.done then
def.done = await(function(cb)
unittests.run_one(idx, counters, cb, player, nil)
end)
end
end
-- Wait for the world to generate/load, run tests that require map access
await(function(cb)
wait_for_map(player, cb)
end)
local pos = vector.round(player:get_pos())
for idx = 1, #unittests.list do
local def = unittests.list[idx]
if not def.done then
def.done = await(function(cb)
unittests.run_one(idx, counters, cb, player, pos)
end)
end
end
-- Print stats
assert(#unittests.list == counters.total)
print(string.rep("+", 80))
print(string.format("Unit Test Results: %s",
counters.total == counters.passed and "PASSED" or "FAILED"))
print(string.format(" %d / %d failed tests.",
counters.total - counters.passed, counters.total))
print(string.format(" Testing took %dms total.", counters.time))
print(string.rep("+", 80))
unittests.on_finished(counters.total == counters.passed)
return counters.total == counters.passed
end
--------------
local modpath = core.get_modpath("unittests")
dofile(modpath .. "/misc.lua")
dofile(modpath .. "/player.lua")
dofile(modpath .. "/crafting.lua")
dofile(modpath .. "/itemdescription.lua")
dofile(modpath .. "/async_env.lua")
dofile(modpath .. "/entity.lua")
--------------
if core.settings:get_bool("devtest_unittests_autostart", false) then
core.after(0, function()
coroutine.wrap(unittests.run_all)()
end)
else
core.register_chatcommand("unittests", {
privs = {basic_privs=true},
description = "Runs devtest unittests (may modify player or map state)",
func = function(name, param)
unittests.on_finished = function(ok)
core.chat_send_player(name,
(ok and "All tests passed." or "There were test failures.") ..
" Check the console for detailed output.")
end
coroutine.wrap(unittests.run_all)()
return true, ""
end,
})
end

View File

@@ -0,0 +1,25 @@
unittests = {}
core.log("info", "Hello World")
local function do_tests()
assert(core == minetest)
-- stuff that should not be here
assert(not core.get_player_by_name)
assert(not core.set_node)
assert(not core.object_refs)
-- stuff that should be here
assert(ItemStack)
assert(core.registered_items[""])
-- alias handling
assert(core.registered_items["unittests:steel_ingot_alias"].name ==
"unittests:steel_ingot")
end
function unittests.async_test()
local ok, err = pcall(do_tests)
if not ok then
core.log("error", err)
end
return ok
end

View File

@@ -0,0 +1,42 @@
local full_description = "Description Test Item\nFor testing item decription"
minetest.register_tool("unittests:description_test", {
description = full_description,
inventory_image = "unittests_description_test.png",
})
minetest.register_chatcommand("item_description", {
param = "",
description = "Show the short and full description of the wielded item.",
func = function(name)
local player = minetest.get_player_by_name(name)
local item = player:get_wielded_item()
return true, string.format("short_description: %s\ndescription: %s",
item:get_short_description(), item:get_description())
end
})
local function test_short_desc()
local function get_short_description(item)
return ItemStack(item):get_short_description()
end
local stack = ItemStack("unittests:description_test")
assert(stack:get_short_description() == "Description Test Item")
assert(get_short_description("unittests:description_test") == "Description Test Item")
assert(minetest.registered_items["unittests:description_test"].short_description == nil)
assert(stack:get_description() == full_description)
assert(stack:get_description() == minetest.registered_items["unittests:description_test"].description)
stack:get_meta():set_string("description", "Hello World")
assert(stack:get_short_description() == "Hello World")
assert(stack:get_description() == "Hello World")
assert(get_short_description(stack) == "Hello World")
assert(get_short_description("unittests:description_test") == "Description Test Item")
stack:get_meta():set_string("short_description", "Foo Bar")
assert(stack:get_short_description() == "Foo Bar")
assert(stack:get_description() == "Hello World")
return true
end
unittests.register("test_short_desc", test_short_desc)

View File

@@ -0,0 +1,82 @@
local function test_random()
-- Try out PseudoRandom
local pseudo = PseudoRandom(13)
assert(pseudo:next() == 22290)
assert(pseudo:next() == 13854)
end
unittests.register("test_random", test_random)
local function test_dynamic_media(cb, player)
if core.get_player_information(player:get_player_name()).protocol_version < 40 then
core.log("warning", "test_dynamic_media: Client too old, skipping test.")
return cb()
end
-- Check that the client acknowledges media transfers
local path = core.get_worldpath() .. "/test_media.obj"
local f = io.open(path, "w")
f:write("# contents don't matter\n")
f:close()
local call_ok = false
local ok = core.dynamic_add_media({
filepath = path,
to_player = player:get_player_name(),
}, function(name)
if not call_ok then
return cb("impossible condition")
end
cb()
end)
if not ok then
return cb("dynamic_add_media() returned error")
end
call_ok = true
-- if the callback isn't called this test will just hang :shrug:
end
unittests.register("test_dynamic_media", test_dynamic_media, {async=true, player=true})
local function test_v3f_metatable(player)
assert(vector.check(player:get_pos()))
end
unittests.register("test_v3f_metatable", test_v3f_metatable, {player=true})
local function test_v3s16_metatable(player, pos)
local node = minetest.get_node(pos)
local found_pos = minetest.find_node_near(pos, 0, node.name, true)
assert(vector.check(found_pos))
end
unittests.register("test_v3s16_metatable", test_v3s16_metatable, {map=true})
local function test_clear_meta(_, pos)
local ref = core.get_meta(pos)
for way = 1, 3 do
ref:set_string("foo", "bar")
assert(ref:contains("foo"))
if way == 1 then
ref:from_table({})
elseif way == 2 then
ref:from_table(nil)
else
ref:set_string("foo", "")
end
assert(#core.find_nodes_with_meta(pos, pos) == 0, "clearing failed " .. way)
end
end
unittests.register("test_clear_meta", test_clear_meta, {map=true})
local on_punch_called
minetest.register_on_punchnode(function()
on_punch_called = true
end)
unittests.register("test_punch_node", function(_, pos)
minetest.place_node(pos, {name="basenodes:dirt"})
on_punch_called = false
minetest.punch_node(pos)
minetest.remove_node(pos)
-- currently failing: assert(on_punch_called)
end, {map=true})

View File

@@ -0,0 +1,3 @@
name = unittests
description = Adds automated unit tests for the engine
depends = basenodes

View File

@@ -0,0 +1,70 @@
--
-- HP Change Reasons
--
local expect = nil
minetest.register_on_player_hpchange(function(player, hp, reason)
if expect == nil then
return
end
for key, value in pairs(reason) do
assert(expect[key] == value)
end
for key, value in pairs(expect) do
assert(reason[key] == value)
end
expect = nil
end)
local function run_hpchangereason_tests(player)
local old_hp = player:get_hp()
player:set_hp(20)
expect = { type = "set_hp", from = "mod" }
player:set_hp(3)
assert(expect == nil)
expect = { a = 234, type = "set_hp", from = "mod" }
player:set_hp(7, { a= 234 })
assert(expect == nil)
expect = { df = 3458973454, type = "fall", from = "mod" }
player:set_hp(10, { type = "fall", df = 3458973454 })
assert(expect == nil)
player:set_hp(old_hp)
end
unittests.register("test_hpchangereason", run_hpchangereason_tests, {player=true})
--
-- Player meta
--
local function run_player_meta_tests(player)
local meta = player:get_meta()
meta:set_string("foo", "bar")
assert(meta:contains("foo"))
assert(meta:get_string("foo") == "bar")
assert(meta:get("foo") == "bar")
local meta2 = player:get_meta()
assert(meta2:get_string("foo") == "bar")
assert(meta2:get("foo") == "bar")
assert(meta:equals(meta2))
meta:set_string("bob", "dillan")
assert(meta:get_string("foo") == "bar")
assert(meta:get_string("bob") == "dillan")
assert(meta:get("bob") == "dillan")
assert(meta2:get_string("foo") == "bar")
assert(meta2:get_string("bob") == "dillan")
assert(meta2:get("bob") == "dillan")
assert(meta:equals(meta2))
meta:set_string("foo", "")
assert(not meta:contains("foo"))
assert(meta:get("foo") == nil)
assert(meta:get_string("foo") == "")
assert(meta:equals(meta2))
end
unittests.register("test_player_meta", run_player_meta_tests, {player=true})

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B