зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1492239 - Use AndroidMixin for android_emulator_unittest; r=bc
This commit is contained in:
Родитель
6d14711d3f
Коммит
efef4d47a1
|
@ -38,7 +38,7 @@ config = {
|
||||||
'start-emulator',
|
'start-emulator',
|
||||||
'download-and-extract',
|
'download-and-extract',
|
||||||
'create-virtualenv',
|
'create-virtualenv',
|
||||||
'verify-emulator',
|
'verify-device',
|
||||||
'install',
|
'install',
|
||||||
'run-tests',
|
'run-tests',
|
||||||
],
|
],
|
||||||
|
|
|
@ -17,6 +17,7 @@ config = {
|
||||||
"unpack": "True"
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
] """,
|
] """,
|
||||||
|
"emulator_avd_name": "test-1",
|
||||||
"emulator_process_name": "emulator64-arm",
|
"emulator_process_name": "emulator64-arm",
|
||||||
"emulator_extra_args": "-show-kernel -debug init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket",
|
"emulator_extra_args": "-show-kernel -debug init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket",
|
||||||
"exes": {
|
"exes": {
|
||||||
|
@ -27,9 +28,6 @@ config = {
|
||||||
"PATH": "%(PATH)s:%(abs_work_dir)s/android-sdk-linux/tools:%(abs_work_dir)s/android-sdk-linux/platform-tools",
|
"PATH": "%(PATH)s:%(abs_work_dir)s/android-sdk-linux/tools:%(abs_work_dir)s/android-sdk-linux/platform-tools",
|
||||||
"MINIDUMP_SAVEPATH": "%(abs_work_dir)s/../minidumps"
|
"MINIDUMP_SAVEPATH": "%(abs_work_dir)s/../minidumps"
|
||||||
},
|
},
|
||||||
"emulator": {
|
|
||||||
"name": "test-1",
|
|
||||||
"device_id": "emulator-5554",
|
|
||||||
},
|
|
||||||
"marionette_extra": "--emulator",
|
"marionette_extra": "--emulator",
|
||||||
|
"bogomips_minimum": 250,
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ config = {
|
||||||
"unpack": "True"
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
] """,
|
] """,
|
||||||
|
"emulator_avd_name": "test-1",
|
||||||
"emulator_process_name": "emulator64-arm",
|
"emulator_process_name": "emulator64-arm",
|
||||||
"emulator_extra_args": "-show-kernel -debug init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket -audio oss",
|
"emulator_extra_args": "-show-kernel -debug init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket -audio oss",
|
||||||
"exes": {
|
"exes": {
|
||||||
|
@ -26,8 +27,5 @@ config = {
|
||||||
"PATH": "%(PATH)s:%(abs_work_dir)s/android-sdk-linux/tools:%(abs_work_dir)s/android-sdk-linux/platform-tools",
|
"PATH": "%(PATH)s:%(abs_work_dir)s/android-sdk-linux/tools:%(abs_work_dir)s/android-sdk-linux/platform-tools",
|
||||||
"MINIDUMP_SAVEPATH": "%(abs_work_dir)s/../minidumps"
|
"MINIDUMP_SAVEPATH": "%(abs_work_dir)s/../minidumps"
|
||||||
},
|
},
|
||||||
"emulator": {
|
"bogomips_minimum": 250,
|
||||||
"name": "test-1",
|
|
||||||
"device_id": "emulator-5554",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ config = {
|
||||||
"unpack": "True"
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
] """,
|
] """,
|
||||||
|
"emulator_avd_name": "test-1",
|
||||||
"emulator_process_name": "emulator64-x86",
|
"emulator_process_name": "emulator64-x86",
|
||||||
"emulator_extra_args": "-show-kernel -debug init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket -qemu -m 1024",
|
"emulator_extra_args": "-show-kernel -debug init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket -qemu -m 1024",
|
||||||
"exes": {
|
"exes": {
|
||||||
|
@ -26,8 +27,4 @@ config = {
|
||||||
"PATH": "%(PATH)s:%(abs_work_dir)s/android-sdk18/tools:%(abs_work_dir)s/android-sdk18/platform-tools",
|
"PATH": "%(PATH)s:%(abs_work_dir)s/android-sdk18/tools:%(abs_work_dir)s/android-sdk18/platform-tools",
|
||||||
"MINIDUMP_SAVEPATH": "%(abs_work_dir)s/../minidumps"
|
"MINIDUMP_SAVEPATH": "%(abs_work_dir)s/../minidumps"
|
||||||
},
|
},
|
||||||
"emulator": {
|
|
||||||
"name": "test-1",
|
|
||||||
"device_id": "emulator-5554",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ config = {
|
||||||
"unpack": "True"
|
"unpack": "True"
|
||||||
}
|
}
|
||||||
] """,
|
] """,
|
||||||
|
"emulator_avd_name": "test-1",
|
||||||
"emulator_process_name": "emulator64-x86",
|
"emulator_process_name": "emulator64-x86",
|
||||||
"emulator_extra_args": "-gpu swiftshader_indirect -skip-adb-auth -verbose -show-kernel -use-system-libs -ranchu -selinux permissive -memory 3072 -cores 4",
|
"emulator_extra_args": "-gpu swiftshader_indirect -skip-adb-auth -verbose -show-kernel -use-system-libs -ranchu -selinux permissive -memory 3072 -cores 4",
|
||||||
"exes": {
|
"exes": {
|
||||||
|
@ -26,9 +27,5 @@ config = {
|
||||||
"MINIDUMP_SAVEPATH": "%(abs_work_dir)s/../minidumps",
|
"MINIDUMP_SAVEPATH": "%(abs_work_dir)s/../minidumps",
|
||||||
# "LIBGL_DEBUG": "verbose"
|
# "LIBGL_DEBUG": "verbose"
|
||||||
},
|
},
|
||||||
"emulator": {
|
|
||||||
"name": "test-1",
|
|
||||||
"device_id": "emulator-5554",
|
|
||||||
},
|
|
||||||
"marionette_extra": "--emulator",
|
"marionette_extra": "--emulator",
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from mozharness.mozilla.automation import TBPL_RETRY, EXIT_STATUS_DICT
|
from mozharness.mozilla.automation import TBPL_RETRY, EXIT_STATUS_DICT
|
||||||
|
@ -18,13 +19,13 @@ class AndroidMixin(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.logcat_proc = None
|
|
||||||
self.logcat_file = None
|
|
||||||
|
|
||||||
self._adb_path = None
|
self._adb_path = None
|
||||||
|
self._device = None
|
||||||
self.device_name = os.environ.get('DEVICE_NAME', None)
|
self.device_name = os.environ.get('DEVICE_NAME', None)
|
||||||
self.device_serial = os.environ.get('DEVICE_SERIAL', None)
|
self.device_serial = os.environ.get('DEVICE_SERIAL', None)
|
||||||
self.device_ip = os.environ.get('DEVICE_IP', None)
|
self.device_ip = os.environ.get('DEVICE_IP', None)
|
||||||
|
self.logcat_proc = None
|
||||||
|
self.logcat_file = None
|
||||||
super(AndroidMixin, self).__init__(**kwargs)
|
super(AndroidMixin, self).__init__(**kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -47,6 +48,21 @@ class AndroidMixin(object):
|
||||||
pass
|
pass
|
||||||
return self._adb_path
|
return self._adb_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device(self):
|
||||||
|
if not self._device:
|
||||||
|
try:
|
||||||
|
import mozdevice
|
||||||
|
self._device = mozdevice.ADBAndroid(adb=self.adb_path,
|
||||||
|
device=self.device_serial,
|
||||||
|
verbose=True)
|
||||||
|
self.info("New mozdevice with adb=%s, device=%s" %
|
||||||
|
(self.adb_path, self.device_serial))
|
||||||
|
except Exception:
|
||||||
|
# As in adb_path, above.
|
||||||
|
pass
|
||||||
|
return self._device
|
||||||
|
|
||||||
def _get_repo_url(self, path):
|
def _get_repo_url(self, path):
|
||||||
"""
|
"""
|
||||||
Return a url for a file (typically a tooltool manifest) in this hg repo
|
Return a url for a file (typically a tooltool manifest) in this hg repo
|
||||||
|
@ -86,6 +102,61 @@ class AndroidMixin(object):
|
||||||
output_dir=dir,
|
output_dir=dir,
|
||||||
cache=c.get("tooltool_cache", None))
|
cache=c.get("tooltool_cache", None))
|
||||||
|
|
||||||
|
def dump_perf_info(self):
|
||||||
|
'''
|
||||||
|
Dump some host and android device performance-related information
|
||||||
|
to an artifact file, to help understand task performance.
|
||||||
|
'''
|
||||||
|
dir = self.query_abs_dirs()['abs_blob_upload_dir']
|
||||||
|
perf_path = os.path.join(dir, "android-performance.log")
|
||||||
|
with open(perf_path, "w") as f:
|
||||||
|
|
||||||
|
f.write('\n\nHost /proc/cpuinfo:\n')
|
||||||
|
out = subprocess.check_output(['cat', '/proc/cpuinfo'])
|
||||||
|
f.write(out)
|
||||||
|
|
||||||
|
f.write('\n\nHost /proc/meminfo:\n')
|
||||||
|
out = subprocess.check_output(['cat', '/proc/meminfo'])
|
||||||
|
f.write(out)
|
||||||
|
|
||||||
|
f.write('\n\nHost process list:\n')
|
||||||
|
out = subprocess.check_output(['ps', '-ef'])
|
||||||
|
f.write(out)
|
||||||
|
|
||||||
|
f.write('\n\nDevice /proc/cpuinfo:\n')
|
||||||
|
cmd = 'cat /proc/cpuinfo'
|
||||||
|
out = self.shell_output(cmd)
|
||||||
|
f.write(out)
|
||||||
|
cpuinfo = out
|
||||||
|
|
||||||
|
f.write('\n\nDevice /proc/meminfo:\n')
|
||||||
|
cmd = 'cat /proc/meminfo'
|
||||||
|
out = self.shell_output(cmd)
|
||||||
|
f.write(out)
|
||||||
|
|
||||||
|
f.write('\n\nDevice process list:\n')
|
||||||
|
cmd = 'ps'
|
||||||
|
out = self.shell_output(cmd)
|
||||||
|
f.write(out)
|
||||||
|
|
||||||
|
# Search android cpuinfo for "BogoMIPS"; if found and < (minimum), retry
|
||||||
|
# this task, in hopes of getting a higher-powered environment.
|
||||||
|
# (Carry on silently if BogoMIPS is not found -- this may vary by
|
||||||
|
# Android implementation -- no big deal.)
|
||||||
|
# See bug 1321605: Sometimes the emulator is really slow, and
|
||||||
|
# low bogomips can be a good predictor of that condition.
|
||||||
|
bogomips_minimum = int(self.config.get('bogomips_minimum') or 0)
|
||||||
|
for line in cpuinfo.split('\n'):
|
||||||
|
m = re.match("BogoMIPS.*: (\d*)", line)
|
||||||
|
if m:
|
||||||
|
bogomips = int(m.group(1))
|
||||||
|
if bogomips_minimum > 0 and bogomips < bogomips_minimum:
|
||||||
|
self.fatal('INFRA-ERROR: insufficient Android bogomips (%d < %d)' %
|
||||||
|
(bogomips, bogomips_minimum),
|
||||||
|
EXIT_STATUS_DICT[TBPL_RETRY])
|
||||||
|
self.info("Found Android bogomips: %d" % bogomips)
|
||||||
|
break
|
||||||
|
|
||||||
def logcat_start(self):
|
def logcat_start(self):
|
||||||
"""
|
"""
|
||||||
Start recording logcat. Writes logcat to the upload directory.
|
Start recording logcat. Writes logcat to the upload directory.
|
||||||
|
@ -121,13 +192,21 @@ class AndroidMixin(object):
|
||||||
"""
|
"""
|
||||||
import mozdevice
|
import mozdevice
|
||||||
try:
|
try:
|
||||||
device = mozdevice.ADBAndroid(adb=self.adb_path, device=self.device_serial)
|
self.device.install_app(apk)
|
||||||
device.install_app(apk)
|
|
||||||
except mozdevice.ADBError:
|
except mozdevice.ADBError:
|
||||||
self.fatal('INFRA-ERROR: Failed to install %s on %s' %
|
self.fatal('INFRA-ERROR: Failed to install %s on %s' %
|
||||||
(self.installer_path, self.device_name),
|
(self.installer_path, self.device_name),
|
||||||
EXIT_STATUS_DICT[TBPL_RETRY])
|
EXIT_STATUS_DICT[TBPL_RETRY])
|
||||||
|
|
||||||
|
def is_boot_completed(self):
|
||||||
|
out = self.device.get_prop('sys.boot_completed', timeout=30)
|
||||||
|
if out.strip() == '1':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def shell_output(self, cmd):
|
||||||
|
return self.device.shell_output(cmd, timeout=30)
|
||||||
|
|
||||||
def screenshot(self, prefix):
|
def screenshot(self, prefix):
|
||||||
"""
|
"""
|
||||||
Save a screenshot of the entire screen to the blob upload directory.
|
Save a screenshot of the entire screen to the blob upload directory.
|
||||||
|
|
|
@ -25,6 +25,7 @@ from mozharness.base.log import FATAL
|
||||||
from mozharness.base.script import BaseScript, PreScriptAction, PostScriptAction
|
from mozharness.base.script import BaseScript, PreScriptAction, PostScriptAction
|
||||||
from mozharness.mozilla.automation import TBPL_RETRY, EXIT_STATUS_DICT
|
from mozharness.mozilla.automation import TBPL_RETRY, EXIT_STATUS_DICT
|
||||||
from mozharness.mozilla.mozbase import MozbaseMixin
|
from mozharness.mozilla.mozbase import MozbaseMixin
|
||||||
|
from mozharness.mozilla.testing.android import AndroidMixin
|
||||||
from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
|
from mozharness.mozilla.testing.testbase import TestingMixin, testing_config_options
|
||||||
from mozharness.mozilla.testing.codecoverage import (
|
from mozharness.mozilla.testing.codecoverage import (
|
||||||
CodeCoverageMixin,
|
CodeCoverageMixin,
|
||||||
|
@ -32,7 +33,8 @@ from mozharness.mozilla.testing.codecoverage import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMixin):
|
class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMixin,
|
||||||
|
AndroidMixin):
|
||||||
"""
|
"""
|
||||||
A mozharness script for Android functional tests (like mochitests and reftests)
|
A mozharness script for Android functional tests (like mochitests and reftests)
|
||||||
run on an Android emulator. This script starts and manages an Android emulator
|
run on an Android emulator. This script starts and manages an Android emulator
|
||||||
|
@ -93,7 +95,7 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
'start-emulator',
|
'start-emulator',
|
||||||
'download-and-extract',
|
'download-and-extract',
|
||||||
'create-virtualenv',
|
'create-virtualenv',
|
||||||
'verify-emulator',
|
'verify-device',
|
||||||
'install',
|
'install',
|
||||||
'run-tests',
|
'run-tests',
|
||||||
],
|
],
|
||||||
|
@ -108,14 +110,12 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
# these are necessary since self.config is read only
|
# these are necessary since self.config is read only
|
||||||
c = self.config
|
c = self.config
|
||||||
abs_dirs = self.query_abs_dirs()
|
abs_dirs = self.query_abs_dirs()
|
||||||
self.adb_path = self.query_exe('adb')
|
|
||||||
self.installer_url = c.get('installer_url')
|
self.installer_url = c.get('installer_url')
|
||||||
self.installer_path = c.get('installer_path')
|
self.installer_path = c.get('installer_path')
|
||||||
self.test_url = c.get('test_url')
|
self.test_url = c.get('test_url')
|
||||||
self.test_packages_url = c.get('test_packages_url')
|
self.test_packages_url = c.get('test_packages_url')
|
||||||
self.test_manifest = c.get('test_manifest')
|
self.test_manifest = c.get('test_manifest')
|
||||||
self.robocop_path = os.path.join(abs_dirs['abs_work_dir'], "robocop.apk")
|
self.robocop_path = os.path.join(abs_dirs['abs_work_dir'], "robocop.apk")
|
||||||
self.emulator = c.get('emulator')
|
|
||||||
self.test_suite = c.get('test_suite')
|
self.test_suite = c.get('test_suite')
|
||||||
self.this_chunk = c.get('this_chunk')
|
self.this_chunk = c.get('this_chunk')
|
||||||
self.total_chunks = c.get('total_chunks')
|
self.total_chunks = c.get('total_chunks')
|
||||||
|
@ -126,7 +126,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
self.test_suite = m.group(1)
|
self.test_suite = m.group(1)
|
||||||
if self.this_chunk is None:
|
if self.this_chunk is None:
|
||||||
self.this_chunk = m.group(2)
|
self.this_chunk = m.group(2)
|
||||||
self.sdk_level = None
|
|
||||||
self.xre_path = None
|
self.xre_path = None
|
||||||
self.device_serial = 'emulator-5554'
|
self.device_serial = 'emulator-5554'
|
||||||
self.log_raw_level = c.get('log_raw_level')
|
self.log_raw_level = c.get('log_raw_level')
|
||||||
|
@ -245,7 +244,7 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.warning("Extra kvm diagnostics failed: %s" % str(e))
|
self.warning("Extra kvm diagnostics failed: %s" % str(e))
|
||||||
|
|
||||||
command = ["emulator", "-avd", self.emulator["name"]]
|
command = ["emulator", "-avd", self.config["emulator_avd_name"]]
|
||||||
if "emulator_extra_args" in self.config:
|
if "emulator_extra_args" in self.config:
|
||||||
command += self.config["emulator_extra_args"].split()
|
command += self.config["emulator_extra_args"].split()
|
||||||
|
|
||||||
|
@ -286,54 +285,8 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
status = func()
|
status = func()
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def _run_with_timeout(self, timeout, cmd, quiet=False):
|
|
||||||
timeout_cmd = ['timeout', '%s' % timeout] + cmd
|
|
||||||
return self._run_proc(timeout_cmd, quiet=quiet)
|
|
||||||
|
|
||||||
def _run_adb_with_timeout(self, timeout, cmd, quiet=False):
|
|
||||||
cmd = [self.adb_path, '-s', self.emulator['device_id']] + cmd
|
|
||||||
return self._run_with_timeout(timeout, cmd, quiet)
|
|
||||||
|
|
||||||
def _run_proc(self, cmd, quiet=False):
|
|
||||||
self.info('Running %s' % subprocess.list2cmdline(cmd))
|
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
|
|
||||||
out, err = p.communicate()
|
|
||||||
if out and not quiet:
|
|
||||||
self.info('%s' % str(out.strip()))
|
|
||||||
if err and not quiet:
|
|
||||||
self.info('stderr: %s' % str(err.strip()))
|
|
||||||
return out, err
|
|
||||||
|
|
||||||
def _verify_adb(self):
|
|
||||||
self.info('Verifying adb connectivity')
|
|
||||||
self._run_with_timeout(180, [self.adb_path, 'wait-for-device'])
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _verify_adb_device(self):
|
|
||||||
out, _ = self._run_with_timeout(30, [self.adb_path, 'devices'])
|
|
||||||
if (self.emulator['device_id'] in out) and ("device" in out):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _is_boot_completed(self):
|
|
||||||
boot_cmd = ['shell', 'getprop', 'sys.boot_completed']
|
|
||||||
out, _ = self._run_adb_with_timeout(30, boot_cmd)
|
|
||||||
if out.strip() == '1':
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _verify_emulator(self):
|
def _verify_emulator(self):
|
||||||
adb_ok = self._verify_adb()
|
boot_ok = self._retry(30, 10, self.is_boot_completed, "Verify Android boot completed",
|
||||||
if not adb_ok:
|
|
||||||
self.warning('Unable to communicate with adb')
|
|
||||||
return False
|
|
||||||
adb_device_ok = self._retry(4, 30, self._verify_adb_device,
|
|
||||||
"Verify emulator visible to adb")
|
|
||||||
if not adb_device_ok:
|
|
||||||
self.warning('Unable to communicate with emulator via adb')
|
|
||||||
return False
|
|
||||||
self._restart_adbd()
|
|
||||||
boot_ok = self._retry(30, 10, self._is_boot_completed, "Verify Android boot completed",
|
|
||||||
max_time=330)
|
max_time=330)
|
||||||
if not boot_ok:
|
if not boot_ok:
|
||||||
self.warning('Unable to verify Android boot completion')
|
self.warning('Unable to verify Android boot completion')
|
||||||
|
@ -343,39 +296,16 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
def _verify_emulator_and_restart_on_fail(self):
|
def _verify_emulator_and_restart_on_fail(self):
|
||||||
emulator_ok = self._verify_emulator()
|
emulator_ok = self._verify_emulator()
|
||||||
if not emulator_ok:
|
if not emulator_ok:
|
||||||
self._screenshot("emulator-startup-screenshot-")
|
self.screenshot("emulator-startup-screenshot-")
|
||||||
self._kill_processes(self.config["emulator_process_name"])
|
self._kill_processes(self.config["emulator_process_name"])
|
||||||
self._run_proc(['ps', '-ef'])
|
subprocess.check_call(['ps', '-ef'])
|
||||||
# remove emulator tmp files
|
# remove emulator tmp files
|
||||||
for dir in glob.glob("/tmp/android-*"):
|
for dir in glob.glob("/tmp/android-*"):
|
||||||
self.rmtree(dir)
|
self.rmtree(dir)
|
||||||
self._restart_adbd()
|
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
self.emulator_proc = self._launch_emulator()
|
self.emulator_proc = self._launch_emulator()
|
||||||
return emulator_ok
|
return emulator_ok
|
||||||
|
|
||||||
def _install_target_apk(self):
|
|
||||||
install_ok = False
|
|
||||||
if int(self.sdk_level) >= 23:
|
|
||||||
cmd = ['install', '-r', '-g', self.installer_path]
|
|
||||||
else:
|
|
||||||
cmd = ['install', '-r', self.installer_path]
|
|
||||||
out, err = self._run_adb_with_timeout(300, cmd, True)
|
|
||||||
if 'Success' in out or 'Success' in err:
|
|
||||||
install_ok = True
|
|
||||||
return install_ok
|
|
||||||
|
|
||||||
def _install_robocop_apk(self):
|
|
||||||
install_ok = False
|
|
||||||
if int(self.sdk_level) >= 23:
|
|
||||||
cmd = ['install', '-r', '-g', self.robocop_path]
|
|
||||||
else:
|
|
||||||
cmd = ['install', '-r', self.robocop_path]
|
|
||||||
out, err = self._run_adb_with_timeout(300, cmd, True)
|
|
||||||
if 'Success' in out or 'Success' in err:
|
|
||||||
install_ok = True
|
|
||||||
return install_ok
|
|
||||||
|
|
||||||
def _kill_processes(self, process_name):
|
def _kill_processes(self, process_name):
|
||||||
p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE, bufsize=0)
|
p = subprocess.Popen(['ps', '-A'], stdout=subprocess.PIPE, bufsize=0)
|
||||||
out, err = p.communicate()
|
out, err = p.communicate()
|
||||||
|
@ -386,28 +316,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
self.info("Killing pid %d." % pid)
|
self.info("Killing pid %d." % pid)
|
||||||
os.kill(pid, signal.SIGKILL)
|
os.kill(pid, signal.SIGKILL)
|
||||||
|
|
||||||
def _restart_adbd(self):
|
|
||||||
self._run_with_timeout(30, [self.adb_path, 'kill-server'])
|
|
||||||
self._run_with_timeout(30, [self.adb_path, 'root'])
|
|
||||||
|
|
||||||
def _screenshot(self, prefix):
|
|
||||||
"""
|
|
||||||
Save a screenshot of the entire screen to the upload directory.
|
|
||||||
"""
|
|
||||||
dirs = self.query_abs_dirs()
|
|
||||||
utility = os.path.join(self.xre_path, "screentopng")
|
|
||||||
if not os.path.exists(utility):
|
|
||||||
self.warning("Unable to take screenshot: %s does not exist" % utility)
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
tmpfd, filename = tempfile.mkstemp(prefix=prefix, suffix='.png',
|
|
||||||
dir=dirs['abs_blob_upload_dir'])
|
|
||||||
os.close(tmpfd)
|
|
||||||
self.info("Taking screenshot with %s; saving to %s" % (utility, filename))
|
|
||||||
subprocess.call([utility, filename], env=self.query_env())
|
|
||||||
except OSError, err:
|
|
||||||
self.warning("Failed to take screenshot: %s" % err.strerror)
|
|
||||||
|
|
||||||
def _build_command(self):
|
def _build_command(self):
|
||||||
c = self.config
|
c = self.config
|
||||||
dirs = self.query_abs_dirs()
|
dirs = self.query_abs_dirs()
|
||||||
|
@ -497,43 +405,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
def _get_repo_url(self, path):
|
|
||||||
"""
|
|
||||||
Return a url for a file (typically a tooltool manifest) in this hg repo
|
|
||||||
and using this revision (or mozilla-central/default if repo/rev cannot
|
|
||||||
be determined).
|
|
||||||
|
|
||||||
:param path specifies the directory path to the file of interest.
|
|
||||||
"""
|
|
||||||
if 'GECKO_HEAD_REPOSITORY' in os.environ and 'GECKO_HEAD_REV' in os.environ:
|
|
||||||
# probably taskcluster
|
|
||||||
repo = os.environ['GECKO_HEAD_REPOSITORY']
|
|
||||||
revision = os.environ['GECKO_HEAD_REV']
|
|
||||||
else:
|
|
||||||
# something unexpected!
|
|
||||||
repo = 'https://hg.mozilla.org/mozilla-central'
|
|
||||||
revision = 'default'
|
|
||||||
self.warning('Unable to find repo/revision for manifest; '
|
|
||||||
'using mozilla-central/default')
|
|
||||||
url = '%s/raw-file/%s/%s' % (
|
|
||||||
repo,
|
|
||||||
revision,
|
|
||||||
path)
|
|
||||||
return url
|
|
||||||
|
|
||||||
def _tooltool_fetch(self, url, dir):
|
|
||||||
manifest_path = self.download_file(
|
|
||||||
url,
|
|
||||||
file_name='releng.manifest',
|
|
||||||
parent_dir=dir
|
|
||||||
)
|
|
||||||
if not os.path.exists(manifest_path):
|
|
||||||
self.fatal("Could not retrieve manifest needed to retrieve "
|
|
||||||
"artifacts from %s" % manifest_path)
|
|
||||||
cache = self.config.get("tooltool_cache", None)
|
|
||||||
if self.tooltool_fetch(manifest_path, output_dir=dir, cache=cache):
|
|
||||||
self.warning("Unable to download from tooltool: %s" % url)
|
|
||||||
|
|
||||||
def _install_emulator(self):
|
def _install_emulator(self):
|
||||||
dirs = self.query_abs_dirs()
|
dirs = self.query_abs_dirs()
|
||||||
if self.config.get('emulator_url'):
|
if self.config.get('emulator_url'):
|
||||||
|
@ -549,65 +420,6 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
else:
|
else:
|
||||||
self.warning("Cannot get emulator: configure emulator_url or emulator_manifest")
|
self.warning("Cannot get emulator: configure emulator_url or emulator_manifest")
|
||||||
|
|
||||||
def _dump_perf_info(self):
|
|
||||||
'''
|
|
||||||
Dump some host and emulator performance-related information
|
|
||||||
to an artifact file, to help understand why jobs run slowly
|
|
||||||
sometimes. This is hopefully a temporary diagnostic.
|
|
||||||
See bug 1321605.
|
|
||||||
'''
|
|
||||||
dir = self.query_abs_dirs()['abs_blob_upload_dir']
|
|
||||||
perf_path = os.path.join(dir, "android-performance.log")
|
|
||||||
with open(perf_path, "w") as f:
|
|
||||||
|
|
||||||
f.write('\n\nHost /proc/cpuinfo:\n')
|
|
||||||
out, _ = self._run_proc(['cat', '/proc/cpuinfo'], quiet=True)
|
|
||||||
f.write(out)
|
|
||||||
|
|
||||||
f.write('\n\nHost /proc/meminfo:\n')
|
|
||||||
out, _ = self._run_proc(['cat', '/proc/meminfo'], quiet=True)
|
|
||||||
f.write(out)
|
|
||||||
|
|
||||||
f.write('\n\nHost process list:\n')
|
|
||||||
out, _ = self._run_proc(['ps', '-ef'], quiet=True)
|
|
||||||
f.write(out)
|
|
||||||
|
|
||||||
f.write('\n\nHost netstat:\n')
|
|
||||||
out, _ = self._run_proc(['netstat', '-a', '-p', '-n', '-t', '-u'], quiet=True)
|
|
||||||
f.write(out)
|
|
||||||
|
|
||||||
f.write('\n\nEmulator /proc/cpuinfo:\n')
|
|
||||||
cmd = ['shell', 'cat', '/proc/cpuinfo']
|
|
||||||
out, _ = self._run_adb_with_timeout(30, cmd, quiet=True)
|
|
||||||
f.write(out)
|
|
||||||
cpuinfo = out
|
|
||||||
|
|
||||||
f.write('\n\nEmulator /proc/meminfo:\n')
|
|
||||||
cmd = ['shell', 'cat', '/proc/meminfo']
|
|
||||||
out, _ = self._run_adb_with_timeout(30, cmd, quiet=True)
|
|
||||||
f.write(out)
|
|
||||||
|
|
||||||
f.write('\n\nEmulator process list:\n')
|
|
||||||
cmd = ['shell', 'ps']
|
|
||||||
out, _ = self._run_adb_with_timeout(30, cmd, quiet=True)
|
|
||||||
f.write(out)
|
|
||||||
|
|
||||||
# Search android cpuinfo for "BogoMIPS"; if found and < 250, retry
|
|
||||||
# this task, in hopes of getting a higher-powered environment.
|
|
||||||
# (Carry on silently if BogoMIPS is not found -- this may vary by
|
|
||||||
# Android implementation -- no big deal.)
|
|
||||||
# See bug 1321605: Sometimes the emulator is really slow, and
|
|
||||||
# low bogomips can be a good predictor of that condition.
|
|
||||||
for line in cpuinfo.split('\n'):
|
|
||||||
m = re.match("BogoMIPS.*: (\d*)", line)
|
|
||||||
if m:
|
|
||||||
bogomips = int(m.group(1))
|
|
||||||
if bogomips < 250:
|
|
||||||
self.fatal('INFRA-ERROR: insufficient Android bogomips (%d < 250)' % bogomips,
|
|
||||||
EXIT_STATUS_DICT[TBPL_RETRY])
|
|
||||||
self.info("Found Android bogomips: %d" % bogomips)
|
|
||||||
break
|
|
||||||
|
|
||||||
def _query_suites(self):
|
def _query_suites(self):
|
||||||
if self.test_suite:
|
if self.test_suite:
|
||||||
return [(self.test_suite, self.test_suite)]
|
return [(self.test_suite, self.test_suite)]
|
||||||
|
@ -709,14 +521,13 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
|
|
||||||
if not os.path.isfile(self.adb_path):
|
if not os.path.isfile(self.adb_path):
|
||||||
self.fatal("The adb binary '%s' is not a valid file!" % self.adb_path)
|
self.fatal("The adb binary '%s' is not a valid file!" % self.adb_path)
|
||||||
self._restart_adbd()
|
|
||||||
|
|
||||||
if not self.config.get("developer_mode"):
|
if not self.config.get("developer_mode"):
|
||||||
self._kill_processes("xpcshell")
|
self._kill_processes("xpcshell")
|
||||||
|
|
||||||
self.emulator_proc = self._launch_emulator()
|
self.emulator_proc = self._launch_emulator()
|
||||||
|
|
||||||
def verify_emulator(self):
|
def verify_device(self):
|
||||||
'''
|
'''
|
||||||
Check to see if the emulator can be contacted via adb.
|
Check to see if the emulator can be contacted via adb.
|
||||||
If any communication attempt fails, kill the emulator, re-launch, and re-check.
|
If any communication attempt fails, kill the emulator, re-launch, and re-check.
|
||||||
|
@ -727,24 +538,14 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
if not emulator_ok:
|
if not emulator_ok:
|
||||||
self.fatal('INFRA-ERROR: Unable to start emulator after %d attempts' % max_restarts,
|
self.fatal('INFRA-ERROR: Unable to start emulator after %d attempts' % max_restarts,
|
||||||
EXIT_STATUS_DICT[TBPL_RETRY])
|
EXIT_STATUS_DICT[TBPL_RETRY])
|
||||||
self._dump_perf_info()
|
self.dump_perf_info()
|
||||||
# Start logcat for the emulator. The adb process runs until the
|
self.logcat_start()
|
||||||
# corresponding emulator is killed. Output is written directly to
|
# Get a post-boot device process list for diagnostics
|
||||||
# the upload directory so that it is uploaded automatically
|
self.info(self.shell_output('ps'))
|
||||||
# at the end of the job.
|
|
||||||
logcat_filename = 'logcat-%s.log' % self.emulator["device_id"]
|
|
||||||
logcat_path = os.path.join(self.abs_dirs['abs_blob_upload_dir'], logcat_filename)
|
|
||||||
logcat_cmd = '%s -s %s logcat -v threadtime Trace:S StrictMode:S '\
|
|
||||||
' ExchangeService:S > %s &' % (self.adb_path, self.emulator["device_id"], logcat_path)
|
|
||||||
self.info(logcat_cmd)
|
|
||||||
os.system(logcat_cmd)
|
|
||||||
# Get a post-boot emulator process list for diagnostics
|
|
||||||
ps_cmd = ['shell', 'ps']
|
|
||||||
self._run_adb_with_timeout(30, ps_cmd)
|
|
||||||
|
|
||||||
def download_and_extract(self):
|
def download_and_extract(self):
|
||||||
"""
|
"""
|
||||||
Download and extract fennec APK, tests, host utils, and robocop (if required).
|
Download and extract fennec APK, tests.zip, host utils, and robocop (if required).
|
||||||
"""
|
"""
|
||||||
super(AndroidEmulatorTest, self).download_and_extract(
|
super(AndroidEmulatorTest, self).download_and_extract(
|
||||||
suite_categories=self._query_suite_categories())
|
suite_categories=self._query_suite_categories())
|
||||||
|
@ -768,36 +569,20 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
"""
|
"""
|
||||||
Install APKs on the emulator
|
Install APKs on the device.
|
||||||
"""
|
"""
|
||||||
install_needed = (not self.test_suite) or \
|
install_needed = (not self.test_suite) or \
|
||||||
self.config["suite_definitions"][self.test_suite].get("install")
|
self.config["suite_definitions"][self.test_suite].get("install")
|
||||||
if install_needed is False:
|
if install_needed is False:
|
||||||
self.info("Skipping apk installation for %s" % self.test_suite)
|
self.info("Skipping apk installation for %s" % self.test_suite)
|
||||||
return
|
return
|
||||||
|
|
||||||
assert self.installer_path is not None, \
|
assert self.installer_path is not None, \
|
||||||
"Either add installer_path to the config or use --installer-path."
|
"Either add installer_path to the config or use --installer-path."
|
||||||
|
self.install_apk(self.installer_path)
|
||||||
cmd = ['shell', 'getprop', 'ro.build.version.sdk']
|
|
||||||
self.sdk_level, _ = self._run_adb_with_timeout(30, cmd)
|
|
||||||
|
|
||||||
# Install Fennec
|
|
||||||
install_ok = self._retry(3, 30, self._install_target_apk, "Install app APK")
|
|
||||||
if not install_ok:
|
|
||||||
self.fatal('INFRA-ERROR: Failed to install %s on %s' %
|
|
||||||
(self.installer_path, self.emulator["name"]),
|
|
||||||
EXIT_STATUS_DICT[TBPL_RETRY])
|
|
||||||
|
|
||||||
# Install Robocop if required
|
# Install Robocop if required
|
||||||
if self.test_suite and self.test_suite.startswith('robocop'):
|
if self.test_suite and self.test_suite.startswith('robocop'):
|
||||||
install_ok = self._retry(3, 30, self._install_robocop_apk, "Install Robocop APK")
|
self.install_apk(self.robocop_path)
|
||||||
if not install_ok:
|
self.info("Finished installing apps for %s" % self.device_serial)
|
||||||
self.fatal('INFRA-ERROR: Failed to install %s on %s' %
|
|
||||||
(self.robocop_path, self.emulator["name"]),
|
|
||||||
EXIT_STATUS_DICT[TBPL_RETRY])
|
|
||||||
|
|
||||||
self.info("Finished installing apps for %s" % self.emulator["name"])
|
|
||||||
|
|
||||||
def run_tests(self):
|
def run_tests(self):
|
||||||
"""
|
"""
|
||||||
|
@ -814,7 +599,11 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
|
|
||||||
cmd = self._build_command()
|
cmd = self._build_command()
|
||||||
|
|
||||||
cwd = self._query_tests_dir(self.test_suite)
|
try:
|
||||||
|
cwd = self._query_tests_dir(self.test_suite)
|
||||||
|
except Exception:
|
||||||
|
self.fatal("Don't know how to run --test-suite '%s'!" % self.test_suite)
|
||||||
|
|
||||||
env = self.query_env()
|
env = self.query_env()
|
||||||
if minidump:
|
if minidump:
|
||||||
env['MINIDUMP_STACKWALK'] = minidump
|
env['MINIDUMP_STACKWALK'] = minidump
|
||||||
|
@ -867,13 +656,14 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
||||||
(suite_category, suite, tbpl_status), level=log_level)
|
(suite_category, suite, tbpl_status), level=log_level)
|
||||||
|
|
||||||
@PostScriptAction('run-tests')
|
@PostScriptAction('run-tests')
|
||||||
def stop_emulator(self, action, success=None):
|
def stop_device(self, action, success=None):
|
||||||
'''
|
'''
|
||||||
Make sure that the emulator has been stopped
|
Make sure that the emulator has been stopped
|
||||||
'''
|
'''
|
||||||
|
self.logcat_stop()
|
||||||
self._kill_processes(self.config["emulator_process_name"])
|
self._kill_processes(self.config["emulator_process_name"])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
emulatorTest = AndroidEmulatorTest()
|
test = AndroidEmulatorTest()
|
||||||
emulatorTest.run_and_exit()
|
test.run_and_exit()
|
||||||
|
|
Загрузка…
Ссылка в новой задаче