Skip to content

Commit 59fbf56

Browse files
Refactor testsetup handling
1 parent 60d93f1 commit 59fbf56

File tree

3 files changed

+41
-50
lines changed

3 files changed

+41
-50
lines changed

src/ReTestItems.jl

Lines changed: 37 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -665,14 +665,14 @@ function include_testfiles!(project_name, projectfile, paths, shouldrun, verbose
665665
# we set it below in tls as __RE_TEST_SETUPS__ for each included file
666666
setup_channel = Channel{Pair{Symbol, TestSetup}}(Inf)
667667
setup_task = @spawn begin
668-
setups = Dict{Symbol, TestSetup}()
669-
for (name, setup) in setup_channel
670-
if haskey(setups, name)
668+
testsetups = Dict{Symbol, TestSetup}()
669+
for (name, ts) in setup_channel
670+
if haskey(testsetups, name)
671671
@warn "Encountered duplicate @testsetup with name: `$name`. Replacing..."
672672
end
673-
setups[name] = setup
673+
testsetups[name] = ts
674674
end
675-
return setups
675+
return testsetups
676676
end
677677
hidden_re = r"\.\w"
678678
@sync for (root, d, files) in Base.walkdir(project_root)
@@ -724,21 +724,21 @@ function include_testfiles!(project_name, projectfile, paths, shouldrun, verbose
724724
# prune empty directories/files
725725
close(setup_channel)
726726
prune!(root_node)
727-
ti = TestItems(root_node)
728-
flatten_testitems!(ti)
729-
check_ids(ti.testitems)
730-
setups = fetch(setup_task)
731-
for (i, x) in enumerate(ti.testitems)
727+
tis = TestItems(root_node)
728+
flatten_testitems!(tis)
729+
check_ids(tis.testitems)
730+
testsetups = fetch(setup_task)
731+
for (i, ti) in enumerate(tis.testitems)
732732
# set a unique number for each testitem
733-
x.number[] = i
733+
ti.number[] = i
734734
# populate testsetups for each testitem
735-
for s in x.setups
736-
if haskey(setups, s)
737-
push!(x.testsetups, setups[s])
735+
for setup_name in ti.setups
736+
if haskey(testsetups, setup_name)
737+
push!(ti.testsetups, setup_name => testsetups[setup_name])
738738
end
739739
end
740740
end
741-
return ti, setups # only returned for testing
741+
return tis, testsetups # only returned for testing
742742
end
743743

744744
function check_ids(testitems)
@@ -821,28 +821,18 @@ function with_source_path(f, path)
821821
end
822822
end
823823

824-
function ensure_setup!(ctx::TestContext, setup::Symbol, setups::Vector{TestSetup}, logs::Symbol)
824+
function ensure_setup!(ctx::TestContext, ts::TestSetup, logs::Symbol)
825825
mods = ctx.setups_evaled
826826
@lock mods.lock begin
827-
mod = get(mods.modules, setup, nothing)
827+
mod = get(mods.modules, ts.name, nothing)
828828
if mod !== nothing
829829
# we've eval-ed this module before, so just return the module name
830830
return nameof(mod)
831831
end
832-
# we haven't eval-ed this module before, so we need to eval it
833-
i = findfirst(s -> s.name == setup, setups)
834-
if i === nothing
835-
# if the setup hasn't been eval-ed before and we don't have it
836-
# in our testsetups, then it was never found during including
837-
# in that case, we return the expected test setup module name
838-
# which will turn into a `using $setup` in the test item
839-
# which will throw an appropriate error
840-
return setup
841-
end
842-
ts = setups[i]
843-
# In case the setup fails to eval, we discard its logs -- the setup will be
844-
# attempted to eval for each of the dependent test items and we'd for each
845-
# failed test item, we'd print the cumulative logs from all the previous attempts.
832+
# We haven't eval-ed this module before, so we need to eval it.
833+
# In case the setup fails to eval, we discard its logs -- we will attempt to eval
834+
# this testsetup for each of the dependent test items, and then for each failed
835+
# test item, we'd print the cumulative logs from all the previous attempts.
846836
isassigned(ts.logstore) && close(ts.logstore[])
847837
ts.logstore[] = open(logpath(ts), "w")
848838
mod_expr = :(module $(gensym(ts.name)) end)
@@ -852,7 +842,7 @@ function ensure_setup!(ctx::TestContext, setup::Symbol, setups::Vector{TestSetup
852842
with_source_path(() -> Core.eval(Main, mod_expr), ts.file)
853843
end
854844
# add the new module to our TestSetupModules
855-
mods.modules[setup] = newmod
845+
mods.modules[ts.name] = newmod
856846
return nameof(newmod)
857847
end
858848
end
@@ -900,9 +890,9 @@ function runtestitem(ti::TestItem; kw...)
900890
# make a fresh TestSetupModules for each testitem run
901891
GLOBAL_TEST_CONTEXT_FOR_TESTING.setups_evaled = TestSetupModules()
902892
empty!(ti.testsetups)
903-
for setup in ti.setups
904-
ts = get(GLOBAL_TEST_SETUPS_FOR_TESTING, setup, nothing)
905-
ts !== nothing && push!(ti.testsetups, ts)
893+
for setup_name in ti.setups
894+
ts = get(GLOBAL_TEST_SETUPS_FOR_TESTING, setup_name, nothing)
895+
ts !== nothing && push!(ti.testsetups, setup_name => ts)
906896
end
907897
runtestitem(ti, GLOBAL_TEST_CONTEXT_FOR_TESTING; kw...)
908898
end
@@ -940,23 +930,24 @@ function runtestitem(
940930
prev = get(task_local_storage(), :__TESTITEM_ACTIVE__, false)
941931
task_local_storage()[:__TESTITEM_ACTIVE__] = true
942932
try
943-
for setup in ti.setups
944-
# TODO(nhd): Consider implementing some affinity to setups, so that we can
945-
# prefer to send testitems to the workers that have already eval'd setups.
946-
# Or maybe allow user-configurable grouping of test items by worker?
947-
# Or group them by file by default?
948-
933+
for (setup_name, ts) in ti.testsetups
949934
# ensure setup has been evaled before
950-
@debugv 1 "Ensuring setup for test item $(repr(name)) $(setup)$(_on_worker())."
951-
ts_mod = ensure_setup!(ctx, setup, ti.testsetups, logs)
935+
@debugv 1 "Ensuring setup for test item $(repr(name)) $(setup_name)$(_on_worker())."
936+
ts_mod = ensure_setup!(ctx, ts, logs)
952937
# eval using in our @testitem module
953-
@debugv 1 "Importing setup for test item $(repr(name)) $(setup)$(_on_worker())."
938+
@debugv 1 "Importing setup for test item $(repr(name)) $(setup_name)$(_on_worker())."
954939
# We look up the testsetups from Main (since tests are eval'd in their own
955940
# temporary anonymous module environment.)
956941
push!(body.args, Expr(:using, Expr(:., :Main, ts_mod)))
957942
# ts_mod is a gensym'd name so that setup modules don't clash
958943
# so we set a const alias inside our @testitem module to make things work
959-
push!(body.args, :(const $setup = $ts_mod))
944+
push!(body.args, :(const $setup_name = $ts_mod))
945+
end
946+
for setup_name in setdiff(ti.setups, keys(ti.testsetups))
947+
# if the setup was requested but is not in our testsetups, then it was never
948+
# found when including files. We still add `using $setup` in the test item
949+
# so that we throw an appropriate error when running the test item.
950+
push!(body.args, Expr(:using, Expr(:., :Main, setup_name)))
960951
end
961952
@debugv 1 "Setup for test item $(repr(name)) done$(_on_worker())."
962953

@@ -994,7 +985,7 @@ function runtestitem(
994985
LineNumberNode(ti.line, ti.file)))
995986
finally
996987
# Make sure all test setup logs are commited to file
997-
foreach(ts->isassigned(ts.logstore) && flush(ts.logstore[]), ti.testsetups)
988+
foreach(ts->isassigned(ts.logstore) && flush(ts.logstore[]), values(ti.testsetups))
998989
ts1 = Test.pop_testset()
999990
task_local_storage()[:__TESTITEM_ACTIVE__] = prev
1000991
@assert ts1 === ts

src/log_capture.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ function print_errors_and_captured_logs(
170170
)
171171
ts = ti.testsets[run_number]
172172
has_errors = any_non_pass(ts)
173-
has_logs = _has_logs(ti, run_number) || any(_has_logs, ti.testsetups)
173+
has_logs = _has_logs(ti, run_number) || any(_has_logs, values(ti.testsetups))
174174
if has_errors || logs == :batched
175175
report_iob = IOContext(IOBuffer(), :color=>Base.get_have_color())
176176
println(report_iob)
@@ -212,7 +212,7 @@ end
212212
# the captured logs or a messgage that no logs were captured. `print_errors_and_captured_logs`
213213
# will call this function only if some logs were collected or when called with `verbose_results`.
214214
function _print_captured_logs(io, ti::TestItem, run_number::Int)
215-
for setup in ti.testsetups
215+
for (_name, setup) in ti.testsetups
216216
_print_captured_logs(io, setup, ti)
217217
end
218218
has_logs = _has_logs(ti, run_number)

src/macros.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ struct TestItem
125125
line::Int
126126
project_root::String
127127
code::Any
128-
testsetups::Vector{TestSetup} # populated by runtests coordinator
128+
testsetups::Dict{Symbol,TestSetup} # populated by runtests coordinator
129129
workerid::Base.RefValue{Int} # populated when the test item is scheduled
130130
testsets::Vector{DefaultTestSet} # populated when the test item is finished running
131131
eval_number::Base.RefValue{Int} # to keep track of how many items have been run so far
@@ -136,7 +136,7 @@ function TestItem(number, name, id, tags, default_imports, setups, retries, time
136136
_id = @something(id, repr(hash(name, hash(relpath(file, project_root)))))
137137
return TestItem(
138138
number, name, _id, tags, default_imports, setups, retries, timeout, skip, file, line, project_root, code,
139-
TestSetup[],
139+
Dict{Symbol,TestSetup}(),
140140
Ref{Int}(0),
141141
DefaultTestSet[],
142142
Ref{Int}(0),

0 commit comments

Comments
 (0)