con un clic
pfunit-troubleshooting
Debug and fix pFUnit test failures in MAPL
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Debug and fix pFUnit test failures in MAPL
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
Safely switch between NAG, gfortran, and Intel compilers when building MAPL
MAPL Fortran coding standards and style conventions
GitHub workflow, commit conventions, and PR process for MAPL
Build MAPL with NAG, gfortran, or Intel compilers on macOS and Linux
MAPL error handling macros and best practices
First-time MAPL development environment setup and configuration
| name | pfunit-troubleshooting |
| description | Debug and fix pFUnit test failures in MAPL |
| compatibility | opencode |
This skill provides comprehensive guidance for debugging and fixing pFUnit test failures in MAPL. pFUnit is the parallel Fortran unit testing framework used throughout MAPL.
# Run single test with diagnostics
./TestName.x -d
# Run filtered tests (substring match)
./TestName.x -f substring
# Run with verbose output
./TestName.x -v
# List all tests
./TestName.x -l
# Run specific test by exact name
./TestName.x -f exact_test_name
# From MAPL build directory (e.g., ~/swdev/VS/MAPL/nag)
cd generic3g/MAPL_cfio/MAPL_cfio_r4.tests
# Set library path (required for NAG)
export DYLD_LIBRARY_PATH=/usr/local/nag/lib:$DYLD_LIBRARY_PATH
# Run tests (< 1 minute)
./generic3g.x
MAPL uses consistent naming for test executables:
generic3g.x - Generic 3D grid testsTestName.x - Other component testsTests are organized by component:
build-dir/
├── base/
│ └── tests/
│ └── TestSomething.x
├── generic3g/
│ └── MAPL_cfio/
│ └── MAPL_cfio_r4.tests/
│ └── generic3g.x
├── gridcomps/
│ └── tests/
└── ...
*.pf - pFUnit test source files (preprocessed to .F90)*.F90 - Generated or manual Fortran test files*.x - Compiled test executablesWhen ctest reports a failure:
# Run ctest to see which tests fail
ctest
# Example output:
# 99% tests passed, 1 tests failed out of 100
# The following tests FAILED:
# 42 - generic3g (Failed)
Navigate to test directory and run with diagnostics:
# Find the test executable
find . -name "generic3g.x" -type f
# Navigate to test directory
cd generic3g/MAPL_cfio/MAPL_cfio_r4.tests
# Run with diagnostics
./generic3g.x -d
The -d flag shows:
Use -f to filter to specific test:
# If test is named "test_vertical_interpolation"
./generic3g.x -f vertical_interpolation
# Multiple matches possible (substring matching)
./generic3g.x -f interpolation
IMPORTANT: The -f flag does substring matching, NOT wildcard/glob matching. Do not use * or ?.
Common failure patterns:
Location: test_vertical_interpolation.pf:45
Expected: 1.23456
Actual: 1.23457
Difference exceeds tolerance
What to check:
Program received signal SIGSEGV: Segmentation fault
What to check:
Test runs but never completes.
What to check:
Symptom:
./generic3g.x -f my_test
# No tests run
Solution: List all available tests:
./generic3g.x -l
Check exact test name and use correct substring.
Symptom:
dyld: Library not loaded: @rpath/libnag_nag.dylib
Solution (NAG on macOS):
export DYLD_LIBRARY_PATH=/usr/local/nag/lib:$DYLD_LIBRARY_PATH
./generic3g.x
Solution (gfortran on macOS):
export DYLD_LIBRARY_PATH=/usr/local/gfortran/lib:$DYLD_LIBRARY_PATH
./generic3g.x
IMPORTANT: This is required for direct test execution. Not needed for ctest if properly configured.
Possible causes:
Debugging approach:
Symptom:
Expected: 1.2345678
Actual: 1.2345679
Common causes:
Solutions:
Relax tolerance (if difference is negligible):
@assertEqual(expected, actual, tolerance=1.0e-5)
Use relative tolerance:
@assertEqual(expected, actual, tolerance=abs(expected)*1.0e-6)
Investigate algorithm if difference is significant
Symptom: Test fails only when run with multiple processes.
Debugging approach:
Run with single process:
mpirun -np 1 ./TestName.x
Run with multiple processes:
mpirun -np 4 ./TestName.x
Check for:
Symptom:
./TestName.x
bash: ./TestName.x: No such file or directory
Solution: Test may not have been built. Rebuild:
cd <build-directory>
make -j8 install
If still not found, test may be disabled or have build errors. Check CMake output.
Debugging workflow:
Verify change didn't break assumptions:
Update tests if behavior intentionally changed:
! Update expected values
@assertEqual(new_expected_value, actual)
Fix code if tests revealed bugs:
Check for stale build artifacts:
make clean
make -j8 install
ctest
@test
subroutine test_my_function()
use pfunit_mod
use MyModule
implicit none
integer :: result
integer :: expected
! Setup
expected = 42
! Execute
result = my_function(input)
! Verify
@assertEqual(expected, result)
end subroutine test_my_function
! Equality assertions
@assertEqual(expected, actual)
@assertEqual(expected, actual, tolerance=1.0e-6)
@assertEqual(expected, actual, message="Custom error message")
! Boolean assertions
@assertTrue(condition)
@assertFalse(condition)
! Exception/error assertions
@assertExceptionRaised("Expected error message")
! Array assertions
@assertEqual(expected_array, actual_array)
@assertEqual(expected_array, actual_array, tolerance=1.0e-6)
@before
subroutine setUp()
! Initialize test fixtures
! Allocate arrays
! Open files
end subroutine setUp
@after
subroutine tearDown()
! Clean up test fixtures
! Deallocate arrays
! Close files
end subroutine tearDown
@test
subroutine test_something()
! setUp() called automatically before this
! Test code here
! tearDown() called automatically after this
end subroutine test_something
CRITICAL: Never use exact equality for floating-point:
! BAD - fragile, compiler-dependent
@assertEqual(1.23456, result)
! GOOD - uses tolerance
@assertEqual(1.23456, result, tolerance=1.0e-5)
! BETTER - relative tolerance for large numbers
@assertEqual(expected, result, tolerance=abs(expected)*1.0e-6)
@test
subroutine test_array_operation()
use pfunit_mod
implicit none
real, allocatable :: expected(:)
real, allocatable :: result(:)
integer :: i
allocate(expected(10))
allocate(result(10))
! Setup expected values
do i = 1, 10
expected(i) = real(i) * 2.0
end do
! Call function under test
call compute_array(result)
! Verify
@assertEqual(expected, result, tolerance=1.0e-6)
deallocate(expected)
deallocate(result)
end subroutine test_array_operation
Quick debugging in pFUnit tests:
@test
subroutine test_with_debug_output()
use pfunit_mod
implicit none
integer :: result
result = my_function(42)
print *, "DEBUG: result =", result
print *, "DEBUG: expected =", 84
@assertEqual(84, result)
end subroutine test_with_debug_output
Run test to see debug output:
./TestName.x -d -f test_with_debug_output
# Compile with debug symbols (should already be in Debug build)
cmake .. -DCMAKE_BUILD_TYPE=Debug
make -j8 install
# Run test under GDB
gdb ./TestName.x
# In GDB:
(gdb) run -f failing_test
(gdb) bt # backtrace when it crashes
(gdb) print variable_name
lldb ./TestName.x
# In LLDB:
(lldb) run -f failing_test
(lldb) bt # backtrace
(lldb) frame variable # show local variables
Different compilers offer different debugging capabilities:
NAG:
# Already has excellent runtime checking in Debug mode
cmake .. -DCMAKE_BUILD_TYPE=Debug
gfortran:
# Add extra runtime checks
cmake .. -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_Fortran_FLAGS="-g -fbacktrace -fcheck=all"
Intel:
# Add runtime checks
cmake .. -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_Fortran_FLAGS="-g -traceback -check all"
# Time entire test suite
time ./TestName.x
# Time specific test
time ./TestName.x -f specific_test
# Run with verbose output to see timing
ctest -V
# Or use ctest's built-in timing
ctest --output-on-failure --verbose
If a test is unexpectedly slow:
CRITICAL: Disabling tests should be rare and temporary.
Valid reasons:
How to disable:
@test(ifdef=SOME_FEATURE_FLAG)
subroutine test_to_disable()
! This test only runs if SOME_FEATURE_FLAG is defined
end subroutine test_to_disable
Or remove from CMakeLists.txt temporarily (prefer ifdef approach).
Good test names are descriptive:
! GOOD
@test
subroutine test_vertical_interpolation_linear_profile()
@test
subroutine test_grid_create_with_invalid_dimensions_should_fail()
! BAD (too vague)
@test
subroutine test1()
@test
subroutine test_grid()
# Run full test suite locally
cd <build-directory>
ctest
# If any failures, debug and fix
cd path/to/failing/test
./TestName.x -d
# Fix code or test, rebuild, retest
make -j8 install
ctest
CRITICAL: All tests must pass before creating PR.
If tests pass locally but fail in CI:
See github-workflow skill for PR process.
When debugging MPI-parallel tests:
# Run under MPI with debugger
mpirun -np 4 xterm -e gdb ./TestName.x
This opens separate debugger windows for each MPI rank.
# Check for memory leaks
valgrind --leak-check=full ./TestName.x -f specific_test
# Check for uninitialized memory
valgrind --track-origins=yes ./TestName.x -f specific_test
Note: Valgrind may report false positives with MPI and OpenMP.
pFUnit tests can't directly test private procedures. Options:
For testing multiple similar cases:
@test(cases=[1, 2, 3, 4, 5])
subroutine test_with_parameter(n)
integer, intent(in) :: n
! Test using parameter n
@assertEqual(n * 2, my_double_function(n))
end subroutine test_with_parameter
When to use this skill: