Merge pull request #27759 from dhruvjamwal/main

Fix #27741: Prevent camel case splitting on numbers in Property Editor.
This commit is contained in:
Chris Hennes
2026-03-07 00:08:47 -06:00
committed by GitHub
8 changed files with 249 additions and 8 deletions
@@ -68,6 +68,12 @@ runs:
testCommand: ${{ inputs.builddir }}/tests/Gui_tests_run --gtest_output=json:${{ inputs.reportdir }}gui_gtest_results.json
testLogFile: ${{ inputs.reportdir }}gui_gtest_test_log.txt
testName: Gui
- name: C++ Qt tests
id: qttests
uses: ./.github/workflows/actions/runCPPTests/runQtTests
with:
builddir: ${{ inputs.builddir }}
testLogFile: ${{ inputs.reportdir }}qt_ctest_log.txt
- name: C++ Material tests
id: material
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
@@ -0,0 +1,55 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# SPDX-FileNotice: Part of the FreeCAD project.
name: runQtTests
description: "Run all QtTest-based C++ tests (those labelled 'Qt' in CTest), generate log and report"
inputs:
builddir:
description: "Build directory containing CTestTestfile.cmake"
required: true
testLogFile:
description: "Path for the command-line output of the tests"
required: true
outputs:
reportText:
description: "Report text"
value: ${{ steps.report.outputs.reportText }}
runs:
using: "composite"
steps:
- name: Run Qt tests via CTest
shell: bash -l {0}
env:
BUILD_DIR: ${{ inputs.builddir }}
TEST_LOG_FILE: ${{ inputs.testLogFile }}
run: |
set -o pipefail
ctest --test-dir "$BUILD_DIR" -L Qt --output-on-failure | tee -a "$TEST_LOG_FILE"
- name: Parse test results
if: always()
id: report
shell: bash -l {0}
env:
TEST_LOG_FILE: ${{ inputs.testLogFile }}
run: |
result=$(grep -E "(Test #|tests passed|tests failed|Total Test time)" "$TEST_LOG_FILE" || true)
if grep -qE "[1-9][0-9]* tests failed" "$TEST_LOG_FILE"
then
reportText="<details><summary>:fire: QtTest C++ tests failed</summary>\n"
else
reportText="<details><summary>:heavy_check_mark: QtTest C++ tests succeeded</summary>\n"
fi
reportText+="\n"
reportText+="Results\n"
reportText+="\n"
reportText+='```\n'
reportText+="$result\n"
reportText+='```\n'
reportText+="</details>\n"
reportText+="\n"
echo "reportText<<EOD" >> $GITHUB_OUTPUT
echo -e "$reportText" >> $GITHUB_OUTPUT
echo "EOD" >> $GITHUB_OUTPUT
echo -e "$reportText"
@@ -0,0 +1,62 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# SPDX-FileNotice: Part of the FreeCAD project.
name: runSingleQtTest
description: "Run a single QtTest-based C++ test executable, generate log and report"
inputs:
testCommand:
description: "Test executable path"
required: true
testLogFile:
description: "Path for the command-line output of the tests"
required: true
testName:
description: "A descriptive name for the test suite"
required: true
outputs:
reportText:
description: "Report text"
value: ${{ steps.report.outputs.reportText }}
runs:
using: "composite"
steps:
- name: Run QtTest tests
shell: bash -l {0}
env:
QT_QPA_PLATFORM: offscreen
TEST_COMMAND: ${{ inputs.testCommand }}
TEST_LOG_FILE: ${{ inputs.testLogFile }}
run: |
set -o pipefail
$TEST_COMMAND | tee -a "$TEST_LOG_FILE"
- name: Parse test results
if: always()
id: report
shell: bash -l {0}
env:
TEST_LOG_FILE: ${{ inputs.testLogFile }}
TEST_NAME: ${{ inputs.testName }}
run: |
# This bonkers sed regex is to try to find the start and end of the QtTest output block, which is not really
# designed to be machine-readable. Why did they have to choose asterisks?!?!
result=$(sed -n '/^\*\*\*\*\*\*\*\*\* Start/,/^\*\*\*\*\*\*\*\*\* Finished/p' "$TEST_LOG_FILE" || true)
if grep -qE "^FAIL!" "$TEST_LOG_FILE"
then
reportText="<details><summary>:fire: QtTest C++ test suite '${TEST_NAME}' failed</summary>\n"
else
reportText="<details><summary>:heavy_check_mark: QtTest C++ test suite '${TEST_NAME}' succeeded</summary>\n"
fi
reportText+="\n"
reportText+="Results\n"
reportText+="\n"
reportText+='```\n'
reportText+="$result\n"
reportText+='```\n'
reportText+="</details>\n"
reportText+="\n"
echo "reportText<<EOD" >> $GITHUB_OUTPUT
echo -e "$reportText" >> $GITHUB_OUTPUT
echo "EOD" >> $GITHUB_OUTPUT
echo -e "$reportText"
+4 -8
View File
@@ -574,18 +574,14 @@ void PropertyItem::setPropertyName(const QString& name, const QString& realName)
setObjectName(propName);
QString display;
bool upper = false;
// Camel case splitting
for (auto&& i : name) {
if (i.isUpper() && !display.isEmpty()) {
// if there is a sequence of capital letters do not insert spaces
if (!upper) {
QChar last = display.at(display.length() - 1);
if (!last.isSpace()) {
display += QLatin1String(" ");
}
QChar last = display.at(display.length() - 1);
if (last.isLower()) {
display += QLatin1String(" ");
}
}
upper = i.isUpper();
display += i;
}
+9
View File
@@ -77,6 +77,15 @@ function(setup_qt_test)
FreeCADGui
${QtTest_LIBRARIES}
${${_testname}_LIBS})
if(NOT WIN32)
set_target_properties(${_testname}_Tests_run PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests)
endif()
set_tests_properties(${_testname}_Tests_run PROPERTIES
LABELS "Qt"
ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
endforeach()
endfunction()
+1
View File
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
add_subdirectory(Dialogs)
add_subdirectory(propertyeditor)
# Standard C++ GTest tests
add_executable(Gui_tests_run
@@ -0,0 +1 @@
setup_qt_test(PropertyItem)
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
#include <QDebug>
#include <QtTest/QTest>
#include <App/Application.h>
#include "Gui/propertyeditor/PropertyItem.h"
#include <src/App/InitApplication.h>
// NOLINTBEGIN(readability-magic-numbers)
class MockPropertyItem final: public Gui::PropertyEditor::PropertyItem
{
public:
MockPropertyItem() = default;
void setTestValue(const QVariant& v)
{
testValue = v;
}
QVariant lastSetValue() const
{
return storedSetValue;
}
protected:
QVariant value(const App::Property* /*prop*/) const override
{
return testValue;
}
void setValue(const QVariant& v) override
{
storedSetValue = v;
}
private:
QVariant testValue;
QVariant storedSetValue;
};
class testPropertyItem: public QObject
{
Q_OBJECT
public:
testPropertyItem()
{
tests::initApplication();
item.reset(new MockPropertyItem());
}
private Q_SLOTS:
void init()
{}
void cleanup()
{}
void test_camelCaseSplitsTwoWords() // NOLINT
{
item->setPropertyName(QLatin1String("CamelCase"));
QCOMPARE(item->propertyName(), QLatin1String("Camel Case"));
}
void test_camelCaseSplitsThreeWords() // NOLINT
{
item->setPropertyName(QLatin1String("ThreeWordProperty"));
QCOMPARE(item->propertyName(), QLatin1String("Three Word Property"));
}
void test_digitBeforeUppercaseNotSplit() // NOLINT
{
item->setPropertyName(QLatin1String("View3D"));
QCOMPARE(item->propertyName(), QLatin1String("View3D"));
}
void test_camelCaseSplitButNotDigit() // NOLINT
{
item->setPropertyName(QLatin1String("MyView3D"));
QCOMPARE(item->propertyName(), QLatin1String("My View3D"));
}
void test_consecutiveUppercaseNotSplit() // NOLINT
{
item->setPropertyName(QLatin1String("MyABC"));
QCOMPARE(item->propertyName(), QLatin1String("My ABC"));
}
void test_notCleverEnoughToSplitConsecutiveCaps() // NOLINT
{
item->setPropertyName(QLatin1String("MyABCOfDoom"));
QCOMPARE(item->propertyName(), QLatin1String("My ABCOf Doom"));
}
void test_underscoresArentTheSameAsSpaces() // NOLINT
{
item->setPropertyName(QLatin1String("Box_Length"));
QCOMPARE(item->propertyName(), QLatin1String("Box_Length"));
}
private:
std::unique_ptr<Gui::PropertyEditor::PropertyItem> item;
};
// NOLINTEND(readability-magic-numbers)
QTEST_MAIN(testPropertyItem)
#include "PropertyItem.moc"