Skip to content

Commit e985652

Browse files
authored
Show evaluated test arguments for most functions (#57825)
When using `@test` any function call, except for broadcast calls, will always display the evaluated arguments. Having this makes it much easier at a glance to debug the problem: ```julia julia> using Test julia> x = [1,2]; julia> y = [1,3]; julia> @test issubset(x, y) Test Failed at REPL[5]:1 Expression: issubset(x, y) Evaluated: issubset([1, 2], [1, 3]) ERROR: There was an error during testing ``` Alternatively, users could use `@testset let` however it can be quite tedious to add those blocks to test suites.
1 parent eed18bd commit e985652

File tree

3 files changed

+98
-65
lines changed

3 files changed

+98
-65
lines changed

stdlib/Test/docs/src/index.md

-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,6 @@ Test Passed
230230
julia> @test 1 ≈ 0.999999
231231
Test Failed at none:1
232232
Expression: 1 ≈ 0.999999
233-
Evaluated: 1 ≈ 0.999999
234233
235234
ERROR: There was an error during testing
236235
```

stdlib/Test/src/Test.jl

+15-14
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,6 @@ using InteractiveUtils: gen_call_with_extracted_types
3030
using Base: typesplit, remove_linenums!
3131
using Serialization: Serialization
3232

33-
const DISPLAY_FAILED = (
34-
:isequal,
35-
:isapprox,
36-
:,
37-
:occursin,
38-
:startswith,
39-
:endswith,
40-
:isempty,
41-
:contains
42-
)
43-
4433
const FAIL_FAST = Ref{Bool}(false)
4534

4635
const record_passes = OncePerProcess{Bool}() do
@@ -201,7 +190,7 @@ function Base.show(io::IO, t::Fail)
201190
print(io, "\n Expected: ", data)
202191
print(io, "\n No exception thrown")
203192
elseif t.test_type === :test
204-
if data !== nothing
193+
if data !== nothing && t.orig_expr != data
205194
# The test was an expression, so display the term-by-term
206195
# evaluated version as well
207196
print(io, "\n Evaluated: ", data)
@@ -587,6 +576,16 @@ macro test_skip(ex, kws...)
587576
return :(record(get_testset(), $testres))
588577
end
589578

579+
function _can_escape_call(@nospecialize ex)
580+
ex.head === :call || return false
581+
582+
# Broadcasted functions are not currently supported
583+
first(string(ex.args[1])) != '.' || return false
584+
585+
# At least one positional argument or keyword
586+
return length(ex.args) > 1
587+
end
588+
590589
# An internal function, called by the code generated by the @test
591590
# macro to get results of the test expression.
592591
# In the special case of a comparison, e.g. x == 5, generate code to
@@ -623,7 +622,7 @@ function get_test_result(ex, source)
623622
$(QuoteNode(source)),
624623
$negate,
625624
))
626-
elseif isa(ex, Expr) && ex.head === :call && ex.args[1] in DISPLAY_FAILED
625+
elseif isa(ex, Expr) && _can_escape_call(ex)
627626
escaped_func = esc(ex.args[1])
628627
quoted_func = QuoteNode(ex.args[1])
629628

@@ -820,7 +819,7 @@ function do_test_throws(result::ExecutionResult, orig_expr, extype)
820819
Base.depwarn("macroexpand no longer throws a LoadError so `@test_throws LoadError ...` is deprecated and passed without checking the error type!", :do_test_throws)
821820
true
822821
elseif extype == ErrorException && isa(exc, FieldError)
823-
Base.depwarn(lazy"ErrorException should no longer be used to test field access; FieldError should be used instead!", :do_test_throws)
822+
Base.depwarn(lazy"Using ErrorException to test field access is deprecated; use FieldError instead.", :do_test_throws)
824823
true
825824
else
826825
isa(exc, extype)
@@ -1637,6 +1636,7 @@ julia> @testset let logi = log(im)
16371636
end
16381637
Test Failed at none:3
16391638
Expression: !(iszero(real(logi)))
1639+
Evaluated: !(iszero(0.0))
16401640
Context: logi = 0.0 + 1.5707963267948966im
16411641
16421642
ERROR: There was an error during testing
@@ -1647,6 +1647,7 @@ julia> @testset let logi = log(im), op = !iszero
16471647
end
16481648
Test Failed at none:3
16491649
Expression: op(real(logi))
1650+
Evaluated: op(0.0)
16501651
Context: logi = 0.0 + 1.5707963267948966im
16511652
op = !iszero
16521653

stdlib/Test/test/runtests.jl

+83-50
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

33
using Test, Random
4-
using Test: guardseed
4+
using Test: guardseed, _can_escape_call
55
using Serialization
66
using Distributed: RemoteException
77

@@ -115,54 +115,61 @@ let fails = @testset NoThrowTestSet begin
115115
@test_throws OverflowError error()
116116
# 2 - Fail - no exception
117117
@test_throws OverflowError 1 + 1
118-
# 3 - Fail - comparison
118+
# 3 & 4 - Fail - comparison
119+
@test 1 == 2
119120
@test 1+1 == 2+2
120-
# 4 - Fail - approximate comparison
121+
# 5 - Fail - approximate comparison
121122
@test 1/1 2/1
122-
# 5 - Fail - chained comparison
123+
# 6 - Fail - chained comparison
123124
@test 1+0 == 2+0 == 3+0
124-
# 6 - Fail - comparison call
125+
# 7 - Fail - comparison call
125126
@test ==(1 - 2, 2 - 1)
126-
# 7 - Fail - splatting
127+
# 8 - Fail - splatting
127128
@test ==(1:2...)
128-
# 8 - Fail - isequal
129+
# 9 - Fail - isequal
129130
@test isequal(0 / 0, 1 / 0)
130-
# 9 - Fail - function splatting
131+
# 10 - Fail - function splatting
131132
@test isequal(1:2...)
132-
# 10 - Fail - isapprox
133+
# 11 - Fail - isapprox
133134
@test isapprox(0 / 1, -1 / 0)
134-
# 11 & 12 - Fail - function with keyword
135+
# 12 & 13 - Fail - function with keyword
135136
@test isapprox(1 / 2, 2 / 1, atol=1 / 1)
136137
@test isapprox(1 - 2, 2 - 1; atol=1 - 1)
137-
# 13 - Fail - function keyword splatting
138+
# 14 - Fail - function keyword splatting
138139
k = [(:atol, 0), (:nans, true)]
139140
@test isapprox(1, 2; k...)
140-
# 14 - Fail - call negation
141+
# 15 - Fail - call negation
141142
@test !isequal(1, 2 - 1)
142-
# 15 - Fail - comparison negation
143+
# 16 - Fail - comparison negation
143144
@test !(2 + 3 == 1 + 4)
144-
# 16 - Fail - chained negation
145+
# 17 - Fail - chained negation
145146
@test !(2 + 3 == 1 + 4 == 5)
146-
# 17 - Fail - isempty
147+
# 18 - Fail - isempty
147148
nonempty = [1, 2, 3]
148149
@test isempty(nonempty)
149150
str1 = "Hello"
150151
str2 = "World"
151-
# 18 - Fail - occursin
152+
# 19 - Fail - occursin
152153
@test occursin(str1, str2)
153-
# 19 - Fail - startswith
154+
# 20 - Fail - startswith
154155
@test startswith(str1, str2)
155-
# 20 - Fail - endswith
156+
# 21 - Fail - endswith
156157
@test endswith(str1, str2)
157-
# 21 - Fail - contains
158-
@test contains(str1, str2)
159-
# 22 - Fail - Type Comparison
158+
# 22 - Fail - contains
159+
@test Base.contains(str1, str2)
160+
# 23 - Fail - issetequal
161+
a = [1, 2]
162+
b = [1, 3]
163+
@test issetequal(a, b)
164+
# 24 - Fail - Type Comparison
160165
@test typeof(1) <: typeof("julia")
161-
# 23 - 26 - Fail - wrong message
166+
# 27 - 28 - Fail - wrong message
162167
@test_throws "A test" error("a test")
163168
@test_throws r"sqrt\([Cc]omplx" sqrt(-1)
164169
@test_throws str->occursin("a T", str) error("a test")
165170
@test_throws ["BoundsError", "acquire", "1-element", "at index [2]"] [1][2]
171+
# 29 - Fail - broadcast
172+
@test 1 .== 2
166173
end
167174
for fail in fails
168175
@test fail isa Test.Fail
@@ -179,125 +186,140 @@ let fails = @testset NoThrowTestSet begin
179186
end
180187

181188
let str = sprint(show, fails[3])
189+
@test occursin("Expression: 1 == 2", str)
190+
@test !occursin("Evaluated", str)
191+
end
192+
193+
let str = sprint(show, fails[4])
182194
@test occursin("Expression: 1 + 1 == 2 + 2", str)
183195
@test occursin("Evaluated: 2 == 4", str)
184196
end
185197

186-
let str = sprint(show, fails[4])
198+
let str = sprint(show, fails[5])
187199
@test occursin("Expression: 1 / 1 ≈ 2 / 1", str)
188200
@test occursin("Evaluated: 1.0 ≈ 2.0", str)
189201
end
190202

191-
let str = sprint(show, fails[5])
203+
let str = sprint(show, fails[6])
192204
@test occursin("Expression: 1 + 0 == 2 + 0 == 3 + 0", str)
193205
@test occursin("Evaluated: 1 == 2 == 3", str)
194206
end
195207

196-
let str = sprint(show, fails[6])
208+
let str = sprint(show, fails[7])
197209
@test occursin("Expression: 1 - 2 == 2 - 1", str)
198210
@test occursin("Evaluated: -1 == 1", str)
199211
end
200212

201-
let str = sprint(show, fails[7])
213+
let str = sprint(show, fails[8])
202214
@test occursin("Expression: (==)(1:2...)", str)
203-
@test !occursin("Evaluated", str)
215+
@test occursin("Evaluated: 1 == 2", str)
204216
end
205217

206-
let str = sprint(show, fails[8])
218+
let str = sprint(show, fails[9])
207219
@test occursin("Expression: isequal(0 / 0, 1 / 0)", str)
208220
@test occursin("Evaluated: isequal(NaN, Inf)", str)
209221
end
210222

211-
let str = sprint(show, fails[9])
223+
let str = sprint(show, fails[10])
212224
@test occursin("Expression: isequal(1:2...)", str)
213225
@test occursin("Evaluated: isequal(1, 2)", str)
214226
end
215227

216-
let str = sprint(show, fails[10])
228+
let str = sprint(show, fails[11])
217229
@test occursin("Expression: isapprox(0 / 1, -1 / 0)", str)
218230
@test occursin("Evaluated: isapprox(0.0, -Inf)", str)
219231
end
220232

221-
let str = sprint(show, fails[11])
233+
let str = sprint(show, fails[12])
222234
@test occursin("Expression: isapprox(1 / 2, 2 / 1, atol = 1 / 1)", str)
223235
@test occursin("Evaluated: isapprox(0.5, 2.0; atol = 1.0)", str)
224236
end
225237

226-
let str = sprint(show, fails[12])
238+
let str = sprint(show, fails[13])
227239
@test occursin("Expression: isapprox(1 - 2, 2 - 1; atol = 1 - 1)", str)
228240
@test occursin("Evaluated: isapprox(-1, 1; atol = 0)", str)
229241
end
230242

231-
let str = sprint(show, fails[13])
243+
let str = sprint(show, fails[14])
232244
@test occursin("Expression: isapprox(1, 2; k...)", str)
233245
@test occursin("Evaluated: isapprox(1, 2; atol = 0, nans = true)", str)
234246
end
235247

236-
let str = sprint(show, fails[14])
248+
let str = sprint(show, fails[15])
237249
@test occursin("Expression: !(isequal(1, 2 - 1))", str)
238250
@test occursin("Evaluated: !(isequal(1, 1))", str)
239251
end
240252

241-
let str = sprint(show, fails[15])
253+
let str = sprint(show, fails[16])
242254
@test occursin("Expression: !(2 + 3 == 1 + 4)", str)
243255
@test occursin("Evaluated: !(5 == 5)", str)
244256
end
245257

246-
let str = sprint(show, fails[16])
258+
let str = sprint(show, fails[17])
247259
@test occursin("Expression: !(2 + 3 == 1 + 4 == 5)", str)
248260
@test occursin("Evaluated: !(5 == 5 == 5)", str)
249261
end
250262

251-
let str = sprint(show, fails[17])
263+
let str = sprint(show, fails[18])
252264
@test occursin("Expression: isempty(nonempty)", str)
253265
@test occursin("Evaluated: isempty([1, 2, 3])", str)
254266
end
255267

256-
let str = sprint(show, fails[18])
268+
let str = sprint(show, fails[19])
257269
@test occursin("Expression: occursin(str1, str2)", str)
258270
@test occursin("Evaluated: occursin(\"Hello\", \"World\")", str)
259271
end
260272

261-
let str = sprint(show, fails[19])
273+
let str = sprint(show, fails[20])
262274
@test occursin("Expression: startswith(str1, str2)", str)
263275
@test occursin("Evaluated: startswith(\"Hello\", \"World\")", str)
264276
end
265277

266-
let str = sprint(show, fails[20])
278+
let str = sprint(show, fails[21])
267279
@test occursin("Expression: endswith(str1, str2)", str)
268280
@test occursin("Evaluated: endswith(\"Hello\", \"World\")", str)
269281
end
270282

271-
let str = sprint(show, fails[21])
272-
@test occursin("Expression: contains(str1, str2)", str)
273-
@test occursin("Evaluated: contains(\"Hello\", \"World\")", str)
283+
let str = sprint(show, fails[22])
284+
@test occursin("Expression: Base.contains(str1, str2)", str)
285+
@test occursin("Evaluated: Base.contains(\"Hello\", \"World\")", str)
274286
end
275287

276-
let str = sprint(show, fails[22])
288+
let str = sprint(show, fails[23])
289+
@test occursin("Expression: issetequal(a, b)", str)
290+
@test occursin("Evaluated: issetequal([1, 2], [1, 3])", str)
291+
end
292+
293+
let str = sprint(show, fails[24])
277294
@test occursin("Expression: typeof(1) <: typeof(\"julia\")", str)
278295
@test occursin("Evaluated: $(typeof(1)) <: $(typeof("julia"))", str)
279296
end
280297

281-
let str = sprint(show, fails[23])
298+
let str = sprint(show, fails[25])
282299
@test occursin("Expected: \"A test\"", str)
283300
@test occursin("Message: \"a test\"", str)
284301
end
285302

286-
let str = sprint(show, fails[24])
303+
let str = sprint(show, fails[26])
287304
@test occursin("Expected: r\"sqrt\\([Cc]omplx\"", str)
288305
@test occursin(r"Message: .*Try sqrt\(Complex", str)
289306
end
290307

291-
let str = sprint(show, fails[25])
308+
let str = sprint(show, fails[27])
292309
@test occursin("Expected: < match function >", str)
293310
@test occursin("Message: \"a test\"", str)
294311
end
295312

296-
let str = sprint(show, fails[26])
313+
let str = sprint(show, fails[28])
297314
@test occursin("Expected: [\"BoundsError\", \"acquire\", \"1-element\", \"at index [2]\"]", str)
298315
@test occursin(r"Message: \"BoundsError.* 1-element.*at index \[2\]", str)
299316
end
300317

318+
let str = sprint(show, fails[29])
319+
@test occursin("Expression: 1 .== 2", str)
320+
@test !occursin("Evaluated", str)
321+
end
322+
301323
end
302324

303325
struct BadError <: Exception end
@@ -345,9 +367,9 @@ let retval_tests = @testset NoThrowTestSet begin
345367
pass_mock = Test.Pass(:test, 1, 2, 3, LineNumberNode(0, "A Pass Mock"))
346368
@test Test.record(ts, pass_mock) isa Test.Pass
347369
error_mock = Test.Error(:test, 1, 2, 3, LineNumberNode(0, "An Error Mock"))
348-
@test Test.record(ts, error_mock) isa Test.Error
370+
@test Test.record(ts, error_mock; print_result=false) isa Test.Error
349371
fail_mock = Test.Fail(:test, 1, 2, 3, nothing, LineNumberNode(0, "A Fail Mock"), false)
350-
@test Test.record(ts, fail_mock) isa Test.Fail
372+
@test Test.record(ts, fail_mock; print_result=false) isa Test.Fail
351373
broken_mock = Test.Broken(:test, LineNumberNode(0, "A Broken Mock"))
352374
@test Test.record(ts, broken_mock) isa Test.Broken
353375
end
@@ -1770,3 +1792,14 @@ end
17701792
end
17711793
end
17721794
end
1795+
1796+
@testset "_can_escape_call" begin
1797+
@test !_can_escape_call(:(f()))
1798+
@test _can_escape_call(:(f(x)))
1799+
@test _can_escape_call(:(f(; x)))
1800+
@test _can_escape_call(:(f(x=1)))
1801+
1802+
@test _can_escape_call(:(x == y))
1803+
@test !_can_escape_call(:(x .== y))
1804+
@test !_can_escape_call(:((==).(x, y)))
1805+
end

0 commit comments

Comments
 (0)