From 3243c0f4be92aa93c74689c155fdac50c0206dcd Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sun, 14 Jun 2026 08:34:52 -0400 Subject: [PATCH] Use SciMLTesting v1.2 folder-based run_tests Convert the test suite to the SciMLTesting v1.2 folder-discovery model: - runtests.jl is now `using SciMLTesting; run_tests()`. The folder layout is the single source of dispatch: * Core = top-level test/*.jl (sampler_tests, bigfloat_tests, passthrough_rng_tests, alloc_tests), run with the main test env. * QA = test/qa/ (its own Project.toml sub-env, activated by run_tests). test_groups.toml is unchanged (Core, QA). - Extract the previously-inline non-QA testsets into self-contained top-level Core files (sampler_tests.jl, bigfloat_tests.jl, passthrough_rng_tests.jl); alloc_tests.jl was already a file. Each is run in its own @safetestset by the harness, so the exact set of tests per GROUP is preserved. - Add SciMLTesting + SafeTestsets to the root test deps ([extras]+[targets].test, with [compat]) and to test/qa/Project.toml. Drop Pkg from the root test deps and its compat (only the old hand-written QA-activation harness used it). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Opus 4.8 (1M context) --- Project.toml | 8 +- test/bigfloat_tests.jl | 25 ++++++ test/passthrough_rng_tests.jl | 16 ++++ test/qa/Project.toml | 4 + test/runtests.jl | 147 +--------------------------------- test/sampler_tests.jl | 91 +++++++++++++++++++++ 6 files changed, 143 insertions(+), 148 deletions(-) create mode 100644 test/bigfloat_tests.jl create mode 100644 test/passthrough_rng_tests.jl create mode 100644 test/sampler_tests.jl diff --git a/Project.toml b/Project.toml index a7a2e1f..89898db 100644 --- a/Project.toml +++ b/Project.toml @@ -14,9 +14,10 @@ Distributions = "0.25" ExplicitImports = "1.14.0" JET = "0.9, 0.10, 0.11" LogExpFunctions = "0.3.14, 1.0" -Pkg = "1" PrecompileTools = "1.2.0" Random = "1.10" +SafeTestsets = "0.1, 1" +SciMLTesting = "1" Statistics = "1" Test = "1" julia = "1.10" @@ -27,9 +28,10 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["AllocCheck", "Aqua", "Statistics", "Test", "Distributions", "JET", "ExplicitImports", "Pkg"] +test = ["AllocCheck", "Aqua", "Statistics", "Test", "Distributions", "JET", "ExplicitImports", "SafeTestsets", "SciMLTesting"] diff --git a/test/bigfloat_tests.jl b/test/bigfloat_tests.jl new file mode 100644 index 0000000..a635052 --- /dev/null +++ b/test/bigfloat_tests.jl @@ -0,0 +1,25 @@ +using PoissonRandom +using Statistics +using Test + +@testset "count_rand with BigFloat (λ < 6)" begin + for _ in 1:100 + result = pois_rand(BigFloat(3.0)) + @test result isa Integer + @test result >= 0 + end +end +@testset "ad_rand with BigFloat (λ >= 6)" begin + for _ in 1:100 + result = pois_rand(BigFloat(15.0)) + @test result isa Integer + @test result >= 0 + end +end +@testset "statistical validity with BigFloat" begin + n = 10000 + λ = BigFloat(10.0) + samples = [pois_rand(λ) for _ in 1:n] + sample_mean = mean(samples) + @test abs(sample_mean - Float64(λ)) < 3 * sqrt(Float64(λ)) +end diff --git a/test/passthrough_rng_tests.jl b/test/passthrough_rng_tests.jl new file mode 100644 index 0000000..6321a1e --- /dev/null +++ b/test/passthrough_rng_tests.jl @@ -0,0 +1,16 @@ +using PoissonRandom +using Random: Random, UInt52Raw +using Test + +prng = PassthroughRNG() +# The CUDA.jl @device_override Random.randexp(::AbstractRNG) shadows our +# specific Random.randexp(::PassthroughRNG) on the GPU because Julia's +# OverlayMethodTable returns overlay matches without consulting the base +# table when the overlay fully covers the signature. The override body +# then calls these against PassthroughRNG; if they MethodError, kernel +# compilation fails with InvalidIRError on jl_f_throw_methoderror. +@test Random.rng_native_52(prng) === UInt64 +@test Random.rand(prng, UInt52Raw()) isa UInt64 +@test Random.rand(prng, UInt64) isa UInt64 +@test Random.rand(prng, Float32) isa Float32 +@test Random.rand(prng, Float64) isa Float64 diff --git a/test/qa/Project.toml b/test/qa/Project.toml index d5bc8f0..6de5d86 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -4,6 +4,8 @@ ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" PoissonRandom = "e409e4f3-bfea-5376-8464-e040bb5c01ab" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] @@ -14,5 +16,7 @@ Aqua = "0.8" ExplicitImports = "1.14.0" JET = "0.9, 0.10, 0.11" Random = "1.10" +SafeTestsets = "0.1, 1" +SciMLTesting = "1" Test = "1" julia = "1.10" diff --git a/test/runtests.jl b/test/runtests.jl index 9c8fded..a18a7cc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,145 +1,2 @@ -using PoissonRandom -import Distributions -using Test, Statistics - -const GROUP = get(ENV, "GROUP", "All") - -if GROUP == "QA" - using Pkg - Pkg.activate(joinpath(@__DIR__, "qa")) - Pkg.develop(Pkg.PackageSpec(path = joinpath(@__DIR__, ".."))) - Pkg.instantiate() - include(joinpath(@__DIR__, "qa", "qa.jl")) -else - n_tsamples = 10^5 - - function test_samples( - rand_func, - distr::Distributions.DiscreteUnivariateDistribution, - n::Int; # number of samples to generate - q::Float64 = 1.0e-8, # confidence interval, 1 - q as confidence - verbose::Bool = false - ) # show intermediate info (for debugging) - - # The basic idea - # ------------------ - # Generate n samples, and count the occurrences of each value within a reasonable range. - # For each distinct value, it computes an confidence interval of the counts - # and checks whether the count is within this interval. - # - # If the distribution has a bounded range, it also checks whether - # the samples are all within this range. - # - # By setting a small q, we ensure that failure of the tests rarely - # happen in practice. - # - λ = distr.λ - n > 1 || error("The number of samples must be greater than 1.") - 0.0 < q < 0.1 || error("The value of q must be within the open interval (0.0, 0.1).") - - # determine the range of values to examine - vmin = minimum(distr) - vmax = maximum(distr) - - rmin = floor(Int, quantile(distr, 0.00001))::Int - rmax = floor(Int, quantile(distr, 0.99999))::Int - m = rmax - rmin + 1 # length of the range - p0 = Distributions.pdf.((distr,), rmin:rmax) # reference probability masses - @assert length(p0) == m - - # determine confidence intervals for counts: - # with probability q, the count will be out of this interval. - # - clb = Vector{Int}(undef, m) - cub = Vector{Int}(undef, m) - for i in 1:m - bp = Distributions.Binomial(n, p0[i]) - clb[i] = floor(Int, quantile(bp, q / 2)) - cub[i] = ceil(Int, Distributions.cquantile(bp, q / 2)) - @assert cub[i] >= clb[i] - end - - # generate samples - samples = [rand_func(λ) for i in 1:n] - @assert length(samples) == n - - # scan samples and get counts - cnts = zeros(Int, m) - for i in 1:n - @inbounds si = samples[i] - if rmin <= si <= rmax - cnts[si - rmin + 1] += 1 - else - vmin <= si <= vmax || - error("Sample value out of valid range.") - end - end - - # check the counts - for i in 1:m - verbose && println("v = $(rmin + i - 1) ==> ($(clb[i]), $(cub[i])): $(cnts[i])") - clb[i] <= cnts[i] <= cub[i] || - error("The counts are out of the confidence interval.") - end - return samples - end - - println("testing count random sampler") - for λ in [0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0, 30.0] - test_samples(PoissonRandom.count_rand, Distributions.Poisson(λ), n_tsamples) - end - - println("testing ad random sampler") - for λ in [5.0, 10.0, 15.0, 20.0, 30.0] - test_samples(PoissonRandom.ad_rand, Distributions.Poisson(λ), n_tsamples) - end - - println("testing mixed random sampler") - for λ in [5.0, 10.0, 15.0, 20.0, 30.0] - test_samples(pois_rand, Distributions.Poisson(λ), n_tsamples) - end - - @testset "BigFloat support" begin - @testset "count_rand with BigFloat (λ < 6)" begin - for _ in 1:100 - result = pois_rand(BigFloat(3.0)) - @test result isa Integer - @test result >= 0 - end - end - @testset "ad_rand with BigFloat (λ >= 6)" begin - for _ in 1:100 - result = pois_rand(BigFloat(15.0)) - @test result isa Integer - @test result >= 0 - end - end - @testset "statistical validity with BigFloat" begin - n = 10000 - λ = BigFloat(10.0) - samples = [pois_rand(λ) for _ in 1:n] - sample_mean = mean(samples) - @test abs(sample_mean - Float64(λ)) < 3 * sqrt(Float64(λ)) - end - end - - @testset "PassthroughRNG dispatch" begin - using Random: Random, UInt52Raw - prng = PassthroughRNG() - # The CUDA.jl @device_override Random.randexp(::AbstractRNG) shadows our - # specific Random.randexp(::PassthroughRNG) on the GPU because Julia's - # OverlayMethodTable returns overlay matches without consulting the base - # table when the overlay fully covers the signature. The override body - # then calls these against PassthroughRNG; if they MethodError, kernel - # compilation fails with InvalidIRError on jl_f_throw_methoderror. - @test Random.rng_native_52(prng) === UInt64 - @test Random.rand(prng, UInt52Raw()) isa UInt64 - @test Random.rand(prng, UInt64) isa UInt64 - @test Random.rand(prng, Float32) isa Float32 - @test Random.rand(prng, Float64) isa Float64 - end - - @testset "Allocation Tests" begin - include("alloc_tests.jl") - end -end +using SciMLTesting +run_tests() diff --git a/test/sampler_tests.jl b/test/sampler_tests.jl new file mode 100644 index 0000000..08c18da --- /dev/null +++ b/test/sampler_tests.jl @@ -0,0 +1,91 @@ +using PoissonRandom +import Distributions +using Statistics + +n_tsamples = 10^5 + +function test_samples( + rand_func, + distr::Distributions.DiscreteUnivariateDistribution, + n::Int; # number of samples to generate + q::Float64 = 1.0e-8, # confidence interval, 1 - q as confidence + verbose::Bool = false + ) # show intermediate info (for debugging) + + # The basic idea + # ------------------ + # Generate n samples, and count the occurrences of each value within a reasonable range. + # For each distinct value, it computes an confidence interval of the counts + # and checks whether the count is within this interval. + # + # If the distribution has a bounded range, it also checks whether + # the samples are all within this range. + # + # By setting a small q, we ensure that failure of the tests rarely + # happen in practice. + # + λ = distr.λ + n > 1 || error("The number of samples must be greater than 1.") + 0.0 < q < 0.1 || error("The value of q must be within the open interval (0.0, 0.1).") + + # determine the range of values to examine + vmin = minimum(distr) + vmax = maximum(distr) + + rmin = floor(Int, quantile(distr, 0.00001))::Int + rmax = floor(Int, quantile(distr, 0.99999))::Int + m = rmax - rmin + 1 # length of the range + p0 = Distributions.pdf.((distr,), rmin:rmax) # reference probability masses + @assert length(p0) == m + + # determine confidence intervals for counts: + # with probability q, the count will be out of this interval. + # + clb = Vector{Int}(undef, m) + cub = Vector{Int}(undef, m) + for i in 1:m + bp = Distributions.Binomial(n, p0[i]) + clb[i] = floor(Int, quantile(bp, q / 2)) + cub[i] = ceil(Int, Distributions.cquantile(bp, q / 2)) + @assert cub[i] >= clb[i] + end + + # generate samples + samples = [rand_func(λ) for i in 1:n] + @assert length(samples) == n + + # scan samples and get counts + cnts = zeros(Int, m) + for i in 1:n + @inbounds si = samples[i] + if rmin <= si <= rmax + cnts[si - rmin + 1] += 1 + else + vmin <= si <= vmax || + error("Sample value out of valid range.") + end + end + + # check the counts + for i in 1:m + verbose && println("v = $(rmin + i - 1) ==> ($(clb[i]), $(cub[i])): $(cnts[i])") + clb[i] <= cnts[i] <= cub[i] || + error("The counts are out of the confidence interval.") + end + return samples +end + +println("testing count random sampler") +for λ in [0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0, 30.0] + test_samples(PoissonRandom.count_rand, Distributions.Poisson(λ), n_tsamples) +end + +println("testing ad random sampler") +for λ in [5.0, 10.0, 15.0, 20.0, 30.0] + test_samples(PoissonRandom.ad_rand, Distributions.Poisson(λ), n_tsamples) +end + +println("testing mixed random sampler") +for λ in [5.0, 10.0, 15.0, 20.0, 30.0] + test_samples(pois_rand, Distributions.Poisson(λ), n_tsamples) +end