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

JuliaLang / julia / #37527

pending completion
#37527

push

local

web-flow
make `IRShow.method_name` inferrable (#49607)

18 of 18 new or added lines in 3 files covered. (100.0%)

68710 of 81829 relevant lines covered (83.97%)

33068903.12 hits per line

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

94.15
/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
6✔
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
31
using Serialization: Serialization
32

33
const DISPLAY_FAILED = (
34
    :isequal,
35
    :isapprox,
36
    :≈,
37
    :occursin,
38
    :startswith,
39
    :endswith,
40
    :isempty,
41
    :contains
42
)
43

44
const FAIL_FAST = Ref{Bool}(false)
45

46
#-----------------------------------------------------------------------
47

48
# Backtrace utility functions
49
function ip_has_file_and_func(ip, file, funcs)
×
50
    return any(fr -> (in_file(fr, file) && fr.func in funcs), StackTraces.lookup(ip))
2,763✔
51
end
52
in_file(frame, file) = string(frame.file) == file
1,167✔
53

54
function test_location(bt, file_ts, file_t)
×
55
    if (isnothing(file_ts) || isnothing(file_t))
×
56
        return macrocall_location(bt, something(file_ts, @__FILE__))
12✔
57
    else
58
        return test_callsite(bt, file_ts, file_t)
18✔
59
    end
60
end
61

62
function test_callsite(bt, file_ts, file_t)
18✔
63
    # We avoid duplicate calls to `StackTraces.lookup`, as it is an expensive call.
64
    # For that, we retrieve locations from lower to higher stack elements
65
    # and only traverse parts of the backtrace which we haven't traversed before.
66
    # The order will always be <internal functions> -> `@test` -> `@testset`.
67
    internal = macrocall_location(bt, @__FILE__)::Int
18✔
68
    test = internal - 1 + findfirst(ip -> any(frame -> in_file(frame, file_t), StackTraces.lookup(ip)), @view bt[internal:end])::Int
70✔
69
    testset = test - 1 + macrocall_location(@view(bt[test:end]), file_ts)::Int
18✔
70

71
    # If stacktrace locations differ, include frames until the `@testset` appears.
72
    test != testset && return testset
18✔
73
    # `@test` and `@testset` occurred at the same stacktrace location.
74
    # This may happen if `@test` occurred directly in scope of the testset,
75
    # or if `@test` occurred in a function that has been inlined in the testset.
76
    frames = StackTraces.lookup(bt[testset])
26✔
77
    outer_frame = findfirst(frame -> in_file(frame, file_ts) && frame.func == Symbol("macro expansion"), frames)::Int
48✔
78
    # The `@test` call occurred directly in scope of a `@testset`.
79
    # The __source__ from `@test` will be printed in the test message upon failure.
80
    # There is no need to include more frames, but always include at least the internal macrocall location in the stacktrace.
81
    in_file(frames[outer_frame], file_t) && return internal
13✔
82
    # The `@test` call was inlined, so we still need to include the callsite.
83
    return testset
×
84
end
85

86
macrocall_location(bt, file) = findfirst(ip -> ip_has_file_and_func(ip, file, (Symbol("macro expansion"),)), bt)
379✔
87

88
function scrub_backtrace(bt, file_ts, file_t)
30✔
89
    do_test_ind = findfirst(ip -> ip_has_file_and_func(ip, @__FILE__, (:do_test, :do_test_throws)), bt)
418✔
90
    if do_test_ind !== nothing && length(bt) > do_test_ind
30✔
91
        bt = bt[do_test_ind + 1:end]
20✔
92
    end
93
    stop_at = test_location(bt, file_ts, file_t)
30✔
94
    !isnothing(stop_at) && !isempty(bt) && return bt[1:stop_at]
42✔
95
    return bt
×
96
end
97

98
function scrub_exc_stack(stack, file_ts, file_t)
35✔
99
    return Any[ (x[1], scrub_backtrace(x[2]::Vector{Union{Ptr{Nothing},Base.InterpreterIP}}, file_ts, file_t)) for x in stack ]
35✔
100
end
101

102
# define most of the test infrastructure without type specialization
103
@nospecialize
104

105
"""
106
    Test.Result
107

108
All tests produce a result object. This object may or may not be
109
stored, depending on whether the test is part of a test set.
110
"""
111
abstract type Result end
112

113
"""
114
    Test.Pass <: Test.Result
115

116
The test condition was true, i.e. the expression evaluated to true or
117
the correct exception was thrown.
118
"""
119
struct Pass <: Result
120
    test_type::Symbol
121
    orig_expr
122
    data
123
    value
124
    source::Union{Nothing,LineNumberNode}
125
    message_only::Bool
126
    function Pass(test_type::Symbol, orig_expr, data, thrown, source::Union{Nothing,LineNumberNode}=nothing, message_only::Bool=false)
80,947,252✔
127
        return new(test_type, orig_expr, data, thrown, source, message_only)
157,587,604✔
128
    end
129
end
130

131
function Base.show(io::IO, t::Pass)
8✔
132
    printstyled(io, "Test Passed"; bold = true, color=:green)
8✔
133
    if t.test_type === :test_throws
8✔
134
        # The correct type of exception was thrown
135
        if t.message_only
6✔
136
            print(io, "\n     Message: ", t.value)
4✔
137
        else
138
            print(io, "\n      Thrown: ", typeof(t.value))
2✔
139
        end
140
    end
141
end
142

143
"""
144
    Test.Fail <: Test.Result
145

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

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

172
function Base.show(io::IO, t::Fail)
1,987✔
173
    printstyled(io, "Test Failed"; bold=true, color=Base.error_color())
1,987✔
174
    print(io, " at ")
1,987✔
175
    printstyled(io, something(t.source.file, :none), ":", t.source.line, "\n"; bold=true, color=:default)
3,973✔
176
    print(io, "  Expression: ", t.orig_expr)
1,987✔
177
    value, data = t.value, t.data
1,987✔
178
    if t.test_type === :test_throws_wrong
1,987✔
179
        # An exception was thrown, but it was of the wrong type
180
        if t.message_only
5✔
181
            print(io, "\n    Expected: ", data)
4✔
182
            print(io, "\n     Message: ", value)
4✔
183
        else
184
            print(io, "\n    Expected: ", data)
1✔
185
            print(io, "\n      Thrown: ", value)
6✔
186
        end
187
    elseif t.test_type === :test_throws_nothing
1,982✔
188
        # An exception was expected, but no exception was thrown
189
        print(io, "\n    Expected: ", data)
3✔
190
        print(io, "\n  No exception thrown")
3✔
191
    elseif t.test_type === :test
1,979✔
192
        if data !== nothing
1,979✔
193
            # The test was an expression, so display the term-by-term
194
            # evaluated version as well
195
            print(io, "\n   Evaluated: ", data)
214✔
196
        end
197
        if t.context !== nothing
1,979✔
198
            print(io, "\n     Context: ", t.context)
×
199
        end
200
    end
201
    println(io) # add some visual space to separate sequential failures
1,987✔
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
    source::LineNumberNode
218

219
    function Error(test_type::Symbol, orig_expr, value, bt, source::LineNumberNode)
64✔
220
        if test_type === :test_error
64✔
221
            bt = scrub_exc_stack(bt, nothing, extract_file(source))
20✔
222
        end
223
        if test_type === :test_error || test_type === :nontest_error
118✔
224
            bt_str = try # try the latest world for this, since we might have eval'd new code for show
33✔
225
                    Base.invokelatest(sprint, Base.show_exception_stack, bt; context=stdout)
35✔
226
                catch ex
227
                    "#=ERROR showing exception stack=# " *
2✔
228
                        try
229
                            sprint(Base.showerror, ex, catch_backtrace(); context=stdout)
2✔
230
                        catch
231
                            "of type " * string(typeof(ex))
35✔
232
                        end
233
                end
234
        else
235
            bt_str = ""
×
236
        end
237
        value = try # try the latest world for this, since we might have eval'd new code for show
64✔
238
                Base.invokelatest(sprint, show, value, context = :limit => true)
67✔
239
            catch ex
240
                "#=ERROR showing error of type " * string(typeof(value)) * "=# " *
3✔
241
                    try
242
                        sprint(Base.showerror, ex, catch_backtrace(); context=stdout)
3✔
243
                    catch
244
                        "of type " * string(typeof(ex))
67✔
245
                    end
246
            end
247
        return new(test_type,
64✔
248
            string(orig_expr),
249
            value,
250
            bt_str,
251
            source)
252
    end
253
end
254

255
function Base.show(io::IO, t::Error)
92✔
256
    if t.test_type === :test_interrupted
92✔
257
        printstyled(io, "Interrupted", color=Base.error_color())
×
258
        return
×
259
    end
260
    printstyled(io, "Error During Test"; bold=true, color=Base.error_color())
92✔
261
    print(io, " at ")
92✔
262
    printstyled(io, something(t.source.file, :none), ":", t.source.line, "\n"; bold=true, color=:default)
184✔
263
    if t.test_type === :test_nonbool
92✔
264
        println(io, "  Expression evaluated to non-Boolean")
45✔
265
        println(io, "  Expression: ", t.orig_expr)
45✔
266
        print(  io, "       Value: ", t.value)
45✔
267
    elseif t.test_type === :test_error
47✔
268
        println(io, "  Test threw exception")
8✔
269
        println(io, "  Expression: ", t.orig_expr)
8✔
270
        # Capture error message and indent to match
271
        join(io, ("  " * line for line in split(t.backtrace, "\n")), "\n")
8✔
272
    elseif t.test_type === :test_unbroken
39✔
273
        # A test that was expected to fail did not
274
        println(io, " Unexpected Pass")
2✔
275
        println(io, " Expression: ", t.orig_expr)
2✔
276
        println(io, " Got correct result, please change to @test if no longer broken.")
2✔
277
    elseif t.test_type === :nontest_error
37✔
278
        # we had an error outside of a @test
279
        println(io, "  Got exception outside of a @test")
37✔
280
        # Capture error message and indent to match
281
        join(io, ("  " * line for line in split(t.backtrace, "\n")), "\n")
37✔
282
    end
283
end
284

285
"""
286
    Test.Broken <: Test.Result
287

288
The test condition is the expected (failed) result of a broken test,
289
or was explicitly skipped with `@test_skip`.
290
"""
291
struct Broken <: Result
292
    test_type::Symbol
788✔
293
    orig_expr
294
end
295

296
function Base.show(io::IO, t::Broken)
2✔
297
    printstyled(io, "Test Broken\n"; bold=true, color=Base.warn_color())
2✔
298
    if t.test_type === :skipped && !(t.orig_expr === nothing)
2✔
299
        print(io, "  Skipped: ", t.orig_expr)
×
300
    elseif !(t.orig_expr === nothing)
2✔
301
        print(io, "  Expression: ", t.orig_expr)
2✔
302
    end
303
end
304

305
# Types that appear in TestSetException.errors_and_fails we convert eagerly into strings
306
# other types we convert lazily
307
function Serialization.serialize(s::Serialization.AbstractSerializer, t::Pass)
1✔
308
    Serialization.serialize_type(s, typeof(t))
1✔
309
    Serialization.serialize(s, t.test_type)
1✔
310
    Serialization.serialize(s, t.orig_expr === nothing ? nothing : string(t.orig_expr))
2✔
311
    Serialization.serialize(s, t.data === nothing ? nothing : string(t.data))
2✔
312
    Serialization.serialize(s, string(t.value))
1✔
313
    Serialization.serialize(s, t.source === nothing ? nothing : t.source)
2✔
314
    Serialization.serialize(s, t.message_only)
1✔
315
    nothing
1✔
316
end
317

318
function Serialization.serialize(s::Serialization.AbstractSerializer, t::Broken)
1✔
319
    Serialization.serialize_type(s, typeof(t))
1✔
320
    Serialization.serialize(s, t.test_type)
1✔
321
    Serialization.serialize(s, t.orig_expr === nothing ? nothing : string(t.orig_expr))
2✔
322
    nothing
1✔
323
end
324

325

326
#-----------------------------------------------------------------------
327

328
abstract type ExecutionResult end
329

330
struct Returned <: ExecutionResult
331
    value
38,281,408✔
332
    data
333
    source::LineNumberNode
334
end
335

336
struct Threw <: ExecutionResult
337
    exception
80,437✔
338
    backtrace::Union{Nothing,Vector{Any}}
339
    source::LineNumberNode
340
end
341

342
function eval_test(evaluated::Expr, quoted::Expr, source::LineNumberNode, negate::Bool=false)
32,044,578✔
343
    evaled_args = evaluated.args
32,044,574✔
344
    quoted_args = quoted.args
32,044,713✔
345
    n = length(evaled_args)
32,044,734✔
346
    kw_suffix = ""
492✔
347
    if evaluated.head === :comparison
32,044,698✔
348
        args = evaled_args
486✔
349
        res = true
486✔
350
        i = 1
486✔
351
        while i < n
62,096,534✔
352
            a, op, b = args[i], args[i+1], args[i+2]
31,230,350✔
353
            if res
31,230,270✔
354
                res = op(a, b)
31,230,267✔
355
            end
356
            quoted_args[i] = a
31,230,467✔
357
            quoted_args[i+2] = b
31,230,470✔
358
            i += 2
31,230,446✔
359
        end
31,230,454✔
360

361
    elseif evaluated.head === :call
1,178,371✔
362
        op = evaled_args[1]
1,178,371✔
363
        kwargs = (evaled_args[2]::Expr).args  # Keyword arguments from `Expr(:parameters, ...)`
1,178,371✔
364
        args = evaled_args[3:n]
1,178,371✔
365

366
        res = op(args...; kwargs...)
1,528,473✔
367

368
        # Create "Evaluated" expression which looks like the original call but has all of
369
        # the arguments evaluated
370
        func_sym = quoted_args[1]::Union{Symbol,Expr}
1,178,371✔
371
        if isempty(kwargs)
1,178,371✔
372
            quoted = Expr(:call, func_sym, args...)
828,269✔
373
        elseif func_sym === :≈ && !res
350,102✔
374
            quoted = Expr(:call, func_sym, args...)
2✔
375
            kw_suffix = " ($(join(["$k=$v" for (k, v) in kwargs], ", ")))"
2✔
376
        else
377
            kwargs_expr = Expr(:parameters, [Expr(:kw, k, v) for (k, v) in kwargs]...)
350,100✔
378
            quoted = Expr(:call, func_sym, kwargs_expr, args...)
1,528,471✔
379
        end
380
    else
381
        throw(ArgumentError("Unhandled expression type: $(evaluated.head)"))
×
382
    end
383

384
    if negate
32,044,730✔
385
        res = !res
2,400✔
386
        quoted = Expr(:call, :!, quoted)
2,400✔
387
    end
388

389
    Returned(res,
32,044,735✔
390
             # stringify arguments in case of failure, for easy remote printing
391
             res === true ? quoted : sprint(print, quoted, context=(:limit => true)) * kw_suffix,
392
             source)
393
end
394

395
const comparison_prec = Base.operator_precedence(:(==))
396

397
"""
398
    test_expr!(ex, kws...)
399

400
Preprocess test expressions of function calls with trailing keyword arguments
401
so that e.g. `@test a ≈ b atol=ε` means `@test ≈(a, b, atol=ε)`.
402
"""
403
test_expr!(m, ex) = ex
41,234✔
404

405
function test_expr!(m, ex, kws...)
420✔
406
    ex isa Expr && ex.head === :call || @goto fail
420✔
407
    for kw in kws
420✔
408
        kw isa Expr && kw.head === :(=) || @goto fail
433✔
409
        kw.head = :kw
433✔
410
        push!(ex.args, kw)
433✔
411
    end
463✔
412
    return ex
420✔
413
@label fail
×
414
    error("invalid test macro call: $m $ex $(join(kws," "))")
×
415
end
416

417
# @test - check if the expression evaluates to true
418
"""
419
    @test ex
420
    @test f(args...) key=val ...
421
    @test ex broken=true
422
    @test ex skip=true
423

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

429
# Examples
430
```jldoctest
431
julia> @test true
432
Test Passed
433

434
julia> @test [1, 2] + [2, 1] == [3, 3]
435
Test Passed
436
```
437

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

442
```jldoctest
443
julia> @test π ≈ 3.14 atol=0.01
444
Test Passed
445
```
446

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

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

454
* `broken=cond` indicates a test that should pass but currently consistently
455
  fails when `cond==true`.  Tests that the expression `ex` evaluates to `false`
456
  or causes an exception.  Returns a `Broken` `Result` if it does, or an `Error`
457
  `Result` if the expression evaluates to `true`.  Regular `@test ex` is
458
  evaluated when `cond==false`.
459
* `skip=cond` marks a test that should not be executed but should be included in
460
  test summary reporting as `Broken`, when `cond==true`.  This can be useful for
461
  tests that intermittently fail, or tests of not-yet-implemented functionality.
462
  Regular `@test ex` is evaluated when `cond==false`.
463

464
# Examples
465

466
```jldoctest
467
julia> @test 2 + 2 ≈ 6 atol=1 broken=true
468
Test Broken
469
  Expression: ≈(2 + 2, 6, atol = 1)
470

471
julia> @test 2 + 2 ≈ 5 atol=1 broken=false
472
Test Passed
473

474
julia> @test 2 + 2 == 5 skip=true
475
Test Broken
476
  Skipped: 2 + 2 == 5
477

478
julia> @test 2 + 2 == 4 skip=false
479
Test Passed
480
```
481

482
!!! compat "Julia 1.7"
483
     The `broken` and `skip` keyword arguments require at least Julia 1.7.
484
"""
485
macro test(ex, kws...)
41,465✔
486
    # Collect the broken/skip keywords and remove them from the rest of keywords
487
    broken = [kw.args[2] for kw in kws if kw.args[1] === :broken]
41,465✔
488
    skip = [kw.args[2] for kw in kws if kw.args[1] === :skip]
41,465✔
489
    kws = filter(kw -> kw.args[1] ∉ (:skip, :broken), kws)
42,361✔
490
    # Validation of broken/skip keywords
491
    for (kw, name) in ((broken, :broken), (skip, :skip))
41,465✔
492
        if length(kw) > 1
82,930✔
493
            error("invalid test macro call: cannot set $(name) keyword multiple times")
×
494
        end
495
    end
82,930✔
496
    if length(skip) > 0 && length(broken) > 0
41,465✔
497
        error("invalid test macro call: cannot set both skip and broken keywords")
×
498
    end
499

500
    # Build the test expression
501
    test_expr!("@test", ex, kws...)
41,465✔
502
    orig_ex = Expr(:inert, ex)
41,465✔
503

504
    result = get_test_result(ex, __source__)
41,465✔
505

506
    return quote
41,465✔
507
        if $(length(skip) > 0 && esc(skip[1]))
37,645,116✔
508
            record(get_testset(), Broken(:skipped, $orig_ex))
×
509
        else
510
            let _do = $(length(broken) > 0 && esc(broken[1])) ? do_broken_test : do_test
37,670,765✔
511
                _do($result, $orig_ex)
37,645,117✔
512
            end
513
        end
514
    end
515
end
516

517
"""
518
    @test_broken ex
519
    @test_broken f(args...) key=val ...
520

521
Indicates a test that should pass but currently consistently fails.
522
Tests that the expression `ex` evaluates to `false` or causes an
523
exception. Returns a `Broken` `Result` if it does, or an `Error` `Result`
524
if the expression evaluates to `true`.  This is equivalent to
525
[`@test ex broken=true`](@ref @test).
526

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

529
# Examples
530
```jldoctest
531
julia> @test_broken 1 == 2
532
Test Broken
533
  Expression: 1 == 2
534

535
julia> @test_broken 1 == 2 atol=0.1
536
Test Broken
537
  Expression: ==(1, 2, atol = 0.1)
538
```
539
"""
540
macro test_broken(ex, kws...)
175✔
541
    test_expr!("@test_broken", ex, kws...)
175✔
542
    orig_ex = Expr(:inert, ex)
175✔
543
    result = get_test_result(ex, __source__)
175✔
544
    # code to call do_test with execution result and original expr
545
    :(do_broken_test($result, $orig_ex))
175✔
546
end
547

548
"""
549
    @test_skip ex
550
    @test_skip f(args...) key=val ...
551

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

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

559
# Examples
560
```jldoctest
561
julia> @test_skip 1 == 2
562
Test Broken
563
  Skipped: 1 == 2
564

565
julia> @test_skip 1 == 2 atol=0.1
566
Test Broken
567
  Skipped: ==(1, 2, atol = 0.1)
568
```
569
"""
570
macro test_skip(ex, kws...)
14✔
571
    test_expr!("@test_skip", ex, kws...)
14✔
572
    orig_ex = Expr(:inert, ex)
14✔
573
    testres = :(Broken(:skipped, $orig_ex))
14✔
574
    :(record(get_testset(), $testres))
14✔
575
end
576

577
# An internal function, called by the code generated by the @test
578
# macro to get results of the test expression.
579
# In the special case of a comparison, e.g. x == 5, generate code to
580
# evaluate each term in the comparison individually so the results
581
# can be displayed nicely.
582
function get_test_result(ex, source)
41,640✔
583
    negate = QuoteNode(false)
×
584
    orig_ex = ex
×
585
    # Evaluate `not` wrapped functions separately for pretty-printing failures
586
    if isa(ex, Expr) && ex.head === :call && length(ex.args) == 2 && ex.args[1] === :!
41,640✔
587
        negate = QuoteNode(true)
×
588
        ex = ex.args[2]
1,973✔
589
    end
590
    # Normalize non-dot comparison operator calls to :comparison expressions
591
    is_splat = x -> isa(x, Expr) && x.head === :...
145,633✔
592
    if isa(ex, Expr) && ex.head === :call && length(ex.args) == 3 &&
55,584✔
593
        first(string(ex.args[1])) != '.' && !is_splat(ex.args[2]) && !is_splat(ex.args[3]) &&
594
        (ex.args[1] === :(==) || Base.operator_precedence(ex.args[1]) == comparison_prec)
595
        ex = Expr(:comparison, ex.args[2], ex.args[1], ex.args[3])
31,866✔
596

597
    # Mark <: and >: as :comparison expressions
598
    elseif isa(ex, Expr) && length(ex.args) == 2 &&
11,220✔
599
        !is_splat(ex.args[1]) && !is_splat(ex.args[2]) &&
600
        Base.operator_precedence(ex.head) == comparison_prec
601
        ex = Expr(:comparison, ex.args[1], ex.head, ex.args[2])
386✔
602
    end
603
    if isa(ex, Expr) && ex.head === :comparison
41,640✔
604
        # pass all terms of the comparison to `eval_comparison`, as an Expr
605
        escaped_terms = [esc(arg) for arg in ex.args]
33,538✔
606
        quoted_terms = [QuoteNode(arg) for arg in ex.args]
33,538✔
607
        testret = :(eval_test(
33,538✔
608
            Expr(:comparison, $(escaped_terms...)),
609
            Expr(:comparison, $(quoted_terms...)),
610
            $(QuoteNode(source)),
611
            $negate,
612
        ))
613
    elseif isa(ex, Expr) && ex.head === :call && ex.args[1] in DISPLAY_FAILED
8,645✔
614
        escaped_func = esc(ex.args[1])
2,578✔
615
        quoted_func = QuoteNode(ex.args[1])
2,578✔
616

617
        escaped_args = []
2,578✔
618
        escaped_kwargs = []
2,578✔
619

620
        # Keywords that occur before `;`. Note that the keywords are being revised into
621
        # a form we can splat.
622
        for a in ex.args[2:end]
2,578✔
623
            if isa(a, Expr) && a.head === :kw
5,226✔
624
                push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a.args[1]), esc(a.args[2])))
439✔
625
            end
626
        end
7,804✔
627

628
        # Keywords that occur after ';'
629
        parameters_expr = ex.args[2]
2,578✔
630
        if isa(parameters_expr, Expr) && parameters_expr.head === :parameters
2,578✔
631
            for a in parameters_expr.args
9✔
632
                if isa(a, Expr) && a.head === :kw
9✔
633
                    push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a.args[1]), esc(a.args[2])))
3✔
634
                elseif isa(a, Expr) && a.head === :...
6✔
635
                    push!(escaped_kwargs, Expr(:..., esc(a.args[1])))
2✔
636
                elseif isa(a, Expr) && a.head === :.
4✔
637
                    push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a.args[2].value), esc(Expr(:., a.args[1], QuoteNode(a.args[2].value)))))
1✔
638
                elseif isa(a, Symbol)
3✔
639
                    push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a), esc(a)))
3✔
640
                end
641
            end
18✔
642
        end
643

644
        # Positional arguments
645
        for a in ex.args[2:end]
2,578✔
646
            isa(a, Expr) && a.head in (:kw, :parameters) && continue
5,226✔
647

648
            if isa(a, Expr) && a.head === :...
4,778✔
649
                push!(escaped_args, Expr(:..., esc(a.args[1])))
2✔
650
            else
651
                push!(escaped_args, esc(a))
4,776✔
652
            end
653
        end
7,804✔
654

655
        testret = :(eval_test(
2,578✔
656
            Expr(:call, $escaped_func, Expr(:parameters, $(escaped_kwargs...)), $(escaped_args...)),
657
            Expr(:call, $quoted_func),
658
            $(QuoteNode(source)),
659
            $negate,
660
        ))
661
    else
662
        testret = :(Returned($(esc(orig_ex)), nothing, $(QuoteNode(source))))
5,524✔
663
    end
664
    result = quote
41,640✔
665
        try
666
            $testret
667
        catch _e
668
            _e isa InterruptException && rethrow()
669
            Threw(_e, Base.current_exceptions(), $(QuoteNode(source)))
670
        end
671
    end
672
    Base.remove_linenums!(result)
41,640✔
673
    result
41,640✔
674
end
675

676
# An internal function, called by the code generated by the @test
677
# macro to actually perform the evaluation and manage the result.
678
function do_test(result::ExecutionResult, orig_expr)
38,281,313✔
679
    # get_testset() returns the most recently added test set
680
    # We then call record() with this test set and the test result
681
    if isa(result, Returned)
38,281,308✔
682
        # expr, in the case of a comparison, will contain the
683
        # comparison with evaluated values of each term spliced in.
684
        # For anything else, just contains the test expression.
685
        # value is the evaluated value of the whole test expression.
686
        # Ideally it is true, but it may be false or non-Boolean.
687
        value = result.value
38,281,284✔
688
        testres = if isa(value, Bool)
38,281,328✔
689
            # a true value Passes
690
            value ? Pass(:test, orig_expr, result.data, value, result.source) :
38,282,313✔
691
                    Fail(:test, orig_expr, result.data, value, nothing, result.source, false)
692
        else
693
            # If the result is non-Boolean, this counts as an Error
694
            Error(:test_nonbool, orig_expr, value, nothing, result.source)
38,280,784✔
695
        end
696
    else
697
        # The predicate couldn't be evaluated without throwing an
698
        # exception, so that is an Error and not a Fail
699
        @assert isa(result, Threw)
9✔
700
        testres = Error(:test_error, orig_expr, result.exception, result.backtrace::Vector{Any}, result.source)
9✔
701
    end
702
    isa(testres, Pass) || trigger_test_failure_break(result)
38,281,836✔
703
    record(get_testset(), testres)
38,280,787✔
704
end
705

706
function do_broken_test(result::ExecutionResult, orig_expr)
367✔
707
    testres = Broken(:test, orig_expr)
367✔
708
    # Assume the test is broken and only change if the result is true
709
    if isa(result, Returned)
367✔
710
        value = result.value
259✔
711
        if isa(value, Bool)
259✔
712
            if value
259✔
713
                testres = Error(:test_unbroken, orig_expr, value, nothing, result.source)
2✔
714
            end
715
        else
716
            # If the result is non-Boolean, this counts as an Error
717
            testres = Error(:test_nonbool, orig_expr, value, nothing, result.source)
×
718
        end
719
    end
720
    record(get_testset(), testres)
367✔
721
end
722

723
#-----------------------------------------------------------------------
724

725
"""
726
    @test_throws exception expr
727

728
Tests that the expression `expr` throws `exception`.
729
The exception may specify either a type,
730
a string, regular expression, or list of strings occurring in the displayed error message,
731
a matching function,
732
or a value (which will be tested for equality by comparing fields).
733
Note that `@test_throws` does not support a trailing keyword form.
734

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

738
# Examples
739
```jldoctest
740
julia> @test_throws BoundsError [1, 2, 3][4]
741
Test Passed
742
      Thrown: BoundsError
743

744
julia> @test_throws DimensionMismatch [1, 2, 3] + [1, 2]
745
Test Passed
746
      Thrown: DimensionMismatch
747

748
julia> @test_throws "Try sqrt(Complex" sqrt(-1)
749
Test Passed
750
     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))."
751
```
752

753
In the final example, instead of matching a single string it could alternatively have been performed with:
754

755
- `["Try", "Complex"]` (a list of strings)
756
- `r"Try sqrt\\([Cc]omplex"` (a regular expression)
757
- `str -> occursin("complex", str)` (a matching function)
758
"""
759
macro test_throws(extype, ex)
4,182✔
760
    orig_ex = Expr(:inert, ex)
4,182✔
761
    result = quote
4,182✔
762
        try
763
            Returned($(esc(ex)), nothing, $(QuoteNode(__source__)))
764
        catch _e
765
            if $(esc(extype)) != InterruptException && _e isa InterruptException
766
                rethrow()
767
            end
768
            Threw(_e, nothing, $(QuoteNode(__source__)))
769
        end
770
    end
771
    Base.remove_linenums!(result)
4,182✔
772
    :(do_test_throws($result, $orig_ex, $(esc(extype))))
4,182✔
773
end
774

775
const MACROEXPAND_LIKE = Symbol.(("@macroexpand", "@macroexpand1", "macroexpand"))
776

777
# An internal function, called by the code generated by @test_throws
778
# to evaluate and catch the thrown exception - if it exists
779
function do_test_throws(result::ExecutionResult, orig_expr, extype)
80,325✔
780
    if isa(result, Threw)
80,325✔
781
        # Check that the right type of exception was thrown
782
        success = false
64✔
783
        message_only = false
64✔
784
        exc = result.exception
80,320✔
785
        # NB: Throwing LoadError from macroexpands is deprecated, but in order to limit
786
        # the breakage in package tests we add extra logic here.
787
        from_macroexpand =
82,124✔
788
            orig_expr isa Expr &&
789
            orig_expr.head in (:call, :macrocall) &&
790
            orig_expr.args[1] in MACROEXPAND_LIKE
791
        if isa(extype, Type)
80,320✔
792
            success =
77,537✔
793
                if from_macroexpand && extype == LoadError && exc isa Exception
794
                    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✔
795
                    true
×
796
                else
797
                    isa(exc, extype)
155,070✔
798
                end
799
        elseif isa(extype, Exception) || !isa(exc, Exception)
3,030✔
800
            if extype isa LoadError && !(exc isa LoadError) && typeof(extype.error) == typeof(exc)
2,542✔
801
                extype = extype.error # deprecated
2✔
802
            end
803
            if isa(exc, typeof(extype))
2,542✔
804
                success = true
13✔
805
                for fld in 1:nfields(extype)
5,076✔
806
                    if !isequal(getfield(extype, fld), getfield(exc, fld))
2,653✔
807
                        success = false
×
808
                        break
×
809
                    end
810
                end
2,542✔
811
            end
812
        else
813
            message_only = true
5✔
814
            exc = sprint(showerror, exc)
241✔
815
            success = contains_warn(exc, extype)
241✔
816
            exc = repr(exc)
241✔
817
            if isa(extype, AbstractString)
241✔
818
                extype = repr(extype)
172✔
819
            elseif isa(extype, Function)
69✔
820
                extype = "< match function >"
1✔
821
            end
822
        end
823
        if success
80,318✔
824
            testres = Pass(:test_throws, orig_expr, extype, exc, result.source, message_only)
80,312✔
825
        else
826
            testres = Fail(:test_throws_wrong, orig_expr, extype, exc, nothing, result.source, message_only)
80,324✔
827
        end
828
    else
829
        testres = Fail(:test_throws_nothing, orig_expr, extype, nothing, nothing, result.source, false)
5✔
830
    end
831
    record(get_testset(), testres)
80,323✔
832
end
833

834
#-----------------------------------------------------------------------
835
# Test for log messages
836

837
# Test for warning messages (deprecated)
838

839
contains_warn(output, s::AbstractString) = occursin(s, output)
199✔
840
contains_warn(output, s::Regex) = occursin(s, output)
3✔
841
contains_warn(output, s::Function) = s(output)
61✔
842
contains_warn(output, S::Union{AbstractArray,Tuple}) = all(s -> contains_warn(output, s), S)
20✔
843

844
"""
845
    @test_warn msg expr
846

847
Test whether evaluating `expr` results in [`stderr`](@ref) output that contains
848
the `msg` string or matches the `msg` regular expression.  If `msg` is
849
a boolean function, tests whether `msg(output)` returns `true`.  If `msg` is a
850
tuple or array, checks that the error output contains/matches each item in `msg`.
851
Returns the result of evaluating `expr`.
852

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

855
Note: Warnings generated by `@warn` cannot be tested with this macro. Use
856
[`@test_logs`](@ref) instead.
857
"""
858
macro test_warn(msg, expr)
13✔
859
    quote
13✔
860
        let fname = tempname()
4✔
861
            try
4✔
862
                ret = open(fname, "w") do f
4✔
863
                    redirect_stderr(f) do
14✔
864
                        $(esc(expr))
14✔
865
                    end
866
                end
867
                @test contains_warn(read(fname, String), $(esc(msg)))
×
868
                ret
4✔
869
            finally
870
                rm(fname, force=true)
4✔
871
            end
872
        end
873
    end
874
end
875

876
"""
877
    @test_nowarn expr
878

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

882
Note: The absence of warnings generated by `@warn` cannot be tested
883
with this macro. Use [`@test_logs`](@ref) instead.
884
"""
885
macro test_nowarn(expr)
14✔
886
    quote
14✔
887
        # Duplicate some code from `@test_warn` to allow printing the content of
888
        # `stderr` again to `stderr` here while suppressing it for `@test_warn`.
889
        # If that shouldn't be used, it would be possible to just use
890
        #     @test_warn isempty $(esc(expr))
891
        # here.
892
        let fname = tempname()
2✔
893
            try
2✔
894
                ret = open(fname, "w") do f
2✔
895
                    redirect_stderr(f) do
14✔
896
                        $(esc(expr))
14✔
897
                    end
898
                end
899
                stderr_content = read(fname, String)
2✔
900
                print(stderr, stderr_content) # this is helpful for debugging
2✔
901
                @test isempty(stderr_content)
×
902
                ret
2✔
903
            finally
904
                rm(fname, force=true)
2✔
905
            end
906
        end
907
    end
908
end
909

910
#-----------------------------------------------------------------------
911

912
# The AbstractTestSet interface is defined by two methods:
913
# record(AbstractTestSet, Result)
914
#   Called by do_test after a test is evaluated
915
# finish(AbstractTestSet)
916
#   Called after the test set has been popped from the test set stack
917
abstract type AbstractTestSet end
918

919
"""
920
    record(ts::AbstractTestSet, res::Result)
921

922
Record a result to a testset. This function is called by the `@testset`
923
infrastructure each time a contained `@test` macro completes, and is given the
924
test result (which could be an `Error`). This will also be called with an `Error`
925
if an exception is thrown inside the test block but outside of a `@test` context.
926
"""
927
function record end
928

929
"""
930
    finish(ts::AbstractTestSet)
931

932
Do any final processing necessary for the given testset. This is called by the
933
`@testset` infrastructure after a test block executes.
934

935
Custom `AbstractTestSet` subtypes should call `record` on their parent (if there
936
is one) to add themselves to the tree of test results. This might be implemented
937
as:
938

939
```julia
940
if get_testset_depth() != 0
941
    # Attach this test set to the parent test set
942
    parent_ts = get_testset()
943
    record(parent_ts, self)
944
    return self
945
end
946
```
947
"""
948
function finish end
949

950
"""
951
    TestSetException
952

953
Thrown when a test set finishes and not all tests passed.
954
"""
955
struct TestSetException <: Exception
956
    pass::Int
220✔
957
    fail::Int
958
    error::Int
959
    broken::Int
960
    errors_and_fails::Vector{Union{Fail, Error}}
961
end
962

963
function Base.show(io::IO, ex::TestSetException)
12✔
964
    print(io, "Some tests did not pass: ")
12✔
965
    print(io, ex.pass,  " passed, ")
12✔
966
    print(io, ex.fail,  " failed, ")
12✔
967
    print(io, ex.error, " errored, ")
12✔
968
    print(io, ex.broken, " broken.")
12✔
969
end
970

971
function Base.showerror(io::IO, ex::TestSetException, bt; backtrace=true)
22✔
972
    printstyled(io, string(ex), color=Base.error_color())
11✔
973
end
974

975
#-----------------------------------------------------------------------
976

977
"""
978
    FallbackTestSet
979

980
A simple fallback test set that throws immediately on a failure.
981
"""
982
struct FallbackTestSet <: AbstractTestSet end
983
fallback_testset = FallbackTestSet()
984

985
struct FallbackTestSetException <: Exception
986
    msg::String
8✔
987
end
988

989
function Base.showerror(io::IO, ex::FallbackTestSetException, bt; backtrace=true)
16✔
990
    printstyled(io, ex.msg, color=Base.error_color())
8✔
991
end
992

993
# Records nothing, and throws an error immediately whenever a Fail or
994
# Error occurs. Takes no action in the event of a Pass or Broken result
995
record(ts::FallbackTestSet, t::Union{Pass, Broken}) = t
×
996
function record(ts::FallbackTestSet, t::Union{Fail, Error})
5✔
997
    println(t)
5✔
998
    throw(FallbackTestSetException("There was an error during testing"))
5✔
999
end
1000
# We don't need to do anything as we don't record anything
1001
finish(ts::FallbackTestSet) = ts
×
1002

1003
#-----------------------------------------------------------------------
1004

1005
"""
1006
    ContextTestSet
1007

1008
Passes test failures through to the parent test set, while adding information
1009
about a context object that is being tested.
1010
"""
1011
struct ContextTestSet <: AbstractTestSet
1012
    parent_ts::AbstractTestSet
18✔
1013
    context_name::Union{Symbol, Expr}
1014
    context::Any
1015
end
1016

1017
function ContextTestSet(name::Union{Symbol, Expr}, @nospecialize(context))
18✔
1018
    if (name isa Expr) && (name.head != :tuple)
18✔
1019
        error("Invalid syntax: $(name)")
×
1020
    end
1021
    return ContextTestSet(get_testset(), name, context)
18✔
1022
end
1023
record(c::ContextTestSet, t) = record(c.parent_ts, t)
74✔
1024
function record(c::ContextTestSet, t::Fail)
×
1025
    context = string(c.context_name, " = ", c.context)
×
1026
    context = t.context === nothing ? context : string(t.context, "\n              ", context)
×
1027
    record(c.parent_ts, Fail(t.test_type, t.orig_expr, t.data, t.value, context, t.source, t.message_only))
×
1028
end
1029

1030
#-----------------------------------------------------------------------
1031

1032
"""
1033
    DefaultTestSet
1034

1035
If using the DefaultTestSet, the test results will be recorded. If there
1036
are any `Fail`s or `Error`s, an exception will be thrown only at the end,
1037
along with a summary of the test results.
1038
"""
1039
mutable struct DefaultTestSet <: AbstractTestSet
1040
    description::String
15,056✔
1041
    results::Vector{Any}
1042
    n_passed::Int
1043
    anynonpass::Bool
1044
    verbose::Bool
1045
    showtiming::Bool
1046
    time_start::Float64
1047
    time_end::Union{Float64,Nothing}
1048
    failfast::Bool
1049
    file::Union{String,Nothing}
1050
end
1051
function DefaultTestSet(desc::AbstractString; verbose::Bool = false, showtiming::Bool = true, failfast::Union{Nothing,Bool} = nothing, source = nothing)
30,115✔
1052
    if isnothing(failfast)
15,058✔
1053
        # pass failfast state into child testsets
1054
        parent_ts = get_testset()
15,054✔
1055
        if parent_ts isa DefaultTestSet
15,054✔
1056
            failfast = parent_ts.failfast
14,824✔
1057
        else
1058
            failfast = false
3✔
1059
        end
1060
    end
1061
    return DefaultTestSet(String(desc)::String, [], 0, false, verbose, showtiming, time(), nothing, failfast, extract_file(source))
15,056✔
1062
end
1063
extract_file(source::LineNumberNode) = extract_file(source.file)
59✔
1064
extract_file(file::Symbol) = string(file)
14,851✔
1065
extract_file(::Nothing) = nothing
×
1066

1067
struct FailFastError <: Exception end
3✔
1068

1069
# For a broken result, simply store the result
1070
record(ts::DefaultTestSet, t::Broken) = (push!(ts.results, t); t)
1,202✔
1071
# For a passed result, do not store the result since it uses a lot of memory
1072
record(ts::DefaultTestSet, t::Pass) = (ts.n_passed += 1; t)
118,989,638✔
1073

1074
# For the other result types, immediately print the error message
1075
# but do not terminate. Print a backtrace.
1076
function record(ts::DefaultTestSet, t::Union{Fail, Error}; print_result::Bool=TESTSET_PRINT_ENABLE[])
4,072✔
1077
    if print_result
2,036✔
1078
        print(ts.description, ": ")
23✔
1079
        # don't print for interrupted tests
1080
        if !(t isa Error) || t.test_type !== :test_interrupted
26✔
1081
            print(t)
43✔
1082
            if !isa(t, Error) # if not gets printed in the show method
23✔
1083
                Base.show_backtrace(stdout, scrub_backtrace(backtrace(), ts.file, extract_file(t.source)))
40✔
1084
            end
1085
            println()
23✔
1086
        end
1087
    end
1088
    push!(ts.results, t)
3,988✔
1089
    (FAIL_FAST[] || ts.failfast) && throw(FailFastError())
2,036✔
1090
    return t
2,033✔
1091
end
1092

1093
# When a DefaultTestSet finishes, it records itself to its parent
1094
# testset, if there is one. This allows for recursive printing of
1095
# the results at the end of the tests
1096
record(ts::DefaultTestSet, t::AbstractTestSet) = push!(ts.results, t)
14,832✔
1097

1098
@specialize
1099

1100
function print_test_errors(ts::DefaultTestSet)
232✔
1101
    for t in ts.results
404✔
1102
        if isa(t, Error) || isa(t, Fail)
3,223✔
1103
            println("Error in testset $(ts.description):")
1,005✔
1104
            show(t)
1,973✔
1105
            println()
1,005✔
1106
        elseif isa(t, DefaultTestSet)
625✔
1107
            print_test_errors(t)
230✔
1108
        end
1109
    end
1,630✔
1110
end
1111

1112
function print_test_results(ts::DefaultTestSet, depth_pad=0)
23✔
1113
    # Calculate the overall number for each type so each of
1114
    # the test result types are aligned
1115
    passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration = get_test_counts(ts)
44✔
1116
    total_pass   = passes + c_passes
23✔
1117
    total_fail   = fails  + c_fails
23✔
1118
    total_error  = errors + c_errors
23✔
1119
    total_broken = broken + c_broken
23✔
1120
    dig_pass   = total_pass   > 0 ? ndigits(total_pass)   : 0
23✔
1121
    dig_fail   = total_fail   > 0 ? ndigits(total_fail)   : 0
23✔
1122
    dig_error  = total_error  > 0 ? ndigits(total_error)  : 0
23✔
1123
    dig_broken = total_broken > 0 ? ndigits(total_broken) : 0
23✔
1124
    total = total_pass + total_fail + total_error + total_broken
23✔
1125
    dig_total = total > 0 ? ndigits(total) : 0
23✔
1126
    # For each category, take max of digits and header width if there are
1127
    # tests of that type
1128
    pass_width   = dig_pass   > 0 ? max(length("Pass"),   dig_pass)   : 0
23✔
1129
    fail_width   = dig_fail   > 0 ? max(length("Fail"),   dig_fail)   : 0
23✔
1130
    error_width  = dig_error  > 0 ? max(length("Error"),  dig_error)  : 0
23✔
1131
    broken_width = dig_broken > 0 ? max(length("Broken"), dig_broken) : 0
23✔
1132
    total_width  = dig_total  > 0 ? max(length("Total"),  dig_total)  : 0
23✔
1133
    duration_width = max(length("Time"), length(duration))
23✔
1134
    # Calculate the alignment of the test result counts by
1135
    # recursively walking the tree of test sets
1136
    align = max(get_alignment(ts, 0), length("Test Summary:"))
23✔
1137
    # Print the outer test set header once
1138
    pad = total == 0 ? "" : " "
23✔
1139
    printstyled(rpad("Test Summary:", align, " "), " |", pad; bold=true)
23✔
1140
    if pass_width > 0
23✔
1141
        printstyled(lpad("Pass", pass_width, " "), "  "; bold=true, color=:green)
16✔
1142
    end
1143
    if fail_width > 0
23✔
1144
        printstyled(lpad("Fail", fail_width, " "), "  "; bold=true, color=Base.error_color())
11✔
1145
    end
1146
    if error_width > 0
23✔
1147
        printstyled(lpad("Error", error_width, " "), "  "; bold=true, color=Base.error_color())
5✔
1148
    end
1149
    if broken_width > 0
23✔
1150
        printstyled(lpad("Broken", broken_width, " "), "  "; bold=true, color=Base.warn_color())
5✔
1151
    end
1152
    if total_width > 0
23✔
1153
        printstyled(lpad("Total", total_width, " "), "  "; bold=true, color=Base.info_color())
23✔
1154
    end
1155
    if ts.showtiming
23✔
1156
        printstyled(lpad("Time", duration_width, " "); bold=true)
23✔
1157
    end
1158
    println()
23✔
1159
    # Recursively print a summary at every level
1160
    print_counts(ts, depth_pad, align, pass_width, fail_width, error_width, broken_width, total_width, duration_width, ts.showtiming)
23✔
1161
end
1162

1163

1164
const TESTSET_PRINT_ENABLE = Ref(true)
1165

1166
# Called at the end of a @testset, behaviour depends on whether
1167
# this is a child of another testset, or the "root" testset
1168
function finish(ts::DefaultTestSet; print_results::Bool=TESTSET_PRINT_ENABLE[])
29,646✔
1169
    ts.time_end = time()
14,823✔
1170
    # If we are a nested test set, do not print a full summary
1171
    # now - let the parent test set do the printing
1172
    if get_testset_depth() != 0
14,823✔
1173
        # Attach this test set to the parent test set
1174
        parent_ts = get_testset()
14,594✔
1175
        record(parent_ts, ts)
29,188✔
1176
        return ts
14,594✔
1177
    end
1178
    passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration = get_test_counts(ts)
229✔
1179
    total_pass   = passes + c_passes
229✔
1180
    total_fail   = fails  + c_fails
229✔
1181
    total_error  = errors + c_errors
229✔
1182
    total_broken = broken + c_broken
229✔
1183
    total = total_pass + total_fail + total_error + total_broken
229✔
1184

1185
    if print_results
229✔
1186
        print_test_results(ts)
21✔
1187
    end
1188

1189
    # Finally throw an error as we are the outermost test set
1190
    if total != total_pass + total_broken
229✔
1191
        # Get all the error/failures and bring them along for the ride
1192
        efs = filter_errors(ts)
35✔
1193
        throw(TestSetException(total_pass, total_fail, total_error, total_broken, efs))
35✔
1194
    end
1195

1196
    # return the testset so it is returned from the @testset macro
1197
    ts
194✔
1198
end
1199

1200
# Recursive function that finds the column that the result counts
1201
# can begin at by taking into account the width of the descriptions
1202
# and the amount of indentation. If a test set had no failures, and
1203
# no failures in child test sets, there is no need to include those
1204
# in calculating the alignment
1205
function get_alignment(ts::DefaultTestSet, depth::Int)
270✔
1206
    # The minimum width at this depth is
1207
    ts_width = 2*depth + length(ts.description)
270✔
1208
    # If not verbose and all passing, no need to look at children
1209
    !ts.verbose && !ts.anynonpass && return ts_width
270✔
1210
    # Return the maximum of this width and the minimum width
1211
    # for all children (if they exist)
1212
    isempty(ts.results) && return ts_width
48✔
1213
    child_widths = map(t->get_alignment(t, depth+1), ts.results)
1,483✔
1214
    return max(ts_width, maximum(child_widths))
48✔
1215
end
1216
get_alignment(ts, depth::Int) = 0
1,188✔
1217

1218
# Recursive function that fetches backtraces for any and all errors
1219
# or failures the testset and its children encountered
1220
function filter_errors(ts::DefaultTestSet)
14,727✔
1221
    efs = []
14,727✔
1222
    for t in ts.results
26,752✔
1223
        if isa(t, DefaultTestSet)
15,919✔
1224
            append!(efs, filter_errors(t))
14,510✔
1225
        elseif isa(t, Union{Fail, Error})
1,409✔
1226
            append!(efs, [t])
1,017✔
1227
        end
1228
    end
18,621✔
1229
    efs
14,727✔
1230
end
1231

1232
# Recursive function that counts the number of test results of each
1233
# type directly in the testset, and totals across the child testsets
1234
function get_test_counts(ts::DefaultTestSet)
29,052✔
1235
    passes, fails, errors, broken = ts.n_passed, 0, 0, 0
29,052✔
1236
    c_passes, c_fails, c_errors, c_broken = 0, 0, 0, 0
×
1237
    for t in ts.results
52,593✔
1238
        isa(t, Fail)   && (fails  += 1)
34,267✔
1239
        isa(t, Error)  && (errors += 1)
34,267✔
1240
        isa(t, Broken) && (broken += 1)
34,267✔
1241
        if isa(t, DefaultTestSet)
34,267✔
1242
            np, nf, ne, nb, ncp, ncf, nce , ncb, duration = get_test_counts(t)
28,347✔
1243
            c_passes += np + ncp
28,347✔
1244
            c_fails  += nf + ncf
28,347✔
1245
            c_errors += ne + nce
28,347✔
1246
            c_broken += nb + ncb
28,347✔
1247
        end
1248
    end
39,778✔
1249
    ts.anynonpass = (fails + errors + c_fails + c_errors > 0)
29,052✔
1250
    (; time_start, time_end) = ts
29,052✔
1251
    duration = if isnothing(time_end)
58,104✔
1252
        ""
×
1253
    else
1254
        dur_s = time_end - time_start
29,052✔
1255
        if dur_s < 60
29,052✔
1256
            string(round(dur_s, digits = 1), "s")
28,383✔
1257
        else
1258
            m, s = divrem(dur_s, 60)
669✔
1259
            s = lpad(string(round(s, digits = 1)), 4, "0")
669✔
1260
            string(round(Int, m), "m", s, "s")
58,773✔
1261
        end
1262
    end
1263
    return passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration
29,052✔
1264
end
1265

1266
# Recursive function that prints out the results at each level of
1267
# the tree of test sets
1268
function print_counts(ts::DefaultTestSet, depth, align,
270✔
1269
                      pass_width, fail_width, error_width, broken_width, total_width, duration_width, showtiming)
1270
    # Count results by each type at this level, and recursively
1271
    # through any child test sets
1272
    passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration = get_test_counts(ts)
270✔
1273
    subtotal = passes + fails + errors + broken + c_passes + c_fails + c_errors + c_broken
270✔
1274
    # Print test set header, with an alignment that ensures all
1275
    # the test results appear above each other
1276
    print(rpad(string("  "^depth, ts.description), align, " "), " | ")
270✔
1277

1278
    np = passes + c_passes
270✔
1279
    if np > 0
270✔
1280
        printstyled(lpad(string(np), pass_width, " "), "  ", color=:green)
252✔
1281
    elseif pass_width > 0
18✔
1282
        # No passes at this level, but some at another level
1283
        print(lpad(" ", pass_width), "  ")
8✔
1284
    end
1285

1286
    nf = fails + c_fails
270✔
1287
    if nf > 0
270✔
1288
        printstyled(lpad(string(nf), fail_width, " "), "  ", color=Base.error_color())
41✔
1289
    elseif fail_width > 0
229✔
1290
        # No fails at this level, but some at another level
1291
        print(lpad(" ", fail_width), "  ")
208✔
1292
    end
1293

1294
    ne = errors + c_errors
270✔
1295
    if ne > 0
270✔
1296
        printstyled(lpad(string(ne), error_width, " "), "  ", color=Base.error_color())
14✔
1297
    elseif error_width > 0
256✔
1298
        # No errors at this level, but some at another level
1299
        print(lpad(" ", error_width), "  ")
224✔
1300
    end
1301

1302
    nb = broken + c_broken
270✔
1303
    if nb > 0
270✔
1304
        printstyled(lpad(string(nb), broken_width, " "), "  ", color=Base.warn_color())
47✔
1305
    elseif broken_width > 0
223✔
1306
        # None broken at this level, but some at another level
1307
        print(lpad(" ", broken_width), "  ")
188✔
1308
    end
1309

1310
    if np == 0 && nf == 0 && ne == 0 && nb == 0
270✔
1311
        printstyled(lpad("None", total_width, " "), "  ", color=Base.info_color())
5✔
1312
    else
1313
        printstyled(lpad(string(subtotal), total_width, " "), "  ", color=Base.info_color())
265✔
1314
    end
1315

1316
    if showtiming
270✔
1317
        printstyled(lpad(string(duration), duration_width, " "))
270✔
1318
    end
1319
    println()
270✔
1320

1321
    # Only print results at lower levels if we had failures or if the user
1322
    # wants.
1323
    if (np + nb != subtotal) || (ts.verbose)
495✔
1324
        for t in ts.results
48✔
1325
            if isa(t, DefaultTestSet)
1,435✔
1326
                print_counts(t, depth + 1, align,
247✔
1327
                    pass_width, fail_width, error_width, broken_width, total_width, duration_width, ts.showtiming)
1328
            end
1329
        end
1,657✔
1330
    end
1331
end
1332

1333
#-----------------------------------------------------------------------
1334

1335
function _check_testset(testsettype, testsetname)
14,866✔
1336
    if !(testsettype isa Type && testsettype <: AbstractTestSet)
14,869✔
1337
        error("Expected `$testsetname` to be an AbstractTestSet, it is a ",
1✔
1338
              typeof(testsettype), ". ",
1339
              typeof(testsettype) == String ?
1340
                  """
1341
                  To use `$testsetname` as a testset name, interpolate it into a string, e.g:
1342
                      @testset "\$$testsetname" begin
1343
                          ...
1344
                      end"""
1345
             :
1346
                  ""
1347
            )
1348
    end
1349
end
1350

1351
"""
1352
    @testset [CustomTestSet] [option=val  ...] ["description"] begin ... end
1353
    @testset [CustomTestSet] [option=val  ...] ["description \$v"] for v in (...) ... end
1354
    @testset [CustomTestSet] [option=val  ...] ["description \$v, \$w"] for v in (...), w in (...) ... end
1355
    @testset [CustomTestSet] [option=val  ...] ["description"] foo()
1356
    @testset let v = (...) ... end
1357

1358
# With begin/end or function call
1359

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

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

1368
Any custom testset type (subtype of `AbstractTestSet`) can be given and it will
1369
also be used for any nested `@testset` invocations. The given options are only
1370
applied to the test set where they are given. The default test set type
1371
accepts three boolean options:
1372
- `verbose`: if `true`, the result summary of the nested testsets is shown even
1373
  when they all pass (the default is `false`).
1374
- `showtiming`: if `true`, the duration of each displayed testset is shown
1375
  (the default is `true`).
1376
- `failfast`: if `true`, any test failure or error will cause the testset and any
1377
  child testsets to return immediately (the default is `false`).
1378
  This can also be set globally via the env var `JULIA_TEST_FAILFAST`.
1379

1380
!!! compat "Julia 1.8"
1381
    `@testset foo()` requires at least Julia 1.8.
1382

1383
!!! compat "Julia 1.9"
1384
    `failfast` requires at least Julia 1.9.
1385

1386
The description string accepts interpolation from the loop indices.
1387
If no description is provided, one is constructed based on the variables.
1388
If a function call is provided, its name will be used.
1389
Explicit description strings override this behavior.
1390

1391
By default the `@testset` macro will return the testset object itself, though
1392
this behavior can be customized in other testset types. If a `for` loop is used
1393
then the macro collects and returns a list of the return values of the `finish`
1394
method, which by default will return a list of the testset objects used in
1395
each iteration.
1396

1397
Before the execution of the body of a `@testset`, there is an implicit
1398
call to `Random.seed!(seed)` where `seed` is the current seed of the global RNG.
1399
Moreover, after the execution of the body, the state of the global RNG is
1400
restored to what it was before the `@testset`. This is meant to ease
1401
reproducibility in case of failure, and to allow seamless
1402
re-arrangements of `@testset`s regardless of their side-effect on the
1403
global RNG state.
1404

1405
## Examples
1406
```jldoctest; filter = r"trigonometric identities |    4      4  [0-9\\.]+s"
1407
julia> @testset "trigonometric identities" begin
1408
           θ = 2/3*π
1409
           @test sin(-θ) ≈ -sin(θ)
1410
           @test cos(-θ) ≈ cos(θ)
1411
           @test sin(2θ) ≈ 2*sin(θ)*cos(θ)
1412
           @test cos(2θ) ≈ cos(θ)^2 - sin(θ)^2
1413
       end;
1414
Test Summary:            | Pass  Total  Time
1415
trigonometric identities |    4      4  0.2s
1416
```
1417

1418
# `@testset for`
1419

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

1424
# `@testset let`
1425

1426
When `@testset let` is used, the macro starts a *transparent* test set with
1427
the given object added as a context object to any failing test contained
1428
therein. This is useful when performing a set of related tests on one larger
1429
object and it is desirable to print this larger object when any of the
1430
individual tests fail. Transparent test sets do not introduce additional levels
1431
of nesting in the test set hierarchy and are passed through directly to the
1432
parent test set (with the context object appended to any failing tests.)
1433

1434
!!! compat "Julia 1.9"
1435
    `@testset let` requires at least Julia 1.9.
1436

1437
## Examples
1438
```jldoctest
1439
julia> @testset let logi = log(im)
1440
           @test imag(logi) == π/2
1441
           @test !iszero(real(logi))
1442
       end
1443
Test Failed at none:3
1444
  Expression: !(iszero(real(logi)))
1445
     Context: logi = 0.0 + 1.5707963267948966im
1446

1447
ERROR: There was an error during testing
1448
```
1449
"""
1450
macro testset(args...)
3,769✔
1451
    isempty(args) && error("No arguments to @testset")
3,769✔
1452

1453
    tests = args[end]
3,769✔
1454

1455
    # Determine if a single block or for-loop style
1456
    if !isa(tests,Expr) || (tests.head !== :for && tests.head !== :block && tests.head !== :call && tests.head !== :let)
7,538✔
1457

1458
        error("Expected function call, begin/end block or for loop as argument to @testset")
×
1459
    end
1460

1461
    FAIL_FAST[] = Base.get_bool_env("JULIA_TEST_FAILFAST", false)
3,769✔
1462

1463
    if tests.head === :for
3,769✔
1464
        return testset_forloop(args, tests, __source__)
285✔
1465
    elseif tests.head === :let
3,484✔
1466
        return testset_context(args, tests, __source__)
7✔
1467
    else
1468
        return testset_beginend_call(args, tests, __source__)
3,477✔
1469
    end
1470
end
1471

1472
trigger_test_failure_break(@nospecialize(err)) =
1,081✔
1473
    ccall(:jl_test_failure_breakpoint, Cvoid, (Any,), err)
1474

1475
"""
1476
Generate the code for an `@testset` with a `let` argument.
1477
"""
1478
function testset_context(args, tests, source)
7✔
1479
    desc, testsettype, options = parse_testset_args(args[1:end-1])
7✔
1480
    if desc !== nothing || testsettype !== nothing
7✔
1481
        # Reserve this syntax if we ever want to allow this, but for now,
1482
        # just do the transparent context test set.
1483
        error("@testset with a `let` argument cannot be customized")
×
1484
    end
1485

1486
    assgn = tests.args[1]
7✔
1487
    if !isa(assgn, Expr) || assgn.head !== :(=)
14✔
1488
        error("`@testset let` must have exactly one assignment")
×
1489
    end
1490
    assignee = assgn.args[1]
7✔
1491

1492
    tests.args[2] = quote
7✔
1493
        $push_testset($(ContextTestSet)($(QuoteNode(assignee)), $assignee; $options...))
14✔
1494
        try
14✔
1495
            $(tests.args[2])
14✔
1496
        finally
1497
            $pop_testset()
14✔
1498
        end
1499
    end
1500

1501
    return esc(tests)
7✔
1502
end
1503

1504
"""
1505
Generate the code for a `@testset` with a function call or `begin`/`end` argument
1506
"""
1507
function testset_beginend_call(args, tests, source)
3,477✔
1508
    desc, testsettype, options = parse_testset_args(args[1:end-1])
3,480✔
1509
    if desc === nothing
88✔
1510
        if tests.head === :call
20✔
1511
            desc = string(tests.args[1]) # use the function name as test name
2✔
1512
        else
1513
            desc = "test set"
18✔
1514
        end
1515
    end
1516
    # If we're at the top level we'll default to DefaultTestSet. Otherwise
1517
    # default to the type of the parent testset
1518
    if testsettype === nothing
3,477✔
1519
        testsettype = :(get_testset_depth() == 0 ? DefaultTestSet : typeof(get_testset()))
3,466✔
1520
    end
1521

1522
    # Generate a block of code that initializes a new testset, adds
1523
    # it to the task local storage, evaluates the test(s), before
1524
    # finally removing the testset and giving it a chance to take
1525
    # action (such as reporting the results)
1526
    ex = quote
6,953✔
1527
        _check_testset($testsettype, $(QuoteNode(testsettype.args[1])))
6,906✔
1528
        local ret
×
1529
        local ts = if ($testsettype === $DefaultTestSet) && $(isa(source, LineNumberNode))
6,906✔
1530
            $(testsettype)($desc; source=$(QuoteNode(source.file)), $options...)
6,885✔
1531
        else
1532
            $(testsettype)($desc; $options...)
3,568✔
1533
        end
1534
        push_testset(ts)
3,562✔
1535
        # we reproduce the logic of guardseed, but this function
1536
        # cannot be used as it changes slightly the semantic of @testset,
1537
        # by wrapping the body in a function
1538
        local RNG = default_rng()
3,559✔
1539
        local oldrng = copy(RNG)
3,562✔
1540
        local oldseed = Random.GLOBAL_SEED
3,562✔
1541
        try
3,562✔
1542
            # RNG is re-seeded with its own seed to ease reproduce a failed test
1543
            Random.seed!(Random.GLOBAL_SEED)
3,562✔
1544
            let
1,616✔
1545
                $(esc(tests))
214,138✔
1546
            end
1547
        catch err
1548
            err isa InterruptException && rethrow()
18✔
1549
            # something in the test block threw an error. Count that as an
1550
            # error in this test set
1551
            trigger_test_failure_break(err)
18✔
1552
            if err isa FailFastError
18✔
1553
                get_testset_depth() > 1 ? rethrow() : failfast_print()
×
1554
            else
1555
                record(ts, Error(:nontest_error, Expr(:tuple), err, Base.current_exceptions(), $(QuoteNode(source))))
3,580✔
1556
            end
1557
        finally
1558
            copy!(RNG, oldrng)
3,562✔
1559
            Random.set_global_seed!(oldseed)
3,562✔
1560
            pop_testset()
3,562✔
1561
            ret = finish(ts)
7,021✔
1562
        end
1563
        ret
3,533✔
1564
    end
1565
    # preserve outer location if possible
1566
    if tests isa Expr && tests.head === :block && !isempty(tests.args) && tests.args[1] isa LineNumberNode
3,477✔
1567
        ex = Expr(:block, tests.args[1], ex)
3,473✔
1568
    end
1569
    return ex
3,477✔
1570
end
1571

1572
function failfast_print()
3✔
1573
    printstyled("\nFail-fast enabled:"; color = Base.error_color(), bold=true)
3✔
1574
    printstyled(" Fail or Error occurred\n\n"; color = Base.error_color())
3✔
1575
end
1576

1577
"""
1578
Generate the code for a `@testset` with a `for` loop argument
1579
"""
1580
function testset_forloop(args, testloop, source)
285✔
1581
    # Pull out the loop variables. We might need them for generating the
1582
    # description and we'll definitely need them for generating the
1583
    # comprehension expression at the end
1584
    loopvars = Expr[]
285✔
1585
    if testloop.args[1].head === :(=)
285✔
1586
        push!(loopvars, testloop.args[1])
248✔
1587
    elseif testloop.args[1].head === :block
37✔
1588
        for loopvar in testloop.args[1].args
37✔
1589
            push!(loopvars, loopvar)
81✔
1590
        end
81✔
1591
    else
1592
        error("Unexpected argument to @testset")
×
1593
    end
1594

1595
    desc, testsettype, options = parse_testset_args(args[1:end-1])
285✔
1596

1597
    if desc === nothing
285✔
1598
        # No description provided. Generate from the loop variable names
1599
        v = loopvars[1].args[1]
104✔
1600
        desc = Expr(:string, "$v = ", esc(v)) # first variable
104✔
1601
        for l = loopvars[2:end]
196✔
1602
            v = l.args[1]
14✔
1603
            push!(desc.args, ", $v = ")
14✔
1604
            push!(desc.args, esc(v))
14✔
1605
        end
26✔
1606
    end
1607

1608
    if testsettype === nothing
285✔
1609
        testsettype = :(get_testset_depth() == 0 ? DefaultTestSet : typeof(get_testset()))
284✔
1610
    end
1611

1612
    # Uses a similar block as for `@testset`, except that it is
1613
    # wrapped in the outer loop provided by the user
1614
    tests = testloop.args[2]
285✔
1615
    blk = quote
570✔
1616
        _check_testset($testsettype, $(QuoteNode(testsettype.args[1])))
13,435✔
1617
        # Trick to handle `break` and `continue` in the test code before
1618
        # they can be handled properly by `finally` lowering.
1619
        if !first_iteration
6,721✔
1620
            pop_testset()
5,466✔
1621
            finish_errored = true
5,466✔
1622
            push!(arr, finish(ts))
10,901✔
1623
            finish_errored = false
5,466✔
1624

1625
            # it's 1000 times faster to copy from tmprng rather than calling Random.seed!
1626
            copy!(RNG, tmprng)
5,466✔
1627

1628
        end
1629
        ts = if ($testsettype === $DefaultTestSet) && $(isa(source, LineNumberNode))
13,435✔
1630
            $(testsettype)($desc; source=$(QuoteNode(source.file)), $options...)
13,347✔
1631
        else
1632
            $(testsettype)($desc; $options...)
6,744✔
1633
        end
1634
        push_testset(ts)
6,720✔
1635
        first_iteration = false
6,720✔
1636
        try
6,720✔
1637
            $(esc(tests))
1,583,696✔
1638
        catch err
1639
            err isa InterruptException && rethrow()
4✔
1640
            # Something in the test block threw an error. Count that as an
1641
            # error in this test set
1642
            trigger_test_failure_break(err)
3✔
1643
            if !isa(err, FailFastError)
3✔
1644
                record(ts, Error(:nontest_error, Expr(:tuple), err, Base.current_exceptions(), $(QuoteNode(source))))
7,946✔
1645
            end
1646
        end
1647
    end
1648
    quote
285✔
1649
        local arr = Vector{Any}()
1,255✔
1650
        local first_iteration = true
1,255✔
1651
        local ts
×
1652
        local finish_errored = false
1,255✔
1653
        local RNG = default_rng()
1,255✔
1654
        local oldrng = copy(RNG)
1,255✔
1655
        local oldseed = Random.GLOBAL_SEED
1,255✔
1656
        Random.seed!(Random.GLOBAL_SEED)
1,255✔
1657
        local tmprng = copy(RNG)
1,255✔
1658
        try
1,255✔
1659
            let
×
1660
                $(Expr(:for, Expr(:block, [esc(v) for v in loopvars]...), blk))
1,352✔
1661
            end
1662
        finally
1663
            # Handle `return` in test body
1664
            if !first_iteration && !finish_errored
1,255✔
1665
                pop_testset()
1,254✔
1666
                push!(arr, finish(ts))
2,493✔
1667
            end
1668
            copy!(RNG, oldrng)
1,255✔
1669
            Random.set_global_seed!(oldseed)
1,255✔
1670
        end
1671
        arr
1,253✔
1672
    end
1673
end
1674

1675
"""
1676
Parse the arguments to the `@testset` macro to pull out the description,
1677
Testset Type, and options. Generally this should be called with all the macro
1678
arguments except the last one, which is the test expression itself.
1679
"""
1680
function parse_testset_args(args)
3,769✔
1681
    desc = nothing
332✔
1682
    testsettype = nothing
332✔
1683
    options = :(Dict{Symbol, Any}())
3,769✔
1684
    for arg in args
3,769✔
1685
        # a standalone symbol is assumed to be the test set we should use
1686
        if isa(arg, Symbol)
222✔
1687
            testsettype = esc(arg)
12✔
1688
        # a string is the description
1689
        elseif isa(arg, AbstractString) || (isa(arg, Expr) && arg.head === :string)
222✔
1690
            desc = esc(arg)
3,644✔
1691
        # an assignment is an option
1692
        elseif isa(arg, Expr) && arg.head === :(=)
9✔
1693
            # we're building up a Dict literal here
1694
            key = Expr(:quote, arg.args[1])
9✔
1695
            push!(options.args, Expr(:call, :(=>), key, esc(arg.args[2])))
9✔
1696
        else
1697
            error("Unexpected argument $arg to @testset")
×
1698
        end
1699
    end
238✔
1700

1701
    (desc, testsettype, options)
3,769✔
1702
end
1703

1704
#-----------------------------------------------------------------------
1705
# Various helper methods for test sets
1706

1707
"""
1708
    get_testset()
1709

1710
Retrieve the active test set from the task's local storage. If no
1711
test set is active, use the fallback default test set.
1712
"""
1713
function get_testset()
38,436,648✔
1714
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
76,554,733✔
1715
    return isempty(testsets) ? fallback_testset : testsets[end]
38,436,034✔
1716
end
1717

1718
"""
1719
    push_testset(ts::AbstractTestSet)
1720

1721
Adds the test set to the `task_local_storage`.
1722
"""
1723
function push_testset(ts::AbstractTestSet)
15,116✔
1724
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
30,009✔
1725
    push!(testsets, ts)
15,116✔
1726
    setindex!(task_local_storage(), testsets, :__BASETESTNEXT__)
15,116✔
1727
end
1728

1729
"""
1730
    pop_testset()
1731

1732
Pops the last test set added to the `task_local_storage`. If there are no
1733
active test sets, returns the fallback default test set.
1734
"""
1735
function pop_testset()
15,114✔
1736
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
30,228✔
1737
    ret = isempty(testsets) ? fallback_testset : pop!(testsets)
30,228✔
1738
    setindex!(task_local_storage(), testsets, :__BASETESTNEXT__)
15,114✔
1739
    return ret
15,114✔
1740
end
1741

1742
"""
1743
    get_testset_depth()
1744

1745
Return the number of active test sets, not including the default test set
1746
"""
1747
function get_testset_depth()
59,423✔
1748
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
118,180✔
1749
    return length(testsets)
59,423✔
1750
end
1751

1752
_args_and_call(args...; kwargs...) = (args[1:end-1], kwargs, args[end](args[1:end-1]...; kwargs...))
700✔
1753
_materialize_broadcasted(f, args...) = Broadcast.materialize(Broadcast.broadcasted(f, args...))
74✔
1754

1755
"""
1756
    @inferred [AllowedType] f(x)
1757

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

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

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

1769
```jldoctest; setup = :(using InteractiveUtils), filter = r"begin\\n(.|\\n)*end"
1770
julia> f(a) = a > 1 ? 1 : 1.0
1771
f (generic function with 1 method)
1772

1773
julia> typeof(f(2))
1774
Int64
1775

1776
julia> @code_warntype f(2)
1777
MethodInstance for f(::Int64)
1778
  from f(a) @ Main none:1
1779
Arguments
1780
  #self#::Core.Const(f)
1781
  a::Int64
1782
Body::UNION{FLOAT64, INT64}
1783
1 ─ %1 = (a > 1)::Bool
1784
└──      goto #3 if not %1
1785
2 ─      return 1
1786
3 ─      return 1.0
1787

1788
julia> @inferred f(2)
1789
ERROR: return type Int64 does not match inferred return type Union{Float64, Int64}
1790
[...]
1791

1792
julia> @inferred max(1, 2)
1793
2
1794

1795
julia> g(a) = a < 10 ? missing : 1.0
1796
g (generic function with 1 method)
1797

1798
julia> @inferred g(20)
1799
ERROR: return type Float64 does not match inferred return type Union{Missing, Float64}
1800
[...]
1801

1802
julia> @inferred Missing g(20)
1803
1.0
1804

1805
julia> h(a) = a < 10 ? missing : f(a)
1806
h (generic function with 1 method)
1807

1808
julia> @inferred Missing h(20)
1809
ERROR: return type Int64 does not match inferred return type Union{Missing, Float64, Int64}
1810
[...]
1811
```
1812
"""
1813
macro inferred(ex)
936✔
1814
    _inferred(ex, __module__)
936✔
1815
end
1816
macro inferred(allow, ex)
9✔
1817
    _inferred(ex, __module__, allow)
9✔
1818
end
1819
function _inferred(ex, mod, allow = :(Union{}))
1,881✔
1820
    if Meta.isexpr(ex, :ref)
1,881✔
1821
        ex = Expr(:call, :getindex, ex.args...)
16✔
1822
    end
1823
    Meta.isexpr(ex, :call)|| error("@inferred requires a call expression")
945✔
1824
    farg = ex.args[1]
945✔
1825
    if isa(farg, Symbol) && farg !== :.. && first(string(farg)) == '.'
945✔
1826
        farg = Symbol(string(farg)[2:end])
64✔
1827
        ex = Expr(:call, GlobalRef(Test, :_materialize_broadcasted),
32✔
1828
            farg, ex.args[2:end]...)
1829
    end
1830
    Base.remove_linenums!(let ex = ex;
4,403✔
1831
        quote
1832
            let
1833
                allow = $(esc(allow))
1834
                allow isa Type || throw(ArgumentError("@inferred requires a type as second argument"))
1835
                $(if any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex.args)
5,173✔
1836
                    # Has keywords
1837
                    args = gensym()
120✔
1838
                    kwargs = gensym()
120✔
1839
                    quote
120✔
1840
                        $(esc(args)), $(esc(kwargs)), result = $(esc(Expr(:call, _args_and_call, ex.args[2:end]..., ex.args[1])))
1841
                        inftypes = $(gen_call_with_extracted_types(mod, Base.return_types, :($(ex.args[1])($(args)...; $(kwargs)...))))
1842
                    end
1843
                else
1844
                    # No keywords
1845
                    quote
1,770✔
1846
                        args = ($([esc(ex.args[i]) for i = 2:length(ex.args)]...),)
1847
                        result = $(esc(ex.args[1]))(args...)
1848
                        inftypes = Base.return_types($(esc(ex.args[1])), Base.typesof(args...))
1849
                    end
1850
                end)
1851
                @assert length(inftypes) == 1
1852
                rettype = result isa Type ? Type{result} : typeof(result)
1853
                rettype <: allow || rettype == typesplit(inftypes[1], allow) || error("return type $rettype does not match inferred return type $(inftypes[1])")
1854
                result
1855
            end
1856
        end
1857
    end)
1858
end
1859

1860
function is_in_mods(m::Module, recursive::Bool, mods)
880,151✔
1861
    while true
880,151✔
1862
        m in mods && return true
4,138,417✔
1863
        recursive || return false
1,635,915✔
1864
        p = parentmodule(m)
458,389✔
1865
        p === m && return false
458,389✔
1866
        m = p
227,186✔
1867
    end
227,186✔
1868
end
1869

1870
"""
1871
    detect_ambiguities(mod1, mod2...; recursive=false,
1872
                                      ambiguous_bottom=false,
1873
                                      allowed_undefineds=nothing)
1874

1875
Return a vector of `(Method,Method)` pairs of ambiguous methods
1876
defined in the specified modules.
1877
Use `recursive=true` to test in all submodules.
1878

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

1883
See [`Test.detect_unbound_args`](@ref) for an explanation of
1884
`allowed_undefineds`.
1885

1886
!!! compat "Julia 1.8"
1887
    `allowed_undefineds` requires at least Julia 1.8.
1888
"""
1889
function detect_ambiguities(mods::Module...;
36✔
1890
                            recursive::Bool = false,
1891
                            ambiguous_bottom::Bool = false,
1892
                            allowed_undefineds = nothing)
1893
    @nospecialize
18✔
1894
    ambs = Set{Tuple{Method,Method}}()
18✔
1895
    mods = collect(mods)::Vector{Module}
36✔
1896
    function sortdefs(m1::Method, m2::Method)
74✔
1897
        ord12 = cmp(m1.file, m2.file)
56✔
1898
        if ord12 == 0
56✔
1899
            ord12 = cmp(m1.line, m2.line)
56✔
1900
        end
1901
        return ord12 <= 0 ? (m1, m2) : (m2, m1)
84✔
1902
    end
1903
    function examine(mt::Core.MethodTable)
199,654✔
1904
        for m in Base.MethodList(mt)
399,074✔
1905
            m.sig == Tuple && continue # ignore Builtins
576,578✔
1906
            is_in_mods(parentmodule(m), recursive, mods) || continue
575,696✔
1907
            world = Base.get_world_counter()
39,234✔
1908
            ambig = Ref{Int32}(0)
39,234✔
1909
            ms = Base._methods_by_ftype(m.sig, nothing, -1, world, true, Ref(typemin(UInt)), Ref(typemax(UInt)), ambig)::Vector
39,234✔
1910
            ambig[] == 0 && continue
39,234✔
1911
            for match2 in ms
2,244✔
1912
                match2 = match2::Core.MethodMatch
17,083✔
1913
                m2 = match2.method
17,083✔
1914
                 if !(m === m2 || Base.morespecific(m2.sig, m.sig))
31,922✔
1915
                    if Base.isambiguous(m, m2; ambiguous_bottom)
6,468✔
1916
                        push!(ambs, sortdefs(m, m2))
84✔
1917
                    end
1918
                end
1919
            end
17,083✔
1920
        end
576,578✔
1921
    end
1922
    work = Base.loaded_modules_array()
18✔
1923
    filter!(mod -> mod === parentmodule(mod), work) # some items in loaded_modules_array are not top modules (really just Base)
739✔
1924
    while !isempty(work)
2,490✔
1925
        mod = pop!(work)
2,472✔
1926
        for n in names(mod, all = true)
2,472✔
1927
            Base.isdeprecated(mod, n) && continue
466,922✔
1928
            if !isdefined(mod, n)
466,922✔
1929
                if is_in_mods(mod, recursive, mods)
36✔
1930
                    if allowed_undefineds === nothing || GlobalRef(mod, n) ∉ allowed_undefineds
4✔
1931
                        println("Skipping ", mod, '.', n)  # typically stale exports
×
1932
                    end
1933
                end
1934
                continue
36✔
1935
            end
1936
            f = Base.unwrap_unionall(getfield(mod, n))
466,886✔
1937
            if isa(f, Module) && f !== mod && parentmodule(f) === mod && nameof(f) === n
466,886✔
1938
                push!(work, f)
1,769✔
1939
            elseif isa(f, DataType) && isdefined(f.name, :mt) && parentmodule(f) === mod && nameof(f) === n && f.name.mt !== Symbol.name.mt && f.name.mt !== DataType.name.mt
465,117✔
1940
                examine(f.name.mt)
199,600✔
1941
            end
1942
        end
469,394✔
1943
    end
2,472✔
1944
    examine(Symbol.name.mt)
18✔
1945
    examine(DataType.name.mt)
18✔
1946
    return collect(ambs)
18✔
1947
end
1948

1949
"""
1950
    detect_unbound_args(mod1, mod2...; recursive=false, allowed_undefineds=nothing)
1951

1952
Return a vector of `Method`s which may have unbound type parameters.
1953
Use `recursive=true` to test in all submodules.
1954

1955
By default, any undefined symbols trigger a warning. This warning can
1956
be suppressed by supplying a collection of `GlobalRef`s for which
1957
the warning can be skipped. For example, setting
1958

1959
```
1960
allowed_undefineds = Set([GlobalRef(Base, :active_repl),
1961
                          GlobalRef(Base, :active_repl_backend)])
1962
```
1963

1964
would suppress warnings about `Base.active_repl` and
1965
`Base.active_repl_backend`.
1966

1967
!!! compat "Julia 1.8"
1968
    `allowed_undefineds` requires at least Julia 1.8.
1969
"""
1970
function detect_unbound_args(mods...;
18✔
1971
                             recursive::Bool = false,
1972
                             allowed_undefineds=nothing)
1973
    @nospecialize mods
9✔
1974
    ambs = Set{Method}()
9✔
1975
    mods = collect(mods)::Vector{Module}
9✔
1976
    function examine(mt::Core.MethodTable)
107,475✔
1977
        for m in Base.MethodList(mt)
214,833✔
1978
            is_in_mods(parentmodule(m), recursive, mods) || continue
304,401✔
1979
            has_unbound_vars(m.sig) || continue
41,826✔
1980
            tuple_sig = Base.unwrap_unionall(m.sig)::DataType
68✔
1981
            if Base.isvatuple(tuple_sig)
68✔
1982
                params = tuple_sig.parameters[1:(end - 1)]
56✔
1983
                tuple_sig = Base.rewrap_unionall(Tuple{params...}, m.sig)
112✔
1984
                world = Base.get_world_counter()
56✔
1985
                mf = ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tuple_sig, nothing, world)
56✔
1986
                if mf !== nothing && mf !== m && mf.sig <: tuple_sig
56✔
1987
                    continue
48✔
1988
                end
1989
            end
1990
            push!(ambs, m)
20✔
1991
        end
304,401✔
1992
    end
1993
    work = Base.loaded_modules_array()
9✔
1994
    filter!(mod -> mod === parentmodule(mod), work) # some items in loaded_modules_array are not top modules (really just Base)
381✔
1995
    while !isempty(work)
1,314✔
1996
        mod = pop!(work)
1,305✔
1997
        for n in names(mod, all = true)
1,305✔
1998
            Base.isdeprecated(mod, n) && continue
250,491✔
1999
            if !isdefined(mod, n)
250,491✔
2000
                if is_in_mods(mod, recursive, mods)
18✔
2001
                    if allowed_undefineds === nothing || GlobalRef(mod, n) ∉ allowed_undefineds
2✔
2002
                        println("Skipping ", mod, '.', n)  # typically stale exports
×
2003
                    end
2004
                end
2005
                continue
18✔
2006
            end
2007
            f = Base.unwrap_unionall(getfield(mod, n))
250,473✔
2008
            if isa(f, Module) && f !== mod && parentmodule(f) === mod && nameof(f) === n
250,473✔
2009
                push!(work, f)
942✔
2010
            elseif isa(f, DataType) && isdefined(f.name, :mt) && parentmodule(f) === mod && nameof(f) === n && f.name.mt !== Symbol.name.mt && f.name.mt !== DataType.name.mt
249,531✔
2011
                examine(f.name.mt)
107,448✔
2012
            end
2013
        end
251,796✔
2014
    end
1,305✔
2015
    examine(Symbol.name.mt)
9✔
2016
    examine(DataType.name.mt)
9✔
2017
    return collect(ambs)
9✔
2018
end
2019

2020
function has_unbound_vars(@nospecialize sig)
20,947✔
2021
    while sig isa UnionAll
20,947✔
2022
        var = sig.var
4,377✔
2023
        sig = sig.body
4,377✔
2024
        if !Core.Compiler.constrains_param(var, sig, #=covariant=#true, #=type_constrains=#true)
4,377✔
2025
            return true
68✔
2026
        end
2027
    end
4,309✔
2028
    return false
20,879✔
2029
end
2030

2031

2032
"""
2033
The `GenericString` can be used to test generic string APIs that program to
2034
the `AbstractString` interface, in order to ensure that functions can work
2035
with string types besides the standard `String` type.
2036
"""
2037
struct GenericString <: AbstractString
2038
    string::AbstractString
60,592✔
2039
end
2040
Base.ncodeunits(s::GenericString) = ncodeunits(s.string)::Int
132,094✔
2041
Base.codeunit(s::GenericString) = codeunit(s.string)::Type{<:Union{UInt8, UInt16, UInt32}}
21✔
2042
Base.codeunit(s::GenericString, i::Integer) = codeunit(s.string, i)::Union{UInt8, UInt16, UInt32}
108✔
2043
Base.isvalid(s::GenericString, i::Integer) = isvalid(s.string, i)::Bool
1,714,628✔
2044
Base.iterate(s::GenericString, i::Integer=1) = iterate(s.string, i)::Union{Nothing,Tuple{AbstractChar,Int}}
8,731✔
2045
Base.reverse(s::GenericString) = GenericString(reverse(s.string))
75✔
2046
Base.reverse(s::SubString{GenericString}) =
75✔
2047
    GenericString(typeof(s.string)(reverse(String(s))))
2048

2049
"""
2050
The `GenericSet` can be used to test generic set APIs that program to
2051
the `AbstractSet` interface, in order to ensure that functions can work
2052
with set types besides the standard `Set` and `BitSet` types.
2053
"""
2054
struct GenericSet{T} <: AbstractSet{T}
2055
    s::AbstractSet{T}
10✔
2056
end
2057

2058
"""
2059
The `GenericDict` can be used to test generic dict APIs that program to
2060
the `AbstractDict` interface, in order to ensure that functions can work
2061
with associative types besides the standard `Dict` type.
2062
"""
2063
struct GenericDict{K,V} <: AbstractDict{K,V}
2064
    s::AbstractDict{K,V}
8✔
2065
end
2066

2067
for G in (GenericSet, GenericDict)
2068
    @eval begin
2069
        Base.iterate(s::$G, state...) = iterate(s.s, state...)
3,535✔
2070
    end
2071
    for f in (:isempty, :length)
2072
        @eval begin
2073
            Base.$f(s::$G) = $f(s.s)
108✔
2074
        end
2075
    end
2076
end
2077

2078
Base.get(s::GenericDict, x, y) = get(s.s, x, y)
228✔
2079

2080
"""
2081
The `GenericArray` can be used to test generic array APIs that program to
2082
the `AbstractArray` interface, in order to ensure that functions can work
2083
with array types besides the standard `Array` type.
2084
"""
2085
struct GenericArray{T,N} <: AbstractArray{T,N}
2086
    a::Array{T,N}
671✔
2087
end
2088

2089
GenericArray{T}(args...) where {T} = GenericArray(Array{T}(args...))
344✔
2090
GenericArray{T,N}(args...) where {T,N} = GenericArray(Array{T,N}(args...))
×
2091

2092
"""
2093
The `GenericOrder` can be used to test APIs for their support
2094
of generic ordered types.
2095
"""
2096
struct GenericOrder{T}
2097
    val::T
6,924✔
2098
end
2099
Base.isless(x::GenericOrder, y::GenericOrder) = isless(x.val,y.val)
13,656✔
2100

2101
Base.keys(a::GenericArray) = keys(a.a)
×
2102
Base.axes(a::GenericArray) = axes(a.a)
1,224✔
2103
Base.length(a::GenericArray) = length(a.a)
3,880✔
2104
Base.size(a::GenericArray) = size(a.a)
23✔
2105
Base.IndexStyle(::Type{<:GenericArray}) = IndexLinear()
481✔
2106
Base.getindex(a::GenericArray, i::Int) = a.a[i]
4,630✔
2107
Base.setindex!(a::GenericArray, x, i::Int) = a.a[i] = x
3,686✔
2108

2109
Base.similar(A::GenericArray, s::Integer...) = GenericArray(similar(A.a, s...))
104✔
2110

2111
"`guardseed(f)` runs the function `f()` and then restores the
2112
state of the global RNG as it was before."
2113
function guardseed(f::Function, r::AbstractRNG=default_rng())
32✔
2114
    old = copy(r)
32✔
2115
    try
17✔
2116
        f()
17✔
2117
    finally
2118
        copy!(r, old)
17✔
2119
    end
2120
end
2121

2122
"`guardseed(f, seed)` is equivalent to running `Random.seed!(seed); f()` and
2123
then restoring the state of the global RNG as it was before."
2124
guardseed(f::Function, seed::Union{Vector{UInt64},Vector{UInt32},Integer,NTuple{4,UInt64}}) = guardseed() do
8✔
2125
    Random.seed!(seed)
8✔
2126
    f()
8✔
2127
end
2128

2129
function _check_bitarray_consistency(B::BitArray{N}) where N
8✔
2130
    n = length(B)
8✔
2131
    if N ≠ 1
8✔
2132
        all(d ≥ 0 for d in B.dims) || (@warn("Negative d in dims: $(B.dims)"); return false)
4✔
2133
        prod(B.dims) ≠ n && (@warn("Inconsistent dims/len: prod(dims)=$(prod(B.dims)) len=$n"); return false)
4✔
2134
    end
2135
    Bc = B.chunks
8✔
2136
    nc = length(Bc)
8✔
2137
    nc == Base.num_bit_chunks(n) || (@warn("Incorrect chunks length for length $n: expected=$(Base.num_bit_chunks(n)) actual=$nc"); return false)
8✔
2138
    n == 0 && return true
8✔
2139
    Bc[end] & Base._msk_end(n) == Bc[end] || (@warn("Nonzero bits in chunk after `BitArray` end"); return false)
8✔
2140
    return true
8✔
2141
end
2142

2143
include("logging.jl")
2144
include("precompile.jl")
2145

2146
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

© 2025 Coveralls, Inc