170 lines
7.8 KiB
Lua
Executable File
170 lines
7.8 KiB
Lua
Executable File
-- Original MIT-licensed integration checks. Staged as a disposable world mod;
|
|
-- never copy this into the persistent play world. No engine globals are mocked.
|
|
local mode = core.settings:get("hearth_launcher_mode")
|
|
local token = core.settings:get("hearth_smoke_token")
|
|
assert(mode == "check" or mode == "smoke", "Invalid launcher test mode")
|
|
assert(token and token:match("^[%x%-]+$"), "Missing launcher test token")
|
|
local modpath = core.get_modpath(core.get_current_modname())
|
|
local finished = false
|
|
|
|
local function finish(ok, message)
|
|
if finished then return end
|
|
finished = true
|
|
if ok then
|
|
core.log("action", "LAST_HEARTH_" .. mode:upper() .. "_PASS " .. token)
|
|
else
|
|
core.log("error", "LAST_HEARTH_TEST_FAIL: " .. tostring(message))
|
|
end
|
|
core.request_shutdown("Last Hearth " .. mode .. " complete", false, 0)
|
|
end
|
|
|
|
local function guarded(fn)
|
|
local ok, message = pcall(fn)
|
|
if not ok then finish(false, message) end
|
|
end
|
|
|
|
local function syntax_check()
|
|
local manifest = dofile(modpath .. "/syntax_manifest.lua")
|
|
assert(#manifest > 0, "Empty syntax manifest")
|
|
for _, entry in ipairs(manifest) do
|
|
local chunk, message = loadfile(modpath .. "/syntax/" .. entry.file)
|
|
assert(chunk, entry.label .. ": " .. tostring(message))
|
|
end
|
|
core.log("action", "Last Hearth: compiled " .. #manifest .. " Lua files (standalone specs not executed)")
|
|
end
|
|
|
|
local function registry_check()
|
|
assert(type(hearth) == "table", "Real hearth global was not loaded")
|
|
assert(type(hearth.model) == "table", "hearth.model missing")
|
|
assert(type(hearth.players) == "table", "hearth.players missing")
|
|
assert(core.get_modpath("hearth"), "hearth mod missing")
|
|
for _, name in ipairs({"default", "doors", "farming", "fire", "stairs", "wool", "sfinv"}) do
|
|
assert(not core.get_modpath(name), "Unexpected MTG dependency: " .. name)
|
|
end
|
|
for name in pairs(core.registered_items) do
|
|
assert(not name:match("^default:"), "MTG item leaked into standalone game: " .. name)
|
|
end
|
|
for _, name in ipairs({"stone", "soil", "grass", "sand", "water_source", "water_flowing",
|
|
"log", "leaves", "loose_stone", "sticks", "berry_bush", "reeds", "planks",
|
|
"wall", "roof", "fire", "fire_lit"}) do
|
|
assert(core.registered_nodes["hearth:" .. name], "Missing node: " .. name)
|
|
end
|
|
for _, name in ipairs({"berries", "raw_food", "cooked_food", "vessel", "water_raw",
|
|
"water_clean", "knife", "axe", "pick", "spear"}) do
|
|
assert(core.registered_items["hearth:" .. name], "Missing item: " .. name)
|
|
end
|
|
for _, name in ipairs({"knife", "axe", "pick", "spear", "vessel", "fire", "wall",
|
|
"roof", "planks", "sticks", "raw_food"}) do
|
|
local output = "hearth:" .. name
|
|
local recipe = core.get_craft_recipe(output)
|
|
assert(recipe.items and next(recipe.items), "Missing craft: " .. output)
|
|
for _, ingredient in pairs(recipe.items) do
|
|
assert(ingredient == "" or core.registered_items[ingredient], "Unknown ingredient: " .. ingredient)
|
|
end
|
|
local inputs = {}
|
|
for index = 1, (recipe.width == 0 and #recipe.items or recipe.width * 3) do
|
|
inputs[index] = ItemStack(recipe.items[index] or "")
|
|
end
|
|
local width = recipe.width > 0 and recipe.width or #inputs
|
|
local result = core.get_craft_result({method = "normal", width = width, items = inputs})
|
|
assert(result.item:get_name() == output, "Engine craft result mismatch: " .. output)
|
|
assert(result.item:get_count() == ItemStack(recipe.output):get_count(), "Craft quantity mismatch: " .. output)
|
|
end
|
|
assert(core.registered_aliases.mapgen_stone == "hearth:stone", "Missing mapgen stone alias")
|
|
assert(core.registered_aliases.mapgen_water_source == "hearth:water_source", "Missing water alias")
|
|
assert(core.registered_biomes.hearth_temperate, "Missing temperate biome")
|
|
assert(core.registered_biomes.hearth_riverbank, "Missing riverbank biome")
|
|
assert(core.settings:get_bool("enable_pvp") == false, "PvP must be disabled")
|
|
assert(core.settings:get_bool("secure.enable_security") == true, "Mod security must be enabled")
|
|
assert(core.settings:get("bind_address") == "127.0.0.1", "Server must bind loopback only")
|
|
end
|
|
|
|
local function unit_check()
|
|
-- Each standalone harness receives its own globals and os table. Mocked core,
|
|
-- hearth and os.time never replace globals of the running game.
|
|
local manifest = dofile(modpath .. "/syntax_manifest.lua")
|
|
local lookup = {}
|
|
for _, entry in ipairs(manifest) do lookup[entry.label] = modpath .. "/syntax/" .. entry.file end
|
|
local count = 0
|
|
for label, filename in pairs(lookup) do
|
|
if label:match("_spec%.lua$") then
|
|
local module = label:match("tests/(.+)_spec%.lua$")
|
|
local target = assert(lookup["mods/hearth/" .. module .. ".lua"], "Missing test module")
|
|
local env = setmetatable({arg = {target}, os = {}}, {__index = _G})
|
|
for k, v in pairs(os) do env.os[k] = v end
|
|
env._G = env
|
|
env.dofile = function(path)
|
|
assert(path == target, "Unit harness attempted unexpected file access")
|
|
local chunk = assert(loadfile(path)); setfenv(chunk, env); return chunk()
|
|
end
|
|
local chunk = assert(loadfile(filename)); setfenv(chunk, env); chunk()
|
|
count = count + 1
|
|
end
|
|
end
|
|
assert(count >= 2, "Expected model and content regression suites")
|
|
core.log("action", "Last Hearth: " .. count .. " isolated unit suites executed")
|
|
end
|
|
|
|
local function emerge_check()
|
|
local callbacks = 0
|
|
core.emerge_area({x = -64, y = -32, z = -64}, {x = 64, y = 96, z = 64},
|
|
function(_, action, remaining)
|
|
if finished then return end
|
|
guarded(function()
|
|
assert(action ~= core.EMERGE_ERRORED and action ~= core.EMERGE_CANCELLED,
|
|
"Map emergence failed or was cancelled")
|
|
callbacks = callbacks + 1
|
|
if remaining ~= 0 then return end
|
|
-- Inspect after mapgen callbacks have completed, on a server step.
|
|
core.after(0, function()
|
|
guarded(function()
|
|
assert(callbacks > 0, "No emerge callbacks received")
|
|
local material = false
|
|
for z = -16, 16, 4 do
|
|
for y = -32, 16, 4 do
|
|
for x = -16, 16, 4 do
|
|
local node = core.get_node_or_nil({x = x, y = y, z = z})
|
|
assert(node and node.name ~= "ignore", "Emerged map contains unloaded nodes")
|
|
assert(core.registered_nodes[node.name], "Unregistered generated node: " .. node.name)
|
|
assert(node.name == "air" or node.name:match("^hearth:"),
|
|
"Foreign generated node: " .. node.name)
|
|
if node.name:match("^hearth:") then material = true end
|
|
end
|
|
end
|
|
end
|
|
assert(material, "Mapgen produced no hearth terrain in the test volume")
|
|
local resources = {}
|
|
for _, name in ipairs({"grass", "loose_stone", "sticks", "berry_bush", "reeds", "log", "water_source"}) do
|
|
local nodes = core.find_nodes_in_area({x = -64, y = -16, z = -64}, {x = 64, y = 96, z = 64}, {"hearth:" .. name})
|
|
resources[#resources + 1] = name .. "=" .. #nodes
|
|
assert(#nodes > 0, "Starting region missing required resource: " .. name)
|
|
end
|
|
core.log("action", "Last Hearth starting resources: " .. table.concat(resources, ", "))
|
|
local p = {x = 0, y = 85, z = 0}
|
|
core.set_node(vector.offset(p, 0, 3, 0), {name = "hearth:roof"})
|
|
for _, d in ipairs({{1,0},{-1,0},{0,1},{0,-1}}) do
|
|
core.set_node(vector.offset(p, d[1]*2, 1, d[2]*2), {name = "hearth:wall"})
|
|
end
|
|
local env = hearth.environment(p)
|
|
assert(env.roof and env.shelter == 1, "A roof and four walls must enclose a shelter")
|
|
core.remove_node(vector.offset(p, 0, 3, 0))
|
|
assert(hearth.environment(p).shelter == 0, "Walls without a roof are exposed")
|
|
finish(true)
|
|
end)
|
|
end)
|
|
end)
|
|
end)
|
|
end
|
|
|
|
core.after(1, function()
|
|
guarded(function()
|
|
syntax_check()
|
|
if mode == "check" then finish(true); return end
|
|
registry_check()
|
|
unit_check()
|
|
emerge_check()
|
|
end)
|
|
end)
|
|
|
|
core.after(60, function() finish(false, "Engine test deadline exceeded") end)
|