A package for running @testitems in parallel.
Wrap your tests in the @testitem macro, place them in a file named *_test.jl, and use runtests to run them:
# test/arithmetic_tests.jl
@testitem "addition" begin
@test 1 + 2 == 3
@test 0 + 2 == 2
@test -1 + 2 == 1
end
@testitem "multiplication" begin
@test 1 * 2 == 2
@test 0 * 2 == 0
@test -1 * 2 == -2
endjulia> using ReTestItems
julia> runtests("test/arithmetic_tests.jl")Run test-items in parallel on multiple processes by passing nworkers:
julia> runtests("test/arithmetic_tests.jl"; nworkers=2)You can run tests using the runtests function,
which will run all tests for the current active project.
julia> using ReTestItems
julia> runtests()Test-items must be in files named with the suffix _test.jl or _tests.jl.
ReTestItems uses these file suffixes to identify which files are "test files";
all other files will be ignored by runtests.
runtests allows you to run a subset of tests by passing the directory or file path(s) you want to run.
julia> runtests(
"test/Database/physical_representation_tests.jl",
"test/PhysicalRepresentation/",
)For interactive sessions, all logs from the tests will be printed out in the REPL by default.
You can disable this by passing logs=:issues in which case logs from a test-item are only printed if that test-items errors or fails.
logs=:issues is also the default for non-interactive sessions.
julia> runtests("test/Database/"; logs=:issues)You can use the name keyword to select test-items by name.
Pass a string to select a test-item by its exact name,
or pass a regular expression (regex) to match multiple test-item names.
julia> runtests("test/Database/"; name="issue-123")
julia> runtests("test/Database/"; name=r"^issue")You can pass tags to select test-items by tag.
When passing multiple tags a test-item is only run if it has all the requested tags.
# Run tests that are tagged as both `regression` and `fast`
julia> runtests("test/Database/"; tags=[:regression, :fast])Filtering by name and tags can be combined to run only test-items that match both the name and tags.
# Run tests named `issue*` which also have tag `regression`.
julia> runtests("test/Database/"; tags=:regression, name=r"^issue")You can run tests in parallel on multiple worker processes using the nworkers keyword.
The nworkers keyword specifies the number of worker processes to use for running tests in parallel:
nworkers=0(the default), runs tests sequentially on the current process.nworkers=1runs tests sequentially in a new process.nworkers=2runs tests in parallel on 2 new processes (and so on fornworkers=3,nworkers=4, ...)
All new workers processes are started before any tests are run. Each worker runs the worker_init_expr (if given), and then selects a test-item to run from a global queue in a work-stealing manner until all test-items have been run.
The number of threads each worker processes should have is specified by the nworker_threads keyword.
For example, nworker_threads=2 will start each worker process with 2 threads, and
nworker_threads="2,1" with start each worker with 2 default threads and 1 interactive
thread
(see the Julia manual on
threadpools).
Note ReTestItems.jl uses distributed parallelism, not multi-threading, to run test-items in parallel.
Tests must be wrapped in a @testitem.
In most cases, a @testitem can just be used instead of a @testset, wrapping together a bunch of related tests:
@testitem "min/max" begin
@test min(1, 2) == 1
@test max(1, 2) == 2
endThe test-item's code is evaluated as top-level code in a new module,
so it can include imports, define new structs or helper functions, as well as declare @tests and @testsets.
@testitem "Do cool stuff" begin
using MyPkgDep
function really_cool_stuff()
# ...
end
@testset "Cool stuff doing" begin
@test really_cool_stuff()
end
endBy default, Test and the package being tested will be imported into the @testitem automatically.
Since a @testitem is the block of code that will be executed, @testitems cannot be nested.
If some test-specific code needs to be shared by multiple @testitems, this code can be placed in a module and marked as @testsetup
and the @testitems can depend on it via the setup keyword.
@testsetup module TestIrrationals
export PI, area
const PI = 3.14159
area(radius) = PI * radius^2
end
@testitem "Arithmetic" setup=[TestIrrationals] begin
@test 1 / PI ≈ 0.31831 atol=1e-6
end
@testitem "Geometry" setup=[TestIrrationals] begin
@test area(1) ≈ PI
endThe setup is run once on each worker process that requires it;
it is not run before every @testitem that depends on the setup.
The skip keyword can be used to skip a @testitem, meaning no code inside that test-item will run.
A skipped test-item logs that it is being skipped and records a single "skipped" test result, similar to @test_skip.
@testitem "skipped" skip=true begin
@test false
endIf skip is given as an Expr, it must return a Bool indicating whether or not to skip the test-item.
This expression will be run in a new module similar to a test-item immediately before the test-item would be run.
# Don't run "orc v1" tests if we don't have orc v1
@testitem "orc v1" skip=:(using LLVM; !LLVM.has_orc_v1()) begin
# tests
endThe skip keyword allows you to define the condition under which a test needs to be skipped,
for example if it can only be run on a certain platform.
See filtering tests for controlling which tests run in a particular runtests call.
If there is something that should be checked after every single @testitem, then it's possible to pass an expression to runtests using the test_end_expr keyword.
This expression will be run immediately after each @testitem.
test_end_expr = quote
@testset "global Foo unchanged" begin
foo = get_global_foo()
@test foo.changes == 0
end
end
runtests("frozzle_tests.jl"; test_end_expr)If there is some set-up that should be done on each worker process before it is used to evaluated test-items, then it is possible to pass an expression to runtests via the worker_init_expr keyword.
This expression will be run on each worker process as soon as it is started.
nworkers = 3
worker_init_expr = quote
set_global_foo_memory_limit!(Sys.total_memory()/nworkers)
end
runtests("frobble_tests.jl"; nworkers, worker_init_expr)- Write tests inside of an
@testitemblock.- These are like an
@testset, except that they must contain all the code they need to run; any imports or definitions required for the tests must be inside the@testitem. - A
@testsetcan still be used, but all@testsets must be inside an@testitem. These nested@testsets can add structure to your test code and to the summary of results printed out at the end of the tests, but serve no other functional purpose. - Tests that might previously have had imports and
structorfunctiondefinitions outside of an@testsetshould instead now declare these inside of a@testitem. @testitemcan be run in parallel by setting thenworkerskeyword.
- These are like an
- Write shared/re-used testing code in a
@testsetup module- If you want to split tests up into multiple
@testitem(so they can run in parallel), but also want to share common helper functions, types, or constants, then put the shared helper code in a module marked with@testsetup. - Each
@testsetupwill only be evaluated once per worker process that requires it. - A
@testsetup moduleis recommended to be used for sharing helper definitions or shared immutable data; not for initializing shared global state that is meant to be mutated (like starting a server). For example, a server should be explicitly started and stopped as needed in a@testitem, not started within a@testsetup module.
- If you want to split tests up into multiple
- Write tests in files named
*_test.jlor*_tests.jl.- ReTestItems scans the directory tree for any file with the correct naming scheme and automatically schedules for evaluation the
@testitemthey contain. - Files without this naming convention will not run.
- Test files can reside in either the
src/ortest/directory, so long as they are named likesrc/sorted_set_tests.jl(note the_tests.jlsuffix). - No explicit
includeof these files is required. - Files containing only
@testsetups can be named*_testsetup.jlor*_testsetups.jl, and these files will always be included. - Note that
test/runtests.jldoes not meet the naming convention, and should not itself contain@testitems.
- ReTestItems scans the directory tree for any file with the correct naming scheme and automatically schedules for evaluation the
- Make sure your
test/runtests.jlscript callsruntests.test/runtests.jlis the script run when you callPkg.test()or] testat the REPL.- This script can have ReTestItems.jl run tests by calling
runtests, for example# test/runtests.jl using ReTestItems, MyPackage runtests(MyPackage)
- Pass to
runtestsany configuration you want your tests to run with, such asretries,testitem_timeout,nworkers,nworker_threads,worker_init_expr,test_end_expr. See theruntestsdocstring for details.
Issues and pull requests are welcome! New contributors should make sure to read the ColPrac Contributor Guide. For significant changes please open an issue for discussion before opening a PR. Information on adding tests is in the test/README.md.