fakelib/tests.lua
2024-05-19 15:24:30 +10:00

325 lines
9.8 KiB
Lua

local error_tests = ...
local success = true
-- Helper functions
------------------------------
local function round(x)
if x >= 0 then
return math.floor(x + 0.5)
end
return math.ceil(x - 0.5)
end
local function is_equal(a, b)
local ta, tb = type(a), type(b)
-- Compare table values recursively
if ta == "table" and tb == "table" then
if next(a) == nil and next(b) == nil then
return true
end
local keys = {}
for k in pairs(a) do
keys[k] = true
end
for k in pairs(b) do
keys[k] = true
end
for k in pairs(keys) do
if not is_equal(a[k], b[k]) then
return false
end
end
return true
end
-- Get rid of floating point errors
if ta == "number" and tb == "number" then
a = round(a * 1000) / 1000
b = round(b * 1000) / 1000
end
return a == b
end
local function test(real, fake, func, ...)
local rvals_real = {pcall(real[func], real, ...)}
local rvals_fake = {pcall(fake[func], fake, ...)}
local i = 1
while true do
if rvals_real[i] == nil and rvals_fake[i] == nil then
break
end
if not is_equal(rvals_real[i], rvals_fake[i]) then
success = false
print(string.format("[fakelib] Test failed with function '%s' and return value #%i", func, i-1))
if #{...} > 0 then
print(" > Function arguments: ", ...)
end
local a = type(rvals_real[i]) == "table" and dump(rvals_real[i]) or tostring(rvals_real[i])
print(string.format(" > Real return value: %s", a))
local b = type(rvals_fake[i]) == "table" and dump(rvals_fake[i]) or tostring(rvals_fake[i])
print(string.format(" > Fake return value: %s", b))
end
i = i + 1
end
end
local test_values = {"abc", "", 0, -1, {}, {true}, true, function() end, 0/0, nil}
local function iter_values()
local i = 0
return function ()
i = i + 1
if i <= 1 then return test_values[i] end
end
end
-- Player tests
------------------------------
local real_player = minetest.get_player_by_name("singleplayer")
local fake_player = fakelib.create_player("singleplayer")
-- Type check
if not fakelib.is_player(real_player) then
success = false
print("[fakelib] Real player is not a player.")
end
if not fakelib.is_player(fake_player) then
success = false
print("[fakelib] Fake player is not a player.")
end
for value in iter_values() do
if fakelib.is_player(value) then
print(string.format("[fakelib] Value '%s' is a player.", tostring(value)))
end
end
-- Function check
for func in pairs(getmetatable(real_player)) do
if not fake_player[func] then
success = false
print(string.format("[fakelib] Missing function '%s'", func))
end
end
-- Functional tests
test(real_player, fake_player, "get_player_name")
test(real_player, fake_player, "get_player_control")
test(real_player, fake_player, "get_player_control_bits")
test(real_player, fake_player, "set_pos", vector.zero())
test(real_player, fake_player, "add_pos", vector.new(1, 1, 1))
test(real_player, fake_player, "get_pos")
test(real_player, fake_player, "set_look_horizontal", 0.6)
test(real_player, fake_player, "set_look_vertical", 0.3)
test(real_player, fake_player, "get_look_dir")
test(real_player, fake_player, "get_look_horizontal")
test(real_player, fake_player, "get_look_pitch")
test(real_player, fake_player, "get_look_yaw")
test(real_player, fake_player, "set_wielded_item", ItemStack("air 99"))
test(real_player, fake_player, "get_wielded_item")
-- Error tests
if error_tests then
for value in iter_values() do
-- Only test dynamic functions, no-op functions don't check arguments
test(real_player, fake_player, "set_pos", value)
test(real_player, fake_player, "add_pos", value)
test(real_player, fake_player, "set_look_horizontal", value)
test(real_player, fake_player, "set_look_vertical", value)
test(real_player, fake_player, "set_wielded_item", value)
end
end
-- Inventory tests
------------------------------
local real_inv = real_player:get_inventory()
local fake_inv = fakelib.create_inventory()
-- Type check
if not fakelib.is_inventory(real_inv) then
success = false
print("[fakelib] Real inventory is not an inventory.")
end
if not fakelib.is_inventory(fake_inv) then
success = false
print("[fakelib] Fake inventory is not an inventory.")
end
for value in iter_values() do
if fakelib.is_inventory(value) then
print(string.format("[fakelib] Value '%s' is an inventory.", tostring(value)))
end
end
-- Function check
for func in pairs(getmetatable(real_inv)) do
if not fake_inv[func] then
success = false
print(string.format("[fakelib] Missing function '%s'", func))
end
end
-- Save and clear
local old_inv = real_inv:get_lists()
real_inv:set_lists({})
local test_item = ItemStack({name = "air", meta = {test = "abc"}})
local test_list = {"", "air 50", test_item, "", "air 50", test_item}
-- Functional tests
test(real_inv, fake_inv, "set_list", "test", test_list)
test(real_inv, fake_inv, "is_empty", "test")
test(real_inv, fake_inv, "set_lists", {test = test_list})
test(real_inv, fake_inv, "set_size", "test", 10)
test(real_inv, fake_inv, "get_size", "test")
test(real_inv, fake_inv, "set_width", "test", 3)
test(real_inv, fake_inv, "get_width", "test")
test(real_inv, fake_inv, "set_stack", "test", 1.5, "air")
test(real_inv, fake_inv, "get_stack", "test", 1)
test(real_inv, fake_inv, "get_list", "test")
test(real_inv, fake_inv, "add_item", "test", "air")
test(real_inv, fake_inv, "room_for_item", "test", "air 99")
test(real_inv, fake_inv, "contains_item", "test", "air 99")
test(real_inv, fake_inv, "contains_item", "test", test_item, true)
test(real_inv, fake_inv, "remove_item", "test", "air 99")
test(real_inv, fake_inv, "get_lists")
-- Error tests
if error_tests then
for value in iter_values() do
test(real_inv, fake_inv, "set_list", value, {})
test(real_inv, fake_inv, "set_list", "test", value)
test(real_inv, fake_inv, "is_empty", value)
test(real_inv, fake_inv, "set_lists", value)
test(real_inv, fake_inv, "set_size", value, 1)
test(real_inv, fake_inv, "set_size", "test", value)
test(real_inv, fake_inv, "get_size", value)
test(real_inv, fake_inv, "set_width", value, 1)
test(real_inv, fake_inv, "set_width", "test", value)
test(real_inv, fake_inv, "get_width", value)
test(real_inv, fake_inv, "set_stack", value, 1, "test")
test(real_inv, fake_inv, "set_stack", "test", value, "test")
test(real_inv, fake_inv, "set_stack", "test", 1, value)
test(real_inv, fake_inv, "get_stack", value, 1)
test(real_inv, fake_inv, "get_stack", "test", value)
test(real_inv, fake_inv, "get_list", value)
test(real_inv, fake_inv, "add_item", value, "test")
test(real_inv, fake_inv, "add_item", "test", value)
test(real_inv, fake_inv, "room_for_item", value, "test")
test(real_inv, fake_inv, "room_for_item", "test", value)
test(real_inv, fake_inv, "contains_item", value, "test")
test(real_inv, fake_inv, "contains_item", "test", value)
test(real_inv, fake_inv, "contains_item", "test", "test", value)
test(real_inv, fake_inv, "remove_item", value, "test")
test(real_inv, fake_inv, "remove_item", "test", value)
end
end
-- Reset
real_inv:set_lists(old_inv)
-- Metadata tests
------------------------------
local real_meta = real_player:get_meta()
local fake_meta = fakelib.create_metadata()
-- Type check
if not fakelib.is_metadata(real_meta) then
success = false
print("[fakelib] Real metadata is not metadata.")
end
if not fakelib.is_metadata(fake_meta) then
success = false
print("[fakelib] Fake metadata is not metadata.")
end
for value in iter_values() do
if fakelib.is_metadata(value) then
print(string.format("[fakelib] Value '%s' is metadata.", tostring(value)))
end
end
-- Function check
for func in pairs(getmetatable(real_meta)) do
if not fake_meta[func] then
success = false
print(string.format("[fakelib] Missing function '%s'", func))
end
end
-- Save and clear
local old_meta = real_meta:to_table()
real_meta:from_table({})
local test_data = {fields = {test = "abc"}}
-- Functional tests
test(real_meta, fake_meta, "from_table", test_data)
test(real_meta, fake_meta, "contains", "test")
test(real_meta, fake_meta, "get", "test")
test(real_meta, fake_meta, "set_string", "test", "xyz")
test(real_meta, fake_meta, "get_string", "test")
test(real_meta, fake_meta, "set_int", "test", 123)
test(real_meta, fake_meta, "get_int", "test")
test(real_meta, fake_meta, "set_float", "test", 123.456789)
test(real_meta, fake_meta, "get_float", "test")
test(real_meta, fake_meta, "get_keys")
test(real_meta, fake_meta, "to_table")
test(real_meta, fake_meta, "equals", real_meta)
-- Error tests
if error_tests then
for value in iter_values() do
test(real_meta, fake_meta, "from_table", value)
test(real_meta, fake_meta, "contains", value)
test(real_meta, fake_meta, "get", value)
test(real_meta, fake_meta, "set_string", value, "test")
test(real_meta, fake_meta, "set_string", "test", value)
test(real_meta, fake_meta, "get_string", value)
test(real_meta, fake_meta, "set_int", value, 0)
test(real_meta, fake_meta, "set_int", "test", value)
test(real_meta, fake_meta, "get_int", value)
test(real_meta, fake_meta, "set_float", value, 0)
test(real_meta, fake_meta, "set_float", "test", value)
test(real_meta, fake_meta, "get_float", value)
test(real_meta, fake_meta, "equals", value)
end
end
-- Reset
real_meta:from_table(old_meta)
-- Misc tests
------------------------------
local basic_vector = {x = 1, y = 1, z = 1}
-- Vector type check
if not fakelib.is_vector({x = 1, y = 1, z = 1}) then
success = false
print("[fakelib] Basic vector is not a vector.")
end
if not fakelib.is_vector(vector.new(1, 1, 1)) then
success = false
print("[fakelib] Vector is not a vector.")
end
for value in iter_values() do
if fakelib.is_vector(value) then
print(string.format("[fakelib] Value '%s' is a vector.", tostring(value)))
end
end
-- Vector metatable
if fakelib.is_vector(basic_vector, true) then
if not basic_vector.add then
print("[fakelib] Failed to add vector metatable to basic vector.")
end
end
------------------------------
return success