Skip to content

Commit

Permalink
#5622: Patch and Entity fingerprint unit tests. The order of spawnarg…
Browse files Browse the repository at this point in the history
…s on entity nodes do not affect the fingerprint.
  • Loading branch information
codereader committed May 23, 2021
1 parent be56c0e commit 07ca6d9
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 5 deletions.
16 changes: 12 additions & 4 deletions radiantcore/entity/EntityNode.cpp
Expand Up @@ -206,15 +206,23 @@ const Vector3& EntityNode::getDirection() const

std::size_t EntityNode::getFingerprint()
{
std::size_t hash = 0;
std::map<std::string, std::string> 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<std::string>()(string::to_lower_copy(key)));
math::combineHash(hash, std::hash<std::string>()(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<std::string>()(pair.first));
math::combineHash(hash, std::hash<std::string>()(pair.second));
}

return hash;
}

Expand Down
94 changes: 94 additions & 0 deletions test/MapMerging.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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<IPatchNode>(algorithm::findFirstPatchWithMaterial(
GlobalMapModule().findOrInsertWorldspawn(), originalMaterial));

auto comparable = std::dynamic_pointer_cast<scene::IComparableNode>(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<scene::IComparableNode>(entityNode);
EXPECT_TRUE(comparable) << "EntityNode is not implementing IComparableNode";

auto entity = std::dynamic_pointer_cast<IEntityNode>(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);
}

}
3 changes: 2 additions & 1 deletion test/resources/tdm/maps/fingerprinting.mapx
Expand Up @@ -8,7 +8,7 @@
<selectionGroups/>
<selectionSets/>
<properties>
<property key="EditTimeInSeconds" value="257"/>
<property key="EditTimeInSeconds" value="287"/>
<property key="LastCameraAngle" value="-36.9 45.6 0"/>
<property key="LastCameraPosition" value="-81.4144 -139.899 261.902"/>
<property key="LastShaderClipboardMaterial" value="textures/numbers/2"/>
Expand Down Expand Up @@ -306,6 +306,7 @@
<keyValue key="name" value="func_static_1"/>
<keyValue key="model" value="func_static_1"/>
<keyValue key="origin" value="96 -32 0"/>
<keyValue key="dummyspawnarg" value="dummyvalue"/>
</keyValues>
<layers>
<layer id="0"/>
Expand Down

0 comments on commit 07ca6d9

Please sign in to comment.