Skip to content

Commit c5b47ae

Browse files
[3.12] pythongh-133131: Discover an appropriate iOS simulator rather than hard-coding iPhone SE 3rd gen (pythonGH-133132) (python#133173)
Determines a candidate simulator at runtime rather than hardcoding iPhone SE. (cherry picked from commit 42b0b06) Co-authored-by: Russell Keith-Magee <russell@keith-magee.com>
1 parent d109d70 commit c5b47ae

2 files changed

Lines changed: 52 additions & 4 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The iOS testbed will now select the most recently released "SE-class" device
2+
for testing if a device isn't explicitly specified.

iOS/testbed/__main__.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,36 @@ async def async_check_output(*args, **kwargs):
8080
)
8181

8282

83+
# Select a simulator device to use.
84+
async def select_simulator_device():
85+
# List the testing simulators, in JSON format
86+
raw_json = await async_check_output(
87+
"xcrun", "simctl", "--set", "testing", "list", "-j"
88+
)
89+
json_data = json.loads(raw_json)
90+
91+
# Any device will do; we'll look for "SE" devices - but the name isn't
92+
# consistent over time. Older Xcode versions will use "iPhone SE (Nth
93+
# generation)"; As of 2025, they've started using "iPhone 16e".
94+
#
95+
# When Xcode is updated after a new release, new devices will be available
96+
# and old ones will be dropped from the set available on the latest iOS
97+
# version. Select the one with the highest minimum runtime version - this
98+
# is an indicator of the "newest" released device, which should always be
99+
# supported on the "most recent" iOS version.
100+
se_simulators = sorted(
101+
(devicetype["minRuntimeVersion"], devicetype["name"])
102+
for devicetype in json_data["devicetypes"]
103+
if devicetype["productFamily"] == "iPhone"
104+
and (
105+
("iPhone " in devicetype["name"] and devicetype["name"].endswith("e"))
106+
or "iPhone SE " in devicetype["name"]
107+
)
108+
)
109+
110+
return se_simulators[-1][1]
111+
112+
83113
# Return a list of UDIDs associated with booted simulators
84114
async def list_devices():
85115
try:
@@ -327,11 +357,25 @@ def update_plist(testbed_path, args):
327357
plistlib.dump(info, f)
328358

329359

330-
async def run_testbed(simulator: str, args: list[str], verbose: bool=False):
360+
async def run_testbed(simulator: str | None, args: list[str], verbose: bool=False):
331361
location = Path(__file__).parent
332362
print("Updating plist...", end="", flush=True)
333363
update_plist(location, args)
334-
print(" done.")
364+
print(" done.", flush=True)
365+
366+
if simulator is None:
367+
simulator = await select_simulator_device()
368+
print(f"Running test on {simulator}", flush=True)
369+
370+
# We need to get an exclusive lock on simulator creation, to avoid issues
371+
# with multiple simulators starting and being unable to tell which
372+
# simulator is due to which testbed instance. See
373+
# https://github.com/python/cpython/issues/130294 for details. Wait up to
374+
# 10 minutes for a simulator to boot.
375+
print("Obtaining lock on simulator creation...", flush=True)
376+
simulator_lock = SimulatorLock(timeout=10*60)
377+
await simulator_lock.acquire()
378+
print("Simulator lock acquired.", flush=True)
335379

336380
# Get the list of devices that are booted at the start of the test run.
337381
# The simulator started by the test suite will be detected as the new
@@ -397,8 +441,10 @@ def main():
397441
)
398442
run.add_argument(
399443
"--simulator",
400-
default="iPhone SE (3rd Generation)",
401-
help="The name of the simulator to use (default: 'iPhone SE (3rd Generation)')",
444+
help=(
445+
"The name of the simulator to use (eg: 'iPhone 16e'). Defaults to ",
446+
"the most recently released 'entry level' iPhone device."
447+
)
402448
)
403449
run.add_argument(
404450
"-v", "--verbose",

0 commit comments

Comments
 (0)