From f03b89e06808ca21975bc071162251fca917f854 Mon Sep 17 00:00:00 2001 From: mwaxmonsky <137746677+mwaxmonsky@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:41:27 -0400 Subject: [PATCH] Add Initial Unit Testing Infrastructure (#1) Adds pFUnit based unit test examples and associated CMake integration. Also adds CI job to build a test environment and run the unit tests. --- .github/workflows/code-coverage.yml | 65 +++++++++++++++++++++++++++++ CMakeLists.txt | 27 ++++++++++++ src/CMakeLists.txt | 14 +++++++ src/hash/CMakeLists.txt | 6 +++ src/util/CMakeLists.txt | 5 +++ test/CMakeLists.txt | 11 +++++ test/test_hash_table.pf | 35 ++++++++++++++++ test/test_hashable.pf | 31 ++++++++++++++ 8 files changed, 194 insertions(+) create mode 100644 .github/workflows/code-coverage.yml create mode 100644 CMakeLists.txt create mode 100644 src/CMakeLists.txt create mode 100644 src/hash/CMakeLists.txt create mode 100644 src/util/CMakeLists.txt create mode 100644 test/CMakeLists.txt create mode 100644 test/test_hash_table.pf create mode 100644 test/test_hashable.pf diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml new file mode 100644 index 0000000..d0fc454 --- /dev/null +++ b/.github/workflows/code-coverage.yml @@ -0,0 +1,65 @@ +name: code-coverage + +on: + push: + branch: sima-history + workflow_dispatch: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + gcc: + runs-on: ubuntu-latest + steps: + - name: Checkout history + uses: actions/checkout@v4 + + - name: Build pFUnit + run: | + git clone --depth 1 --branch v4.10.0 https://github.com/Goddard-Fortran-Ecosystem/pFUnit.git + cd pFUnit + cmake -B./build -S. + cd build + make install + + - name: Build history + run: | + cmake \ + -DCMAKE_PREFIX_PATH=/home/runner/work/history_output/history_output/pFUnit/build/installed \ + -DHISTORY_ENABLE_CODE_COVERAGE=ON \ + -B./build \ + -S. + cd build + make + + - name: Run tests + run: | + cd build && ctest -V --output-on-failure --output-junit test_results.xml + + - name: Upload unit test results + uses: actions/upload-artifact@v4 + with: + name: unit-test-results + path: build/test_results.xml + + - name: Setup GCov + run: | + python3 -m venv venv + source venv/bin/activate + pip3 install gcovr + + - name: Run Gcov + run: | + source venv/bin/activate + cd build + gcovr -r .. --filter '\.\./src' --html history_code_coverage.html --txt + + - name: Upload code coverage results + uses: actions/upload-artifact@v4 + with: + name: code-coverage-results + path: build/history_code_coverage.html + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..246c8bb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.21) + +project(History-Output VERSION 0.0.1 LANGUAGES Fortran) + +set(CMAKE_Fortran_FLAGS "-O0 --coverage") +if(NOT History-Output_IS_TOP_LEVEL) + message(WARNING "History-Output is not integrated into the CMake build of any top level " + "project yet and this CMake is for testing purposes only. " + "Making a change to this project's CMake will not impact the build of " + "a parent project at this time.") +endif() + +option(HISTORY_ENABLE_TESTS "Run pFUnit unit tests" OFF) +option(HISTORY_ENABLE_CODE_COVERAGE "Run code coverage tool" OFF) + +if(HISTORY_ENABLE_CODE_COVERAGE) + add_compile_options(-O0 --coverage) + add_link_options(-lgcov) +endif() + +set(DCMAKE_BUILD_TYPE Debug) +add_subdirectory(src) + +if(HISTORY_ENABLE_TESTS OR HISTORY_ENABLE_CODE_COVERAGE) + enable_testing() + add_subdirectory(test) +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..caff37d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,14 @@ +set(HISTORY_SOURCES + hist_api.F90 + hist_buffer.F90 + hist_field.F90 +) + +add_library(history ${HISTORY_SOURCES}) + +target_compile_options(history PRIVATE -ffree-line-length-none) + +add_subdirectory(util) +add_subdirectory(hash) + +target_include_directories(history PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/hash/CMakeLists.txt b/src/hash/CMakeLists.txt new file mode 100644 index 0000000..8e0accd --- /dev/null +++ b/src/hash/CMakeLists.txt @@ -0,0 +1,6 @@ +set(HISTORY_HASH_SOURCES + hist_hash_table.F90 + hist_hashable.F90 +) + +target_sources(history PRIVATE ${HISTORY_HASH_SOURCES}) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt new file mode 100644 index 0000000..fa96353 --- /dev/null +++ b/src/util/CMakeLists.txt @@ -0,0 +1,5 @@ +set(HISTORY_UTIL_SOURCES + hist_msg_handler.F90 +) + +target_sources(history PRIVATE ${HISTORY_UTIL_SOURCES}) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..db828ff --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,11 @@ +find_package(PFUNIT REQUIRED) + +add_pfunit_ctest(hashable_test + TEST_SOURCES test_hashable.pf + LINK_LIBRARIES history +) + +add_pfunit_ctest(hash_table_test + TEST_SOURCES test_hash_table.pf + LINK_LIBRARIES history +) diff --git a/test/test_hash_table.pf b/test/test_hash_table.pf new file mode 100644 index 0000000..389fe1a --- /dev/null +++ b/test/test_hash_table.pf @@ -0,0 +1,35 @@ +@test +subroutine test_hash_table_add_hashable_char_value_same_after_get() + use funit + use hist_hash_table, only: hist_hash_table_t + use hist_hashable, only: hist_hashable_char_t + use hist_hashable, only: new_hashable_char + use hist_hashable, only: hist_hashable_t + + type(hist_hash_table_t) :: htable + type(hist_hashable_char_t), pointer :: hp => NULL() + class(hist_hashable_t), pointer :: hpout => NULL() + + call htable%initialize(4) + call new_hashable_char("1", hp) + call htable%add_hash_key(hp) + hpout=>htable%table_value("1") + + ! Verify the key of the hashed value from the hashable is the same as the hashable that was passed in + @assertEqual("1", hpout%key()) + +end subroutine test_hash_table_add_hashable_char_value_same_after_get + +@test +subroutine test_hash_table_key_is_expected() + use funit + use hist_hash_table, only: hist_hash_table_t + + type(hist_hash_table_t) :: htable + + call htable%initialize(4) + + ! Assert the key hash method returns the integer version of the provided string + @assertEqual(1, htable%key_hash("1")) + +end subroutine test_hash_table_key_is_expected diff --git a/test/test_hashable.pf b/test/test_hashable.pf new file mode 100644 index 0000000..334bf93 --- /dev/null +++ b/test/test_hashable.pf @@ -0,0 +1,31 @@ +@test +subroutine test_hashable_int_key_and_val_equal_input() + use funit + use hist_hashable, only: hist_hashable_int_t + use hist_hashable, only: new_hashable_int + + type(hist_hashable_int_t), pointer :: p=>NULL() + + call new_hashable_int(1, p) + + ! Assert value and key are the same the hashable was created with + @assertEqual(1, p%val()) + @assertEqual("1", p%key()) + +end subroutine test_hashable_int_key_and_val_equal_input + +@test +subroutine test_hashable_char_key_equal_input() + use funit + use hist_hashable, only: hist_hashable_char_t + use hist_hashable, only: new_hashable_char + + type(hist_hashable_char_t), pointer :: p=>NULL() + + call new_hashable_char("a", p) + + ! Assert key is same as input into the hashable + @assertEqual("a", p%key()) + +end subroutine test_hashable_char_key_equal_input +