Skip to content

Commit c05a2a5

Browse files
committed
ids: use directly testset trees instead of a stack
1 parent b6628c8 commit c05a2a5

File tree

3 files changed

+59
-55
lines changed

3 files changed

+59
-55
lines changed

src/ReTest.jl

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ end
9090
isfor(ts::TestsetExpr) = ts.loops !== nothing
9191
isfinal(ts::TestsetExpr) = isempty(ts.children)
9292

93+
function tsdepth(ts::Union{TestsetExpr,Testset.ReTestSet})
94+
d = 1
95+
while ts.parent !== nothing
96+
d += 1
97+
ts = ts.parent
98+
end
99+
d
100+
end
101+
93102
struct _Invalid
94103
global const invalid = _Invalid.instance
95104
end
@@ -215,7 +224,7 @@ end
215224
function resolve!(mod::Module, ts::TestsetExpr, pat::Pattern;
216225
# external calls
217226
verbose::Int, id::Int64, strict::Bool, static::Maybe{Bool},
218-
ids::Vector{Int64}, warned::Ref{Bool},
227+
warned::Ref{Bool},
219228
# only recursive calls
220229
force::Bool=false, shown::Bool=true, depth::Int=0)
221230

@@ -229,9 +238,8 @@ function resolve!(mod::Module, ts::TestsetExpr, pat::Pattern;
229238
warned[] = true
230239
end
231240
ts.id = id
232-
push!(ids, id)
233241
id += 1
234-
ts.run = force | (static !== false) & alwaysmatches(pat, length(ids))
242+
ts.run = force | (static !== false) & alwaysmatches(pat, tsdepth(ts))
235243

236244
parentstrs = ts.parent === nothing ? [""] : ts.parent.strings
237245
ts.descwidth = 0
@@ -256,7 +264,7 @@ function resolve!(mod::Module, ts::TestsetExpr, pat::Pattern;
256264
end
257265

258266
function decide(subj)
259-
m = matches(pat, subj, ids)
267+
m = matches(pat, subj, ts)
260268
# For the curious reader, setting `s = something(static, missing)`, there
261269
# are few "formulas" to compute the result without `if`, but using only
262270
# `coalesce, |, &, ==, !=, ===, !==, (a,b) -> a, (a,b) -> b, (a,b) -> !a,
@@ -367,15 +375,14 @@ function resolve!(mod::Module, ts::TestsetExpr, pat::Pattern;
367375
runc, id = resolve!(mod, tsc, pat, force = !strict && ts.run,
368376
shown=shown & ts.options.transient_verbose, static=static,
369377
depth=depth+1, verbose=verbose-1, id=id, strict=strict,
370-
ids=ids, warned=warned)
378+
warned=warned)
371379
run |= runc
372380
ts.descwidth = max(ts.descwidth, tsc.descwidth)
373381
if tsc.run
374382
ts.hasbrokenrec |= tsc.hasbrokenrec
375383
end
376384
end
377385

378-
pop!(ids)
379386
if !run || !shown
380387
ts.descwidth = 0
381388
end
@@ -1285,12 +1292,11 @@ function fetchtests((mod, pat), verbose, module_header, maxidw; static, strict,
12851292
hasbroken = false
12861293

12871294
id = 1
1288-
ids = Int64[]
12891295
warned = Ref(false)
12901296

12911297
for ts in tests
12921298
run, id = resolve!(mod, ts, pat, verbose=verbose, id=id, strict=strict,
1293-
static=static, ids=ids, warned=warned)
1299+
static=static, warned=warned)
12941300
run || continue
12951301
descwidth = max(descwidth, ts.descwidth)
12961302
hasbroken |= ts.hasbrokenrec
@@ -1323,12 +1329,11 @@ hasmany(tests) = length(tests) > 1 || isfor(tests[1])
13231329
function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0, parentsubj=""
13241330
; maxidw::Int, marks::Bool, # external calls
13251331
# only recursive calls:
1326-
evaldesc=true, repeated=nothing, ids::Vector{Int64}=Int64[], show::Bool=true)
1332+
evaldesc=true, repeated=nothing, show::Bool=true)
13271333
@assert ts.run
13281334
desc = ts.desc
13291335

13301336
if ts.loops === nothing
1331-
push!(ids, ts.id)
13321337
if evaldesc && !(desc isa String)
13331338
try
13341339
desc = Core.eval(mod, desc)
@@ -1339,8 +1344,7 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0, parent
13391344
subject = nothing
13401345
if parentsubj isa String && desc isa String
13411346
subject = parentsubj * '/' * desc
1342-
if isfinal(ts) && !matches(pat, subject, ids)
1343-
pop!(ids)
1347+
if isfinal(ts) && !matches(pat, subject, ts)
13441348
return false, false, false
13451349
end
13461350
end
@@ -1371,9 +1375,8 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0, parent
13711375
for tsc in ts.children
13721376
tsc.run || continue
13731377
dryrun(mod, tsc, pat, align + 2, subject,
1374-
maxidw=maxidw, marks=marks, ids=ids, show=true)
1378+
maxidw=maxidw, marks=marks, show=true)
13751379
end
1376-
pop!(ids)
13771380
false, false, false # meaningless unused triple
13781381
elseif marks
13791382
passes, fails, unrun = false, false, false
@@ -1389,13 +1392,12 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0, parent
13891392
for tsc in ts.children
13901393
tsc.run || continue
13911394
cp, cf, cu = dryrun(mod, tsc, pat, align + 2, subject,
1392-
maxidw=maxidw, marks=marks, ids=ids, show=false)
1395+
maxidw=maxidw, marks=marks, show=false)
13931396
passes |= cp
13941397
fails |= cf
13951398
unrun |= cu
13961399
passes && fails && unrun && break
13971400
end
1398-
pop!(ids)
13991401
if show
14001402
passes &&
14011403
printstyled("", color = :light_black, bold=true)
@@ -1412,7 +1414,6 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0, parent
14121414
if show
14131415
println()
14141416
end
1415-
pop!(ids)
14161417
false, false, false
14171418
end
14181419
else
@@ -1438,7 +1439,7 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0, parent
14381439
beginend.id = ts.id
14391440
beginend.results = ts.results
14401441
dryrun(mod, beginend, pat, align, parentsubj; evaldesc=false,
1441-
repeated=repeated, maxidw=maxidw, marks=marks, ids=ids, show=show)
1442+
repeated=repeated, maxidw=maxidw, marks=marks, show=show)
14421443
end
14431444

14441445
loopvalues = ts.loopvalues

src/patterns.jl

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,41 +79,42 @@ alwaysmatches(dep::Depth, d) = dep.d == d
7979

8080
## matches
8181

82-
matches(pat::And, x, ids) = all(p -> matches(p, x, ids), pat.xs)
82+
matches(pat::And, x, ts) = all(p -> matches(p, x, ts), pat.xs)
8383

84-
matches(pat::Or, x, ids) =
84+
matches(pat::Or, x, ts) =
8585
if pat.xs isa AbstractUnitRange{<:Integer} && minimum(pat.xs) >= 0
86-
ids[end] pat.xs # this is optimised, i.e. it's not O(n)
86+
ts.id pat.xs # this is optimised, i.e. it's not O(n)
8787
else
88-
any(p -> matches(p, x, ids), pat.xs)
88+
any(p -> matches(p, x, ts), pat.xs)
8989
end
9090

91-
matches(pat::Not, x, ids) = !matches(pat.x, x, ids)
92-
matches(::Interpolated, x::Union{Missing,AbstractString}, ids) = x !== missing
91+
matches(pat::Not, x, ts) = !matches(pat.x, x, ts)
92+
matches(::Interpolated, x::Union{Missing,AbstractString}, ts) = x !== missing
9393
matches(rx::Regex, x, _) = occursin(rx, x)
94-
matches(rx::Regex, ::Missing, ids) = alwaysmatches(rx, length(ids)) | missing
95-
matches(pat::Integer, _, ids) =
94+
matches(rx::Regex, ::Missing, ts) = alwaysmatches(rx, tsdepth(ts)) | missing
95+
matches(pat::Integer, _, ts) =
9696
@inbounds pat >= 0 ?
97-
pat == ids[end] :
98-
pat != -ids[end]
97+
pat == ts.id :
98+
pat != -ts.id
9999

100-
function matches(pat::Reachable, desc, ids::Vector{Int64})
100+
function matches(pat::Reachable, desc, ts)
101101
if desc !== missing
102102
desc = SubString(desc)
103103
end
104104
m = false
105-
for d = length(ids):-1:1
106-
@inbounds id = ids[d]
107-
m |= matches(pat.x, desc, @view ids[1:d])
105+
while true
106+
m |= matches(pat.x, desc, ts)
108107
m === true && return true
108+
ts.parent === nothing && break
109+
ts = ts.parent
109110
if desc !== missing
110111
desc = SubString(desc, 1, findlast('/', desc)-1)
111112
end
112113
end
113114
m
114115
end
115116

116-
matches(d::Depth, _, ids) = d.d == length(ids)
117+
matches(d::Depth, _, ts) = d.d == tsdepth(ts)
117118

118119

119120
## make_pattern

src/testset.jl

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ Format(stats, desc_align) = Format(stats, desc_align, 0, 0, 0,0 ,0)
6060

6161
mutable struct ReTestSet <: AbstractTestSet
6262
mod::Module # enclosing module
63-
description::AbstractString
63+
parent::Union{Nothing,ReTestSet}
64+
description::String
65+
subject::String
6466
id::Int64
65-
ids::Vector{Int64} # shared instance in an id stack
6667
overall::Bool # TODO: could be conveyed by having self.mod == ""
6768
results::Vector
6869
n_passed::Int
@@ -72,8 +73,13 @@ mutable struct ReTestSet <: AbstractTestSet
7273
exception::Union{TestSetException,Nothing}
7374
end
7475

75-
ReTestSet(mod, desc, id::Integer=0; ids=Int64[], overall=false, verbose=true) =
76-
ReTestSet(mod, desc, id, ids, overall, [], 0, false, verbose, NamedTuple(), nothing)
76+
function ReTestSet(mod, desc::String, id::Integer=0;
77+
overall=false, verbose=true, parent=nothing)
78+
parentsubj = parent === nothing ? "" : parent.subject
79+
subject = string(parentsubj, '/', desc)
80+
ReTestSet(mod, parent, desc, subject, id, overall, [], 0, false,
81+
verbose, NamedTuple(), nothing)
82+
end
7783

7884
# For a non-passed result, simply store the result
7985
record(ts::ReTestSet, t::Union{Broken,Fail,Error}) = (push!(ts.results, t); t)
@@ -437,11 +443,12 @@ default_rng() = isdefined(Random, :default_rng) ?
437443
Random.default_rng() :
438444
Random.GLOBAL_RNG
439445

440-
function get_testset_ids_string(remove_last=false)
446+
function make_retestset(mod, desc, id, verbose, remove_last=false)
441447
_testsets = get(task_local_storage(), :__BASETESTNEXT__, Test.AbstractTestSet[])
448+
@assert !(remove_last && isempty(_testsets))
442449
testsets = @view _testsets[1:end-remove_last]
443-
(isempty(testsets) ? Int64[] : resize!(testsets[end].ids, length(testsets)),
444-
join('/' * ts.description for ts in testsets))
450+
ReTestSet(mod, desc, id; verbose=verbose,
451+
parent = isempty(testsets) ? nothing : testsets[end])
445452
end
446453

447454
# HACK: we re-use the same macro name `@testset` for actual execution (like in `Test`)
@@ -483,13 +490,10 @@ function testset_beginend(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, d
483490
# action (such as reporting the results)
484491
desc = esc(desc)
485492
ex = quote
486-
local ids, current_str = get_testset_ids_string()
487-
current_str = string(current_str, '/', $desc)
488-
push!(ids, $id)
489-
if !$isfinal || matches($pat, current_str, ids)
493+
local ts = make_retestset($mod, $desc, $id, $(options.transient_verbose))
494+
495+
if !$isfinal || matches($pat, ts.subject, ts)
490496
local ret
491-
local ts = ReTestSet($mod, $desc, $id; verbose=$(options.transient_verbose),
492-
ids=ids)
493497
if nworkers() == 1 && get_testset_depth() == 0 && $(chan.preview) !== nothing
494498
put!($(chan.preview), $desc)
495499
end
@@ -517,7 +521,7 @@ function testset_beginend(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, d
517521
Base.catch_stack(), $(QuoteNode(source))))
518522
finally
519523
copy!(RNG, oldrng)
520-
$results[current_str] = !anyfailed(ts)
524+
$results[ts.subject] = !anyfailed(ts)
521525
pop_testset()
522526
ret = finish(ts, $chan)
523527
end
@@ -542,11 +546,10 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64,
542546

543547
desc = esc(desc)
544548
blk = quote
545-
local ids, current_str = get_testset_ids_string(!first_iteration)
546-
current_str = string(current_str, '/', $desc)
547-
push!(ids, $id)
549+
local ts0 = make_retestset($mod, $desc, $id, $(options.transient_verbose),
550+
!first_iteration)
548551

549-
if !$isfinal || matches($pat, current_str, ids)
552+
if !$isfinal || matches($pat, ts0.subject, ts0)
550553
# Trick to handle `break` and `continue` in the test code before
551554
# they can be handled properly by `finally` lowering.
552555
if !first_iteration
@@ -555,8 +558,7 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64,
555558
# it's 1000 times faster to copy from tmprng rather than calling Random.seed!
556559
copy!(RNG, tmprng)
557560
end
558-
ts = ReTestSet($mod, $desc, $id; verbose=$(options.transient_verbose),
559-
ids=ids)
561+
ts = ts0
560562
if nworkers() == 1 && get_testset_depth() == 0 && $(chan.preview) !== nothing
561563
put!($(chan.preview), ts.description)
562564
end
@@ -566,13 +568,13 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64,
566568
let
567569
ts.timed = @stats $stats $(esc(tests))
568570
end
569-
$results[current_str] = !anyfailed(ts)
571+
$results[ts.subject] = !anyfailed(ts)
570572
catch err
571573
err isa InterruptException && rethrow()
572574
# Something in the test block threw an error. Count that as an
573575
# error in this test set
574576
record(ts, Error(:nontest_error, Expr(:tuple), err, Base.catch_stack(), $(QuoteNode(source))))
575-
$results[current_str] = false
577+
$results[ts.subject] = false
576578
end
577579
end
578580
end

0 commit comments

Comments
 (0)