# This file is a part of Julia. License is MIT: https://julialang.org/license using Test # Tests for @__LINE__ inside and outside of macros @test (@__LINE__) == 6 macro macro_caller_lineno() @test 9 == (@__LINE__) != __source__.line > 12 return __source__.line end @test @macro_caller_lineno() == (@__LINE__) > 12 # @__LINE__ in a macro expands to the location of the macrocall in the source # while __source__.line is the location of the macro caller macro nested_LINE_expansion() return quote return (@emit_LINE, $(__source__.line)) end end macro nested_LINE_expansion2() return :((@emit_LINE, $(__source__.line))) end macro emit_LINE() return quote (@__LINE__, $(__source__.line)) end end @test (@emit_LINE) == ((@__LINE__) - 3, @__LINE__) @test @nested_LINE_expansion() == ((@__LINE__() - 4, @__LINE__() - 12), @__LINE__()) @test @nested_LINE_expansion2() == ((@__LINE__() - 5, @__LINE__() - 9), @__LINE__()) loaded_files = String[] push!(Base.include_callbacks, (mod::Module, fn::String) -> push!(loaded_files, fn)) include("test_sourcepath.jl") @test length(loaded_files) == 1 && endswith(loaded_files[1], "test_sourcepath.jl") pop!(Base.include_callbacks) thefname = "the fname!//\\&\1*" include_string_test_func = include_string(@__MODULE__, "include_string_test() = @__FILE__", thefname) @test include_string_test_func() == thefname @test include_string(@__MODULE__, "Base.source_path()", thefname) == Base.source_path() @test basename(@__FILE__) == "loading.jl" @test isabspath(@__FILE__) @test isdir(@__DIR__) @test @__DIR__() == dirname(@__FILE__) let exename = `$(Base.julia_cmd()) --compiled-modules=yes --startup-file=no`, wd = sprint(show, abspath(pwd(), "")), s_dir = sprint(show, joinpath(realpath(tempdir()), "")) @test wd != s_dir @test readchomp(`$exename -E "@__DIR__" -i`) == wd @test readchomp(`$exename -E "cd(()->eval(:(@__DIR__)), $s_dir)" -i`) == s_dir @test readchomp(`$exename -E "@__DIR__"`) == wd # non-interactive end # Issue #5789 and PR #13542: mktempdir() do dir cd(dir) do let true_filename = "cAsEtEsT.jl", lowered_filename="casetest.jl" touch(true_filename) @test Base.isfile_casesensitive(true_filename) @test !Base.isfile_casesensitive(lowered_filename) # check that case-sensitivity only applies to basename of a path: if isfile(lowered_filename) # case-insensitive filesystem mkdir("cAsEtEsT") touch(joinpath("cAsEtEsT", true_filename)) @test Base.isfile_casesensitive(joinpath("casetest", true_filename)) @test !Base.isfile_casesensitive(joinpath("casetest", lowered_filename)) end end # Test Unicode normalization; pertinent for OS X let nfc_name = "\U00F4.jl" touch(nfc_name) @test Base.isfile_casesensitive(nfc_name) end end end ## unit tests of project parsing ## import Base: SHA1, PkgId, load_path, identify_package, locate_package, version_slug, dummy_uuid import UUIDs: UUID, uuid4, uuid_version import Random: shuffle, randstring using Test let shastr = "ab"^20 hash = SHA1(shastr) @test hash == eval(Meta.parse(repr(hash))) # check show method @test string(hash) == shastr @test "check $hash" == "check $shastr" end let uuidstr = "ab"^4 * "-" * "ab"^2 * "-" * "ab"^2 * "-" * "ab"^2 * "-" * "ab"^6 uuid = UUID(uuidstr) @test uuid == eval(Meta.parse(repr(uuid))) # check show method @test string(uuid) == uuidstr == sprint(print, uuid) @test "check $uuid" == "check $uuidstr" end function subset(v::Vector{T}, m::Int) where T T[v[j] for j = 1:length(v) if ((m >>> (j - 1)) & 1) == 1] end function perm(p::Vector, i::Int) for j = length(p):-1:1 i, k = divrem(i, j) p[j], p[k+1] = p[k+1], p[j] end return p end @testset "explicit_project_deps_get" begin mktempdir() do dir project_file = joinpath(dir, "Project.toml") touch(project_file) # dummy_uuid calls realpath # various UUIDs to work with proj_uuid = dummy_uuid(project_file) root_uuid = uuid4() this_uuid = uuid4() # project file to subset/permute lines = split(""" name = "Root" uuid = "$root_uuid" [deps] This = "$this_uuid" """, '\n') N = length(lines) # test every permutation of every subset of lines for m = 0:2^N-1 s = subset(lines, m) # each subset of lines for i = 1:factorial(count_ones(m)) p = perm(s, i) # each permutation of the subset open(project_file, write=true) do io for line in p println(io, line) end end # look at lines and their order n = findfirst(line -> startswith(line, "name"), p) u = findfirst(line -> startswith(line, "uuid"), p) d = findfirst(line -> line == "[deps]", p) t = findfirst(line -> startswith(line, "This"), p) # look up various packages by name root = Base.explicit_project_deps_get(project_file, "Root") this = Base.explicit_project_deps_get(project_file, "This") that = Base.explicit_project_deps_get(project_file, "That") # test that the correct answers are given @test root == (something(n, N+1) ≥ something(d, N+1) ? false : something(u, N+1) < something(d, N+1) ? root_uuid : proj_uuid) @test this == (something(d, N+1) < something(t, N+1) ≤ N ? this_uuid : false) @test that == false end end end end ## functional testing of package identification, location & loading ## saved_load_path = copy(LOAD_PATH) saved_depot_path = copy(DEPOT_PATH) saved_home_project = Base.HOME_PROJECT[] saved_active_project = Base.ACTIVE_PROJECT[] push!(empty!(LOAD_PATH), "project") push!(empty!(DEPOT_PATH), "depot") Base.HOME_PROJECT[] = nothing Base.ACTIVE_PROJECT[] = nothing @test load_path() == [abspath("project","Project.toml")] @testset "project & manifest identify_package & locate_package" begin local path for (names, uuid, path) in [ ("Foo", "767738be-2f1f-45a9-b806-0234f3164144", "project/deps/Foo1/src/Foo.jl" ), ("Bar.Foo", "6f418443-bd2e-4783-b551-cdbac608adf2", "project/deps/Foo2.jl/src/Foo.jl" ), ("Bar", "2a550a13-6bab-4a91-a4ee-dff34d6b99d0", "project/deps/Bar/src/Bar.jl" ), ("Foo.Baz", "6801f525-dc68-44e8-a4e8-cabd286279e7", "depot/packages/Baz/81oLe/src/Baz.jl"), ("Foo.Qux", "b5ec9b9c-e354-47fd-b367-a348bdc8f909", "project/deps/Qux.jl" ), ] n = map(String, split(names, '.')) pkg = identify_package(n...) @test pkg == PkgId(UUID(uuid), n[end]) @test joinpath(@__DIR__, normpath(path)) == locate_package(pkg) end @test identify_package("Baz") == nothing @test identify_package("Qux") == nothing @testset "equivalent package names" begin local classes = [ ["Foo"], ["Bar", "Foo.Bar"], ["Foo.Baz", "Bar.Baz", "Foo.Bar.Baz"], ["Bar.Foo", "Foo.Bar.Foo", "Foo.Baz.Foo", "Bar.Baz.Foo"], ["Foo.Qux", "Foo.Baz.Qux", "Bar.Baz.Qux", "Foo.Bar.Foo.Qux", "Bar.Foo.Qux", "Foo.Baz.Foo.Qux", "Bar.Baz.Foo.Qux", "Foo.Bar.Baz.Foo.Qux"], ["Baz", "Qux", "Bar.Qux", "Bar.Baz.Bar", "Bar.Foo.Bar", "Bar.Foo.Baz", "Bar.Foo.Qux.Foo", "Bar.Foo.Qux.Bar", "Bar.Foo.Qux.Baz"], ] for i = 1:length(classes) A = classes[i] for x in A X = identify_package(map(String, split(x, '.'))...) for y in A Y = identify_package(map(String, split(y, '.'))...) @test X == Y end for j = i+1:length(classes) B = classes[j] for z in B Z = identify_package(map(String, split(z, '.'))...) @test X != Z end end end end end end module NotPkgModule; end @testset "project & manifest import" begin @test !@isdefined Foo @test !@isdefined Bar import Foo @test @isdefined Foo @test !@isdefined Bar import Bar @test @isdefined Foo @test @isdefined Bar @testset "module graph structure" begin local classes = Dict( "Foo1" => [Foo], "Bar" => [Bar, Foo.Bar], "Baz" => [Foo.Baz, Bar.Baz, Foo.Bar.Baz], "Foo2" => [Bar.Foo, Foo.Bar.Foo, Foo.Baz.Foo, Bar.Baz.Foo], "Qux" => [Foo.Qux, Foo.Baz.Qux, Bar.Baz.Qux, Foo.Bar.Foo.Qux, Bar.Foo.Qux, Foo.Baz.Foo.Qux, Bar.Baz.Foo.Qux, Foo.Bar.Baz.Foo.Qux], ) for (i, (this, mods)) in enumerate(classes) for x in mods @test x.this == this for y in mods @test x === y end for (j, (that, mods′)) in enumerate(classes) i == j && continue for z in mods′ @test x !== z end end end end end @test Foo.which == "path" @testset "pathof" begin @test pathof(Foo) == normpath(abspath(@__DIR__, "project/deps/Foo1/src/Foo.jl")) @test pathof(NotPkgModule) === nothing end end ## systematic generation of test environments ## const M = 3 # number of node names const N = 12 # different UUIDs per name const NODES = 1:M*N const NAMES = map(string, ('A':'Z')[1:M]) const UUIDS = [uuid4() for i = 1:M, j = 1:N] const KIND = [(i + j) % 3 for i = 1:M, j = 1:N] const KIND0 = filter(i -> KIND[i] == 0, NODES) const KIND2 = filter(i -> KIND[i] == 2, NODES) # kind 0: no project file # kind 1: project file without name or uuid # kind 2: project file with name and uuid # explicit env: root can be anything, everything else kind 2 # implicit env: nodes can be anything, names must be unique # allowed dependencies between kinds (explicit and implicit): allowed(i::Int, j::Int) = KIND[i] ≤ KIND[j] && !(KIND[i] == KIND[j] == 1) # node names/labels L(i::Int) = NAMES[mod1(i, M)] # first generate random dependency graphs const graphs = Any[] while length(graphs) < 100 if (flat = rand(Bool)) root = rand(KIND0) pool = root ∪ filter(i -> L(i) ≠ L(root), NODES) size = rand(1:N) else root = rand(NODES) pool = filter(i -> i ≠ root, KIND2) size = rand(1:length(NODES)÷2) end graph = Dict(root => Int[]) KIND[root] ≠ 0 && push!(graph[root], root) for _ = 1:size i = rand(keys(graph)) J = filter(pool) do j allowed(i, j) && all(L(j) ≠ L(k) for k in graph[i]) end isempty(J) && continue j = rand(J) push!(graph[i], j) if j ∉ keys(graph) graph[j] = [j] flat && filter!(k -> L(k) ≠ L(j), pool) isempty(pool) && break end end roots = flat ? reduce(∪, values(graph)) : graph[root] for i in keys(graph) KIND[i] == 0 && delete!(graph, i) end t = (flat, root, Dict(L(i) => i for i in roots), Dict(i => Dict(L(j) => j for j in deps) for (i, deps) in graph)) t in graphs || push!(graphs, t) end # materialize dependency graphs as explicit environments function make_entry_point(path::String, name::String, uuid::UUID) mkpath(dirname(path)) open(path, "w") do io print(io, """ module $name name = $(repr(name)) uuid = $(repr(string(uuid))) end """) end end function make_env(flat, root, roots, graph, paths, dummies) pkg(i::Int) = PkgId(KIND[i] == 2 ? UUIDS[i] : get(dummies, i, nothing), L(i)) return (flat, pkg(root), Dict(n => pkg(i) for (n, i) in roots), Dict(pkg(i) => Dict(n => pkg(j) for (n, j) in d) for (i, d) in graph), Dict{PkgId,Union{Nothing,String}}(pkg(i) => path for (i, path) in paths), ) end const depots = [mktempdir() for _ = 1:3] const envs = Dict{String,Any}() append!(empty!(DEPOT_PATH), depots) for (flat, root, roots, graph) in graphs if flat all(KIND[i] == 2 for i in values(roots)) || continue all(KIND[i] == 2 for i in keys(graph)) || continue end dir = mktempdir() dummies = Dict{Int,UUID}() paths = Dict{Int,Union{Nothing,String}}() root_path = rand([false, true, joinpath("src", "$(randstring()).jl")]) # generate project file project_file = joinpath(dir, "Project.toml") open(project_file, "w") do io name, uuid, kind = L(root), UUIDS[root], KIND[root] kind != 0 && println(io, "name = ", repr(name)) kind == 2 && println(io, "uuid = ", repr(string(uuid))) kind == 1 && (dummies[root] = dummy_uuid(project_file)) root_path isa String && println(io, "path = ", repr(root_path)) println(io, "[deps]") for (n, i) in roots i == root && continue @assert KIND[i] == 2 println(io, "$n = ", repr(string(UUIDS[i]))) end kind == 0 && return # generate entry point if root_path == false kind == 2 && (paths[root] = nothing) else root_path == true && (root_path = joinpath("src", "$name.jl")) root_path = joinpath(dir, root_path) make_entry_point(root_path, name, uuid) paths[root] = root_path end end # count manifest entries counts = Dict(name => 0 for name in NAMES) for (i, _) in graph i == root && continue @assert KIND[i] == 2 counts[L(i)] += 1 end # generate manifest file open(joinpath(dir, "Manifest.toml"), "w") do io for (i, deps) in graph i == root && continue name, uuid = L(i), UUIDS[i] println(io, "[[$name]]") println(io, "uuid = ", repr(string(uuid))) if rand() < 2/3 sha1 = SHA1(rand(UInt8, 20)) println(io, "git-tree-sha1 = ", repr(string(sha1))) path = joinpath( rand(depots), "packages", name, version_slug(uuid, sha1), "src", "$name.jl", ) make_entry_point(path, name, uuid) paths[i] = path # may get overwritten below end if rand() < 1/4 path = joinpath("deps", "$(randstring()).jl") println(io, "path = ", repr(path)) path = joinpath(dir, path) make_entry_point(path, name, uuid) paths[i] = path # may overwrite path above end # neither can occur, i.e. no entry in paths deps = delete!(copy(deps), name) isempty(deps) && continue if all(counts[n] == 1 for n in keys(deps)) println(io, "deps = ", repr(keys(deps))) else println(io, " [$name.deps]") for (n, j) in deps @assert KIND[j] == 2 println(io, " $n = ", repr(string(UUIDS[j]))) end end end end envs[dir] = make_env(flat, root, roots, graph, paths, dummies) end # materialize dependency graphs as implicit environments (if possible) for (flat, root, roots, graph) in graphs flat || continue dir = mktempdir() dummies = Dict{Int,UUID}() paths = Dict{Int,Union{Nothing,String}}() for (name, i) in roots uuid, kind = UUIDS[i], KIND[i] # generate package entry point entry = joinpath(dir, name, "src", "$name.jl") make_entry_point(entry, name, uuid) paths[i] = entry kind == 0 && continue deps = delete!(copy(graph[i]), name) # generate project file project_file = joinpath(dir, name, "Project.toml") open(project_file, "w") do io kind != 0 && println(io, "name = ", repr(name)) kind == 2 && println(io, "uuid = ", repr(string(uuid))) kind != 2 && (dummies[i] = dummy_uuid(project_file)) isempty(deps) || println(io, "[deps]") for (n, j) in deps @assert KIND[j] == 2 println(io, " $n = ", repr(string(UUIDS[j]))) end end end envs[dir] = make_env(flat, root, roots, graph, paths, dummies) end ## use generated environments to test package loading ## function test_find( roots::Dict{String,PkgId}, graph::Dict{PkgId,Dict{String,PkgId}}, paths::Dict{PkgId,Union{Nothing,String}}, ) # check direct dependencies for name in NAMES id = identify_package(name) @test id == get(roots, name, nothing) path = locate_package(id) @test path == get(paths, id, nothing) end # check indirect dependencies for where in keys(graph) where.uuid === nothing && continue deps = get(graph, where, Dict(where.name => where)) for name in NAMES id = identify_package(where, name) @test id == get(deps, name, nothing) path = locate_package(id) @test path == get(paths, id, nothing) end end end @testset "find_package with one env in load path" begin for (env, (_, _, roots, graph, paths)) in envs push!(empty!(LOAD_PATH), env) test_find(roots, graph, paths) end end @testset "find_package with two envs in load path" begin for x = false:true, (env1, (_, _, roots1, graph1, paths1)) in (x ? envs : rand(envs, 10)), (env2, (_, _, roots2, graph2, paths2)) in (x ? rand(envs, 10) : envs) push!(empty!(LOAD_PATH), env1, env2) roots = merge(roots2, roots1) graph = merge(graph2, graph1) paths = merge(paths2, paths1) test_find(roots, graph, paths) end end @testset "find_package with three envs in load path" begin for (env1, (_, _, roots1, graph1, paths1)) in rand(envs, 10), (env2, (_, _, roots2, graph2, paths2)) in rand(envs, 10), (env3, (_, _, roots3, graph3, paths3)) in rand(envs, 10) push!(empty!(LOAD_PATH), env1, env2, env3) roots = merge(roots3, roots2, roots1) graph = merge(graph3, graph2, graph1) paths = merge(paths3, paths2, paths1) test_find(roots, graph, paths) end end # normalization of paths by include (#26424) @test_throws ErrorException("could not open file $(joinpath(@__DIR__, "notarealfile.jl"))") include("./notarealfile.jl") old_act_proj = Base.ACTIVE_PROJECT[] pushfirst!(LOAD_PATH, "@") try Base.ACTIVE_PROJECT[] = joinpath(@__DIR__, "TestPkg") @eval using TestPkg finally Base.ACTIVE_PROJECT[] = old_act_proj popfirst!(LOAD_PATH) end @testset "--project and JULIA_PROJECT paths should be absolutified" begin mktempdir() do dir; cd(dir) do mkdir("foo") script = """ using Test old = Base.active_project() cd("foo") @test Base.active_project() == old """ @test success(`$(Base.julia_cmd()) --project=foo -e $(script)`) withenv("JULIA_PROJECT" => "foo") do @test success(`$(Base.julia_cmd()) -e $(script)`) end end; end end ## cleanup after tests ## for env in keys(envs) rm(env, force=true, recursive=true) end for depot in depots rm(depot, force=true, recursive=true) end append!(empty!(LOAD_PATH), saved_load_path) append!(empty!(DEPOT_PATH), saved_depot_path) Base.HOME_PROJECT[] = saved_home_project Base.ACTIVE_PROJECT[] = saved_active_project # issue #28190 module Foo; import Libdl; end import .Foo.Libdl; import Libdl @test Foo.Libdl === Libdl