Skip to content

Commit 4822283

Browse files
ChrisRackauckas-ClaudeChrisRackauckasclaude
authored
Canonicalize test units to @safetestset (#159)
Wrap the independent test units in `@safetestset` so each runs in its own module, matching the canonical OrdinaryDiffEq structure (test isolation + world-age safety). Behavior-preserving: same tests run with the same assertions; only the unit wrapping changed. - runtests.jl Core: the three plain `@testset` units ("integral", "vector expression error handling", "definite integral with infinite bounds") become `@safetestset ... begin include("...") end`. The shared integral-test infrastructure (variables, basic_integrals, sym_integrals, test_integrals) moved into test/integral_setup.jl, which the "integral" unit includes; the two self-contained units moved into their own files. The include() form is used so each unit's `using` lines run before any package macros are reached. - qa/qa.jl: the "Aqua" and "JET" plain `@testset` units become `@safetestset`, each carrying its own `using` block. - Added SafeTestsets to the test deps (root [extras]/[targets].test + [compat]) and to test/qa/Project.toml (the QA group activates its own env). GROUP=Core verified locally on Julia 1.11: all three units pass (integral 1/1, vector expression 7/7, definite integral 3/3). Co-authored-by: Chris Rackauckas <accounts@chrisrackauckas.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4f6a6d1 commit 4822283

8 files changed

Lines changed: 387 additions & 372 deletions

File tree

Project.toml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,11 @@ SymbolicLimits = "0.2.4, 1.1"
2828
SymbolicUtils = "4.20.1"
2929
Symbolics = "7.12.0"
3030
TermInterface = "2"
31+
Pkg = "1"
32+
SafeTestsets = "0.1, 1"
3133
SciMLTesting = "1"
3234
Test = "1"
3335
julia = "1.10"
3436

3537
[extras]
36-
SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283"
37-
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
38-
39-
[targets]
40-
test = ["SciMLTesting", "Test"]
38+
test = ["SciMLTesting", "Test", "Pkg", "SafeTestsets"]

test/definite_integral_tests.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using SymbolicNumericIntegration
2+
using Symbolics
3+
using Test
4+
5+
@variables x
6+
7+
# Test exp(-x) from 0 to Inf - should give 1
8+
result1 = integrate(exp(-x), (x, 0, Inf); symbolic = true, detailed = false)
9+
@test result1 1.0
10+
11+
# Note: x*exp(-x) and x^2*exp(-x) integrals are flaky due to randomness in the
12+
# symbolic integration engine. The definite integral code itself works correctly
13+
# when the antiderivative is found.
14+
15+
# Test exp(x) from -Inf to 0 - should give 1
16+
result4 = integrate(exp(x), (x, -Inf, 0); symbolic = true, detailed = false)
17+
@test result4 1.0
18+
19+
# Finite bounds should still work
20+
result6 = integrate(x, (x, 0, 1); symbolic = false, detailed = false)
21+
@test result6 == 1 // 2

test/integral_setup.jl

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
using SymbolicNumericIntegration
2+
using Symbolics
3+
4+
@variables x a b c β
5+
6+
"""
7+
a list of basic standard integral tests
8+
based on http://integral-table.com/ with modifications
9+
"""
10+
basic_integrals = [
11+
# Basic Forms
12+
1,
13+
x^2,
14+
4x^3,
15+
# Integrals of Rational Functions
16+
1 / x,
17+
1 / (2x + 5),
18+
1 / (x + 1)^2,
19+
(x + 3)^3,
20+
x * (x - 2)^4,
21+
1 / (1 + x^2),
22+
1 / (9 + x^2),
23+
x / (4 + x^2),
24+
x^2 / (16 + x^2),
25+
x^3 / (1 + x^2),
26+
1 / (x^2 - 5x + 6),
27+
1 / (x^2 + x + 1),
28+
x / (x + 4)^2,
29+
x / (x^2 + x + 1),
30+
# Integrals with Roots
31+
sqrt(x - 2),
32+
1 / sqrt(x - 1),
33+
1 / sqrt(x + 1),
34+
1 / sqrt(4 - x),
35+
x * sqrt(x - 3),
36+
sqrt(2x + 5),
37+
(3x - 1)^1.5,
38+
x / sqrt(x - 1),
39+
x / sqrt(x + 1),
40+
sqrt(x / (4 - x)),
41+
sqrt(x / (4 + x)),
42+
x * sqrt(2x + 3),
43+
sqrt(x * (x + 2)),
44+
sqrt(x^3 * (x + 3)),
45+
sqrt(x^2 + 4),
46+
sqrt(x^2 - 4),
47+
sqrt(4 - x^2),
48+
x * sqrt(x^2 + 9),
49+
x * sqrt(x^2 - 9),
50+
1 / sqrt(x^2 + 4),
51+
1 / sqrt(x^2 - 4),
52+
1 / sqrt(4 - x^2),
53+
x / sqrt(x^2 + 4),
54+
x / sqrt(x^2 - 4),
55+
x / sqrt(4 - x^2),
56+
x^2 / sqrt(x^2 + 4),
57+
x^2 / sqrt(x^2 - 4),
58+
sqrt(x^2 - 5x + 6),
59+
x * sqrt(x^2 - 5x + 6),
60+
1 / sqrt(x^2 - 5x + 6),
61+
1 / (4 + x^2)^1.5,
62+
# Integrals with Logarithms
63+
log(x),
64+
x * log(x),
65+
x^2 * log(x),
66+
log(2x) / x,
67+
log(x) / x^2,
68+
log(2x + 1),
69+
log(x^2 + 4),
70+
log(x^2 - 4),
71+
log(x^2 - 5x + 6),
72+
x * log(x + 2),
73+
x * log(9 - 4x^2),
74+
log(x)^2,
75+
log(x)^3,
76+
x * log(x)^2,
77+
x^2 * log(x)^2,
78+
# Integrals with Exponentials
79+
exp(x),
80+
sqrt(x) * exp(x),
81+
x * exp(x),
82+
x * exp(3x),
83+
x^2 * exp(x),
84+
x^2 * exp(5x),
85+
x^3 * exp(x),
86+
x^3 * exp(2x),
87+
exp(x^2),
88+
x * exp(x^2),
89+
# Integrals with Trigonometric Functions
90+
sin(4x),
91+
sin(x)^2,
92+
sin(x)^3,
93+
cos(3x),
94+
cos(x)^2,
95+
cos(2x)^3,
96+
sin(x) * cos(x),
97+
sin(3x) * cos(5x),
98+
sin(x)^2 * cos(x),
99+
sin(3x)^2 * cos(x),
100+
sin(x) * cos(x)^2,
101+
sin(x) * cos(5x)^2,
102+
sin(x)^2 * cos(x),
103+
sin(x)^2 * cos(x)^2,
104+
sin(4x)^2 * cos(4x)^2,
105+
tan(x),
106+
tan(7x),
107+
tan(x)^2,
108+
tan(x)^3,
109+
sec(x),
110+
sec(x) * tan(x),
111+
sec(x)^2 * tan(x),
112+
csc(x),
113+
sec(x) * csc(x),
114+
# Products of Trigonometric Functions and Monomials
115+
x * cos(x),
116+
x * cos(3x),
117+
x^2 * cos(x),
118+
x^2 * cos(5x),
119+
x * sin(x),
120+
x * sin(3x),
121+
x^2 * sin(x),
122+
x^2 * sin(5x),
123+
x * cos(x)^2,
124+
x * sin(x)^2,
125+
x * tan(x)^2,
126+
x * sec(x)^2,
127+
x^3 * sin(x),
128+
x^4 * cos(2x),
129+
sin(x)^2 * cos(x)^3,
130+
# Products of Trigonometric Functions and Exponentials
131+
exp(x) * sin(x),
132+
exp(3x) * sin(2x),
133+
exp(x) * cos(x),
134+
exp(2x) * cos(7x),
135+
x * exp(x) * sin(x),
136+
x * exp(x) * cos(x),
137+
# Integrals of Hyperbolic Functions
138+
cosh(x),
139+
exp(x) * cosh(x),
140+
sinh(3x),
141+
exp(2x) * sinh(3x),
142+
tanh(x),
143+
exp(x) * tanh(x),
144+
cos(x) * cosh(x),
145+
cos(x) * sinh(x),
146+
sin(x) * cosh(x),
147+
sin(x) * sinh(x),
148+
sinh(x) * cosh(x),
149+
sinh(3x) * cosh(5x),
150+
# Misc
151+
exp(x) / (1 + exp(x)),
152+
cos(exp(x)) * sin(exp(x)) * exp(x),
153+
cos(exp(x))^2 * sin(exp(x)) * exp(x),
154+
1 / (x * log(x)),
155+
1 / (exp(x) - 1),
156+
1 / (exp(x) + 5),
157+
sqrt(x) * log(x),
158+
log(log(x)) / x,
159+
x^3 * exp(x^2),
160+
sin(log(x)),
161+
x * cos(x) * exp(x),
162+
log(x - 1)^2,
163+
1 / (exp(2x) - 1),
164+
exp(x) / (exp(2x) - 1),
165+
x / (exp(2x) - 1),
166+
# derivative-divide examples (Lamangna 7.10.2)
167+
exp(x) * exp(exp(x)),
168+
exp(sqrt(x)) / sqrt(x),
169+
log(log(x)) / (x * log(x)),
170+
log(cos(x)) * tan(x),
171+
# rothstein-Trager examples (Lamangna 7.10.9)
172+
1 / (x^3 - x),
173+
1 / (x^3 + 1),
174+
1 / (x^2 - 8),
175+
(x + 1) / (x^2 + 1),
176+
x / (x^4 - 4),
177+
x^3 / (x^4 + 1),
178+
1 / (x^4 + 1),
179+
# exponential/trigonometric/logarithmic integral functions
180+
exp(2x) / x,
181+
exp(x + 1) / (x + 1),
182+
x * exp(2x^2 + 1) / (2x^2 + 1),
183+
sin(3x) / x,
184+
sin(x + 1) / (x + 1),
185+
cos(5x) / x,
186+
x * cos(x^2 - 1) / (x^2 - 1),
187+
1 / log(3x - 1),
188+
1 / (x * log(log(x))),
189+
x / log(x^2),
190+
# from Geddes & Stefanus
191+
exp(1 / (1 + log(x))) * ((2 * log(x) + 1) / x),
192+
1 / (1 + exp(x)),
193+
sin(2x) * exp(x),
194+
-10 * exp(x^-10) / x^11,
195+
# bypass = true
196+
β, # turn of bypass = true
197+
(log(x - 1) + (x - 1)^-1) * log(x),
198+
exp(x) / x - exp(x) / x^2,
199+
cos(x) / x - sin(x) / x^2,
200+
1 / log(x) - 1 / log(x)^2,
201+
]
202+
203+
sym_integrals = [
204+
# Basic Forms
205+
a * x^2,
206+
a * x + b * x^2 - c * x^3,
207+
(3(x^2) + 3(a^2) - 4) / (4 * a * b),
208+
a / x,
209+
1 / (a * x + 5),
210+
1 / (x + a),
211+
x / (x + a),
212+
1 / (x + a)^2,
213+
x / (x + a)^2,
214+
(x + a)^3,
215+
x * (x - a)^4,
216+
1 / (a + x^2),
217+
sqrt(x - a),
218+
1 / sqrt(a * x - 1),
219+
x * sqrt(a * x + b),
220+
log(a * x),
221+
x * log(a * x),
222+
x^2 * log(a * x),
223+
log(a * x) / x,
224+
log(x^2 - a * x + b),
225+
log(a * x)^2,
226+
log(a + x),
227+
log(a + x^2) * x,
228+
# x^2 * log(a * x + b)^2,
229+
exp(a * x),
230+
x * exp(a * x),
231+
x^2 * exp(a * x),
232+
x * exp(a * x^2),
233+
sin(a * x),
234+
sin(a * x)^2,
235+
cos(a * x + b)^2,
236+
sin(a * x) * cos(a * x),
237+
sin(a * x) * cos(b * x),
238+
tan(a * x),
239+
sec(a * x),
240+
x * cos(a * x),
241+
x^2 * cos(a * x),
242+
exp(a * x) * sin(b * x),
243+
x * exp(a * x) * sin(a * x),
244+
x * exp(a * x) * cos(b * x),
245+
cosh(a * x),
246+
exp(a * x) * cosh(b * x),
247+
cos(a * x) * cosh(b * x),
248+
sin(a * x) * cos(b * x) * exp(c * x),
249+
sin(a * x) * sinh(b * x) * exp(c * x),
250+
sec(a * x)^2 * tan(a * x),
251+
exp(a * x) / (1 + exp(a * x)),
252+
exp(a * x) / exp(b * x),
253+
cos(exp(a * x)) * sin(exp(a * x)) * exp(a * x),
254+
1 / (x * log(a * x)),
255+
log(log(a * x)) / x,
256+
log(a + log(x)) / x,
257+
sin(log(a * x)),
258+
# x / (exp(a * x) - b),
259+
exp(a * x) / (b * exp(a * x) + c),
260+
exp(a * x) * exp(exp(a * x)),
261+
log(cos(a * x)) * tan(a * x),
262+
1 / (x^3 + a),
263+
exp(a * x + b) / x,
264+
sin(x + a) / (x + a),
265+
cos(a * x) / x,
266+
x / log(a * x^2),
267+
# bypass = true
268+
β, # turn of bypass = true
269+
exp(a * x) / x - exp(a * x) / x^2,
270+
cos(a * x) / x - sin(a * x) / x^2,
271+
]
272+
273+
function test_integrals(basic = true, subs = nothing; kw...)
274+
args = isempty(kw) ? Dict() : Dict(kw)
275+
args[:detailed] = false
276+
misses = []
277+
k = 1
278+
279+
integrals = basic ? basic_integrals : sym_integrals
280+
args[:symbolic] = !basic
281+
282+
for (i, eq) in enumerate(integrals)
283+
if isequal(eq, β)
284+
printstyled("**** bypass on ****\n"; color = :red)
285+
bypass = true
286+
args[:bypass] = true
287+
else
288+
if subs != nothing
289+
eq = substitute(eq, subs)
290+
end
291+
292+
printstyled(k, ": "; color = :blue)
293+
k += 1
294+
printstyled(eq, " =>\n"; color = :green)
295+
sol = SymbolicNumericIntegration.integrate(eq, x; args...)
296+
if sol == nothing
297+
printstyled("\t<no solution>\n"; color = :red)
298+
push!(misses, eq)
299+
else
300+
printstyled('\t', sol, '\n'; color = :cyan)
301+
end
302+
end
303+
end
304+
305+
n = length(misses)
306+
if n > 0
307+
println("**** missess (n=$n) *****")
308+
end
309+
for eq in misses
310+
printstyled(eq, '\n'; color = :red)
311+
end
312+
return n
313+
end

test/integral_tests.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using SymbolicNumericIntegration
2+
using Symbolics
3+
using Test
4+
5+
include("integral_setup.jl")
6+
7+
n = test_integrals(;
8+
symbolic = false, verbose = false, homotopy = true, num_steps = 2,
9+
num_trials = 10
10+
)
11+
@test n > 0

test/qa/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[deps]
22
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
33
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
4+
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
45
SymbolicNumericIntegration = "78aadeae-fbc0-11eb-17b6-c7ec0477ba9e"
56
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
67

test/qa/qa.jl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
using SymbolicNumericIntegration
2-
using Aqua
3-
using JET
4-
using Test
1+
using SafeTestsets
2+
3+
@safetestset "Aqua" begin
4+
using SymbolicNumericIntegration
5+
using Aqua
6+
using Test
57

6-
@testset "Aqua" begin
78
Aqua.test_all(SymbolicNumericIntegration; piracies = false)
89
@test_broken false # Aqua piracy: Base.signbit(::Complex)/signbit(::SymbolicUtils.Sym) in src/integral.jl + DataDrivenSparse.active_set! in src/sparse.jl — see https://github.com/SciML/SymbolicNumericIntegration.jl/issues/156
910
end
1011

11-
@testset "JET" begin
12+
@safetestset "JET" begin
13+
using SymbolicNumericIntegration
14+
using JET
15+
using Test
16+
1217
JET.test_package(SymbolicNumericIntegration; target_defined_modules = true)
1318
end

0 commit comments

Comments
 (0)