Skip to content

Commit 29a8716

Browse files
committed
retest: add tag keyword to add labels to testsets
1 parent 1608595 commit 29a8716

File tree

4 files changed

+131
-21
lines changed

4 files changed

+131
-21
lines changed

src/ReTest.jl

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import InlineTest: retest, @testset_macro
3737

3838
abstract type Pattern end
3939
function matches end
40-
function setresult end
40+
function setresult! end
4141

4242

4343
# * includes
@@ -116,7 +116,7 @@ function pastresult(marks::Dict, subject)
116116
sym === _pass ? true : sym === _fail ? false : nothing
117117
end
118118

119-
function setresult(marks::Dict, subject, success::Bool)
119+
function setresult!(marks::Dict, subject, success::Bool)
120120
ms = get(marks, subject, Symbol())
121121
res = success ? _pass : _fail
122122
if ms isa Symbol
@@ -136,6 +136,43 @@ function setresult(marks::Dict, subject, success::Bool)
136136
end
137137
end
138138

139+
function markiter(marks, subject, skipres::Bool)
140+
ms = get(marks, subject, Symbol())
141+
if ms isa Symbol
142+
if ms === Symbol() || skipres && ms (_pass, _fail)
143+
()
144+
else
145+
(ms,)
146+
end
147+
else
148+
if skipres
149+
Iterators.filter(m -> m (_pass, _fail), ms)
150+
else
151+
ms
152+
end
153+
end
154+
end
155+
156+
function addmark!(marks, subject, m::Symbol)
157+
ms = get(marks, subject, Symbol())
158+
if ms isa Symbol
159+
if ms === m
160+
false
161+
elseif ms === Symbol()
162+
marks[subject] = m
163+
true
164+
else
165+
marks[subject] = [ms, m]
166+
true
167+
end
168+
elseif findfirst(==(m), ms) === nothing
169+
push!(ms, m)
170+
true
171+
else
172+
false
173+
end
174+
end
175+
139176
function delmark!(marks, subject, m::Symbol)
140177
ms = get(marks, subject, Symbol())
141178
if ms isa Symbol
@@ -553,6 +590,7 @@ const retest_defaults = (
553590
load = false,
554591
seed = false,
555592
marks = true,
593+
tag = Symbol[],
556594
spin = true,
557595
clear = false,
558596
)
@@ -572,7 +610,7 @@ def(kw::Symbol) =
572610
[id::Bool], shuffle::Bool=false, recursive::Bool=true,
573611
static::Union{Bool,Nothing}=nothing, dup::Bool=false,
574612
load::Bool=false, seed::Union{Integer,Bool}=false,
575-
marks::Bool=true, spin::Bool=true)
613+
marks::Bool=true, tag=[], spin::Bool=true)
576614
577615
Run tests declared with [`@testset`](@ref) blocks, within modules `mod` if specified,
578616
or within all currently loaded modules otherwise.
@@ -615,7 +653,11 @@ Filtering `pattern`s can be specified to run only a subset of the tests.
615653
the tests. As a special case, if `seed === false` (the default), no seeding
616654
is performed, and if `seed === true`, a seed is chosen randomly.
617655
* When `marks` and `dry` are `true`, "check marks" are printed next to testsets
618-
which passed or failed in previous runs.
656+
which passed or failed in previous runs, as well as labels.
657+
* The `tag` keyword allows to tag a testset with labels, encoded as symbols.
658+
When `tag` is a list of symbols, tag all matching testsets with these.
659+
When `tag` is a symbol, tag all matching testsets with it.
660+
Currently, `tag` has an effect only if `dry` is `true`.
619661
* When `spin` is `true`, the description of the testset being currently executed
620662
is shown (if there is only one), as well as a "spinner". This is disabled when
621663
all the available threads/workers are used to run tests (i.e. typically
@@ -714,6 +756,7 @@ function retest(@nospecialize(args::ArgType...);
714756
load::Bool = def(:load),
715757
seed::Integer = def(:seed),
716758
marks::Bool = def(:marks),
759+
tag = def(:tag),
717760
spin::Bool = def(:spin),
718761
# clear: clear marks for matching tests, only active if dry=true
719762
clear::Bool = def(:clear),
@@ -760,6 +803,15 @@ function retest(@nospecialize(args::ArgType...);
760803

761804
maxidw[] = id ? maxidw[] : 0
762805

806+
if tag isa Symbol
807+
tag = [tag]
808+
elseif !(tag isa Vector{Symbol})
809+
tag = vec(collect(Symbol, tag))
810+
end
811+
if !dry && !isempty(tag)
812+
@warn "tag keyword: labels can be added only in dry mode"
813+
end
814+
763815
for imod in eachindex(modules)
764816
mod, pat = modules[imod]
765817
tests = alltests[imod]
@@ -776,7 +828,8 @@ function retest(@nospecialize(args::ArgType...);
776828
end
777829
if verbose > 0
778830
foreach(ts -> dryrun(mod, ts, pat, id ? 0 : module_header*2,
779-
maxidw = id ? maxidw[] : 0, marks=marks, clear=clear),
831+
maxidw = id ? maxidw[] : 0, marks=marks, tag=tag,
832+
clear=clear),
780833
tests)
781834
end
782835
continue
@@ -1454,7 +1507,8 @@ hasmany(tests) = length(tests) > 1 || isfor(tests[1])
14541507

14551508
function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
14561509
parentsubj::Union{Missing, String}=""
1457-
; maxidw::Int, marks::Bool, clear::Bool, # external calls
1510+
# external calls:
1511+
; maxidw::Int, marks::Bool, tag::Vector{Symbol}, clear::Bool,
14581512
# only recursive calls:
14591513
evaldesc=true, repeated=nothing, show::Bool=true)
14601514
@assert ts.run
@@ -1470,13 +1524,24 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
14701524

14711525
subject = desc isa String ? parentsubj * "/" * desc :
14721526
missing
1473-
if isfinal(ts) && false === matches(pat, subject, ts)
1527+
final = isfinal(ts)
1528+
if final || !isempty(tag)
1529+
ismatch = matches(pat, subject, ts)
1530+
end
1531+
if final && false === ismatch
14741532
return false, false, false
14751533
end
14761534

14771535
res = pastresult(ts.marks, subject)
14781536
if clear && res !== nothing
14791537
delmark!(ts.marks, subject, res ? _pass : _fail)
1538+
# should we set res=nothing here ? not doing it might be confusing,
1539+
# but it gives an idea about which marks are being deleted
1540+
end
1541+
if subject !== missing
1542+
for mark=tag
1543+
ismatch && addmark!(ts.marks, subject, mark)
1544+
end
14801545
end
14811546

14821547
if show
@@ -1489,9 +1554,14 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
14891554
color=:light_black)
14901555
end
14911556

1492-
if marks && res !== nothing
1493-
printstyled(res ? "" : "", color = res ? :green : Base.error_color(),
1494-
bold=true)
1557+
if marks
1558+
for mark in markiter(ts.marks, subject, true)
1559+
printstyled(" ", mark, color=:cyan, bold=false)
1560+
end
1561+
if res !== nothing
1562+
printstyled(res ? "" : "", color = res ? :green : Base.error_color(),
1563+
bold=true)
1564+
end
14951565
end
14961566
end
14971567

@@ -1501,7 +1571,7 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
15011571
for tsc in ts.children
15021572
tsc.run || continue
15031573
dryrun(mod, tsc, pat, align + 2, subject,
1504-
maxidw=maxidw, marks=marks, clear=clear, show=true)
1574+
maxidw=maxidw, marks=marks, tag=tag, clear=clear, show=true)
15051575
end
15061576
false, false, false # meaningless unused triple
15071577
elseif marks
@@ -1518,7 +1588,8 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
15181588
for tsc in ts.children
15191589
tsc.run || continue
15201590
cp, cf, cu = dryrun(mod, tsc, pat, align + 2, subject,
1521-
maxidw=maxidw, marks=marks, clear=clear, show=false)
1591+
maxidw=maxidw, marks=marks, tag=tag, clear=clear,
1592+
show=false)
15221593
passes |= cp
15231594
fails |= cf
15241595
unrun |= cu
@@ -1567,7 +1638,8 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0,
15671638
ts.iter = iter # necessary when reachable is used
15681639
beginend.marks = ts.marks
15691640
dryrun(mod, beginend, pat, align, parentsubj; evaldesc=false,
1570-
repeated=repeated, maxidw=maxidw, marks=marks, clear=clear, show=show)
1641+
repeated=repeated, maxidw=maxidw, marks=marks, tag=tag,
1642+
clear=clear, show=show)
15711643
end
15721644

15731645
loopvalues = ts.loopvalues

src/testset.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ using Distributed: myid, nworkers
1414

1515
import InlineTest: @testset
1616

17-
using ..ReTest: Pattern, matches, setresult
17+
using ..ReTest: Pattern, matches, setresult!
1818

1919
# mostly copied from Test stdlib
2020
# changed from Test: pass nothing as file in ip_has_file_and_func
@@ -528,7 +528,7 @@ function testset_beginend(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, d
528528
Base.catch_stack(), $(QuoteNode(source))))
529529
finally
530530
copy!(RNG, oldrng)
531-
setresult($marks, ts.subject, !anyfailed(ts))
531+
setresult!($marks, ts.subject, !anyfailed(ts))
532532
pop_testset()
533533
ret = finish(ts, $chan)
534534
end
@@ -576,13 +576,13 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64,
576576
let
577577
ts.timed = @stats $stats $(esc(tests))
578578
end
579-
setresult($marks, ts.subject, !anyfailed(ts))
579+
setresult!($marks, ts.subject, !anyfailed(ts))
580580
catch err
581581
err isa InterruptException && rethrow()
582582
# Something in the test block threw an error. Count that as an
583583
# error in this test set
584584
record(ts, Error(:nontest_error, Expr(:tuple), err, Base.catch_stack(), $(QuoteNode(source))))
585-
setresult($marks, ts.subject, false)
585+
setresult!($marks, ts.subject, false)
586586
end
587587
end
588588
end

test/runtests.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,42 @@ x ✔
11091109
""")
11101110
check(Marks, "x", dry=true, marks=true, verbose=1, id=false, interpolated, [], output="""
11111111
x ✔ ✔ ✘
1112+
""")
1113+
1114+
check(Marks, dry=true, clear=true, marks=true, id=false, [], output="""
1115+
a ✔ ✔ ✘ ⋯
1116+
l1 ✔ ✔ ⋯
1117+
l2 ✔ ✔
1118+
x ✔ ✔ ✘
1119+
""")
1120+
check(Marks, dry=true, clear=true, marks=true, id=false, [], output="""
1121+
a ⋯
1122+
l1 ⋯
1123+
l2 ⋯
1124+
x ⋯
1125+
""")
1126+
1127+
retest(Marks, "y1", -4, dry=true, tag=:ylabel) # should not label "/x"
1128+
retest(Marks, r"a$", dry=true, tag=[:a1, :a2])
1129+
retest(Marks, r"a$", dry=false)
1130+
retest(Marks, r"a$", dry=true, tag=[:a3 :a4])
1131+
retest(Marks, r"a$", dry=true, tag=(:a5, :a6))
1132+
check(Marks, "-l", -4, dry=true, verbose=9, id=false, marks=true, [], output="""
1133+
a a1 a2 a3 a4 a5 a6 ✔
1134+
b
1135+
c
1136+
e
1137+
x
1138+
y1 ylabel
1139+
z1 ylabel
1140+
z2 ylabel
1141+
z3 ylabel
1142+
z4 ylabel
1143+
y2
1144+
z1
1145+
z2
1146+
z3
1147+
z4
11121148
""")
11131149
end
11141150

test/setup.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function check(x...; runtests=false, output::Union{Nothing,String}=nothing,
2020
verbose=ReTest.def(:verbose), stats=ReTest.def(:stats), dry=ReTest.def(:dry),
2121
strict::Bool=ReTest.def(:strict), recursive=ReTest.def(:recursive),
2222
static=ReTest.def(:static), id=ReTest.def(:id), load=ReTest.def(:load),
23-
marks::Bool=false)
23+
marks::Bool=false, clear::Bool=ReTest.def(:clear))
2424
@assert !(runtests & (output !== nothing)) "unimplemented"
2525
args = x[1:end-1]
2626
expected = x[end]
@@ -32,15 +32,17 @@ function check(x...; runtests=false, output::Union{Nothing,String}=nothing,
3232
if runtests
3333
getfield(args[1], :runtests)(args[2:end]...; verbose=verbose, stats=stats, dry=dry,
3434
strict=strict, recursive=recursive, static=static,
35-
id=id, load=load, marks=marks)
35+
id=id, load=load, marks=marks, clear=clear)
3636
elseif output === nothing
3737
retest(args...; verbose=verbose, stats=stats, dry=dry, strict=strict,
38-
recursive=recursive, static=static, id=id, load=load, marks=marks)
38+
recursive=recursive, static=static, id=id, load=load, marks=marks,
39+
clear=clear)
3940
else
4041
mktemp() do path, io
4142
redirect_stdout(io) do
4243
retest(args...; verbose=verbose, stats=stats, dry=dry, strict=strict,
43-
recursive=recursive, static=static, id=id, load=load, marks=marks)
44+
recursive=recursive, static=static, id=id, load=load, marks=marks,
45+
clear=clear)
4446
end
4547
seekstart(io)
4648
printed = join(map(rstrip, split(readchomp(io), '\n')), '\n')

0 commit comments

Comments
 (0)