From b00072594d1214ba90be6948bce4b3013360c35e Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 11 Sep 2026 19:09:24 +0200 Subject: [PATCH] test: cover testItemStack --- tests/testItemStack.cpp | 160 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/tests/testItemStack.cpp b/tests/testItemStack.cpp index 5d8ec2c..822dc8f 100644 --- a/tests/testItemStack.cpp +++ b/tests/testItemStack.cpp @@ -158,3 +158,163 @@ VF_TEST(splittingAStackLeavesTheLargerHalfBehind) { CHECK_EQ(lastOne.count, 1); CHECK(single.empty()); + MetaStack pile = makeStack(ItemId::Sand, 30); + MetaStack five = splitOff(pile, 5); + CHECK_EQ(five.count, 5); + CHECK_EQ(pile.count, 25); + CHECK_EQ(splitOff(pile, 0).count, 0); +} + +VF_TEST(splittingCarriesTheMetadataOntoBothHalves) { + MetaStack named = makeStack(ItemId::Stone, 20); + named.meta.repairCost = 3; + MetaStack half = splitHalf(named); + CHECK_EQ(half.meta.repairCost, 3); + CHECK_EQ(named.meta.repairCost, 3); + CHECK(stacksMergeable(half, named)); +} + +VF_TEST(defragmentPacksCompatibleStacksAndFreesTheSlotsBehindThem) { + std::vector pouch = { + makeStack(ItemId::Stone, 40), MetaStack{}, makeStack(ItemId::Stone, 40), + makeStack(ItemId::Planks, 5), makeStack(ItemId::Stone, 10), + makeStack(ItemId::Planks, 7), + }; + + int freed = defragmentStacks(pouch); + CHECK_EQ(totalCountOf(pouch, ItemId::Stone), 90); + CHECK_EQ(totalCountOf(pouch, ItemId::Planks), 12); + CHECK(pouch[0].item == ItemId::Stone); + CHECK_EQ(pouch[0].count, 64); + CHECK(pouch[1].item == ItemId::Stone); + CHECK_EQ(pouch[1].count, 26); + CHECK(pouch[2].item == ItemId::Planks); + CHECK_EQ(pouch[2].count, 12); + CHECK(pouch[3].empty()); + CHECK(pouch[5].empty()); + CHECK_EQ(freed, 2); +} + +VF_TEST(defragmentKeepsStacksWithDifferentMetadataApart) { + uint32_t rng = 11u; + MetaStack worn = makeTool(ItemId::IronAxe); + damageStack(worn, 3, rng); + + std::vector pouch = {makeTool(ItemId::IronAxe), worn, makeTool(ItemId::IronAxe)}; + defragmentStacks(pouch); + + CHECK_EQ(totalCountOf(pouch, ItemId::IronAxe), 3); + CHECK_EQ(pouch[0].count, 1); + CHECK_EQ(pouch[1].count, 1); + CHECK_EQ(pouch[2].count, 1); +} + +VF_TEST(sortingIsStableForStacksThatCompareEqual) { + std::vector pouch = { + makeStack(ItemId::Planks, 10), MetaStack{}, makeStack(ItemId::Stone, 10), + makeStack(ItemId::Planks, 10), makeStack(ItemId::Stone, 64), + }; + pouch[0].meta.repairCost = 1; + pouch[3].meta.repairCost = 2; + + sortStacks(pouch); + + CHECK(pouch[0].item == ItemId::Stone); + CHECK_EQ(pouch[0].count, 64); + CHECK(pouch[1].item == ItemId::Stone); + CHECK(pouch[2].item == ItemId::Planks); + CHECK_EQ(pouch[2].meta.repairCost, 1); + CHECK(pouch[3].item == ItemId::Planks); + CHECK_EQ(pouch[3].meta.repairCost, 2); + CHECK(pouch[4].empty()); +} + +VF_TEST(nbtRoundTripKeepsDurabilityEnchantmentsAndTheCustomName) { + EnchantSet enchants; + addEnchant(enchants, EnchantKind::Sharpness, 4); + addEnchant(enchants, EnchantKind::Unbreaking, 3); + addEnchant(enchants, EnchantKind::Mending, 1); + + MetaStack sword = makeEnchantedTool(ItemId::DiamondSword, enchants); + sword.meta.customName = "Vulpine Edge"; + sword.meta.durability.used = 137; + sword.meta.repairCost = 9; + + MetaStack restored; + CHECK(stackFromNbt(stackToNbt(sword), restored)); + + CHECK(restored.item == ItemId::DiamondSword); + CHECK_EQ(restored.count, 1); + CHECK_EQ(restored.meta.durability.used, 137); + CHECK_EQ(restored.meta.durability.max, sword.meta.durability.max); + CHECK_EQ(restored.meta.repairCost, 9); + CHECK(restored.meta.customName == std::string("Vulpine Edge")); + CHECK_EQ(restored.meta.enchants.levelOf(EnchantKind::Sharpness), 4); + CHECK_EQ(restored.meta.enchants.levelOf(EnchantKind::Unbreaking), 3); + CHECK_EQ(restored.meta.enchants.levelOf(EnchantKind::Mending), 1); + CHECK(stacksIdentical(sword, restored)); +} + +VF_TEST(nbtRoundTripSurvivesTheBinaryEncoderAndKeepsEmptySlotsEmpty) { + std::vector pouch = { + makeStack(ItemId::Stone, 64), + MetaStack{}, + makeNamedStack(ItemId::GoldSword, 1, "Bribe"), + makeStack(ItemId::Apple, 3), + }; + pouch[2].meta.unbreakable = true; + + NbtTag root = NbtTag::makeCompound(); + root.put("Pouch", stackListToNbt(pouch)); + + std::vector bytes = serializeNbt("Test", root); + std::string name; + NbtTag decoded; + CHECK(deserializeNbt(bytes.data(), bytes.size(), name, decoded)); + + const NbtTag* list = decoded.find("Pouch"); + CHECK(list != nullptr); + if (list == nullptr) return; + + std::vector restored; + CHECK(stackListFromNbt(*list, restored)); + CHECK_EQ(restored.size(), pouch.size()); + for (std::size_t i = 0; i < pouch.size(); ++i) { + CHECK(stacksIdentical(pouch[i], restored[i])); + } + CHECK(restored[1].empty()); + CHECK(restored[2].meta.unbreakable); +} + +VF_TEST(nbtRejectsAStackTagThatNamesAnItemOutsideTheTable) { + NbtTag broken = NbtTag::makeCompound(); + broken.put("Id", NbtTag::makeShort(static_cast(static_cast(ItemId::Count) + 40))); + broken.put("Count", NbtTag::makeShort(1)); + + MetaStack out = makeStack(ItemId::Stone, 5); + CHECK(!stackFromNbt(broken, out)); + CHECK(out.empty()); + + NbtTag notACompound = NbtTag::makeInt(4); + CHECK(!stackFromNbt(notACompound, out)); +} + +VF_TEST(aToolBreaksOnExactlyTheHitThatReachesZeroDurability) { + uint32_t rng = 5u; + MetaStack pickaxe = makeTool(ItemId::WoodPickaxe); + int max = pickaxe.meta.durability.max; + CHECK(max > 1); + + for (int hit = 1; hit < max; ++hit) { + WearResult wear = damageStack(pickaxe, 1, rng); + CHECK(wear == WearResult::Damaged); + } + CHECK_EQ(pickaxe.meta.durability.remaining(), 1); + CHECK(!pickaxe.empty()); + + WearResult final = damageStack(pickaxe, 1, rng); + CHECK(final == WearResult::Broke); + CHECK(pickaxe.empty()); + CHECK(damageStack(pickaxe, 1, rng) == WearResult::NotDamageable); +} +