mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 17:55:50 +00:00
3680684f70
This makes the changes necessary to use TestRunnerActivity when geckoview is installed and requested, but we do not yet attempt to run any such test tasks in automation.
247 lines
9.0 KiB
Python
247 lines
9.0 KiB
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 posixpath
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
import traceback
|
|
from optparse import OptionParser
|
|
|
|
import mozcrash
|
|
import mozlog
|
|
from mozdevice import ADBAndroid
|
|
from mozprofile import Profile
|
|
|
|
|
|
class GeckoviewOptions(OptionParser):
|
|
def __init__(self):
|
|
OptionParser.__init__(self)
|
|
self.add_option("--utility-path",
|
|
action="store", type="string", dest="utility_path",
|
|
default=None,
|
|
help="absolute path to directory containing utility programs")
|
|
self.add_option("--symbols-path",
|
|
action="store", type="string", 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("--appname",
|
|
action="store", type="string", dest="app",
|
|
default="org.mozilla.geckoview_example",
|
|
help="geckoview_example package name")
|
|
self.add_option("--deviceSerial",
|
|
action="store", type="string", dest="deviceSerial",
|
|
default=None,
|
|
help="serial ID of remote device to test")
|
|
self.add_option("--adbpath",
|
|
action="store", type="string", dest="adbPath",
|
|
default="adb",
|
|
help="Path to adb binary.")
|
|
self.add_option("--remoteTestRoot",
|
|
action="store", type="string", dest="remoteTestRoot",
|
|
default=None,
|
|
help="remote directory to use as test root \
|
|
(eg. /mnt/sdcard/tests or /data/local/tests)")
|
|
|
|
|
|
class GeckoviewTestRunner:
|
|
"""
|
|
A quick-and-dirty test harness to verify the geckoview_example
|
|
app starts without crashing.
|
|
"""
|
|
|
|
def __init__(self, log, options):
|
|
self.log = log
|
|
self.device = ADBAndroid(adb=options.adbPath,
|
|
device=options.deviceSerial,
|
|
test_root=options.remoteTestRoot)
|
|
self.options = options
|
|
self.appname = self.options.app.split('/')[-1]
|
|
self.logcat = None
|
|
self.build_profile()
|
|
self.log.debug("options=%s" % vars(options))
|
|
|
|
def build_profile(self):
|
|
test_root = self.device.test_root
|
|
self.remote_profile = posixpath.join(test_root, 'gv-profile')
|
|
self.device.mkdir(self.remote_profile, parents=True)
|
|
profile = Profile()
|
|
self.device.push(profile.profile, self.remote_profile)
|
|
self.log.debug("profile %s -> %s" %
|
|
(str(profile.profile), str(self.remote_profile)))
|
|
|
|
def installed(self):
|
|
"""
|
|
geckoview_example installed
|
|
"""
|
|
if not self.device.is_app_installed(self.appname):
|
|
return (False, "%s not installed" % self.appname)
|
|
return (True, "%s installed" % self.appname)
|
|
|
|
def start(self):
|
|
"""
|
|
geckoview_example starts
|
|
"""
|
|
try:
|
|
self.device.stop_application(self.appname)
|
|
self.device.clear_logcat()
|
|
|
|
args = ["-profile", self.remote_profile]
|
|
env = {}
|
|
env["MOZ_CRASHREPORTER"] = "1"
|
|
env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
|
|
env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
|
|
env["XPCOM_DEBUG_BREAK"] = "stack"
|
|
env["DISABLE_UNSAFE_CPOW_WARNINGS"] = "1"
|
|
env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
|
|
env["MOZ_IN_AUTOMATION"] = "1"
|
|
env["R_LOG_VERBOSE"] = "1"
|
|
env["R_LOG_LEVEL"] = "6"
|
|
env["R_LOG_DESTINATION"] = "stderr"
|
|
self.device.launch_activity("org.mozilla.geckoview_example",
|
|
"GeckoViewActivity",
|
|
extra_args=args, moz_env=env)
|
|
except Exception:
|
|
return (False, "Exception during %s startup" % self.appname)
|
|
return (True, "%s started" % self.appname)
|
|
|
|
def started(self):
|
|
"""
|
|
startup logcat messages
|
|
"""
|
|
expected = [
|
|
"zerdatime",
|
|
"Displayed %s/.GeckoViewActivity" % self.appname
|
|
]
|
|
# wait up to 60 seconds for startup
|
|
for wait_time in xrange(60):
|
|
time.sleep(1)
|
|
self.logcat = self.device.get_logcat()
|
|
for line in self.logcat:
|
|
for e in expected:
|
|
if e in line:
|
|
self.log.debug(line.strip())
|
|
expected.remove(e)
|
|
if len(expected) == 0:
|
|
return (True, "All expected logcat messages found")
|
|
for e in expected:
|
|
self.log.error("missing from logcat: '%s'" % e)
|
|
return (False, "'%s' not found in logcat" % expected[0])
|
|
|
|
def run_tests(self):
|
|
"""
|
|
Run simple tests to verify that the geckoview_example app starts.
|
|
"""
|
|
all_tests = [self.installed, self.start, self.started]
|
|
self.log.suite_start(all_tests)
|
|
pass_count = 0
|
|
fail_count = 0
|
|
for test in all_tests:
|
|
self.test_name = test.__doc__.strip()
|
|
self.log.test_start(self.test_name)
|
|
|
|
expected = 'PASS'
|
|
(passed, message) = test()
|
|
if passed:
|
|
pass_count = pass_count + 1
|
|
else:
|
|
fail_count = fail_count + 1
|
|
status = 'PASS' if passed else 'FAIL'
|
|
|
|
self.log.test_end(self.test_name, status, expected, message)
|
|
|
|
crashed = self.check_for_crashes()
|
|
if crashed:
|
|
fail_count = 1
|
|
else:
|
|
self.log.info("Passed: %d" % pass_count)
|
|
self.log.info("Failed: %d" % fail_count)
|
|
self.log.suite_end()
|
|
|
|
return 1 if fail_count else 0
|
|
|
|
def check_for_crashes(self):
|
|
if self.logcat:
|
|
if mozcrash.check_for_java_exception(self.logcat, self.test_name):
|
|
return True
|
|
symbols_path = self.options.symbols_path
|
|
try:
|
|
dump_dir = tempfile.mkdtemp()
|
|
remote_dir = posixpath.join(self.remote_profile, 'minidumps')
|
|
crash_dir_found = False
|
|
# wait up to 60 seconds for gecko startup to progress through
|
|
# crashreporter initialization, in case all tests finished quickly
|
|
for wait_time in xrange(60):
|
|
time.sleep(1)
|
|
if self.device.is_dir(remote_dir):
|
|
crash_dir_found = True
|
|
break
|
|
if not crash_dir_found:
|
|
# If crash reporting is enabled (MOZ_CRASHREPORTER=1), the
|
|
# minidumps directory is automatically created when the app
|
|
# (first) starts, so its lack of presence is a hint that
|
|
# something went wrong.
|
|
print "Automation Error: No crash directory (%s) found on remote device" % \
|
|
remote_dir
|
|
# Whilst no crash was found, the run should still display as a failure
|
|
return True
|
|
self.device.pull(remote_dir, dump_dir)
|
|
crashed = mozcrash.log_crashes(self.log, dump_dir, symbols_path, test=self.test_name)
|
|
finally:
|
|
try:
|
|
shutil.rmtree(dump_dir)
|
|
except Exception:
|
|
self.log.warn("unable to remove directory: %s" % dump_dir)
|
|
return crashed
|
|
|
|
def cleanup(self):
|
|
"""
|
|
Cleanup at end of job run.
|
|
"""
|
|
self.log.debug("Cleaning up...")
|
|
self.device.stop_application(self.appname)
|
|
self.device.rm(self.remote_profile, force=True, recursive=True)
|
|
self.log.debug("Cleanup complete.")
|
|
|
|
|
|
def run_test_harness(log, parser, options):
|
|
runner = GeckoviewTestRunner(log, options)
|
|
result = -1
|
|
try:
|
|
result = runner.run_tests()
|
|
except KeyboardInterrupt:
|
|
log.info("rungeckoview.py | Received keyboard interrupt")
|
|
result = -1
|
|
except Exception:
|
|
traceback.print_exc()
|
|
log.error(
|
|
"rungeckoview.py | Received unexpected exception while running tests")
|
|
result = 1
|
|
finally:
|
|
try:
|
|
runner.cleanup()
|
|
except Exception:
|
|
# ignore device error while cleaning up
|
|
traceback.print_exc()
|
|
return result
|
|
|
|
|
|
def main(args=sys.argv[1:]):
|
|
parser = GeckoviewOptions()
|
|
mozlog.commandline.add_logging_group(parser)
|
|
options, args = parser.parse_args()
|
|
if args:
|
|
print >>sys.stderr, """Usage: %s""" % sys.argv[0]
|
|
sys.exit(1)
|
|
log = mozlog.commandline.setup_logging("rungeckoview", options,
|
|
{"tbpl": sys.stdout})
|
|
return run_test_harness(log, parser, options)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|