@@ -141,10 +141,12 @@ async def log_stream_task(initial_devices):
141141 else :
142142 suppress_dupes = False
143143 sys .stdout .write (line )
144+ sys .stdout .flush ()
144145
145146
146- async def xcode_test (location , simulator ):
147+ async def xcode_test (location , simulator , verbose ):
147148 # Run the test suite on the named simulator
149+ print ("Starting xcodebuild..." )
148150 args = [
149151 "xcodebuild" ,
150152 "test" ,
@@ -159,13 +161,17 @@ async def xcode_test(location, simulator):
159161 "-derivedDataPath" ,
160162 str (location / "DerivedData" ),
161163 ]
164+ if not verbose :
165+ args += ["-quiet" ]
166+
162167 async with async_process (
163168 * args ,
164169 stdout = subprocess .PIPE ,
165170 stderr = subprocess .STDOUT ,
166171 ) as process :
167172 while line := (await process .stdout .readline ()).decode (* DECODE_ARGS ):
168173 sys .stdout .write (line )
174+ sys .stdout .flush ()
169175
170176 status = await asyncio .wait_for (process .wait (), timeout = 1 )
171177 exit (status )
@@ -182,7 +188,9 @@ def clone_testbed(
182188 sys .exit (10 )
183189
184190 if framework is None :
185- if not (source / "Python.xcframework/ios-arm64_x86_64-simulator/bin" ).is_dir ():
191+ if not (
192+ source / "Python.xcframework/ios-arm64_x86_64-simulator/bin"
193+ ).is_dir ():
186194 print (
187195 f"The testbed being cloned ({ source } ) does not contain "
188196 f"a simulator framework. Re-run with --framework"
@@ -202,33 +210,48 @@ def clone_testbed(
202210 )
203211 sys .exit (13 )
204212
205- print ("Cloning testbed project..." )
206- shutil .copytree (source , target )
213+ print ("Cloning testbed project:" )
214+ print (f" Cloning { source } ..." , end = "" , flush = True )
215+ shutil .copytree (source , target , symlinks = True )
216+ print (" done" )
207217
208218 if framework is not None :
209219 if framework .suffix == ".xcframework" :
210- print ("Installing XCFramework..." )
211- xc_framework_path = target / "Python.xcframework"
212- shutil .rmtree (xc_framework_path )
213- shutil .copytree (framework , xc_framework_path )
220+ print (" Installing XCFramework..." , end = "" , flush = True )
221+ xc_framework_path = (target / "Python.xcframework" ).resolve ()
222+ if xc_framework_path .is_dir ():
223+ shutil .rmtree (xc_framework_path )
224+ else :
225+ xc_framework_path .unlink ()
226+ xc_framework_path .symlink_to (
227+ framework .relative_to (xc_framework_path .parent , walk_up = True )
228+ )
229+ print (" done" )
214230 else :
215- print ("Installing simulator Framework ..." )
231+ print (" Installing simulator framework ..." , end = "" , flush = True )
216232 sim_framework_path = (
217233 target / "Python.xcframework" / "ios-arm64_x86_64-simulator"
234+ ).resolve ()
235+ if sim_framework_path .is_dir ():
236+ shutil .rmtree (sim_framework_path )
237+ else :
238+ sim_framework_path .unlink ()
239+ sim_framework_path .symlink_to (
240+ framework .relative_to (sim_framework_path .parent , walk_up = True )
218241 )
219- shutil .rmtree (sim_framework_path )
220- shutil .copytree (framework , sim_framework_path )
242+ print (" done" )
221243 else :
222- print ("Using pre-existing iOS framework." )
244+ print (" Using pre-existing iOS framework." )
223245
224246 for app_src in apps :
225- print (f"Installing app { app_src .name !r} ..." )
247+ print (f" Installing app { app_src .name !r} ..." , end = "" , flush = True )
226248 app_target = target / f"iOSTestbed/app/{ app_src .name } "
227249 if app_target .is_dir ():
228250 shutil .rmtree (app_target )
229251 shutil .copytree (app_src , app_target )
252+ print (" done" )
230253
231- print (f"Testbed project created in { target } " )
254+ print (f"Successfully cloned testbed: { target . resolve () } " )
232255
233256
234257def update_plist (testbed_path , args ):
@@ -243,10 +266,11 @@ def update_plist(testbed_path, args):
243266 plistlib .dump (info , f )
244267
245268
246- async def run_testbed (simulator : str , args : list [str ]):
269+ async def run_testbed (simulator : str , args : list [str ], verbose : bool = False ):
247270 location = Path (__file__ ).parent
248- print ("Updating plist..." )
271+ print ("Updating plist..." , end = "" , flush = True )
249272 update_plist (location , args )
273+ print (" done." )
250274
251275 # Get the list of devices that are booted at the start of the test run.
252276 # The simulator started by the test suite will be detected as the new
@@ -256,7 +280,7 @@ async def run_testbed(simulator: str, args: list[str]):
256280 try :
257281 async with asyncio .TaskGroup () as tg :
258282 tg .create_task (log_stream_task (initial_devices ))
259- tg .create_task (xcode_test (location , simulator ))
283+ tg .create_task (xcode_test (location , simulator = simulator , verbose = verbose ))
260284 except* MySystemExit as e :
261285 raise SystemExit (* e .exceptions [0 ].args ) from None
262286 except* subprocess .CalledProcessError as e :
@@ -315,6 +339,11 @@ def main():
315339 default = "iPhone SE (3rd Generation)" ,
316340 help = "The name of the simulator to use (default: 'iPhone SE (3rd Generation)')" ,
317341 )
342+ run .add_argument (
343+ "-v" , "--verbose" ,
344+ action = "store_true" ,
345+ help = "Enable verbose output" ,
346+ )
318347
319348 try :
320349 pos = sys .argv .index ("--" )
@@ -330,7 +359,7 @@ def main():
330359 clone_testbed (
331360 source = Path (__file__ ).parent ,
332361 target = Path (context .location ),
333- framework = Path (context .framework ) if context .framework else None ,
362+ framework = Path (context .framework ). resolve () if context .framework else None ,
334363 apps = [Path (app ) for app in context .apps ],
335364 )
336365 elif context .subcommand == "run" :
@@ -348,6 +377,7 @@ def main():
348377 asyncio .run (
349378 run_testbed (
350379 simulator = context .simulator ,
380+ verbose = context .verbose ,
351381 args = test_args ,
352382 )
353383 )
0 commit comments