From 62303e922d72549df27b5750540698500e12eb56 Mon Sep 17 00:00:00 2001 From: codereader Date: Sun, 27 Sep 2020 14:39:07 +0200 Subject: [PATCH] #5336: Add two more unit tests covering CSG merge --- test/CSG.cpp | 78 ++++++++++++++++++++++++- test/algorithm/Scene.h | 32 +++++++++++ test/resources/tdm/maps/csg_merge.map | 79 ++++++++++++++++++++++++++ test/test.cpp | 5 -- tools/msvc/Tests/Tests.vcxproj | 1 - tools/msvc/Tests/Tests.vcxproj.filters | 1 - 6 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 test/resources/tdm/maps/csg_merge.map delete mode 100644 test/test.cpp diff --git a/test/CSG.cpp b/test/CSG.cpp index a45548d4b8..171911d861 100644 --- a/test/CSG.cpp +++ b/test/CSG.cpp @@ -5,13 +5,89 @@ #include "entitylib.h" #include "algorithm/Scene.h" +TEST_F(RadiantTest, CSGMergeTwoRegularWorldspawnBrushes) +{ + loadMap("csg_merge.map"); + + // Locate the first worldspawn brush + auto worldspawn = GlobalMapModule().getWorldspawn(); + + // Try to merge the two brushes with the "1" and "2" materials + auto firstBrush = test::algorithm::findFirstBrushWithMaterial(worldspawn, "1"); + auto secondBrush = test::algorithm::findFirstBrushWithMaterial(worldspawn, "2"); + + ASSERT_TRUE(Node_getIBrush(firstBrush)->getNumFaces() == 5); + ASSERT_TRUE(Node_getIBrush(secondBrush)->getNumFaces() == 5); + + // Select the brushes and merge them + GlobalSelectionSystem().setSelectedAll(false); + Node_setSelected(firstBrush, true); + Node_setSelected(secondBrush, true); + + // CSG merge + GlobalCommandSystem().executeCommand("CSGMerge"); + + // The two brushes should be gone, replaced by a new one + ASSERT_TRUE(firstBrush->getParent() == nullptr); + ASSERT_TRUE(secondBrush->getParent() == nullptr); + + // The merged brush will carry both materials + auto brushWithMaterial1 = test::algorithm::findFirstBrushWithMaterial(worldspawn, "1"); + auto brushWithMaterial2 = test::algorithm::findFirstBrushWithMaterial(worldspawn, "2"); + + ASSERT_TRUE(brushWithMaterial1 == brushWithMaterial2); + ASSERT_TRUE(Node_getIBrush(brushWithMaterial1)->getNumFaces() == 6); +} + +TEST_F(RadiantTest, CSGMergeFourRegularWorldspawnBrushes) +{ + loadMap("csg_merge.map"); + + // Locate the first worldspawn brush + auto worldspawn = GlobalMapModule().getWorldspawn(); + + // Try to merge the two brushes with the "1" and "2" materials + std::vector brushes = { + test::algorithm::findFirstBrushWithMaterial(worldspawn, "1"), + test::algorithm::findFirstBrushWithMaterial(worldspawn, "2"), + test::algorithm::findFirstBrushWithMaterial(worldspawn, "3"), + test::algorithm::findFirstBrushWithMaterial(worldspawn, "4") + }; + + // Check the correct setup + for (const auto& brush : brushes) + { + ASSERT_TRUE(Node_getIBrush(brush)->getNumFaces() == 5); + } + + // Select the brushes and merge them + GlobalSelectionSystem().setSelectedAll(false); + for (const auto& brush : brushes) + { + Node_setSelected(brush, true); + } + + // CSG merge + GlobalCommandSystem().executeCommand("CSGMerge"); + + // All brushes should be gone, replaced by a new one + for (const auto& brush : brushes) + { + ASSERT_TRUE(brush->getParent() == nullptr); + } + + // The combined brush should be a 6-sided cuboid + auto brushWithMaterial1 = test::algorithm::findFirstBrushWithMaterial(worldspawn, "1"); + ASSERT_TRUE(Node_getIBrush(brushWithMaterial1)->getNumFaces() == 6); +} + // Issue #5336: Crash when using CSG Merge on brushes that are entities TEST_F(RadiantTest, CSGMergeWithFuncStatic) { loadMap("csg_merge_with_func_static.map"); // Locate the first worldspawn brush - scene::INodePtr firstBrush = test::algorithm::getNthChild(GlobalMapModule().getWorldspawn(), 0); + auto firstBrush = test::algorithm::getNthChild(GlobalMapModule().getWorldspawn(), 0); ASSERT_TRUE(firstBrush); // Locate the func_static in the map diff --git a/test/algorithm/Scene.h b/test/algorithm/Scene.h index ab128f0f79..6faca2738b 100644 --- a/test/algorithm/Scene.h +++ b/test/algorithm/Scene.h @@ -3,6 +3,7 @@ #include #include "inode.h" #include "ientity.h" +#include "ibrush.h" namespace test::algorithm { @@ -28,4 +29,35 @@ inline scene::INodePtr getNthChild(const scene::INodePtr& parent, std::size_t in return candidate; } +// Finds the first matching child brush of the given parent node matching the given predicate +inline scene::INodePtr findFirstBrush(const scene::INodePtr& parent, + const std::function& predicate) +{ + scene::INodePtr candidate; + + parent->foreachNode([&](const scene::INodePtr& node) + { + auto brushNode = std::dynamic_pointer_cast(node); + + if (brushNode && predicate(brushNode)) + { + candidate = node; + return false; + } + + return true; + }); + + return candidate; +} + +// Finds the first matching child brush of the given parent node, with any of the brush's faces matching the given material +inline scene::INodePtr findFirstBrushWithMaterial(const scene::INodePtr& parent, const std::string& material) +{ + return findFirstBrush(parent, [&](const IBrushNodePtr& brush) + { + return brush->getIBrush().hasShader(material); + }); +} + } diff --git a/test/resources/tdm/maps/csg_merge.map b/test/resources/tdm/maps/csg_merge.map new file mode 100644 index 0000000000..fd97f4a156 --- /dev/null +++ b/test/resources/tdm/maps/csg_merge.map @@ -0,0 +1,79 @@ +Version 2 +// entity 0 +{ +"classname" "worldspawn" +"description" "Brushes in this map have numbers as their material names, such that the unit test algorithm can find them" +// primitive 0 +{ +brushDef3 +{ +( 0 0 1 -64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +( 0 0 -1 -64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +( 0 -1 0 0 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +( -1 0 0 64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +( 0.7071067932881648 0.7071067932881648 0 -90.5096695408851 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +} +} +// primitive 1 +{ +brushDef3 +{ +( 0 0 1 -64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +( 0 1 0 -64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +( 1 0 0 -128 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +( 0 0 -1 -64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +( -0.7071067932881648 -0.7071067932881648 0 90.5096695408851 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +} +} +// primitive 2 +{ +brushDef3 +{ +( 0 0 1 -64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 62 ) ) "4" 0 0 0 +( 0 1 0 -64 ) ( ( 0.03125 0 2 ) ( 0 0.03125 0 ) ) "4" 0 0 0 +( 1 0 0 -192 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "4" 0 0 0 +( 0 0 -1 -64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 2 ) ) "4" 0 0 0 +( -0.7071067932881648 -0.7071067932881648 0 135.7645043113276 ) ( ( 0.03125 0 126.5857849121094 ) ( 0 0.03125 0 ) ) "4" 0 0 0 +} +} +// primitive 3 +{ +brushDef3 +{ +( 0 0 1 -64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 62 ) ) "3" 0 0 0 +( 0 0 -1 -64 ) ( ( 0.03125 0 0 ) ( 0 0.03125 2 ) ) "3" 0 0 0 +( 0 -1 0 0 ) ( ( 0.03125 0 62 ) ( 0 0.03125 0 ) ) "3" 0 0 0 +( -1 0 0 128 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "3" 0 0 0 +( 0.7071067932881648 0.7071067932881648 0 -135.7645043113276 ) ( ( 0.03125 0 1.41421365737915 ) ( 0 0.03125 0 ) ) "3" 0 0 0 +} +} +} +// entity 1 +{ +"classname" "func_static" +"name" "func_static_1" +"model" "func_static_1" +"origin" "96 -32 0" +// primitive 0 +{ +brushDef3 +{ +( 0 0 1 -64 ) ( ( 0.03125 0 2 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +( 0 0 -1 -64 ) ( ( 0.03125 0 2 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +( 0 -1 0 -32 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +( -1 0 0 -32 ) ( ( 0.03125 0 62 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +( 0.7071067932881648 0.7071067932881648 0 -7.105427357601002e-15 ) ( ( 0.03125 0 2 ) ( 0 0.03125 0 ) ) "1" 0 0 0 +} +} +// primitive 1 +{ +brushDef3 +{ +( 0 0 1 -64 ) ( ( 0.03125 0 2 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +( 0 1 0 -32 ) ( ( 0.03125 0 0 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +( 1 0 0 -32 ) ( ( 0.03125 0 2 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +( 0 0 -1 -64 ) ( ( 0.03125 0 2 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +( -0.7071067932881648 -0.7071067932881648 0 7.105427357601002e-15 ) ( ( 0.03125 0 2 ) ( 0 0.03125 0 ) ) "2" 0 0 0 +} +} +} diff --git a/test/test.cpp b/test/test.cpp deleted file mode 100644 index 564f0b3d59..0000000000 --- a/test/test.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "RadiantTest.h" - -TEST_F(RadiantTest, BaseTest) { - EXPECT_TRUE(true); -} diff --git a/tools/msvc/Tests/Tests.vcxproj b/tools/msvc/Tests/Tests.vcxproj index 3c2529ea92..6c48918abb 100644 --- a/tools/msvc/Tests/Tests.vcxproj +++ b/tools/msvc/Tests/Tests.vcxproj @@ -63,7 +63,6 @@ - diff --git a/tools/msvc/Tests/Tests.vcxproj.filters b/tools/msvc/Tests/Tests.vcxproj.filters index f8700109bc..f7065a3977 100644 --- a/tools/msvc/Tests/Tests.vcxproj.filters +++ b/tools/msvc/Tests/Tests.vcxproj.filters @@ -3,7 +3,6 @@ -