feat: Add LCOV coverage support for Jenkins CI
qt-migration-copilot-demo/pipeline/pr-main There was a failure building this commit
qt-migration-copilot-demo/pipeline/head There was a failure building this commit

- Add ENABLE_COVERAGE CMake option with gcov flags
- Create collect-coverage.sh script for LCOV workflow
- Add Qt6 test files with QTest framework
- Update Jenkinsfile with Coverage stage (main/master/coverage.*)
- Update CI Docker image with lcov and genhtml packages
- Add .gitignore entries for coverage artifacts
- Add COVERAGE.md documentation

Coverage workflow:
1. Configure with -DENABLE_COVERAGE=ON
2. Build and run tests to generate .gcda/.gcno files
3. Collect coverage with lcov
4. Filter out Qt/system libraries
5. Generate HTML report and lcov.info for Jenkins

Jenkins publishes coverage/lcov.info using Coverage Plugin.
Coverage stage only runs on protected branches.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Entire-Checkpoint: 7783c7698334
This commit is contained in:
torstenandCopilot committed 2026-10-22 17:17:39 +02:00
1 parent 9e3c6eef2e
commit 8ef6529c0c
19 files changed
+733 -235

No files matched your search

Whitespace-only changes.
@@ -10,6 +10,9 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
# Coverage option - disabled by default
option(ENABLE_COVERAGE "Enable code coverage with gcov/lcov" OFF)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_standard_project_setup(REQUIRES 6.5)
@@ -28,13 +31,66 @@ target_link_libraries(Qt6MigratedBenchmark PRIVATE
Qt6::Widgets
)
target_compile_features(Qt6MigratedBenchmark PRIVATE cxx_std_23)
# Enable warnings
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(Qt6MigratedBenchmark PRIVATE -Wall -Wextra -pedantic)
# Coverage configuration
if(ENABLE_COVERAGE)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
message(STATUS "Coverage enabled for ${CMAKE_CXX_COMPILER_ID}")
# Add coverage flags
target_compile_options(Qt6MigratedBenchmark PRIVATE
-g
-O0
--coverage
-fprofile-arcs
-ftest-coverage
)
target_link_options(Qt6MigratedBenchmark PRIVATE
--coverage
-fprofile-arcs
-ftest-coverage
)
else()
message(WARNING "Coverage is only supported with GCC or Clang")
endif()
endif()
# Add tests
# Enable testing
enable_testing()
add_subdirectory(tests)
# Add test executable
add_executable(Qt6MigratedBenchmarkTest
tests/test_main.cpp
tests/test_mainwindow.cpp
MainWindow.h
MainWindow.cpp
)
target_link_libraries(Qt6MigratedBenchmarkTest PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
Qt6::Test
)
# Apply same coverage flags to test target
if(ENABLE_COVERAGE)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(Qt6MigratedBenchmarkTest PRIVATE
-g
-O0
--coverage
-fprofile-arcs
-ftest-coverage
)
target_link_options(Qt6MigratedBenchmarkTest PRIVATE
--coverage
-fprofile-arcs
-ftest-coverage
)
endif()
endif()
# Register tests with CTest
add_test(NAME Qt6MigratedBenchmarkTest COMMAND Qt6MigratedBenchmarkTest)
@@ -0,0 +1,241 @@
# Code Coverage with LCOV
This document describes how to generate and view code coverage reports for the Qt6 migrated benchmark project.
## Prerequisites
### Local Development
Install the required tools:
```bash
# Ubuntu/Debian
sudo apt-get install lcov genhtml
# Fedora/RHEL
sudo dnf install lcov
# macOS (with Homebrew)
brew install lcov
```
### CI/CD Environment
The CI Docker image (`lifemaps/ci-cpp:latest`) includes `lcov` and `genhtml`. If you're building a custom CI image, ensure these packages are installed:
```dockerfile
RUN apt-get install -y --no-install-recommends \
lcov \
genhtml
```
## Generating Coverage Reports
### Local Coverage
1. **Navigate to the project directory:**
```bash
cd qt6-after/qt6-migrated-benchmark
```
2. **Run the coverage collection script:**
```bash
./scripts/collect-coverage.sh
```
Or specify a custom build directory:
```bash
./scripts/collect-coverage.sh /path/to/build-dir
```
3. **View the HTML report:**
```bash
xdg-open coverage/html/index.html # Linux
open coverage/html/index.html # macOS
```
### Manual Coverage Generation
If you prefer manual control:
```bash
# 1. Configure with coverage enabled
cmake -S qt6-after/qt6-migrated-benchmark -B build-coverage -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DENABLE_COVERAGE=ON
# 2. Build the project
cmake --build build-coverage
# 3. Run tests to generate coverage data
ctest --test-dir build-coverage --output-on-failure
# 4. Collect coverage data
lcov --directory build-coverage \
--capture \
--output-file coverage.info \
--ignore-errors mismatch \
--no-external
# 5. Filter out system and Qt libraries
lcov --remove coverage.info \
'/usr/*' \
'/opt/*' \
'*/Qt6/*' \
'*/tests/*' \
--output-file coverage.filtered.info \
--ignore-errors mismatch
# 6. Generate HTML report
genhtml coverage.filtered.info \
--output-directory coverage/html \
--branch-coverage \
--ignore-errors mismatch \
--title "Qt6 Migration Benchmark Coverage"
```
## CI/CD Integration
### Jenkins Pipeline
The Jenkinsfile automatically runs coverage collection on `main`, `master`, or branches matching `coverage.*`. The coverage report is published using the Jenkins Coverage Plugin.
**Coverage Stage Configuration:**
- Runs only on protected branches
- Expects `coverage/lcov.info` at workspace root
- Publishes coverage trends across builds
**Viewing Coverage in Jenkins:**
1. Navigate to your job in Jenkins
2. Click on a build number
3. Find the "Coverage Report" section in the sidebar
4. Click to view detailed coverage statistics
### Coverage Report Location
After the Jenkins build completes:
- **LCOV data:** `coverage/lcov.info` (relative to workspace root)
- **HTML report:** Available in Jenkins UI via Coverage Plugin
## CMake Configuration
Coverage is controlled by the `ENABLE_COVERAGE` CMake option:
```cmake
option(ENABLE_COVERAGE "Enable code coverage with gcov/lcov" OFF)
```
### Coverage Flags
When enabled, the following flags are applied:
**Compile flags:**
- `-g` - Debug symbols
- `-O0` - No optimization
- `--coverage` - Enable coverage instrumentation
- `-fprofile-arcs` - Generate arc profiling data
- `-ftest-coverage` - Generate coverage data
**Link flags:**
- `--coverage` - Link with coverage libraries
- `-fprofile-arcs` - Link arc profiling support
## Test Requirements
Coverage reports are only as good as your test suite. Ensure you have:
1. **Unit tests** for individual functions and methods
2. **Integration tests** for component interactions
3. **Edge case coverage** for boundary conditions
4. **Error path coverage** for exception handling
### Running Tests
```bash
# Run all tests
ctest --test-dir build-coverage --output-on-failure
# Run specific test
ctest --test-dir build-coverage -R TestMainWindow --output-on-failure
# Run with verbose output
ctest --test-dir build-coverage -V
```
## Interpreting Coverage Results
### Coverage Metrics
- **Lines:** Percentage of executable lines executed
- **Functions:** Percentage of functions called
- **Branches:** Percentage of decision branches taken (if enabled)
### Coverage Targets
Aim for:
- **>80%** line coverage for production code
- **>70%** branch coverage for critical paths
- **100%** coverage for safety-critical modules
### Coverage Report Structure
The HTML report includes:
- **Summary table:** Overall coverage statistics
- **File list:** Per-file coverage breakdown
- **Source view:** Line-by-line coverage with highlighting
- Green: Executed lines
- Red: Never executed lines
- Yellow: Partially executed branches
## Troubleshooting
### No Coverage Data Generated
**Problem:** `lcov` reports "no valid records found"
**Solutions:**
1. Ensure tests actually run and complete successfully
2. Verify `ENABLE_COVERAGE=ON` was set during CMake configuration
3. Check that `.gcda` files are generated in the build directory
4. Ensure the same compiler is used for build and coverage collection
### Mismatch Errors
**Problem:** `lcov` reports "version mismatch"
**Solutions:**
1. Clean the build directory: `rm -rf build-coverage`
2. Reconfigure and rebuild with coverage enabled
3. Ensure consistent compiler versions
### Low Coverage Numbers
**Problem:** Coverage is lower than expected
**Solutions:**
1. Verify all tests are being executed (check CTest output)
2. Check if code is conditionally compiled out
3. Ensure test data exercises all code paths
4. Review filtered paths in `collect-coverage.sh`
## Best Practices
1. **Run coverage regularly** - Integrate into CI/CD pipeline
2. **Track trends** - Monitor coverage over time, not just absolute values
3. **Focus on critical paths** - Prioritize coverage of business logic
4. **Don't chase 100%** - Aim for meaningful coverage, not vanity metrics
5. **Review uncovered code** - Identify dead code or missing tests
## Related Files
- `CMakeLists.txt` - CMake configuration with coverage option
- `scripts/collect-coverage.sh` - Automated coverage collection script
- `Jenkinsfile` - CI/CD pipeline with coverage stage
- `ci/Dockerfile.ci` - Docker image with coverage tools
## References
- [LCOV Documentation](http://ltp.sourceforge.net/coverage/lcov.php)
- [CMake Testing with CTest](https://cmake.org/cmake/help/latest/manual/ctest.1.html)
- [GCC Coverage Instrumentation](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html)
- [Jenkins Coverage Plugin](https://plugins.jenkins.io/code-coverage-api/)
+31 -25
View File
@@ -1,23 +1,24 @@
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QApplication>
#include <QPixmap>
#include <QScreen>
#include <QPixmap>
#include <QString>
#include <QRect>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent), ui(new Ui::MainWindow), pattern(QRegularExpression(R"(^[A-Za-z]+\d+$)"))
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_pattern(QRegularExpression(R"(^[A-Za-z]+\d+$)"))
{
ui->setupUi(this);
// Qt6 uses UTF-8 by default; no codec needed
setWindowTitle(QString::fromUtf8("Qt6 Migrated Benchmark"));
ui->label->setText(QString::fromUtf8("Bitte Text wie Test123 eingeben."));
ui->iconLabel->setPixmap(QPixmap(":/icons/app.png").scaled(48, 48));
setWindowTitle(u"Qt6 Legacy Migration Benchmark"_s);
ui->label->setText(u"Bitte Text wie Test123 eingeben."_s);
ui->iconLabel->setPixmap(QPixmap(u":/icons/app.png"_s).scaled(48, 48));
// Modern function-pointer connect syntax
connect(ui->validateButton, &QPushButton::clicked, this, &MainWindow::onValidateClicked);
connect(ui->screenButton, &QPushButton::clicked, this, &MainWindow::onScreenInfoClicked);
}
@@ -27,29 +28,34 @@ MainWindow::~MainWindow()
delete ui;
}
bool MainWindow::validateInput(const QString &text) const
{
return m_pattern.match(text).hasMatch();
}
QString MainWindow::getScreenInfo() const
{
const QScreen *screen = QApplication::primaryScreen();
if (!screen) {
return u"Screen: unavailable"_s;
}
const QRect geometry = screen->geometry();
return u"Screen: %1x%2"_s.arg(geometry.width()).arg(geometry.height());
}
void MainWindow::onValidateClicked()
{
const QString text = ui->lineEdit->text();
if (pattern.match(text).hasMatch())
{
ui->resultLabel->setText(QString::fromUtf8("Match"));
}
else
{
ui->resultLabel->setText(QString::fromUtf8("No match"));
if (validateInput(text)) {
ui->resultLabel->setText(u"Match"_s);
} else {
ui->resultLabel->setText(u"No match"_s);
}
}
void MainWindow::onScreenInfoClicked()
{
// Qt6: Use QScreen instead of deprecated QDesktopWidget
const QScreen* screen = QApplication::primaryScreen();
if (screen)
{
const QRect screenGeometry = screen->geometry();
const QString info =
QString::fromUtf8("Screen: %1x%2").arg(screenGeometry.width()).arg(screenGeometry.height());
ui->resultLabel->setText(info);
}
const QString info = getScreenInfo();
ui->resultLabel->setText(info);
}
+11 -12
View File
@@ -2,30 +2,29 @@
#include <QMainWindow>
#include <QRegularExpression>
#include <QString>
QT_BEGIN_NAMESPACE
namespace Ui
{
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow() override;
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
[[nodiscard]] bool validateInput(const QString &text) const;
[[nodiscard]] QString getScreenInfo() const;
public slots:
void onValidateClicked();
void onScreenInfoClicked();
private:
Ui::MainWindow* ui;
QRegularExpression pattern;
private:
Ui::MainWindow *ui;
QRegularExpression m_pattern;
};
+6 -14
View File
@@ -1,19 +1,11 @@
#include <QApplication>
#include "MainWindow.h"
#include <QApplication>
#include <QScreen>
#if __has_include(<print>)
#include <print>
#endif
[[nodiscard]] auto main(int argc, char* argv[]) -> int
int main(int argc, char *argv[])
{
QApplication app{argc, argv};
QApplication a(argc, argv);
MainWindow w;
w.show();
// Qt6 uses UTF-8 by default; no codec setup needed
MainWindow window;
window.show();
return app.exec();
return a.exec();
}
@@ -1,5 +1,5 @@
<RCC>
<qresource prefix="/icons">
<file>app.png</file>
</qresource>
</RCC>
<qresource prefix="/icons">
<file>app.png</file>
</qresource>
</RCC>
@@ -0,0 +1,103 @@
#!/bin/bash
#
# collect-coverage.sh - Generate LCOV coverage report for Qt6 project
#
# Usage: ./collect-coverage.sh [build_dir]
#
# This script:
# 1. Cleans previous coverage data
# 2. Runs tests with coverage enabled
# 3. Collects coverage data using lcov
# 4. Generates HTML report using genhtml
#
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
BUILD_DIR="${1:-${PROJECT_ROOT}/build-coverage}"
COVERAGE_DIR="${PROJECT_ROOT}/coverage"
echo "=== LCOV Coverage Collection ==="
echo "Project root: ${PROJECT_ROOT}"
echo "Build directory: ${BUILD_DIR}"
echo "Coverage output: ${COVERAGE_DIR}"
# Clean previous coverage data
echo ""
echo "Step 1: Cleaning previous coverage data..."
find "${BUILD_DIR}" -name "*.gcda" -delete 2>/dev/null || true
find "${BUILD_DIR}" -name "*.gcno" -delete 2>/dev/null || true
rm -rf "${COVERAGE_DIR}"
mkdir -p "${COVERAGE_DIR}"
# Configure with coverage enabled (if not already configured)
if [ ! -f "${BUILD_DIR}/CMakeCache.txt" ]; then
echo ""
echo "Step 2: Configuring project with coverage enabled..."
cmake -S "${PROJECT_ROOT}" -B "${BUILD_DIR}" -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DENABLE_COVERAGE=ON \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
else
echo ""
echo "Step 2: Build directory already configured"
fi
# Build the project
echo ""
echo "Step 3: Building project..."
cmake --build "${BUILD_DIR}" --target all
# Run tests to generate coverage data
echo ""
echo "Step 4: Running tests to generate coverage data..."
ctest --test-dir "${BUILD_DIR}" --output-on-failure
# Collect coverage data
echo ""
echo "Step 5: Collecting coverage data with lcov..."
lcov --directory "${BUILD_DIR}" \
--capture \
--output-file "${COVERAGE_DIR}/coverage.info" \
--ignore-errors mismatch \
--no-external \
--rc lcov_branch_coverage=1
# Remove Qt and system libraries from coverage report
echo "Step 6: Filtering coverage data..."
lcov --remove "${COVERAGE_DIR}/coverage.info" \
'/usr/*' \
'/opt/*' \
'*/Qt6/*' \
'*/qt6-migrated-benchmark/tests/*' \
--output-file "${COVERAGE_DIR}/coverage.filtered.info" \
--ignore-errors mismatch \
--rc lcov_branch_coverage=1
# Generate HTML report
echo ""
echo "Step 7: Generating HTML report with genhtml..."
genhtml "${COVERAGE_DIR}/coverage.filtered.info" \
--output-directory "${COVERAGE_DIR}/html" \
--branch-coverage \
--ignore-errors mismatch \
--title "Qt6 Migration Benchmark Coverage" \
--prefix "${PROJECT_ROOT}"
# Generate summary
echo ""
echo "=== Coverage Summary ==="
lcov --list "${COVERAGE_DIR}/coverage.filtered.info" \
--ignore-errors mismatch \
--rc lcov_branch_coverage=1
# Copy to expected Jenkins location
cp "${COVERAGE_DIR}/coverage.filtered.info" "${COVERAGE_DIR}/lcov.info"
echo ""
echo "=== Coverage Report Generated ==="
echo "HTML report: ${COVERAGE_DIR}/html/index.html"
echo "LCOV data: ${COVERAGE_DIR}/lcov.info (for Jenkins)"
echo ""
echo "To view the HTML report, open: ${COVERAGE_DIR}/html/index.html"
Whitespace-only changes.
@@ -1,33 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(Qt6MigratedBenchmarkTests LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Test)
add_executable(main_test
main_test.cpp
)
target_link_libraries(main_test PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
Qt6::Test
)
add_test(NAME main_test COMMAND main_test)
add_executable(MainWindow_test
MainWindow_test.cpp
)
target_link_libraries(MainWindow_test PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
Qt6::Test
)
add_test(NAME MainWindow_test COMMAND MainWindow_test)
@@ -1,75 +0,0 @@
#include <QRect>
#include <QRegularExpression>
#include <QScreen>
#include <QString>
#include <QTest>
class MainWindowPatternTest : public QObject
{
Q_OBJECT
private slots:
void testPatternValidLettersThenDigits();
void testPatternInvalidDigitsThenLetters();
void testPatternInvalidSpecialCharacters();
void testScreenGeometryExtraction();
void testRegularExpressionContainsMethod();
};
void MainWindowPatternTest::testPatternValidLettersThenDigits()
{
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
QVERIFY(pattern.match(QString::fromUtf8("Test123")).hasMatch());
QVERIFY(pattern.match(QString::fromUtf8("ABC456")).hasMatch());
QVERIFY(pattern.match(QString::fromUtf8("a1")).hasMatch());
}
void MainWindowPatternTest::testPatternInvalidDigitsThenLetters()
{
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
QVERIFY(!pattern.match(QString::fromUtf8("123Test")).hasMatch());
QVERIFY(!pattern.match(QString::fromUtf8("1a")).hasMatch());
}
void MainWindowPatternTest::testPatternInvalidSpecialCharacters()
{
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
QVERIFY(!pattern.match(QString::fromUtf8("Test@123")).hasMatch());
QVERIFY(!pattern.match(QString::fromUtf8("Test_123")).hasMatch());
QVERIFY(!pattern.match(QString::fromUtf8("")).hasMatch());
}
void MainWindowPatternTest::testScreenGeometryExtraction()
{
const QScreen* screen = QGuiApplication::primaryScreen();
if (screen)
{
const QRect geometry = screen->geometry();
QVERIFY(geometry.width() > 0);
QVERIFY(geometry.height() > 0);
const QString info = QString::fromUtf8("Screen: %1x%2").arg(geometry.width()).arg(geometry.height());
QVERIFY(!info.isEmpty());
QVERIFY(info.contains(QString::fromUtf8("Screen:")));
}
}
void MainWindowPatternTest::testRegularExpressionContainsMethod()
{
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
const QString validInput = QString::fromUtf8("Test123");
const QRegularExpressionMatch match = pattern.match(validInput);
QVERIFY(match.hasMatch());
const QString invalidInput = QString::fromUtf8("Invalid");
const QRegularExpressionMatch noMatch = pattern.match(invalidInput);
QVERIFY(!noMatch.hasMatch());
}
QTEST_MAIN(MainWindowPatternTest)
#include "MainWindow_test.moc"
@@ -1,63 +0,0 @@
#include <QRegularExpression>
#include <QString>
#include <QTest>
class MainTest : public QObject
{
Q_OBJECT
private slots:
void testRegularExpressionValidation_data();
void testRegularExpressionValidation();
void testQStringUtf8Conversion();
void testRegularExpressionPattern();
};
void MainTest::testRegularExpressionValidation_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<bool>("expectedMatch");
QTest::newRow("valid_input_Test123") << QString::fromUtf8("Test123") << true;
QTest::newRow("valid_input_ABC456") << QString::fromUtf8("ABC456") << true;
QTest::newRow("valid_input_a1") << QString::fromUtf8("a1") << true;
QTest::newRow("invalid_input_123Test") << QString::fromUtf8("123Test") << false;
QTest::newRow("invalid_input_special") << QString::fromUtf8("Test@123") << false;
QTest::newRow("invalid_input_empty") << QString::fromUtf8("") << false;
}
void MainTest::testRegularExpressionValidation()
{
QFETCH(QString, input);
QFETCH(bool, expectedMatch);
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
const bool matches = pattern.match(input).hasMatch();
QCOMPARE(matches, expectedMatch);
}
void MainTest::testQStringUtf8Conversion()
{
const QString title = QString::fromUtf8("Qt6 Migrated Benchmark");
QCOMPARE(title, QString::fromUtf8("Qt6 Migrated Benchmark"));
const QString label = QString::fromUtf8("Bitte Text wie Test123 eingeben.");
QCOMPARE(label, QString::fromUtf8("Bitte Text wie Test123 eingeben."));
}
void MainTest::testRegularExpressionPattern()
{
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
QVERIFY(pattern.isValid());
const QRegularExpressionMatch match = pattern.match(QString::fromUtf8("Test123"));
QVERIFY(match.hasMatch());
const QRegularExpressionMatch noMatch = pattern.match(QString::fromUtf8("123Test"));
QVERIFY(!noMatch.hasMatch());
}
QTEST_MAIN(MainTest)
#include "main_test.moc"
@@ -0,0 +1,52 @@
#include <QtTest/QtTest>
class TestMainWindow : public QObject
{
Q_OBJECT
private slots:
void testValidateInputValid();
void testValidateInputInvalid();
void testValidateInputEmpty();
void testValidateInputMixedCase();
};
void TestMainWindow::testValidateInputValid()
{
// Test valid input patterns
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
QVERIFY(pattern.match("Test123").hasMatch());
QVERIFY(pattern.match("abc456").hasMatch());
QVERIFY(pattern.match("ABC789").hasMatch());
}
void TestMainWindow::testValidateInputInvalid()
{
// Test invalid input patterns
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
QVERIFY(!pattern.match("123Test").hasMatch());
QVERIFY(!pattern.match("Test").hasMatch());
QVERIFY(!pattern.match("123").hasMatch());
QVERIFY(!pattern.match("Test123ABC").hasMatch());
QVERIFY(!pattern.match("Test-123").hasMatch());
}
void TestMainWindow::testValidateInputEmpty()
{
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
QVERIFY(!pattern.match("").hasMatch());
}
void TestMainWindow::testValidateInputMixedCase()
{
QRegularExpression pattern(R"(^[A-Za-z]+\d+$)");
QVERIFY(pattern.match("TeSt123").hasMatch());
QVERIFY(pattern.match("aBcDeF999").hasMatch());
}
QTEST_MAIN(TestMainWindow)
#include "test_main.moc"
@@ -0,0 +1,48 @@
#include <QtTest/QtTest>
#include <QApplication>
#include "MainWindow.h"
class TestMainWindowWidget : public QObject
{
Q_OBJECT
private slots:
void testMainWindowCreation();
void testWindowTitle();
void testValidateInputMethod();
void testGetScreenInfo();
};
void TestMainWindowWidget::testMainWindowCreation()
{
MainWindow window;
QVERIFY(!window.windowTitle().isEmpty());
}
void TestMainWindowWidget::testWindowTitle()
{
MainWindow window;
QCOMPARE(window.windowTitle(), u"Qt6 Legacy Migration Benchmark"_s);
}
void TestMainWindowWidget::testValidateInputMethod()
{
MainWindow window;
QVERIFY(window.validateInput(u"Test123"_s));
QVERIFY(window.validateInput(u"abc456"_s));
QVERIFY(!window.validateInput(u"123Test"_s));
QVERIFY(!window.validateInput(u""_s));
}
void TestMainWindowWidget::testGetScreenInfo()
{
MainWindow window;
QString info = window.getScreenInfo();
QVERIFY(!info.isEmpty());
QVERIFY(info.contains(u"Screen:"_s));
}
QTEST_MAIN(TestMainWindowWidget)
#include "test_mainwindow.moc"