From 854dcfaf15f0871c73fe9aa9c29dc842bd895549 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 28 Aug 2026 10:36:55 +0200 Subject: [PATCH] test: add tests for hopper item transfer between containers --- tests/testHopper.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 tests/testHopper.cpp diff --git a/tests/testHopper.cpp b/tests/testHopper.cpp new file mode 100644 index 0000000..ebaa9b4 --- /dev/null +++ b/tests/testHopper.cpp @@ -0,0 +1,123 @@ +#include "testHarness.h" +#include "vf/Hopper.h" + +using namespace vf; + +VF_TEST(hopperPullsFromContainerAboveOneItemPerCycle) { + World world; + Vec3i pos{5, 4, 5}; + world.setBlock(pos.x, pos.y, pos.z, Block::Hopper); + + ContainerStore store; + store.ensure(pos + Vec3i{0, 1, 0}, 5).insert(ItemId::Stone, 3); + + HopperState state; + CHECK(tickHopper(world, pos, state, store)); + CHECK_EQ(store.at(pos)->totalItems(), 1); + CHECK_EQ(store.at(pos + Vec3i{0, 1, 0})->totalItems(), 2); +} + +VF_TEST(hopperPushesIntoTargetItFaces) { + World world; + Vec3i pos{5, 4, 5}; + world.setBlock(pos.x, pos.y, pos.z, Block::Hopper); + + ContainerStore store; + store.ensure(pos, hopperSlots).insert(ItemId::Dirt, 4); + store.ensure(pos + Vec3i{0, -1, 0}, 5); + + HopperState state; + CHECK(tickHopper(world, pos, state, store)); + CHECK_EQ(store.at(pos + Vec3i{0, -1, 0})->totalItems(), 1); + CHECK_EQ(store.at(pos)->totalItems(), 3); +} + +VF_TEST(hopperRespectsCooldownBetweenTransfers) { + World world; + Vec3i pos{0, 6, 0}; + world.setBlock(pos.x, pos.y, pos.z, Block::Hopper); + + ContainerStore store; + store.ensure(pos + Vec3i{0, 1, 0}, 5).insert(ItemId::Stone, 64); + + HopperState state; + CHECK(tickHopper(world, pos, state, store)); // transfer, arms cooldown 8 + for (int i = 0; i < hopperCooldownTicks; ++i) + CHECK(!tickHopper(world, pos, state, store)); // idle while cooldown drains + CHECK(tickHopper(world, pos, state, store)); // fires again + CHECK_EQ(store.at(pos)->totalItems(), 2); +} + +VF_TEST(hopperDoesNothingWhenSourceEmptyAndNoPushTarget) { + World world; + Vec3i pos{1, 1, 1}; + world.setBlock(pos.x, pos.y, pos.z, Block::Hopper); + + ContainerStore store; + store.ensure(pos, hopperSlots); + store.ensure(pos + Vec3i{0, 1, 0}, 5); // empty source + + HopperState state; + CHECK(!tickHopper(world, pos, state, store)); + CHECK_EQ(store.at(pos)->totalItems(), 0); +} + +VF_TEST(hopperBlockedWhenTargetIsFull) { + World world; + Vec3i pos{2, 2, 2}; + world.setBlock(pos.x, pos.y, pos.z, Block::Hopper); + + ContainerStore store; + store.ensure(pos, hopperSlots).insert(ItemId::Stone, 5); + Container& target = store.ensure(pos + Vec3i{0, -1, 0}, 1); + target.insert(ItemId::Stone, 64); // exactly full + + HopperState state; + CHECK(!tickHopper(world, pos, state, store)); + CHECK_EQ(target.totalItems(), 64); + CHECK_EQ(store.at(pos)->totalItems(), 5); +} + +VF_TEST(hopperIgnoredWhenWorldBlockIsNotAHopper) { + World world; + Vec3i pos{3, 3, 3}; + world.setBlock(pos.x, pos.y, pos.z, Block::Air); + + ContainerStore store; + store.ensure(pos + Vec3i{0, 1, 0}, 5).insert(ItemId::Stone, 5); + + HopperState state; + CHECK(!tickHopper(world, pos, state, store)); + CHECK(store.at(pos) == nullptr); +} + +VF_TEST(hopperCanTransferReportsReadiness) { + World world; + Vec3i pos{7, 7, 7}; + world.setBlock(pos.x, pos.y, pos.z, Block::Hopper); + + ContainerStore store; + store.ensure(pos, hopperSlots); + HopperState state; + CHECK(!hopperCanTransfer(pos, state, store)); + + store.ensure(pos + Vec3i{0, 1, 0}, 5).insert(ItemId::Stone, 1); + CHECK(hopperCanTransfer(pos, state, store)); +} + +VF_TEST(hopperChainMovesEntireStackAcrossManyCycles) { + World world; + Vec3i pos{4, 4, 4}; + world.setBlock(pos.x, pos.y, pos.z, Block::Hopper); + + ContainerStore store; + store.ensure(pos + Vec3i{0, 1, 0}, 5).insert(ItemId::Stone, 5); + store.ensure(pos + Vec3i{0, -1, 0}, 5); + + HopperState state; + for (int tick = 0; tick < 100; ++tick) + tickHopper(world, pos, state, store); + + CHECK_EQ(store.at(pos + Vec3i{0, 1, 0})->totalItems(), 0); + CHECK_EQ(store.at(pos + Vec3i{0, -1, 0})->totalItems(), 5); +}