From f2805a596f7d9bbe63f78d997e22410df84e2525 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Thu, 8 Jun 2017 22:59:12 -0500 Subject: [PATCH 1/4] Correct `@test` using chained comparison --- base/test.jl | 5 +---- test/test.jl | 7 +++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/base/test.jl b/base/test.jl index 9180f27061f01..45ac74eee39eb 100644 --- a/base/test.jl +++ b/base/test.jl @@ -300,10 +300,7 @@ function get_test_result(ex) $(esc(ex.args[2])), $(esc(ex.args[1])), $(esc(ex.args[3]))))) elseif isa(ex, Expr) && ex.head == :comparison # pass all terms of the comparison to `eval_comparison`, as an Expr - terms = ex.args - for i = 1:length(terms) - terms[i] = esc(terms[i]) - end + terms = [esc(arg) for arg in ex.args] testret = :(eval_comparison(Expr(:comparison, $(terms...)))) else testret = :(Returned($(esc(ex)), nothing)) diff --git a/test/test.jl b/test/test.jl index 01898c234e93c..0bc69fb52c869 100644 --- a/test/test.jl +++ b/test/test.jl @@ -60,16 +60,19 @@ fails = @testset NoThrowTestSet begin @test_throws OverflowError 1 + 1 # Fail - comparison @test 1+1 == 2+2 + # Fail - chained comparison + @test 1+0 == 2+0 == 3+0 # Error - unexpected pass @test_broken true end -for i in 1:3 +for i in 1:length(fails) - 1 @test isa(fails[i], Base.Test.Fail) end @test contains(sprint(show, fails[1]), "Thrown: ErrorException") @test contains(sprint(show, fails[2]), "No exception thrown") @test contains(sprint(show, fails[3]), "Evaluated: 2 == 4") -@test contains(sprint(show, fails[4]), "Unexpected Pass") +@test contains(sprint(show, fails[4]), "Evaluated: 1 == 2 == 3") +@test contains(sprint(show, fails[5]), "Unexpected Pass") # Test printing of a TestSetException tse_str = sprint(show, Test.TestSetException(1,2,3,4,Vector{Union{Base.Test.Error, Base.Test.Fail}}())) From a7ba25bc4c6f54d18b6ea93d3eda0b974909a7a1 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Fri, 9 Jun 2017 08:42:01 -0500 Subject: [PATCH 2/4] =?UTF-8?q?Revise=20`get=5Ftest=5Fresult`=20to=20work?= =?UTF-8?q?=20correctly=20with=20=E2=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Escaping the ≈ operator would result in the displayed evaluated expression appearing as `x isapprox y` instead of `x ≈ y`. Additionally, the code was written to ensure that the arguments of `@test` are only evaluated once. --- base/test.jl | 33 ++++++++++++++++++++------------- test/test.jl | 13 +++++++++++-- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/base/test.jl b/base/test.jl index 45ac74eee39eb..7f6152119aa84 100644 --- a/base/test.jl +++ b/base/test.jl @@ -186,19 +186,22 @@ struct Threw <: ExecutionResult backtrace end -function eval_comparison(ex::Expr) +function eval_comparison(evaluated::Expr, quoted::Expr) res = true i = 1 - a = ex.args - n = length(a) + args = evaluated.args + quoted_args = quoted.args + n = length(args) while i < n - res = a[i+1](a[i], a[i+2]) - if !isa(res,Bool) || !res - break + a, op, b = args[i], args[i+1], args[i+2] + if res + res = op(a, b) === true # Keep `res` type stable end + quoted_args[i] = a + quoted_args[i+2] = b i += 2 end - Returned(res, ex) + Returned(res, quoted) end const comparison_prec = Base.operator_precedence(:(==)) @@ -293,15 +296,19 @@ end function get_test_result(ex) orig_ex = Expr(:inert, ex) # Normalize non-dot comparison operator calls to :comparison expressions - if isa(ex, Expr) && ex.head == :call && length(ex.args)==3 && + if isa(ex, Expr) && ex.head == :call && length(ex.args) == 3 && first(string(ex.args[1])) != '.' && (ex.args[1] === :(==) || Base.operator_precedence(ex.args[1]) == comparison_prec) - testret = :(eval_comparison(Expr(:comparison, - $(esc(ex.args[2])), $(esc(ex.args[1])), $(esc(ex.args[3]))))) - elseif isa(ex, Expr) && ex.head == :comparison + ex = Expr(:comparison, ex.args[2], ex.args[1], ex.args[3]) + end + if isa(ex, Expr) && ex.head == :comparison # pass all terms of the comparison to `eval_comparison`, as an Expr - terms = [esc(arg) for arg in ex.args] - testret = :(eval_comparison(Expr(:comparison, $(terms...)))) + escaped_terms = [esc(arg) for arg in ex.args] + quoted_terms = [QuoteNode(arg) for arg in ex.args] + testret = :(eval_comparison( + Expr(:comparison, $(escaped_terms...)), + Expr(:comparison, $(quoted_terms...)), + )) else testret = :(Returned($(esc(ex)), nothing)) end diff --git a/test/test.jl b/test/test.jl index 0bc69fb52c869..bd81cd37279d9 100644 --- a/test/test.jl +++ b/test/test.jl @@ -7,6 +7,12 @@ @test strip("\t hi \n") == "hi" @test strip("\t this should fail \n") != "hi" +# @test should only evaluate the arguments once +let g = Int[], f = (x) -> (push!(g, x); x) + @test f(1) == 1 + @test g == [1] +end + # Test @test_broken with fail @test_broken false @test_broken 1 == 2 @@ -60,6 +66,8 @@ fails = @testset NoThrowTestSet begin @test_throws OverflowError 1 + 1 # Fail - comparison @test 1+1 == 2+2 + # Fail - approximate comparison + @test 1/1 ≈ 2/1 # Fail - chained comparison @test 1+0 == 2+0 == 3+0 # Error - unexpected pass @@ -71,8 +79,9 @@ end @test contains(sprint(show, fails[1]), "Thrown: ErrorException") @test contains(sprint(show, fails[2]), "No exception thrown") @test contains(sprint(show, fails[3]), "Evaluated: 2 == 4") -@test contains(sprint(show, fails[4]), "Evaluated: 1 == 2 == 3") -@test contains(sprint(show, fails[5]), "Unexpected Pass") +@test contains(sprint(show, fails[4]), "Evaluated: 1.0 ≈ 2.0") +@test contains(sprint(show, fails[5]), "Evaluated: 1 == 2 == 3") +@test contains(sprint(show, fails[6]), "Unexpected Pass") # Test printing of a TestSetException tse_str = sprint(show, Test.TestSetException(1,2,3,4,Vector{Union{Base.Test.Error, Base.Test.Fail}}())) From 2a37b808b38162d1e52e378f0f8a20ea439c68a4 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Fri, 9 Jun 2017 09:18:39 -0500 Subject: [PATCH 3/4] Update tests to check expression output --- test/test.jl | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/test/test.jl b/test/test.jl index bd81cd37279d9..a916906be340d 100644 --- a/test/test.jl +++ b/test/test.jl @@ -76,12 +76,30 @@ end for i in 1:length(fails) - 1 @test isa(fails[i], Base.Test.Fail) end -@test contains(sprint(show, fails[1]), "Thrown: ErrorException") -@test contains(sprint(show, fails[2]), "No exception thrown") -@test contains(sprint(show, fails[3]), "Evaluated: 2 == 4") -@test contains(sprint(show, fails[4]), "Evaluated: 1.0 ≈ 2.0") -@test contains(sprint(show, fails[5]), "Evaluated: 1 == 2 == 3") -@test contains(sprint(show, fails[6]), "Unexpected Pass") + +str = sprint(show, fails[1]) +@test contains(str, "Expression: error()") +@test contains(str, "Thrown: ErrorException") + +str = sprint(show, fails[2]) +@test contains(str, "Expression: 1 + 1") +@test contains(str, "No exception thrown") + +str = sprint(show, fails[3]) +@test contains(str, "Expression: 1 + 1 == 2 + 2") +@test contains(str, "Evaluated: 2 == 4") + +str = sprint(show, fails[4]) +@test contains(str, "Expression: 1 / 1 ≈ 2 / 1") +@test contains(str, "Evaluated: 1.0 ≈ 2.0") + +str = sprint(show, fails[5]) +@test contains(str, "Expression: 1 + 0 == 2 + 0 == 3 + 0") +@test contains(str, "Evaluated: 1 == 2 == 3") + +str = sprint(show, fails[6]) +@test contains(str, "Unexpected Pass") +@test contains(str, "Expression: true") # Test printing of a TestSetException tse_str = sprint(show, Test.TestSetException(1,2,3,4,Vector{Union{Base.Test.Error, Base.Test.Fail}}())) From d4e2b5754589b9ccca3ab06f0ad8172bc071f3ba Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Fri, 9 Jun 2017 09:48:37 -0500 Subject: [PATCH 4/4] Add test for comparison calls --- test/test.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test.jl b/test/test.jl index a916906be340d..6b63c30b9ccb0 100644 --- a/test/test.jl +++ b/test/test.jl @@ -70,6 +70,8 @@ fails = @testset NoThrowTestSet begin @test 1/1 ≈ 2/1 # Fail - chained comparison @test 1+0 == 2+0 == 3+0 + # Fail - comparison call + @test ==(1 - 2, 2 - 1) # Error - unexpected pass @test_broken true end @@ -98,6 +100,10 @@ str = sprint(show, fails[5]) @test contains(str, "Evaluated: 1 == 2 == 3") str = sprint(show, fails[6]) +@test contains(str, "Expression: 1 - 2 == 2 - 1") +@test contains(str, "Evaluated: -1 == 1") + +str = sprint(show, fails[7]) @test contains(str, "Unexpected Pass") @test contains(str, "Expression: true")