test: cover layFrame and chargeTraveller

This commit is contained in:
dualfroz committed 2026-09-12 17:41:22 +02:00
1 parent e215d7190b
commit 0e137cd23b
1 file changed
+161
+161
View File
@@ -0,0 +1,161 @@
#include "testHarness.h"
#include "vf/Portal.h"
#include <cstdlib>
using namespace vf;
namespace {
// Lays an obsidian ring by hand, corners included, so the scanner has to work
// the geometry out rather than being handed a PortalFrame back.
PortalFrame layFrame(World& world, const Vec3i& origin, int width, int height, bool alongX) {
PortalFrame frame;
frame.origin = origin;
frame.width = width;
frame.height = height;
frame.alongX = alongX;
for (int across = -1; across <= width; ++across) {
for (int up = -1; up <= height; ++up) {
Vec3i cell = frame.interiorBlock(across, up);
bool ring = across == -1 || across == width || up == -1 || up == height;
world.setBlock(cell.x, cell.y, cell.z, ring ? Block::Obsidian : Block::Air);
}
}
return frame;
}
void chargeTraveller(PortalNetwork& network, int entityId) {
for (int tick = 0; tick < network.requiredStandingTicks(); ++tick)
network.tickEntity(entityId, true);
}
int countInterior(const World& world, const PortalFrame& frame, Block block) {
int total = 0;
for (int across = 0; across < frame.width; ++across)
for (int up = 0; up < frame.height; ++up) {
Vec3i cell = frame.interiorBlock(across, up);
if (world.blockAt(cell.x, cell.y, cell.z) == block) ++total;
}
return total;
}
} // namespace
VF_TEST(portalScanFindsTheSmallestLegalObsidianFrame) {
World world;
layFrame(world, {4, 40, 4}, 2, 3, true);
PortalScan scan = PortalScan::detect(world, {5, 41, 4}, DimensionId::Overworld);
CHECK(scan.found);
CHECK_EQ(scan.frame.width, 2);
CHECK_EQ(scan.frame.height, 3);
CHECK(scan.frame.alongX);
CHECK_EQ(scan.frame.origin.x, 4);
CHECK_EQ(scan.frame.origin.y, 40);
CHECK_EQ(scan.frame.origin.z, 4);
CHECK_EQ(scan.interiorArea, 6);
CHECK_EQ(static_cast<int>(scan.frame.dimension), static_cast<int>(DimensionId::Overworld));
}
VF_TEST(portalScanReadsFramesStandingInTheZPlane) {
World world;
layFrame(world, {-3, 20, 7}, 3, 4, false);
PortalScan scan = PortalScan::detect(world, {-3, 22, 8}, DimensionId::Nether);
CHECK(scan.found);
CHECK(!scan.frame.alongX);
CHECK_EQ(scan.frame.width, 3);
CHECK_EQ(scan.frame.height, 4);
CHECK_EQ(scan.frame.origin.z, 7);
CHECK_EQ(scan.frame.origin.x, -3);
CHECK_EQ(static_cast<int>(scan.frame.dimension), static_cast<int>(DimensionId::Nether));
}
VF_TEST(portalScanAcceptsAWideFrameUpToTheLimit) {
World world;
layFrame(world, {0, 30, 0}, 21, 21, true);
PortalScan wide = PortalScan::detect(world, {10, 40, 0}, DimensionId::Overworld);
CHECK(wide.found);
CHECK_EQ(wide.frame.width, 21);
CHECK_EQ(wide.frame.height, 21);
World oversized;
layFrame(oversized, {0, 30, 0}, 22, 3, true);
CHECK(!PortalScan::detect(oversized, {10, 31, 0}, DimensionId::Overworld).found);
}
VF_TEST(portalScanRejectsFramesUnderTheMinimumSize) {
World narrow;
layFrame(narrow, {0, 30, 0}, 1, 3, true);
CHECK(!PortalScan::detect(narrow, {0, 31, 0}, DimensionId::Overworld).found);
World squat;
layFrame(squat, {0, 30, 0}, 2, 2, true);
CHECK(!PortalScan::detect(squat, {0, 31, 0}, DimensionId::Overworld).found);
}
VF_TEST(portalScanIgnoresMissingCornersButNotMissingSides) {
World world;
PortalFrame frame = layFrame(world, {4, 40, 4}, 2, 3, true);
Vec3i corner = frame.interiorBlock(-1, -1);
world.setBlock(corner.x, corner.y, corner.z, Block::Air);
CHECK(PortalScan::detect(world, {5, 41, 4}, DimensionId::Overworld).found);
Vec3i side = frame.leftColumn(1);
world.setBlock(side.x, side.y, side.z, Block::Air);
CHECK(!PortalScan::detect(world, {5, 41, 4}, DimensionId::Overworld).found);
}
VF_TEST(portalScanRefusesAFrameWithSomethingStuckInTheOpening) {
World world;
layFrame(world, {4, 40, 4}, 2, 3, true);
world.setBlock(5, 41, 4, Block::Stone);
CHECK(!PortalScan::detect(world, {4, 40, 4}, DimensionId::Overworld).found);
}
VF_TEST(lightingAFrameFillsExactlyItsOpening) {
World world;
PortalFrame frame = layFrame(world, {4, 40, 4}, 3, 4, true);
CHECK_EQ(PortalScan::light(world, frame), 12);
CHECK_EQ(countInterior(world, frame, Block::NetherPortal), 12);
CHECK_EQ(PortalScan::light(world, frame), 0);
Vec3i outside = frame.interiorBlock(0, 0);
CHECK_EQ(world.blockAt(outside.x, outside.y, outside.z - 1), Block::Air);
Vec3i ringBlock = frame.bottomRow(0);
CHECK_EQ(world.blockAt(ringBlock.x, ringBlock.y, ringBlock.z), Block::Obsidian);
CHECK_EQ(PortalScan::douse(world, frame), 12);
CHECK_EQ(countInterior(world, frame, Block::Air), 12);
}
VF_TEST(breakingTheFrameUnmakesTheLitPortal) {
DimensionSet dimensions(77u);
PortalNetwork network(dimensions);
World& world = dimensions.overworld().world();
PortalFrame frame = layFrame(world, {4, 40, 4}, 2, 3, true);
int id = network.ignite(DimensionId::Overworld, {4, 41, 4});
CHECK(id >= 0);
CHECK_EQ(countInterior(world, frame, Block::NetherPortal), 6);
CHECK_EQ(network.activeCountIn(DimensionId::Overworld), 1);
// A missing corner is cosmetic; the portal survives a validation sweep.
Vec3i corner = frame.interiorBlock(-1, 3);
world.setBlock(corner.x, corner.y, corner.z, Block::Air);
CHECK_EQ(network.validate(DimensionId::Overworld), 0);
CHECK_EQ(countInterior(world, frame, Block::NetherPortal), 6);
Vec3i side = frame.topRow(1);
world.setBlock(side.x, side.y, side.z, Block::Air);
CHECK_EQ(network.validate(DimensionId::Overworld), 1);
CHECK_EQ(countInterior(world, frame, Block::NetherPortal), 0);
CHECK_EQ(network.activeCountIn(DimensionId::Overworld), 0);
CHECK(!network.portal(id)->active);
}