diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e4d67029f..b6220c6e3 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -35,3 +35,8 @@ Do PRs in upstream repositories need to be merged first? If so add the "waiting for other repos" label and list the upstream PRs - waiting on noaa-emc/nems/pull/ - waiting on noaa-emc/fv3atm/pull/ + +# Requirements before merging +- [ ] All new code in this PR is tested by at least one unit test +- [ ] All new code in this PR includes Doxygen documentation +- [ ] All new code in this PR does not add new compilation warnings (check CI output) diff --git a/.github/workflows/GCC.yml b/.github/workflows/GCC.yml index 5f3891684..86d0bf668 100644 --- a/.github/workflows/GCC.yml +++ b/.github/workflows/GCC.yml @@ -24,7 +24,7 @@ jobs: mpi: ["mpich", "openmpi"] steps: - + - name: checkout-fv3atm uses: actions/checkout@v4 with: @@ -59,6 +59,7 @@ jobs: spack config add "packages:mpi:require:'${{ matrix.mpi }}'" spack concretize |& tee ${SPACK_ENV}/log.concretize spack install -j2 --fail-fast + echo "spackrc=$?" >> ${GITHUB_ENV} spack clean --all build_fv3atm: @@ -73,9 +74,20 @@ jobs: steps: - - name: install-doxygen + # Only do Doxygen and gcovr build for one job + - name: decide-doc-gcovr-build + run: | + if [[ "${{ matrix.cmake_opts }}" == "-D32BIT=ON" && "${{ matrix.gcc_ver }}" == 12 && "${{ matrix.mpi }}" == mpich ]]; then + echo 'devbuild=ON' | tee -a ${GITHUB_ENV} + echo 'gcov_cmake="-DCMAKE_Fortran_FLAGS=-fprofile-abs-path -fprofile-arcs -ftest-coverage -O0"' | tee -a ${GITHUB_ENV} + else + echo 'devbuild=OFF' | tee -a ${GITHUB_ENV} + fi + + - name: install-utilities run: | sudo apt-get install doxygen graphviz + python3 -m pip install gcovr - name: install-cmake run: | @@ -109,14 +121,34 @@ jobs: export CC=mpicc export CXX=mpicxx export FC=mpif90 - cat /home/runner/work/fv3atm/fv3atm/spack-develop/opt/spack/linux-ubuntu22.04-zen2/gcc-12.3.0/fms-2023.04-*/lib/cmake/fms/fms-config.cmake - cmake ${GITHUB_WORKSPACE}/fv3atm -DBUILD_TESTING=ON ${{ matrix.cmake_opts }} -DENABLE_DOCS=ON + cmake ${GITHUB_WORKSPACE}/fv3atm -DBUILD_TESTING=ON ${{ matrix.cmake_opts }} -DENABLE_DOCS=ON ${{ env.gcov_cmake }} make -j2 - ls -l /home/runner/work/fv3atm/fv3atm/fv3atm/io - - uses: actions/upload-artifact@v4 + - name: run-tests + run: | + cd $GITHUB_WORKSPACE/build + ctest -j2 --output-on-failure --rerun-failed + + - name: get-test-coverage + if: ${{ env.devbuild == 'ON' }} + run: | + cd $GITHUB_WORKSPACE/build + gcovr -r .. -v --html-details --gcov-executable gcov-12 --exclude $GITHUB_WORKSPACE/fv3atm/tests --exclude $GITHUB_WORKSPACE/fv3atm/stochastic_physics_repo --exclude $GITHUB_WORKSPACE/fv3atm/build/ccpp --exclude $GITHUB_WORKSPACE/fv3atm/ccpp/physics --exclude $GITHUB_WORKSPACE/fv3atm/ccpp/framework --exclude $GITHUB_WORKSPACE/fv3atm/atmos_cubed_sphere --exclude CMakeFiles --print-summary -o test-coverage.html + + - name: upload-test-coverage + uses: actions/upload-artifact@v4 + if: ${{ env.devbuild == 'ON' }} + with: + name: test-coverage-fv3atm-${{ github.sha }} + path: | + ${{ github.workspace }}/build/*.html + ${{ github.workspace }}/build/*.css + + - name: upload-docs + uses: actions/upload-artifact@v4 + if: ${{ env.devbuild == 'ON' }} with: - name: docs-gcc${{ matrix.gcc_ver }}-${{ matrix.mpi }}-${{ matrix.cmake_opts }} + name: docs-fv3atm path: | build/docs/html @@ -126,4 +158,3 @@ jobs: with: name: ccpp_prebuild_logs-gcc${{ matrix.gcc_ver }}-${{ matrix.mpi }}-${{ matrix.cmake_opts }} path: ${{ github.workspace }}/build/ccpp/ccpp_prebuild.* - diff --git a/CMakeLists.txt b/CMakeLists.txt index c310110ca..ae94fef14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,6 +157,11 @@ if(OPENMP) target_link_libraries(fv3atm PUBLIC OpenMP::OpenMP_Fortran) endif() +if(BUILD_TESTING) + include(CTest) + add_subdirectory(tests) +endif() + ############################################################################### ### Install ############################################################################### diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..5667d3d61 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,29 @@ +# This file sets up CTest unit tests for fv3atm. +# +# Alex Richert, Jan 2024 + +# Stage test data +execute_process(COMMAND cmake -E create_symlink + "${CMAKE_CURRENT_SOURCE_DIR}/data" + "${CMAKE_CURRENT_BINARY_DIR}/data" + ) + +function(add_fv3atm_mpi_test TESTNAME) + add_executable(${TESTNAME} ${TESTNAME}.F90) + target_link_libraries(${TESTNAME} PRIVATE fv3atm MPI::MPI_Fortran PIO::PIO_Fortran) + add_test(${TESTNAME} ${MPIEXEC_EXECUTABLE} -n 2 ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}) +endfunction() + +function(add_fv3atm_serial_test TESTNAME) + add_executable(${TESTNAME} ${TESTNAME}.F90) + target_link_libraries(${TESTNAME} PRIVATE fv3atm) + add_test(${TESTNAME} ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}) +endfunction() + +#foreach(testname test_write_netcdf) +# add_fv3atm_mpi_test(${testname}) +#endforeach() + +foreach(testname test_post_nems_routines) + add_fv3atm_serial_test(${testname}) +endforeach() diff --git a/tests/data/post_namelist.nml b/tests/data/post_namelist.nml new file mode 100644 index 000000000..262d229c7 --- /dev/null +++ b/tests/data/post_namelist.nml @@ -0,0 +1,20 @@ +&model_inputs + modelname = "DMMY" + submodelname = "SUBM" +/ +&nampgb + kpo = 5 + kth = 7 + kpv = 9 + po = 0.5 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 + th = 1.0 2.0 3.0 4.0 5.0 6.0 7.0 + pv = 11. 12. 13. 14. 15. 16. 17. 18. 19. + hyb_sigp = F + d3d_on = T + gocart_on = T + popascal = T + rdaod = T + nasa_on = T + gccpp_on = T + d2d_chem = T +/ diff --git a/tests/data/post_namelist_empty.nml b/tests/data/post_namelist_empty.nml new file mode 100644 index 000000000..4efa4ecd5 --- /dev/null +++ b/tests/data/post_namelist_empty.nml @@ -0,0 +1,4 @@ +&model_inputs +/ +&nampgb +/ diff --git a/tests/test_post_nems_routines.F90 b/tests/test_post_nems_routines.F90 new file mode 100644 index 000000000..371c3bc8d --- /dev/null +++ b/tests/test_post_nems_routines.F90 @@ -0,0 +1,55 @@ +! This program provides unit testing for the subroutines in io/post_nems_routines.F90 +! +! Alex Richert, 11 Jan 2024 +program test_post_nems_routines + + use ctlblk_mod, only : komax,hyb_sigp,d3d_on,gocart_on, & + rdaod,nasa_on,gccpp_on,d2d_chem,modelname,submodelname, lsm + + implicit none + + character (len=*), parameter :: post_namelist_empty="data/post_namelist_empty.nml" + character (len=*), parameter :: post_namelist="data/post_namelist.nml" + integer :: kpo,kth,kpv + real(4),dimension(komax) :: po,th,pv + logical :: popascal + real, parameter :: tini=tiny(1.0) + + ! Verify default settings by using empty nml file + call read_postnmlt(kpo,kth,kpv,po,th,pv,trim(post_namelist_empty)) + if (kpo.ne.0) stop 1 + if (kth.ne.6) stop 2 + if (kpv.ne.8) stop 3 + if (any(po.ne.0.0)) stop 4 + if (any(abs(th(1:6)-(/310.,320.,350.,450.,550.,650./)).gt.tini)) stop 5 + if (any(abs(pv(1:8)-(/0.5,-0.5,1.0,-1.0,1.5,-1.5,2.0,-2.0/)).gt.tini)) stop 6 + if (.not.hyb_sigp) stop 7 + if (d3d_on) stop 8 + if (gocart_on) stop 9 + if (lsm.ne.46) stop 10 ! 'lsm' is determined by 'popascal' + if (rdaod) stop 11 + if (nasa_on) stop 12 + if (gccpp_on) stop 13 + if (d2d_chem) stop 14 + + ! Now use fully populated nml file + call read_postnmlt(kpo,kth,kpv,po,th,pv,trim(post_namelist)) + if (kpo.ne.5) stop 101 + if (kth.ne.7) stop 102 + if (kpv.ne.9) stop 103 + if (po(1).ne.0.5) stop 104 + if (any(po(2:komax).ne.1.0)) stop 104 + if (any(abs(th(1:7)-(/1.,2.,3.,4.,5.,6.,7./)).gt.tini)) stop 105 + if (any(abs(pv(1:9)-(/11.,12.,13.,14.,15.,16.,17.,18.,19./)).gt.tini)) stop 106 + if (hyb_sigp) stop 107 + if (.not.d3d_on) stop 108 + if (.not.gocart_on) stop 109 + if (lsm.ne.5) stop 110 ! 'lsm' is determined by 'popascal' + if (.not.rdaod) stop 111 + if (.not.nasa_on) stop 112 + if (.not.gccpp_on) stop 113 + if (.not.d2d_chem) stop 114 + if (trim(modelname).ne."DMMY") stop 115 + if (trim(submodelname).ne."SUBM") stop 116 + +end program test_post_nems_routines