Skip to content

Commit 4fe7630

Browse files
freakboy3742mhsmithjoerick
authored
feat: add configuration option for test executor arguments (#2636)
* Add test-execution-args option. * Add usage of test-execution-args. * Add CI configuration to use test-execution-args. * Document the test-execution-args setting. * Simplify code using or syntax instead of inline if. Co-authored-by: Malcolm Smith <[email protected]> * Clarified some Android-specific terminology, and added details about the default args to the test runner. * Switch to a dict-based test-execution configuration * Add tests for test-execution parsing. * Add all the files before pushing... * Add note about default Android version for testbed. * Improve description of test-execution setting. Co-authored-by: Joe Rickerby <[email protected]> * Switch to using test-runtime. --------- Co-authored-by: Malcolm Smith <[email protected]> Co-authored-by: Joe Rickerby <[email protected]>
1 parent c53e541 commit 4fe7630

File tree

11 files changed

+231
-8
lines changed

11 files changed

+231
-8
lines changed

.github/workflows/test.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,13 @@ jobs:
7979
- os: macos-15
8080
python_version: '3.13'
8181
test_select: ios
82+
# Exercise iOS on a non-default simulator.
83+
test_runtime: 'args: --simulator "iPhone 16e,OS=18.5"'
8284
- os: macos-15-intel
8385
python_version: '3.13'
8486
test_select: android
87+
# Exercise Android on a non-default simulator
88+
test_runtime: 'args: --managed minVersion'
8589
- os: macos-15
8690
python_version: '3.13'
8791
test_select: android
@@ -154,6 +158,7 @@ jobs:
154158
CIBW_ARCHS_MACOS: x86_64 universal2 arm64
155159
CIBW_BUILD_FRONTEND: ${{ matrix.test_select && 'build' || 'build[uv]' }}
156160
CIBW_PLATFORM: ${{ matrix.test_select }}
161+
CIBW_TEST_RUNTIME: ${{ matrix.test_runtime }}
157162

158163
- name: Run a sample build (GitHub Action, only)
159164
uses: ./
@@ -179,6 +184,7 @@ jobs:
179184
uses: ./
180185
env:
181186
CIBW_PLATFORM: ${{ matrix.test_select }}
187+
CIBW_TEST_RUNTIME: ${{ matrix.test_runtime }}
182188
with:
183189
package-dir: sample_proj
184190
output-dir: wheelhouse_config_file
@@ -202,6 +208,8 @@ jobs:
202208
path: wheelhouse/*.whl
203209

204210
- name: Test cibuildwheel
211+
env:
212+
CIBW_TEST_RUNTIME: ${{ matrix.test_runtime }}
205213
run: |
206214
uv run --no-sync bin/run_tests.py --test-select=${{ matrix.test_select || 'native' }} ${{ (runner.os == 'Linux' && runner.arch == 'X64') && '--run-podman' || '' }}
207215

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ Usage
5959

6060
| | Linux | macOS | Windows | Linux ARM | macOS ARM | Windows ARM | Android | iOS |
6161
|-----------------|-------|-------|---------|-----------|-----------|-------------|---------|-----|
62-
| GitHub Actions |||||| ✅² | ✅⁴ | ✅³ |
63-
| Azure Pipelines |||| || ✅² | ✅⁴ | ✅³ |
62+
| GitHub Actions |||||| ✅² | ✅⁴ | ✅³ |
63+
| Azure Pipelines |||| || ✅² | ✅⁴ | ✅³ |
6464
| Travis CI || ||| | | ✅⁴ | |
6565
| CircleCI ||| ||| | ✅⁴ | ✅³ |
6666
| Gitlab CI |||| ✅¹ || | ✅⁴ | ✅³ |
@@ -70,7 +70,6 @@ Usage
7070
<sup[Uses cross-compilation](https://cibuildwheel.pypa.io/en/stable/faq/#windows-arm64). It is not possible to test `arm64` on this CI platform.</sup><br>
7171
<sup>³ Requires a macOS runner; runs tests on the simulator for the runner's architecture. </sup><br>
7272
<sup>⁴ Building for Android requires the runner to be Linux x86_64, macOS ARM64 or macOS x86_64. Testing has [additional requirements](https://cibuildwheel.pypa.io/en/stable/platforms/#android).</sup><br>
73-
<sup>⁵ The `macos-15` and `macos-latest` images are [incompatible with cibuildwheel at this time](https://cibuildwheel.pypa.io/en/stable/platforms/#ios-system-requirements) when building iOS wheels.</sup><br>
7473

7574
<!--intro-end-->
7675

@@ -160,12 +159,13 @@ The following diagram summarises the steps that cibuildwheel takes on each platf
160159
| | [`test-groups`](https://cibuildwheel.pypa.io/en/stable/options/#test-groups) | Specify test dependencies from your project's `dependency-groups` |
161160
| | [`test-skip`](https://cibuildwheel.pypa.io/en/stable/options/#test-skip) | Skip running tests on some builds |
162161
| | [`test-environment`](https://cibuildwheel.pypa.io/en/stable/options/#test-environment) | Set environment variables for the test environment |
162+
| | [`test-runtime`](https://cibuildwheel.pypa.io/en/stable/options/#test-runtime) | Controls how the tests will be executed. |
163163
| **Debugging** | [`debug-keep-container`](https://cibuildwheel.pypa.io/en/stable/options/#debug-keep-container) | Keep the container after running for debugging. |
164164
| | [`debug-traceback`](https://cibuildwheel.pypa.io/en/stable/options/#debug-traceback) | Print full traceback when errors occur. |
165165
| | [`build-verbosity`](https://cibuildwheel.pypa.io/en/stable/options/#build-verbosity) | Increase/decrease the output of the build |
166166

167167

168-
<!--[[[end]]] (sum: FxE3nIgFiY) -->
168+
<!--[[[end]]] (sum: dbfwOkj/k/) -->
169169

170170
These options can be specified in a pyproject.toml file, or as environment variables, see [configuration docs](https://cibuildwheel.pypa.io/en/latest/configuration/).
171171

bin/generate_schema.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,24 @@
224224
test-environment:
225225
description: Set environment variables for the test environment
226226
type: string_table
227+
test-runtime:
228+
description: Additional configuration for the test runner
229+
oneOf:
230+
- type: string
231+
pattern: '^$'
232+
- type: object
233+
additionalProperties: false
234+
- type: string
235+
pattern: 'args:'
236+
- type: object
237+
additionalProperties: false
238+
required: [args]
239+
properties:
240+
args:
241+
type: array
242+
items:
243+
type: string
244+
227245
"""
228246

229247
schema = yaml.safe_load(starter)
@@ -304,6 +322,7 @@
304322
test-sources: {"$ref": "#/$defs/inherit"}
305323
test-requires: {"$ref": "#/$defs/inherit"}
306324
test-environment: {"$ref": "#/$defs/inherit"}
325+
test-runtime: {"$ref": "#/$defs/inherit"}
307326
"""
308327
)
309328

cibuildwheel/options.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from .selector import BuildSelector, EnableGroup, TestSelector, selector_matches
2525
from .typing import PLATFORMS, PlatformName
2626
from .util import resources
27-
from .util.helpers import format_safe, strtobool, unwrap
27+
from .util.helpers import format_safe, parse_key_value_string, strtobool, unwrap
2828
from .util.packaging import DependencyConstraints
2929

3030
MANYLINUX_ARCHS: Final[tuple[str, ...]] = (
@@ -92,6 +92,20 @@ class GlobalOptions:
9292
allow_empty: bool
9393

9494

95+
@dataclasses.dataclass(frozen=True)
96+
class TestRuntimeConfig:
97+
args: Sequence[str] = ()
98+
99+
@classmethod
100+
def from_config_string(cls, config_string: str) -> Self:
101+
config_dict = parse_key_value_string(config_string, [], ["args"])
102+
args = config_dict.get("args") or []
103+
return cls(args=args)
104+
105+
def options_summary(self) -> str | dict[str, str]:
106+
return {"args": repr(self.args)}
107+
108+
95109
@dataclasses.dataclass(frozen=True, kw_only=True)
96110
class BuildOptions:
97111
globals: GlobalOptions
@@ -110,6 +124,7 @@ class BuildOptions:
110124
test_extras: str
111125
test_groups: list[str]
112126
test_environment: ParsedEnvironment
127+
test_runtime: TestRuntimeConfig
113128
build_verbosity: int
114129
build_frontend: BuildFrontendConfig
115130
config_settings: str
@@ -761,6 +776,20 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
761776
msg = f"Malformed environment option {test_environment_config!r}"
762777
raise errors.ConfigurationError(msg) from e
763778

779+
test_runtime_str = self.reader.get(
780+
"test-runtime",
781+
env_plat=False,
782+
option_format=ShlexTableFormat(sep="; ", pair_sep=":", allow_merge=False),
783+
)
784+
if not test_runtime_str:
785+
test_runtime = TestRuntimeConfig()
786+
else:
787+
try:
788+
test_runtime = TestRuntimeConfig.from_config_string(test_runtime_str)
789+
except ValueError as e:
790+
msg = f"Failed to parse test runtime config. {e}"
791+
raise errors.ConfigurationError(msg) from e
792+
764793
test_requires = self.reader.get(
765794
"test-requires", option_format=ListFormat(sep=" ")
766795
).split()
@@ -868,6 +897,7 @@ def _compute_build_options(self, identifier: str | None) -> BuildOptions:
868897
test_command=test_command,
869898
test_sources=test_sources,
870899
test_environment=test_environment,
900+
test_runtime=test_runtime,
871901
test_requires=[*test_requires, *test_requirements_from_groups],
872902
test_extras=test_extras,
873903
test_groups=test_groups,

cibuildwheel/platforms/android.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -638,17 +638,27 @@ def test_wheel(state: BuildState, wheel: Path) -> None:
638638
)
639639
raise errors.FatalError(msg)
640640

641+
# By default, run on a testbed managed emulator running the newest supported
642+
# Android version. However, if the user specifies a --managed or --connected
643+
# test execution argument, that argument takes precedence.
644+
test_runtime_args = state.options.test_runtime.args
645+
646+
if any(arg.startswith(("--managed", "--connected")) for arg in test_runtime_args):
647+
emulator_args = []
648+
else:
649+
emulator_args = ["--managed", "maxVersion"]
650+
641651
# Run the test app.
642652
call(
643653
state.python_dir / "android.py",
644654
"test",
645-
"--managed",
646-
"maxVersion",
647655
"--site-packages",
648656
site_packages_dir,
649657
"--cwd",
650658
cwd_dir,
659+
*emulator_args,
651660
*(["-v"] if state.options.build_verbosity > 0 else []),
661+
*test_runtime_args,
652662
"--",
653663
*test_args,
654664
env=state.build_env,

cibuildwheel/platforms/ios.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import dataclasses
44
import os
5+
import platform
56
import shlex
67
import shutil
78
import subprocess
@@ -653,11 +654,35 @@ def build(options: Options, tmp_path: Path) -> None:
653654
)
654655
raise errors.FatalError(msg)
655656

657+
test_runtime_args = build_options.test_runtime.args
658+
659+
# 2025-10: The GitHub Actions macos-15 runner has a known issue where
660+
# the default simulator won't start due to a disk performance issue;
661+
# see https://github.com/actions/runner-images/issues/12777 for details.
662+
# In the meantime, if it looks like we're running on a GitHub Actions
663+
# macos-15 runner, use a simulator that is known to work, unless the
664+
# user explicitly specifies a simulator.
665+
os_version, _, arch = platform.mac_ver()
666+
if (
667+
"GITHUB_ACTIONS" in os.environ
668+
and os_version.startswith("15.")
669+
and arch == "arm64"
670+
and not any(
671+
arg.startswith("--simulator") for arg in test_runtime_args
672+
)
673+
):
674+
test_runtime_args = [
675+
"--simulator",
676+
"iPhone 16e,OS=18.5",
677+
*test_runtime_args,
678+
]
679+
656680
call(
657681
"python",
658682
testbed_path,
659683
"run",
660684
*(["--verbose"] if build_options.build_verbosity > 0 else []),
685+
*test_runtime_args,
661686
"--",
662687
*final_command,
663688
env=test_env,

cibuildwheel/resources/cibuildwheel.schema.json

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,39 @@
569569
],
570570
"title": "CIBW_TEST_ENVIRONMENT"
571571
},
572+
"test-runtime": {
573+
"description": "Additional configuration for the test runner",
574+
"oneOf": [
575+
{
576+
"type": "string",
577+
"pattern": "^$"
578+
},
579+
{
580+
"type": "object",
581+
"additionalProperties": false
582+
},
583+
{
584+
"type": "string",
585+
"pattern": "args:"
586+
},
587+
{
588+
"type": "object",
589+
"additionalProperties": false,
590+
"required": [
591+
"args"
592+
],
593+
"properties": {
594+
"args": {
595+
"type": "array",
596+
"items": {
597+
"type": "string"
598+
}
599+
}
600+
}
601+
}
602+
],
603+
"title": "CIBW_TEST_RUNTIME"
604+
},
572605
"overrides": {
573606
"type": "array",
574607
"description": "An overrides array",
@@ -638,6 +671,9 @@
638671
},
639672
"test-environment": {
640673
"$ref": "#/$defs/inherit"
674+
},
675+
"test-runtime": {
676+
"$ref": "#/$defs/inherit"
641677
}
642678
}
643679
},
@@ -748,6 +784,9 @@
748784
},
749785
"test-environment": {
750786
"$ref": "#/properties/test-environment"
787+
},
788+
"test-runtime": {
789+
"$ref": "#/properties/test-runtime"
751790
}
752791
}
753792
}
@@ -876,6 +915,9 @@
876915
},
877916
"test-environment": {
878917
"$ref": "#/properties/test-environment"
918+
},
919+
"test-runtime": {
920+
"$ref": "#/properties/test-runtime"
879921
}
880922
}
881923
},
@@ -936,6 +978,9 @@
936978
},
937979
"test-environment": {
938980
"$ref": "#/properties/test-environment"
981+
},
982+
"test-runtime": {
983+
"$ref": "#/properties/test-runtime"
939984
}
940985
}
941986
},
@@ -1009,6 +1054,9 @@
10091054
},
10101055
"test-environment": {
10111056
"$ref": "#/properties/test-environment"
1057+
},
1058+
"test-runtime": {
1059+
"$ref": "#/properties/test-runtime"
10121060
}
10131061
}
10141062
},
@@ -1069,6 +1117,9 @@
10691117
},
10701118
"test-environment": {
10711119
"$ref": "#/properties/test-environment"
1120+
},
1121+
"test-runtime": {
1122+
"$ref": "#/properties/test-runtime"
10721123
}
10731124
}
10741125
},
@@ -1129,6 +1180,9 @@
11291180
},
11301181
"test-environment": {
11311182
"$ref": "#/properties/test-environment"
1183+
},
1184+
"test-runtime": {
1185+
"$ref": "#/properties/test-runtime"
11321186
}
11331187
}
11341188
},
@@ -1189,6 +1243,9 @@
11891243
},
11901244
"test-environment": {
11911245
"$ref": "#/properties/test-environment"
1246+
},
1247+
"test-runtime": {
1248+
"$ref": "#/properties/test-runtime"
11921249
}
11931250
}
11941251
}

cibuildwheel/resources/defaults.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ test-requires = []
2525
test-extras = []
2626
test-groups = []
2727
test-environment = {}
28+
test-runtime = {}
2829

2930
container-engine = "docker"
3031

0 commit comments

Comments
 (0)