@@ -29,13 +29,16 @@ using Random: AbstractRNG, default_rng
29
29
using InteractiveUtils: gen_call_with_extracted_types
30
30
using Base: typesplit, remove_linenums!
31
31
using Serialization: Serialization
32
-
33
- const FAIL_FAST = Ref {Bool} (false )
32
+ using Base. ScopedValues: ScopedValue, @with
34
33
35
34
const record_passes = OncePerProcess {Bool} () do
36
35
return Base. get_bool_env (" JULIA_TEST_RECORD_PASSES" , false )
37
36
end
38
37
38
+ const global_fail_fast = OncePerProcess {Bool} () do
39
+ return Base. get_bool_env (" JULIA_TEST_FAILFAST" , false )
40
+ end
41
+
39
42
# -----------------------------------------------------------------------
40
43
41
44
# Backtrace utility functions
@@ -1109,7 +1112,7 @@ if get_testset_depth() != 0
1109
1112
end
1110
1113
```
1111
1114
"""
1112
- function finish end
1115
+ finish (ts :: AbstractTestSet ) = ts
1113
1116
1114
1117
"""
1115
1118
TestSetException
@@ -1144,7 +1147,6 @@ end
1144
1147
A simple fallback test set that throws immediately on a failure.
1145
1148
"""
1146
1149
struct FallbackTestSet <: AbstractTestSet end
1147
- const fallback_testset = FallbackTestSet ()
1148
1150
1149
1151
struct FallbackTestSetException <: Exception
1150
1152
msg:: String
@@ -1161,8 +1163,6 @@ function record(ts::FallbackTestSet, t::Union{Fail, Error})
1161
1163
println (t)
1162
1164
throw (FallbackTestSetException (" There was an error during testing" ))
1163
1165
end
1164
- # We don't need to do anything as we don't record anything
1165
- finish (ts:: FallbackTestSet ) = ts
1166
1166
1167
1167
# -----------------------------------------------------------------------
1168
1168
@@ -1237,7 +1237,7 @@ function DefaultTestSet(desc::AbstractString; verbose::Bool = false, showtiming:
1237
1237
if parent_ts isa DefaultTestSet
1238
1238
failfast = parent_ts. failfast
1239
1239
else
1240
- failfast = false
1240
+ failfast = global_fail_fast ()
1241
1241
end
1242
1242
end
1243
1243
return DefaultTestSet (String (desc):: String ,
@@ -1281,7 +1281,7 @@ function record(ts::DefaultTestSet, t::Union{Fail, Error}; print_result::Bool=TE
1281
1281
end
1282
1282
end
1283
1283
@lock ts. results_lock push! (ts. results, t)
1284
- (FAIL_FAST[] || ts. failfast) && throw (FailFastError ())
1284
+ ts. failfast && throw (FailFastError ())
1285
1285
return t
1286
1286
end
1287
1287
@@ -1408,9 +1408,6 @@ function print_test_results(io::IO, ts::AbstractTestSet, depth_pad=0)
1408
1408
end
1409
1409
end
1410
1410
1411
-
1412
- const TESTSET_PRINT_ENABLE = Ref (true )
1413
-
1414
1411
# Called at the end of a @testset, behaviour depends on whether
1415
1412
# this is a child of another testset, or the "root" testset
1416
1413
function finish (ts:: DefaultTestSet ; print_results:: Bool = TESTSET_PRINT_ENABLE[])
@@ -1836,8 +1833,6 @@ macro testset(args...)
1836
1833
error (" Expected function call, begin/end block or for loop as argument to @testset" )
1837
1834
end
1838
1835
1839
- FAIL_FAST[] = Base. get_bool_env (" JULIA_TEST_FAILFAST" , false )
1840
-
1841
1836
if tests. head === :for
1842
1837
return testset_forloop (args, tests, __source__)
1843
1838
elseif tests. head === :let
@@ -1881,21 +1876,11 @@ function testset_context(args, ex, source)
1881
1876
else
1882
1877
error (" Malformed `let` expression is given" )
1883
1878
end
1884
- reverse! (contexts)
1885
-
1886
1879
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))
1897
1882
end
1898
-
1883
+ ex . args[ 2 ] = test_ex
1899
1884
return esc (ex)
1900
1885
end
1901
1886
@@ -1946,7 +1931,7 @@ function testset_beginend_call(args, tests, source)
1946
1931
else
1947
1932
$ (testsettype)($ desc; $ options... )
1948
1933
end
1949
- push_testset (ts)
1934
+
1950
1935
# we reproduce the logic of guardseed, but this function
1951
1936
# cannot be used as it changes slightly the semantic of @testset,
1952
1937
# by wrapping the body in a function
@@ -1955,26 +1940,27 @@ function testset_beginend_call(args, tests, source)
1955
1940
local ts_rng = get_rng (ts)
1956
1941
local tls_seed = isnothing (ts_rng) ? set_rng! (ts, tls_seed_orig) : ts_rng
1957
1942
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
1963
1950
end
1964
1951
catch err
1965
1952
err isa InterruptException && rethrow ()
1966
1953
# something in the test block threw an error. Count that as an
1967
1954
# error in this test set
1968
1955
trigger_test_failure_break (err)
1969
1956
if is_failfast_error (err)
1970
- get_testset_depth () > 1 ? rethrow () : failfast_print ()
1957
+ get_testset_depth () > 0 ? rethrow () : failfast_print ()
1971
1958
else
1972
1959
record (ts, Error (:nontest_error , Expr (:tuple ), err, Base. current_exceptions (), $ (QuoteNode (source)), nothing ))
1973
1960
end
1974
1961
finally
1975
1962
copy! (default_rng (), default_rng_orig)
1976
1963
copy! (Random. get_tls_seed (), tls_seed_orig)
1977
- pop_testset ()
1978
1964
ret = finish (ts)
1979
1965
end
1980
1966
ret
@@ -2031,59 +2017,41 @@ function testset_forloop(args, testloop, source)
2031
2017
tests = testloop. args[2 ]
2032
2018
blk = quote
2033
2019
_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
2043
2020
ts = if ($ testsettype === $ DefaultTestSet) && $ (isa (source, LineNumberNode))
2044
2021
$ (testsettype)($ desc; source= $ (QuoteNode (source. file)), $ options... , rng= tls_seed)
2045
2022
else
2046
2023
$ (testsettype)($ desc; $ options... )
2047
2024
end
2048
- push_testset (ts)
2049
- first_iteration = false
2050
2025
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
2052
2031
catch err
2053
2032
err isa InterruptException && rethrow ()
2054
2033
# Something in the test block threw an error. Count that as an
2055
2034
# error in this test set
2056
2035
trigger_test_failure_break (err)
2057
2036
if is_failfast_error (err)
2058
- get_testset_depth () > 1 ? rethrow () : failfast_print ()
2037
+ get_testset_depth () > 0 ? rethrow () : failfast_print ()
2059
2038
else
2060
2039
record (ts, Error (:nontest_error , Expr (:tuple ), err, Base. current_exceptions (), $ (QuoteNode (source)), nothing ))
2061
2040
end
2041
+ finally
2042
+ copy! (default_rng (), default_rng_orig)
2043
+ copy! (Random. get_tls_seed (), tls_seed_orig)
2044
+ push! (arr, finish (ts))
2062
2045
end
2063
2046
end
2064
2047
quote
2065
2048
local arr = Vector {Any} ()
2066
- local first_iteration = true
2067
- local ts
2068
2049
local rng_option = get ($ (options), :rng , nothing )
2069
- local finish_errored = false
2070
2050
local default_rng_orig = copy (default_rng ())
2071
2051
local tls_seed_orig = copy (Random. get_tls_seed ())
2072
2052
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))
2087
2055
end
2088
2056
arr
2089
2057
end
@@ -2132,39 +2100,22 @@ end
2132
2100
# -----------------------------------------------------------------------
2133
2101
# Various helper methods for test sets
2134
2102
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
+
2135
2111
"""
2136
2112
get_testset()
2137
2113
2138
2114
Retrieve the active test set from the task's local storage. If no
2139
2115
test set is active, use the fallback default test set.
2140
2116
"""
2141
2117
function 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))
2168
2119
end
2169
2120
2170
2121
"""
@@ -2173,8 +2124,7 @@ end
2173
2124
Return the number of active test sets, not including the default test set
2174
2125
"""
2175
2126
function get_testset_depth ()
2176
- testsets = get (task_local_storage (), :__BASETESTNEXT__ , AbstractTestSet[])
2177
- return length (testsets)
2127
+ something (Base. ScopedValues. get (TESTSET_DEPTH))
2178
2128
end
2179
2129
2180
2130
_args_and_call ((args... , f). .. ; kwargs... ) = (args, kwargs, f (args... ; kwargs... ))
0 commit comments