Fortran - Testing - Unit tests and test suites
In the previous post, we’ve spent some time improving a number of “temporary” tests. We introduced an assertion function to compare two real values, and we prevent failing tests from stopping the entire test program.
Moving tests to their own procedures
The approach of adding tests directly in the main test program block doesn’t really scale, as the saying goes. polyline_length may be a simple function, but for more complicated functions with multiple branches and loops, we’d have to write many of these tests. The test program keeps growing, and eventually it becomes a mess. It doesn’t help that all local variables have to be declared at the top. Even if that wasn’t needed, it isn’t very clear where a test starts or ends. Everything happens in the same scope, potentially becoming a memory management issue too. This also makes it hard to delete tests we no longer want. Removing lines from a big test program likely breaks other tests, or we forget to remove things we no longer need.
By Matthias Noback
read moreFortran - Testing - Improving temporary test programs
How can we know that the function we wrote, works as intended? We could run it, and manually verify its correctness. The simplest way to do this is to call the function in the main program block, print the output, and compare it with what we expect. Say our function calculates the length of a polyline, stored as a two-dimensional array of reals, representing (x,y) coordinates:
pure function polyline_length(coordinates) result(length)
real(kind=real64), dimension(:, :), intent(in) :: coordinates
real(kind=real64) :: length
real(kind=real64) :: distance
integer :: index
length = 0.0_real64
do index = 1, size(coordinates, 1) - 1
distance = sqrt((coordinates(index, 1) - &
coordinates(index + 1, 1))**2 + &
(coordinates(index, 2) - &
coordinates(index + 1, 2))**2)
length = length + distance
end do
end function polyline_length
Temporary test programs
We could modify the main program block of our actual program, but it’s a lot simpler and safer to create a separate “throw-away” test program, with only the code we need. We’d write a short test program that sets up some coordinates, calls the function, then prints the result:
By Matthias Noback
read moreFortran - Errors and error handling - Part 7 - Fatal errors
We’ve encountered several ways of designing functions in a way that allows them to fail for some reason, without stopping the program, or making it otherwise risky or awkward to use the function. We introduced the error_t type which is very flexible. It can be used to provide some information to the caller, helping them understand what went wrong and how it can be fixed. By allowing errors to be wrapped inside others, we can create chains of errors that describe the problem at various abstraction levels. It gives back control to the user: how do they want to deal with an error? Would they like to try something else? Or, in the end, should we just stop trying and quit te program?
By Matthias Noback
read moreFortran - Errors and error handling - Part 6 - Guarantees
Parsing an array of strings to a polyline should fail if one of the strings could not be parsed to a string. But it should also fail if the resulting array of points is empty. Or at least, if that makes sense in the application’s domain. You could say that a polyline with no points is still a polyline, just like an empty set is still a set. Similarly, in some scenarios it may be okay for a polyline to have just a single point. For now, let’s skip the mathematical discussion and take this as a practical rule that we want to enforce: a polyline has at least 2 points. The type we currently have can’t give us such a guarantee:
By Matthias Noback
read more