Revision db393d03244c103cffead1297323ff079d3844ee authored by Valentin Churavy on 28 June 2023, 07:30:52 UTC, committed by Valentin Churavy on 28 June 2023, 08:20:32 UTC
1 parent 6f73354
loading.jl
# 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
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...