Bug 1318091 - Support |mach gtest| for android; r=bc

Add basic support for 'mach gtest' on Android.
Handling of Android-only and desktop-only options is awkward; I hope to
re-visit this after bug 1519369.

Differential Revision: https://phabricator.services.mozilla.com/D28129

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Geoff Brown 2019-04-22 15:55:23 +00:00
Родитель 9ac9cd5861
Коммит 069f6587db
2 изменённых файлов: 81 добавлений и 15 удалений

Просмотреть файл

@ -597,6 +597,25 @@ class GTestCommands(MachCommandBase):
@CommandArgument('--shuffle', '-s', action='store_true',
help='Randomize the execution order of tests.')
@CommandArgument('--package',
default='org.mozilla.geckoview.test',
help='(Android only) Package name of test app.')
@CommandArgument('--adbpath',
dest='adb_path',
help='(Android only) Path to adb binary.')
@CommandArgument('--deviceSerial',
dest='device_serial',
help="(Android only) adb serial number of remote device. "
"Required when more than one device is connected to the host. "
"Use 'adb devices' to see connected devices.")
@CommandArgument('--remoteTestRoot',
dest='remote_test_root',
help='(Android only) Remote directory to use as test root '
'(eg. /mnt/sdcard/tests or /data/local/tests).')
@CommandArgument('--libxul',
dest='libxul_path',
help='(Android only) Path to gtest libxul.so.')
@CommandArgumentGroup('debugging')
@CommandArgument('--debug', action='store_true', group='debugging',
help='Enable the debugger. Not specifying a --debugger option will result in the default debugger being used.')
@ -606,8 +625,9 @@ class GTestCommands(MachCommandBase):
group='debugging',
help='Command-line arguments to pass to the debugger itself; split as the Bourne shell would.')
def gtest(self, shuffle, jobs, gtest_filter, tbpl_parser, debug, debugger,
debugger_args):
def gtest(self, shuffle, jobs, gtest_filter, tbpl_parser,
package, adb_path, device_serial, remote_test_root, libxul_path,
debug, debugger, debugger_args):
# We lazy build gtest because it's slow to link
try:
@ -632,6 +652,17 @@ class GTestCommands(MachCommandBase):
self._run_make(directory='browser/app', target='repackage',
ensure_exit_code=True)
if conditions.is_android(self):
if jobs != 1:
print("--jobs is not supported on Android and will be ignored")
if debug or debugger or debugger_args:
print("--debug options are not supported on Android and will be ignored")
return self.android_gtest(shuffle, gtest_filter,
package, adb_path, device_serial, remote_test_root, libxul_path)
if package or adb_path or device_serial or remote_test_root or libxul_path:
print("One or more Android-only options will be ignored")
app_path = self.get_binary_path('app')
args = [app_path, '-unittest', '--gtest_death_test_style=threadsafe'];
@ -708,6 +739,36 @@ class GTestCommands(MachCommandBase):
return exit_code
def android_gtest(self, shuffle, gtest_filter,
package, adb_path, device_serial, remote_test_root, libxul_path):
# setup logging for mozrunner
from mozlog.commandline import setup_logging
format_args = {'level': self._mach_context.settings['test']['level']}
default_format = self._mach_context.settings['test']['format']
log = setup_logging('mach-gtest', {}, {default_format: sys.stdout}, format_args)
# ensure that a device is available and test app is installed
from mozrunner.devices.android_device import (verify_android_device, get_adb_path)
verify_android_device(self, install=True, app=package, device_serial=device_serial)
if not adb_path:
adb_path = get_adb_path(self)
if not libxul_path:
libxul_path = os.path.join(self.topobjdir, "dist", "bin", "gtest", "libxul.so")
# run gtest via remotegtests.py
import imp
path = os.path.join('testing', 'gtest', 'remotegtests.py')
with open(path, 'r') as fh:
imp.load_module('remotegtests', fh, path,
('.py', 'r', imp.PY_SOURCE))
import remotegtests
tester = remotegtests.RemoteGTests()
tester.run_gtest(shuffle, gtest_filter, package, adb_path, device_serial,
remote_test_root, libxul_path, None)
return 0
def prepend_debugger_args(self, args, debugger, debugger_args):
'''
Given an array with program arguments, prepend arguments to run it under a

Просмотреть файл

@ -30,7 +30,7 @@ class RemoteGTests(object):
A test harness to run gtest on Android.
"""
def build_environment(self, options, test_filter):
def build_environment(self, shuffle, test_filter):
"""
Create and return a dictionary of all the appropriate env variables
and values.
@ -40,32 +40,35 @@ class RemoteGTests(object):
env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
env["MOZ_CRASHREPORTER"] = "1"
env["MOZ_RUN_GTEST"] = "1"
# custom output parser is mandatory on Android
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:
if shuffle:
env["GTEST_SHUFFLE"] = "True"
if test_filter:
env["GTEST_FILTER"] = test_filter
return env
def run_gtest(self, options, test_filter):
def run_gtest(self, shuffle, test_filter, package, adb_path, device_serial,
remote_test_root, libxul_path, symbols_path):
"""
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,
update_mozinfo()
self.device = mozdevice.ADBDevice(adb=adb_path,
device=device_serial,
test_root=remote_test_root,
logger_name=LOGGER_NAME,
verbose=True)
root = self.device.test_root
self.remote_profile = posixpath.join(root, 'gtest-profile')
self.remote_minidumps = posixpath.join(root, 'gtest-minidumps')
self.remote_log = posixpath.join(root, 'gtest.log')
self.package = options.package
self.package = package
self.cleanup()
self.device.mkdir(self.remote_profile, parents=True)
self.device.mkdir(self.remote_minidumps, parents=True)
@ -78,9 +81,9 @@ class RemoteGTests(object):
# 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)
self.device.push(libxul_path, remote)
env = self.build_environment(options, test_filter)
env = self.build_environment(shuffle, test_filter)
args = ["-unittest", "--gtest_death_test_style=threadsafe",
"-profile %s" % self.remote_profile]
if 'geckoview' in self.package:
@ -93,7 +96,7 @@ class RemoteGTests(object):
waiter = AppWaiter(self.device, self.remote_log)
timed_out = waiter.wait(self.package)
self.shutdown(use_kill=True if timed_out else False)
if self.check_for_crashes(options.symbols_path):
if self.check_for_crashes(symbols_path):
return False
return True
@ -313,7 +316,7 @@ class remoteGtestOptions(OptionParser):
self.add_option("--remoteTestRoot",
action="store",
type=str,
dest="test_root",
dest="remote_test_root",
help="Remote directory to use as test root "
"(eg. /mnt/sdcard/tests or /data/local/tests).")
self.add_option("--libxul",
@ -357,12 +360,14 @@ def main():
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, test_filter)
result = tester.run_gtest(options.shuffle, test_filter, options.package,
options.adb_path, options.device_serial,
options.remote_test_root, options.libxul_path,
options.symbols_path)
except KeyboardInterrupt:
log.info("gtest | Received keyboard interrupt")
except Exception as e: