Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 25 additions & 35 deletions .github/workflows/sonarcloud.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,20 @@ jobs:
sonarcloud:
name: SonarCloud
runs-on: ubuntu-24.04
container: silkeh/clang:18
env:
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis

- name: Install gcovr
run: pip install gcovr==8.2

- name: Install sonar-scanner and build-wrapper
uses: SonarSource/sonarcloud-github-c-cpp@v3
- name: Install Prerequisites
run: |
apt-get update
apt-get install -y \
ccache \
unzip

- name: Install CMake
uses: lukka/get-cmake@v3.30.5
Expand All @@ -53,63 +55,51 @@ jobs:
-GNinja \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTS=ON \
-DCI_BUILD=ON \
-DENABLE_COVERAGE=ON \
-DENABLE_CLANG_TIDY=OFF

- name: Build the code in debug mode
- name: Build the code
timeout-minutes: 30
run: cmake --build build/ --config Debug
run: cmake --build build/ --config Release

- name: Run tests to generate coverage statistics
timeout-minutes: 10
working-directory: build
run: ninja run_all_tests -j1 -k 0
run: ninja coverage -k 0

- name: Test Summary
if: ${{ !cancelled() }}
uses: test-summary/action@v2
with:
paths: "build/reports/tests/*.junit.xml"

- name: Collect coverage into one XML report
if: ${{ !cancelled() }}
run: |
gcovr \
--root . \
--object-directory build \
--force-color \
--no-markers \
--decisions \
--calls \
--exclude-noncode-lines \
--gcov-ignore-parse-errors negative_hits.warn \
--sonarqube "coverage.xml"

- name: Upload coverage report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: coverage-sonar
path: coverage.xml
name: coverage
path: build/reports/coverage.txt
retention-days: 1 # This sets the artifact TTL to 1 day (minimum is 1 day)
overwrite: true

- name: Run sonar-scanner
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v4
if: ${{ !cancelled() }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
sonar-scanner \
--define sonar.projectKey=Fastcode_NUClear \
--define sonar.organization=fastcode \
--define sonar.sources=src \
--define sonar.tests=tests \
--define sonar.cfamily.compile-commands=build/compile_commands.json \
--define sonar.coverageReportPaths=coverage.xml
with:
args: >
--define sonar.projectKey=Fastcode_NUClear
--define sonar.organization=fastcode
--define sonar.sources=src
--define sonar.tests=tests
--define sonar.cfamily.compile-commands=build/compile_commands.json
--define sonar.cfamily.llvm-cov.reportPath=build/reports/coverage.txt

- name: Upload Traces
if: ${{ !cancelled() }}
Expand Down
57 changes: 50 additions & 7 deletions cmake/TestRunner.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,68 @@ get_all_catch_test_targets(all_targets ${PROJECT_SOURCE_DIR})

# Create a custom command for each test target to run it
# Make sure that coverage data is written with paths relative to the source directory
unset(reports)
unset(report_outputs)
unset(coverage_reports)
foreach(target ${all_targets})

set(sonarqube_report_file "${PROJECT_BINARY_DIR}/reports/tests/${target}.sonarqube.xml")
unset(command_prefix)
if(ENABLE_COVERAGE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Extra output files for the profile data
set(raw_coverage "${PROJECT_BINARY_DIR}/reports/tests/${target}.profraw")
set(indexed_coverage "${PROJECT_BINARY_DIR}/reports/tests/${target}.profdata")
set(coverage_report "${PROJECT_BINARY_DIR}/reports/tests/${target}.txt")
set(command_prefix "cmake" "-E" "env" "LLVM_PROFILE_FILE=${raw_coverage}")
list(APPEND coverage_reports ${coverage_report})
endif()

set(junit_report_file "${PROJECT_BINARY_DIR}/reports/tests/${target}.junit.xml")
list(APPEND reports ${sonarqube_report_file} ${junit_report_file})
list(APPEND report_outputs ${junit_report_file})
add_custom_command(
OUTPUT ${sonarqube_report_file} ${junit_report_file}
COMMAND $<TARGET_FILE:${target}> --reporter console --reporter SonarQube::out=${sonarqube_report_file} --reporter
JUnit::out=${junit_report_file}
OUTPUT ${junit_report_file} ${raw_coverage}
COMMAND ${command_prefix} $<TARGET_FILE:${target}> --reporter console --reporter JUnit::out=${junit_report_file}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${target}
USES_TERMINAL
COMMENT "Running test ${target}"
)

if(ENABLE_COVERAGE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_custom_command(
OUTPUT ${indexed_coverage}
COMMAND llvm-profdata merge -sparse ${raw_coverage} -o ${indexed_coverage}
DEPENDS ${raw_coverage}
)

add_custom_command(
OUTPUT ${coverage_report}
COMMAND llvm-cov show --show-branches=count -Xdemangler c++filt -instr-profile=${indexed_coverage}
$<TARGET_FILE:${target}> > ${coverage_report}
DEPENDS ${indexed_coverage}
)
endif()

endforeach()

# Create a custom target that depends on all test targets
add_custom_target(
run_all_tests
DEPENDS ${reports}
DEPENDS ${report_outputs} ${all_targets}
COMMENT "Running all Catch2 tests"
)

# Concatenate all the coverage reports together
if(ENABLE_COVERAGE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")

add_custom_command(
OUTPUT ${PROJECT_BINARY_DIR}/reports/coverage.txt
COMMAND cat ${coverage_reports} > ${PROJECT_BINARY_DIR}/reports/coverage.txt
DEPENDS ${coverage_reports}
)

add_custom_target(
coverage
DEPENDS ${PROJECT_BINARY_DIR}/reports/coverage.txt
COMMENT "Running all Catch2 tests with coverage"
)

endif()
15 changes: 11 additions & 4 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,18 @@ target_compile_features(nuclear PUBLIC cxx_std_14)

option(ENABLE_COVERAGE "Compile with coverage support enabled.")
if(ENABLE_COVERAGE)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(WARNING "Coverage is enabled but not build in debug mode. Coverage results may be misleading.")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(WARNING "Coverage is enabled but not build in debug mode. Coverage results may be misleading.")
endif()
target_compile_options(nuclear PUBLIC -fprofile-arcs -ftest-coverage -fprofile-abs-path -fprofile-update=atomic)
target_link_options(nuclear PUBLIC -fprofile-arcs)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(nuclear PUBLIC -fprofile-instr-generate -fcoverage-mapping)
target_link_options(nuclear PUBLIC -fprofile-instr-generate)
else()
message(FATAL_ERROR "Coverage is not supported for the current compiler.")
endif()
target_compile_options(nuclear PUBLIC -fprofile-arcs -ftest-coverage -fprofile-abs-path -fprofile-update=atomic)
target_link_options(nuclear PUBLIC -fprofile-arcs)
endif(ENABLE_COVERAGE)

# Enable warnings, and all warnings are errors
Expand Down
Loading