From 0c0f5a87388c4f243a3e24222a54d508e89438eb Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 15 Oct 2026 09:13:25 -0500 Subject: [PATCH] Move test_density_interface to a Test extension Loading Test accounted for nearly all of DensityInterface's load time. `test_density_interface` is now defined in DensityInterfaceTestExt, which loads when Test is loaded. Calling it without Test loaded throws a MethodError whose message tells the user to run `using Test`. Extensions require Julia 1.9, so the minimum supported version is now Julia 1.10 (LTS), and CI tests LTS instead of 1.0. Fixes #38 Assisted-by: Claude Opus 5.5 --- .github/workflows/ci.yml | 2 +- Project.toml | 10 ++++- .../DensityInterfaceTestExt.jl | 26 ++++--------- src/DensityInterface.jl | 37 ++++++++++++++++++- test/test_interface.jl | 12 ++++++ 5 files changed, 63 insertions(+), 24 deletions(-) rename src/interface_test.jl => ext/DensityInterfaceTestExt.jl (51%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a765b69..ce81ab9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: fail-fast: false matrix: version: - - '1.0' + - 'lts' - '1' - 'nightly' os: diff --git a/Project.toml b/Project.toml index a875e93..88281ef 100644 --- a/Project.toml +++ b/Project.toml @@ -4,15 +4,21 @@ version = "0.4.0" [deps] InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[weakdeps] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +[extensions] +DensityInterfaceTestExt = "Test" + [compat] InverseFunctions = "0.1" -julia = "1" +julia = "1.10" [extras] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Documenter", "LinearAlgebra"] +test = ["Documenter", "LinearAlgebra", "Test"] diff --git a/src/interface_test.jl b/ext/DensityInterfaceTestExt.jl similarity index 51% rename from src/interface_test.jl rename to ext/DensityInterfaceTestExt.jl index 24259f2..706af1c 100644 --- a/src/interface_test.jl +++ b/ext/DensityInterfaceTestExt.jl @@ -1,26 +1,12 @@ # This file is a part of DensityInterface.jl, licensed under the MIT License (MIT). -""" - DensityInterface.test_density_interface(object, x, ref_logd_at_x; kwargs...) +module DensityInterfaceTestExt -Test that `object` is compatible with `DensityInterface`. +using Test: @test, @testset +using DensityInterface: DensityInterface, DensityKind, IsDensity, IsOrHasDensity, + logdensityof, densityof, logfuncdensity, funcdensity -Tests that either `DensityKind(object) isa IsOrHasDensity`. - -Also tests that [`logdensityof(object, x)`](@ref) equals `ref_logd_at_x` and -that the behavior of [`logdensityof(object)`](@ref), -[`densityof(object, x)`](@ref) and [`densityof(object)`](@ref) is consistent. - -The results of `logdensityof(object, x)` and `densityof(object, x)` are compared to -`ref_logd_at_x` and `exp(ref_logd_at_x)` using `isapprox`. `kwargs...` are -forwarded to `isapprox`. - -Also tests that `d = logfuncdensity(logdensityof(object))` returns a density -(`DensityKind(d) == IsDensity()`) that is equivalent to `object` in respect to -`logdensityof` and `densityof`, and that `funcdensity(densityof(object))` -behaves the same way. -""" -function test_density_interface(object, x, ref_logd_at_x; kwargs...) +function DensityInterface.test_density_interface(object, x, ref_logd_at_x; kwargs...) @testset "test_density_interface: $object with input $x" begin ref_d_at_x = exp(ref_logd_at_x) @@ -43,3 +29,5 @@ function test_density_interface(object, x, ref_logd_at_x; kwargs...) end end end + +end # module diff --git a/src/DensityInterface.jl b/src/DensityInterface.jl index b7798e2..35d1cc1 100644 --- a/src/DensityInterface.jl +++ b/src/DensityInterface.jl @@ -9,9 +9,42 @@ associated with a density. module DensityInterface using InverseFunctions -using Test include("interface.jl") -include("interface_test.jl") + +""" + DensityInterface.test_density_interface(object, x, ref_logd_at_x; kwargs...) + +Test that `object` is compatible with `DensityInterface`. + +Tests that either `DensityKind(object) isa IsOrHasDensity`. + +Also tests that [`logdensityof(object, x)`](@ref) equals `ref_logd_at_x` and +that the behavior of [`logdensityof(object)`](@ref), +[`densityof(object, x)`](@ref) and [`densityof(object)`](@ref) is consistent. + +The results of `logdensityof(object, x)` and `densityof(object, x)` are compared to +`ref_logd_at_x` and `exp(ref_logd_at_x)` using `isapprox`. `kwargs...` are +forwarded to `isapprox`. + +Also tests that `d = logfuncdensity(logdensityof(object))` returns a density +(`DensityKind(d) == IsDensity()`) that is equivalent to `object` in respect to +`logdensityof` and `densityof`, and that `funcdensity(densityof(object))` +behaves the same way. + +!!! note + This function is available only after the `Test` standard library has been + loaded. +""" +function test_density_interface end + +function __init__() + Base.Experimental.register_error_hint(MethodError) do io, exc, _, _ + if exc.f === test_density_interface && + Base.get_extension(DensityInterface, :DensityInterfaceTestExt) === nothing + print(io, "\n`test_density_interface` requires the Test standard library; run `using Test` first.") + end + end +end end # module diff --git a/test/test_interface.jl b/test/test_interface.jl index 8bc2264..6aaf16d 100644 --- a/test/test_interface.jl +++ b/test/test_interface.jl @@ -36,4 +36,16 @@ DensityInterface.logdensityof(::MyMeasure, x::Any) = -norm(x)^2 @test DensityKind(object3) === IsDensity() DensityInterface.test_density_interface(object3, x, -norm(x)^2) end + + # Needs a process that has not loaded Test + code = """ + using DensityInterface + try + DensityInterface.test_density_interface(1, 1, 1) + catch e + showerror(stdout, e) + end + """ + cmd = `$(Base.julia_cmd()) --startup-file=no --project=$(Base.active_project()) -e $code` + @test occursin("run `using Test` first", read(cmd, String)) end