From 0168685d1ec1ebfcae9cccdcecdb6ee4b03e8b2e Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 25 Jun 2026 06:00:03 -0400 Subject: [PATCH 1/5] QA: run_qa v1.6 form + ExplicitImports Convert the hand-rolled Aqua/JET QA into a single SciMLTesting `run_qa` call (v1.6 form) and enable ExplicitImports. - Aqua: `run_qa` runs `Aqua.test_all`; the original non-recursive ambiguities check is preserved via `aqua_kwargs`. - JET: folds the separate `test_jet.jl` into `run_qa` (`test_package`). - ExplicitImports: five checks pass; per-check `ignore` lists cover upstream re-exports / non-public names (ModelingToolkit, Symbolics, PythonCall, ModelingToolkitBase, SymbolicUtils, SymbolicIndexingInterface). `no_implicit_imports` is tracked-broken (issue #139) pending an explicit-imports refactor of the heavy `using` deps. - Bump SciMLTesting compat floor to 1.6 (root + QA env). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- Project.toml | 2 +- test/qa/Project.toml | 2 +- test/qa/qa.jl | 60 ++++++++++++++++++++++++++++++++++++-------- test/qa/test_jet.jl | 20 --------------- test/runtests.jl | 1 - 5 files changed, 51 insertions(+), 34 deletions(-) delete mode 100644 test/qa/test_jet.jl diff --git a/Project.toml b/Project.toml index 0c333df..5c9b4fe 100644 --- a/Project.toml +++ b/Project.toml @@ -15,7 +15,7 @@ ParserCombinator = "2" Pkg = "1" PythonCall = "0.9.29" SafeTestsets = "0.1, 1" -SciMLTesting = "1" +SciMLTesting = "1.6" Test = "1.10" julia = "1.10" diff --git a/test/qa/Project.toml b/test/qa/Project.toml index 2bea051..b082d60 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -13,5 +13,5 @@ BaseModelica = {path = "../.."} Aqua = "0.8" JET = "0.9, 0.11.2" SafeTestsets = "0.1, 1" -SciMLTesting = "1" +SciMLTesting = "1.6" julia = "1.10" diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 53e7585..1cd1fed 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -1,11 +1,49 @@ -using BaseModelica, Aqua -@testset "Aqua" begin - Aqua.find_persistent_tasks_deps(BaseModelica) - Aqua.test_ambiguities(BaseModelica, recursive = false) - Aqua.test_deps_compat(BaseModelica) - Aqua.test_piracies(BaseModelica) - Aqua.test_project_extras(BaseModelica) - Aqua.test_stale_deps(BaseModelica) - Aqua.test_unbound_args(BaseModelica) - Aqua.test_undefined_exports(BaseModelica) -end +using SciMLTesting, BaseModelica, Test +using JET + +run_qa( + BaseModelica; + explicit_imports = true, + # MLStyle's @match expands to ambiguous-looking method tables; the original QA + # ran ambiguities non-recursively, so keep that. + aqua_kwargs = (; ambiguities = (; recursive = false)), + ei_kwargs = (; + all_qualified_accesses_via_owners = (; + # All re-exported by ModelingToolkit/Symbolics from their base libs + # (ModelingToolkitBase / SymbolicIndexingInterface / SymbolicUtils); + # owner-vs-access mismatch only. + ignore = ( + :SymbolicContinuousCallback, # owned by ModelingToolkitBase + :SymbolicDiscreteCallback, # owned by ModelingToolkitBase + :getname, # owned by SymbolicIndexingInterface + :setdefault, # owned by ModelingToolkitBase + :setguess, # owned by ModelingToolkitBase + :unwrap, # owned by SymbolicUtils + ), + ), + all_qualified_accesses_are_public = (; + # Non-public names of upstream deps; will go public as those base libs + # mark their API. + ignore = ( + :Constant, # ModelingToolkitBase.MissingGuessValue + :ImperativeAffect, # ModelingToolkitBase + :isparameter, # ModelingToolkitBase + :SymbolicContinuousCallback, # ModelingToolkit (-> ModelingToolkitBase) + :SymbolicDiscreteCallback, # ModelingToolkit (-> ModelingToolkitBase) + :getname, # ModelingToolkit (-> SymbolicIndexingInterface) + :setdefault, # ModelingToolkit (-> ModelingToolkitBase) + :setguess, # ModelingToolkit (-> ModelingToolkitBase) + :unwrap, # Symbolics (-> SymbolicUtils) + :value, # Symbolics + :None, # PythonCall.pybuiltins + :hasattr, # PythonCall.pybuiltins + :len, # PythonCall.pybuiltins + ), + ), + ), + # The module relies on many implicit imports from heavy `using` of + # ModelingToolkit/ParserCombinator/PythonCall/MLStyle/CondaPkg; making them + # explicit is a large refactor tracked in + # https://github.com/SciML/BaseModelica.jl/issues/139. + ei_broken = (:no_implicit_imports,) +) diff --git a/test/qa/test_jet.jl b/test/qa/test_jet.jl deleted file mode 100644 index da9397a..0000000 --- a/test/qa/test_jet.jl +++ /dev/null @@ -1,20 +0,0 @@ -using BaseModelica -using JET -using Test - -@testset "JET static analysis" begin - # Test that JET finds no errors in the BaseModelica module - # This helps ensure type stability and catch potential runtime errors - # Note: We use target_modules to filter reports to only BaseModelica code, - # since dependencies like SymbolicUtils use union types that trigger false positives. - @testset "Package analysis" begin - result = JET.report_package(BaseModelica; target_modules = (BaseModelica,)) - @test length(JET.get_reports(result)) == 0 - end - - # Note: @test_opt tests are not included because the codebase intentionally - # uses dynamic dispatch patterns from MLStyle.jl (@match) and ParserCombinator.jl - # which result in expected runtime dispatch. These are design choices that enable - # clean pattern matching syntax. Full type stability would require major refactoring - # that's not practical for this domain-specific language parser. -end diff --git a/test/runtests.jl b/test/runtests.jl index 66a23a3..80caa6d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,7 +13,6 @@ function qa_group() Pkg.develop(PackageSpec(path = dirname(@__DIR__))) Pkg.instantiate() @safetestset "Quality Assurance" include(joinpath(@__DIR__, "qa", "qa.jl")) - @safetestset "JET Static Analysis" include(joinpath(@__DIR__, "qa", "test_jet.jl")) return nothing end From 91518d09fe79555f93fbbcab70fe7504a3f34929 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 25 Jun 2026 11:29:54 -0400 Subject: [PATCH 2/5] QA: disable Aqua persistent_tasks (PythonCall/CondaPkg probe false positive) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The run_qa v1.6 conversion routes QA through `Aqua.test_all`, which runs `test_persistent_tasks` by default — a check the pre-conversion qa.jl never asserted (it only called the non-asserting `find_persistent_tasks_deps`). That probe loads BaseModelica in a freshly generated wrapper environment built from this Project.toml alone. `using BaseModelica` there drives PythonCall's `__init__` -> CondaPkg, which installs a fresh pixi/conda environment in the wrapper dir and the wrapper precompile then fails to produce a usable cache for the package (ModelingToolkit cache-load race during precompile). The wrapper subprocess exits before signalling, so Aqua reports "done.log was not created, but precompilation exited" and fails the QA lane — a false positive, not a real persistent task. The package defines no `__init__`/`@async`/`Timer` of its own. Disable just that sub-check (restoring the pre-conversion scope). Verified locally on Julia 1.12.6 against released SciMLTesting 1.6.0: QA group is green (16 passed, 1 broken = the tracked `no_implicit_imports`, 0 failed). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- test/qa/qa.jl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 1cd1fed..5e1c5b0 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -6,7 +6,17 @@ run_qa( explicit_imports = true, # MLStyle's @match expands to ambiguous-looking method tables; the original QA # ran ambiguities non-recursively, so keep that. - aqua_kwargs = (; ambiguities = (; recursive = false)), + # + # persistent_tasks is disabled because Aqua's probe loads the package in a + # freshly generated wrapper environment built from this Project.toml alone, and + # `using BaseModelica` there drives PythonCall's `__init__` -> CondaPkg resolve. + # That precompile takes far longer than the probe's load window, so the wrapper + # subprocess exits before signalling and Aqua reports "done.log was not created, + # but precompilation exited" — a false positive. The package defines no + # `__init__`/`@async`/`Timer`, so it leaves no persistent tasks of its own. The + # pre-run_qa qa.jl never asserted this check (it only called the non-asserting + # `find_persistent_tasks_deps`); this keeps that scope. + aqua_kwargs = (; ambiguities = (; recursive = false), persistent_tasks = false), ei_kwargs = (; all_qualified_accesses_via_owners = (; # All re-exported by ModelingToolkit/Symbolics from their base libs From f1d5deb3d244370b5350d6ec7a3b36b3b00a0e72 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 26 Jun 2026 15:46:09 -0400 Subject: [PATCH 3/5] QA: strip redundant public-API EI ignores (SciMLTesting 1.7 + base-lib make-public) SciMLTesting 1.7 runs the two public-API ExplicitImports checks only on Julia >= 1.11 (skipped on lts). After the base-lib make-public releases (ModelingToolkitBase 1.48.0, Symbolics 7.29.0, SymbolicUtils 4.36.0, SciMLBase 3.24.0, ModelingToolkit 11.29.0), most entries in the all_qualified_accesses_are_public ignore-list are now public. Emptied the list and re-ran the QA group against the registered releases on Julia 1.12 (public checks RUN); restored only the 4 names still flagged non-public: - getname (ModelingToolkit re-export of SymbolicIndexingInterface) - None / hasattr / len (PythonCall.pybuiltins, non-SciML dep) Removed 9 now-public names: Constant, ImperativeAffect, isparameter, SymbolicContinuousCallback, SymbolicDiscreteCallback, setdefault, setguess, unwrap, value. Verified: 1.12 QA 16 pass / 1 broken / 0 fail; 1.10 lts 14 pass / 1 broken / 0 fail (public checks skipped on lts). all_qualified_accesses_via_owners and ei_broken=(:no_implicit_imports,) left untouched. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- test/qa/qa.jl | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 5e1c5b0..62eb408 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -32,19 +32,8 @@ run_qa( ), ), all_qualified_accesses_are_public = (; - # Non-public names of upstream deps; will go public as those base libs - # mark their API. ignore = ( - :Constant, # ModelingToolkitBase.MissingGuessValue - :ImperativeAffect, # ModelingToolkitBase - :isparameter, # ModelingToolkitBase - :SymbolicContinuousCallback, # ModelingToolkit (-> ModelingToolkitBase) - :SymbolicDiscreteCallback, # ModelingToolkit (-> ModelingToolkitBase) - :getname, # ModelingToolkit (-> SymbolicIndexingInterface) - :setdefault, # ModelingToolkit (-> ModelingToolkitBase) - :setguess, # ModelingToolkit (-> ModelingToolkitBase) - :unwrap, # Symbolics (-> SymbolicUtils) - :value, # Symbolics + :getname, # ModelingToolkit (re-export of SymbolicIndexingInterface, still non-public) :None, # PythonCall.pybuiltins :hasattr, # PythonCall.pybuiltins :len, # PythonCall.pybuiltins From 07e699b94f57bf2f3d0d264f4ae93a4a692f329b Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sat, 27 Jun 2026 00:02:37 -0400 Subject: [PATCH 4/5] QA: record 1.12 public-API verification basis for the EI ignore list Verified the all_qualified_accesses_are_public ignore list on Julia 1.12 (public-API checks active) against the registered base-lib make-public releases: SciMLBase 3.27.0, DiffEqBase 7.6.0, SciMLTesting 1.7.0, ModelingToolkit 11.29.0, JET 0.11.5. The four ignored names (getname, None, hasattr, len) are all still-non-public names of non-SciML dependencies (ModelingToolkit re-export of SymbolicIndexingInterface; PythonCall.pybuiltins); none is SciMLBase-owned, so none migrates to SciMLBase and each legitimately stays ignored. The source has no qualified DiffEqBase.X accesses of SciMLBase-owned public names to rewrite. Annotate the list with the verification basis. QA group result: Julia 1.12 (checks active) 16 pass / 1 broken / 0 error; Julia 1.10 (checks skipped per SciMLTesting 1.7) 14 pass / 1 broken / 0 error. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- test/qa/qa.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/qa/qa.jl b/test/qa/qa.jl index 62eb408..16fcb1d 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -32,6 +32,10 @@ run_qa( ), ), all_qualified_accesses_are_public = (; + # Verified on Julia 1.12 (public-API checks active) against the registered + # base-lib make-public releases (SciMLBase 3.27, DiffEqBase 7.6, + # SciMLTesting 1.7). None of these are SciMLBase-owned; each is a + # still-non-public name of a non-SciML dependency, so they stay ignored. ignore = ( :getname, # ModelingToolkit (re-export of SymbolicIndexingInterface, still non-public) :None, # PythonCall.pybuiltins From 99007f0809a2c808ad47f250fc81ff6088c7fb04 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Thu, 2 Jul 2026 21:09:48 -0400 Subject: [PATCH 5/5] CI: re-trigger QA lane (self-hosted runner lost communication) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prior run's QA (julia 1) lane failed with "The self-hosted runner lost communication with the server" — the test step was killed mid-run (step stuck in_progress, downstream steps left pending), not a QA assertion failure. All other lanes (Core lts/1/pre, Downgrade Core, runic, typos) passed. The stale run is too old to re-run via the API, so this empty commit re-triggers CI onto a healthy runner. Verified locally on Julia 1.12.6 against the current registered releases (SciMLTesting 1.7.0, JET 0.11.5, SciMLBase 3.31.0, ModelingToolkit 11.30.1, ModelingToolkitBase 1.50.0, Symbolics 7.29.0, SymbolicUtils 4.38.1): QA group is green — 16 passed, 1 broken (tracked no_implicit_imports, #139), 0 failed. Co-Authored-By: Chris Rackauckas