• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #38182

15 Aug 2025 03:55AM UTC coverage: 77.87% (-0.4%) from 78.28%
#38182

push

local

web-flow
🤖 [master] Bump the SparseArrays stdlib from 30201ab to bb5ecc0 (#59263)

Stdlib: SparseArrays
URL: https://github.com/JuliaSparse/SparseArrays.jl.git
Stdlib branch: main
Julia branch: master
Old commit: 30201ab
New commit: bb5ecc0
Julia version: 1.13.0-DEV
SparseArrays version: 1.13.0
Bump invoked by: @ViralBShah
Powered by:
[BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl)

Diff:
https://github.com/JuliaSparse/SparseArrays.jl/compare/30201abcb...bb5ecc091

```
$ git log --oneline 30201ab..bb5ecc0
bb5ecc0 fast quadratic form for dense matrix, sparse vectors (#640)
34ece87 Extend 3-arg `dot` to generic `HermOrSym` sparse matrices (#643)
095b685 Exclude unintended complex symmetric sparse matrices from 3-arg `dot` (#642)
8049287 Fix signature for 2-arg matrix-matrix `dot` (#641)
cff971d Make cond(::SparseMatrix, 1 / Inf) discoverable from 2-norm error (#629)
```

Co-authored-by: ViralBShah <744411+ViralBShah@users.noreply.github.com>

48274 of 61993 relevant lines covered (77.87%)

9571166.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.29
/stdlib/Test/src/Test.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
"""
4
Simple unit testing functionality:
5

6
* `@test`
7
* `@test_throws`
8

9
All tests belong to a *test set*. There is a default, task-level
10
test set that throws on the first failure. Users can choose to wrap
11
their tests in (possibly nested) test sets that will store results
12
and summarize them at the end of the test set with `@testset`.
13
"""
14
module Test
15

16
export @test, @test_throws, @test_broken, @test_skip,
17
    @test_warn, @test_nowarn,
18
    @test_logs, @test_deprecated
19

20
export @testset
21
export @inferred
22
export detect_ambiguities, detect_unbound_args
23
export GenericString, GenericSet, GenericDict, GenericArray, GenericOrder
24
export TestSetException
25
export TestLogger, LogRecord
26

27
using Random
28
using Random: AbstractRNG, default_rng
29
using InteractiveUtils: gen_call_with_extracted_types
30
using Base: typesplit, remove_linenums!
31
using Serialization: Serialization
32

33
const FAIL_FAST = Ref{Bool}(false)
34

35
const record_passes = OncePerProcess{Bool}() do
36
    return Base.get_bool_env("JULIA_TEST_RECORD_PASSES", false)
58✔
37
end
38

39
#-----------------------------------------------------------------------
40

41
# Backtrace utility functions
42
function ip_has_file_and_func(ip, file, funcs)
43
    return any(fr -> (in_file(fr, file) && fr.func in funcs), StackTraces.lookup(ip))
3,697✔
44
end
45
in_file(frame, file) = string(frame.file) == file
2,252✔
46

47
function test_location(bt, file_ts, file_t)
48
    if (isnothing(file_ts) || isnothing(file_t))
52✔
49
        return macrocall_location(bt, something(file_ts, @__FILE__))
33✔
50
    else
51
        return test_callsite(bt, file_ts, file_t)
19✔
52
    end
53
end
54

55
function test_callsite(bt, file_ts, file_t)
19✔
56
    # We avoid duplicate calls to `StackTraces.lookup`, as it is an expensive call.
57
    # For that, we retrieve locations from lower to higher stack elements
58
    # and only traverse parts of the backtrace which we haven't traversed before.
59
    # The order will always be <internal functions> -> `@test` -> `@testset`.
60
    internal = @something(macrocall_location(bt, @__FILE__), return nothing)
19✔
61
    test = internal - 1 + @something(findfirst(ip -> any(frame -> in_file(frame, file_t), StackTraces.lookup(ip)), @view bt[internal:end]), return nothing)
63✔
62
    testset = test - 1 + @something(macrocall_location(@view(bt[test:end]), file_ts), return nothing)
19✔
63

64
    # If stacktrace locations differ, include frames until the `@testset` appears.
65
    test != testset && return testset
19✔
66
    # `@test` and `@testset` occurred at the same stacktrace location.
67
    # This may happen if `@test` occurred directly in scope of the testset,
68
    # or if `@test` occurred in a function that has been inlined in the testset.
69
    frames = StackTraces.lookup(bt[testset])
27✔
70
    outer_frame = findfirst(frame -> in_file(frame, file_ts) && frame.func == Symbol("macro expansion"), frames)
96✔
71
    isnothing(outer_frame) && return nothing
28✔
72
    # The `@test` call occurred directly in scope of a `@testset`.
73
    # The __source__ from `@test` will be printed in the test message upon failure.
74
    # There is no need to include more frames, but always include at least the internal macrocall location in the stacktrace.
80✔
75
    in_file(frames[outer_frame], file_t) && return internal
14✔
76
    # The `@test` call was inlined, so we still need to include the callsite.
77
    return testset
×
78
end
79

80
macrocall_location(bt, file) = findfirst(ip -> ip_has_file_and_func(ip, file, (Symbol("macro expansion"),)), bt)
548✔
81

82
function scrub_backtrace(bt, file_ts, file_t)
52✔
83
    do_test_ind = findfirst(ip -> ip_has_file_and_func(ip, @__FILE__, (:do_test, :do_test_throws)), bt)
1,100✔
84
    if do_test_ind !== nothing && length(bt) > do_test_ind
52✔
85
        bt = bt[do_test_ind + 1:end]
42✔
86
    end
87
    stop_at = test_location(bt, file_ts, file_t)
52✔
88
    !isnothing(stop_at) && !isempty(bt) && return bt[1:stop_at]
104✔
89
    return bt
×
90
end
91

92
function scrub_exc_stack(stack, file_ts, file_t)
93
    return Any[ (x[1], scrub_backtrace(x[2]::Vector{Union{Ptr{Nothing},Base.InterpreterIP}}, file_ts, file_t)) for x in stack ]
31✔
94
end
95

96
# define most of the test infrastructure without type specialization
97
@nospecialize
98

99
"""
100
    Test.Result
101

102
All tests produce a result object. This object may or may not be
103
stored, depending on whether the test is part of a test set.
104
"""
105
abstract type Result end
106

107
"""
108
    Test.Pass <: Test.Result
109

110
The test condition was true, i.e. the expression evaluated to true or
111
the correct exception was thrown.
112
"""
113
struct Pass <: Result
114
    test_type::Symbol
115
    orig_expr
116
    data
117
    value
118
    source::Union{Nothing,LineNumberNode}
119
    message_only::Bool
120
    function Pass(test_type::Symbol, orig_expr, data, thrown, source::Union{Nothing,LineNumberNode}=nothing, message_only::Bool=false)
1,458✔
121
        return new(test_type, orig_expr, data, thrown, source, message_only)
412,882,567✔
122
    end
123
end
124

125
function Base.show(io::IO, t::Pass)
10✔
126
    printstyled(io, "Test Passed"; bold = true, color=:green)
20✔
127
    if t.test_type === :test_throws
10✔
128
        # The correct type of exception was thrown
129
        if t.message_only
7✔
130
            print(io, "\n     Message: ", t.value)
4✔
131
        else
132
            print(io, "\n      Thrown: ", typeof(t.value))
3✔
133
        end
134
    end
135
end
136

137
"""
138
    Test.Fail <: Test.Result
139

140
The test condition was false, i.e. the expression evaluated to false or
141
the correct exception was not thrown.
142
"""
143
struct Fail <: Result
144
    test_type::Symbol
145
    orig_expr::String
146
    data::Union{Nothing, String}
147
    value::String
148
    context::Union{Nothing, String}
149
    source::LineNumberNode
150
    message_only::Bool
151
    backtrace::Union{Nothing, String}
152
    function Fail(test_type::Symbol, orig_expr, data, value, context, source::LineNumberNode, message_only::Bool, backtrace=nothing)
1,387✔
153
        return new(test_type,
3,486✔
154
            string(orig_expr),
155
            data === nothing ? nothing : string(data),
156
            string(isa(data, Type) ? typeof(value) : value),
157
            context,
158
            source,
159
            message_only,
160
            backtrace)
161
    end
162
end
163

164
# Deprecated fallback constructor without `context` argument (added in Julia 1.9). Remove in Julia 2.0.
165
Fail(test_type::Symbol, orig_expr, data, value, source::LineNumberNode, message_only::Bool=false) =
×
166
    Fail(test_type, orig_expr, data, value, nothing, source, message_only)
167

168
function Base.show(io::IO, t::Fail)
4,552✔
169
    printstyled(io, "Test Failed"; bold=true, color=Base.error_color())
4,552✔
170
    print(io, " at ")
4,552✔
171
    printstyled(io, something(t.source.file, :none), ":", t.source.line, "\n"; bold=true, color=:default)
9,104✔
172
    print(io, "  Expression: ", t.orig_expr)
4,552✔
173
    value, data = t.value, t.data
4,552✔
174
    if t.test_type === :test_throws_wrong
4,552✔
175
        # An exception was thrown, but it was of the wrong type
176
        if t.message_only
5✔
177
            print(io, "\n    Expected: ", data)
4✔
178
            print(io, "\n     Message: ", value)
4✔
179
        else
180
            print(io, "\n    Expected: ", data)
1✔
181
            print(io, "\n      Thrown: ", value)
1✔
182
            print(io, "\n")
1✔
183
            if t.backtrace !== nothing
1✔
184
                # Capture error message and indent to match
185
                join(io, ("      " * line for line in filter!(!isempty, split(t.backtrace, "\n"))), "\n")
1✔
186
            end
187
        end
188
    elseif t.test_type === :test_throws_nothing
4,547✔
189
        # An exception was expected, but no exception was thrown
190
        print(io, "\n    Expected: ", data)
6✔
191
        print(io, "\n  No exception thrown")
6✔
192
    elseif t.test_type === :test
4,541✔
193
        if data !== nothing && t.orig_expr != data
4,541✔
194
            # The test was an expression, so display the term-by-term
195
            # evaluated version as well
196
            print(io, "\n   Evaluated: ", data)
4,150✔
197
        end
198
        if t.context !== nothing
4,541✔
199
            print(io, "\n     Context: ", t.context)
354✔
200
        end
201
    end
202
end
203

204
"""
205
    Test.Error <: Test.Result
206

207
The test condition couldn't be evaluated due to an exception, or
208
it evaluated to something other than a [`Bool`](@ref).
209
In the case of `@test_broken` it is used to indicate that an
210
unexpected `Pass` `Result` occurred.
211
"""
212
struct Error <: Result
213
    test_type::Symbol
214
    orig_expr::String
215
    value::String
216
    backtrace::String
217
    context::Union{Nothing, String}
218
    source::LineNumberNode
219

220
    function Error(test_type::Symbol, orig_expr, value, excs::Union{Base.ExceptionStack,Nothing},
90✔
221
                   source::LineNumberNode, context::Union{Nothing, String}=nothing)
222
        @nospecialize orig_expr value
90✔
223
        bt_str = ""
90✔
224
        if !isnothing(excs)
137✔
225
            if test_type === :test_error
48✔
226
                excs = scrub_exc_stack(excs, nothing, extract_file(source))
22✔
227
            end
228
            if test_type === :test_error || test_type === :nontest_error
74✔
229
                bt_str = try
48✔
230
                    # try the latest world for this, since we might have eval'd new code for show
231
                    # Apply REPL backtrace scrubbing to hide REPL internals, similar to how REPL.jl handles it
232
                    Base.invokelatest(sprint, Base.show_exception_stack, Base.scrub_repl_backtrace(excs); context=stdout)
70✔
233
                catch ex
234
                    "#=ERROR showing exception stack=# " *
2✔
235
                        try
236
                            sprint(Base.showerror, ex, catch_backtrace(); context=stdout)
2✔
237
                        catch
238
                            "of type " * string(typeof(ex))
50✔
239
                        end
240
                end
241
            end
242
        end
243
        value = try # try the latest world for this, since we might have eval'd new code for show
89✔
244
                Base.invokelatest(sprint, show, value, context = :limit => true)
92✔
245
            catch ex
246
                "#=ERROR showing error of type " * string(typeof(value)) * "=# " *
3✔
247
                    try
248
                        sprint(Base.showerror, ex, catch_backtrace(); context=stdout)
3✔
249
                    catch
250
                        "of type " * string(typeof(ex))
92✔
251
                    end
252
            end
253
        return new(test_type,
89✔
254
            string(orig_expr),
255
            value,
256
            bt_str,
257
            context,
258
            source)
259
    end
260

261
    # Internal constructor for creating Error with pre-processed values (used by ContextTestSet)
262
    function Error(test_type::Symbol, orig_expr::String, value::String, backtrace::String, context::Union{Nothing, String}, source::LineNumberNode)
2✔
263
        return new(test_type, orig_expr, value, backtrace, context, source)
4✔
264
    end
265
end
266

267
function Base.show(io::IO, t::Error)
196✔
268
    if t.test_type === :test_interrupted
196✔
269
        printstyled(io, "Interrupted", color=Base.error_color())
×
270
        return
×
271
    end
272
    printstyled(io, "Error During Test"; bold=true, color=Base.error_color())
196✔
273
    print(io, " at ")
196✔
274
    printstyled(io, something(t.source.file, :none), ":", t.source.line, "\n"; bold=true, color=:default)
392✔
275
    if t.test_type === :test_nonbool
196✔
276
        println(io, "  Expression evaluated to non-Boolean")
87✔
277
        println(io, "  Expression: ", t.orig_expr)
87✔
278
        print(  io, "       Value: ", t.value)
87✔
279
    elseif t.test_type === :test_error
109✔
280
        println(io, "  Test threw exception")
40✔
281
        println(io, "  Expression: ", t.orig_expr)
40✔
282
        if t.context !== nothing
40✔
283
            println(io, "     Context: ", t.context)
1✔
284
        end
285
        # Capture error message and indent to match
286
        join(io, ("  " * line for line in filter!(!isempty, split(t.backtrace, "\n"))), "\n")
40✔
287
    elseif t.test_type === :test_unbroken
69✔
288
        # A test that was expected to fail did not
289
        println(io, " Unexpected Pass")
8✔
290
        println(io, " Expression: ", t.orig_expr)
8✔
291
        println(io, " Got correct result, please change to @test if no longer broken.")
8✔
292
    elseif t.test_type === :nontest_error
61✔
293
        # we had an error outside of a @test
294
        println(io, "  Got exception outside of a @test")
61✔
295
        # Capture error message and indent to match
296
        join(io, ("  " * line for line in filter!(!isempty, split(t.backtrace, "\n"))), "\n")
61✔
297
    end
298
end
299

300
"""
301
    Test.Broken <: Test.Result
302

303
The test condition is the expected (failed) result of a broken test,
304
or was explicitly skipped with `@test_skip`.
305
"""
306
struct Broken <: Result
307
    test_type::Symbol
995✔
308
    orig_expr
309
end
310

311
function Base.show(io::IO, t::Broken)
2✔
312
    printstyled(io, "Test Broken\n"; bold=true, color=Base.warn_color())
2✔
313
    if t.test_type === :skipped && !(t.orig_expr === nothing)
2✔
314
        print(io, "  Skipped: ", t.orig_expr)
×
315
    elseif !(t.orig_expr === nothing)
2✔
316
        print(io, "  Expression: ", t.orig_expr)
2✔
317
    end
318
end
319

320
# Types that appear in TestSetException.errors_and_fails we convert eagerly into strings
321
# other types we convert lazily
322
function Serialization.serialize(s::Serialization.AbstractSerializer, t::Pass)
1✔
323
    Serialization.serialize_type(s, typeof(t))
1✔
324
    Serialization.serialize(s, t.test_type)
1✔
325
    Serialization.serialize(s, t.orig_expr === nothing ? nothing : string(t.orig_expr))
2✔
326
    Serialization.serialize(s, t.data === nothing ? nothing : string(t.data))
2✔
327
    Serialization.serialize(s, string(t.value))
1✔
328
    Serialization.serialize(s, t.source === nothing ? nothing : t.source)
2✔
329
    Serialization.serialize(s, t.message_only)
1✔
330
    nothing
1✔
331
end
332

333
function Serialization.serialize(s::Serialization.AbstractSerializer, t::Broken)
1✔
334
    Serialization.serialize_type(s, typeof(t))
1✔
335
    Serialization.serialize(s, t.test_type)
1✔
336
    Serialization.serialize(s, t.orig_expr === nothing ? nothing : string(t.orig_expr))
2✔
337
    nothing
1✔
338
end
339

340

341
#-----------------------------------------------------------------------
342

343
abstract type ExecutionResult end
344

345
struct Returned <: ExecutionResult
346
    value
67,970,973✔
347
    data
348
    source::LineNumberNode
349
end
350

351
struct Threw <: ExecutionResult
352
    exception
175,579✔
353
    current_exceptions::Base.ExceptionStack
354
    source::LineNumberNode
355
end
356

357
function eval_test_comparison(comparison::Expr, quoted::Expr, source::LineNumberNode, negate::Bool=false)
40,560,502✔
358
    comparison.head === :comparison || throw(ArgumentError("$comparison is not a comparison expression"))
40,560,493✔
359
    comparison_args = comparison.args
40,560,452✔
360
    quoted_args = quoted.args
40,560,502✔
361
    n = length(comparison_args)
40,560,471✔
362
    kw_suffix = ""
40,560,456✔
363

364
    res = true
40,560,456✔
365
    i = 1
40,560,452✔
366
    while i < n
91,520,999✔
367
        a, op, b = comparison_args[i], comparison_args[i+1], comparison_args[i+2]
50,963,312✔
368
        if res
50,963,203✔
369
            res = op(a, b)
50,963,202✔
370
        end
371
        quoted_args[i] = a
50,963,548✔
372
        quoted_args[i+2] = b
50,962,856✔
373
        i += 2
50,962,375✔
374
    end
50,962,698✔
375

376
    if negate
40,559,746✔
377
        res = !res
1,520✔
378
        quoted = Expr(:call, :!, quoted)
1,520✔
379
    end
380

381
    Returned(res,
40,559,743✔
382
             # stringify arguments in case of failure, for easy remote printing
383
             res === true ? quoted : sprint(print, quoted, context=(:limit => true)) * kw_suffix,
384
             source)
385
end
386

387
function eval_test_function(func, args, kwargs, quoted_func::Union{Expr,Symbol}, source::LineNumberNode, negate::Bool=false)
25,391,707✔
388
    res = func(args...; kwargs...)
25,743,048✔
389

390
    # Create "Evaluated" expression which looks like the original call but has all of
391
    # the arguments evaluated
392
    kw_suffix = ""
25,391,695✔
393
    if quoted_func === :≈ && !res
25,391,698✔
394
        kw_suffix = " ($(join(["$k=$v" for (k, v) in kwargs], ", ")))"
4✔
395
        quoted_args = args
2✔
396
    elseif isempty(kwargs)
25,391,693✔
397
        quoted_args = args
25,040,360✔
398
    else
399
        kwargs_expr = Expr(:parameters, [Expr(:kw, k, v) for (k, v) in kwargs]...)
351,333✔
400
        quoted_args = [kwargs_expr, args...]
351,333✔
401
    end
402

403
    # Properly render broadcast function call syntax, e.g. `(==).(1, 2)` or `Base.:(==).(1, 2)`.
404
    quoted = if isa(quoted_func, Expr) && quoted_func.head === :. && length(quoted_func.args) == 1
25,391,695✔
405
        Expr(:., quoted_func.args[1], Expr(:tuple, quoted_args...))
2✔
406
    else
407
        Expr(:call, quoted_func, quoted_args...)
50,783,388✔
408
    end
409

410
    if negate
25,391,695✔
411
        res = !res
467,555✔
412
        quoted = Expr(:call, :!, quoted)
467,555✔
413
    end
414

415
    Returned(res,
25,391,695✔
416
             # stringify arguments in case of failure, for easy remote printing
417
             res === true ? quoted : sprint(print, quoted, context=(:limit => true)) * kw_suffix,
418
             source)
419
end
420

421
const comparison_prec = Base.operator_precedence(:(==))
422

423
"""
424
    test_expr!(ex, kws...)
425

426
Preprocess test expressions of function calls with trailing keyword arguments
427
so that e.g. `@test a ≈ b atol=ε` means `@test ≈(a, b, atol=ε)`.
428
"""
429
test_expr!(m, ex) = ex
×
430

431
function test_expr!(m, ex, kws...)
462✔
432
    ex isa Expr && ex.head === :call || @goto fail
462✔
433
    for kw in kws
462✔
434
        kw isa Expr && kw.head === :(=) || @goto fail
477✔
435
        kw.head = :kw
477✔
436
        push!(ex.args, kw)
477✔
437
    end
529✔
438
    return ex
462✔
439
@label fail
440
    error("invalid test macro call: $m $ex $(join(kws," "))")
×
441
end
442

443
# @test - check if the expression evaluates to true
444
"""
445
    @test ex
446
    @test f(args...) key=val ...
447
    @test ex broken=true
448
    @test ex skip=true
449

450
Test that the expression `ex` evaluates to `true`.
451
If executed inside a `@testset`, return a `Pass` `Result` if it does, a `Fail` `Result` if it is
452
`false`, and an `Error` `Result` if it could not be evaluated.
453
If executed outside a `@testset`, throw an exception instead of returning `Fail` or `Error`.
454

455
# Examples
456
```jldoctest
457
julia> @test true
458
Test Passed
459

460
julia> @test [1, 2] + [2, 1] == [3, 3]
461
Test Passed
462
```
463

464
The `@test f(args...) key=val...` form is equivalent to writing
465
`@test f(args..., key=val...)` which can be useful when the expression
466
is a call using infix syntax such as approximate comparisons:
467

468
```jldoctest
469
julia> @test π ≈ 3.14 atol=0.01
470
Test Passed
471
```
472

473
This is equivalent to the uglier test `@test ≈(π, 3.14, atol=0.01)`.
474
It is an error to supply more than one expression unless the first
475
is a call expression and the rest are assignments (`k=v`).
476

477
You can use any key for the `key=val` arguments, except for `broken` and `skip`,
478
which have special meanings in the context of `@test`:
479

480
* `broken=cond` indicates a test that should pass but currently consistently
481
  fails when `cond==true`.  Tests that the expression `ex` evaluates to `false`
482
  or causes an exception.  Returns a `Broken` `Result` if it does, or an `Error`
483
  `Result` if the expression evaluates to `true`.  Regular `@test ex` is
484
  evaluated when `cond==false`.
485
* `skip=cond` marks a test that should not be executed but should be included in
486
  test summary reporting as `Broken`, when `cond==true`.  This can be useful for
487
  tests that intermittently fail, or tests of not-yet-implemented functionality.
488
  Regular `@test ex` is evaluated when `cond==false`.
489

490
# Examples
491

492
```jldoctest
493
julia> @test 2 + 2 ≈ 6 atol=1 broken=true
494
Test Broken
495
  Expression: ≈(2 + 2, 6, atol = 1)
496

497
julia> @test 2 + 2 ≈ 5 atol=1 broken=false
498
Test Passed
499

500
julia> @test 2 + 2 == 5 skip=true
501
Test Broken
502
  Skipped: 2 + 2 == 5
503

504
julia> @test 2 + 2 == 4 skip=false
505
Test Passed
506
```
507

508
!!! compat "Julia 1.7"
509
     The `broken` and `skip` keyword arguments require at least Julia 1.7.
510
"""
511
macro test(ex, kws...)
55,162✔
512
    # Collect the broken/skip keywords and remove them from the rest of keywords
513
    broken = [kw.args[2] for kw in kws if kw.args[1] === :broken]
55,162✔
514
    skip = [kw.args[2] for kw in kws if kw.args[1] === :skip]
55,162✔
515
    kws = filter(kw -> kw.args[1] ∉ (:skip, :broken), kws)
56,194✔
516
    # Validation of broken/skip keywords
517
    for (kw, name) in ((broken, :broken), (skip, :skip))
55,162✔
518
        if length(kw) > 1
110,324✔
519
            error("invalid test macro call: cannot set $(name) keyword multiple times")
×
520
        end
521
    end
110,324✔
522
    if length(skip) > 0 && length(broken) > 0
55,162✔
523
        error("invalid test macro call: cannot set both skip and broken keywords")
×
524
    end
525

526
    # Build the test expression
527
    test_expr!("@test", ex, kws...)
55,162✔
528

529
    result = get_test_result(ex, __source__)
55,162✔
530

531
    ex = Expr(:inert, ex)
55,162✔
532
    result = quote
110,300✔
533
        if $(length(skip) > 0 && esc(skip[1]))
65,518,959✔
534
            record(get_testset(), Broken(:skipped, $ex))
32✔
535
        else
536
            let _do = $(length(broken) > 0 && esc(broken[1])) ? do_broken_test : do_test
65,685,968✔
537
                _do($result, $ex)
65,518,953✔
538
            end
1✔
539
        end
540
    end
541
    return result
55,162✔
542
end
1✔
543

544
"""
545
    @test_broken ex
546
    @test_broken f(args...) key=val ...
1✔
547

548
Indicates a test that should pass but currently consistently fails.
549
Tests that the expression `ex` evaluates to `false` or causes an
550
exception. Returns a `Broken` `Result` if it does, or an `Error` `Result`
551
if the expression evaluates to `true`.  This is equivalent to
552
[`@test ex broken=true`](@ref @test).
553

554
The `@test_broken f(args...) key=val...` form works as for the `@test` macro.
555

556
# Examples
557
```jldoctest
1✔
558
julia> @test_broken 1 == 2
1✔
559
Test Broken
560
  Expression: 1 == 2
561

562
julia> @test_broken 1 == 2 atol=0.1
563
Test Broken
1✔
564
  Expression: ==(1, 2, atol = 0.1)
565
```
566
"""
567
macro test_broken(ex, kws...)
220✔
568
    test_expr!("@test_broken", ex, kws...)
219✔
569
    result = get_test_result(ex, __source__)
219✔
570
    # code to call do_test with execution result and original expr
571
    ex = Expr(:inert, ex)
220✔
572
    return :(do_broken_test($result, $ex))
219✔
573
end
574

575
"""
576
    @test_skip ex
577
    @test_skip f(args...) key=val ...
578

579
Marks a test that should not be executed but should be included in test
580
summary reporting as `Broken`. This can be useful for tests that intermittently
581
fail, or tests of not-yet-implemented functionality.  This is equivalent to
582
[`@test ex skip=true`](@ref @test).
583

584
The `@test_skip f(args...) key=val...` form works as for the `@test` macro.
585

586
# Examples
587
```jldoctest
588
julia> @test_skip 1 == 2
589
Test Broken
590
  Skipped: 1 == 2
591

592
julia> @test_skip 1 == 2 atol=0.1
593
Test Broken
594
  Skipped: ==(1, 2, atol = 0.1)
595
```
596
"""
597
macro test_skip(ex, kws...)
27✔
598
    test_expr!("@test_skip", ex, kws...)
27✔
599
    ex = Expr(:inert, ex)
27✔
600
    testres = :(Broken(:skipped, $ex))
27✔
601
    return :(record(get_testset(), $testres))
27✔
602
end
603

604
function _should_escape_call(@nospecialize ex)
10,940✔
605
    isa(ex, Expr) || return false
11,231✔
606

607
    args = if ex.head === :call
10,649✔
608
        ex.args[2:end]
19,206✔
609
    elseif ex.head === :. && length(ex.args) == 2 && isa(ex.args[2], Expr) && ex.args[2].head === :tuple
995✔
610
        # Support for broadcasted function calls (e.g. `(==).(1, 2)`)
611
        ex.args[2].args
3✔
612
    else
613
        # Expression is not a function call
614
        return false
10,649✔
615
    end
616

617
    # Avoid further processing on calls without any arguments
618
    return length(args) > 0
9,657✔
619
end
620

621
# Escapes all of the positional arguments and keywords of a function such that we can call
622
# the function at runtime.
623
function _escape_call(@nospecialize ex)
9,569✔
624
    if isa(ex, Expr) && ex.head === :call
9,569✔
625
        # Update broadcast comparison calls to the function call syntax
626
        # (e.g. `1 .== 1` becomes `(==).(1, 1)`)
627
        func_str = string(ex.args[1])
9,561✔
628
        escaped_func = if first(func_str) == '.'
9,561✔
629
            esc(Expr(:., Symbol(func_str[2:end])))
6✔
630
        else
631
            esc(ex.args[1])
19,116✔
632
        end
633
        quoted_func = QuoteNode(ex.args[1])
9,561✔
634
        args = ex.args[2:end]
19,121✔
635
    elseif isa(ex, Expr) && ex.head === :. && length(ex.args) == 2 && isa(ex.args[2], Expr) && ex.args[2].head === :tuple
8✔
636
        # Support for broadcasted function calls (e.g. `(==).(1, 2)`)
637
        escaped_func = if isa(ex.args[1], Expr) && ex.args[1].head == :.
7✔
638
            Expr(:call, Expr(:., :Broadcast, QuoteNode(:BroadcastFunction)), esc(ex.args[1]))
2✔
639
        else
640
            Expr(:., esc(ex.args[1]))
8✔
641
        end
642
        quoted_func = QuoteNode(Expr(:., ex.args[1]))
5✔
643
        args = ex.args[2].args
5✔
644
    else
645
        throw(ArgumentError("$ex is not a call expression"))
3✔
646
    end
647

648
    escaped_args = []
9,566✔
649
    escaped_kwargs = []
9,566✔
650

651
    # Positional arguments and keywords that occur before `;`. Note that the keywords are
652
    # being revised into a form we can splat.
653
    for a in args
9,566✔
654
        if isa(a, Expr) && a.head === :parameters
16,441✔
655
            continue
52✔
656
        elseif isa(a, Expr) && a.head === :kw
16,389✔
657
            # Keywords that occur before `;`. Note that the keywords are being revised into
658
            # a form we can splat.
659
            push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a.args[1]), esc(a.args[2])))
570✔
660
        elseif isa(a, Expr) && a.head === :...
15,819✔
661
            push!(escaped_args, Expr(:..., esc(a.args[1])))
18✔
662
        else
663
            push!(escaped_args, esc(a))
15,801✔
664
        end
665
    end
16,441✔
666

667
    # Keywords that occur after ';'
668
    if length(args) > 0 && isa(args[1], Expr) && args[1].head === :parameters
10,039✔
669
        for kw in args[1].args
52✔
670
            if isa(kw, Expr) && kw.head === :kw
58✔
671
                push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(kw.args[1]), esc(kw.args[2])))
48✔
672
            elseif isa(kw, Expr) && kw.head === :...
10✔
673
                push!(escaped_kwargs, Expr(:..., esc(kw.args[1])))
3✔
674
            elseif isa(kw, Expr) && kw.head === :.
7✔
675
                push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(kw.args[2].value), esc(Expr(:., kw.args[1], QuoteNode(kw.args[2].value)))))
2✔
676
            elseif isa(kw, Symbol)
5✔
677
                push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(kw), esc(kw)))
5✔
678
            end
679
        end
58✔
680
    end
681

682
    return (;
9,566✔
683
        func=escaped_func,
684
        args=escaped_args,
685
        kwargs=escaped_kwargs,
686
        quoted_func,
687
    )
688
end
689

690
# An internal function, called by the code generated by the @test
691
# macro to get results of the test expression.
692
# In the special case of a comparison, e.g. x == 5, generate code to
693
# evaluate each term in the comparison individually so the results
694
# can be displayed nicely.
695
function get_test_result(ex, source)
55,381✔
696
    negate = QuoteNode(false)
55,381✔
697
    orig_ex = ex
55,381✔
698
    # Evaluate `not` wrapped functions separately for pretty-printing failures
699
    if isa(ex, Expr) && ex.head === :call && length(ex.args) == 2 && ex.args[1] === :!
55,381✔
700
        negate = QuoteNode(true)
2,542✔
701
        ex = ex.args[2]
2,542✔
702
    end
703
    # Normalize non-dot comparison operator calls to :comparison expressions
704
    is_splat = x -> isa(x, Expr) && x.head === :...
159,383✔
705
    if isa(ex, Expr) && ex.head === :call && length(ex.args) == 3 &&
75,196✔
706
        first(string(ex.args[1])) != '.' && !is_splat(ex.args[2]) && !is_splat(ex.args[3]) &&
707
        (ex.args[1] === :(==) || Base.operator_precedence(ex.args[1]) == comparison_prec)
708
        ex = Expr(:comparison, ex.args[2], ex.args[1], ex.args[3])
42,340✔
709

710
    # Mark <: and >: as :comparison expressions
711
    elseif isa(ex, Expr) && length(ex.args) == 2 &&
13,041✔
712
        !is_splat(ex.args[1]) && !is_splat(ex.args[2]) &&
713
        Base.operator_precedence(ex.head) == comparison_prec
714
        ex = Expr(:comparison, ex.args[1], ex.head, ex.args[2])
482✔
715
    end
716
    if isa(ex, Expr) && ex.head === :comparison
55,381✔
717
        # pass all terms of the comparison to `eval_test_comparison`, as a tuple
718
        escaped_terms = [esc(arg) for arg in ex.args]
44,448✔
719
        quoted_terms = [QuoteNode(arg) for arg in ex.args]
88,896✔
720
        testret = :(eval_test_comparison(
44,448✔
721
            Expr(:comparison, $(escaped_terms...)),
722
            Expr(:comparison, $(quoted_terms...)),
723
            $(QuoteNode(source)),
724
            $negate,
725
        ))
726
    elseif _should_escape_call(ex)
10,933✔
727
        call = _escape_call(ex)
9,552✔
728
        testret = :(eval_test_function(
9,552✔
729
            $(call.func),
730
            ($(call.args...),),
731
            ($(call.kwargs...),),
732
            $(call.quoted_func),
733
            $(QuoteNode(source)),
734
            $negate,
735
        ))
736
    else
737
        ex = Expr(:block, source, esc(orig_ex))
1,381✔
738
        testret = :(Returned($ex, nothing, $(QuoteNode(source))))
1,381✔
739
    end
740
    result = quote
55,381✔
741
        try
65,519,087✔
742
            $testret
81,996,581✔
743
        catch _e
744
            _e isa InterruptException && rethrow()
76✔
745
            Threw(_e, Base.current_exceptions(), $(QuoteNode(source)))
65,519,212✔
746
        end
747
    end
748
    result
55,381✔
749
end
750

751
# An internal function, called by the code generated by the @test
752
# macro to actually perform the evaluation and manage the result.
753
function do_test(result::ExecutionResult, @nospecialize orig_expr)
67,970,730✔
754
    # get_testset() returns the most recently added test set
755
    # We then call record() with this test set and the test result
756
    if isa(result, Returned)
67,970,732✔
757
        # expr, in the case of a comparison, will contain the
758
        # comparison with evaluated values of each term spliced in.
759
        # For anything else, just contains the test expression.
760
        # value is the evaluated value of the whole test expression.
761
        # Ideally it is true, but it may be false or non-Boolean.
762
        value = result.value
67,970,697✔
763
        testres = if isa(value, Bool)
67,970,688✔
764
            # a true value Passes
765
            value ? Pass(:test, orig_expr, result.data, value, result.source) :
67,972,020✔
766
                    Fail(:test, orig_expr, result.data, value, nothing, result.source, false)
767
        else
768
            # If the result is non-Boolean, this counts as an Error
769
            Error(:test_nonbool, orig_expr, value, nothing, result.source, nothing)
67,971,635✔
770
        end
771
    else
772
        # The predicate couldn't be evaluated without throwing an
773
        # exception, so that is an Error and not a Fail
774
        @assert isa(result, Threw)
21✔
775
        testres = Error(:test_error, orig_expr, result.exception, result.current_exceptions, result.source, nothing)
21✔
776
    end
777
    isa(testres, Pass) || trigger_test_failure_break(result)
67,973,005✔
778
    record(get_testset(), testres)
67,971,582✔
779
end
780

781
function do_broken_test(result::ExecutionResult, @nospecialize orig_expr)
407✔
782
    testres = Broken(:test, orig_expr)
407✔
783
    # Assume the test is broken and only change if the result is true
784
    if isa(result, Returned)
407✔
785
        value = result.value
262✔
786
        if isa(value, Bool)
262✔
787
            if value
262✔
788
                testres = Error(:test_unbroken, orig_expr, value, nothing, result.source, nothing)
4✔
789
            end
790
        else
791
            # If the result is non-Boolean, this counts as an Error
792
            testres = Error(:test_nonbool, orig_expr, value, nothing, result.source, nothing)
×
793
        end
794
    end
795
    record(get_testset(), testres)
407✔
796
end
797

798
#-----------------------------------------------------------------------
799

800
"""
801
    @test_throws exception expr
802
    @test_throws extype pattern expr
803

804
Tests that the expression `expr` throws `exception`.
805
The exception may specify either a type,
806
a string, regular expression, or list of strings occurring in the displayed error message,
807
a matching function,
808
or a value (which will be tested for equality by comparing fields).
809

810
In the two-argument form, `@test_throws exception expr`, the `exception` can be a type or a pattern.
811

812
In the three-argument form, `@test_throws extype pattern expr`, both the exception type and
813
a message pattern are tested. The `extype` must be a type, and `pattern` may be
814
a string, regular expression, or list of strings occurring in the displayed error message,
815
a matching function, or a value.
816

817
Note that `@test_throws` does not support a trailing keyword form.
818

819
!!! compat "Julia 1.8"
820
    The ability to specify anything other than a type or a value as `exception` requires Julia v1.8 or later.
821

822
!!! compat "Julia 1.13"
823
    The three-argument form `@test_throws extype pattern expr` requires Julia v1.12 or later.
824

825
# Examples
826
```jldoctest
827
julia> @test_throws BoundsError [1, 2, 3][4]
828
Test Passed
829
      Thrown: BoundsError
830

831
julia> @test_throws DimensionMismatch [1, 2, 3] + [1, 2]
832
Test Passed
833
      Thrown: DimensionMismatch
834

835
julia> @test_throws "Try sqrt(Complex" sqrt(-1)
836
Test Passed
837
     Message: "DomainError with -1.0:\\nsqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x))."
838

839
julia> @test_throws ErrorException "error foo" error("error foo 1")
840
Test Passed
841
      Thrown: ErrorException
842
```
843

844
In the third example, instead of matching a single string it could alternatively have been performed with:
845

846
- `["Try", "Complex"]` (a list of strings)
847
- `r"Try sqrt\\([Cc]omplex"` (a regular expression)
848
- `str -> occursin("complex", str)` (a matching function)
849

850
In the final example, both the exception type (`ErrorException`) and message pattern (`"error foo"`) are tested.
851
"""
852
macro test_throws(extype, ex)
6,620✔
853
    orig_ex = Expr(:inert, ex)
6,620✔
854
    ex = Expr(:block, __source__, esc(ex))
6,620✔
855
    result = quote
6,620✔
856
        try
71,993✔
857
            Returned($ex, nothing, $(QuoteNode(__source__)))
143,364✔
858
        catch _e
859
            if $(esc(extype)) != InterruptException && _e isa InterruptException
71,997✔
860
                rethrow()
×
861
            end
862
            Threw(_e, Base.current_exceptions(), $(QuoteNode(__source__)))
143,771✔
863
        end
864
    end
865
    return :(do_test_throws($result, $orig_ex, $(esc(extype))))
6,620✔
866
end
867

868
macro test_throws(extype, pattern, ex)
10✔
869
    orig_ex = Expr(:inert, ex)
10✔
870
    ex = Expr(:block, __source__, esc(ex))
10✔
871
    result = quote
10✔
872
        try
873
            Returned($ex, nothing, $(QuoteNode(__source__)))
874
        catch _e
875
            if $(esc(extype)) != InterruptException && _e isa InterruptException
876
                rethrow()
877
            end
878
            Threw(_e, Base.current_exceptions(), $(QuoteNode(__source__)))
879
        end
880
    end
881
    return :(do_test_throws($result, $orig_ex, $(esc(extype)), $(esc(pattern))))
10✔
882
end
883

884
const MACROEXPAND_LIKE = Symbol.(("@macroexpand", "@macroexpand1", "macroexpand"))
885

886
function isequalexception(@nospecialize(a), @nospecialize(b))
7,480✔
887
    for fld in 1:nfields(b)
7,543✔
888
        if !isequal(getfield(a, fld), getfield(b, fld))
7,583✔
889
            return false
×
890
        end
891
    end
7,697✔
892
    return true
7,480✔
893
end
894
function isequalexception(a::UndefVarError, b::UndefVarError)
32✔
895
    # Ignore different world ages
896
    return isequal(a.var, b.var) && isequal(a.scope, b.scope)
32✔
897
end
898

899
# An internal function, called by the code generated by @test_throws
900
# to evaluate and catch the thrown exception - if it exists
901
function do_test_throws(result::ExecutionResult, @nospecialize(orig_expr), extype, pattern=nothing)
339,511✔
902
    if isa(result, Threw)
350,830✔
903
        # Check that the right type of exception was thrown
904
        success = false
175,413✔
905
        message_only = false
175,413✔
906
        exc = result.exception
175,413✔
907

908
        # Handle three-argument form (type + pattern)
909
        if pattern !== nothing
175,413✔
910
            # In 3-arg form, first argument must be a type
911
            if !isa(extype, Type)
9✔
912
                testres = Fail(:test_throws_wrong, orig_expr, extype, exc, nothing, result.source, false, "First argument must be an exception type in three-argument form")
1✔
913
                record(get_testset(), testres)
1✔
914
                return
1✔
915
            end
916

917
            # Format combined expected value for display
918
            pattern_str = isa(pattern, AbstractString) ? repr(pattern) :
11✔
919
                         isa(pattern, Function) ? "< match function >" :
920
                         string(pattern)
921
            combined_expected = string(extype) * " with pattern " * pattern_str
8✔
922

923
            # Check both type and pattern
924
            type_success = isa(exc, extype)
8✔
925
            if type_success
8✔
926
                exc_msg = sprint(showerror, exc)
7✔
927
                pattern_success = contains_warn(exc_msg, pattern)
7✔
928
                success = pattern_success
7✔
929
            else
930
                success = false
1✔
931
            end
932
            extype = combined_expected  # Use combined format for all results
8✔
933
        else
934
            # Original two-argument form logic
935
            # NB: Throwing LoadError from macroexpands is deprecated, but in order to limit
936
            # the breakage in package tests we add extra logic here.
937
            from_macroexpand =
192,747✔
938
                orig_expr isa Expr &&
939
                orig_expr.head in (:call, :macrocall) &&
940
                orig_expr.args[1] in MACROEXPAND_LIKE
941
            if isa(extype, Type)
175,404✔
942
                success =
167,375✔
943
                    if from_macroexpand && extype == LoadError && exc isa Exception
944
                        Base.depwarn("macroexpand no longer throws a LoadError so `@test_throws LoadError ...` is deprecated and passed without checking the error type!", :do_test_throws)
2✔
945
                        true
×
946
                    elseif extype == ErrorException && isa(exc, FieldError)
167,373✔
947
                        Base.depwarn(lazy"Using ErrorException to test field access is deprecated; use FieldError instead.", :do_test_throws)
1✔
948
                        true
×
949
                    else
950
                        isa(exc, extype)
334,744✔
951
                    end
952
            elseif isa(extype, Exception) || !isa(exc, Exception)
8,554✔
953
                if extype isa LoadError && !(exc isa LoadError) && typeof(extype.error) == typeof(exc)
7,513✔
954
                    extype = extype.error # deprecated
2✔
955
                end
956
                # Support `UndefVarError(:x)` meaning `UndefVarError(:x, scope)` for any `scope`.
957
                # Retains the behaviour from pre-v1.11 when `UndefVarError` didn't have `scope`.
958
                if isa(extype, UndefVarError) && !isdefined(extype, :scope)
7,513✔
959
                    success = exc isa UndefVarError && exc.var == extype.var
1✔
960
                else isa(exc, typeof(extype))
7,512✔
961
                    success = isequalexception(exc, extype)
7,512✔
962
                end
963
            else
964
                message_only = true
516✔
965
                exc = sprint(showerror, exc)
516✔
966
                success = contains_warn(exc, extype)
516✔
967
                exc = repr(exc)
516✔
968
                if isa(extype, AbstractString)
516✔
969
                    extype = repr(extype)
423✔
970
                elseif isa(extype, Function)
93✔
971
                    extype = "< match function >"
63✔
972
                end
973
            end
974
        end
975
        if success
175,409✔
976
            testres = Pass(:test_throws, orig_expr, extype, exc, result.source, message_only)
175,400✔
977
        else
978
            excs = result.current_exceptions
9✔
979
            bt = scrub_exc_stack(excs, nothing, extract_file(result.source))
9✔
980
            bt_str = try # try the latest world for this, since we might have eval'd new code for show
9✔
981
                Base.invokelatest(sprint, Base.show_exception_stack, bt; context=stdout)
9✔
982
            catch ex
983
                "#=ERROR showing exception stack=# " *
×
984
                    try
985
                        sprint(Base.showerror, ex, catch_backtrace(); context=stdout)
×
986
                    catch
987
                        "of type " * string(typeof(ex))
9✔
988
                    end
989
            end
990
            testres = Fail(:test_throws_wrong, orig_expr, extype, exc, nothing, result.source, message_only, bt_str)
9✔
991
        end
992
    else
993
        # Handle no exception case - need to format extype properly for 3-arg form
994
        if pattern !== nothing
7✔
995
            pattern_str = isa(pattern, AbstractString) ? repr(pattern) :
1✔
996
                         isa(pattern, Function) ? "< match function >" :
997
                         string(pattern)
998
            extype = string(extype) * " with pattern " * pattern_str
1✔
999
        end
1000
        testres = Fail(:test_throws_nothing, orig_expr, extype, nothing, nothing, result.source, false)
7✔
1001
    end
1002
    record(get_testset(), testres)
175,416✔
1003
end
1004

1005
#-----------------------------------------------------------------------
1006
# Test for log messages
1007

1008
# Test for warning messages (deprecated)
1009

1010
contains_warn(output, s::AbstractString) = occursin(s, output)
455✔
1011
contains_warn(output, s::Regex) = occursin(s, output)
29✔
1012
contains_warn(output, s::Function) = s(output)
100✔
1013
contains_warn(output, S::Union{AbstractArray,Tuple}) = all(s -> contains_warn(output, s), S)
23✔
1014

1015
"""
1016
    @test_warn msg expr
1017

1018
Test whether evaluating `expr` results in [`stderr`](@ref) output that contains
1019
the `msg` string or matches the `msg` regular expression.  If `msg` is
1020
a boolean function, tests whether `msg(output)` returns `true`.  If `msg` is a
1021
tuple or array, checks that the error output contains/matches each item in `msg`.
1022
Returns the result of evaluating `expr`.
1023

1024
See also [`@test_nowarn`](@ref) to check for the absence of error output.
1025

1026
Note: Warnings generated by `@warn` cannot be tested with this macro. Use
1027
[`@test_logs`](@ref) instead.
1028
"""
1029
macro test_warn(msg, expr)
16✔
1030
    test_warn_expr(expr, msg)
16✔
1031
end
1032

1033
"""
1034
    @test_nowarn expr
1035

1036
Test whether evaluating `expr` results in empty [`stderr`](@ref) output
1037
(no warnings or other messages).  Returns the result of evaluating `expr`.
1038

1039
Note: The absence of warnings generated by `@warn` cannot be tested
1040
with this macro. Use [`@test_logs`](@ref) instead.
1041
"""
1042
macro test_nowarn(expr)
27✔
1043
    # allow printing the content of `stderr` again to `stderr` here while suppressing it
1044
    # for `@test_warn`. If that shouldn't be used, this could just be `test_warn_expr(expr, #=msg=#isempty)`
1045
    test_warn_expr(expr, function (s)
63✔
1046
        print(stderr, s) # this is helpful for debugging
36✔
1047
        isempty(s)
36✔
1048
    end)
1049
end
1050

1051
function test_warn_expr(@nospecialize(expr), @nospecialize(msg))
43✔
1052
    return :(let fname = tempname()
43✔
1053
        try
5✔
1054
            f = open(fname, "w")
5✔
1055
            stdold = stderr
5✔
1056
            redirect_stderr(f)
10✔
1057
            ret = try
5✔
1058
                # We deliberately don't use the thunk versions of open/redirect
1059
                # to ensure that adding the macro does not change the toplevel-ness
1060
                # of the resulting expression.
1061
                $(esc(expr))
5✔
1062
            finally
1063
                redirect_stderr(stdold)
5✔
1064
                close(f)
5✔
1065
            end
1066
            @test contains_warn(read(fname, String), $(esc(msg)))
1067
            ret
5✔
1068
        finally
1069
            rm(fname, force=true)
5✔
1070
        end
1071
    end)
1072
end
1073

1074
#-----------------------------------------------------------------------
1075

1076
# The AbstractTestSet interface is defined by two methods:
1077
# record(AbstractTestSet, Result)
1078
#   Called by do_test after a test is evaluated
1079
# finish(AbstractTestSet)
1080
#   Called after the test set has been popped from the test set stack
1081
abstract type AbstractTestSet end
1082

1083
"""
1084
    record(ts::AbstractTestSet, res::Result)
1085

1086
Record a result to a testset. This function is called by the `@testset`
1087
infrastructure each time a contained `@test` macro completes, and is given the
1088
test result (which could be an `Error`). This will also be called with an `Error`
1089
if an exception is thrown inside the test block but outside of a `@test` context.
1090
"""
1091
function record end
1092

1093
"""
1094
    finish(ts::AbstractTestSet)
1095

1096
Do any final processing necessary for the given testset. This is called by the
1097
`@testset` infrastructure after a test block executes.
1098

1099
Custom `AbstractTestSet` subtypes should call `record` on their parent (if there
1100
is one) to add themselves to the tree of test results. This might be implemented
1101
as:
1102

1103
```julia
1104
if get_testset_depth() != 0
1105
    # Attach this test set to the parent test set
1106
    parent_ts = get_testset()
1107
    record(parent_ts, self)
1108
    return self
1109
end
1110
```
1111
"""
1112
function finish end
1113

1114
"""
1115
    TestSetException
1116

1117
Thrown when a test set finishes and not all tests passed.
1118
"""
1119
struct TestSetException <: Exception
1120
    pass::Int
313✔
1121
    fail::Int
1122
    error::Int
1123
    broken::Int
1124
    errors_and_fails::Vector{Union{Fail, Error}}
1125
end
1126

1127
function Base.show(io::IO, ex::TestSetException)
14✔
1128
    print(io, "Some tests did not pass: ")
14✔
1129
    print(io, ex.pass,  " passed, ")
14✔
1130
    print(io, ex.fail,  " failed, ")
14✔
1131
    print(io, ex.error, " errored, ")
14✔
1132
    print(io, ex.broken, " broken.")
14✔
1133
end
1134

1135
function Base.showerror(io::IO, ex::TestSetException, bt; backtrace=true)
26✔
1136
    printstyled(io, string(ex), color=Base.error_color())
13✔
1137
end
1138

1139
#-----------------------------------------------------------------------
1140

1141
"""
1142
    FallbackTestSet
1143

1144
A simple fallback test set that throws immediately on a failure.
1145
"""
1146
struct FallbackTestSet <: AbstractTestSet end
1147
const fallback_testset = FallbackTestSet()
1148

1149
struct FallbackTestSetException <: Exception
1150
    msg::String
1151
end
1152

1153
function Base.showerror(io::IO, ex::FallbackTestSetException, bt; backtrace=true)
12✔
1154
    printstyled(io, ex.msg, color=Base.error_color())
6✔
1155
end
1156

1157
# Records nothing, and throws an error immediately whenever a Fail or
1158
# Error occurs. Takes no action in the event of a Pass or Broken result
1159
record(ts::FallbackTestSet, t::Union{Pass, Broken}) = t
6✔
1160
function record(ts::FallbackTestSet, t::Union{Fail, Error})
4✔
1161
    println(t)
8✔
1162
    throw(FallbackTestSetException("There was an error during testing"))
4✔
1163
end
1164
# We don't need to do anything as we don't record anything
1165
finish(ts::FallbackTestSet) = ts
×
1166

1167
#-----------------------------------------------------------------------
1168

1169
"""
1170
    ContextTestSet
1171

1172
Passes test failures through to the parent test set, while adding information
1173
about a context object that is being tested.
1174
"""
1175
struct ContextTestSet <: AbstractTestSet
1176
    parent_ts::AbstractTestSet
31,326✔
1177
    context_name::Union{Symbol, Expr}
1178
    context::Any
1179
end
1180

1181
function ContextTestSet(name::Union{Symbol, Expr}, @nospecialize(context))
431✔
1182
    if (name isa Expr) && (name.head != :tuple)
31,323✔
1183
        error("Invalid syntax: $(name)")
×
1184
    end
1185
    return ContextTestSet(get_testset(), name, context)
31,323✔
1186
end
1187
record(c::ContextTestSet, t) = record(c.parent_ts, t)
62,592✔
1188
function record(c::ContextTestSet, t::Fail)
363✔
1189
    context = string(c.context_name, " = ", c.context)
726✔
1190
    context = t.context === nothing ? context : string(t.context, "\n              ", context)
607✔
1191
    record(c.parent_ts, Fail(t.test_type, t.orig_expr, t.data, t.value, context, t.source, t.message_only))
363✔
1192
end
1193
function record(c::ContextTestSet, t::Error)
2✔
1194
    context = string(c.context_name, " = ", c.context)
3✔
1195
    context = t.context === nothing ? context : string(t.context, "\n              ", context)
2✔
1196
    # Create a new Error with the same data but updated context using internal constructor
1197
    new_error = Error(t.test_type, t.orig_expr, t.value, t.backtrace, context, t.source)
2✔
1198
    record(c.parent_ts, new_error)
2✔
1199
end
1200

1201
#-----------------------------------------------------------------------
1202

1203
"""
1204
    DefaultTestSet
1205

1206
If using the DefaultTestSet, the test results will be recorded. If there
1207
are any `Fail`s or `Error`s, an exception will be thrown only at the end,
1208
along with a summary of the test results.
1209
"""
1210
mutable struct DefaultTestSet <: AbstractTestSet
1211
    description::String
30,463✔
1212
    results::Vector{Any}
1213
    n_passed::Int
1214
    anynonpass::Bool
1215
    verbose::Bool
1216
    showtiming::Bool
1217
    time_start::Float64
1218
    time_end::Union{Float64,Nothing}
1219
    failfast::Bool
1220
    file::Union{String,Nothing}
1221
    rng::Union{Nothing,AbstractRNG}
1222
end
1223
function DefaultTestSet(desc::AbstractString; verbose::Bool = false, showtiming::Bool = true, failfast::Union{Nothing,Bool} = nothing, source = nothing, rng = nothing)
60,926✔
1224
    if isnothing(failfast)
30,466✔
1225
        # pass failfast state into child testsets
1226
        parent_ts = get_testset()
30,460✔
1227
        if parent_ts isa DefaultTestSet
30,460✔
1228
            failfast = parent_ts.failfast
30,186✔
1229
        else
1230
            failfast = false
274✔
1231
        end
1232
    end
1233
    return DefaultTestSet(String(desc)::String, [], 0, false, verbose, showtiming, time(), nothing, failfast, extract_file(source), rng)
30,463✔
1234
end
1235
extract_file(source::LineNumberNode) = extract_file(source.file)
103✔
1236
extract_file(file::Symbol) = string(file)
30,260✔
1237
extract_file(::Nothing) = nothing
255✔
1238

1239
struct FailFastError <: Exception end
1240

1241
# For a broken result, simply store the result
1242
record(ts::DefaultTestSet, t::Broken) = (push!(ts.results, t); t)
964✔
1243
# For a passed result, do not store the result since it uses a lot of memory, unless
1244
# `record_passes()` is true. i.e. set env var `JULIA_TEST_RECORD_PASSES=true` before running any testsets
1245
function record(ts::DefaultTestSet, t::Pass)
14,547✔
1246
    ts.n_passed += 1
137,382,315✔
1247
    if record_passes()
137,382,315✔
1248
        # throw away the captured data so it can be GC-ed
1249
        t_nodata = Pass(t.test_type, t.orig_expr, nothing, t.value, t.source, t.message_only)
205,073,155✔
1250
        push!(ts.results, t_nodata)
137,382,315✔
1251
        return t_nodata
137,382,315✔
1252
    end
1253
    return t
×
1254
end
1255

1256
# For the other result types, immediately print the error message
1257
# but do not terminate. Print a backtrace.
1258
function record(ts::DefaultTestSet, t::Union{Fail, Error}; print_result::Bool=TESTSET_PRINT_ENABLE[])
6,108✔
1259
    if print_result
3,054✔
1260
        println() # add some visual space to separate sequential failures
24✔
1261
        print(ts.description, ": ")
24✔
1262
        # don't print for interrupted tests
1263
        if !(t isa Error) || t.test_type !== :test_interrupted
27✔
1264
            print(t)
45✔
1265
            if !isa(t, Error) # if not gets printed in the show method
24✔
1266
                Base.show_backtrace(stdout, scrub_backtrace(backtrace(), ts.file, extract_file(t.source)); prefix="  ")
42✔
1267
            end
1268
            println()
24✔
1269
        end
1270
    end
1271
    push!(ts.results, t)
5,978✔
1272
    (FAIL_FAST[] || ts.failfast) && throw(FailFastError())
3,054✔
1273
    return t
3,050✔
1274
end
1275

1276
"""
1277
    print_verbose(::AbstractTestSet)::Bool
1278

1279
Whether printing involving this `AbstractTestSet` should be verbose or not.
1280

1281
Defaults to `false`.
1282
"""
1283
function print_verbose end
1284

1285
"""
1286
    results(::AbstractTestSet)
1287

1288
Return an iterator of results aggregated by this `AbstractTestSet`, if any were recorded.
1289

1290
Defaults to the empty tuple.
1291
"""
1292
function results end
1293

1294
print_verbose(ts::DefaultTestSet) = ts.verbose
234✔
1295
results(ts::DefaultTestSet) = ts.results
317✔
1296

1297
# When a DefaultTestSet finishes, it records itself to its parent
1298
# testset, if there is one. This allows for recursive printing of
1299
# the results at the end of the tests
1300
record(ts::DefaultTestSet, t::AbstractTestSet) = push!(ts.results, t)
30,196✔
1301

1302
@specialize
1303

1304
"""
1305
    print_test_errors(::AbstractTestSet)
1306

1307
Prints the errors that were recorded by this `AbstractTestSet` after it
1308
was `finish`ed.
1309
"""
1310
function print_test_errors(ts::AbstractTestSet)
252✔
1311
    for t in results(ts)
252✔
1312
        if isa(t, Error) || isa(t, Fail)
139,387,712✔
1313
            println("Error in testset $(ts.description):")
1,658✔
1314
            show(t)
3,256✔
1315
            println()
1,658✔
1316
        elseif isa(t, AbstractTestSet)
69,692,228✔
1317
            print_test_errors(t)
251✔
1318
        end
1319
    end
69,693,886✔
1320
end
1321

1322
"""
1323
    print_test_results(ts::AbstractTestSet, depth_pad=0)
1324

1325
Print the results of an `AbstractTestSet` as a formatted table.
1326

1327
`depth_pad` refers to how much padding should be added in front of all output.
1328

1329
Called inside of `Test.finish`, if the `finish`ed testset is the topmost
1330
testset.
1331
"""
1332
function print_test_results(ts::AbstractTestSet, depth_pad=0)
25✔
1333
    # Calculate the overall number for each type so each of
1334
    # the test result types are aligned
1335
    tc = get_test_counts(ts)
49✔
1336
    total_pass   = tc.passes + tc.cumulative_passes
25✔
1337
    total_fail   = tc.fails  + tc.cumulative_fails
25✔
1338
    total_error  = tc.errors + tc.cumulative_errors
25✔
1339
    total_broken = tc.broken + tc.cumulative_broken
25✔
1340
    dig_pass   = total_pass   > 0 ? ndigits(total_pass)   : 0
25✔
1341
    dig_fail   = total_fail   > 0 ? ndigits(total_fail)   : 0
25✔
1342
    dig_error  = total_error  > 0 ? ndigits(total_error)  : 0
25✔
1343
    dig_broken = total_broken > 0 ? ndigits(total_broken) : 0
25✔
1344
    total = total_pass + total_fail + total_error + total_broken
25✔
1345
    dig_total = total > 0 ? ndigits(total) : 0
25✔
1346
    # For each category, take max of digits and header width if there are
1347
    # tests of that type
1348
    pass_width   = dig_pass   > 0 ? max(length("Pass"),   dig_pass)   : 0
25✔
1349
    fail_width   = dig_fail   > 0 ? max(length("Fail"),   dig_fail)   : 0
25✔
1350
    error_width  = dig_error  > 0 ? max(length("Error"),  dig_error)  : 0
25✔
1351
    broken_width = dig_broken > 0 ? max(length("Broken"), dig_broken) : 0
25✔
1352
    total_width  = max(textwidth("Total"),  dig_total)
25✔
1353
    duration_width = max(textwidth("Time"), textwidth(tc.duration))
25✔
1354
    # Calculate the alignment of the test result counts by
1355
    # recursively walking the tree of test sets
1356
    align = max(get_alignment(ts, depth_pad), textwidth("Test Summary:"))
25✔
1357
    # Print the outer test set header once
1358
    printstyled(rpad("Test Summary:", align, " "), " |", " "; bold=true)
25✔
1359
    if pass_width > 0
25✔
1360
        printstyled(lpad("Pass", pass_width, " "), "  "; bold=true, color=:green)
17✔
1361
    end
1362
    if fail_width > 0
25✔
1363
        printstyled(lpad("Fail", fail_width, " "), "  "; bold=true, color=Base.error_color())
24✔
1364
    end
1365
    if error_width > 0
25✔
1366
        printstyled(lpad("Error", error_width, " "), "  "; bold=true, color=Base.error_color())
10✔
1367
    end
1368
    if broken_width > 0
25✔
1369
        printstyled(lpad("Broken", broken_width, " "), "  "; bold=true, color=Base.warn_color())
4✔
1370
    end
1371
    if total_width > 0 || total == 0
25✔
1372
        printstyled(lpad("Total", total_width, " "), "  "; bold=true, color=Base.info_color())
50✔
1373
    end
1374
    timing = isdefined(ts, :showtiming) ? ts.showtiming : false
25✔
1375
    if timing
25✔
1376
        printstyled(lpad("Time", duration_width, " "); bold=true)
25✔
1377
    end
1378
    println()
25✔
1379
    # Recursively print a summary at every level
1380
    print_counts(ts, depth_pad, align, pass_width, fail_width, error_width, broken_width, total_width, duration_width, timing)
25✔
1381
    # Print the RNG of the outer testset if there are failures
1382
    if total != total_pass + total_broken
25✔
1383
        rng = get_rng(ts)
12✔
1384
        if !isnothing(rng)
23✔
1385
            println("RNG of the outermost testset: ", rng)
11✔
1386
        end
1387
    end
1388
end
1389

1390

1391
const TESTSET_PRINT_ENABLE = Ref(true)
1392

1393
# Called at the end of a @testset, behaviour depends on whether
1394
# this is a child of another testset, or the "root" testset
1395
function finish(ts::DefaultTestSet; print_results::Bool=TESTSET_PRINT_ENABLE[])
60,420✔
1396
    ts.time_end = time()
30,210✔
1397
    # If we are a nested test set, do not print a full summary
1398
    # now - let the parent test set do the printing
1399
    if get_testset_depth() != 0
30,531✔
1400
        # Attach this test set to the parent test set
1401
        parent_ts = get_testset()
29,935✔
1402
        record(parent_ts, ts)
29,990✔
1403
        return ts
29,935✔
1404
    end
1405
    tc = get_test_counts(ts)
275✔
1406
    total_pass   = tc.passes + tc.cumulative_passes
275✔
1407
    total_fail   = tc.fails  + tc.cumulative_fails
275✔
1408
    total_error  = tc.errors + tc.cumulative_errors
275✔
1409
    total_broken = tc.broken + tc.cumulative_broken
275✔
1410
    total = total_pass + total_fail + total_error + total_broken
275✔
1411

1412
    if print_results
275✔
1413
        print_test_results(ts)
24✔
1414
    end
1415

1416
    # Finally throw an error as we are the outermost test set
1417
    if total != total_pass + total_broken
275✔
1418
        # Get all the error/failures and bring them along for the ride
1419
        efs = filter_errors(ts)
52✔
1420
        throw(TestSetException(total_pass, total_fail, total_error, total_broken, efs))
52✔
1421
    end
1422

1423
    # return the testset so it is returned from the @testset macro
1424
    return ts
223✔
1425
end
1426

1427
# Recursive function that finds the column that the result counts
1428
# can begin at by taking into account the width of the descriptions
1429
# and the amount of indentation. If a test set had no failures, and
1430
# no failures in child test sets, there is no need to include those
1431
# in calculating the alignment
1432
function get_alignment(ts::DefaultTestSet, depth::Int)
296✔
1433
    # The minimum width at this depth is
1434
    ts_width = 2*depth + length(ts.description)
296✔
1435
    # If not verbose and all passing, no need to look at children
1436
    !ts.verbose && !ts.anynonpass && return ts_width
296✔
1437
    # Return the maximum of this width and the minimum width
1438
    # for all children (if they exist)
1439
    isempty(ts.results) && return ts_width
65✔
1440
    child_widths = map(t->get_alignment(t, depth+1), ts.results)
23,845,036✔
1441
    return max(ts_width, maximum(child_widths))
65✔
1442
end
1443
get_alignment(ts, depth::Int) = 0
×
1444

1445
# Recursive function that fetches backtraces for any and all errors
1446
# or failures the testset and its children encountered
1447
function filter_errors(ts::DefaultTestSet)
29,576✔
1448
    efs = Any[]
29,576✔
1449
    for t in ts.results
29,576✔
1450
        if isa(t, DefaultTestSet)
67,438,654✔
1451
            append!(efs, filter_errors(t))
29,410✔
1452
        elseif isa(t, Union{Fail, Error})
67,409,337✔
1453
            push!(efs, t)
1,382✔
1454
        end
1455
    end
67,438,654✔
1456
    return efs
29,576✔
1457
end
1458

1459
"""
1460
    Test.get_rng(ts::AbstractTestSet)::Union{Nothing,AbstractRNG}
1461

1462
Return the global random number generator (RNG) associated to the input testset `ts`.
1463
If no RNG is associated to it, return `nothing`.
1464
"""
1465
get_rng(::AbstractTestSet) = nothing
27✔
1466
get_rng(ts::DefaultTestSet) = ts.rng
9,021✔
1467
"""
1468
    Test.set_rng!(ts::AbstractTestSet, rng::AbstractRNG)::AbstractRNG
1469

1470
Set the global random number generator (RNG) associated to the input testset `ts` to `rng`.
1471
If no RNG is associated to it, do nothing.
1472
In any case, always return the input `rng`.
1473
"""
1474
set_rng!(::AbstractTestSet, rng::AbstractRNG) = rng
27✔
1475
set_rng!(ts::DefaultTestSet, rng::AbstractRNG) = ts.rng = rng
9,005✔
1476

1477
"""
1478
    TestCounts
1479

1480
Holds the state for recursively gathering the results of a test set for display purposes.
1481

1482
Fields:
1483

1484
 * `customized`: Whether the function `get_test_counts` was customized for the `AbstractTestSet`
1485
                 this counts object is for. If a custom method was defined, always pass `true`
1486
                 to the constructor.
1487
 * `passes`: The number of passing `@test` invocations.
1488
 * `fails`: The number of failing `@test` invocations.
1489
 * `errors`: The number of erroring `@test` invocations.
1490
 * `broken`: The number of broken `@test` invocations.
1491
 * `passes`: The cumulative number of passing `@test` invocations.
1492
 * `fails`: The cumulative number of failing `@test` invocations.
1493
 * `errors`: The cumulative number of erroring `@test` invocations.
1494
 * `broken`: The cumulative number of broken `@test` invocations.
1495
 * `duration`: The total duration the `AbstractTestSet` in question ran for, as a formatted `String`.
1496
"""
1497
struct TestCounts
1498
    customized::Bool
59,277✔
1499
    passes::Int
1500
    fails::Int
1501
    errors::Int
1502
    broken::Int
1503
    cumulative_passes::Int
1504
    cumulative_fails::Int
1505
    cumulative_errors::Int
1506
    cumulative_broken::Int
1507
    duration::String
1508
end
1509

1510
""""
1511
    get_test_counts(::AbstractTestSet)::TestCounts
1512

1513
Recursive function that counts the number of test results of each
1514
type directly in the testset, and totals across the child testsets.
1515

1516
Custom `AbstractTestSet` should implement this function to get their totals
1517
counted & displayed with `DefaultTestSet` as well.
1518

1519
If this is not implemented for a custom `TestSet`, the printing falls back to
1520
reporting `x` for failures and `?s` for the duration.
1521
"""
1522
function get_test_counts end
1523

1524
get_test_counts(ts::AbstractTestSet) = TestCounts(false, 0,0,0,0,0,0,0,0, format_duration(ts))
39✔
1525

1526
function get_test_counts(ts::DefaultTestSet)
59,209✔
1527
    passes, fails, errors, broken = ts.n_passed, 0, 0, 0
59,209✔
1528
    # cumulative results
1529
    c_passes, c_fails, c_errors, c_broken = 0, 0, 0, 0
59,209✔
1530
    for t in ts.results
59,209✔
1531
        isa(t, Fail)   && (fails  += 1)
321,962,070✔
1532
        isa(t, Error)  && (errors += 1)
321,962,070✔
1533
        isa(t, Broken) && (broken += 1)
321,962,070✔
1534
        if isa(t, AbstractTestSet)
321,962,070✔
1535
            tc = get_test_counts(t)::TestCounts
58,452✔
1536
            c_passes += tc.passes + tc.cumulative_passes
58,427✔
1537
            c_fails  += tc.fails + tc.cumulative_fails
58,427✔
1538
            c_errors += tc.errors + tc.cumulative_errors
58,427✔
1539
            c_broken += tc.broken + tc.cumulative_broken
58,427✔
1540
        end
1541
    end
321,962,070✔
1542
    duration = format_duration(ts)
59,209✔
1543
    ts.anynonpass = (fails + errors + c_fails + c_errors > 0)
59,209✔
1544
    return TestCounts(true, passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration)
59,209✔
1545
end
1546

1547
"""
1548
    format_duration(::AbstractTestSet)
1549

1550
Return a formatted string for printing the duration the testset ran for.
1551

1552
If not defined, falls back to `"?s"`.
1553
"""
1554
format_duration(::AbstractTestSet) = "?s"
×
1555

1556
function format_duration(ts::DefaultTestSet)
59,209✔
1557
    (; time_start, time_end) = ts
59,209✔
1558
    isnothing(time_end) && return ""
118,418✔
1559

1560
    dur_s = time_end - time_start
59,209✔
1561
    if dur_s < 60
59,209✔
1562
        string(round(dur_s, digits = 1), "s")
58,564✔
1563
    else
1564
        m, s = divrem(dur_s, 60)
645✔
1565
        s = lpad(string(round(s, digits = 1)), 4, "0")
645✔
1566
        string(round(Int, m), "m", s, "s")
645✔
1567
    end
1568
end
1569

1570
print_verbose(::AbstractTestSet) = false
×
1571
results(::AbstractTestSet) = ()
1✔
1572

1573
# Recursive function that prints out the results at each level of
1574
# the tree of test sets
1575
function print_counts(ts::AbstractTestSet, depth, align,
298✔
1576
                      pass_width, fail_width, error_width, broken_width, total_width, duration_width, showtiming)
1577
    # Count results by each type at this level, and recursively
1578
    # through any child test sets
1579
    tc = get_test_counts(ts)
298✔
1580
    fallbackstr = tc.customized ? " " : "x"
298✔
1581
    subtotal = tc.passes + tc.fails + tc.errors + tc.broken +
298✔
1582
               tc.cumulative_passes + tc.cumulative_fails + tc.cumulative_errors + tc.cumulative_broken
1583
    # Print test set header, with an alignment that ensures all
1584
    # the test results appear above each other
1585
    print(rpad(string("  "^depth, ts.description), align, " "), " | ")
298✔
1586

1587
    n_passes = tc.passes + tc.cumulative_passes
298✔
1588
    if n_passes > 0
298✔
1589
        printstyled(lpad(string(n_passes), pass_width, " "), "  ", color=:green)
277✔
1590
    elseif pass_width > 0
21✔
1591
        # No passes at this level, but some at another level
1592
        printstyled(lpad(fallbackstr, pass_width, " "), "  ", color=:green)
9✔
1593
    end
1594

1595
    n_fails = tc.fails + tc.cumulative_fails
298✔
1596
    if n_fails > 0
298✔
1597
        printstyled(lpad(string(n_fails), fail_width, " "), "  ", color=Base.error_color())
114✔
1598
    elseif fail_width > 0
241✔
1599
        # No fails at this level, but some at another level
1600
        printstyled(lpad(fallbackstr, fail_width, " "), "  ", color=Base.error_color())
220✔
1601
    end
1602

1603
    n_errors = tc.errors + tc.cumulative_errors
298✔
1604
    if n_errors > 0
298✔
1605
        printstyled(lpad(string(n_errors), error_width, " "), "  ", color=Base.error_color())
38✔
1606
    elseif error_width > 0
279✔
1607
        # No errors at this level, but some at another level
1608
        printstyled(lpad(fallbackstr, error_width, " "), "  ", color=Base.error_color())
245✔
1609
    end
1610

1611
    n_broken = tc.broken + tc.cumulative_broken
298✔
1612
    if n_broken > 0
298✔
1613
        printstyled(lpad(string(n_broken), broken_width, " "), "  ", color=Base.warn_color())
118✔
1614
    elseif broken_width > 0
239✔
1615
        # None broken at this level, but some at another level
1616
        printstyled(lpad(fallbackstr, broken_width, " "), "  ", color=Base.warn_color())
199✔
1617
    end
1618

1619
    if n_passes == 0 && n_fails == 0 && n_errors == 0 && n_broken == 0
298✔
1620
        total_str = tc.customized ? string(subtotal) : "?"
6✔
1621
        printstyled(lpad(total_str, total_width, " "), "  ", color=Base.info_color())
12✔
1622
    else
1623
        printstyled(lpad(string(subtotal), total_width, " "), "  ", color=Base.info_color())
292✔
1624
    end
1625

1626
    if showtiming
298✔
1627
        printstyled(lpad(tc.duration, duration_width, " "))
298✔
1628
    end
1629
    println()
298✔
1630

1631
    # Only print results at lower levels if we had failures or if the user
1632
    # wants. Requires the given `AbstractTestSet` to have a vector of results
1633
    if ((n_passes + n_broken != subtotal) || print_verbose(ts))
532✔
1634
        for t in results(ts)
66✔
1635
            if isa(t, AbstractTestSet)
23,844,971✔
1636
                print_counts(t, depth + 1, align,
273✔
1637
                    pass_width, fail_width, error_width, broken_width, total_width, duration_width, ts.showtiming)
1638
            end
1639
        end
23,844,971✔
1640
    end
1641
end
1642

1643
#-----------------------------------------------------------------------
1644

1645
function _check_testset(testsettype, testsetname)
29,286✔
1646
    if !(testsettype isa Type && testsettype <: AbstractTestSet)
30,264✔
1647
        error("Expected `$testsetname` to be an AbstractTestSet, it is a ",
1✔
1648
              typeof(testsettype), ". ",
1649
              typeof(testsettype) == String ?
1650
                  """
1651
                  To use `$testsetname` as a testset name, interpolate it into a string, e.g:
1652
                      @testset "\$$testsetname" begin
1653
                          ...
1654
                      end"""
1655
             :
1656
                  ""
1657
            )
1658
    end
1659
end
1660

1661
"""
1662
    @testset [CustomTestSet] [options...] ["description"] begin test_ex end
1663
    @testset [CustomTestSet] [options...] ["description \$v"] for v in itr test_ex end
1664
    @testset [CustomTestSet] [options...] ["description \$v, \$w"] for v in itrv, w in itrw test_ex end
1665
    @testset [CustomTestSet] [options...] ["description"] test_func()
1666
    @testset let v = v, w = w; test_ex; end
1667

1668
# With begin/end or function call
1669

1670
When @testset is used, with begin/end or a single function call, the macro
1671
starts a new test set in which to evaluate the given expression.
1672

1673
If no custom testset type is given it defaults to creating a `DefaultTestSet`.
1674
`DefaultTestSet` records all the results and, if there are any `Fail`s or
1675
`Error`s, throws an exception at the end of the top-level (non-nested) test set,
1676
along with a summary of the test results.
1677

1678
Any custom testset type (subtype of `AbstractTestSet`) can be given and it will
1679
also be used for any nested `@testset` invocations. The given options are only
1680
applied to the test set where they are given. The default test set type
1681
accepts the following options:
1682
- `verbose::Bool`: if `true`, the result summary of the nested testsets is shown even
1683
  when they all pass (the default is `false`).
1684
- `showtiming::Bool`: if `true`, the duration of each displayed testset is shown
1685
  (the default is `true`).
1686
- `failfast::Bool`: if `true`, any test failure or error will cause the testset and any
1687
  child testsets to return immediately (the default is `false`).
1688
  This can also be set globally via the env var `JULIA_TEST_FAILFAST`.
1689
- `rng::Random.AbstractRNG`: use the given random number generator (RNG) as the global one
1690
  for the testset.  `rng` must be `copy!`-able.  This option can be useful to locally
1691
  reproduce stochastic test failures which only depend on the state of the global RNG.
1692

1693
!!! compat "Julia 1.8"
1694
    `@testset test_func()` requires at least Julia 1.8.
1695

1696
!!! compat "Julia 1.9"
1697
    `failfast` requires at least Julia 1.9.
1698

1699
!!! compat "Julia 1.12"
1700
    The `rng` option requires at least Julia 1.12.
1701

1702
The description string accepts interpolation from the loop indices.
1703
If no description is provided, one is constructed based on the variables.
1704
If a function call is provided, its name will be used.
1705
Explicit description strings override this behavior.
1706

1707
By default the `@testset` macro will return the testset object itself, though
1708
this behavior can be customized in other testset types. If a `for` loop is used
1709
then the macro collects and returns a list of the return values of the `finish`
1710
method, which by default will return a list of the testset objects used in
1711
each iteration.
1712

1713
Before the execution of the body of a `@testset`, there is an implicit
1714
call to `copy!(Random.default_rng(), rng)` where `rng` is the RNG of the current task, or
1715
the value of the RNG passed via the `rng` option.
1716
Moreover, after the execution of the body, the state of the global RNG is
1717
restored to what it was before the `@testset`. This is meant to ease
1718
reproducibility in case of failure, and to allow seamless
1719
re-arrangements of `@testset`s regardless of their side-effect on the
1720
global RNG state.
1721

1722
!!! note "RNG of nested testsets"
1723
    Unless changed with the `rng` option, the same RNG is set at the beginning of all
1724
    nested testsets.  The RNG printed to screen when a testset has failures is the global RNG of
1725
    the outermost testset even if inner testsets have different RNGs manually set by the user.
1726

1727
## Examples
1728
```jldoctest; filter = r"trigonometric identities |    4      4  [0-9\\.]+s"
1729
julia> @testset "trigonometric identities" begin
1730
           θ = 2/3*π
1731
           @test sin(-θ) ≈ -sin(θ)
1732
           @test cos(-θ) ≈ cos(θ)
1733
           @test sin(2θ) ≈ 2*sin(θ)*cos(θ)
1734
           @test cos(2θ) ≈ cos(θ)^2 - sin(θ)^2
1735
       end;
1736
Test Summary:            | Pass  Total  Time
1737
trigonometric identities |    4      4  0.2s
1738
```
1739

1740
# `@testset for`
1741

1742
When `@testset for` is used, the macro starts a new test set for each iteration of
1743
the provided loop. The semantics of each test set are otherwise identical to that
1744
of the `begin/end` case (as if used for each loop iteration).
1745

1746
# `@testset let`
1747

1748
When `@testset let` is used, the macro starts a *transparent* test set with
1749
the given object added as a context object to any failing or erroring test contained
1750
therein. This is useful when performing a set of related tests on one larger
1751
object and it is desirable to print this larger object when any of the
1752
individual tests fail. Transparent test sets do not introduce additional levels
1753
of nesting in the test set hierarchy and are passed through directly to the
1754
parent test set (with the context object appended to any failing tests.)
1755

1756
!!! compat "Julia 1.9"
1757
    `@testset let` requires at least Julia 1.9.
1758

1759
!!! compat "Julia 1.10"
1760
    Multiple `let` assignments are supported since Julia 1.10.
1761

1762
!!! compat "Julia 1.13"
1763
    Context is shown when a test errors since Julia 1.13.
1764

1765
# Special implicit world age increment for `@testset begin`
1766

1767
World age inside `@testset begin` increments implicitly after every statement.
1768
This matches the behavior of ordinary toplevel code, but not that of ordinary
1769
`begin/end` blocks, i.e. with respect to world age, `@testset begin` behaves
1770
as if the body of the `begin/end` block was written at toplevel.
1771

1772
## Examples
1773
```jldoctest
1774
julia> @testset let logi = log(im)
1775
           @test imag(logi) == π/2
1776
           @test !iszero(real(logi))
1777
       end
1778
Test Failed at none:3
1779
  Expression: !(iszero(real(logi)))
1780
   Evaluated: !(iszero(0.0))
1781
     Context: logi = 0.0 + 1.5707963267948966im
1782
ERROR: There was an error during testing
1783

1784
julia> @testset let logi = log(im), op = !iszero
1785
           @test imag(logi) == π/2
1786
           @test op(real(logi))
1787
       end
1788
Test Failed at none:3
1789
  Expression: op(real(logi))
1790
   Evaluated: op(0.0)
1791
     Context: logi = 0.0 + 1.5707963267948966im
1792
              op = !iszero
1793
ERROR: There was an error during testing
1794
```
1795
"""
1796
macro testset(args...)
5,385✔
1797
    isempty(args) && error("No arguments to @testset")
5,385✔
1798

1799
    tests = args[end]
5,385✔
1800

1801
    # Determine if a single block or for-loop style
1802
    if !isa(tests,Expr) || (tests.head !== :for && tests.head !== :block && tests.head !== :call && tests.head !== :let)
10,770✔
1803

1804
        error("Expected function call, begin/end block or for loop as argument to @testset")
×
1805
    end
1806

1807
    FAIL_FAST[] = Base.get_bool_env("JULIA_TEST_FAILFAST", false)
5,385✔
1808

1809
    if tests.head === :for
5,385✔
1810
        return testset_forloop(args, tests, __source__)
437✔
1811
    elseif tests.head === :let
4,948✔
1812
        return testset_context(args, tests, __source__)
101✔
1813
    else
1814
        return testset_beginend_call(args, tests, __source__)
4,847✔
1815
    end
1816
end
1817

1818
trigger_test_failure_break(@nospecialize(err)) =
1,456✔
1819
    ccall(:jl_test_failure_breakpoint, Cvoid, (Any,), err)
1820

1821
is_failfast_error(err::FailFastError) = true
×
1822
is_failfast_error(err::LoadError) = is_failfast_error(err.error) # handle `include` barrier
6✔
1823
is_failfast_error(err) = false
×
1824

1825
"""
1826
Generate the code for an `@testset` with a `let` argument.
1827
"""
1828
function testset_context(args, ex, source)
101✔
1829
    desc, testsettype, options = parse_testset_args(args[1:end-1])
101✔
1830
    if desc !== nothing || testsettype !== nothing
101✔
1831
        # Reserve this syntax if we ever want to allow this, but for now,
1832
        # just do the transparent context test set.
1833
        error("@testset with a `let` argument cannot be customized")
×
1834
    end
1835

1836
    let_ex = ex.args[1]
101✔
1837

1838
    if Meta.isexpr(let_ex, :(=))
101✔
1839
        contexts = Any[let_ex.args[1]]
97✔
1840
    elseif Meta.isexpr(let_ex, :block)
4✔
1841
        contexts = Any[]
4✔
1842
        for assign_ex in let_ex.args
4✔
1843
            if Meta.isexpr(assign_ex, :(=))
12✔
1844
                push!(contexts, assign_ex.args[1])
12✔
1845
            else
1846
                error("Malformed `let` expression is given")
×
1847
            end
1848
        end
12✔
1849
    else
1850
        error("Malformed `let` expression is given")
×
1851
    end
1852
    reverse!(contexts)
101✔
1853

1854
    test_ex = ex.args[2]
101✔
1855

1856
    ex.args[2] = quote
101✔
1857
        $(map(contexts) do context
6,380✔
1858
            :($push_testset($(ContextTestSet)($(QuoteNode(context)), $context; $options...)))
109✔
1859
        end...)
1860
        try
6,380✔
1861
            $(test_ex)
6,935✔
1862
        finally
1863
            $(map(_->:($pop_testset()), contexts)...)
6,489✔
1864
        end
1865
    end
1866

1867
    return esc(ex)
101✔
1868
end
1869

1870
function insert_toplevel_latestworld(@nospecialize(tests))
4,845✔
1871
    isa(tests, Expr) || return tests
4,845✔
1872
    (tests.head !== :block) && return tests
4,845✔
1873
    ret = Expr(:block)
4,836✔
1874
    for arg in tests.args
4,836✔
1875
        push!(ret.args, arg)
87,838✔
1876
        if isa(arg, LineNumberNode) ||
131,756✔
1877
          (isa(arg, Expr) && arg.head in (:latestworld, :var"latestworld-if-toplevel"))
1878
            continue
43,920✔
1879
        end
1880
        push!(ret.args, Expr(:var"latestworld-if-toplevel"))
43,918✔
1881
    end
87,838✔
1882
    return ret
4,836✔
1883
end
1884

1885
"""
1886
Generate the code for a `@testset` with a function call or `begin`/`end` argument
1887
"""
1888
function testset_beginend_call(args, tests, source)
4,847✔
1889
    desc, testsettype, options = parse_testset_args(args[1:end-1])
4,847✔
1890
    if desc === nothing
4,845✔
1891
        if tests.head === :call
34✔
1892
            desc = string(tests.args[1]) # use the function name as test name
2✔
1893
        else
1894
            desc = "test set"
32✔
1895
        end
1896
    end
1897
    # If we're at the top level we'll default to DefaultTestSet. Otherwise
1898
    # default to the type of the parent testset
1899
    if testsettype === nothing
4,845✔
1900
        testsettype = :(get_testset_depth() == 0 ? DefaultTestSet : typeof(get_testset()))
4,826✔
1901
    end
1902

1903
    tests = insert_toplevel_latestworld(tests)
4,845✔
1904

1905
    # Generate a block of code that initializes a new testset, adds
1906
    # it to the task local storage, evaluates the test(s), before
1907
    # finally removing the testset and giving it a chance to take
1908
    # action (such as reporting the results)
1909
    ex = quote
9,689✔
1910
        _check_testset($testsettype, $(QuoteNode(testsettype.args[1])))
7,772✔
1911
        local ret
1912
        local ts = if ($testsettype === $DefaultTestSet) && $(isa(source, LineNumberNode))
7,772✔
1913
            $(testsettype)($desc; source=$(QuoteNode(source.file)), $options...)
7,769✔
1914
        else
1915
            $(testsettype)($desc; $options...)
4,017✔
1916
        end
1917
        push_testset(ts)
4,017✔
1918
        # we reproduce the logic of guardseed, but this function
1919
        # cannot be used as it changes slightly the semantic of @testset,
1920
        # by wrapping the body in a function
1921
        local default_rng_orig = copy(default_rng())
4,017✔
1922
        local tls_seed_orig = copy(Random.get_tls_seed())
4,017✔
1923
        local tls_seed = isnothing(get_rng(ts)) ? set_rng!(ts, tls_seed_orig) : get_rng(ts)
4,019✔
1924
        try
4,017✔
1925
            # default RNG is reset to its state from last `seed!()` to ease reproduce a failed test
1926
            copy!(Random.default_rng(), tls_seed)
4,493✔
1927
            copy!(Random.get_tls_seed(), Random.default_rng())
4,017✔
1928
            let
1,901✔
1929
                $(esc(tests))
279,581✔
1930
            end
1931
        catch err
1932
            err isa InterruptException && rethrow()
21✔
1933
            # something in the test block threw an error. Count that as an
1934
            # error in this test set
1935
            trigger_test_failure_break(err)
21✔
1936
            if is_failfast_error(err)
39✔
1937
                get_testset_depth() > 1 ? rethrow() : failfast_print()
2✔
1938
            else
1939
                record(ts, Error(:nontest_error, Expr(:tuple), err, Base.current_exceptions(), $(QuoteNode(source)), nothing))
4,042✔
1940
            end
1941
        finally
1942
            copy!(default_rng(), default_rng_orig)
4,017✔
1943
            copy!(Random.get_tls_seed(), tls_seed_orig)
4,017✔
1944
            pop_testset()
4,017✔
1945
            ret = finish(ts)
4,514✔
1946
        end
1947
        ret
3,975✔
1948
    end
1949
    # preserve outer location if possible
1950
    if tests isa Expr && tests.head === :block && !isempty(tests.args) && tests.args[1] isa LineNumberNode
4,845✔
1951
        ex = Expr(:block, tests.args[1], ex)
4,836✔
1952
    end
1953
    return ex
4,845✔
1954
end
1955

1956
function failfast_print()
4✔
1957
    printstyled("\nFail-fast enabled:"; color = Base.error_color(), bold=true)
8✔
1958
    printstyled(" Fail or Error occurred\n\n"; color = Base.error_color())
4✔
1959
end
1960

1961
"""
1962
Generate the code for a `@testset` with a `for` loop argument
1963
"""
1964
function testset_forloop(args, testloop, source)
437✔
1965
    # Pull out the loop variables. We might need them for generating the
1966
    # description and we'll definitely need them for generating the
1967
    # comprehension expression at the end
1968
    loopvars = Expr[]
437✔
1969
    if testloop.args[1].head === :(=)
437✔
1970
        push!(loopvars, testloop.args[1])
382✔
1971
    elseif testloop.args[1].head === :block
55✔
1972
        for loopvar in testloop.args[1].args
55✔
1973
            push!(loopvars, loopvar)
121✔
1974
        end
121✔
1975
    else
1976
        error("Unexpected argument to @testset")
×
1977
    end
1978

1979
    desc, testsettype, options = parse_testset_args(args[1:end-1])
437✔
1980

1981
    if desc === nothing
437✔
1982
        # No description provided. Generate from the loop variable names
1983
        v = loopvars[1].args[1]
176✔
1984
        desc = Expr(:string, "$v = ", esc(v)) # first variable
176✔
1985
        for l = loopvars[2:end]
176✔
1986
            v = l.args[1]
24✔
1987
            push!(desc.args, ", $v = ")
24✔
1988
            push!(desc.args, esc(v))
24✔
1989
        end
24✔
1990
    end
1991

1992
    if testsettype === nothing
437✔
1993
        testsettype = :(get_testset_depth() == 0 ? DefaultTestSet : typeof(get_testset()))
436✔
1994
    end
1995

1996
    # Uses a similar block as for `@testset`, except that it is
1997
    # wrapped in the outer loop provided by the user
1998
    tests = testloop.args[2]
437✔
1999
    blk = quote
874✔
2000
        _check_testset($testsettype, $(QuoteNode(testsettype.args[1])))
39,621✔
2001
        # Trick to handle `break` and `continue` in the test code before
2002
        # they can be handled properly by `finally` lowering.
2003
        if !first_iteration
19,811✔
2004
            pop_testset()
15,225✔
2005
            finish_errored = true
15,225✔
2006
            push!(arr, finish(ts))
24,172✔
2007
            finish_errored = false
15,225✔
2008
            copy!(default_rng(), tls_seed)
15,564✔
2009
        end
2010
        ts = if ($testsettype === $DefaultTestSet) && $(isa(source, LineNumberNode))
39,621✔
2011
            $(testsettype)($desc; source=$(QuoteNode(source.file)), $options..., rng=tls_seed)
39,594✔
2012
        else
2013
            $(testsettype)($desc; $options...)
19,810✔
2014
        end
2015
        push_testset(ts)
19,810✔
2016
        first_iteration = false
19,810✔
2017
        try
19,810✔
2018
            $(esc(tests))
29,134✔
2019
        catch err
2020
            err isa InterruptException && rethrow()
5✔
2021
            # Something in the test block threw an error. Count that as an
2022
            # error in this test set
2023
            trigger_test_failure_break(err)
4✔
2024
            if is_failfast_error(err)
4✔
2025
                get_testset_depth() > 1 ? rethrow() : failfast_print()
1✔
2026
            else
2027
                record(ts, Error(:nontest_error, Expr(:tuple), err, Base.current_exceptions(), $(QuoteNode(source)), nothing))
26,883✔
2028
            end
2029
        end
2030
    end
2031
    quote
437✔
2032
        local arr = Vector{Any}()
4,507✔
2033
        local first_iteration = true
4,507✔
2034
        local ts
2035
        local rng_option = get($(options), :rng, nothing)
4,507✔
2036
        local finish_errored = false
4,507✔
2037
        local default_rng_orig = copy(default_rng())
4,507✔
2038
        local tls_seed_orig = copy(Random.get_tls_seed())
4,507✔
2039
        local tls_seed = isnothing(rng_option) ? copy(Random.get_tls_seed()) : rng_option
4,507✔
2040
        copy!(Random.default_rng(), tls_seed)
4,667✔
2041
        try
4,507✔
2042
            let
2✔
2043
                $(Expr(:for, Expr(:block, [esc(v) for v in loopvars]...), blk))
4,679✔
2044
            end
2045
        finally
2046
            # Handle `return` in test body
2047
            if !first_iteration && !finish_errored
4,587✔
2048
                pop_testset()
4,585✔
2049
                @assert @isdefined(ts) "Assertion to tell the compiler about the definedness of this variable"
4,587✔
2050
                push!(arr, finish(ts))
8,329✔
2051
            end
2052
            copy!(default_rng(), default_rng_orig)
4,587✔
2053
            copy!(Random.get_tls_seed(), tls_seed_orig)
4,766✔
2054
        end
2055
        arr
4,504✔
2056
    end
2057
end
2058

2059
"""
2060
Parse the arguments to the `@testset` macro to pull out the description,
2061
Testset Type, and options. Generally this should be called with all the macro
2062
arguments except the last one, which is the test expression itself.
2063
"""
2064
function parse_testset_args(args)
5,087✔
2065
    desc = nothing
5,385✔
2066
    testsettype = nothing
5,385✔
2067
    options = :(Dict{Symbol, Any}())
5,385✔
2068
    for arg in args
5,385✔
2069
        # a standalone symbol is assumed to be the test set we should use
2070
        # the same is true for a symbol that's not exported from a module
2071
        if isa(arg, Symbol) || Base.isexpr(arg, :.)
5,130✔
2072
            if testsettype !== nothing
22✔
2073
                msg = """Multiple testset types provided to @testset. \
1✔
2074
                    This is deprecated and may error in the future."""
2075
                Base.depwarn(msg, :testset_multiple_testset_types; force=true)
1✔
2076
            end
2077
            testsettype = esc(arg)
24✔
2078
        # a string is the description
2079
        elseif isa(arg, AbstractString) || (isa(arg, Expr) && arg.head === :string)
5,097✔
2080
            if desc !== nothing
5,074✔
2081
                msg = """Multiple descriptions provided to @testset. \
1✔
2082
                    This is deprecated and may error in the future."""
2083
                Base.depwarn(msg, :testset_multiple_descriptions; force=true)
1✔
2084
            end
2085
            desc = esc(arg)
5,073✔
2086
        # an assignment is an option
2087
        elseif isa(arg, Expr) && arg.head === :(=)
13✔
2088
            # we're building up a Dict literal here
2089
            key = Expr(:quote, arg.args[1])
13✔
2090
            push!(options.args, Expr(:call, :(=>), key, esc(arg.args[2])))
13✔
2091
        else
2092
            error("Unexpected argument $arg to @testset")
×
2093
        end
2094
    end
5,124✔
2095

2096
    (desc, testsettype, options)
5,383✔
2097
end
2098

2099
#-----------------------------------------------------------------------
2100
# Various helper methods for test sets
2101

2102
"""
2103
    get_testset()
2104

2105
Retrieve the active test set from the task's local storage. If no
2106
test set is active, use the fallback default test set.
2107
"""
2108
function get_testset()
68,330,789✔
2109
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
68,330,786✔
2110
    return isempty(testsets) ? fallback_testset : testsets[end]
68,330,381✔
2111
end
2112

2113
"""
2114
    push_testset(ts::AbstractTestSet)
2115

2116
Adds the test set to the `task_local_storage`.
2117
"""
2118
function push_testset(ts::AbstractTestSet)
61,836✔
2119
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
61,836✔
2120
    push!(testsets, ts)
61,836✔
2121
    setindex!(task_local_storage(), testsets, :__BASETESTNEXT__)
61,836✔
2122
end
2123

2124
"""
2125
    pop_testset()
2126

2127
Pops the last test set added to the `task_local_storage`. If there are no
2128
active test sets, returns the fallback default test set.
2129
"""
2130
function pop_testset()
61,835✔
2131
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
61,835✔
2132
    ret = isempty(testsets) ? fallback_testset : pop!(testsets)
123,670✔
2133
    setindex!(task_local_storage(), testsets, :__BASETESTNEXT__)
61,835✔
2134
    return ret
61,835✔
2135
end
2136

2137
"""
2138
    get_testset_depth()
2139

2140
Return the number of active test sets, not including the default test set
2141
"""
2142
function get_testset_depth()
120,974✔
2143
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
120,974✔
2144
    return length(testsets)
120,974✔
2145
end
2146

2147
_args_and_call((args..., f)...; kwargs...) = (args, kwargs, f(args...; kwargs...))
784✔
2148
_materialize_broadcasted(f, args...) = Broadcast.materialize(Broadcast.broadcasted(f, args...))
74✔
2149

2150
"""
2151
    @inferred [AllowedType] f(x)
2152

2153
Tests that the call expression `f(x)` returns a value of the same type inferred by the
2154
compiler. It is useful to check for type stability.
2155

2156
`f(x)` can be any call expression. Returns the result of `f(x)` if the types match, and an
2157
`Error` `Result` if it finds different types.
2158

2159
Optionally, `AllowedType` relaxes the test, by making it pass when either the type of `f(x)`
2160
matches the inferred type modulo `AllowedType`, or when the return type is a subtype of
2161
`AllowedType`. This is useful when testing type stability of functions returning a small
2162
union such as `Union{Nothing, T}` or `Union{Missing, T}`.
2163

2164
```jldoctest; setup = :(using InteractiveUtils; using Base: >), filter = r"begin\\n(.|\\n)*end"
2165
julia> f(a) = a > 1 ? 1 : 1.0
2166
f (generic function with 1 method)
2167

2168
julia> typeof(f(2))
2169
Int64
2170

2171
julia> @code_warntype f(2)
2172
MethodInstance for f(::Int64)
2173
  from f(a) @ Main none:1
2174
Arguments
2175
  #self#::Core.Const(f)
2176
  a::Int64
2177
Body::UNION{FLOAT64, INT64}
2178
1 ─ %1 = :>::Core.Const(>)
2179
│   %2 = (%1)(a, 1)::Bool
2180
└──      goto #3 if not %2
2181
2 ─      return 1
2182
3 ─      return 1.0
2183

2184
julia> @inferred f(2)
2185
ERROR: return type Int64 does not match inferred return type Union{Float64, Int64}
2186
[...]
2187

2188
julia> @inferred max(1, 2)
2189
2
2190

2191
julia> g(a) = a < 10 ? missing : 1.0
2192
g (generic function with 1 method)
2193

2194
julia> @inferred g(20)
2195
ERROR: return type Float64 does not match inferred return type Union{Missing, Float64}
2196
[...]
2197

2198
julia> @inferred Missing g(20)
2199
1.0
2200

2201
julia> h(a) = a < 10 ? missing : f(a)
2202
h (generic function with 1 method)
2203

2204
julia> @inferred Missing h(20)
2205
ERROR: return type Int64 does not match inferred return type Union{Missing, Float64, Int64}
2206
[...]
2207
```
2208
"""
2209
macro inferred(ex)
1,508✔
2210
    _inferred(ex, __module__)
1,508✔
2211
end
2212
macro inferred(allow, ex)
33✔
2213
    _inferred(ex, __module__, allow)
33✔
2214
end
2215
function _inferred(ex, mod, allow = :(Union{}))
3,049✔
2216
    if Meta.isexpr(ex, :ref)
3,049✔
2217
        ex = Expr(:call, :getindex, ex.args...)
30✔
2218
    end
2219
    Meta.isexpr(ex, :call)|| error("@inferred requires a call expression")
1,541✔
2220
    farg = ex.args[1]
1,541✔
2221
    if isa(farg, Symbol) && farg !== :.. && first(string(farg)) == '.'
2,856✔
2222
        farg = Symbol(string(farg)[2:end])
64✔
2223
        ex = Expr(:call, GlobalRef(Test, :_materialize_broadcasted),
64✔
2224
            farg, ex.args[2:end]...)
2225
    end
2226
    result = let ex = ex
1,541✔
2227
        quote
5,434✔
2228
            let allow = $(esc(allow))
2229
                allow isa Type || throw(ArgumentError("@inferred requires a type as second argument"))
2230
                $(if any(@nospecialize(a)->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex.args)
7,955✔
2231
                    # Has keywords
2232
                    args = gensym()
139✔
2233
                    kwargs = gensym()
139✔
2234
                    quote
139✔
2235
                        $(esc(args)), $(esc(kwargs)), result = $(esc(Expr(:call, _args_and_call, ex.args[2:end]..., ex.args[1])))
2236
                        inftype = $(gen_call_with_extracted_types(mod, Base.infer_return_type, :($(ex.args[1])($(args)...; $(kwargs)...)); is_source_reflection = false))
2237
                    end
2238
                else
2239
                    # No keywords
2240
                    quote
2,943✔
2241
                        args = ($([esc(ex.args[i]) for i = 2:length(ex.args)]...),)
2242
                        result = $(esc(ex.args[1]))(args...)
2243
                        inftype = Base.infer_return_type($(esc(ex.args[1])), Base.typesof(args...))
2244
                    end
2245
                end)
2246
                rettype = result isa Type ? Type{result} : typeof(result)
2247
                rettype <: allow || rettype == typesplit(inftype, allow) || error("return type $rettype does not match inferred return type $inftype")
2248
                result
2249
            end
2250
        end
2251
    end
2252
    return remove_linenums!(result)
1,541✔
2253
end
2254

2255
function is_in_mods(m::Module, recursive::Bool, mods)
610,059✔
2256
    while true
610,059✔
2257
        m in mods && return true
2,629,203✔
2258
        recursive || return false
1,146,168✔
2259
        p = parentmodule(m)
366,992✔
2260
        p === m && return false
366,992✔
2261
        m = p
192,034✔
2262
    end
192,034✔
2263
end
2264

2265
"""
2266
    detect_ambiguities(mod1, mod2...; recursive=false,
2267
                                      ambiguous_bottom=false,
2268
                                      allowed_undefineds=nothing)
2269

2270
Return a vector of `(Method,Method)` pairs of ambiguous methods
2271
defined in the specified modules.
2272
Use `recursive=true` to test in all submodules.
2273

2274
`ambiguous_bottom` controls whether ambiguities triggered only by
2275
`Union{}` type parameters are included; in most cases you probably
2276
want to set this to `false`. See [`Base.isambiguous`](@ref).
2277

2278
See [`Test.detect_unbound_args`](@ref) for an explanation of
2279
`allowed_undefineds`.
2280

2281
!!! compat "Julia 1.8"
2282
    `allowed_undefineds` requires at least Julia 1.8.
2283
"""
2284
function detect_ambiguities(mods::Module...;
36✔
2285
                            recursive::Bool = false,
2286
                            ambiguous_bottom::Bool = false,
2287
                            allowed_undefineds = nothing)
2288
    @nospecialize
18✔
2289
    ambs = Set{Tuple{Method,Method}}()
18✔
2290
    mods = collect(mods)::Vector{Module}
24✔
2291
    function sortdefs(m1::Method, m2::Method)
18✔
2292
        ord12 = cmp(m1.file, m2.file)
18✔
2293
        if ord12 == 0
18✔
2294
            ord12 = cmp(m1.line, m2.line)
18✔
2295
        end
2296
        return ord12 <= 0 ? (m1, m2) : (m2, m1)
27✔
2297
    end
2298
    function examine(mt::Core.MethodTable)
36✔
2299
        for m in Base.MethodList(mt)
36✔
2300
            is_in_mods(parentmodule(m), recursive, mods) || continue
498,647✔
2301
            world = Base.get_world_counter()
27,441✔
2302
            ambig = Ref{Int32}(0)
27,441✔
2303
            ms = Base._methods_by_ftype(m.sig, nothing, -1, world, true, Ref(typemin(UInt)), Ref(typemax(UInt)), ambig)::Vector
27,441✔
2304
            ambig[] == 0 && continue
27,441✔
2305
            for match2 in ms
743✔
2306
                match2 = match2::Core.MethodMatch
6,439✔
2307
                m2 = match2.method
6,439✔
2308
                 if !(m === m2 || Base.morespecific(m2.sig, m.sig))
12,135✔
2309
                    if Base.isambiguous(m, m2; ambiguous_bottom)
848✔
2310
                        push!(ambs, sortdefs(m, m2))
27✔
2311
                    end
2312
                end
2313
            end
6,439✔
2314
        end
498,647✔
2315
    end
2316
    examine(Core.methodtable)
18✔
2317
    return collect(ambs)
18✔
2318
end
2319

2320
"""
2321
    detect_unbound_args(mod1, mod2...; recursive=false, allowed_undefineds=nothing)
2322

2323
Return a vector of `Method`s which may have unbound type parameters.
2324
Use `recursive=true` to test in all submodules.
2325

2326
By default, any undefined symbols trigger a warning. This warning can
2327
be suppressed by supplying a collection of `GlobalRef`s for which
2328
the warning can be skipped. For example, setting
2329

2330
```
2331
allowed_undefineds = Set([GlobalRef(Base, :active_repl),
2332
                          GlobalRef(Base, :active_repl_backend)])
2333
```
2334

2335
would suppress warnings about `Base.active_repl` and
2336
`Base.active_repl_backend`.
2337

2338
!!! compat "Julia 1.8"
2339
    `allowed_undefineds` requires at least Julia 1.8.
2340
"""
2341
function detect_unbound_args(mods...;
8✔
2342
                             recursive::Bool = false,
2343
                             allowed_undefineds=nothing)
2344
    @nospecialize mods
4✔
2345
    ambs = Set{Method}()
4✔
2346
    mods = collect(mods)::Vector{Module}
4✔
2347
    function examine(mt::Core.MethodTable)
8✔
2348
        for m in Base.MethodList(mt)
8✔
2349
            is_in_mods(parentmodule(m), recursive, mods) || continue
111,412✔
2350
            has_unbound_vars(m.sig) || continue
36,109✔
2351
            tuple_sig = Base.unwrap_unionall(m.sig)::DataType
35✔
2352
            if Base.isvatuple(tuple_sig)
35✔
2353
                params = tuple_sig.parameters[1:(end - 1)]
39✔
2354
                tuple_sig = Base.rewrap_unionall(Tuple{params...}, m.sig)
54✔
2355
                world = Base.get_world_counter()
27✔
2356
                mf = ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tuple_sig, nothing, world)
27✔
2357
                if mf !== nothing && mf !== m && mf.sig <: tuple_sig
27✔
2358
                    continue
23✔
2359
                end
2360
            end
2361
            push!(ambs, m)
12✔
2362
        end
111,412✔
2363
    end
2364
    examine(Core.methodtable)
4✔
2365
    return collect(ambs)
4✔
2366
end
2367

2368
function has_unbound_vars(@nospecialize sig)
2369
    while sig isa UnionAll
18,072✔
2370
        var = sig.var
3,003✔
2371
        sig = sig.body
3,003✔
2372
        if !Core.Compiler.constrains_param(var, sig, #=covariant=#true, #=type_constrains=#true)
3,003✔
2373
            return true
35✔
2374
        end
2375
    end
2,968✔
2376
    return false
18,037✔
2377
end
2378

2379

2380
"""
2381
The `GenericString` can be used to test generic string APIs that program to
2382
the `AbstractString` interface, in order to ensure that functions can work
2383
with string types besides the standard `String` type.
2384
"""
2385
struct GenericString <: AbstractString
2386
    string::AbstractString
60,648✔
2387
end
2388
Base.ncodeunits(s::GenericString) = ncodeunits(s.string)::Int
138,966✔
2389
Base.codeunit(s::GenericString) = codeunit(s.string)::Type{<:Union{UInt8, UInt16, UInt32}}
21✔
2390
Base.codeunit(s::GenericString, i::Integer) = codeunit(s.string, i)::Union{UInt8, UInt16, UInt32}
144✔
2391
Base.isvalid(s::GenericString, i::Integer) = isvalid(s.string, i)::Bool
1,722,310✔
2392
Base.iterate(s::GenericString, i::Integer=1) = iterate(s.string, i)::Union{Nothing,Tuple{AbstractChar,Int}}
12,428✔
2393
Base.reverse(s::GenericString) = GenericString(reverse(s.string))
75✔
2394
Base.reverse(s::SubString{GenericString}) =
75✔
2395
    GenericString(typeof(s.string)(reverse(String(s))))
2396

2397
"""
2398
The `GenericSet` can be used to test generic set APIs that program to
2399
the `AbstractSet` interface, in order to ensure that functions can work
2400
with set types besides the standard `Set` and `BitSet` types.
2401
"""
2402
struct GenericSet{T} <: AbstractSet{T}
2403
    s::AbstractSet{T}
10✔
2404
end
2405

2406
"""
2407
The `GenericDict` can be used to test generic dict APIs that program to
2408
the `AbstractDict` interface, in order to ensure that functions can work
2409
with associative types besides the standard `Dict` type.
2410
"""
2411
struct GenericDict{K,V} <: AbstractDict{K,V}
2412
    s::AbstractDict{K,V}
10✔
2413
end
2414

2415
for G in (GenericSet, GenericDict)
2416
    @eval begin
2417
        Base.iterate(s::$G, state...) = iterate(s.s, state...)
16,783✔
2418
    end
2419
    for f in (:isempty, :length)
2420
        @eval begin
2421
            Base.$f(s::$G) = $f(s.s)
770✔
2422
        end
2423
    end
2424
end
2425

2426
Base.get(s::GenericDict, x, y) = get(s.s, x, y)
930✔
2427
Base.pop!(s::GenericDict, k) = pop!(s.s, k)
×
2428
Base.setindex!(s::GenericDict, v, k) = setindex!(s.s, v, k)
×
2429

2430
"""
2431
The `GenericArray` can be used to test generic array APIs that program to
2432
the `AbstractArray` interface, in order to ensure that functions can work
2433
with array types besides the standard `Array` type.
2434
"""
2435
struct GenericArray{T,N} <: AbstractArray{T,N}
2436
    a::Array{T,N}
1,189✔
2437
end
2438

2439
GenericArray{T}(args...) where {T} = GenericArray(Array{T}(args...))
760✔
2440
GenericArray{T,N}(args...) where {T,N} = GenericArray(Array{T,N}(args...))
×
2441

2442
"""
2443
The `GenericOrder` can be used to test APIs for their support
2444
of generic ordered types.
2445
"""
2446
struct GenericOrder{T}
2447
    val::T
6,924✔
2448
end
2449
Base.isless(x::GenericOrder, y::GenericOrder) = isless(x.val,y.val)
13,512✔
2450

2451
Base.keys(a::GenericArray) = keys(a.a)
×
2452
Base.axes(a::GenericArray) = axes(a.a)
102,613✔
2453
Base.length(a::GenericArray) = length(a.a)
3,330✔
2454
Base.size(a::GenericArray) = size(a.a)
19,125✔
2455
Base.IndexStyle(::Type{<:GenericArray}) = IndexLinear()
10,568✔
2456
Base.getindex(a::GenericArray, i::Int) = a.a[i]
18,567✔
2457
Base.setindex!(a::GenericArray, x, i::Int) = a.a[i] = x
6,625✔
2458

2459
Base.similar(A::GenericArray, s::Integer...) = GenericArray(similar(A.a, s...))
129✔
2460

2461
"`guardseed(f)` runs the function `f()` and then restores the
2462
state of the global RNG as it was before."
2463
function guardseed(f::Function, r::AbstractRNG=default_rng())
20✔
2464
    old = copy(r)
28✔
2465
    try
15✔
2466
        f()
15✔
2467
    finally
2468
        copy!(r, old)
15✔
2469
    end
2470
end
2471

2472
"`guardseed(f, seed)` is equivalent to running `Random.seed!(seed); f()` and
2473
then restoring the state of the global RNG as it was before."
2474
guardseed(f::Function, seed::Union{Vector{UInt64},Vector{UInt32},Integer,NTuple{4,UInt64}}) = guardseed() do
8✔
2475
    Random.seed!(seed)
8✔
2476
    f()
8✔
2477
end
2478

2479
function _check_bitarray_consistency(B::BitArray{N}) where N
289,203✔
2480
    n = length(B)
289,203✔
2481
    if N ≠ 1
289,203✔
2482
        all(d ≥ 0 for d in B.dims) || (@warn("Negative d in dims: $(B.dims)"); return false)
2,442✔
2483
        prod(B.dims) ≠ n && (@warn("Inconsistent dims/len: prod(dims)=$(prod(B.dims)) len=$n"); return false)
2,442✔
2484
    end
2485
    Bc = B.chunks
289,203✔
2486
    nc = length(Bc)
289,203✔
2487
    nc == Base.num_bit_chunks(n) || (@warn("Incorrect chunks length for length $n: expected=$(Base.num_bit_chunks(n)) actual=$nc"); return false)
289,203✔
2488
    n == 0 && return true
289,203✔
2489
    Bc[end] & Base._msk_end(n) == Bc[end] || (@warn("Nonzero bits in chunk after `BitArray` end"); return false)
287,729✔
2490
    return true
287,729✔
2491
end
2492

2493
include("logging.jl")
2494
include("precompile.jl")
2495

2496
end # module
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc