mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Fix pip tests, in particular under windows
This commit is contained in:
parent
493f493bbe
commit
31e91b03bb
2 changed files with 155 additions and 120 deletions
|
@ -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)),
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Reference in a new issue