From e28c71ae500955d64a0b2a578e34c39c596ef3dd Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 25 Jun 2026 09:09:05 -0400 Subject: [PATCH 1/2] QA: run_qa v1.6 form + ExplicitImports Convert test/qa/qa.jl from the hand-rolled Aqua body to the SciMLTesting run_qa v1.6 form and enable the ExplicitImports checks. Aqua mapping (preserves every prior check): - test_ambiguities(recursive = false) -> aqua_kwargs ambiguities recursive = false - find_persistent_tasks_deps / deps_compat / project_extras / stale_deps / unbound_args -> default Aqua.test_all sub-checks - test_piracies(treat_as_own = []) -> default (empty), dropped - test_undefined_exports(broken = true) -> aqua_broken = (:undefined_exports,), tracked in SciML/EasyModelAnalysis.jl#300 (`Variable`/`rotate!` leak dead through @reexport) ExplicitImports (explicit_imports = true): - no_stale_explicit_imports, all_explicit_imports_via_owners, all_explicit_imports_are_public: pass - all_qualified_accesses_via_owners / all_qualified_accesses_are_public: pass with documented per-name ignore-lists for dependency-internal qualified accesses (AbstractSystem, DynamicPPL, unwrap, AbstractMCMCEnsemble, LN_SBPLX, successful_retcode) - no_implicit_imports: many heavy reexport / bulk-using names; left @test_broken via ei_broken, tracked in SciML/EasyModelAnalysis.jl#301 test/qa/Project.toml: SciMLTesting compat floor -> "1.6" (the broken-marker and ExplicitImports kwargs require 1.6.0). Aqua kept as a direct dep (the ambiguities sub-check spawns a child process that needs it); ExplicitImports comes transitively through SciMLTesting. Verified locally on Julia 1.10 against released SciMLTesting 1.6.0 (no dev-from-branch): QA group is 15 Pass, 2 Broken (undefined_exports + no_implicit_imports), 0 Fail/0 Error. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- test/qa/Project.toml | 2 +- test/qa/qa.jl | 46 ++++++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/test/qa/Project.toml b/test/qa/Project.toml index 03e281b..0a6b61b 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -11,5 +11,5 @@ EasyModelAnalysis = {path = "../.."} [compat] Aqua = "0.8" SafeTestsets = "1, 0.1" -SciMLTesting = "1" +SciMLTesting = "1.6" julia = "1.10" diff --git a/test/qa/qa.jl b/test/qa/qa.jl index b7c83e0..00879cb 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -1,14 +1,32 @@ -using EasyModelAnalysis, Aqua -@testset "Aqua" begin - Aqua.find_persistent_tasks_deps(EasyModelAnalysis) - Aqua.test_ambiguities(EasyModelAnalysis, recursive = false) - Aqua.test_deps_compat(EasyModelAnalysis) - Aqua.test_piracies( - EasyModelAnalysis, - treat_as_own = [] - ) - Aqua.test_project_extras(EasyModelAnalysis) - Aqua.test_stale_deps(EasyModelAnalysis) - Aqua.test_unbound_args(EasyModelAnalysis) - Aqua.test_undefined_exports(EasyModelAnalysis, broken = true) -end +using SciMLTesting, EasyModelAnalysis, Test + +run_qa( + EasyModelAnalysis; + explicit_imports = true, + aqua_kwargs = (; ambiguities = (; recursive = false)), + # undefined_exports: `Variable` and `rotate!` leak in (dead) via `@reexport` + # (SciML/EasyModelAnalysis.jl#300) + aqua_broken = (:undefined_exports,), + ei_kwargs = (; + all_qualified_accesses_via_owners = (; + ignore = ( + :AbstractSystem, # ModelingToolkitBase (accessed via ModelingToolkit) + :DynamicPPL, # DynamicPPL (accessed via Turing) + :unwrap, # SymbolicUtils (accessed via Symbolics) + ), + ), + all_qualified_accesses_are_public = (; + ignore = ( + :AbstractMCMCEnsemble, # AbstractMCMC (not public) + :AbstractSystem, # ModelingToolkit (not public) + :DynamicPPL, # Turing (not public) + :LN_SBPLX, # NLopt (not public) + :successful_retcode, # SciMLBase (not public) + :unwrap, # Symbolics (not public) + ), + ), + ), + # no_implicit_imports: heavy reexport / bulk-using of the SciML stack + # (SciML/EasyModelAnalysis.jl#301) + ei_broken = (:no_implicit_imports,), +) From 0c2d5c2a35b150653069652bd1091c5b4545328a Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Mon, 29 Jun 2026 12:51:10 -0400 Subject: [PATCH 2/2] QA: drop now-resolvable ExplicitImports exceptions ExplicitImports findings against the released stack (SciMLBase 3.30, ModelingToolkit 11.29, Symbolics 7.29) no longer flag three names that the conversion commit had ignored, so the ignore-lists are trimmed to only the genuinely-irreducible external API: - all_qualified_accesses_via_owners: dropped :AbstractSystem (now owned by ModelingToolkit) and :unwrap (now owned by Symbolics). Only :DynamicPPL remains (reached through Turing's re-export; DynamicPPL is not a direct dependency). - all_qualified_accesses_are_public: dropped :AbstractSystem, :successful_retcode (now public in SciMLBase) and :unwrap (now public in Symbolics). The kept three are genuinely non-public external names: :AbstractMCMCEnsemble (AbstractMCMC), :DynamicPPL (Turing submodule re-export), :LN_SBPLX (NLopt algorithm constant). No source change was needed: src already qualifies these through their public owners (SciMLBase.successful_retcode, ModelingToolkit.AbstractSystem, Symbolics.unwrap), so the public/owner checks now pass without an ignore. no_implicit_imports stays @test_broken (ei_broken): the module deliberately `@reexport`s the SciML stack (DifferentialEquations, ModelingToolkit, Distributions, Plots), so converting the 47 implicit names to explicit imports would break the intended public surface and mis-attribute owners (e.g. solve/remake to SciMLExpectations). Tracked in SciML/EasyModelAnalysis.jl#301. undefined_exports stays aqua_broken (#300). Verified GROUP=QA locally on Julia 1.12 against released SciMLTesting 1.7.0: QA/qa.jl is 15 Pass, 2 Broken (no_implicit_imports + undefined_exports), 0 Fail / 0 Error. The two trimmed public/owner checks pass with no Unexpected Pass. Co-Authored-By: Chris Rackauckas --- test/qa/qa.jl | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 00879cb..3da5e9e 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -9,20 +9,15 @@ run_qa( aqua_broken = (:undefined_exports,), ei_kwargs = (; all_qualified_accesses_via_owners = (; - ignore = ( - :AbstractSystem, # ModelingToolkitBase (accessed via ModelingToolkit) - :DynamicPPL, # DynamicPPL (accessed via Turing) - :unwrap, # SymbolicUtils (accessed via Symbolics) - ), + # DynamicPPL.acclogp!! is reached through Turing's re-export; DynamicPPL + # is not a direct dependency, so accessing it via Turing is intentional. + ignore = (:DynamicPPL,), ), all_qualified_accesses_are_public = (; ignore = ( - :AbstractMCMCEnsemble, # AbstractMCMC (not public) - :AbstractSystem, # ModelingToolkit (not public) - :DynamicPPL, # Turing (not public) - :LN_SBPLX, # NLopt (not public) - :successful_retcode, # SciMLBase (not public) - :unwrap, # Symbolics (not public) + :AbstractMCMCEnsemble, # AbstractMCMC: no public alias for the ensemble type + :DynamicPPL, # Turing: submodule re-export, no public alias + :LN_SBPLX, # NLopt: algorithm constant, not marked public ), ), ),