summaryrefslogtreecommitdiff
path: root/tests/test_activation.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_activation.py')
-rw-r--r--tests/test_activation.py249
1 files changed, 249 insertions, 0 deletions
diff --git a/tests/test_activation.py b/tests/test_activation.py
new file mode 100644
index 0000000..7382cfc
--- /dev/null
+++ b/tests/test_activation.py
@@ -0,0 +1,249 @@
+from __future__ import absolute_import, unicode_literals
+
+import os
+import pipes
+import re
+import subprocess
+import sys
+from os.path import dirname, join, normcase, realpath
+
+import pytest
+import six
+
+import virtualenv
+
+IS_INSIDE_CI = "CI_RUN" in os.environ
+
+
+def need_executable(name, check_cmd):
+ """skip running this locally if executable not found, unless we're inside the CI"""
+
+ def wrapper(fn):
+ fn = getattr(pytest.mark, name)(fn)
+ if not IS_INSIDE_CI:
+ # locally we disable, so that contributors don't need to have everything setup
+ # noinspection PyBroadException
+ try:
+ fn.version = subprocess.check_output(check_cmd, env=get_env())
+ except Exception as exception:
+ return pytest.mark.skip(reason="{} is not available due {}".format(name, exception))(fn)
+ return fn
+
+ return wrapper
+
+
+def requires(on):
+ def wrapper(fn):
+ return need_executable(on.cmd.replace(".exe", ""), (on.check))(fn)
+
+ return wrapper
+
+
+def norm_path(path):
+ # python may return Windows short paths, normalize
+ path = realpath(path)
+ if virtualenv.is_win:
+ from ctypes import create_unicode_buffer, windll
+
+ buffer_cont = create_unicode_buffer(256)
+ get_long_path_name = windll.kernel32.GetLongPathNameW
+ get_long_path_name(six.text_type(path), buffer_cont, 256) # noqa: F821
+ result = buffer_cont.value
+ else:
+ result = path
+ return normcase(result)
+
+
+@pytest.fixture(scope="session")
+def activation_env(tmp_path_factory):
+ path = tmp_path_factory.mktemp("activation-test-env")
+ prev_cwd = os.getcwd()
+ try:
+ os.chdir(str(path))
+ home_dir, _, __, bin_dir = virtualenv.path_locations(str(path / "env"))
+ virtualenv.create_environment(home_dir, no_pip=True, no_setuptools=True, no_wheel=True)
+ yield home_dir, bin_dir
+ finally:
+ os.chdir(str(prev_cwd))
+
+
+class Activation(object):
+ cmd = ""
+ extension = "test"
+ invoke_script = []
+ command_separator = os.linesep
+ activate_cmd = "source"
+ activate_script = ""
+ check_has_exe = []
+ check = []
+ env = {}
+
+ def __init__(self, activation_env, tmp_path):
+ self.home_dir = activation_env[0]
+ self.bin_dir = activation_env[1]
+ self.path = tmp_path
+
+ def quote(self, s):
+ return pipes.quote(s)
+
+ def python_cmd(self, cmd):
+ return "{} -c {}".format(self.quote(virtualenv.expected_exe), self.quote(cmd))
+
+ def python_script(self, script):
+ return "{} {}".format(self.quote(virtualenv.expected_exe), self.quote(script))
+
+ def print_python_exe(self):
+ return self.python_cmd("import sys; print(sys.executable)")
+
+ def print_os_env_var(self, var):
+ val = '"{}"'.format(var)
+ return self.python_cmd("import os; print(os.environ.get({}, None))".format(val))
+
+ def __call__(self, monkeypatch):
+ absolute_activate_script = norm_path(join(self.bin_dir, self.activate_script))
+
+ site_packages = subprocess.check_output(
+ [
+ os.path.join(self.bin_dir, virtualenv.expected_exe),
+ "-c",
+ "from distutils.sysconfig import get_python_lib; print(get_python_lib())",
+ ],
+ universal_newlines=True,
+ ).strip()
+ pydoc_test = self.path.__class__(site_packages) / "pydoc_test.py"
+ pydoc_test.write_text('"""This is pydoc_test.py"""')
+
+ commands = [
+ self.print_python_exe(),
+ self.print_os_env_var("VIRTUAL_ENV"),
+ self.activate_call(absolute_activate_script),
+ self.print_python_exe(),
+ self.print_os_env_var("VIRTUAL_ENV"),
+ # pydoc loads documentation from the virtualenv site packages
+ "pydoc -w pydoc_test",
+ "deactivate",
+ self.print_python_exe(),
+ self.print_os_env_var("VIRTUAL_ENV"),
+ "", # just finish with an empty new line
+ ]
+ script = self.command_separator.join(commands)
+ test_script = self.path / "script.{}".format(self.extension)
+ test_script.write_text(script)
+ assert test_script.exists()
+
+ monkeypatch.chdir(str(self.path))
+ invoke_shell = self.invoke_script + [str(test_script)]
+
+ monkeypatch.delenv(str("VIRTUAL_ENV"), raising=False)
+
+ # in case the tool is provided by the dev environment (e.g. xonosh)
+ env = get_env()
+ env.update(self.env)
+
+ raw = subprocess.check_output(invoke_shell, universal_newlines=True, stderr=subprocess.STDOUT, env=env)
+ out = re.sub(r"pydev debugger: process \d+ is connecting\n\n", "", raw, re.M).strip().split("\n")
+
+ # pre-activation
+ assert out[0], raw
+ assert out[1] == "None", raw
+
+ # post-activation
+ exe = "{}.exe".format(virtualenv.expected_exe) if virtualenv.is_win else virtualenv.expected_exe
+ assert norm_path(out[2]) == norm_path(join(self.bin_dir, exe)), raw
+ assert norm_path(out[3]) == norm_path(str(self.home_dir)).replace("\\\\", "\\"), raw
+
+ assert out[4] == "wrote pydoc_test.html"
+ content = self.path / "pydoc_test.html"
+ assert content.exists(), raw
+
+ # post deactivation, same as before
+ assert out[-2] == out[0], raw
+ assert out[-1] == "None", raw
+
+ def activate_call(self, script):
+ return "{} {}".format(pipes.quote(self.activate_cmd), pipes.quote(script)).strip()
+
+
+def get_env():
+ env = os.environ.copy()
+ env[str("PATH")] = os.pathsep.join([dirname(sys.executable)] + env.get(str("PATH"), str("")).split(os.pathsep))
+ return env
+
+
+class BashActivation(Activation):
+ cmd = "bash.exe" if virtualenv.is_win else "bash"
+ invoke_script = [cmd]
+ extension = "sh"
+ activate_script = "activate"
+ check = [cmd, "--version"]
+
+
+@pytest.mark.skipif(sys.platform == "win32", reason="no sane way to provision bash on Windows yet")
+@requires(BashActivation)
+def test_bash(activation_env, monkeypatch, tmp_path):
+ BashActivation(activation_env, tmp_path)(monkeypatch)
+
+
+class CshActivation(Activation):
+ cmd = "csh.exe" if virtualenv.is_win else "csh"
+ invoke_script = [cmd]
+ extension = "csh"
+ activate_script = "activate.csh"
+ check = [cmd, "--version"]
+
+
+@pytest.mark.skipif(sys.platform == "win32", reason="no sane way to provision csh on Windows yet")
+@requires(CshActivation)
+def test_csh(activation_env, monkeypatch, tmp_path):
+ CshActivation(activation_env, tmp_path)(monkeypatch)
+
+
+class FishActivation(Activation):
+ cmd = "fish.exe" if virtualenv.is_win else "fish"
+ invoke_script = [cmd]
+ extension = "fish"
+ activate_script = "activate.fish"
+ check = [cmd, "--version"]
+
+
+@pytest.mark.skipif(sys.platform == "win32", reason="no sane way to provision fish on Windows yet")
+@requires(FishActivation)
+def test_fish(activation_env, monkeypatch, tmp_path):
+ FishActivation(activation_env, tmp_path)(monkeypatch)
+
+
+class PowershellActivation(Activation):
+ cmd = "powershell.exe" if virtualenv.is_win else "pwsh"
+ extension = "ps1"
+ invoke_script = [cmd, "-File"]
+ activate_script = "activate.ps1"
+ activate_cmd = "."
+ check = [cmd, "-c", "$PSVersionTable"]
+
+ @staticmethod
+ def quote(s):
+ """powershell double double quote needed for quotes within single quotes"""
+ return pipes.quote(s).replace('"', '""')
+
+
+@requires(PowershellActivation)
+def test_powershell(activation_env, monkeypatch, tmp_path):
+ PowershellActivation(activation_env, tmp_path)(monkeypatch)
+
+
+class XonoshActivation(Activation):
+ cmd = "xonsh"
+ extension = "xsh"
+ invoke_script = [sys.executable, "-m", "xonsh"]
+ activate_script = "activate.xsh"
+ check = [sys.executable, "-m", "xonsh", "--version"]
+ env = {"XONSH_DEBUG": "1", "XONSH_SHOW_TRACEBACK": "True"}
+
+ def activate_call(self, script):
+ return "{} {}".format(self.activate_cmd, repr(script)).strip()
+
+
+@pytest.mark.skipif(sys.version_info < (3, 4), reason="xonosh requires Python 3.4 at least")
+@requires(XonoshActivation)
+def test_xonosh(activation_env, monkeypatch, tmp_path):
+ XonoshActivation(activation_env, tmp_path)(monkeypatch)