diff options
Diffstat (limited to '')
-rw-r--r-- | tests/test_integration.py | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/tests/test_integration.py b/tests/test_integration.py new file mode 100644 index 0000000..bc2f4ff --- /dev/null +++ b/tests/test_integration.py @@ -0,0 +1,136 @@ +# SPDX-License-Identifier: MIT + +import os +import os.path +import platform +import re +import shutil +import subprocess +import sys +import tarfile +import urllib.request + +import filelock +import pytest + +import build.__main__ + + +IS_WINDOWS = sys.platform.startswith('win') +IS_PYPY3 = platform.python_implementation() == 'PyPy' + + +INTEGRATION_SOURCES = { + 'dateutil': ('dateutil/dateutil', '2.8.1'), + 'pip': ('pypa/pip', '20.2.1'), + 'Solaar': ('pwr-Solaar/Solaar', '1.0.3'), + 'flit': ('takluyver/flit', '2.3.0'), +} + +_SDIST = re.compile('.*.tar.gz') +_WHEEL = re.compile('.*.whl') +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +def get_project(name, tmp_path): + dest = tmp_path / name + if name == 'build': + # our own project is available in-source, just ignore development files + + def _ignore_folder(base, filenames): + ignore = [n for n in filenames if n in excl or any(n.endswith(i) for i in ('_cache', '.egg-info', '.pyc'))] + if os.path.basename == ROOT and 'build' in filenames: # ignore build only at root (our module is build too) + ignore.append('build') + return ignore + + excl = '.tox', 'dist', '.git', '__pycache__', '.integration-sources', '.github', 'tests', 'docs' + shutil.copytree(ROOT, str(dest), ignore=_ignore_folder) + return dest + + # for other projects download from github and cache it + tar_store = os.path.join(ROOT, '.integration-sources') + try: + os.makedirs(tar_store) + except OSError: # python 2 has no exist_ok, and checking with exists is not parallel safe + pass # just ignore, if the creation failed we will have another failure soon that will notify the user + + github_org_repo, version = INTEGRATION_SOURCES[name] + tar_filename = f'{name}-{version}.tar.gz' + tarball = os.path.join(tar_store, tar_filename) + with filelock.FileLock(os.path.join(tar_store, f'{tar_filename}.lock')): + if not os.path.exists(tarball): + url = f'https://github.com/{github_org_repo}/archive/{version}.tar.gz' + with urllib.request.urlopen(url) as request, open(tarball, 'wb') as file_handler: + shutil.copyfileobj(request, file_handler) + with tarfile.open(tarball, 'r:gz') as tar_handler: + tar_handler.extractall(str(dest)) + return dest / f'{name}-{version}' + + +@pytest.mark.parametrize( + 'call', + [ + None, # via code + [sys.executable, '-m', 'build'], # module + ['pyproject-build'], # entrypoint + ], + ids=['code', 'module', 'entrypoint'], +) +@pytest.mark.parametrize( + 'args', + [[], ['-x', '--no-isolation']], + ids=['isolated', 'no_isolation'], +) +@pytest.mark.parametrize( + 'project', + [ + 'build', + 'pip', + 'dateutil', + 'Solaar', + 'flit', + ], +) +@pytest.mark.isolated +def test_build(monkeypatch, project, args, call, tmp_path): + if project == 'flit' and '--no-isolation' in args: + pytest.xfail("can't build flit without isolation due to missing dependencies") + if project == 'Solaar' and IS_WINDOWS and IS_PYPY3: + pytest.xfail('Solaar fails building wheels via sdists on Windows on PyPy 3') + + monkeypatch.chdir(tmp_path) + monkeypatch.setenv('SETUPTOOLS_SCM_PRETEND_VERSION', '0+dummy') # for the projects that use setuptools_scm + + if call and call[0] == 'pyproject-build': + exe_name = f"pyproject-build{'.exe' if sys.platform.startswith('win') else ''}" + exe = os.path.join(os.path.dirname(sys.executable), exe_name) + if os.path.exists(exe): + call[0] = exe + else: + pytest.skip('Running via PYTHONPATH, so the pyproject-build entrypoint is not available') + path = get_project(project, tmp_path) + pkgs = tmp_path / 'pkgs' + args = [str(path), '-o', str(pkgs)] + args + + if call is None: + build.__main__.main(args) + else: + subprocess.check_call(call + args) + + pkg_names = os.listdir(str(pkgs)) + assert list(filter(_SDIST.match, pkg_names)) + assert list(filter(_WHEEL.match, pkg_names)) + + +def test_isolation(tmp_dir, package_test_flit, mocker): + try: + import flit_core # noqa: F401 + except ModuleNotFoundError: + pass + else: + pytest.xfail('flit_core is available -- we want it missing!') # pragma: no cover + + mocker.patch('build.__main__._error') + + build.__main__.main([package_test_flit, '-o', tmp_dir, '--no-isolation']) + build.__main__._error.assert_called_with("Backend 'flit_core.buildapi' is not available.") |