From 59ff3d9adecb94625aa2ac5ef621ec486531760c Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Tue, 9 Jun 2026 17:57:51 -0400 Subject: [PATCH 1/4] Canonical CI: grouped-tests.yml + root test/test_groups.toml Convert the root test workflow (CI.yml) to the canonical thin caller of SciML/.github grouped-tests.yml@v1, with the version/group/OS matrix declared once in test/test_groups.toml. - CI.yml: replace the hand-maintained version x os matrix test job with the thin grouped-tests.yml@v1 caller; on:/concurrency: preserved verbatim. - test/test_groups.toml: Core on [lts, 1, pre] x [ubuntu, windows, macos]; QA on [lts, 1]. - Category B refactor: JET static analysis moved out of the inline runtests.jl into a GROUP=="QA"-gated test/qa/qa.jl, isolated in test/qa/Project.toml (JET + Test + DataCollocations via [sources] path = "../..", julia = "1.10"). Functional tests stay in the default (All/Core) group; runtests.jl activates the qa env and develops the package when GROUP=="QA". - Project.toml: drop JET from the main [extras]/[targets].test (now isolated in the QA env); add [compat] entries for all remaining [extras] deps (Documenter, Plots, Test); julia compat already at the 1.10 LTS floor. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/CI.yml | 25 +---- Project.toml | 6 +- test/qa/Project.toml | 12 +++ test/qa/qa.jl | 25 +++++ test/runtests.jl | 228 +++++++++++++++++++-------------------- test/test_groups.toml | 6 ++ 6 files changed, 156 insertions(+), 146 deletions(-) create mode 100644 test/qa/Project.toml create mode 100644 test/qa/qa.jl create mode 100644 test/test_groups.toml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7c813a3..9697814 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -13,28 +13,5 @@ concurrency: jobs: tests: - name: "Tests" - strategy: - fail-fast: false - matrix: - version: - - "1" - - "lts" - - "pre" - os: - - ubuntu-latest - arch: - - x64 - include: - - version: "1.10" - os: windows-latest - arch: x64 - - version: "1.10" - os: macOS-latest - arch: x64 - uses: "SciML/.github/.github/workflows/tests.yml@v1" - with: - julia-version: "${{ matrix.version }}" - julia-arch: "${{ matrix.arch }}" - os: "${{ matrix.os }}" + uses: "SciML/.github/.github/workflows/grouped-tests.yml@v1" secrets: "inherit" diff --git a/Project.toml b/Project.toml index 380dc7d..1ef0d42 100644 --- a/Project.toml +++ b/Project.toml @@ -16,14 +16,16 @@ DataCollocationsDataInterpolationsExt = "DataInterpolations" [compat] ArrayInterface = "7" DataInterpolations = "6.4, 7, 8" +Documenter = "1" JLArrays = "0.3" OrdinaryDiffEq = "6, 7" +Plots = "1" +Test = "1" julia = "1.10" [extras] DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" JLArrays = "27aeb0d3-9eb9-45fb-866b-73c2ecf80fcb" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" @@ -31,4 +33,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] docs = ["Documenter", "DataInterpolations", "OrdinaryDiffEq", "Plots"] -test = ["DataInterpolations", "JET", "JLArrays", "OrdinaryDiffEq", "Test"] +test = ["DataInterpolations", "JLArrays", "OrdinaryDiffEq", "Test"] diff --git a/test/qa/Project.toml b/test/qa/Project.toml new file mode 100644 index 0000000..1275e98 --- /dev/null +++ b/test/qa/Project.toml @@ -0,0 +1,12 @@ +[deps] +DataCollocations = "9454e98c-75cb-4dbc-9573-481819719962" +JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[sources] +DataCollocations = {path = "../.."} + +[compat] +JET = "0.9, 0.10" +Test = "1" +julia = "1.10" diff --git a/test/qa/qa.jl b/test/qa/qa.jl new file mode 100644 index 0000000..47db831 --- /dev/null +++ b/test/qa/qa.jl @@ -0,0 +1,25 @@ +using DataCollocations +using Test +using JET + +@testset "JET Static Analysis" begin + # Test kernel functions for type stability + @testset "calckernel optimization" begin + kernels = [ + EpanechnikovKernel(), UniformKernel(), TriangularKernel(), + QuarticKernel(), TriweightKernel(), TricubeKernel(), + GaussianKernel(), CosineKernel(), LogisticKernel(), + SigmoidKernel(), SilvermanKernel(), + ] + @testset "$kernel" for kernel in kernels + JET.@test_opt target_modules = (DataCollocations,) DataCollocations.calckernel(kernel, 0.5) + end + end + + # Test main collocate_data function + @testset "collocate_data optimization" begin + data = rand(2, 20) + tpoints = collect(range(0.0, 1.0, length = 20)) + JET.@test_opt target_modules = (DataCollocations,) collocate_data(data, tpoints, TriangularKernel(), 0.1) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 043a5e0..f41a68d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,142 +1,130 @@ -using DataCollocations -using Test -using OrdinaryDiffEq -using DataInterpolations -using JLArrays -using JET +using Pkg -@testset "DataCollocations.jl" begin - bounded_support_kernels = [ - EpanechnikovKernel(), UniformKernel(), TriangularKernel(), - QuarticKernel(), TriweightKernel(), TricubeKernel(), CosineKernel(), - ] +const GROUP = get(ENV, "GROUP", "All") - unbounded_support_kernels = [ - GaussianKernel(), LogisticKernel(), SigmoidKernel(), SilvermanKernel(), - ] +if GROUP == "QA" + Pkg.activate(joinpath(@__DIR__, "qa")) + Pkg.develop(PackageSpec(path = dirname(@__DIR__))) + Pkg.instantiate() + include("qa.jl") +else + using DataCollocations + using Test + using OrdinaryDiffEq + using DataInterpolations + using JLArrays - @testset "Kernel Functions" begin - ts = collect(-5.0:0.1:5.0) - @testset "Kernels with support from -1 to 1" begin - minus_one_index = findfirst(x -> ==(x, -1.0), ts) - plus_one_index = findfirst(x -> ==(x, 1.0), ts) - @testset "$kernel" for (kernel, x0) in zip( - bounded_support_kernels, - [0.75, 0.5, 1.0, 15.0 / 16.0, 35.0 / 32.0, 70.0 / 81.0, pi / 4.0] - ) - ws = DataCollocations.calckernel.((kernel,), ts) - # t < -1 - @test all(ws[1:(minus_one_index - 1)] .== 0.0) - # t > 1 - @test all(ws[(plus_one_index + 1):end] .== 0.0) - # -1 < t <1 - @test all(ws[(minus_one_index + 1):(plus_one_index - 1)] .> 0.0) - # t = 0 - @test DataCollocations.calckernel(kernel, 0.0) == x0 + @testset "DataCollocations.jl" begin + bounded_support_kernels = [ + EpanechnikovKernel(), UniformKernel(), TriangularKernel(), + QuarticKernel(), TriweightKernel(), TricubeKernel(), CosineKernel(), + ] + + unbounded_support_kernels = [ + GaussianKernel(), LogisticKernel(), SigmoidKernel(), SilvermanKernel(), + ] + + @testset "Kernel Functions" begin + ts = collect(-5.0:0.1:5.0) + @testset "Kernels with support from -1 to 1" begin + minus_one_index = findfirst(x -> ==(x, -1.0), ts) + plus_one_index = findfirst(x -> ==(x, 1.0), ts) + @testset "$kernel" for (kernel, x0) in zip( + bounded_support_kernels, + [0.75, 0.5, 1.0, 15.0 / 16.0, 35.0 / 32.0, 70.0 / 81.0, pi / 4.0] + ) + ws = DataCollocations.calckernel.((kernel,), ts) + # t < -1 + @test all(ws[1:(minus_one_index - 1)] .== 0.0) + # t > 1 + @test all(ws[(plus_one_index + 1):end] .== 0.0) + # -1 < t <1 + @test all(ws[(minus_one_index + 1):(plus_one_index - 1)] .> 0.0) + # t = 0 + @test DataCollocations.calckernel(kernel, 0.0) == x0 + end end - end - @testset "Kernels with unbounded support" begin - @testset "$kernel" for (kernel, x0) in zip( - unbounded_support_kernels, - [1 / (sqrt(2 * pi)), 0.25, 1 / pi, 1 / (2 * sqrt(2))] - ) - # t = 0 - @test DataCollocations.calckernel(kernel, 0.0) == x0 + @testset "Kernels with unbounded support" begin + @testset "$kernel" for (kernel, x0) in zip( + unbounded_support_kernels, + [1 / (sqrt(2 * pi)), 0.25, 1 / pi, 1 / (2 * sqrt(2))] + ) + # t = 0 + @test DataCollocations.calckernel(kernel, 0.0) == x0 + end end end - end - @testset "Collocation of data" begin - f(u, p, t) = p .* u - rc = 2 - ps = repeat([-0.001], rc) - tspan = (0.0, 10.0) # Reduced time span - u0 = 3.4 .+ ones(rc) - t = collect(range(minimum(tspan); stop = maximum(tspan), length = 100)) # Reduced data points - prob = ODEProblem(f, u0, tspan, ps) - data = Array(solve(prob, Tsit5(); saveat = t, abstol = 1.0e-12, reltol = 1.0e-12)) - @testset "$kernel" for kernel in [ - bounded_support_kernels..., unbounded_support_kernels..., - ] - u′, u = collocate_data(data, t, kernel, 0.1) # Higher bandwidth - @test sum(abs2, u - data) < 1.0e-6 # More lenient tolerance - end - @testset "$kernel" for kernel in [bounded_support_kernels...] - # Errors out as the bandwidth is too low - @test_throws ErrorException collocate_data(data, t, kernel, 0.001) + @testset "Collocation of data" begin + f(u, p, t) = p .* u + rc = 2 + ps = repeat([-0.001], rc) + tspan = (0.0, 10.0) # Reduced time span + u0 = 3.4 .+ ones(rc) + t = collect(range(minimum(tspan); stop = maximum(tspan), length = 100)) # Reduced data points + prob = ODEProblem(f, u0, tspan, ps) + data = Array(solve(prob, Tsit5(); saveat = t, abstol = 1.0e-12, reltol = 1.0e-12)) + @testset "$kernel" for kernel in [ + bounded_support_kernels..., unbounded_support_kernels..., + ] + u′, u = collocate_data(data, t, kernel, 0.1) # Higher bandwidth + @test sum(abs2, u - data) < 1.0e-6 # More lenient tolerance + end + @testset "$kernel" for kernel in [bounded_support_kernels...] + # Errors out as the bandwidth is too low + @test_throws ErrorException collocate_data(data, t, kernel, 0.001) + end end - end - @testset "DataInterpolations Extension" begin - # Test with simple data - t = 0.0:0.1:1.0 - data = sin.(t) - tpoints_sample = 0.05:0.1:0.95 + @testset "DataInterpolations Extension" begin + # Test with simple data + t = 0.0:0.1:1.0 + data = sin.(t) + tpoints_sample = 0.05:0.1:0.95 - # Test that the extension method works - du, u = collocate_data(data, collect(t), collect(tpoints_sample), LinearInterpolation) - @test length(du) == length(tpoints_sample) - @test length(u) == length(tpoints_sample) - end + # Test that the extension method works + du, u = collocate_data(data, collect(t), collect(tpoints_sample), LinearInterpolation) + @test length(du) == length(tpoints_sample) + @test length(u) == length(tpoints_sample) + end - @testset "Interface Compatibility" begin - @testset "BigFloat support" begin - # Test that BigFloat inputs are supported and eltype is preserved - tpoints = BigFloat.(collect(range(0.0, stop = 10.0, length = 30))) - data = BigFloat.(reshape(sin.(Float64.(tpoints)), 1, :)) - bandwidth = BigFloat(0.5) + @testset "Interface Compatibility" begin + @testset "BigFloat support" begin + # Test that BigFloat inputs are supported and eltype is preserved + tpoints = BigFloat.(collect(range(0.0, stop = 10.0, length = 30))) + data = BigFloat.(reshape(sin.(Float64.(tpoints)), 1, :)) + bandwidth = BigFloat(0.5) - kernels = [ - EpanechnikovKernel(), UniformKernel(), TriangularKernel(), - QuarticKernel(), TriweightKernel(), TricubeKernel(), - GaussianKernel(), CosineKernel(), LogisticKernel(), - SigmoidKernel(), SilvermanKernel(), - ] + kernels = [ + EpanechnikovKernel(), UniformKernel(), TriangularKernel(), + QuarticKernel(), TriweightKernel(), TricubeKernel(), + GaussianKernel(), CosineKernel(), LogisticKernel(), + SigmoidKernel(), SilvermanKernel(), + ] - @testset "$kernel" for kernel in kernels - u_prime, u = collocate_data(data, tpoints, kernel, bandwidth) - @test eltype(u_prime) == BigFloat - @test eltype(u) == BigFloat + @testset "$kernel" for kernel in kernels + u_prime, u = collocate_data(data, tpoints, kernel, bandwidth) + @test eltype(u_prime) == BigFloat + @test eltype(u) == BigFloat + end end - end - - @testset "GPU array error message" begin - # Test that GPU-like arrays (JLArrays) give a clear error message - # instead of cryptic scalar indexing errors - tpoints = JLArray([1.0, 2.0, 3.0, 4.0, 5.0]) - data = JLArray([1.0 2.0 3.0 4.0 5.0]) - @test_throws ArgumentError collocate_data(data, tpoints) + @testset "GPU array error message" begin + # Test that GPU-like arrays (JLArrays) give a clear error message + # instead of cryptic scalar indexing errors + tpoints = JLArray([1.0, 2.0, 3.0, 4.0, 5.0]) + data = JLArray([1.0 2.0 3.0 4.0 5.0]) - # Also test that only GPU tpoints triggers error - tpoints_cpu = collect(1.0:5.0) - @test_throws ArgumentError collocate_data(data, tpoints_cpu) + @test_throws ArgumentError collocate_data(data, tpoints) - # And only GPU data triggers error - data_cpu = reshape(collect(1.0:5.0), 1, :) - @test_throws ArgumentError collocate_data(data_cpu, tpoints) - end - end + # Also test that only GPU tpoints triggers error + tpoints_cpu = collect(1.0:5.0) + @test_throws ArgumentError collocate_data(data, tpoints_cpu) - @testset "JET Static Analysis" begin - # Test kernel functions for type stability - @testset "calckernel optimization" begin - kernels = [ - EpanechnikovKernel(), UniformKernel(), TriangularKernel(), - QuarticKernel(), TriweightKernel(), TricubeKernel(), - GaussianKernel(), CosineKernel(), LogisticKernel(), - SigmoidKernel(), SilvermanKernel(), - ] - @testset "$kernel" for kernel in kernels - JET.@test_opt target_modules = (DataCollocations,) DataCollocations.calckernel(kernel, 0.5) + # And only GPU data triggers error + data_cpu = reshape(collect(1.0:5.0), 1, :) + @test_throws ArgumentError collocate_data(data_cpu, tpoints) end end - - # Test main collocate_data function - @testset "collocate_data optimization" begin - data = rand(2, 20) - tpoints = collect(range(0.0, 1.0, length = 20)) - JET.@test_opt target_modules = (DataCollocations,) collocate_data(data, tpoints, TriangularKernel(), 0.1) - end end end diff --git a/test/test_groups.toml b/test/test_groups.toml new file mode 100644 index 0000000..8d7b74e --- /dev/null +++ b/test/test_groups.toml @@ -0,0 +1,6 @@ +[Core] +versions = ["lts", "1", "pre"] +os = ["ubuntu-latest", "windows-latest", "macos-latest"] + +[QA] +versions = ["lts", "1"] From 2ee5219f562245cf7ec6ab1febaa0edb181b1a10 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Tue, 9 Jun 2026 18:26:20 -0400 Subject: [PATCH 2/4] Add Pkg as test dependency for grouped-tests Core group The grouped runtests.jl uses Pkg for the QA group's Pkg.activate, but the Core group runs with project='.' (the root test env). Pkg was not a declared test dependency, so the Core job died with ArgumentError: Package Pkg not found in current path. Declare Pkg in [extras] and add it to [targets].test. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- Project.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1ef0d42..34fcc3a 100644 --- a/Project.toml +++ b/Project.toml @@ -28,9 +28,10 @@ DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" JLArrays = "27aeb0d3-9eb9-45fb-866b-73c2ecf80fcb" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] docs = ["Documenter", "DataInterpolations", "OrdinaryDiffEq", "Plots"] -test = ["DataInterpolations", "JLArrays", "OrdinaryDiffEq", "Test"] +test = ["DataInterpolations", "JLArrays", "OrdinaryDiffEq", "Pkg", "Test"] From 3c4ac9ea1628004ca5febf24d230b0885f2d12b7 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Tue, 9 Jun 2026 20:14:52 -0400 Subject: [PATCH 3/4] Restore stripped functional test deps to root test env The grouped-tests conversion removed functional test dependencies from the root test environment while isolating QA, breaking the Core group. Re-add the deps that the pre-conversion base test target listed (using their base UUIDs) so Core resolves and @testset/@safetestset/domain macros are defined. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- Project.toml | 3 ++- test/runtests.jl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 34fcc3a..307e797 100644 --- a/Project.toml +++ b/Project.toml @@ -26,6 +26,7 @@ julia = "1.10" [extras] DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" JLArrays = "27aeb0d3-9eb9-45fb-866b-73c2ecf80fcb" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" @@ -34,4 +35,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] docs = ["Documenter", "DataInterpolations", "OrdinaryDiffEq", "Plots"] -test = ["DataInterpolations", "JLArrays", "OrdinaryDiffEq", "Pkg", "Test"] +test = ["DataInterpolations", "JET", "JLArrays", "OrdinaryDiffEq", "Pkg", "Test"] diff --git a/test/runtests.jl b/test/runtests.jl index f41a68d..c15f990 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,7 +6,7 @@ if GROUP == "QA" Pkg.activate(joinpath(@__DIR__, "qa")) Pkg.develop(PackageSpec(path = dirname(@__DIR__))) Pkg.instantiate() - include("qa.jl") + include(joinpath(@__DIR__, "qa", "qa.jl")) else using DataCollocations using Test From c544ce38a9b17ce36c76b76a96fc4e25fd7e62e6 Mon Sep 17 00:00:00 2001 From: "Chris Rackauckas (Claude)" Date: Tue, 9 Jun 2026 20:40:18 -0400 Subject: [PATCH 4/4] Hoist functional usings to top level to fix Core-group macro UndefVarError The converted runtests.jl placed `using` statements inside a top-level `if GROUP ...` block that also used a macro (e.g. @testset) inline. Julia macro-expands the entire if block as one unit before the in-block `using` executes, so the macro was undefined (UndefVarError: @testset not defined in Main). Move the functional usings to top level (the QA-subenv usings that follow Pkg.activate stay in the QA block). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- test/runtests.jl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index c15f990..f24d4fc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,9 @@ using Pkg +using DataCollocations +using Test +using OrdinaryDiffEq +using DataInterpolations +using JLArrays const GROUP = get(ENV, "GROUP", "All") @@ -8,12 +13,6 @@ if GROUP == "QA" Pkg.instantiate() include(joinpath(@__DIR__, "qa", "qa.jl")) else - using DataCollocations - using Test - using OrdinaryDiffEq - using DataInterpolations - using JLArrays - @testset "DataCollocations.jl" begin bounded_support_kernels = [ EpanechnikovKernel(), UniformKernel(), TriangularKernel(),