diff --git a/.github/workflows/code_coverage.yml b/.github/workflows/code_coverage.yml new file mode 100644 index 0000000..bf657d6 --- /dev/null +++ b/.github/workflows/code_coverage.yml @@ -0,0 +1,97 @@ +name: Unit Tests and Code Coverage + +on: + push: + branches: [ "main" ] + + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-22.04 +# 22 has gcc 11 and clang 14 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get install -y cmake ninja-build \ + libxrandr-dev libxinerama-dev \ + libxcursor-dev libxi-dev libgl1-mesa-dev \ + gcovr lcov + +# Do we need a very modern compiler? +# - name: Install clang18 +# run: | +# wget https://apt.llvm.org/llvm.sh +# chmod a+x llvm.sh +# echo ""|sudo ./llvm.sh 18 + + - name: Create build directory + run: | + mkdir build + du -hs build + +# This can be used to login to the runner and debug +# - name: Setup tmate session +# uses: mxschmitt/action-tmate@v3 + + - name: Configure CMake + run: | + # The following settings are needed for clang-18 + #export CC=/usr/bin/clang-18 + #export CXX=/usr/bin/clang++-18 + #export CXXFLAGS="-std=c++20" + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug \ + -DENABLE_GEOM2D_UNIT_TESTS=ON \ + -DBUILD_GMOCK=OFF \ + -DINSTALL_GTEST=OFF + + - name: Build with Ninja + run: ninja -C build + + - name: Run tests + run: | + ninja -C build test + + - name: Configure lcov to capture branch coverage + run: | + cp /etc/lcovrc /dev/shm + cat /dev/shm/lcovrc |sed 's/branch_coverage = 0/branch_coverage = 1/g' > ~/.lcovrc + + - name: Collect coverage + run: | + ninja -C build cov + +# The following article describes how to publish code coverage to codecov.io: +# https://dev.to/askrodney/cmake-coverage-example-with-github-actions-and-codecovio-5bjp +# I can't get it to work, though. We can just download the html, for now. +# - name: Generate JSON coverage report +# working-directory: ./build +# run: | +# gcovr -r .. . --branches --cobertura > coverage.xml + + - name: Upload build artifact + uses: actions/upload-artifact@v3 + with: + name: TEST_Geometry2D_linux_amd64_artifact1 + path: build/bin/TEST_Geometry2D + + - name: Publish coverage artifact + uses: actions/upload-artifact@v3 + with: + name: code-coverage + path: build/coverage + + + # - name: Upload Release Asset + # uses: actions/upload-release-asset@v1 + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # with: + # upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing its `id` + # asset_path: ./build/ryzen_mon_glgui + # asset_name: ryzen_mon_glgui_artifact1.zip # Name of the asset to display on the release page + # asset_content_type: application/zip \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f093044..93f7e8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,3 +175,43 @@ if (EXISTS "${CMAKE_SOURCE_DIR}/${C_CXX_HEADERS_DIR}" AND IS_DIRECTORY "${CMAKE_ endif (EXISTS "${CMAKE_SOURCE_DIR}/${C_CXX_HEADERS_DIR}" AND IS_DIRECTORY "${CMAKE_SOURCE_DIR}/${C_CXX_HEADERS_DIR}") include_directories("${CMAKE_SOURCE_DIR}/third_party") + + +# Define an option to enable/disable unit tests +option(ENABLE_GEOM2D_UNIT_TESTS "Enable unit tests for /olcUTIL_Geometry2D" OFF) + +# unit tests +# I use this with: cmake -G Ninja -DBUILD_GMOCK=OFF -DINSTALL_GTEST=OFF -DENABLE_GEOM2D_UNIT_TESTS=ON .. +if(ENABLE_GEOM2D_UNIT_TESTS) + enable_testing() + + # Code Coverage + set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${PROJECT_SOURCE_DIR}/cmake") + include(coverage) + # the following pattern excludes all tests from coverage report + add_coverage_target("*/tests/*") + + # Fetch Google Test + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.14.0 + ) + FetchContent_MakeAvailable(googletest) + + add_executable(unit_tests + tests/test_circle.cpp # TODO: use cmake to find all test files + ) + + # Disable optimization for coverage + # TODO: only tested with gcc + target_compile_options(unit_tests PRIVATE + -O0 -fprofile-arcs -ftest-coverage --coverage + ) + target_link_options(unit_tests PRIVATE --coverage) + + target_link_libraries(unit_tests gtest_main) + + add_test(NAME unit_tests COMMAND unit_tests) +endif() diff --git a/cmake/coverage.cmake b/cmake/coverage.cmake new file mode 100644 index 0000000..8ecfd61 --- /dev/null +++ b/cmake/coverage.cmake @@ -0,0 +1,51 @@ +function(add_coverage_target exclude) + + find_program(GCOV gcov) + if (NOT GCOV) + message(WARNING "program gcov not found") + endif() + + find_program(LCOV lcov) + if (NOT LCOV) + message(WARNING "program lcov not found") + endif() + + find_program(GENHTML genhtml) + if (NOT GENHTML) + message(WARNING "program genhtml not found") + endif() + + if (LCOV AND GCOV AND GENHTML) + # the following link describes how to write unit tests for header only libraries + # https://stackoverflow.com/questions/9666800/getting-useful-gcov-results-for-header-only-libraries + set(covname cov.info) + # the original file set all compile options here, but I prefer to set them in the CMakeLists.txt + #add_compile_options(-fprofile-arcs -ftest-coverage) + #add_link_options(--coverage) + add_custom_target(cov DEPENDS ${covname}) + + # lcov on ubuntu 22 doesn't support --branch-coverage + set(COV_OPT + "" # i modify /etc/lcovrc in the github action + #"--branch-coverage" + #"--rc lcov_branch_coverage=1" # this doesn't work with -l option + ) + # option for genhtml --rc genhtml_branch_coverage=1 + + add_custom_command( + OUTPUT ${covname} + COMMAND ${LCOV} -c -o ${covname} ${COV_OPT} -d . -b . --gcov-tool ${GCOV} #--ignore-errors mismatch + # In the following I explicitly exclude system headers from the coverage report + COMMAND ${LCOV} -r ${covname} ${COV_OPT} -o ${covname} ${exclude} "\*/googletest/\*" "\*/g++\*/bits/\*" "'*/g++-v13/*'" + COMMAND ${LCOV} -l ${covname} ${COV_OPT} + COMMAND ${GENHTML} ${covname} --rc lcov_branch_coverage=1 -output coverage + COMMAND ${LCOV} -l ${covname} ${COV_OPT} 2>/dev/null | grep Total | sed 's/|//g' | sed 's/Total://g' | awk '{print $1}' | sed s/%//g > coverage/total + ) + set_directory_properties(PROPERTIES + ADDITIONAL_CLEAN_FILES ${covname} + ) + else() + message(WARNING "unable to add target `cov`: missing coverage tools") + endif() + +endfunction() \ No newline at end of file diff --git a/tests/test_circle.cpp b/tests/test_circle.cpp new file mode 100644 index 0000000..b0b0cac --- /dev/null +++ b/tests/test_circle.cpp @@ -0,0 +1,54 @@ +#include +#include "../olcUTIL_Geometry2D.h" + +using namespace olc::utils::geom2d; + +class CircleTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Initialize the circle + c = circle({{0.0f, 0.0f}, 10.0f}); + } + + circle c; +}; + +// The test name consists of the module that is tested, the scenario under which it is tested and the expected behavior + +TEST_F(CircleTest, circle_contains_PointOnCircleBoundary_True) +{ + // Create a point on the circle boundary + olc::v_2d p(0.0f, 10.0f); + + // Check if the circle contains the point + bool result = contains(c, p); + + // Assert that the result is true + ASSERT_TRUE(result); +} + +TEST_F(CircleTest, circleContains_PointIsInside_True) +{ + // Create a point inside the circle + olc::v_2d p(1.0f, 1.0f); + + // Check if the circle contains the point + bool result = contains(c, p); + + // Assert that the result is true + ASSERT_TRUE(result); +} + +TEST_F(CircleTest, circleContains_PointIsOutside_False) +{ + // Create a point outside the circle + olc::v_2d p(12.0f, 12.0f); + + // Check if the circle contains the point + bool result = contains(c, p); + + // Assert that the result is false + ASSERT_FALSE(result); +}