@@ -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 }  
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