Skip to content

Commit ef56802

Browse files
[3.11] 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 6007e43 commit ef56802

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
@@ -81,6 +81,36 @@ async def async_check_output(*args, **kwargs):
8181
)
8282

8383

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

341371

342-
async def run_testbed(simulator: str, args: list[str], verbose: bool=False):
372+
async def run_testbed(simulator: str | None, args: list[str], verbose: bool=False):
343373
location = Path(__file__).parent
344374
print("Updating plist...", end="", flush=True)
345375
update_plist(location, args)
346-
print(" done.")
376+
print(" done.", flush=True)
377+
378+
if simulator is None:
379+
simulator = await select_simulator_device()
380+
print(f"Running test on {simulator}", flush=True)
381+
382+
# We need to get an exclusive lock on simulator creation, to avoid issues
383+
# with multiple simulators starting and being unable to tell which
384+
# simulator is due to which testbed instance. See
385+
# https://github.com/python/cpython/issues/130294 for details. Wait up to
386+
# 10 minutes for a simulator to boot.
387+
print("Obtaining lock on simulator creation...", flush=True)
388+
simulator_lock = SimulatorLock(timeout=10*60)
389+
await simulator_lock.acquire()
390+
print("Simulator lock acquired.", flush=True)
347391

348392
# Get the list of devices that are booted at the start of the test run.
349393
# The simulator started by the test suite will be detected as the new
@@ -409,8 +453,10 @@ def main():
409453
)
410454
run.add_argument(
411455
"--simulator",
412-
default="iPhone SE (3rd Generation)",
413-
help="The name of the simulator to use (default: 'iPhone SE (3rd Generation)')",
456+
help=(
457+
"The name of the simulator to use (eg: 'iPhone 16e'). Defaults to ",
458+
"the most recently released 'entry level' iPhone device."
459+
)
414460
)
415461
run.add_argument(
416462
"-v", "--verbose",

0 commit comments

Comments
 (0)