Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
also raised to 30 seconds (still configurable) to reduce false positives when a
package without persistent tasks is merely slow to shut down. ([#315])
- Make `test_piracy` check modules recursively for type piracy. This may result in failures for downstream users. ([#377])
- `test_all` runs its subprocess-based checks concurrently. On Julia 1.13 and
later, `find_persistent_tasks_deps` also checks dependencies concurrently.
([#315])

## Version [v0.8.16] - 2026-06-05

Expand Down
4 changes: 4 additions & 0 deletions docs/src/persistent_tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ If precompilation instead fails outright (for example because a dependency
cannot be precompiled), that is reported as a precompilation error rather than a
persistent task, so the failure message points at the real cause.

On Julia 1.13 and later, `find_persistent_tasks_deps` checks dependencies
concurrently. Older Julia versions check them one at a time because concurrent
environment instantiation is not safe there.

## How to fix failing packages

Often, the easiest fix is to modify the `__init__` function to check whether the
Expand Down
62 changes: 50 additions & 12 deletions src/Aqua.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,48 +60,86 @@ function test_all(
persistent_tasks = true,
undocumented_names = false,
)
if ambiguities !== false
# Launch subprocess-based checks together, then record their Test results on
# this task. Persistent-task setup runs first because it changes Pkg state
# that the other checks read when launching their subprocesses.
persistent_tasks_launch = enabled(persistent_tasks) ?
Threads.@spawn(_launch_persistent_tasks(
PkgId(testtarget);
askwargs(persistent_tasks)...,
)) : nothing
ambiguities_task = nothing
stale_deps_task = nothing
persistent_tasks_task = nothing
tasks = Task[]
try
persistent_tasks_launch === nothing ||
timedwait(() -> istaskdone(persistent_tasks_launch), Inf)
ambiguities_task = enabled(ambiguities) ?
Threads.@spawn(_result_ambiguities(
aspkgids([testtarget]);
askwargs(ambiguities)...,
)) : nothing
stale_deps_task = enabled(stale_deps) ?
Threads.@spawn(find_stale_deps(
aspkgid(testtarget);
askwargs(stale_deps)...,
)) : nothing
persistent_tasks_task = persistent_tasks_launch === nothing ? nothing :
Threads.@spawn(_await_persistent_tasks(persistent_tasks_launch))
append!(tasks, filter(!isnothing, [
ambiguities_task, stale_deps_task, persistent_tasks_task
]))
timedwait(() -> all(istaskdone, tasks), Inf)
finally
persistent_tasks_launch === nothing ||
_stop_persistent_tasks(persistent_tasks_launch)
timedwait(() -> all(istaskdone, tasks), Inf)
end

if enabled(ambiguities)
@testset "Method ambiguity" begin
test_ambiguities([testtarget]; askwargs(ambiguities)...)
_report_ambiguities(fetch(ambiguities_task))
end
end
if unbound_args !== false
if enabled(unbound_args)
@testset "Unbound type parameters" begin
test_unbound_args(testtarget; askwargs(unbound_args)...)
end
end
if undefined_exports !== false
if enabled(undefined_exports)
@testset "Undefined exports" begin
test_undefined_exports(testtarget; askwargs(undefined_exports)...)
end
end
if project_extras !== false
if enabled(project_extras)
@testset "Compare Project.toml and test/Project.toml" begin
isempty(askwargs(project_extras)) || error("Keyword arguments not supported")
test_project_extras(testtarget)
end
end
if stale_deps !== false
if enabled(stale_deps)
@testset "Stale dependencies" begin
test_stale_deps(testtarget; askwargs(stale_deps)...)
stale = fetch(stale_deps_task)
@test isempty(stale)
end
end
if deps_compat !== false
if enabled(deps_compat)
@testset "Compat bounds" begin
test_deps_compat(testtarget; askwargs(deps_compat)...)
end
end
if piracies !== false
if enabled(piracies)
@testset "Piracy" begin
test_piracies(testtarget; askwargs(piracies)...)
end
end
if persistent_tasks !== false
if enabled(persistent_tasks)
@testset "Persistent tasks" begin
test_persistent_tasks(testtarget; askwargs(persistent_tasks)...)
_report_persistent_tasks(fetch(persistent_tasks_task))
end
end
if undocumented_names !== false
if enabled(undocumented_names)
@testset "Undocumented names" begin
isempty(askwargs(undocumented_names)) ||
error("Keyword arguments not supported")
Expand Down
23 changes: 17 additions & 6 deletions src/ambiguities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,28 @@ function reprexclude(exspecs::Vector{ExcludeSpec})
return string("Aqua.ExcludeSpec[", join(itemreprs, ", "), "]")
end

function _test_ambiguities(packages::Vector{PkgId}; broken::Bool = false, kwargs...)
function _test_ambiguities(packages::Vector{PkgId}; kwargs...)
_report_ambiguities(_result_ambiguities(packages; kwargs...))
end

# Run the ambiguity-detection subprocess and return the data needed to report
# the result. Separated from `_report_ambiguities` so that `test_all` can run
# this subprocess concurrently with other checks and record the result
# afterwards on the main task (where `Test` keeps its task-local state).
function _result_ambiguities(packages::Vector{PkgId}; broken::Bool = false, kwargs...)
num_ambiguities, strout, strerr =
_find_ambiguities(packages; skipdetails = broken, kwargs...)
return (; num_ambiguities, strout, strerr, broken)
end

print(stderr, strerr)
print(stdout, strout)
function _report_ambiguities(result)
print(stderr, result.strerr)
print(stdout, result.strout)

if broken
@test_broken iszero(num_ambiguities)
if result.broken
@test_broken iszero(result.num_ambiguities)
else
@test iszero(num_ambiguities)
@test iszero(result.num_ambiguities)
end
end

Expand Down
Loading
Loading