mirror of https://github.com/mamba-org/mamba.git
532 lines
18 KiB
Python
532 lines
18 KiB
Python
import os
|
|
import platform
|
|
import shutil
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
# Need to import everything to get fixtures
|
|
from .helpers import * # noqa: F403
|
|
from . import helpers
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
helpers.dry_run_tests == helpers.DryRun.ULTRA_DRY, reason="Running ultra dry tests"
|
|
)
|
|
class TestUpdate:
|
|
current_root_prefix = os.environ["MAMBA_ROOT_PREFIX"]
|
|
current_prefix = os.environ["CONDA_PREFIX"]
|
|
|
|
env_name = helpers.random_string()
|
|
root_prefix = os.path.expanduser(os.path.join("~", "tmproot" + helpers.random_string()))
|
|
prefix = os.path.join(root_prefix, "envs", env_name)
|
|
old_version = "0.21.10"
|
|
medium_old_version = "0.22"
|
|
|
|
@staticmethod
|
|
@pytest.fixture(scope="class")
|
|
def root(existing_cache):
|
|
os.environ["MAMBA_ROOT_PREFIX"] = TestUpdate.root_prefix
|
|
os.environ["CONDA_PREFIX"] = TestUpdate.prefix
|
|
|
|
yield
|
|
|
|
os.environ["MAMBA_ROOT_PREFIX"] = TestUpdate.current_root_prefix
|
|
os.environ["CONDA_PREFIX"] = TestUpdate.current_prefix
|
|
shutil.rmtree(TestUpdate.root_prefix)
|
|
|
|
@staticmethod
|
|
@pytest.fixture
|
|
def env_created(root):
|
|
if helpers.dry_run_tests == helpers.DryRun.OFF:
|
|
helpers.create(
|
|
f"xtensor={TestUpdate.old_version}",
|
|
"-n",
|
|
TestUpdate.env_name,
|
|
"--json",
|
|
no_dry_run=True,
|
|
)
|
|
res = helpers.umamba_list("xtensor", "-n", TestUpdate.env_name, "--json")
|
|
assert len(res) == 1
|
|
assert res[0]["version"].startswith(TestUpdate.old_version)
|
|
|
|
yield TestUpdate.env_name
|
|
|
|
shutil.rmtree(TestUpdate.prefix)
|
|
|
|
def test_constrained_update(self, env_created):
|
|
update_res = helpers.update(
|
|
"xtensor<=" + self.medium_old_version, "-n", env_created, "--json"
|
|
)
|
|
xtensor_link = [
|
|
to_link for to_link in update_res["actions"]["LINK"] if to_link["name"] == "xtensor"
|
|
][0]
|
|
|
|
assert xtensor_link["version"].startswith(self.medium_old_version)
|
|
|
|
# test that we relink noarch packages
|
|
def test_update_python_noarch(self, root):
|
|
if helpers.dry_run_tests == helpers.DryRun.OFF:
|
|
helpers.create(
|
|
"python=3.9",
|
|
"six",
|
|
"requests",
|
|
"-n",
|
|
TestUpdate.env_name,
|
|
"--json",
|
|
no_dry_run=True,
|
|
)
|
|
else:
|
|
return
|
|
|
|
res = helpers.umamba_list("python", "-n", TestUpdate.env_name, "--json")
|
|
assert len(res) >= 1
|
|
pyelem = [r for r in res if r["name"] == "python"][0]
|
|
assert pyelem["version"].startswith("3.9")
|
|
|
|
res = helpers.umamba_list("requests", "-n", TestUpdate.env_name, "--json")
|
|
prev_requests = [r for r in res if r["name"] == "requests"][0]
|
|
assert prev_requests["version"]
|
|
|
|
def site_packages_path(p, pyver):
|
|
if platform.system() == "Windows":
|
|
return os.path.join(self.prefix, "Lib\\site-packages\\", p)
|
|
else:
|
|
return os.path.join(self.prefix, f"lib/python{pyver}/site-packages", p)
|
|
|
|
assert os.path.exists(site_packages_path("requests/__pycache__", "3.9"))
|
|
|
|
prev_six = helpers.umamba_list("six", "-n", TestUpdate.env_name, "--json")[0]
|
|
|
|
update_res = helpers.update("-n", TestUpdate.env_name, "python=3.10", "--json")
|
|
|
|
six_link = [
|
|
to_link for to_link in update_res["actions"]["LINK"] if to_link["name"] == "six"
|
|
][0]
|
|
|
|
assert six_link["version"] == prev_six["version"]
|
|
assert six_link["build_string"] == prev_six["build_string"]
|
|
|
|
requests_link = [
|
|
to_link for to_link in update_res["actions"]["LINK"] if to_link["name"] == "requests"
|
|
][0]
|
|
requests_unlink = [
|
|
to_link for to_link in update_res["actions"]["UNLINK"] if to_link["name"] == "requests"
|
|
][0]
|
|
|
|
assert requests_link["version"] == requests_unlink["version"]
|
|
if platform.system() != "Windows":
|
|
assert not os.path.exists(site_packages_path("", "3.9"))
|
|
assert os.path.exists(site_packages_path("requests/__pycache__", "3.10"))
|
|
|
|
assert requests_link["version"] == prev_requests["version"]
|
|
assert requests_link["build_string"] == prev_requests["build_string"]
|
|
|
|
def test_further_constrained_update(self, env_created):
|
|
update_res = helpers.update("xtensor==0.24.5=*_0", "--json")
|
|
xtensor_link = [
|
|
to_link for to_link in update_res["actions"]["LINK"] if to_link["name"] == "xtensor"
|
|
][0]
|
|
|
|
assert xtensor_link["version"] == "0.24.5"
|
|
assert xtensor_link["build_number"] == 0
|
|
|
|
def test_classic_spec(self, env_created):
|
|
update_res = helpers.update("xtensor", "--json", "-n", TestUpdate.env_name)
|
|
|
|
xtensor_link = [
|
|
to_link for to_link in update_res["actions"]["LINK"] if to_link["name"] == "xtensor"
|
|
][0]
|
|
assert TestUpdate.old_version != xtensor_link["version"]
|
|
|
|
if helpers.dry_run_tests == helpers.DryRun.OFF:
|
|
pkg = helpers.get_concrete_pkg(update_res, "xtensor")
|
|
pkg_info = helpers.get_concrete_pkg_info(helpers.get_env(TestUpdate.env_name), pkg)
|
|
version = pkg_info["version"]
|
|
|
|
assert TestUpdate.old_version != version
|
|
|
|
# This should do nothing since python is not installed!
|
|
update_res = helpers.update("python", "-n", TestUpdate.env_name, "--json")
|
|
|
|
# TODO fix this?!
|
|
assert update_res["message"] == "All requested packages already installed"
|
|
assert update_res["success"] is True
|
|
assert "action" not in update_res
|
|
|
|
def test_update_all(self, env_created):
|
|
update_res = helpers.update("--all", "--json")
|
|
|
|
xtensor_link = [
|
|
to_link for to_link in update_res["actions"]["LINK"] if to_link["name"] == "xtensor"
|
|
][0]
|
|
assert TestUpdate.old_version != xtensor_link["version"]
|
|
|
|
if helpers.dry_run_tests == helpers.DryRun.OFF:
|
|
pkg = helpers.get_concrete_pkg(update_res, "xtensor")
|
|
pkg_info = helpers.get_concrete_pkg_info(helpers.get_env(TestUpdate.env_name), pkg)
|
|
version = pkg_info["version"]
|
|
|
|
assert TestUpdate.old_version != version
|
|
|
|
with open(Path(self.prefix) / "conda-meta" / "history") as h:
|
|
history = h.readlines()
|
|
print("".join(history))
|
|
for el in reversed(history):
|
|
x = el.strip()
|
|
if x.startswith(">=="):
|
|
break
|
|
assert not x.startswith("update specs:")
|
|
|
|
@pytest.mark.parametrize(
|
|
"alias",
|
|
[
|
|
None,
|
|
"https://conda.anaconda.org/",
|
|
"https://repo.mamba.pm/",
|
|
"https://repo.mamba.pm",
|
|
],
|
|
)
|
|
def test_channel_alias(self, alias, env_created):
|
|
if alias:
|
|
res = helpers.update(
|
|
"-n",
|
|
TestUpdate.env_name,
|
|
"xtensor",
|
|
"--json",
|
|
"--dry-run",
|
|
"--channel-alias",
|
|
alias,
|
|
)
|
|
else:
|
|
res = helpers.update("-n", TestUpdate.env_name, "xtensor", "--json", "--dry-run")
|
|
|
|
for to_link in res["actions"]["LINK"]:
|
|
assert to_link["channel"] == "conda-forge"
|
|
|
|
@pytest.mark.parametrize("output_flag", ["", "--json", "--quiet"])
|
|
def test_update_check_logs(self, env_created, output_flag):
|
|
res = helpers.update("-n", TestUpdate.env_name, "xtensor=0.24.5", output_flag)
|
|
|
|
if output_flag == "--json":
|
|
assert res["success"]
|
|
elif output_flag == "--quiet":
|
|
assert res == ""
|
|
else:
|
|
assert "To activate this environment, use:" not in res
|
|
|
|
|
|
class TestUpdateConfig:
|
|
current_root_prefix = os.environ["MAMBA_ROOT_PREFIX"]
|
|
current_prefix = os.environ["CONDA_PREFIX"]
|
|
|
|
env_name = helpers.random_string()
|
|
root_prefix = os.path.expanduser(os.path.join("~", "tmproot" + helpers.random_string()))
|
|
prefix = os.path.join(root_prefix, "envs", env_name)
|
|
|
|
@staticmethod
|
|
@pytest.fixture(scope="class")
|
|
def root(existing_cache):
|
|
os.environ["MAMBA_ROOT_PREFIX"] = TestUpdateConfig.root_prefix
|
|
os.environ["CONDA_PREFIX"] = TestUpdateConfig.prefix
|
|
helpers.create("-n", "base", no_dry_run=True)
|
|
helpers.create("-n", TestUpdateConfig.env_name, "--offline", no_dry_run=True)
|
|
|
|
yield
|
|
|
|
os.environ["MAMBA_ROOT_PREFIX"] = TestUpdateConfig.current_root_prefix
|
|
os.environ["CONDA_PREFIX"] = TestUpdateConfig.current_prefix
|
|
shutil.rmtree(TestUpdateConfig.root_prefix)
|
|
|
|
@staticmethod
|
|
@pytest.fixture
|
|
def env_created(root):
|
|
os.environ["MAMBA_ROOT_PREFIX"] = TestUpdateConfig.root_prefix
|
|
os.environ["CONDA_PREFIX"] = TestUpdateConfig.prefix
|
|
|
|
yield
|
|
|
|
for v in ("CONDA_CHANNELS", "MAMBA_TARGET_PREFIX"):
|
|
if v in os.environ:
|
|
os.environ.pop(v)
|
|
|
|
@classmethod
|
|
def config_tests(cls, res, root_prefix=root_prefix, target_prefix=prefix):
|
|
assert res["root_prefix"] == root_prefix
|
|
assert res["target_prefix"] == target_prefix
|
|
assert res["use_target_prefix_fallback"]
|
|
assert res["use_default_prefix_fallback"]
|
|
assert res["use_root_prefix_fallback"]
|
|
checks = (
|
|
helpers.MAMBA_ALLOW_EXISTING_PREFIX
|
|
| helpers.MAMBA_NOT_ALLOW_MISSING_PREFIX
|
|
| helpers.MAMBA_NOT_ALLOW_NOT_ENV_PREFIX
|
|
| helpers.MAMBA_EXPECT_EXISTING_PREFIX
|
|
)
|
|
assert res["target_prefix_checks"] == checks
|
|
|
|
@pytest.mark.parametrize(
|
|
"source,file_type",
|
|
[
|
|
("cli_only", None),
|
|
("spec_file_only", "classic"),
|
|
("spec_file_only", "explicit"),
|
|
("spec_file_only", "yaml"),
|
|
("both", "classic"),
|
|
("both", "explicit"),
|
|
("both", "yaml"),
|
|
],
|
|
)
|
|
def test_specs(self, source, file_type, env_created):
|
|
cmd = []
|
|
specs = []
|
|
|
|
if source in ("cli_only", "both"):
|
|
specs = ["xtensor-python", "xtl"]
|
|
cmd = list(specs)
|
|
|
|
if source in ("spec_file_only", "both"):
|
|
f_name = helpers.random_string()
|
|
spec_file = os.path.join(TestUpdateConfig.root_prefix, f_name)
|
|
|
|
if file_type == "classic":
|
|
file_content = ["xtensor >0.20", "xsimd"]
|
|
specs += file_content
|
|
elif file_type == "explicit":
|
|
explicit_specs = [
|
|
"https://conda.anaconda.org/conda-forge/linux-64/xtensor-0.21.5-hc9558a2_0.tar.bz2#d330e02e5ed58330638a24601b7e4887",
|
|
"https://conda.anaconda.org/conda-forge/linux-64/xsimd-7.4.8-hc9558a2_0.tar.bz2#32d5b7ad7d6511f1faacf87e53a63e5f",
|
|
]
|
|
file_content = ["@EXPLICIT"] + explicit_specs
|
|
specs = explicit_specs
|
|
else: # yaml
|
|
spec_file += ".yaml"
|
|
file_content = ["dependencies:", " - xtensor >0.20", " - xsimd"]
|
|
specs += ["xtensor >0.20", "xsimd"]
|
|
|
|
with open(spec_file, "w") as f:
|
|
f.write("\n".join(file_content))
|
|
|
|
cmd += ["-f", spec_file]
|
|
|
|
res = helpers.install(*cmd, "--print-config-only")
|
|
|
|
TestUpdateConfig.config_tests(res)
|
|
assert res["env_name"] == ""
|
|
assert res["specs"] == specs
|
|
|
|
@pytest.mark.parametrize("root_prefix", (None, "env_var", "cli"))
|
|
@pytest.mark.parametrize("target_is_root", (False, True))
|
|
@pytest.mark.parametrize("cli_prefix", (False, True))
|
|
@pytest.mark.parametrize("cli_env_name", (False, True))
|
|
@pytest.mark.parametrize("yaml_name", (False, True, "prefix"))
|
|
@pytest.mark.parametrize("env_var", (False, True))
|
|
@pytest.mark.parametrize("current_target_prefix_fallback", (False, True))
|
|
def test_target_prefix(
|
|
self,
|
|
root_prefix,
|
|
target_is_root,
|
|
cli_prefix,
|
|
cli_env_name,
|
|
yaml_name,
|
|
env_var,
|
|
current_target_prefix_fallback,
|
|
env_created,
|
|
):
|
|
cmd = []
|
|
|
|
if root_prefix in (None, "cli"):
|
|
os.environ["MAMBA_DEFAULT_ROOT_PREFIX"] = os.environ.pop("MAMBA_ROOT_PREFIX")
|
|
|
|
if root_prefix == "cli":
|
|
cmd += ["-r", TestUpdateConfig.root_prefix]
|
|
|
|
r = TestUpdateConfig.root_prefix
|
|
|
|
if target_is_root:
|
|
p = r
|
|
n = "base"
|
|
else:
|
|
p = TestUpdateConfig.prefix
|
|
n = TestUpdateConfig.env_name
|
|
|
|
expected_p = p
|
|
|
|
if cli_prefix:
|
|
cmd += ["-p", p]
|
|
|
|
if cli_env_name:
|
|
cmd += ["-n", n]
|
|
|
|
if yaml_name:
|
|
f_name = helpers.random_string() + ".yaml"
|
|
spec_file = os.path.join(TestUpdateConfig.prefix, f_name)
|
|
|
|
if yaml_name == "prefix":
|
|
yaml_n = p
|
|
else:
|
|
yaml_n = n
|
|
if not (cli_prefix or cli_env_name or target_is_root):
|
|
expected_p = os.path.join(TestUpdateConfig.root_prefix, "envs", yaml_n)
|
|
|
|
file_content = [
|
|
f"name: {yaml_n}",
|
|
"dependencies: [xtensor]",
|
|
]
|
|
with open(spec_file, "w") as f:
|
|
f.write("\n".join(file_content))
|
|
|
|
cmd += ["-f", spec_file]
|
|
|
|
if env_var:
|
|
os.environ["MAMBA_TARGET_PREFIX"] = p
|
|
|
|
if not current_target_prefix_fallback:
|
|
os.environ.pop("CONDA_PREFIX")
|
|
os.environ.pop("CONDA_DEFAULT_ENV")
|
|
else:
|
|
os.environ["CONDA_PREFIX"] = p
|
|
|
|
if (cli_prefix and cli_env_name) or (yaml_name == "prefix"):
|
|
with pytest.raises(helpers.subprocess.CalledProcessError):
|
|
helpers.install(*cmd, "--print-config-only")
|
|
elif not (
|
|
cli_prefix or cli_env_name or yaml_name or env_var or current_target_prefix_fallback
|
|
):
|
|
# Fallback on root prefix
|
|
res = helpers.install(*cmd, "--print-config-only")
|
|
TestUpdateConfig.config_tests(res, root_prefix=r, target_prefix=r)
|
|
else:
|
|
res = helpers.install(*cmd, "--print-config-only")
|
|
TestUpdateConfig.config_tests(res, root_prefix=r, target_prefix=expected_p)
|
|
|
|
def test_target_prefix_with_no_settings(
|
|
self,
|
|
existing_cache,
|
|
):
|
|
# Specify no arg
|
|
cmd = []
|
|
|
|
# Get the actual set MAMBA_ROOT_PREFIX when setting up `TestUpdateConfig` class
|
|
os.environ["MAMBA_DEFAULT_ROOT_PREFIX"] = os.environ.pop("MAMBA_ROOT_PREFIX")
|
|
os.environ.pop("CONDA_PREFIX")
|
|
os.environ.pop("CONDA_DEFAULT_ENV")
|
|
|
|
# Fallback on root prefix
|
|
res = helpers.install(*cmd, "--print-config-only")
|
|
TestUpdateConfig.config_tests(
|
|
res,
|
|
root_prefix=TestUpdateConfig.root_prefix,
|
|
target_prefix=TestUpdateConfig.root_prefix,
|
|
)
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform == "win32",
|
|
reason="MAMBA_ROOT_PREFIX is set in windows GH workflow",
|
|
)
|
|
def test_target_prefix_with_no_settings_and_no_env_var(
|
|
self,
|
|
existing_cache,
|
|
):
|
|
# Specify no arg
|
|
cmd = []
|
|
|
|
os.environ.pop("MAMBA_ROOT_PREFIX")
|
|
os.environ.pop("CONDA_PREFIX")
|
|
os.environ.pop("CONDA_DEFAULT_ENV")
|
|
|
|
# Fallback on root prefix
|
|
res = helpers.install(*cmd, "--print-config-only")
|
|
|
|
TestUpdateConfig.config_tests(
|
|
res,
|
|
root_prefix=TestUpdateConfig.current_root_prefix,
|
|
target_prefix=TestUpdateConfig.current_root_prefix,
|
|
)
|
|
|
|
@pytest.mark.parametrize("cli", (False, True))
|
|
@pytest.mark.parametrize("yaml", (False, True))
|
|
@pytest.mark.parametrize("env_var", (False, True))
|
|
@pytest.mark.parametrize("rc_file", (False, True))
|
|
def test_channels(self, cli, yaml, env_var, rc_file, env_created):
|
|
cmd = []
|
|
expected_channels = []
|
|
|
|
if cli:
|
|
cmd += ["-c", "cli"]
|
|
expected_channels += ["cli"]
|
|
|
|
if yaml:
|
|
f_name = helpers.random_string() + ".yaml"
|
|
spec_file = os.path.join(TestUpdateConfig.prefix, f_name)
|
|
|
|
file_content = [
|
|
"channels: [yaml]",
|
|
"dependencies: [xtensor]",
|
|
]
|
|
with open(spec_file, "w") as f:
|
|
f.write("\n".join(file_content))
|
|
|
|
cmd += ["-f", spec_file]
|
|
expected_channels += ["yaml"]
|
|
|
|
if env_var:
|
|
os.environ["CONDA_CHANNELS"] = "env_var"
|
|
expected_channels += ["env_var"]
|
|
|
|
if rc_file:
|
|
f_name = helpers.random_string() + ".yaml"
|
|
rc_file = os.path.join(TestUpdateConfig.prefix, f_name)
|
|
|
|
file_content = ["channels: [rc]"]
|
|
with open(rc_file, "w") as f:
|
|
f.write("\n".join(file_content))
|
|
|
|
cmd += ["--rc-file", rc_file]
|
|
expected_channels += ["rc"]
|
|
|
|
res = helpers.install(*cmd, "--print-config-only", no_rc=not rc_file, default_channel=False)
|
|
TestUpdateConfig.config_tests(res)
|
|
if expected_channels:
|
|
assert res["channels"] == expected_channels
|
|
else:
|
|
assert res["channels"] == ["conda-forge"]
|
|
|
|
@pytest.mark.parametrize("type", ("yaml", "classic", "explicit"))
|
|
def test_multiple_spec_files(self, type, env_created):
|
|
cmd = []
|
|
specs = ["xtensor", "xsimd"]
|
|
explicit_specs = [
|
|
"https://conda.anaconda.org/conda-forge/linux-64/xtensor-0.21.5-hc9558a2_0.tar.bz2#d330e02e5ed58330638a24601b7e4887",
|
|
"https://conda.anaconda.org/conda-forge/linux-64/xsimd-7.4.8-hc9558a2_0.tar.bz2#32d5b7ad7d6511f1faacf87e53a63e5f",
|
|
]
|
|
|
|
for i in range(2):
|
|
f_name = helpers.random_string()
|
|
file = os.path.join(TestUpdateConfig.prefix, f_name)
|
|
|
|
if type == "yaml":
|
|
file += ".yaml"
|
|
file_content = [f"dependencies: [{specs[i]}]"]
|
|
elif type == "classic":
|
|
file_content = [specs[i]]
|
|
else: # explicit
|
|
file_content = ["@EXPLICIT", explicit_specs[i]]
|
|
|
|
with open(file, "w") as f:
|
|
f.write("\n".join(file_content))
|
|
|
|
cmd += ["-f", file]
|
|
|
|
res = helpers.install(*cmd, "--print-config-only")
|
|
if type == "yaml" or type == "classic":
|
|
assert res["specs"] == specs
|
|
else: # explicit
|
|
assert res["specs"] == [explicit_specs[0]]
|
|
|
|
def test_channel_specific(self, env_created):
|
|
helpers.install("quantstack::sphinx", no_dry_run=True)
|
|
res = helpers.update("quantstack::sphinx", "-c", "conda-forge", "--json")
|
|
assert "actions" not in res
|