mirror of https://github.com/mamba-org/mamba.git
301 lines
11 KiB
Python
301 lines
11 KiB
Python
import os
|
|
import sys
|
|
import platform
|
|
import subprocess
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
# Need to import everything to get fixtures
|
|
from .helpers import * # noqa: F403
|
|
from . import helpers
|
|
|
|
__this_dir__ = Path(__file__).parent.resolve()
|
|
|
|
|
|
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
|
|
@pytest.mark.parametrize("env_selector", ["", "name", "prefix"])
|
|
def test_remove(tmp_home, tmp_root_prefix, env_selector, tmp_xtensor_env, tmp_env_name):
|
|
env_pkgs = [p["name"] for p in helpers.umamba_list("-p", tmp_xtensor_env, "--json")]
|
|
|
|
if env_selector == "prefix":
|
|
res = helpers.remove("xtensor", "-p", tmp_xtensor_env, "--json")
|
|
elif env_selector == "name":
|
|
res = helpers.remove("xtensor", "-n", tmp_env_name, "--json")
|
|
else:
|
|
res = helpers.remove("xtensor", "--dry-run", "--json")
|
|
|
|
keys = {"dry_run", "success", "prefix", "actions"}
|
|
assert keys.issubset(set(res.keys()))
|
|
assert res["success"]
|
|
assert len(res["actions"]["UNLINK"]) == len(env_pkgs)
|
|
for p in res["actions"]["UNLINK"]:
|
|
assert p["name"] in env_pkgs
|
|
assert res["actions"]["PREFIX"] == str(tmp_xtensor_env)
|
|
|
|
|
|
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
|
|
@pytest.mark.skipif(sys.platform == "win32", reason="This test is currently failing on Windows")
|
|
def test_remove_orphaned(tmp_home, tmp_root_prefix, tmp_xtensor_env, tmp_env_name):
|
|
env_pkgs = [p["name"] for p in helpers.umamba_list("-p", tmp_xtensor_env, "--json")]
|
|
helpers.install("xtensor-python", "-n", tmp_env_name, no_dry_run=True)
|
|
|
|
res = helpers.remove("xtensor-python", "-p", tmp_xtensor_env, "--json")
|
|
|
|
keys = {"dry_run", "success", "prefix", "actions"}
|
|
assert keys.issubset(set(res.keys()))
|
|
assert res["success"]
|
|
|
|
assert len(res["actions"]["UNLINK"]) > 1
|
|
assert res["actions"]["UNLINK"][0]["name"] == "xtensor-python"
|
|
assert res["actions"]["PREFIX"] == str(tmp_xtensor_env)
|
|
|
|
res = helpers.remove("xtensor", "-p", tmp_xtensor_env, "--json")
|
|
|
|
keys = {"dry_run", "success", "prefix", "actions"}
|
|
assert keys.issubset(set(res.keys()))
|
|
assert res["success"]
|
|
# TODO: find a better use case so we can revert to len(env_pkgs) instead
|
|
# of magic number
|
|
# assert len(res["actions"]["UNLINK"]) == len(env_pkgs) + (
|
|
assert len(res["actions"]["UNLINK"]) == 3 + (
|
|
1 if helpers.dry_run_tests == helpers.DryRun.DRY else 0
|
|
) + (platform.system() == "Linux") # xtl is not removed on Linux
|
|
for p in res["actions"]["UNLINK"]:
|
|
assert p["name"] in env_pkgs
|
|
assert res["actions"]["PREFIX"] == str(tmp_xtensor_env)
|
|
|
|
|
|
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
|
|
def test_remove_force(tmp_home, tmp_root_prefix, tmp_xtensor_env, tmp_env_name):
|
|
# check that we can remove a package without solving the environment (putting
|
|
# it in a bad state, actually)
|
|
helpers.install("xtensor-python", "-n", tmp_env_name, no_dry_run=True)
|
|
|
|
res = helpers.remove("xtl", "-p", str(tmp_xtensor_env), "--json", "--force")
|
|
|
|
keys = {"dry_run", "success", "prefix", "actions"}
|
|
assert keys.issubset(set(res.keys()))
|
|
assert res["success"]
|
|
assert len(res["actions"]["UNLINK"]) == 1
|
|
assert res["actions"]["UNLINK"][0]["name"] == "xtl"
|
|
assert res["actions"]["PREFIX"] == str(tmp_xtensor_env)
|
|
|
|
|
|
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
|
|
def test_remove_no_prune_deps(tmp_home, tmp_root_prefix, tmp_xtensor_env, tmp_env_name):
|
|
helpers.install("xtensor-python", "-n", tmp_env_name, no_dry_run=True)
|
|
|
|
res = helpers.remove("xtensor", "-p", tmp_xtensor_env, "--json", "--no-prune-deps")
|
|
|
|
keys = {"dry_run", "success", "prefix", "actions"}
|
|
assert keys.issubset(set(res.keys()))
|
|
assert res["success"]
|
|
assert len(res["actions"]["UNLINK"]) == 2
|
|
removed_names = [x["name"] for x in res["actions"]["UNLINK"]]
|
|
assert "xtensor" in removed_names
|
|
assert "xtensor-python" in removed_names
|
|
assert res["actions"]["PREFIX"] == str(tmp_xtensor_env)
|
|
|
|
|
|
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
|
|
def test_remove_in_use(tmp_home, tmp_root_prefix, tmp_xtensor_env, tmp_env_name):
|
|
helpers.install("python=3.9", "-n", tmp_env_name, "--json", no_dry_run=True)
|
|
if platform.system() == "Windows":
|
|
pyexe = Path(tmp_xtensor_env) / "python.exe"
|
|
else:
|
|
pyexe = Path(tmp_xtensor_env) / "bin" / "python"
|
|
|
|
env = helpers.get_fake_activate(tmp_xtensor_env)
|
|
|
|
pyproc = subprocess.Popen(
|
|
pyexe, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env
|
|
)
|
|
time.sleep(1)
|
|
|
|
helpers.remove("python", "-v", "-p", str(tmp_xtensor_env), no_dry_run=True)
|
|
|
|
if platform.system() == "Windows":
|
|
pyexe_trash = Path(str(pyexe) + ".mamba_trash")
|
|
assert pyexe.exists() is False
|
|
pyexe_trash_exists = pyexe_trash.exists()
|
|
trash_file = Path(tmp_xtensor_env) / "conda-meta" / "mamba_trash.txt"
|
|
|
|
if pyexe_trash_exists:
|
|
assert pyexe_trash.exists()
|
|
assert trash_file.exists()
|
|
all_trash_files = list(Path(tmp_xtensor_env).rglob("*.mamba_trash"))
|
|
|
|
with open(trash_file) as fi:
|
|
lines = [x.strip() for x in fi.readlines()]
|
|
assert all([line.endswith(".mamba_trash") for line in lines])
|
|
assert len(all_trash_files) == len(lines)
|
|
linesp = [Path(tmp_xtensor_env) / line for line in lines]
|
|
for atf in all_trash_files:
|
|
assert atf in linesp
|
|
else:
|
|
assert trash_file.exists() is False
|
|
assert pyexe_trash.exists() is False
|
|
# No change if file still in use
|
|
helpers.install("cpp-filesystem", "-n", tmp_env_name, "--json", no_dry_run=True)
|
|
|
|
if pyexe_trash_exists:
|
|
assert trash_file.exists()
|
|
assert pyexe_trash.exists()
|
|
|
|
with open(trash_file) as fi:
|
|
lines = [x.strip() for x in fi.readlines()]
|
|
assert all([line.endswith(".mamba_trash") for line in lines])
|
|
assert len(all_trash_files) == len(lines)
|
|
linesp = [Path(tmp_xtensor_env) / line for line in lines]
|
|
for atf in all_trash_files:
|
|
assert atf in linesp
|
|
else:
|
|
assert trash_file.exists() is False
|
|
assert pyexe_trash.exists() is False
|
|
|
|
subprocess.Popen(f"TASKKILL /F /PID {pyproc.pid} /T")
|
|
# check that another env mod clears lingering trash files
|
|
time.sleep(0.5)
|
|
helpers.install("xsimd", "-n", tmp_env_name, "--json", no_dry_run=True)
|
|
assert trash_file.exists() is False
|
|
assert pyexe_trash.exists() is False
|
|
|
|
else:
|
|
assert pyexe.exists() is False
|
|
pyproc.kill()
|
|
|
|
|
|
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
|
|
def test_remove_then_clean(tmp_home, tmp_root_prefix):
|
|
env_file = __this_dir__ / "env-requires-pip-install.yaml"
|
|
env_name = "env_to_clean"
|
|
helpers.create("-n", env_name, "-f", env_file, no_dry_run=True)
|
|
helpers.remove("-n", env_name, "pip", no_dry_run=True)
|
|
helpers.clean("-ay", no_dry_run=True)
|
|
|
|
|
|
def remove_config_common_assertions(res, root_prefix, target_prefix):
|
|
assert res["root_prefix"] == str(root_prefix)
|
|
assert res["target_prefix"] == str(target_prefix)
|
|
assert res["use_target_prefix_fallback"]
|
|
assert not res["use_default_prefix_fallback"]
|
|
assert not 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
|
|
|
|
|
|
def test_remove_config_specs(tmp_home, tmp_root_prefix, tmp_prefix):
|
|
specs = ["xtensor-python", "xtl"]
|
|
cmd = list(specs)
|
|
|
|
res = helpers.remove(*cmd, "--print-config-only")
|
|
|
|
remove_config_common_assertions(res, root_prefix=tmp_root_prefix, target_prefix=tmp_prefix)
|
|
assert res["env_name"] == ""
|
|
assert res["specs"] == specs
|
|
|
|
|
|
@pytest.mark.parametrize("use_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("env_var", (False, True))
|
|
@pytest.mark.parametrize("current_target_prefix_fallback", (False, True))
|
|
def test_remove_config_target_prefix(
|
|
tmp_home,
|
|
tmp_root_prefix,
|
|
tmp_env_name,
|
|
tmp_prefix,
|
|
use_root_prefix,
|
|
target_is_root,
|
|
cli_prefix,
|
|
cli_env_name,
|
|
env_var,
|
|
current_target_prefix_fallback,
|
|
):
|
|
(tmp_root_prefix / "conda-meta").mkdir(parents=True, exist_ok=True)
|
|
|
|
cmd = []
|
|
|
|
if use_root_prefix in (None, "cli"):
|
|
os.environ["MAMBA_DEFAULT_ROOT_PREFIX"] = os.environ.pop("MAMBA_ROOT_PREFIX")
|
|
|
|
if use_root_prefix == "cli":
|
|
cmd += ["-r", str(tmp_root_prefix)]
|
|
|
|
r = str(tmp_root_prefix)
|
|
|
|
if target_is_root:
|
|
p = r
|
|
n = "base"
|
|
else:
|
|
p = str(tmp_prefix)
|
|
n = str(tmp_env_name)
|
|
|
|
if cli_prefix:
|
|
cmd += ["-p", p]
|
|
|
|
if cli_env_name:
|
|
cmd += ["-n", n]
|
|
|
|
if env_var:
|
|
os.environ["MAMBA_TARGET_PREFIX"] = p
|
|
|
|
if not current_target_prefix_fallback:
|
|
os.environ.pop("CONDA_PREFIX")
|
|
else:
|
|
os.environ["CONDA_PREFIX"] = p
|
|
|
|
if (cli_prefix and cli_env_name) or not (
|
|
cli_prefix or cli_env_name or env_var or current_target_prefix_fallback
|
|
):
|
|
with pytest.raises(subprocess.CalledProcessError):
|
|
helpers.remove(*cmd, "--print-config-only")
|
|
else:
|
|
res = helpers.remove(*cmd, "--print-config-only")
|
|
remove_config_common_assertions(res, root_prefix=r, target_prefix=p)
|
|
|
|
|
|
def test_uninstall(tmp_home, tmp_root_prefix, tmp_xtensor_env, tmp_env_name):
|
|
# Install xtensor and then uninstall xtensor the first time with the `remove`
|
|
# subcommand and a second time with the `uninstall` subcommand and check that
|
|
# their outputs are the same and that the environment is in the same state
|
|
helpers.create("-n", tmp_env_name, "--json", no_dry_run=True)
|
|
|
|
res_list = helpers.umamba_list("-n", tmp_env_name, "--json")
|
|
n_packages_after_init = len(res_list)
|
|
|
|
# Install xtensor
|
|
helpers.install("xtensor", "-n", tmp_env_name, no_dry_run=True)
|
|
|
|
# Remove xtensor
|
|
res_remove = helpers.remove("xtensor", "-n", tmp_env_name, "--json")
|
|
assert res_remove["success"]
|
|
assert res_remove["actions"]["PREFIX"] == str(tmp_xtensor_env)
|
|
|
|
# Check that the environment does not contain any packages
|
|
res_list = helpers.umamba_list("-n", tmp_env_name, "--json")
|
|
assert len(res_list) == n_packages_after_init
|
|
|
|
# Reinstall xtensor
|
|
helpers.install("xtensor", "-n", tmp_env_name, no_dry_run=True)
|
|
|
|
# Uninstall xtensor
|
|
res_uninstall = helpers.uninstall("xtensor", "-n", tmp_env_name, "--json")
|
|
assert res_uninstall["success"]
|
|
assert res_uninstall["actions"]["PREFIX"] == str(tmp_xtensor_env)
|
|
|
|
# Check that the environment does not contain any packages
|
|
res_list = helpers.umamba_list("-n", tmp_env_name, "--json")
|
|
assert len(res_list) == n_packages_after_init
|
|
|
|
# Check that the outputs of the `remove` and `uninstall` subcommands are the same
|
|
assert res_remove == res_uninstall
|