Add test causing crash and fix map sorting function. (#22889)

* Add test causing crash and fix map sorting function.

When opening a model from FreeCAD 0.7, the model would crash when I
changed some of the parameters.  This turned out to be due to the
ElementNameComparator::operator() not correctly sorting the items going
into the map, which caused the map to be invalid.

This change adds a test that represented the exact names causing the
crash as well as a fix for the problem.  Names are now sorted:

1. Empty names first
2. Identifier based names second.
3. Hex based names last.

Identifiers are sorted lexicographically for the name portion and
numerically for the number portion, smallest to largest.

Hex based names are sorted by the value of the hex number, smallest to
largest.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix lint issues.

* Add another test form to the mix.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Weston Schmidt
2025-09-05 12:07:02 -07:00
committed by GitHub
parent 6d71a564d5
commit 140c9febc4
2 changed files with 321 additions and 122 deletions

View File

@@ -1,6 +1,11 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include <gtest/gtest.h>
#include <utility>
#include <map>
#include <string>
#include <vector>
#include "App/IndexedName.h"
#include "App/MappedElement.h"
@@ -172,13 +177,24 @@ TEST_F(MappedElementTest, comparatorBothStartWithTheSameHexDigits)
EXPECT_FALSE(comp(mappedName1, mappedName2));
}
TEST_F(MappedElementTest, DISABLED_comparatorHexWithoutTerminatorIsBroken)
TEST_F(MappedElementTest, comparatorHexWithoutTerminator)
{
// Arrange
Data::MappedName mappedName1 {"#fed"};
Data::MappedName mappedName2 {"#abcdef"};
auto comp = Data::ElementNameComparator();
// Act & Assert
EXPECT_TRUE(comp(mappedName1, mappedName2));
}
TEST_F(MappedElementTest, comparatorHexWithoutTerminatorSameLength)
{
// Arrange
Data::MappedName mappedName1 {"#fed"};
Data::MappedName mappedName2 {"#abc"};
auto comp = Data::ElementNameComparator();
// Act & Assert
EXPECT_FALSE(comp(mappedName1, mappedName2));
}
@@ -205,7 +221,7 @@ TEST_F(MappedElementTest, comparatorNoHexDigitsSameStringNumericCompare)
EXPECT_FALSE(comp(mappedName1, mappedName2));
}
TEST_F(MappedElementTest, DISABLED_comparatorIntegerWithoutTerminatorIsBroken)
TEST_F(MappedElementTest, comparatorIntegerWithoutTerminatorIsBroken)
{
// Arrange
Data::MappedName mappedName1 {"Edge123456"};
@@ -215,3 +231,87 @@ TEST_F(MappedElementTest, DISABLED_comparatorIntegerWithoutTerminatorIsBroken)
// Act & Assert
EXPECT_FALSE(comp(mappedName1, mappedName2));
}
TEST_F(MappedElementTest, comparatorThreeComplexHexNamesInMap)
{
// Arrange
Data::MappedName name1("#19c9:e;:U;FUS;:Hce4:7,E");
Data::MappedName name2("#1dadb:11;:L#1061a;FUS;:H:d,E");
Data::MappedName name3("#1dae6:8;:L#1dae4;FUS;:H:d,E");
std::map<Data::MappedName, int, Data::ElementNameComparator> testMap;
testMap[name1] = 1;
testMap[name2] = 2;
testMap[name3] = 3;
// Assert: map should have 3 unique keys
EXPECT_EQ(testMap.size(), 3);
// Collect keys in order
std::vector<std::string> keys;
for (const auto& kv : testMap) {
keys.push_back(kv.first.toString());
}
// Print for debug (optional)
// for (const auto& k : keys) std::cout << k << std::endl;
// The expected order depends on your comparator logic.
// If you want to check the exact order, set it here:
// (Replace with the correct expected order if needed)
std::vector<std::string> expectedOrder = {"#19c9:e;:U;FUS;:Hce4:7,E",
"#1dadb:11;:L#1061a;FUS;:H:d,E",
"#1dae6:8;:L#1dae4;FUS;:H:d,E"};
EXPECT_EQ(keys, expectedOrder);
}
TEST_F(MappedElementTest, comparatorLargerWorkedExampleWithMap)
{
// Arrange
Data::MappedName name0("Edge123;:U;FUS;:Hce4:7,E");
Data::MappedName name1("#1dad:e;:U;FUS;:Hce4:7,E");
Data::MappedName name2("#1dadb:11;:L#1061a;FUS;:H:d,E");
Data::MappedName name3("#1dae6:8;:L#1dae4;FUS;:H:d,E");
Data::MappedName name4("Edge999;;:L#1dae4;FUS;:H:d,E");
Data::MappedName name5("g4v2;SKT;:H1234,F;:H5678:2,E;:G0(g1;SKT;:H9012,E);XTR;:H3456:2,F");
std::map<Data::MappedName, int, Data::ElementNameComparator> testMap;
testMap[name0] = 1;
testMap[name1] = 2;
testMap[name2] = 3;
testMap[name3] = 4;
testMap[name0] = 5; // Duplicate, should not affect size
testMap[name1] = 6; // Duplicate, should not affect size
testMap[name4] = 7; // New entry
testMap[name4] = 8; // Duplicate, should not affect size
testMap[name2] = 9; // Duplicate, should not affect size
testMap[name3] = 10; // Duplicate, should not affect size
testMap[name5] = 11;
// Assert: map should have 5 unique keys
EXPECT_EQ(testMap.size(), 6);
// Collect keys in order
std::vector<std::string> keys;
for (const auto& kv : testMap) {
keys.push_back(kv.first.toString());
}
// Print for debug (optional)
// for (const auto& k : keys) std::cout << k << std::endl;
// The expected order depends on your comparator logic.
// If you want to check the exact order, set it here:
// (Replace with the correct expected order if needed)
std::vector<std::string> expectedOrder = {
"Edge123;:U;FUS;:Hce4:7,E",
"Edge999;;:L#1dae4;FUS;:H:d,E",
"g4v2;SKT;:H1234,F;:H5678:2,E;:G0(g1;SKT;:H9012,E);XTR;:H3456:2,F",
"#1dad:e;:U;FUS;:Hce4:7,E",
"#1dadb:11;:L#1061a;FUS;:H:d,E",
"#1dae6:8;:L#1dae4;FUS;:H:d,E"};
EXPECT_EQ(keys, expectedOrder);
}