-
Notifications
You must be signed in to change notification settings - Fork 122
/
test_integration.py
143 lines (119 loc) · 4.78 KB
/
test_integration.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# SPDX-License-Identifier: MIT
import importlib.util
import os
import os.path
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_PYPY = sys.implementation.name == 'pypy'
MISSING_UV = not shutil.which('uv')
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.network
@pytest.mark.parametrize(
'call',
[
None, # via code
[sys.executable, '-m', 'build'], # module
['pyproject-build'], # entrypoint
],
ids=['code', 'module', 'entrypoint'],
)
@pytest.mark.parametrize(
'args',
[
[],
pytest.param(
['--installer', 'uv'],
marks=pytest.mark.skipif(MISSING_UV, reason='uv executable not found'),
),
['-x', '--no-isolation'],
],
ids=['isolated_pip', 'isolated_uv', 'no_isolation'],
)
@pytest.mark.parametrize(
'project',
[
'build',
'pip',
'dateutil',
'Solaar',
'flit',
],
)
@pytest.mark.isolated
def test_build(request, monkeypatch, project, args, call, tmp_path):
if args == ['--installer', 'uv'] and IS_WINDOWS and IS_PYPY:
pytest.xfail('uv cannot find PyPy executable')
if project in {'build', 'flit'} and '--no-isolation' in args:
pytest.xfail(f"can't build {project} without isolation due to missing dependencies")
if project == 'Solaar' and IS_WINDOWS and IS_PYPY:
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):
if importlib.util.find_spec('flit_core'):
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.")