From 63aa1103823b259e073ce5eab83c034825acb0db Mon Sep 17 00:00:00 2001 From: Sebastian Stock <42280794+sostock@users.noreply.github.com> Date: Fri, 25 Dec 2020 02:49:35 +0100 Subject: [PATCH] Correct handling of non-Booleans in eval_test (#38695) --- stdlib/Test/src/Test.jl | 10 ++-- stdlib/Test/test/runtests.jl | 88 ++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 9585396d11856..c14678903f0aa 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -264,18 +264,18 @@ struct Threw <: ExecutionResult end function eval_test(evaluated::Expr, quoted::Expr, source::LineNumberNode, negate::Bool=false) - res = true - i = 1 evaled_args = evaluated.args quoted_args = quoted.args n = length(evaled_args) kw_suffix = "" if evaluated.head === :comparison args = evaled_args + res = true + i = 1 while i < n a, op, b = args[i], args[i+1], args[i+2] if res - res = op(a, b) === true # Keep `res` type stable + res = op(a, b) end quoted_args[i] = a quoted_args[i+2] = b @@ -287,7 +287,7 @@ function eval_test(evaluated::Expr, quoted::Expr, source::LineNumberNode, negate kwargs = evaled_args[2].args # Keyword arguments from `Expr(:parameters, ...)` args = evaled_args[3:n] - res = op(args...; kwargs...) === true + res = op(args...; kwargs...) # Create "Evaluated" expression which looks like the original call but has all of # the arguments evaluated @@ -312,7 +312,7 @@ function eval_test(evaluated::Expr, quoted::Expr, source::LineNumberNode, negate Returned(res, # stringify arguments in case of failure, for easy remote printing - res ? quoted : sprint(io->print(IOContext(io, :limit => true), quoted))*kw_suffix, + res === true ? quoted : sprint(io->print(IOContext(io, :limit => true), quoted))*kw_suffix, source) end diff --git a/stdlib/Test/test/runtests.jl b/stdlib/Test/test/runtests.jl index 8585166721d00..140553f134f56 100644 --- a/stdlib/Test/test/runtests.jl +++ b/stdlib/Test/test/runtests.jl @@ -1043,3 +1043,91 @@ end @test occursin(expected, result) end end + +# Non-booleans in @test (#35888) +struct T35888 end +Base.isequal(::T35888, ::T35888) = T35888() +Base.:!(::T35888) = missing +let errors = @testset NoThrowTestSet begin + # 1 - evaluates to non-Boolean + @test missing + # 2 - evaluates to non-Boolean + @test !missing + # 3 - evaluates to non-Boolean + @test isequal(5) + # 4 - evaluates to non-Boolean + @test !isequal(5) + # 5 - evaluates to non-Boolean + @test isequal(T35888(), T35888()) + # 6 - evaluates to non-Boolean + @test !isequal(T35888(), T35888()) + # 7 - evaluates to non-Boolean + @test 1 < 2 < missing + # 8 - evaluates to non-Boolean + @test !(1 < 2 < missing) + # 9 - TypeError in chained comparison + @test 1 < 2 < missing < 4 + # 10 - TypeError in chained comparison + @test !(1 < 2 < missing < 4) + end + + for err in errors + @test err isa Test.Error + end + + let str = sprint(show, errors[1]) + @test occursin("Expression evaluated to non-Boolean", str) + @test occursin("Expression: missing", str) + @test occursin("Value: missing", str) + end + + let str = sprint(show, errors[2]) + @test occursin("Expression evaluated to non-Boolean", str) + @test occursin("Expression: !missing", str) + @test occursin("Value: missing", str) + end + + let str = sprint(show, errors[3]) + @test occursin("Expression evaluated to non-Boolean", str) + @test occursin("Expression: isequal(5)", str) + end + + let str = sprint(show, errors[4]) + @test occursin("Expression evaluated to non-Boolean", str) + @test occursin("Expression: !(isequal(5))", str) + end + + let str = sprint(show, errors[5]) + @test occursin("Expression evaluated to non-Boolean", str) + @test occursin("Expression: isequal(T35888(), T35888())", str) + @test occursin("Value: $T35888()", str) + end + + let str = sprint(show, errors[6]) + @test occursin("Expression evaluated to non-Boolean", str) + @test occursin("Expression: !(isequal(T35888(), T35888()))", str) + @test occursin("Value: missing", str) + end + + let str = sprint(show, errors[7]) + @test occursin("Expression evaluated to non-Boolean", str) + @test occursin("Expression: 1 < 2 < missing", str) + @test occursin("Value: missing", str) + end + + let str = sprint(show, errors[8]) + @test occursin("Expression evaluated to non-Boolean", str) + @test occursin("Expression: !(1 < 2 < missing)", str) + @test occursin("Value: missing", str) + end + + let str = sprint(show, errors[9]) + @test occursin("TypeError: non-boolean (Missing) used in boolean context", str) + @test occursin("Expression: 1 < 2 < missing < 4", str) + end + + let str = sprint(show, errors[10]) + @test occursin("TypeError: non-boolean (Missing) used in boolean context", str) + @test occursin("Expression: !(1 < 2 < missing < 4)", str) + end +end