Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a test-sources configuration option. #2062

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ Options
| | [`CIBW_DEPENDENCY_VERSIONS`](https://cibuildwheel.pypa.io/en/stable/options/#dependency-versions) | Specify how cibuildwheel controls the versions of the tools it uses |
| **Testing** | [`CIBW_TEST_COMMAND`](https://cibuildwheel.pypa.io/en/stable/options/#test-command) | Execute a shell command to test each built wheel |
| | [`CIBW_BEFORE_TEST`](https://cibuildwheel.pypa.io/en/stable/options/#before-test) | Execute a shell command before testing each wheel |
| | [`CIBW_TEST_SOURCES`](https://cibuildwheel.pypa.io/en/stable/options/#test-sources) | Files and folders from the source tree that are copied into an isolated tree before running the tests |
| | [`CIBW_TEST_REQUIRES`](https://cibuildwheel.pypa.io/en/stable/options/#test-requires) | Install Python dependencies before running the tests |
| | [`CIBW_TEST_EXTRAS`](https://cibuildwheel.pypa.io/en/stable/options/#test-extras) | Install your wheel for testing using extras_require |
| | [`CIBW_TEST_SKIP`](https://cibuildwheel.pypa.io/en/stable/options/#test-skip) | Skip running tests on some builds |
Expand Down
18 changes: 14 additions & 4 deletions cibuildwheel/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
from .util import (
BuildFrontendConfig,
BuildSelector,
copy_test_sources,
find_compatible_wheel,
get_build_verbosity_extra_flags,
prepare_command,
read_python_configs,
split_config_settings,
test_fail_cwd_file,
unwrap,
)

Expand Down Expand Up @@ -401,9 +401,19 @@ def build_in_container(
package=container_package_dir,
wheel=wheel_to_test,
)
test_cwd = testing_temp_dir / "test_cwd"
container.call(["mkdir", "-p", test_cwd])
container.copy_into(test_fail_cwd_file, test_cwd / "test_fail.py")

if build_options.test_sources:
test_cwd = testing_temp_dir / "test_cwd"
container.call(["mkdir", "-p", test_cwd])
copy_test_sources(
build_options.test_sources,
build_options.package_dir,
test_cwd,
copy_into=container.copy_into,
)
else:
# There are no test sources. Run the tests in the project directory.
test_cwd = PurePosixPath(container_project_path)

container.call(["sh", "-c", test_command_prepared], cwd=test_cwd, env=virtualenv_env)

Expand Down
16 changes: 12 additions & 4 deletions cibuildwheel/macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
BuildSelector,
call,
combine_constraints,
copy_test_sources,
detect_ci_provider,
download,
find_compatible_wheel,
Expand All @@ -44,7 +45,6 @@
read_python_configs,
shell,
split_config_settings,
test_fail_cwd_file,
unwrap,
virtualenv,
)
Expand Down Expand Up @@ -736,9 +736,17 @@ def build(options: Options, tmp_path: Path) -> None:
wheel=repaired_wheel,
)

test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir(exist_ok=True)
(test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text())
if build_options.test_sources:
test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir(exist_ok=True)
copy_test_sources(
build_options.test_sources,
build_options.package_dir,
test_cwd,
)
else:
# There are no test sources. Run the tests in the project directory.
test_cwd = Path(".").resolve()

shell_with_arch(test_command_prepared, cwd=test_cwd, env=virtualenv_env)

Expand Down
14 changes: 11 additions & 3 deletions cibuildwheel/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import functools
import shlex
import textwrap
from collections.abc import Generator, Iterable, Set
from collections.abc import Callable, Generator, Iterable, Set
from pathlib import Path
from typing import Any, Literal, Mapping, Sequence, Union # noqa: TID251

Expand Down Expand Up @@ -90,6 +90,7 @@ class BuildOptions:
dependency_constraints: DependencyConstraints | None
test_command: str | None
before_test: str | None
test_sources: list[str]
test_requires: list[str]
test_extras: str
test_groups: list[str]
Expand Down Expand Up @@ -169,11 +170,12 @@ class ListFormat(OptionFormat):
A format that joins lists with a separator.
"""

def __init__(self, sep: str) -> None:
def __init__(self, sep: str, quote: Callable[[str], str] | None = None) -> None:
self.sep = sep
self.quote = quote if quote else lambda s: s

def format_list(self, value: SettingList) -> str:
return self.sep.join(str(v) for v in value)
return self.sep.join(self.quote(str(v)) for v in value)

def merge_values(self, before: str, after: str) -> str:
return f"{before}{self.sep}{after}"
Expand Down Expand Up @@ -677,6 +679,11 @@ def build_options(self, identifier: str | None) -> BuildOptions:
dependency_versions = self.reader.get("dependency-versions")
test_command = self.reader.get("test-command", option_format=ListFormat(sep=" && "))
before_test = self.reader.get("before-test", option_format=ListFormat(sep=" && "))
test_sources = shlex.split(
self.reader.get(
"test-sources", option_format=ListFormat(sep=" ", quote=shlex.quote)
)
)
test_requires = self.reader.get(
"test-requires", option_format=ListFormat(sep=" ")
).split()
Expand Down Expand Up @@ -785,6 +792,7 @@ def build_options(self, identifier: str | None) -> BuildOptions:
return BuildOptions(
globals=self.globals,
test_command=test_command,
test_sources=test_sources,
test_requires=[*test_requires, *test_requirements_from_groups],
test_extras=test_extras,
test_groups=test_groups,
Expand Down
16 changes: 12 additions & 4 deletions cibuildwheel/pyodide.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
BuildSelector,
call,
combine_constraints,
copy_test_sources,
download,
ensure_node,
extract_zip,
Expand All @@ -31,7 +32,6 @@
read_python_configs,
shell,
split_config_settings,
test_fail_cwd_file,
virtualenv,
)

Expand Down Expand Up @@ -379,9 +379,17 @@ def build(options: Options, tmp_path: Path) -> None:
package=build_options.package_dir.resolve(),
)

test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir(exist_ok=True)
(test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text())
if build_options.test_sources:
test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir(exist_ok=True)
copy_test_sources(
build_options.test_sources,
build_options.package_dir,
test_cwd,
)
else:
# There are no test sources. Run the tests in the project directory.
test_cwd = Path(".").resolve()

shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)

Expand Down
33 changes: 33 additions & 0 deletions cibuildwheel/resources/cibuildwheel.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,21 @@
],
"title": "CIBW_TEST_EXTRAS"
},
"test-sources": {
"description": "Test files that are required by the test environment",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"title": "CIBW_TEST_SOURCES"
},
"test-groups": {
"description": "Install extra groups when testing",
"oneOf": [
Expand Down Expand Up @@ -500,6 +515,9 @@
"test-extras": {
"$ref": "#/$defs/inherit"
},
"test-sources": {
"$ref": "#/$defs/inherit"
},
"test-requires": {
"$ref": "#/$defs/inherit"
}
Expand Down Expand Up @@ -586,6 +604,9 @@
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
Expand Down Expand Up @@ -693,6 +714,9 @@
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
Expand Down Expand Up @@ -741,6 +765,9 @@
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
Expand Down Expand Up @@ -802,6 +829,9 @@
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
Expand Down Expand Up @@ -850,6 +880,9 @@
"test-extras": {
"$ref": "#/properties/test-extras"
},
"test-sources": {
"$ref": "#/properties/test-sources"
},
"test-groups": {
"$ref": "#/properties/test-groups"
},
Expand Down
1 change: 1 addition & 0 deletions cibuildwheel/resources/defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ repair-wheel-command = ""

test-command = ""
before-test = ""
test-sources = []
test-requires = []
test-extras = []
test-groups = []
Expand Down
17 changes: 0 additions & 17 deletions cibuildwheel/resources/testing_temp_dir_file.py

This file was deleted.

41 changes: 38 additions & 3 deletions cibuildwheel/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import typing
import urllib.request
from collections import defaultdict
from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence
from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping, Sequence
from dataclasses import dataclass
from enum import Enum
from functools import lru_cache, total_ordering
Expand All @@ -35,6 +35,7 @@
from packaging.version import Version
from platformdirs import user_cache_path

from . import errors
from ._compat import tomllib
from .architecture import Architecture
from .typing import PathOrStr, PlatformName
Expand Down Expand Up @@ -63,8 +64,6 @@

free_thread_enable_313: Final[Path] = resources_dir / "free-threaded-enable-313.xml"

test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"


MANYLINUX_ARCHS: Final[tuple[str, ...]] = (
"x86_64",
Expand Down Expand Up @@ -394,6 +393,42 @@ def move_file(src_file: Path, dst_file: Path) -> Path:
return Path(resulting_file).resolve(strict=True)


def copy_into_local(src: Path, dst: PurePath) -> None:
"""Copy a path from src to dst, regardless of whether it's a file or a directory."""
# Ensure the target folder location exists
Path(dst.parent).mkdir(exist_ok=True, parents=True)

if src.is_dir():
shutil.copytree(src, dst)
else:
shutil.copy(src, dst)


def copy_test_sources(
test_sources: list[str],
package_dir: Path,
test_dir: PurePath,
copy_into: Callable[[Path, PurePath], None] = copy_into_local,
) -> None:
"""Copy the list of test sources from the package to the test directory.

:param test_sources: A list of test paths, relative to the package_dir.
:param package_dir: The root of the package directory.
:param test_dir: The folder where test sources should be placed.
:param copy_info: The copy function to use. By default, does a local
filesystem copy; but an OCIContainer.copy_info method (or equivalent)
can be provided.
"""
for test_path in test_sources:
source = package_dir.resolve() / test_path

if not source.exists():
msg = f"Test source {test_path} does not exist."
raise errors.FatalError(msg)

copy_into(source, test_dir / test_path)


class DependencyConstraints:
def __init__(self, base_file_path: Path):
assert base_file_path.exists()
Expand Down
16 changes: 12 additions & 4 deletions cibuildwheel/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
BuildSelector,
call,
combine_constraints,
copy_test_sources,
download,
extract_zip,
find_compatible_wheel,
Expand All @@ -38,7 +39,6 @@
read_python_configs,
shell,
split_config_settings,
test_fail_cwd_file,
unwrap,
virtualenv,
)
Expand Down Expand Up @@ -572,9 +572,17 @@ def build(options: Options, tmp_path: Path) -> None:
package=options.globals.package_dir.resolve(),
wheel=repaired_wheel,
)
test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir()
(test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text())
if build_options.test_sources:
test_cwd = identifier_tmp_dir / "test_cwd"
test_cwd.mkdir()
copy_test_sources(
build_options.test_sources,
build_options.package_dir,
test_cwd,
)
else:
# There are no test sources. Run the tests in the project directory.
test_cwd = Path(".").resolve()

shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env)

Expand Down
Loading
Loading