diff --git a/news/117-testing-remove-macos-app b/news/117-testing-remove-macos-app new file mode 100644 index 0000000..94f8ab4 --- /dev/null +++ b/news/117-testing-remove-macos-app @@ -0,0 +1,19 @@ +### Enhancements + +* + +### Bug fixes + +* Remove macOS apps before and after menuinst tests. (#117) + +### Deprecations + +* + +### Docs + +* + +### Other + +* diff --git a/tests/conftest.py b/tests/conftest.py index 4dc3a91..bd67ab4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,76 +1,60 @@ +import itertools import os -import subprocess +import shutil import sys from pathlib import Path -# TIP: You can debug the tests with this setup: -# CONDA_STANDALONE=src/entry_point.py pytest ... -CONDA_EXE = os.environ.get( - "CONDA_STANDALONE", - os.path.join(sys.prefix, "standalone_conda", "conda.exe"), -) +import pytest +from utils import _get_shortcut_dirs -menuinst_pkg_specs = [ - ( - "conda-test/label/menuinst-tests::package_1", - { - "win32": "Package 1/A.lnk", - "darwin": "A.app/Contents/MacOS/a", - "linux": "package-1_a.desktop", - }, - ), -] -if os.name == "nt": - menuinst_pkg_specs.append( +@pytest.fixture +def menuinst_pkg_specs(): + specs = [ ( - "conda-forge::miniforge_console_shortcut", - {"win32": "{base}/{base} Prompt ({name}).lnk"}, + "conda-test/label/menuinst-tests::package_1", + { + "win32": "Package 1/A.lnk", + "darwin": "A.app", + "linux": "package-1_a.desktop", + }, ), - ) - - -def run_conda(*args, **kwargs) -> subprocess.CompletedProcess: - check = kwargs.pop("check", False) - sudo = None - if "needs_sudo" in kwargs: - if kwargs["needs_sudo"]: - if sys.platform == "win32": - raise NotImplementedError( - "Calling run_conda with elevated privileged is not available on Windows" - ) - sudo = ["sudo", "-E"] - del kwargs["needs_sudo"] - cmd = [*sudo, CONDA_EXE] if sudo else [CONDA_EXE] - - process = subprocess.run([*cmd, *args], **kwargs) - if check: - if kwargs.get("capture_output"): - print(process.stdout) - print(process.stderr, file=sys.stderr) - process.check_returncode() - return process - - -def _get_shortcut_dirs() -> list[Path]: - if sys.platform == "win32": - from menuinst.platforms.win_utils.knownfolders import dirs_src as win_locations - - return [ - Path(win_locations["user"]["start"][0]), - Path(win_locations["system"]["start"][0]), - ] - if sys.platform == "darwin": - return [ - Path(os.environ["HOME"], "Applications"), - Path("/Applications"), - ] - if sys.platform == "linux": - paths = [ - Path(os.environ["HOME"], ".local", "share", "applications"), - Path("/usr/share/applications"), + ] + if os.name == "nt": + specs.append( + ( + "conda-forge::miniforge_console_shortcut", + {"win32": "{base}/{base} Prompt ({name}).lnk"}, + ), + ) + return specs + + +# If the test app already exists, tests will fail, +# so clean up before and after the run. +def _clean_macos_apps(shortcuts: dict[str, list[Path]]): + if not sys.platform == "darwin": + return + for shortcut in itertools.chain.from_iterable(shortcuts.values()): + if shortcut.exists(): + shutil.rmtree(shortcut) + + +@pytest.fixture +def clean_shortcuts( + tmp_path: Path, menuinst_pkg_specs: list[tuple[str, dict[str, str]]] +): + # The shortcut will take 'root_prefix' as the base, but conda-standalone + # sets that to its temporary 'sys.prefix' as provided by the pyinstaller + # self-extraction. We override it via 'CONDA_ROOT_PREFIX' in the same + # way 'constructor' will do it. + variables = {"base": Path(sys.prefix).name, "name": tmp_path.name} + shortcuts = {} + for package, spec in menuinst_pkg_specs: + shortcuts[package] = [ + folder / spec[sys.platform].format(**variables) + for folder in _get_shortcut_dirs() ] - if xdg_data_home := os.environ.get("XDG_DATA_HOME"): - paths.append(Path(xdg_data_home, "applications")) - return paths - raise NotImplementedError(sys.platform) + _clean_macos_apps(shortcuts) + yield shortcuts + _clean_macos_apps(shortcuts) diff --git a/tests/test_main.py b/tests/test_main.py index fb658a0..7b24235 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -9,8 +9,8 @@ from pathlib import Path import pytest -from conftest import CONDA_EXE, _get_shortcut_dirs, menuinst_pkg_specs, run_conda from ruamel.yaml import YAML +from utils import CONDA_EXE, run_conda HERE = Path(__file__).parent @@ -169,28 +169,18 @@ def test_extract_conda_pkgs_num_processors(tmp_path: Path): ) -_pkg_specs_params = pytest.mark.parametrize( - "pkg_spec, shortcut_path", menuinst_pkg_specs -) - - -@_pkg_specs_params -def test_menuinst_conda(tmp_path: Path, pkg_spec: str, shortcut_path: dict[str, str]): +def test_menuinst_conda(tmp_path: Path, clean_shortcuts: dict[str, list[Path]]): "Check 'regular' conda can process menuinst JSONs" + env = os.environ.copy() env["CONDA_ROOT_PREFIX"] = sys.prefix - # The shortcut will take 'root_prefix' as the base, but conda-standalone - # sets that to its temporary 'sys.prefix' as provided by the pyinstaller - # self-extraction. We override it via 'CONDA_ROOT_PREFIX' in the same - # way 'constructor' will do it. - variables = {"base": Path(sys.prefix).name, "name": tmp_path.name} process = run_conda( "create", "-vvv", "-p", tmp_path, "-y", - pkg_spec, + *clean_shortcuts.keys(), "--no-deps", env=env, capture_output=True, @@ -201,17 +191,19 @@ def test_menuinst_conda(tmp_path: Path, pkg_spec: str, shortcut_path: dict[str, print(process.stderr, file=sys.stderr) assert "menuinst Exception" not in process.stdout assert list(tmp_path.glob("Menu/*.json")) - assert any( - (folder / shortcut_path[sys.platform].format(**variables)).is_file() - for folder in _get_shortcut_dirs() - ) + shortcuts_found = [ + package + for package, shortcuts in clean_shortcuts.items() + if any(shortcut.exists() for shortcut in shortcuts) + ] + assert sorted(shortcuts_found) == sorted(clean_shortcuts.keys()) process = run_conda( "remove", "-vvv", "-p", tmp_path, "-y", - pkg_spec.split("::")[-1], + *[pkg_spec.split("::")[-1] for pkg_spec in clean_shortcuts.keys()], env=env, capture_output=True, text=True, @@ -219,24 +211,24 @@ def test_menuinst_conda(tmp_path: Path, pkg_spec: str, shortcut_path: dict[str, ) print(process.stdout) print(process.stderr, file=sys.stderr) - assert all( - not (folder / shortcut_path[sys.platform].format(**variables)).is_file() - for folder in _get_shortcut_dirs() - ) + shortcuts_found = [ + package + for package, shortcuts in clean_shortcuts.items() + if any(shortcut.exists() for shortcut in shortcuts) + ] + assert shortcuts_found == [] -@_pkg_specs_params -def test_menuinst_constructor(tmp_path: Path, pkg_spec: str, shortcut_path: str): +def test_menuinst_constructor(tmp_path: Path, clean_shortcuts: dict[str, list[Path]]): "The constructor helper should also be able to process menuinst JSONs" run_kwargs = dict(capture_output=True, text=True, check=True) - variables = {"base": Path(sys.prefix).name, "name": tmp_path.name} process = run_conda( "create", "-vvv", "-p", tmp_path, "-y", - pkg_spec, + *clean_shortcuts.keys(), "--no-deps", "--no-shortcuts", **run_kwargs, @@ -261,10 +253,12 @@ def test_menuinst_constructor(tmp_path: Path, pkg_spec: str, shortcut_path: str) ) print(process.stdout) print(process.stderr, file=sys.stderr) - assert any( - (folder / shortcut_path[sys.platform].format(**variables)).is_file() - for folder in _get_shortcut_dirs() - ) + shortcuts_found = [ + package + for package, shortcuts in clean_shortcuts.items() + if any(shortcut.exists() for shortcut in shortcuts) + ] + assert sorted(shortcuts_found) == sorted(clean_shortcuts.keys()) process = run_conda( "constructor", @@ -280,10 +274,12 @@ def test_menuinst_constructor(tmp_path: Path, pkg_spec: str, shortcut_path: str) ) print(process.stdout) print(process.stderr, file=sys.stderr) - assert all( - not (folder / shortcut_path[sys.platform].format(**variables)).is_file() - for folder in _get_shortcut_dirs() - ) + shortcuts_found = [ + package + for package, shortcuts in clean_shortcuts.items() + if any(shortcut.exists() for shortcut in shortcuts) + ] + assert shortcuts_found == [] def test_python(): diff --git a/tests/test_uninstall.py b/tests/test_uninstall.py index 654949e..5142ebc 100644 --- a/tests/test_uninstall.py +++ b/tests/test_uninstall.py @@ -18,8 +18,8 @@ run_plan, run_plan_elevated, ) -from conftest import _get_shortcut_dirs, menuinst_pkg_specs, run_conda from ruamel.yaml import YAML +from utils import _get_shortcut_dirs, run_conda if TYPE_CHECKING: from conda.testing.fixtures import CondaCLIFixture, TmpEnvFixture @@ -269,8 +269,9 @@ def test_uninstallation_keep_config_dir( def test_uninstallation_menuinst( mock_system_paths: dict[str, Path], monkeypatch: MonkeyPatch, + menuinst_pkg_specs: list[tuple[str, dict[str, str]]], ): - def _shortcuts_found(shortcut_env: Path) -> list: + def _shortcuts_found(base_env: Path, shortcut_env: Path) -> list: variables = { "base": base_env.name, "name": shortcut_env.name, @@ -287,7 +288,7 @@ def _shortcuts_found(shortcut_env: Path) -> list: package[0] for package in menuinst_pkg_specs if any( - (folder / package[1][sys.platform].format(**variables)).is_file() + (folder / package[1][sys.platform].format(**variables)).exists() for folder in shortcut_dirs ) ] @@ -311,9 +312,9 @@ def _shortcuts_found(shortcut_env: Path) -> list: shortcuts = [package[0] for package in menuinst_pkg_specs] shortcut_env = base_env / "envs" / "shortcutenv" run_conda("create", "-y", "-p", str(shortcut_env), *shortcuts) - assert _shortcuts_found(shortcut_env) == shortcuts + assert _shortcuts_found(base_env, shortcut_env) == shortcuts run_uninstaller(base_env) - assert _shortcuts_found(shortcut_env) == [] + assert _shortcuts_found(base_env, shortcut_env) == [] @pytest.mark.parametrize( diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..328875f --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,57 @@ +import os +import subprocess +import sys +from pathlib import Path + +# TIP: You can debug the tests with this setup: +# CONDA_STANDALONE=src/entry_point.py pytest ... +CONDA_EXE = os.environ.get( + "CONDA_STANDALONE", + os.path.join(sys.prefix, "standalone_conda", "conda.exe"), +) + + +def run_conda(*args, **kwargs) -> subprocess.CompletedProcess: + check = kwargs.pop("check", False) + sudo = None + if "needs_sudo" in kwargs: + if kwargs["needs_sudo"]: + if sys.platform == "win32": + raise NotImplementedError( + "Calling run_conda with elevated privileged is not available on Windows" + ) + sudo = ["sudo", "-E"] + del kwargs["needs_sudo"] + cmd = [*sudo, CONDA_EXE] if sudo else [CONDA_EXE] + + process = subprocess.run([*cmd, *args], **kwargs) + if check: + if kwargs.get("capture_output"): + print(process.stdout) + print(process.stderr, file=sys.stderr) + process.check_returncode() + return process + + +def _get_shortcut_dirs() -> list[Path]: + if sys.platform == "win32": + from menuinst.platforms.win_utils.knownfolders import dirs_src as win_locations + + return [ + Path(win_locations["user"]["start"][0]), + Path(win_locations["system"]["start"][0]), + ] + if sys.platform == "darwin": + return [ + Path(os.environ["HOME"], "Applications"), + Path("/Applications"), + ] + if sys.platform == "linux": + paths = [ + Path(os.environ["HOME"], ".local", "share", "applications"), + Path("/usr/share/applications"), + ] + if xdg_data_home := os.environ.get("XDG_DATA_HOME"): + paths.append(Path(xdg_data_home, "applications")) + return paths + raise NotImplementedError(sys.platform)