Bug 1318091 - Add more options to remotegtests.py; r=bc
authorGeoff Brown <gbrown@mozilla.com>
Tue, 16 Apr 2019 17:47:58 +0000
changeset 469740 3e3f0733baa2
parent 469739 1bb8ad865648
child 469741 5bcd5457bb38
push id35880
push usercbrindusan@mozilla.com
push dateWed, 17 Apr 2019 09:36:19 +0000
treeherdermozilla-central@79e6ed0b08d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbc
bugs1318091
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1318091 - Add more options to remotegtests.py; r=bc Support --shuffle and <gtest_filter> for android gtest. Differential Revision: https://phabricator.services.mozilla.com/D27731
testing/gtest/remotegtests.py
--- a/testing/gtest/remotegtests.py
+++ b/testing/gtest/remotegtests.py
@@ -25,33 +25,38 @@ LOGGER_NAME = 'gtest'
 log = mozlog.unstructured.getLogger(LOGGER_NAME)
 
 
 class RemoteGTests(object):
     """
        A test harness to run gtest on Android.
     """
 
-    def build_environment(self):
+    def build_environment(self, options, test_filter):
         """
            Create and return a dictionary of all the appropriate env variables
            and values.
         """
         env = {}
         env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
         env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
         env["MOZ_CRASHREPORTER"] = "1"
         env["MOZ_RUN_GTEST"] = "1"
         env["MOZ_TBPL_PARSER"] = "1"
         env["MOZ_GTEST_LOG_PATH"] = self.remote_log
         env["MOZ_GTEST_MINIDUMPS_PATH"] = self.remote_minidumps
         env["MOZ_IN_AUTOMATION"] = "1"
+        if options.shuffle:
+            env["GTEST_SHUFFLE"] = "True"
+        if test_filter:
+            env["GTEST_FILTER"] = test_filter
+
         return env
 
-    def run_gtest(self, options):
+    def run_gtest(self, options, test_filter):
         """
            Launch the test app, run gtest, collect test results and wait for completion.
            Return False if a crash or other failure is detected, else True.
         """
         self.device = mozdevice.ADBDevice(adb=options.adb_path,
                                           device=options.device_serial,
                                           test_root=options.test_root,
                                           logger_name=LOGGER_NAME,
@@ -70,17 +75,17 @@ class RemoteGTests(object):
             raise Exception("%s is not installed on this device" % self.package)
         if not self.device._have_root_shell:
             raise Exception("a device with a root shell is required to run Android gtest")
 
         # TODO -- consider packaging the gtest libxul.so in an apk
         remote = "/data/app/%s-1/lib/x86_64/" % self.package
         self.device.push(options.libxul_path, remote)
 
-        env = self.build_environment()
+        env = self.build_environment(options, test_filter)
         args = ["-unittest", "--gtest_death_test_style=threadsafe",
                 "-profile %s" % self.remote_profile]
         if 'geckoview' in self.package:
             activity = "TestRunnerActivity"
             self.device.launch_activity(self.package, activity_name=activity,
                                         e10s=False,  # gtest is non-e10s on desktop
                                         moz_env=env, extra_args=args)
         else:
@@ -212,16 +217,21 @@ class AppWaiter(object):
     def get_top(self):
         top = self.device.get_top_activity(timeout=60)
         if top is None:
             log.info("Failed to get top activity, retrying, once...")
             top = self.device.get_top_activity(timeout=60)
         return top
 
     def wait_for_start(self, package):
+        if self.update_log():
+            # if log content is available, assume the app started; otherwise,
+            # a short run (few tests) might complete without ever being detected
+            # in the foreground
+            return package
         top = None
         while top != package and not self.start_timed_out():
             time.sleep(1)
             top = self.get_top()
         return top
 
     def wait(self, package):
         """
@@ -277,17 +287,17 @@ class AppWaiter(object):
         for line in new_content.lstrip('\n').split('\n'):
             print(line)
         self.last_output_time = datetime.datetime.now()
         return True
 
 
 class remoteGtestOptions(OptionParser):
     def __init__(self):
-        OptionParser.__init__(self)
+        OptionParser.__init__(self, usage="usage: %prog [options] test_filter")
         self.add_option("--package",
                         dest="package",
                         default="org.mozilla.geckoview.test",
                         help="Package name of test app.")
         self.add_option("--adbpath",
                         action="store",
                         type=str,
                         dest="adb_path",
@@ -312,16 +322,20 @@ class remoteGtestOptions(OptionParser):
                         dest="libxul_path",
                         default=None,
                         help="Path to gtest libxul.so.")
         self.add_option("--symbols-path",
                         dest="symbols_path",
                         default=None,
                         help="absolute path to directory containing breakpad "
                              "symbols, or the URL of a zip file containing symbols")
+        self.add_option("--shuffle",
+                        action="store_true",
+                        default=False,
+                        help="Randomize the execution order of tests.")
 
 
 def update_mozinfo():
     """
        Walk up directories to find mozinfo.json and update the info.
     """
     path = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
     dirs = set()
@@ -332,24 +346,28 @@ def update_mozinfo():
         path = os.path.split(path)[0]
     mozinfo.find_and_update_from_json(*dirs)
 
 
 def main():
     parser = remoteGtestOptions()
     options, args = parser.parse_args()
     if not options.libxul_path:
-        log.error("--libxul is required")
+        parser.error("--libxul is required")
         sys.exit(1)
+    if len(args) > 1:
+        parser.error("only one test_filter is allowed")
+        sys.exit(1)
+    test_filter = args[0] if args else None
     update_mozinfo()
     tester = RemoteGTests()
     result = False
     try:
         device_exception = False
-        result = tester.run_gtest(options)
+        result = tester.run_gtest(options, test_filter)
     except KeyboardInterrupt:
         log.info("gtest | Received keyboard interrupt")
     except Exception as e:
         log.error(str(e))
         if isinstance(e, mozdevice.ADBTimeoutError):
             device_exception = True
     finally:
         if not device_exception: