Original FruitThe original "Fruit" fortran unit test framework can be found here. It is released under BSD licence. Usagefruit fortran unit test libraryinput command list: [filename] : list modules and subroutines in the upper directory get: fileName routineType routineName : generate module_*.f90 unit: testName : generate test_testName.f90 gen: :generate unitTest clean: merge: testModuleFileName routineType routineName targetFileName : merge routine scons: :preparations for compilation, make SConstruct file help: print assert commands 1. Go to a directory containing a fortran source code you want to test and make a subdirectory named 'test' or something else. 2. Move to the 'test' directory and type 'fruit list'. You will get lists of fortran modules, subroutines, and functions in the upper directory. 3. Type 'fruit get ../code.f90 module I_want_to_test' and fruit will generate 'module_I_want_to_test.f90' file which contains the module. 4. Type 'fruit unit I_want_to_test' and fruit will generate test_I_want_to_test.f90' file. 5. Edit the test file. 6. Type 'fruit gen' and fruit will generate an executable file 'unitTest'. 7. Run 'unitTest'. Modify 'module_I_want_to_test.f90' file and go to step 5 if you want. 8. Type 'fruit merge module_I_want_to_test.f90 module I_want_to_test ../code.f90'. Fruit will generate a backup of '../code.f90' and change the module. * You can use subroutine and function instead of module. * Modules, subroutines, or functions you want to test must end with 'end module', 'end subroutine', or 'end function'. Unit test subroutinesassert_true assert_true(logical value,optional character message) If not true, print message. assert_equals If value2 is different from value1, print values and message. Value1 is the value you want to get. Value2 is the value you really got. assert_equals(value1, value2, optional character message) * value1, value2: integer, logical, character assert_equals(value1, value2, errorToleranceRange, optional character message) * value1, value2, errorToleranceRange: real, double assert_equals(array1,array2,integer arraySize, optional character message) *array1, array2: integer, character assert_equals(array1, array2, integer arraySize, errorToleranceRange, optional character message) *array1, array2, errorToleranceRange: real, double assert_equals(integer array1, integer array2, integer dimension1, integer dimension2, optional character message) *source code *fruit output $ lsHear is a source code I want to test. area.f90 $ cat area.f90The code has a module named 'element_area'. And it has a bug in subroutine 'area_tri' Modules, subroutines, or functions you want to test must end with 'end module', 'end subroutine', or 'end function'. module element_area type rectangle real:: width,height end type type triangle real:: width,height end type interface get_area module procedure area_rect module procedure area_tri end interface contains subroutine area_rect(rect,area) implicit none type(rectangle),intent(in):: rect real,intent(out):: area area = rect%width * rect%height end subroutine subroutine area_tri(tri,area) implicit none type(triangle),intent(in):: tri real,intent(out):: area area = 0.4 * tri%width * tri%height end subroutine end module $ mkdir testMake 'test' directory. Unit test codes will be placed in this directory. $ cd testMove to the 'test' directory. $ fruit list'fruit list' will show you lists of modules, subroutines, and functions in the upper directory. You can test modules, subroutines, and functions in '../*.f90' or '../*.f' files * MODULES from ../*.f90 ../area.f90:module element_area * SUBROUTINES from ../*.f90 ../area.f90: subroutine area_rect(rect,area) ../area.f90: subroutine area_tri(tri,area) * FUNCTION from ../*.f90 * SUBROUTINES from ../*.f grep: ../*.f: No such file or directory * FUNCTION from ../*.f grep: ../*.f: No such file or directory $ fruit get ../area.f90 module element_area'fruit get' will copy the module you want to test from the source code to a 'module_[name].f90' file. file=../area.f90 RoutineType=module RoutineName=element_area generating file module_element_area.f90 writing module element_area to module_element_area.f90 $ cat module_element_area.f90'module_[name].f90' file contains the module you want to test. ! generated by fruit get ! Sat Feb 14 10:10:04 2009 module element_area type rectangle real:: width,height end type type triangle real:: width,height end type interface get_area module procedure area_rect module procedure area_tri end interface contains subroutine area_rect(rect,area) implicit none type(rectangle),intent(in):: rect real,intent(out):: area area = rect%width * rect%height end subroutine subroutine area_tri(tri,area) implicit none type(triangle),intent(in):: tri real,intent(out):: area area = 0.4 * tri%width * tri%height end subroutine end module $ fruit unit element_area'fruit unit' generates a 'test_[name].f90' file. This is the main test code. test module: element_area created test file: test_element_area.f90 $ cat test_element_area.f90'test_[name].f90' file generated by 'fruit unit' is a skeleton of the unit test. Test codes should go into subroutine test_*. 'setup' and 'teardown' will be called before and after every 'test_*' call. !! unit test of module element_area !! Sat Feb 14 10:12:06 2009 module TEST_element_area use fruit contains subroutine setup end subroutine setup subroutine teardown end subroutine teardown subroutine test_ use element_area end subroutine end module TEST_element_area $ vi test_element_area.f90Edit 'test_[name].f90' file. $ cat test_element_area.f90This is the edited 'test_[name].f90' file. You can find subroutines and functions used for unit test by typing 'fruit help'. You can add specification by adding ' character :: spec="specification" '. !! unit test of module element_area !! Sat Feb 14 10:12:06 2009 module TEST_element_area use fruit contains subroutine setup end subroutine setup subroutine teardown end subroutine teardown subroutine test_rectangel use element_area type(rectangle):: myrect real:: area real:: tolmin=1.e-4 myrect%width=5.0 myrect%height=3.0 call get_area(myrect,area) call assert_equals(15.0,area,tolmin) end subroutine subroutine test_triangle use element_area type(triangle):: mytri real:: area real:: tolmin=1.e-4 character :: spec='triangle 4*3/2=6' mytri%width=4.0 mytri%height=3.0 call get_area(mytri,area) call assert_equals(6.0,area,tolmin) end subroutine end module TEST_element_area $ fruit gen'fruit gen' will automatically generate unit test program 'unitTest'. (It also generates ''SConstruct', fruit_gen.f90', '*.o', and '*.mod' files. You can delete those files generated by 'fruit gen' by typing 'fruit clean') generating unitTest scons: Reading SConscript files ... module name(s): ['element_area'] test name(s): ['test_element_area'] scons: done reading SConscript files. scons: Building targets ... gfortran -o test_element_area.o -c -O2 -I/home/user/lib/gplpython/gpl/f90 test_element_area.f90 gfortran -o fruit_gen.o -c -O2 -I/home/user/lib/gplpython/gpl/f90 fruit_gen.f90 gfortran -o unitTest module_element_area.o test_element_area.o fruit_gen.o -L/home/user/lib/gplpython/gpl/f90 -lfruit scons: done building targets. $ ./unitTestRun 'unitTest'. It shows the test results. [[[ Test module initialized ]]] . : successful assert, F : failed assert .. running test: test_rectangel . .. running test: test_triangle F // Un-satisfied spec: -- triangle 4*3/2=6 [[[ FRUIT summary: ]]] Some tests failed! -- Failed assertion messages: [test_triangle]: Expected [6.0000000], Got [4.8000002] -- end of failed assertion messages. Total asserts : 2 Successful : 1 Failed : 1 Successful rate: 50.00% Successful asserts / total asserts : [ 1 / 2 ] Successful cases / total cases : [ 0 / 0 ] [[[ End of FRUIT summary ]]] $ vi module_element_area.f90Fix the bug in the 'module_[name].f90' file. $ cat module_element_area.f90The bug is fixed now. ! generated by fruit get ! Sat Feb 14 10:10:04 2009 module element_area type rectangle real:: width,height end type type triangle real:: width,height end type interface get_area module procedure area_rect module procedure area_tri end interface contains subroutine area_rect(rect,area) implicit none type(rectangle),intent(in):: rect real,intent(out):: area area = rect%width * rect%height end subroutine subroutine area_tri(tri,area) implicit none type(triangle),intent(in):: tri real,intent(out):: area area = 0.5 * tri%width * tri%height end subroutine end module $ fruit genCompile 'unitTest' again. generating unitTest scons: Reading SConscript files ... module name(s): ['element_area'] test name(s): ['test_element_area'] scons: done reading SConscript files. scons: Building targets ... gfortran -o module_element_area.o -c -O2 -I/home/user/lib/gplpython/gpl/f90 module_element_area.f90 gfortran -o test_element_area.o -c -O2 -I/home/user/lib/gplpython/gpl/f90 test_element_area.f90 gfortran -o fruit_gen.o -c -O2 -I/home/user/lib/gplpython/gpl/f90 fruit_gen.f90 gfortran -o unitTest module_element_area.o test_element_area.o fruit_gen.o -L/home/user/lib/gplpython/gpl/f90 -lfruit scons: done building targets. $ ./unitTestEvery test is successful now. [[[ Test module initialized ]]] . : successful assert, F : failed assert .. running test: test_rectangel . .. running test: test_triangle . [[[ FRUIT summary: ]]] SUCCESSFUL! No messages Total asserts : 2 Successful : 2 Failed : 0 Successful rate: 100.00% Successful asserts / total asserts : [ 2 / 2 ] Successful cases / total cases : [ 0 / 0 ] [[[ End of FRUIT summary ]]] $ fruit merge module_element_area.f90 module element_area ../area.f90Merge the modified module to the original source code. 'fruit merge' backs up the original file and replace the buggy module with the modified module. FRUIT merge routine: module element_area input fileName: module_element_area.f90 target fileName: ../area.f90 number of substitution=1 backup: ../area.f90.bak001 $ cd ..$ lsarea.f90 area.f90.bak001 test $ cat area.f90The module is fine now. module element_area type rectangle real:: width,height end type type triangle real:: width,height end type interface get_area module procedure area_rect module procedure area_tri end interface contains subroutine area_rect(rect,area) implicit none type(rectangle),intent(in):: rect real,intent(out):: area area = rect%width * rect%height end subroutine subroutine area_tri(tri,area) implicit none type(triangle),intent(in):: tri real,intent(out):: area area = 0.5 * tri%width * tri%height end subroutine end module |