pFUnit
The Preprocessor - pFUnitParser

Overview of Preprocessor (pFUnitParser.py)

/////////////////////////////////////////////////////////////////////

Using The Preprocessor

How to write tests using the ".pf" files. We expect this to be the main way people write pFUnit-based tests. Please see the Examples directory for a wide range of examples. The .pf files themselves are generally to be found in an example's "tests" subdirectory.

Configuration - testSuites.inc

The include file "testSuites.inc" tells the preprocessor to generate code for TestSuites listed therein. The suite names are based on the TestCases provided in the preprocessor input file or the name of the preprocessor input file (.pf) itself. For example, if no module is defined in a .pf file, i.e. the preprocessor will define the module, one can set up a "testSuites.inc" as follows.

! To load "exampleTestsNoModule.pf".
ADD_TEST_SUITE(exampleTestsNoModule_suite)

For a .pf file that contains a module associated with a test suite the syntax is as follows.

! To load "exampleTests.pf" implementing the module exampleTests_mod.
ADD_TEST_SUITE(exampleTests_mod_suite)

Invocation

To run the preprocessor on on a preprocessor input file "exampleTests.pf", invoke:

$ ${PFUNIT}/bin/pFUnitParser.py exampleTests.pf exampleTests.F90

A convenient GNUmakefile rule is as follows.

%.F90: %.pf
$(PFUNIT)/bin/pFUnitParser.py $< $@

Preprocessor Input File (.pf)

The preprocessor input file is a Fortran free format file that contains subroutines, including those implementing the suite of tests, or a module with the tests, TestCases, and support for parameters. The preprocessor reads and parses this file producing a fortran file implementing the tests, automating some boilerplate code. Embedded "@" directives inform the preprocessor about information needed to generate the test suite. If the .pf file does not implement a module providing a test suite, the preprocessor will use the name of .pf file referred to by "testSuites.inc". Currently only one test suite per .pf file is allowed, a limitation of the current implementation of the parser.

Many example .pf files may be found in the examples' "tests" subdirectories in the Examples directory.

Below we present the most commonly used directives first, but in a .pf file using all of these capabilities, the most common order is as follows.

Directives

Preprocessor "@" directives, which in keeping with Fortran style are not case sensitive, instruct the preprocessor how to interpret parts of the code relevant to the generation of the test suite. The most important directives follow.

@Test

This directive is used to indicate a test routine to the preprocessor, which then includes it in the test suite. There may be multiple tests in the .pf file, each annotated by the @Test directive.

@Test also supports MPI-parallel tests (see @MPITest ).

An example, from Examples/Fixture:

@Test
subroutine testBracketInterior(this)
class (Test_LinearInterpolator), intent(inout) :: this
@assertEqual([3,4], this%interpolator%getBracket(at=4.))
end subroutine testBracketInterior
@Test
subroutine testInterpolateAtNode(this)
class (Test_LinearInterpolator), intent(inout) :: this
@assertEqual(2., this%interpolator%interpolate(at=3.))
end subroutine testInterpolateAtNode

@MPITest

@MPITest is deprecated as @Test now handles this case.

This directive indicates an MPI parallel test to the preprocessor, which then includes it in an MPI enabled test suite. The directive takes a single argument, the requested number of MPI processes to run. The syntax, exemplified by one of the tests from Examples/MPI_Halo:

@Test( npes=[1,2,3])
subroutine testHaloInterior(this)
implicit none
class (MpiTestMethod) :: this
integer, parameter :: N = 2
real :: a(N,0:N+1)
integer :: p
p = this%getProcessRank()
a(:,1:N) = p
a(:,0) = -1
a(:,N+1) = -1
call haloFill(a, this%getMpiCommunicator())
@assertEqual(real(p), a(1,1))
@assertEqual(real(p), a(2,1))
@assertEqual(real(p), a(1,2))
@assertEqual(real(p), a(2,2))
end subroutine testHaloInterior

@Assert

The @Assert directives are expanded into calls to similarly named pFUnit library routines. The syntax for the directives follows the pattern for @assertEqual below.

@assertEqual(expected,found,'An identifying or explanatory message.')

The preprocessor will automatically add information about source location (file & line number) to the call emitted to the test suite code. It also adds the check for exceptions.

For more information about @assert directives, please refer to the following.

@Parameters

The @Parameter directive indicates the declaration of the parameterized type used to generate the iteration over the multiple parameter values. It also identifies the names of the parameters to be iterated over. The preprocessor extracts type information from the declaration of the parameter type collection that immediately follows the directive. This directive will set up the iteration. To define the parameter values per iteration the getParameters method of the abstract ParameterizedTest must be implemented. For example:

@Parameters = [p1,p2]
type, extends(AbstractTestParameter) :: exampleCase
integer :: i
real :: x
end type exampleCase

@TestCase

This directive identifies to the preprocessor the TestCase declaration. The type declared at this point extends TestCase (or its extension), which includes setting methods such as the following: setUp, tearDown, runMethod, userMethod. For the extension MPITestCase, as with ParameterizedTestCase, you have the option (requirement if parameters are used) to set getParameters and getParameterString. For example:

@TestCase
type, extends(MPITestCase) :: Test_Parameters
integer :: p1, p2
procedure(runMethod), pointer :: userMethod => null()
contains
procedure, nopass :: getParameters
procedure :: getParameterString => getParameterString_
procedure :: runMethod
end type Test_Parameters