test: cover Session and Ledger
This commit is contained in:
1 parent
83f9ea128d
commit
7f6a1e4320
1 file changed
+156
@@ -0,0 +1,156 @@
|
||||
#include "testHarness.h"
|
||||
#include "vf/SlotAction.h"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace vf;
|
||||
|
||||
namespace {
|
||||
|
||||
const CraftingRegistry& sharedRegistry() {
|
||||
static CraftingRegistry registry;
|
||||
return registry;
|
||||
}
|
||||
|
||||
struct Session {
|
||||
ContainerMenu menu;
|
||||
CursorStack cursor;
|
||||
DragState drag;
|
||||
|
||||
explicit Session(MenuKind kind) : menu(kind, &sharedRegistry()) {}
|
||||
|
||||
SlotActionResult click(ClickKind kind, int slotIndex) {
|
||||
return applySlotAction(menu, cursor, drag, clickAt(kind, slotIndex));
|
||||
}
|
||||
SlotActionResult hotbar(int slotIndex, int button) {
|
||||
return applySlotAction(menu, cursor, drag, hotbarSwapAt(slotIndex, button));
|
||||
}
|
||||
};
|
||||
|
||||
// The seven items the random walk shuffles around. Tools are in the mix so the
|
||||
// walk also has stacks that refuse to merge.
|
||||
const std::array<ItemId, 7> walkItems = {ItemId::Stone, ItemId::Planks, ItemId::Sand,
|
||||
ItemId::CoalLump, ItemId::Apple, ItemId::IronPickaxe,
|
||||
ItemId::RawBeef};
|
||||
|
||||
struct Ledger {
|
||||
std::array<int, 7> counts{};
|
||||
|
||||
void snapshot(const ContainerMenu& menu, const CursorStack& cursor) {
|
||||
for (std::size_t i = 0; i < walkItems.size(); ++i) {
|
||||
counts[i] = totalAcrossMenuAndCursor(menu, cursor, walkItems[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void seedMenu(ContainerMenu& menu, uint32_t& rng) {
|
||||
for (int i = 0; i < menu.slotCount(); ++i) {
|
||||
if (menu.slot(i).takeOnly) continue;
|
||||
if (stackRandom(rng) % 3u == 0u) continue;
|
||||
ItemId item = walkItems[stackRandom(rng) % walkItems.size()];
|
||||
MetaStack stack = makeStack(item, 1 + static_cast<int>(stackRandom(rng) % 40u));
|
||||
if (!menu.canPlace(i, stack)) continue;
|
||||
menu.slot(i).cell.insert(stack);
|
||||
}
|
||||
}
|
||||
|
||||
SlotAction randomAction(const ContainerMenu& menu, uint32_t& rng) {
|
||||
ClickKind kind = static_cast<ClickKind>(stackRandom(rng) %
|
||||
static_cast<uint32_t>(ClickKind::Count));
|
||||
int slotIndex = static_cast<int>(stackRandom(rng) % static_cast<uint32_t>(menu.slotCount()));
|
||||
// Every eighth action aims outside the window, which is the only way to
|
||||
// reach the throw-away path with a full cursor.
|
||||
if (stackRandom(rng) % 8u == 0u) slotIndex = -1;
|
||||
if (kind == ClickKind::HotbarSwap) {
|
||||
return hotbarSwapAt(slotIndex, static_cast<int>(stackRandom(rng) % 9u));
|
||||
}
|
||||
return clickAt(kind, slotIndex);
|
||||
}
|
||||
|
||||
struct WalkCoverage {
|
||||
int applied = 0;
|
||||
int dropped = 0;
|
||||
int distinctKindsApplied = 0;
|
||||
};
|
||||
|
||||
WalkCoverage runConservationWalk(MenuKind kind, uint32_t seed, int steps) {
|
||||
Session session(kind);
|
||||
uint32_t rng = seed;
|
||||
seedMenu(session.menu, rng);
|
||||
|
||||
Ledger before;
|
||||
before.snapshot(session.menu, session.cursor);
|
||||
|
||||
std::array<int, 7> thrown{};
|
||||
std::array<int, static_cast<std::size_t>(ClickKind::Count)> perKind{};
|
||||
WalkCoverage coverage;
|
||||
for (int step = 0; step < steps; ++step) {
|
||||
SlotAction action = randomAction(session.menu, rng);
|
||||
SlotActionResult outcome =
|
||||
applySlotAction(session.menu, session.cursor, session.drag, action);
|
||||
if (outcome.applied) {
|
||||
++coverage.applied;
|
||||
++perKind[static_cast<std::size_t>(action.kind)];
|
||||
}
|
||||
coverage.dropped += outcome.dropped;
|
||||
CHECK_EQ(outcome.created, 0);
|
||||
CHECK_EQ(outcome.dropped, static_cast<int>([&] {
|
||||
int sum = 0;
|
||||
for (const MetaStack& lost : outcome.droppedStacks) sum += lost.count;
|
||||
return sum;
|
||||
}()));
|
||||
for (const MetaStack& lost : outcome.droppedStacks) {
|
||||
for (std::size_t i = 0; i < walkItems.size(); ++i) {
|
||||
if (lost.item == walkItems[i]) thrown[i] += lost.count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ledger after;
|
||||
after.snapshot(session.menu, session.cursor);
|
||||
for (std::size_t i = 0; i < walkItems.size(); ++i) {
|
||||
CHECK_EQ(after.counts[i] + thrown[i], before.counts[i]);
|
||||
}
|
||||
for (int hits : perKind) {
|
||||
if (hits > 0) ++coverage.distinctKindsApplied;
|
||||
}
|
||||
return coverage;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
VF_TEST(everyClickKindHasAName) {
|
||||
for (int i = 0; i < static_cast<int>(ClickKind::Count); ++i) {
|
||||
CHECK(std::string(clickKindName(static_cast<ClickKind>(i))) != std::string("unknown"));
|
||||
}
|
||||
}
|
||||
|
||||
VF_TEST(aLeftClickPicksUpTheWholeStackAndPutsItDownAgain) {
|
||||
Session session(MenuKind::Chest);
|
||||
session.menu.slot(0).cell.stack = makeStack(ItemId::Stone, 37);
|
||||
|
||||
SlotActionResult pickup = session.click(ClickKind::Left, 0);
|
||||
CHECK(pickup.applied);
|
||||
CHECK_EQ(pickup.moved, 37);
|
||||
CHECK(pickup.cursorChanged);
|
||||
CHECK_EQ(session.cursor.held.count, 37);
|
||||
CHECK(session.menu.stackAt(0).empty());
|
||||
|
||||
SlotActionResult drop = session.click(ClickKind::Left, 4);
|
||||
CHECK(drop.applied);
|
||||
CHECK_EQ(drop.moved, 37);
|
||||
CHECK(session.cursor.empty());
|
||||
CHECK_EQ(session.menu.stackAt(4).count, 37);
|
||||
|
||||
CHECK(!session.click(ClickKind::Left, 9).applied);
|
||||
CHECK(!session.click(ClickKind::Left, 999).applied);
|
||||
}
|
||||
|
||||
VF_TEST(aLeftClickMergesIntoAMatchingStackAndSwapsWithAForeignOne) {
|
||||
Session session(MenuKind::Chest);
|
||||
session.menu.slot(0).cell.stack = makeStack(ItemId::Sand, 50);
|
||||
session.menu.slot(1).cell.stack = makeStack(ItemId::Sand, 60);
|
||||
session.menu.slot(2).cell.stack = makeStack(ItemId::Planks, 8);
|
||||
|
||||
Reference in new issue
Block a user