Skip to content

Commit 288004c

Browse files
committed
runtests: filter statically some testsets
For toplevel testsets which are "final", and which have a description not involving loop variables, we can filter them statically, i.e. skip their evaluation when the regex doesn't match; this can be quite faster.
1 parent 11fbc45 commit 288004c

File tree

1 file changed

+17
-8
lines changed

1 file changed

+17
-8
lines changed

src/InlineTest.jl

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,18 @@ __init__() = INLINE_TEST[] = gensym()
1616
function tests(m)
1717
inline_test::Symbol = m (InlineTest, InlineTest.InlineTestTest) ? :__INLINE_TEST__ : INLINE_TEST[]
1818
if !isdefined(m, inline_test)
19-
@eval m $inline_test = Expr[]
19+
@eval m $inline_test = Tuple{Expr,Union{String,Missing},Bool}[]
2020
end
2121
getfield(m, inline_test)
2222
end
2323

2424
replacetestset(x) = x, false
2525

2626
# replace unqualified `@testset` by @testsetr
27-
# return also (as 2nd element) whether the expression contains a (possibly nested) @testset
27+
# return also (as 3nd element) whether the expression contains a (possibly nested) @testset
2828
# (if a testset is "final", i.e. without nested testsets, then the Regex matching logic
2929
# is different: we then don't use "partial matching")
30+
# the 2nd returned element is whether the testset is final (used only in `addtest`)
3031
function replacetestset(x::Expr)
3132
if x.head === :macrocall && x.args[1] === Symbol("@testset")
3233
body = map(replacetestset, x.args[2:end])
@@ -35,17 +36,21 @@ function replacetestset(x::Expr)
3536
:($(Testset.FINAL[]) = $final),
3637
Expr(:macrocall, Expr(:., :InlineTest, QuoteNode(Symbol("@testsetr"))),
3738
map(first, body)...)),
38-
true)
39+
final, true)
3940
else
4041
body = map(replacetestset, x.args)
4142
(Expr(x.head, map(first, body)...),
42-
any(last, body))
43+
missing, any(last, body)) # missing: a non-testset doesn't have a "final" attribute...
4344
end
4445
end
4546

4647
function addtest(args::Tuple, m::Module)
47-
ts, _ = replacetestset(:(@testset($(args...))))
48-
push!(tests(m), ts)
48+
desc = args[1] isa String ? args[1] : missing
49+
# args[1] might not be a string if none was passed, or for a testset-for with
50+
# interpolated loop variable (in which case it's difficult to statically know the
51+
# final description)
52+
ts, final, _ = replacetestset(:(@testset($(args...))))
53+
push!(tests(m), (ts, desc, final))
4954
nothing
5055
end
5156

@@ -79,11 +84,15 @@ function runtests(m::Module, regex::Regex = r""; wrap::Bool=false)
7984
if wrap
8085
Core.eval(m, :(InlineTest.Test.@testset $("Tests for module $m") begin
8186
let $(Testset.REGEX[]) = ($partial, $regex)
82-
$(tests(m)...)
87+
$(map(first, tests(m))...)
8388
end
8489
end))
8590
else
86-
for ts in tests(m)
91+
for (ts, desc, final) in tests(m)
92+
# bypass evaluation if we know statically that testset won't be run
93+
if desc isa String && final && !occursin(regex, desc) # TODO: handle non-final case
94+
continue
95+
end
8796
# it's faster to evel in a loop than to eval a block containing tests(m)
8897
Core.eval(m, :(let $(Testset.REGEX[]) = ($partial, $regex)
8998
$ts

0 commit comments

Comments
 (0)