summaryrefslogtreecommitdiffstats
path: root/testing/xpcshell/mach_commands.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/xpcshell/mach_commands.py')
-rw-r--r--testing/xpcshell/mach_commands.py274
1 files changed, 274 insertions, 0 deletions
diff --git a/testing/xpcshell/mach_commands.py b/testing/xpcshell/mach_commands.py
new file mode 100644
index 000000000..d821ff46f
--- /dev/null
+++ b/testing/xpcshell/mach_commands.py
@@ -0,0 +1,274 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Integrates the xpcshell test runner with mach.
+
+from __future__ import absolute_import, unicode_literals, print_function
+
+import argparse
+import errno
+import os
+import sys
+
+from mozlog import structured
+
+from mozbuild.base import (
+ MachCommandBase,
+ MozbuildObject,
+ MachCommandConditions as conditions,
+)
+
+from mach.decorators import (
+ CommandProvider,
+ Command,
+)
+
+from xpcshellcommandline import parser_desktop, parser_remote
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+if sys.version_info[0] < 3:
+ unicode_type = unicode
+else:
+ unicode_type = str
+
+
+# This should probably be consolidated with similar classes in other test
+# runners.
+class InvalidTestPathError(Exception):
+ """Exception raised when the test path is not valid."""
+
+
+class XPCShellRunner(MozbuildObject):
+ """Run xpcshell tests."""
+ def run_suite(self, **kwargs):
+ return self._run_xpcshell_harness(**kwargs)
+
+ def run_test(self, **kwargs):
+ """Runs an individual xpcshell test."""
+
+ # TODO Bug 794506 remove once mach integrates with virtualenv.
+ build_path = os.path.join(self.topobjdir, 'build')
+ if build_path not in sys.path:
+ sys.path.append(build_path)
+
+ src_build_path = os.path.join(self.topsrcdir, 'mozilla', 'build')
+ if os.path.isdir(src_build_path):
+ sys.path.append(src_build_path)
+
+ return self.run_suite(**kwargs)
+
+ def _run_xpcshell_harness(self, **kwargs):
+ # Obtain a reference to the xpcshell test runner.
+ import runxpcshelltests
+
+ log = kwargs.pop("log")
+
+ xpcshell = runxpcshelltests.XPCShellTests(log=log)
+ self.log_manager.enable_unstructured()
+
+ tests_dir = os.path.join(self.topobjdir, '_tests', 'xpcshell')
+ # We want output from the test to be written immediately if we are only
+ # running a single test.
+ single_test = (len(kwargs["testPaths"]) == 1 and
+ os.path.isfile(kwargs["testPaths"][0]) or
+ kwargs["manifest"] and
+ (len(kwargs["manifest"].test_paths()) == 1))
+
+ if single_test:
+ kwargs["verbose"] = True
+
+ if kwargs["xpcshell"] is None:
+ kwargs["xpcshell"] = self.get_binary_path('xpcshell')
+
+ if kwargs["mozInfo"] is None:
+ kwargs["mozInfo"] = os.path.join(self.topobjdir, 'mozinfo.json')
+
+ if kwargs["symbolsPath"] is None:
+ kwargs["symbolsPath"] = os.path.join(self.distdir, 'crashreporter-symbols')
+
+ if kwargs["logfiles"] is None:
+ kwargs["logfiles"] = False
+
+ if kwargs["profileName"] is None:
+ kwargs["profileName"] = "firefox"
+
+ if kwargs["pluginsPath"] is None:
+ kwargs['pluginsPath'] = os.path.join(self.distdir, 'plugins')
+
+ if kwargs["testingModulesDir"] is None:
+ kwargs["testingModulesDir"] = os.path.join(self.topobjdir, '_tests/modules')
+
+ if kwargs["utility_path"] is None:
+ kwargs['utility_path'] = self.bindir
+
+ if kwargs["manifest"] is None:
+ kwargs["manifest"] = os.path.join(tests_dir, "xpcshell.ini")
+
+ if kwargs["failure_manifest"] is None:
+ kwargs["failure_manifest"] = os.path.join(self.statedir, 'xpcshell.failures.ini')
+
+ # Use the object directory for the temp directory to minimize the chance
+ # of file scanning. The overhead from e.g. search indexers and anti-virus
+ # scanners like Windows Defender can add tons of overhead to test execution.
+ # We encourage people to disable these things in the object directory.
+ temp_dir = os.path.join(self.topobjdir, 'temp')
+ try:
+ os.mkdir(temp_dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+ kwargs['tempDir'] = temp_dir
+
+ # Python through 2.7.2 has issues with unicode in some of the
+ # arguments. Work around that.
+ filtered_args = {}
+ for k, v in kwargs.iteritems():
+ if isinstance(v, unicode_type):
+ v = v.encode('utf-8')
+
+ if isinstance(k, unicode_type):
+ k = k.encode('utf-8')
+
+ filtered_args[k] = v
+
+ result = xpcshell.runTests(**filtered_args)
+
+ self.log_manager.disable_unstructured()
+
+ if not result and not xpcshell.sequential:
+ print("Tests were run in parallel. Try running with --sequential "
+ "to make sure the failures were not caused by this.")
+ return int(not result)
+
+
+class AndroidXPCShellRunner(MozbuildObject):
+ """Get specified DeviceManager"""
+ def get_devicemanager(self, devicemanager, ip, port, remote_test_root):
+ import mozdevice
+ dm = None
+ if devicemanager == "adb":
+ if ip:
+ dm = mozdevice.DroidADB(ip, port, packageName=None, deviceRoot=remote_test_root)
+ else:
+ dm = mozdevice.DroidADB(packageName=None, deviceRoot=remote_test_root)
+ else:
+ if ip:
+ dm = mozdevice.DroidSUT(ip, port, deviceRoot=remote_test_root)
+ else:
+ raise Exception("You must provide a device IP to connect to via the --ip option")
+ return dm
+
+ """Run Android xpcshell tests."""
+ def run_test(self, **kwargs):
+ # TODO Bug 794506 remove once mach integrates with virtualenv.
+ build_path = os.path.join(self.topobjdir, 'build')
+ if build_path not in sys.path:
+ sys.path.append(build_path)
+
+ import remotexpcshelltests
+
+ dm = self.get_devicemanager(kwargs["dm_trans"], kwargs["deviceIP"], kwargs["devicePort"],
+ kwargs["remoteTestRoot"])
+
+ log = kwargs.pop("log")
+ self.log_manager.enable_unstructured()
+
+ if kwargs["xpcshell"] is None:
+ kwargs["xpcshell"] = "xpcshell"
+
+ if not kwargs["objdir"]:
+ kwargs["objdir"] = self.topobjdir
+
+ if not kwargs["localLib"]:
+ kwargs["localLib"] = os.path.join(self.topobjdir, 'dist/fennec')
+
+ if not kwargs["localBin"]:
+ kwargs["localBin"] = os.path.join(self.topobjdir, 'dist/bin')
+
+ if not kwargs["testingModulesDir"]:
+ kwargs["testingModulesDir"] = os.path.join(self.topobjdir, '_tests/modules')
+
+ if not kwargs["mozInfo"]:
+ kwargs["mozInfo"] = os.path.join(self.topobjdir, 'mozinfo.json')
+
+ if not kwargs["manifest"]:
+ kwargs["manifest"] = os.path.join(self.topobjdir, '_tests/xpcshell/xpcshell.ini')
+
+ if not kwargs["symbolsPath"]:
+ kwargs["symbolsPath"] = os.path.join(self.distdir, 'crashreporter-symbols')
+
+ if not kwargs["localAPK"]:
+ for file_name in os.listdir(os.path.join(kwargs["objdir"], "dist")):
+ if file_name.endswith(".apk") and file_name.startswith("fennec"):
+ kwargs["localAPK"] = os.path.join(kwargs["objdir"], "dist", file_name)
+ print ("using APK: %s" % kwargs["localAPK"])
+ break
+ else:
+ raise Exception("APK not found in objdir. You must specify an APK.")
+
+ if not kwargs["sequential"]:
+ kwargs["sequential"] = True
+
+ options = argparse.Namespace(**kwargs)
+ xpcshell = remotexpcshelltests.XPCShellRemote(dm, options, log)
+
+ result = xpcshell.runTests(testClass=remotexpcshelltests.RemoteXPCShellTestThread,
+ mobileArgs=xpcshell.mobileArgs,
+ **vars(options))
+
+ self.log_manager.disable_unstructured()
+
+ return int(not result)
+
+
+def get_parser():
+ build_obj = MozbuildObject.from_environment(cwd=here)
+ if conditions.is_android(build_obj):
+ return parser_remote()
+ else:
+ return parser_desktop()
+
+
+@CommandProvider
+class MachCommands(MachCommandBase):
+ @Command('xpcshell-test', category='testing',
+ description='Run XPCOM Shell tests (API direct unit testing)',
+ conditions=[lambda *args: True],
+ parser=get_parser)
+ def run_xpcshell_test(self, test_objects=None, **params):
+ from mozbuild.controller.building import BuildDriver
+
+ if test_objects is not None:
+ from manifestparser import TestManifest
+ m = TestManifest()
+ m.tests.extend(test_objects)
+ params['manifest'] = m
+
+ driver = self._spawn(BuildDriver)
+ driver.install_tests(test_objects)
+
+ # We should probably have a utility function to ensure the tree is
+ # ready to run tests. Until then, we just create the state dir (in
+ # case the tree wasn't built with mach).
+ self._ensure_state_subdir_exists('.')
+
+ params['log'] = structured.commandline.setup_logging("XPCShellTests",
+ params,
+ {"mach": sys.stdout},
+ {"verbose": True})
+
+ if conditions.is_android(self):
+ from mozrunner.devices.android_device import verify_android_device
+ verify_android_device(self)
+ xpcshell = self._spawn(AndroidXPCShellRunner)
+ else:
+ xpcshell = self._spawn(XPCShellRunner)
+ xpcshell.cwd = self._mach_context.cwd
+
+ try:
+ return xpcshell.run_test(**params)
+ except InvalidTestPathError as e:
+ print(e.message)
+ return 1