blob: b21c07686f30d6ea819c2ca089ba3c4e5b3633ec [file] [view]
# Testing Eigen Changes
Use this guide when adding or changing tests. The checked-out source is authoritative:
- [`test/main.h`](../test/main.h) configures and runs the test framework and aggregates the shared helpers.
- [`test/numerical_test_helpers.h`](../test/numerical_test_helpers.h) defines numerical comparison, assertion, and
tolerance helpers.
- [`test/product_test_helpers.h`](../test/product_test_helpers.h) defines matrix-product error bounds.
- [`test/random_matrix_helper.h`](../test/random_matrix_helper.h) and
[`test/type_test_helpers.h`](../test/type_test_helpers.h) define random-matrix and type utilities.
- [`cmake/EigenTesting.cmake`](../cmake/EigenTesting.cmake) defines test registration and splitting.
- [`test/CMakeLists.txt`](../test/CMakeLists.txt) and
[`unsupported/test/CMakeLists.txt`](../unsupported/test/CMakeLists.txt) register the suites.
- [`cmake/EigenConfigureTesting.cmake`](../cmake/EigenConfigureTesting.cmake) defines aggregate build and check
targets.
## Configure And Build
Configure a dedicated build directory. Unit tests are excluded from CMake's default `all` target, although a bare
build may still build enabled auxiliary libraries.
```bash
cmake -G Ninja -S . -B build
cmake --build build --target buildtests
ctest --test-dir build --parallel --output-on-failure
```
Useful aggregate targets are `BuildOfficial`, `BuildUnsupported`, `buildsmoketests`, `buildtests_gpu`, `check`, and
`check_gpu`. Build and run one test explicitly when possible:
```bash
cmake --build build --target bdcsvd_3
ctest --test-dir build -R '^bdcsvd_3$' --output-on-failure
```
Run the generated wrappers from the build directory because they invoke the configured build tool relative to their
working directory:
```bash
cd build
./buildtests.sh <regex>
./check.sh <regex>
```
They filter registered parent names such as `bdcsvd`, not generated part names such as `bdcsvd_3`; use the explicit
target recipe for one part.
Use a separate build directory for each materially different configuration. Do not rewrite one cache and describe
the result as a second test run.
```bash
cmake -G Ninja -S . -B build-row-major -DEIGEN_DEFAULT_TO_ROW_MAJOR=ON
cmake -G Ninja -S . -B build-no-vector -DEIGEN_TEST_NO_EXPLICIT_VECTORIZATION=ON
```
Consult the top-level [`CMakeLists.txt`](../CMakeLists.txt) and nearby test CMake files for current options instead of
copying an option inventory into documentation.
## Current Test Framework
Eigen currently uses its own framework, not GoogleTest:
1. Add `test/<name>.cpp` or `unsupported/test/<name>.cpp`.
2. Include `main.h`, then the public umbrella header for tests of public behavior. A focused test of a private utility
may include its implementation header only when that matches an established nearby pattern; never present such a
path as a user include.
3. Use `VERIFY`, `VERIFY_IS_EQUAL`, `VERIFY_IS_APPROX`, and the other helpers exposed through `test/main.h`.
4. End with `EIGEN_DECLARE_TEST(<name>) { ... }`.
5. Register the source with `ei_add_test(<name>)` in the matching `CMakeLists.txt`, then reconfigure.
Keep `test/main.h` limited to framework configuration, registration, shared-helper aggregation, and the test driver.
Put reusable utilities in a narrowly named helper header; include it from `main.h` only when most tests need it.
For compile-failure coverage, use the established `failtest/` pattern. Its `_ok` target must compile and its `_ko`
target must fail with `EIGEN_SHOULD_FAIL_TO_BUILD` defined. `_ko` is a `WILL_FAIL` test whose action is a build, so it
cannot tell the intended compile error from any other build failure: keep the construct narrow, and leave the
`RESOURCE_LOCK` that `ei_add_failtest` uses to serialize the suite in place.
## Split Tests
`ei_add_test` scans the source for `CALL_SUBTEST_N`, `EIGEN_TEST_PART_N`, and `EIGEN_SUFFIXES;...` markers.
- With `EIGEN_SPLIT_LARGE_TESTS=ON`, every discovered suffix becomes an executable `<name>_<N>` compiled with
`EIGEN_TEST_PART_<N>=1`; the parent `<name>` target builds all parts.
- `EIGEN_SUFFIXES;...` supplies an explicit suffix list when ordinary source scanning cannot see macro-generated or
conditional parts.
- With splitting off, tests containing only `CALL_SUBTEST_N` or `EIGEN_SUFFIXES` fold into one `<name>` executable
compiled with `EIGEN_TEST_PART_ALL=1`.
- An explicit `EIGEN_TEST_PART_N` marker forces splitting even when the option is off. If any such marker is present,
all suffixes discovered in that source are emitted.
`ctest -R '^<name>$'` does not match split parts. Use `ctest -R '<name>'` for every part or anchor one generated name.
After changing subtest registration, reconfigure and read back the generated target list. Two failure modes are
silent: a subtest function whose `CALL_SUBTEST` call was dropped still compiles and looks like coverage, and a part
reached only through a dispatch macro is not built under `EIGEN_SPLIT_LARGE_TESTS=ON` unless an `EIGEN_SUFFIXES`
marker lists it.
## Coverage That Can Fail
A test that passes when the change is reverted is not coverage. Establish that it fails at the parent commit, or when
that is impractical, that it reaches the new code by construction.
- Reach a new fast path through the public entry that selects it, with inputs that actually take it — not only through
a direct call to the new method. Pin the selection with a `STATIC_CHECK` on the flag or trait where one exists, in
both directions: a type that must opt in and one that must stay out.
- Cover the branches the change adds, not just one convenient shape: sizes that are not a multiple of the packet or
block dimension, complex scalars where conjugation is otherwise a no-op, both storage orders, and the uncompressed
or strided variants of an input type.
- Verify the complete result against an independent reference; skipping coefficients the test setup did not write
hides corruption in exactly those places.
- Exercise the customization points users are documented to have (custom scalars, functors without declared traits),
not only the built-in specializations that happen to satisfy a new precondition.
## Build-System Tests
[`test/buildsystem`](../test/buildsystem) holds the coverage for Eigen's own CMake surface: what an install tree
contains, what `find_package(Eigen3)` and the version ranges in
[`cmake/Eigen3ConfigVersion.cmake.in`](../cmake/Eigen3ConfigVersion.cmake.in) accept, and how an embedding project
opts out of Eigen's install rules. They exist because those are claims
[`doc/TopicCMakeGuide.dox`](../doc/TopicCMakeGuide.dox) makes to users and nothing else checks; the blocking
documentation job only builds the docs, it does not run what they describe.
Not every scenario answers to the documentation. A find module that has to survive a second configure of the same
build tree, or the wiring that routes a compiler launcher into a test's compile command, is CMake behavior nothing
else exercises either.
```bash
cmake -G Ninja -S . -B build -DEIGEN_BUILD_TESTING=ON
cmake -E chdir build ctest -L buildsystem --output-on-failure
```
`ctest --test-dir` would be the shorter spelling, but that option arrived in CMake 3.20; the 3.17 Eigen supports
accepts and ignores it, inspects the source directory instead, reports that no tests were found, and exits
successfully.
No target needs building first: each scenario runs its own nested configure, build, and install into the CTest
binary directory. Add a claim by dropping a scenario in `scenarios/` and naming it in the list in
`test/buildsystem/CMakeLists.txt`; the driver `run_scenario.cmake` supplies the assertion helpers.
Two hazards specific to these tests. Eigen calls `export(PACKAGE Eigen3)`, so CMake's user package registry names
every Eigen build tree on the machine — a `find_package` scenario must disable both registries and assert the package
came from the prefix it installed, or it passes without reading that prefix at all. And because CMake registers the
tests, a guard that stops matching yields an empty selection rather than a failure, so the CI job runs `ctest` with
`--no-tests=error`.
## Configurations The Test Suite Cannot See
- In the default host-test configuration, no test compiles an `EIGEN_NO_DEBUG` code path: `test/main.h` undefines
`NDEBUG`, and `Macros.h` derives `EIGEN_NO_DEBUG` from it. (HIP/SYCL device compilation and an explicit
`-DEIGEN_NO_DEBUG` define it independently.) Behavior that depends on the macro needs a dedicated `-DEIGEN_NO_DEBUG`
test target or a standalone `-DNDEBUG` check. Conversely, an `eigen_assert` body is only type-checked where
assertions are enabled, so it can call members its argument type does not have and still compile in every release
build.
- Run an `EIGEN_DEFAULT_TO_ROW_MAJOR` build when layout is in play, and pin the layout explicitly where a test aliases
one object's storage through a view whose default layout is fixed.
- Cover `EIGEN_TEST_NO_EXPLICIT_VECTORIZATION`, `EIGEN_UNALIGNED_VECTORIZE=0`, or a narrower
`EIGEN_DEFAULT_DENSE_INDEX_TYPE` when the change reasons about packets, alignment, or index width.
## Numerical Assertions
`VERIFY_IS_APPROX` is a convenient broad comparison, not a machine-epsilon guarantee. `test_precision<T>()` uses
`NumTraits<T>::dummy_precision()` generically and currently specializes float to `1e-3` and double/long double to
`1e-6`. Do not use it alone to claim ULP accuracy, backward stability, or IEEE special-value conformance.
For numerical kernels, add explicit named bounds based on epsilon, dimension, conditioning, or a backward-error
model as appropriate. Check NaN, infinity, and signed zero explicitly when their distinction matters. Follow
[`numerics.md`](numerics.md) for solver, packet, and scalar-math coverage.
Two ways a comparison silently accepts everything, both of which have shipped here: a tolerance computed by the
operation under test (a bound formed as `(A.cwiseAbs() * B.cwiseAbs())` goes through the product code being tested —
accumulate it independently instead), and a comparison that admits non-finite values (`error <= tolerance` holds for
two infinities, and `if (error > bound)` never fires for a NaN error — assert the negation and reject a non-finite
tolerance).
Run reproducible failures directly with a fixed seed and repeat count:
```bash
EIGEN_REPEAT=10 EIGEN_SEED=1 build/test/foo_3
build/test/foo_3 r10 s1
```
## External BLAS And Shim Libraries
`EIGEN_TEST_EXTERNAL_BLAS=ON` finds a system BLAS, defines `EIGEN_USE_BLAS`, and links that BLAS into applicable
official tests. With it off, ordinary tests exercise Eigen's normal implementation; they do not transparently use
the in-tree `eigen_blas` library. `EIGEN_BUILD_BLAS` and `EIGEN_BUILD_LAPACK` separately build Eigen's ABI shim
libraries, which are also used to satisfy some optional sparse-backend links. There is currently no
`EIGEN_TEST_EXTERNAL_LAPACK` option.
Report the exact targets, CTest regexes, configurations, compiler, and seeds run. Also report relevant hardware or
optional backends that were unavailable locally.