diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index 724334326..a971632c9 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -12,6 +12,7 @@ unittests.list = {} -- player = false, -- Does test require a player? -- map = false, -- Does test require map access? -- async = false, -- Does the test run asynchronously? (read notes above!) +-- random = false, -- Does the test use math.random directly or indirectly? -- } function unittests.register(name, func, opts) local def = table.copy(opts or {}) @@ -47,8 +48,18 @@ local function await(invoke) return coroutine.yield() end +local function printf(fmt, ...) + print(fmt:format(...)) +end + function unittests.run_one(idx, counters, out_callback, player, pos) local def = unittests.list[idx] + local seed + if def.random then + seed = core.get_us_time() + math.randomseed(seed) + end + if not def.player then player = nil elseif player == nil then @@ -70,8 +81,10 @@ function unittests.run_one(idx, counters, out_callback, player, pos) if not status then core.log("error", err) end - print(string.format("[%s] %s - %dms", - status and "PASS" or "FAIL", def.name, ms_taken)) + printf("[%s] %s - %dms", status and "PASS" or "FAIL", def.name, ms_taken) + if seed and not status then + printf("Random was seeded to %d", seed) + end counters.time = counters.time + ms_taken counters.total = counters.total + 1 if status then @@ -160,11 +173,11 @@ function unittests.run_all() -- Print stats assert(#unittests.list == counters.total) print(string.rep("+", 80)) - print(string.format("Devtest 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)) + local passed = counters.total == counters.passed + printf("Devtest Unit Test Results: %s", passed and "PASSED" or "FAILED") + printf(" %d / %d failed tests.", + counters.total - counters.passed, counters.total) + printf(" Testing took %dms total.", counters.time) print(string.rep("+", 80)) unittests.on_finished(counters.total == counters.passed) return counters.total == counters.passed diff --git a/games/devtest/mods/unittests/raycast.lua b/games/devtest/mods/unittests/raycast.lua index db5e78b34..1dc196cc5 100644 --- a/games/devtest/mods/unittests/raycast.lua +++ b/games/devtest/mods/unittests/raycast.lua @@ -36,6 +36,28 @@ end unittests.register("test_raycast_pointabilities", test_raycast_pointabilities, {map=true}) local function test_raycast_noskip(_, pos) + local function random_point_in_area(min, max) + local extents = max - min + local v = extents:multiply(vector.new( + math.random(), + math.random(), + math.random() + )) + return min + v + end + + -- FIXME a variation of this unit test fails in an edge case. + -- This is because Luanti does not handle perfectly diagonal raycasts correctly: + -- Perfect diagonals collide with neither "outside" face and may thus "pierce" nodes. + -- Enable the following code to reproduce: + if 0 == 1 then + pos = vector.new(6, 32, -3) + math.randomseed(1596190898) + function random_point_in_area(min, max) + return min:combine(max, math.random) + end + end + local function cuboid_minmax(extent) return pos:offset(-extent, -extent, -extent), pos:offset(extent, extent, extent) @@ -62,10 +84,10 @@ local function test_raycast_noskip(_, pos) for _ = 1, 100 do local ray_start repeat - ray_start = vector.random_in_area(cuboid_minmax(r)) + ray_start = random_point_in_area(cuboid_minmax(r)) until not ray_start:in_area(cuboid_minmax(1.501)) -- Pick a random position inside the dirt - local ray_end = vector.random_in_area(cuboid_minmax(1.499)) + local ray_end = random_point_in_area(cuboid_minmax(1.499)) -- The first pointed thing should have only air "in front" of it, -- or a dirt node got falsely skipped. local pt = core.raycast(ray_start, ray_end, false, false):next() @@ -78,4 +100,4 @@ local function test_raycast_noskip(_, pos) vm:write_to_map() end -unittests.register("test_raycast_noskip", test_raycast_noskip, {map = true}) +unittests.register("test_raycast_noskip", test_raycast_noskip, {map = true, random = true})