# This file is a part of Julia. License is MIT: https://julialang.org/license original_depot_path = copy(Base.DEPOT_PATH) using Test # Tests for @__LINE__ inside and outside of macros @test (@__LINE__) == 8 macro macro_caller_lineno() @test 11 == (@__LINE__) != __source__.line > 14 return __source__.line end @test @macro_caller_lineno() == (@__LINE__) > 14 # @__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 isdir(Base.source_dir()) @test basename(@__FILE__) == "loading.jl" @test isabspath(@__FILE__) @test isdir(@__DIR__) @test @__DIR__() == dirname(@__FILE__) @test !endswith(@__DIR__, Base.Filesystem.path_separator) let exename = `$(Base.julia_cmd()) --compiled-modules=yes --startup-file=no --color=no`, wd = sprint(show, pwd()) s_dir = sprint(show, 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 @test !endswith(wd, Base.Filesystem.path_separator) @test !endswith(s_dir, Base.Filesystem.path_separator) end @test Base.in_sysimage(Base.PkgId(Base.UUID("cf7118a7-6976-5b1a-9a39-7adc72f591a4"), "UUIDs")) @test Base.in_sysimage(Base.PkgId(Base.UUID("3a7fdc7e-7467-41b4-9f64-ea033d046d5b"), "NotAPackage")) == false ## Unit tests for safe file operations ## @test Base.isaccessiblefile("/root/path/doesn't/exist") == false @test Base.isaccessiblepath("/root/path/doesn't/exist") == false @test Base.isaccessibledir("/root/path/doesn't/exist") == false # 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 shastr1 = "ab"^20, shastr2 = "ac"^20 hash1 = SHA1(shastr1) hash2 = SHA1(shastr2) @test isless(hash1, hash2) @test !isless(hash2, hash1) @test !isless(hash1, hash1) end # Test bad SHA1 values @test_throws ArgumentError SHA1("this is not a valid SHA1") @test_throws ArgumentError parse(SHA1, "either is this") @test tryparse(SHA1, "nor this") === nothing 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" @test UUID(UInt128(uuid)) == uuid @test UUID(uuid) === uuid @test UUID(convert(NTuple{2, UInt64}, uuid)) == uuid @test UUID(convert(NTuple{4, UInt32}, uuid)) == uuid uuidstr2 = "ba"^4 * "-" * "ba"^2 * "-" * "ba"^2 * "-" * "ba"^2 * "-" * "ba"^6 uuid2 = UUID(uuidstr2) uuids = [uuid, uuid2] @test (uuids .== uuid) == [true, false] @test parse(UUID, uuidstr2) == uuid2 end @test_throws ArgumentError UUID("@"^4 * "-" * "@"^2 * "-" * "@"^2 * "-" * "@"^2 * "-" * "@"^6) @test_throws ArgumentError parse(UUID, "not a UUID") @test tryparse(UUID, "either is this") === nothing @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() old_load_path = copy(LOAD_PATH) try copy!(LOAD_PATH, [project_file]) write(project_file, """ name = "Root" uuid = "$root_uuid" [deps] This = "$this_uuid" """) # look up various packages by name root = Base.identify_package("Root") this = Base.identify_package("This") that = Base.identify_package("That") @test root.uuid == root_uuid @test this.uuid == this_uuid @test that == nothing write(project_file, """ name = "Root" This = "$this_uuid" [deps] """) # look up various packages by name root = Base.identify_package("Root") this = Base.identify_package("This") that = Base.identify_package("That") @test root.uuid == proj_uuid @test this == nothing @test that == nothing finally copy!(LOAD_PATH, old_load_path) end end end # extras @testset "extras" 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() old_load_path = copy(LOAD_PATH) try copy!(LOAD_PATH, [project_file]) write(project_file, """ name = "Root" uuid = "$root_uuid" [extras] This = "$this_uuid" """) # look up various packages by name root = Base.identify_package("Root") this = Base.identify_package("This") that = Base.identify_package("That") @test root.uuid == root_uuid @test this == nothing @test that == nothing @test Base.get_uuid_name(project_file, this_uuid) == "This" finally copy!(LOAD_PATH, old_load_path) end end end ## functional testing of package identification, location & loading ## saved_load_path = copy(LOAD_PATH) saved_depot_path = copy(DEPOT_PATH) saved_active_project = Base.ACTIVE_PROJECT[] watcher_counter = Ref(0) push!(Base.active_project_callbacks, () -> watcher_counter[] += 1) push!(Base.active_project_callbacks, () -> error("broken")) push!(empty!(LOAD_PATH), joinpath(@__DIR__, "project")) append!(empty!(DEPOT_PATH), [mktempdir(), joinpath(@__DIR__, "depot")]) @test watcher_counter[] == 0 @test_logs (:error, r"active project callback .* failed") Base.set_active_project(nothing) @test watcher_counter[] == 1 pop!(Base.active_project_callbacks) @test load_path() == [joinpath(@__DIR__, "project", "Project.toml")] # locate `tail(names)` package by following the search path graph through `names` starting from `where` function recurse_package(where::PkgId, name::String, names::String...) pkg = identify_package(where, name) pkg === nothing && return nothing return recurse_package(pkg, names...) end recurse_package(pkg::String) = identify_package(pkg) recurse_package(where::PkgId, pkg::String) = identify_package(where, pkg) function recurse_package(name::String, names::String...) pkg = identify_package(name) pkg === nothing && return nothing return recurse_package(pkg, names...) end @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 = recurse_package(n...) @test pkg == PkgId(UUID(uuid), n[end]) @test joinpath(@__DIR__, normpath(path)) == locate_package(pkg) @test Base.compilecache_path(pkg, UInt64(0)) == Base.compilecache_path(pkg, UInt64(0)) end @test identify_package("Baz") == nothing @test identify_package("Qux") == nothing @testset "equivalent package names" begin 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 = recurse_package(map(String, split(x, '.'))...) for y in A Y = recurse_package(map(String, split(y, '.'))...) @test X == Y end for j = i+1:length(classes) B = classes[j] for z in B Z = recurse_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 @testset "pkgdir" begin @test pkgdir(Foo) == normpath(abspath(@__DIR__, "project/deps/Foo1")) @test pkgdir(Foo.SubFoo1) == normpath(abspath(@__DIR__, "project/deps/Foo1")) @test pkgdir(Foo.SubFoo2) == normpath(abspath(@__DIR__, "project/deps/Foo1")) @test pkgdir(NotPkgModule) === nothing @test pkgdir(Foo, "src") == normpath(abspath(@__DIR__, "project/deps/Foo1/src")) @test pkgdir(Foo.SubFoo1, "src") == normpath(abspath(@__DIR__, "project/deps/Foo1/src")) @test pkgdir(Foo.SubFoo2, "src") == normpath(abspath(@__DIR__, "project/deps/Foo1/src")) @test pkgdir(NotPkgModule, "src") === nothing end @testset "pkgversion" begin @test pkgversion(Foo) == v"1.2.3" @test pkgversion(Foo.SubFoo1) == v"1.2.3" @test pkgversion(Foo.SubFoo2) == v"1.2.3" @test pkgversion(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) @testset "load code uniqueness" begin @test allunique(UUIDS) @test allunique(depots) @test allunique(DEPOT_PATH) end 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 = id === nothing ? nothing : 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 = id === nothing ? nothing : 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 begin exc = try; include("./notarealfile.jl"); "unexpectedly reached!"; catch exc; exc; end @test exc isa SystemError exc.prefix end == "opening file $(repr(joinpath(@__DIR__, "notarealfile.jl")))" old_act_proj = Base.ACTIVE_PROJECT[] pushfirst!(LOAD_PATH, "@") try Base.set_active_project(joinpath(@__DIR__, "TestPkg")) @eval using TestPkg finally Base.set_active_project(old_act_proj) popfirst!(LOAD_PATH) end @test pkgversion(TestPkg) == v"1.2.3" @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 """ cmd = `$(Base.julia_cmd()) --startup-file=no -e $(script)` cmd = addenv(cmd, "JULIA_PROJECT" => "foo") cmd = pipeline(cmd; stdout, stderr) @test success(cmd) end; end end # Base.active_project when version directory exist in depot, but contains no project file mktempdir() do dir vdir = Base.DEFAULT_LOAD_PATH[2] vdir = replace(vdir, "#" => VERSION.major, count = 1) vdir = replace(vdir, "#" => VERSION.minor, count = 1) vdir = replace(vdir, "#" => VERSION.patch, count = 1) vdir = vdir[2:end] # remove @ vpath = joinpath(dir, "environments", vdir) mkpath(vpath) script = "@assert startswith(Base.active_project(), $(repr(vpath)))" cmd = `$(Base.julia_cmd()) --startup-file=no -e $(script)` cmd = addenv(cmd, "JULIA_DEPOT_PATH" => dir, "JULIA_LOAD_PATH" => Sys.iswindows() ? ";" : ":") cmd = pipeline(cmd; stdout, stderr) @test success(cmd) end @testset "expansion of JULIA_LOAD_PATH" begin s = Sys.iswindows() ? ';' : ':' tmp = "/this/does/not/exist" cases = Dict{Any,Vector{String}}( nothing => Base.DEFAULT_LOAD_PATH, "" => [], "$s" => Base.DEFAULT_LOAD_PATH, "$tmp$s" => [tmp; Base.DEFAULT_LOAD_PATH], "$s$tmp" => [Base.DEFAULT_LOAD_PATH; tmp], ) for (env, result) in pairs(cases) script = "LOAD_PATH == $(repr(result)) || error()" cmd = `$(Base.julia_cmd()) --startup-file=no -e $script` cmd = addenv(cmd, "JULIA_LOAD_PATH" => env) cmd = pipeline(cmd; stdout, stderr) @test success(cmd) end end @testset "expansion of JULIA_DEPOT_PATH" begin s = Sys.iswindows() ? ';' : ':' tmp = "/this/does/not/exist" DEFAULT = Base.append_default_depot_path!(String[]) cases = Dict{Any,Vector{String}}( nothing => DEFAULT, "" => [], "$s" => DEFAULT, "$tmp$s" => [tmp; DEFAULT], "$s$tmp" => [DEFAULT; tmp], ) for (env, result) in pairs(cases) script = "DEPOT_PATH == $(repr(result)) || error()" cmd = `$(Base.julia_cmd()) --startup-file=no -e $script` cmd = addenv(cmd, "JULIA_DEPOT_PATH" => env) cmd = pipeline(cmd; stdout, stderr) @test success(cmd) end end @testset "Issue #25719" begin empty!(LOAD_PATH) @test Base.root_module(Core, :Core) == Core push!(LOAD_PATH, "@stdlib") @test Base.root_module(Base, :Test) == Test @test_throws KeyError(:SomeNonExistentPackage) Base.root_module(Base, :SomeNonExistentPackage) end ## cleanup after tests ## for env in keys(envs) rm(env, force=true, recursive=true) end for depot in depots try rm(depot, force=true, recursive=true) catch err @show err end end append!(empty!(LOAD_PATH), saved_load_path) append!(empty!(DEPOT_PATH), saved_depot_path) pop!(Base.active_project_callbacks) Base.set_active_project(saved_active_project) @test watcher_counter[] == 3 # issue #28190 module Foo28190; import Libdl; end import .Foo28190.Libdl; import Libdl @test Foo28190.Libdl === Libdl @testset "include with mapexpr" begin let exprs = Any[] @test 13 === include_string(@__MODULE__, "1+1\n3*4") do ex ex isa LineNumberNode || push!(exprs, ex) Meta.isexpr(ex, :call) ? :(1 + $ex) : ex end @test exprs == [:(1 + 1), :(3 * 4)] end # test using test_exec.jl, just because that is the shortest handy file for incl in (include, (mapexpr,path) -> Base.include(mapexpr, @__MODULE__, path)) let exprs = Any[] incl("test_exec.jl") do ex ex isa LineNumberNode || push!(exprs, ex) Meta.isexpr(ex, :macrocall) ? :nothing : ex end @test length(exprs) == 2 && exprs[1] == :(using Test) @test Meta.isexpr(exprs[2], :macrocall) && exprs[2].args[[1,3]] == [Symbol("@test"), :(1 == 2)] end end end @testset "`Base.project_names` and friends" begin # Some functions in Pkg assumes that these tuples have the same length n = length(Base.project_names) @test length(Base.manifest_names) == n @test length(Base.preferences_names) == n end @testset "Manifest formats" begin deps = Dict{String,Any}( "Serialization" => Any[Dict{String, Any}("uuid"=>"9e88b42a-f829-5b0c-bbe9-9e923198166b")], "Random" => Any[Dict{String, Any}("deps"=>["Serialization"], "uuid"=>"9a3f8284-a2c9-5f02-9a11-845980a1fd5c")], "Logging" => Any[Dict{String, Any}("uuid"=>"56ddb016-857b-54e1-b83d-db4d58db5568")] ) @testset "v1.0" begin env_dir = joinpath(@__DIR__, "manifest", "v1.0") manifest_file = joinpath(env_dir, "Manifest.toml") isfile(manifest_file) || error("Reference manifest is missing") raw_manifest = Base.parsed_toml(manifest_file) @test Base.is_v1_format_manifest(raw_manifest) @test Base.get_deps(raw_manifest) == deps end @testset "v2.0" begin env_dir = joinpath(@__DIR__, "manifest", "v2.0") manifest_file = joinpath(env_dir, "Manifest.toml") isfile(manifest_file) || error("Reference manifest is missing") raw_manifest = Base.parsed_toml(manifest_file) @test Base.is_v1_format_manifest(raw_manifest) == false @test Base.get_deps(raw_manifest) == deps end end @testset "error message loading pkg bad module name" begin mktempdir() do tmp old_loadpath = copy(LOAD_PATH) try push!(LOAD_PATH, tmp) write(joinpath(tmp, "BadCase.jl"), "module badcase end") @test_logs (:warn, r"The call to compilecache failed.*") match_mode=:any begin @test_throws ErrorException("package `BadCase` did not define the expected module `BadCase`, \ check for typos in package module name") (@eval using BadCase) end finally copy!(LOAD_PATH, old_loadpath) end end end @testset "Preferences loading" begin mktempdir() do dir this_uuid = uuid4() that_uuid = uuid4() # First, create outer environment with exported preferences mkpath(joinpath(dir, "outer_env")) open(joinpath(dir, "outer_env", "Project.toml"), write=true) do io write(io, """ [deps] This = "$(this_uuid)" That = "$(that_uuid)" [preferences.This] pref1 = "outer-project" pref2 = "outer-project" pref3 = "outer-project" pref4 = "outer-project" pref5 = "outer-project" pref6 = "outer-project" [preferences.That] pref1 = "outer-project" """) end # Override some of those preferences above here: open(joinpath(dir, "outer_env", "JuliaLocalPreferences.toml"), write=true) do io write(io, """ [This] pref2 = "outer-jlp" """) end # Ensure that a `JuliaLocalPreferences.toml` disables `LocalPreferences.toml` # We test that both overriding `pref2` and trying to clear `pref5` are ignored open(joinpath(dir, "outer_env", "LocalPreferences.toml"), write=true) do io write(io, """ [This] pref2 = "outer-lp" __clear__ = ["pref5"] """) end # Next, set up an inner environment that will override some of the preferences # set by the outer environment, even clearing `pref6`. mkpath(joinpath(dir, "inner_env")) open(joinpath(dir, "inner_env", "Project.toml"), write=true) do io write(io, """ name = "Root" uuid = "$(uuid4())" [extras] This = "$(this_uuid)" [preferences.This] pref3 = "inner-project" pref4 = "inner-project" __clear__ = ["pref6"] """) end # And have an override here as well, this time only LocalPreferences.toml open(joinpath(dir, "inner_env", "LocalPreferences.toml"), write=true) do io write(io, """ [This] pref4 = "inner-lp" """) end # Finally, we load preferences with a stacked environment, and ensure that # we get the appropriate outputs: old_load_path = copy(LOAD_PATH) try copy!(LOAD_PATH, [joinpath(dir, "inner_env", "Project.toml"), joinpath(dir, "outer_env", "Project.toml")]) function test_this_prefs(this_prefs) @test this_prefs["pref1"] == "outer-project" @test this_prefs["pref2"] == "outer-jlp" @test this_prefs["pref3"] == "inner-project" @test this_prefs["pref4"] == "inner-lp" @test this_prefs["pref5"] == "outer-project" @test !haskey(this_prefs, "pref6") end # Test directly loading the UUID we're interested in test_this_prefs(Base.get_preferences(this_uuid)) # Also test loading _all_ preferences all_prefs = Base.get_preferences() @test haskey(all_prefs, "This") @test haskey(all_prefs, "That") @test all_prefs["That"]["pref1"] == "outer-project" # Ensure that the sub-tree of `This` still satisfies our tests test_this_prefs(all_prefs["This"]) finally copy!(LOAD_PATH, old_load_path) end end end @testset "Loading with incomplete manifest/depot #45977" begin mktempdir() do tmp # Set up a stacked env. cp(joinpath(@__DIR__, "depot"), joinpath(tmp, "depot")) mkdir(joinpath(tmp, "Env1")) mkdir(joinpath(tmp, "Global")) for env in ["Env1", "Global"] write(joinpath(tmp, env, "Project.toml"), """ [deps] Baz = "6801f525-dc68-44e8-a4e8-cabd286279e7" """) end write(joinpath(tmp, "Global", "Manifest.toml"), """ [[Baz]] uuid = "6801f525-dc68-44e8-a4e8-cabd286279e7" git-tree-sha1 = "efc7e24c53d6a328011975294a2c75fed2f9800a" """) # This SHA does not exist in the depot. write(joinpath(tmp, "Env1", "Manifest.toml"), """ [[Baz]] uuid = "6801f525-dc68-44e8-a4e8-cabd286279e7" git-tree-sha1 = "5f2f6e72d001b014b48b26ec462f3714c342e167" """) old_load_path = copy(LOAD_PATH) old_depot_path = copy(DEPOT_PATH) try empty!(LOAD_PATH) push!(empty!(DEPOT_PATH), joinpath(tmp, "depot")) push!(LOAD_PATH, joinpath(tmp, "Global")) pkg = Base.identify_package("Baz") # Package in manifest in current env not present in depot @test Base.locate_package(pkg) !== nothing @test Base.find_package("Baz") !== nothing # coverage pushfirst!(LOAD_PATH, joinpath(tmp, "Env1")) @test Base.locate_package(pkg) === nothing write(joinpath(tmp, "Env1", "Manifest.toml"), """ """) # Package in current env not present in manifest pkg, env = Base.identify_package_env("Baz") @test Base.locate_package(pkg, env) === nothing finally copy!(LOAD_PATH, old_load_path) copy!(DEPOT_PATH, old_depot_path) end end end @testset "Extensions" begin depot_path = mktempdir() try proj = joinpath(@__DIR__, "project", "Extensions", "HasDepWithExtensions.jl") function gen_extension_cmd(compile, distr=false) load_distr = distr ? "using Distributed; addprocs(1)" : "" ew = distr ? "@everywhere" : "" cmd = """ $load_distr begin $ew push!(empty!(DEPOT_PATH), $(repr(depot_path))) using HasExtensions $ew using HasExtensions $ew Base.get_extension(HasExtensions, :Extension) === nothing || error("unexpectedly got an extension") $ew HasExtensions.ext_loaded && error("ext_loaded set") using HasDepWithExtensions $ew using HasDepWithExtensions $ew Base.get_extension(HasExtensions, :Extension).extvar == 1 || error("extvar in Extension not set") $ew HasExtensions.ext_loaded || error("ext_loaded not set") $ew HasExtensions.ext_folder_loaded && error("ext_folder_loaded set") $ew HasDepWithExtensions.do_something() || error("do_something errored") using ExtDep2 $ew using ExtDep2 $ew HasExtensions.ext_folder_loaded || error("ext_folder_loaded not set") end """ return `$(Base.julia_cmd()) $compile --startup-file=no -e $cmd` end for compile in (`--compiled-modules=no`, ``, ``) # Once when requiring precompilation, once where it is already precompiled cmd = gen_extension_cmd(compile) cmd = addenv(cmd, "JULIA_LOAD_PATH" => proj) cmd = pipeline(cmd; stdout, stderr) @test success(cmd) end sep = Sys.iswindows() ? ';' : ':' cmd = gen_extension_cmd(``, true) cmd = addenv(cmd, "JULIA_LOAD_PATH" => join([proj, "@stdlib"], sep)) str = read(cmd, String) @test !occursin("Error during loading of extension", str) @test !occursin("ConcurrencyViolationError", str) # 48351 cmd = gen_extension_cmd(``) cmd = addenv(cmd, "JULIA_LOAD_PATH" => join([mktempdir(), proj], sep)) cmd = pipeline(cmd; stdout, stderr) @test success(cmd) # Only load env from where package is loaded envs = [joinpath(@__DIR__, "project", "Extensions", "EnvWithHasExtensionsv2"), joinpath(@__DIR__, "project", "Extensions", "EnvWithHasExtensions")] cmd = addenv(```$(Base.julia_cmd()) --startup-file=no -e ' begin push!(empty!(DEPOT_PATH), '$(repr(depot_path))') using HasExtensions using ExtDep Base.get_extension(HasExtensions, :Extension) === nothing || error("unexpectedly loaded ext from other env") Base.get_extension(HasExtensions, :Extension2) === nothing && error("did not load ext from active env") end ' ```, "JULIA_LOAD_PATH" => join(envs, sep)) @test success(cmd) test_ext_proj = """ begin using HasExtensions using ExtDep Base.get_extension(HasExtensions, :Extension) isa Module || error("expected extension to load") using ExtDep2 Base.get_extension(HasExtensions, :ExtensionFolder) isa Module || error("expected extension to load") end """ for compile in (`--compiled-modules=no`, ``) cmd_proj_ext = `$(Base.julia_cmd()) $compile --startup-file=no -e $test_ext_proj` proj = joinpath(@__DIR__, "project", "Extensions") cmd_proj_ext = addenv(cmd_proj_ext, "JULIA_LOAD_PATH" => join([joinpath(proj, "HasExtensions.jl"), joinpath(proj, "EnvWithDeps")], sep)) run(cmd_proj_ext) end finally try rm(depot_path, force=true, recursive=true) catch err @show err end end end pkgimage(val) = val == 1 ? `--pkgimage=yes` : `--pkgimage=no` opt_level(val) = `-O$val` debug_level(val) = `-g$val` inline(val) = val == 1 ? `--inline=yes` : `--inline=no` check_bounds(val) = if val == 0 `--check-bounds=auto` elseif val == 1 `--check-bounds=yes` elseif val == 2 `--check-bounds=no` end @testset "CacheFlags" begin cf = Base.CacheFlags() opts = Base.JLOptions() @test cf.use_pkgimages == opts.use_pkgimages @test cf.debug_level == opts.debug_level @test cf.check_bounds == opts.check_bounds @test cf.inline == opts.can_inline @test cf.opt_level == opts.opt_level # OOICCDDP for (P, D, C, I, O) in Iterators.product(0:1, 0:2, 0:2, 0:1, 0:3) julia = joinpath(Sys.BINDIR, Base.julia_exename()) script = """ let cf = Base.CacheFlags() opts = Base.JLOptions() cf.use_pkgimages == opts.use_pkgimages == $P || error("use_pkgimages") cf.debug_level == opts.debug_level == $D || error("debug_level") cf.check_bounds == opts.check_bounds == $C || error("check_bounds") cf.inline == opts.can_inline == $I || error("inline") cf.opt_level == opts.opt_level == $O || error("opt_level") end """ cmd = `$julia $(pkgimage(P)) $(opt_level(O)) $(debug_level(D)) $(check_bounds(C)) $(inline(I)) -e $script` @test success(pipeline(cmd; stdout, stderr)) end cf = Base.CacheFlags(255) @test cf.use_pkgimages @test cf.debug_level == 3 @test cf.check_bounds == 3 @test cf.inline @test cf.opt_level == 3 io = PipeBuffer() show(io, cf) @test read(io, String) == "use_pkgimages = true, debug_level = 3, check_bounds = 3, inline = true, opt_level = 3" end empty!(Base.DEPOT_PATH) append!(Base.DEPOT_PATH, original_depot_path) @testset "loading deadlock detector" begin pkid1 = Base.PkgId("pkgid1") pkid2 = Base.PkgId("pkgid2") pkid3 = Base.PkgId("pkgid3") pkid4 = Base.PkgId("pkgid4") e = Base.Event() @test nothing === @lock Base.require_lock Base.start_loading(pkid4) # module pkgid4 @test nothing === @lock Base.require_lock Base.start_loading(pkid1) # module pkgid1 t1 = @async begin @test nothing === @lock Base.require_lock Base.start_loading(pkid2) # @async module pkgid2; using pkgid1; end notify(e) @test "loaded_pkgid1" == @lock Base.require_lock Base.start_loading(pkid1) @lock Base.require_lock Base.end_loading(pkid2, "loaded_pkgid2") end wait(e) reset(e) t2 = @async begin @test nothing === @lock Base.require_lock Base.start_loading(pkid3) # @async module pkgid3; using pkgid2; end notify(e) @test "loaded_pkgid2" == @lock Base.require_lock Base.start_loading(pkid2) @lock Base.require_lock Base.end_loading(pkid3, "loaded_pkgid3") end wait(e) reset(e) @test_throws(ConcurrencyViolationError("deadlock detected in loading pkgid3 -> pkgid2 -> pkgid1 -> pkgid3 && pkgid4"), @lock Base.require_lock Base.start_loading(pkid3)).value # try using pkgid3 @test_throws(ConcurrencyViolationError("deadlock detected in loading pkgid4 -> pkgid4 && pkgid1"), @lock Base.require_lock Base.start_loading(pkid4)).value # try using pkgid4 @lock Base.require_lock Base.end_loading(pkid1, "loaded_pkgid1") # end @lock Base.require_lock Base.end_loading(pkid4, "loaded_pkgid4") # end wait(t2) wait(t1) end @testset "Upgradable stdlibs" begin @test success(`$(Base.julia_cmd()) --startup-file=no -e 'using DelimitedFiles'`) end