mirror of https://github.com/mamba-org/mamba.git
194 lines
6.2 KiB
Python
194 lines
6.2 KiB
Python
import copy
|
|
import os
|
|
import pathlib
|
|
import platform
|
|
from typing import Any, Optional
|
|
from collections.abc import Generator, Mapping
|
|
|
|
import pytest
|
|
|
|
from . import helpers
|
|
|
|
####################
|
|
# Config options #
|
|
####################
|
|
|
|
|
|
def pytest_addoption(parser):
|
|
"""Add command line argument to pytest."""
|
|
parser.addoption(
|
|
"--mamba-pkgs-dir",
|
|
action="store",
|
|
default=None,
|
|
help="Package cache to reuse between tests",
|
|
)
|
|
parser.addoption(
|
|
"--no-eager-clean",
|
|
action="store_true",
|
|
default=False,
|
|
help=(
|
|
"Do not eagerly delete temporary folders such as HOME and MAMBA_ROOT_PREFIX"
|
|
"created during tests."
|
|
"These folders take a lot of disk space so we delete them eagerly."
|
|
"For debugging, it can be convenient to keep them."
|
|
"With this option, cleaning will fallback on the default pytest policy."
|
|
),
|
|
)
|
|
|
|
|
|
##################
|
|
# Test fixture #
|
|
##################
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def tmp_environ() -> Generator[Mapping[str, Any], None, None]:
|
|
"""Saves and restore environment variables.
|
|
|
|
This is used for test that need to modify ``os.environ``
|
|
"""
|
|
old_environ = copy.deepcopy(os.environ)
|
|
yield old_environ
|
|
os.environ.clear()
|
|
os.environ.update(old_environ)
|
|
|
|
|
|
@pytest.fixture
|
|
def tmp_home(
|
|
request, tmp_environ, tmp_path_factory: pytest.TempPathFactory
|
|
) -> Generator[pathlib.Path, None, None]:
|
|
"""Change the home directory to a tmp folder for the duration of a test."""
|
|
# Try multiple combination for Unix/Windows
|
|
home_envs = ["HOME", "USERPROFILE"]
|
|
used_homes = [env for env in home_envs if env in os.environ]
|
|
|
|
new_home = pathlib.Path.home()
|
|
if len(used_homes) > 0:
|
|
new_home = tmp_path_factory.mktemp("home")
|
|
new_home.mkdir(parents=True, exist_ok=True)
|
|
for env in used_homes:
|
|
os.environ[env] = str(new_home)
|
|
|
|
if platform.system() == "Windows":
|
|
os.environ["APPDATA"] = str(new_home / "AppData" / "Roaming")
|
|
os.environ["LOCALAPPDATA"] = str(new_home / "AppData" / "Local")
|
|
|
|
yield new_home
|
|
|
|
# Pytest would clean it automatically but this can be large (0.5 Gb for repodata)
|
|
# We clean it explicitly
|
|
on_ci = "CI" in os.environ
|
|
if not request.config.getoption("--no-eager-clean"):
|
|
try:
|
|
helpers.rmtree(new_home)
|
|
# Silence possible cleanup exceptions on CI, weird things happening there
|
|
except Exception as e:
|
|
if not on_ci:
|
|
raise e
|
|
|
|
|
|
@pytest.fixture
|
|
def tmp_clean_env(tmp_environ: None) -> None:
|
|
"""Remove all Conda/Mamba activation artifacts from environment."""
|
|
for k, v in os.environ.items():
|
|
if k.startswith(("CONDA", "_CONDA", "MAMBA", "_MAMBA", "XDG_")):
|
|
del os.environ[k]
|
|
|
|
def keep_in_path(p: str, prefix: Optional[str] = tmp_environ.get("CONDA_PREFIX")) -> bool:
|
|
if "condabin" in p:
|
|
return False
|
|
# On windows, PATH is also used for dyanamic libraries.
|
|
if (prefix is not None) and (platform.system() != "Windows"):
|
|
p = str(pathlib.Path(p).expanduser().resolve())
|
|
prefix = str(pathlib.Path(prefix).expanduser().resolve())
|
|
return not p.startswith(prefix)
|
|
return True
|
|
|
|
path_list = os.environ["PATH"].split(os.pathsep)
|
|
path_list = [p for p in path_list if keep_in_path(p)]
|
|
os.environ["PATH"] = os.pathsep.join(path_list)
|
|
# os.environ restored by tmp_clean_env and tmp_environ
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def tmp_pkgs_dirs(tmp_path_factory: pytest.TempPathFactory, request) -> pathlib.Path:
|
|
"""A common package cache for mamba downloads.
|
|
|
|
The directory is not used automatically when calling this fixture.
|
|
"""
|
|
if (p := request.config.getoption("--mamba-pkgs-dir")) is not None:
|
|
p = pathlib.Path(p)
|
|
p.mkdir(parents=True, exist_ok=True)
|
|
return p
|
|
|
|
return tmp_path_factory.mktemp("pkgs_dirs")
|
|
|
|
|
|
@pytest.fixture(params=[False])
|
|
def shared_pkgs_dirs(request) -> bool:
|
|
"""A dummy fixture to control the use of shared package dir."""
|
|
return request.param
|
|
|
|
|
|
@pytest.fixture
|
|
def tmp_root_prefix(
|
|
request,
|
|
tmp_path_factory: pytest.TempPathFactory,
|
|
tmp_clean_env: None,
|
|
tmp_pkgs_dirs: pathlib.Path,
|
|
shared_pkgs_dirs: bool,
|
|
) -> Generator[pathlib.Path, None, None]:
|
|
"""Change the micromamba root directory to a tmp folder for the duration of a test."""
|
|
new_root_prefix = tmp_path_factory.mktemp("mamba")
|
|
new_root_prefix.mkdir(parents=True, exist_ok=True)
|
|
os.environ["MAMBA_ROOT_PREFIX"] = str(new_root_prefix)
|
|
|
|
if shared_pkgs_dirs:
|
|
os.environ["CONDA_PKGS_DIRS"] = str(tmp_pkgs_dirs)
|
|
|
|
yield new_root_prefix
|
|
|
|
# Pytest would clean it automatically but this can be large (0.5 Gb for repodata)
|
|
# We clean it explicitly
|
|
on_ci = "CI" in os.environ
|
|
if not request.config.getoption("--no-eager-clean"):
|
|
try:
|
|
helpers.rmtree(new_root_prefix)
|
|
# Silence possible cleanup exceptions on CI, weird things happening there
|
|
except Exception as e:
|
|
if not on_ci:
|
|
raise e
|
|
# os.environ restored by tmp_clean_env and tmp_environ
|
|
|
|
|
|
@pytest.fixture(params=[helpers.random_string])
|
|
def tmp_env_name(request) -> str:
|
|
"""Return the explicit or implicit parametrization."""
|
|
if callable(request.param):
|
|
return request.param()
|
|
return request.param
|
|
|
|
|
|
@pytest.fixture
|
|
def tmp_empty_env(
|
|
tmp_root_prefix: pathlib.Path, tmp_env_name: str
|
|
) -> Generator[pathlib.Path, None, None]:
|
|
"""An empty environment created under a temporary root prefix."""
|
|
helpers.create("-n", tmp_env_name, no_dry_run=True)
|
|
yield tmp_root_prefix / "envs" / tmp_env_name
|
|
|
|
|
|
@pytest.fixture
|
|
def tmp_prefix(tmp_empty_env: pathlib.Path) -> Generator[pathlib.Path, None, None]:
|
|
"""Change the conda prefix to a tmp folder for the duration of a test."""
|
|
os.environ["CONDA_PREFIX"] = str(tmp_empty_env)
|
|
yield tmp_empty_env
|
|
# os.environ restored by tmp_environ through tmp_root_prefix
|
|
|
|
|
|
@pytest.fixture
|
|
def tmp_xtensor_env(tmp_prefix: pathlib.Path) -> Generator[pathlib.Path, None, None]:
|
|
"""An activated environment with Xtensor installed."""
|
|
helpers.install("-c", "conda-forge", "--json", "xtensor", no_dry_run=True)
|
|
yield tmp_prefix
|