using SoftGlobalScope
if VERSION < v"0.7.0-DEV.2005"
    using Base.Test
else
    using Test
end

# filter line numbers out of expressions, to make them easier to compare
nl(x) = x
nl(a::AbstractVector) = map(nl, filter(x -> !isa(x, LineNumberNode), a))
nl(ex::Expr) = Expr(ex.head, nl(ex.args)...)
macro nl_str(expr)
    :(nl(Meta.parse($expr)))
end

# test module that defines some globals
module TestMod
    a = 0
    b = 1
    c = 2
end

@testset "softscope" begin
    if VERSION < v"0.7.0-DEV.2308"
        @test softscope(TestMod, nl"for i=r; a += 1; end") == nl"for i=r; a += 1; end"
    elseif VERSION < v"1.5.0-DEV.263"
        @test softscope(TestMod, nl"for i=r; a += 1; end") == nl"for i=r; global a += 1; end"
        @test softscope(TestMod, nl"while a < 3; a += 1; end") == nl"while a < 3; global a += 1; end"
        @test softscope(TestMod, nl"begin; a += 1; end") == nl"begin; a += 1; end"
        @test softscope(TestMod, nl"begin; for i=r; a += 1; end; end") == nl"begin; for i=r; global a += 1; end; end"
        @test softscope(TestMod, nl"let; a += 1; end") == nl"let; global a += 1; end"
        @test softscope(TestMod, nl"let; local a; a += 1; end") == nl"let; local a; a += 1; end"
        @test softscope(TestMod, nl"let; local a=0; a += 1; end") == nl"let; local a=0; a += 1; end"
        @test softscope(TestMod, nl"let; local a::Int=0; a += 1; end") == nl"let; local a::Int=0; a += 1; end"
        @test softscope(TestMod, nl"let; begin; local a::Int=0; end; a += 1; end") == nl"let; begin; local a::Int=0; end; a += 1; end"
        @test softscope(TestMod, nl"let b=2; a+=1; b=3; end") == nl"let b=2; global a+=1; b=3; end"
        @test softscope(TestMod, nl"let b=2, (c,d)=e; a+=1; b=3; c=4; end") == nl"let b=2, (c,d)=e; global a+=1; b=3; c=4; end"
        @test softscope(TestMod, nl"let b=2, (c′,d)=e; a+=1; b=3; c=4; end") == nl"let b=2, (c′,d)=e; global a+=1; b=3; global c=4; end"
        @test softscope(TestMod, nl"let b::Int=2; a+=1; b=3; end") == nl"let b::Int=2; global a+=1; b=3; end"
        @test softscope(TestMod, nl"try; a=1; catch; b=2; finally; end") == nl"try; global a=1; catch; global b=2; finally; end"
        @test softscope(TestMod, nl"try; a=1; catch b; b=2; finally; end") == nl"try; global a=1; catch b; b=2; finally; end"
        @test softscope(TestMod, nl"try; x=1; catch; end") == nl"try; x=1; catch; end"
        @test softscope(TestMod, nl"try; x=1; finally; end") == nl"try; x=1; finally; end"
        @test softscope(TestMod, nl"begin; aa=0; for i=1:10; aa+=i; end; end") == nl"begin; aa=0; for i=1:10; global aa+=i; end; end"
        @test softscope(TestMod, nl"begin; (aa, bb)=(0, 1); for i=1:10; aa+=i; bb+=1; end; end") == nl"begin; (aa, bb)=(0, 1); for i=1:10; global aa+=i; global bb+=1; end; end"
        @test softscope(TestMod, nl"begin; if true; aa=0; end; for i=1:10; aa+=i; end") == nl"begin; if true; aa=0; end; for i=1:10; global aa+=i; end"
        @test softscope(TestMod, nl"begin; for i=1:10; a+=1; end; if a==0; aa=2; else aa=3; end; while aa > 0; aa -= 1; end; end") == nl"begin; for i=1:10; global a+=1; end; if a==0; aa=2; else aa=3; end; while aa > 0; global aa -= 1; end; end"
        @test softscope(TestMod, nl"for i = 1:10; let a = i ; println(a) ; end ; end") == nl"for i = 1:10; let a = i ; println(a) ; end ; end"
        @test softscope(TestMod, nl"begin; local x; x = 0; for i = 1:10; x += i ; end ; end") == nl"begin; local x; x = 0; for i = 1:10; x += i ; end ; end"
        @test softscope(TestMod, nl"let x=1; x=0; for i = 1:10; x += i; end; end") == nl"let x=1; x=0; for i = 1:10; x += i; end; end"
        @test softscope(TestMod, nl"for i = 1:10; for j = 1:3; global x += 1; end; x = 3; end") == nl"for i = 1:10; for j = 1:3; global x += 1; end; x = 3; end"
        @test softscope(TestMod, nl"let; global aa = 0; for i = 1:10; aa+=i; end; end") == nl"let; global aa = 0; for i = 1:10; global aa+=i; end; end"
        @test softscope(TestMod, nl"for i = (a = 1; 1:10); end") == nl"for i = (a = 1; 1:10); end"
        @test softscope(TestMod, nl"while (a += 1) < 10 ; end") == nl"while (a += 1) < 10 ; end"
        @test softscope(TestMod, nl"let a = (a = 2); end") == nl"let a = (global a = 2); end"
        @test softscope(TestMod, nl"let a = (a = 1), b = (b = 2), c = (aa = 3); end") == nl"let a = (global a = 1), b = (global b = 2), c = (aa = 3); end"
        @test softscope(TestMod, nl"let a = (b = 1), b = (a = 2), c = (aa = 3); end") == nl"let a = (global b = 1), b = (a = 2), c = (aa = 3); end"
        @test softscope(TestMod, nl"for i = (a = 1; 1:10); for j = (a = 2; 1:10); end; end") == nl"for i = (a = 1; 1:10); for j = (global a = 2; 1:10); end; end"
        @test softscope(TestMod, nl"while (a += 1) < 10; while (b += 1) < 10; end; end") == nl"while (a += 1) < 10; while (global b += 1) < 10; end; end"
        @test softscope(TestMod, nl"let; f(a) = (a = 1); end") == nl"let; f(a) = (a = 1); end"
        @test softscope(TestMod, nl"sqrt((for i = 1:10; a+=1; end; a))") == nl"sqrt((for i = 1:10; global a+=1; end; a))"
        @test softscope(TestMod, nl"let a = (local b = 2; a = 1), b = (b = 3); end") == nl"let a = (local b = 2; global a = 1), b = (b = 3); end"
        @test softscope(TestMod, nl"f(a=(for i = 1:10; a+=1; end; a))") == nl"f(a=(for i = 1:10; global a+=1; end; a))"
        @test softscope(TestMod, nl"let a; a = 1; end") == nl"let a; a = 1; end"
        @test softscope(TestMod, nl"let a, b; a = 1; end") == nl"let a, b; a = 1; end"
        @test softscope(TestMod, nl"for i=r; true && (a += 1); end") == nl"for i=r; true && (global a += 1); end"
        @test softscope(TestMod, nl"for i=r; true || (a += 1); end") == nl"for i=r; true || (global a += 1); end"
        @test softscope(TestMod, nl"for i=r; 0 < (a += 1) < 10; end") == nl"for i=r; 0 < (global a += 1) < 10; end"
        @test softscope(TestMod, nl"for i = 1:10; x,y = (i+1, i+2); end") == nl"for i = 1:10; x,y = (i+1, i+2); end"
        @test softscope(TestMod, nl"for i = 1:10; a,x = (i+a, i+1); end") == nl"for i = 1:10; begin; global a; a,x = (i+a, i+1); end; end"
        @test softscope(TestMod, nl"for i = 1:10; a,b,x = (i+a, a+b, i+1); end") == nl"for i = 1:10; begin; global a, b; a,b,x = (i+a, a+b, i+1); end; end"
        @test softscope(TestMod, nl"j=0; for i = 1:10; (a[j+=1],x) = (i, i+1); end") == nl"j=0; for i = 1:10; (a[global j+=1],x) = (i, i+1); end"
        @test softscope(TestMod, nl"myfunc(a) = (b = 0; for i = 1:10; b += i; a += b; end; a)") == nl"myfunc(a) = (b = 0; for i = 1:10; b += i; a += b; end; a)"
        @test softscope(TestMod, nl"let; x = (a=1, aa=2); end") == nl"let; x = (a=1, aa=2); end"
        @test softscope(TestMod, nl"for i = 1:10; if true; a = 3; elseif false; a = 4; else; a = 7; end; end") == nl"for i = 1:10; if true; global a = 3; elseif false; global a = 4; else; global a = 7; end; end"
    end
end

# make == comparison of LoadErrors work in 0.6
if VERSION < v"0.7"
    Base.:(==)(a::LoadError, b::LoadError) = a.file == b.file && a.line == b.line && a.error == b.error
    Base.:(==)(a::ErrorException, b::ErrorException) = a.msg == b.msg
end

@testset "softscope_include_string" begin
    @test softscope_include_string(TestMod, "for i=1:10; a += 1; end; a") == 10
    @test softscope_include_string(TestMod, "aa=0; for i=1:10; aa += i; end; aa") == 55
    @test softscope_include_string(TestMod, "aa2=0\nfor i=1:10; aa2 += i; end\naa2") == 55
    @test softscope_include_string(TestMod, "module AModule; const amod=123; end") isa Module
    @test TestMod.AModule.amod == 123
    for (code,line) in (("1\n2\n1+",3), ("1;2;1+",1))
        try
            softscope_include_string(TestMod, code, "bar")
        catch e
            @test e == LoadError("bar", line, ErrorException("syntax: incomplete: premature end of input"))
        end
    end
    try
        softscope_include_string(TestMod, "1\n2\n1++++2", "bar")
    catch e
        @test e == LoadError("bar", 3, ErrorException("syntax: \"++\" is not a unary operator"))
    end
    softscope_include_string(TestMod, """"blah blah"\ntestdoc = 1""")
    @test TestMod.testdoc == 1
    @test Docs.docstr(Docs.Binding(TestMod, :testdoc)).text[1] == "blah blah"
    @test softscope_include_string(Main, "1\n   ") === softscope_include_string(Main, "1\n#b") === 1
end

@testset "softscope_macro" begin
    Core.eval(TestMod, :(global a = 0 ; using SoftGlobalScope))
    @test Core.eval(TestMod, :(@softscope (for i=1:10; a += 1; end; a))) == 10
    @test Core.eval(TestMod, :(@softscope (amacro=0; for i=1:10; amacro += i; end; amacro))) == 55
end
