Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca89560d47 | ||
|
|
1cabf81f7c |
No files matched your search
@@ -1,144 +0,0 @@
|
||||
# Add LCOV Coverage for a Qt5-to-Qt6 Migration Project
|
||||
|
||||
Work in the supplied Qt5-to-Qt6 migration repository. Inspect the actual repository layout, CMake targets, tests, and `Jenkinsfile` before making changes. Do not assume target names or paths.
|
||||
|
||||
## Goal
|
||||
|
||||
Add reliable LCOV coverage generation for the Qt6 project so Jenkins Coverage Plugin can consume:
|
||||
|
||||
```text
|
||||
coverage/lcov.info
|
||||
```
|
||||
|
||||
## Repository-specific Jenkins CI integration
|
||||
|
||||
The repository contains Jenkins-related CI support files in `./ci/`. Before changing the build or pipeline, inspect and understand all of the following files:
|
||||
|
||||
```text
|
||||
ci/allowed_signers
|
||||
ci/check_pr_policies.py
|
||||
ci/Dockerfile.ci
|
||||
```
|
||||
|
||||
Analyze how these files are used by Jenkins, including the CI image, installed tools and versions, signing or trust configuration, pull-request policy checks, workspace-relative paths, user permissions, and any commands or environment variables that affect builds and tests. Adapt the coverage implementation to the existing conventions instead of bypassing them. In particular:
|
||||
|
||||
- ensure `lcov`, `genhtml`, Qt6, CMake, CTest, and any required Python/runtime dependencies are available in the Jenkins CI image or document the required image changes;
|
||||
- preserve and integrate existing policy, signing, and verification checks;
|
||||
- use the existing Jenkins container/tooling invocation and workspace layout;
|
||||
- do not weaken, remove, or bypass checks from `ci/check_pr_policies.py` or trust rules from `ci/allowed_signers`;
|
||||
- update `ci/Dockerfile.ci` and related CI configuration when needed so the coverage stages work reproducibly;
|
||||
- verify that coverage artifacts remain workspace-relative and are available to later Jenkins stages;
|
||||
- document any CI-file changes and explain how they support the Jenkins coverage workflow.
|
||||
|
||||
If any of these files are absent, inaccessible, or not actually referenced by the current Jenkins setup, report that explicitly and do not invent their contents or usage.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Do not modify the Qt5 baseline under `qt5-before`.
|
||||
- Preserve normal non-coverage builds.
|
||||
- Add an optional CMake coverage mode, disabled by default, for GCC and Clang where supported.
|
||||
- Use debug symbols and suitable low-optimization coverage flags.
|
||||
- Ensure Qt6 tests are registered with CTest and run with:
|
||||
|
||||
```bash
|
||||
ctest --test-dir <build-dir> --output-on-failure
|
||||
```
|
||||
|
||||
- Add an executable script, preferably:
|
||||
|
||||
```text
|
||||
scripts/collect-coverage.sh
|
||||
```
|
||||
|
||||
The script must:
|
||||
|
||||
- accept an optional build directory, defaulting to `build/coverage`;
|
||||
- check that `lcov` is installed;
|
||||
- optionally require `genhtml` when generating HTML;
|
||||
- create `coverage/lcov.info`;
|
||||
- optionally create `coverage/html/index.html`;
|
||||
- include only project source files;
|
||||
- exclude Qt, system, compiler, build, and external dependency paths;
|
||||
- use safe error handling such as `set -euo pipefail`;
|
||||
- fail on missing, empty, or invalid coverage data;
|
||||
- validate that the tracefile contains valid LCOV records.
|
||||
|
||||
- Update `Jenkinsfile` with explicit stages for:
|
||||
|
||||
1. configuring the coverage build;
|
||||
2. compiling;
|
||||
3. running CTest;
|
||||
4. collecting LCOV data;
|
||||
5. validating and archiving coverage artifacts;
|
||||
6. publishing coverage with the Jenkins Coverage Plugin.
|
||||
|
||||
- Configure the plugin to read exactly:
|
||||
|
||||
```text
|
||||
coverage/lcov.info
|
||||
```
|
||||
|
||||
Prefer `recordCoverage` with the LCOV parser when compatible with the existing Jenkins setup. Inspect the current `Jenkinsfile` and follow its conventions.
|
||||
|
||||
- The Jenkins build must fail when:
|
||||
- a test fails;
|
||||
- LCOV generation fails;
|
||||
- `coverage/lcov.info` is missing, empty, or invalid.
|
||||
|
||||
- Use workspace-relative paths only.
|
||||
- Preserve generated coverage output outside source control; update `.gitignore` if necessary.
|
||||
- Do not commit secrets or fake coverage results.
|
||||
- Update the documentation with:
|
||||
- required Qt6, CMake, CTest, `lcov`, and `genhtml` versions;
|
||||
- Debian/Ubuntu installation instructions;
|
||||
- local build, test, and coverage commands;
|
||||
- Jenkins Coverage Plugin requirements;
|
||||
- artifact locations;
|
||||
- the role of `ci/allowed_signers`, `ci/check_pr_policies.py`, and `ci/Dockerfile.ci` in the Jenkins workflow.
|
||||
|
||||
Document and verify this workflow:
|
||||
|
||||
```bash
|
||||
rm -rf build/coverage coverage
|
||||
|
||||
cmake -S qt6-after/qt6-migrated-benchmark \
|
||||
-B build/coverage \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DENABLE_COVERAGE=ON
|
||||
|
||||
cmake --build build/coverage
|
||||
|
||||
ctest --test-dir build/coverage \
|
||||
--output-on-failure
|
||||
|
||||
./scripts/collect-coverage.sh build/coverage
|
||||
```
|
||||
|
||||
Expected files:
|
||||
|
||||
```text
|
||||
coverage/lcov.info
|
||||
coverage/html/index.html
|
||||
```
|
||||
|
||||
Verify, where possible:
|
||||
|
||||
- normal configuration and build;
|
||||
- coverage configuration and build;
|
||||
- CTest execution;
|
||||
- LCOV generation;
|
||||
- LCOV validation;
|
||||
- HTML report generation;
|
||||
- ShellCheck for new shell scripts;
|
||||
- the Jenkins CI image and `./ci/` policy/signing checks relevant to the coverage stages.
|
||||
|
||||
At the end, report:
|
||||
|
||||
- changed files, including any files under `ci/`;
|
||||
- coverage-enabled CMake targets;
|
||||
- exact LCOV and HTML report paths;
|
||||
- Jenkins publication stage;
|
||||
- how the `./ci/` files were analyzed and adapted;
|
||||
- successful checks;
|
||||
- checks skipped because dependencies were unavailable.
|
||||
|
||||
@@ -2,8 +2,3 @@
|
||||
.*~
|
||||
*~
|
||||
build
|
||||
build-coverage
|
||||
coverage/
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
Vendored
+50
-134
@@ -1,5 +1,11 @@
|
||||
pipeline {
|
||||
agent none
|
||||
agent {
|
||||
docker {
|
||||
image 'lifemaps/ci-cpp:latest'
|
||||
reuseNode true
|
||||
args '-u root:root'
|
||||
}
|
||||
}
|
||||
|
||||
options {
|
||||
timestamps()
|
||||
@@ -9,139 +15,65 @@ pipeline {
|
||||
environment {
|
||||
PROJECT_DIR = "qt6-after/qt6-migrated-benchmark"
|
||||
BUILD_DIR = "build"
|
||||
COVERAGE_BUILD_DIR = "build-coverage"
|
||||
CHANGE_TARGET = "${env.CHANGE_TARGET ?: 'origin/main'}"
|
||||
QT_QPA_PLATFORM = "offscreen"
|
||||
LANG = "C.UTF-8"
|
||||
LC_ALL = "C.UTF-8"
|
||||
DOCKER_IMAGE = "lifemaps/ci-cpp:latest"
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Docker Image') {
|
||||
agent any
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
script {
|
||||
echo "Checking if Docker image exists locally..."
|
||||
def imageExists = sh(script: "docker inspect ${DOCKER_IMAGE} >/dev/null 2>&1", returnStatus: true) == 0
|
||||
if (!imageExists) {
|
||||
echo "Docker image not found. Building from ci/Dockerfile.ci..."
|
||||
sh 'docker build -t ${DOCKER_IMAGE} -f ci/Dockerfile.ci .'
|
||||
} else {
|
||||
echo "Docker image ${DOCKER_IMAGE} found locally."
|
||||
}
|
||||
}
|
||||
checkout scm
|
||||
sh 'git fetch --all --prune'
|
||||
sh 'git submodule update --init --recursive || true'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build and Test') {
|
||||
agent {
|
||||
docker {
|
||||
image 'lifemaps/ci-cpp:latest'
|
||||
reuseNode true
|
||||
}
|
||||
stage('Tool Versions') {
|
||||
steps {
|
||||
sh '''
|
||||
python3 --version
|
||||
cmake --version
|
||||
ninja --version
|
||||
g++ --version
|
||||
'''
|
||||
}
|
||||
stages {
|
||||
stage('Cleanup') {
|
||||
steps {
|
||||
script {
|
||||
echo "Cleaning workspace to avoid corruption issues..."
|
||||
sh '''
|
||||
if [ -d "${WORKSPACE}/${BUILD_DIR}" ]; then
|
||||
echo "Removing build directory with permission fix..."
|
||||
chmod -R u+w "${WORKSPACE}/${BUILD_DIR}" 2>/dev/null || true
|
||||
rm -rf "${WORKSPACE}/${BUILD_DIR}"
|
||||
fi
|
||||
if [ -d "${WORKSPACE}/${COVERAGE_BUILD_DIR}" ]; then
|
||||
echo "Removing coverage build directory..."
|
||||
chmod -R u+w "${WORKSPACE}/${COVERAGE_BUILD_DIR}" 2>/dev/null || true
|
||||
rm -rf "${WORKSPACE}/${COVERAGE_BUILD_DIR}"
|
||||
fi
|
||||
if [ -d "${WORKSPACE}/coverage" ]; then
|
||||
echo "Removing coverage output directory..."
|
||||
chmod -R u+w "${WORKSPACE}/coverage" 2>/dev/null || true
|
||||
rm -rf "${WORKSPACE}/coverage"
|
||||
fi
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
sh 'git fetch --all --prune'
|
||||
sh 'git submodule update --init --recursive || true'
|
||||
}
|
||||
}
|
||||
stage('Qt Diagnostics') {
|
||||
steps {
|
||||
sh '''
|
||||
echo "QT_QPA_PLATFORM=$QT_QPA_PLATFORM"
|
||||
echo "LANG=$LANG"
|
||||
echo "LC_ALL=$LC_ALL"
|
||||
dpkg -l | grep -E "qt6-base-dev|qtbase5-dev|qt6-tools" || true
|
||||
pkg-config --list-all | grep -i qt || true
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Tool Versions') {
|
||||
steps {
|
||||
sh '''
|
||||
python3 --version
|
||||
cmake --version
|
||||
ninja --version
|
||||
g++ --version
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Policy Checks') {
|
||||
steps {
|
||||
sh 'python3 ci/check_pr_policies.py'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Qt Diagnostics') {
|
||||
steps {
|
||||
sh '''
|
||||
echo "QT_QPA_PLATFORM=$QT_QPA_PLATFORM"
|
||||
echo "LANG=$LANG"
|
||||
echo "LC_ALL=$LC_ALL"
|
||||
dpkg -l | grep -E "qt6-base-dev|qtbase5-dev|qt6-tools" || true
|
||||
pkg-config --list-all | grep -i qt || true
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Configure') {
|
||||
steps {
|
||||
sh 'cmake -S ${PROJECT_DIR} -B ${BUILD_DIR} -G Ninja -DCMAKE_BUILD_TYPE=Debug'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Policy Checks') {
|
||||
steps {
|
||||
sh 'python3 ci/check_pr_policies.py'
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'cmake --build ${BUILD_DIR} --parallel'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Configure') {
|
||||
steps {
|
||||
sh 'cmake -S ${PROJECT_DIR} -B ${BUILD_DIR} -G Ninja -DCMAKE_BUILD_TYPE=Debug'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'cmake --build ${BUILD_DIR} --parallel'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh 'QT_QPA_PLATFORM=offscreen LANG=C.UTF-8 LC_ALL=C.UTF-8 ctest --test-dir ${BUILD_DIR} --output-on-failure'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Coverage') {
|
||||
when {
|
||||
expression {
|
||||
def branch = env.CHANGE_BRANCH ?: env.BRANCH_NAME
|
||||
return branch ==~ /main|master|coverage.*|feature.*/
|
||||
}
|
||||
}
|
||||
steps {
|
||||
echo "Running coverage collection..."
|
||||
sh '''
|
||||
${PROJECT_DIR}/scripts/collect-coverage.sh ${WORKSPACE}/${COVERAGE_BUILD_DIR}
|
||||
'''
|
||||
}
|
||||
post {
|
||||
always {
|
||||
recordCoverage tools: [[parser: 'LCOV', pattern: "${PROJECT_DIR}/coverage/lcov.info"]],
|
||||
sourceCodeRetention: 'LAST_BUILD'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Test') {
|
||||
steps {
|
||||
sh 'QT_QPA_PLATFORM=offscreen LANG=C.UTF-8 LC_ALL=C.UTF-8 ctest --test-dir ${BUILD_DIR} --output-on-failure'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,23 +82,7 @@ pipeline {
|
||||
always {
|
||||
script {
|
||||
if (getContext(hudson.FilePath)) {
|
||||
sh '''
|
||||
if [ -d "${WORKSPACE}/${BUILD_DIR}" ]; then
|
||||
echo "Final cleanup: Removing build directory..."
|
||||
chmod -R u+w "${WORKSPACE}/${BUILD_DIR}" 2>/dev/null || true
|
||||
rm -rf "${WORKSPACE}/${BUILD_DIR}"
|
||||
fi
|
||||
if [ -d "${WORKSPACE}/${COVERAGE_BUILD_DIR}" ]; then
|
||||
echo "Final cleanup: Removing coverage build directory..."
|
||||
chmod -R u+w "${WORKSPACE}/${COVERAGE_BUILD_DIR}" 2>/dev/null || true
|
||||
rm -rf "${WORKSPACE}/${COVERAGE_BUILD_DIR}"
|
||||
fi
|
||||
if [ -d "${WORKSPACE}/coverage" ]; then
|
||||
echo "Final cleanup: Removing coverage output..."
|
||||
chmod -R u+w "${WORKSPACE}/coverage" 2>/dev/null || true
|
||||
rm -rf "${WORKSPACE}/coverage"
|
||||
fi
|
||||
'''
|
||||
cleanWs deleteDirs: true, disableDeferredWipeout: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-9
@@ -2,10 +2,6 @@ FROM ubuntu:26.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Create jenkins user (UID 1001 to avoid conflicts with existing users)
|
||||
RUN groupadd -g 1001 jenkins && \
|
||||
useradd -u 1001 -g 1001 -m -s /bin/bash jenkins
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
python3 \
|
||||
@@ -22,8 +18,7 @@ RUN apt-get update && \
|
||||
qt6-base-dev \
|
||||
qt6-base-dev-tools \
|
||||
qt6-tools-dev \
|
||||
qt6-tools-dev-tools \
|
||||
lcov && \
|
||||
qt6-tools-dev-tools && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set Qt platform for headless testing
|
||||
@@ -33,6 +28,3 @@ ENV QT_QPA_PLATFORM=offscreen
|
||||
RUN git config --global --add safe.directory "*"
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
# Switch to non-root user
|
||||
USER jenkins
|
||||
+1
-2
@@ -1,3 +1,2 @@
|
||||
torsten.wierschin@life-maps.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL8degz9DpjybBeBrdvMc50ajsi+fioRETtE59tyzKO+
|
||||
torsten.wierschin@life-maps.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO+c+9jJKrLpTk4pLg+p1Qo0CjCYzfKfEhXEu0MAlZpj
|
||||
nkana@outlook.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBR68AzU7UtY+NJPIRloEeIlQIZIP3xhvkWB8BediMeQ
|
||||
torsten.wierschin@life-maps.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMV8yH39fMOQE5BeCsxI8RMUXbDBJvRuS5phTTfrXF0H
|
||||
Whitespace-only changes.
@@ -10,10 +10,7 @@ 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 Test)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
|
||||
qt_standard_project_setup(REQUIRES 6.5)
|
||||
|
||||
@@ -31,69 +28,13 @@ target_link_libraries(Qt6MigratedBenchmark PRIVATE
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
# 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()
|
||||
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)
|
||||
endif()
|
||||
|
||||
# Enable testing
|
||||
# Add tests
|
||||
enable_testing()
|
||||
|
||||
# Add test executable
|
||||
add_executable(Qt6MigratedBenchmarkTest
|
||||
tests/test_mainwindow.cpp
|
||||
MainWindow.h
|
||||
MainWindow.cpp
|
||||
)
|
||||
|
||||
target_include_directories(Qt6MigratedBenchmarkTest PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
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)
|
||||
add_subdirectory(tests)
|
||||
@@ -1,241 +0,0 @@
|
||||
# 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/)
|
||||
@@ -4,20 +4,20 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPixmap>
|
||||
#include <QRect>
|
||||
#include <QScreen>
|
||||
#include <QString>
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent)
|
||||
: QMainWindow(parent), ui(new Ui::MainWindow), m_pattern(QRegularExpression(R"(^[A-Za-z]+\d+$)"))
|
||||
: QMainWindow(parent), ui(new Ui::MainWindow), pattern(QRegularExpression(R"(^[A-Za-z]+\d+$)"))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowTitle(QStringLiteral("Qt6 Legacy Migration Benchmark"));
|
||||
|
||||
ui->label->setText(QStringLiteral("Bitte Text wie Test123 eingeben."));
|
||||
ui->iconLabel->setPixmap(QPixmap(QStringLiteral(":/icons/app.png")).scaled(48, 48));
|
||||
// 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));
|
||||
|
||||
// Modern function-pointer connect syntax
|
||||
connect(ui->validateButton, &QPushButton::clicked, this, &MainWindow::onValidateClicked);
|
||||
connect(ui->screenButton, &QPushButton::clicked, this, &MainWindow::onScreenInfoClicked);
|
||||
}
|
||||
@@ -27,38 +27,29 @@ 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 QStringLiteral("Screen: unavailable");
|
||||
}
|
||||
const QRect geometry = screen->geometry();
|
||||
return QStringLiteral("Screen: %1x%2").arg(geometry.width()).arg(geometry.height());
|
||||
}
|
||||
|
||||
void MainWindow::onValidateClicked()
|
||||
{
|
||||
const QString text = ui->lineEdit->text();
|
||||
|
||||
if (validateInput(text))
|
||||
if (pattern.match(text).hasMatch())
|
||||
{
|
||||
ui->resultLabel->setText(QStringLiteral("Match"));
|
||||
ui->resultLabel->setText(QString::fromUtf8("Match"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->resultLabel->setText(QStringLiteral("No match"));
|
||||
ui->resultLabel->setText(QString::fromUtf8("No match"));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onScreenInfoClicked()
|
||||
{
|
||||
const QString info = getScreenInfo();
|
||||
ui->resultLabel->setText(info);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@@ -18,16 +19,13 @@ class MainWindow : public QMainWindow
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget* parent = nullptr);
|
||||
~MainWindow();
|
||||
~MainWindow() override;
|
||||
|
||||
[[nodiscard]] bool validateInput(const QString& text) const;
|
||||
[[nodiscard]] QString getScreenInfo() const;
|
||||
|
||||
public slots:
|
||||
private slots:
|
||||
void onValidateClicked();
|
||||
void onScreenInfoClicked();
|
||||
|
||||
private:
|
||||
Ui::MainWindow* ui;
|
||||
QRegularExpression m_pattern;
|
||||
QRegularExpression pattern;
|
||||
};
|
||||
@@ -1,12 +1,19 @@
|
||||
#include "MainWindow.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QScreen>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
#if __has_include(<print>)
|
||||
#include <print>
|
||||
#endif
|
||||
|
||||
[[nodiscard]] auto main(int argc, char* argv[]) -> int
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
QApplication app{argc, argv};
|
||||
|
||||
return a.exec();
|
||||
// Qt6 uses UTF-8 by default; no codec setup needed
|
||||
MainWindow window;
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/icons">
|
||||
<file>app.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
<qresource prefix="/icons">
|
||||
<file>app.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -1,103 +0,0 @@
|
||||
#!/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,unused \
|
||||
--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.
@@ -0,0 +1,33 @@
|
||||
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)
|
||||
@@ -0,0 +1,75 @@
|
||||
#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"
|
||||
@@ -0,0 +1,63 @@
|
||||
#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"
|
||||
@@ -1,88 +0,0 @@
|
||||
#include "MainWindow.h"
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class TestMainWindow : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void testValidateInputValid();
|
||||
void testValidateInputInvalid();
|
||||
void testValidateInputEmpty();
|
||||
void testValidateInputMixedCase();
|
||||
void testMainWindowCreation();
|
||||
void testWindowTitle();
|
||||
void testValidateInputMethod();
|
||||
void testGetScreenInfo();
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
void TestMainWindow::testMainWindowCreation()
|
||||
{
|
||||
MainWindow window;
|
||||
QVERIFY(!window.windowTitle().isEmpty());
|
||||
}
|
||||
|
||||
void TestMainWindow::testWindowTitle()
|
||||
{
|
||||
MainWindow window;
|
||||
QCOMPARE(window.windowTitle(), QStringLiteral("Qt6 Legacy Migration Benchmark"));
|
||||
}
|
||||
|
||||
void TestMainWindow::testValidateInputMethod()
|
||||
{
|
||||
MainWindow window;
|
||||
|
||||
QVERIFY(window.validateInput(QStringLiteral("Test123")));
|
||||
QVERIFY(window.validateInput(QStringLiteral("abc456")));
|
||||
QVERIFY(!window.validateInput(QStringLiteral("123Test")));
|
||||
QVERIFY(!window.validateInput(QStringLiteral("")));
|
||||
}
|
||||
|
||||
void TestMainWindow::testGetScreenInfo()
|
||||
{
|
||||
MainWindow window;
|
||||
QString info = window.getScreenInfo();
|
||||
QVERIFY(!info.isEmpty());
|
||||
QVERIFY(info.contains(QStringLiteral("Screen:")));
|
||||
}
|
||||
|
||||
QTEST_MAIN(TestMainWindow)
|
||||
|
||||
#include "test_mainwindow.moc"
|
||||
Reference in new issue
Block a user