Skip to content

Commit

Permalink
CI: Respect [compat] entries for test-only deps when running in Bas…
Browse files Browse the repository at this point in the history
…e Julia's Buildkite CI; run the `ambiguous` testgroup in Buildkite CI; mark `Aqua.test_project_toml_formatting` as `@test_skip`
  • Loading branch information
DilumAluthge committed Nov 16, 2023
1 parent f154de2 commit 3d9eadb
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 23 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ jobs:
- run: julia --color=yes .ci/test_and_change_uuid.jl
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
env:
SPARSEARRAYS_AQUA_TEST: true
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1
with:
Expand Down
175 changes: 175 additions & 0 deletions test/TestDepsUtils.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
module TestDepsUtils

import Pkg

# Base Julia's Buildkite CI doesn't run stdlib tests
# via `Pkg.test`. Therefore, test deps must be manually
# installed if missing.
#
# The `force_install` kwarg is only used for debugging.
function install_all_test_deps(; force_install::Bool = false)
project = nothing
for pkg_name in get_test_deps()
(; project) = install_single_test_dep(
pkg_name;
project,
force_install,
)
end
return nothing
end

function install_single_test_dep(
pkg_name::AbstractString;
project::Union{AbstractString, Nothing},
force_install::Bool,
)
pkg_uuid_str = get_uuid_str(pkg_name)
pkg_compat = get_compat(pkg_name)
pkg_spec = Pkg.PackageSpec(;
name = pkg_name,
version = pkg_compat, # make sure we respect the `[compat]` entry
uuid = pkg_uuid_str,
)

found_package = Base.find_package(pkg_name)

if (found_package === nothing) || force_install
if project === nothing
Pkg.activate(; temp = true)
project = Base.active_project()
@debug "Activated a new temp project at $(project)"
end
@debug "Installing $(pkg_name).jl"
iob = IOBuffer()
try
# To keep the logs (the Base Julia Buildkite CI logs) clean,
# we don't print the Pkg output to the log unless the `Pkg.add`
# fails. Therefore, we pass `io = iob`.
Pkg.add(pkg_spec; io = iob)
catch
println(String(take!(iob)))
rethrow()
end
else
@debug "Found $(pkg_name).jl" found_package
end

return (; project)
end

function get_project_dict()
root_test = @__DIR__ # ./test/
root = dirname(root_test) # ./
project_filename = joinpath(root, "Project.toml") # ./Project.toml
project_dict = Pkg.TOML.parsefile(project_filename)
return project_dict
end

function get_testprojecttoml_dict()
root_test = @__DIR__ # ./test/
testprojecttoml_filename = joinpath(root_test, "Project.toml") # ./test/Project.toml
if ispath(testprojecttoml_filename)
testprojecttoml_dict = Pkg.TOML.parsefile(project_filename)
else
# It is totally fine if the `./test/Project.toml` file doesn't exist.
testprojecttoml_dict = Dict()
end
return testprojecttoml_dict
end

function get_test_deps()
project_dict = get_project_dict()
targets_section = get(project_dict, "targets", Dict())
test_target_list = get(targets_section, "test", [])

testprojecttoml_dict = get_testprojecttoml_dict()
testprojecttoml_deps_section = get(testprojecttoml_dict, "deps", Dict())

if isempty(test_target_list) && isempty(testprojecttoml_deps_section)
# We require that at least one of the following conditions is true:
# 1. The main `Project.toml` has a non-empty test target.
# 2. `test/Project.toml` exists and has a non-empty `[deps]` section.
#
# After all, we know that this repo has at least one test dependency, namely
# Test. So that test dependency has to be listed somewhere.
error("Could not find any test dependencies in either Project.toml or test/Project.toml")
end

all_test_deps = vcat(
test_target_list,
collect(keys(testprojecttoml_deps_section)),
)
return sort(unique(all_test_deps))
end

function get_uuid_str(pkg_name::AbstractString)
project_dict = get_project_dict()
deps_section = get(project_dict, "deps", Dict())
extras_section = get(project_dict, "extras", Dict())

testprojecttoml_dict = get_testprojecttoml_dict()
testprojecttoml_deps_section = get(testprojecttoml_dict, "deps", Dict())

if haskey(deps_section, pkg_name)
# First, check `[deps]` in Project.toml
pkg_uuid_str = deps_section[pkg_name]
elseif haskey(extras_section, pkg_name)
# Next, check `[extras]` in Project.toml
pkg_uuid_str = extras_section[pkg_name]
else
# Finally, we assume it's in the `[deps]` in test/Project.toml
pkg_uuid_str = testprojecttoml_deps_section[pkg_name]
end

return pkg_uuid_str
end

# Suppose that the compat entry looks like this:
#
# ```
# [compat]
# PkgName = "1.1, 2.2, 3.3"
# ````
#
# In this case, if we pass the string "1.1, 2.2, 3.3" as the value of
# `version` when doing `Pkg.add(; name, uuid, version)`, Pkg will throw an
# error, because it doesn't like the commas. So, we need to parse the
# string "1.1, 2.2, 3.3" into a `Pkg.Types.VersionSpec`, which Pkg will then
# accept as the value of `version`.
function get_compat(pkg_name::AbstractString)
pkg_compat_str = _get_compat_str(pkg_name)
pkg_compat_versionspec = parse_compat_entry(pkg_compat_str)

return pkg_compat_versionspec
end

function _get_compat_str(pkg_name::AbstractString)
project_dict = get_project_dict()
compat_section = get(project_dict, "compat", Dict())

testprojecttoml_dict = get_testprojecttoml_dict()
testprojecttomlcompat_section = get(testprojecttoml_dict, "compat", Dict())

if haskey(compat_section, pkg_name)
# First, check `[compat]` in Project.toml
pkg_compat_str = compat_section[pkg_name]
else
# Finally, we assume it's in the `[compat]` in test/Project.toml
pkg_compat_str = testprojecttomlcompat_section[pkg_name]
end

return pkg_compat_str
end

# The `parse_compat_entry` function uses Pkg internals. Therefore,
# we might need to have different versions of `parse_compat_entry`
# for different minor versions of Julia.
function parse_compat_entry end
@static if VERSION >= v"1.0.0-"
parse_compat_entry(pkg_compat_str) = Pkg.Types.semver_spec(pkg_compat_str)
else
error("Unsupported Julia version: $(VERSION)")
end

end # module
23 changes: 6 additions & 17 deletions test/ambiguous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,9 @@ original_env = copy(ENV)
original_project = Base.active_project()
###

import Pkg

# Because julia CI doesn't run stdlib tests via `Pkg.test` test deps must be manually installed if missing
if Base.find_package("Aqua") === nothing
@debug "Installing Aqua.jl for SparseArrays.jl tests"
iob = IOBuffer()
Pkg.activate(; temp = true)
try
# TODO: make this version tie to compat in Project.toml
# or do this another safer way
Pkg.add(name="Aqua", version="0.8", io=iob) # Needed for custom julia version resolve tests
catch
println(String(take!(iob)))
rethrow()
end
end
include("TestDepsUtils.jl")
# TestDepsUtils.install_all_test_deps(; force_install = true) # uncomment this line for debugging
TestDepsUtils.install_all_test_deps()

using Test, LinearAlgebra, SparseArrays, Aqua

Expand All @@ -46,7 +33,9 @@ using Test, LinearAlgebra, SparseArrays, Aqua
@testset "Compat bounds" begin
Aqua.test_deps_compat(SparseArrays)
end

@testset "Project.toml formatting" begin
@test_skip Aqua.test_project_toml_formatting(SparseArrays)
end
@testset "Piracy" begin
@test_broken Aqua.Piracy.hunt(SparseArrays) == Method[]
end
Expand Down
4 changes: 0 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license
using Test, LinearAlgebra, SparseArrays

if Base.get_bool_env("SPARSEARRAYS_AQUA_TEST", false)
include("ambiguous.jl")
end

for file in readlines(joinpath(@__DIR__, "testgroups"))
file == "" && continue # skip empty lines
include(file * ".jl")
Expand Down
1 change: 1 addition & 0 deletions test/testgroups
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
allowscalar
ambiguous
cholmod
fixed
higherorderfns
Expand Down

0 comments on commit 3d9eadb

Please sign in to comment.