Skip to content

Commit 1bda46f

Browse files
authored
gh-45: introduce pfunit syntax slowly (#47)
1 parent 4f49761 commit 1bda46f

7 files changed

Lines changed: 685 additions & 416 deletions

.markdownlint.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
22
"default": true,
3-
"MD013": { "line_length": 132 },
3+
"MD013": {
4+
"line_length": 132,
5+
"code_blocks": false,
6+
},
47
"MD024": false, # Multiple headings with the same content
58
"MD041": false, # First line in a file should be a top-level heading
69
}

config.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,10 @@ episodes:
6969
- 1-what-is-a-unit-tests.md
7070
- 2-refactor-fortran.md
7171
- 3-writing-your-first-unit-test.md
72-
- 4-pfunit-syntax.md
72+
- 4-pfunit-basics.md
7373
- 5-integrating-with-build-systems.md
74-
- 6-testing-parallel-code.md
74+
- 6-parameterising-pfunit-tests.md
75+
- 7-testing-parallel-code.md
7576

7677
# Information for Learners
7778
learners:

episodes/4-pfunit-basics.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
title: "pFUnit basics"
3+
teaching:
4+
exercises:
5+
---
6+
7+
:::::::::::::::::::::::::::::::::::::: questions
8+
9+
- What is the syntax of writing a unit test in Fortran?
10+
- How do I build my tests with my existing build system?
11+
12+
::::::::::::::::::::::::::::::::::::::::::::::::
13+
14+
::::::::::::::::::::::::::::::::::::: objectives
15+
16+
- Able to write a unit test for a Fortran procedure with test-drive, veggies and/or pFUnit.
17+
- Understand the similarities between each framework and where they differ.
18+
19+
::::::::::::::::::::::::::::::::::::::::::::::::
20+
21+
## What framework will we look at?
22+
23+
There are multiple frameworks available for writing unit tests in Fortran, as detailed on the
24+
[Fortran Lang website](https://fortran-lang.org/packages/programming/). However, we recommend
25+
the use of [pFUnit](https://github.com/Goddard-Fortran-Ecosystem/pFUnit) as it is…
26+
27+
- the most feature rich framework.
28+
- the most widely used framework.
29+
- being maintained.
30+
- able to integrate with CMake and make.
31+
32+
**Key features of pFUnit:**
33+
34+
- **Supports MPI**: Supports testing MPI parallelized code, including parametrizing tests by
35+
number of MPI ranks.
36+
- **Simple interface**: Tests are written in **.pf** format which is then pre-processed by a tool
37+
provided by pFUnit into **.f90** before compilation. This removes the need to write a lot of
38+
boilerplate code.
39+
40+
## The most basic pFUnit test
41+
42+
As we've seen in the [previous episode](../3-writing-your-first-unit-test.html), if we were to write our own unit tests using a
43+
custom testing setup we would need to define a test runner that could track success and failure states for each test and report
44+
the reason for each failure back to us.
45+
46+
Alternatively, if we were to use pFUnit, there is no longer a need to define this test runner because pFUnit handles that for us.
47+
Therefore, the most basic test we can define using pFunit becomes simple. For example, if we wanted to test the Fortran intrinsic
48+
function **dot_product**, we could write the following test.
49+
50+
```fortran
51+
module test_dot_product_intrinsic
52+
use funit
53+
implicit none
54+
contains
55+
@Test
56+
subroutine test_dot_product()
57+
integer :: a(10), b(10), c
58+
59+
! Define inputs and expected outputs for the scenario we want to test
60+
a = [1,2,3,4,5,6,7,8,9,10]
61+
b = [11,12,13,14,15,16,17,18,19,20]
62+
c = 935
63+
64+
! Check that the call to dot_product returned what we expect
65+
@assertEqual(c, dot_product(a, b), message="Unexpected value returned for the dot_product")
66+
67+
end subroutine test_dot_product
68+
end module test_dot_product_intrinsic
69+
```
70+
71+
Here we have introduced some new syntax in the form of **@Test** and **@AssertEqual**. These are pFUnit pre-processor directives
72+
which simplify how we write tests:
73+
74+
- **@Test** designates the subroutine **test_dot_product** as a test that should be ran on execution of your pFUnit test suite.
75+
- **@AssertEqual** is one of many assert directives provided by pFUnit. More specifically, **@AssertEqual** allows the
76+
exact comparison of values (also works for comparing arrays). For a full list of the available assertion directives see
77+
[pFUnit documentation page for their preprocessor directives](https://pfunit.sourceforge.net/page_Assert.html)
78+
- As is done here, it is recommended to provide a helpful message, in case of an assertion
79+
failing, to help diagnose the issue.
80+
81+
::: callout
82+
83+
### @AssertEqual for floating point values
84+
85+
For floating point values, @AssertEqual no longer carries out an exact comparison but become a comparison up to a tolerance.
86+
87+
:::
88+
89+
If we then wish to add a new test case we can add another subroutine, again decorated with **@Test**:
90+
91+
```fortran
92+
module test_dot_product_intrinsic
93+
use funit
94+
implicit none
95+
contains
96+
@Test
97+
subroutine test_dot_product()
98+
integer :: a(10), b(10), c
99+
100+
! Define inputs and expected outputs for the scenario we want to test
101+
a = [1,2,3,4,5,6,7,8,9,10]
102+
b = [11,12,13,14,15,16,17,18,19,20]
103+
c = 935
104+
105+
! Check that the call to dot_product returned what we expect
106+
@AssertEqual(c, dot_product(a, b), message="Unexpected value returned for the dot_product")
107+
108+
end subroutine test_dot_product
109+
110+
@Test
111+
subroutine test_dot_product_all_zeros()
112+
integer :: a(10), b(10), c
113+
114+
! Define inputs and expected outputs for the scenario we want to test
115+
a = 0
116+
b = 0
117+
c = 0
118+
119+
! Check that the call to dot_product returned what we expect
120+
@AssertEqual(c, dot_product(a, b), message="Unexpected value returned for the dot_product")
121+
122+
end subroutine test_dot_product_all_zeros
123+
end module test_dot_product_intrinsic
124+
```
125+
126+
::: challenge
127+
128+
### Challenge: Test temperature conversions using pFUnit
129+
130+
Continuing with part two of
131+
[3-writing-your-first-unit-test/challenge](https://github.com/carpentries-incubator/fortran-unit-testing/tree/main/exercises/3-writing-your-first-unit-test/challenge)
132+
from the exercises. Write a single test for the temperature conversion using pFUnit.
133+
134+
::: solution
135+
136+
A solution is provided in [3-writing-your-first-unit-test/solution](https://github.com/carpentries-incubator/fortran-unit-testing/tree/main/exercises/3-writing-your-first-unit-test/solution).
137+
138+
:::
139+
140+
:::

0 commit comments

Comments
 (0)