Testing¶
Test categories¶
Tests are organised into three directories by purpose:
Category | Directory | When to use |
|---|---|---|
Unit |
| Fast checks of a single module (allocator, reordering, statistics) |
Verification |
| Comparing numerical results against analytical solutions |
Performance |
| Benchmarking throughput — large problems, many iterations, no correctness checks |
Each test is registered with a CTest label matching its category (unit, verification, or performance) and its backend (omp or cuda).
Writing a test¶
Create a Fortran source file in the appropriate directory. Use #ifdef CUDA guards so the same file compiles for both backends.
Unit test example (tests/unit/test_example.f90):
program test_example
use MPI
use m_common, only: dp
implicit none
integer :: ierr
logical :: allpass
call MPI_Init(ierr)
allpass = .true.
! --- your checks here ---
if (1 + 1 /= 2) then
print *, 'FAIL: arithmetic'
allpass = .false.
end if
call MPI_Finalize(ierr)
if (.not. allpass) error stop 1
end program test_example
Verification test — use check_norm from m_test_utils:
use m_test_utils, only: check_norm
...
call check_norm(error_norm, 1e-8_dp, 'my_operator_periodic', allpass)
check_norm prints a standardised PASSED/FAILED line with the norm value and tolerance.
Performance test — use report_perf from m_test_utils:
use m_test_utils, only: report_perf
...
call report_perf('my_kernel', elapsed_time, n_iters, ndof, bytes_per_dof)
This emits machine-parseable output: PERF_METRIC: <label> time=<X>s bw=<Y> GiB/s
Registering a test¶
Add a line to tests/CMakeLists.txt using the function matching your category:
# Unit test, 1 MPI rank, OMP backend
define_test(unit/test_example.f90 1 omp)
# Verification test, 4 MPI ranks, OMP backend
define_verification_test(verification/test_example.f90 4 omp)
# Performance test, 1 MPI rank, CUDA backend (inside the NVHPC block)
define_performance_test(performance/perf_example.f90 1 cuda)
For dual-backend tests, register twice — once in the OMP section and once inside the if(NVHPC) block:
# OMP section
define_test(unit/test_example.f90 1 omp)
# Inside the NVHPC/PGI block
define_test(unit/test_example.f90 1 cuda)
Running tests¶
$ FC=mpif90 cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
$ cd build
$ make
$ ctest -R test_example --output-on-failure
Common CTest commands:
# All unit + verification tests (same as CI)
$ ctest -L "unit|verification" --output-on-failure
# By category
$ ctest -L unit
$ ctest -L verification
$ ctest -L performance
# By backend
$ ctest -L omp
$ ctest -L cuda
# List all tests without running
$ ctest -N
Conventions¶
File naming:
test_*.f90for unit/verification,perf_*.f90for performance.Exit code: Call
error stop 1on failure so CTest detects it.Backend guards: Use
#ifdef CUDA/#else/#endiffor backend-specific code.MPI: All tests call
MPI_Init/MPI_Finalize, even single-rank tests. Tests are launched withmpirun --oversubscribe -np <N>.Shared utilities: All tests automatically link against
x3d2_test_utils(providescheck_normandreport_perf).