@@ -60,6 +60,7 @@ include("macros.jl")
60
60
include (" junit_xml.jl" )
61
61
include (" testcontext.jl" )
62
62
include (" log_capture.jl" )
63
+ include (" filtering.jl" )
63
64
64
65
function __init__ ()
65
66
DEFAULT_STDOUT[] = stdout
@@ -208,25 +209,11 @@ will be run.
208
209
"""
209
210
function runtests end
210
211
211
- # We assume copy-pasting test name would be the most common use-case
212
- _shouldrun (name:: AbstractString , ti_name) = name == ti_name
213
- # Regex used for partial matches on test item name (commonly used with XUnit.jl)
214
- _shouldrun (pattern:: Regex , ti_name) = contains (ti_name, pattern)
215
- _shouldrun (tags:: AbstractVector{Symbol} , ti_tags) = issubset (tags, ti_tags)
216
- # All tags must be present in the test item, aka we use AND chaining for tags.
217
- # OR chaining would make it hard to run more specific subsets of tests + one can run
218
- # mutliple independents `runtests` with AND chaining to get most of the benefits of OR chaining
219
- # (with the caveat that overlaps would be run multiple times)
220
- _shouldrun (tag:: Symbol , ti_tags) = tag in ti_tags
221
- _shouldrun (:: Nothing , x) = true
222
-
223
- default_shouldrun (ti:: TestItem ) = true
224
-
225
- runtests (; kw... ) = runtests (default_shouldrun, dirname (Base. active_project ()); kw... )
212
+ runtests (; kw... ) = runtests (Returns (true ), dirname (Base. active_project ()); kw... )
226
213
runtests (shouldrun; kw... ) = runtests (shouldrun, dirname (Base. active_project ()); kw... )
227
- runtests (paths:: AbstractString... ; kw... ) = runtests (default_shouldrun , paths... ; kw... )
214
+ runtests (paths:: AbstractString... ; kw... ) = runtests (Returns ( true ) , paths... ; kw... )
228
215
229
- runtests (pkg:: Module ; kw... ) = runtests (default_shouldrun , pkg; kw... )
216
+ runtests (pkg:: Module ; kw... ) = runtests (Returns ( true ) , pkg; kw... )
230
217
function runtests (shouldrun, pkg:: Module ; kw... )
231
218
dir = pkgdir (pkg)
232
219
isnothing (dir) && error (" Could not find directory for `$pkg `" )
@@ -263,7 +250,7 @@ function runtests(
263
250
timeout_profile_wait >= 0 || throw (ArgumentError (" `timeout_profile_wait` must be a non-negative number, got $(repr (timeout_profile_wait)) " ))
264
251
# If we were given paths but none were valid, then nothing to run.
265
252
! isempty (paths) && isempty (paths′) && return nothing
266
- shouldrun_combined (ti) = shouldrun (ti) && _shouldrun (name, ti . name) && _shouldrun ( tags, ti . tags )
253
+ ti_filter = TestItemFilter (shouldrun, tags, name )
267
254
mkpath (RETESTITEMS_TEMP_FOLDER[]) # ensure our folder wasn't removed
268
255
save_current_stdio ()
269
256
nworkers = max (0 , nworkers)
@@ -274,10 +261,10 @@ function runtests(
274
261
debuglvl = Int (debug)
275
262
if debuglvl > 0
276
263
LoggingExtras. withlevel (LoggingExtras. Debug; verbosity= debuglvl) do
277
- _runtests (shouldrun_combined , paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs, timeout_profile_wait, gc_between_testitems)
264
+ _runtests (ti_filter , paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs, timeout_profile_wait, gc_between_testitems)
278
265
end
279
266
else
280
- return _runtests (shouldrun_combined , paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs, timeout_profile_wait, gc_between_testitems)
267
+ return _runtests (ti_filter , paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs, timeout_profile_wait, gc_between_testitems)
281
268
end
282
269
end
283
270
291
278
# By tracking and reusing test environments, we can avoid this issue.
292
279
const TEST_ENVS = Dict {String, String} ()
293
280
294
- function _runtests (shouldrun , paths, nworkers:: Int , nworker_threads:: String , worker_init_expr:: Expr , test_end_expr:: Expr , testitem_timeout:: Int , retries:: Int , memory_threshold:: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol , timeout_profile_wait:: Int , gc_between_testitems:: Bool )
281
+ function _runtests (ti_filter , paths, nworkers:: Int , nworker_threads:: String , worker_init_expr:: Expr , test_end_expr:: Expr , testitem_timeout:: Int , retries:: Int , memory_threshold:: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol , timeout_profile_wait:: Int , gc_between_testitems:: Bool )
295
282
# Don't recursively call `runtests` e.g. if we `include` a file which calls it.
296
283
# So we ignore the `runtests(...)` call in `test/runtests.jl` when `runtests(...)`
297
284
# was called from the command line.
@@ -311,7 +298,7 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
311
298
if is_running_test_runtests_jl (proj_file)
312
299
# Assume this is `Pkg.test`, so test env already active.
313
300
@debugv 2 " Running in current environment `$(Base. active_project ()) `"
314
- return _runtests_in_current_env (shouldrun , paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs, timeout_profile_wait, gc_between_testitems)
301
+ return _runtests_in_current_env (ti_filter , paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs, timeout_profile_wait, gc_between_testitems)
315
302
else
316
303
@debugv 1 " Activating test environment for `$proj_file `"
317
304
orig_proj = Base. active_project ()
@@ -324,7 +311,7 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
324
311
testenv = TestEnv. activate ()
325
312
TEST_ENVS[proj_file] = testenv
326
313
end
327
- _runtests_in_current_env (shouldrun , paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs, timeout_profile_wait, gc_between_testitems)
314
+ _runtests_in_current_env (ti_filter , paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs, timeout_profile_wait, gc_between_testitems)
328
315
finally
329
316
Base. set_active_project (orig_proj)
330
317
end
@@ -333,16 +320,16 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
333
320
end
334
321
335
322
function _runtests_in_current_env (
336
- shouldrun , paths, projectfile:: String , nworkers:: Int , nworker_threads, worker_init_expr:: Expr , test_end_expr:: Expr ,
323
+ ti_filter , paths, projectfile:: String , nworkers:: Int , nworker_threads, worker_init_expr:: Expr , test_end_expr:: Expr ,
337
324
testitem_timeout:: Int , retries:: Int , memory_threshold:: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol ,
338
- timeout_profile_wait:: Int , gc_between_testitems:: Bool ,
325
+ timeout_profile_wait:: Int , gc_between_testitems:: Bool
339
326
)
340
327
start_time = time ()
341
328
proj_name = something (Pkg. Types. read_project (projectfile). name, " " )
342
329
@info " Scanning for test items in project `$proj_name ` at paths: $(join (paths, " , " )) "
343
330
inc_time = time ()
344
331
@debugv 1 " Including tests in $paths "
345
- testitems, _ = include_testfiles! (proj_name, projectfile, paths, shouldrun , verbose_results, report)
332
+ testitems, _ = include_testfiles! (proj_name, projectfile, paths, ti_filter , verbose_results, report)
346
333
ntestitems = length (testitems. testitems)
347
334
@debugv 1 " Done including tests in $paths "
348
335
@info " Finished scanning for test items in $(round (time () - inc_time, digits= 2 )) seconds." *
@@ -660,49 +647,8 @@ function _is_subproject(dir, current_projectfile)
660
647
return true
661
648
end
662
649
663
- # Error if we are trying to `include` a file with anything except an `@testitem` or
664
- # `@testsetup` call at the top-level.
665
- # Re-use `Base.include` to avoid duplicating subtle code-loading logic from `Base`.
666
- # Note:
667
- # For now this is just checks for `:macrocall` so we can support alternative macros that
668
- # expand to be an `@testitem`. We will likely _tighten_ this check in future.
669
- # i.e. We may in future throw an error for files that currently successfully get included.
670
- # i.e. Only `@testitem` and `@testsetup` calls are officially supported.
671
- checked_include (mod, filepath) = Base. include (check_retestitem_macrocall, mod, filepath)
672
- function check_retestitem_macrocall (expr)
673
- if Meta. isexpr (expr, :error )
674
- # If the expression failed to parse, most user-friendly to throw the ParseError,
675
- # rather than report an error about using only `@testitem` or `@testsetup`.
676
- Core. eval (Main, expr)
677
- end
678
- is_retestitem_macrocall (expr) || _throw_not_macrocall (expr)
679
- return expr
680
- end
681
- function is_retestitem_macrocall (expr:: Expr )
682
- if expr. head == :macrocall
683
- # For now, we're not checking for `@testitem`/`@testsetup` only,
684
- # but we can still guard against the most common issue.
685
- name = expr. args[1 ]
686
- if name != Symbol (" @testset" ) && name != Symbol (" @test" )
687
- return true
688
- end
689
- end
690
- return false
691
- end
692
- function _throw_not_macrocall (expr)
693
- # `Base.include` sets the `:SOURCE_PATH` before the `mapexpr`
694
- # (`check_retestitem_macrocall`) is first called
695
- file = get (task_local_storage (), :SOURCE_PATH , " unknown" )
696
- msg = """
697
- Test files must only include `@testitem` and `@testsetup` calls.
698
- In $(repr (file)) got:
699
- $(Base. remove_linenums! (expr))
700
- """
701
- error (msg)
702
- end
703
-
704
650
# for each directory, kick off a recursive test-finding task
705
- function include_testfiles! (project_name, projectfile, paths, shouldrun , verbose_results:: Bool , report:: Bool )
651
+ function include_testfiles! (project_name, projectfile, paths, ti_filter :: TestItemFilter , verbose_results:: Bool , report:: Bool )
706
652
project_root = dirname (projectfile)
707
653
subproject_root = nothing # don't recurse into directories with their own Project.toml.
708
654
root_node = DirNode (project_name; report, verbose= verbose_results)
@@ -748,7 +694,7 @@ function include_testfiles!(project_name, projectfile, paths, shouldrun, verbose
748
694
continue
749
695
end
750
696
fpath = relpath (filepath, project_root)
751
- file_node = FileNode (fpath, shouldrun ; report, verbose= verbose_results)
697
+ file_node = FileNode (fpath, ti_filter ; report, verbose= verbose_results)
752
698
testitem_names = Set {String} () # to enforce that names in the same file are unique
753
699
push! (dir_node, file_node)
754
700
@debugv 1 " Including test items from file `$filepath `"
@@ -757,7 +703,7 @@ function include_testfiles!(project_name, projectfile, paths, shouldrun, verbose
757
703
task_local_storage (:__RE_TEST_ITEMS__ , ($ file_node, $ testitem_names)) do
758
704
task_local_storage (:__RE_TEST_PROJECT__ , $ (project_root)) do
759
705
task_local_storage (:__RE_TEST_SETUPS__ , $ setup_channel) do
760
- checked_include ( Main, $ filepath)
706
+ Base . include ( $ ti_filter, Main, $ filepath)
761
707
end
762
708
end
763
709
end
0 commit comments