Skip to content

Commit d9b6e5c

Browse files
authored
dryrun: keep track of loop variables from parent testsets (#28)
Partially fixes #27. The full fix might be to make `resolve!` also keep track of these loop variables; but this would involve storing lists of collected iterators for each nested testset: once we evaluate an iterator for filtering in `resolve!`, we have to store it in case it has some random behavior (e.g. `rand(1:9, i)` where `i` is a loop variable from a parent testset), in which case re-evaling it at execution time would lead to inconsistency. The testset tree would also have to be traversed depth-first by also unfolding testset-for. This could be a nice improvement if the perfs don't suffer (instead of storing parent strings subjects and looping through them, we would store iterators and recurse into them).
1 parent d7a3ff0 commit d9b6e5c

File tree

2 files changed

+73
-14
lines changed

2 files changed

+73
-14
lines changed

src/ReTest.jl

+46-14
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,13 @@ function resolve!(mod::Module, ts::TestsetExpr, pat::Pattern;
243243
strings = empty!(ts.strings)
244244
desc = ts.desc
245245
ts.loopvalues = nothing # unnecessary ?
246-
ts.loopiters = nothing
246+
loopiters = ts.loopiters =
247+
if ts.loops === nothing
248+
nothing
249+
else
250+
Expr(:tuple, (arg.args[1] for arg in ts.loops)...)
251+
end
252+
247253
if 0 != ts.id != id && !warned[] && has(pat, Integer)
248254
# this can happen when nested testsets are added and Revise is active
249255
@warn "testset IDs have changed since last run"
@@ -337,7 +343,6 @@ function resolve!(mod::Module, ts::TestsetExpr, pat::Pattern;
337343
else # we have a testset-for with description which needs interpolation, or
338344
# the iterator must be computed to get an iterator counter
339345
xs = ()
340-
loopiters = Expr(:tuple, (arg.args[1] for arg in loops)...)
341346

342347
try
343348
# we need to evaluate roughly the following:
@@ -359,7 +364,6 @@ function resolve!(mod::Module, ts::TestsetExpr, pat::Pattern;
359364
xs = Core.eval(mod, xsgen)
360365
@assert xs isa Vector
361366
ts.loopvalues = xs
362-
ts.loopiters = loopiters
363367
catch
364368
@assert xs == ()
365369
ts.descwidth = shown ? descwidth(missing) : 0
@@ -409,15 +413,20 @@ function resolve!(mod::Module, ts::TestsetExpr, pat::Pattern;
409413
run, id
410414
end
411415

412-
eval_desc(mod, ts, x) =
416+
eval_desc(mod, ts, x; stack=false) = # stack => x == iterstack in dryrun
413417
if ts.desc isa String
414418
ts.desc
415419
else
416420
try
417-
Core.eval(mod, quote
418-
let $(ts.loopiters) = $x
419-
$(ts.desc)
420-
end
421+
Core.eval(mod,
422+
if stack
423+
Expr(:let, x, ts.desc)
424+
else
425+
quote
426+
let $(ts.loopiters) = $x
427+
$(ts.desc)
428+
end
429+
end
421430
end)::String
422431
catch
423432
missing
@@ -442,7 +451,7 @@ function make_ts(ts::TestsetExpr, pat::Pattern, stats, chan)
442451
end
443452
else
444453
c = count(x -> x === nothing, (ts.loopvalues, ts.loopiters))
445-
@assert c == 0 || c == 2
454+
@assert c == 0 || c == 1
446455
if c == 0
447456
loops = [Expr(:(=), ts.loopiters, ts.loopvalues)]
448457
else
@@ -1448,7 +1457,7 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
14481457
# external calls:
14491458
; maxidw::Int, marks::Bool, tag::Vector, clear::Bool,
14501459
# only recursive calls:
1451-
evaldesc=true, repeated=nothing, show::Bool=true)
1460+
evaldesc=true, repeated=nothing, show::Bool=true, iterstack=Expr(:block))
14521461
@assert ts.run
14531462
desc = ts.desc
14541463

@@ -1518,7 +1527,8 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
15181527
for tsc in ts.children
15191528
tsc.run || continue
15201529
dryrun(mod, tsc, pat, align + 2, subject,
1521-
maxidw=maxidw, marks=marks, tag=tag, clear=clear, show=true)
1530+
maxidw=maxidw, marks=marks, tag=tag, clear=clear, show=true,
1531+
iterstack=iterstack)
15221532
end
15231533
false, false, false # meaningless unused triple
15241534
elseif marks
@@ -1536,7 +1546,7 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
15361546
tsc.run || continue
15371547
cp, cf, cu = dryrun(mod, tsc, pat, align + 2, subject,
15381548
maxidw=maxidw, marks=marks, tag=tag, clear=clear,
1539-
show=false)
1549+
show=false, iterstack=iterstack)
15401550
passes |= cp
15411551
fails |= cf
15421552
unrun |= cu
@@ -1585,10 +1595,30 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
15851595
ts.iter = iter # necessary when reachable is used
15861596
dryrun(mod, beginend, pat, align, parentsubj; evaldesc=false,
15871597
repeated=repeated, maxidw=maxidw, marks=marks, tag=tag,
1588-
clear=clear, show=show)
1598+
clear=clear, show=show, iterstack=iterstack)
15891599
end
15901600

15911601
loopvalues = ts.loopvalues
1602+
if loopvalues === nothing
1603+
# we check whether we can now evaluate loopvalues via iterstack
1604+
try
1605+
# cf. resolve!
1606+
xssym = gensym()
1607+
xsgen = quote
1608+
let $xssym = []
1609+
$(Expr(:for, Expr(:block, ts.loops...),
1610+
Expr(:call, Expr(:., :Base, QuoteNode(:push!)),
1611+
xssym, ts.loopiters)))
1612+
$xssym
1613+
end
1614+
end
1615+
loopvalues = Core.eval(mod, Expr(:let, iterstack, xsgen))
1616+
@assert loopvalues isa Vector
1617+
catch
1618+
@assert loopvalues == nothing
1619+
end
1620+
end
1621+
15921622
if loopvalues === nothing
15931623
# ts.desc is probably a String (cf. resolve!); if so, don't print repeated
15941624
# identitical lines (caveat: if subjects of children would change randomly)
@@ -1615,7 +1645,8 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
16151645
else
16161646
passes, fails, unrun = false, false, false
16171647
for (i, x) in enumerate(loopvalues)
1618-
descx = eval_desc(mod, ts, x)
1648+
push!(iterstack.args, Expr(:(=), ts.loopiters, x))
1649+
descx = eval_desc(mod, ts, iterstack, stack=true)
16191650
if descx === missing
16201651
# we would usually have `i == 1`, but not in some rare cases;
16211652
# once we find an uninterpolated description, we still assume
@@ -1628,6 +1659,7 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
16281659
else
16291660
lp, lf, lu = dryrun_beginend(descx, iter=i)
16301661
end
1662+
pop!(iterstack.args)
16311663
passes |= lp
16321664
fails |= lf
16331665
unrun |= lu

test/runtests.jl

+27
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,33 @@ end # MultiLoops
635635
check(MultiLoops, "1 1", [(1, 1)])
636636
end
637637

638+
module LoopsVariablesDryrun
639+
# check that even with for-iterators which depend on previous loop variables,
640+
# dryrun mode is able to compute them and corresponding descriptions,
641+
# and filter accordingly
642+
643+
using ReTest
644+
645+
@testset "a$i" for i=1:2
646+
@testset "b$j" for j=1:i
647+
@test true
648+
end
649+
end
650+
end
651+
652+
@chapter Loops begin
653+
# no match, so this it at least filtered for final testsets
654+
check(LoopsVariablesDryrun, "a1/b3", dry=true, verbose=9, [], output="""
655+
1| a1
656+
1| a2
657+
""")
658+
check(LoopsVariablesDryrun, "a2/b1", dry=true, verbose=9, [], output="""
659+
1| a1
660+
1| a2
661+
2| b1
662+
""")
663+
end
664+
638665
# * Anonym ...................................................................
639666

640667
module Anonym

0 commit comments

Comments
 (0)