@@ -29,13 +29,16 @@ using Random: AbstractRNG, default_rng
2929using InteractiveUtils: gen_call_with_extracted_types
3030using Base: typesplit, remove_linenums!
3131using Serialization: Serialization
32-
33- const FAIL_FAST = Ref {Bool} (false )
32+ using Base. ScopedValues: ScopedValue, @with
3433
3534const record_passes = OncePerProcess {Bool} () do
3635 return Base. get_bool_env (" JULIA_TEST_RECORD_PASSES" , false )
3736end
3837
38+ const global_fail_fast = OncePerProcess {Bool} () do
39+ return Base. get_bool_env (" JULIA_TEST_FAILFAST" , false )
40+ end
41+
3942# -----------------------------------------------------------------------
4043
4144# Backtrace utility functions
@@ -1109,7 +1112,7 @@ if get_testset_depth() != 0
11091112end
11101113```
11111114"""
1112- function finish end
1115+ finish (ts :: AbstractTestSet ) = ts
11131116
11141117"""
11151118 TestSetException
@@ -1144,7 +1147,6 @@ end
11441147A simple fallback test set that throws immediately on a failure.
11451148"""
11461149struct FallbackTestSet <: AbstractTestSet end
1147- const fallback_testset = FallbackTestSet ()
11481150
11491151struct FallbackTestSetException <: Exception
11501152 msg:: String
@@ -1161,8 +1163,6 @@ function record(ts::FallbackTestSet, t::Union{Fail, Error})
11611163 println (t)
11621164 throw (FallbackTestSetException (" There was an error during testing" ))
11631165end
1164- # We don't need to do anything as we don't record anything
1165- finish (ts:: FallbackTestSet ) = ts
11661166
11671167# -----------------------------------------------------------------------
11681168
@@ -1237,7 +1237,7 @@ function DefaultTestSet(desc::AbstractString; verbose::Bool = false, showtiming:
12371237 if parent_ts isa DefaultTestSet
12381238 failfast = parent_ts. failfast
12391239 else
1240- failfast = false
1240+ failfast = global_fail_fast ()
12411241 end
12421242 end
12431243 return DefaultTestSet (String (desc):: String ,
@@ -1281,7 +1281,7 @@ function record(ts::DefaultTestSet, t::Union{Fail, Error}; print_result::Bool=TE
12811281 end
12821282 end
12831283 @lock ts. results_lock push! (ts. results, t)
1284- (FAIL_FAST[] || ts. failfast) && throw (FailFastError ())
1284+ ts. failfast && throw (FailFastError ())
12851285 return t
12861286end
12871287
@@ -1408,9 +1408,6 @@ function print_test_results(io::IO, ts::AbstractTestSet, depth_pad=0)
14081408 end
14091409end
14101410
1411-
1412- const TESTSET_PRINT_ENABLE = Ref (true )
1413-
14141411# Called at the end of a @testset, behaviour depends on whether
14151412# this is a child of another testset, or the "root" testset
14161413function finish (ts:: DefaultTestSet ; print_results:: Bool = TESTSET_PRINT_ENABLE[])
@@ -1836,8 +1833,6 @@ macro testset(args...)
18361833 error (" Expected function call, begin/end block or for loop as argument to @testset" )
18371834 end
18381835
1839- FAIL_FAST[] = Base. get_bool_env (" JULIA_TEST_FAILFAST" , false )
1840-
18411836 if tests. head === :for
18421837 return testset_forloop (args, tests, __source__)
18431838 elseif tests. head === :let
@@ -1881,21 +1876,11 @@ function testset_context(args, ex, source)
18811876 else
18821877 error (" Malformed `let` expression is given" )
18831878 end
1884- reverse! (contexts)
1885-
18861879 test_ex = ex. args[2 ]
1887-
1888- ex. args[2 ] = quote
1889- $ (map (contexts) do context
1890- :($ push_testset ($ (ContextTestSet)($ (QuoteNode (context)), $ context; $ options... )))
1891- end ... )
1892- try
1893- $ (test_ex)
1894- finally
1895- $ (map (_-> :($ pop_testset ()), contexts)... )
1896- end
1880+ for context in contexts
1881+ test_ex = :($ Test. @with_testset ($ ContextTestSet ($ (QuoteNode (context)), $ context; $ options... ), $ test_ex))
18971882 end
1898-
1883+ ex . args[ 2 ] = test_ex
18991884 return esc (ex)
19001885end
19011886
@@ -1946,7 +1931,7 @@ function testset_beginend_call(args, tests, source)
19461931 else
19471932 $ (testsettype)($ desc; $ options... )
19481933 end
1949- push_testset (ts)
1934+
19501935 # we reproduce the logic of guardseed, but this function
19511936 # cannot be used as it changes slightly the semantic of @testset,
19521937 # by wrapping the body in a function
@@ -1955,26 +1940,27 @@ function testset_beginend_call(args, tests, source)
19551940 local ts_rng = get_rng (ts)
19561941 local tls_seed = isnothing (ts_rng) ? set_rng! (ts, tls_seed_orig) : ts_rng
19571942 try
1958- # default RNG is reset to its state from last `seed!()` to ease reproduce a failed test
1959- copy! (Random. default_rng (), tls_seed)
1960- copy! (Random. get_tls_seed (), Random. default_rng ())
1961- let
1962- $ (esc (tests))
1943+ @with_testset ts begin
1944+ # default RNG is reset to its state from last `seed!()` to ease reproduce a failed test
1945+ copy! (Random. default_rng (), tls_seed)
1946+ copy! (Random. get_tls_seed (), Random. default_rng ())
1947+ let
1948+ $ (esc (tests))
1949+ end
19631950 end
19641951 catch err
19651952 err isa InterruptException && rethrow ()
19661953 # something in the test block threw an error. Count that as an
19671954 # error in this test set
19681955 trigger_test_failure_break (err)
19691956 if is_failfast_error (err)
1970- get_testset_depth () > 1 ? rethrow () : failfast_print ()
1957+ get_testset_depth () > 0 ? rethrow () : failfast_print ()
19711958 else
19721959 record (ts, Error (:nontest_error , Expr (:tuple ), err, Base. current_exceptions (), $ (QuoteNode (source)), nothing ))
19731960 end
19741961 finally
19751962 copy! (default_rng (), default_rng_orig)
19761963 copy! (Random. get_tls_seed (), tls_seed_orig)
1977- pop_testset ()
19781964 ret = finish (ts)
19791965 end
19801966 ret
@@ -2031,59 +2017,41 @@ function testset_forloop(args, testloop, source)
20312017 tests = testloop. args[2 ]
20322018 blk = quote
20332019 _check_testset ($ testsettype, $ (QuoteNode (testsettype. args[1 ])))
2034- # Trick to handle `break` and `continue` in the test code before
2035- # they can be handled properly by `finally` lowering.
2036- if ! first_iteration
2037- pop_testset ()
2038- finish_errored = true
2039- push! (arr, finish (ts))
2040- finish_errored = false
2041- copy! (default_rng (), tls_seed)
2042- end
20432020 ts = if ($ testsettype === $ DefaultTestSet) && $ (isa (source, LineNumberNode))
20442021 $ (testsettype)($ desc; source= $ (QuoteNode (source. file)), $ options... , rng= tls_seed)
20452022 else
20462023 $ (testsettype)($ desc; $ options... )
20472024 end
2048- push_testset (ts)
2049- first_iteration = false
20502025 try
2051- $ (esc (tests))
2026+ @with_testset ts begin
2027+ # default RNG is reset to its state from last `seed!()` to ease reproduce a failed test
2028+ copy! (Random. default_rng (), tls_seed)
2029+ $ (esc (tests))
2030+ end
20522031 catch err
20532032 err isa InterruptException && rethrow ()
20542033 # Something in the test block threw an error. Count that as an
20552034 # error in this test set
20562035 trigger_test_failure_break (err)
20572036 if is_failfast_error (err)
2058- get_testset_depth () > 1 ? rethrow () : failfast_print ()
2037+ get_testset_depth () > 0 ? rethrow () : failfast_print ()
20592038 else
20602039 record (ts, Error (:nontest_error , Expr (:tuple ), err, Base. current_exceptions (), $ (QuoteNode (source)), nothing ))
20612040 end
2041+ finally
2042+ copy! (default_rng (), default_rng_orig)
2043+ copy! (Random. get_tls_seed (), tls_seed_orig)
2044+ push! (arr, finish (ts))
20622045 end
20632046 end
20642047 quote
20652048 local arr = Vector {Any} ()
2066- local first_iteration = true
2067- local ts
20682049 local rng_option = get ($ (options), :rng , nothing )
2069- local finish_errored = false
20702050 local default_rng_orig = copy (default_rng ())
20712051 local tls_seed_orig = copy (Random. get_tls_seed ())
20722052 local tls_seed = isnothing (rng_option) ? copy (Random. get_tls_seed ()) : rng_option
2073- copy! (Random. default_rng (), tls_seed)
2074- try
2075- let
2076- $ (Expr (:for , Expr (:block , [esc (v) for v in loopvars]. .. ), blk))
2077- end
2078- finally
2079- # Handle `return` in test body
2080- if ! first_iteration && ! finish_errored
2081- pop_testset ()
2082- @assert @isdefined (ts) " Assertion to tell the compiler about the definedness of this variable"
2083- push! (arr, finish (ts))
2084- end
2085- copy! (default_rng (), default_rng_orig)
2086- copy! (Random. get_tls_seed (), tls_seed_orig)
2053+ let
2054+ $ (Expr (:for , Expr (:block , [esc (v) for v in loopvars]. .. ), blk))
20872055 end
20882056 arr
20892057 end
@@ -2132,39 +2100,22 @@ end
21322100# -----------------------------------------------------------------------
21332101# Various helper methods for test sets
21342102
2103+ const CURRENT_TESTSET = ScopedValue {AbstractTestSet} (FallbackTestSet ())
2104+ const TESTSET_DEPTH = ScopedValue {Int} (0 )
2105+ const TESTSET_PRINT_ENABLE = ScopedValue {Bool} (true )
2106+
2107+ macro with_testset (ts, expr)
2108+ :(@with (CURRENT_TESTSET => $ (esc (ts)), TESTSET_DEPTH => get_testset_depth () + 1 , $ (esc (expr))))
2109+ end
2110+
21352111"""
21362112 get_testset()
21372113
21382114Retrieve the active test set from the task's local storage. If no
21392115test set is active, use the fallback default test set.
21402116"""
21412117function get_testset ()
2142- testsets = get (task_local_storage (), :__BASETESTNEXT__ , AbstractTestSet[])
2143- return isempty (testsets) ? fallback_testset : testsets[end ]
2144- end
2145-
2146- """
2147- push_testset(ts::AbstractTestSet)
2148-
2149- Adds the test set to the `task_local_storage`.
2150- """
2151- function push_testset (ts:: AbstractTestSet )
2152- testsets = get (task_local_storage (), :__BASETESTNEXT__ , AbstractTestSet[])
2153- push! (testsets, ts)
2154- setindex! (task_local_storage (), testsets, :__BASETESTNEXT__ )
2155- end
2156-
2157- """
2158- pop_testset()
2159-
2160- Pops the last test set added to the `task_local_storage`. If there are no
2161- active test sets, returns the fallback default test set.
2162- """
2163- function pop_testset ()
2164- testsets = get (task_local_storage (), :__BASETESTNEXT__ , AbstractTestSet[])
2165- ret = isempty (testsets) ? fallback_testset : pop! (testsets)
2166- setindex! (task_local_storage (), testsets, :__BASETESTNEXT__ )
2167- return ret
2118+ something (Base. ScopedValues. get (CURRENT_TESTSET))
21682119end
21692120
21702121"""
@@ -2173,8 +2124,7 @@ end
21732124Return the number of active test sets, not including the default test set
21742125"""
21752126function get_testset_depth ()
2176- testsets = get (task_local_storage (), :__BASETESTNEXT__ , AbstractTestSet[])
2177- return length (testsets)
2127+ something (Base. ScopedValues. get (TESTSET_DEPTH))
21782128end
21792129
21802130_args_and_call ((args... , f). .. ; kwargs... ) = (args, kwargs, f (args... ; kwargs... ))
0 commit comments