#!/usr/bin/env python # # 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/.
import argparse import datetime import glob import os import posixpath import shutil import sys import tempfile import time import traceback
import mozcrash import mozdevice import mozinfo import mozlog import six
log.info("Running Android gtest") ifnot self.device.is_app_installed(self.package): raise Exception("%s is not installed on this device" % self.package)
# Push the gtest libxul.so to the device. The harness assumes an architecture- # appropriate library is specified and pushes it to the arch-agnostic remote # directory. # TODO -- consider packaging the gtest libxul.so in an apk
self.device.push(libxul_path, self.remote_libdir)
for buildid in ["correct", "broken", "missing"]:
libxul_buildid_name = "libxul_{}_buildid.so".format(buildid)
libxul_buildid_path = os.path.join(
os.path.dirname(libxul_path), libxul_buildid_name
) if os.path.isfile(libxul_buildid_path):
self.device.push(libxul_buildid_path, self.remote_libdir)
# Push support files to device. Avoid sub-directories so that libxul.so # is not included. for f in glob.glob(os.path.join(test_dir, "*")): ifnot os.path.isdir(f):
self.device.push(f, self.remote_profile)
def shutdown(self, use_kill): """
Stop the remote application. If use_kill is specified, a multi-stage kill procedure is used,
attempting to trigger ANR and minidump reports before ending
the process. """ ifnot use_kill:
self.device.stop_application(self.package) else: # Trigger an ANR report with "kill -3" (SIGQUIT) try:
self.device.pkill(self.package, sig=3, attempts=1) except mozdevice.ADBTimeoutError: raise except: # NOQA: E722 pass
time.sleep(3) # Trigger a breakpad dump with "kill -6" (SIGABRT) try:
self.device.pkill(self.package, sig=6, attempts=1) except mozdevice.ADBTimeoutError: raise except: # NOQA: E722 pass # Wait for process to end
retries = 0 while retries < 3: if self.device.process_exist(self.package):
log.info("%s still alive after SIGABRT: waiting..." % self.package)
time.sleep(5) else: break
retries += 1 if self.device.process_exist(self.package): try:
self.device.pkill(self.package, sig=9, attempts=1) except mozdevice.ADBTimeoutError: raise except: # NOQA: E722
log.warning("%s still alive after SIGKILL!" % self.package) if self.device.process_exist(self.package):
self.device.stop_application(self.package) # Test harnesses use the MOZ_CRASHREPORTER environment variables to suppress # the interactive crash reporter, but that may not always be effective; # check for and cleanup errant crashreporters.
crashreporter = "%s.CrashReporter" % self.package if self.device.process_exist(crashreporter):
log.warning("%s unexpectedly found running. Killing..." % crashreporter) try:
self.device.pkill(crashreporter) except mozdevice.ADBTimeoutError: raise except: # NOQA: E722 pass if self.device.process_exist(crashreporter):
log.error("%s still running!!" % crashreporter)
def check_for_crashes(self, symbols_path): """
Pull minidumps from the remote device and generate crash reports.
Returns Trueif a crash was detected, or suspected. """ try:
dump_dir = tempfile.mkdtemp()
remote_dir = self.remote_minidumps ifnot self.device.is_dir(remote_dir): returnFalse
self.device.pull(remote_dir, dump_dir)
crashed = mozcrash.check_for_crashes(
dump_dir, symbols_path, test_name="gtest"
) except Exception as e:
log.error("unable to check for crashes: %s" % str(e))
crashed = True finally: try:
shutil.rmtree(dump_dir) except Exception:
log.warning("unable to remove directory: %s" % dump_dir) return crashed
def start_timed_out(self): if datetime.datetime.now() - self.start_time > self.start_timeout_delta: returnTrue returnFalse
def timed_out(self): if datetime.datetime.now() - self.start_time > self.timeout_delta: returnTrue returnFalse
def output_timed_out(self): if datetime.datetime.now() - self.last_output_time > self.output_timeout_delta: returnTrue returnFalse
def get_top(self):
top = self.device.get_top_activity(timeout=60) if top isNone:
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):
top = None while top != package andnot self.start_timed_out(): 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
time.sleep(1)
top = self.get_top() return top
def wait(self, package): """
Wait until:
- the app loses foreground, or
- no new output is observed for the output timeout, or
- the timeout is exceeded. While waiting, update the log every periodically: pull the gtest log from
device and log any new content. """
top = self.wait_for_start(package) if top != package:
log.testFail("gtest | %s failed to start" % package) return whilenot self.timed_out(): ifnot self.update_log():
top = self.get_top() if top != package or self.output_timed_out():
time.sleep(self.output_poll_interval) break
time.sleep(self.output_poll_interval)
self.update_log() if self.timed_out():
log.testFail( "gtest | timed out after %d seconds", self.timeout_delta.seconds
) elif self.output_timed_out():
log.testFail( "gtest | timed out after %d seconds without output",
self.output_timeout_delta.seconds,
) else:
log.info("gtest | wait for %s complete; top activity=%s" % (package, top)) returnTrueif top == package elseFalse
def update_log(self): """
Pull the test log from the remote device and display new content. """ ifnot self.device.is_file(self.remote_log):
log.info("gtest | update_log %s is not a file." % self.remote_log) returnFalse try:
new_content = self.device.get_file(
self.remote_log, offset=self.remote_log_len
) except mozdevice.ADBTimeoutError: raise except Exception as e:
log.info("gtest | update_log : exception reading log: %s" % str(e)) returnFalse ifnot new_content:
log.info("gtest | update_log : no new content") returnFalse
new_content = six.ensure_text(new_content)
last_full_line_pos = new_content.rfind("\n") if last_full_line_pos <= 0: # wait for a full line returnFalse # trim partial line
new_content = new_content[:last_full_line_pos]
self.remote_log_len += len(new_content) for line in new_content.lstrip("\n").split("\n"):
print(line)
self.last_output_time = datetime.datetime.now() returnTrue
class remoteGtestOptions(argparse.ArgumentParser): def __init__(self):
super(remoteGtestOptions, self).__init__(
usage="usage: %prog [options] test_filter"
)
self.add_argument( "--package",
dest="package",
default="org.mozilla.geckoview.test_runner",
help="Package name of test app.",
)
self.add_argument( "--adbpath",
action="store",
type=str,
dest="adb_path",
default="adb",
help="Path to adb binary.",
)
self.add_argument( "--deviceSerial",
action="store",
type=str,
dest="device_serial",
help="adb serial number of remote device. This is required " "when more than one device is connected to the host. " "Use 'adb devices' to see connected devices. ",
)
self.add_argument( "--remoteTestRoot",
action="store",
type=str,
dest="remote_test_root",
help="Remote directory to use as test root " "(eg. /data/local/tmp/test_root).",
)
self.add_argument( "--libxul",
action="store",
type=str,
dest="libxul_path",
default=None,
help="Path to gtest libxul.so.",
)
self.add_argument( "--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_argument( "--shuffle",
action="store_true",
default=False,
help="Randomize the execution order of tests.",
)
self.add_argument( "--tests-path",
default=None,
help="Path to gtest directory containing test support files.",
)
self.add_argument("args", nargs=argparse.REMAINDER)
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() 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 main():
parser = remoteGtestOptions()
options = parser.parse_args()
args = options.args ifnot options.libxul_path:
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 elseNone
tester = RemoteGTests()
result = False try:
device_exception = False
result = tester.run_gtest(
options.tests_path,
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:
log.error(str(e))
traceback.print_exc() if isinstance(e, mozdevice.ADBTimeoutError):
device_exception = True finally: ifnot device_exception:
tester.cleanup()
sys.exit(0 if result else 1)
if __name__ == "__main__":
main()
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.