From 81c7f9e5ad148c37c05beac7f8a2556bb8fb9089 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sun, 14 Jun 2026 04:51:54 -0400 Subject: [PATCH] Canonicalize tests to @safetestset for per-unit module isolation Convert the independent top-level test units in test/runtests.jl from plain `@testset` to `@safetestset`, so each unit runs in its own freshly generated module (matching the canonical OrdinaryDiffEq structure for isolation between tests and world-age safety). Each unit body is extracted into its own self-contained file and invoked as `@safetestset "X" begin include("x.jl") end`: - explicit_imports.jl (Explicit Imports; already self-contained upstream) - examples_tests.jl (Examples: set_log_level + parametrized example loop) - creation_tests.jl (Creation: includes test_create/test_pycreate/test_jfem) - assign_tests.jl (assign) - interface_tests.jl (Interface) Each new extracted file carries its own `using FEniCS` / `using Test`. The include() form (rather than an inline @safetestset body) is used so imports run before any FEniCS symbols are resolved, and include paths resolve relative to test/ as before. The parametrized example loop and other inner testsets stay plain @testset since the unit-level @safetestset already isolates them. Add SafeTestsets to test deps ([extras] + [targets].test, compat "0.1, 1"). Same units run in the same order with the same assertions; only the isolation mechanism changed. FEniCS requires the FEniCS Python stack (CI container cmhyett/julia-fenics), so this is verified statically (Meta.parseall clean on all test files; each @safetestset has its imports); CI will run the suite. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- Project.toml | 4 ++- test/assign_tests.jl | 14 +++++++++++ test/creation_tests.jl | 6 +++++ test/examples_tests.jl | 19 ++++++++++++++ test/interface_tests.jl | 14 +++++++++++ test/runtests.jl | 56 +++++++++-------------------------------- 6 files changed, 68 insertions(+), 45 deletions(-) create mode 100644 test/assign_tests.jl create mode 100644 test/creation_tests.jl create mode 100644 test/examples_tests.jl create mode 100644 test/interface_tests.jl diff --git a/Project.toml b/Project.toml index b13eb0c..7aa2b05 100644 --- a/Project.toml +++ b/Project.toml @@ -17,6 +17,7 @@ OrdinaryDiffEqSDIRK = "1, 2" ProgressMeter = "1.2" PyCall = "1.90" Requires = "0.5, 1.0" +SafeTestsets = "0.1, 1" SpecialFunctions = "0.8, 0.9, 1.2, 2" julia = "1.6" @@ -25,7 +26,8 @@ ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b" ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" OrdinaryDiffEqSDIRK = "2d112036-d095-4a1e-ab9a-08536f3ecdbf" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["ADTypes", "ExplicitImports", "Test", "OrdinaryDiffEq", "OrdinaryDiffEqSDIRK"] +test = ["ADTypes", "ExplicitImports", "Test", "OrdinaryDiffEq", "OrdinaryDiffEqSDIRK", "SafeTestsets"] diff --git a/test/assign_tests.jl b/test/assign_tests.jl new file mode 100644 index 0000000..cf6021c --- /dev/null +++ b/test/assign_tests.jl @@ -0,0 +1,14 @@ +using FEniCS +using Test + +@testset "assign" begin + mesh = UnitIntervalMesh(10) + V = FunctionSpace(mesh, "P", 1) + u = interpolate(Expression("x[0]", degree = 1), V) + arr1 = get_array(u) + arr2 = randn(size(arr1)) + assign(u, arr2) + @test get_array(u) == arr2 + assign(u, arr1) + @test get_array(u) == arr1 +end diff --git a/test/creation_tests.jl b/test/creation_tests.jl new file mode 100644 index 0000000..00542ad --- /dev/null +++ b/test/creation_tests.jl @@ -0,0 +1,6 @@ +using FEniCS +using Test + +@test include("test_create.jl") +@test include("test_pycreate.jl") +@test include("test_jfem.jl") diff --git a/test/examples_tests.jl b/test/examples_tests.jl new file mode 100644 index 0000000..4670d0b --- /dev/null +++ b/test/examples_tests.jl @@ -0,0 +1,19 @@ +using FEniCS +using Test + +FEniCS.set_log_level(FEniCS.WARNING) + +examples_dir = joinpath(@__DIR__, "..", "examples") +@assert ispath(examples_dir) +example_filenames = readdir(examples_dir) +@assert "tutorial1.jl" in example_filenames + +@testset "Example $(filename)" for filename in example_filenames + path = joinpath(examples_dir, filename) + @test (include(path); true) + # Test that PyCall is not used in the example. + # Use of PyCall indicates, that we did not wrap enough + # functionality + s = read(path, String) + @test !occursin("PyCall", s) +end diff --git a/test/interface_tests.jl b/test/interface_tests.jl new file mode 100644 index 0000000..a6a99bf --- /dev/null +++ b/test/interface_tests.jl @@ -0,0 +1,14 @@ +using FEniCS +using Test + +#tests relating to interface.jl file +@testset "Interface" begin + global mesh = UnitSquareMesh(2, 2) + dbc(x, y) = 1 + global u = feMesh(mesh, dbc) + @test (u.n_nodes == 9) + @test (u.n_elements == 8) + @test (u.n_internal_nodes == 1) + @test (u.n_boundary_nodes == 8) + @test (u.node_vals == [1, 1, 1, 1, 0, 1, 1, 1, 1]) +end diff --git a/test/runtests.jl b/test/runtests.jl index 4691c1f..5431f89 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,53 +1,21 @@ -using FEniCS -using Test +using SafeTestsets -FEniCS.set_log_level(FEniCS.WARNING) - -@testset "Explicit Imports" begin +@safetestset "Explicit Imports" begin include("explicit_imports.jl") end -examples_dir = joinpath(@__DIR__, "..", "examples") -@assert ispath(examples_dir) -example_filenames = readdir(examples_dir) -@assert "tutorial1.jl" in example_filenames - -@testset "Example $(filename)" for filename in example_filenames - path = joinpath(examples_dir, filename) - @test (include(path); true) - # Test that PyCall is not used in the example. - # Use of PyCall indicates, that we did not wrap enough - # functionality - s = read(path, String) - @test !occursin("PyCall", s) +@safetestset "Examples" begin + include("examples_tests.jl") end -@testset "Creation" begin - @test include("test_create.jl") - @test include("test_pycreate.jl") - @test include("test_jfem.jl") -end; +@safetestset "Creation" begin + include("creation_tests.jl") +end -@testset "assign" begin - mesh = UnitIntervalMesh(10) - V = FunctionSpace(mesh, "P", 1) - u = interpolate(Expression("x[0]", degree = 1), V) - arr1 = get_array(u) - arr2 = randn(size(arr1)) - assign(u, arr2) - @test get_array(u) == arr2 - assign(u, arr1) - @test get_array(u) == arr1 +@safetestset "assign" begin + include("assign_tests.jl") end -#tests relating to interface.jl file -@testset "Interface" begin - global mesh = UnitSquareMesh(2, 2) - dbc(x, y) = 1 - global u = feMesh(mesh, dbc) - @test (u.n_nodes == 9) - @test (u.n_elements == 8) - @test (u.n_internal_nodes == 1) - @test (u.n_boundary_nodes == 8) - @test (u.node_vals == [1, 1, 1, 1, 0, 1, 1, 1, 1]) -end; +@safetestset "Interface" begin + include("interface_tests.jl") +end