From 8d4a2509d317744d0303b8a92514d4d1c6f0d2b2 Mon Sep 17 00:00:00 2001 From: Dilum Aluthge Date: Fri, 19 Mar 2021 15:17:51 -0400 Subject: [PATCH] Add the `force_latest_compatible_version` keyword argument to `Pkg.test` --- src/API.jl | 3 +- src/Operations.jl | 15 +++- src/Pkg.jl | 1 + src/force_latest_compatible_version.jl | 74 +++++++++++++++++++ test/force_latest_compatible_version.jl | 71 ++++++++++++++++++ test/{pkg-uuid.jl => pkg_uuid.jl} | 0 test/runtests.jl | 1 + .../BothOldAndNew/.gitignore | 1 + .../BothOldAndNew/Project.toml | 18 +++++ .../BothOldAndNew/src/BothOldAndNew.jl | 7 ++ .../BothOldAndNew/test/runtests.jl | 7 ++ .../NewOnly/.gitignore | 1 + .../NewOnly/Project.toml | 18 +++++ .../NewOnly/src/NewOnly.jl | 7 ++ .../NewOnly/test/runtests.jl | 7 ++ .../OldOnly1/.gitignore | 1 + .../OldOnly1/Project.toml | 18 +++++ .../OldOnly1/src/OldOnly1.jl | 7 ++ .../OldOnly1/test/runtests.jl | 7 ++ .../OldOnly2/.gitignore | 1 + .../OldOnly2/Project.toml | 18 +++++ .../OldOnly2/src/OldOnly2.jl | 7 ++ .../OldOnly2/test/runtests.jl | 7 ++ 23 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 src/force_latest_compatible_version.jl create mode 100644 test/force_latest_compatible_version.jl rename test/{pkg-uuid.jl => pkg_uuid.jl} (100%) create mode 100644 test/test_packages/force_latest_compatible_version/BothOldAndNew/.gitignore create mode 100644 test/test_packages/force_latest_compatible_version/BothOldAndNew/Project.toml create mode 100644 test/test_packages/force_latest_compatible_version/BothOldAndNew/src/BothOldAndNew.jl create mode 100644 test/test_packages/force_latest_compatible_version/BothOldAndNew/test/runtests.jl create mode 100644 test/test_packages/force_latest_compatible_version/NewOnly/.gitignore create mode 100644 test/test_packages/force_latest_compatible_version/NewOnly/Project.toml create mode 100644 test/test_packages/force_latest_compatible_version/NewOnly/src/NewOnly.jl create mode 100644 test/test_packages/force_latest_compatible_version/NewOnly/test/runtests.jl create mode 100644 test/test_packages/force_latest_compatible_version/OldOnly1/.gitignore create mode 100644 test/test_packages/force_latest_compatible_version/OldOnly1/Project.toml create mode 100644 test/test_packages/force_latest_compatible_version/OldOnly1/src/OldOnly1.jl create mode 100644 test/test_packages/force_latest_compatible_version/OldOnly1/test/runtests.jl create mode 100644 test/test_packages/force_latest_compatible_version/OldOnly2/.gitignore create mode 100644 test/test_packages/force_latest_compatible_version/OldOnly2/Project.toml create mode 100644 test/test_packages/force_latest_compatible_version/OldOnly2/src/OldOnly2.jl create mode 100644 test/test_packages/force_latest_compatible_version/OldOnly2/test/runtests.jl diff --git a/src/API.jl b/src/API.jl index d21a8d7fdb..1d3e77609d 100644 --- a/src/API.jl +++ b/src/API.jl @@ -410,6 +410,7 @@ function test(ctx::Context, pkgs::Vector{PackageSpec}; coverage=false, test_fn=nothing, julia_args::Union{Cmd, AbstractVector{<:AbstractString}}=``, test_args::Union{Cmd, AbstractVector{<:AbstractString}}=``, + force_latest_compatible_version::Bool=false, kwargs...) julia_args = Cmd(julia_args) test_args = Cmd(test_args) @@ -424,7 +425,7 @@ function test(ctx::Context, pkgs::Vector{PackageSpec}; manifest_resolve!(ctx.env.manifest, pkgs) ensure_resolved(ctx.env.manifest, pkgs) end - Operations.test(ctx, pkgs; coverage=coverage, test_fn=test_fn, julia_args=julia_args, test_args=test_args) + Operations.test(ctx, pkgs; coverage, test_fn, julia_args, test_args, force_latest_compatible_version) return end diff --git a/src/Operations.jl b/src/Operations.jl index 686a97f424..2337d7b8d3 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -1423,7 +1423,8 @@ end # ctx + pkg used to compute parent dep graph function sandbox(fn::Function, ctx::Context, target::PackageSpec, target_path::String, - sandbox_path::String, sandbox_project_override) + sandbox_path::String, sandbox_project_override; + force_latest_compatible_version::Bool=false) active_manifest = manifestfile_path(dirname(ctx.env.project_file)) sandbox_project = projectfile_path(sandbox_path) @@ -1485,6 +1486,12 @@ function sandbox(fn::Function, ctx::Context, target::PackageSpec, target_path::S end write_env(temp_ctx.env, update_undo = false) + if force_latest_compatible_version + if !_force_latest_compatible_version(temp_ctx; target_name = target.name) + pkgerror("One or more direct dependencies is not at the latest compatible version") + end + end + # Run sandboxed code path_sep = Sys.iswindows() ? ';' : ':' withenv(fn, "JULIA_LOAD_PATH" => "@$(path_sep)$(tmp)", "JULIA_PROJECT" => nothing) @@ -1566,7 +1573,7 @@ testdir(source_path::String) = joinpath(source_path, "test") testfile(source_path::String) = joinpath(testdir(source_path), "runtests.jl") function test(ctx::Context, pkgs::Vector{PackageSpec}; coverage=false, julia_args::Cmd=``, test_args::Cmd=``, - test_fn=nothing) + test_fn=nothing, force_latest_compatible_version::Bool=false) Pkg.instantiate(ctx; allow_autoprecomp = false) # do precomp later within sandbox # load manifest data @@ -1602,7 +1609,7 @@ function test(ctx::Context, pkgs::Vector{PackageSpec}; gen_target_project(ctx.env, ctx.registries, pkg, source_path, "test") # now we sandbox printpkgstyle(ctx.io, :Testing, pkg.name) - sandbox(ctx, pkg, source_path, testdir(source_path), test_project_override) do + sandbox(ctx, pkg, source_path, testdir(source_path), test_project_override; force_latest_compatible_version) do test_fn !== nothing && test_fn() sandbox_ctx = Context() status(sandbox_ctx.env; mode=PKGMODE_COMBINED, io=sandbox_ctx.io) @@ -1809,4 +1816,6 @@ function status(env::EnvCache, pkgs::Vector{PackageSpec}=PackageSpec[]; end end +include("force_latest_compatible_version.jl") + end # module diff --git a/src/Pkg.jl b/src/Pkg.jl index 39e99c5afe..75500c90f1 100644 --- a/src/Pkg.jl +++ b/src/Pkg.jl @@ -187,6 +187,7 @@ const update = API.up - `coverage::Bool=false`: enable or disable generation of coverage statistics. - `julia_args::Union{Cmd, Vector{String}}`: options to be passed the test process. - `test_args::Union{Cmd, Vector{String}}`: test arguments (`ARGS`) available in the test process. + - `force_latest_compatible_version::Bool=false`: force the latest compatible version of each direct dependency. !!! compat "Julia 1.3" `julia_args` and `test_args` requires at least Julia 1.3. diff --git a/src/force_latest_compatible_version.jl b/src/force_latest_compatible_version.jl new file mode 100644 index 0000000000..27857eb360 --- /dev/null +++ b/src/force_latest_compatible_version.jl @@ -0,0 +1,74 @@ +# const Types = Pkg.Types +# const load_direct_deps = Pkg.Operations.load_direct_deps +# const load_manifest_deps = Pkg.Operations.load_manifest_deps +# const stdlibs = Pkg.Types.stdlibs + +# activate ../Foo +# ctx = Types.Context() + +function _force_latest_compatible_version(ctx::Types.Context; target_name = nothing) + direct_deps = load_direct_deps(ctx.env) + direct_deps_uuids = getproperty.(direct_deps, Ref(:uuid)) + nonstdlib_direct_deps_uuids = filter(x -> x ∉ keys(stdlibs()), direct_deps_uuids) + return _force_latest_compatible_version(ctx, nonstdlib_direct_deps_uuids; target_name) +end + +function _force_latest_compatible_version(ctx::Types.Context, uuid_list::AbstractVector{Base.UUID}; + target_name = nothing) + isempty(uuid_list) && return true + results = _force_latest_compatible_version.(Ref(ctx), uuid_list; target_name) + return all(results) +end + +function _force_latest_compatible_version(ctx::Types.Context, uuid::Base.UUID; + target_name= nothing) + manifest_deps = load_manifest_deps(ctx.env.manifest) + manifest_deps_uuids = getproperty.(manifest_deps, Ref(:uuid)) + idx = only(findall(manifest_deps_uuids .== uuid)) + dep = manifest_deps[idx] + name = dep.name + active_version = dep.version + if haskey(ctx.env.project.compat, name) + compat_entry_string = ctx.env.project.compat[name] + latest_compatible_version = _get_latest_compatible_version(ctx, uuid, compat_entry_string) + else + if name != target_name + @warn( + "Package does not have a [compat] entry", + name, uuid, active_version, + ) + end + return true + end + result = active_version == latest_compatible_version + if !result + @error( + "Package is not at the latest compatible version", + name, uuid, active_version, + latest_compatible_version, compat_entry_string, + ) + end + return result +end + +function _get_latest_compatible_version(ctx::Types.Context, uuid::Base.UUID, + compat_entry_string::AbstractString) + all_registered_versions = _get_all_registered_versions(ctx, uuid) + compat_spec = Pkg.Types.semver_spec(compat_entry_string) + compatible_versions = filter(x -> x in compat_spec, all_registered_versions) + latest_compatible_version = maximum(compatible_versions) + return latest_compatible_version +end + +function _get_all_registered_versions(ctx::Types.Context, uuid::Base.UUID) + versions = Base.VersionNumber[] + for reg in ctx.registries + pkg = get(reg, uuid, nothing) + pkg == nothing && continue + info = Registry.registry_info(pkg) + append!(versions, keys(info.version_info)) + end + unique!(versions) + sort!(versions) + return versions +end diff --git a/test/force_latest_compatible_version.jl b/test/force_latest_compatible_version.jl new file mode 100644 index 0000000000..c4ad102477 --- /dev/null +++ b/test/force_latest_compatible_version.jl @@ -0,0 +1,71 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module ForceLatestCompatibleVersionTests + +import ..Pkg # ensure we are using the correct Pkg +import ..Utils +using Test + +@testset "force_latest_compatible_version" begin + @testset "`force_latest_compatible_version` kwarg to `Pkg.test`" begin + parent_dir = joinpath(@__DIR__, "test_packages", "force_latest_compatible_version") + + @testset "OldOnly1: `SomePkg = \"=0.1.0\"`" begin + mktempdir() do tmp_dir + test_package = joinpath(tmp_dir, "OldOnly1") + cp(joinpath(parent_dir, "OldOnly1"), test_package; force = true) + Utils.isolate() do + Pkg.activate(test_package) + Pkg.instantiate() + Pkg.build() + @test Pkg.test(; force_latest_compatible_version = false) == nothing + @test Pkg.test(; force_latest_compatible_version = true) == nothing + end + end + end + + @testset "OldOnly2: `SomePkg = \"0.1\"`" begin + mktempdir() do tmp_dir + test_package = joinpath(tmp_dir, "OldOnly2") + cp(joinpath(parent_dir, "OldOnly2"), test_package; force = true) + Utils.isolate() do + Pkg.activate(test_package) + Pkg.instantiate() + Pkg.build() + @test Pkg.test(; force_latest_compatible_version = false) == nothing + @test_throws Pkg.Types.PkgError Pkg.test(; force_latest_compatible_version = true) + end + end + end + + @testset "BothOldAndNew: `SomePkg = \"0.1, 0.2\"`" begin + mktempdir() do tmp_dir + test_package = joinpath(tmp_dir, "BothOldAndNew") + cp(joinpath(parent_dir, "BothOldAndNew"), test_package; force = true) + Utils.isolate() do + Pkg.activate(test_package) + Pkg.instantiate() + Pkg.build() + @test Pkg.test(; force_latest_compatible_version = false) == nothing + @test_throws Pkg.Types.PkgError Pkg.test(; force_latest_compatible_version = true) + end + end + end + + @testset "NewOnly: `SomePkg = \"0.2\"`" begin + mktempdir() do tmp_dir + test_package = joinpath(tmp_dir, "NewOnly") + cp(joinpath(parent_dir, "NewOnly"), test_package; force = true) + Utils.isolate() do + Pkg.activate(test_package) + Pkg.instantiate() + Pkg.build() + @test_throws Pkg.Resolve.ResolverError Pkg.test(; force_latest_compatible_version = false) + @test_throws Pkg.Resolve.ResolverError Pkg.test(; force_latest_compatible_version = true) + end + end + end + end +end + +end # module diff --git a/test/pkg-uuid.jl b/test/pkg_uuid.jl similarity index 100% rename from test/pkg-uuid.jl rename to test/pkg_uuid.jl diff --git a/test/runtests.jl b/test/runtests.jl index c8a9fe62b5..f906ffcac4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -38,6 +38,7 @@ include("platformengines.jl") include("sandbox.jl") include("resolve.jl") include("misc.jl") +include("force_latest_compatible_version.jl") # clean up locally cached registry rm(joinpath(@__DIR__, "registries"); force = true, recursive = true) diff --git a/test/test_packages/force_latest_compatible_version/BothOldAndNew/.gitignore b/test/test_packages/force_latest_compatible_version/BothOldAndNew/.gitignore new file mode 100644 index 0000000000..ba39cc531e --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/BothOldAndNew/.gitignore @@ -0,0 +1 @@ +Manifest.toml diff --git a/test/test_packages/force_latest_compatible_version/BothOldAndNew/Project.toml b/test/test_packages/force_latest_compatible_version/BothOldAndNew/Project.toml new file mode 100644 index 0000000000..a0a0bbc608 --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/BothOldAndNew/Project.toml @@ -0,0 +1,18 @@ +name = "BothOldAndNew" +uuid = "ebb7d249-15d2-4389-abff-d529bd2cf37c" +authors = ["Dilum Aluthge "] +version = "0.1.0" + +[deps] +MLJModelInterface = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" + +[compat] +MLJModelInterface = "0.1, 0.2" +ScientificTypes = "0.6" + +[extras] +ScientificTypes = "321657f4-b219-11e9-178b-2701a2544e81" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["ScientificTypes", "Test"] diff --git a/test/test_packages/force_latest_compatible_version/BothOldAndNew/src/BothOldAndNew.jl b/test/test_packages/force_latest_compatible_version/BothOldAndNew/src/BothOldAndNew.jl new file mode 100644 index 0000000000..bcae2a4c44 --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/BothOldAndNew/src/BothOldAndNew.jl @@ -0,0 +1,7 @@ +module BothOldAndNew + +import MLJModelInterface + +f(x) = x + x + +end # module diff --git a/test/test_packages/force_latest_compatible_version/BothOldAndNew/test/runtests.jl b/test/test_packages/force_latest_compatible_version/BothOldAndNew/test/runtests.jl new file mode 100644 index 0000000000..a8d00c9bfe --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/BothOldAndNew/test/runtests.jl @@ -0,0 +1,7 @@ +import BothOldAndNew +import ScientificTypes +import Test + +Test.@testset "BothOldAndNew.jl" begin + Test.@test BothOldAndNew.f(1) == 2 +end diff --git a/test/test_packages/force_latest_compatible_version/NewOnly/.gitignore b/test/test_packages/force_latest_compatible_version/NewOnly/.gitignore new file mode 100644 index 0000000000..ba39cc531e --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/NewOnly/.gitignore @@ -0,0 +1 @@ +Manifest.toml diff --git a/test/test_packages/force_latest_compatible_version/NewOnly/Project.toml b/test/test_packages/force_latest_compatible_version/NewOnly/Project.toml new file mode 100644 index 0000000000..6e528e0d7e --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/NewOnly/Project.toml @@ -0,0 +1,18 @@ +name = "NewOnly" +uuid = "2c7b3c3b-62b6-437a-a36e-3d5963db0b1f" +authors = ["Dilum Aluthge "] +version = "0.1.0" + +[deps] +MLJModelInterface = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" + +[compat] +MLJModelInterface = "0.2" +ScientificTypes = "0.6" + +[extras] +ScientificTypes = "321657f4-b219-11e9-178b-2701a2544e81" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["ScientificTypes", "Test"] diff --git a/test/test_packages/force_latest_compatible_version/NewOnly/src/NewOnly.jl b/test/test_packages/force_latest_compatible_version/NewOnly/src/NewOnly.jl new file mode 100644 index 0000000000..71c401e9f7 --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/NewOnly/src/NewOnly.jl @@ -0,0 +1,7 @@ +module NewOnly + +import MLJModelInterface + +f(x) = x + x + +end # module diff --git a/test/test_packages/force_latest_compatible_version/NewOnly/test/runtests.jl b/test/test_packages/force_latest_compatible_version/NewOnly/test/runtests.jl new file mode 100644 index 0000000000..7d9f5a52f5 --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/NewOnly/test/runtests.jl @@ -0,0 +1,7 @@ +import NewOnly +import ScientificTypes +import Test + +Test.@testset "NewOnly.jl" begin + Test.@test NewOnly.f(1) == 2 +end diff --git a/test/test_packages/force_latest_compatible_version/OldOnly1/.gitignore b/test/test_packages/force_latest_compatible_version/OldOnly1/.gitignore new file mode 100644 index 0000000000..ba39cc531e --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/OldOnly1/.gitignore @@ -0,0 +1 @@ +Manifest.toml diff --git a/test/test_packages/force_latest_compatible_version/OldOnly1/Project.toml b/test/test_packages/force_latest_compatible_version/OldOnly1/Project.toml new file mode 100644 index 0000000000..af6345da53 --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/OldOnly1/Project.toml @@ -0,0 +1,18 @@ +name = "OldOnly1" +uuid = "abc047b9-6ad4-4533-a374-d50ae908e3e9" +authors = ["Dilum Aluthge "] +version = "0.1.0" + +[deps] +MLJModelInterface = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" + +[compat] +MLJModelInterface = "=0.1.0" +ScientificTypes = "0.6" + +[extras] +ScientificTypes = "321657f4-b219-11e9-178b-2701a2544e81" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["ScientificTypes", "Test"] diff --git a/test/test_packages/force_latest_compatible_version/OldOnly1/src/OldOnly1.jl b/test/test_packages/force_latest_compatible_version/OldOnly1/src/OldOnly1.jl new file mode 100644 index 0000000000..a06d8ad934 --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/OldOnly1/src/OldOnly1.jl @@ -0,0 +1,7 @@ +module OldOnly1 + +import MLJModelInterface + +f(x) = x + x + +end # module diff --git a/test/test_packages/force_latest_compatible_version/OldOnly1/test/runtests.jl b/test/test_packages/force_latest_compatible_version/OldOnly1/test/runtests.jl new file mode 100644 index 0000000000..cb390427a1 --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/OldOnly1/test/runtests.jl @@ -0,0 +1,7 @@ +import OldOnly1 +import ScientificTypes +import Test + +Test.@testset "OldOnly1.jl" begin + Test.@test OldOnly1.f(1) == 2 +end diff --git a/test/test_packages/force_latest_compatible_version/OldOnly2/.gitignore b/test/test_packages/force_latest_compatible_version/OldOnly2/.gitignore new file mode 100644 index 0000000000..ba39cc531e --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/OldOnly2/.gitignore @@ -0,0 +1 @@ +Manifest.toml diff --git a/test/test_packages/force_latest_compatible_version/OldOnly2/Project.toml b/test/test_packages/force_latest_compatible_version/OldOnly2/Project.toml new file mode 100644 index 0000000000..235e583d88 --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/OldOnly2/Project.toml @@ -0,0 +1,18 @@ +name = "OldOnly2" +uuid = "abc047b9-6ad4-4533-a374-d50ae908e3e9" +authors = ["Dilum Aluthge "] +version = "0.1.0" + +[deps] +MLJModelInterface = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" + +[compat] +MLJModelInterface = "0.1" +ScientificTypes = "0.6" + +[extras] +ScientificTypes = "321657f4-b219-11e9-178b-2701a2544e81" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["ScientificTypes", "Test"] diff --git a/test/test_packages/force_latest_compatible_version/OldOnly2/src/OldOnly2.jl b/test/test_packages/force_latest_compatible_version/OldOnly2/src/OldOnly2.jl new file mode 100644 index 0000000000..dbe3efa7fc --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/OldOnly2/src/OldOnly2.jl @@ -0,0 +1,7 @@ +module OldOnly2 + +import MLJModelInterface + +f(x) = x + x + +end # module diff --git a/test/test_packages/force_latest_compatible_version/OldOnly2/test/runtests.jl b/test/test_packages/force_latest_compatible_version/OldOnly2/test/runtests.jl new file mode 100644 index 0000000000..e5cdfc46fa --- /dev/null +++ b/test/test_packages/force_latest_compatible_version/OldOnly2/test/runtests.jl @@ -0,0 +1,7 @@ +import OldOnly2 +import ScientificTypes +import Test + +Test.@testset "OldOnly2.jl" begin + Test.@test OldOnly2.f(1) == 2 +end