From 07ca6d9459a0a6ebb56e172c6cd414389b0ff754 Mon Sep 17 00:00:00 2001 From: codereader Date: Sun, 23 May 2021 07:17:31 +0200 Subject: [PATCH] #5622: Patch and Entity fingerprint unit tests. The order of spawnargs on entity nodes do not affect the fingerprint. --- radiantcore/entity/EntityNode.cpp | 16 +++- test/MapMerging.cpp | 94 +++++++++++++++++++++ test/resources/tdm/maps/fingerprinting.mapx | 3 +- 3 files changed, 108 insertions(+), 5 deletions(-) diff --git a/radiantcore/entity/EntityNode.cpp b/radiantcore/entity/EntityNode.cpp index 76988cd266..17fc6f02c7 100644 --- a/radiantcore/entity/EntityNode.cpp +++ b/radiantcore/entity/EntityNode.cpp @@ -206,15 +206,23 @@ const Vector3& EntityNode::getDirection() const std::size_t EntityNode::getFingerprint() { - std::size_t hash = 0; + std::map sortedKeyValues; - // Entities are just a collection of key/value pairs, use them lower case, ignore inherited keys + // Entities are just a collection of key/value pairs, + // use them in lower case form, ignore inherited keys, sort before hashing _spawnArgs.forEachKeyValue([&](const std::string& key, const std::string& value) { - math::combineHash(hash, std::hash()(string::to_lower_copy(key))); - math::combineHash(hash, std::hash()(string::to_lower_copy(value))); + sortedKeyValues.emplace(string::to_lower_copy(key), string::to_lower_copy(value)); }, false); + std::size_t hash = 0; + + for (const auto& pair : sortedKeyValues) + { + math::combineHash(hash, std::hash()(pair.first)); + math::combineHash(hash, std::hash()(pair.second)); + } + return hash; } diff --git a/test/MapMerging.cpp b/test/MapMerging.cpp index 4dcb6d18b0..894e01e266 100644 --- a/test/MapMerging.cpp +++ b/test/MapMerging.cpp @@ -3,6 +3,7 @@ #include "icommandsystem.h" #include "itransformable.h" #include "ibrush.h" +#include "ipatch.h" #include "icomparablenode.h" #include "algorithm/Scene.h" #include "registry/registry.h" @@ -68,4 +69,97 @@ TEST_F(MapMergeTest, BrushFingerprint) lastFingerprint = comparable->getFingerprint(); } +TEST_F(MapMergeTest, PatchFingerprint) +{ + GlobalCommandSystem().executeCommand("OpenMap", cmd::Argument("maps/fingerprinting.mapx")); + + auto originalMaterial = "textures/numbers/1"; + auto patch = std::dynamic_pointer_cast(algorithm::findFirstPatchWithMaterial( + GlobalMapModule().findOrInsertWorldspawn(), originalMaterial)); + + auto comparable = std::dynamic_pointer_cast(patch); + EXPECT_TRUE(comparable) << "PatchNode is not implementing IComparableNode"; + + auto originalFingerprint = comparable->getFingerprint(); + + EXPECT_NE(originalFingerprint, 0); // shouldn't be empty + + // Calling it twice shouldn't change the value + EXPECT_EQ(comparable->getFingerprint(), originalFingerprint); + + // Change the material of this patch, the fingerprint should change now + patch->getPatch().setShader("textures/somethingelse"); + EXPECT_NE(comparable->getFingerprint(), originalFingerprint); + + // Change it back + patch->getPatch().setShader(originalMaterial); + EXPECT_EQ(comparable->getFingerprint(), originalFingerprint); + + // Change a single control vertex + auto& control = patch->getPatch().ctrlAt(0, 0); + + auto lastFingerprint = comparable->getFingerprint(); + + // Change a 3D coordinate + control.vertex.x() += 0.1; + EXPECT_NE(comparable->getFingerprint(), lastFingerprint); + lastFingerprint = comparable->getFingerprint(); + + // Change a 2D component + control.texcoord.x() += 0.1; + EXPECT_NE(comparable->getFingerprint(), lastFingerprint); + lastFingerprint = comparable->getFingerprint(); + + // Append vertices + patch->getPatch().appendPoints(true, false); + EXPECT_NE(comparable->getFingerprint(), lastFingerprint); +} + +TEST_F(MapMergeTest, EntityFingerprint) +{ + GlobalCommandSystem().executeCommand("OpenMap", cmd::Argument("maps/fingerprinting.mapx")); + + auto entityNode = algorithm::getEntityByName(GlobalMapModule().getRoot(), "func_static_1"); + + auto comparable = std::dynamic_pointer_cast(entityNode); + EXPECT_TRUE(comparable) << "EntityNode is not implementing IComparableNode"; + + auto entity = std::dynamic_pointer_cast(entityNode); + + auto originalFingerprint = comparable->getFingerprint(); + + EXPECT_NE(originalFingerprint, 0); // shouldn't be empty + + // Calling it twice shouldn't change the value + EXPECT_EQ(comparable->getFingerprint(), originalFingerprint); + + // Change a spawnarg slightly + auto oldOrigin = entity->getEntity().getKeyValue("origin"); + + entity->getEntity().setKeyValue("origin", "96 -32 53"); + EXPECT_NE(comparable->getFingerprint(), originalFingerprint); + + // Change it back + entity->getEntity().setKeyValue("origin", oldOrigin); + EXPECT_EQ(comparable->getFingerprint(), originalFingerprint); + + // Add a new spawnarg + entity->getEntity().setKeyValue("origin22", "whatever"); + EXPECT_NE(comparable->getFingerprint(), originalFingerprint); + + // Change it back + entity->getEntity().setKeyValue("origin22", ""); + EXPECT_EQ(comparable->getFingerprint(), originalFingerprint); + + // Remove a spawnarg, this alters the order of keyvalues + auto originalValue = entity->getEntity().getKeyValue("dummyspawnarg"); + + entity->getEntity().setKeyValue("dummyspawnarg", ""); + EXPECT_NE(comparable->getFingerprint(), originalFingerprint); + + // Change it back, even though the order is different, the fingerprint should be the same + entity->getEntity().setKeyValue("dummyspawnarg", originalValue); + EXPECT_EQ(comparable->getFingerprint(), originalFingerprint); +} + } diff --git a/test/resources/tdm/maps/fingerprinting.mapx b/test/resources/tdm/maps/fingerprinting.mapx index 5e66e00b3b..67ccdc6c60 100644 --- a/test/resources/tdm/maps/fingerprinting.mapx +++ b/test/resources/tdm/maps/fingerprinting.mapx @@ -8,7 +8,7 @@ - + @@ -306,6 +306,7 @@ +