@@ -60,6 +60,7 @@ include("macros.jl")
6060include (" junit_xml.jl" )
6161include (" testcontext.jl" )
6262include (" log_capture.jl" )
63+ include (" filtering.jl" )
6364
6465function __init__ ()
6566 DEFAULT_STDOUT[] = stdout
@@ -208,25 +209,11 @@ will be run.
208209"""
209210function runtests end
210211
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... )
226213runtests (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... )
228215
229- runtests (pkg:: Module ; kw... ) = runtests (default_shouldrun , pkg; kw... )
216+ runtests (pkg:: Module ; kw... ) = runtests (Returns ( true ) , pkg; kw... )
230217function runtests (shouldrun, pkg:: Module ; kw... )
231218 dir = pkgdir (pkg)
232219 isnothing (dir) && error (" Could not find directory for `$pkg `" )
@@ -263,7 +250,7 @@ function runtests(
263250 timeout_profile_wait >= 0 || throw (ArgumentError (" `timeout_profile_wait` must be a non-negative number, got $(repr (timeout_profile_wait)) " ))
264251 # If we were given paths but none were valid, then nothing to run.
265252 ! 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 )
267254 mkpath (RETESTITEMS_TEMP_FOLDER[]) # ensure our folder wasn't removed
268255 save_current_stdio ()
269256 nworkers = max (0 , nworkers)
@@ -274,10 +261,10 @@ function runtests(
274261 debuglvl = Int (debug)
275262 if debuglvl > 0
276263 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)
278265 end
279266 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)
281268 end
282269end
283270
291278# By tracking and reusing test environments, we can avoid this issue.
292279const TEST_ENVS = Dict {String, String} ()
293280
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 )
295282 # Don't recursively call `runtests` e.g. if we `include` a file which calls it.
296283 # So we ignore the `runtests(...)` call in `test/runtests.jl` when `runtests(...)`
297284 # was called from the command line.
@@ -311,7 +298,7 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
311298 if is_running_test_runtests_jl (proj_file)
312299 # Assume this is `Pkg.test`, so test env already active.
313300 @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)
315302 else
316303 @debugv 1 " Activating test environment for `$proj_file `"
317304 orig_proj = Base. active_project ()
@@ -324,7 +311,7 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
324311 testenv = TestEnv. activate ()
325312 TEST_ENVS[proj_file] = testenv
326313 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)
328315 finally
329316 Base. set_active_project (orig_proj)
330317 end
@@ -333,16 +320,16 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
333320end
334321
335322function _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 ,
337324 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
339326)
340327 start_time = time ()
341328 proj_name = something (Pkg. Types. read_project (projectfile). name, " " )
342329 @info " Scanning for test items in project `$proj_name ` at paths: $(join (paths, " , " )) "
343330 inc_time = time ()
344331 @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)
346333 ntestitems = length (testitems. testitems)
347334 @debugv 1 " Done including tests in $paths "
348335 @info " Finished scanning for test items in $(round (time () - inc_time, digits= 2 )) seconds." *
@@ -660,49 +647,8 @@ function _is_subproject(dir, current_projectfile)
660647 return true
661648end
662649
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-
704650# 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 )
706652 project_root = dirname (projectfile)
707653 subproject_root = nothing # don't recurse into directories with their own Project.toml.
708654 root_node = DirNode (project_name; report, verbose= verbose_results)
@@ -748,7 +694,7 @@ function include_testfiles!(project_name, projectfile, paths, shouldrun, verbose
748694 continue
749695 end
750696 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)
752698 testitem_names = Set {String} () # to enforce that names in the same file are unique
753699 push! (dir_node, file_node)
754700 @debugv 1 " Including test items from file `$filepath `"
@@ -757,7 +703,7 @@ function include_testfiles!(project_name, projectfile, paths, shouldrun, verbose
757703 task_local_storage (:__RE_TEST_ITEMS__ , ($ file_node, $ testitem_names)) do
758704 task_local_storage (:__RE_TEST_PROJECT__ , $ (project_root)) do
759705 task_local_storage (:__RE_TEST_SETUPS__ , $ setup_channel) do
760- checked_include ( Main, $ filepath)
706+ Base . include ( $ ti_filter, Main, $ filepath)
761707 end
762708 end
763709 end
0 commit comments