Quickstart (using CMake)#
You will learn to
create a minimal project with CMake,
add unit tests to the project,
build the project and run the tests.
Before jumping in#
To begin this quickstart tutorial on Fortuno, ensure to have a recent version of the CMake build system (version 3.22 or newer) and a Fortran compiler that implements the Fortran 2018 standard. Fortuno operates smoothly with recent versions of several popular Fortran compilers, but older Fortran compilers are known to fail to build it. Please check the minimal compiler versions in the Fortuno readme.
Getting comfortable#
We’ll create a library named mylib containing a single function factorial() to calculate the
factorial of an integer. The testing of the library shall be automated using unit tests.
Create a directory (e.g. mylib) for the project with the 3 subdirectories src, app and test.
mkdir mylib
cd mylib
mkdir src app test
We develop the first version of our library by creating src/mylib.f90:
!> Demo library to be unit-tested.
module mylib
implicit none
private
public :: factorial
contains
!> Calculates the factorial of a number.
function factorial(nn) result(fact)
!> number to calculate the factorial of
integer, intent(in) :: nn
!> factorial (note, there is no check made for integer overflow!)
integer :: fact
integer :: ii
fact = 1
do ii = 2, nn
fact = fact * ii
end do
end function factorial
end module mylib
The main executable of our project (app/main.f90) should just print out the factorial for three
specific values, so that we can check whether our factorial() function works as expected:
program main
use mylib, only: factorial
implicit none
print "('factorial(', i0, ') = ', i0)",&
& 0, factorial(0),&
& 1, factorial(1),&
& 2, factorial(2)
end program main
Now, let’s automatize the testing procedure. We will write three unit tests, which check the
factorial function for the specific input values 0, 1 and 2. The last test should intentionally fail
to demonstrate the error reporting. Create a file test/testapp.f90 with following content:
!> Fortuno unit tests
module test_mylib
use mylib, only : factorial
use fortuno_serial, only : is_equal, test => serial_case_item, check => serial_check, test_list
implicit none
contains
function tests()
type(test_list) :: tests
tests = test_list([&
test("factorial_0", test_factorial_0),&
test("factorial_1", test_factorial_1),&
test("factorial_2", test_factorial_2)&
])
end function tests
! Test: 0! = 1
subroutine test_factorial_0()
call check(factorial(0) == 1)
end subroutine test_factorial_0
! Test: 1! = 1
subroutine test_factorial_1()
call check(is_equal(factorial(1), 1))
end subroutine test_factorial_1
! Test: 2! = 3 (will fail to demonstrate the output of a failing test)
subroutine test_factorial_2()
! Failing check, you should obtain detailed info about the failure.
call check(&
& is_equal(factorial(2), 3),&
& msg="Test failed for demonstration purposes"&
)
end subroutine test_factorial_2
end module test_mylib
!> Test app driving Fortuno unit tests.
program testapp
use test_mylib, only : tests
use fortuno_serial, only : execute_serial_cmd_app
implicit none
call execute_serial_cmd_app(tests())
end program testapp
Finally, we create a CMakeLists.txt in the main folder to describe the project for CMake:
Note
This is a highly minimalistic CMake configuration file created for demonstration purpose only.
cmake_minimum_required(VERSION 3.22)
project(mylib VERSION 0.1.0 LANGUAGES Fortran)
include(FetchContent)
FetchContent_Declare(
Fortuno
GIT_REPOSITORY "https://github.com/fortuno-repos/fortuno.git"
GIT_TAG "v0.1.0"
FIND_PACKAGE_ARGS
)
FetchContent_MakeAvailable(Fortuno)
add_library(mylib src/mylib.f90)
add_executable(mylib_app app/main.f90)
target_link_libraries(mylib_app PRIVATE mylib)
add_executable(testapp test/testapp.f90)
target_link_libraries(testapp PRIVATE mylib Fortuno::fortuno_serial)
enable_testing()
add_test(NAME testapp COMMAND testapp)
Now, we configure the project by running:
FC=gfortran cmake -B build
Then we build our library, the main app and the tests.
cmake --build build
Finally we run the unit tests by invoking CTest:
ctest --test-dir build --verbsose
The expected output will show a test failure for the test application. The verbose output reveals that the test app launched three unit tests in total, one of them failing.
1: === Fortuno - flextensible unit testing framework for Fortran ===
1:
1: # Executing test items
1: ..F
1:
1: # Logged event(s)
1:
1: Failed [run] factorial_2
1:
1: -> Unsuccessful check
1: Check: 1
1: Msg: Test failed for demonstration purposes
1: Failure: mismatching integer values
1: Value1: 2
1: Value2: 3
1:
1:
1: # Test runs
1: Total: 3
1: Succeeded: 2 ( 66.7%)
1: Failed: 1 ( 33.3%)
1:
1: === FAILED ===
1/1 Test #1: testapp ..........................***Failed 0.00 sec
0% tests passed, 1 tests failed out of 1
Total Test time (real) = 0.01 sec
Congratulations! You’ve now implemented and completed your first set of Fortuno unit tests, assessing your project’s integrity.
See also
Section Key concepts contains a detailed analyzis of this minimal test application and also more information on some key concepts.
For real projects, consider to use the Fortran project cookiecutter template to generate a fully featured CMake setup following best practices.