test: extend coverage of testHeaderCoexistence
This commit is contained in:
1 parent
dc91a40f5d
commit
cbb38b2c76
1 file changed
+115
@@ -152,3 +152,118 @@ VF_TEST(everyPublicHeaderIsListedInTheCoexistenceGuard) {
|
||||
}
|
||||
CHECK(onDisk.size() > 60u);
|
||||
|
||||
std::set<std::string> included;
|
||||
std::ifstream source(root / "tests" / "testHeaderCoexistence.cpp");
|
||||
CHECK(source.good());
|
||||
const std::string prefix = "#include \"vf/";
|
||||
std::string line;
|
||||
while (std::getline(source, line)) {
|
||||
if (line.rfind(prefix, 0) != 0) continue;
|
||||
std::size_t end = line.find('"', prefix.size());
|
||||
if (end == std::string::npos) continue;
|
||||
included.insert(line.substr(prefix.size(), end - prefix.size()));
|
||||
}
|
||||
|
||||
for (const std::string& header : onDisk) {
|
||||
// A header on disk but missing from the include list above.
|
||||
CHECK(included.count(header) == 1u);
|
||||
if (included.count(header) != 1u) {
|
||||
std::printf(" unguarded header: core/include/vf/%s\n", header.c_str());
|
||||
}
|
||||
}
|
||||
for (const std::string& header : included) {
|
||||
// An include line left behind after a header was deleted or renamed.
|
||||
CHECK(onDisk.count(header) == 1u);
|
||||
}
|
||||
CHECK_EQ(included.size(), onDisk.size());
|
||||
}
|
||||
|
||||
// The two names that actually collided, now proven to coexist: an enum in
|
||||
// Survival.h and a struct in Damage.h, both reachable from one scope.
|
||||
VF_TEST(survivalAndDamageHeadersDeclareDistinctSourceTypes) {
|
||||
DamageSource legacy = DamageSource::Fall;
|
||||
CHECK(legacy == DamageSource::Fall);
|
||||
CHECK(legacy != DamageSource::Lava);
|
||||
|
||||
HurtSource blow;
|
||||
blow.type = DamageType::Fall;
|
||||
blow.byPlayer = true;
|
||||
blow.attackerId = 7;
|
||||
CHECK(blow.type == DamageType::Fall);
|
||||
CHECK(blow.byPlayer);
|
||||
CHECK_EQ(blow.attackerId, 7);
|
||||
CHECK_EQ(blow.ownerId, -1);
|
||||
|
||||
// Distinct types: the sizes below are unrelated, and the point is only
|
||||
// that both names resolve in the same translation unit.
|
||||
CHECK(sizeof(DamageSource) >= 1u);
|
||||
CHECK(sizeof(HurtSource) > sizeof(DamageSource));
|
||||
}
|
||||
|
||||
// Every block in the palette must map to a tile inside the atlas, on every
|
||||
// face. A block added without a faceTile case silently falls through to the
|
||||
// stone tile, which is the kind of thing nobody notices until the atlas is
|
||||
// resized. Both facts are checked here because faceTile and tileCount live in
|
||||
// the one header every module shares.
|
||||
VF_TEST(everyPaletteBlockMapsToATileInsideTheAtlas) {
|
||||
const Face faces[6] = {Face::PosX, Face::NegX, Face::PosY,
|
||||
Face::NegY, Face::PosZ, Face::NegZ};
|
||||
int highestTile = -1;
|
||||
for (int i = 1; i < static_cast<int>(Block::Count); ++i) {
|
||||
Block block = static_cast<Block>(i);
|
||||
for (Face face : faces) {
|
||||
int tile = faceTile(block, face);
|
||||
CHECK(tile >= 0);
|
||||
CHECK(tile < tileCount);
|
||||
if (tile > highestTile) highestTile = tile;
|
||||
}
|
||||
}
|
||||
// The atlas must not be padded with layers nothing references.
|
||||
CHECK_EQ(highestTile, tileCount - 1);
|
||||
}
|
||||
|
||||
// Opacity and solidity are independent flags, but light emission is only
|
||||
// meaningful within the 0..15 range the light engine packs into four bits.
|
||||
VF_TEST(blockLightEmissionStaysInsideTheFourBitLightRange) {
|
||||
for (int i = 0; i < static_cast<int>(Block::Count); ++i) {
|
||||
Block block = static_cast<Block>(i);
|
||||
int emission = blockLightEmission(block);
|
||||
CHECK(emission >= 0);
|
||||
CHECK(emission <= 15);
|
||||
if (isAir(block)) CHECK_EQ(emission, 0);
|
||||
}
|
||||
CHECK_EQ(blockLightEmission(Block::Glowstone), 15);
|
||||
CHECK_EQ(blockLightEmission(Block::EndPortal), 15);
|
||||
CHECK_EQ(blockLightEmission(Block::NetherPortal), 11);
|
||||
CHECK_EQ(blockLightEmission(Block::Stone), 0);
|
||||
}
|
||||
|
||||
// A transparent block must never be opaque, or the mesher would both skip the
|
||||
// neighbouring face and draw the block in the transparent pass.
|
||||
VF_TEST(transparentBlocksAreNeverAlsoOpaque) {
|
||||
for (int i = 0; i < static_cast<int>(Block::Count); ++i) {
|
||||
Block block = static_cast<Block>(i);
|
||||
if (isTransparent(block)) CHECK(!isOpaque(block));
|
||||
if (isOpaque(block)) CHECK(occludesLight(block));
|
||||
}
|
||||
CHECK(isTransparent(Block::Glass));
|
||||
CHECK(!isOpaque(Block::Glass));
|
||||
CHECK(isOpaque(Block::Stone));
|
||||
CHECK(occludesLight(Block::Stone));
|
||||
}
|
||||
|
||||
// faceVisible is the single rule the mesher trusts. It has to stay coherent
|
||||
// with isAir and isOpaque for the whole palette, not just for the handful of
|
||||
// blocks that existed when it was written.
|
||||
VF_TEST(faceVisibilityAgreesWithAirAndOpacityAcrossThePalette) {
|
||||
for (int i = 0; i < static_cast<int>(Block::Count); ++i) {
|
||||
Block self = static_cast<Block>(i);
|
||||
CHECK(!faceVisible(self, Block::Stone));
|
||||
if (isAir(self)) {
|
||||
CHECK(!faceVisible(self, Block::Air));
|
||||
continue;
|
||||
}
|
||||
CHECK(faceVisible(self, Block::Air));
|
||||
CHECK(!faceVisible(self, self));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user