M_verify(3fm) - [M_verify::INTRO] a collection of Fortran routines for supporting code development by providing error processing, debugging procedures and unit testing. (LICENSE:PD)
Synopsis
Quote
Description
Examples
Author
License
Module procedures
use M_verify, only : unit_check, unit_check_start, unit_check_done, unit_check_stop use M_verify, only : unit_check_good, unit_check_bad use M_verify, only : unit_check_msg use M_verify, only : debug use M_verify, only : fstop use M_verify, only : assertModule values
use M_verify, only : unit_check_limit, unit_check_keep_going use M_verify, only : unit_check_command
Do not let your victories go to your head, nor let your failures go to your heart.
The M_verify(3fm) Fortran module provides procedures and variables useful for providing error processing, debugging capabilities, and unit testing.
o allows for a user-defined command to be called to collect results or mail failure alerts, ... o supports easily composing a message from up to nine scalar intrinsic values and different message levels o allows stopping on first failure or continuing o provides for a non-zero exit code if any tests fail
unit_check_keep_going logical variable that can be used to turn off program termination on errors. unit_check_level An integer that can be used to specify different debug levels unit_check_command name of command to execute. Defaults to the name "".
unit_check_start(3f) start tests of a procedure and optionally call command NAME start ...
unit_check(3f) if expression is false optionally call command NAME badand stop program (by default)
unit_check_done(3f) call command NAME goodif no failures; else call
command NAME bad
unit_check_stop(3f) | |||||||||||||||||
stop program with exit value of 0 if no failures
else with an exit value of 1
| |||||||||||||||||
For unit testing, the existence of a command called "goodbad" is initially assumed. This is generally a script that makes entries for each unit in an SQLite data file which is then used to create CSV and HTML reports on the status of each unit. A sample goodbad(1) command written in the bash(1) shell and using the sqlite3(1) command should be included in this distribution as an example.
fstop(3f) calls STOP VALUE passing in a value (1-32), with optional message pdec(3f) write ASCII Decimal Equivalent (ADE) numbers vertically beneath string debug logical variable that can be tested by routines as a flag to process debug statements. The flexibility introduced by calling an external script or program is that The goodbad(1) command can be changed as desired to write CSV files or simple logs or to notify developers with e-mail as desired.
The routines in M_verify(3f) are often combined with the M_hashkeys(3fm) routines and various math and statistical routines to quickly create unit tests.
Comparisons of real values can be done with a tolerance with M_Compare_Float_Numbers(3fm), for example.
The intrinsics ANY(3f) and ALL(3f) are particularly useful in calls to unit_check(3f).
Sample program
!!program demo_unit_tests module M_msg__demo private public one !! regular routines public two !! regular routines public test_suite_M_demo !! special name for use with test_suite(1bash). containsExpected output:!! regular routines subroutine one() end subroutine one
subroutine two() end subroutine two
!! unit test subroutine test_suite_M_demo use M_verify, only: unit_check_start, unit_check use M_verify, only: unit_check_good, unit_check_bad, unit_check_done use M_verify, only: unit_check_msg, unit_check_stop implicit none integer :: i, j, k integer,allocatable :: array(:) integer :: arr(4)=[21,51,14,45] integer :: a=21, b=51, c=14, d=45 ! TEST-DRIVEN DEVELOPMENT ! optional set-up perform initialization operations common to all tests within a module i=1 j=2 k=3 array=[10,20,30,40,50,60,70] call test_one() call test_two() ! optional tear-down perform finalization operations common to all tests within a module contains
subroutine test_one() ! register an entry for specified name ("one") in database with status of zero (0) call unit_check_start(one)
! if mask test fails, can ! * produce a SUCCESS: or FAIL: message and stop program ! * change database status for specified entry to -1 and stop program, else continue ! * produce a SUCCESS: or FAIL: message and keep going ! * produce a FAIL: message if test fails but no SUCCESS: message if test passes call unit_check(one,i > 0,msg=I > 0)
! using ANY(3f) and ALL(3f) call unit_check(one,all([i,j,k] > 0), testing if everyone greater than zero) ! display message built of scalars as well call unit_check(one,all(.not.[i,j,k] == 4),for set ,i,j,k,testing if no one is equal to four)
! for tests that are hard to reduce to a logical test just call unit_check_bad(3f) if fail if(i+j+k < 1)then call unit_check_bad(one) endif
call unit_check_done(one,checks on "one" ended) end subroutine test_one
subroutine test_two ! use of all(3f), any(3f), merge(3f) can be useful ! if you know what these would produce ! write(*,*)[A,X,X,X,X,B] == B ! this would return an array, the last element having the value T, else F ! write(*,*)all([A,X,X,X,X,X] == X) ! this would return F ! write(*,*)any([A,X,X,X,X,X] == B) ! this would return F ! write(*,*)any([A,X,X,X,X,B] == B) ! this would return T ! write(*,*).not.all(array < 100) ! write(*,*)all(array < 100) ! write(*,*)all([a,b,c,d] == [21,51,14,45]) ! compare a list. This would return T ! write(*,*)all(arr == [21,51,14,45]) ! compare an array. This would return T ! you know how valuable ANY(3f) and ALL(3f) will be call unit_check_start(two,check on "two" passed) call unit_check(two, 1 > 0 .and. abs(10.10000-10.10001) < 0.0001,msg=two looks good) call unit_check_done(two,checks on "two" ended) end subroutine test_two
end subroutine test_suite_M_demo
end module M_msg__demo
program demo_M_verify use M_msg__demo, only: test_suite_M_demo use M_verify, only: unit_check_command, unit_check_keep_going,unit_check_level unit_check_command= unit_check_keep_going=.true. unit_check_level=0 call test_suite_M_demo end program demo_M_verify
unit_check: one SUCCESS:I > 0 unit_check: one SUCCESS:testing if everyone greater than zero unit_check: one SUCCESS:for set 1 2 3 testing if no one is equal to four unit_check_done: one PASSED GOOD:3 BAD:0unit_check: two SUCCESS:two looks good unit_check_done: two PASSED GOOD:1 BAD:0
John S. Urban
Public Domain