Skip to content

Commit

Permalink
Refactor LazyTestResult away (#237)
Browse files Browse the repository at this point in the history
  • Loading branch information
lgoettgens authored Nov 24, 2023
1 parent f3e9fd9 commit e9a4230
Show file tree
Hide file tree
Showing 18 changed files with 211 additions and 311 deletions.
3 changes: 3 additions & 0 deletions src/Aqua.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ using Base: PkgId, UUID
using Pkg: Pkg, TOML, PackageSpec
using Test

@static if VERSION < v"1.1.0-DEV.472"
using Compat: isnothing
end
@static if VERSION < v"1.3.0-DEV.349"
using Compat: findfirst
end
Expand Down
22 changes: 0 additions & 22 deletions src/ambiguities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,6 @@ test_ambiguities(packages; kwargs...) = _test_ambiguities(aspkgids(packages); kw

const ExcludeSpec = Pair{Base.PkgId,String}

aspkgids(pkg::Union{Module,PkgId}) = aspkgids([pkg])
aspkgids(packages) = mapfoldl(aspkgid, push!, packages, init = PkgId[])

aspkgid(pkg::PkgId) = pkg
function aspkgid(m::Module)
if !ispackage(m)
error("Non-package (non-toplevel) module is not supported. Got: $m")
end
return PkgId(m)
end
function aspkgid(name::Symbol)
# Maybe `Base.depwarn()`
return Base.identify_package(String(name))::PkgId
end

ispackage(m::Module) =
if m in (Base, Core)
true
else
parentmodule(m) == m
end

strnameof(x) = string(x)
strnameof(x::Type) = string(nameof(x))

Expand Down
10 changes: 4 additions & 6 deletions src/deps_compat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ function test_julia_compat(pkg::PkgId; broken::Bool = false)
end

function has_julia_compat(pkg::PkgId)
result = root_project_or_failed_lazytest(pkg)
result isa LazyTestResult && error("Unable to locate Project.toml")
root_project_path = result
root_project_path, found = root_project_toml(pkg)
found || error("Unable to locate Project.toml")
prj = TOML.parsefile(root_project_path)
return has_julia_compat(prj)
end
Expand All @@ -93,9 +92,8 @@ function has_julia_compat(prj::Dict{String,Any})
end

function find_missing_deps_compat(pkg::PkgId, deps_type::String = "deps"; kwargs...)
result = root_project_or_failed_lazytest(pkg)
result isa LazyTestResult && error("Unable to locate Project.toml")
root_project_path = result
root_project_path, found = root_project_toml(pkg)
found || error("Unable to locate Project.toml")
missing_compat =
find_missing_deps_compat(TOML.parsefile(root_project_path), deps_type; kwargs...)

Expand Down
14 changes: 7 additions & 7 deletions src/persistent_tasks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ function test_persistent_tasks(package::Module; kwargs...)
end

function has_persistent_tasks(package::PkgId; tmax = 10)
result = root_project_or_failed_lazytest(package)
result isa LazyTestResult && error("Unable to locate Project.toml")
return !precompile_wrapper(result, tmax)
root_project_path, found = root_project_toml(package)
found || error("Unable to locate Project.toml")
return !precompile_wrapper(root_project_path, tmax)
end

"""
Expand All @@ -102,9 +102,9 @@ These are likely the ones blocking precompilation of your package.
Any additional kwargs (e.g., `tmax`) are passed to [`Aqua.test_persistent_tasks`](@ref).
"""
function find_persistent_tasks_deps(package::PkgId; kwargs...)
result = root_project_or_failed_lazytest(package)
result isa LazyTestResult && error("Unable to locate Project.toml")
prj = TOML.parsefile(result)
root_project_path, found = root_project_toml(package)
found || error("Unable to locate Project.toml")
prj = TOML.parsefile(root_project_path)
deps = get(prj, "deps", Dict{String,Any}())
filter!(deps) do (name, uuid)
id = PkgId(UUID(uuid), name)
Expand All @@ -121,7 +121,7 @@ function precompile_wrapper(project, tmax)
if VERSION < v"1.10.0-"
return true
end
prev_project = Base.active_project()
prev_project = Base.active_project()::String
isdefined(Pkg, :respect_sysimage_versions) && Pkg.respect_sysimage_versions(false)
try
pkgdir = dirname(project)
Expand Down
128 changes: 55 additions & 73 deletions src/project_extras.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,103 +7,85 @@ Check that test target of the root project and test project
Julia < 1.2 while recording test-only dependency compatibility in
`test/Project.toml`.
"""
function test_project_extras(packages)
@testset "$(result.label)" for result in analyze_project_extras(packages)
@debug result.label result
@test result true
end
function test_project_extras(pkg::PkgId; kwargs...)
msgs = analyze_project_extras(pkg; kwargs...)
@test isempty(msgs)
end

function project_toml_path(dir)
candidates = joinpath.(dir, ["Project.toml", "JuliaProject.toml"])
i = findfirst(isfile, candidates)
i === nothing && return candidates[1], false
return candidates[i], true
# Remove with next breaking version
function test_project_extras(packages::Vector{<:Union{Module,PkgId}}; kwargs...)
@testset "$pkg" for pkg in packages
test_project_extras(pkg; kwargs...)
end
end

analyze_project_extras(packages) = map(_analyze_project_extras, aspkgids(packages))
function test_project_extras(mod::Module; kwargs...)
test_project_extras(aspkgid(mod); kwargs...)
end

is_julia12_or_later(compat::AbstractString) = is_julia12_or_later(semver_spec(compat))
is_julia12_or_later(compat::VersionSpec) = isempty(compat semver_spec("1.0 - 1.1"))

function _analyze_project_extras(pkg::PkgId)
label = string(pkg)
function analyze_project_extras(pkg::PkgId)
root_project_path, found = root_project_toml(pkg)
found || error("Unable to locate Project.toml")

result = root_project_or_failed_lazytest(pkg)
result isa LazyTestResult && return result
root_project_path = result

package_loc = Base.locate_package(pkg)
package_loc === nothing &&
return LazyTestResult(label, "Base.locate_package failed.", false)
pkgpath = dirname(dirname(package_loc))
test_project_path, found = project_toml_path(joinpath(pkgpath, "test"))
if !found
return LazyTestResult(label, "test/Project.toml file does not exist.", true)
end
test_project_path, found =
project_toml_path(joinpath(dirname(root_project_path), "test"))
found || return String[] # having no test/Project.toml is fine
root_project = TOML.parsefile(root_project_path)
test_project = TOML.parsefile(test_project_path)

# Ignore root project's extras if only supporting julia 1.2 or later.
# See: # https://julialang.github.io/Pkg.jl/v1/creating-packages/#Test-specific-dependencies-in-Julia-1.2-and-above-1
julia_version = get(get(root_project, "compat", Dict()), "julia", "1")
if is_julia12_or_later(julia_version)
return LazyTestResult(
label,
string(
"Supporting only post-1.2 `julia` ($julia_version); ",
"ignoring root project.",
),
true,
)
end
julia_version = get(get(root_project, "compat", Dict{String,Any}()), "julia", nothing)
isnothing(julia_version) && return String["Could not find `julia` compat."]
is_julia12_or_later(julia_version) && return String[]

# `extras_deps`: test-only dependencies according to /Project.toml
all_extras_deps = get(root_project, "extras", Dict())
target = Set{String}(get(get(root_project, "targets", Dict()), "test", []))
extras_deps = setdiff(
Set{Pair{String,String}}(p for p in all_extras_deps if first(p) in target),
Set{Pair{String,String}}(get(root_project, "deps", [])),
# `extras_test_deps`: test-only dependencies according to Project.toml
deps = [PkgId(UUID(v), k) for (k, v) in get(root_project, "deps", Dict{String,Any}())]
target =
Set{String}(get(get(root_project, "targets", Dict{String,Any}()), "test", String[]))
extras_test_deps = setdiff(
[
PkgId(UUID(v), k) for
(k, v) in get(root_project, "extras", Dict{String,Any}()) if k in target
],
deps,
)

# `test_deps`: test-only dependencies according to /test/Project.toml:
# `test_deps`: test-only dependencies according to test/Project.toml:
test_deps = setdiff(
Set{Pair{String,String}}(get(test_project, "deps", [])),
Set{Pair{String,String}}(get(root_project, "deps", [])),
[root_project["name"] => root_project["uuid"]],
[PkgId(UUID(v), k) for (k, v) in get(test_project, "deps", Dict{String,Any}())],
deps,
[PkgId(UUID(root_project["uuid"]), root_project["name"])],
)

not_in_extras = setdiff(test_deps, extras_deps)
not_in_test = setdiff(extras_deps, test_deps)
not_in_extras = setdiff(test_deps, extras_test_deps)
not_in_test = setdiff(extras_test_deps, test_deps)
if isempty(not_in_extras) && isempty(not_in_test)
return LazyTestResult(
label,
"""
Root and test projects are consistent.
Root project: $root_project_path
Test project: $test_project_path
""",
true,
)
return String[]
else
msg = sprint() do io
println(
io,
"Root and test projects should be consistent for projects supporting Julia <= 1.1.",
)
if !isempty(not_in_extras)
println(io, "Test dependencies not in root project ($root_project_path):")
for (name, uuid) in sort!(collect(not_in_extras))
println(io, " $name = \"$uuid\"")
end
msgs = String[]
push!(
msgs,
"Root and test projects should be consistent for projects supporting Julia <= 1.1.",
)
if !isempty(not_in_extras)
msg = "Test dependencies not in root project ($root_project_path):"
for pkgs in sort!(collect(not_in_extras); by = (pkg -> pkg.name))
msg *= "\n\t$pkgs"
end
if !isempty(not_in_test)
println(io, "Dependencies not in test project ($test_project_path):")
for (name, uuid) in sort!(collect(not_in_test))
println(io, " $name = \"$uuid\"")
end
push!(msgs, msg)
end
if !isempty(not_in_test)
msg = "Dependencies not in test project ($test_project_path):"
for pkgs in sort!(collect(not_in_test); by = (pkg -> pkg.name))
msg *= "\n\t$pkgs"
end
push!(msgs, msg)
end
return LazyTestResult(label, msg, false)

return msgs
end
end
68 changes: 20 additions & 48 deletions src/stale_deps.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,29 @@ directly, this can be achieved via transitivity as well.
# Keyword Arguments
- `ignore::Vector{Symbol}`: names of dependent packages to be ignored.
"""
function test_stale_deps(packages; kwargs...)
@testset "$(result.label)" for result in analyze_stale_deps(packages, kwargs...)
@debug result.label result
@test result true
end
function test_stale_deps(pkg::PkgId; kwargs...)
stale_deps = find_stale_deps(pkg; kwargs...)
@test isempty(stale_deps)
end

analyze_stale_deps(packages, kwargs...) =
[_analyze_stale_deps_1(pkg; kwargs...) for pkg in aspkgids(packages)]
function test_stale_deps(mod::Module; kwargs...)
test_stale_deps(aspkgid(mod); kwargs...)
end

function _analyze_stale_deps_1(pkg::PkgId; ignore::AbstractVector{Symbol} = Symbol[])
label = "$pkg"
# Remove in next breaking release
function test_stale_deps(packages::Vector{<:Union{Module,PkgId}}; kwargs...)
@testset "$pkg" for pkg in packages
test_stale_deps(pkg; kwargs...)
end
end

result = root_project_or_failed_lazytest(pkg)
result isa LazyTestResult && return result
root_project_path = result
function find_stale_deps(pkg::PkgId; ignore::AbstractVector{Symbol} = Symbol[])
root_project_path, found = root_project_toml(pkg)
found || error("Unable to locate Project.toml")

@debug "Parsing `$root_project_path`"
prj = TOML.parsefile(root_project_path)
raw_deps = get(prj, "deps", nothing)
if raw_deps === nothing
return LazyTestResult(label, "No `deps` table in `$root_project_path`", true)
end
deps = [PkgId(UUID(v), k) for (k, v) in raw_deps]

raw_weakdeps = get(prj, "weakdeps", nothing)
weakdeps =
isnothing(raw_weakdeps) ? PkgId[] : [PkgId(UUID(v), k) for (k, v) in raw_weakdeps]
deps = [PkgId(UUID(v), k) for (k, v) in get(prj, "deps", Dict{String,Any}())]
weakdeps = [PkgId(UUID(v), k) for (k, v) in get(prj, "weakdeps", Dict{String,Any}())]

marker = "_START_MARKER_"
code = """
Expand All @@ -66,14 +61,12 @@ function _analyze_stale_deps_1(pkg::PkgId; ignore::AbstractVector{Symbol} = Symb
"""
cmd = Base.julia_cmd()
output = read(`$cmd --startup-file=no --color=no -e $code`, String)
@debug("Checked modules loaded in a separate process.", cmd, Text(code), Text(output))
pos = findfirst(marker, output)
@assert !isnothing(pos)
output = output[pos.stop+1:end]
loaded_uuids = map(UUID, eachline(IOBuffer(output)))

return _analyze_stale_deps_2(;
pkg = pkg,
return find_stale_deps_2(;
deps = deps,
weakdeps = weakdeps,
loaded_uuids = loaded_uuids,
Expand All @@ -82,14 +75,12 @@ function _analyze_stale_deps_1(pkg::PkgId; ignore::AbstractVector{Symbol} = Symb
end

# Side-effect -free part of stale dependency analysis.
function _analyze_stale_deps_2(;
pkg::PkgId,
function find_stale_deps_2(;
deps::AbstractVector{PkgId},
weakdeps::AbstractVector{PkgId},
loaded_uuids::AbstractVector{UUID},
ignore::AbstractVector{Symbol},
)
label = "$pkg"
deps_uuids = [p.uuid for p in deps]
pkgid_from_uuid = Dict(p.uuid => p for p in deps)

Expand All @@ -98,24 +89,5 @@ function _analyze_stale_deps_2(;
stale_pkgs = setdiff(stale_pkgs, weakdeps)
stale_pkgs = [p for p in stale_pkgs if !(Symbol(p.name) in ignore)]

if isempty(stale_pkgs)
return LazyTestResult(
label,
"""
All packages in `deps` are loaded via `using $(pkg.name)`.
""",
true,
)
end

stale_msg = join(("* $p" for p in stale_pkgs), "\n")
msglines = [
"Some package(s) in `deps` of $pkg are not loaded during via" *
" `using $(pkg.name)`.",
stale_msg,
"",
"To ignore from stale dependency detection, pass the package name to" *
" `ignore` keyword argument of `Aqua.test_stale_deps`",
]
return LazyTestResult(label, join(msglines, "\n"), false)
return stale_pkgs
end
Loading

0 comments on commit e9a4230

Please sign in to comment.