M_debug(3fm) - [M_debug] 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
use M_debug, only : unit_check, unit_check_start, unit_check_good, unit_check_bad, unit_check_done use M_debug, only : unit_check_limit, unit_check_keep_going, unit_check_command use M_debug, only : unit_check_msg use M_debug, only : debug use M_debug, only : fstop use M_debug, only : stderr use M_debug, only : assert
Do not let your victories go to your head, nor let your failures go to your heart.
The M_debug(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
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 "goodbad".
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 badunit_check_good(3f) call command command NAME good unit_check_bad(3f) call command command NAME bad and stop program by default unit_check_msg(3f) write message
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 stderr(3f) Write message on stderr 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_debug(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_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_debug, only: unit_check_start, unit_check use M_debug, only: unit_check_good, unit_check_bad, unit_check_done use M_debug, only: unit_check_msg 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.gt.0,msg=I > 0)
! using ANY(3f) and ALL(3f) call unit_check(one,all([i,j,k].gt.0), testing if everyone greater than zero) ! display message built of scalars as well call unit_check(one,all(.not.[i,j,k].eq.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.lt.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].eq.B ! this would return an array, the last element having the value T, else F ! write(*,*)all([A,X,X,X,X,X].eq.X) ! this would return F ! write(*,*)any([A,X,X,X,X,X].eq.B) ! this would return F ! write(*,*)any([A,X,X,X,X,B].eq.B) ! this would return T ! write(*,*).not.all(array.lt.100) ! write(*,*)all(array.lt.100) ! write(*,*)all([a,b,c,d].eq.[21,51,14,45]) ! compare a list. This would return T ! write(*,*)all(arr.eq.[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.gt.0 .and. abs(10.10000-10.10001).lt.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_demo
program demo_M_debug use M_demo, only: test_suite_M_demo use M_debug, 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_debug
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
M_debug (3) | October 17, 2020 |