diff --git a/stdlib/Test/docs/src/index.md b/stdlib/Test/docs/src/index.md index 5580650219cfa..e8c8952d9168c 100644 --- a/stdlib/Test/docs/src/index.md +++ b/stdlib/Test/docs/src/index.md @@ -149,7 +149,7 @@ f (generic function with 1 method) julia> @testset f(1) Test Summary: | Pass Total f | 1 1 -Test.DefaultTestSet("f", Any[], 1, false, false) +Test.DefaultTestSet("f", Test.FallbackTestSet(), Any[], 1, false, false) ``` This can be used to allow for factorization of test sets, making it easier to run individual diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index f87db1eb75313..d2c978d11df1e 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -974,12 +974,47 @@ along with a summary of the test results. """ mutable struct DefaultTestSet <: AbstractTestSet description::String + parent::Union{Nothing,AbstractTestSet} results::Vector{Any} n_passed::Int anynonpass::Bool verbose::Bool end -DefaultTestSet(desc::AbstractString; verbose::Bool = false) = DefaultTestSet(String(desc)::String, [], 0, false, verbose) + +DefaultTestSet( + desc::AbstractString, + parent::Union{Nothing,AbstractTestSet} = nothing; + verbose::Bool = false, +) = DefaultTestSet(String(desc)::String, parent, [], 0, false, verbose) + +function printdescription(io, ts::DefaultTestSet) + descs = [ts.description] + parent = ts.parent + while parent isa DefaultTestSet + push!(descs, parent.description) + parent = parent.parent + end + printstyled(io, "Test Set:", bold = true) + if length(descs) == 1 + println(io, " ", descs[1]) + else + println(io) + for (i, d) in enumerate(reverse!(descs)) + println(io, " "^2(i - 1), d) + end + end +end + +""" + subtestset(T::Type{<:AbstractTestSet}, description, parent; kwargs...) -> ts::T + +Create a test set `ts` of type `T` with the `description :: String` under the +`parent :: AbstractTestSet` test set. Default implementation ignores `parent`; +i.e., it returns `T(description; kwargs...)`. +""" +subtestset(T::Type{<:AbstractTestSet}, desc, ::Any; kwargs...) = T(desc; kwargs...) +subtestset(::Type{DefaultTestSet}, desc, parent; kwargs...) = + DefaultTestSet(desc, parent; kwargs...) # For a broken result, simply store the result record(ts::DefaultTestSet, t::Broken) = (push!(ts.results, t); t) @@ -990,7 +1025,7 @@ record(ts::DefaultTestSet, t::Pass) = (ts.n_passed += 1; t) # but do not terminate. Print a backtrace. function record(ts::DefaultTestSet, t::Union{Fail, Error}) if TESTSET_PRINT_ENABLE[] - print(ts.description, ": ") + printdescription(stdout, ts) # don't print for interrupted tests if !(t isa Error) || t.test_type !== :test_interrupted print(t) @@ -1333,7 +1368,7 @@ function testset_beginend_call(args, tests, source) ex = quote _check_testset($testsettype, $(QuoteNode(testsettype.args[1]))) local ret - local ts = $(testsettype)($desc; $options...) + local ts = subtestset($testsettype, $desc, get_testset(); $options...) push_testset(ts) # we reproduce the logic of guardseed, but this function # cannot be used as it changes slightly the semantic of @testset, @@ -1415,7 +1450,7 @@ function testset_forloop(args, testloop, source) copy!(RNG, tmprng) end - ts = $(testsettype)($desc; $options...) + ts = subtestset($testsettype, $desc, parent_ts; $options...) push_testset(ts) first_iteration = false try @@ -1435,6 +1470,7 @@ function testset_forloop(args, testloop, source) local oldrng = copy(RNG) Random.seed!(Random.GLOBAL_SEED) local tmprng = copy(RNG) + local parent_ts = get_testset() try let $(Expr(:for, Expr(:block, [esc(v) for v in loopvars]...), blk)) diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index fd0f39a34003e..b0502e9d09e90 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -1025,6 +1025,42 @@ end end end +@testset "parent" begin + @testset "beginend" begin + rootts = Test.get_testset() + @testset "first level" begin + @testset "second level" begin + ts = Test.get_testset() + @test ts.parent.description === "first level" + @test ts.parent.parent === rootts + end + end + end + @testset "forloop" begin + rootts = Test.get_testset() + @testset "i = $i" for i in 1:3 + @testset "j = $j" for j in 1:3 + ts = Test.get_testset() + @test ts.parent.description[1:3] == "i =" + @test ts.parent.parent === rootts + end + end + end +end + +@testset "printdescription" begin + ts = rootts = Test.DefaultTestSet("a") + ts = Test.subtestset(Test.DefaultTestSet, "b", ts) + ts = Test.subtestset(Test.DefaultTestSet, "c", ts) + @test sprint(Test.printdescription, ts) === """ + Test Set: + a + b + c + """ + @test sprint(Test.printdescription, rootts) === "Test Set: a\n" +end + # Issue 20620 @test @inferred(.![true, false]) == [false, true] @test @inferred([3, 4] .- [1, 2] .+ [-2, -2]) == [0, 0]