Skip to content

Commit d20f521

Browse files
committed
generalize pastresults into marks
`marks` will be used to store arbitrary symbols.
1 parent 816624d commit d20f521

File tree

4 files changed

+81
-28
lines changed

4 files changed

+81
-28
lines changed

src/ReTest.jl

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

3838
abstract type Pattern end
3939
function matches end
40+
function setresult end
4041

4142

4243
# * includes
@@ -69,7 +70,8 @@ mutable struct TestsetExpr
6970
parent::Maybe{TestsetExpr}
7071
children::Vector{TestsetExpr}
7172
strings::Vector{Union{String,Missing}}
72-
pastresults::Dict{String,Bool}
73+
# Union to avoid creating a vector in most cases
74+
marks::Dict{String, Union{Symbol, Vector{Symbol}}} # TODO: should be a MultiDict
7375
# loopvalues & loopiters: when successful in evaluating loop values in resolve!,
7476
# we "flatten" the nested for loops into a single loop, with loopvalues
7577
# containing tuples of values, and loopiters the tuples of variables to which the
@@ -84,7 +86,7 @@ mutable struct TestsetExpr
8486

8587
TestsetExpr(source, mod, desc, options, loops, parent, children=TestsetExpr[]) =
8688
new(0, source, mod, desc, options, loops, parent, children, String[],
87-
Dict{String,Bool}())
89+
Dict{String, Union{Symbol, Vector{Symbol}}}())
8890
end
8991

9092
isfor(ts::TestsetExpr) = ts.loops !== nothing
@@ -99,6 +101,54 @@ function tsdepth(ts::Union{TestsetExpr,Testset.ReTestSet})
99101
d
100102
end
101103

104+
const _pass = :__pass__
105+
const _fail = :__fail__
106+
107+
# marks contains at most one of _pass, _fail
108+
109+
# return true (pass), false (fail), or nothing (unrun)
110+
function pastresult(marks::Dict, subject)
111+
ms = get(marks, subject, Symbol())
112+
sym::Symbol = ms isa Symbol ? ms : isempty(ms) ? Symbol() : ms[1]
113+
sym === _pass ? true : sym === _fail ? false : nothing
114+
end
115+
116+
function setresult(marks::Dict, subject, success::Bool)
117+
ms = get(marks, subject, Symbol())
118+
res = success ? _pass : _fail
119+
if ms isa Symbol
120+
# ms could be Symbol() from get's default or delmark!
121+
if ms (_pass, _fail, Symbol())
122+
marks[subject] = res
123+
else
124+
# res always in first position
125+
marks[subject] = [res, ms]
126+
end
127+
else # ms isa Vector
128+
if !isempty(ms) && ms[1] (_pass, _fail)
129+
ms[1] = res
130+
else
131+
pushfirst!(ms, res)
132+
end
133+
end
134+
end
135+
136+
function delmark!(marks, subject, m::Symbol)
137+
ms = get(marks, subject, Symbol())
138+
if ms isa Symbol
139+
if ms === m
140+
marks[subject] = Symbol()
141+
end
142+
else
143+
p = findfirst(==(m), ms)
144+
if p !== nothing
145+
deleteat!(ms, p)
146+
end
147+
end
148+
nothing
149+
end
150+
151+
102152
struct _Invalid
103153
global const invalid = _Invalid.instance
104154
end
@@ -418,7 +468,7 @@ function make_ts(ts::TestsetExpr, pat::Pattern, stats, chan)
418468
if ts.loops === nothing
419469
quote
420470
@testset $(ts.mod) $(isfinal(ts)) $pat $(ts.id) $(ts.desc) $(ts.options) #=
421-
=# $(ts.pastresults) $stats $chan $body
471+
=# $(ts.marks) $stats $chan $body
422472
end
423473
else
424474
c = count(x -> x === nothing, (ts.loopvalues, ts.loopiters))
@@ -430,7 +480,7 @@ function make_ts(ts::TestsetExpr, pat::Pattern, stats, chan)
430480
end
431481
quote
432482
@testset $(ts.mod) $(isfinal(ts)) $pat $(ts.id) $(ts.desc) $(ts.options) #=
433-
=# $(ts.pastresults) $stats $chan $loops $body
483+
=# $(ts.marks) $stats $chan $loops $body
434484
end
435485
end
436486
end
@@ -1414,9 +1464,9 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0, parent
14141464
end
14151465
end
14161466

1417-
res = get(ts.pastresults, subject, nothing)
1467+
res = pastresult(ts.marks, subject)
14181468
if clear && res !== nothing
1419-
delete!(ts.pastresults, subject)
1469+
delmark!(ts.marks, subject, res ? _pass : _fail)
14201470
end
14211471

14221472
if show
@@ -1503,7 +1553,7 @@ function dryrun(mod::Module, ts::TestsetExpr, pat::Pattern, align::Int=0, parent
15031553
ts.parent, ts.children)
15041554
beginend.run = true
15051555
beginend.id = ts.id
1506-
beginend.pastresults = ts.pastresults
1556+
beginend.marks = ts.marks
15071557
dryrun(mod, beginend, pat, align, parentsubj; evaldesc=false,
15081558
repeated=repeated, maxidw=maxidw, marks=marks, clear=clear, show=show)
15091559
end

src/patterns.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,14 @@ end
127127

128128
matches(d::Depth, _, ts) = d.d == tsdepth(ts)
129129

130-
matches(::Pass, subj::AbstractString, ts) = get(ts.pastresults, subj, false)
131-
matches(::Fail, subj::AbstractString, ts) = !get(ts.pastresults, subj, true)
130+
matches(::Pass, subj::AbstractString, ts) = something(pastresult(ts.marks, subj), false)
131+
matches(::Fail, subj::AbstractString, ts) = !something(pastresult(ts.marks, subj), true)
132132
# TODO: test method below
133+
# instead of `isempty(ts.marks)`, a tighter test would be to check that no value in marks
134+
# contains the pass/fail marks; but this would be a bit more expensive, as values have to be
135+
# iterated over
133136
matches(::Union{Pass,Fail}, ::Missing, ts) =
134-
isempty(ts.pastresults) ? false : missing
137+
isempty(ts.marks) ? false : missing
135138

136139

137140
## make_pattern

src/testset.jl

Lines changed: 17 additions & 17 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
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
@@ -65,7 +65,7 @@ mutable struct ReTestSet <: AbstractTestSet
6565
subject::String
6666
id::Int64
6767
overall::Bool # TODO: could be conveyed by having self.mod == ""
68-
pastresults::Dict{String,Bool}
68+
marks::Dict{String, Union{Symbol, Vector{Symbol}}} # used in matches()
6969
results::Vector
7070
n_passed::Int
7171
anynonpass::Bool
@@ -76,10 +76,10 @@ end
7676

7777
function ReTestSet(mod, desc::String, id::Integer=0;
7878
overall=false, verbose=true, parent=nothing,
79-
pastresults=Dict{String,Bool}())
79+
marks=Dict{String, Union{Symbol, Vector{Symbol}}}())
8080
parentsubj = parent === nothing ? "" : parent.subject
8181
subject = string(parentsubj, '/', desc)
82-
ReTestSet(mod, parent, desc, subject, id, overall, pastresults, [],
82+
ReTestSet(mod, parent, desc, subject, id, overall, marks, [],
8383
0, false, verbose, NamedTuple(), nothing)
8484
end
8585

@@ -447,11 +447,11 @@ default_rng() = isdefined(Random, :default_rng) ?
447447
Random.default_rng() :
448448
Random.GLOBAL_RNG
449449

450-
function make_retestset(mod, desc, id, verbose, pastresults, remove_last=false)
450+
function make_retestset(mod, desc, id, verbose, marks, remove_last=false)
451451
_testsets = get(task_local_storage(), :__BASETESTNEXT__, Test.AbstractTestSet[])
452452
@assert !(remove_last && isempty(_testsets))
453453
testsets = @view _testsets[1:end-remove_last]
454-
ReTestSet(mod, desc, id; verbose=verbose, pastresults=pastresults,
454+
ReTestSet(mod, desc, id; verbose=verbose, marks=marks,
455455
parent = isempty(testsets) ? nothing : testsets[end])
456456
end
457457

@@ -472,30 +472,30 @@ end
472472

473473
# non-inline testset with regex filtering support
474474
macro testset(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, desc::Union{String,Expr},
475-
options, pastresults::Dict, stats::Bool, chan, body)
475+
options, marks::Dict, stats::Bool, chan, body)
476476
Testset.testset_beginend(mod, isfinal, pat, id, desc,
477-
options, pastresults, stats, chan, body, __source__)
477+
options, marks, stats, chan, body, __source__)
478478
end
479479

480480
macro testset(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, desc::Union{String,Expr},
481-
options, pastresults::Dict, stats::Bool, chan, loops, body)
481+
options, marks::Dict, stats::Bool, chan, loops, body)
482482
Testset.testset_forloop(mod, isfinal, pat, id, desc,
483-
options, pastresults, stats, chan, loops, body, __source__)
483+
options, marks, stats, chan, loops, body, __source__)
484484
end
485485

486486
"""
487487
Generate the code for a `@testset` with a `begin`/`end` argument
488488
"""
489489
function testset_beginend(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, desc, options,
490-
pastresults::Dict, stats::Bool, chan, tests, source)
490+
marks::Dict, stats::Bool, chan, tests, source)
491491
# Generate a block of code that initializes a new testset, adds
492492
# it to the task local storage, evaluates the test(s), before
493493
# finally removing the testset and giving it a chance to take
494494
# action (such as reporting the results)
495495
desc = esc(desc)
496496
ex = quote
497497
local ts = make_retestset($mod, $desc, $id, $(options.transient_verbose),
498-
$pastresults)
498+
$marks)
499499

500500
if !$isfinal || matches($pat, ts.subject, ts)
501501
local ret
@@ -526,7 +526,7 @@ function testset_beginend(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, d
526526
Base.catch_stack(), $(QuoteNode(source))))
527527
finally
528528
copy!(RNG, oldrng)
529-
$pastresults[ts.subject] = !anyfailed(ts)
529+
setresult($marks, ts.subject, !anyfailed(ts))
530530
pop_testset()
531531
ret = finish(ts, $chan)
532532
end
@@ -546,13 +546,13 @@ end
546546
Generate the code for a `@testset` with a `for` loop argument
547547
"""
548548
function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64,
549-
desc::Union{String,Expr}, options, pastresults::Dict, stats, chan,
549+
desc::Union{String,Expr}, options, marks::Dict, stats, chan,
550550
loops, tests, source)
551551

552552
desc = esc(desc)
553553
blk = quote
554554
local ts0 = make_retestset($mod, $desc, $id, $(options.transient_verbose),
555-
$pastresults, !first_iteration)
555+
$marks, !first_iteration)
556556

557557
if !$isfinal || matches($pat, ts0.subject, ts0)
558558
# Trick to handle `break` and `continue` in the test code before
@@ -573,13 +573,13 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64,
573573
let
574574
ts.timed = @stats $stats $(esc(tests))
575575
end
576-
$pastresults[ts.subject] = !anyfailed(ts)
576+
setresult($marks, ts.subject, !anyfailed(ts))
577577
catch err
578578
err isa InterruptException && rethrow()
579579
# Something in the test block threw an error. Count that as an
580580
# error in this test set
581581
record(ts, Error(:nontest_error, Expr(:tuple), err, Base.catch_stack(), $(QuoteNode(source))))
582-
$pastresults[ts.subject] = false
582+
setresult($marks, ts.subject, false)
583583
end
584584
end
585585
end

test/test_patterns.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import ReTest
66

77
struct MockTestset
88
id
9-
pastresults
9+
marks
1010
parent
1111

1212
MockTestset() = new(rand(1:typemax(Int)), Dict(), nothing)

0 commit comments

Comments
 (0)