-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathmeta_testing_tools.jl
More file actions
183 lines (161 loc) · 5.4 KB
/
meta_testing_tools.jl
File metadata and controls
183 lines (161 loc) · 5.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# This is tools for testing ChainRulesTestUtils itself
# if they were less nasty in implementation we might consider moving them to a package
# MetaTesting.jl
# need to bring this into scope explictly so can use in @testset nonpassing_results
using Test: DefaultTestSet
"""
results(f)
`f` should be a function that takes no argument, and calls some code that used `@test`.
Invoking it via `results(f)` will prevent those `@test` being added to the current testset,
and will instead return a (flat) collection of all the test results.
"""
function results(f)
mute() do
res = []
# Specify testset type incase parent testset is some other typer
@testset DefaultTestSet "results internal" begin
f()
ts = Test.get_testset() # this is the current testset "nonpassing internal"
res = _flatten_results(ts)
# Prevent the failure being recorded in parent testset.
empty!(ts.results)
ts.anynonpass = false
end
# Note: we allow the "results internal" testset to still be pushed as an empty
# passing testset in its parent testset. We could remove that if we wanted
return res
end
end
"""
mute(f)
Calls `f()` silencing stdout.
"""
function mute(f)
# TODO: once we are on Julia 1.6 this can be change to just use
# `redirect_stdout(devnull)` See: https://github.com/JuliaLang/julia/pull/36146
mktemp() do path, tempio
redirect_stdout(tempio) do
f()
end
end
end
_flatten_results(x::Test.Result) = [x,]
_flatten_results(ts::Test.DefaultTestSet) = _flatten_results(ts.results)
function _flatten_results(xs::Vector)
if isempty(xs)
return Test.Result[]
else
return mapreduce(_flatten_results, vcat, xs)
end
end
"""
fails(f)
`f` should be a function that takes no argument, and calls some code that used `@test`.
`fails(f)` returns true if at least 1 `@test` fails.
If a test errors then it will display that error and throw an error of its own.
"""
function fails(f)
did_fail = false
for result in results(f)
did_fail |= result isa Test.Fail
if result isa Test.Error
show(result) # Log a error message, with original backtrace
# Sadly we can't throw the original exception as it is only stored as a String
error("Error occurred during `fails`")
end
end
return did_fail
end
"""
passes(f)
`f` should be a function that takes no argument, and calls some code that used `@test`.
`passes(f)` returns true if at least 1 `@test` passes and none error or fail.
If a test errors then it will display that error and throw an error of its own.
If a test fails then it will display that failure and return false
(Tests that are marked as broken are ignored).
"""
function passes(f)
did_pass = false
for result in results(f)
did_pass |= result isa Test.Pass
if result isa Test.Fail
show(result) # display failure
return false
end
if result isa Test.Error
show(result) # Log a error message, with original backtrace
# Sadly we can't throw the original exception as it is only stored as a String
error("Error occurred during `passes`")
end
end
return did_pass
end
#Meta Meta tests
@testset "meta_testing_tools.jl" begin
@testset "results" begin
@testset "No Tests" begin
res = results(()->nothing)
@test length(res) === 0
end
@testset "Single Test Pass" begin
res = results(()->@test true)
@test length(res) === 1
@test res[1].orig_expr == true
end
@testset "Single Test Fails" begin
res = results(()->@test false)
@test length(res) === 1
@test res[1].orig_expr == false
end
@testset "Single Testset" begin
res = results() do
@testset "inner" begin
@test false == true
@test true == true
@test true == false
end
end
@test length(res) === 3
@test res[1].orig_expr == :(false==true)
@test res[2].orig_expr == :(true==true)
@test res[3].orig_expr == :(true==false)
end
end
@testset "fails" begin
@test !fails(()->@test true)
@test fails(()->@test false)
@test !fails(()->@test_broken false)
@test fails() do
@testset "eg" begin
@test true
@test false
@test true
end
end
@test_throws Exception mute() do # mute it so we don't see the reprinted error.
fails(()->@test error("Bad"))
end
end
@testset "passes" begin
@test passes(()->@test true)
@test !passes(()->@test false)
@test !passes(()->@test_broken false)
@test passes() do
@testset "eg" begin
@test true
@test_broken false
@test true
end
end
@test !(passes() do
@testset "eg" begin
@test true
@test false
@test true
end
end)
@test_throws Exception mute() do # mute it so we don't see the reprinted error.
passes(()->@test error("Bad"))
end
end
end