Skip to content

Commit

Permalink
Correct handling of non-Booleans in eval_test (JuliaLang#38695)
Browse files Browse the repository at this point in the history
  • Loading branch information
sostock authored and ElOceanografo committed May 4, 2021
1 parent 3475564 commit 63aa110
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 5 deletions.
10 changes: 5 additions & 5 deletions stdlib/Test/src/Test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down
88 changes: 88 additions & 0 deletions stdlib/Test/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 63aa110

Please sign in to comment.