Fix pip tests, in particular under windows

This commit is contained in:
Pedro Algarvio 2019-04-15 16:45:44 +01:00
parent 493f493bbe
commit 31e91b03bb
No known key found for this signature in database
GPG key ID: BB36BF6584A298FF
2 changed files with 155 additions and 120 deletions

View file

@ -8,6 +8,7 @@ tests.integration.modules.pip
from __future__ import absolute_import, print_function, unicode_literals
import os
import re
import sys
import pprint
import shutil
import tempfile
@ -30,16 +31,45 @@ class PipModuleTest(ModuleCase):
def setUp(self):
super(PipModuleTest, self).setUp()
# Restore the environ
self.addCleanup(os.environ.update, os.environ.copy())
self.venv_test_dir = tempfile.mkdtemp(dir=TMP)
# Remove the venv test directory
self.addCleanup(shutil.rmtree, self.venv_test_dir, ignore_errors=True)
self.venv_dir = os.path.join(self.venv_test_dir, 'venv')
for key in os.environ.copy():
if key.startswith('PIP_'):
os.environ.pop(key)
self.pip_temp = os.path.join(self.venv_test_dir, '.pip-temp')
# Remove the pip-temp directory
self.addCleanup(shutil.rmtree, self.pip_temp, ignore_errors=True)
if not os.path.isdir(self.pip_temp):
os.makedirs(self.pip_temp)
os.environ['PIP_SOURCE_DIR'] = os.environ['PIP_BUILD_DIR'] = ''
for item in ('venv_dir', 'venv_test_dir', 'pip_temp'):
self.addCleanup(delattr, self, item)
def _create_virtualenv(self, path):
'''
The reason why the virtualenv creation is proxied by this function is mostly
because under windows, we can't seem to properly create a virtualenv off of
another virtualenv(we can on linux) and also because, we really don't want to
test virtualenv creation off of another virtualenv, we want a virtualenv created
from the original python
'''
try:
if salt.utils.is_windows():
python = os.path.join(sys.real_prefix, os.path.basename(sys.executable))
else:
python = os.path.join(sys.real_prefix, 'bin', os.path.basename(sys.executable))
# We're running off a virtualenv, and we don't want to create a virtualenv off of
# a virtualenv
kwargs = {'python': python}
except AttributeError:
# We're running off of the system python
kwargs = {}
self.run_function('virtualenv.create', [path], **kwargs)
def tearDown(self):
super(PipModuleTest, self).tearDown()
@ -83,7 +113,7 @@ class PipModuleTest(ModuleCase):
def test_issue_2087_missing_pip(self):
# Let's create the testing virtualenv
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
# Let's remove the pip binary
pip_bin = os.path.join(self.venv_dir, 'bin', 'pip')
@ -114,7 +144,7 @@ class PipModuleTest(ModuleCase):
@skip_if_not_root
def test_requirements_as_list_of_chains__cwd_set__absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
# Create a requirements file that depends on another one.
@ -157,7 +187,7 @@ class PipModuleTest(ModuleCase):
@skip_if_not_root
def test_requirements_as_list_of_chains__cwd_not_set__absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
# Create a requirements file that depends on another one.
@ -200,7 +230,7 @@ class PipModuleTest(ModuleCase):
@skip_if_not_root
def test_requirements_as_list__absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
req1_filename = os.path.join(self.venv_dir, 'requirements.txt')
req2_filename = os.path.join(self.venv_dir, 'requirements2.txt')
@ -235,7 +265,7 @@ class PipModuleTest(ModuleCase):
@skip_if_not_root
def test_requirements_as_list__non_absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
# Create a requirements file that depends on another one.
@ -277,7 +307,7 @@ class PipModuleTest(ModuleCase):
@skip_if_not_root
def test_chained_requirements__absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
# Create a requirements file that depends on another one.
@ -310,7 +340,7 @@ class PipModuleTest(ModuleCase):
@skip_if_not_root
def test_chained_requirements__non_absolute_file_path(self):
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
# Create a requirements file that depends on another one.
req_basepath = (self.venv_dir)
@ -348,7 +378,7 @@ class PipModuleTest(ModuleCase):
@skip_if_not_root
def test_issue_4805_nested_requirements(self):
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
# Create a requirements file that depends on another one.
req1_filename = os.path.join(self.venv_dir, 'requirements.txt')
@ -381,7 +411,7 @@ class PipModuleTest(ModuleCase):
def test_pip_uninstall(self):
# Let's create the testing virtualenv
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
ret = self.run_function('pip.install', ['pep8'], bin_env=self.venv_dir)
if not isinstance(ret, dict):
@ -423,7 +453,7 @@ class PipModuleTest(ModuleCase):
def test_pip_install_upgrade(self):
# Create the testing virtualenv
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
ret = self.run_function(
'pip.install', ['pep8==1.3.4'], bin_env=self.venv_dir
)
@ -498,7 +528,7 @@ class PipModuleTest(ModuleCase):
]
# Create the testing virtualenv
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
ret = self.run_function(
'pip.install', [],
editable='{0}'.format(','.join(editables)),
@ -532,7 +562,7 @@ class PipModuleTest(ModuleCase):
]
# Create the testing virtualenv
self.run_function('virtualenv.create', [self.venv_dir])
self._create_virtualenv(self.venv_dir)
ret = self.run_function(
'pip.install', ['pep8'],
editable='{0}'.format(','.join(editables)),

View file

@ -54,9 +54,10 @@ class VirtualEnv(object):
def __init__(self, test, venv_dir):
self.venv_dir = venv_dir
self.test = test
self.test.addCleanup(shutil.rmtree, self.venv_dir, ignore_errors=True)
def __enter__(self):
ret = self.test.run_function('virtualenv.create', [self.venv_dir])
ret = self.test._create_virtualenv(self.venv_dir)
self.test.assertEqual(
ret['retcode'], 0,
msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format(
@ -64,14 +65,35 @@ class VirtualEnv(object):
)
)
def __exit__(self, exc_type, exc_value, traceback):
if os.path.isdir(self.venv_dir):
shutil.rmtree(self.venv_dir, ignore_errors=True)
def __exit__(self, *args):
pass
@skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed')
class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
def _create_virtualenv(self, path):
'''
The reason why the virtualenv creation is proxied by this function is mostly
because under windows, we can't seem to properly create a virtualenv off of
another virtualenv(we can on linux) and also because, we really don't want to
test virtualenv creation off of another virtualenv, we want a virtualenv created
from the original python
'''
self.addCleanup(shutil.rmtree, path, ignore_errors=True)
try:
if salt.utils.is_windows():
python = os.path.join(sys.real_prefix, os.path.basename(sys.executable))
else:
python = os.path.join(sys.real_prefix, 'bin', os.path.basename(sys.executable))
# We're running off a virtualenv, and we don't want to create a virtualenv off of
# a virtualenv
kwargs = {'python': python}
except AttributeError:
# We're running off of the system python
kwargs = {}
return self.run_function('virtualenv.create', [path], **kwargs)
@skip_if_not_root
def test_pip_installed_removed(self):
'''
@ -117,7 +139,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
)
# We now create the missing virtualenv
ret = self.run_function('virtualenv.create', [venv_dir])
ret = self._create_virtualenv(venv_dir)
self.assertEqual(ret['retcode'], 0)
# The state should not have any issues running now
@ -131,8 +153,6 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
os.environ.pop('SHELL')
else:
os.environ['SHELL'] = orig_shell
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir, ignore_errors=True)
@skipIf(six.PY3, 'Issue is specific to carbon module, which is PY2-only')
@skipIf(salt.utils.platform.is_windows(), "Carbon does not install in Windows")
@ -200,63 +220,56 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
venv_dir = os.path.join(
RUNTIME_VARS.TMP, 'issue-2028-pip-installed'
)
self.addCleanup(shutil.rmtree, venv_dir, ignore_errors=True)
pep8_bin = os.path.join(venv_dir, 'bin', 'pep8')
if salt.utils.platform.is_windows():
pep8_bin = os.path.join(venv_dir, 'Scripts', 'pep8.exe')
try:
self.assertSaltTrueReturn(ret)
self.assertTrue(
os.path.isfile(pep8_bin)
)
finally:
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir, ignore_errors=True)
self.assertSaltTrueReturn(ret)
self.assertTrue(
os.path.isfile(pep8_bin)
)
def test_issue_2087_missing_pip(self):
venv_dir = os.path.join(
RUNTIME_VARS.TMP, 'issue-2087-missing-pip'
)
try:
# Let's create the testing virtualenv
ret = self.run_function('virtualenv.create', [venv_dir])
self.assertEqual(
ret['retcode'], 0,
msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format(
pprint.pformat(ret)
)
# Let's create the testing virtualenv
ret = self._create_virtualenv(venv_dir)
self.assertEqual(
ret['retcode'], 0,
msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format(
pprint.pformat(ret)
)
)
# Let's remove the pip binary
pip_bin = os.path.join(venv_dir, 'bin', 'pip')
site_dir = self.run_function('virtualenv.get_distribution_path', [venv_dir, 'pip'])
if salt.utils.platform.is_windows():
pip_bin = os.path.join(venv_dir, 'Scripts', 'pip.exe')
site_dir = os.path.join(venv_dir, 'lib', 'site-packages')
if not os.path.isfile(pip_bin):
self.skipTest(
'Failed to find the pip binary to the test virtualenv'
)
os.remove(pip_bin)
# Also remove the pip dir from site-packages
# This is needed now that we're using python -m pip instead of the
# pip binary directly. python -m pip will still work even if the
# pip binary is missing
shutil.rmtree(os.path.join(site_dir, 'pip'))
# Let's run the state which should fail because pip is missing
ret = self.run_function('state.sls', mods='issue-2087-missing-pip')
self.assertSaltFalseReturn(ret)
self.assertInSaltComment(
'Error installing \'pep8\': Could not find a `pip` binary',
ret
# Let's remove the pip binary
pip_bin = os.path.join(venv_dir, 'bin', 'pip')
site_dir = self.run_function('virtualenv.get_distribution_path', [venv_dir, 'pip'])
if salt.utils.platform.is_windows():
pip_bin = os.path.join(venv_dir, 'Scripts', 'pip.exe')
site_dir = os.path.join(venv_dir, 'lib', 'site-packages')
if not os.path.isfile(pip_bin):
self.skipTest(
'Failed to find the pip binary to the test virtualenv'
)
finally:
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir, ignore_errors=True)
os.remove(pip_bin)
# Also remove the pip dir from site-packages
# This is needed now that we're using python -m pip instead of the
# pip binary directly. python -m pip will still work even if the
# pip binary is missing
shutil.rmtree(os.path.join(site_dir, 'pip'))
# Let's run the state which should fail because pip is missing
ret = self.run_function('state.sls', mods='issue-2087-missing-pip')
self.assertSaltFalseReturn(ret)
self.assertInSaltComment(
'Error installing \'pep8\': Could not find a `pip` binary',
ret
)
def test_issue_5940_multiple_pip_mirrors(self):
'''
@ -269,6 +282,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
venv_dir = os.path.join(
RUNTIME_VARS.TMP, '5940-multiple-pip-mirrors'
)
self.addCleanup(shutil.rmtree, venv_dir, ignore_errors=True)
try:
self.assertSaltTrueReturn(ret)
@ -279,9 +293,6 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
pip_version = self.run_function('pip.version', [venv_dir])
if salt.utils.versions.compare(ver1=pip_version, oper='>=', ver2='7.0.0'):
self.skipTest('the --mirrors arg has been deprecated and removed in pip==7.0.0')
finally:
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir, ignore_errors=True)
@destructiveTest
@skip_if_not_root
@ -382,63 +393,59 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
venv_dir = os.path.join(
RUNTIME_VARS.TMP, '6833-pip-upgrade-pip'
)
ret = self.run_function('virtualenv.create', [venv_dir])
ret = self._create_virtualenv(venv_dir)
try:
self.assertEqual(
ret['retcode'], 0,
msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format(
pprint.pformat(ret)
)
self.assertEqual(
ret['retcode'], 0,
msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format(
pprint.pformat(ret)
)
self.assertIn(
'New python executable',
ret['stdout'],
msg='Expected STDOUT did not match. Full return dictionary:\n{}'.format(
pprint.pformat(ret)
)
)
self.assertIn(
'New python executable',
ret['stdout'],
msg='Expected STDOUT did not match. Full return dictionary:\n{}'.format(
pprint.pformat(ret)
)
)
# Let's install a fixed version pip over whatever pip was
# previously installed
ret = self.run_function(
'pip.install', ['pip==8.0'], upgrade=True,
bin_env=venv_dir
)
if not isinstance(ret, dict):
self.fail(
'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret)
)
# Let's install a fixed version pip over whatever pip was
# previously installed
ret = self.run_function(
'pip.install', ['pip==8.0'], upgrade=True,
bin_env=venv_dir
self.assertEqual(ret['retcode'], 0)
self.assertIn(
'Successfully installed pip',
ret['stdout']
)
# Let's make sure we have pip 8.0 installed
self.assertEqual(
self.run_function('pip.list', ['pip'], bin_env=venv_dir),
{'pip': '8.0.0'}
)
# Now the actual pip upgrade pip test
ret = self.run_state(
'pip.installed', name='pip==8.0.1', upgrade=True,
bin_env=venv_dir
)
if not isinstance(ret, dict):
self.fail(
'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret)
)
if not isinstance(ret, dict):
self.fail(
'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret)
)
self.assertEqual(ret['retcode'], 0)
self.assertIn(
'Successfully installed pip',
ret['stdout']
)
# Let's make sure we have pip 8.0 installed
self.assertEqual(
self.run_function('pip.list', ['pip'], bin_env=venv_dir),
{'pip': '8.0.0'}
)
# Now the actual pip upgrade pip test
ret = self.run_state(
'pip.installed', name='pip==8.0.1', upgrade=True,
bin_env=venv_dir
)
if not isinstance(ret, dict):
self.fail(
'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret)
)
self.assertSaltTrueReturn(ret)
self.assertSaltStateChangesEqual(ret, {'pip==8.0.1': 'Installed'})
finally:
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir, ignore_errors=True)
self.assertSaltTrueReturn(ret)
self.assertSaltStateChangesEqual(ret, {'pip==8.0.1': 'Installed'})
def test_pip_installed_specific_env(self):
# Create the testing virtualenv
@ -454,7 +461,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
reqf.write(b'pep8\n')
try:
self.run_function('virtualenv.create', [venv_dir])
self._create_virtualenv(venv_dir)
# The requirements file should not be found the base environment
ret = self.run_state(
@ -489,8 +496,6 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
ret
)
finally:
if os.path.isdir(venv_dir):
shutil.rmtree(venv_dir, ignore_errors=True)
if os.path.isfile(requirements_file):
os.unlink(requirements_file)
@ -498,7 +503,7 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
# This test case should be moved to a format_call unit test specific to
# the state internal keywords
venv_dir = os.path.join(RUNTIME_VARS.TMP, 'pip-installed-unless')
venv_create = self.run_function('virtualenv.create', [venv_dir])
venv_create = self._create_virtualenv(venv_dir)
if venv_create['retcode'] > 0:
self.skipTest(
'Failed to create testcase virtual environment: {0}'.format(