@@ -123,6 +123,36 @@ async def async_check_output(*args, **kwargs):
123123 )
124124
125125
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+
126156# Return a list of UDIDs associated with booted simulators
127157async def list_devices ():
128158 try :
@@ -371,12 +401,16 @@ def update_plist(testbed_path, args):
371401 plistlib .dump (info , f )
372402
373403
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 ):
375405 location = Path (__file__ ).parent
376406 print ("Updating plist..." , end = "" , flush = True )
377407 update_plist (location , args )
378408 print (" done." , flush = True )
379409
410+ if simulator is None :
411+ simulator = await select_simulator_device ()
412+ print (f"Running test on { simulator } " , flush = True )
413+
380414 # We need to get an exclusive lock on simulator creation, to avoid issues
381415 # with multiple simulators starting and being unable to tell which
382416 # simulator is due to which testbed instance. See
@@ -453,8 +487,10 @@ def main():
453487 )
454488 run .add_argument (
455489 "--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+ )
458494 )
459495 run .add_argument (
460496 "-v" , "--verbose" ,
0 commit comments