Skip to content

Commit 1c3428c

Browse files
committed
Replace test_filter/custom_tests by a single testsuite argument.
1 parent b550414 commit 1c3428c

3 files changed

Lines changed: 128 additions & 93 deletions

File tree

README.md

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,36 @@ using ParallelTestRunner
4040
runtests(MyModule, ARGS)
4141
```
4242

43-
### Filtering
43+
### Customizing the test suite
4444

45-
`runtests` takes a keyword argument that acts as a filter function
45+
By default, `runtests` automatically discovers all `.jl` files in your `test/` directory (excluding `runtests.jl` itself) using the `find_tests` function. You can customize which tests to run by providing a custom `testsuite` dictionary:
4646

4747
```julia
48-
function test_filter(test)
49-
if Sys.iswindows() && test == "ext/specialfunctions"
50-
return false
48+
# Manually define your test suite
49+
testsuite = Dict(
50+
"basic" => quote
51+
include("basic.jl")
52+
end,
53+
"advanced" => quote
54+
include("advanced.jl")
5155
end
52-
return true
56+
)
57+
58+
runtests(MyModule, ARGS; testsuite)
59+
```
60+
61+
You can also use `find_tests` to automatically discover tests and then filter or modify them:
62+
63+
```julia
64+
# Start with autodiscovered tests
65+
testsuite = find_tests(pwd())
66+
67+
# Remove tests that shouldn't run on Windows
68+
if Sys.iswindows()
69+
delete!(testsuite, "ext/specialfunctions")
5370
end
5471

55-
runtests(MyModule, ARGS; test_filter)
72+
runtests(MyModule, ARGS; testsuite)
5673
```
5774

5875
### Provide defaults

src/ParallelTestRunner.jl

Lines changed: 70 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module ParallelTestRunner
22

3-
export runtests, addworkers, addworker
3+
export runtests, addworkers, addworker, find_tests
44

55
using Malt
66
using Dates
@@ -445,9 +445,51 @@ function addworker(; env=Vector{Pair{String, String}}())
445445
end
446446

447447
"""
448-
runtests(mod::Module, ARGS; RecordType = TestRecord,
449-
test_filter = Returns(true),
450-
custom_tests = Dict(),
448+
find_tests(dir::String) -> Dict{String, Expr}
449+
450+
Discover test files in a directory and return a test suite dictionary.
451+
452+
Walks through `dir` and finds all `.jl` files (excluding `runtests.jl`), returning a
453+
dictionary mapping test names to expression that include each test file.
454+
"""
455+
function find_tests(dir::String)
456+
tests = Dict{String, Expr}()
457+
for (rootpath, dirs, files) in walkdir(dir)
458+
# find Julia files
459+
filter!(files) do file
460+
endswith(file, ".jl") && file !== "runtests.jl"
461+
end
462+
isempty(files) && continue
463+
464+
# strip extension
465+
files = map(files) do file
466+
file[1:(end - 3)]
467+
end
468+
469+
# prepend subdir
470+
subdir = relpath(rootpath, dir)
471+
if subdir != "."
472+
files = map(files) do file
473+
joinpath(subdir, file)
474+
end
475+
end
476+
477+
# unify path separators
478+
files = map(files) do file
479+
replace(file, path_separator => '/')
480+
end
481+
482+
for file in files
483+
path = joinpath(rootpath, file * ".jl")
484+
tests[file] = :(include($path))
485+
end
486+
end
487+
return tests
488+
end
489+
490+
"""
491+
runtests(mod::Module, ARGS; testsuite::Dict{String,Expr}=find_tests(pwd()),
492+
RecordType = TestRecord,
451493
init_code = :(),
452494
test_worker = Returns(nothing),
453495
stdout = Base.stdout,
@@ -463,9 +505,9 @@ Run Julia tests in parallel across multiple worker processes.
463505
464506
Several keyword arguments are also supported:
465507
508+
- `testsuite`: Dictionary mapping test names to expressions to execute (default: `find_tests(pwd())`).
509+
By default, automatically discovers all `.jl` files in the test directory.
466510
- `RecordType`: Type of test record to use for tracking test results (default: `TestRecord`)
467-
- `test_filter`: Optional function to filter which tests to run (default: run all tests)
468-
- `custom_tests`: Optional dictionary of custom tests, mapping test names to expressions.
469511
- `init_code`: Code use to initialize each test's sandbox module (e.g., import auxiliary
470512
packages, define constants, etc).
471513
- `test_worker`: Optional function that takes a test name and returns a specific worker.
@@ -494,14 +536,24 @@ Several keyword arguments are also supported:
494536
## Examples
495537
496538
```julia
497-
# Run all tests with default settings
539+
# Run all tests with default settings (auto-discovers .jl files)
498540
runtests(MyModule, ARGS)
499541
500542
# Run only tests matching "integration"
501543
runtests(MyModule, ["integration"])
502544
503-
# Run with custom filter function
504-
runtests(MyModule, ARGS; test_filter = test -> occursin("unit", test))
545+
# Customize the test suite
546+
testsuite = find_tests(pwd())
547+
delete!(testsuite, "slow_test") # Remove a specific test
548+
runtests(MyModule, ARGS; testsuite)
549+
550+
# Define a custom test suite manually
551+
testsuite = Dict(
552+
"custom" => quote
553+
@test 1 + 1 == 2
554+
end
555+
)
556+
runtests(MyModule, ARGS; testsuite)
505557
506558
# Use custom test record type
507559
runtests(MyModule, ARGS; RecordType = MyCustomTestRecord)
@@ -512,9 +564,9 @@ runtests(MyModule, ARGS; RecordType = MyCustomTestRecord)
512564
Workers are automatically recycled when they exceed memory limits to prevent out-of-memory
513565
issues during long test runs. The memory limit is set based on system architecture.
514566
"""
515-
function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = TestRecord,
516-
custom_tests::Dict{String, Expr}=Dict{String, Expr}(), init_code = :(),
517-
test_worker = Returns(nothing), stdout = Base.stdout, stderr = Base.stderr)
567+
function runtests(mod::Module, ARGS; testsuite::Dict{String,Expr} = find_tests(pwd()),
568+
RecordType = TestRecord, init_code = :(), test_worker = Returns(nothing),
569+
stdout = Base.stdout, stderr = Base.stderr)
518570
#
519571
# set-up
520572
#
@@ -545,51 +597,8 @@ function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = T
545597
error("Unknown test options `$(join(optlike_args, " "))` (try `--help` for usage instructions)")
546598
end
547599

548-
WORKDIR = pwd()
549-
550-
# choose tests
551-
tests = []
552-
test_runners = Dict()
553-
## custom tests by the user
554-
for (name, runner) in custom_tests
555-
push!(tests, name)
556-
test_runners[name] = runner
557-
end
558-
## files in the test folder
559-
for (rootpath, dirs, files) in walkdir(WORKDIR)
560-
# find Julia files
561-
filter!(files) do file
562-
endswith(file, ".jl") && file !== "runtests.jl"
563-
end
564-
isempty(files) && continue
565-
566-
# strip extension
567-
files = map(files) do file
568-
file[1:(end - 3)]
569-
end
570-
571-
# prepend subdir
572-
subdir = relpath(rootpath, WORKDIR)
573-
if subdir != "."
574-
files = map(files) do file
575-
joinpath(subdir, file)
576-
end
577-
end
578-
579-
# unify path separators
580-
files = map(files) do file
581-
replace(file, path_separator => '/')
582-
end
583-
584-
append!(tests, files)
585-
for file in files
586-
test_runners[file] = quote
587-
include($(joinpath(WORKDIR, file * ".jl")))
588-
end
589-
end
590-
end
591-
## finalize
592-
unique!(tests)
600+
# determine test order
601+
tests = collect(keys(testsuite))
593602
Random.shuffle!(tests)
594603
historical_durations = load_test_history(mod)
595604
sort!(tests, by = x -> -get(historical_durations, x, Inf))
@@ -603,11 +612,8 @@ function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = T
603612
exit(0)
604613
end
605614

606-
# filter tests
607-
if isempty(ARGS)
608-
filter!(test_filter, tests)
609-
else
610-
# let the user filter
615+
# filter tests based on command-line arguments
616+
if !isempty(ARGS)
611617
filter!(tests) do test
612618
any(arg -> startswith(test, arg), ARGS)
613619
end
@@ -834,8 +840,8 @@ function runtests(mod::Module, ARGS; test_filter = Returns(true), RecordType = T
834840
put!(printer_channel, (:started, test, worker_id(wrkr)))
835841
result = try
836842
Malt.remote_eval_wait(Main, wrkr, :(import ParallelTestRunner))
837-
Malt.remote_call_fetch(invokelatest, wrkr, runtest, RecordType, test_runners[test], test,
838-
init_code, io_ctx.color)
843+
Malt.remote_call_fetch(invokelatest, wrkr, runtest, RecordType,
844+
testsuite[test], test, init_code, io_ctx.color)
839845
catch ex
840846
if isa(ex, InterruptException)
841847
# the worker got interrupted, signal other tasks to stop

0 commit comments

Comments
 (0)