Skip to content

Commit ecce3d9

Browse files
authored
Merge pull request #1336 from pypa/test-cwd-error-message
Add test failure with descriptive error message when running tests without a {project} placeholder
2 parents 63ef1b0 + a157a51 commit ecce3d9

6 files changed

Lines changed: 80 additions & 10 deletions

File tree

cibuildwheel/linux.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
prepare_command,
2222
read_python_configs,
2323
split_config_settings,
24+
test_fail_cwd_file,
2425
unwrap,
2526
)
2627

@@ -306,9 +307,11 @@ def build_in_container(
306307
# set up a virtual environment to install and test from, to make sure
307308
# there are no dependencies that were pulled in at build time.
308309
container.call(["pip", "install", "virtualenv", *dependency_constraint_flags], env=env)
309-
venv_dir = (
310-
PurePath(container.call(["mktemp", "-d"], capture_output=True).strip()) / "venv"
310+
311+
testing_temp_dir = PurePosixPath(
312+
container.call(["mktemp", "-d"], capture_output=True).strip()
311313
)
314+
venv_dir = testing_temp_dir / "venv"
312315

313316
container.call(["python", "-m", "virtualenv", "--no-download", venv_dir], env=env)
314317

@@ -345,10 +348,14 @@ def build_in_container(
345348
project=container_project_path,
346349
package=container_package_dir,
347350
)
348-
container.call(["sh", "-c", test_command_prepared], cwd="/root", env=virtualenv_env)
351+
test_cwd = testing_temp_dir / "test_cwd"
352+
container.call(["mkdir", "-p", test_cwd])
353+
container.copy_into(test_fail_cwd_file, test_cwd / "test_fail.py")
354+
355+
container.call(["sh", "-c", test_command_prepared], cwd=test_cwd, env=virtualenv_env)
349356

350357
# clean up test environment
351-
container.call(["rm", "-rf", venv_dir])
358+
container.call(["rm", "-rf", testing_temp_dir])
352359

353360
# move repaired wheels to output
354361
if compatible_wheel is None:

cibuildwheel/macos.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
read_python_configs,
3737
shell,
3838
split_config_settings,
39+
test_fail_cwd_file,
3940
unwrap,
4041
virtualenv,
4142
)
@@ -563,17 +564,20 @@ def build(options: Options, tmp_path: Path) -> None:
563564
"pip", "install", *build_options.test_requires, env=virtualenv_env
564565
)
565566

566-
# run the tests from $HOME, with an absolute path in the command
567+
# run the tests from a temp dir, with an absolute path in the command
567568
# (this ensures that Python runs the tests against the installed wheel
568569
# and not the repo code)
569570
test_command_prepared = prepare_command(
570571
build_options.test_command,
571572
project=Path(".").resolve(),
572573
package=build_options.package_dir.resolve(),
573574
)
574-
shell_with_arch(
575-
test_command_prepared, cwd=os.environ["HOME"], env=virtualenv_env
576-
)
575+
576+
test_cwd = identifier_tmp_dir / "test_cwd"
577+
test_cwd.mkdir(exist_ok=True)
578+
(test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text())
579+
580+
shell_with_arch(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
577581

578582
# we're all done here; move it to output (overwrite existing)
579583
if compatible_wheel is None:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# this file is copied to the testing cwd, to raise the below error message if
2+
# pytest/unittest is run from there.
3+
4+
import unittest
5+
6+
7+
class TestStringMethods(unittest.TestCase):
8+
def test_fail(self):
9+
self.fail(
10+
"cibuildwheel executes tests from a different working directory to "
11+
"your project. This ensures only your wheel is imported, preventing "
12+
"Python from accessing files that haven't been packaged into the "
13+
"wheel. Please specify a path to your tests when invoking pytest "
14+
"using the {project} placeholder, e.g. `pytest {project}` or "
15+
"`pytest {project}/tests`. cibuildwheel will replace {project} with "
16+
"the path to your project."
17+
)

cibuildwheel/util.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767

6868
install_certifi_script: Final[Path] = resources_dir / "install_certifi.py"
6969

70+
test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"
71+
7072
BuildFrontend = Literal["pip", "build"]
7173

7274
MANYLINUX_ARCHS: Final[tuple[str, ...]] = (

cibuildwheel/windows.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
read_python_configs,
3636
shell,
3737
split_config_settings,
38+
test_fail_cwd_file,
3839
unwrap,
3940
virtualenv,
4041
)
@@ -542,15 +543,19 @@ def build(options: Options, tmp_path: Path) -> None:
542543
if build_options.test_requires:
543544
call("pip", "install", *build_options.test_requires, env=virtualenv_env)
544545

545-
# run the tests from c:\, with an absolute path in the command
546+
# run the tests from a temp dir, with an absolute path in the command
546547
# (this ensures that Python runs the tests against the installed wheel
547548
# and not the repo code)
548549
test_command_prepared = prepare_command(
549550
build_options.test_command,
550551
project=Path(".").resolve(),
551552
package=options.globals.package_dir.resolve(),
552553
)
553-
shell(test_command_prepared, cwd="c:\\", env=virtualenv_env)
554+
test_cwd = identifier_tmp_dir / "test_cwd"
555+
test_cwd.mkdir()
556+
(test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text())
557+
558+
shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)
554559

555560
# we're all done here; move it to output (remove if already exists)
556561
if compatible_wheel is None:

test/test_testing.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import subprocess
55
import textwrap
6+
from pathlib import Path
67

78
import pytest
89

@@ -151,3 +152,37 @@ def test_failing_test(tmp_path):
151152
)
152153

153154
assert len(os.listdir(output_dir)) == 0
155+
156+
157+
@pytest.mark.parametrize("test_runner", ["pytest", "unittest"])
158+
def test_bare_pytest_invocation(
159+
tmp_path: Path, capfd: pytest.CaptureFixture[str], test_runner: str
160+
):
161+
"""Check that if a user runs pytest in the the test cwd, it raises a helpful error"""
162+
project_dir = tmp_path / "project"
163+
output_dir = tmp_path / "output"
164+
project_with_a_test.generate(project_dir)
165+
166+
with pytest.raises(subprocess.CalledProcessError):
167+
utils.cibuildwheel_run(
168+
project_dir,
169+
output_dir=output_dir,
170+
add_env={
171+
"CIBW_TEST_REQUIRES": "pytest" if test_runner == "pytest" else "",
172+
"CIBW_TEST_COMMAND": (
173+
"python -m pytest" if test_runner == "pytest" else "python -m unittest"
174+
),
175+
# Skip CPython 3.8 on macOS arm64, see comment above in
176+
# 'test_failing_test'
177+
"CIBW_SKIP": "cp38-macosx_arm64",
178+
},
179+
)
180+
181+
assert len(os.listdir(output_dir)) == 0
182+
183+
captured = capfd.readouterr()
184+
185+
assert (
186+
"Please specify a path to your tests when invoking pytest using the {project} placeholder"
187+
in captured.out + captured.err
188+
)

0 commit comments

Comments
 (0)