Bug 1090276 - Support mach cppunittest on Android; r=dminor
authorGeoff Brown <gbrown@mozilla.com>
Mon, 14 Sep 2015 08:40:35 -0600
changeset 262330 b89b4f875debe615c069a0109311570ae7774aa6
parent 262329 ce840d7632babd00f8a2d8081ac27a06e9c99a51
child 262331 edabe4800045ffe74d5d794f427945cb9345620d
push idunknown
push userunknown
push dateunknown
reviewersdminor
bugs1090276
milestone43.0a1
Bug 1090276 - Support mach cppunittest on Android; r=dminor
testing/mach_commands.py
testing/remotecppunittests.py
testing/runcppunittests.py
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -13,16 +13,17 @@ import shutil
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
 )
 
 from mozbuild.base import MachCommandBase
+from mozbuild.base import MachCommandConditions as conditions
 from argparse import ArgumentParser
 
 
 UNKNOWN_TEST = '''
 I was unable to find tests in the argument(s) given.
 
 You need to specify a test directory, filename, test suite name, or
 abbreviation.
@@ -288,40 +289,86 @@ class MachCommands(MachCommandBase):
     @CommandArgument('test_files', nargs='*', metavar='N',
         help='Test to run. Can be specified as one or more files or ' \
             'directories, or omitted. If omitted, the entire test suite is ' \
             'executed.')
 
     def run_cppunit_test(self, **params):
         import mozinfo
         from mozlog import commandline
-        import runcppunittests as cppunittests
-
         log = commandline.setup_logging("cppunittest",
                                         {},
                                         {"tbpl": sys.stdout})
 
-        if len(params['test_files']) == 0:
-            testdir = os.path.join(self.distdir, 'cppunittests')
-            manifest = os.path.join(self.topsrcdir, 'testing', 'cppunittest.ini')
-            tests = cppunittests.extract_unittests_from_args([testdir], mozinfo.info, manifest)
-        else:
-            tests = cppunittests.extract_unittests_from_args(params['test_files'], mozinfo.info, None)
-
         # See if we have crash symbols
         symbols_path = os.path.join(self.distdir, 'crashreporter-symbols')
         if not os.path.isdir(symbols_path):
             symbols_path = None
 
-        tester = cppunittests.CPPUnitTests()
+        # If no tests specified, run all tests in main manifest
+        tests = params['test_files']
+        if len(tests) == 0:
+            tests = [os.path.join(self.distdir, 'cppunittests')]
+            manifest_path = os.path.join(self.topsrcdir, 'testing', 'cppunittest.ini')
+        else:
+            manifest_path = None
+
+        if conditions.is_android(self):
+            from mozrunner.devices.android_device import verify_android_device
+            verify_android_device(self, install=False)
+            return self.run_android_test(tests, symbols_path, manifest_path, log)
+
+        return self.run_desktop_test(tests, symbols_path, manifest_path, log)
+
+    def run_desktop_test(self, tests, symbols_path, manifest_path, log):
+        import runcppunittests as cppunittests
+        from mozlog import commandline
+
+        parser = cppunittests.CPPUnittestOptions()
+        commandline.add_logging_group(parser)
+        options, args = parser.parse_args()
+
+        options.symbols_path = symbols_path
+        options.manifest_path = manifest_path
+        options.xre_path = self.bindir
+
         try:
-            result = tester.run_tests(tests, self.bindir, symbols_path, interactive=True)
+            result = cppunittests.run_test_harness(options, tests)
         except Exception as e:
             log.error("Caught exception running cpp unit tests: %s" % str(e))
             result = False
+            raise
+
+        return 0 if result else 1
+
+    def run_android_test(self, tests, symbols_path, manifest_path, log):
+        import remotecppunittests as remotecppunittests
+        from mozlog import commandline
+
+        parser = remotecppunittests.RemoteCPPUnittestOptions()
+        commandline.add_logging_group(parser)
+        options, args = parser.parse_args()
+
+        options.symbols_path = symbols_path
+        options.manifest_path = manifest_path
+        options.xre_path = self.bindir
+        options.dm_trans = "adb"
+        options.local_lib = self.bindir.replace('bin', 'fennec')
+        for file in os.listdir(os.path.join(self.topobjdir, "dist")):
+            if file.endswith(".apk") and file.startswith("fennec"):
+                options.local_apk = os.path.join(self.topobjdir, "dist", file)
+                log.info("using APK: " + options.local_apk)
+                break
+
+        try:
+            result = remotecppunittests.run_test_harness(options, tests)
+        except Exception as e:
+            log.error("Caught exception running cpp unit tests: %s" % str(e))
+            result = False
+            raise
 
         return 0 if result else 1
 
 def executable_name(name):
     return name + '.exe' if sys.platform.startswith('win') else name
 
 @CommandProvider
 class CheckSpiderMonkeyCommand(MachCommandBase):
--- a/testing/remotecppunittests.py
+++ b/testing/remotecppunittests.py
@@ -215,46 +215,32 @@ class RemoteCPPUnittestOptions(cppunitte
         self.add_option("--localBinDir", action="store",
                         type = "string", dest = "local_bin",
                         help = "local path to bin directory")
         defaults["local_bin"] = build_obj.bindir if build_obj is not None else None
 
         self.add_option("--remoteTestRoot", action = "store",
                     type = "string", dest = "remote_test_root",
                     help = "remote directory to use as test root (eg. /data/local/tests)")
+        # /data/local/tests is used because it is usually not possible to set +x permissions
+        # on binaries on /mnt/sdcard
+        defaults["remote_test_root"] = "/data/local/tests"
+
         self.add_option("--with-b2g-emulator", action = "store",
                     type = "string", dest = "with_b2g_emulator",
                     help = "Start B2G Emulator (specify path to b2g home)")
-        # /data/local/tests is used because it is usually not possible to set +x permissions
-        # on binaries on /mnt/sdcard
-        defaults["remote_test_root"] = "/data/local/tests"
 
         self.add_option("--addEnv", action = "append",
                     type = "string", dest = "add_env",
                     help = "additional remote environment variable definitions (eg. --addEnv \"somevar=something\")")
         defaults["add_env"] = None
 
         self.set_defaults(**defaults)
 
-def main():
-    parser = RemoteCPPUnittestOptions()
-    mozlog.commandline.add_logging_group(parser)
-    options, args = parser.parse_args()
-    if not args:
-        print >>sys.stderr, """Usage: %s <test binary> [<test binary>...]""" % sys.argv[0]
-        sys.exit(1)
-    if options.local_lib is not None and not os.path.isdir(options.local_lib):
-        print >>sys.stderr, """Error: --localLib directory %s not found""" % options.local_lib
-        sys.exit(1)
-    if options.local_apk is not None and not os.path.isfile(options.local_apk):
-        print >>sys.stderr, """Error: --apk file %s not found""" % options.local_apk
-        sys.exit(1)
-    if not options.xre_path:
-        print >>sys.stderr, """Error: --xre-path is required"""
-        sys.exit(1)
+def run_test_harness(options, args):
     if options.with_b2g_emulator:
         from mozrunner import B2GEmulatorRunner
         runner = B2GEmulatorRunner(b2g_home=options.with_b2g_emulator)
         runner.start()
     if options.dm_trans == "adb":
         if options.with_b2g_emulator:
             # because we just started the emulator, we need more than the
             # default number of retries here.
@@ -272,29 +258,50 @@ def main():
                 runner.wait()
             raise
     else:
         dm = devicemanagerSUT.DeviceManagerSUT(options.device_ip, options.device_port, deviceRoot=options.remote_test_root)
         if not options.device_ip:
             print "Error: you must provide a device IP to connect to via the --deviceIP option"
             sys.exit(1)
 
-    log = mozlog.commandline.setup_logging("remotecppunittests", options,
-                                           {"tbpl": sys.stdout})
-
     options.xre_path = os.path.abspath(options.xre_path)
     cppunittests.update_mozinfo()
     progs = cppunittests.extract_unittests_from_args(args,
                                                      mozinfo.info,
                                                      options.manifest_path)
     tester = RemoteCPPUnitTests(dm, options, [item[0] for item in progs])
     try:
         result = tester.run_tests(progs, options.xre_path, options.symbols_path)
+    finally:
+        if options.with_b2g_emulator:
+            runner.cleanup()
+            runner.wait()
+    return result
+
+def main():
+    parser = RemoteCPPUnittestOptions()
+    mozlog.commandline.add_logging_group(parser)
+    options, args = parser.parse_args()
+    if not args:
+        print >>sys.stderr, """Usage: %s <test binary> [<test binary>...]""" % sys.argv[0]
+        sys.exit(1)
+    if options.local_lib is not None and not os.path.isdir(options.local_lib):
+        print >>sys.stderr, """Error: --localLib directory %s not found""" % options.local_lib
+        sys.exit(1)
+    if options.local_apk is not None and not os.path.isfile(options.local_apk):
+        print >>sys.stderr, """Error: --apk file %s not found""" % options.local_apk
+        sys.exit(1)
+    if not options.xre_path:
+        print >>sys.stderr, """Error: --xre-path is required"""
+        sys.exit(1)
+
+    log = mozlog.commandline.setup_logging("remotecppunittests", options,
+                                           {"tbpl": sys.stdout})
+    try:
+        result = run_test_harness(options, args)
     except Exception, e:
         log.error(str(e))
         result = False
-    if options.with_b2g_emulator:
-        runner.cleanup()
-        runner.wait()
     sys.exit(0 if result else 1)
 
 if __name__ == '__main__':
     main()
--- a/testing/runcppunittests.py
+++ b/testing/runcppunittests.py
@@ -222,42 +222,44 @@ def update_mozinfo():
     dirs = set()
     while path != os.path.expanduser('~'):
         if path in dirs:
             break
         dirs.add(path)
         path = os.path.split(path)[0]
     mozinfo.find_and_update_from_json(*dirs)
 
+def run_test_harness(options, args):
+    update_mozinfo()
+    progs = extract_unittests_from_args(args, mozinfo.info, options.manifest_path)
+    options.xre_path = os.path.abspath(options.xre_path)
+    if mozinfo.isMac:
+        options.xre_path = os.path.join(os.path.dirname(options.xre_path), 'Resources')
+    tester = CPPUnitTests()
+    result = tester.run_tests(progs, options.xre_path, options.symbols_path)
+
+    return result
+
 def main():
     parser = CPPUnittestOptions()
     mozlog.commandline.add_logging_group(parser)
     options, args = parser.parse_args()
     if not args:
         print >>sys.stderr, """Usage: %s <test binary> [<test binary>...]""" % sys.argv[0]
         sys.exit(1)
     if not options.xre_path:
         print >>sys.stderr, """Error: --xre-path is required"""
         sys.exit(1)
     if options.manifest_path and len(args) > 1:
         print >>sys.stderr, "Error: multiple arguments not supported with --test-manifest"
         sys.exit(1)
-
     log = mozlog.commandline.setup_logging("cppunittests", options,
                                            {"tbpl": sys.stdout})
-
-    update_mozinfo()
-    progs = extract_unittests_from_args(args, mozinfo.info, options.manifest_path)
-    options.xre_path = os.path.abspath(options.xre_path)
-    if mozinfo.isMac:
-        options.xre_path = os.path.join(os.path.dirname(options.xre_path), 'Resources')
-    tester = CPPUnitTests()
-
     try:
-        result = tester.run_tests(progs, options.xre_path, options.symbols_path)
+        result = run_test_harness(options, args)
     except Exception as e:
         log.error(str(e))
         result = False
 
     sys.exit(0 if result else 1)
 
 if __name__ == '__main__':
     main()