Fix integration tests on Windows (#4769)

This commit is contained in:
Benoit Pierre 2017-10-06 21:51:42 +02:00 committed by Paul Moore
parent 6af0a836f3
commit 961737ab98
18 changed files with 231 additions and 119 deletions

View File

@ -1,16 +1,36 @@
environment: environment:
matrix: matrix:
# Unit and integration tests.
- PYTHON: "C:\\Python27" - PYTHON: "C:\\Python27"
- PYTHON: "C:\\Python33" RUN_INTEGRATION_TESTS: "True"
- PYTHON: "C:\\Python34" - PYTHON: "C:\\Python36-x64"
- PYTHON: "C:\\Python35" RUN_INTEGRATION_TESTS: "True"
# Unit tests only.
- PYTHON: "C:\\Python27-x64" - PYTHON: "C:\\Python27-x64"
- PYTHON: "C:\\Python33"
- PYTHON: "C:\\Python33-x64" - PYTHON: "C:\\Python33-x64"
- PYTHON: "C:\\Python34"
- PYTHON: "C:\\Python34-x64" - PYTHON: "C:\\Python34-x64"
- PYTHON: "C:\\Python35"
- PYTHON: "C:\\Python35-x64" - PYTHON: "C:\\Python35-x64"
- PYTHON: "C:\\Python36"
install: install:
cmd: "%PYTHON%\\python.exe -m pip install tox" - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- "python --version"
- "pip install certifi tox"
# Fix git SSL errors.
- "python -m certifi >cacert.txt"
- "set /p GIT_SSL_CAINFO=<cacert.txt"
- "set GIT_SSL_CAINFO"
build: off build: off
test_script: test_script:
- "%PYTHON%\\Scripts\\tox.exe -e py -- -m unit -n 8" # Shorten paths, workaround https://bugs.python.org/issue18199
- "subst T: %TEMP%"
- "set TEMP=T:\\"
- "set TMP=T:\\"
- "tox -e py -- -m unit -n 3"
- "if \"%RUN_INTEGRATION_TESTS%\" == \"True\" (
tox -e py -- -m integration -n 3 --duration=5 )"

View File

@ -945,7 +945,7 @@ class InstallRequirement(object):
def get_dist(self): def get_dist(self):
"""Return a pkg_resources.Distribution built from self.egg_info_path""" """Return a pkg_resources.Distribution built from self.egg_info_path"""
egg_info = self.egg_info_path('').rstrip('/') egg_info = self.egg_info_path('').rstrip(os.path.sep)
base_dir = os.path.dirname(egg_info) base_dir = os.path.dirname(egg_info)
metadata = pkg_resources.PathMetadata(base_dir, egg_info) metadata = pkg_resources.PathMetadata(base_dir, egg_info)
dist_name = os.path.splitext(os.path.basename(egg_info))[0] dist_name = os.path.splitext(os.path.basename(egg_info))[0]

View File

@ -77,8 +77,6 @@ def isolate(tmpdir):
We use an autouse function scoped fixture because we want to ensure that We use an autouse function scoped fixture because we want to ensure that
every test has it's own isolated home directory. every test has it's own isolated home directory.
""" """
# TODO: Ensure Windows will respect $HOME, including for the cache
# directory
# TODO: Figure out how to isolate from *system* level configuration files # TODO: Figure out how to isolate from *system* level configuration files
# as well as user level configuration files. # as well as user level configuration files.
@ -91,21 +89,36 @@ def isolate(tmpdir):
fake_root = os.path.join(str(tmpdir), "fake-root") fake_root = os.path.join(str(tmpdir), "fake-root")
os.makedirs(fake_root) os.makedirs(fake_root)
# Set our home directory to our temporary directory, this should force all if sys.platform == 'win32':
# of our relative configuration files to be read from here instead of the # Note: this will only take effect in subprocesses...
# user's actual $HOME directory. home_drive, home_path = os.path.splitdrive(home_dir)
os.environ["HOME"] = home_dir os.environ.update({
'USERPROFILE': home_dir,
# Isolate ourselves from XDG directories 'HOMEDRIVE': home_drive,
os.environ["XDG_DATA_HOME"] = os.path.join(home_dir, ".local", "share") 'HOMEPATH': home_path,
os.environ["XDG_CONFIG_HOME"] = os.path.join(home_dir, ".config") })
os.environ["XDG_CACHE_HOME"] = os.path.join(home_dir, ".cache") for env_var, sub_path in (
os.environ["XDG_RUNTIME_DIR"] = os.path.join(home_dir, ".runtime") ('APPDATA', 'AppData/Roaming'),
os.environ["XDG_DATA_DIRS"] = ":".join([ ('LOCALAPPDATA', 'AppData/Local'),
os.path.join(fake_root, "usr", "local", "share"), ):
os.path.join(fake_root, "usr", "share"), path = os.path.join(home_dir, *sub_path.split('/'))
]) os.environ[env_var] = path
os.environ["XDG_CONFIG_DIRS"] = os.path.join(fake_root, "etc", "xdg") os.makedirs(path)
else:
# Set our home directory to our temporary directory, this should force
# all of our relative configuration files to be read from here instead
# of the user's actual $HOME directory.
os.environ["HOME"] = home_dir
# Isolate ourselves from XDG directories
os.environ["XDG_DATA_HOME"] = os.path.join(home_dir, ".local", "share")
os.environ["XDG_CONFIG_HOME"] = os.path.join(home_dir, ".config")
os.environ["XDG_CACHE_HOME"] = os.path.join(home_dir, ".cache")
os.environ["XDG_RUNTIME_DIR"] = os.path.join(home_dir, ".runtime")
os.environ["XDG_DATA_DIRS"] = ":".join([
os.path.join(fake_root, "usr", "local", "share"),
os.path.join(fake_root, "usr", "share"),
])
os.environ["XDG_CONFIG_DIRS"] = os.path.join(fake_root, "etc", "xdg")
# Configure git, because without an author name/email git will complain # Configure git, because without an author name/email git will complain
# and cause test failures. # and cause test failures.
@ -116,6 +129,7 @@ def isolate(tmpdir):
# We want to disable the version check from running in the tests # We want to disable the version check from running in the tests
os.environ["PIP_DISABLE_PIP_VERSION_CHECK"] = "true" os.environ["PIP_DISABLE_PIP_VERSION_CHECK"] = "true"
# FIXME: Windows...
os.makedirs(os.path.join(home_dir, ".config", "git")) os.makedirs(os.path.join(home_dir, ".config", "git"))
with open(os.path.join(home_dir, ".config", "git", "config"), "wb") as fp: with open(os.path.join(home_dir, ".config", "git", "config"), "wb") as fp:
fp.write( fp.write(
@ -143,6 +157,15 @@ def virtualenv_template(tmpdir_factory):
pip_source_dir=pip_src, pip_source_dir=pip_src,
relocatable=True, relocatable=True,
) )
if sys.platform == 'win32':
# Work around setuptools' easy_install.exe
# not working properly after relocation.
for exe in os.listdir(venv.bin):
if exe.startswith('easy_install'):
(venv.bin / exe).remove()
with open(venv.bin / 'easy_install.bat', 'w') as fp:
fp.write('python.exe -m easy_install %*\n')
# Rename original virtualenv directory to make sure # Rename original virtualenv directory to make sure
# it's not reused by mistake from one of the copies. # it's not reused by mistake from one of the copies.
venv_template = tmpdir / "venv_template" venv_template = tmpdir / "venv_template"

View File

@ -93,7 +93,7 @@ def test_download_wheel_archive(script, data):
It should download a wheel archive path It should download a wheel archive path
""" """
wheel_filename = 'colander-0.9.9-py2.py3-none-any.whl' wheel_filename = 'colander-0.9.9-py2.py3-none-any.whl'
wheel_path = os.path.join(data.find_links, wheel_filename) wheel_path = '/'.join((data.find_links, wheel_filename))
result = script.pip( result = script.pip(
'download', wheel_path, 'download', wheel_path,
'-d', '.', '--no-deps' '-d', '.', '--no-deps'
@ -107,7 +107,7 @@ def test_download_should_download_wheel_deps(script, data):
""" """
wheel_filename = 'colander-0.9.9-py2.py3-none-any.whl' wheel_filename = 'colander-0.9.9-py2.py3-none-any.whl'
dep_filename = 'translationstring-1.1.tar.gz' dep_filename = 'translationstring-1.1.tar.gz'
wheel_path = os.path.join(data.find_links, wheel_filename) wheel_path = '/'.join((data.find_links, wheel_filename))
result = script.pip( result = script.pip(
'download', wheel_path, 'download', wheel_path,
'-d', '.', '--find-links', data.find_links, '--no-index' '-d', '.', '--find-links', data.find_links, '--no-index'

View File

@ -6,7 +6,10 @@ from doctest import ELLIPSIS, OutputChecker
import pytest import pytest
from tests.lib import _create_test_package, _create_test_package_with_srcdir from tests.lib import (
_create_test_package, _create_test_package_with_srcdir, need_bzr,
need_mercurial
)
distribute_re = re.compile('^distribute==[0-9.]+\n', re.MULTILINE) distribute_re = re.compile('^distribute==[0-9.]+\n', re.MULTILINE)
@ -319,7 +322,7 @@ def test_freeze_git_remote(script, tmpdir):
_check_output(result.stdout, expected) _check_output(result.stdout, expected)
@pytest.mark.mercurial @need_mercurial
def test_freeze_mercurial_clone(script, tmpdir): def test_freeze_mercurial_clone(script, tmpdir):
""" """
Test freezing a Mercurial clone. Test freezing a Mercurial clone.
@ -361,7 +364,7 @@ def test_freeze_mercurial_clone(script, tmpdir):
_check_output(result.stdout, expected) _check_output(result.stdout, expected)
@pytest.mark.bzr @need_bzr
def test_freeze_bazaar_clone(script, tmpdir): def test_freeze_bazaar_clone(script, tmpdir):
""" """
Test freezing a Bazaar clone. Test freezing a Bazaar clone.
@ -478,7 +481,7 @@ def test_freeze_with_requirement_option_multiple(script):
simple2==1.0 simple2==1.0
""") """)
expected += "## The following requirements were added by pip freeze:" expected += "## The following requirements were added by pip freeze:"
expected += os.linesep + textwrap.dedent("""\ expected += '\n' + textwrap.dedent("""\
...meta==1.0... ...meta==1.0...
""") """)
_check_output(result.stdout, expected) _check_output(result.stdout, expected)

View File

@ -1,3 +1,4 @@
import distutils
import glob import glob
import os import os
import sys import sys
@ -8,11 +9,11 @@ import pytest
from pip._internal import pep425tags from pip._internal import pep425tags
from pip._internal.status_codes import ERROR from pip._internal.status_codes import ERROR
from pip._internal.utils import appdirs
from pip._internal.utils.misc import rmtree from pip._internal.utils.misc import rmtree
from tests.lib import ( from tests.lib import (
_create_svn_repo, _create_test_package, create_test_package_with_setup, _create_svn_repo, _create_test_package, create_test_package_with_setup,
path_to_url, pyversion, pyversion_tuple, requirements_file need_bzr, need_mercurial, path_to_url, pyversion, pyversion_tuple,
requirements_file
) )
from tests.lib.local_repos import local_checkout from tests.lib.local_repos import local_checkout
from tests.lib.path import Path from tests.lib.path import Path
@ -217,6 +218,7 @@ def test_install_editable_uninstalls_existing_from_path(script, data):
assert simple_folder in result.files_deleted, str(result.stdout) assert simple_folder in result.files_deleted, str(result.stdout)
@need_mercurial
def test_install_editable_from_hg(script, tmpdir): def test_install_editable_from_hg(script, tmpdir):
"""Test cloning from Mercurial.""" """Test cloning from Mercurial."""
pkg_path = _create_test_package(script, name='testpackage', vcs='hg') pkg_path = _create_test_package(script, name='testpackage', vcs='hg')
@ -225,6 +227,7 @@ def test_install_editable_from_hg(script, tmpdir):
result.assert_installed('testpackage', with_files=['.hg']) result.assert_installed('testpackage', with_files=['.hg'])
@need_mercurial
def test_vcs_url_final_slash_normalization(script, tmpdir): def test_vcs_url_final_slash_normalization(script, tmpdir):
""" """
Test that presence or absence of final slash in VCS URL is normalized. Test that presence or absence of final slash in VCS URL is normalized.
@ -235,7 +238,7 @@ def test_vcs_url_final_slash_normalization(script, tmpdir):
result.assert_installed('testpackage', with_files=['.hg']) result.assert_installed('testpackage', with_files=['.hg'])
@pytest.mark.bzr @need_bzr
def test_install_editable_from_bazaar(script, tmpdir): def test_install_editable_from_bazaar(script, tmpdir):
"""Test checking out from Bazaar.""" """Test checking out from Bazaar."""
pkg_path = _create_test_package(script, name='testpackage', vcs='bazaar') pkg_path = _create_test_package(script, name='testpackage', vcs='bazaar')
@ -245,7 +248,7 @@ def test_install_editable_from_bazaar(script, tmpdir):
@pytest.mark.network @pytest.mark.network
@pytest.mark.bzr @need_bzr
def test_vcs_url_urlquote_normalization(script, tmpdir): def test_vcs_url_urlquote_normalization(script, tmpdir):
""" """
Test that urlquoted characters are normalized for repo URL comparison. Test that urlquoted characters are normalized for repo URL comparison.
@ -289,13 +292,14 @@ def test_install_relative_directory(script, data):
# Compute relative install path to FSPkg from scratch path. # Compute relative install path to FSPkg from scratch path.
full_rel_path = data.packages.join('FSPkg') - script.scratch_path full_rel_path = data.packages.join('FSPkg') - script.scratch_path
full_rel_url = (
'file:' + full_rel_path.replace(os.path.sep, '/') + '#egg=FSPkg'
)
embedded_rel_path = script.scratch_path.join(full_rel_path) embedded_rel_path = script.scratch_path.join(full_rel_path)
# For each relative path, install as either editable or not using either # For each relative path, install as either editable or not using either
# URLs with egg links or not. # URLs with egg links or not.
for req_path in (full_rel_path, for req_path in (full_rel_path, full_rel_url, embedded_rel_path):
'file:' + full_rel_path + '#egg=FSPkg',
embedded_rel_path):
# Regular install. # Regular install.
result = script.pip('install', req_path, result = script.pip('install', req_path,
cwd=script.scratch_path) cwd=script.scratch_path)
@ -498,6 +502,7 @@ def test_install_using_install_option_and_editable(script, tmpdir):
@pytest.mark.network @pytest.mark.network
@need_mercurial
def test_install_global_option_using_editable(script, tmpdir): def test_install_global_option_using_editable(script, tmpdir):
""" """
Test using global distutils options, but in an editable installation Test using global distutils options, but in an editable installation
@ -653,12 +658,10 @@ def test_install_package_with_prefix(script, data):
'--no-binary', 'simple', '--no-index', 'simple==1.0', '--no-binary', 'simple', '--no-index', 'simple==1.0',
) )
if hasattr(sys, "pypy_version_info"): rel_prefix_path = script.scratch / 'prefix'
path = script.scratch / 'prefix'
else:
path = script.scratch / 'prefix' / 'lib' / 'python{0}'.format(pyversion) # noqa
install_path = ( install_path = (
path / 'site-packages' / 'simple-1.0-py{0}.egg-info'.format(pyversion) distutils.sysconfig.get_python_lib(prefix=rel_prefix_path) /
'simple-1.0-py{0}.egg-info'.format(pyversion)
) )
assert install_path in result.files_created, str(result) assert install_path in result.files_created, str(result)
@ -673,8 +676,11 @@ def test_install_editable_with_prefix(script):
version='0.1') version='0.1')
""")) """))
site_packages = os.path.join( if hasattr(sys, "pypy_version_info"):
'prefix', 'lib', 'python{0}'.format(pyversion), 'site-packages') site_packages = os.path.join(
'prefix', 'lib', 'python{0}'.format(pyversion), 'site-packages')
else:
site_packages = distutils.sysconfig.get_python_lib(prefix='prefix')
# make sure target path is in PYTHONPATH # make sure target path is in PYTHONPATH
pythonpath = script.scratch_path / site_packages pythonpath = script.scratch_path / site_packages
@ -745,7 +751,7 @@ def test_url_req_case_mismatch_no_index(script, data):
tests/data/packages contains Upper-1.0.tar.gz and Upper-2.0.tar.gz tests/data/packages contains Upper-1.0.tar.gz and Upper-2.0.tar.gz
'requiresupper' has install_requires = ['upper'] 'requiresupper' has install_requires = ['upper']
""" """
Upper = os.path.join(data.find_links, 'Upper-1.0.tar.gz') Upper = '/'.join((data.find_links, 'Upper-1.0.tar.gz'))
result = script.pip( result = script.pip(
'install', '--no-index', '-f', data.find_links, Upper, 'requiresupper' 'install', '--no-index', '-f', data.find_links, Upper, 'requiresupper'
) )
@ -772,7 +778,7 @@ def test_url_req_case_mismatch_file_index(script, data):
set of packages as it requires a prepared index.html file and set of packages as it requires a prepared index.html file and
subdirectory-per-package structure. subdirectory-per-package structure.
""" """
Dinner = os.path.join(data.find_links3, 'dinner', 'Dinner-1.0.tar.gz') Dinner = '/'.join((data.find_links3, 'dinner', 'Dinner-1.0.tar.gz'))
result = script.pip( result = script.pip(
'install', '--index-url', data.find_links3, Dinner, 'requiredinner' 'install', '--index-url', data.find_links3, Dinner, 'requiredinner'
) )
@ -964,6 +970,12 @@ def test_cleanup_after_failed_wheel(script, data, common_wheels):
@pytest.mark.network @pytest.mark.network
def test_install_builds_wheels(script, data, common_wheels): def test_install_builds_wheels(script, data, common_wheels):
# We need to use a subprocess to get the right value on Windows.
res = script.run('python', '-c', (
'from pip._internal.utils import appdirs; '
'print(appdirs.user_cache_dir("pip"))'
))
wheels_cache = os.path.join(res.stdout.rstrip('\n'), 'wheels')
# NB This incidentally tests a local tree + tarball inputs # NB This incidentally tests a local tree + tarball inputs
# see test_install_editable_from_git_autobuild_wheel for editable # see test_install_editable_from_git_autobuild_wheel for editable
# vcs coverage. # vcs coverage.
@ -976,9 +988,8 @@ def test_install_builds_wheels(script, data, common_wheels):
" upper-2.0 wheelbroken-0.1") " upper-2.0 wheelbroken-0.1")
# Must have installed it all # Must have installed it all
assert expected in str(res), str(res) assert expected in str(res), str(res)
root = appdirs.user_cache_dir('pip')
wheels = [] wheels = []
for top, dirs, files in os.walk(os.path.join(root, "wheels")): for top, dirs, files in os.walk(wheels_cache):
wheels.extend(files) wheels.extend(files)
# and built wheels for upper and wheelbroken # and built wheels for upper and wheelbroken
assert "Running setup.py bdist_wheel for upper" in str(res), str(res) assert "Running setup.py bdist_wheel for upper" in str(res), str(res)
@ -1085,7 +1096,9 @@ def test_double_install(script):
""" """
Test double install passing with two same version requirements Test double install passing with two same version requirements
""" """
result = script.pip('install', 'pip', 'pip', expect_error=False) result = script.pip('install', 'pip', 'pip',
use_module=True,
expect_error=False)
msg = "Double requirement given: pip (already in pip, name='pip')" msg = "Double requirement given: pip (already in pip, name='pip')"
assert msg not in result.stderr assert msg not in result.stderr
@ -1115,7 +1128,7 @@ def test_install_incompatible_python_requires(script, common_wheels):
) )
result = script.pip('install', pkga_path, expect_error=True) result = script.pip('install', pkga_path, expect_error=True)
assert ("pkga requires Python '<1.0' " assert ("pkga requires Python '<1.0' "
"but the running Python is ") in result.stderr "but the running Python is ") in result.stderr, str(result)
def test_install_incompatible_python_requires_editable(script, common_wheels): def test_install_incompatible_python_requires_editable(script, common_wheels):
@ -1134,7 +1147,7 @@ def test_install_incompatible_python_requires_editable(script, common_wheels):
result = script.pip( result = script.pip(
'install', '--editable=%s' % pkga_path, expect_error=True) 'install', '--editable=%s' % pkga_path, expect_error=True)
assert ("pkga requires Python '<1.0' " assert ("pkga requires Python '<1.0' "
"but the running Python is ") in result.stderr "but the running Python is ") in result.stderr, str(result)
@pytest.mark.network @pytest.mark.network

View File

@ -5,6 +5,7 @@ import pytest
from pip._internal.locations import write_delete_marker_file from pip._internal.locations import write_delete_marker_file
from pip._internal.status_codes import PREVIOUS_BUILD_DIR_ERROR from pip._internal.status_codes import PREVIOUS_BUILD_DIR_ERROR
from tests.lib import need_mercurial
from tests.lib.local_repos import local_checkout from tests.lib.local_repos import local_checkout
@ -36,6 +37,7 @@ def test_no_clean_option_blocks_cleaning_after_install(script, data):
@pytest.mark.network @pytest.mark.network
@need_mercurial
def test_cleanup_after_install_editable_from_hg(script, tmpdir): def test_cleanup_after_install_editable_from_hg(script, tmpdir):
""" """
Test clean up after cloning from Mercurial. Test clean up after cloning from Mercurial.

View File

@ -203,16 +203,19 @@ def test_options_from_venv_config(script, virtualenv):
def test_install_no_binary_via_config_disables_cached_wheels( def test_install_no_binary_via_config_disables_cached_wheels(
script, data, common_wheels): script, data, common_wheels):
script.pip('install', 'wheel', '--no-index', '-f', common_wheels) script.pip('install', 'wheel', '--no-index', '-f', common_wheels)
config_file = tempfile.NamedTemporaryFile(mode='wt') config_file = tempfile.NamedTemporaryFile(mode='wt', delete=False)
script.environ['PIP_CONFIG_FILE'] = config_file.name try:
config_file.write(textwrap.dedent("""\ script.environ['PIP_CONFIG_FILE'] = config_file.name
[global] config_file.write(textwrap.dedent("""\
no-binary = :all: [global]
""")) no-binary = :all:
config_file.flush() """))
res = script.pip( config_file.close()
'install', '--no-index', '-f', data.find_links, res = script.pip(
'upper', expect_stderr=True) 'install', '--no-index', '-f', data.find_links,
'upper', expect_stderr=True)
finally:
os.unlink(config_file.name)
assert "Successfully installed upper-2.0" in str(res), str(res) assert "Successfully installed upper-2.0" in str(res), str(res)
# No wheel building for upper, which was blacklisted # No wheel building for upper, which was blacklisted
assert "Running setup.py bdist_wheel for upper" not in str(res), str(res) assert "Running setup.py bdist_wheel for upper" not in str(res), str(res)

View File

@ -70,7 +70,7 @@ def test_nonexistent_extra_warns_user_no_wheel(script, data):
assert ( assert (
"simple 3.0 does not provide the extra 'nonexistent'" "simple 3.0 does not provide the extra 'nonexistent'"
in result.stderr in result.stderr
) ), str(result)
def test_nonexistent_extra_warns_user_with_wheel(script, data): def test_nonexistent_extra_warns_user_with_wheel(script, data):

View File

@ -1,3 +1,4 @@
import os
import textwrap import textwrap
from pip._vendor.six.moves.urllib import parse as urllib_parse from pip._vendor.six.moves.urllib import parse as urllib_parse
@ -29,7 +30,7 @@ def test_find_links_requirements_file_relative_path(script, data):
--no-index --no-index
--find-links=%s --find-links=%s
parent==0.1 parent==0.1
""" % data.packages)) """ % data.packages.replace(os.path.sep, '/')))
result = script.pip( result = script.pip(
'install', 'install',
'-r', '-r',

View File

@ -1,10 +1,11 @@
import os.path import os
import textwrap import textwrap
import pytest import pytest
from tests.lib import ( from tests.lib import (
_create_test_package_with_subdirectory, pyversion, requirements_file _create_test_package_with_subdirectory, path_to_url, pyversion,
requirements_file
) )
from tests.lib.local_repos import local_checkout from tests.lib.local_repos import local_checkout
@ -68,13 +69,13 @@ def test_relative_requirements_file(script, data):
# Compute relative install path to FSPkg from scratch path. # Compute relative install path to FSPkg from scratch path.
full_rel_path = data.packages.join('FSPkg') - script.scratch_path full_rel_path = data.packages.join('FSPkg') - script.scratch_path
full_rel_url = 'file:' + full_rel_path + '#egg=FSPkg'
embedded_rel_path = script.scratch_path.join(full_rel_path) embedded_rel_path = script.scratch_path.join(full_rel_path)
# For each relative path, install as either editable or not using either # For each relative path, install as either editable or not using either
# URLs with egg links or not. # URLs with egg links or not.
for req_path in (full_rel_path, for req_path in (full_rel_path, full_rel_url, embedded_rel_path):
'file:' + full_rel_path + '#egg=FSPkg', req_path = req_path.replace(os.path.sep, '/')
embedded_rel_path):
# Regular install. # Regular install.
with requirements_file(req_path + '\n', with requirements_file(req_path + '\n',
script.scratch_path) as reqs_file: script.scratch_path) as reqs_file:
@ -203,7 +204,7 @@ def test_install_local_editable_with_subdirectory(script):
result = script.pip( result = script.pip(
'install', '-e', 'install', '-e',
'%s#egg=version_subpkg&subdirectory=version_subdir' % '%s#egg=version_subpkg&subdirectory=version_subdir' %
('git+file://%s' % version_pkg_path,) ('git+%s' % path_to_url(version_pkg_path),)
) )
result.assert_installed('version-subpkg', sub_dir='version_subdir') result.assert_installed('version-subpkg', sub_dir='version_subdir')
@ -216,7 +217,7 @@ def test_install_local_with_subdirectory(script):
result = script.pip( result = script.pip(
'install', 'install',
'%s#egg=version_subpkg&subdirectory=version_subdir' % '%s#egg=version_subpkg&subdirectory=version_subdir' %
('git+file://%s' % version_pkg_path,) ('git+' + path_to_url(version_pkg_path),)
) )
result.assert_installed('version_subpkg.py', editable=False) result.assert_installed('version_subpkg.py', editable=False)
@ -228,9 +229,13 @@ def test_wheel_user_with_prefix_in_pydistutils_cfg(
# Make sure wheel is available in the virtualenv # Make sure wheel is available in the virtualenv
script.pip('install', 'wheel', '--no-index', '-f', common_wheels) script.pip('install', 'wheel', '--no-index', '-f', common_wheels)
virtualenv.system_site_packages = True virtualenv.system_site_packages = True
homedir = script.environ["HOME"] if os.name == 'posix':
user_filename = ".pydistutils.cfg"
else:
user_filename = "pydistutils.cfg"
user_cfg = os.path.join(os.path.expanduser('~'), user_filename)
script.scratch_path.join("bin").mkdir() script.scratch_path.join("bin").mkdir()
with open(os.path.join(homedir, ".pydistutils.cfg"), "w") as cfg: with open(user_cfg, "w") as cfg:
cfg.write(textwrap.dedent(""" cfg.write(textwrap.dedent("""
[install] [install]
prefix=%s""" % script.scratch_path)) prefix=%s""" % script.scratch_path))
@ -310,7 +315,7 @@ def test_constraints_local_install_causes_error(script, data):
def test_constraints_constrain_to_local_editable(script, data): def test_constraints_constrain_to_local_editable(script, data):
to_install = data.src.join("singlemodule") to_install = data.src.join("singlemodule")
script.scratch_path.join("constraints.txt").write( script.scratch_path.join("constraints.txt").write(
"-e file://%s#egg=singlemodule" % to_install "-e %s#egg=singlemodule" % path_to_url(to_install)
) )
result = script.pip( result = script.pip(
'install', '--no-index', '-f', data.find_links, '-c', 'install', '--no-index', '-f', data.find_links, '-c',
@ -321,7 +326,7 @@ def test_constraints_constrain_to_local_editable(script, data):
def test_constraints_constrain_to_local(script, data): def test_constraints_constrain_to_local(script, data):
to_install = data.src.join("singlemodule") to_install = data.src.join("singlemodule")
script.scratch_path.join("constraints.txt").write( script.scratch_path.join("constraints.txt").write(
"file://%s#egg=singlemodule" % to_install "%s#egg=singlemodule" % path_to_url(to_install)
) )
result = script.pip( result = script.pip(
'install', '--no-index', '-f', data.find_links, '-c', 'install', '--no-index', '-f', data.find_links, '-c',
@ -331,13 +336,13 @@ def test_constraints_constrain_to_local(script, data):
def test_constrained_to_url_install_same_url(script, data): def test_constrained_to_url_install_same_url(script, data):
to_install = data.src.join("singlemodule") to_install = data.src.join("singlemodule")
script.scratch_path.join("constraints.txt").write( constraints = path_to_url(to_install) + "#egg=singlemodule"
"file://%s#egg=singlemodule" % to_install script.scratch_path.join("constraints.txt").write(constraints)
)
result = script.pip( result = script.pip(
'install', '--no-index', '-f', data.find_links, '-c', 'install', '--no-index', '-f', data.find_links, '-c',
script.scratch_path / 'constraints.txt', to_install) script.scratch_path / 'constraints.txt', to_install)
assert 'Running setup.py install for singlemodule' in result.stdout assert ('Running setup.py install for singlemodule'
in result.stdout), str(result)
@pytest.mark.network @pytest.mark.network
@ -377,7 +382,7 @@ def test_double_install_spurious_hash_mismatch(
def test_install_with_extras_from_constraints(script, data): def test_install_with_extras_from_constraints(script, data):
to_install = data.packages.join("LocalExtras") to_install = data.packages.join("LocalExtras")
script.scratch_path.join("constraints.txt").write( script.scratch_path.join("constraints.txt").write(
"file://%s#egg=LocalExtras[bar]" % to_install "%s#egg=LocalExtras[bar]" % path_to_url(to_install)
) )
result = script.pip_install_local( result = script.pip_install_local(
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras') '-c', script.scratch_path / 'constraints.txt', 'LocalExtras')
@ -387,7 +392,7 @@ def test_install_with_extras_from_constraints(script, data):
def test_install_with_extras_from_install(script, data): def test_install_with_extras_from_install(script, data):
to_install = data.packages.join("LocalExtras") to_install = data.packages.join("LocalExtras")
script.scratch_path.join("constraints.txt").write( script.scratch_path.join("constraints.txt").write(
"file://%s#egg=LocalExtras" % to_install "%s#egg=LocalExtras" % path_to_url(to_install)
) )
result = script.pip_install_local( result = script.pip_install_local(
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]') '-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]')
@ -397,7 +402,7 @@ def test_install_with_extras_from_install(script, data):
def test_install_with_extras_joined(script, data): def test_install_with_extras_joined(script, data):
to_install = data.packages.join("LocalExtras") to_install = data.packages.join("LocalExtras")
script.scratch_path.join("constraints.txt").write( script.scratch_path.join("constraints.txt").write(
"file://%s#egg=LocalExtras[bar]" % to_install "%s#egg=LocalExtras[bar]" % path_to_url(to_install)
) )
result = script.pip_install_local( result = script.pip_install_local(
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]' '-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]'
@ -409,7 +414,7 @@ def test_install_with_extras_joined(script, data):
def test_install_with_extras_editable_joined(script, data): def test_install_with_extras_editable_joined(script, data):
to_install = data.packages.join("LocalExtras") to_install = data.packages.join("LocalExtras")
script.scratch_path.join("constraints.txt").write( script.scratch_path.join("constraints.txt").write(
"-e file://%s#egg=LocalExtras[bar]" % to_install "-e %s#egg=LocalExtras[bar]" % path_to_url(to_install)
) )
result = script.pip_install_local( result = script.pip_install_local(
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]') '-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]')

View File

@ -1,3 +1,4 @@
import distutils
import glob import glob
import os import os
import sys import sys
@ -187,11 +188,8 @@ def test_install_wheel_with_prefix(script, data):
'install', 'simple.dist==0.1', '--prefix', prefix_dir, 'install', 'simple.dist==0.1', '--prefix', prefix_dir,
'--no-index', '--find-links=' + data.find_links, '--no-index', '--find-links=' + data.find_links,
) )
if hasattr(sys, "pypy_version_info"): lib = distutils.sysconfig.get_python_lib(prefix=Path('scratch') / 'prefix')
lib = Path('scratch') / 'prefix' / 'site-packages' assert lib in result.files_created, str(result)
else:
lib = Path('scratch') / 'prefix' / 'lib'
assert lib in result.files_created
def test_install_from_wheel_installs_deps(script, data): def test_install_from_wheel_installs_deps(script, data):
@ -234,7 +232,7 @@ def test_install_user_wheel(script, virtualenv, data, common_wheels):
egg_info_folder = script.user_site / 'has.script-1.0.dist-info' egg_info_folder = script.user_site / 'has.script-1.0.dist-info'
assert egg_info_folder in result.files_created, str(result) assert egg_info_folder in result.files_created, str(result)
script_file = script.user_bin / 'script.py' script_file = script.user_bin / 'script.py'
assert script_file in result.files_created assert script_file in result.files_created, str(result)
def test_install_from_wheel_gen_entrypoint(script, data): def test_install_from_wheel_gen_entrypoint(script, data):

View File

@ -180,6 +180,8 @@ def test_uninstall_entry_point(script, console_scripts):
} }
) )
script_name = script.bin_path.join(console_scripts.split('=')[0].strip()) script_name = script.bin_path.join(console_scripts.split('=')[0].strip())
if sys.platform == 'win32':
script_name += '.exe'
result = script.pip('install', pkg_path) result = script.pip('install', pkg_path)
assert script_name.exists assert script_name.exists
result = script.pip('list', '--format=json') result = script.pip('list', '--format=json')
@ -204,6 +206,8 @@ def test_uninstall_gui_scripts(script):
entry_points={"gui_scripts": ["test_ = distutils_install", ], } entry_points={"gui_scripts": ["test_ = distutils_install", ], }
) )
script_name = script.bin_path.join('test_') script_name = script.bin_path.join('test_')
if sys.platform == 'win32':
script_name += '.exe'
script.pip('install', pkg_path) script.pip('install', pkg_path)
assert script_name.exists assert script_name.exists
script.pip('uninstall', pkg_name, '-y') script.pip('uninstall', pkg_name, '-y')
@ -424,8 +428,8 @@ def test_uninstall_setuptools_develop_install(script, data):
script.run('python', 'setup.py', 'install', script.run('python', 'setup.py', 'install',
expect_stderr=True, cwd=pkg_path) expect_stderr=True, cwd=pkg_path)
list_result = script.pip('list', '--format=json') list_result = script.pip('list', '--format=json')
assert {"name": "FSPkg", "version": "0.1.dev0"} \ assert {"name": os.path.normcase("FSPkg"), "version": "0.1.dev0"} \
in json.loads(list_result.stdout) in json.loads(list_result.stdout), str(list_result)
# Uninstall both develop and install # Uninstall both develop and install
uninstall = script.pip('uninstall', 'FSPkg', '-y') uninstall = script.pip('uninstall', 'FSPkg', '-y')
assert any(filename.endswith('.egg') assert any(filename.endswith('.egg')

View File

@ -1,7 +1,7 @@
""" """
tests specific to uninstalling --user installs tests specific to uninstalling --user installs
""" """
from os.path import isdir, isfile from os.path import isdir, isfile, normcase
import pytest import pytest
@ -49,8 +49,8 @@ class Tests_UninstallUserSite:
result3 = script.pip('uninstall', '-vy', 'pip-test-package') result3 = script.pip('uninstall', '-vy', 'pip-test-package')
# uninstall console is mentioning user scripts, but not global scripts # uninstall console is mentioning user scripts, but not global scripts
assert script.user_bin_path in result3.stdout assert normcase(script.user_bin_path) in result3.stdout, str(result3)
assert script.bin_path not in result3.stdout assert normcase(script.bin_path) not in result3.stdout, str(result3)
# uninstall worked # uninstall worked
assert_all_changes(result2, result3, [script.venv / 'build', 'cache']) assert_all_changes(result2, result3, [script.venv / 'build', 'cache'])

View File

@ -7,7 +7,9 @@ import re
import textwrap import textwrap
import site import site
import shutil import shutil
import subprocess
import pytest
import scripttest import scripttest
import six import six
import virtualenv import virtualenv
@ -32,7 +34,9 @@ def path_to_url(path):
filepath = path.split(os.path.sep) filepath = path.split(os.path.sep)
url = '/'.join(filepath) url = '/'.join(filepath)
if drive: if drive:
return 'file:///' + drive + url # Note: match urllib.request.pathname2url's
# behavior: uppercase the drive letter.
return 'file:///' + drive.upper() + url
return 'file://' + url return 'file://' + url
@ -197,10 +201,11 @@ class TestPipResult(object):
) )
egg_link_file = self.files_created[egg_link_path] egg_link_file = self.files_created[egg_link_path]
egg_link_contents = egg_link_file.bytes.replace(os.linesep, '\n')
# FIXME: I don't understand why there's a trailing . here # FIXME: I don't understand why there's a trailing . here
if not (egg_link_file.bytes.endswith('\n.') and if not (egg_link_contents.endswith('\n.') and
egg_link_file.bytes[:-2].endswith(pkg_dir)): egg_link_contents[:-2].endswith(pkg_dir)):
raise TestFailure(textwrap.dedent(u'''\ raise TestFailure(textwrap.dedent(u'''\
Incorrect egg_link file %r Incorrect egg_link file %r
Expected ending: %r Expected ending: %r
@ -209,7 +214,7 @@ class TestPipResult(object):
-------------------------------''' % ( -------------------------------''' % (
egg_link_file, egg_link_file,
pkg_dir + '\n.', pkg_dir + '\n.',
repr(egg_link_file.bytes)) repr(egg_link_contents))
)) ))
if use_user_site: if use_user_site:
@ -283,13 +288,20 @@ class PipTestEnvironment(scripttest.TestFileEnvironment):
self.site_packages_path = self.lib_path.join("site-packages") self.site_packages_path = self.lib_path.join("site-packages")
self.user_base_path = self.venv_path.join("user") self.user_base_path = self.venv_path.join("user")
self.user_bin_path = self.user_base_path.join(
self.bin_path - self.venv_path
)
self.user_site_path = self.venv_path.join( self.user_site_path = self.venv_path.join(
"user", "user",
site.USER_SITE[len(site.USER_BASE) + 1:], site.USER_SITE[len(site.USER_BASE) + 1:],
) )
if sys.platform == 'win32':
if sys.version_info >= (3, 5):
scripts_base = self.user_site_path.join('..').normpath
else:
scripts_base = self.user_base_path
self.user_bin_path = scripts_base.join('Scripts')
else:
self.user_bin_path = self.user_base_path.join(
self.bin_path - self.venv_path
)
# Create a Directory to use as a scratch pad # Create a Directory to use as a scratch pad
self.scratch_path = base_path.join("scratch").mkdir() self.scratch_path = base_path.join("scratch").mkdir()
@ -308,6 +320,8 @@ class PipTestEnvironment(scripttest.TestFileEnvironment):
environ["PYTHONUSERBASE"] = self.user_base_path environ["PYTHONUSERBASE"] = self.user_base_path
# Writing bytecode can mess up updated file detection # Writing bytecode can mess up updated file detection
environ["PYTHONDONTWRITEBYTECODE"] = "1" environ["PYTHONDONTWRITEBYTECODE"] = "1"
# Make sure we get UTF-8 on output, even on Windows...
environ["PYTHONIOENCODING"] = "UTF-8"
kwargs["environ"] = environ kwargs["environ"] = environ
# Call the TestFileEnvironment __init__ # Call the TestFileEnvironment __init__
@ -343,6 +357,9 @@ class PipTestEnvironment(scripttest.TestFileEnvironment):
run_from = kw.pop('run_from', None) run_from = kw.pop('run_from', None)
assert not cwd or not run_from, "Don't use run_from; it's going away" assert not cwd or not run_from, "Don't use run_from; it's going away"
cwd = cwd or run_from or self.cwd cwd = cwd or run_from or self.cwd
if sys.platform == 'win32':
# Partial fix for ScriptTest.run using `shell=True` on Windows.
args = [str(a).replace('^', '^^').replace('&', '^&') for a in args]
return TestPipResult( return TestPipResult(
super(PipTestEnvironment, self).run(cwd=cwd, *args, **kw), super(PipTestEnvironment, self).run(cwd=cwd, *args, **kw),
verbose=self.verbose, verbose=self.verbose,
@ -358,8 +375,12 @@ class PipTestEnvironment(scripttest.TestFileEnvironment):
# Python 3.3 is deprecated and we emit a warning on it. # Python 3.3 is deprecated and we emit a warning on it.
if pyversion_tuple[:2] == (3, 3): if pyversion_tuple[:2] == (3, 3):
kwargs['expect_stderr'] = True kwargs['expect_stderr'] = True
if kwargs.pop('use_module', False):
return self.run("pip", *args, **kwargs) exe = 'python'
args = ('-m', 'pip') + args
else:
exe = 'pip'
return self.run(exe, *args, **kwargs)
def pip_install_local(self, *args, **kwargs): def pip_install_local(self, *args, **kwargs):
return self.pip( return self.pip(
@ -730,3 +751,25 @@ def create_basic_wheel_for_package(script, name, version, depends, extras):
script.temp_path.mkdir() script.temp_path.mkdir()
return retval return retval
def need_executable(name, check_cmd):
def wrapper(fn):
try:
subprocess.check_output(check_cmd)
except OSError:
return pytest.mark.skip(reason='%s is not available' % name)(fn)
return fn
return wrapper
def need_bzr(fn):
return pytest.mark.bzr(need_executable(
'Bazaar', ('bzr', 'version', '--short')
)(fn))
def need_mercurial(fn):
return pytest.mark.mercurial(need_executable(
'Mercurial', ('hg', 'version')
)(fn))

View File

@ -39,7 +39,7 @@ class TestUserCacheDir:
def test_user_cache_dir_linux(self, monkeypatch): def test_user_cache_dir_linux(self, monkeypatch):
monkeypatch.setattr(appdirs, "WINDOWS", False) monkeypatch.setattr(appdirs, "WINDOWS", False)
monkeypatch.setattr(os, "path", posixpath) monkeypatch.setattr(os, "path", posixpath)
monkeypatch.delenv("XDG_CACHE_HOME") monkeypatch.delenv("XDG_CACHE_HOME", raising=False)
monkeypatch.setenv("HOME", "/home/test") monkeypatch.setenv("HOME", "/home/test")
monkeypatch.setattr(sys, "platform", "linux2") monkeypatch.setattr(sys, "platform", "linux2")
@ -58,7 +58,7 @@ class TestUserCacheDir:
monkeypatch.setattr(appdirs, "WINDOWS", False) monkeypatch.setattr(appdirs, "WINDOWS", False)
monkeypatch.setattr(os, "path", posixpath) monkeypatch.setattr(os, "path", posixpath)
# Verify that we are not affected by http://bugs.python.org/issue14768 # Verify that we are not affected by http://bugs.python.org/issue14768
monkeypatch.delenv("XDG_CACHE_HOME") monkeypatch.delenv("XDG_CACHE_HOME", raising=False)
monkeypatch.setenv("HOME", "/") monkeypatch.setenv("HOME", "/")
monkeypatch.setattr(sys, "platform", "linux2") monkeypatch.setattr(sys, "platform", "linux2")
@ -115,7 +115,7 @@ class TestSiteConfigDirs:
def test_site_config_dirs_linux(self, monkeypatch): def test_site_config_dirs_linux(self, monkeypatch):
monkeypatch.setattr(appdirs, "WINDOWS", False) monkeypatch.setattr(appdirs, "WINDOWS", False)
monkeypatch.setattr(os, "path", posixpath) monkeypatch.setattr(os, "path", posixpath)
monkeypatch.delenv("XDG_CONFIG_DIRS") monkeypatch.delenv("XDG_CONFIG_DIRS", raising=False)
monkeypatch.setattr(sys, "platform", "linux2") monkeypatch.setattr(sys, "platform", "linux2")
assert appdirs.site_config_dirs("pip") == [ assert appdirs.site_config_dirs("pip") == [
@ -194,7 +194,7 @@ class TestUserDataDir:
def test_user_data_dir_linux(self, monkeypatch): def test_user_data_dir_linux(self, monkeypatch):
monkeypatch.setattr(appdirs, "WINDOWS", False) monkeypatch.setattr(appdirs, "WINDOWS", False)
monkeypatch.setattr(os, "path", posixpath) monkeypatch.setattr(os, "path", posixpath)
monkeypatch.delenv("XDG_DATA_HOME") monkeypatch.delenv("XDG_DATA_HOME", raising=False)
monkeypatch.setenv("HOME", "/home/test") monkeypatch.setenv("HOME", "/home/test")
monkeypatch.setattr(sys, "platform", "linux2") monkeypatch.setattr(sys, "platform", "linux2")
@ -213,7 +213,7 @@ class TestUserDataDir:
monkeypatch.setattr(appdirs, "WINDOWS", False) monkeypatch.setattr(appdirs, "WINDOWS", False)
monkeypatch.setattr(os, "path", posixpath) monkeypatch.setattr(os, "path", posixpath)
# Verify that we are not affected by http://bugs.python.org/issue14768 # Verify that we are not affected by http://bugs.python.org/issue14768
monkeypatch.delenv("XDG_DATA_HOME") monkeypatch.delenv("XDG_DATA_HOME", raising=False)
monkeypatch.setenv("HOME", "/") monkeypatch.setenv("HOME", "/")
monkeypatch.setattr(sys, "platform", "linux2") monkeypatch.setattr(sys, "platform", "linux2")
@ -276,7 +276,7 @@ class TestUserConfigDir:
def test_user_config_dir_linux(self, monkeypatch): def test_user_config_dir_linux(self, monkeypatch):
monkeypatch.setattr(appdirs, "WINDOWS", False) monkeypatch.setattr(appdirs, "WINDOWS", False)
monkeypatch.setattr(os, "path", posixpath) monkeypatch.setattr(os, "path", posixpath)
monkeypatch.delenv("XDG_CONFIG_HOME") monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
monkeypatch.setenv("HOME", "/home/test") monkeypatch.setenv("HOME", "/home/test")
monkeypatch.setattr(sys, "platform", "linux2") monkeypatch.setattr(sys, "platform", "linux2")
@ -295,7 +295,7 @@ class TestUserConfigDir:
monkeypatch.setattr(appdirs, "WINDOWS", False) monkeypatch.setattr(appdirs, "WINDOWS", False)
monkeypatch.setattr(os, "path", posixpath) monkeypatch.setattr(os, "path", posixpath)
# Verify that we are not affected by http://bugs.python.org/issue14768 # Verify that we are not affected by http://bugs.python.org/issue14768
monkeypatch.delenv("XDG_CONFIG_HOME") monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
monkeypatch.setenv("HOME", "/") monkeypatch.setenv("HOME", "/")
monkeypatch.setattr(sys, "platform", "linux2") monkeypatch.setattr(sys, "platform", "linux2")

View File

@ -385,22 +385,18 @@ class TestInstallRequirement(object):
req = InstallRequirement.from_editable(url) req = InstallRequirement.from_editable(url)
assert req.link.url == url assert req.link.url == url
def test_get_dist(self): @pytest.mark.parametrize('path', (
req = InstallRequirement.from_line('foo') '/path/to/foo.egg-info'.replace('/', os.path.sep),
req.egg_info_path = Mock(return_value='/path/to/foo.egg-info')
dist = req.get_dist()
assert isinstance(dist, pkg_resources.Distribution)
assert dist.project_name == 'foo'
assert dist.location == '/path/to'
def test_get_dist_trailing_slash(self):
# Tests issue fixed by https://github.com/pypa/pip/pull/2530 # Tests issue fixed by https://github.com/pypa/pip/pull/2530
'/path/to/foo.egg-info/'.replace('/', os.path.sep),
))
def test_get_dist(self, path):
req = InstallRequirement.from_line('foo') req = InstallRequirement.from_line('foo')
req.egg_info_path = Mock(return_value='/path/to/foo.egg-info/') req.egg_info_path = Mock(return_value=path)
dist = req.get_dist() dist = req.get_dist()
assert isinstance(dist, pkg_resources.Distribution) assert isinstance(dist, pkg_resources.Distribution)
assert dist.project_name == 'foo' assert dist.project_name == 'foo'
assert dist.location == '/path/to' assert dist.location == '/path/to'.replace('/', os.path.sep)
def test_markers(self): def test_markers(self):
for line in ( for line in (

View File

@ -4,6 +4,7 @@ envlist =
py27, py33, py34, py35, py36, py37, pypy py27, py33, py34, py35, py36, py37, pypy
[testenv] [testenv]
passenv = GIT_SSL_CAINFO
setenv = setenv =
# This is required in order to get UTF-8 output inside of the subprocesses # This is required in order to get UTF-8 output inside of the subprocesses
# that our tests use. # that our tests use.