Skip to content

Testing and conventions

The test suite does two jobs: it checks that the code is correct, and it pins the conventions the documentation asserts, so that the two cannot drift apart.

Running the tests

bash
julia --project=. -e 'using Pkg; Pkg.test()'

test/runtests.jl seeds the generator (Random.seed!) so failures are reproducible, wraps each file in a @testsection with TimerOutputs, and includes the files in a deliberate order.

test_nlopt_ext.jl must stay last

Loading NLopt permanently activates TensNDNLoptExt for the session, and test/test_tens_projection.jl asserts the behavior of the cheap, no-NLopt path. Moving the extension test earlier makes the projection tests exercise a different code path than intended.

Conventions pinned by tests

Each of the following is a statement made somewhere in the documentation that a test would catch if the code changed under it.

ConventionWhere it is statedWhy it needs a test
    The Walpole basisthe widely repeated   is false
    The Walpole basislikewise,   
the Walpole Gram matrix is The Walpole basisit is what makes every TI projection closed-form
   Kelvin–Mandelmust be checked on a rotated frame: in the canonical frame   and every convention error hides
annihilate axially invariant order-2 tensors — and only thoseThe extended Walpole algebraa source comment once claimed they annihilate every symmetric tensor, which is false
the   space is closed under and invThe extended Walpole algebrait is the justification for the storage type
  Curvilinear calculusthe storage order is easy to transpose
GRAD appends on the right, DIV contracts the last indexCurvilinear calculusinvisible on symmetric fields, wrong by a transpose otherwise
   on a sphereSubmanifoldsties the normal orientation to the curvature sign

What a good test looks like here

Prefer an identity that fails unless everything is right over a check against a transcribed number.

  • The Laplacian of a harmonic function vanishes only if every Christoffel term is correct. LAPLACE(rⁿcos nθ) == 0 in polar coordinates, and the spheroidal harmonics , are the strongest single checks on a coordinate system.

  • A closed form matched against the operator route — the Green's function pages do this — exercises HESS, the symmetrization and the basis handling at once.

  • A round trip (arg_TI(tens_TI(x...)) == x) catches parametrization errors that a one-way check cannot.

  • Equilibrium residuals: DIV(σ) ≈ 0 for an exact solution tests the full order-2 divergence including connection terms.

Non-obvious traps

Comparing get_arrays. Two mathematically equal tensors stored on different bases have different component arrays. Always bring them to a common basis and variance with components before subtracting — see Tensors.

Element types that are not AbstractFloat. ForwardDiff.Dual <: Real but not <: AbstractFloat. The tolerant symmetry predicates are therefore declared on ApproxType (src/array_utils.jl), which includes Dual; before that union existed, a few ulp of round-off made a Dual-valued tensor look non-minor-symmetric, _KM_of_array built a   matrix instead of a   one, and every proj_tens call on a Dual died with a DimensionMismatch. Any new predicate should use ApproxType, not AbstractFloat.

Symbolic assumptions. symbols("θ", real = true) is not enough for SymPy to reduce ; a test on a sphere will then compare against expressions carrying . Declare the tightest assumptions available and use the rules mechanism for the rest.

Doctests

Docstring examples that print deterministic output — types, booleans, small integers, exact tuples — are written as jldoctest and run by

bash
julia --project=docs -e '
  using TensND, Documenter, LinearAlgebra, SymPy, Tensors, OMEinsum, Rotations
  DocMeta.setdocmeta!(TensND, :DocTestSetup,
      :(using TensND, LinearAlgebra, SymPy, Tensors, OMEinsum, Rotations); recursive = true)
  doctest(TensND)'

Examples printing Float64 component dumps or symbolic expressions stay plain ````juliablocks: both are fragile across Julia and dependency versions, and a doctest that has to be re-blessed on every upgrade trains people to re-bless it without reading. Those examples are still executed — as@example` blocks — on the manual and tutorial pages.