79
79
import json
80
80
import os
81
81
import pathlib
82
+ import platform
82
83
import shutil
83
84
import signal
84
85
import subprocess
86
+ import tempfile
85
87
import time
86
88
87
89
from absl import app
113
115
}
114
116
115
117
_RESULT_FILE = "Results1.json"
116
- _TEST_RETRY = 3
118
+ _MAX_ATTEMPTS = 3
117
119
_CMD_TIMEOUT = 300
118
120
119
121
_DEVICE_NONE = "None"
@@ -251,7 +253,7 @@ def main(argv):
251
253
252
254
for app_path in ios_testapps :
253
255
bundle_id = _get_bundle_id (app_path , config )
254
- logs = _run_apple_test (testapp_dir , bundle_id , app_path , ios_helper_app , device_id , _TEST_RETRY )
256
+ logs = _run_apple_test (testapp_dir , bundle_id , app_path , ios_helper_app , device_id )
255
257
tests .append (Test (testapp_path = app_path , logs = logs ))
256
258
257
259
_shutdown_simulator ()
@@ -291,7 +293,7 @@ def main(argv):
291
293
292
294
for app_path in tvos_testapps :
293
295
bundle_id = _get_bundle_id (app_path , config )
294
- logs = _run_apple_test (testapp_dir , bundle_id , app_path , tvos_helper_app , device_id , _TEST_RETRY )
296
+ logs = _run_apple_test (testapp_dir , bundle_id , app_path , tvos_helper_app , device_id , record_video_display = "external" )
295
297
tests .append (Test (testapp_path = app_path , logs = logs ))
296
298
297
299
_shutdown_simulator ()
@@ -326,7 +328,7 @@ def main(argv):
326
328
327
329
for app_path in android_testapps :
328
330
package_name = _get_package_name (app_path )
329
- logs = _run_android_test (testapp_dir , package_name , app_path , android_helper_project , _TEST_RETRY )
331
+ logs = _run_android_test (testapp_dir , package_name , app_path , android_helper_project )
330
332
tests .append (Test (testapp_path = app_path , logs = logs ))
331
333
332
334
_shutdown_emulator ()
@@ -387,23 +389,77 @@ def _build_tvos_helper(helper_project, device_name, device_os):
387
389
return os .path .join (file_dir , file_name )
388
390
389
391
390
- def _record_apple_tests (video_name ):
391
- command = "xcrun simctl io booted recordVideo -f --codec=h264 %s" % video_name
392
- logging .info ("Recording test: %s" , command )
393
- return subprocess .Popen (command , stdout = subprocess .PIPE , stderr = subprocess .PIPE , shell = True , preexec_fn = os .setsid )
394
-
395
-
396
- def _stop_recording (record_process ):
397
- logging .info ("Stop recording test" )
398
- try :
399
- os .killpg (record_process .pid , signal .SIGINT )
400
- except :
401
- logging .info ("Stop recording test failed!!!" )
392
+ def _record_apple_tests (video_name , display ):
393
+ args = [
394
+ "xcrun" ,
395
+ "simctl" ,
396
+ "io" ,
397
+ "booted" ,
398
+ "recordVideo" ,
399
+ "-f" ,
400
+ "--codec=h264" ,
401
+ ]
402
+ if display is not None :
403
+ args .append ("--display=" + display )
404
+ args .append (video_name )
405
+ return _start_recording (args )
406
+
407
+
408
+ def _start_recording (args ):
409
+ logging .info ("Starting screen recording: %s" , subprocess .list2cmdline (args ))
410
+
411
+ output_file = tempfile .TemporaryFile ()
412
+
413
+ # Specify CREATE_NEW_PROCESS_GROUP on Windows because it is required for
414
+ # sending CTRL_C_SIGNAL, which is done by _stop_recording().
415
+ proc = subprocess .Popen (
416
+ args ,
417
+ stdout = output_file ,
418
+ stderr = subprocess .STDOUT ,
419
+ creationflags =
420
+ subprocess .CREATE_NEW_PROCESS_GROUP
421
+ if platform .system () == "Windows"
422
+ else 0 ,
423
+ )
424
+ logging .info ("Started screen recording with PID %s" , proc .pid )
425
+ return (proc , output_file )
426
+
427
+
428
+ def _stop_recording (start_recording_retval ):
429
+ (proc , output_file ) = start_recording_retval
430
+ logging .info ("Stopping screen recording with PID %s by sending CTRL+C" ,
431
+ proc .pid )
432
+
433
+ # Make sure that `returncode` is set on the `Popen` object.
434
+ proc .poll ()
435
+
436
+ if proc .returncode is not None :
437
+ logging .warning (
438
+ "WARNING: Screen recording process ended prematurely with exit code %s" ,
439
+ proc .returncode
440
+ )
441
+ output_file .seek (0 )
442
+ logging .warning ("==== BEGIN screen recording output ====" )
443
+ for line in output_file .read ().decode ("utf8" , errors = "replace" ).splitlines ():
444
+ logging .warning ("%s" , line .rstrip ())
445
+ logging .warning ("==== END screen recording output ====" )
446
+
447
+ output_file .close ()
448
+
449
+ proc .send_signal (
450
+ signal .CTRL_C_SIGNAL
451
+ if platform .system () == "Windows"
452
+ else signal .SIGINT
453
+ )
454
+ proc .wait ()
402
455
time .sleep (5 )
403
456
404
457
405
458
def _save_recorded_apple_video (video_name , summary_dir ):
406
- logging .info ("Save test video to: %s" , summary_dir )
459
+ logging .info ("Save test video %s to: %s" , video_name , summary_dir )
460
+ if not os .path .exists (video_name ):
461
+ logging .warning ("Save test video failed: file not found: %s" , video_name )
462
+ return
407
463
shutil .move (video_name , os .path .join (summary_dir , video_name ))
408
464
409
465
@@ -502,23 +558,24 @@ def _has_uitests(app_path, config):
502
558
return api .get ("has_uitests" , False )
503
559
504
560
505
- def _run_apple_test (testapp_dir , bundle_id , app_path , helper_app , device_id , retry = 1 ):
561
+ def _run_apple_test (testapp_dir , bundle_id , app_path , helper_app , device_id , max_attempts = _MAX_ATTEMPTS , record_video_display = None ):
506
562
"""Run helper test and collect test result."""
507
- logging .info ("Running apple helper test: %s, %s, %s, %s" , bundle_id , app_path , helper_app , device_id )
508
- _install_apple_app (app_path , device_id )
509
- video_name = "video-%s-%s-%s.mp4" % (bundle_id , retry , FLAGS .logfile_name )
510
- record_process = _record_apple_tests (video_name )
511
- _run_xctest (helper_app , device_id )
512
- _stop_recording (record_process )
513
- log = _get_apple_test_log (bundle_id , app_path , device_id )
514
- _uninstall_apple_app (bundle_id , device_id )
515
- result = test_validation .validate_results (log , test_validation .CPP )
516
- if not result .complete or (FLAGS .test_type == "uitest" and result .fails > 0 ):
563
+ attempt_num = 1
564
+ while attempt_num <= max_attempts :
565
+ logging .info ("Running apple helper test (attempt %s of %s): %s, %s, %s, %s" , attempt_num , max_attempts , bundle_id , app_path , helper_app , device_id )
566
+ _install_apple_app (app_path , device_id )
567
+ video_name = "video-%s-%s-%s.mp4" % (bundle_id , attempt_num , FLAGS .logfile_name )
568
+ record_process = _record_apple_tests (video_name , display = record_video_display )
569
+ _run_xctest (helper_app , device_id )
570
+ _stop_recording (record_process )
571
+ log = _get_apple_test_log (bundle_id , app_path , device_id )
572
+ _uninstall_apple_app (bundle_id , device_id )
573
+ result = test_validation .validate_results (log , test_validation .CPP )
574
+ if result .complete and (FLAGS .test_type != "uitest" or result .fails == 0 ):
575
+ break
517
576
_save_recorded_apple_video (video_name , testapp_dir )
518
- if retry > 1 :
519
- logging .info ("Retry _run_apple_test. Remaining retry: %s" , retry - 1 )
520
- return _run_apple_test (testapp_dir , bundle_id , app_path , helper_app , device_id , retry = retry - 1 )
521
-
577
+ attempt_num += 1
578
+
522
579
return log
523
580
524
581
@@ -629,7 +686,7 @@ def _create_and_boot_emulator(sdk_id):
629
686
if not FLAGS .ci :
630
687
command = "$ANDROID_HOME/emulator/emulator -avd test_emulator &"
631
688
else :
632
- command = "$ANDROID_HOME/emulator/emulator -avd test_emulator -no-audio -no-boot-anim -gpu auto &"
689
+ command = "$ANDROID_HOME/emulator/emulator -avd test_emulator -no-window -no- audio -no-boot-anim -gpu auto &"
633
690
logging .info ("Boot test emulator: %s" , command )
634
691
subprocess .Popen (command , universal_newlines = True , shell = True , stdout = subprocess .PIPE )
635
692
@@ -673,25 +730,27 @@ def _get_package_name(app_path):
673
730
return package_name
674
731
675
732
676
- def _run_android_test (testapp_dir , package_name , app_path , helper_project , retry = 1 ):
677
- logging .info ("Running android helper test: %s, %s, %s" , package_name , app_path , helper_project )
678
- _install_android_app (app_path )
679
- video_name = "video-%s-%s-%s.mp4" % (package_name , retry , FLAGS .logfile_name )
680
- logcat_name = "logcat-%s-%s-%s.txt" % (package_name , retry , FLAGS .logfile_name )
681
- record_process = _record_android_tests (video_name )
682
- _clear_android_logcat ()
683
- _run_instrumented_test ()
684
- _stop_recording (record_process )
685
- log = _get_android_test_log (package_name )
686
- _uninstall_android_app (package_name )
687
-
688
- result = test_validation .validate_results (log , test_validation .CPP )
689
- if not result .complete or (FLAGS .test_type == "uitest" and result .fails > 0 ):
733
+ def _run_android_test (testapp_dir , package_name , app_path , helper_project , max_attempts = _MAX_ATTEMPTS ):
734
+ attempt_num = 1
735
+ while attempt_num <= max_attempts :
736
+ logging .info ("Running android helper test (attempt %s of %s): %s, %s, %s" , attempt_num , max_attempts , package_name , app_path , helper_project )
737
+ _install_android_app (app_path )
738
+ video_name = "video-%s-%s-%s.mp4" % (package_name , attempt_num , FLAGS .logfile_name )
739
+ logcat_name = "logcat-%s-%s-%s.txt" % (package_name , attempt_num , FLAGS .logfile_name )
740
+ record_process = _record_android_tests (video_name )
741
+ _clear_android_logcat ()
742
+ _run_instrumented_test ()
743
+ _stop_recording (record_process )
744
+ log = _get_android_test_log (package_name )
745
+ _uninstall_android_app (package_name )
746
+
747
+ result = test_validation .validate_results (log , test_validation .CPP )
748
+ if result .complete and (FLAGS .test_type != "uitest" or result .fails == 0 ):
749
+ break
750
+
690
751
_save_recorded_android_video (video_name , testapp_dir )
691
752
_save_android_logcat (logcat_name , testapp_dir )
692
- if retry > 1 :
693
- logging .info ("Retry _run_android_test. Remaining retry: %s" , retry - 1 )
694
- return _run_android_test (testapp_dir , package_name , app_path , helper_project , retry = retry - 1 )
753
+ attempt_num += 1
695
754
696
755
return log
697
756
@@ -726,9 +785,13 @@ def _install_android_helper_app(helper_project):
726
785
727
786
728
787
def _record_android_tests (video_name ):
729
- command = "adb shell screenrecord /sdcard/%s" % video_name
730
- logging .info ("Recording test: %s" , command )
731
- return subprocess .Popen (command , stdout = subprocess .PIPE , stderr = subprocess .PIPE , shell = True , preexec_fn = os .setsid )
788
+ return _start_recording ([
789
+ "adb" ,
790
+ "shell" ,
791
+ "screenrecord" ,
792
+ "--bugreport" ,
793
+ "/sdcard/%s" % video_name ,
794
+ ])
732
795
733
796
734
797
def _save_recorded_android_video (video_name , summary_dir ):
@@ -774,12 +837,20 @@ def _get_android_test_log(test_package):
774
837
return result .stdout
775
838
776
839
777
- def _run_with_retry (args , shell = False , check = True , timeout = _CMD_TIMEOUT , retry_time = _TEST_RETRY , device = _DEVICE_NONE , type = _RESET_TYPE_REBOOT ):
778
- logging .info ("run_with_retry: %s; remaining retry: %s" , args , retry_time )
779
- if retry_time > 1 :
840
+ def _run_with_retry (args , shell = False , check = True , timeout = _CMD_TIMEOUT , max_attempts = _MAX_ATTEMPTS , device = _DEVICE_NONE , type = _RESET_TYPE_REBOOT ):
841
+ attempt_num = 1
842
+ while attempt_num <= max_attempts :
843
+ logging .info ("run_with_retry: %s (attempt %s of %s)" , args , attempt_num , max_attempts )
780
844
try :
781
845
subprocess .run (args , shell = shell , check = check , timeout = timeout )
782
- except :
846
+ except subprocess .SubprocessError :
847
+ logging .exception ("run_with_retry: %s (attempt %s of %s) FAILED" , args , attempt_num , max_attempts )
848
+
849
+ # If retries have been exhausted, just raise the exception
850
+ if attempt_num >= max_attempts :
851
+ raise
852
+
853
+ # Otherwise, reset the emulator/simulator and try again.
783
854
if device == _DEVICE_NONE :
784
855
pass
785
856
elif device == _DEVICE_ANDROID :
@@ -788,9 +859,10 @@ def _run_with_retry(args, shell=False, check=True, timeout=_CMD_TIMEOUT, retry_t
788
859
else :
789
860
# Apple
790
861
_reset_simulator_on_error (device , type )
791
- _run_with_retry (args , shell , check , timeout , retry_time - 1 , device , type )
792
- else :
793
- subprocess .run (args , shell = shell , check = False , timeout = timeout )
862
+ else :
863
+ break
864
+
865
+ attempt_num += 1
794
866
795
867
796
868
if __name__ == '__main__' :
0 commit comments