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

JuliaLang / julia / #37632

26 Sep 2023 06:44AM UTC coverage: 86.999% (-0.9%) from 87.914%
#37632

push

local

web-flow
inference: make `throw` block deoptimization concrete-eval friendly (#49235)

The deoptimization can sometimes destroy the effects analysis and
disable [semi-]concrete evaluation that is otherwise possible. This is
because the deoptimization was designed with the type domain
profitability in mind (#35982), and hasn't been adequately considering
the effects domain.

This commit makes the deoptimization aware of the effects domain more
and enables the `throw` block deoptimization only when the effects
already known to be ineligible for concrete-evaluation.

In our current effect system, `ALWAYS_FALSE`/`false` means that the
effect can not be refined to `ALWAYS_TRUE`/`true` anymore (unless given
user annotation later). Therefore we can enable the `throw` block
deoptimization without hindering the chance of concrete-evaluation when
any of the following conditions are met:
- `effects.consistent === ALWAYS_FALSE`
- `effects.effect_free === ALWAYS_FALSE`
- `effects.terminates === false`
- `effects.nonoverlayed === false`

Here are some numbers:

| Metric | master | this commit | #35982 reverted (set
`unoptimize_throw_blocks=false`) |

|-------------------------|-----------|-------------|--------------------------------------------|
| Base (seconds) | 15.579300 | 15.206645 | 15.296319 |
| Stdlibs (seconds) | 17.919013 | 17.667094 | 17.738128 |
| Total (seconds) | 33.499279 | 32.874737 | 33.035448 |
| Precompilation (seconds) | 49.967516 | 49.421121 | 49.999998 |
| First time `plot(rand(10,3))` [^1] | `2.476678 seconds (11.74 M
allocations)` | `2.430355 seconds (11.77 M allocations)` | `2.514874
seconds (11.64 M allocations)` |
| First time `solve(prob, QNDF())(5.0)` [^2] | `4.469492 seconds (15.32
M allocations)` | `4.499217 seconds (15.41 M allocations)` | `4.470772
seconds (15.38 M allocations)` |

[^1]: With disabling precompilation of Plots.jl.
[^2]: With disabling precompilation of OrdinaryDiffEq.

These numbers ma... (continued)

7 of 7 new or added lines in 1 file covered. (100.0%)

73407 of 84377 relevant lines covered (87.0%)

11275130.05 hits per line

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

94.89
/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
2✔
15

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

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

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

33
const 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))
4,079✔
51
end
52
in_file(frame, file) = string(frame.file) == file
1,681✔
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__))
20✔
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 = @something(macrocall_location(bt, @__FILE__), return nothing)
18✔
68
    test = internal - 1 + @something(findfirst(ip -> any(frame -> in_file(frame, file_t), StackTraces.lookup(ip)), @view bt[internal:end]), return nothing)
70✔
69
    testset = test - 1 + @something(macrocall_location(@view(bt[test:end]), file_ts), return nothing)
18✔
70

71
    # If stacktrace locations differ, include frames until the `@testset` appears.
72
    test != testset && return testset
98✔
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)
48✔
78
    isnothing(outer_frame) && return nothing
26✔
79
    # The `@test` call occurred directly in scope of a `@testset`.
80
    # The __source__ from `@test` will be printed in the test message upon failure.
81
    # There is no need to include more frames, but always include at least the internal macrocall location in the stacktrace.
82
    in_file(frames[outer_frame], file_t) && return internal
13✔
83
    # The `@test` call was inlined, so we still need to include the callsite.
84
    return testset
×
85
end
86

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

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

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

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

106
"""
107
    Test.Result
108

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

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

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

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

90✔
144
"""
145
    Test.Fail <: Test.Result
146

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

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

175
function Base.show(io::IO, t::Fail)
2,081✔
176
    printstyled(io, "Test Failed"; bold=true, color=Base.error_color())
2,081✔
177
    print(io, " at ")
2,081✔
178
    printstyled(io, something(t.source.file, :none), ":", t.source.line, "\n"; bold=true, color=:default)
4,161✔
179
    print(io, "  Expression: ", t.orig_expr)
2,081✔
180
    value, data = t.value, t.data
2,081✔
181
    if t.test_type === :test_throws_wrong
2,081✔
182
        # An exception was thrown, but it was of the wrong type
183
        if t.message_only
5✔
184
            print(io, "\n    Expected: ", data)
4✔
185
            print(io, "\n     Message: ", value)
4✔
186
        else
187
            print(io, "\n    Expected: ", data)
1✔
188
            print(io, "\n      Thrown: ", value)
1✔
189
            print(io, "\n")
1✔
190
            if t.backtrace !== nothing
1✔
191
                # Capture error message and indent to match
192
                join(io, ("      " * line for line in split(t.backtrace, "\n")), "\n")
6✔
193
            end
194
        end
195
    elseif t.test_type === :test_throws_nothing
2,076✔
196
        # An exception was expected, but no exception was thrown
197
        print(io, "\n    Expected: ", data)
5✔
198
        print(io, "\n  No exception thrown")
5✔
199
    elseif t.test_type === :test
2,071✔
200
        if data !== nothing
2,071✔
201
            # The test was an expression, so display the term-by-term
202
            # evaluated version as well
203
            print(io, "\n   Evaluated: ", data)
252✔
204
        end
205
        if t.context !== nothing
2,071✔
206
            print(io, "\n     Context: ", t.context)
194✔
207
        end
208
    end
209
    println(io) # add some visual space to separate sequential failures
2,081✔
210
end
211

212
"""
213
    Test.Error <: Test.Result
214

215
The test condition couldn't be evaluated due to an exception, or
216
it evaluated to something other than a [`Bool`](@ref).
217
In the case of `@test_broken` it is used to indicate that an
218
unexpected `Pass` `Result` occurred.
219
"""
220
struct Error <: Result
221
    test_type::Symbol
222
    orig_expr::String
223
    value::String
224
    backtrace::String
225
    source::LineNumberNode
226

227
    function Error(test_type::Symbol, orig_expr, value, bt, source::LineNumberNode)
66✔
228
        if test_type === :test_error
66✔
229
            bt = scrub_exc_stack(bt, nothing, extract_file(source))
24✔
230
        end
231
        if test_type === :test_error || test_type === :nontest_error
120✔
232
            bt_str = try # try the latest world for this, since we might have eval'd new code for show
35✔
233
                    Base.invokelatest(sprint, Base.show_exception_stack, bt; context=stdout)
37✔
234
                catch ex
235
                    "#=ERROR showing exception stack=# " *
2✔
236
                        try
237
                            sprint(Base.showerror, ex, catch_backtrace(); context=stdout)
2✔
238
                        catch
239
                            "of type " * string(typeof(ex))
37✔
240
                        end
241
                end
242
        else
243
            bt_str = ""
×
244
        end
245
        value = try # try the latest world for this, since we might have eval'd new code for show
66✔
246
                Base.invokelatest(sprint, show, value, context = :limit => true)
69✔
247
            catch ex
248
                "#=ERROR showing error of type " * string(typeof(value)) * "=# " *
3✔
249
                    try
250
                        sprint(Base.showerror, ex, catch_backtrace(); context=stdout)
3✔
251
                    catch
252
                        "of type " * string(typeof(ex))
69✔
253
                    end
254
            end
255
        return new(test_type,
66✔
256
            string(orig_expr),
257
            value,
258
            bt_str,
259
            source)
260
    end
261
end
262

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

293
"""
294
    Test.Broken <: Test.Result
295

296
The test condition is the expected (failed) result of a broken test,
297
or was explicitly skipped with `@test_skip`.
298
"""
1✔
299
struct Broken <: Result
1✔
300
    test_type::Symbol
2,102✔
301
    orig_expr
302
end
303

304
function Base.show(io::IO, t::Broken)
2✔
305
    printstyled(io, "Test Broken\n"; bold=true, color=Base.warn_color())
2✔
306
    if t.test_type === :skipped && !(t.orig_expr === nothing)
2✔
307
        print(io, "  Skipped: ", t.orig_expr)
×
308
    elseif !(t.orig_expr === nothing)
2✔
309
        print(io, "  Expression: ", t.orig_expr)
2✔
310
    end
311
end
312

313
# Types that appear in TestSetException.errors_and_fails we convert eagerly into strings
314
# other types we convert lazily
315
function Serialization.serialize(s::Serialization.AbstractSerializer, t::Pass)
1✔
316
    Serialization.serialize_type(s, typeof(t))
1✔
317
    Serialization.serialize(s, t.test_type)
1✔
318
    Serialization.serialize(s, t.orig_expr === nothing ? nothing : string(t.orig_expr))
2✔
319
    Serialization.serialize(s, t.data === nothing ? nothing : string(t.data))
2✔
320
    Serialization.serialize(s, string(t.value))
1✔
321
    Serialization.serialize(s, t.source === nothing ? nothing : t.source)
2✔
322
    Serialization.serialize(s, t.message_only)
1✔
323
    nothing
1✔
324
end
325

326
function Serialization.serialize(s::Serialization.AbstractSerializer, t::Broken)
1✔
327
    Serialization.serialize_type(s, typeof(t))
1✔
328
    Serialization.serialize(s, t.test_type)
1✔
329
    Serialization.serialize(s, t.orig_expr === nothing ? nothing : string(t.orig_expr))
2✔
330
    nothing
1✔
331
end
332

333

334
#-----------------------------------------------------------------------
335

336
abstract type ExecutionResult end
337

338
struct Returned <: ExecutionResult
339
    value
67,724,215✔
340
    data
341
    source::LineNumberNode
342
end
343

344
struct Threw <: ExecutionResult
345
    exception
226,523✔
346
    backtrace::Union{Nothing,Vector{Any}}
347
    source::LineNumberNode
348
end
349

350
function eval_test(evaluated::Expr, quoted::Expr, source::LineNumberNode, negate::Bool=false)
42,296,315✔
351
    evaled_args = evaluated.args
42,296,290✔
352
    quoted_args = quoted.args
42,296,354✔
353
    n = length(evaled_args)
42,296,374✔
354
    kw_suffix = ""
495✔
355
    if evaluated.head === :comparison
42,296,279✔
356
        args = evaled_args
489✔
357
        res = true
489✔
358
        i = 1
489✔
359
        while i < n
92,127,386✔
360
            a, op, b = args[i], args[i+1], args[i+2]
51,241,986✔
361
            if res
51,241,955✔
362
                res = op(a, b)
51,241,959✔
363
            end
364
            quoted_args[i] = a
51,242,107✔
365
            quoted_args[i+2] = b
51,242,116✔
366
            i += 2
51,242,097✔
367
        end
51,242,095✔
368

369
    elseif evaluated.head === :call
1,410,714✔
370
        op = evaled_args[1]
1,410,714✔
371
        kwargs = (evaled_args[2]::Expr).args  # Keyword arguments from `Expr(:parameters, ...)`
1,410,714✔
372
        args = evaled_args[3:n]
1,410,714✔
373

374
        res = op(args...; kwargs...)
1,755,555✔
375

6✔
376
        # Create "Evaluated" expression which looks like the original call but has all of
377
        # the arguments evaluated
378
        func_sym = quoted_args[1]::Union{Symbol,Expr}
1,410,708✔
379
        if isempty(kwargs)
1,410,708✔
380
            quoted = Expr(:call, func_sym, args...)
1,065,873✔
381
        elseif func_sym === :≈ && !res
344,835✔
382
            quoted = Expr(:call, func_sym, args...)
2✔
383
            kw_suffix = " ($(join(["$k=$v" for (k, v) in kwargs], ", ")))"
2✔
384
        else
385
            kwargs_expr = Expr(:parameters, [Expr(:kw, k, v) for (k, v) in kwargs]...)
344,833✔
386
            quoted = Expr(:call, func_sym, kwargs_expr, args...)
1,755,541✔
387
        end
388
    else
389
        throw(ArgumentError("Unhandled expression type: $(evaluated.head)"))
×
390
    end
391

392
    if negate
42,296,354✔
393
        res = !res
3,936✔
394
        quoted = Expr(:call, :!, quoted)
3,936✔
395
    end
396

397
    Returned(res,
42,296,351✔
398
             # stringify arguments in case of failure, for easy remote printing
399
             res === true ? quoted : sprint(print, quoted, context=(:limit => true)) * kw_suffix,
400
             source)
401
end
402

403
const comparison_prec = Base.operator_precedence(:(==))
404

405
"""
406
    test_expr!(ex, kws...)
407

408
Preprocess test expressions of function calls with trailing keyword arguments
409
so that e.g. `@test a ≈ b atol=ε` means `@test ≈(a, b, atol=ε)`.
410
"""
411
test_expr!(m, ex) = ex
43,359✔
412

413
function test_expr!(m, ex, kws...)
439✔
414
    ex isa Expr && ex.head === :call || @goto fail
439✔
415
    for kw in kws
439✔
416
        kw isa Expr && kw.head === :(=) || @goto fail
452✔
417
        kw.head = :kw
452✔
418
        push!(ex.args, kw)
452✔
419
    end
470✔
420
    return ex
439✔
421
@label fail
×
422
    error("invalid test macro call: $m $ex $(join(kws," "))")
×
423
end
424

425
# @test - check if the expression evaluates to true
426
"""
427
    @test ex
428
    @test f(args...) key=val ...
429
    @test ex broken=true
430
    @test ex skip=true
431

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

437
# Examples
438
```jldoctest
439
julia> @test true
440
Test Passed
441

442
julia> @test [1, 2] + [2, 1] == [3, 3]
443
Test Passed
444
```
445

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

450
```jldoctest
451
julia> @test π ≈ 3.14 atol=0.01
452
Test Passed
453
```
454

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

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

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

472
# Examples
473

474
```jldoctest
475
julia> @test 2 + 2 ≈ 6 atol=1 broken=true
476
Test Broken
477
  Expression: ≈(2 + 2, 6, atol = 1)
478

479
julia> @test 2 + 2 ≈ 5 atol=1 broken=false
480
Test Passed
481

482
julia> @test 2 + 2 == 5 skip=true
483
Test Broken
484
  Skipped: 2 + 2 == 5
485

486
julia> @test 2 + 2 == 4 skip=false
487
Test Passed
488
```
489

490
!!! compat "Julia 1.7"
491
     The `broken` and `skip` keyword arguments require at least Julia 1.7.
492
"""
493
macro test(ex, kws...)
43,581✔
494
    # Collect the broken/skip keywords and remove them from the rest of keywords
495
    broken = [kw.args[2] for kw in kws if kw.args[1] === :broken]
43,581✔
496
    skip = [kw.args[2] for kw in kws if kw.args[1] === :skip]
43,581✔
497
    kws = filter(kw -> kw.args[1] ∉ (:skip, :broken), kws)
44,507✔
498
    # Validation of broken/skip keywords
499
    for (kw, name) in ((broken, :broken), (skip, :skip))
43,581✔
500
        if length(kw) > 1
87,162✔
501
            error("invalid test macro call: cannot set $(name) keyword multiple times")
×
502
        end
503
    end
87,162✔
504
    if length(skip) > 0 && length(broken) > 0
43,581✔
505
        error("invalid test macro call: cannot set both skip and broken keywords")
×
506
    end
507

508
    # Build the test expression
509
    test_expr!("@test", ex, kws...)
43,581✔
510

511
    result = get_test_result(ex, __source__)
43,581✔
512

513
    ex = Expr(:inert, ex)
43,581✔
514
    result = quote
87,155✔
515
        if $(length(skip) > 0 && esc(skip[1]))
65,786,901✔
516
            record(get_testset(), Broken(:skipped, $ex))
×
517
        else
518
            let _do = $(length(broken) > 0 && esc(broken[1])) ? do_broken_test : do_test
65,812,751✔
519
                _do($result, $ex)
81✔
520
            end
521
        end
522
    end
523
    return result
43,581✔
524
end
525

526
"""
527
    @test_broken ex
528
    @test_broken f(args...) key=val ...
529

530
Indicates a test that should pass but currently consistently fails.
531
Tests that the expression `ex` evaluates to `false` or causes an
532
exception. Returns a `Broken` `Result` if it does, or an `Error` `Result`
533
if the expression evaluates to `true`.  This is equivalent to
534
[`@test ex broken=true`](@ref @test).
535

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

538
# Examples
539
```jldoctest
540
julia> @test_broken 1 == 2
541
Test Broken
542
  Expression: 1 == 2
543

544
julia> @test_broken 1 == 2 atol=0.1
545
Test Broken
546
  Expression: ==(1, 2, atol = 0.1)
547
```
548
"""
549
macro test_broken(ex, kws...)
201✔
550
    test_expr!("@test_broken", ex, kws...)
201✔
551
    result = get_test_result(ex, __source__)
201✔
552
    # code to call do_test with execution result and original expr
553
    ex = Expr(:inert, ex)
201✔
554
    return :(do_broken_test($result, $ex))
201✔
555
end
556

557
"""
558
    @test_skip ex
559
    @test_skip f(args...) key=val ...
560

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

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

568
# Examples
569
```jldoctest
570
julia> @test_skip 1 == 2
571
Test Broken
572
  Skipped: 1 == 2
573

574
julia> @test_skip 1 == 2 atol=0.1
575
Test Broken
576
  Skipped: ==(1, 2, atol = 0.1)
577
```
578
"""
579
macro test_skip(ex, kws...)
16✔
580
    test_expr!("@test_skip", ex, kws...)
16✔
581
    ex = Expr(:inert, ex)
16✔
582
    testres = :(Broken(:skipped, $ex))
16✔
583
    return :(record(get_testset(), $testres))
16✔
584
end
585

586
# An internal function, called by the code generated by the @test
587
# macro to get results of the test expression.
588
# In the special case of a comparison, e.g. x == 5, generate code to
589
# evaluate each term in the comparison individually so the results
590
# can be displayed nicely.
591
function get_test_result(ex, source)
43,782✔
592
    negate = QuoteNode(false)
×
593
    orig_ex = ex
×
594
    # Evaluate `not` wrapped functions separately for pretty-printing failures
595
    if isa(ex, Expr) && ex.head === :call && length(ex.args) == 2 && ex.args[1] === :!
43,782✔
596
        negate = QuoteNode(true)
2,147✔
597
        ex = ex.args[2]
2,147✔
598
    end
599
    # Normalize non-dot comparison operator calls to :comparison expressions
600
    is_splat = x -> isa(x, Expr) && x.head === :...
153,595✔
601
    if isa(ex, Expr) && ex.head === :call && length(ex.args) == 3 &&
58,287✔
602
        first(string(ex.args[1])) != '.' && !is_splat(ex.args[2]) && !is_splat(ex.args[3]) &&
603
        (ex.args[1] === :(==) || Base.operator_precedence(ex.args[1]) == comparison_prec)
604
        ex = Expr(:comparison, ex.args[2], ex.args[1], ex.args[3])
33,470✔
605

606
    # Mark <: and >: as :comparison expressions
607
    elseif isa(ex, Expr) && length(ex.args) == 2 &&
11,921✔
608
        !is_splat(ex.args[1]) && !is_splat(ex.args[2]) &&
609
        Base.operator_precedence(ex.head) == comparison_prec
610
        ex = Expr(:comparison, ex.args[1], ex.head, ex.args[2])
401✔
611
    end
612
    if isa(ex, Expr) && ex.head === :comparison
43,782✔
613
        # pass all terms of the comparison to `eval_comparison`, as an Expr
614
        escaped_terms = [esc(arg) for arg in ex.args]
35,245✔
615
        quoted_terms = [QuoteNode(arg) for arg in ex.args]
35,245✔
616
        testret = :(eval_test(
35,245✔
617
            Expr(:comparison, $(escaped_terms...)),
618
            Expr(:comparison, $(quoted_terms...)),
619
            $(QuoteNode(source)),
620
            $negate,
621
        ))
622
    elseif isa(ex, Expr) && ex.head === :call && ex.args[1] in DISPLAY_FAILED
13,425✔
623
        escaped_func = esc(ex.args[1])
2,833✔
624
        quoted_func = QuoteNode(ex.args[1])
2,833✔
625

626
        escaped_args = []
2,833✔
627
        escaped_kwargs = []
2,833✔
628

629
        # Keywords that occur before `;`. Note that the keywords are being revised into
630
        # a form we can splat.
631
        for a in ex.args[2:end]
2,833✔
632
            if isa(a, Expr) && a.head === :kw
5,716✔
633
                push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a.args[1]), esc(a.args[2])))
480✔
634
            end
635
        end
8,549✔
636

637
        # Keywords that occur after ';'
638
        parameters_expr = ex.args[2]
2,833✔
639
        if isa(parameters_expr, Expr) && parameters_expr.head === :parameters
2,833✔
640
            for a in parameters_expr.args
10✔
641
                if isa(a, Expr) && a.head === :kw
10✔
642
                    push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a.args[1]), esc(a.args[2])))
4✔
643
                elseif isa(a, Expr) && a.head === :...
6✔
644
                    push!(escaped_kwargs, Expr(:..., esc(a.args[1])))
2✔
645
                elseif isa(a, Expr) && a.head === :.
4✔
646
                    push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a.args[2].value), esc(Expr(:., a.args[1], QuoteNode(a.args[2].value)))))
1✔
647
                elseif isa(a, Symbol)
3✔
648
                    push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a), esc(a)))
3✔
649
                end
650
            end
20✔
651
        end
652

653
        # Positional arguments
654
        for a in ex.args[2:end]
2,833✔
655
            isa(a, Expr) && a.head in (:kw, :parameters) && continue
5,716✔
656

657
            if isa(a, Expr) && a.head === :...
5,226✔
658
                push!(escaped_args, Expr(:..., esc(a.args[1])))
2✔
659
            else
660
                push!(escaped_args, esc(a))
5,224✔
661
            end
662
        end
8,549✔
663

664
        testret = :(eval_test(
2,833✔
665
            Expr(:call, $escaped_func, Expr(:parameters, $(escaped_kwargs...)), $(escaped_args...)),
666
            Expr(:call, $quoted_func),
667
            $(QuoteNode(source)),
668
            $negate,
669
        ))
670
    else
671
        ex = Expr(:block, source, esc(orig_ex))
5,704✔
672
        testret = :(Returned($ex, nothing, $(QuoteNode(source))))
5,704✔
673
    end
674
    result = quote
43,782✔
675
        try
65,787,143✔
676
            $testret
88,838,711✔
677
        catch _e
678
            _e isa InterruptException && rethrow()
102✔
679
            Threw(_e, Base.current_exceptions(), $(QuoteNode(source)))
65,787,239✔
680
        end
681
    end
682
    result
43,782✔
683
end
684

685
# An internal function, called by the code generated by the @test
686
# macro to actually perform the evaluation and manage the result.
687
function do_test(result::ExecutionResult, orig_expr)
67,723,964✔
688
    # get_testset() returns the most recently added test set
689
    # We then call record() with this test set and the test result
690
    if isa(result, Returned)
67,723,947✔
691
        # expr, in the case of a comparison, will contain the
692
        # comparison with evaluated values of each term spliced in.
693
        # For anything else, just contains the test expression.
694
        # value is the evaluated value of the whole test expression.
695
        # Ideally it is true, but it may be false or non-Boolean.
696
        value = result.value
67,723,926✔
697
        testres = if isa(value, Bool)
67,724,008✔
698
            # a true value Passes
699
            value ? Pass(:test, orig_expr, result.data, value, result.source) :
67,725,058✔
700
                    Fail(:test, orig_expr, result.data, value, nothing, result.source, false)
701
        else
702
            # If the result is non-Boolean, this counts as an Error
703
            Error(:test_nonbool, orig_expr, value, nothing, result.source)
67,723,755✔
704
        end
705
    else
706
        # The predicate couldn't be evaluated without throwing an
707
        # exception, so that is an Error and not a Fail
708
        @assert isa(result, Threw)
11✔
709
        testres = Error(:test_error, orig_expr, result.exception, result.backtrace::Vector{Any}, result.source)
11✔
710
    end
711
    isa(testres, Pass) || trigger_test_failure_break(result)
67,724,861✔
712
    record(get_testset(), testres)
67,723,749✔
713
end
714

715
function do_broken_test(result::ExecutionResult, orig_expr)
406✔
716
    testres = Broken(:test, orig_expr)
406✔
717
    # Assume the test is broken and only change if the result is true
718
    if isa(result, Returned)
406✔
719
        value = result.value
286✔
720
        if isa(value, Bool)
286✔
721
            if value
286✔
722
                testres = Error(:test_unbroken, orig_expr, value, nothing, result.source)
2✔
723
            end
724
        else
725
            # If the result is non-Boolean, this counts as an Error
726
            testres = Error(:test_nonbool, orig_expr, value, nothing, result.source)
×
727
        end
728
    end
729
    record(get_testset(), testres)
406✔
730
end
731

732
#-----------------------------------------------------------------------
733

734
"""
735
    @test_throws exception expr
736

737
Tests that the expression `expr` throws `exception`.
738
The exception may specify either a type,
739
a string, regular expression, or list of strings occurring in the displayed error message,
740
a matching function,
741
or a value (which will be tested for equality by comparing fields).
742
Note that `@test_throws` does not support a trailing keyword form.
743

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

747
# Examples
748
```jldoctest
749
julia> @test_throws BoundsError [1, 2, 3][4]
750
Test Passed
751
      Thrown: BoundsError
752

753
julia> @test_throws DimensionMismatch [1, 2, 3] + [1, 2]
754
Test Passed
755
      Thrown: DimensionMismatch
756

757
julia> @test_throws "Try sqrt(Complex" sqrt(-1)
758
Test Passed
759
     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))."
760
```
761

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

764
- `["Try", "Complex"]` (a list of strings)
765
- `r"Try sqrt\\([Cc]omplex"` (a regular expression)
766
- `str -> occursin("complex", str)` (a matching function)
767
"""
768
macro test_throws(extype, ex)
4,402✔
769
    orig_ex = Expr(:inert, ex)
4,402✔
770
    ex = Expr(:block, __source__, esc(ex))
4,402✔
771
    result = quote
4,402✔
772
        try
65,766✔
773
            Returned($ex, nothing, $(QuoteNode(__source__)))
129,567✔
774
        catch _e
775
            if $(esc(extype)) != InterruptException && _e isa InterruptException
65,766✔
776
                rethrow()
×
777
            end
778
            Threw(_e, Base.current_exceptions(), $(QuoteNode(__source__)))
131,529✔
779
        end
780
    end
781
    return :(do_test_throws($result, $orig_ex, $(esc(extype))))
4,402✔
782
end
783

784
const MACROEXPAND_LIKE = Symbol.(("@macroexpand", "@macroexpand1", "macroexpand"))
785

786
# An internal function, called by the code generated by @test_throws
787
# to evaluate and catch the thrown exception - if it exists
788
function do_test_throws(result::ExecutionResult, orig_expr, extype)
161,672✔
789
    if isa(result, Threw)
161,672✔
790
        # Check that the right type of exception was thrown
791
        success = false
161,666✔
792
        message_only = false
420✔
793
        exc = result.exception
161,666✔
794
        # NB: Throwing LoadError from macroexpands is deprecated, but in order to limit
795
        # the breakage in package tests we add extra logic here.
796
        from_macroexpand =
167,206✔
797
            orig_expr isa Expr &&
798
            orig_expr.head in (:call, :macrocall) &&
799
            orig_expr.args[1] in MACROEXPAND_LIKE
800
        if isa(extype, Type)
161,666✔
801
            success =
158,928✔
802
                if from_macroexpand && extype == LoadError && exc isa Exception
803
                    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✔
804
                    true
×
805
                else
806
                    isa(exc, extype)
317,852✔
807
                end
808
        elseif isa(extype, Exception) || !isa(exc, Exception)
2,991✔
809
            if extype isa LoadError && !(exc isa LoadError) && typeof(extype.error) == typeof(exc)
2,491✔
810
                extype = extype.error # deprecated
2✔
811
            end
812
            if isa(exc, typeof(extype))
2,491✔
813
                success = true
84✔
814
                for fld in 1:nfields(extype)
4,974✔
815
                    if !isequal(getfield(extype, fld), getfield(exc, fld))
2,595✔
816
                        success = false
×
817
                        break
×
818
                    end
819
                end
2,491✔
820
            end
821
        else
822
            message_only = true
247✔
823
            exc = sprint(showerror, exc)
247✔
824
            success = contains_warn(exc, extype)
247✔
825
            exc = repr(exc)
247✔
826
            if isa(extype, AbstractString)
247✔
827
                extype = repr(extype)
178✔
828
            elseif isa(extype, Function)
69✔
829
                extype = "< match function >"
1✔
830
            end
831
        end
832
        if success
161,664✔
833
            testres = Pass(:test_throws, orig_expr, extype, exc, result.source, message_only)
161,658✔
834
        else
835
            if result.backtrace !== nothing
6✔
836
                bt = scrub_exc_stack(result.backtrace, nothing, extract_file(result.source))
12✔
837
                bt_str = try # try the latest world for this, since we might have eval'd new code for show
6✔
838
                    Base.invokelatest(sprint, Base.show_exception_stack, bt; context=stdout)
6✔
839
                catch ex
840
                    "#=ERROR showing exception stack=# " *
×
841
                        try
842
                            sprint(Base.showerror, ex, catch_backtrace(); context=stdout)
×
843
                        catch
844
                            "of type " * string(typeof(ex))
6✔
845
                        end
846
                end
847
            else
848
                bt_str = nothing
×
849
            end
850
            testres = Fail(:test_throws_wrong, orig_expr, extype, exc, nothing, result.source, message_only, bt_str)
161,670✔
851
        end
852
    else
853
        testres = Fail(:test_throws_nothing, orig_expr, extype, nothing, nothing, result.source, false)
6✔
854
    end
855
    record(get_testset(), testres)
161,670✔
856
end
857

858
#-----------------------------------------------------------------------
859
# Test for log messages
860

861
# Test for warning messages (deprecated)
862

863
contains_warn(output, s::AbstractString) = occursin(s, output)
205✔
864
contains_warn(output, s::Regex) = occursin(s, output)
3✔
865
contains_warn(output, s::Function) = s(output)
61✔
866
contains_warn(output, S::Union{AbstractArray,Tuple}) = all(s -> contains_warn(output, s), S)
20✔
867

868
"""
869
    @test_warn msg expr
870

871
Test whether evaluating `expr` results in [`stderr`](@ref) output that contains
872
the `msg` string or matches the `msg` regular expression.  If `msg` is
873
a boolean function, tests whether `msg(output)` returns `true`.  If `msg` is a
874
tuple or array, checks that the error output contains/matches each item in `msg`.
875
Returns the result of evaluating `expr`.
876

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

879
Note: Warnings generated by `@warn` cannot be tested with this macro. Use
880
[`@test_logs`](@ref) instead.
881
"""
882
macro test_warn(msg, expr)
13✔
883
    quote
13✔
884
        let fname = tempname()
4✔
885
            try
4✔
886
                ret = open(fname, "w") do f
4✔
887
                    redirect_stderr(f) do
14✔
888
                        $(esc(expr))
14✔
889
                    end
890
                end
891
                @test contains_warn(read(fname, String), $(esc(msg)))
4✔
892
                ret
4✔
893
            finally
894
                rm(fname, force=true)
4✔
895
            end
896
        end
897
    end
898
end
899

900
"""
901
    @test_nowarn expr
902

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

906
Note: The absence of warnings generated by `@warn` cannot be tested
907
with this macro. Use [`@test_logs`](@ref) instead.
908
"""
909
macro test_nowarn(expr)
20✔
910
    quote
20✔
911
        # Duplicate some code from `@test_warn` to allow printing the content of
912
        # `stderr` again to `stderr` here while suppressing it for `@test_warn`.
913
        # If that shouldn't be used, it would be possible to just use
914
        #     @test_warn isempty $(esc(expr))
915
        # here.
916
        let fname = tempname()
2✔
917
            try
2✔
918
                ret = open(fname, "w") do f
2✔
919
                    redirect_stderr(f) do
20✔
920
                        $(esc(expr))
20✔
921
                    end
922
                end
923
                stderr_content = read(fname, String)
2✔
924
                print(stderr, stderr_content) # this is helpful for debugging
2✔
925
                @test isempty(stderr_content)
×
926
                ret
2✔
927
            finally
928
                rm(fname, force=true)
2✔
929
            end
930
        end
931
    end
932
end
933

934
#-----------------------------------------------------------------------
935

936
# The AbstractTestSet interface is defined by two methods:
937
# record(AbstractTestSet, Result)
938
#   Called by do_test after a test is evaluated
939
# finish(AbstractTestSet)
940
#   Called after the test set has been popped from the test set stack
941
abstract type AbstractTestSet end
942

943
"""
944
    record(ts::AbstractTestSet, res::Result)
945

946
Record a result to a testset. This function is called by the `@testset`
947
infrastructure each time a contained `@test` macro completes, and is given the
948
test result (which could be an `Error`). This will also be called with an `Error`
949
if an exception is thrown inside the test block but outside of a `@test` context.
950
"""
951
function record end
952

953
"""
954
    finish(ts::AbstractTestSet)
955

956
Do any final processing necessary for the given testset. This is called by the
957
`@testset` infrastructure after a test block executes.
958

959
Custom `AbstractTestSet` subtypes should call `record` on their parent (if there
960
is one) to add themselves to the tree of test results. This might be implemented
961
as:
962

963
```julia
964
if get_testset_depth() != 0
965
    # Attach this test set to the parent test set
966
    parent_ts = get_testset()
967
    record(parent_ts, self)
968
    return self
969
end
970
```
971
"""
972
function finish end
973

974
"""
975
    TestSetException
976

977
Thrown when a test set finishes and not all tests passed.
978
"""
979
struct TestSetException <: Exception
980
    pass::Int
227✔
981
    fail::Int
982
    error::Int
983
    broken::Int
984
    errors_and_fails::Vector{Union{Fail, Error}}
985
end
986

987
function Base.show(io::IO, ex::TestSetException)
12✔
988
    print(io, "Some tests did not pass: ")
12✔
989
    print(io, ex.pass,  " passed, ")
12✔
990
    print(io, ex.fail,  " failed, ")
12✔
991
    print(io, ex.error, " errored, ")
12✔
992
    print(io, ex.broken, " broken.")
12✔
993
end
994

995
function Base.showerror(io::IO, ex::TestSetException, bt; backtrace=true)
22✔
996
    printstyled(io, string(ex), color=Base.error_color())
11✔
997
end
998

999
#-----------------------------------------------------------------------
1000

1001
"""
1002
    FallbackTestSet
1003

1004
A simple fallback test set that throws immediately on a failure.
1005
"""
1006
struct FallbackTestSet <: AbstractTestSet end
1007
fallback_testset = FallbackTestSet()
1008

1009
struct FallbackTestSetException <: Exception
1010
    msg::String
8✔
1011
end
1012

1013
function Base.showerror(io::IO, ex::FallbackTestSetException, bt; backtrace=true)
16✔
1014
    printstyled(io, ex.msg, color=Base.error_color())
8✔
1015
end
1016

1017
# Records nothing, and throws an error immediately whenever a Fail or
1018
# Error occurs. Takes no action in the event of a Pass or Broken result
1019
record(ts::FallbackTestSet, t::Union{Pass, Broken}) = t
×
1020
function record(ts::FallbackTestSet, t::Union{Fail, Error})
5✔
1021
    println(t)
5✔
1022
    throw(FallbackTestSetException("There was an error during testing"))
5✔
1023
end
1024
# We don't need to do anything as we don't record anything
1025
finish(ts::FallbackTestSet) = ts
×
1026

1027
#-----------------------------------------------------------------------
1028

1029
"""
1030
    ContextTestSet
1031

1032
Passes test failures through to the parent test set, while adding information
1033
about a context object that is being tested.
1034
"""
1035
struct ContextTestSet <: AbstractTestSet
1036
    parent_ts::AbstractTestSet
194✔
1037
    context_name::Union{Symbol, Expr}
1038
    context::Any
1039
end
1040

1041
function ContextTestSet(name::Union{Symbol, Expr}, @nospecialize(context))
194✔
1042
    if (name isa Expr) && (name.head != :tuple)
194✔
1043
        error("Invalid syntax: $(name)")
×
1044
    end
1045
    return ContextTestSet(get_testset(), name, context)
194✔
1046
end
1047
record(c::ContextTestSet, t) = record(c.parent_ts, t)
116✔
1048
function record(c::ContextTestSet, t::Fail)
182✔
1049
    context = string(c.context_name, " = ", c.context)
364✔
1050
    context = t.context === nothing ? context : string(t.context, "\n              ", context)
267✔
1051
    record(c.parent_ts, Fail(t.test_type, t.orig_expr, t.data, t.value, context, t.source, t.message_only))
182✔
1052
end
1053

1054
#-----------------------------------------------------------------------
1055

1056
"""
1057
    DefaultTestSet
1058

1059
If using the DefaultTestSet, the test results will be recorded. If there
1060
are any `Fail`s or `Error`s, an exception will be thrown only at the end,
1061
along with a summary of the test results.
1062
"""
1063
mutable struct DefaultTestSet <: AbstractTestSet
1064
    description::String
10,106✔
1065
    results::Vector{Any}
1066
    n_passed::Int
1067
    anynonpass::Bool
1068
    verbose::Bool
1069
    showtiming::Bool
1070
    time_start::Float64
1071
    time_end::Union{Float64,Nothing}
1072
    failfast::Bool
1073
    file::Union{String,Nothing}
1074
end
1075
function DefaultTestSet(desc::AbstractString; verbose::Bool = false, showtiming::Bool = true, failfast::Union{Nothing,Bool} = nothing, source = nothing)
20,215✔
1076
    if isnothing(failfast)
10,108✔
1077
        # pass failfast state into child testsets
1078
        parent_ts = get_testset()
10,104✔
1079
        if parent_ts isa DefaultTestSet
10,104✔
1080
            failfast = parent_ts.failfast
9,867✔
1081
        else
1082
            failfast = false
3✔
1083
        end
1084
    end
1085
    return DefaultTestSet(String(desc)::String, [], 0, false, verbose, showtiming, time(), nothing, failfast, extract_file(source))
10,106✔
1086
end
1087
extract_file(source::LineNumberNode) = extract_file(source.file)
135✔
1088
extract_file(file::Symbol) = string(file)
10,027✔
1089
extract_file(::Nothing) = nothing
×
1090

1091
struct FailFastError <: Exception end
3✔
1092

1093
# For a broken result, simply store the result
1094
record(ts::DefaultTestSet, t::Broken) = (push!(ts.results, t); t)
3,800✔
1095
# For a passed result, do not store the result since it uses a lot of memory
1096
record(ts::DefaultTestSet, t::Pass) = (ts.n_passed += 1; t)
205,985,185✔
1097

1098
# For the other result types, immediately print the error message
1099
# but do not terminate. Print a backtrace.
1100
function record(ts::DefaultTestSet, t::Union{Fail, Error}; print_result::Bool=TESTSET_PRINT_ENABLE[])
4,290✔
1101
    if print_result
2,145✔
1102
        print(ts.description, ": ")
23✔
1103
        # don't print for interrupted tests
1104
        if !(t isa Error) || t.test_type !== :test_interrupted
26✔
1105
            print(t)
43✔
1106
            if !isa(t, Error) # if not gets printed in the show method
23✔
1107
                Base.show_backtrace(stdout, scrub_backtrace(backtrace(), ts.file, extract_file(t.source)))
40✔
1108
            end
1109
            println()
23✔
1110
        end
1111
    end
1112
    push!(ts.results, t)
4,202✔
1113
    (FAIL_FAST[] || ts.failfast) && throw(FailFastError())
2,145✔
1114
    return t
2,142✔
1115
end
1116

1117
# When a DefaultTestSet finishes, it records itself to its parent
1118
# testset, if there is one. This allows for recursive printing of
1119
# the results at the end of the tests
1120
record(ts::DefaultTestSet, t::AbstractTestSet) = push!(ts.results, t)
9,875✔
1121

1122
@specialize
1123

1124
function print_test_errors(ts::DefaultTestSet)
234✔
1125
    for t in ts.results
403✔
1126
        if isa(t, Error) || isa(t, Fail)
5,871✔
1127
            println("Error in testset $(ts.description):")
1,054✔
1128
            show(t)
2,069✔
1129
            println()
1,054✔
1130
        elseif isa(t, DefaultTestSet)
1,901✔
1131
            print_test_errors(t)
232✔
1132
        end
1133
    end
2,955✔
1134
end
1135

1136
function print_test_results(ts::DefaultTestSet, depth_pad=0)
22✔
1137
    # Calculate the overall number for each type so each of
1138
    # the test result types are aligned
1139
    passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration = get_test_counts(ts)
42✔
1140
    total_pass   = passes + c_passes
22✔
1141
    total_fail   = fails  + c_fails
22✔
1142
    total_error  = errors + c_errors
22✔
1143
    total_broken = broken + c_broken
22✔
1144
    dig_pass   = total_pass   > 0 ? ndigits(total_pass)   : 0
22✔
1145
    dig_fail   = total_fail   > 0 ? ndigits(total_fail)   : 0
22✔
1146
    dig_error  = total_error  > 0 ? ndigits(total_error)  : 0
22✔
1147
    dig_broken = total_broken > 0 ? ndigits(total_broken) : 0
22✔
1148
    total = total_pass + total_fail + total_error + total_broken
22✔
1149
    dig_total = total > 0 ? ndigits(total) : 0
22✔
1150
    # For each category, take max of digits and header width if there are
1151
    # tests of that type
1152
    pass_width   = dig_pass   > 0 ? max(length("Pass"),   dig_pass)   : 0
22✔
1153
    fail_width   = dig_fail   > 0 ? max(length("Fail"),   dig_fail)   : 0
22✔
1154
    error_width  = dig_error  > 0 ? max(length("Error"),  dig_error)  : 0
22✔
1155
    broken_width = dig_broken > 0 ? max(length("Broken"), dig_broken) : 0
22✔
1156
    total_width  = dig_total  > 0 ? max(length("Total"),  dig_total)  : 0
22✔
1157
    duration_width = max(length("Time"), length(duration))
22✔
1158
    # Calculate the alignment of the test result counts by
1159
    # recursively walking the tree of test sets
1160
    align = max(get_alignment(ts, 0), length("Test Summary:"))
22✔
1161
    # Print the outer test set header once
1162
    pad = total == 0 ? "" : " "
22✔
1163
    printstyled(rpad("Test Summary:", align, " "), " |", pad; bold=true)
22✔
1164
    if pass_width > 0
22✔
1165
        printstyled(lpad("Pass", pass_width, " "), "  "; bold=true, color=:green)
15✔
1166
    end
1167
    if fail_width > 0
22✔
1168
        printstyled(lpad("Fail", fail_width, " "), "  "; bold=true, color=Base.error_color())
11✔
1169
    end
1170
    if error_width > 0
22✔
1171
        printstyled(lpad("Error", error_width, " "), "  "; bold=true, color=Base.error_color())
5✔
1172
    end
1173
    if broken_width > 0
22✔
1174
        printstyled(lpad("Broken", broken_width, " "), "  "; bold=true, color=Base.warn_color())
3✔
1175
    end
1176
    if total_width > 0
22✔
1177
        printstyled(lpad("Total", total_width, " "), "  "; bold=true, color=Base.info_color())
22✔
1178
    end
1179
    if ts.showtiming
22✔
1180
        printstyled(lpad("Time", duration_width, " "); bold=true)
22✔
1181
    end
1182
    println()
22✔
1183
    # Recursively print a summary at every level
1184
    print_counts(ts, depth_pad, align, pass_width, fail_width, error_width, broken_width, total_width, duration_width, ts.showtiming)
22✔
1185
end
1186

1187

1188
const TESTSET_PRINT_ENABLE = Ref(true)
1189

1190
# Called at the end of a @testset, behaviour depends on whether
1191
# this is a child of another testset, or the "root" testset
1192
function finish(ts::DefaultTestSet; print_results::Bool=TESTSET_PRINT_ENABLE[])
19,742✔
1193
    ts.time_end = time()
9,871✔
1194
    # If we are a nested test set, do not print a full summary
1195
    # now - let the parent test set do the printing
1196
    if get_testset_depth() != 0
9,921✔
1197
        # Attach this test set to the parent test set
1198
        parent_ts = get_testset()
9,635✔
1199
        record(parent_ts, ts)
9,635✔
1200
        return ts
9,635✔
1201
    end
1202
    passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration = get_test_counts(ts)
236✔
1203
    total_pass   = passes + c_passes
236✔
1204
    total_fail   = fails  + c_fails
236✔
1205
    total_error  = errors + c_errors
236✔
1206
    total_broken = broken + c_broken
236✔
1207
    total = total_pass + total_fail + total_error + total_broken
236✔
1208

1209
    if print_results
236✔
1210
        print_test_results(ts)
20✔
1211
    end
1212

1213
    # Finally throw an error as we are the outermost test set
1214
    if total != total_pass + total_broken
236✔
1215
        # Get all the error/failures and bring them along for the ride
1216
        efs = filter_errors(ts)
40✔
1217
        throw(TestSetException(total_pass, total_fail, total_error, total_broken, efs))
40✔
1218
    end
1219

1220
    # return the testset so it is returned from the @testset macro
1221
    ts
196✔
1222
end
1223

1224
# Recursive function that finds the column that the result counts
1225
# can begin at by taking into account the width of the descriptions
1226
# and the amount of indentation. If a test set had no failures, and
1227
# no failures in child test sets, there is no need to include those
1228
# in calculating the alignment
1229
function get_alignment(ts::DefaultTestSet, depth::Int)
271✔
1230
    # The minimum width at this depth is
1231
    ts_width = 2*depth + length(ts.description)
271✔
1232
    # If not verbose and all passing, no need to look at children
1233
    !ts.verbose && !ts.anynonpass && return ts_width
271✔
1234
    # Return the maximum of this width and the minimum width
1235
    # for all children (if they exist)
1236
    isempty(ts.results) && return ts_width
52✔
1237
    child_widths = map(t->get_alignment(t, depth+1), ts.results)
1,577✔
1238
    return max(ts_width, maximum(child_widths))
52✔
1239
end
1240
get_alignment(ts, depth::Int) = 0
1,276✔
1241

1242
# Recursive function that fetches backtraces for any and all errors
1243
# or failures the testset and its children encountered
1244
function filter_errors(ts::DefaultTestSet)
9,767✔
1245
    efs = []
9,767✔
1246
    for t in ts.results
18,003✔
1247
        if isa(t, DefaultTestSet)
11,054✔
1248
            append!(efs, filter_errors(t))
9,543✔
1249
        elseif isa(t, Union{Fail, Error})
1,511✔
1250
            append!(efs, [t])
1,077✔
1251
        end
1252
    end
12,585✔
1253
    efs
9,767✔
1254
end
1255

1256
# Recursive function that counts the number of test results of each
1257
# type directly in the testset, and totals across the child testsets
1258
function get_test_counts(ts::DefaultTestSet)
18,820✔
1259
    passes, fails, errors, broken = ts.n_passed, 0, 0, 0
18,820✔
1260
    c_passes, c_fails, c_errors, c_broken = 0, 0, 0, 0
×
1261
    for t in ts.results
34,500✔
1262
        isa(t, Fail)   && (fails  += 1)
28,091✔
1263
        isa(t, Error)  && (errors += 1)
28,091✔
1264
        isa(t, Broken) && (broken += 1)
28,091✔
1265
        if isa(t, DefaultTestSet)
28,091✔
1266
            np, nf, ne, nb, ncp, ncf, nce , ncb, duration = get_test_counts(t)
18,106✔
1267
            c_passes += np + ncp
18,106✔
1268
            c_fails  += nf + ncf
18,106✔
1269
            c_errors += ne + nce
18,106✔
1270
            c_broken += nb + ncb
18,106✔
1271
        end
1272
    end
31,231✔
1273
    ts.anynonpass = (fails + errors + c_fails + c_errors > 0)
18,820✔
1274
    (; time_start, time_end) = ts
18,820✔
1275
    duration = if isnothing(time_end)
37,640✔
1276
        ""
×
1277
    else
1278
        dur_s = time_end - time_start
18,820✔
1279
        if dur_s < 60
18,820✔
1280
            string(round(dur_s, digits = 1), "s")
18,003✔
1281
        else
1282
            m, s = divrem(dur_s, 60)
817✔
1283
            s = lpad(string(round(s, digits = 1)), 4, "0")
817✔
1284
            string(round(Int, m), "m", s, "s")
38,457✔
1285
        end
1286
    end
1287
    return passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration
18,820✔
1288
end
1289

1290
# Recursive function that prints out the results at each level of
1291
# the tree of test sets
1292
function print_counts(ts::DefaultTestSet, depth, align,
271✔
1293
                      pass_width, fail_width, error_width, broken_width, total_width, duration_width, showtiming)
1294
    # Count results by each type at this level, and recursively
1295
    # through any child test sets
1296
    passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration = get_test_counts(ts)
271✔
1297
    subtotal = passes + fails + errors + broken + c_passes + c_fails + c_errors + c_broken
271✔
1298
    # Print test set header, with an alignment that ensures all
1299
    # the test results appear above each other
1300
    print(rpad(string("  "^depth, ts.description), align, " "), " | ")
271✔
1301

1302
    np = passes + c_passes
271✔
1303
    if np > 0
271✔
1304
        printstyled(lpad(string(np), pass_width, " "), "  ", color=:green)
252✔
1305
    elseif pass_width > 0
19✔
1306
        # No passes at this level, but some at another level
1307
        print(lpad(" ", pass_width), "  ")
9✔
1308
    end
1309

1310
    nf = fails + c_fails
271✔
1311
    if nf > 0
271✔
1312
        printstyled(lpad(string(nf), fail_width, " "), "  ", color=Base.error_color())
45✔
1313
    elseif fail_width > 0
226✔
1314
        # No fails at this level, but some at another level
1315
        print(lpad(" ", fail_width), "  ")
206✔
1316
    end
1317

1318
    ne = errors + c_errors
271✔
1319
    if ne > 0
271✔
1320
        printstyled(lpad(string(ne), error_width, " "), "  ", color=Base.error_color())
16✔
1321
    elseif error_width > 0
255✔
1322
        # No errors at this level, but some at another level
1323
        print(lpad(" ", error_width), "  ")
224✔
1324
    end
1325

1326
    nb = broken + c_broken
271✔
1327
    if nb > 0
271✔
1328
        printstyled(lpad(string(nb), broken_width, " "), "  ", color=Base.warn_color())
50✔
1329
    elseif broken_width > 0
221✔
1330
        # None broken at this level, but some at another level
1331
        print(lpad(" ", broken_width), "  ")
185✔
1332
    end
1333

1334
    if np == 0 && nf == 0 && ne == 0 && nb == 0
271✔
1335
        printstyled(lpad("None", total_width, " "), "  ", color=Base.info_color())
6✔
1336
    else
1337
        printstyled(lpad(string(subtotal), total_width, " "), "  ", color=Base.info_color())
265✔
1338
    end
1339

1340
    if showtiming
271✔
1341
        printstyled(lpad(string(duration), duration_width, " "))
271✔
1342
    end
1343
    println()
271✔
1344

1345
    # Only print results at lower levels if we had failures or if the user
1346
    # wants.
1347
    if (np + nb != subtotal) || (ts.verbose)
493✔
1348
        for t in ts.results
52✔
1349
            if isa(t, DefaultTestSet)
1,525✔
1350
                print_counts(t, depth + 1, align,
249✔
1351
                    pass_width, fail_width, error_width, broken_width, total_width, duration_width, ts.showtiming)
1352
            end
1353
        end
1,744✔
1354
    end
1355
end
1356

1357
#-----------------------------------------------------------------------
1358

1359
function _check_testset(testsettype, testsetname)
5,038✔
1360
    if !(testsettype isa Type && testsettype <: AbstractTestSet)
9,917✔
1361
        error("Expected `$testsetname` to be an AbstractTestSet, it is a ",
1✔
1362
              typeof(testsettype), ". ",
1363
              typeof(testsettype) == String ?
1364
                  """
1365
                  To use `$testsetname` as a testset name, interpolate it into a string, e.g:
1366
                      @testset "\$$testsetname" begin
1367
                          ...
1368
                      end"""
1369
             :
1370
                  ""
1371
            )
1372
    end
1373
end
1374

1375
"""
1376
    @testset [CustomTestSet] [options...] ["description"] begin test_ex end
1377
    @testset [CustomTestSet] [options...] ["description \$v"] for v in itr test_ex end
1378
    @testset [CustomTestSet] [options...] ["description \$v, \$w"] for v in itrv, w in itrw test_ex end
1379
    @testset [CustomTestSet] [options...] ["description"] test_func()
1380
    @testset let v = v, w = w; test_ex; end
1381

1382
# With begin/end or function call
1383

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

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

1392
Any custom testset type (subtype of `AbstractTestSet`) can be given and it will
1393
also be used for any nested `@testset` invocations. The given options are only
1394
applied to the test set where they are given. The default test set type
1395
accepts three boolean options:
1396
- `verbose`: if `true`, the result summary of the nested testsets is shown even
1397
  when they all pass (the default is `false`).
1398
- `showtiming`: if `true`, the duration of each displayed testset is shown
1399
  (the default is `true`).
1400
- `failfast`: if `true`, any test failure or error will cause the testset and any
1401
  child testsets to return immediately (the default is `false`).
1402
  This can also be set globally via the env var `JULIA_TEST_FAILFAST`.
1403

1404
!!! compat "Julia 1.8"
1405
    `@testset test_func()` requires at least Julia 1.8.
1406

1407
!!! compat "Julia 1.9"
1408
    `failfast` requires at least Julia 1.9.
1409

1410
The description string accepts interpolation from the loop indices.
1411
If no description is provided, one is constructed based on the variables.
1412
If a function call is provided, its name will be used.
1413
Explicit description strings override this behavior.
1414

1415
By default the `@testset` macro will return the testset object itself, though
1416
this behavior can be customized in other testset types. If a `for` loop is used
1417
then the macro collects and returns a list of the return values of the `finish`
1418
method, which by default will return a list of the testset objects used in
1419
each iteration.
1420

1421
Before the execution of the body of a `@testset`, there is an implicit
1422
call to `Random.seed!(seed)` where `seed` is the current seed of the global RNG.
1423
Moreover, after the execution of the body, the state of the global RNG is
1424
restored to what it was before the `@testset`. This is meant to ease
1425
reproducibility in case of failure, and to allow seamless
1426
re-arrangements of `@testset`s regardless of their side-effect on the
1427
global RNG state.
1428

1429
## Examples
1430
```jldoctest; filter = r"trigonometric identities |    4      4  [0-9\\.]+s"
1431
julia> @testset "trigonometric identities" begin
1432
           θ = 2/3*π
1433
           @test sin(-θ) ≈ -sin(θ)
1434
           @test cos(-θ) ≈ cos(θ)
1435
           @test sin(2θ) ≈ 2*sin(θ)*cos(θ)
1436
           @test cos(2θ) ≈ cos(θ)^2 - sin(θ)^2
1437
       end;
1438
Test Summary:            | Pass  Total  Time
1439
trigonometric identities |    4      4  0.2s
1440
```
1441

1442
# `@testset for`
1443

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

1448
# `@testset let`
1449

1450
When `@testset let` is used, the macro starts a *transparent* test set with
1451
the given object added as a context object to any failing test contained
1452
therein. This is useful when performing a set of related tests on one larger
1453
object and it is desirable to print this larger object when any of the
1454
individual tests fail. Transparent test sets do not introduce additional levels
1455
of nesting in the test set hierarchy and are passed through directly to the
1456
parent test set (with the context object appended to any failing tests.)
1457

1458
!!! compat "Julia 1.9"
1459
    `@testset let` requires at least Julia 1.9.
1460

1461
!!! compat "Julia 1.10"
1462
    Multiple `let` assignements are supported since Julia 1.10.
1463

1464
## Examples
1465
```jldoctest
1466
julia> @testset let logi = log(im)
1467
           @test imag(logi) == π/2
1468
           @test !iszero(real(logi))
1469
       end
1470
Test Failed at none:3
1471
  Expression: !(iszero(real(logi)))
1472
     Context: logi = 0.0 + 1.5707963267948966im
1473

1474
ERROR: There was an error during testing
1475

1476
julia> @testset let logi = log(im), op = !iszero
1477
           @test imag(logi) == π/2
1478
           @test op(real(logi))
1479
       end
1480
Test Failed at none:3
1481
  Expression: op(real(logi))
1482
     Context: logi = 0.0 + 1.5707963267948966im
1483
              op = !iszero
1484

1485
ERROR: There was an error during testing
1486
```
1487
"""
1488
macro testset(args...)
3,946✔
1489
    isempty(args) && error("No arguments to @testset")
3,946✔
1490

1491
    tests = args[end]
3,946✔
1492

1493
    # Determine if a single block or for-loop style
1494
    if !isa(tests,Expr) || (tests.head !== :for && tests.head !== :block && tests.head !== :call && tests.head !== :let)
7,892✔
1495

1496
        error("Expected function call, begin/end block or for loop as argument to @testset")
×
1497
    end
1498

1499
    FAIL_FAST[] = Base.get_bool_env("JULIA_TEST_FAILFAST", false)
3,946✔
1500

1501
    if tests.head === :for
3,946✔
1502
        return testset_forloop(args, tests, __source__)
298✔
1503
    elseif tests.head === :let
3,648✔
1504
        return testset_context(args, tests, __source__)
18✔
1505
    else
1506
        return testset_beginend_call(args, tests, __source__)
3,630✔
1507
    end
1508
end
1509

1510
trigger_test_failure_break(@nospecialize(err)) =
1,140✔
1511
    ccall(:jl_test_failure_breakpoint, Cvoid, (Any,), err)
1512

1513
"""
1514
Generate the code for an `@testset` with a `let` argument.
1515
"""
1516
function testset_context(args, ex, source)
18✔
1517
    desc, testsettype, options = parse_testset_args(args[1:end-1])
18✔
1518
    if desc !== nothing || testsettype !== nothing
18✔
1519
        # Reserve this syntax if we ever want to allow this, but for now,
1520
        # just do the transparent context test set.
1521
        error("@testset with a `let` argument cannot be customized")
×
1522
    end
1523

1524
    let_ex = ex.args[1]
18✔
1525

1526
    if Meta.isexpr(let_ex, :(=))
18✔
1527
        contexts = Any[let_ex.args[1]]
17✔
1528
    elseif Meta.isexpr(let_ex, :block)
1✔
1529
        contexts = Any[]
1✔
1530
        for assign_ex in let_ex.args
1✔
1531
            if Meta.isexpr(assign_ex, :(=))
2✔
1532
                push!(contexts, assign_ex.args[1])
2✔
1533
            else
1534
                error("Malformed `let` expression is given")
×
1535
            end
1536
        end
2✔
1537
    else
1538
        error("Malformed `let` expression is given")
×
1539
    end
1540
    reverse!(contexts)
18✔
1541

1542
    test_ex = ex.args[2]
18✔
1543

1544
    ex.args[2] = quote
18✔
1545
        $(map(contexts) do context
109✔
1546
            :($push_testset($(ContextTestSet)($(QuoteNode(context)), $context; $options...)))
19✔
1547
        end...)
1548
        try
109✔
1549
            $(test_ex)
109✔
1550
        finally
1551
            $(map(_->:($pop_testset()), contexts)...)
128✔
1552
        end
1553
    end
1554

1555
    return esc(ex)
18✔
1556
end
1557

1558
"""
1559
Generate the code for a `@testset` with a function call or `begin`/`end` argument
1560
"""
1561
function testset_beginend_call(args, tests, source)
3,630✔
1562
    desc, testsettype, options = parse_testset_args(args[1:end-1])
3,633✔
1563
    if desc === nothing
97✔
1564
        if tests.head === :call
21✔
1565
            desc = string(tests.args[1]) # use the function name as test name
2✔
1566
        else
1567
            desc = "test set"
19✔
1568
        end
1569
    end
1570
    # If we're at the top level we'll default to DefaultTestSet. Otherwise
1571
    # default to the type of the parent testset
1572
    if testsettype === nothing
97✔
1573
        testsettype = :(get_testset_depth() == 0 ? DefaultTestSet : typeof(get_testset()))
3,619✔
1574
    end
1575

1576
    # Generate a block of code that initializes a new testset, adds
1577
    # it to the task local storage, evaluates the test(s), before
1578
    # finally removing the testset and giving it a chance to take
1579
    # action (such as reporting the results)
1580
    ex = quote
7,259✔
1581
        _check_testset($testsettype, $(QuoteNode(testsettype.args[1])))
4,450✔
1582
        local ret
×
1583
        local ts = if ($testsettype === $DefaultTestSet) && $(isa(source, LineNumberNode))
4,450✔
1584
            $(testsettype)($desc; source=$(QuoteNode(source.file)), $options...)
4,429✔
1585
        else
1586
            $(testsettype)($desc; $options...)
2,343✔
1587
        end
1588
        push_testset(ts)
2,337✔
1589
        # we reproduce the logic of guardseed, but this function
1590
        # cannot be used as it changes slightly the semantic of @testset,
1591
        # by wrapping the body in a function
1592
        local RNG = default_rng()
2,336✔
1593
        local oldrng = copy(RNG)
2,337✔
1594
        local oldseed = Random.GLOBAL_SEED
2,337✔
1595
        try
2,337✔
1596
            # RNG is re-seeded with its own seed to ease reproduce a failed test
1597
            Random.seed!(Random.GLOBAL_SEED)
2,337✔
1598
            let
1,282✔
1599
                $(esc(tests))
212,649✔
1600
            end
1601
        catch err
1602
            err isa InterruptException && rethrow()
18✔
1603
            # something in the test block threw an error. Count that as an
1604
            # error in this test set
1605
            trigger_test_failure_break(err)
18✔
1606
            if err isa FailFastError
18✔
1607
                get_testset_depth() > 1 ? rethrow() : failfast_print()
×
1608
            else
1609
                record(ts, Error(:nontest_error, Expr(:tuple), err, Base.current_exceptions(), $(QuoteNode(source))))
2,355✔
1610
            end
1611
        finally
1612
            copy!(RNG, oldrng)
2,337✔
1613
            Random.set_global_seed!(oldseed)
2,337✔
1614
            pop_testset()
2,337✔
1615
            ret = finish(ts)
2,350✔
1616
        end
1617
        ret
2,305✔
1618
    end
1619
    # preserve outer location if possible
1620
    if tests isa Expr && tests.head === :block && !isempty(tests.args) && tests.args[1] isa LineNumberNode
3,630✔
1621
        ex = Expr(:block, tests.args[1], ex)
3,627✔
1622
    end
1623
    return ex
3,630✔
1624
end
1625

1626
function failfast_print()
3✔
1627
    printstyled("\nFail-fast enabled:"; color = Base.error_color(), bold=true)
3✔
1628
    printstyled(" Fail or Error occurred\n\n"; color = Base.error_color())
3✔
1629
end
1630

1631
"""
1632
Generate the code for a `@testset` with a `for` loop argument
1633
"""
1634
function testset_forloop(args, testloop, source)
298✔
1635
    # Pull out the loop variables. We might need them for generating the
1636
    # description and we'll definitely need them for generating the
1637
    # comprehension expression at the end
1638
    loopvars = Expr[]
298✔
1639
    if testloop.args[1].head === :(=)
298✔
1640
        push!(loopvars, testloop.args[1])
261✔
1641
    elseif testloop.args[1].head === :block
37✔
1642
        for loopvar in testloop.args[1].args
37✔
1643
            push!(loopvars, loopvar)
80✔
1644
        end
80✔
1645
    else
1646
        error("Unexpected argument to @testset")
×
1647
    end
1648

1649
    desc, testsettype, options = parse_testset_args(args[1:end-1])
298✔
1650

1651
    if desc === nothing
298✔
1652
        # No description provided. Generate from the loop variable names
1653
        v = loopvars[1].args[1]
105✔
1654
        desc = Expr(:string, "$v = ", esc(v)) # first variable
105✔
1655
        for l = loopvars[2:end]
199✔
1656
            v = l.args[1]
13✔
1657
            push!(desc.args, ", $v = ")
26✔
1658
            push!(desc.args, esc(v))
13✔
1659
        end
24✔
1660
    end
1661

1662
    if testsettype === nothing
298✔
1663
        testsettype = :(get_testset_depth() == 0 ? DefaultTestSet : typeof(get_testset()))
297✔
1664
    end
1665

1666
    # Uses a similar block as for `@testset`, except that it is
1667
    # wrapped in the outer loop provided by the user
1668
    tests = testloop.args[2]
298✔
1669
    blk = quote
596✔
1670
        _check_testset($testsettype, $(QuoteNode(testsettype.args[1])))
5,413✔
1671
        # Trick to handle `break` and `continue` in the test code before
1672
        # they can be handled properly by `finally` lowering.
1673
        if !first_iteration
2,710✔
1674
            pop_testset()
1,895✔
1675
            finish_errored = true
1,895✔
1676
            push!(arr, finish(ts))
3,758✔
1677
            finish_errored = false
1,895✔
1678

1679
            # it's 1000 times faster to copy from tmprng rather than calling Random.seed!
1680
            copy!(RNG, tmprng)
1,895✔
1681

1682
        end
1683
        ts = if ($testsettype === $DefaultTestSet) && $(isa(source, LineNumberNode))
5,413✔
1684
            $(testsettype)($desc; source=$(QuoteNode(source.file)), $options...)
5,235✔
1685
        else
1686
            $(testsettype)($desc; $options...)
2,733✔
1687
        end
1688
        push_testset(ts)
2,709✔
1689
        first_iteration = false
2,709✔
1690
        try
2,709✔
1691
            $(esc(tests))
1,576,049✔
1692
        catch err
1693
            err isa InterruptException && rethrow()
4✔
1694
            # Something in the test block threw an error. Count that as an
1695
            # error in this test set
1696
            trigger_test_failure_break(err)
3✔
1697
            if !isa(err, FailFastError)
3✔
1698
                record(ts, Error(:nontest_error, Expr(:tuple), err, Base.current_exceptions(), $(QuoteNode(source))))
3,624✔
1699
            end
1700
        end
1701
    end
1702
    quote
298✔
1703
        local arr = Vector{Any}()
775✔
1704
        local first_iteration = true
775✔
1705
        local ts
×
1706
        local finish_errored = false
775✔
1707
        local RNG = default_rng()
775✔
1708
        local oldrng = copy(RNG)
775✔
1709
        local oldseed = Random.GLOBAL_SEED
775✔
1710
        Random.seed!(Random.GLOBAL_SEED)
775✔
1711
        local tmprng = copy(RNG)
775✔
1712
        try
775✔
1713
            let
×
1714
                $(Expr(:for, Expr(:block, [esc(v) for v in loopvars]...), blk))
912✔
1715
            end
1716
        finally
1717
            # Handle `return` in test body
1718
            if !first_iteration && !finish_errored
815✔
1719
                pop_testset()
814✔
1720
                push!(arr, finish(ts))
1,611✔
1721
            end
1722
            copy!(RNG, oldrng)
815✔
1723
            Random.set_global_seed!(oldseed)
815✔
1724
        end
1725
        arr
773✔
1726
    end
1727
end
1728

1729
"""
1730
Parse the arguments to the `@testset` macro to pull out the description,
1731
Testset Type, and options. Generally this should be called with all the macro
1732
arguments except the last one, which is the test expression itself.
1733
"""
1734
function parse_testset_args(args)
3,946✔
1735
    desc = nothing
355✔
1736
    testsettype = nothing
355✔
1737
    options = :(Dict{Symbol, Any}())
3,946✔
1738
    for arg in args
3,946✔
1739
        # a standalone symbol is assumed to be the test set we should use
1740
        if isa(arg, Symbol)
232✔
1741
            testsettype = esc(arg)
12✔
1742
        # a string is the description
1743
        elseif isa(arg, AbstractString) || (isa(arg, Expr) && arg.head === :string)
232✔
1744
            desc = esc(arg)
3,808✔
1745
        # an assignment is an option
1746
        elseif isa(arg, Expr) && arg.head === :(=)
9✔
1747
            # we're building up a Dict literal here
1748
            key = Expr(:quote, arg.args[1])
9✔
1749
            push!(options.args, Expr(:call, :(=>), key, esc(arg.args[2])))
9✔
1750
        else
1751
            error("Unexpected argument $arg to @testset")
×
1752
        end
1753
    end
248✔
1754

1755
    (desc, testsettype, options)
3,946✔
1756
end
1757

1758
#-----------------------------------------------------------------------
1759
# Various helper methods for test sets
1760

1761
"""
1762
    get_testset()
1763

1764
Retrieve the active test set from the task's local storage. If no
1765
test set is active, use the fallback default test set.
1766
"""
1767
function get_testset()
67,936,412✔
1768
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
135,554,836✔
1769
    return isempty(testsets) ? fallback_testset : testsets[end]
67,935,813✔
1770
end
1771

1772
"""
1773
    push_testset(ts::AbstractTestSet)
1774

1775
Adds the test set to the `task_local_storage`.
1776
"""
1777
function push_testset(ts::AbstractTestSet)
10,342✔
1778
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
20,455✔
1779
    push!(testsets, ts)
10,342✔
1780
    setindex!(task_local_storage(), testsets, :__BASETESTNEXT__)
10,342✔
1781
end
1782

1783
"""
1784
    pop_testset()
1785

1786
Pops the last test set added to the `task_local_storage`. If there are no
1787
active test sets, returns the fallback default test set.
1788
"""
1789
function pop_testset()
10,340✔
1790
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
20,680✔
1791
    ret = isempty(testsets) ? fallback_testset : pop!(testsets)
20,680✔
1792
    setindex!(task_local_storage(), testsets, :__BASETESTNEXT__)
10,340✔
1793
    return ret
10,340✔
1794
end
1795

1796
"""
1797
    get_testset_depth()
1798

1799
Return the number of active test sets, not including the default test set
1800
"""
1801
function get_testset_depth()
39,615✔
1802
    testsets = get(task_local_storage(), :__BASETESTNEXT__, AbstractTestSet[])
78,546✔
1803
    return length(testsets)
39,615✔
1804
end
1805

1806
_args_and_call(args...; kwargs...) = (args[1:end-1], kwargs, args[end](args[1:end-1]...; kwargs...))
704✔
1807
_materialize_broadcasted(f, args...) = Broadcast.materialize(Broadcast.broadcasted(f, args...))
74✔
1808

1809
"""
1810
    @inferred [AllowedType] f(x)
1811

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

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

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

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

1827
julia> typeof(f(2))
1828
Int64
1829

1830
julia> @code_warntype f(2)
1831
MethodInstance for f(::Int64)
1832
  from f(a) @ Main none:1
1833
Arguments
1834
  #self#::Core.Const(f)
1835
  a::Int64
1836
Body::UNION{FLOAT64, INT64}
1837
1 ─ %1 = (a > 1)::Bool
1838
└──      goto #3 if not %1
1839
2 ─      return 1
1840
3 ─      return 1.0
1841

1842
julia> @inferred f(2)
1843
ERROR: return type Int64 does not match inferred return type Union{Float64, Int64}
1844
[...]
1845

1846
julia> @inferred max(1, 2)
1847
2
1848

1849
julia> g(a) = a < 10 ? missing : 1.0
1850
g (generic function with 1 method)
1851

1852
julia> @inferred g(20)
1853
ERROR: return type Float64 does not match inferred return type Union{Missing, Float64}
1854
[...]
1855

1856
julia> @inferred Missing g(20)
1857
1.0
1858

1859
julia> h(a) = a < 10 ? missing : f(a)
1860
h (generic function with 1 method)
1861

1862
julia> @inferred Missing h(20)
1863
ERROR: return type Int64 does not match inferred return type Union{Missing, Float64, Int64}
1864
[...]
1865
```
1866
"""
1867
macro inferred(ex)
977✔
1868
    _inferred(ex, __module__)
977✔
1869
end
1870
macro inferred(allow, ex)
23✔
1871
    _inferred(ex, __module__, allow)
23✔
1872
end
1873
function _inferred(ex, mod, allow = :(Union{}))
1,977✔
1874
    if Meta.isexpr(ex, :ref)
1,977✔
1875
        ex = Expr(:call, :getindex, ex.args...)
16✔
1876
    end
1877
    Meta.isexpr(ex, :call)|| error("@inferred requires a call expression")
1,000✔
1878
    farg = ex.args[1]
1,000✔
1879
    if isa(farg, Symbol) && farg !== :.. && first(string(farg)) == '.'
1,000✔
1880
        farg = Symbol(string(farg)[2:end])
64✔
1881
        ex = Expr(:call, GlobalRef(Test, :_materialize_broadcasted),
32✔
1882
            farg, ex.args[2:end]...)
1883
    end
1884
    result = let ex = ex
1,000✔
1885
        quote
3,638✔
1886
            let allow = $(esc(allow))
1887
                allow isa Type || throw(ArgumentError("@inferred requires a type as second argument"))
1888
                $(if any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex.args)
5,427✔
1889
                    # Has keywords
1890
                    args = gensym()
122✔
1891
                    kwargs = gensym()
122✔
1892
                    quote
122✔
1893
                        $(esc(args)), $(esc(kwargs)), result = $(esc(Expr(:call, _args_and_call, ex.args[2:end]..., ex.args[1])))
1894
                        inftypes = $(gen_call_with_extracted_types(mod, Base.return_types, :($(ex.args[1])($(args)...; $(kwargs)...))))
1895
                    end
1896
                else
1897
                    # No keywords
1898
                    quote
1,878✔
1899
                        args = ($([esc(ex.args[i]) for i = 2:length(ex.args)]...),)
1900
                        result = $(esc(ex.args[1]))(args...)
1901
                        inftypes = Base.return_types($(esc(ex.args[1])), Base.typesof(args...))
1902
                    end
1903
                end)
1904
                @assert length(inftypes) == 1
1905
                rettype = result isa Type ? Type{result} : typeof(result)
1906
                rettype <: allow || rettype == typesplit(inftypes[1], allow) || error("return type $rettype does not match inferred return type $(inftypes[1])")
1907
                result
1908
            end
1909
        end
1910
    end
1911
    return remove_linenums!(result)
1,000✔
1912
end
1913

1914
function is_in_mods(m::Module, recursive::Bool, mods)
646,036✔
1915
    while true
646,036✔
1916
        m in mods && return true
2,565,064✔
1917
        recursive || return false
1,190,284✔
1918
        p = parentmodule(m)
541,588✔
1919
        p === m && return false
541,588✔
1920
        m = p
279,514✔
1921
    end
279,514✔
1922
end
1923

1924
"""
1925
    detect_ambiguities(mod1, mod2...; recursive=false,
1926
                                      ambiguous_bottom=false,
1927
                                      allowed_undefineds=nothing)
1928

1929
Return a vector of `(Method,Method)` pairs of ambiguous methods
1930
defined in the specified modules.
1931
Use `recursive=true` to test in all submodules.
1932

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

1✔
1937
See [`Test.detect_unbound_args`](@ref) for an explanation of
1✔
1938
`allowed_undefineds`.
1939

1940
!!! compat "Julia 1.8"
1941
    `allowed_undefineds` requires at least Julia 1.8.
1942
"""
1943
function detect_ambiguities(mods::Module...;
37✔
1944
                            recursive::Bool = false,
1✔
1945
                            ambiguous_bottom::Bool = false,
1946
                            allowed_undefineds = nothing)
1947
    @nospecialize
18✔
1948
    ambs = Set{Tuple{Method,Method}}()
18✔
1949
    mods = collect(mods)::Vector{Module}
36✔
1950
    function sortdefs(m1::Method, m2::Method)
76✔
1951
        ord12 = cmp(m1.file, m2.file)
58✔
1952
        if ord12 == 0
58✔
1953
            ord12 = cmp(m1.line, m2.line)
56✔
1954
        end
1955
        return ord12 <= 0 ? (m1, m2) : (m2, m1)
88✔
1956
    end
1957
    function examine(mt::Core.MethodTable)
163,558✔
1958
        for m in Base.MethodList(mt)
326,866✔
1959
            m.sig == Tuple && continue # ignore Builtins
494,888✔
1960
            is_in_mods(parentmodule(m), recursive, mods) || continue
494,006✔
1961
            world = Base.get_world_counter()
34,923✔
1962
            ambig = Ref{Int32}(0)
34,923✔
1963
            ms = Base._methods_by_ftype(m.sig, nothing, -1, world, true, Ref(typemin(UInt)), Ref(typemax(UInt)), ambig)::Vector
34,923✔
1964
            ambig[] == 0 && continue
34,923✔
1965
            for match2 in ms
1,979✔
1966
                match2 = match2::Core.MethodMatch
15,541✔
1967
                m2 = match2.method
15,541✔
1968
                 if !(m === m2 || Base.morespecific(m2.sig, m.sig))
29,107✔
1969
                    if Base.isambiguous(m, m2; ambiguous_bottom)
5,795✔
1970
                        push!(ambs, sortdefs(m, m2))
88✔
1971
                    end
1972
                end
1973
            end
15,541✔
1974
        end
494,888✔
1975
    end
1976
    work = Base.loaded_modules_array()
18✔
1977
    filter!(mod -> mod === parentmodule(mod), work) # some items in loaded_modules_array are not top modules (really just Base)
382✔
1978
    while !isempty(work)
1,741✔
1979
        mod = pop!(work)
1,723✔
1980
        for n in names(mod, all = true)
1,723✔
1981
            Base.isdeprecated(mod, n) && continue
373,587✔
1982
            if !isdefined(mod, n)
373,587✔
1983
                if is_in_mods(mod, recursive, mods)
72✔
1984
                    if allowed_undefineds === nothing || GlobalRef(mod, n) ∉ allowed_undefineds
8✔
1985
                        println("Skipping ", mod, '.', n)  # typically stale exports
2✔
1986
                    end
1987
                end
1988
                continue
72✔
1989
            end
1990
            f = Base.unwrap_unionall(getfield(mod, n))
373,515✔
1991
            if isa(f, Module) && f !== mod && parentmodule(f) === mod && nameof(f) === n
373,515✔
1992
                push!(work, f)
1,377✔
1993
            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
372,138✔
1994
                examine(f.name.mt)
163,504✔
1995
            end
1996
        end
375,310✔
1997
    end
1,723✔
1998
    examine(Symbol.name.mt)
18✔
1999
    examine(DataType.name.mt)
18✔
2000
    return collect(ambs)
18✔
2001
end
2002

2003
"""
2004
    detect_unbound_args(mod1, mod2...; recursive=false, allowed_undefineds=nothing)
2005

2006
Return a vector of `Method`s which may have unbound type parameters.
2007
Use `recursive=true` to test in all submodules.
2008

2009
By default, any undefined symbols trigger a warning. This warning can
2010
be suppressed by supplying a collection of `GlobalRef`s for which
2011
the warning can be skipped. For example, setting
2012

2013
```
2014
allowed_undefineds = Set([GlobalRef(Base, :active_repl),
2015
                          GlobalRef(Base, :active_repl_backend)])
2016
```
2017

2018
would suppress warnings about `Base.active_repl` and
2019
`Base.active_repl_backend`.
2020

2021
!!! compat "Julia 1.8"
2022
    `allowed_undefineds` requires at least Julia 1.8.
2023
"""
2024
function detect_unbound_args(mods...;
8✔
2025
                             recursive::Bool = false,
2026
                             allowed_undefineds=nothing)
2027
    @nospecialize mods
4✔
2028
    ambs = Set{Method}()
4✔
2029
    mods = collect(mods)::Vector{Module}
4✔
2030
    function examine(mt::Core.MethodTable)
39,486✔
2031
        for m in Base.MethodList(mt)
78,918✔
2032
            is_in_mods(parentmodule(m), recursive, mods) || continue
116,663✔
2033
            has_unbound_vars(m.sig) || continue
44,468✔
2034
            tuple_sig = Base.unwrap_unionall(m.sig)::DataType
62✔
2035
            if Base.isvatuple(tuple_sig)
62✔
2036
                params = tuple_sig.parameters[1:(end - 1)]
50✔
2037
                tuple_sig = Base.rewrap_unionall(Tuple{params...}, m.sig)
100✔
2038
                world = Base.get_world_counter()
50✔
2039
                mf = ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tuple_sig, nothing, world)
50✔
2040
                if mf !== nothing && mf !== m && mf.sig <: tuple_sig
50✔
2041
                    continue
42✔
2042
                end
2043
            end
2044
            push!(ambs, m)
20✔
2045
        end
116,663✔
2046
    end
2047
    work = Base.loaded_modules_array()
4✔
2048
    filter!(mod -> mod === parentmodule(mod), work) # some items in loaded_modules_array are not top modules (really just Base)
103✔
2049
    while !isempty(work)
443✔
2050
        mod = pop!(work)
439✔
2051
        for n in names(mod, all = true)
439✔
2052
            Base.isdeprecated(mod, n) && continue
90,705✔
2053
            if !isdefined(mod, n)
90,705✔
2054
                if is_in_mods(mod, recursive, mods)
16✔
2055
                    if allowed_undefineds === nothing || GlobalRef(mod, n) ∉ allowed_undefineds
4✔
2056
                        println("Skipping ", mod, '.', n)  # typically stale exports
2✔
2057
                    end
2058
                end
2059
                continue
16✔
2060
            end
2061
            f = Base.unwrap_unionall(getfield(mod, n))
90,689✔
2062
            if isa(f, Module) && f !== mod && parentmodule(f) === mod && nameof(f) === n
90,689✔
2063
                push!(work, f)
344✔
2064
            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
90,345✔
2065
                examine(f.name.mt)
39,474✔
2066
            end
2067
        end
91,144✔
2068
    end
439✔
2069
    examine(Symbol.name.mt)
4✔
2070
    examine(DataType.name.mt)
4✔
2071
    return collect(ambs)
4✔
2072
end
2073

2074
function has_unbound_vars(@nospecialize sig)
22,265✔
2075
    while sig isa UnionAll
22,265✔
2076
        var = sig.var
4,608✔
2077
        sig = sig.body
4,608✔
2078
        if !Core.Compiler.constrains_param(var, sig, #=covariant=#true, #=type_constrains=#true)
4,608✔
2079
            return true
62✔
2080
        end
2081
    end
4,546✔
2082
    return false
22,203✔
2083
end
2084

2085

2086
"""
2087
The `GenericString` can be used to test generic string APIs that program to
2088
the `AbstractString` interface, in order to ensure that functions can work
2089
with string types besides the standard `String` type.
2090
"""
2091
struct GenericString <: AbstractString
2092
    string::AbstractString
60,640✔
2093
end
2094
Base.ncodeunits(s::GenericString) = ncodeunits(s.string)::Int
132,074✔
2095
Base.codeunit(s::GenericString) = codeunit(s.string)::Type{<:Union{UInt8, UInt16, UInt32}}
21✔
2096
Base.codeunit(s::GenericString, i::Integer) = codeunit(s.string, i)::Union{UInt8, UInt16, UInt32}
108✔
2097
Base.isvalid(s::GenericString, i::Integer) = isvalid(s.string, i)::Bool
1,714,603✔
2098
Base.iterate(s::GenericString, i::Integer=1) = iterate(s.string, i)::Union{Nothing,Tuple{AbstractChar,Int}}
8,889✔
2099
Base.reverse(s::GenericString) = GenericString(reverse(s.string))
75✔
2100
Base.reverse(s::SubString{GenericString}) =
75✔
2101
    GenericString(typeof(s.string)(reverse(String(s))))
2102

2103
"""
2104
The `GenericSet` can be used to test generic set APIs that program to
2105
the `AbstractSet` interface, in order to ensure that functions can work
2106
with set types besides the standard `Set` and `BitSet` types.
2107
"""
2108
struct GenericSet{T} <: AbstractSet{T}
2109
    s::AbstractSet{T}
10✔
2110
end
2111

2112
"""
2113
The `GenericDict` can be used to test generic dict APIs that program to
2114
the `AbstractDict` interface, in order to ensure that functions can work
2115
with associative types besides the standard `Dict` type.
2116
"""
2117
struct GenericDict{K,V} <: AbstractDict{K,V}
2118
    s::AbstractDict{K,V}
8✔
2119
end
2120

2121
for G in (GenericSet, GenericDict)
2122
    @eval begin
2123
        Base.iterate(s::$G, state...) = iterate(s.s, state...)
3,786✔
2124
    end
2125
    for f in (:isempty, :length)
2126
        @eval begin
2127
            Base.$f(s::$G) = $f(s.s)
108✔
2128
        end
2129
    end
2130
end
2131

2132
Base.get(s::GenericDict, x, y) = get(s.s, x, y)
228✔
2133

2134
"""
2135
The `GenericArray` can be used to test generic array APIs that program to
2136
the `AbstractArray` interface, in order to ensure that functions can work
2137
with array types besides the standard `Array` type.
2138
"""
2139
struct GenericArray{T,N} <: AbstractArray{T,N}
2140
    a::Array{T,N}
725✔
2141
end
2142

2143
GenericArray{T}(args...) where {T} = GenericArray(Array{T}(args...))
344✔
2144
GenericArray{T,N}(args...) where {T,N} = GenericArray(Array{T,N}(args...))
×
2145

2146
"""
2147
The `GenericOrder` can be used to test APIs for their support
2148
of generic ordered types.
2149
"""
2150
struct GenericOrder{T}
2151
    val::T
6,924✔
2152
end
2153
Base.isless(x::GenericOrder, y::GenericOrder) = isless(x.val,y.val)
13,656✔
2154

2155
Base.keys(a::GenericArray) = keys(a.a)
×
2156
Base.axes(a::GenericArray) = axes(a.a)
3,090✔
2157
Base.length(a::GenericArray) = length(a.a)
2,462✔
2158
Base.size(a::GenericArray) = size(a.a)
29✔
2159
Base.IndexStyle(::Type{<:GenericArray}) = IndexLinear()
499✔
2160
Base.getindex(a::GenericArray, i::Int) = a.a[i]
5,146✔
2161
Base.setindex!(a::GenericArray, x, i::Int) = a.a[i] = x
3,824✔
2162

2163
Base.similar(A::GenericArray, s::Integer...) = GenericArray(similar(A.a, s...))
116✔
2164

2165
"`guardseed(f)` runs the function `f()` and then restores the
2166
state of the global RNG as it was before."
2167
function guardseed(f::Function, r::AbstractRNG=default_rng())
32✔
2168
    old = copy(r)
32✔
2169
    try
17✔
2170
        f()
17✔
2171
    finally
2172
        copy!(r, old)
17✔
2173
    end
2174
end
2175

2176
"`guardseed(f, seed)` is equivalent to running `Random.seed!(seed); f()` and
2177
then restoring the state of the global RNG as it was before."
2178
guardseed(f::Function, seed::Union{Vector{UInt64},Vector{UInt32},Integer,NTuple{4,UInt64}}) = guardseed() do
8✔
2179
    Random.seed!(seed)
8✔
2180
    f()
8✔
2181
end
2182

2183
function _check_bitarray_consistency(B::BitArray{N}) where N
289,829✔
2184
    n = length(B)
289,829✔
2185
    if N ≠ 1
289,829✔
2186
        all(d ≥ 0 for d in B.dims) || (@warn("Negative d in dims: $(B.dims)"); return false)
2,442✔
2187
        prod(B.dims) ≠ n && (@warn("Inconsistent dims/len: prod(dims)=$(prod(B.dims)) len=$n"); return false)
2,442✔
2188
    end
2189
    Bc = B.chunks
289,829✔
2190
    nc = length(Bc)
289,829✔
2191
    nc == Base.num_bit_chunks(n) || (@warn("Incorrect chunks length for length $n: expected=$(Base.num_bit_chunks(n)) actual=$nc"); return false)
289,829✔
2192
    n == 0 && return true
289,829✔
2193
    Bc[end] & Base._msk_end(n) == Bc[end] || (@warn("Nonzero bits in chunk after `BitArray` end"); return false)
288,355✔
2194
    return true
288,355✔
2195
end
2196

2197
include("logging.jl")
2198
include("precompile.jl")
2199

2200
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