diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2361a69..335dbde 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -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 diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 1606434..736b0dc 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -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: | diff --git a/InlineTest/src/InlineTest.jl b/InlineTest/src/InlineTest.jl index 993fe21..a53441f 100644 --- a/InlineTest/src/InlineTest.jl +++ b/InlineTest/src/InlineTest.jl @@ -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) @@ -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 diff --git a/Project.toml b/Project.toml index a51b311..1203427 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ReTest" uuid = "e0db7c4e-2690-44b9-bad6-7687da720f89" +version = "0.4.0" authors = ["Rafael Fourquet "] -version = "0.3.4" [deps] Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" @@ -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" diff --git a/README.md b/README.md index 3f0be60..5c6db76 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/ReTest.jl b/src/ReTest.jl index 8e8de1f..e331e89 100644 --- a/src/ReTest.jl +++ b/src/ReTest.jl @@ -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, @@ -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 @@ -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")) @@ -888,8 +897,7 @@ 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 @@ -897,10 +905,6 @@ function retest(@nospecialize(args::ArgType...); # 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, @@ -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) @@ -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()) @@ -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] @@ -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 @@ -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: diff --git a/src/hijack.jl b/src/hijack.jl index d4b9fe1..5c2fb97 100644 --- a/src/hijack.jl +++ b/src/hijack.jl @@ -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. @@ -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) @@ -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, @@ -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 @@ -215,6 +205,7 @@ 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], ['-', '.'] => '_') @@ -222,7 +213,7 @@ function hijack(path::AbstractString, modname=nothing; parentmodule::Module=Main 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 @@ -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) @@ -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 @@ -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) @@ -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) @@ -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 @@ -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") @@ -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", diff --git a/src/patterns.jl b/src/patterns.jl index 29b8afa..9bc1100 100644 --- a/src/patterns.jl +++ b/src/patterns.jl @@ -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 @@ -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) diff --git a/src/precompile.jl b/src/precompile.jl index 9c47a1a..22a064c 100644 --- a/src/precompile.jl +++ b/src/precompile.jl @@ -20,27 +20,41 @@ end # _ReTestPrecompileTestsModule stderr_pipe = Pipe() stdout_pipe = Pipe() + function run_workload(f) + redirect_stdio(; stderr=stderr_pipe, stdout=stdout_pipe) do + try + f() + catch ex + if !(ex isa Test.TestSetException) + rethrow() + end + end + end + end + @compile_workload begin try - redirect_stdio(; stderr=stderr_pipe, stdout=stdout_pipe) do - retest(_ReTestPrecompile, _ReTestPrecompileTests; recursive=false, stats=true, spin=true) + run_workload() do + retest(_ReTestPrecompile, _ReTestPrecompileTests; + recursive=false, stats=true, spin=true) + end + run_workload() do + retest(_ReTestPrecompileTests, "precomp"; recursive=false) end - catch ex + catch close(stderr_pipe.in) close(stdout_pipe.in) - if !(ex isa Test.TestSetException) - stdout_str = read(stdout_pipe, String) - stderr_str = read(stderr_pipe, String) + stdout_str = read(stdout_pipe, String) + stderr_str = read(stderr_pipe, String) - @error "Precompilation failed, this is the captured stdout ($(length(stdout_str)) chars):" - println(stdout_str) + @error "Precompilation failed, this is the captured stdout ($(length(stdout_str)) chars):" + println(stdout_str) - @error "And this is the captured stderr ($(length(stderr_str)) chars):" - println(stderr_str) + @error "And this is the captured stderr ($(length(stderr_str)) chars):" + println(stderr_str) - rethrow() - end + rethrow() finally empty!(ReTest.TESTED_MODULES) close(stderr_pipe) diff --git a/src/testset.jl b/src/testset.jl index c4d3fb6..a0de8bd 100644 --- a/src/testset.jl +++ b/src/testset.jl @@ -2,7 +2,8 @@ module Testset using Test: AbstractTestSet, Broken, DefaultTestSet, Error, Fail, Pass, Test, TestSetException, get_testset, get_testset_depth, - parse_testset_args, pop_testset, push_testset + parse_testset_args +using Base.ScopedValues: @with import Test: finish, record @@ -47,11 +48,6 @@ function scrub_exc_stack(stack) return Any[ (x[1], scrub_backtrace(x[2])) for x in stack ] end -# Compat for catch_stack(), which was deprecated in 1.7 -@static if VERSION < v"1.7" - current_exceptions() = Base.catch_stack() -end - mutable struct Format stats::Bool desc_align::Int @@ -230,8 +226,7 @@ function print_test_results(ts::ReTestSet, fmt::Format; end if fmt.stats # copied from Julia/test/runtests.jl - compile_header = VERSION >= v"1.6-" ? " Compile /" : "" - printstyled("| Time /$compile_header GC | Alloc ΔRSS |", color=:white) + printstyled("| Time / Compile / GC | Alloc ΔRSS |", color=:white) end println() end @@ -420,15 +415,13 @@ function print_counts(ts::ReTestSet, fmt::Format, depth, align, time_str = hide_zero(@sprintf("%6.2f", timed.time), "s") printstyled("| ", time_str, " ", color=:white) - if VERSION >= v"1.6-" - compile_str = all(==(' '), time_str) ? - ' '^6 : # print percentages only if time itself is shown! - # (also, this can result in weird things, like "30663.3%", - # if e.g. there was no @test in the @testset) - hide_zero(@sprintf("%5.1f", timed.compile_time / 10^7 / timed.time), "%") - # can be >= 100% !? - printstyled(compile_str, " ", color=:white) - end + compile_str = all(==(' '), time_str) ? + ' '^6 : # print percentages only if time itself is shown! + # (also, this can result in weird things, like "30663.3%", + # if e.g. there was no @test in the @testset) + hide_zero(@sprintf("%5.1f", timed.compile_time / 10^7 / timed.time), "%") + # can be >= 100% !? + printstyled(compile_str, " ", color=:white) gc_str = all(==(' '), time_str) ? ' '^5 : hide_zero(@sprintf("%4.1f", 100 * timed.gctime / timed.time), "%") @@ -461,12 +454,11 @@ default_rng() = isdefined(Random, :default_rng) ? Random.default_rng() : Random.GLOBAL_RNG -function make_retestset(mod, desc, id, verbose, marks, remove_last=false, iter=1) - _testsets = get(task_local_storage(), :__BASETESTNEXT__, Test.AbstractTestSet[]) - @assert !(remove_last && isempty(_testsets)) - testsets = @view _testsets[1:end-remove_last] +function make_retestset(mod, desc, id, verbose, marks, iter=1) + parent_ts = get_testset() + parent = parent_ts isa ReTestSet ? parent_ts : nothing ReTestSet(mod, desc, id; verbose=verbose, marks=marks, iter=iter, - parent = isempty(testsets) ? nothing : testsets[end]) + parent=parent) end # HACK: we re-use the same macro name `@testset` for actual execution (like in `Test`) @@ -514,38 +506,36 @@ function testset_beginend(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, d if nworkers() == 1 && get_testset_depth() == 0 && $(chan.preview) !== nothing put!($(chan.preview), ($id, $desc)) end - push_testset(ts) # we reproduce the logic of guardseed, but this function # cannot be used as it changes slightly the semantic of @testset, # by wrapping the body in a function local default_rng_orig = copy(default_rng()) - @static if VERSION >= v"1.11" - local tls_seed_orig = copy(Random.get_tls_seed()) - end + local tls_seed_orig = copy(Random.get_tls_seed()) try - # RNG is re-seeded with the desired seed for the test - if ReTest.test_seed[] !== false - Random.seed!(ReTest.test_seed[]) - end - let - ts.timed = @stats $stats $(esc(tests)) - end - catch err - err isa InterruptException && rethrow() - # something in the test block threw an error. Count that as an - # error in this test set - record(ts, Error(:nontest_error, Expr(:tuple), err, - current_exceptions(), $(QuoteNode(source)))) + @with(Test.CURRENT_TESTSET => ts, + Test.TESTSET_DEPTH => get_testset_depth() + 1, + try + # RNG is re-seeded with the desired seed for the test + if ReTest.test_seed[] !== false + Random.seed!(ReTest.test_seed[]) + end + let + ts.timed = @stats $stats $(esc(tests)) + end + catch err + err isa InterruptException && rethrow() + # something in the test block threw an error. Count that as an + # error in this test set + record(ts, Error(:nontest_error, Expr(:tuple), err, + current_exceptions(), $(QuoteNode(source)))) + end) finally copy!(default_rng(), default_rng_orig) - @static if VERSION >= v"1.11" - copy!(Random.get_tls_seed(), tls_seed_orig) - end + copy!(Random.get_tls_seed(), tls_seed_orig) setresult!($marks, ts.subject, !anyfailed(ts)) - pop_testset() ret = finish(ts, $chan) end ret @@ -569,42 +559,48 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, desc = esc(desc) blk = quote iter += 1 - local ts0 = make_retestset($mod, $desc, $id, $(options.transient_verbose), - $marks, !first_iteration, iter) + local ts = make_retestset($mod, $desc, $id, $(options.transient_verbose), + $marks, iter) - if !$isfinal || matches($pat, ts0.subject, ts0) - # Trick to handle `break` and `continue` in the test code before - # they can be handled properly by `finally` lowering. + if !$isfinal || matches($pat, ts.subject, ts) if !first_iteration - pop_testset() - push!(arr, finish(ts, $chan)) - if ts.exception !== nothing - # ts.exception might be set in finish(...) above - # In this case, we currently don't want to continue with subsequent - # iterations, as is done in Test. - # See also https://github.com/JuliaLang/julia/pull/41715 - break - end # it's 1000 times faster to copy from tmprng rather than calling Random.seed! copy!(default_rng(), tmprng) end - ts = ts0 if nworkers() == 1 && get_testset_depth() == 0 && $(chan.preview) !== nothing put!($(chan.preview), ($id, ts.description)) end - push_testset(ts) first_iteration = false + pending_ts = ts try - let - ts.timed = @stats $stats $(esc(tests)) + @with(Test.CURRENT_TESTSET => ts, + Test.TESTSET_DEPTH => get_testset_depth() + 1, + try + let + ts.timed = @stats $stats $(esc(tests)) + end + setresult!($marks, ts.subject, !anyfailed(ts)) + catch err + err isa InterruptException && rethrow() + # Something in the test block threw an error. Count that as an + # error in this test set + record(ts, Error(:nontest_error, Expr(:tuple), err, current_exceptions(), $(QuoteNode(source)))) + setresult!($marks, ts.subject, false) + end) + push!(arr, finish(ts, $chan)) + pending_ts = nothing + if ts.exception !== nothing + # ts.exception might be set in finish(...) above + # In this case, we currently don't want to continue with subsequent + # iterations, as is done in Test. + # See also https://github.com/JuliaLang/julia/pull/41715 + break + end + finally + if pending_ts !== nothing + # body exited via return/break/continue; finalize before unwinding + push!(arr, finish(pending_ts, $chan)) end - setresult!($marks, ts.subject, !anyfailed(ts)) - catch err - err isa InterruptException && rethrow() - # Something in the test block threw an error. Count that as an - # error in this test set - record(ts, Error(:nontest_error, Expr(:tuple), err, current_exceptions(), $(QuoteNode(source)))) - setresult!($marks, ts.subject, false) end end end @@ -613,12 +609,10 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, local arr = Vector{Any}() local first_iteration = true local iter = 0 - local ts + local pending_ts = nothing local default_rng_orig = copy(default_rng()) - @static if VERSION >= v"1.11" - local tls_seed_orig = copy(Random.get_tls_seed()) - end + local tls_seed_orig = copy(Random.get_tls_seed()) local tmprng = copy(default_rng()) if ReTest.test_seed[] !== false @@ -629,16 +623,8 @@ function testset_forloop(mod::Module, isfinal::Bool, pat::Pattern, id::Int64, $(Expr(:for, Expr(:block, [esc(v) for v in loops]...), blk)) end finally - # Handle `return` in test body - if !first_iteration && ts.exception === nothing - pop_testset() - push!(arr, finish(ts, $chan)) - end - copy!(default_rng(), default_rng_orig) - @static if VERSION >= v"1.11" - copy!(Random.get_tls_seed(), tls_seed_orig) - end + copy!(Random.get_tls_seed(), tls_seed_orig) end arr end @@ -683,13 +669,6 @@ macro stats(yes, ex) end end -@static if VERSION >= v"1.9" - # In 1.9 this function was changed to return a tuple of (compile_time, recompilation_time) - cumulative_compile_time_ns() = sum(Base.cumulative_compile_time_ns()) -else - cumulative_compile_time_ns() = isdefined(Base, :cumulative_compile_time_ns) ? - Base.cumulative_compile_time_ns() : - UInt(0) -end +cumulative_compile_time_ns() = sum(Base.cumulative_compile_time_ns()) end # module diff --git a/src/utils.jl b/src/utils.jl index 3f15bb6..658a267 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -3,11 +3,18 @@ Maybe{T} = Union{T,Nothing} issubmodule(m::Module, s) = s isa Module && parentmodule(s) == m && m != s function submodules(m::Module) - nms = filter!(names(m, all=true)) do y - Base.isdefined(m, y) && !Base.isdeprecated(m, y) + # use invokelatest so freshly-loaded submodules are visible regardless + # of the caller's world age (Julia 1.13+ binding semantics) + result = Module[] + for y in names(m, all=true) + if invokelatest(isdefined, m, y) && !Base.isdeprecated(m, y) + v = invokelatest(getglobal, m, y) + if issubmodule(m, v) + push!(result, v) + end + end end - symbols = Core.eval.(Ref(m), nms) - filter!(x -> issubmodule(m, x), symbols) + result end # list of recursive submodules of m, including m itself @@ -38,7 +45,7 @@ end function is_replaced(mod::Module) par = parentmodule(mod) while par != mod - getfield(par, nameof(mod)) != mod && return true + invokelatest(getglobal, par, nameof(mod)) != mod && return true mod = par par = parentmodule(par) end diff --git a/test/FakePackage/Project.toml b/test/FakePackage/Project.toml index 9eb11d2..5543512 100644 --- a/test/FakePackage/Project.toml +++ b/test/FakePackage/Project.toml @@ -5,3 +5,7 @@ version = "0.1.0" [deps] InlineTest = "bd334432-b1e7-49c7-a2dc-dd9149e4ebd6" ReTest = "e0db7c4e-2690-44b9-bad6-7687da720f89" + +[sources] +InlineTest = {path = "../../InlineTest"} +ReTest = {path = "../.."} diff --git a/test/FakePackage/test/runtests.jl b/test/FakePackage/test/runtests.jl index bf4a5df..2128326 100644 --- a/test/FakePackage/test/runtests.jl +++ b/test/FakePackage/test/runtests.jl @@ -57,15 +57,13 @@ RUN = [] @testset "include parent" begin include("included.jl") include(joinpath(@__DIR__, "included.jl")) - if VERSION >= v"1.5" - include(identity, "included.jl") - include(identity, joinpath(@__DIR__, "included.jl")) - end + include(identity, "included.jl") + include(identity, joinpath(@__DIR__, "included.jl")) end end # Included ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ retest(Included) -@test Included.RUN == (VERSION >= v"1.5" ? [0, 0, 0, 0] : [0, 0]) +@test Included.RUN == [0, 0, 0, 0] ReTest.Test.@testset "retest: load" begin @test process_args(()).modules == [ diff --git a/test/Hijack/Project.toml b/test/Hijack/Project.toml index cbb55f0..295e56d 100644 --- a/test/Hijack/Project.toml +++ b/test/Hijack/Project.toml @@ -5,3 +5,6 @@ version = "0.1.0" [deps] ReTest = "e0db7c4e-2690-44b9-bad6-7687da720f89" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[sources] +ReTest = {path = "../.."} diff --git a/test/runtests.jl b/test/runtests.jl index 4110cb3..2f65ca7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -168,18 +168,16 @@ end # N check(N, 1, "i j l1", strict=false) # reachable - if VERSION >= v"1.3" - check(N, reachable(1), " i j k l1 m") - check(N, reachable(1), dry=true, id=false, verbose=3, "", output = """ + check(N, reachable(1), " i j k l1 m") + check(N, reachable(1), dry=true, id=false, verbose=3, "", output = """ i j k l1 m """) - check(N, reachable("l1"), " i l1 m") - check(N, not(reachable("l1")), " i j k") - end + check(N, reachable("l1"), " i l1 m") + check(N, not(reachable("l1")), " i j k") end # * P ........................................................................ @@ -210,10 +208,8 @@ end # P check(P, "B", "a b b|c"; verbose=0, runtests=true) # idem, case-insensitive check(P, r"B", "") # not case-insensitive - if VERSION >= v"1.3" - check(P, "b|c", "a b|c") # "b" is not matched - check(P, "B|C", "a b|c") # idem, case-insensitive - end + check(P, "b|c", "a b|c") # "b" is not matched + check(P, "B|C", "a b|c") # idem, case-insensitive check(P, "d&e", "D&E") check(P, "d&E", "D&E") @@ -244,9 +240,7 @@ end check(Depth, depth(2), [1, 2, 4]) check(Depth, depth(3), [1, 2, 3]) check(Depth, depth.(2:3), [1, 2, 3, 4]) - if VERSION >= v"1.3" - check(Depth, reachable(depth(2)), [1, 2, 3, 4]) - end + check(Depth, reachable(depth(2)), [1, 2, 3, 4]) end @@ -529,19 +523,17 @@ loops 1 sub final """) - if VERSION >= v"1.3" - check(Loops1, reachable("loops 1"), verbose=9, [9, 1, 0, -1, 2, 0, -1]) - check(Loops1, reachable("loops 1"), verbose=9, dry=true, id=false, [], output=raw""" + check(Loops1, reachable("loops 1"), verbose=9, [9, 1, 0, -1, 2, 0, -1]) + check(Loops1, reachable("loops 1"), verbose=9, dry=true, id=false, [], output=raw""" loops 1 "local$(i)" (repeated) sub final """) - check(Loops1, reachable("loops 1"), verbose=9, dry=true, id=false, static=true, [], - output="loops 1") - check(Loops1, reachable(interpolated), verbose=9, dry=true, id=false, [], - output="loops 1") - end + check(Loops1, reachable("loops 1"), verbose=9, dry=true, id=false, static=true, [], + output="loops 1") + check(Loops1, reachable(interpolated), verbose=9, dry=true, id=false, [], + output="loops 1") end # same as Loops1, but the iterator can be statically computed, only descriptions can't @@ -868,8 +860,7 @@ end end @chapter MiscSeed begin - rands = VERSION < v"1.7-" ? [0x24ae, 0x837e] : - [0x12c8, 0x0093] + rands = [0x12c8, 0x0093] MiscSeed.runtests(verbose=0, seed=1) @test MiscSeed.RAND1 === MiscSeed.RAND2 === rands[1] MiscSeed.runtests(verbose=0, seed=2) @@ -1179,9 +1170,8 @@ x hx check(Marks, "a", "-l", -4, :a2, dry=true, verbose=9, id=false, marks=true, [], output="a a1 a2 a3 a4 a5 a6 ✔") check(Marks, "a", "-l", -4, :a2, dry=false, verbose=9, id=false, marks=true, ["a"]) - if VERSION >= v"1.3" - check(Marks, "-l", -4, not(reachable(:a1)), dry=true, verbose=9, id=false, - marks=true, [], output=""" + check(Marks, "-l", -4, not(reachable(:a1)), dry=true, verbose=9, id=false, + marks=true, [], output=""" x hx y1 hx ylabel z1 hx hz1 ylabel @@ -1194,8 +1184,8 @@ x hx z3 hx z4 hx """) - check(Marks, "-l", -4, reachable("x"), not(:ylabel), dry=true, verbose=9, id=false, - marks=true, [], output=""" + check(Marks, "-l", -4, reachable("x"), not(:ylabel), dry=true, verbose=9, id=false, + marks=true, [], output=""" x hx y1 hx ylabel y2 hx @@ -1204,7 +1194,6 @@ x hx z3 hx z4 hx """) - end check(Marks, -4, [r"y1$", "z3"], dry=true, marks=true, id=false, tag=not(:ylabel), verbose=9, [], output=""" x hx @@ -1672,97 +1661,106 @@ end Pkg.develop(PackageSpec(path="..")) # ReTest Pkg.test("Hijack") - if VERSION < v"1.5" - using Revise - @test ReTest.get_revise(nothing) === nothing - - else - - using Hijack - @test !haskey(ReTest.loaded_testmodules, Hijack) + using Hijack + @test !haskey(ReTest.loaded_testmodules, Hijack) + # call load=true first so the next stmt sees the freshly-loaded + # modules at its own (later) compilation world (Julia 1.13+) + process_args((Hijack,), load=true) + invokelatest() do @test first.(process_args((Hijack,), load=true).modules) == [ HijackLoadTrueTests, HijackLoadTrueTests2 ] - Hijack_testmodules = ReTest.loaded_testmodules[Hijack] - @test Hijack_testmodules == [HijackLoadTrueTests, HijackLoadTrueTests2] - @test first.(process_args((Hijack,), load=true).modules) == - [ - HijackLoadTrueTests, - HijackLoadTrueTests2 - ] == Hijack_testmodules # to be sure no module was overwritten - - ReTest.hijack(Hijack, testset=false) # testset: just check that this method - # also accepts this kw (TODO: test it!) + end + Hijack_testmodules = ReTest.loaded_testmodules[Hijack] + @test Hijack_testmodules == [HijackLoadTrueTests, HijackLoadTrueTests2] + @test first.(process_args((Hijack,), load=true).modules) == + [ + HijackLoadTrueTests, + HijackLoadTrueTests2 + ] == Hijack_testmodules # to be sure no module was overwritten + + ReTest.hijack(Hijack, testset=false) # testset: just check that this method + # also accepts this kw (TODO: test it!) + invokelatest() do retest(HijackTests) - @test Hijack.RUN == [1, 5, 4] - empty!(Hijack.RUN) + end + @test Hijack.RUN == [1, 5, 4] + empty!(Hijack.RUN) - @test_throws ErrorException ReTest.hijack(Hijack, :HijackTests2, revise=true) - # Revise not loaded || VERSION < v"1.5" - @test_throws ErrorException ReTest.load(Hijack, "load_revise.jl", - revise=true, parentmodule=Load) + @test_throws ErrorException ReTest.hijack(Hijack, :HijackTests2, revise=true) + # Revise not loaded + @test_throws ErrorException ReTest.load(Hijack, "load_revise.jl", + revise=true, parentmodule=Load) - using Revise ############################### - @test nameof(ReTest.get_revise(nothing)) == :Revise + using Revise ############################### + @test nameof(ReTest.get_revise(nothing)) == :Revise - Test.@testset "load(revise=true)" begin - # big hack, this should belong to the FakePackage chapter, but we can't load - # Revise then, because we @test_throws above for Revise not loaded - @test ReTest.load(Hijack, "../../FakePackage/test/FakePackageTests2.jl", - parentmodule=Load, revise=true) == - Load.AlternateFakePackageTests + Test.@testset "load(revise=true)" begin + # big hack, this should belong to the FakePackage chapter, but we can't load + # Revise then, because we @test_throws above for Revise not loaded + m = ReTest.load(Hijack, "../../FakePackage/test/FakePackageTests2.jl", + parentmodule=Load, revise=true) + invokelatest() do + @test m == Load.AlternateFakePackageTests @test first.(process_args((Load.AlternateFakePackageTests,)).modules) == [Load.AlternateFakePackageTests] + end - # here, Load.HijackTests gets defined - @test ReTest.load(Hijack, "load_revise.jl", parentmodule=Load) == # revise=true - Load.HijackTests - @test Load.load_revise_function() == 1 - @test Load.HijackTests.load_revise_function() == 1 - @test first.(process_args((Load.HijackTests,)).modules) == [Load.HijackTests] - Load.HijackTests.check(Load.HijackTests, [1]) - - HL = ReTest.load(Hijack, "HijackTestsLoad123.jl", parentmodule=Load) - @test HL == [Load.HijackTestsLoad1, Load.HijackTestsLoad2] - @test HL[1].f() == 1 - @test HL[2].f() == 1 - @test Load.HijackTestsLoad3.f() == 1 - - lpf = ReTest.load("Hijack/test/load_path.jl", parentmodule=Load2) - @test Load2.load_path_function() == 1 - @test lpf == Load2.load_path_function + # here, Load.HijackTests gets defined + m = ReTest.load(Hijack, "load_revise.jl", parentmodule=Load) # revise=true + invokelatest() do + @test m == Load.HijackTests end + @test Load.load_revise_function() == 1 + @test Load.HijackTests.load_revise_function() == 1 + @test first.(process_args((Load.HijackTests,)).modules) == [Load.HijackTests] + Load.HijackTests.check(Load.HijackTests, [1]) + + HL = ReTest.load(Hijack, "HijackTestsLoad123.jl", parentmodule=Load) + @test HL == [Load.HijackTestsLoad1, Load.HijackTestsLoad2] + @test HL[1].f() == 1 + @test HL[2].f() == 1 + @test Load.HijackTestsLoad3.f() == 1 + + lpf = ReTest.load("Hijack/test/load_path.jl", parentmodule=Load2) + @test Load2.load_path_function() == 1 + @test lpf == Load2.load_path_function + end - ReTest.hijack(Hijack, :HijackTests2) # revise=true by default + ReTest.hijack(Hijack, :HijackTests2) # revise=true by default + invokelatest() do retest(HijackTests2) - @test Hijack.RUN == [1, 5, 4] - empty!(Hijack.RUN) + end + @test Hijack.RUN == [1, 5, 4] + empty!(Hijack.RUN) - # Submodules - ReTest.hijack("Hijack/test/submodules_tests.jl", :SubMod1, revise=true) + # Submodules + ReTest.hijack("Hijack/test/submodules_tests.jl", :SubMod1, revise=true) + Base.invokelatest() do retest(SubMod1) @test SubMod1.RUN == [1]; empty!(SubMod1.RUN) @test SubMod1.SubModule.RUN == [1]; empty!(SubMod1.SubModule.RUN) @test SubMod1.SubModule.Sub.RUN == [1]; empty!(SubMod1.SubModule.Sub.RUN) + end - ## UPDATING ###### + ## UPDATING ###### - orig(file) = let s = splitext(file) - join([s[1], ".orig", s[2]]) - end - function update_file!(f, file) - cp(file, orig(file), force=true) - write(file, f(chomp(read(file, String)))) - end - restore_file!(file) = mv(orig(file), file, force=true) + orig(file) = let s = splitext(file) + join([s[1], ".orig", s[2]]) + end + function update_file!(f, file) + cp(file, orig(file), force=true) + write(file, f(chomp(read(file, String)))) + end + restore_file!(file) = mv(orig(file), file, force=true) - # EDIT FILE 1 - sub_file = "./Hijack/test/subdir/sub.jl" - update_file!(sub_file) do _ - """ + # EDIT FILE 1 + sub_file = "./Hijack/test/subdir/sub.jl" + update_file!(sub_file) do _ + """ @test true @testset "sub" begin @@ -1771,104 +1769,107 @@ end push!(Hijack.RUN, 2) end """ - end + end - # EDIT FILE 2 - load_revise = "./Hijack/test/load_revise.jl" - update_file!(load_revise) do content - content = replace(content, - "load_revise_function() = 1" => "load_revise_function() = 2") - lines = split(content, r"\n|\r\n") # cf. https://github.com/JuliaLang/julia/pull/20390 - @assert endswith(lines[14], "@test true") - @assert endswith(lines[15], "trace(1)") - insert!(lines, 16, "@test true") - insert!(lines, 17, "trace(2)") - join(lines, '\n') - end + # EDIT FILE 2 + load_revise = "./Hijack/test/load_revise.jl" + update_file!(load_revise) do content + content = replace(content, + "load_revise_function() = 1" => "load_revise_function() = 2") + lines = split(content, r"\n|\r\n") # cf. https://github.com/JuliaLang/julia/pull/20390 + @assert endswith(lines[14], "@test true") + @assert endswith(lines[15], "trace(1)") + insert!(lines, 16, "@test true") + insert!(lines, 17, "trace(2)") + join(lines, '\n') + end - # EDIT FILES 3 & 4 & 5 - mod_revise = "Hijack/test/submodules_tests.jl" - submod_revise = "Hijack/test/submodule.jl" - subsubmod_revise = "Hijack/test/subsubmodule.jl" - for sub in (mod_revise, submod_revise, subsubmod_revise) - update_file!(sub) do content - replace(content, "push!(RUN, 1)" => "push!(RUN, 2)") - end + # EDIT FILES 3 & 4 & 5 + mod_revise = "Hijack/test/submodules_tests.jl" + submod_revise = "Hijack/test/submodule.jl" + subsubmod_revise = "Hijack/test/subsubmodule.jl" + for sub in (mod_revise, submod_revise, subsubmod_revise) + update_file!(sub) do content + replace(content, "push!(RUN, 1)" => "push!(RUN, 2)") end + end - # EDIT FILE 6 - load_hijack123 = "Hijack/test/HijackTestsLoad123.jl" - update_file!(load_hijack123) do content - replace(content, "f() = 1" => "f() = 2") - end + # EDIT FILE 6 + load_hijack123 = "Hijack/test/HijackTestsLoad123.jl" + update_file!(load_hijack123) do content + replace(content, "f() = 1" => "f() = 2") + end - # EDIT FILE 7 - load_path_file = "Hijack/test/load_path.jl" - update_file!(load_path_file) do content - replace(content, "load_path_function() = 1" => "load_path_function() = 2") - end + # EDIT FILE 7 + load_path_file = "Hijack/test/load_path.jl" + update_file!(load_path_file) do content + replace(content, "load_path_function() = 1" => "load_path_function() = 2") + end - Revise.revise() - try - Test.@testset "revise works" begin - retest(HijackTests2) - @test Hijack.RUN == [2, 5, 4] - Load.HijackTests.check(Load.HijackTests, [1, 2]) - @test Load.load_revise_function() == 2 - @test Load.HijackTests.load_revise_function() == 2 - retest(SubMod1) - @test SubMod1.RUN == [2]; empty!(SubMod1.RUN) - @test SubMod1.SubModule.RUN == [2]; empty!(SubMod1.SubModule.RUN) - @test SubMod1.SubModule.Sub.RUN == [2]; empty!(SubMod1.SubModule.Sub.RUN) - - @test Load.HijackTestsLoad1.f() == 2 - @test Load.HijackTestsLoad2.f() == 2 - @test Load.HijackTestsLoad3.f() == 2 - @test Load2.load_path_function() == 2 - end - finally - restore_file!(sub_file) - restore_file!(load_revise) - restore_file!(mod_revise) - restore_file!(submod_revise) - restore_file!(subsubmod_revise) - restore_file!(load_hijack123) - restore_file!(load_path_file) + Revise.revise() + try + Test.@testset "revise works" begin + retest(HijackTests2) + @test Hijack.RUN == [2, 5, 4] + Load.HijackTests.check(Load.HijackTests, [1, 2]) + @test Load.load_revise_function() == 2 + @test Load.HijackTests.load_revise_function() == 2 + retest(SubMod1) + @test SubMod1.RUN == [2]; empty!(SubMod1.RUN) + @test SubMod1.SubModule.RUN == [2]; empty!(SubMod1.SubModule.RUN) + @test SubMod1.SubModule.Sub.RUN == [2]; empty!(SubMod1.SubModule.Sub.RUN) + + @test Load.HijackTestsLoad1.f() == 2 + @test Load.HijackTestsLoad2.f() == 2 + @test Load.HijackTestsLoad3.f() == 2 + @test Load2.load_path_function() == 2 end + finally + restore_file!(sub_file) + restore_file!(load_revise) + restore_file!(mod_revise) + restore_file!(submod_revise) + restore_file!(subsubmod_revise) + restore_file!(load_hijack123) + restore_file!(load_path_file) + end - # These two tests currently just spin forever - if false - @warn "Skipping some hijack tests because they cause Revise to get into an infinite loop" - - # test lazy=true - empty!(Hijack.RUN) - ReTest.hijack("./Hijack/test/lazy.jl", :HijackLazy, lazy=true) - retest(HijackLazy) - @test Hijack.RUN == [1, 3] - - # test lazy=:brutal - empty!(Hijack.RUN) - ReTest.hijack("./Hijack/test/lazy.jl", :HijackBrutal, lazy=:brutal) - retest(HijackBrutal) - @test Hijack.RUN == [3] - end + # These two tests currently just spin forever + if false + @warn "Skipping some hijack tests because they cause Revise to get into an infinite loop" - # test lazy=:wrong + # test lazy=true empty!(Hijack.RUN) - @test_throws ArgumentError ReTest.hijack("./Hijack/test/lazy.jl", :HijackWrong, lazy=:wrong) + ReTest.hijack("./Hijack/test/lazy.jl", :HijackLazy, lazy=true) + retest(HijackLazy) + @test Hijack.RUN == [1, 3] - # test include=:outline + # test lazy=:brutal empty!(Hijack.RUN) - ReTest.hijack("./Hijack/test/testset.jl", :HijackTestset, include=:outline) - retest(HijackTestset) - @test Hijack.RUN == [1, 2, 3] + ReTest.hijack("./Hijack/test/lazy.jl", :HijackBrutal, lazy=:brutal) + retest(HijackBrutal) + @test Hijack.RUN == [3] + end - # test include=:static - empty!(Hijack.RUN) - @test_throws ErrorException ReTest.hijack("./Hijack/test/include_static.jl", :HijackInclude, include=:static, testset=true) - @test_throws ErrorException ReTest.hijack("./Hijack/test/include_static.jl", :HijackInclude, include=:notvalid) - ReTest.hijack("./Hijack/test/include_static.jl", :HijackInclude, include=:static, - include_functions=[:include, :custom_include_function]) + # test lazy=:wrong + empty!(Hijack.RUN) + @test_throws ArgumentError ReTest.hijack("./Hijack/test/lazy.jl", :HijackWrong, lazy=:wrong) + + # test include=:outline + empty!(Hijack.RUN) + ReTest.hijack("./Hijack/test/testset.jl", :HijackTestset, include=:outline) + invokelatest() do + retest(HijackTestset) + end + @test Hijack.RUN == [1, 2, 3] + + # test include=:static + empty!(Hijack.RUN) + @test_throws ErrorException ReTest.hijack("./Hijack/test/include_static.jl", :HijackInclude, include=:static, testset=true) + @test_throws ErrorException ReTest.hijack("./Hijack/test/include_static.jl", :HijackInclude, include=:notvalid) + ReTest.hijack("./Hijack/test/include_static.jl", :HijackInclude, include=:static, + include_functions=[:include, :custom_include_function]) + invokelatest() do check(HijackInclude, dry=true, verbose=9, [], output=""" 1| include_static 2| include_static_included1 1 @@ -1879,6 +1880,6 @@ end 4| include_static_included2 """) retest(HijackInclude) - @test Hijack.RUN == [1, 2, 3, 2, 3] end + @test Hijack.RUN == [1, 2, 3, 2, 3] end diff --git a/test/test_patterns.jl b/test/test_patterns.jl index dc7cc65..aea01f3 100644 --- a/test/test_patterns.jl +++ b/test/test_patterns.jl @@ -16,8 +16,7 @@ end ReTest.tsdepth(::MockTestset) = 1 const basic_patterns = [and(), or(), not(0), interpolated, 0, r"", :label, - depth(2), pass, fail, iter(1)] -VERSION >= v"1.3" && push!(basic_patterns, reachable(1)) + depth(2), pass, fail, iter(1), reachable(1)] @testset "patterns: ==" begin for a = basic_patterns, b = basic_patterns @@ -46,12 +45,10 @@ VERSION >= v"1.3" && push!(basic_patterns, reachable(1)) @test not(a) == not(deepcopy(a)) @test not(a) != not(b) @test not(not(a)) == not(deepcopy(not(a))) - if VERSION >= v"1.3" - @test reachable(a) == reachable(a) - @test reachable(a) == reachable(deepcopy(a)) - @test reachable(a) != reachable(b) - @test reachable(reachable(a)) == reachable(deepcopy(reachable(a))) - end + @test reachable(a) == reachable(a) + @test reachable(a) == reachable(deepcopy(a)) + @test reachable(a) != reachable(b) + @test reachable(reachable(a)) == reachable(deepcopy(reachable(a))) end end end @@ -67,8 +64,7 @@ end end @testset "patterns: not" begin - pats = [or(1, 3), and(1, r"a"), not(1), interpolated, depth(3)] - VERSION >= v"1.3" && push!(pats, reachable("c")) + pats = [or(1, 3), and(1, r"a"), not(1), interpolated, depth(3), reachable("c")] for p ∈ pats @test -p == not(p) end