Skip to content

Commit ca11c2f

Browse files
Test: add durations to testset reports (JuliaLang#43252)
1 parent 83d7eba commit ca11c2f

5 files changed

Lines changed: 111 additions & 69 deletions

File tree

stdlib/Test/Project.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,9 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
66
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
77
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
88
Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
9+
10+
[extras]
11+
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
12+
13+
[targets]
14+
test = ["Distributed"]

stdlib/Test/docs/src/index.md

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -113,19 +113,19 @@ Test.TestSetException
113113

114114
We can put our tests for the `foo(x)` function in a test set:
115115

116-
```jldoctest testfoo
116+
```jldoctest testfoo; filter = r"[0-9\.]+s"
117117
julia> @testset "Foo Tests" begin
118118
@test foo("a") == 1
119119
@test foo("ab") == 4
120120
@test foo("abc") == 9
121121
end;
122-
Test Summary: | Pass Total
123-
Foo Tests | 3 3
122+
Test Summary: | Pass Total Time
123+
Foo Tests | 3 3 0.0s
124124
```
125125

126126
Test sets can also be nested:
127127

128-
```jldoctest testfoo
128+
```jldoctest testfoo; filter = r"[0-9\.]+s"
129129
julia> @testset "Foo Tests" begin
130130
@testset "Animals" begin
131131
@test foo("cat") == 9
@@ -136,20 +136,19 @@ julia> @testset "Foo Tests" begin
136136
@test foo(fill(1.0, i)) == i^2
137137
end
138138
end;
139-
Test Summary: | Pass Total
140-
Foo Tests | 8 8
139+
Test Summary: | Pass Total Time
140+
Foo Tests | 8 8 0.0s
141141
```
142142

143143
As well as call functions:
144144

145-
```jldoctest testfoo
145+
```jldoctest testfoo; filter = r"[0-9\.]+s"
146146
julia> f(x) = @test isone(x)
147147
f (generic function with 1 method)
148148
149-
julia> @testset f(1)
150-
Test Summary: | Pass Total
151-
f | 1 1
152-
Test.DefaultTestSet("f", Any[], 1, false, false)
149+
julia> @testset f(1);
150+
Test Summary: | Pass Total Time
151+
f | 1 1 0.0s
153152
```
154153

155154
This can be used to allow for factorization of test sets, making it easier to run individual
@@ -158,7 +157,7 @@ Note that in the case of functions, the test set will be given the name of the c
158157
In the event that a nested test set has no failures, as happened here, it will be hidden in the
159158
summary, unless the `verbose=true` option is passed:
160159

161-
```jldoctest testfoo
160+
```jldoctest testfoo; filter = r"[0-9\.]+s"
162161
julia> @testset verbose = true "Foo Tests" begin
163162
@testset "Animals" begin
164163
@test foo("cat") == 9
@@ -169,17 +168,17 @@ julia> @testset verbose = true "Foo Tests" begin
169168
@test foo(fill(1.0, i)) == i^2
170169
end
171170
end;
172-
Test Summary: | Pass Total
173-
Foo Tests | 8 8
174-
Animals | 2 2
175-
Arrays 1 | 2 2
176-
Arrays 2 | 2 2
177-
Arrays 3 | 2 2
171+
Test Summary: | Pass Total Time
172+
Foo Tests | 8 8 0.0s
173+
Animals | 2 2 0.0s
174+
Arrays 1 | 2 2 0.0s
175+
Arrays 2 | 2 2 0.0s
176+
Arrays 3 | 2 2 0.0s
178177
```
179178

180179
If we do have a test failure, only the details for the failed test sets will be shown:
181180

182-
```julia-repl
181+
```julia-repl; filter = r"[0-9\.]+s"
183182
julia> @testset "Foo Tests" begin
184183
@testset "Animals" begin
185184
@testset "Felines" begin
@@ -199,10 +198,10 @@ Arrays: Test Failed
199198
Expression: foo(fill(1.0, 4)) == 15
200199
Evaluated: 16 == 15
201200
[...]
202-
Test Summary: | Pass Fail Total
203-
Foo Tests | 3 1 4
204-
Animals | 2 2
205-
Arrays | 1 1 2
201+
Test Summary: | Pass Fail Total Time
202+
Foo Tests | 3 1 4 0.0s
203+
Animals | 2 2 0.0s
204+
Arrays | 1 1 2 0.0s
206205
ERROR: Some tests did not pass: 3 passed, 1 failed, 0 errored, 0 broken.
207206
```
208207

stdlib/Test/src/Test.jl

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -978,8 +978,11 @@ mutable struct DefaultTestSet <: AbstractTestSet
978978
n_passed::Int
979979
anynonpass::Bool
980980
verbose::Bool
981+
showtiming::Bool
982+
time_start::Float64
983+
time_end::Union{Float64,Nothing}
981984
end
982-
DefaultTestSet(desc::AbstractString; verbose::Bool = false) = DefaultTestSet(String(desc)::String, [], 0, false, verbose)
985+
DefaultTestSet(desc::AbstractString; verbose::Bool = false, showtiming::Bool = true) = DefaultTestSet(String(desc)::String, [], 0, false, verbose, showtiming, time(), nothing)
983986

984987
# For a broken result, simply store the result
985988
record(ts::DefaultTestSet, t::Broken) = (push!(ts.results, t); t)
@@ -1026,7 +1029,7 @@ end
10261029
function print_test_results(ts::DefaultTestSet, depth_pad=0)
10271030
# Calculate the overall number for each type so each of
10281031
# the test result types are aligned
1029-
passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken = get_test_counts(ts)
1032+
passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration = get_test_counts(ts)
10301033
total_pass = passes + c_passes
10311034
total_fail = fails + c_fails
10321035
total_error = errors + c_errors
@@ -1044,6 +1047,7 @@ function print_test_results(ts::DefaultTestSet, depth_pad=0)
10441047
error_width = dig_error > 0 ? max(length("Error"), dig_error) : 0
10451048
broken_width = dig_broken > 0 ? max(length("Broken"), dig_broken) : 0
10461049
total_width = dig_total > 0 ? max(length("Total"), dig_total) : 0
1050+
duration_width = max(length("Time"), length(duration))
10471051
# Calculate the alignment of the test result counts by
10481052
# recursively walking the tree of test sets
10491053
align = max(get_alignment(ts, 0), length("Test Summary:"))
@@ -1063,11 +1067,14 @@ function print_test_results(ts::DefaultTestSet, depth_pad=0)
10631067
printstyled(lpad("Broken", broken_width, " "), " "; bold=true, color=Base.warn_color())
10641068
end
10651069
if total_width > 0
1066-
printstyled(lpad("Total", total_width, " "); bold=true, color=Base.info_color())
1070+
printstyled(lpad("Total", total_width, " "), " "; bold=true, color=Base.info_color())
1071+
end
1072+
if ts.showtiming
1073+
printstyled(lpad("Time", duration_width, " "); bold=true)
10671074
end
10681075
println()
10691076
# Recursively print a summary at every level
1070-
print_counts(ts, depth_pad, align, pass_width, fail_width, error_width, broken_width, total_width)
1077+
print_counts(ts, depth_pad, align, pass_width, fail_width, error_width, broken_width, total_width, duration_width, ts.showtiming)
10711078
end
10721079

10731080

@@ -1076,6 +1083,7 @@ const TESTSET_PRINT_ENABLE = Ref(true)
10761083
# Called at the end of a @testset, behaviour depends on whether
10771084
# this is a child of another testset, or the "root" testset
10781085
function finish(ts::DefaultTestSet)
1086+
ts.time_end = time()
10791087
# If we are a nested test set, do not print a full summary
10801088
# now - let the parent test set do the printing
10811089
if get_testset_depth() != 0
@@ -1084,7 +1092,7 @@ function finish(ts::DefaultTestSet)
10841092
record(parent_ts, ts)
10851093
return ts
10861094
end
1087-
passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken = get_test_counts(ts)
1095+
passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration = get_test_counts(ts)
10881096
total_pass = passes + c_passes
10891097
total_fail = fails + c_fails
10901098
total_error = errors + c_errors
@@ -1148,24 +1156,36 @@ function get_test_counts(ts::DefaultTestSet)
11481156
isa(t, Error) && (errors += 1)
11491157
isa(t, Broken) && (broken += 1)
11501158
if isa(t, DefaultTestSet)
1151-
np, nf, ne, nb, ncp, ncf, nce , ncb = get_test_counts(t)
1159+
np, nf, ne, nb, ncp, ncf, nce , ncb, duration = get_test_counts(t)
11521160
c_passes += np + ncp
11531161
c_fails += nf + ncf
11541162
c_errors += ne + nce
11551163
c_broken += nb + ncb
11561164
end
11571165
end
11581166
ts.anynonpass = (fails + errors + c_fails + c_errors > 0)
1159-
return passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken
1167+
duration = if isnothing(ts.time_end)
1168+
""
1169+
else
1170+
dur_s = ts.time_end - ts.time_start
1171+
if dur_s < 60
1172+
string(round(dur_s, digits = 1), "s")
1173+
else
1174+
m, s = divrem(dur_s, 60)
1175+
s = lpad(string(round(s, digits = 1)), 4, "0")
1176+
string(round(Int, m), "m", s, "s")
1177+
end
1178+
end
1179+
return passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration
11601180
end
11611181

11621182
# Recursive function that prints out the results at each level of
11631183
# the tree of test sets
11641184
function print_counts(ts::DefaultTestSet, depth, align,
1165-
pass_width, fail_width, error_width, broken_width, total_width)
1185+
pass_width, fail_width, error_width, broken_width, total_width, duration_width, showtiming)
11661186
# Count results by each type at this level, and recursively
11671187
# through any child test sets
1168-
passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken = get_test_counts(ts)
1188+
passes, fails, errors, broken, c_passes, c_fails, c_errors, c_broken, duration = get_test_counts(ts)
11691189
subtotal = passes + fails + errors + broken + c_passes + c_fails + c_errors + c_broken
11701190
# Print test set header, with an alignment that ensures all
11711191
# the test results appear above each other
@@ -1204,9 +1224,13 @@ function print_counts(ts::DefaultTestSet, depth, align,
12041224
end
12051225

12061226
if np == 0 && nf == 0 && ne == 0 && nb == 0
1207-
printstyled("No tests", color=Base.info_color())
1227+
printstyled(lpad("None", total_width, " "), " ", color=Base.info_color())
12081228
else
1209-
printstyled(lpad(string(subtotal), total_width, " "), color=Base.info_color())
1229+
printstyled(lpad(string(subtotal), total_width, " "), " ", color=Base.info_color())
1230+
end
1231+
1232+
if showtiming
1233+
printstyled(lpad(string(duration), duration_width, " "))
12101234
end
12111235
println()
12121236

@@ -1216,7 +1240,7 @@ function print_counts(ts::DefaultTestSet, depth, align,
12161240
for t in ts.results
12171241
if isa(t, DefaultTestSet)
12181242
print_counts(t, depth + 1, align,
1219-
pass_width, fail_width, error_width, broken_width, total_width)
1243+
pass_width, fail_width, error_width, broken_width, total_width, duration_width, ts.showtiming)
12201244
end
12211245
end
12221246
end
@@ -1256,8 +1280,11 @@ along with a summary of the test results.
12561280
Any custom testset type (subtype of `AbstractTestSet`) can be given and it will
12571281
also be used for any nested `@testset` invocations. The given options are only
12581282
applied to the test set where they are given. The default test set type
1259-
accepts the `verbose` boolean option: if `true`, the result summary of the
1260-
nested testsets is shown even when they all pass (the default is `false`).
1283+
accepts two boolean options:
1284+
- `verbose`: if `true`, the result summary of the nested testsets is shown even
1285+
when they all pass (the default is `false`).
1286+
- `showtiming`: if `true`, the duration of each displayed testset is shown
1287+
(the default is `true`).
12611288
12621289
The description string accepts interpolation from the loop indices.
12631290
If no description is provided, one is constructed based on the variables.
@@ -1278,16 +1305,16 @@ re-arrangements of `@testset`s regardless of their side-effect on the
12781305
global RNG state.
12791306
12801307
# Examples
1281-
```jldoctest
1308+
```jldoctest; filter = r"trigonometric identities | 4 4 [0-9\\.]+s"
12821309
julia> @testset "trigonometric identities" begin
12831310
θ = 2/3*π
12841311
@test sin(-θ) ≈ -sin(θ)
12851312
@test cos(-θ) ≈ cos(θ)
12861313
@test sin(2θ) ≈ 2*sin(θ)*cos(θ)
12871314
@test cos(2θ) ≈ cos(θ)^2 - sin(θ)^2
12881315
end;
1289-
Test Summary: | Pass Total
1290-
trigonometric identities | 4 4
1316+
Test Summary: | Pass Total Time
1317+
trigonometric identities | 4 4 0.2s
12911318
```
12921319
"""
12931320
macro testset(args...)

stdlib/Test/test/runtests.jl

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -760,13 +760,13 @@ let msg = read(pipeline(ignorestatus(`$(Base.julia_cmd()) --startup-file=no --co
760760
@test foo(fill(1., 4)) == 15
761761
end
762762
end'`), stderr=devnull), String)
763-
@test occursin("""
764-
Test Summary: | Pass Fail Total
765-
Foo Tests | 2 2 4
766-
Animals | 1 1 2
767-
Felines | 1 1
768-
Canines | 1 1
769-
Arrays | 1 1 2
763+
@test occursin(r"""
764+
Test Summary: | Pass Fail Total Time
765+
Foo Tests | 2 2 4 \s*\d*.\ds
766+
Animals | 1 1 2 \s*\d*.\ds
767+
Felines | 1 1 \s*\d*.\ds
768+
Canines | 1 1 \s*\d*.\ds
769+
Arrays | 1 1 2 \s*\d*.\ds
770770
""", msg)
771771
end
772772

@@ -1078,18 +1078,18 @@ let ex = :(something_complex + [1, 2, 3])
10781078
end
10791079

10801080
@testset "verbose option" begin
1081-
expected = """
1082-
Test Summary: | Pass Total
1083-
Parent | 9 9
1084-
Child 1 | 3 3
1085-
Child 1.1 (long name) | 1 1
1086-
Child 1.2 | 1 1
1087-
Child 1.3 | 1 1
1088-
Child 2 | 3 3
1089-
Child 3 | 3 3
1090-
Child 3.1 | 1 1
1091-
Child 3.2 | 1 1
1092-
Child 3.3 | 1 1
1081+
expected = r"""
1082+
Test Summary: | Pass Total Time
1083+
Parent | 9 9 \s*\d*.\ds
1084+
Child 1 | 3 3 \s*\d*.\ds
1085+
Child 1.1 (long name) | 1 1 \s*\d*.\ds
1086+
Child 1.2 | 1 1 \s*\d*.\ds
1087+
Child 1.3 | 1 1 \s*\d*.\ds
1088+
Child 2 | 3 3 \s*\d*.\ds
1089+
Child 3 | 3 3 \s*\d*.\ds
1090+
Child 3.1 | 1 1 \s*\d*.\ds
1091+
Child 3.2 | 1 1 \s*\d*.\ds
1092+
Child 3.3 | 1 1 \s*\d*.\ds
10931093
"""
10941094

10951095
mktemp() do f, _

0 commit comments

Comments
 (0)