@@ -123,6 +123,36 @@ async def async_check_output(*args, **kwargs):
123
123
)
124
124
125
125
126
+ # Select a simulator device to use.
127
+ async def select_simulator_device ():
128
+ # List the testing simulators, in JSON format
129
+ raw_json = await async_check_output (
130
+ "xcrun" , "simctl" , "--set" , "testing" , "list" , "-j"
131
+ )
132
+ json_data = json .loads (raw_json )
133
+
134
+ # Any device will do; we'll look for "SE" devices - but the name isn't
135
+ # consistent over time. Older Xcode versions will use "iPhone SE (Nth
136
+ # generation)"; As of 2025, they've started using "iPhone 16e".
137
+ #
138
+ # When Xcode is updated after a new release, new devices will be available
139
+ # and old ones will be dropped from the set available on the latest iOS
140
+ # version. Select the one with the highest minimum runtime version - this
141
+ # is an indicator of the "newest" released device, which should always be
142
+ # supported on the "most recent" iOS version.
143
+ se_simulators = sorted (
144
+ (devicetype ["minRuntimeVersion" ], devicetype ["name" ])
145
+ for devicetype in json_data ["devicetypes" ]
146
+ if devicetype ["productFamily" ] == "iPhone"
147
+ and (
148
+ ("iPhone " in devicetype ["name" ] and devicetype ["name" ].endswith ("e" ))
149
+ or "iPhone SE " in devicetype ["name" ]
150
+ )
151
+ )
152
+
153
+ return se_simulators [- 1 ][1 ]
154
+
155
+
126
156
# Return a list of UDIDs associated with booted simulators
127
157
async def list_devices ():
128
158
try :
@@ -371,12 +401,16 @@ def update_plist(testbed_path, args):
371
401
plistlib .dump (info , f )
372
402
373
403
374
- async def run_testbed (simulator : str , args : list [str ], verbose : bool = False ):
404
+ async def run_testbed (simulator : str | None , args : list [str ], verbose : bool = False ):
375
405
location = Path (__file__ ).parent
376
406
print ("Updating plist..." , end = "" , flush = True )
377
407
update_plist (location , args )
378
408
print (" done." , flush = True )
379
409
410
+ if simulator is None :
411
+ simulator = await select_simulator_device ()
412
+ print (f"Running test on { simulator } " , flush = True )
413
+
380
414
# We need to get an exclusive lock on simulator creation, to avoid issues
381
415
# with multiple simulators starting and being unable to tell which
382
416
# simulator is due to which testbed instance. See
@@ -453,8 +487,10 @@ def main():
453
487
)
454
488
run .add_argument (
455
489
"--simulator" ,
456
- default = "iPhone SE (3rd Generation)" ,
457
- help = "The name of the simulator to use (default: 'iPhone SE (3rd Generation)')" ,
490
+ help = (
491
+ "The name of the simulator to use (eg: 'iPhone 16e'). Defaults to " ,
492
+ "the most recently released 'entry level' iPhone device."
493
+ )
458
494
)
459
495
run .add_argument (
460
496
"-v" , "--verbose" ,
0 commit comments