Skip to content

Commit b0f044c

Browse files
authored
Merge pull request #95 from BerkeleyLab/doc-assert
Feature: logical_assert thinner wrapper around Assert's assert_always
2 parents 49cdc3c + d801b0c commit b0f044c

File tree

5 files changed

+107
-34
lines changed

5 files changed

+107
-34
lines changed

include/julienne-assert-macros.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
! julienne-assert-macros.h: provides preprocessor-based assertion macros
22
! that are guaranteed to compile away statically when disabled.
33

4+
#include "assert_macros.h"
5+
46
#ifndef ASSERTIONS
57
! Assertions are off by default
68
#define ASSERTIONS 0
@@ -10,7 +12,7 @@
1012
#undef call_julienne_assert
1113

1214
#if ASSERTIONS
13-
# define call_julienne_assert(assertion) call call_julienne_assert_(assertion, __FILE__, __LINE__)
15+
# define call_julienne_assert(assertion) call call_julienne_assert_(assertion, __FILE__, __LINE__, "call_julienne_assert(" // CPP_STRINGIFY_SOURCE(assertion) // ") ")
1416
#else
1517
# define call_julienne_assert(assertion)
1618
#endif

src/julienne/julienne_assert_m.f90

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,64 @@ module julienne_assert_m
1010
public :: call_julienne_assert_
1111
public :: julienne_assert
1212

13+
interface julienne_assert
14+
module procedure idiomatic_assert
15+
module procedure logical_assert
16+
end interface
17+
1318
interface call_julienne_assert_
1419

15-
pure module subroutine julienne_assert(test_diagnosis, file, line)
16-
!! This subroutine wraps the Assert library's `assert` subroutine.
20+
pure module subroutine idiomatic_assert(test_diagnosis, file, line, description)
21+
!! Error terminate if `test_diagnosis%test_passed() == .false.`, in which
22+
!! case the stop code contains
23+
!!
24+
!! 1. The description argument if present and if called via
25+
!! `julienne_assert; otherwise, a copy of the invoking statement,
26+
!! 2. The value of `test_diagnosis%diagnostics_string(),`,
27+
!! 3. The file name if present, and
28+
!! 4. The line number if present.
1729
!!
18-
!! Use Cases
19-
!! ---------
20-
!! 1. Invoke julienne_assert via the generic interface `call_julienne_assert` to
21-
!! facilitate complete removal when compiling without the flag `-DASSERTIONS`.
22-
!! 2. Invoke julienne_assert via direct procedure call to guarantee execution.
30+
!! Most compilers write the stop code to `error_unit`.
2331
!!
2432
!! Usage
2533
!! -----
26-
!! Make the only actual argument an expression containing `test_diagnosis_t` defined
27-
!! operations, such as `x .approximates. y .within. tolerance`. The expression
28-
!! result will be a `test_diagnosis_t` object on which `julienne_assert` will invoke
29-
!! the `diagnostics_string()` type-bound procedure, the result of which julienne_assert
30-
!! will include in the stop code of an `error stop` if the expresssion is untrue.
31-
!! The resulting stop code will contain such information as the operand values and
32-
!! roles (expected value, actual value, tolerance value). In use case 1, compiling
33-
!! with `-DASSERTIONS` will cause the preprocessor to insert the corresponding
34-
!! invocations's line number and the encompassing file's name as the `file` and `line`
35-
!! arguments, respectively, which `julienne_assert` will include in the stop code.
36-
!! Most compilers will write the stop code to `error_unit`.
37-
!!
38-
!! If a literal reproduction of the test expression suffices, such as when the
39-
!! expression is `allocated(a)`, then instead invoke the Assert library's `assert`
40-
!! subroutine by that library's `call_assert` macro or by direct call.
41-
!! When invoking via the macro, make the only actual argument, `assertion`, a
42-
!! `logical` expression. Then if compiling with `-DASSERTIONS` and if the assertion
43-
!! evaluates to `.false.`, the stop code will include the text of the expression
44-
!! argument, the file name, and the line number of the `call_assert` macro invocation.
34+
!!
35+
!! `call julienne_assert(.all. (["a","b","c"] .isBefore. "efg"))`
36+
!! `call_julienne_assert(.all. (["a","b","c"] .isBefore. "efg"))`
37+
!!
38+
!! The first line above guarantees execution, whereas the second ensures
39+
!! removal when compiled without `-DASSERTIONS`. When invoked via macro,
40+
!! the second line also causes the automatic insertion of items 1-4 above.
4541
implicit none
4642
type(test_diagnosis_t), intent(in) :: test_diagnosis
47-
character(len=*), intent(in), optional :: file
43+
character(len=*), intent(in), optional :: file, description
44+
integer, intent(in), optional :: line
45+
end subroutine
46+
47+
pure module subroutine logical_assert(assertion, file, line, description)
48+
!! Error terminate if `assertion == .false.`, in which case the stop code
49+
!! contains
50+
!!
51+
!! - The description argument if present and if called via
52+
!! `julienne_assert; otherwise, a copy of the invoking statement,
53+
!! - The file name if present, and
54+
!! - The line number if present.
55+
!!
56+
!! Most compilers write the stop code to `error_unit`.
57+
!!
58+
!!
59+
!! Usage
60+
!! -----
61+
!!
62+
!! `call julienne_assert(associated(A))`
63+
!! `call_julienne_assert(associated(A))`
64+
!!
65+
!! The first line above guarantees execution, whereas the second ensures
66+
!! removal when compiled without `-DASSERTIONS`. When invoked via macro,
67+
!! the second line also causes the automatic insertion of items 1-4 above.
68+
implicit none
69+
logical, intent(in) :: assertion
70+
character(len=*), intent(in), optional :: file, description
4871
integer, intent(in), optional :: line
4972
end subroutine
5073

src/julienne/julienne_assert_s.f90

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,32 @@
77

88
contains
99

10-
module procedure julienne_assert
10+
module procedure idiomatic_assert
11+
character(len=:), allocatable :: description_
12+
1113
if (.not. test_diagnosis%test_passed()) then
12-
associate(diagnostics_string => test_diagnosis%diagnostics_string())
13-
call assert_always(.false., diagnostics_string%string(), file, line)
14-
end associate
14+
if (present(description)) then
15+
description_ = new_line('') // description // new_line('') // test_diagnosis%diagnostics_string()
16+
else
17+
description_ = new_line('') // test_diagnosis%diagnostics_string()
18+
end if
19+
call assert_always(.false., description_, file, line)
1520
end if
21+
22+
end procedure
23+
24+
module procedure logical_assert
25+
character(len=:), allocatable :: description_
26+
27+
if (.not. assertion) then
28+
if (present(description)) then
29+
description_ = new_line('') // description // new_line('')
30+
else
31+
description_ = new_line('')
32+
end if
33+
call assert_always(.false., description_ , file, line)
34+
end if
35+
1636
end procedure
1737

1838
end submodule julienne_assert_s

test/test-julienne_assert-intentional-failure.F90 renamed to test/idiomatic-assertion-failure-test.F90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ program test_julienne_assert_intentional_failure
1111
associate(command_line => command_line_t())
1212
if (.not. command_line%argument_present([character(len=len("--help"))::"--help","-h"])) then
1313
#ifdef RUN_FALSE_ASSERTIONS
14-
print '(a)', new_line('') // 'Test julienne_assert intentional failure: ' // new_line('')
14+
print '(a)', new_line('') // 'Test the intentional failure of an idiomatic assertion: ' // new_line('')
1515
call_julienne_assert(1 .equalsExpected. 2)
1616
#else
1717
print *
1818
print '(a)', 'Skipping the test in ' // __FILE__ // '.'
19-
print '(a)', 'Add the following to your fpm command to test assertion failure: --flag "-DASSERTIONS -DRUN_FALSE_ASSERTIONS"'
19+
print '(a)', 'Add the following to your fpm command to test assertion failures: --flag "-DASSERTIONS -DRUN_FALSE_ASSERTIONS"'
2020
print *
2121
#endif
2222
end if
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
! Copyright (c) 2024-2025, The Regents of the University of California and Sourcery Institute
2+
! Terms of use are as specified in LICENSE.txt
3+
4+
#include "julienne-assert-macros.h"
5+
6+
program test_julienne_assert_intentional_failure
7+
!! Conditionally test an assertion that is hardwired to fail.
8+
use julienne_m, only : call_julienne_assert_, command_line_t, operator(.equalsExpected.)
9+
implicit none
10+
integer, allocatable :: array(:)
11+
12+
associate(command_line => command_line_t())
13+
if (.not. command_line%argument_present([character(len=len("--help"))::"--help","-h"])) then
14+
#ifdef RUN_FALSE_ASSERTIONS
15+
print '(a)', new_line('') // 'Test the intentional failure of a logical assertion: ' // new_line('')
16+
if (allocated(array)) deallocate(array)
17+
call_julienne_assert(allocated(array))
18+
#else
19+
print *
20+
print '(a)', 'Skipping the test in ' // __FILE__ // '.'
21+
print '(a)', 'Add the following to your fpm command to test assertion failures: --flag "-DASSERTIONS -DRUN_FALSE_ASSERTIONS"'
22+
print *
23+
#endif
24+
end if
25+
end associate
26+
27+
end program
28+

0 commit comments

Comments
 (0)