Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ jobs:

- name: Test episode 5
run: docker build . -f docker/Dockerfile.ep-5

- name: Test episode 6
run: docker build . -f docker/Dockerfile.ep-6
41 changes: 34 additions & 7 deletions docker/Dockerfile.ep-3
Original file line number Diff line number Diff line change
@@ -1,13 +1,40 @@
FROM ghcr.io/ucl-arc/fortran-unit-testing-exercises:main

COPY --chown=vscode episodes/3-fortran-unit-test-syntax /home/vscode/3-fortran-unit-test-syntax
COPY --chown=vscode episodes/3-writing-your-first-unit-test /home/vscode/3-writing-your-first-unit-test

WORKDIR /home/vscode/3-fortran-unit-test-syntax/solution
# Test the Challenge code

# build tests with cmake
RUN cmake -B build -DCMAKE_PREFIX_PATH=/home/vscode/pfunit/build/installed && \
cmake --build build
WORKDIR /home/vscode/3-writing-your-first-unit-test/challenge

# test pfunit with ctest
RUN ctest --test-dir build --output-on-failure
# Rebuild without pFUnit
RUN cmake -B build-std && \
cmake --build build-std

# Test without pFUnit
RUN ctest --test-dir build-std --output-on-failure


# Test the Solution code

# Cleanup directories
RUN rm -rf build-std build-pf

# Copy solution code
RUN cp ../solution/test_temp_conversions.f90 test/standard_fortran/ && \
cp ../solution/test_temp_conversions.pf test/pfunit/

RUN ls test/pfunit/

# Rebuild without pFUnit
RUN cmake -B build-std && \
cmake --build build-std

# Rebuild with pFUnit
RUN cmake -B build-pf -DCMAKE_PREFIX_PATH=/home/vscode/pfunit/build/installed && \
cmake --build build-pf

# Test without pFUnit
RUN ctest --test-dir build-std --output-on-failure

# Test with pFUnit
RUN ctest --test-dir build-pf --output-on-failure
8 changes: 3 additions & 5 deletions docker/Dockerfile.ep-4
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
FROM ghcr.io/ucl-arc/fortran-unit-testing-exercises:main

COPY --chown=vscode episodes/4-debugging-a-broken-test /home/vscode/4-debugging-a-broken-test
COPY --chown=vscode episodes/4-fortran-unit-test-syntax /home/vscode/4-fortran-unit-test-syntax

WORKDIR /home/vscode/4-debugging-a-broken-test/challenge

# Fix intentional bug in code
RUN sed -i -E 's/.*matrix\(row, col\) = temp_matrix\(row, col\)/matrix\(col, row\) = temp_matrix\(row, col\)/g' src/matrix_transforms.f90
WORKDIR /home/vscode/4-fortran-unit-test-syntax/solution

# build tests with cmake
RUN cmake -B build -DCMAKE_PREFIX_PATH=/home/vscode/pfunit/build/installed && \
cmake --build build

# test pfunit with ctest
RUN ctest --test-dir build --output-on-failure

23 changes: 7 additions & 16 deletions docker/Dockerfile.ep-5
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
FROM ghcr.io/ucl-arc/fortran-unit-testing-exercises:main

COPY --chown=vscode episodes/5-testing-parallel-code /home/vscode/5-testing-parallel-code
COPY --chown=vscode episodes/5-debugging-a-broken-test /home/vscode/5-debugging-a-broken-test

WORKDIR /home/vscode/5-testing-parallel-code/challenge
WORKDIR /home/vscode/5-debugging-a-broken-test/challenge

# Fix intentional bug in code
RUN mv ../solution/test_find_steady_state.pf ./test/ && \
echo "set(test_find_steady_state_src \${test_srcs})\n\
list(FILTER test_find_steady_state_src INCLUDE REGEX \".*test_find_steady_state.pf\")\n\
\n\
add_pfunit_ctest (pfunit_find_steady_state_tests\n\
TEST_SOURCES \${test_find_steady_state_src}\n\
LINK_LIBRARIES sut\n\
MAX_PES 8\n\
)\n\
" >> test/CMakeLists.txt
RUN sed -i -E 's/.*matrix\(row, col\) = temp_matrix\(row, col\)/matrix\(col, row\) = temp_matrix\(row, col\)/g' src/matrix_transforms.f90

# build tests with cmake
RUN cmake -B build-cmake -DCMAKE_PREFIX_PATH=/home/vscode/pfunit/build/installed && \
cmake --build build-cmake
RUN cmake -B build -DCMAKE_PREFIX_PATH=/home/vscode/pfunit/build/installed && \
cmake --build build

# test with ctest, allowing MPI to oversubscribe
RUN ctest --test-dir build-cmake --output-on-failure
# test pfunit with ctest
RUN ctest --test-dir build --output-on-failure
24 changes: 24 additions & 0 deletions docker/Dockerfile.ep-6
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM ghcr.io/ucl-arc/fortran-unit-testing-exercises:main

COPY --chown=vscode episodes/6-testing-parallel-code /home/vscode/6-testing-parallel-code

WORKDIR /home/vscode/6-testing-parallel-code/challenge

# Fix intentional bug in code
RUN mv ../solution/test_find_steady_state.pf ./test/ && \
echo "set(test_find_steady_state_src \${test_srcs})\n\
list(FILTER test_find_steady_state_src INCLUDE REGEX \".*test_find_steady_state.pf\")\n\
\n\
add_pfunit_ctest (pfunit_find_steady_state_tests\n\
TEST_SOURCES \${test_find_steady_state_src}\n\
LINK_LIBRARIES sut\n\
MAX_PES 8\n\
)\n\
" >> test/CMakeLists.txt

# build tests with cmake
RUN cmake -B build-cmake -DCMAKE_PREFIX_PATH=/home/vscode/pfunit/build/installed && \
cmake --build build-cmake

# test with ctest, allowing MPI to oversubscribe
RUN ctest --test-dir build-cmake --output-on-failure
15 changes: 15 additions & 0 deletions episodes/3-writing-your-first-unit-test/challenge/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)

# Set project name
project(
"temp_conversions"
LANGUAGES "Fortran"
VERSION "0.0.1"
DESCRIPTION "Library for converting between various temperatures"
)

# Define src file(s)
set(PROJ_SRC_FILES "${PROJECT_SOURCE_DIR}/src/temp_conversions.f90")

enable_testing()
add_subdirectory("test")
63 changes: 63 additions & 0 deletions episodes/3-writing-your-first-unit-test/challenge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Writing your first unit test - Standard Fortran

This exercise aims to teach the principles of unit testing and how to write a good
unit test. The tests within this challenge are intended to be written using standard
Fortran without the use of a testing framework, in order to teach the principles alone.

## The src code

In [src](./src) you will find a library [temp_conversions.f90](./src/temp_conversions.f90)
which provides functions for converting between various units of temperature. The functions
provided are...

- **fahrenheit_to_celsius**: Which takes in a temperature in Fahrenheit and returns a temperature in Celsius.
- **celsius_to_kelvin**: Which takes in a temperature in Celsius and returns a temperature in Kelvin.

## The tasks

### Part 1 - Test with Standard Fortran

Imagine you wish to use the temp_conversions library to convert Fahrenheit to Kelvin. We
know that there is no function which does this direct conversion. With this is mind, write
a test, or tests, to give you confidence that temp_conversions can correctly convert
Fahrenheit to Kelvin.

To get you started, the file [test_temp_conversions.f90](./test/standard_fortran/test_temp_conversions.f90)
has been provided. `test_temp_conversions.f90` contains some boilerplate to make writing a
test easier. There is an empty test subroutine `test` provided which takes in a logical
`passed` and a character array `failure_message`. The logical `passed` should indicate if
the test was successful. The character array `failure_message`, should be populated with a
message that will be printed to the terminal in the event that `passed` is `.false.`. Once
the test subroutine is written it should be called within the main body of the test program
as indicated in `test_temp_conversions.f90`.

> Not: If you add a new test file or change the name of `test_temp_conversions.f90`, you will
> need to update list of tests (`test_src`) in [test/pfunit/CMakeLists.txt](./test/pfunit/CMakeLists.txt)

### Part 2 - Convert tests to use pFUnit

Convert your tests from [Part 1](#part-1---test-with-standard-fortran), to use
[pFUnit](https://github.com/Goddard-Fortran-Ecosystem/pFUnit).

A file [test_temp_conversions.pf](./test/pfunit/test_temp_conversions.pf) containing a template
for your pFUnit test(s) has been provided. Comments within this file indicate the aspects of
the pFUnit test you must write.

> Note: This template has been written to facilitate conversion of
> [test_temp_conversions.f90](./test/standard_fortran/test_temp_conversions.f90) as provided with this repo
> to pFUnit. If your version of test_temp_conversions.f90, produced in Part 1, is significantly
> different, You may prefer to use a different structure to the one provided in the template.

To build and run your pFUnit test(s) you must add the pFUnit lib to the `CMAKE_PREFIX_PATH`
when building via the following command.

```bash
cmake -B build -DCMAKE_PREFIX_PATH=/path/to/pfunit/install
cmake --build build
ctest --test-dir build --output-on-failure
```

If your test does not get built, ensure you have added it to the list of tests (`test_src`)
in [test/pfunit/CMakeLists.txt](./test/pfunit/CMakeLists.txt)

> If you are using the devcontainer, there is an installation of pFUnit at /home/vscode/pfunit/build/installed
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module temp_conversions
implicit none
private
public :: fahrenheit_to_celsius, celsius_to_kelvin

contains

function fahrenheit_to_celsius(fahrenheit) result(celsius)
real, intent(in) :: fahrenheit
real :: celsius
celsius = (fahrenheit - 32.0) * 5.0 / 9.0
end function fahrenheit_to_celsius

function celsius_to_kelvin(celsius) result(kelvin)
real, intent(in) :: celsius
real :: kelvin
kelvin = celsius + 273.15
end function celsius_to_kelvin

end module temp_conversions
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
find_package(PFUNIT QUIET)

add_subdirectory("standard_fortran")

if (PFUNIT_FOUND)
add_subdirectory("pfunit")
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.f90
*.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
message(STATUS "Using pFUnit")

set(TEST_DIR "${PROJECT_SOURCE_DIR}/test/pfunit")

# Create library for src code
add_library (sut STATIC ${PROJ_SRC_FILES})

# List all test files
set(test_srcs "test_temp_conversions.pf")

add_pfunit_ctest (pfunit_test_temp_conversions_exec
TEST_SOURCES ${test_srcs}
LINK_LIBRARIES sut # your application library
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module test_temp_conversions
use temp_conversions, only : fahrenheit_to_celsius, celsius_to_kelvin
! allow(C121)
use funit
implicit none

public

!> Test parameter type to package the test parameters
! Your changes here...

!> Test case type to specify the style of test (paramaterized)
! Your changes here...

contains

!*************************************************************************!
! Test fahrenheit_to_celsius !
!*************************************************************************!

!> Test suite for tests of fahrenheit_to_celsius
! Your changes here...

!> Unit test subroutine for fahrenheit_to_celsius
! Your changes here...

!*************************************************************************!
! Test celsius_to_kelvin !
!*************************************************************************!

!> Test suite for tests of celsius_to_kelvin
! Your changes here...

!> Unit test subroutine for celsius_to_kelvin
! Your changes here...

!*************************************************************************!
! Constructors !
!*************************************************************************!

!> Constructor for converting test parameters into a test case
! Your changes here...

!> Constructor for converting test parameters into a string
! Your changes here...

end module test_temp_conversions
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
message(STATUS "Using standard Fortran")

set(TEST_DIR "${PROJECT_SOURCE_DIR}/test/standard_fortran")

# Define test file(s)
set(test_srcs "test_temp_conversions.f90")

# Build test executable
add_executable(test_temp_conversions_exec
"${PROJ_SRC_FILES}"
"${test_srcs}"
)

# Add test as ctest
add_test(NAME test_temp_conversions COMMAND test_temp_conversions_exec)

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
program test_temp_conversions
use temp_conversions, only : fahrenheit_to_celsius, celsius_to_kelvin
implicit none

integer :: i

! Declare passed and failure message arrays to be set by a test subroutine(s)
logical :: passed(1)
character(len=200) :: failure_message(1)

! Call your test subroutine(s) here
call test(passed(1), failure_message(1))

if (all(passed)) then
write(*,*) "All tests passed!"
else
do i = 1, size(passed)
if (.not. passed(i)) then
write(*,*) "FAIL: ", trim(failure_message(i))
end if
end do
stop 1
end if

contains
!> The test subroutine
subroutine test(passed, failure_message)
!> A logical to track whether the test passed or not
logical, intent(out) :: passed
!> A failure message to be displayed if passed is false
character(len=200), intent(out) :: failure_message

! No test has been written yet so just default passed to .true.
passed = .true.

! Populate the failure message
write(failure_message, '(A,A,A)') "It is useful to include input, expected output and actual output values here. To do ", &
"that, replace (A,A) with the correct format for your values, for example ", &
"(A,F7.2,A,F7.2,A,F7.2)."
end subroutine test
end program test_temp_conversions
Loading