test: cover testScoreboard

This commit is contained in:
dualfroz committed 2026-09-12 23:01:51 +02:00
1 parent 5f13d20936
commit 5db59e566e
1 file changed
+216
+216
View File
@@ -155,3 +155,219 @@ VF_TEST(manualObjectivesOnlyMoveWhenSomethingSetsThem) {
CHECK(!board.tracked("bounty", "newcomer")); CHECK(!board.tracked("bounty", "newcomer"));
CHECK(board.statsFor("newcomer") == nullptr); CHECK(board.statsFor("newcomer") == nullptr);
board.setScore("bounty", "a", 2147483647);
CHECK(board.addScore("bounty", "a", 10));
CHECK_EQ(board.score("bounty", "a"), 2147483647);
}
VF_TEST(entriesComeBackSortedAndFormattedForTheirObjective) {
Scoreboard board;
ScoreObjective bounty = manualObjective("bounty");
board.addObjective(bounty);
board.setScore("bounty", "cara", 5);
board.setScore("bounty", "bea", 9);
board.setScore("bounty", "abel", 5);
std::vector<ScoreEntry> ranked = board.entries("bounty");
CHECK_EQ(ranked.size(), static_cast<std::size_t>(3));
CHECK_EQ(ranked[0].player, std::string("bea"));
CHECK_EQ(ranked[1].player, std::string("abel"));
CHECK_EQ(ranked[2].player, std::string("cara"));
CHECK_EQ(ranked[0].formatted, std::string("9"));
ScoreObjective ascending = manualObjective("race");
ascending.sort = ScoreObjective::Sort::Ascending;
ascending.format = ScoreObjective::Format::Duration;
board.addObjective(ascending);
board.setScore("race", "bea", 20 * 65);
board.setScore("race", "abel", 20 * 3661);
board.setScore("race", "cara", 20 * 9);
std::vector<ScoreEntry> fastest = board.entries("race");
CHECK_EQ(fastest[0].player, std::string("cara"));
CHECK_EQ(fastest[0].formatted, std::string("9s"));
CHECK_EQ(fastest[1].formatted, std::string("1m 05s"));
CHECK_EQ(fastest[2].formatted, std::string("1h 01m 01s"));
ScoreObjective walked = statObjective("walked", StatKind::DistanceWalked);
walked.format = ScoreObjective::Format::Distance;
board.addObjective(walked);
board.stats("abel").add(StatKind::DistanceWalked, 123456);
board.stats("bea").add(StatKind::DistanceWalked, 7);
std::vector<ScoreEntry> distances = board.entries("walked");
CHECK_EQ(distances.size(), static_cast<std::size_t>(2));
CHECK_EQ(distances[0].player, std::string("abel"));
CHECK_EQ(distances[0].formatted, std::string("1234.56 m"));
CHECK_EQ(distances[1].formatted, std::string("0.07 m"));
CHECK(board.entries("missing").empty());
CHECK_EQ(walked.formatScore(-250), std::string("-2.50 m"));
CHECK_EQ(ascending.formatScore(0), std::string("0s"));
}
VF_TEST(displaySlotsShowOneObjectiveEachAndCanBeCleared) {
Scoreboard board;
board.addObjective(manualObjective("bounty"));
board.addObjective(statObjective("deaths", StatKind::Deaths));
board.setScore("bounty", "abel", 3);
board.setScore("bounty", "bea", 8);
board.setScore("bounty", "cara", 1);
CHECK(board.displayedObjective(DisplaySlot::Sidebar).empty());
board.setDisplay(DisplaySlot::Sidebar, "bounty");
board.setDisplay(DisplaySlot::PlayerList, "deaths");
board.setDisplay(DisplaySlot::BelowName, "nothing at all");
CHECK_EQ(board.displayedObjective(DisplaySlot::Sidebar), std::string("bounty"));
CHECK_EQ(board.displayedObjective(DisplaySlot::PlayerList), std::string("deaths"));
CHECK(board.displayedObjective(DisplaySlot::BelowName).empty());
std::vector<ScoreEntry> sidebar = board.displayLines(DisplaySlot::Sidebar, 2);
CHECK_EQ(sidebar.size(), static_cast<std::size_t>(2));
CHECK_EQ(sidebar[0].player, std::string("bea"));
CHECK_EQ(sidebar[1].player, std::string("abel"));
CHECK_EQ(board.displayLines(DisplaySlot::Sidebar, 15).size(), static_cast<std::size_t>(3));
CHECK(board.displayLines(DisplaySlot::BelowName, 15).empty());
board.clearDisplay(DisplaySlot::Sidebar);
CHECK(board.displayedObjective(DisplaySlot::Sidebar).empty());
CHECK(board.displayLines(DisplaySlot::Sidebar, 15).empty());
board.setDisplay(DisplaySlot::Sidebar, "bounty");
CHECK(board.removeObjective("bounty"));
CHECK(!board.removeObjective("bounty"));
CHECK(board.displayedObjective(DisplaySlot::Sidebar).empty());
CHECK_EQ(board.score("bounty", "bea"), 0);
CHECK_EQ(std::string(displaySlotName(DisplaySlot::Sidebar)), std::string("sidebar"));
CHECK_EQ(std::string(displaySlotName(DisplaySlot::BelowName)), std::string("belowName"));
}
VF_TEST(teamsDecideFriendlyFireNameVisibilityAndDisplayName) {
Scoreboard board;
ScoreTeam red = makeTeam("red", ScoreTeam::Color::Red);
red.friendlyFire = false;
red.prefix = "[R] ";
red.suffix = " *";
red.members.push_back("abel");
red.members.push_back("bea");
CHECK(board.addTeam(red));
CHECK(!board.addTeam(red));
CHECK(board.addTeam(makeTeam("blue", ScoreTeam::Color::Blue)));
CHECK(board.joinTeam("blue", "cara"));
CHECK(!board.joinTeam("green", "cara"));
CHECK_EQ(board.teamNames().size(), static_cast<std::size_t>(2));
CHECK(board.teamOf("abel") != nullptr);
CHECK_EQ(board.teamOf("abel")->name, std::string("red"));
CHECK_EQ(board.team("red")->members.size(), static_cast<std::size_t>(2));
CHECK(board.teamOf("nobody") == nullptr);
CHECK(!board.friendlyFireAllowed("abel", "bea"));
CHECK(board.friendlyFireAllowed("abel", "cara"));
CHECK(board.friendlyFireAllowed("cara", "abel"));
CHECK(board.friendlyFireAllowed("nobody", "abel"));
CHECK_EQ(board.displayNameOf("abel"), std::string("[R] abel *"));
CHECK_EQ(board.displayNameOf("cara"), std::string("cara"));
// Moving a player leaves the old roster behind.
CHECK(board.joinTeam("blue", "bea"));
CHECK_EQ(board.team("red")->members.size(), static_cast<std::size_t>(1));
CHECK_EQ(board.teamOf("bea")->name, std::string("blue"));
CHECK(board.friendlyFireAllowed("abel", "bea"));
CHECK(board.leaveTeam("bea"));
CHECK(!board.leaveTeam("bea"));
CHECK(board.teamOf("bea") == nullptr);
CHECK_EQ(board.team("blue")->members.size(), static_cast<std::size_t>(1));
CHECK(board.removeTeam("blue"));
CHECK(!board.removeTeam("blue"));
CHECK(board.teamOf("cara") == nullptr);
CHECK_EQ(std::string(scoreTeamColorName(ScoreTeam::Color::Red)), std::string("red"));
CHECK_EQ(std::string(scoreTeamColorName(ScoreTeam::Color::Aqua)), std::string("aqua"));
}
VF_TEST(nameVisibilityFollowsTheTeamRuleForTheWatcher) {
Scoreboard board;
ScoreTeam hidden = makeTeam("hidden", ScoreTeam::Color::Gray);
hidden.nameVisibility = ScoreTeam::Visibility::Never;
hidden.members.push_back("ghost");
board.addTeam(hidden);
ScoreTeam clan = makeTeam("clan", ScoreTeam::Color::Green);
clan.nameVisibility = ScoreTeam::Visibility::HideForOtherTeams;
clan.members.push_back("abel");
clan.members.push_back("bea");
board.addTeam(clan);
ScoreTeam masked = makeTeam("masked", ScoreTeam::Color::Purple);
masked.nameVisibility = ScoreTeam::Visibility::HideForOwnTeam;
masked.members.push_back("cara");
masked.members.push_back("dana");
board.addTeam(masked);
CHECK(!board.nameVisibleTo("abel", "ghost"));
CHECK(!board.nameVisibleTo("ghost", "ghost"));
CHECK(board.nameVisibleTo("abel", "bea"));
CHECK(!board.nameVisibleTo("cara", "bea"));
CHECK(!board.nameVisibleTo("dana", "cara"));
CHECK(board.nameVisibleTo("abel", "cara"));
CHECK(board.nameVisibleTo("abel", "loner"));
CHECK(board.nameVisibleTo("loner", "loner"));
}
VF_TEST(everyPlayerTouchedByTheBoardIsListedOnceInOrder) {
Scoreboard board;
board.addObjective(manualObjective("bounty"));
board.addObjective(statObjective("mined", StatKind::BlocksBroken));
board.addTeam(makeTeam("red", ScoreTeam::Color::Red));
board.setScore("bounty", "cara", 1);
board.stats("abel").addBlockBroken(Block::Stone);
board.joinTeam("red", "bea");
board.stats("cara").addBlockBroken(Block::Dirt, 4);
std::vector<std::string> everyone = board.players();
CHECK_EQ(everyone.size(), static_cast<std::size_t>(3));
CHECK_EQ(everyone[0], std::string("abel"));
CHECK_EQ(everyone[1], std::string("bea"));
CHECK_EQ(everyone[2], std::string("cara"));
// The automatic column only lists players who actually have a sheet.
std::vector<ScoreEntry> mined = board.entries("mined");
CHECK_EQ(mined.size(), static_cast<std::size_t>(2));
CHECK_EQ(mined[0].player, std::string("cara"));
CHECK_EQ(mined[0].score, 4);
CHECK_EQ(mined[1].player, std::string("abel"));
}
VF_TEST(aStatisticObjectiveOnOneSubjectIgnoresTheOthers) {
Scoreboard board;
board.addObjective(statObjective("zombieHunter", StatKind::MobsKilled,
static_cast<int>(Species::Zombie)));
board.addObjective(statObjective("anyKill", StatKind::MobsKilled));
board.addObjective(statObjective("pickaxeUse", StatKind::ItemsUsed,
static_cast<int>(ItemId::IronPickaxe)));
StatSheet& sheet = board.stats("foksiu");
sheet.addMobKilled(Species::Zombie, 3);
sheet.addMobKilled(Species::Sheep, 9);
sheet.addItemUsed(ItemId::IronPickaxe, 2);
sheet.addItemUsed(ItemId::WoodAxe, 40);
CHECK_EQ(board.score("zombieHunter", "foksiu"), 3);
CHECK_EQ(board.score("anyKill", "foksiu"), 12);
CHECK_EQ(board.score("pickaxeUse", "foksiu"), 2);
sheet.addMobKilled(Species::Zombie);
CHECK_EQ(board.score("zombieHunter", "foksiu"), 4);
CHECK_EQ(board.score("anyKill", "foksiu"), 13);
// A stat far past the score range is clamped, not wrapped.
sheet.add(StatKind::PlayTicks, 4000000000ll);
board.addObjective(statObjective("played", StatKind::PlayTicks));
CHECK_EQ(board.score("played", "foksiu"), 2147483647);
}