blob: 692eefb771ad43b25e0ee035fd7ae0f46b3e30cb [file] [edit]
from __future__ import annotations
import os
import shutil
import subprocess
import sys
from argparse import Namespace
import pytest
from virtualenv.activation import FishActivator
from virtualenv.info import IS_WIN
FISH = shutil.which("fish")
@pytest.mark.parametrize(
("tcl_lib", "tk_lib", "present"),
[
("/path/to/tcl", "/path/to/tk", True),
(None, None, False),
],
)
def test_fish_tkinter_generation(tmp_path, tcl_lib, tk_lib, present) -> None:
# GIVEN
class MockInterpreter:
pass
interpreter = MockInterpreter()
interpreter.tcl_lib = tcl_lib
interpreter.tk_lib = tk_lib
class MockCreator:
def __init__(self, dest) -> None:
self.dest = dest
self.bin_dir = dest / "bin"
self.bin_dir.mkdir()
self.interpreter = interpreter
self.pyenv_cfg = {}
self.env_name = "my-env"
creator = MockCreator(tmp_path)
options = Namespace(prompt=None)
activator = FishActivator(options)
# WHEN
activator.generate(creator)
content = (creator.bin_dir / "activate.fish").read_text(encoding="utf-8")
# THEN
# PKG_CONFIG_PATH is always set
assert 'set -gx _OLD_PKG_CONFIG_PATH "$PKG_CONFIG_PATH"' in content
assert 'set -gx PKG_CONFIG_PATH "$VIRTUAL_ENV/lib/pkgconfig:$PKG_CONFIG_PATH"' in content
assert "set -e _OLD_PKG_CONFIG_PATH" in content
if present:
assert "set -gx TCL_LIBRARY '/path/to/tcl'" in content
assert "set -gx TK_LIBRARY '/path/to/tk'" in content
else:
assert "if test -n ''\n if set -q TCL_LIBRARY;" in content
assert "if test -n ''\n if set -q TK_LIBRARY;" in content
@pytest.mark.skipif(IS_WIN, reason="fish is not available on Windows")
@pytest.mark.skipif(FISH is None, reason="fish is not installed")
def test_fish_prompt_survives_shadowed_source(activation_python, tmp_path) -> None:
# A user function shadowing `.`/`source` must not hijack the prompt's exit-status restore. Source a copy of the
# rendered script from an ASCII path so the driver avoids the venv path's special characters.
script = tmp_path / "activate.fish"
rendered = activation_python.creator.bin_dir / "activate.fish"
script.write_text(rendered.read_text(encoding="utf-8"), encoding="utf-8")
start, trap = tmp_path / "start", tmp_path / "trap"
start.mkdir()
trap.mkdir()
driver = tmp_path / "driver.fish"
driver.write_text(
f"cd '{start}'\nfunction . ; cd '{trap}' ; end\nsource '{script}'\nfish_prompt\necho \"PWD=$PWD\"\n",
encoding="utf-8",
)
out = subprocess.run(
[FISH, str(driver)], capture_output=True, text=True, encoding="utf-8", timeout=60, check=True
).stdout
assert f"PWD={start}\n" in out, out
@pytest.mark.skipif(IS_WIN, reason="we have not setup fish in CI yet")
def test_fish(activation_tester_class, activation_tester, monkeypatch, tmp_path) -> None:
monkeypatch.setenv("HOME", str(tmp_path))
fish_conf_dir = tmp_path / ".config" / "fish"
fish_conf_dir.mkdir(parents=True)
(fish_conf_dir / "config.fish").write_text("", encoding="utf-8")
class Fish(activation_tester_class):
def __init__(self, session) -> None:
super().__init__(FishActivator, session, "fish", "activate.fish", "fish")
def print_prompt(self) -> str:
return "fish_prompt"
def _get_test_lines(self, activate_script):
return [
self.print_python_exe(),
self.print_os_env_var("VIRTUAL_ENV"),
self.print_os_env_var("VIRTUAL_ENV_PROMPT"),
self.print_os_env_var("PATH"),
self.activate_call(activate_script),
self.print_python_exe(),
self.print_os_env_var("VIRTUAL_ENV"),
self.print_os_env_var("VIRTUAL_ENV_PROMPT"),
self.print_os_env_var("PATH"),
self.print_prompt(),
# \\ loads documentation from the virtualenv site packages
self.pydoc_call,
self.deactivate,
self.print_python_exe(),
self.print_os_env_var("VIRTUAL_ENV"),
self.print_os_env_var("VIRTUAL_ENV_PROMPT"),
self.print_os_env_var("PATH"),
"", # just finish with an empty new line
]
def assert_output(self, out, raw, _) -> None:
"""Compare _get_test_lines() with the expected values."""
assert out[0], raw
assert out[1] == "None", raw
assert out[2] == "None", raw
# self.activate_call(activate_script) runs at this point
expected = self._creator.exe.parent / os.path.basename(sys.executable)
assert self.norm_path(out[4]) == self.norm_path(expected), raw
assert self.norm_path(out[5]) == self.norm_path(self._creator.dest).replace("\\\\", "\\"), raw
assert out[6] == self._creator.env_name
# Some attempts to test the prompt output print more than 1 line.
# So we need to check if the prompt exists on any of them.
prompt_text = f"({self._creator.env_name}) "
assert any(prompt_text in line for line in out[7:-5]), raw
assert out[-5] == "wrote pydoc_test.html", raw
content = tmp_path / "pydoc_test.html"
assert content.exists(), raw
# post deactivation, same as before
assert out[-4] == out[0], raw
assert out[-3] == "None", raw
assert out[-2] == "None", raw
# Check that the PATH is restored
assert out[3] == out[13], raw
# Check that PATH changed after activation
assert out[3] != out[8], raw
activation_tester(Fish)