Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
ddd1aa9
feat(test): define/use test harness, test fixtures
rouson Aug 9, 2025
c2f951c
refac(test_harness): mv help I/O to harness report
rouson Aug 9, 2025
a738cac
feat(test):don't run intentional fail if --help/-h
rouson Aug 9, 2025
b0c034b
fix(intentional-fail): mv 'implicit none'
rouson Aug 9, 2025
162628d
feat(test_harness): automatically report tally
rouson Aug 9, 2025
3bed85c
refac(test): further simplify test driver
rouson Aug 9, 2025
d40d334
feat: default preprocessing of test main
rouson Aug 9, 2025
5820af7
feat(test_diagnosis): add .expect. operator
rouson Aug 9, 2025
559c1ed
fix(include): use GCC minor and patch versions
rouson Aug 9, 2025
f110cbc
fix: work around gfortran 14 builds
rouson Aug 9, 2025
ebc5be1
fix(command_line_test): mv code
rouson Aug 9, 2025
fe2f192
chore(CI): use more descriptive names
rouson Aug 9, 2025
f4c44d1
chore(CI): print versions, set env vars
rouson Aug 9, 2025
56e126f
fix(include/language-support): add compilers
rouson Aug 9, 2025
5ea38ec
chore(test_fixture): priv component, non-alloc arg
rouson Aug 10, 2025
9e75285
chore(test_harness): priv component, non-alloc arg
rouson Aug 10, 2025
932edcc
doc(test/main,harness): add/edit comments
rouson Aug 10, 2025
db2981e
chore(test_harness): mk component array non-poly
rouson Aug 10, 2025
6120c8f
refac(test_harness): disambiguate binding name
rouson Aug 10, 2025
4df3368
refac(test_harness): rm dummy args
rouson Aug 10, 2025
7ed8437
refac(test_harness): collapse print statements
rouson Aug 10, 2025
664578e
chore(CI): fix compiler names
rouson Aug 10, 2025
a928d8f
fix(CI): only run command-line tests if not in CI
rouson Aug 11, 2025
b757a10
Merge branch 'test-harness-feature' of github.com:berkeleylab/julienn…
rouson Aug 11, 2025
cff34b6
fix(CI): fix logic to skip command_line_t tests
rouson Aug 11, 2025
03a05c8
fix(command_line_test_t): fix output statements
rouson Aug 11, 2025
06ca107
fix(CI): use legacy-main with GCC version < 14.3.0
rouson Aug 11, 2025
5a5ab9c
fix(CI): ensure non-empty test-suite main programs
rouson Aug 11, 2025
e049da7
fix(command_line_test): separate ptr decl/def
rouson Aug 11, 2025
0c9dd15
fix(command_line_test): gfoetran 12 workaround
rouson Aug 11, 2025
c94c0cf
doc(README): add example of operator(.expect.)
rouson Aug 11, 2025
d268f93
doc(demo): refactor driver using test harness
rouson Aug 11, 2025
f49a117
doc(demo): update README.md
rouson Aug 11, 2025
0d34d8c
chore: use .f90 file extension
rouson Aug 11, 2025
3204809
chore: rm vector_test_description_t tests
rouson Aug 11, 2025
0fd7b73
BREAKING: rm vector_test_description_{m,s}
rouson Aug 11, 2025
8a3379a
doc(vector_test_description): rm all references
rouson Aug 11, 2025
90dd463
fix(I/O): improve language in test summaries
rouson Aug 11, 2025
1d97f90
refac(test_describe): absorb test filter logic
rouson Aug 11, 2025
fa98397
fix(assert_test): typo in associate statement
rouson Aug 11, 2025
df778fa
refac(test_description_substring): rm global var
rouson Aug 12, 2025
b027deb
refac(test_t): add run command
rouson Aug 12, 2025
3b21544
refac(test%report): tighten variable scoping
rouson Aug 12, 2025
d004e68
refac(compiler_t): get compiler info from preprocessor
rouson Aug 13, 2025
415979a
fix: typo in GCC macro
rouson Aug 13, 2025
3ad16f1
Update test/main.F90
rouson Aug 13, 2025
4d944bc
Update include/language-support.F90
rouson Aug 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build-with-flang.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build with LLVM Flang
name: Build with LLVM Flang 20

on: [push, pull_request]

Expand Down Expand Up @@ -28,5 +28,5 @@ jobs:
fpm --version
$FC --version
export FPM_FC=$FC
export FPM_FFLAGS="-mmlir -allow-assumed-rank -O3"
export FPM_FFLAGS="-O3"
fpm test
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI
name: Build with gfortran-14

on: [push, pull_request]

Expand All @@ -8,6 +8,10 @@ jobs:
strategy:
matrix:
os: [macos-13, ubuntu-24.04]
fail-fast: true
env:
FC: gfortran-14
CXX: g++-14

steps:
- name: Checkout code
Expand All @@ -21,8 +25,10 @@ jobs:
if: contains(matrix.os, 'ubuntu')
run: |
sudo apt update
sudo apt install -y build-essential gfortran-14 g++-14
sudo apt install -y build-essential $FC $CC

- name: Build and Run unit tests
- name: Build and Test with GCC
run: |
fpm test --compiler gfortran-14
fpm --version
$FC --version
fpm test --compiler $FC
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ Example expressions | Operand types
`(i .lessThan. j) .also. (k .equalsExpected. m))` | `integer`, `real`, `double precision`
`x .lessThan. y` | `integer`, `real`, `double precision`
`x .greaterThan. y` | `integer`, `real`, `double precision`
`i .greaterThan. j` | `integer`, `real`, `double precision`
`i .equalsExpected. j` | `integer`, `character`
`i .isAtLeast. j` | `integer`, `real`, `double precision`
`i .isAtMost. j` | `integer`, `real`, `double precision`
`s .isBefore. t` | `character`
`s .isAfter. t` | `character`
`.expect. command_line%argument_present("--help")`| `logical`


where `.isAtLeast.` and `.isAtMost.` can alternatively be spelled
`.greaterThanOrEqualTo.` and `.lessThanOrEqualTo.`, respectively;
Expand Down Expand Up @@ -250,12 +251,10 @@ GCC `gfortran` | 13, 14, 15 | see 1 below
Intel `ifx` | 2025.1.1 Build 20250418 | see 2 below

1. `gfortran` issues:
- With GCC 14 or earlier, the `test_description_t` constructor's
- With GCC 14.2.0 or earlier, the `test_description_t` constructor's
`diagnosis_function` actual argument must be a procedure pointer conforming
conforming with the `diagnosis_function_i` abstract interface.
- The `string_t` `bracket` type-bound function crashes for GCC 14 or earlier.
- _Deprecated feature_: Each element of a [`vector_test_description_t`] array
must be defined in a separate statement.
- The `string_t` `bracket` type-bound function crashes for GCC 14.2.0 or earlier.
2. `ifx` issue:
- Two `string_t` tests fail as described in issue [#51].

Expand Down Expand Up @@ -316,5 +315,4 @@ See our online [documentation] or build the documentation locally by installing
[Garden]: https://gitlab.com/everythingfunctional/garden
[handy-dandy]: https://github.com/rouson/handy-dandy/blob/7caaa4dc3d6e5331914a3025f0cb1db5ac1a886f/src/fresh-llvm-build.sh
[Sourcery]: https://github.com/sourceryinstitute/sourcery
[`vector_test_diagnosis_i`]: https://github.com/BerkeleyLab/julienne/blob/37bcc959efa8f9e27ae50fecfd37a6bf52ef0a43/src/julienne/julienne_vector_test_description_m.F90#L18
[Veggies]: https://gitlab.com/everythingfunctional/veggies
63 changes: 16 additions & 47 deletions demo/README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
Getting Started
===============
To get started with Julienne, review and test the demonstration project in this directory.
Then copy the `main.F90` and `specimen_test_m.F90` files to your project's test directory.
Then copy the `main.f90` and `specimen_test_m.f90` files to your project's test directory.
Finally, modify the files as described below to adapt them to your project.

Testing the Demonstration Project
--------------------------------
This demonstration project defines a trivial library named "specimen" in the `src`
subdirectory and a test suite the `test` subdirectory. The test suite includes five tests:
subdirectory and a test suite in the `test` subdirectory. The test suite includes five tests:

1. Two tests pass.
1. Three tests pass.
2. One test intentionally fails to demonstrate diagnostic output.
3. One test is skipped to demonstrate the reporting and tallying of skipped tests.
4. One test passes with three compilers but is skipped with GCC due to a compiler bug.

Test Julienne by setting your present working directory to the `demo/` subdirectory in a
terminal window and then building and running the demonstration project's test suite using
the command corresponding to your compiler in the table below.

|Vendor | Version/Build | Example shell command |
|---------|-------------------------|------------------------------------------------------------------------------------|
|LLVM | 20.1.4 (Homebrew) | `fpm test --compiler flang-new` |
|GCC | 14.2.0_1 (Homebrew) | `fpm test --compiler gfortran --profile release` |
|GCC | 13.3.0_1 (Homebrew) | `fpm test --compiler gfortran --profile release --flag "-ffree-line-length-0"` |
|NAG | 7.2 Build 7227 | `fpm test --compiler nagfor --flag -fpp` |
|Intel | 2025.1.0 Build 20250317 | `fpm test --compiler ifx --flag "-fpp -O3 -coarray"` |
|Vendor | Version/Build Tested | Example shell command |
|---------|-------------------------|------------------------------------------------------|
|LLVM | 20.1.4 | `fpm test --compiler flang-new` |
|GCC | 14.3.0 | `fpm test --compiler gfortran --profile release` |
|NAG | 7.2 Build 7235 | `fpm test --compiler nagfor --flag -fpp` |
|Intel | 2025.1.0 Build 20250317 | `fpm test --compiler ifx --flag "-fpp -O3 -coarray"` |

Setting Up Your Project's Test Suite
------------------------------------

1. If you build your project with the Fortran Package Manager ([`fpm`](https://github.com/fotran-lang/fpm)), then you might copy the `main.F90` and `specimen_test_m.F90` files from this subdirectory to a `test/` subdirectory in the root of your project's source tree.
2. Rename the `specimen_test_m.F90` file, the `specimen_test_m` module, and the `specimen_test_t` derived type and any references thereto, replacing `specimen` with the name of an entity that you intend to test -- most likely a module containing procedures or derived type with type-bound procedures.
3. Similarly replace occurrences of `specimen` in the resulting`test/main.F90` file.
4. In the `results()` function body of your new `*_test_m.F90` file, replace the `test_descriptions_t` array constructor elements with your own test descriptions. The test output will read most naturally if your description string (the first argument) contains a gerund: a verb ending in "ing" and used as a noun, such as `producing` above
1. If you build your project with the Fortran Package Manager ([`fpm`](https://github.com/fotran-lang/fpm)), then you might copy the `main.f90` and `specimen_test_m.f90` files from this subdirectory to a `test/` subdirectory in the root of your project's source tree.
2. Rename the `specimen_test_m.f90` file, the `specimen_test_m` module, and the `specimen_test_t` derived type and any references thereto, replacing `specimen` with the name of an entity that you intend to test -- most likely a module containing procedures or derived type with type-bound procedures.
3. Similarly replace occurrences of `specimen` in the resulting`test/main.f90` file.
4. In the `results()` function body of your new `*_test_m.f90` file, replace the `test_descriptions_t` array constructor elements with your own test descriptions. The test output will read most naturally if your description string (the first argument) contains a gerund: a verb ending in "ing" and used as a noun, such as `producing` above.
5. Replace the function name (the second argument) with the name of a function that will perform your test.
7. Edit the correspondingly-renamed function to perform the test. The function must take no arguments and define a `test_diagnosis_t` result.

Expand All @@ -43,7 +41,7 @@ The options include
2. Invoking the `test_diagnosis_t` constructor and using Julienne's `string_t` constructors to form a diagnostic string.

`String_t` is a generic interface to various specific functions, each of which takes an argument of a different data type, kind, and rank (TKR) and defines a `string_t` result containing a charater representation of the function argument.
Please see Julienne's online [documentation] for the currently supported TKR.
Please see Julienne's online [documentation](https://berkeleylab.github.io/julienne) for the currently supported TKR.
Please submit an issue to request support for additional TKR or submit a pull request to contribute such support.

#### Forming diagnostic strings from array data
Expand Down Expand Up @@ -117,7 +115,7 @@ Skipping Tests
--------------
When a test is known to cause a compile-time or runtime crash in a specific scenario, e.g., with a specific compiler or compiler version, including that test will prevent the test suite from building or running to completion.
It can be useful to skip a test with the problematic compiler but to report the test as skipped and account for the skipped tests in the tally of test results.
For this purpose, the `test_description_t` and `vector_test_description_t` constructor functions have optional second arguments `diagnosis_function` and `vector_diagnosis_function`, respectively.
For this purpose, the `test_description_t` constructor function has a `diagnosis_function` argument that is optional.
When these arguments are not `present`, the `test_t`'s `report` procedure will report the test as skipped but will terminate normally as long as the sum of the passing tests and skipped tests equals the total number of tests.
One might accomplish this with the compiler's predefined preprocessor macro:
```
Expand All @@ -127,7 +125,7 @@ One might accomplish this with the compiler's predefined preprocessor macro:
,test_description_t('constructing bracketed strings' ) &
#endif
```
which presently appears in Julienne `test/string_test_m.F90` test in order to work around a runtime crash known to be caused by a `gfortran` bug.
which presently appears in Julienne `test/string_test_m.f90` test in order to work around a runtime crash known to be caused by a `gfortran` bug.

String_t Functions
------------------
Expand Down Expand Up @@ -161,32 +159,3 @@ class string_t{
base_name(string_t) string_t
}
```

Deprecated: Vector Diagnosis Function
-------------------------------------
Julienne's `vector_diagnosis_function_i` abstract interface and the corresponding `vector_test_description_t` type were developed before Julienne's `operator(.all.)` and `operator(.and.)`.
Because the operators replace the interface and type with simpler functionality, it is likely that a future release will remove the `vector_*` entities.

The Unified Modeling Language ([UML]) class diagram below depicts the class relationships involved when test function performs multiple checks and defines a result containing an array of corresponding `test_diagnosis_t` objects:
```mermaid
%%{init: { 'theme':'default', "class" : {"hideEmptyMembersBox": true} } }%%
classDiagram

class vector_test_description_t{
vector_test_description_t(description : string_t[1..*], vector_diagnosis_function : vector_diagnosis_function_i)
run() test_result_t[1..*]
}
vector_test_description_t --> test_diagnosis_t : run() invokes vector_diagnosis_function to construct array of
vector_test_description_t --> test_result_t : run() uses test_diagnostics_t array to construct array of

class test_result_t{
test_result_t(test_passed : logical, diagnosis : test_diagnosis_t)
}

class test_diagnosis_t{
test_diagnosis_t(test_passed : logical, diagnostics_string : string_t)
}
```

[documentation]: https://berkeleylab.github.io/julienne
[UML]: https://wikipedia.org/Unified_modeling_language
2 changes: 1 addition & 1 deletion demo/fpm.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name = "Example-Test-Suite"

[dependencies]
julienne = {git = "https://github.com/berkeleylab/julienne", tag = "2.1.0-rc5"}
julienne = {path = "../"}
38 changes: 0 additions & 38 deletions demo/include/language-support.F90

This file was deleted.

File renamed without changes.
51 changes: 0 additions & 51 deletions demo/test/main.F90

This file was deleted.

23 changes: 23 additions & 0 deletions demo/test/main.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
! Copyright (c) 2024-2025, The Regents of the University of California and Sourcery Institute
! Terms of use are as specified in LICENSE.txt

program test_suite_driver
!! Example test-suite driver
use julienne_m ,only : test_fixture_t, test_harness_t ! Import test infrastructure
use specimen_test_m ,only : specimen_test_t ! Must be a non-abstract child type extending Julienne's test_t type
use iso_fortran_env ,only : compiler_version
implicit none

call stop_if_compiler_too_old

! Construct a test harness from an array of test fixtures, each of which is
! constructed from a structure constructor for a type that extends test_t.
associate(test_harness => test_harness_t( [ test_fixture_t(specimen_test_t()) ] ))
call test_harness%report_results
end associate

contains
#if __GNUC__ && ( __GNUC__ < 14 || (__GNUC__ == 14 && __GNUC_MINOR__ < 3) )
stop "GFortran " // __VERSION__ // " too old: GCC >= 14.3.0 required"
#endif
end program
43 changes: 4 additions & 39 deletions demo/test/specimen_test_m.F90 → demo/test/specimen_test_m.f90
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
! Copyright (c) 2024-2025, The Regents of the University of California and Sourcery Institute
! Terms of use are as specified in LICENSE.txt

#include "language-support.F90"

module specimen_test_m
!! Example unit test for the specimen_t test subject
use specimen_m, only : specimen_t
Expand All @@ -17,11 +15,8 @@ module specimen_test_m
,operator(.within.) &
,operator(.all.) &
,operator(.equalsExpected.) &
,operator(.greaterThan.) &
,operator(.lessThan.)
#if defined(__GFORTRAN__)
use julienne_m, only : diagnosis_function_i ! work around gfortran's missing Fortran 2008 feature
#endif
,operator(.lessThan.) &
,operator(.isAtMost.)

implicit none

Expand All @@ -41,8 +36,6 @@ pure function subject() result(specimen_description)
specimen_description = "A specimen_t object"
end function

#if ! defined(__GFORTRAN__)

function results() result(test_results)
type(test_result_t), allocatable :: test_results(:)
type(test_description_t), allocatable :: test_descriptions(:)
Expand All @@ -61,35 +54,6 @@ function results() result(test_results)
test_results = test_descriptions%run()
end function

#else

function results() result(test_results)
!! work around missing Fortran 2008 feature in gfortran versions earlier than 15
type(test_result_t), allocatable :: test_results(:)
type(test_description_t), allocatable :: test_descriptions(:)
procedure(diagnosis_function_i), pointer :: check_operators_ptr => check_zero_using_operators
procedure(diagnosis_function_i), pointer :: check_constructor_ptr => check_zero_using_constructor
procedure(diagnosis_function_i), pointer :: check_aggregate_ptr => check_aggregate_diagnosis
procedure(diagnosis_function_i), pointer :: check_print_diagnosis_ptr => check_print_diagnosis

! Omitting the optional 2nd argument in the 3rd test_description_t constructor below skips the described
! test. When the test suite runs, it reports the test as skipped and reports a tally of skippped tests.

test_descriptions = [ &
test_description_t("diagnosing the zero function using Julienne operators", check_operators_ptr) &
,test_description_t("diagnosing the zero function using a diagnosis constructor", check_constructor_ptr) &
,test_description_t("aggregating diagnoses of the zero and one functions using operator(.all.)") &
,test_description_t("(intentional failure to demonstrate diagnostic output)", check_print_diagnosis_ptr) &
,test_description_t("skipping a test when no diagnosis function is specified") &
]
test_descriptions = pack( &
array = test_descriptions &
,mask = test_descriptions%contains_text(test_description_substring) .or. index(subject(), test_description_substring)/=0 &
)
test_results = test_descriptions%run()
end function
#endif

function check_zero_using_operators() result(test_diagnosis)
!! Construct a test diagnosis using Julienne's operator(.approximates.) and operator(.within.)
type(test_diagnosis_t) test_diagnosis
Expand Down Expand Up @@ -123,8 +87,9 @@ function check_aggregate_diagnosis() result(test_diagnosis)
end function

function check_print_diagnosis() result(test_diagnosis)
!! Intentional test failure to demonstrate diagnostic output
type(test_diagnosis_t) test_diagnosis
test_diagnosis = 2 .lessThan. 1 ! intentional test failure
test_diagnosis = 2 .isAtMost. 1
end function

end module
Loading