mirror of https://github.com/mamba-org/mamba.git
Matchspec hardening (#3907)
Co-authored-by: Julien Jerphanion <git@jjerphan.xyz>
This commit is contained in:
parent
6ad6a6afc7
commit
542241ff3b
|
@ -17,6 +17,7 @@
|
|||
#include "mamba/core/tasksync.hpp"
|
||||
#include "mamba/download/mirror_map.hpp"
|
||||
#include "mamba/fs/filesystem.hpp"
|
||||
#include "mamba/solver/libsolv/parameters.hpp"
|
||||
#include "mamba/solver/request.hpp"
|
||||
#include "mamba/specs/authentication_info.hpp"
|
||||
#include "mamba/specs/platform.hpp"
|
||||
|
@ -146,6 +147,7 @@ namespace mamba
|
|||
// Configurable
|
||||
bool experimental = false;
|
||||
bool experimental_repodata_parsing = true;
|
||||
bool experimental_matchspec_parsing = false;
|
||||
bool debug = false;
|
||||
|
||||
// TODO check writable and add other potential dirs
|
||||
|
|
|
@ -53,9 +53,18 @@ namespace mamba::solver::libsolv
|
|||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Global Database settings.
|
||||
*/
|
||||
struct Settings
|
||||
{
|
||||
MatchSpecParser matchspec_parser = MatchSpecParser::Libsolv;
|
||||
};
|
||||
|
||||
using logger_type = std::function<void(LogLevel, std::string_view)>;
|
||||
|
||||
explicit Database(specs::ChannelResolveParams channel_params);
|
||||
Database(specs::ChannelResolveParams channel_params, Settings settings);
|
||||
Database(const Database&) = delete;
|
||||
Database(Database&&);
|
||||
|
||||
|
@ -66,6 +75,8 @@ namespace mamba::solver::libsolv
|
|||
|
||||
[[nodiscard]] auto channel_params() const -> const specs::ChannelResolveParams&;
|
||||
|
||||
[[nodiscard]] auto settings() const -> const Settings&;
|
||||
|
||||
void set_logger(logger_type callback);
|
||||
|
||||
auto add_repo_from_repodata_json(
|
||||
|
@ -75,7 +86,7 @@ namespace mamba::solver::libsolv
|
|||
PipAsPythonDependency add = PipAsPythonDependency::No,
|
||||
PackageTypes package_types = PackageTypes::CondaOrElseTarBz2,
|
||||
VerifyPackages verify_packages = VerifyPackages::No,
|
||||
RepodataParser parser = RepodataParser::Mamba
|
||||
RepodataParser repo_parser = RepodataParser::Mamba
|
||||
) -> expected_t<RepoInfo>;
|
||||
|
||||
auto add_repo_from_native_serialization(
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define MAMBA_SOLVER_LIBSOLV_SOLVER_HPP
|
||||
|
||||
#include "mamba/core/error_handling.hpp"
|
||||
#include "mamba/solver/libsolv/parameters.hpp"
|
||||
#include "mamba/solver/libsolv/unsolvable.hpp"
|
||||
#include "mamba/solver/request.hpp"
|
||||
#include "mamba/solver/solution.hpp"
|
||||
|
@ -22,12 +23,18 @@ namespace mamba::solver::libsolv
|
|||
|
||||
using Outcome = std::variant<Solution, UnSolvable>;
|
||||
|
||||
[[nodiscard]] auto solve(Database& database, Request&& request) -> expected_t<Outcome>;
|
||||
[[nodiscard]] auto solve(Database& database, const Request& request) -> expected_t<Outcome>;
|
||||
[[nodiscard]] auto
|
||||
solve(Database& database, Request&& request, MatchSpecParser ms_parser = MatchSpecParser::Mixed)
|
||||
-> expected_t<Outcome>;
|
||||
|
||||
[[nodiscard]] auto
|
||||
solve(Database& database, const Request& request, MatchSpecParser ms_parser = MatchSpecParser::Mixed)
|
||||
-> expected_t<Outcome>;
|
||||
|
||||
private:
|
||||
|
||||
auto solve_impl(Database& database, const Request& request) -> expected_t<Outcome>;
|
||||
auto solve_impl(Database& database, const Request& request, MatchSpecParser ms_parser)
|
||||
-> expected_t<Outcome>;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1423,6 +1423,14 @@ namespace mamba
|
|||
.set_env_var_names()
|
||||
.set_post_merge_hook(detail::not_supported_option_hook));
|
||||
|
||||
insert(Configurable("experimental_matchspec_parsing", &m_context.experimental_matchspec_parsing)
|
||||
.group("Basic")
|
||||
.description( //
|
||||
"Enable internal parsing and matching of MatchSpecs using Mamba's experimental implementation rather than Libsolv's.\n"
|
||||
"This is not mean for production"
|
||||
)
|
||||
.set_env_var_names());
|
||||
|
||||
insert(Configurable("debug", &m_context.debug)
|
||||
.group("Basic")
|
||||
.set_env_var_names()
|
||||
|
|
|
@ -465,7 +465,13 @@ namespace mamba
|
|||
LOG_WARNING << "No 'channels' specified";
|
||||
}
|
||||
|
||||
solver::libsolv::Database db{ channel_context.params() };
|
||||
solver::libsolv::Database db{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(db);
|
||||
|
||||
auto exp_load = load_channels(ctx, channel_context, db, package_caches);
|
||||
|
@ -494,7 +500,15 @@ namespace mamba
|
|||
// Console stream prints on destruction
|
||||
}
|
||||
|
||||
auto outcome = solver::libsolv::Solver().solve(db, request).value();
|
||||
auto outcome = solver::libsolv::Solver()
|
||||
.solve(
|
||||
db,
|
||||
request,
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Mixed
|
||||
)
|
||||
.value();
|
||||
|
||||
if (auto* unsolvable = std::get_if<solver::libsolv::UnSolvable>(&outcome))
|
||||
{
|
||||
|
@ -643,7 +657,13 @@ namespace mamba
|
|||
bool remove_prefix_on_failure
|
||||
)
|
||||
{
|
||||
solver::libsolv::Database database{ channel_context.params() };
|
||||
solver::libsolv::Database database{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(database);
|
||||
|
||||
init_channels(ctx, channel_context);
|
||||
|
|
|
@ -136,7 +136,13 @@ namespace mamba
|
|||
}
|
||||
PrefixData& prefix_data = exp_prefix_data.value();
|
||||
|
||||
solver::libsolv::Database database{ channel_context.params() };
|
||||
solver::libsolv::Database database{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(database);
|
||||
load_installed_packages_in_database(ctx, database, prefix_data);
|
||||
|
||||
|
@ -190,7 +196,15 @@ namespace mamba
|
|||
/* .strict_repo_priority= */ ctx.channel_priority == ChannelPriority::Strict,
|
||||
};
|
||||
|
||||
auto outcome = solver::libsolv::Solver().solve(database, request).value();
|
||||
auto outcome = solver::libsolv::Solver()
|
||||
.solve(
|
||||
database,
|
||||
request,
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Mixed
|
||||
)
|
||||
.value();
|
||||
if (auto* unsolvable = std::get_if<solver::libsolv::UnSolvable>(&outcome))
|
||||
{
|
||||
if (ctx.output_params.json)
|
||||
|
|
|
@ -34,7 +34,13 @@ namespace mamba
|
|||
config.load();
|
||||
|
||||
auto channel_context = ChannelContext::make_conda_compatible(ctx);
|
||||
solver::libsolv::Database db{ channel_context.params() };
|
||||
solver::libsolv::Database db{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(db);
|
||||
|
||||
// bool installed = (type == QueryType::kDepends) || (type == QueryType::kWhoneeds);
|
||||
|
|
|
@ -152,7 +152,13 @@ namespace mamba
|
|||
|
||||
populate_context_channels_from_specs(raw_update_specs, ctx);
|
||||
|
||||
solver::libsolv::Database db{ channel_context.params() };
|
||||
solver::libsolv::Database db{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(db);
|
||||
|
||||
MultiPackageCache package_caches(ctx.pkgs_dirs, ctx.validation_params);
|
||||
|
@ -191,7 +197,15 @@ namespace mamba
|
|||
// Console stream prints on destruction
|
||||
}
|
||||
|
||||
auto outcome = solver::libsolv::Solver().solve(db, request).value();
|
||||
auto outcome = solver::libsolv::Solver()
|
||||
.solve(
|
||||
db,
|
||||
request,
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Mixed
|
||||
)
|
||||
.value();
|
||||
if (auto* unsolvable = std::get_if<solver::libsolv::UnSolvable>(&outcome))
|
||||
{
|
||||
unsolvable->explain_problems_to(
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
@ -30,17 +29,24 @@ namespace mamba::solver::libsolv
|
|||
{
|
||||
struct Database::DatabaseImpl
|
||||
{
|
||||
explicit DatabaseImpl(specs::ChannelResolveParams p_channel_params)
|
||||
: matcher(std::move(p_channel_params))
|
||||
explicit DatabaseImpl(specs::ChannelResolveParams p_channel_params, Settings settings_)
|
||||
: settings(std::move(settings_))
|
||||
, matcher(std::move(p_channel_params))
|
||||
{
|
||||
}
|
||||
|
||||
Settings settings;
|
||||
solv::ObjPool pool = {};
|
||||
Matcher matcher;
|
||||
};
|
||||
|
||||
Database::Database(specs::ChannelResolveParams channel_params)
|
||||
: m_data(std::make_unique<DatabaseImpl>(std::move(channel_params)))
|
||||
: Database(channel_params, Settings{})
|
||||
{
|
||||
}
|
||||
|
||||
Database::Database(specs::ChannelResolveParams channel_params, Settings settings)
|
||||
: m_data(std::make_unique<DatabaseImpl>(std::move(channel_params), std::move(settings)))
|
||||
{
|
||||
pool().set_disttype(DISTTYPE_CONDA);
|
||||
// Ensure that debug logging never goes to stdout as to not interfere json output
|
||||
|
@ -87,6 +93,11 @@ namespace mamba::solver::libsolv
|
|||
return m_data->matcher.channel_params();
|
||||
}
|
||||
|
||||
auto Database::settings() const -> const Settings&
|
||||
{
|
||||
return m_data->settings;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
auto libsolv_to_log_level(int type) -> LogLevel
|
||||
|
@ -144,7 +155,7 @@ namespace mamba::solver::libsolv
|
|||
PipAsPythonDependency add,
|
||||
PackageTypes package_types,
|
||||
VerifyPackages verify_packages,
|
||||
RepodataParser parser
|
||||
RepodataParser repo_parser
|
||||
) -> expected_t<RepoInfo>
|
||||
{
|
||||
const auto verify_artifacts = static_cast<bool>(verify_packages);
|
||||
|
@ -161,7 +172,7 @@ namespace mamba::solver::libsolv
|
|||
|
||||
auto make_repo = [&]() -> expected_t<solv::ObjRepoView>
|
||||
{
|
||||
if (parser == RepodataParser::Mamba)
|
||||
if (repo_parser == RepodataParser::Mamba)
|
||||
{
|
||||
return mamba_read_json(
|
||||
pool(),
|
||||
|
@ -170,10 +181,20 @@ namespace mamba::solver::libsolv
|
|||
std::string(url),
|
||||
channel_id,
|
||||
package_types,
|
||||
MatchSpecParser::Libsolv, // Backward compatibility
|
||||
settings().matchspec_parser,
|
||||
verify_artifacts
|
||||
);
|
||||
}
|
||||
|
||||
if (settings().matchspec_parser != MatchSpecParser::Libsolv)
|
||||
{
|
||||
return make_unexpected(
|
||||
" Libsolv repodata parser can only be used with Libsolv MatchSpec parser."
|
||||
"A Libsolv Repodata parser option been passed to this function while a"
|
||||
" non-Libsolv MatchSpec parser option has been give to the Database constructor.",
|
||||
mamba_error_code::incorrect_usage
|
||||
);
|
||||
}
|
||||
return libsolv_read_json(repo, path, package_types, verify_artifacts)
|
||||
.transform(
|
||||
[&url, &channel_id](solv::ObjRepoView p_repo)
|
||||
|
@ -241,12 +262,7 @@ namespace mamba::solver::libsolv
|
|||
{
|
||||
auto s_repo = solv::ObjRepoView(*repo.m_ptr);
|
||||
auto [id, solv] = s_repo.add_solvable();
|
||||
set_solvable(
|
||||
pool(),
|
||||
solv,
|
||||
pkg,
|
||||
MatchSpecParser::Libsolv // Backward compatibility
|
||||
);
|
||||
set_solvable(pool(), solv, pkg, settings().matchspec_parser);
|
||||
}
|
||||
|
||||
void Database::add_repo_from_packages_impl_post(const RepoInfo& repo, PipAsPythonDependency add)
|
||||
|
@ -330,10 +346,11 @@ namespace mamba::solver::libsolv
|
|||
|
||||
namespace
|
||||
{
|
||||
auto pool_add_matchspec_throwing(solv::ObjPool& pool, const specs::MatchSpec& ms)
|
||||
auto
|
||||
pool_add_matchspec_throwing(solv::ObjPool& pool, const specs::MatchSpec& ms, MatchSpecParser parser)
|
||||
-> solv::DependencyId
|
||||
{
|
||||
return pool_add_matchspec(pool, ms, MatchSpecParser::Mixed)
|
||||
return pool_add_matchspec(pool, ms, parser)
|
||||
.or_else([](mamba_error&& error) { throw std::move(error); })
|
||||
.value_or(0);
|
||||
}
|
||||
|
@ -344,7 +361,7 @@ namespace mamba::solver::libsolv
|
|||
static_assert(std::is_same_v<std::underlying_type_t<PackageId>, solv::SolvableId>);
|
||||
|
||||
pool().ensure_whatprovides();
|
||||
const auto ms_id = pool_add_matchspec_throwing(pool(), ms);
|
||||
const auto ms_id = pool_add_matchspec_throwing(pool(), ms, settings().matchspec_parser);
|
||||
auto solvables = pool().select_solvables({ SOLVER_SOLVABLE_PROVIDES, ms_id });
|
||||
auto out = std::vector<PackageId>(solvables.size());
|
||||
std::transform(
|
||||
|
@ -361,7 +378,7 @@ namespace mamba::solver::libsolv
|
|||
static_assert(std::is_same_v<std::underlying_type_t<PackageId>, solv::SolvableId>);
|
||||
|
||||
pool().ensure_whatprovides();
|
||||
const auto ms_id = pool_add_matchspec_throwing(pool(), ms);
|
||||
const auto ms_id = pool_add_matchspec_throwing(pool(), ms, settings().matchspec_parser);
|
||||
auto solvables = pool().what_matches_dep(SOLVABLE_REQUIRES, ms_id);
|
||||
auto out = std::vector<PackageId>(solvables.size());
|
||||
std::transform(
|
||||
|
|
|
@ -50,17 +50,13 @@ namespace mamba::solver::libsolv
|
|||
}
|
||||
}
|
||||
|
||||
auto Solver::solve_impl(Database& mpool, const Request& request) -> expected_t<Outcome>
|
||||
auto Solver::solve_impl(Database& mpool, const Request& request, MatchSpecParser ms_parser)
|
||||
-> expected_t<Outcome>
|
||||
{
|
||||
auto& pool = Database::Impl::get(mpool);
|
||||
const auto& flags = request.flags;
|
||||
|
||||
return solver::libsolv::request_to_decision_queue(
|
||||
request,
|
||||
pool,
|
||||
flags.force_reinstall,
|
||||
MatchSpecParser::Mixed
|
||||
)
|
||||
return solver::libsolv::request_to_decision_queue(request, pool, flags.force_reinstall, ms_parser)
|
||||
.transform(
|
||||
[&](auto&& jobs) -> Outcome
|
||||
{
|
||||
|
@ -90,24 +86,26 @@ namespace mamba::solver::libsolv
|
|||
);
|
||||
}
|
||||
|
||||
auto Solver::solve(Database& mpool, Request&& request) -> expected_t<Outcome>
|
||||
auto Solver::solve(Database& mpool, Request&& request, MatchSpecParser ms_parser)
|
||||
-> expected_t<Outcome>
|
||||
{
|
||||
if (request.flags.order_request)
|
||||
{
|
||||
std::sort(request.jobs.begin(), request.jobs.end(), make_request_cmp());
|
||||
}
|
||||
return solve_impl(mpool, request);
|
||||
return solve_impl(mpool, request, ms_parser);
|
||||
}
|
||||
|
||||
auto Solver::solve(Database& mpool, const Request& request) -> expected_t<Outcome>
|
||||
auto Solver::solve(Database& mpool, const Request& request, MatchSpecParser ms_parser)
|
||||
-> expected_t<Outcome>
|
||||
{
|
||||
if (request.flags.order_request)
|
||||
{
|
||||
auto sorted_request = request;
|
||||
std::sort(sorted_request.jobs.begin(), sorted_request.jobs.end(), make_request_cmp());
|
||||
return solve_impl(mpool, sorted_request);
|
||||
return solve_impl(mpool, sorted_request, ms_parser);
|
||||
}
|
||||
return solve_impl(mpool, request);
|
||||
return solve_impl(mpool, request, ms_parser);
|
||||
}
|
||||
|
||||
} // namespace mamba
|
||||
|
|
|
@ -37,9 +37,16 @@ namespace
|
|||
{
|
||||
using PackageInfo = specs::PackageInfo;
|
||||
|
||||
TEST_CASE("Create a database")
|
||||
TEST_CASE("Create a database", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
auto db = libsolv::Database({});
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Libsolv,
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
CAPTURE(matchspec_parser);
|
||||
|
||||
auto db = libsolv::Database({}, { matchspec_parser });
|
||||
REQUIRE(std::is_move_constructible_v<libsolv::Database>);
|
||||
REQUIRE(db.repo_count() == 0);
|
||||
|
||||
|
@ -162,6 +169,12 @@ namespace
|
|||
|
||||
SECTION("Depending on a given dependency")
|
||||
{
|
||||
// Complex repoqueries do not work with namespace callbacks
|
||||
if (matchspec_parser != libsolv::MatchSpecParser::Libsolv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t count = 0;
|
||||
db.for_each_package_depending_on(
|
||||
specs::MatchSpec::parse("x").value(),
|
||||
|
@ -351,6 +364,14 @@ namespace
|
|||
libsolv::VerifyPackages::Yes,
|
||||
libsolv::RepodataParser::Libsolv
|
||||
);
|
||||
|
||||
// Libsolv repodata parser only works with its own matchspec
|
||||
if (matchspec_parser != libsolv::MatchSpecParser::Libsolv)
|
||||
{
|
||||
REQUIRE_FALSE(repo1.has_value());
|
||||
return;
|
||||
}
|
||||
|
||||
REQUIRE(repo1.has_value());
|
||||
REQUIRE(repo1->package_count() == 33);
|
||||
|
||||
|
@ -420,6 +441,14 @@ namespace
|
|||
libsolv::VerifyPackages::No,
|
||||
libsolv::RepodataParser::Libsolv
|
||||
);
|
||||
|
||||
// Libsolv repodata parser only works with its own matchspec
|
||||
if (matchspec_parser != libsolv::MatchSpecParser::Libsolv)
|
||||
{
|
||||
REQUIRE_FALSE(repo1.has_value());
|
||||
return;
|
||||
}
|
||||
|
||||
REQUIRE(repo1.has_value());
|
||||
REQUIRE(repo1->package_count() == 33);
|
||||
|
||||
|
|
|
@ -67,16 +67,25 @@ namespace
|
|||
{
|
||||
using namespace specs::match_spec_literals;
|
||||
|
||||
TEST_CASE("Solve a fresh environment with one repository")
|
||||
TEST_CASE("Solve a fresh environment with one repository", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
auto db = libsolv::Database({});
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Libsolv,
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
|
||||
auto db = libsolv::Database({}, { matchspec_parser });
|
||||
|
||||
// A conda-forge/linux-64 subsample with one version of numpy and pip and their dependencies
|
||||
const auto repo = db.add_repo_from_repodata_json(
|
||||
mambatests::test_data_dir / "repodata/conda-forge-numpy-linux-64.json",
|
||||
"https://conda.anaconda.org/conda-forge/linux-64",
|
||||
"conda-forge",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
libsolv::PipAsPythonDependency::No,
|
||||
libsolv::PackageTypes::CondaOrElseTarBz2,
|
||||
libsolv::VerifyPackages::No,
|
||||
libsolv::RepodataParser::Mamba
|
||||
);
|
||||
REQUIRE(repo.has_value());
|
||||
|
||||
|
@ -86,7 +95,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -111,7 +120,7 @@ namespace
|
|||
/* .flags= */ std::move(flags),
|
||||
/* .jobs= */ { Request::Install{ "numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -137,7 +146,7 @@ namespace
|
|||
},
|
||||
/* .jobs= */ { Request::Install{ "numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -163,7 +172,7 @@ namespace
|
|||
},
|
||||
/* .jobs= */ { Request::Install{ "numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -189,7 +198,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "does-not-exist"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<libsolv::UnSolvable>(outcome.value()));
|
||||
|
@ -205,22 +214,32 @@ namespace
|
|||
},
|
||||
/* .jobs= */ { Request::Install{ "numpy"_ms }, Request::Install{ "python=2.7"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<libsolv::UnSolvable>(outcome.value()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Remove packages")
|
||||
TEST_CASE("Remove packages", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
auto db = libsolv::Database({});
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Libsolv,
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
|
||||
auto db = libsolv::Database({}, { matchspec_parser });
|
||||
|
||||
// A conda-forge/linux-64 subsample with one version of numpy and pip and their dependencies
|
||||
const auto repo = db.add_repo_from_repodata_json(
|
||||
mambatests::test_data_dir / "repodata/conda-forge-numpy-linux-64.json",
|
||||
"https://conda.anaconda.org/conda-forge/linux-64",
|
||||
"conda-forge"
|
||||
"conda-forge",
|
||||
libsolv::PipAsPythonDependency::No,
|
||||
libsolv::PackageTypes::CondaOrElseTarBz2,
|
||||
libsolv::VerifyPackages::No,
|
||||
libsolv::RepodataParser::Mamba
|
||||
);
|
||||
REQUIRE(repo.has_value());
|
||||
db.set_installed_repo(repo.value());
|
||||
|
@ -231,7 +250,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Remove{ "numpy"_ms, true } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -253,7 +272,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Remove{ "numpy"_ms, true }, Request::Remove{ "pip"_ms, true } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -278,7 +297,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Remove{ "numpy"_ms, false } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -296,7 +315,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Remove{ "does-not-exist"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -306,22 +325,36 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Reinstall packages")
|
||||
TEST_CASE("Reinstall packages", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
auto db = libsolv::Database({});
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Libsolv,
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
|
||||
auto db = libsolv::Database({}, { matchspec_parser });
|
||||
|
||||
// A conda-forge/linux-64 subsample with one version of numpy and pip and their dependencies
|
||||
const auto repo_installed = db.add_repo_from_repodata_json(
|
||||
mambatests::test_data_dir / "repodata/conda-forge-numpy-linux-64.json",
|
||||
"installed",
|
||||
"installed"
|
||||
"installed",
|
||||
libsolv::PipAsPythonDependency::No,
|
||||
libsolv::PackageTypes::CondaOrElseTarBz2,
|
||||
libsolv::VerifyPackages::No,
|
||||
libsolv::RepodataParser::Mamba
|
||||
);
|
||||
REQUIRE(repo_installed.has_value());
|
||||
db.set_installed_repo(repo_installed.value());
|
||||
const auto repo = db.add_repo_from_repodata_json(
|
||||
mambatests::test_data_dir / "repodata/conda-forge-numpy-linux-64.json",
|
||||
"https://conda.anaconda.org/conda-forge/linux-64",
|
||||
"conda-forge"
|
||||
"conda-forge",
|
||||
libsolv::PipAsPythonDependency::No,
|
||||
libsolv::PackageTypes::CondaOrElseTarBz2,
|
||||
libsolv::VerifyPackages::No,
|
||||
libsolv::RepodataParser::Mamba
|
||||
);
|
||||
REQUIRE(repo.has_value());
|
||||
|
||||
|
@ -333,7 +366,7 @@ namespace
|
|||
/* .flags= */ std::move(flags),
|
||||
/* .jobs= */ { Request::Install{ "numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -345,24 +378,36 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Solve a existing environment with one repository")
|
||||
TEST_CASE("Solve a existing environment with one repository", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
auto db = libsolv::Database({});
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Libsolv,
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
auto db = libsolv::Database({}, { matchspec_parser });
|
||||
|
||||
// A conda-forge/linux-64 subsample with one version of numpy and pip and their dependencies
|
||||
const auto repo = db.add_repo_from_repodata_json(
|
||||
mambatests::test_data_dir / "repodata/conda-forge-numpy-linux-64.json",
|
||||
"https://conda.anaconda.org/conda-forge/linux-64",
|
||||
"conda-forge",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
libsolv::PipAsPythonDependency::No,
|
||||
libsolv::PackageTypes::CondaOrElseTarBz2,
|
||||
libsolv::VerifyPackages::No,
|
||||
libsolv::RepodataParser::Mamba
|
||||
);
|
||||
REQUIRE(repo.has_value());
|
||||
|
||||
SECTION("numpy 1.0 is installed")
|
||||
{
|
||||
const auto installed = db.add_repo_from_packages(std::array{
|
||||
specs::PackageInfo("numpy", "1.0.0", "phony", 0),
|
||||
});
|
||||
const auto installed = db.add_repo_from_packages(
|
||||
std::array{
|
||||
specs::PackageInfo("numpy", "1.0.0", "phony", 0),
|
||||
},
|
||||
"installed",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
db.set_installed_repo(installed);
|
||||
|
||||
SECTION("Installing numpy does not upgrade")
|
||||
|
@ -371,7 +416,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -385,7 +430,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Update{ "numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -413,7 +458,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Update{ "numpy<=1.1"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -426,11 +471,15 @@ namespace
|
|||
{
|
||||
auto pkg_numpy = specs::PackageInfo("numpy", "1.0.0", "phony", 0);
|
||||
pkg_numpy.dependencies = { "python=2.0", "foo" };
|
||||
const auto installed = db.add_repo_from_packages(std::array{
|
||||
pkg_numpy,
|
||||
specs::PackageInfo("python", "2.0.0", "phony", 0),
|
||||
specs::PackageInfo("foo"),
|
||||
});
|
||||
const auto installed = db.add_repo_from_packages(
|
||||
std::array{
|
||||
pkg_numpy,
|
||||
specs::PackageInfo("python", "2.0.0", "phony", 0),
|
||||
specs::PackageInfo("foo"),
|
||||
},
|
||||
"installed",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
db.set_installed_repo(installed);
|
||||
|
||||
SECTION("numpy is upgraded with cleaning dependencies")
|
||||
|
@ -439,7 +488,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Update{ "numpy"_ms, true } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -470,7 +519,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Update{ "numpy"_ms, true }, Request::Keep{ "foo"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -500,7 +549,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Update{ "numpy"_ms, false } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -529,7 +578,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Update{ "python"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -559,11 +608,11 @@ namespace
|
|||
pkg_numpy.dependencies = { "python=4.0", "foo" };
|
||||
auto pkg_foo = specs::PackageInfo("foo", "1.0.0", "phony", 0);
|
||||
pkg_foo.constrains = { "numpy=1.0.0", "foo" };
|
||||
const auto installed = db.add_repo_from_packages(std::array{
|
||||
pkg_numpy,
|
||||
pkg_foo,
|
||||
specs::PackageInfo("python", "4.0.0", "phony", 0),
|
||||
});
|
||||
const auto installed = db.add_repo_from_packages(
|
||||
std::array{ pkg_numpy, pkg_foo, specs::PackageInfo("python", "4.0.0", "phony", 0) },
|
||||
"installed",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
db.set_installed_repo(installed);
|
||||
|
||||
SECTION("numpy upgrade lead to allowed python downgrade")
|
||||
|
@ -578,7 +627,7 @@ namespace
|
|||
},
|
||||
/* .jobs= */ { Request::Update{ "numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -613,7 +662,7 @@ namespace
|
|||
},
|
||||
/* .jobs= */ { Request::Update{ "numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -625,16 +674,25 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Solve a fresh environment with multiple repositories")
|
||||
TEST_CASE("Solve a fresh environment with multiple repositories", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
auto db = libsolv::Database({});
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Libsolv,
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
auto db = libsolv::Database({}, { matchspec_parser });
|
||||
|
||||
const auto repo1 = db.add_repo_from_packages(std::array{
|
||||
specs::PackageInfo("numpy", "1.0.0", "repo1", 0),
|
||||
});
|
||||
const auto repo2 = db.add_repo_from_packages(std::array{
|
||||
specs::PackageInfo("numpy", "2.0.0", "repo2", 0),
|
||||
});
|
||||
const auto repo1 = db.add_repo_from_packages(
|
||||
std::array{ specs::PackageInfo("numpy", "1.0.0", "repo1", 0) },
|
||||
"repo1",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
const auto repo2 = db.add_repo_from_packages(
|
||||
std::array{ specs::PackageInfo("numpy", "2.0.0", "repo2", 0) },
|
||||
"repo2",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
db.set_repo_priority(repo1, { 2, 0 });
|
||||
db.set_repo_priority(repo2, { 1, 0 });
|
||||
|
||||
|
@ -645,7 +703,7 @@ namespace
|
|||
/* .jobs= */ { Request::Install{ "numpy>=2.0"_ms } },
|
||||
};
|
||||
request.flags.strict_repo_priority = false;
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -664,16 +722,22 @@ namespace
|
|||
/* .jobs= */ { Request::Install{ "numpy>=2.0"_ms } },
|
||||
};
|
||||
request.flags.strict_repo_priority = true;
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<libsolv::UnSolvable>(outcome.value()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Install highest priority package")
|
||||
TEST_CASE("Install highest priority package", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
auto db = libsolv::Database({});
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Libsolv,
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
|
||||
auto db = libsolv::Database({}, { matchspec_parser });
|
||||
|
||||
auto mkfoo = [](std::string version,
|
||||
std::size_t build_number = 0,
|
||||
|
@ -690,16 +754,20 @@ namespace
|
|||
|
||||
SECTION("Pins are respected")
|
||||
{
|
||||
db.add_repo_from_packages(std::array{
|
||||
mkfoo("1.0.0", 0, { "feat" }, 0),
|
||||
mkfoo("2.0.0", 1, {}, 1),
|
||||
});
|
||||
db.add_repo_from_packages(
|
||||
std::array{
|
||||
mkfoo("1.0.0", 0, { "feat" }, 0),
|
||||
mkfoo("2.0.0", 1, {}, 1),
|
||||
},
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "foo"_ms }, Request::Pin{ "foo==1.0"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -713,15 +781,19 @@ namespace
|
|||
|
||||
SECTION("Track features has highest priority")
|
||||
{
|
||||
db.add_repo_from_packages(std::array{
|
||||
mkfoo("1.0.0", 0, {}, 0),
|
||||
mkfoo("2.0.0", 1, { "feat" }, 1),
|
||||
});
|
||||
db.add_repo_from_packages(
|
||||
std::array{
|
||||
mkfoo("1.0.0", 0, {}, 0),
|
||||
mkfoo("2.0.0", 1, { "feat" }, 1),
|
||||
},
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "foo"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -735,15 +807,19 @@ namespace
|
|||
|
||||
SECTION("Version has second highest priority")
|
||||
{
|
||||
db.add_repo_from_packages(std::array{
|
||||
mkfoo("2.0.0", 0, {}, 0),
|
||||
mkfoo("1.0.0", 1, {}, 1),
|
||||
});
|
||||
db.add_repo_from_packages(
|
||||
std::array{
|
||||
mkfoo("2.0.0", 0, {}, 0),
|
||||
mkfoo("1.0.0", 1, {}, 1),
|
||||
},
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "foo"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -757,15 +833,19 @@ namespace
|
|||
|
||||
SECTION("Build number has third highest priority")
|
||||
{
|
||||
db.add_repo_from_packages(std::array{
|
||||
mkfoo("2.0.0", 1, {}, 0),
|
||||
mkfoo("2.0.0", 0, {}, 1),
|
||||
});
|
||||
db.add_repo_from_packages(
|
||||
std::array{
|
||||
mkfoo("2.0.0", 1, {}, 0),
|
||||
mkfoo("2.0.0", 0, {}, 1),
|
||||
},
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "foo"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -779,15 +859,19 @@ namespace
|
|||
|
||||
SECTION("Timestamp has lowest priority")
|
||||
{
|
||||
db.add_repo_from_packages(std::array{
|
||||
mkfoo("2.0.0", 0, {}, 0),
|
||||
mkfoo("2.0.0", 0, {}, 1),
|
||||
});
|
||||
db.add_repo_from_packages(
|
||||
std::array{
|
||||
mkfoo("2.0.0", 0, {}, 0),
|
||||
mkfoo("2.0.0", 0, {}, 1),
|
||||
},
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "foo"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -800,21 +884,30 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Respect channel-specific MatchSpec")
|
||||
TEST_CASE("Respect channel-specific MatchSpec", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
auto db = libsolv::Database({
|
||||
/* .platforms= */ { "linux-64", "noarch" },
|
||||
/* .channel_alias= */ specs::CondaURL::parse("https://conda.anaconda.org/").value(),
|
||||
});
|
||||
// Libsolv MatchSpec parser is not able to handle channels
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
|
||||
auto db = libsolv::Database(
|
||||
{
|
||||
/* .platforms= */ { "linux-64", "noarch" },
|
||||
/* .channel_alias= */ specs::CondaURL::parse("https://conda.anaconda.org/").value(),
|
||||
},
|
||||
{ matchspec_parser }
|
||||
);
|
||||
|
||||
SECTION("Different channels")
|
||||
{
|
||||
auto pkg1 = specs::PackageInfo("foo", "1.0.0", "conda", 0);
|
||||
pkg1.package_url = "https://conda.anaconda.org/conda-forge/linux-64/foo-1.0.0-phony.conda";
|
||||
db.add_repo_from_packages(std::array{ pkg1 });
|
||||
db.add_repo_from_packages(std::array{ pkg1 }, "repo1", libsolv::PipAsPythonDependency::No);
|
||||
auto pkg2 = specs::PackageInfo("foo", "1.0.0", "mamba", 0);
|
||||
pkg2.package_url = "https://conda.anaconda.org/mamba-forge/linux-64/foo-1.0.0-phony.conda";
|
||||
db.add_repo_from_packages(std::array{ pkg2 });
|
||||
db.add_repo_from_packages(std::array{ pkg2 }, "repo2", libsolv::PipAsPythonDependency::No);
|
||||
|
||||
SECTION("conda-forge::foo")
|
||||
{
|
||||
|
@ -822,7 +915,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "conda-forge::foo"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -840,7 +933,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "mamba-forge::foo"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -859,7 +952,7 @@ namespace
|
|||
/* .jobs= */ { Request::Install{ "pixi-forge::foo"_ms } },
|
||||
};
|
||||
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<libsolv::UnSolvable>(outcome.value()));
|
||||
|
@ -871,7 +964,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "https://conda.anaconda.org/mamba-forge::foo"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -890,7 +983,10 @@ namespace
|
|||
mambatests::test_data_dir / "repodata/conda-forge-numpy-linux-64.json",
|
||||
"https://conda.anaconda.org/conda-forge/linux-64",
|
||||
"conda-forge",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
libsolv::PipAsPythonDependency::No,
|
||||
libsolv::PackageTypes::CondaOrElseTarBz2,
|
||||
libsolv::VerifyPackages::No,
|
||||
libsolv::RepodataParser::Mamba
|
||||
);
|
||||
REQUIRE(repo_linux.has_value());
|
||||
|
||||
|
@ -901,7 +997,10 @@ namespace
|
|||
mambatests::test_data_dir / "repodata/conda-forge-numpy-linux-64.json",
|
||||
"https://conda.anaconda.org/conda-forge/noarch",
|
||||
"conda-forge",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
libsolv::PipAsPythonDependency::No,
|
||||
libsolv::PackageTypes::CondaOrElseTarBz2,
|
||||
libsolv::VerifyPackages::No,
|
||||
libsolv::RepodataParser::Mamba
|
||||
);
|
||||
REQUIRE(repo_noarch.has_value());
|
||||
|
||||
|
@ -911,7 +1010,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "conda-forge/win-64::numpy"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<libsolv::UnSolvable>(outcome.value()));
|
||||
|
@ -923,7 +1022,7 @@ namespace
|
|||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "conda-forge::numpy[subdir=linux-64]"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -940,11 +1039,17 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Respect pins")
|
||||
TEST_CASE("Respect pins", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
using PackageInfo = specs::PackageInfo;
|
||||
|
||||
auto db = libsolv::Database({});
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Libsolv,
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
|
||||
auto db = libsolv::Database({}, { matchspec_parser });
|
||||
|
||||
SECTION("Respect pins through direct dependencies")
|
||||
{
|
||||
|
@ -953,13 +1058,17 @@ namespace
|
|||
auto pkg2 = PackageInfo("foo");
|
||||
pkg2.version = "2.0";
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg1, pkg2 });
|
||||
db.add_repo_from_packages(
|
||||
std::array{ pkg1, pkg2 },
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Pin{ "foo=1.0"_ms }, Request::Install{ "foo"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -984,13 +1093,17 @@ namespace
|
|||
pkg4.version = "2.0";
|
||||
pkg4.dependencies = { "foo=2.0" };
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg1, pkg2, pkg3, pkg4 });
|
||||
db.add_repo_from_packages(
|
||||
std::array{ pkg1, pkg2, pkg3, pkg4 },
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Pin{ "foo=1.0"_ms }, Request::Install{ "bar"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -1014,13 +1127,17 @@ namespace
|
|||
auto pkg2 = PackageInfo("bar");
|
||||
pkg2.version = "1.0";
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg1, pkg2 });
|
||||
db.add_repo_from_packages(
|
||||
std::array{ pkg1, pkg2 },
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Pin{ "foo=1.0"_ms }, Request::Install{ "bar"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -1038,13 +1155,13 @@ namespace
|
|||
auto pkg = PackageInfo("bar");
|
||||
pkg.version = "1.0";
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg });
|
||||
db.add_repo_from_packages(std::array{ pkg }, "repo", libsolv::PipAsPythonDependency::No);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Pin{ "foo=1.0"_ms }, Request::Install{ "bar"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -1058,11 +1175,17 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Handle complex matchspecs")
|
||||
TEST_CASE("Handle complex matchspecs", "[mamba::solver][mamba::solver::libsolv]")
|
||||
{
|
||||
using PackageInfo = specs::PackageInfo;
|
||||
|
||||
auto db = libsolv::Database({});
|
||||
// Libsolv MatchSpec parser cannot handle complex specs
|
||||
const auto matchspec_parser = GENERATE(
|
||||
libsolv::MatchSpecParser::Mixed,
|
||||
libsolv::MatchSpecParser::Mamba
|
||||
);
|
||||
|
||||
auto db = libsolv::Database({}, { matchspec_parser });
|
||||
|
||||
SECTION("*[md5=0bab699354cbd66959550eb9b9866620]")
|
||||
{
|
||||
|
@ -1071,13 +1194,17 @@ namespace
|
|||
auto pkg2 = PackageInfo("foo");
|
||||
pkg2.md5 = "bad";
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg1, pkg2 });
|
||||
db.add_repo_from_packages(
|
||||
std::array{ pkg1, pkg2 },
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "*[md5=0bab699354cbd66959550eb9b9866620]"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -1096,13 +1223,13 @@ namespace
|
|||
auto pkg1 = PackageInfo("foo");
|
||||
pkg1.md5 = "0bab699354cbd66959550eb9b9866620";
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg1 });
|
||||
db.add_repo_from_packages(std::array{ pkg1 }, "repo", libsolv::PipAsPythonDependency::No);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "foo[md5=notreallymd5]"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<libsolv::UnSolvable>(outcome.value()));
|
||||
|
@ -1115,13 +1242,17 @@ namespace
|
|||
auto pkg2 = PackageInfo("foo");
|
||||
pkg2.build_string = "bld";
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg1, pkg2 });
|
||||
db.add_repo_from_packages(
|
||||
std::array{ pkg1, pkg2 },
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "foo[build=bld]"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -1146,13 +1277,17 @@ namespace
|
|||
pkg3.build_string = "bld";
|
||||
pkg3.build_number = 4;
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg1, pkg2, pkg3 });
|
||||
db.add_repo_from_packages(
|
||||
std::array{ pkg1, pkg2, pkg3 },
|
||||
"repo",
|
||||
libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "foo[build=bld]"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
|
@ -1172,13 +1307,13 @@ namespace
|
|||
pkg.version = "=*,=*";
|
||||
pkg.build_string = "pyhd*";
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg });
|
||||
db.add_repo_from_packages(std::array{ pkg }, "repo", libsolv::PipAsPythonDependency::No);
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Install{ "foo[version='=*,=*', build='pyhd*']"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
const auto outcome = libsolv::Solver().solve(db, request, matchspec_parser);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<libsolv::UnSolvable>(outcome.value()));
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace
|
|||
{
|
||||
using namespace specs::match_spec_literals;
|
||||
|
||||
TEST_CASE("Create a request")
|
||||
TEST_CASE("Create a request", "[mamba::solver]")
|
||||
{
|
||||
auto request = Request{
|
||||
{},
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace
|
|||
{
|
||||
using PackageInfo = specs::PackageInfo;
|
||||
|
||||
TEST_CASE("Create a Solution")
|
||||
TEST_CASE("Create a Solution", "[mamba::solver]")
|
||||
{
|
||||
auto solution = Solution{ {
|
||||
Solution::Omit{ PackageInfo("omit") },
|
||||
|
|
|
@ -33,6 +33,13 @@ namespace mambapy
|
|||
.def(py::init(&enum_from_str<RepodataParser>));
|
||||
py::implicitly_convertible<py::str, RepodataParser>();
|
||||
|
||||
py::enum_<MatchSpecParser>(m, "MatchSpecParser")
|
||||
.value("Mixed", MatchSpecParser::Mixed)
|
||||
.value("Mamba", MatchSpecParser::Mamba)
|
||||
.value("Libsolv", MatchSpecParser::Libsolv)
|
||||
.def(py::init(&enum_from_str<MatchSpecParser>));
|
||||
py::implicitly_convertible<py::str, MatchSpecParser>();
|
||||
|
||||
py::enum_<PipAsPythonDependency>(m, "PipAsPythonDependency")
|
||||
.value("No", PipAsPythonDependency::No)
|
||||
.value("Yes", PipAsPythonDependency::Yes)
|
||||
|
@ -117,7 +124,21 @@ namespace mambapy
|
|||
.def("__deepcopy__", &deepcopy<RepoInfo>, py::arg("memo"));
|
||||
|
||||
py::class_<Database>(m, "Database")
|
||||
.def(py::init<specs::ChannelResolveParams>(), py::arg("channel_params"))
|
||||
.def(
|
||||
py::init(
|
||||
[](specs::ChannelResolveParams channel_params, MatchSpecParser matchspec_parser)
|
||||
{
|
||||
return Database(
|
||||
channel_params,
|
||||
Database::Settings{
|
||||
matchspec_parser,
|
||||
}
|
||||
);
|
||||
}
|
||||
),
|
||||
py::arg("channel_params"),
|
||||
py::arg("matchspec_parser") = MatchSpecParser::Libsolv
|
||||
)
|
||||
.def("set_logger", &Database::set_logger, py::call_guard<py::gil_scoped_acquire>())
|
||||
.def(
|
||||
"add_repo_from_repodata_json",
|
||||
|
@ -235,12 +256,15 @@ namespace mambapy
|
|||
constexpr auto solver_job_v2_migrator = [](Solver&, py::args, py::kwargs)
|
||||
{ throw std::runtime_error("All jobs need to be passed in the libmambapy.solver.Request."); };
|
||||
|
||||
py::class_<Solver>(m, "Solver") //
|
||||
py::class_<Solver>(m, "Solver")
|
||||
.def(py::init())
|
||||
.def(
|
||||
"solve",
|
||||
[](Solver& self, Database& database, const solver::Request& request)
|
||||
{ return self.solve(database, request); }
|
||||
[](Solver& self, Database& database, const solver::Request& request, MatchSpecParser ms_parser
|
||||
) { return self.solve(database, request, ms_parser); },
|
||||
py::arg("database"),
|
||||
py::arg("request"),
|
||||
py::arg("matchspec_parser") = MatchSpecParser::Mixed
|
||||
)
|
||||
.def("add_jobs", solver_job_v2_migrator)
|
||||
.def("add_global_job", solver_job_v2_migrator)
|
||||
|
|
|
@ -3,14 +3,14 @@ import libmambapy
|
|||
|
||||
def test_context_instance_scoped():
|
||||
ctx = libmambapy.Context() # Initialize and then terminate libmamba internals
|
||||
return ctx
|
||||
assert ctx is not None
|
||||
|
||||
|
||||
def test_context_no_log_nor_signal_handling():
|
||||
ctx = libmambapy.Context(
|
||||
libmambapy.ContextOptions(enable_logging=False, enable_signal_handling=False)
|
||||
)
|
||||
return ctx
|
||||
assert ctx is not None
|
||||
|
||||
|
||||
def test_channel_context():
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import copy
|
||||
import json
|
||||
import itertools
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -32,6 +31,17 @@ def test_RepodataParser():
|
|||
libsolv.RepodataParser("NoParser")
|
||||
|
||||
|
||||
def test_MatchSpecParser():
|
||||
assert libsolv.MatchSpecParser.Mamba.name == "Mamba"
|
||||
assert libsolv.MatchSpecParser.Libsolv.name == "Libsolv"
|
||||
assert libsolv.MatchSpecParser.Mixed.name == "Mixed"
|
||||
|
||||
assert libsolv.MatchSpecParser("Libsolv") == libsolv.MatchSpecParser.Libsolv
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
libsolv.MatchSpecParser("NoParser")
|
||||
|
||||
|
||||
def test_PipASPythonDependency():
|
||||
assert libsolv.PipAsPythonDependency.No.name == "No"
|
||||
assert libsolv.PipAsPythonDependency.Yes.name == "Yes"
|
||||
|
@ -129,8 +139,12 @@ def test_Database_logger():
|
|||
|
||||
|
||||
@pytest.mark.parametrize("add_pip_as_python_dependency", [True, False])
|
||||
def test_Database_RepoInfo_from_packages(add_pip_as_python_dependency):
|
||||
db = libsolv.Database(libmambapy.specs.ChannelResolveParams())
|
||||
@pytest.mark.parametrize("matchspec_parser", ["Mixed", "Mamba", "Libsolv"])
|
||||
def test_Database_RepoInfo_from_packages(add_pip_as_python_dependency, matchspec_parser):
|
||||
db = libsolv.Database(
|
||||
libmambapy.specs.ChannelResolveParams(),
|
||||
matchspec_parser=matchspec_parser,
|
||||
)
|
||||
assert db.repo_count() == 0
|
||||
assert db.installed_repo() is None
|
||||
assert db.package_count() == 0
|
||||
|
@ -193,31 +207,43 @@ def tmp_repodata_json(tmp_path):
|
|||
return file
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
["add_pip_as_python_dependency", "package_types", "repodata_parser"],
|
||||
itertools.product(
|
||||
[True, False],
|
||||
["TarBz2Only", "CondaOrElseTarBz2"],
|
||||
["Mamba", "Libsolv"],
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("add_pip_as_python_dependency", [True, False])
|
||||
@pytest.mark.parametrize("package_types", ["TarBz2Only", "CondaOrElseTarBz2"])
|
||||
@pytest.mark.parametrize("repodata_parser", ["Mamba", "Libsolv"])
|
||||
@pytest.mark.parametrize("matchspec_parser", ["Mixed", "Mamba", "Libsolv"])
|
||||
def test_Database_RepoInfo_from_repodata(
|
||||
tmp_path, tmp_repodata_json, add_pip_as_python_dependency, package_types, repodata_parser
|
||||
tmp_path,
|
||||
tmp_repodata_json,
|
||||
add_pip_as_python_dependency,
|
||||
package_types,
|
||||
repodata_parser,
|
||||
matchspec_parser,
|
||||
):
|
||||
db = libsolv.Database(libmambapy.specs.ChannelResolveParams())
|
||||
db = libsolv.Database(
|
||||
libmambapy.specs.ChannelResolveParams(),
|
||||
matchspec_parser=matchspec_parser,
|
||||
)
|
||||
|
||||
url = "https://repo.mamba.pm"
|
||||
channel_id = "conda-forge"
|
||||
|
||||
# Json
|
||||
repo = db.add_repo_from_repodata_json(
|
||||
path=tmp_repodata_json,
|
||||
url=url,
|
||||
channel_id=channel_id,
|
||||
add_pip_as_python_dependency=add_pip_as_python_dependency,
|
||||
package_types=package_types,
|
||||
repodata_parser=repodata_parser,
|
||||
)
|
||||
def add_repo_json():
|
||||
return db.add_repo_from_repodata_json(
|
||||
path=tmp_repodata_json,
|
||||
url=url,
|
||||
channel_id=channel_id,
|
||||
add_pip_as_python_dependency=add_pip_as_python_dependency,
|
||||
package_types=package_types,
|
||||
repodata_parser=repodata_parser,
|
||||
)
|
||||
|
||||
if (repodata_parser == "Libsolv") and (matchspec_parser != "Libsolv"):
|
||||
with pytest.raises(libmambapy.MambaNativeException) as e:
|
||||
add_repo_json()
|
||||
assert "Libsolv repodata parser can only be used with Libsolv MatchSpec parser" in e
|
||||
return
|
||||
|
||||
repo = add_repo_json()
|
||||
db.set_installed_repo(repo)
|
||||
|
||||
assert repo.package_count() == 1 if package_types in ["TarBz2Only", "CondaOnly"] else 2
|
||||
|
@ -248,7 +274,7 @@ def test_Database_RepoInfo_from_repodata(
|
|||
assert repo_loaded.package_count() == 1 if package_types in ["TarBz2Only", "CondaOnly"] else 2
|
||||
|
||||
|
||||
def test_Database_RepoInfo_from_repodata_error():
|
||||
def test_Database_RepoInfo_from_repodata_missing():
|
||||
db = libsolv.Database(libmambapy.specs.ChannelResolveParams())
|
||||
channel_id = "conda-forge"
|
||||
|
||||
|
|
Loading…
Reference in New Issue