diff options
| author | ras0219 <533828+ras0219@users.noreply.github.com> | 2020-11-27 19:05:47 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-11-27 19:05:47 -0800 |
| commit | 896498fdbae91d5f97bfffcadef21b066277fcf2 (patch) | |
| tree | 7baa83b49db5c30f1dee5b0db7b97f1dedf7083d /toolsrc/src/vcpkg-test/dependencies.cpp | |
| parent | 6c9cda1635859571de5c964bbacdece824045305 (diff) | |
| download | vcpkg-896498fdbae91d5f97bfffcadef21b066277fcf2.tar.gz vcpkg-896498fdbae91d5f97bfffcadef21b066277fcf2.zip | |
[vcpkg] Introduce `create_versioned_install_plan()` (#14633)
* [vcpkg] Implement constraints in manifests
* [vcpkg] Add SourceControlFile::check_against_feature_flags to prevent accidentally ignoring versioning fields
* [vcpkg] Switch check_against_feature_flags to accept fs::path
* [vcpkg] Implement overrides parsing in manifests
* [vcpkg] Address CR comments
* [vcpkg] Initial implementation of create_versioned_install_plan()
* [vcpkg] Implement port-version minimums
* [vcpkg] Implement relaxation phase
* [vcpkg] Refactor tests to use check_name_and_version
* [vcpkg] Implemented simple relaxed scheme
* [vcpkg] More relaxed scheme tests
* [vcpkg] Mixed scheme testing
* [vcpkg] Support versions and features without defaults
* [vcpkg] Support versions and features without defaults 2
* [vcpkg] Only consider greater of toplevel and baseilne
* [vcpkg] Implement overrides
* [vcpkg] Install defaults
* [vcpkg] Handle defaults of transitive packages
* [vcpkg] Fix warnings for Span of initializer_list
* [vcpkg] Use CMakeVarProvider during versioned install
* [vcpkg] Handle inter-feature dependencies
* [vcpkg] Correctly handle qualified Dependencies at toplevel
* [vcpkg] Address CR comments
Co-authored-by: Robert Schumacher <roschuma@microsoft.com>
Diffstat (limited to 'toolsrc/src/vcpkg-test/dependencies.cpp')
| -rw-r--r-- | toolsrc/src/vcpkg-test/dependencies.cpp | 1150 |
1 files changed, 1047 insertions, 103 deletions
diff --git a/toolsrc/src/vcpkg-test/dependencies.cpp b/toolsrc/src/vcpkg-test/dependencies.cpp index 2f82d21b4..bcc2f14a0 100644 --- a/toolsrc/src/vcpkg-test/dependencies.cpp +++ b/toolsrc/src/vcpkg-test/dependencies.cpp @@ -1,152 +1,1096 @@ #include <catch2/catch.hpp> +#include <vcpkg/base/graphs.h> + #include <vcpkg/dependencies.h> -#include <vcpkg/paragraphparser.h> #include <vcpkg/portfileprovider.h> #include <vcpkg/sourceparagraph.h> +#include <vcpkg/triplet.h> + +#include <memory> +#include <unordered_map> +#include <vector> #include <vcpkg-test/mockcmakevarprovider.h> #include <vcpkg-test/util.h> using namespace vcpkg; -using namespace vcpkg::Parse; -TEST_CASE ("parse depends", "[dependencies]") +using Test::make_control_file; +using Test::make_status_feature_pgh; +using Test::make_status_pgh; +using Test::MockCMakeVarProvider; +using Test::PackageSpecMap; + +struct MockBaselineProvider : PortFileProvider::IBaselineProvider +{ + mutable std::map<std::string, Versions::Version, std::less<>> v; + + Optional<Versions::Version> get_baseline_version(StringView name) const override + { + auto it = v.find(name); + if (it == v.end()) return nullopt; + return it->second; + } +}; + +struct MockVersionedPortfileProvider : PortFileProvider::IVersionedPortfileProvider +{ + mutable std::map<std::string, std::map<Versions::Version, SourceControlFileLocation, VersionTMapLess>> v; + + ExpectedS<const SourceControlFileLocation&> get_control_file( + const vcpkg::Versions::VersionSpec& versionspec) const override + { + return get_control_file(versionspec.port_name, versionspec.version); + } + + ExpectedS<const SourceControlFileLocation&> get_control_file(const std::string& name, + const vcpkg::Versions::Version& version) const + { + auto it = v.find(name); + if (it == v.end()) return std::string("Unknown port name"); + auto it2 = it->second.find(version); + if (it2 == it->second.end()) return std::string("Unknown port version"); + return it2->second; + } + + virtual const std::vector<vcpkg::Versions::VersionSpec>& get_port_versions(StringView) const override + { + Checks::unreachable(VCPKG_LINE_INFO); + } + + SourceControlFileLocation& emplace(std::string&& name, + Versions::Version&& version, + Versions::Scheme scheme = Versions::Scheme::String) + { + auto it = v.find(name); + if (it == v.end()) + it = v.emplace(name, std::map<Versions::Version, SourceControlFileLocation, VersionTMapLess>{}).first; + + auto it2 = it->second.find(version); + if (it2 == it->second.end()) + { + auto scf = std::make_unique<SourceControlFile>(); + auto core = std::make_unique<SourceParagraph>(); + core->name = name; + core->version = version.text(); + core->port_version = version.port_version(); + core->version_scheme = scheme; + scf->core_paragraph = std::move(core); + it2 = it->second.emplace(version, SourceControlFileLocation{std::move(scf), fs::u8path(name)}).first; + } + return it2->second; + } +}; + +using Versions::Constraint; +using Versions::Scheme; + +template<class T> +T unwrap(ExpectedS<T> e) +{ + if (!e.has_value()) + { + INFO(e.error()); + REQUIRE(false); + } + return std::move(*e.get()); +} + +static void check_name_and_version(const Dependencies::InstallPlanAction& ipa, + StringLiteral name, + Versions::Version v, + std::initializer_list<StringLiteral> features = {}) +{ + CHECK(ipa.spec.name() == name); + CHECK(ipa.source_control_file_location.has_value()); + CHECK(ipa.feature_list.size() == features.size() + 1); + { + INFO("ipa.feature_list = [" << Strings::join(", ", ipa.feature_list) << "]"); + for (auto&& f : features) + { + INFO("f = \"" << f.c_str() << "\""); + CHECK(Util::find(ipa.feature_list, f) != ipa.feature_list.end()); + } + CHECK(Util::find(ipa.feature_list, "core") != ipa.feature_list.end()); + } + if (auto scfl = ipa.source_control_file_location.get()) + { + CHECK(scfl->source_control_file->core_paragraph->version == v.text()); + CHECK(scfl->source_control_file->core_paragraph->port_version == v.port_version()); + } +} + +static const PackageSpec& toplevel_spec() +{ + static const PackageSpec ret("toplevel-spec", Test::X86_WINDOWS); + return ret; +} + +TEST_CASE ("basic version install single", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"1", 0}); + + MockCMakeVarProvider var_provider; + + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"a"}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + REQUIRE(install_plan.install_actions.at(0).spec.name() == "a"); +} + +TEST_CASE ("basic version install detect cycle", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["b"] = {"1", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"1", 0}).source_control_file->core_paragraph->dependencies = { + Dependency{"b", {}, {}, DependencyConstraint{}}, + }; + vp.emplace("b", {"1", 0}).source_control_file->core_paragraph->dependencies = { + Dependency{"a", {}, {}, DependencyConstraint{}}, + }; + + MockCMakeVarProvider var_provider; + + auto install_plan = Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"a"}}, {}, toplevel_spec()); + + REQUIRE(!install_plan.has_value()); +} + +TEST_CASE ("basic version install scheme", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["b"] = {"1", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"1", 0}).source_control_file->core_paragraph->dependencies = { + Dependency{"b", {}, {}, DependencyConstraint{}}, + }; + vp.emplace("b", {"1", 0}); + + MockCMakeVarProvider var_provider; + + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"a"}}, {}, toplevel_spec())); + + CHECK(install_plan.size() == 2); + + StringLiteral names[] = {"b", "a"}; + for (size_t i = 0; i < install_plan.install_actions.size() && i < 2; ++i) + { + CHECK(install_plan.install_actions[i].spec.name() == names[i]); + } +} + +TEST_CASE ("basic version install scheme diamond", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["b"] = {"1", 0}; + bp.v["c"] = {"1", 0}; + bp.v["d"] = {"1", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"1", 0}).source_control_file->core_paragraph->dependencies = { + Dependency{"b", {}, {}, DependencyConstraint{}}, + Dependency{"c", {}, {}, DependencyConstraint{}}, + }; + vp.emplace("b", {"1", 0}).source_control_file->core_paragraph->dependencies = { + Dependency{"c", {}, {}, DependencyConstraint{}}, + Dependency{"d", {}, {}, DependencyConstraint{}}, + }; + vp.emplace("c", {"1", 0}).source_control_file->core_paragraph->dependencies = { + Dependency{"d", {}, {}, DependencyConstraint{}}, + }; + vp.emplace("d", {"1", 0}); + + MockCMakeVarProvider var_provider; + + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"a"}}, {}, toplevel_spec())); + + CHECK(install_plan.size() == 4); + + StringLiteral names[] = {"d", "c", "b", "a"}; + for (size_t i = 0; i < install_plan.install_actions.size() && i < 4; ++i) + { + CHECK(install_plan.install_actions[i].spec.name() == names[i]); + } +} + +TEST_CASE ("basic version install scheme baseline missing", "[versionplan]") +{ + MockBaselineProvider bp; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"1", 0}); + + MockCMakeVarProvider var_provider; + + auto install_plan = Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"a"}}, {}, toplevel_spec()); + + REQUIRE(!install_plan.has_value()); +} + +TEST_CASE ("basic version install scheme baseline missing success", "[versionplan]") { - auto w = parse_dependencies_list("liba (windows)"); - REQUIRE(w); - auto& v = *w.get(); - REQUIRE(v.size() == 1); - REQUIRE(v.at(0).name == "liba"); - REQUIRE(v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}})); - REQUIRE(v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}})); - REQUIRE(!v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}})); + MockBaselineProvider bp; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"1", 0}); + vp.emplace("a", {"2", 0}); + vp.emplace("a", {"3", 0}); + + MockCMakeVarProvider var_provider; + + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Exact, "2"}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"2", 0}); } -TEST_CASE ("filter depends", "[dependencies]") +TEST_CASE ("basic version install scheme baseline", "[versionplan]") { - const std::unordered_map<std::string, std::string> x64_win_cmake_vars{{"VCPKG_TARGET_ARCHITECTURE", "x64"}, - {"VCPKG_CMAKE_SYSTEM_NAME", ""}}; + MockBaselineProvider bp; + bp.v["a"] = {"2", 0}; - const std::unordered_map<std::string, std::string> arm_uwp_cmake_vars{{"VCPKG_TARGET_ARCHITECTURE", "arm"}, - {"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}}; + MockVersionedPortfileProvider vp; + vp.emplace("a", {"1", 0}); + vp.emplace("a", {"2", 0}); + vp.emplace("a", {"3", 0}); + + MockCMakeVarProvider var_provider; - auto deps_ = parse_dependencies_list("liba (!uwp), libb, libc (uwp)"); - REQUIRE(deps_); - auto& deps = *deps_.get(); - auto v = filter_dependencies(deps, Test::X64_WINDOWS, x64_win_cmake_vars); - REQUIRE(v.size() == 2); - REQUIRE(v.at(0).package_spec.name() == "liba"); - REQUIRE(v.at(1).package_spec.name() == "libb"); + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"a"}}, {}, toplevel_spec())); - auto v2 = filter_dependencies(deps, Test::ARM_UWP, arm_uwp_cmake_vars); - REQUIRE(v.size() == 2); - REQUIRE(v2.at(0).package_spec.name() == "libb"); - REQUIRE(v2.at(1).package_spec.name() == "libc"); + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"2", 0}); } -TEST_CASE ("parse feature depends", "[dependencies]") +TEST_CASE ("version string baseline agree", "[versionplan]") { - auto u_ = parse_dependencies_list("libwebp[anim, gif2webp, img2webp, info, mux, nearlossless, " - "simd, cwebp, dwebp], libwebp[vwebp-sdl, extras] (!osx)"); - REQUIRE(u_); - auto& v = *u_.get(); - REQUIRE(v.size() == 2); - auto&& a0 = v.at(0); - REQUIRE(a0.name == "libwebp"); - REQUIRE(a0.features.size() == 9); - REQUIRE(a0.platform.is_empty()); + MockBaselineProvider bp; + bp.v["a"] = {"2", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"1", 0}); + vp.emplace("a", {"2", 0}); + vp.emplace("a", {"3", 0}); + + MockCMakeVarProvider var_provider; + + auto install_plan = Dependencies::create_versioned_install_plan( + vp, bp, var_provider, {Dependency{"a", {}, {}, {Constraint::Type::Exact, "2"}}}, {}, toplevel_spec()); + + REQUIRE(install_plan.has_value()); +} + +TEST_CASE ("version install scheme baseline conflict", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"2", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"1", 0}); + vp.emplace("a", {"2", 0}); + vp.emplace("a", {"3", 0}); + + MockCMakeVarProvider var_provider; + + auto install_plan = + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Exact, "3"}}, + }, + {}, + toplevel_spec()); + + REQUIRE(!install_plan.has_value()); +} + +TEST_CASE ("version install string port version", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"2", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"2", 0}); + vp.emplace("a", {"2", 1}); + vp.emplace("a", {"2", 2}); + + MockCMakeVarProvider var_provider; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 1}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"2", 1}); +} + +TEST_CASE ("version install string port version 2", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"2", 1}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"2", 0}); + vp.emplace("a", {"2", 1}); + vp.emplace("a", {"2", 2}); + + MockCMakeVarProvider var_provider; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 0}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"2", 1}); +} + +TEST_CASE ("version install transitive string", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"2", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"2", 0}).source_control_file->core_paragraph->dependencies = { + Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Exact, "1"}}, + }; + vp.emplace("a", {"2", 1}).source_control_file->core_paragraph->dependencies = { + Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Exact, "2"}}, + }; + vp.emplace("b", {"1", 0}); + vp.emplace("b", {"2", 0}); + + MockCMakeVarProvider var_provider; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 1}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "b", {"2", 0}); + check_name_and_version(install_plan.install_actions[1], "a", {"2", 1}); +} + +TEST_CASE ("version install simple relaxed", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"2", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"2", 0}, Scheme::Relaxed); + vp.emplace("a", {"3", 0}, Scheme::Relaxed); + + MockCMakeVarProvider var_provider; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "3", 0}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"3", 0}); +} + +TEST_CASE ("version install transitive relaxed", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"2", 0}; + bp.v["b"] = {"2", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"2", 0}, Scheme::Relaxed); + vp.emplace("a", {"3", 0}, Scheme::Relaxed).source_control_file->core_paragraph->dependencies = { + Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Minimum, "3"}}, + }; + vp.emplace("b", {"2", 0}, Scheme::Relaxed); + vp.emplace("b", {"3", 0}, Scheme::Relaxed); + + MockCMakeVarProvider var_provider; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "3", 0}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "b", {"3", 0}); + check_name_and_version(install_plan.install_actions[1], "a", {"3", 0}); +} + +TEST_CASE ("version install diamond relaxed", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"2", 0}; + bp.v["b"] = {"3", 0}; + + MockVersionedPortfileProvider vp; + vp.emplace("a", {"2", 0}, Scheme::Relaxed); + vp.emplace("a", {"3", 0}, Scheme::Relaxed).source_control_file->core_paragraph->dependencies = { + Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Minimum, "2", 1}}, + Dependency{"c", {}, {}, DependencyConstraint{Constraint::Type::Minimum, "5", 1}}, + }; + vp.emplace("b", {"2", 1}, Scheme::Relaxed); + vp.emplace("b", {"3", 0}, Scheme::Relaxed).source_control_file->core_paragraph->dependencies = { + Dependency{"c", {}, {}, DependencyConstraint{Constraint::Type::Minimum, "9", 2}}, + }; + vp.emplace("c", {"5", 1}, Scheme::Relaxed); + vp.emplace("c", {"9", 2}, Scheme::Relaxed); + + MockCMakeVarProvider var_provider; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Minimum, "3", 0}}, + Dependency{"b", {}, {}, {Constraint::Type::Minimum, "2", 1}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 3); + check_name_and_version(install_plan.install_actions[0], "c", {"9", 2}); + check_name_and_version(install_plan.install_actions[1], "b", {"3", 0}); + check_name_and_version(install_plan.install_actions[2], "a", {"3", 0}); +} + +TEST_CASE ("version install scheme change in port version", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + vp.emplace("a", {"2", 0}).source_control_file->core_paragraph->dependencies = { + Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Exact, "1"}}, + }; + vp.emplace("a", {"2", 1}).source_control_file->core_paragraph->dependencies = { + Dependency{"b", {}, {}, DependencyConstraint{Constraint::Type::Minimum, "1", 1}}, + }; + vp.emplace("b", {"1", 0}, Scheme::String); + vp.emplace("b", {"1", 1}, Scheme::Relaxed); + + MockCMakeVarProvider var_provider; + + SECTION ("lower baseline") + { + MockBaselineProvider bp; + bp.v["a"] = {"2", 0}; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 1}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "b", {"1", 1}); + check_name_and_version(install_plan.install_actions[1], "a", {"2", 1}); + } + SECTION ("higher baseline") + { + MockBaselineProvider bp; + bp.v["a"] = {"2", 1}; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {}, {}, {Constraint::Type::Exact, "2", 0}}, + }, + {}, + toplevel_spec())); - auto&& a1 = v.at(1); - REQUIRE(a1.name == "libwebp"); - REQUIRE(a1.features.size() == 2); - REQUIRE(!a1.platform.is_empty()); - REQUIRE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}})); - REQUIRE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}})); - REQUIRE_FALSE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}})); + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "b", {"1", 1}); + check_name_and_version(install_plan.install_actions[1], "a", {"2", 1}); + } } -TEST_CASE ("qualified dependency", "[dependencies]") +TEST_CASE ("version install simple feature", "[versionplan]") { - using namespace Test; - PackageSpecMap spec_map; - auto spec_a = FullPackageSpec{spec_map.emplace("a", "b, b[b1] (linux)"), {}}; - auto spec_b = FullPackageSpec{spec_map.emplace("b", "", {{"b1", ""}}), {}}; + MockVersionedPortfileProvider vp; + auto a_x = std::make_unique<FeatureParagraph>(); + a_x->name = "x"; + vp.emplace("a", {"1", 0}, Scheme::Relaxed).source_control_file->feature_paragraphs.push_back(std::move(a_x)); - PortFileProvider::MapPortFileProvider map_port{spec_map.map}; MockCMakeVarProvider var_provider; - auto plan = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {spec_a}, {}); - REQUIRE(plan.install_actions.size() == 2); - REQUIRE(plan.install_actions.at(0).feature_list == std::vector<std::string>{"core"}); + SECTION ("with baseline") + { + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + + auto install_plan = unwrap(Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {"x"}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x"}); + } + + SECTION ("without baseline") + { + MockBaselineProvider bp; + + auto install_plan = unwrap(Dependencies::create_versioned_install_plan( + vp, + bp, + var_provider, + { + Dependency{"a", {"x"}, {}, {Constraint::Type::Minimum, "1", 0}}, + }, + {}, + toplevel_spec())); - FullPackageSpec linspec_a{{"a", Triplet::from_canonical_name("x64-linux")}, {}}; - var_provider.dep_info_vars[linspec_a.package_spec].emplace("VCPKG_CMAKE_SYSTEM_NAME", "Linux"); - auto plan2 = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {linspec_a}, {}); - REQUIRE(plan2.install_actions.size() == 2); - REQUIRE(plan2.install_actions.at(0).feature_list == std::vector<std::string>{"b1", "core"}); + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x"}); + } } -TEST_CASE ("resolve_deps_as_top_level", "[dependencies]") +static std::unique_ptr<FeatureParagraph> make_fpgh(std::string name) { - using namespace Test; - PackageSpecMap spec_map; - FullPackageSpec spec_a{spec_map.emplace("a", "b, b[b1] (linux)"), {}}; - FullPackageSpec spec_b{spec_map.emplace("b", "", {{"b1", ""}}), {}}; - FullPackageSpec spec_c{spec_map.emplace("c", "b", {{"c1", "b[b1]"}, {"c2", "c[c1], a"}}, {"c1"}), {"core"}}; - FullPackageSpec spec_d{spec_map.emplace("d", "c[core]"), {}}; + auto f = std::make_unique<FeatureParagraph>(); + f->name = std::move(name); + return f; +} + +TEST_CASE ("version install transitive features", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + auto a_x = make_fpgh("x"); + a_x->dependencies.push_back(Dependency{"b", {"y"}}); + vp.emplace("a", {"1", 0}, Scheme::Relaxed).source_control_file->feature_paragraphs.push_back(std::move(a_x)); + + auto b_y = make_fpgh("y"); + vp.emplace("b", {"1", 0}, Scheme::Relaxed).source_control_file->feature_paragraphs.push_back(std::move(b_y)); - PortFileProvider::MapPortFileProvider map_port{spec_map.map}; MockCMakeVarProvider var_provider; - Triplet t_linux = Triplet::from_canonical_name("x64-linux"); - var_provider.dep_info_vars[{"a", t_linux}].emplace("VCPKG_CMAKE_SYSTEM_NAME", "Linux"); + + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["b"] = {"1", 0}; + + auto install_plan = unwrap(Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {"x"}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "b", {"1", 0}, {"y"}); + check_name_and_version(install_plan.install_actions[1], "a", {"1", 0}, {"x"}); +} + +TEST_CASE ("version install transitive feature versioned", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + auto a_x = make_fpgh("x"); + a_x->dependencies.push_back(Dependency{"b", {"y"}, {}, {Constraint::Type::Minimum, "2", 0}}); + vp.emplace("a", {"1", 0}, Scheme::Relaxed).source_control_file->feature_paragraphs.push_back(std::move(a_x)); + { - auto deps = vcpkg::Dependencies::resolve_deps_as_top_level( - *spec_map.map.at("a").source_control_file, Test::X86_WINDOWS, {}, var_provider); - REQUIRE(deps.size() == 1); - REQUIRE(deps.at(0) == spec_b); + auto b_y = make_fpgh("y"); + vp.emplace("b", {"1", 0}, Scheme::Relaxed).source_control_file->feature_paragraphs.push_back(std::move(b_y)); } { - auto deps = vcpkg::Dependencies::resolve_deps_as_top_level( - *spec_map.map.at("a").source_control_file, t_linux, {}, var_provider); - REQUIRE(deps.size() == 1); - REQUIRE(deps.at(0) == FullPackageSpec({"b", t_linux}, {"b1"})); + auto b_y = make_fpgh("y"); + b_y->dependencies.push_back(Dependency{"c"}); + vp.emplace("b", {"2", 0}, Scheme::Relaxed).source_control_file->feature_paragraphs.push_back(std::move(b_y)); } + + vp.emplace("c", {"1", 0}, Scheme::Relaxed); + + MockCMakeVarProvider var_provider; + + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["c"] = {"1", 0}; + + auto install_plan = unwrap(Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"a", {"x"}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 3); + check_name_and_version(install_plan.install_actions[0], "c", {"1", 0}); + check_name_and_version(install_plan.install_actions[1], "b", {"2", 0}, {"y"}); + check_name_and_version(install_plan.install_actions[2], "a", {"1", 0}, {"x"}); +} + +TEST_CASE ("version install constraint-reduction", "[versionplan]") +{ + MockCMakeVarProvider var_provider; + + SECTION ("higher baseline") { - // without defaults - auto deps = vcpkg::Dependencies::resolve_deps_as_top_level( - *spec_map.map.at("c").source_control_file, Test::X86_WINDOWS, {}, var_provider); - REQUIRE(deps.size() == 1); - REQUIRE(deps.at(0) == spec_b); + MockVersionedPortfileProvider vp; + + vp.emplace("b", {"1", 0}, Scheme::Relaxed).source_control_file->core_paragraph->dependencies = { + Dependency{"c", {}, {}, {Constraint::Type::Minimum, "2"}}, + }; + vp.emplace("b", {"2", 0}, Scheme::Relaxed).source_control_file->core_paragraph->dependencies = { + Dependency{"c", {}, {}, {Constraint::Type::Minimum, "1"}}, + }; + + vp.emplace("c", {"1", 0}, Scheme::Relaxed); + // c@2 is used to detect if certain constraints were evaluated + vp.emplace("c", {"2", 0}, Scheme::Relaxed); + + MockBaselineProvider bp; + bp.v["b"] = {"2", 0}; + bp.v["c"] = {"1", 0}; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"b", {}, {}, {Constraint::Type::Minimum, "1"}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "c", {"1", 0}); + check_name_and_version(install_plan.install_actions[1], "b", {"2", 0}); } - FullPackageSpec spec_b_with_b1{spec_b.package_spec, {"b1"}}; + + SECTION ("higher toplevel") + { + MockVersionedPortfileProvider vp; + + vp.emplace("b", {"1", 0}, Scheme::Relaxed).source_control_file->core_paragraph->dependencies = { + Dependency{"c", {}, {}, {Constraint::Type::Minimum, "2"}}, + }; + vp.emplace("b", {"2", 0}, Scheme::Relaxed).source_control_file->core_paragraph->dependencies = { + Dependency{"c", {}, {}, {Constraint::Type::Minimum, "1"}}, + }; + + vp.emplace("c", {"1", 0}, Scheme::Relaxed); + // c@2 is used to detect if certain constraints were evaluated + vp.emplace("c", {"2", 0}, Scheme::Relaxed); + + MockBaselineProvider bp; + bp.v["b"] = {"1", 0}; + bp.v["c"] = {"1", 0}; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + { + Dependency{"b", {}, {}, {Constraint::Type::Minimum, "2"}}, + }, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "c", {"1", 0}); + check_name_and_version(install_plan.install_actions[1], "b", {"2", 0}); + } +} + +TEST_CASE ("version install overrides", "[versionplan]") +{ + MockCMakeVarProvider var_provider; + + MockVersionedPortfileProvider vp; + + vp.emplace("b", {"1", 0}, Scheme::Relaxed); + vp.emplace("b", {"2", 0}, Scheme::Relaxed); + vp.emplace("c", {"1", 0}, Scheme::String); + vp.emplace("c", {"2", 0}, Scheme::String); + + MockBaselineProvider bp; + bp.v["b"] = {"2", 0}; + bp.v["c"] = {"2", 0}; + + SECTION ("string") { - // with defaults of c (c1) - auto deps = vcpkg::Dependencies::resolve_deps_as_top_level( - *spec_map.map.at("c").source_control_file, Test::X86_WINDOWS, {"default"}, var_provider); - REQUIRE(deps.size() == 1); - REQUIRE(deps.at(0) == spec_b_with_b1); + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + {Dependency{"c"}}, + {DependencyOverride{"b", "1"}, DependencyOverride{"c", "1"}}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "c", {"1", 0}); } + + SECTION ("relaxed") { - // with c1 - auto deps = vcpkg::Dependencies::resolve_deps_as_top_level( - *spec_map.map.at("c").source_control_file, Test::X86_WINDOWS, {"c1"}, var_provider); - REQUIRE(deps.size() == 1); - REQUIRE(deps.at(0) == spec_b_with_b1); + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + {Dependency{"b"}}, + {DependencyOverride{"b", "1"}, DependencyOverride{"c", "1"}}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "b", {"1", 0}); } +} + +TEST_CASE ("version install transitive overrides", "[versionplan]") +{ + MockCMakeVarProvider var_provider; + + MockVersionedPortfileProvider vp; + + vp.emplace("b", {"1", 0}, Scheme::Relaxed) + .source_control_file->core_paragraph->dependencies.push_back({"c", {}, {}, {Constraint::Type::Exact, "2", 1}}); + vp.emplace("b", {"2", 0}, Scheme::Relaxed); + vp.emplace("c", {"1", 0}, Scheme::String); + vp.emplace("c", {"2", 1}, Scheme::String); + + MockBaselineProvider bp; + bp.v["b"] = {"2", 0}; + bp.v["c"] = {"2", 1}; + + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, + bp, + var_provider, + {Dependency{"b"}}, + {DependencyOverride{"b", "1"}, DependencyOverride{"c", "1"}}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "c", {"1", 0}); + check_name_and_version(install_plan.install_actions[1], "b", {"1", 0}); +} + +TEST_CASE ("version install default features", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + auto a_x = make_fpgh("x"); + auto& a_scf = vp.emplace("a", {"1", 0}, Scheme::Relaxed).source_control_file; + a_scf->core_paragraph->default_features.push_back("x"); + a_scf->feature_paragraphs.push_back(std::move(a_x)); + + MockCMakeVarProvider var_provider; + + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, bp, var_provider, {Dependency{"a"}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x"}); +} + +TEST_CASE ("version dont install default features", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + auto a_x = make_fpgh("x"); + auto& a_scf = vp.emplace("a", {"1", 0}, Scheme::Relaxed).source_control_file; + a_scf->core_paragraph->default_features.push_back("x"); + a_scf->feature_paragraphs.push_back(std::move(a_x)); + + MockCMakeVarProvider var_provider; + + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + + auto install_plan = unwrap(Dependencies::create_versioned_install_plan( + vp, bp, var_provider, {Dependency{"a", {"core"}}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}); +} + +TEST_CASE ("version install transitive default features", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + auto a_x = make_fpgh("x"); + auto& a_scf = vp.emplace("a", {"1", 0}, Scheme::Relaxed).source_control_file; + a_scf->core_paragraph->default_features.push_back("x"); + a_scf->feature_paragraphs.push_back(std::move(a_x)); + + auto& b_scf = vp.emplace("b", {"1", 0}, Scheme::Relaxed).source_control_file; + b_scf->core_paragraph->dependencies.push_back({"a", {"core"}}); + + MockCMakeVarProvider var_provider; + + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["b"] = {"1", 0}; + + auto install_plan = unwrap( + Dependencies::create_versioned_install_plan(vp, bp, var_provider, {Dependency{"b"}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x"}); + check_name_and_version(install_plan.install_actions[1], "b", {"1", 0}); +} + +static PlatformExpression::Expr parse_platform(StringView l) +{ + return unwrap(PlatformExpression::parse_platform_expression(l, PlatformExpression::MultipleBinaryOperators::Deny)); +} + +TEST_CASE ("version install qualified dependencies", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + vp.emplace("b", {"1", 0}, Scheme::Relaxed); + vp.emplace("c", {"1", 0}, Scheme::Relaxed); + + MockBaselineProvider bp; + bp.v["b"] = {"1", 0}; + bp.v["c"] = {"1", 0}; + + SECTION ("windows") { - // with c2 implying c1 - auto deps = vcpkg::Dependencies::resolve_deps_as_top_level( - *spec_map.map.at("c").source_control_file, Test::X86_WINDOWS, {"c2"}, var_provider); - REQUIRE(deps.size() == 2); - REQUIRE(deps.at(0) == spec_a); - REQUIRE(deps.at(1) == spec_b_with_b1); + MockCMakeVarProvider var_provider; + var_provider.dep_info_vars[toplevel_spec()] = {{"VCPKG_CMAKE_SYSTEM_NAME", "Windows"}}; + + auto install_plan = unwrap(Dependencies::create_versioned_install_plan( + vp, + bp, + var_provider, + {{"b", {}, parse_platform("!linux")}, {"c", {}, parse_platform("linux")}}, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "b", {"1", 0}); } + + SECTION ("linux") { - // d -> c[core] - auto deps = vcpkg::Dependencies::resolve_deps_as_top_level( - *spec_map.map.at("d").source_control_file, Test::X86_WINDOWS, {}, var_provider); - REQUIRE(deps.size() == 1); - REQUIRE(deps.at(0) == spec_c); + MockCMakeVarProvider var_provider; + var_provider.dep_info_vars[toplevel_spec()] = {{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}}; + + auto install_plan = unwrap(Dependencies::create_versioned_install_plan( + vp, + bp, + var_provider, + {{"b", {}, parse_platform("!linux")}, {"c", {}, parse_platform("linux")}}, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "c", {"1", 0}); } } + +TEST_CASE ("version install qualified default suppression", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + auto& a_scf = vp.emplace("a", {"1", 0}, Scheme::Relaxed).source_control_file; + a_scf->core_paragraph->default_features.push_back("x"); + a_scf->feature_paragraphs.push_back(make_fpgh("x")); + + vp.emplace("b", {"1", 0}, Scheme::Relaxed) + .source_control_file->core_paragraph->dependencies.push_back({"a", {"core"}}); + + MockCMakeVarProvider var_provider; + + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["b"] = {"1", 0}; + + auto install_plan = unwrap(Dependencies::create_versioned_install_plan( + vp, + bp, + var_provider, + {{"b", {}, parse_platform("!linux")}, {"a", {"core"}, parse_platform("linux")}}, + {}, + toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x"}); + check_name_and_version(install_plan.install_actions[1], "b", {"1", 0}); +} + +TEST_CASE ("version install qualified transitive", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + vp.emplace("a", {"1", 0}, Scheme::Relaxed); + vp.emplace("c", {"1", 0}, Scheme::Relaxed); + + auto& b_scf = vp.emplace("b", {"1", 0}, Scheme::Relaxed).source_control_file; + b_scf->core_paragraph->dependencies.push_back({"a", {}, parse_platform("!linux")}); + b_scf->core_paragraph->dependencies.push_back({"c", {}, parse_platform("linux")}); + + MockCMakeVarProvider var_provider; + + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["b"] = {"1", 0}; + bp.v["c"] = {"1", 0}; + + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"b"}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 2); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}); + check_name_and_version(install_plan.install_actions[1], "b", {"1", 0}); +} + +TEST_CASE ("version install different vars", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + auto& b_scf = vp.emplace("b", {"1", 0}, Scheme::Relaxed).source_control_file; + b_scf->core_paragraph->dependencies.push_back({"a", {}, parse_platform("!linux")}); + + auto& a_scf = vp.emplace("a", {"1", 0}, Scheme::Relaxed).source_control_file; + a_scf->core_paragraph->dependencies.push_back({"c", {}, parse_platform("linux")}); + + vp.emplace("c", {"1", 0}, Scheme::Relaxed); + + MockCMakeVarProvider var_provider; + var_provider.dep_info_vars[PackageSpec{"a", Test::X86_WINDOWS}] = {{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}}; + + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["b"] = {"1", 0}; + bp.v["c"] = {"1", 0}; + + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"b"}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 3); + check_name_and_version(install_plan.install_actions[0], "c", {"1", 0}); + check_name_and_version(install_plan.install_actions[1], "a", {"1", 0}); + check_name_and_version(install_plan.install_actions[2], "b", {"1", 0}); +} + +TEST_CASE ("version install qualified features", "[versionplan]") +{ + MockVersionedPortfileProvider vp; + + auto& b_scf = vp.emplace("b", {"1", 0}, Scheme::Relaxed).source_control_file; + b_scf->core_paragraph->default_features.push_back("x"); + b_scf->feature_paragraphs.push_back(make_fpgh("x")); + b_scf->feature_paragraphs.back()->dependencies.push_back({"a", {}, parse_platform("!linux")}); + + auto& a_scf = vp.emplace("a", {"1", 0}, Scheme::Relaxed).source_control_file; + a_scf->core_paragraph->default_features.push_back("y"); + a_scf->feature_paragraphs.push_back(make_fpgh("y")); + a_scf->feature_paragraphs.back()->dependencies.push_back({"c", {}, parse_platform("linux")}); + + auto& c_scf = vp.emplace("c", {"1", 0}, Scheme::Relaxed).source_control_file; + c_scf->core_paragraph->default_features.push_back("z"); + c_scf->feature_paragraphs.push_back(make_fpgh("z")); + c_scf->feature_paragraphs.back()->dependencies.push_back({"d", {}, parse_platform("linux")}); + + vp.emplace("d", {"1", 0}, Scheme::Relaxed); + + MockCMakeVarProvider var_provider; + var_provider.dep_info_vars[PackageSpec{"a", Test::X86_WINDOWS}] = {{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}}; + + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + bp.v["b"] = {"1", 0}; + bp.v["c"] = {"1", 0}; + bp.v["d"] = {"1", 0}; + + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"b"}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 3); + check_name_and_version(install_plan.install_actions[0], "c", {"1", 0}, {"z"}); + check_name_and_version(install_plan.install_actions[1], "a", {"1", 0}, {"y"}); + check_name_and_version(install_plan.install_actions[2], "b", {"1", 0}, {"x"}); +} + +TEST_CASE ("version install self features", "[versionplan]") +{ + MockBaselineProvider bp; + bp.v["a"] = {"1", 0}; + + MockVersionedPortfileProvider vp; + auto& a_scf = vp.emplace("a", {"1", 0}).source_control_file; + a_scf->feature_paragraphs.push_back(make_fpgh("x")); + a_scf->feature_paragraphs.back()->dependencies.push_back({"a", {"core", "y"}}); + a_scf->feature_paragraphs.push_back(make_fpgh("y")); + a_scf->feature_paragraphs.push_back(make_fpgh("z")); + + MockCMakeVarProvider var_provider; + + auto install_plan = + unwrap(Dependencies::create_versioned_install_plan(vp, bp, var_provider, {{"a", {"x"}}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x", "y"}); +} |
