Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ jobs:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, version: '1.10', arch: x64}
- { os: ubuntu-latest, version: 'nightly', arch: x64}
- { os: ubuntu-latest, version: '1', arch: x86 }
- { os: windows-latest, version: '1', arch: x64}
- { os: macOS-latest, version: '1', arch: aarch64}
- { os: ubuntu-latest, version: '1.13.0-rc1', arch: x64 }
- { os: ubuntu-latest, version: '1.13.0-rc1', arch: x86 }
- { os: windows-latest, version: '1.13.0-rc1', arch: x64}
- { os: macOS-latest, version: '1.13.0-rc1', arch: aarch64}

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/Documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@latest
with:
version: '1.13.0-rc1'
- uses: julia-actions/cache@v2
- name: Install dependencies
run: |
Expand Down
10 changes: 8 additions & 2 deletions InlineTest/src/InlineTest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ const INLINE_TEST = Symbol("##InlineTest-01b48f5c342f65df7fcd07f28f0d2cacbb09f0a
const TESTED_MODULES = Union{Module,Nothing}[]
const TESTSET_MACROS = Symbol[]

get_tests(m::Module) = getfield(m, INLINE_TEST).tests
function get_tests(m::Module)
invokelatest() do
getfield(m, INLINE_TEST).tests
end
end

function register(m::Module, macros::Vector{Symbol})
push!(TESTED_MODULES, m)
Expand Down Expand Up @@ -111,7 +115,9 @@ function get_inline_mod!(mod)::Module
@eval mod module $INLINE_TEST
const tests = (tests=[], news=[], map=Dict{Union{String,Expr},Int}())
const TESTSET_MACROS = Symbol[]
__init__() = $register($mod, TESTSET_MACROS)
# use invokelatest so TESTSET_MACROS (defined in the same world)
# is visible to __init__ under Julia 1.13+ binding semantics
__init__() = $register($mod, invokelatest(getglobal, @__MODULE__, :TESTSET_MACROS))
end
end
end
Expand Down
9 changes: 6 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "ReTest"
uuid = "e0db7c4e-2690-44b9-bad6-7687da720f89"
version = "0.4.0"
authors = ["Rafael Fourquet <fourquet.rafael@gmail.com>"]
version = "0.3.4"

[deps]
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Expand All @@ -12,11 +12,14 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[sources]
InlineTest = {path = "InlineTest"}

[compat]
InlineTest = "=0.2.0"
Revise = "3.1"
PrecompileTools = "1.2.1"
julia = "1.10"
Revise = "3.1"
julia = "1.13"

[extras]
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaTesting.github.io/ReTest.jl/stable)
[![](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaTesting.github.io/ReTest.jl/dev)

> [!NOTE]
> For compatibility reasons, ReTest v0.4 requires at least Julia 1.13.

`ReTest` is a testing framework for Julia allowing:

1. Defining tests in source files, whose execution is deferred and triggered
Expand Down
46 changes: 28 additions & 18 deletions src/ReTest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export Test,
detect_ambiguities, detect_unbound_args,
GenericString, GenericSet, GenericDict, GenericArray, GenericOrder

using Base.ScopedValues: @with

using Test: Test,
@test, @test_throws, @test_broken, @test_skip,
@test_warn, @test_nowarn,
Expand Down Expand Up @@ -179,11 +181,17 @@ function replace_ts(source, mod, x::Expr, parent; static_include::Bool,
x, false
end
else @label default
body_br = map(z -> replace_ts(source, mod, z, parent; static_include=static_include,
include_functions=include_functions),
x.args)
filter!(x -> first(x) !== invalid, body_br)
Expr(x.head, first.(body_br)...), any(last.(body_br))
new_args = Any[]
hasbroken = false
for z in x.args
nz, br = replace_ts(source, mod, z, parent;
static_include=static_include,
include_functions=include_functions)
nz === invalid && continue
push!(new_args, nz)
hasbroken |= br
end
Expr(x.head, new_args...), hasbroken
end
end

Expand Down Expand Up @@ -776,8 +784,9 @@ function retest(@nospecialize(args::ArgType...);
root = Testset.ReTestSet(Main, "Overall", overall=true)

maxidw = Ref{Int}(0) # visual width for showing IDs (Ref for mutability in hack below)
tests_descs_hasbrokens = fetchtests.(modules, verbose, module_header, Ref(maxidw);
tests_descs_hasbrokens = [fetchtests(m, verbose, module_header, maxidw;
strict=strict, dup=dup, static=static)
for m in modules]
isempty(tests_descs_hasbrokens) &&
throw(ArgumentError("no modules using ReTest could be found"))

Expand Down Expand Up @@ -888,19 +897,14 @@ function retest(@nospecialize(args::ArgType...);

printlock = ReentrantLock()
previewchan =
if spin && stdout isa Base.TTY && (nthreads() > 1 && VERSION >= v"1.3" ||
nprocs() > 1)
if spin && stdout isa Base.TTY && (nthreads() > 1 || nprocs() > 1)
RemoteChannel(() -> Channel{Maybe{Tuple{Int64,String}}}(Inf))
# needs to be "remote" in the case nprocs() == 2, as then nworkers() == 1,
# which means the one remote worker will put descriptions on previewchan
# (if nworkers() > 1, descriptions are not put because we can't predict
# the order in which they complete, and then the previewer will
# not show the descriptions, just the spinning wheel)

# on VERSION < v"1.3" : we can't call `thread_pin` (see below), and in this
# case previewing doesn't work well, as the worker and previewer tasks
# can end up in the same thread, and the previewer is not responsive

# channel size: if nworkers() == 1, then 2 would suffice (one for
# the "compilation step", one for @testset execution step, and then
# the printer would empty the channel; but for two workers and more,
Expand Down Expand Up @@ -1151,7 +1155,13 @@ function retest(@nospecialize(args::ArgType...);
resp = remotecall_fetch(wrkr, mod, ts, pat, chan
) do mod, ts, pat, chan
mts = make_ts(ts, pat, format.stats, chan)
Core.eval(mod, mts)
# Run in a fresh dynamic scope so a surrounding
# Test.@testset (whose CURRENT_TESTSET is now a
# ScopedValue on Julia 1.13+) doesn't make our
# top-level testset look nested.
@with(Test.CURRENT_TESTSET => Test.FallbackTestSet(),
Test.TESTSET_DEPTH => 0,
Core.eval(mod, mts))
end
if resp isa Vector
ntests += length(resp)
Expand All @@ -1174,7 +1184,7 @@ function retest(@nospecialize(args::ArgType...);
end # worker = @task begin ...

try
if previewchan !== nothing && nthreads() > 1 && VERSION >= v"1.3"
if previewchan !== nothing && nthreads() > 1
# we try to keep thread #1 free of heavy work, so that the previewer stays
# responsive
tid = rand(2:nthreads())
Expand Down Expand Up @@ -1276,8 +1286,8 @@ function process_args(@nospecialize(args);
stestmod = Symbol(mod, :Tests)

testmods = get(loaded_testmodules, mod, nothing)
if testmods === nothing && isdefined(Main, stestmod)
testmod = getfield(Main, stestmod)
if testmods === nothing && invokelatest(isdefined, Main, stestmod)
testmod = invokelatest(getglobal, Main, stestmod)
# TODO: test this branch
if testmod isa Module
testmods = [testmod]
Expand Down Expand Up @@ -1405,7 +1415,7 @@ function process_args(@nospecialize(args);

# remove modules which don't have tests, which can happen when a parent module without
# tests is passed to retest in order to run tests in its submodules
filter!(m -> isdefined(m, INLINE_TEST), modules)
filter!(m -> invokelatest(isdefined, m, INLINE_TEST), modules)

# Remove the precompilation module if we're not precompiling
if ccall(:jl_generating_output, Cint, ()) == 0
Expand Down Expand Up @@ -1460,7 +1470,7 @@ function update_TESTED_MODULES!(double_check::Bool=false)
for sub in recsubmodules(mod)
# new version: just check the assumption
nameof(sub) == INLINE_TEST && continue
if isdefined(sub, INLINE_TEST)
if invokelatest(isdefined, sub, INLINE_TEST)
@assert sub in TESTED_MODULES
end
# old effective version:
Expand Down
65 changes: 10 additions & 55 deletions src/hijack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
Include file `testpath` into `parentmodule`. If `revise` is `true`, `Revise`,
which must be loaded beforehand in your Julia session, is used to track all
recursively included files (in particular testsets). The `revise` keyword
defaults to `true` when `Revise` is loaded and `VERSION >= v"1.5"`, and to
`false` otherwise.
defaults to `true` when `Revise` is loaded, and to `false` otherwise.

The point of using this function is when `revise` is `true` and in particular
when files are included recursively.
Expand All @@ -15,16 +14,12 @@ and if there are no recursively included files, this should be equivalent
to `Revise.includet(testpath)`, provided `parentmodule == Main` and
all `@testset`s defined in `testpath` are in a module defining
`__revise_mode__ = :eval`.

!!! compat "Julia 1.5"
This function requires at least Julia 1.5 when `revise` is `true`.
"""
function load(testpath::AbstractString;
parentmodule::Module=Main, revise::Maybe{Bool}=nothing)

revise === true && VERSION < v"1.5" &&
error("the `revise` keyword requires at least Julia 1.5")
Revise = get_revise(revise)
testpath = normpath(abspath(testpath))

if Revise === nothing
Base.include(parentmodule, testpath)
Expand Down Expand Up @@ -53,10 +48,7 @@ If `revise` is `true`, `Revise`, which must be loaded beforehand in your Julia
session, is used to track the test files (in particular testsets). Note that
this might be brittle, and it's recommended instead to load your test module
via `using ModTests`. The `revise` keyword defaults to `true` when `Revise` is
loaded and `VERSION >= v"1.5"`, and to `false` otherwise.

!!! compat "Julia 1.5"
This function requires at least Julia 1.5 when `revise` is `true`.
loaded, and to `false` otherwise.
"""
function load(packagemod::Module, testfile::Maybe{AbstractString}=nothing;
parentmodule::Module=Main, revise::Maybe{Bool}=nothing,
Expand Down Expand Up @@ -200,8 +192,6 @@ be loaded beforehand in your Julia session. Note that this might be brittle
and not work in all cases. `revise` defaults to `true` when `Revise` is loaded,
and to `false` otherwise.

!!! compat "Julia 1.5"
This function requires at least Julia 1.5.
"""
function hijack end

Expand All @@ -215,14 +205,15 @@ function hijack(path::AbstractString, modname=nothing; parentmodule::Module=Main

# do first, to error early if necessary
Revise = get_revise(revise)
include = setinclude(include, testset)

if modname === nothing
modname = replace(splitext(basename(path))[1], ['-', '.'] => '_')
end
modname = Symbol(modname)

newmod = @eval parentmodule module $modname end
populate_mod!(newmod, path; lazy=lazy, include=setinclude(include, testset),
populate_mod!(newmod, path; lazy=lazy, include=include,
include_functions=include_functions,
Revise=Revise)
newmod
Expand Down Expand Up @@ -250,6 +241,7 @@ function populate_mod!(mod::Module, path; lazy, Revise, include::Maybe{Symbol}=n
lazy ∈ (true, false, :brutal) ||
throw(ArgumentError("the `lazy` keyword must be `true`, `false` or `:brutal`"))

path = normpath(abspath(path))
files = Revise === nothing ? nothing : Dict(path => mod)
substitute!(x) = substitute_retest!(x, lazy, include, files;
include_functions=include_functions)
Expand All @@ -269,8 +261,6 @@ function populate_mod!(mod::Module, path; lazy, Revise, include::Maybe{Symbol}=n
end

function revise_track(Revise, files)
# uniquemod serves in v1.5 to make hijack w/ revise still work in many cases,
# when there aren't nested submodules/includes
for (filepath, mod) in files
if isfile(filepath) # some files might not exist when they are conditionally
# included
Expand Down Expand Up @@ -332,11 +322,7 @@ function substitute_retest!(ex, lazy, include_::Maybe{Symbol}, files=nothing;
include($substitute!, $newfile)
$files[newfile] = $(root_module[])
end)
if VERSION >= v"1.6"
# v1.5 doesn't play well with @__MODULE__, the let expression
# simply... vanishes; so we have to use root_module instead
push!(ex2.args[2].args, :(@assert $(root_module[]) == @__MODULE__))
end
push!(ex2.args[2].args, :(@assert $(root_module[]) == @__MODULE__))
# TODO: add `copy!(::Expr, ::Expr)` to Base
ex.head = ex2.head
copy!(ex.args, ex2.args)
Expand Down Expand Up @@ -456,9 +442,6 @@ and not enclosed within `BaseTests` or `StdLibTests`.
The `lazy` and `revise` keywords have the same meaning as in [`ReTest.hijack`](@ref).
Depending on the value of `lazy`, some test files are skipped when they
are known to fail.

!!! compat "Julia 1.5"
This function requires at least Julia 1.5.
"""
function hijack_base(tests, modname=nothing; parentmodule::Module=Main, lazy=false,
base=:BaseTests, stdlib=:StdLibTests, revise::Maybe{Bool}=nothing)
Expand Down Expand Up @@ -511,9 +494,9 @@ function hijack_base(tests, modname=nothing; parentmodule::Module=Main, lazy=fal
# e.g. `tuple`, collision betwen the tuple function and test/tuple.jl
comp = Symbol(comp, :_)
end
if isdefined(mod, comp) && ith != length(components) ||
if invokelatest(isdefined, mod, comp) && ith != length(components) ||
modname !== nothing && ith == 1 # module already freshly created
mod = getfield(mod, comp)
mod = invokelatest(getglobal, mod, comp)
else
# we always re-eval leaf-modules
mod = @eval mod module $comp end
Expand All @@ -528,7 +511,7 @@ function hijack_base(tests, modname=nothing; parentmodule::Module=Main, lazy=fal
end

get_revise(revise) =
if revise === true || revise === nothing && VERSION >= v"1.5"
if revise === true || revise === nothing
Revise = get(Base.loaded_modules, revise_pkgid(), nothing)
Revise === nothing && revise === true &&
error("Revise is not loaded")
Expand Down Expand Up @@ -573,34 +556,6 @@ function test_path(test)
end
end

if VERSION < v"1.8.0-DEV.34"
const TESTNAMES = [
"subarray", "core", "compiler", "worlds",
"keywordargs", "numbers", "subtype",
"char", "strings", "triplequote", "unicode", "intrinsics",
"dict", "hashing", "iobuffer", "staged", "offsetarray",
"arrayops", "tuple", "reduce", "reducedim", "abstractarray",
"intfuncs", "simdloop", "vecelement", "rational",
"bitarray", "copy", "math", "fastmath", "functional", "iterators",
"operators", "ordering", "path", "ccall", "parse", "loading", "gmp",
"sorting", "spawn", "backtrace", "exceptions",
"file", "read", "version", "namedtuple",
"mpfr", "broadcast", "complex",
"floatapprox", "stdlib", "reflection", "regex", "float16",
"combinatorics", "sysinfo", "env", "rounding", "ranges", "mod2pi",
"euler", "show", "client",
"errorshow", "sets", "goto", "llvmcall", "llvmcall2", "ryu",
"some", "meta", "stacktraces", "docs",
"misc", "threads", "stress", "binaryplatforms", "atexit",
"enums", "cmdlineargs", "int", "interpreter",
"checked", "bitset", "floatfuncs", "precompile",
"boundscheck", "error", "ambiguous", "cartesian", "osutils",
"channels", "iostream", "secretbuffer", "specificity",
"reinterpretarray", "syntax", "corelogging", "missing", "asyncmap",
"smallarrayshrink", "opaque_closure"
]
end

const BLACKLIST = [
# failing at load time (in hijack_base)
"backtrace", "misc", "threads", "cmdlineargs","boundscheck",
Expand Down
12 changes: 2 additions & 10 deletions src/patterns.jl
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,8 @@ function make_pattern(str::AbstractString)
rx =
if isempty(str)
r"" # in order to know to match unconditionally
elseif VERSION >= v"1.3"
r""i * str
else
Regex(str, "i")
r""i * str
end
neg ? not(rx) : rx
end
Expand Down Expand Up @@ -462,14 +460,8 @@ julia> retest(Fail, reachable("a"), verbose=9, dry=true, static=true)
1| a
```

!!! compat "Julia 1.3"
This function requires at least Julia 1.3.
"""
function reachable end

if VERSION >= v"1.3"
reachable(x) = Reachable(make_pattern(x))
end
reachable(x) = Reachable(make_pattern(x))

"""
depth(d::Integer)
Expand Down
Loading
Loading