test: cover testPotionCatalog

This commit is contained in:
dualfroz committed 2026-09-12 18:32:53 +02:00
1 parent 3bd3ad25bc
commit 5c3db750d6
1 file changed
+160
+160
View File
@@ -155,3 +155,163 @@ VF_TEST(lingeringBottlesDoseAQuarterAndLeaveACloudBehindThem) {
CHECK_EQ(catalog.variantTicks(lingeringStrong), 450);
CHECK_EQ(catalog.variantLevel(lingeringStrong), 2);
PotionItem lingeringWater = bottleOf(PotionKind::Water, false, false,
PotionItem::Form::Lingering);
CHECK_EQ(catalog.variantTicks(lingeringWater), 0);
CHECK_EQ(catalog.cloudTicks(lingeringWater), 0);
}
VF_TEST(instantPotionsStayAtOneTickInEveryFormAndModifier) {
const PotionCatalog& catalog = potionCatalog();
const PotionKind instants[] = {PotionKind::Healing, PotionKind::Harming};
const PotionItem::Form forms[] = {PotionItem::Form::Drinkable, PotionItem::Form::Splash,
PotionItem::Form::Lingering};
for (PotionKind kind : instants) {
CHECK(catalog.entry(kind).instant);
CHECK(!catalog.entry(kind).acceptsRedstone());
CHECK(catalog.entry(kind).acceptsGlowstone());
for (PotionItem::Form form : forms) {
CHECK_EQ(catalog.variantTicks(bottleOf(kind, false, false, form)), 1);
CHECK_EQ(catalog.variantTicks(bottleOf(kind, true, false, form)), 1);
CHECK_EQ(catalog.variantLevel(bottleOf(kind, true, false, form)), 2);
}
}
}
VF_TEST(everyCatalogRouteIsARealRecipeAndAlsoTheShortestOne) {
const PotionCatalog& catalog = potionCatalog();
const BrewGraph& graph = brewGraph();
int routesChecked = 0;
for (const PotionEntry& row : catalog.entries()) {
if (row.ingredient == Ingredient::None) {
CHECK_EQ(row.kind, PotionKind::Water);
continue;
}
bool changed = false;
PotionItem brewed = brewResult(bottleOf(row.base), row.ingredient, changed);
CHECK(changed);
CHECK_EQ(brewed.kind, row.kind);
CHECK_EQ(graph.depthOf(bottleOf(row.kind)), graph.depthOf(bottleOf(row.base)) + 1);
++routesChecked;
}
CHECK_EQ(routesChecked, static_cast<int>(PotionKind::Count) - 1);
}
VF_TEST(harmfulFlagsAndEffectLookupsAgreeWithTheEffectModule) {
const PotionCatalog& catalog = potionCatalog();
for (const PotionEntry& row : catalog.entries()) {
if (!row.carriesEffect()) {
CHECK(!row.harmful);
CHECK(!row.instant);
continue;
}
CHECK_EQ(row.harmful, isHarmfulEffect(row.effect));
CHECK_EQ(row.instant, isInstantEffect(row.effect));
CHECK_EQ(catalog.kindForEffect(row.effect), row.kind);
}
const EffectKind uncovered[] = {EffectKind::Haste, EffectKind::MiningFatigue,
EffectKind::Nausea, EffectKind::Resistance,
EffectKind::Wither, EffectKind::HealthBoost,
EffectKind::Absorption, EffectKind::Saturation,
EffectKind::Levitation, EffectKind::Count};
for (EffectKind effect : uncovered) CHECK_EQ(catalog.kindForEffect(effect), PotionKind::Count);
}
VF_TEST(invertedKindReadsTheFermentedSpiderEyeBranchesStraightOffTheRecipeTable) {
const PotionCatalog& catalog = potionCatalog();
CHECK_EQ(catalog.invertedKind(PotionKind::Water), PotionKind::Weakness);
CHECK_EQ(catalog.invertedKind(PotionKind::NightVision), PotionKind::Invisibility);
CHECK_EQ(catalog.invertedKind(PotionKind::Healing), PotionKind::Harming);
CHECK_EQ(catalog.invertedKind(PotionKind::Poison), PotionKind::Harming);
CHECK_EQ(catalog.invertedKind(PotionKind::Swiftness), PotionKind::Slowness);
CHECK_EQ(catalog.invertedKind(PotionKind::Leaping), PotionKind::Slowness);
CHECK_EQ(catalog.invertedKind(PotionKind::Strength), PotionKind::Weakness);
const PotionKind untouched[] = {PotionKind::Awkward, PotionKind::Regeneration,
PotionKind::FireResistance, PotionKind::WaterBreathing,
PotionKind::Harming, PotionKind::Slowness};
for (PotionKind kind : untouched) CHECK_EQ(catalog.invertedKind(kind), PotionKind::Count);
// Every inversion the catalog reports has to survive a real brew.
for (int index = 0; index < static_cast<int>(PotionKind::Count); ++index) {
PotionKind kind = static_cast<PotionKind>(index);
PotionKind inverted = catalog.invertedKind(kind);
bool changed = false;
PotionItem brewed = brewResult(bottleOf(kind), Ingredient::FermentedSpiderEye, changed);
CHECK_EQ(changed, inverted != PotionKind::Count);
if (changed) CHECK_EQ(brewed.kind, inverted);
}
}
VF_TEST(everyPotionInTheCatalogHasABrewingPathThatARealStandCanReplay) {
const PotionCatalog& catalog = potionCatalog();
const BrewGraph& graph = brewGraph();
std::vector<BrewPath> paths = graph.drinkablePaths();
CHECK_EQ(paths.size(), catalog.size());
for (std::size_t i = 0; i < paths.size(); ++i) {
const BrewPath& path = paths[i];
const PotionEntry& row = catalog.entries()[i];
CHECK(path.reachable);
CHECK_EQ(path.target.kind, row.kind);
CHECK_EQ(path.totalTicks, path.stepCount() * brewDurationTicks);
CHECK_EQ(path.stepCount(), graph.depthOf(path.target));
PotionItem brewed = replayOnAStand(path.steps);
CHECK_EQ(brewed.kind, row.kind);
CHECK_EQ(brewed.form, PotionItem::Form::Drinkable);
CHECK(!brewed.upgraded);
CHECK(!brewed.extended);
}
}
VF_TEST(everyReachableVariantReplaysExactlyThroughItsOwnShortestPath) {
const BrewGraph& graph = brewGraph();
int replayed = 0;
for (int index = 0; index < BrewGraph::stateCount(); ++index) {
PotionItem state = BrewGraph::stateAt(index);
BrewPath path = graph.shortestPath(state);
if (!path.reachable) continue;
CHECK(replayThroughSteps(path.steps) == state);
CHECK_EQ(path.stepCount(), graph.depthOf(state));
++replayed;
}
CHECK_EQ(replayed, graph.reachableStateCount());
}
VF_TEST(theGraphReachesExactlyTheVariantsTheCatalogDeclaresAndNothingElse) {
const PotionCatalog& catalog = potionCatalog();
const BrewGraph& graph = brewGraph();
int expected = 0;
for (const PotionEntry& row : catalog.entries()) expected += row.variantCount() * 3;
CHECK_EQ(graph.reachableStateCount(), expected);
CHECK(graph.reachableStateCount() < BrewGraph::stateCount());
int fromDepths = 0;
for (int depth = 0; depth <= BrewGraph::stateCount(); ++depth) {
fromDepths += static_cast<int>(graph.statesAtDepth(depth).size());
}
CHECK_EQ(fromDepths, graph.reachableStateCount());
CHECK_EQ(graph.statesAtDepth(0).size(), std::size_t{1});
CHECK(graph.statesAtDepth(0)[0] == PotionItem{});
}
VF_TEST(modifiersAPotionRefusesLeaveThatVariantWithNoPathAtAll) {
const BrewGraph& graph = brewGraph();
const PotionCatalog& catalog = potionCatalog();
const PotionKind noGlowstone[] = {PotionKind::Weakness, PotionKind::FireResistance,
PotionKind::WaterBreathing, PotionKind::NightVision,
PotionKind::Invisibility};
for (PotionKind kind : noGlowstone) {
CHECK(!catalog.entry(kind).acceptsGlowstone());
CHECK(!graph.reachable(bottleOf(kind, true, false)));
CHECK_EQ(graph.depthOf(bottleOf(kind, true, false)), -1);
CHECK(graph.reachable(bottleOf(kind, false, true)));
}