Skip to content

test: uniformize monorepo onto OrdinaryDiffEq canonical structure#961

Draft
ChrisRackauckas-Claude wants to merge 6 commits into
SciML:masterfrom
ChrisRackauckas-Claude:uniformize-monorepo-structure
Draft

test: uniformize monorepo onto OrdinaryDiffEq canonical structure#961
ChrisRackauckas-Claude wants to merge 6 commits into
SciML:masterfrom
ChrisRackauckas-Claude:uniformize-monorepo-structure

Conversation

@ChrisRackauckas-Claude
Copy link
Copy Markdown
Contributor

Uniformizes the NonlinearSolve.jl monorepo onto the OrdinaryDiffEq.jl canonical structure: SafeTestsets group-dispatch testing (no ReTestItems), all test deps in [extras]/[targets].test (no test/Project.toml), and centralized CI workflows threaded with a repo-specific group env var.

Canonicalization done

Tests / framework (ReTestItems -> SafeTestsets)

  • Removed all @testitem/@testsetup/@testsnippet/@run_package_tests/ReTestItems/TestItemRunner usage from the root and all 10 sublibraries.
  • Each test item is now @safetestset "<title>" include("<file>__itemN.jl"). The item body runs at module top level (matching the original @testitem semantics) — this is required so BenchmarkTools @ballocated solve!($cache) interpolation resolves the package globals; an inline @safetestset begin ... end evaluates the interpolation in the wrong module and fails. Test bodies are preserved byte-for-byte, then Runic-formatted.
  • @testsetup/@testsnippet modules are extracted to setup_<name>.jl and included by the items that referenced them via setup=[...].
  • Every runtests.jl reads NONLINEARSOLVE_TEST_GROUP (falling back to GROUP) and group-guards its includes. Root test files carry per-item group guards so the mixed core/downstream/nopre/bounds/verbosity/cuda tagging is preserved exactly.
  • Root test/runtests.jl is now a _detect_sublibrary_group dispatcher (mirrors OrdinaryDiffEq): a GROUP naming a lib/<X> activates that sublibrary, develops its in-repo [sources] transitively (incl. the umbrella root for leaf libs), and Pkg.tests it with the sublib group env set; otherwise it runs the root's own @safetestset groups. Heavy/optional group deps (ModelingToolkit, Enzyme/Mooncake/SciMLSensitivity, CUDA) remain Pkg.add-on-demand to keep the default matrix light.

Project.toml

  • Dropped ReTestItems/TestItemRunner from every [compat]/[extras]/[targets].test; added SafeTestsets. No test/Project.toml anywhere (the test/trim/ trimming subproject is unrelated and untouched). No [compat] floors lowered.

test_groups.toml (added per sublibrary so the project-model matrix matches real groups)

  • NonlinearSolveSciPy: only wrappers (it has no Core tests — critical, else the default Core job would run nothing).
  • BracketingNonlinearSolve, SimpleNonlinearSolve: Core + adjoint.
  • Others: Core on [lts,1.11,1,pre] (Aqua/ExplicitImports run inside Core; declaring real groups avoids a spurious empty QA job).

Workflows

  • SublibraryCI.yml: group-env-name: NONLINEARSOLVE_TEST_GROUP, check-bounds: auto.
  • DowngradeSublibraries.yml: group-env-name/group-env-value: Core (no allow-reresolve; auto-discovers lib/*; sibling+stdlib skip list kept).
  • CI_NonlinearSolve.yml (root, GROUP-dispatched over the root's own groups): threaded group-env-name: NONLINEARSOLVE_TEST_GROUP, check-bounds: auto. No hardcoded sublibrary rows — sublibs are covered by SublibraryCI's project model.
  • Downgrade.yml: already downgrade.yml@v1, group: core, no allow-reresolve — unchanged.
  • GPU.yml: kept as-is. The SimpleNonlinearSolve CUDA path could move into the project model via a cuda test_groups.toml runner entry, but the root NonlinearSolve CUDA job is not a sublibrary, so both bespoke gpu-v100 jobs are kept. They set GROUP=cuda, which the new runtests honor via the GROUP fallback.

Root [sources] (the 4 "missing" sublibraries are intentionally NOT added)

  • SciMLJacobianOperators is only a transitive dep (via NonlinearSolveFirstOrder), like RosenbrockTableaus in OrdinaryDiffEq, which is not hoisted into root [sources].
  • SCCNonlinearSolve, NonlinearSolveHomotopyContinuation, NonlinearSolveSciPy are leaf libraries that depend back on the umbrella root (<root> = {path = "../.."}). Adding root->leaf edges would create cycles in the path graph (forbidden by the audit's rule B). All 10 sublibraries are still discovered and tested by SublibraryCI's lib/* scan.

Verified locally (isolated depot, Julia 1.11)

  • TOML-parsed every changed Project.toml; confirmed all [targets].test deps appear in [extras]/[deps], zero test/Project.toml remain, zero @testitem/@testsnippet/ReTestItems remain. YAML-parsed all changed workflows; parsed all test_groups.toml; parse-checked all 217 converted .jl files. Runic-checked all 210 changed .jl (pass).
  • Instantiated the root project and 3 sublibraries (NonlinearSolveBase, NonlinearSolveSpectralMethods, BracketingNonlinearSolve) — [sources] path graph resolves.
  • Ran, via the new SafeTestsets dispatch with NONLINEARSOLVE_TEST_GROUP=Core:
    • NonlinearSolveBase Core: Testing NonlinearSolveBase tests passed (Aqua 11/11, Explicit Imports 3/3, Banded vcat 1/1, Termination 1/1, standardize_forwarddiff_tag 2/2, maybe_wrap_nonlinear_f 4/4, EnzymeExt 6/6 + 4/4, PolyAlgorithm 9/9, Bounds transform 8/8).
    • NonlinearSolveSpectralMethods Core (exercises @testsetup + @ballocated $cache): tests passed (DFSane 12/12, Iterator 2/2, NewtonRaphson Fails 2/2, Kwargs 3/3, Termination 27/27).
    • BracketingNonlinearSolve Core (exercises @testsnippet): tests passed (Interval Nonlinear Problems 17861/17861, Muller 13/13, Aqua 11/11, etc.).
  • Ran the root's smallest group via the new dispatcher (GROUP=misc): Testing NonlinearSolve tests passed (Aqua 10/10, Explicit Imports 3/3) — confirms per-item group guards select only misc items.
  • Unit-tested the root _detect_sublibrary_group/_find_lib routing (e.g. scimljacobianoperators -> sublib, simplenonlinearsolve_adjoint -> SimpleNonlinearSolve/adjoint, core/misc -> root-own).

What CI must confirm

  • The full SublibraryCI project-model matrix across all 10 sublibraries x groups x Julia versions (lts/1.11/1/pre), including the heavier wrappers/SciPy/Homotopy/FirstOrder/QuasiNewton suites not run locally.
  • The full root CI matrix (core/downstream/wrappers/misc/nopre/trim x lts/1.11/1 x ubuntu/macos), DowngradeSublibraries, and root Downgrade.
  • The bespoke GPU.yml CUDA jobs on the self-hosted runner.

Ignore until reviewed by @ChrisRackauckas.

ChrisRackauckas and others added 6 commits June 7, 2026 16:36
Convert the NonlinearSolve.jl monorepo to the OrdinaryDiffEq.jl canonical
style: SafeTestsets group-dispatch testing (no ReTestItems/@testitem), test
deps in [extras]+[targets].test (no test/Project.toml), and centralized CI
threaded with a repo-specific group env var.

Tests / framework
- Replace ReTestItems/TestItemRunner with SafeTestsets across the root and all
  10 sublibraries. Every `@testitem`/`@testsnippet`/`@testsetup` is rewritten:
  each test item becomes `@safetestset "<title>" include("<file>__itemN.jl")`,
  with the item body run at module top level (matches the original @testitem
  semantics — required for BenchmarkTools `@ballocated f($cache)` interpolation
  to resolve package globals). `@testsetup`/`@testsnippet` modules are extracted
  to `setup_<name>.jl` and included per item. Test bodies are preserved
  byte-for-byte (then Runic-formatted).
- Each runtests.jl reads `NONLINEARSOLVE_TEST_GROUP` (falling back to `GROUP`)
  and group-guards its includes. Root files carry per-item group guards so the
  mixed core/downstream/nopre/bounds/etc. tagging is preserved exactly.
- Root test/runtests.jl is now a `_detect_sublibrary_group` dispatcher (mirrors
  OrdinaryDiffEq): a GROUP naming a lib/<X> activates that sublibrary, develops
  its in-repo [sources] (transitively, incl. the umbrella root for leaf libs),
  and Pkg.tests it with the sublib group env set; otherwise it runs the root's
  own @safetestset groups. Heavy/optional group deps (ModelingToolkit,
  Enzyme/Mooncake/SciMLSensitivity, CUDA) are still Pkg.add-ed on demand.

Project.toml
- Drop ReTestItems/TestItemRunner from every [compat]/[extras]/[targets].test;
  add SafeTestsets. No test/Project.toml anywhere (the trim/ trimming
  subproject is unrelated and left untouched). No [compat] floors lowered.

test_groups.toml
- Added per sublibrary so the project-model matrix matches real groups:
  NonlinearSolveSciPy declares only `wrappers` (it has no Core tests);
  Bracketing/SimpleNonlinearSolve add `adjoint`; the rest declare `Core` on
  [lts,1.11,1,pre] (their Aqua/ExplicitImports run inside Core, so no spurious
  empty QA job).

Workflows
- SublibraryCI.yml: group-env-name=NONLINEARSOLVE_TEST_GROUP, check-bounds=auto.
- DowngradeSublibraries.yml: group-env-name/value=Core (no allow-reresolve;
  auto-discovers lib/*; sibling+stdlib skip list kept).
- CI_NonlinearSolve.yml (root, GROUP-dispatched root groups): thread
  group-env-name=NONLINEARSOLVE_TEST_GROUP, check-bounds=auto. No hardcoded
  sublibrary rows (sublibs are covered by SublibraryCI's project model).
- Downgrade.yml already uses downgrade.yml@v1 with group=core, no
  allow-reresolve — unchanged.
- GPU.yml left as-is: the SimpleNonlinearSolve CUDA path could move into the
  project model via a `cuda` test_groups runner, but the root NonlinearSolve
  CUDA job is not a sublibrary, so both bespoke gpu-v100 jobs are kept (they set
  GROUP=cuda, which the new runtests honor). See PR notes.

Root [sources] note: the 4 not-yet-listed sublibraries are intentionally not
added. SciMLJacobianOperators is only a transitive dep (via FirstOrder), like
RosenbrockTableaus in OrdinaryDiffEq, which is not hoisted to root [sources];
SCCNonlinearSolve/NonlinearSolveHomotopyContinuation/NonlinearSolveSciPy are
leaf libraries that depend back on the umbrella root (`<root> = {path="../.."}`),
so adding root->leaf edges would create cycles in the path graph. All 10
sublibraries are still discovered and tested by SublibraryCI's lib/* scan.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the lowercase() wrapping around the group env read in every
test/runtests.jl (root and lib/*) and convert all group-name string
comparisons, defaults, and gates to canonical Title-case (All, Core,
QA-style) to match SciML/OrdinaryDiffEq.jl. The _detect_sublibrary_group
default sub-group is now "Core". Custom group keys in test_groups.toml
([Adjoint], [Wrappers]) are aligned to the same casing as their
@safetestset gates. No dispatch logic, env var names, fallback chains,
deps, [sources], assertions, or workflow callers were changed.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make workflow group inputs match the canonical-capitalized group names
compared in runtests.jl / test files (case-sensitive):

- CI_NonlinearSolve.yml matrix.group: core/downstream/wrappers/misc/nopre/trim
  -> Core/Downstream/Wrappers/Misc/NoPre/Trim
- GPU.yml GROUP: cuda -> CUDA
- Downgrade.yml group: core -> Core

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… sources

- TagBot.yml: add the 2 missing registered libs
  (NonlinearSolveHomotopyContinuation, NonlinearSolveSciPy) so all 10
  registered sublibraries are tagged
- NonlinearSolveSciPy test_groups.toml: declare [Core] so basic_tests.jl
  (the package-load/algorithm-defined checks) actually runs in SublibraryCI;
  previously only [Wrappers] was declared and the default Core group found
  nothing
- NonlinearSolveBase Project.toml: add [sources] for the in-repo
  SciMLJacobianOperators so the umbrella resolves the local path on Julia < 1.11

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Rename plain LICENSE -> LICENSE.md at the root and in the 9 sublibraries
  that still used the extensionless name (NonlinearSolveSciPy already had
  LICENSE.md), matching OrdinaryDiffEq's canonical layout. Pure renames; no
  content change.
- Add root .codecov.yml with 'comment: false', matching OrdinaryDiffEq.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add OrdinaryDiffEq-style sublibrary READMEs (Zulip + Global Docs + ColPrac +
SciML Code Style badge block, plus the 'component of the NonlinearSolve.jl
monorepo' wording) to the seven sublibraries that lacked a README:
NonlinearSolveBase, NonlinearSolveFirstOrder,
NonlinearSolveHomotopyContinuation, NonlinearSolveQuasiNewton,
NonlinearSolveSciPy, NonlinearSolveSpectralMethods, SCCNonlinearSolve.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants