5. Generated tests
fobis testgen MODULE writes a test program for a module's public procedures. Without a model it gives a skeleton: the program, the declarations of every argument, and the calls. FoBiS builds it in a scratch copy of the project before showing it, so what you see compiles:
$ fobis testgen gauss_legendre --skeleton
Module gauss_legendre (src/gauss_legendre.F90): building the skeleton in a scratch copy...
Test of module gauss_legendre:
test gauss_nodes
Skeleton (builds; give the inputs values and add the checks):
! Test of module gauss_legendre: generated by fobis testgen; review it before committing.
program test_gauss_legendre
use gauss_legendre
use quad_rules, only : R8P
implicit none
integer :: failures = 0
call test_gauss_nodes()
if (failures > 0) then
print '(a,i0,a)', 'FAILED: ', failures, ' check(s)'
error stop 1
end if
print '(a)', 'test_gauss_legendre: all checks passed'
contains
subroutine test_gauss_nodes()
integer :: n
real(r8p), allocatable :: x(:)
real(r8p), allocatable :: w(:)
! TODO: give the inputs values, then check the outputs against expected values
call gauss_nodes(n=n, x=x, w=w)
end subroutine test_gauss_nodes
end program test_gauss_legendre
(fobis testgen gauss_legendre --apply writes it to test/)With the model, the skeleton is the starting point. The model fills in inputs and checks, and FoBiS builds and runs the program in the scratch copy. It refuses a program that does not compile, fails, or checks nothing, and sends the compiler's errors or the failing check back, for up to three more attempts:
$ fobis testgen gauss_legendre --max 2
Module gauss_legendre (src/gauss_legendre.F90): building the skeleton in a scratch copy...
[llm] gauss_legendre: attempt 1/4
Using the running llama-server at http://127.0.0.1:41675
Test of module gauss_legendre:
test gauss_nodes
Accepted after 1 attempt(s): builds, runs (exit 0), calls gauss_nodes; 6 check(s). Review it before committing.
Model notes: The checks verify that the nodes and weights returned by the `gauss_nodes` procedure are correct for a 3-point rule. The expected values were obtained by analyzing the implementation of the procedure and understanding the properties of the Legendre polynomial.
program test_gauss_legendre
use gauss_legendre
use quad_rules, only : R8P
implicit none
integer :: failures = 0
call test_gauss_nodes()
if (failures > 0) then
print '(a,i0,a)', 'FAILED: ', failures, ' check(s)'
error stop 1
end if
print '(a)', 'test_gauss_legendre: all checks passed'
contains
subroutine test_gauss_nodes()
integer :: n
real(r8p), allocatable :: x(:)
real(r8p), allocatable :: w(:)
! TODO: give the inputs values, then check the outputs against expected values
n = 3
allocate(x(n), w(n))
call gauss_nodes(n=n, x=x, w=w)
if (abs(x(1) + 0.774596669241483_R8P) > 1.e-15_R8P) then
print '(a, ES10.3, a, ES10.3)', 'gauss_nodes: x(1) = ', x(1), ', expected = ', -0.774596669241483_R8P
failures = failures + 1
end if
if (abs(x(2) - 0.000000000000000_R8P) > 1.e-15_R8P) then
print '(a, ES10.3, a, ES10.3)', 'gauss_nodes: x(2) = ', x(2), ', expected = ', 0.000000000000000_R8P
failures = failures + 1
end if
if (abs(x(3) - 0.774596669241483_R8P) > 1.e-15_R8P) then
print '(a, ES10.3, a, ES10.3)', 'gauss_nodes: x(3) = ', x(3), ', expected = ', 0.774596669241483_R8P
failures = failures + 1
end if
if (abs(w(1) - 0.555555555555556_R8P) > 1.e-15_R8P) then
print '(a, ES10.3, a, ES10.3)', 'gauss_nodes: w(1) = ', w(1), ', expected = ', 0.555555555555556_R8P
failures = failures + 1
end if
if (abs(w(2) - 0.888888888888889_R8P) > 1.e-15_R8P) then
print '(a, ES10.3, a, ES10.3)', 'gauss_nodes: w(2) = ', w(2), ', expected = ', 0.888888888888889_R8P
failures = failures + 1
end if
if (abs(w(3) - 0.555555555555556_R8P) > 1.e-15_R8P) then
print '(a, ES10.3, a, ES10.3)', 'gauss_nodes: w(3) = ', w(3), ', expected = ', 0.555555555555556_R8P
failures = failures + 1
end if
end subroutine test_gauss_nodes
end program test_gauss_legendre
(fobis testgen gauss_legendre --apply writes it to test/)Accepted at the first attempt: the nodes ±0.774596669241483 (that is ±sqrt(3/5)) and 0 and the weights 5/9, 8/9, 5/9 are those of the 3-point Gauss-Legendre rule, and the program passed against the real implementation. "Review it before committing" is meant: a test checks what the model believed. Here the values can be checked against any table; for a procedure without a known answer, the checks may only repeat what the code does. --apply writes the program to the test directory, never over an existing file.

coder-7b on an NVIDIA A30 of a cluster node, reached through ssh; recorded in real time.
What you learned
fobis testgen, --skeleton, --context, --apply, what an accepted test proves. Reference: test generation.