test: extend coverage of testAdvancement

This commit is contained in:
dualfroz committed 2026-09-10 17:59:00 +02:00
1 parent 1530c4701e
commit 30b64ccfaa
1 file changed
+159
+159
View File
@@ -313,3 +313,162 @@ VF_TEST(aParentLoopIsReportedInsteadOfWalkedForever) {
CHECK_EQ(tree.size(), static_cast<std::size_t>(0)); CHECK_EQ(tree.size(), static_cast<std::size_t>(0));
} }
VF_TEST(criteriaOnlyAcceptTheEventsTheyDescribe) {
AdvancementCriterion anyKill;
anyKill.trigger = Trigger::MobKilled;
CHECK(anyKill.accepts(Event::mobKilled(Species::Pig)));
CHECK(anyKill.accepts(Event::mobKilled(Species::Creeper)));
CHECK(!anyKill.accepts(Event::itemObtained(ItemId::Stick)));
AdvancementCriterion creepersOnly = anyKill;
creepersOnly.matchMask = AdvancementCriterion::MatchSpecies;
creepersOnly.species = Species::Creeper;
CHECK(creepersOnly.accepts(Event::mobKilled(Species::Creeper)));
CHECK(!creepersOnly.accepts(Event::mobKilled(Species::Pig)));
AdvancementCriterion minedWithIron;
minedWithIron.trigger = Trigger::BlockBroken;
minedWithIron.matchMask = AdvancementCriterion::MatchBlock | AdvancementCriterion::MatchTool;
minedWithIron.block = Block::DiamondOre;
minedWithIron.tool = ItemId::IronPickaxe;
CHECK(minedWithIron.accepts(Event::blockBroken(Block::DiamondOre, ItemId::IronPickaxe)));
CHECK(!minedWithIron.accepts(Event::blockBroken(Block::DiamondOre, ItemId::StonePickaxe)));
CHECK(!minedWithIron.accepts(Event::blockBroken(Block::CoalOre, ItemId::IronPickaxe)));
AdvancementCriterion brewed;
brewed.trigger = Trigger::PotionBrewed;
brewed.matchMask = AdvancementCriterion::MatchPotion;
brewed.potion = PotionKind::Swiftness;
CHECK(brewed.accepts(Event::potionBrewed(PotionKind::Swiftness)));
CHECK(!brewed.accepts(Event::potionBrewed(PotionKind::Healing)));
CHECK(!brewed.accepts(Event::dimensionEntered(DimensionId::End)));
for (int i = 0; i < static_cast<int>(Trigger::Count); ++i) {
Trigger trigger = static_cast<Trigger>(i);
Trigger reparsed = Trigger::Count;
CHECK(parseAdvancementTriggerName(advancementTriggerName(trigger), reparsed));
CHECK(reparsed == trigger);
}
Trigger unknown = Trigger::Count;
CHECK(!parseAdvancementTriggerName("telekinesis", unknown));
}
VF_TEST(oneUnreadableAdvancementDoesNotCostTheRest) {
DataPack pack = contentPack();
pack.addFile("data/voxelforge/advancements/broken.json",
R"json({"criteria": {"x": {"trigger": "telekinesis"}}})json");
pack.addFile("data/voxelforge/advancements/empty.json", R"json({"criteria": {}})json");
pack.addFile("data/voxelforge/advancements/ghost.json",
R"json({"criteria": {"x": {"trigger": "itemObtained",
"item": "voxelforge:cheese"}}})json");
pack.addFile("data/voxelforge/advancements/mismatch.json",
R"json({"criteria": {"x": {"trigger": "itemObtained"}},
"requirements": [["y"]]})json");
std::vector<DataPackError> errors;
AdvancementTree tree = loadTree(pack, errors);
CHECK_EQ(errors.size(), static_cast<std::size_t>(4));
CHECK_EQ(tree.size(), static_cast<std::size_t>(4));
CHECK(tree.find(id("voxelforge:root")) != nullptr);
CHECK(tree.find(id("voxelforge:broken")) == nullptr);
for (const DataPackError& failure : errors) {
CHECK(failure.file.find("data/voxelforge/advancements/") == 0u);
CHECK(!failure.reason.empty());
}
}
VF_TEST(advancementDocumentsAreCheckedFieldByField) {
Advancement parsed;
std::string problem;
const char* const rejected[] = {
R"json([])json",
R"json({})json",
R"json({"criteria": {}})json",
R"json({"criteria": {"x": {}}})json",
R"json({"criteria": {"x": {"trigger": "itemObtained", "count": 0}}})json",
R"json({"criteria": {"x": {"trigger": "itemObtained", "item": "NOT AN ID"}}})json",
R"json({"criteria": {"x": {"trigger": "mobKilled", "species": "dragon"}}})json",
R"json({"criteria": {"x": {"trigger": "dimensionEntered", "dimension": "attic"}}})json",
R"json({"criteria": {"x": {"trigger": "potionBrewed", "potion": "coffee"}}})json",
R"json({"criteria": {"x": {"trigger": "villagerTraded", "profession": "banker"}}})json",
R"json({"criteria": {"x": {"trigger": "blockBroken", "block": "voxelforge:cheese"}}})json",
R"json({"parent": "BAD ID", "criteria": {"x": {"trigger": "itemObtained"}}})json",
R"json({"criteria": {"x": {"trigger": "itemObtained"}}, "requirements": [[]]})json",
R"json({"criteria": {"x": {"trigger": "itemObtained"}},
"rewards": {"experience": -1}})json",
R"json({"criteria": {"x": {"trigger": "itemObtained"}},
"rewards": {"recipes": ["NOT AN ID"]}})json",
R"json({"criteria": {"x": {"trigger": "itemObtained"}}, "display": "a string"})json",
};
for (const char* text : rejected) {
JsonParseResult document = parseJson(text);
CHECK(document.ok);
CHECK(!parseAdvancementJson(document.root, "voxelforge", parsed, problem));
CHECK(!problem.empty());
}
JsonParseResult minimal =
parseJson(R"json({"criteria": {"x": {"trigger": "itemObtained"}}})json");
CHECK(parseAdvancementJson(minimal.root, "voxelforge", parsed, problem));
CHECK(parsed.isRoot());
CHECK(!parsed.hidden);
CHECK_EQ(parsed.criteria.size(), static_cast<std::size_t>(1));
CHECK_EQ(parsed.criteria[0].matchMask, 0u);
CHECK_EQ(parsed.criteria[0].requiredCount, 1);
CHECK(parsed.reward.empty());
// A bare namespace default applies to every id inside the document.
JsonParseResult defaulted = parseJson(
R"json({"parent": "root", "criteria": {"x": {"trigger": "itemObtained",
"item": "stick"}}, "rewards": {"recipes": ["torch"]}})json");
CHECK(parseAdvancementJson(defaulted.root, "extras", parsed, problem));
CHECK(parsed.parent == ResourceId("extras", "root"));
CHECK(parsed.criteria[0].item == ItemId::Stick);
CHECK(parsed.reward.recipes[0] == ResourceId("extras", "torch"));
}
VF_TEST(oneMiningRunFeedsBothTheAdvancementTreeAndTheStatSheet) {
DataPackMeta meta;
meta.name = "mining";
DataPack pack(meta);
pack.addFile("data/voxelforge/advancements/quarry.json",
R"json({"display": {"title": "Quarry"},
"criteria": {"stone": {"trigger": "blockBroken",
"block": "voxelforge:stone", "count": 5}},
"rewards": {"experience": 15}})json");
std::vector<DataPackError> errors;
AdvancementTree tree = loadTree(pack, errors);
CHECK(errors.empty());
AdvancementProgress progress(tree);
Scoreboard board;
ScoreObjective mined;
mined.name = "mined";
mined.criterion = ScoreObjective::Criterion::Statistic;
mined.statKind = StatKind::BlocksBroken;
mined.statSubject = static_cast<int>(Block::Stone);
CHECK(board.addObjective(mined));
const AdvancementId quarry = id("voxelforge:quarry");
for (int strike = 1; strike <= 4; ++strike) {
progress.notify(Event::blockBroken(Block::Stone, ItemId::IronPickaxe), strike * 10);
board.stats("foksiu").addBlockBroken(Block::Stone);
CHECK_EQ(board.score("mined", "foksiu"), strike);
CHECK(!progress.granted(quarry));
}
std::vector<AdvancementId> granted =
progress.notify(Event::blockBroken(Block::Stone, ItemId::IronPickaxe), 50);
board.stats("foksiu").addBlockBroken(Block::Stone);
CHECK_EQ(granted.size(), static_cast<std::size_t>(1));
CHECK(progress.granted(quarry));
CHECK_EQ(progress.totalExperience(), 15);
CHECK_EQ(board.score("mined", "foksiu"), 5);
CHECK_EQ(board.stats("foksiu").blocksBroken(Block::Stone), 5);
// Breaking something else moves the sheet but not this advancement.
board.stats("foksiu").addBlockBroken(Block::Dirt, 3);
CHECK_EQ(board.score("mined", "foksiu"), 5);
CHECK_EQ(board.stats("foksiu").total(StatKind::BlocksBroken), 8);
}