Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

bug 650890 - port remote talos to mozharness. p=callek r=aki

  • Loading branch information...
commit 493b8475b424c6851838b7d1904f4edc9e4d74f4 1 parent 5c82416
Justin Wood authored
2  LICENSE
View
@@ -35,7 +35,7 @@ Mozilla Public License Version 2.0
means any form of the work other than Source Code Form.
1.7. "Larger Work"
- means a work that combines Covered Software with other material, in
+ means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
21 configs/users/aki/peptest.py
View
@@ -0,0 +1,21 @@
+import os
+
+config = {
+ # mozharness script options
+ "log_name": "pep",
+ "base_work_dir": os.path.join(os.getcwd(), "peptest"),
+ "pypi_url": "http://people.mozilla.com/~jhammel/pypi",
+ "test_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/firefox-15.0a1.en-US.mac.tests.zip",
+
+ # peptest options
+ "installer_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/firefox-15.0a1.en-US.mac.dmg",
+ "app": "firefox",
+ "test_manifest": "tests/firefox/firefox_all.ini", # this is relative to the test folder specified by test_url
+ "profile_path": None,
+ "timeout": 60,
+ "server_path": None,
+ "server_port": None,
+ "tracer_threshold": 50,
+ "tracer_interval": 10,
+ "symbols_path": None,
+}
40 configs/users/aki/tablet1.py
View
@@ -0,0 +1,40 @@
+config = {
+ "log_name": "talos",
+ "base_work_dir": "/src/talosrunner/tablet_bwd",
+
+ "installer_url": "http://ftp.mozilla.org/pub/mozilla.org/mobile/nightly/latest-mozilla-central-android/fennec-15.0a1.en-US.android-arm.apk",
+ "pypi_url": "http://people.mozilla.com/~jhammel/pypi/",
+ "device_name": "aki_tablet",
+ "device_package_name": "org.mozilla.fennec",
+
+ # set graph_server to "''" to not use a graph_server
+# "graph_server": "graphs-stage.mozilla.org",
+ "graph_server": "''",
+
+ "results_link": "/server/collect.cgi",
+ "talos_suites": ["tsvg"],
+ "talos_config_file": "remote.config",
+
+ # this needs to be set to either your_IP:8000, or an existing webserver
+ # that serves talos.
+ "talos_webserver": "10.251.25.44:8000",
+
+ # Set this to start a webserver automatically
+ "start_python_webserver": True,
+
+ # adb or sut
+ "device_protocol": "adb",
+
+ # set this for adb-over-ip or sut.
+ "device_ip": "10.251.28.128",
+
+ # setting this to tegra250 will add tegra-specific behavior
+ "device_type": "non-tegra",
+
+ # enable_automation will run steps that may be undesirable for the
+ # average user.
+ "enable_automation": True,
+
+# "actions": ["check-device"],
+# "no_actions": ["preclean", "pull", "download", "unpack"],
+}
44 configs/users/aki/tegra1.py
View
@@ -0,0 +1,44 @@
+config = {
+ "log_name": "talos",
+ "base_work_dir": "/src/talosrunner/tegra_bwd",
+
+ "installer_url": "http://ftp.mozilla.org/pub/mozilla.org/mobile/nightly/latest-mozilla-central-android/fennec-15.0a1.en-US.android-arm.apk",
+ "pypi_url": "http://people.mozilla.com/~jhammel/pypi/",
+ "device_name": "tegra-031",
+ "device_package_name": "org.mozilla.fennec",
+ "talos_device_name": "tegra-031",
+
+ # set graph_server to a real graph server if you want to publish your
+ # results (the device needs to be in the database already or you'll
+ # get errors)
+ "graph_server": "graphs-stage.mozilla.org",
+
+ "results_link": "/server/collect.cgi",
+ "talos_suites": ["tsvg"],
+ "talos_config_file": "remote.config",
+
+ # this needs to be set to either your_IP:8000, or an existing webserver
+ # that serves talos.
+# "talos_webserver": "10.251.25.44:8000",
+ "talos_webserver": "bm-remote.build.mozilla.org",
+
+ # adb or sut
+ "device_protocol": "sut",
+
+ # set this to >0 if you want devicemanager output.
+ # beware, this will send binary characters to your terminal
+# "devicemanager_debug_level": 2,
+
+ # set this for adb-over-ip or sut.
+ "device_ip": "10.250.49.18",
+
+ # setting this to tegra250 will add tegra-specific behavior
+ "device_type": "tegra250",
+
+ # enable_automation will run steps that may be undesirable for the
+ # average user.
+ "enable_automation": True,
+
+# "actions": ["check-device"],
+# "no_actions": ["preclean", "pull", "download", "unpack"],
+}
53 configs/users/callek/tegra1-foopy.py
View
@@ -0,0 +1,53 @@
+config = {
+ "log_name": "talos",
+ #"base_work_dir": "",
+
+ "installer_url": "http://ftp.mozilla.org/pub/mozilla.org/mobile/nightly/latest-mozilla-central-android/en-US/fennec-16.0a1.en-US.android-arm.apk",
+ "repository": "http://hg.mozilla.org/mozilla-central",
+# "pypi_url": "http://people.mozilla.com/~jwood/pypi/",
+# "pywin32_url": "http://people.mozilla.org/~jwood/pypi/pywin32-216.win32-py2.6.exe",
+ "device_name": "tegra-224",
+ "device_package_name": "org.mozilla.fennec",
+ "talos_device_name": "tegra-224",
+# "virtualenv_modules": ["pywin32", "talos"],
+# "exes": { "easy_install": ['d:\\Sources\\mozharness\\build\\venv\\Scripts\\python.exe',
+# 'd:\\Sources\\mozharness\\build\\venv\\scripts\\easy_install-2.6-script.py'], },
+
+ # set graph_server to a real graph server if you want to publish your
+ # results (the device needs to be in the database already or you'll
+ # get errors)
+ "graph_server": "graphs.allizom.org",
+
+ "results_link": "/server/collect.cgi",
+ "talos_suites": ["tsvg"],
+ "tests": ["tsvg"],
+# "talos_config_file": "venv/Lib/site-packages/talos/remote.config",
+ "talos_config_file": "venv/lib/python2.6/site-packages/talos/remote.config",
+
+ # this needs to be set to either your_IP:8000, or an existing webserver
+ # that serves talos.
+# "talos_webserver": "10.251.25.44:8000",
+ "talos_webserver": "bm-remote.build.mozilla.org",
+ "talos_branch": "MobileTest",
+ "disable_chrome": True,
+
+ # adb or sut
+ "device_protocol": "sut",
+
+ # set this to >0 if you want devicemanager output.
+ # beware, this will send binary characters to your terminal
+# "devicemanager_debug_level": 2,
+
+ # set this for adb-over-ip or sut.
+ "device_ip": "10.250.51.64",
+
+ # setting this to tegra250 will add tegra-specific behavior
+ "device_type": "tegra250",
+
+ # enable_automation will run steps that may be undesirable for the
+ # average user.
+ "enable_automation": True,
+
+# "actions": ["check-device"],
+# "no_actions": ["preclean", "pull", "download", "unpack"],
+}
52 configs/users/callek/tegra1.py
View
@@ -0,0 +1,52 @@
+config = {
+ "log_name": "talos",
+ #"base_work_dir": "",
+
+ "installer_url": "http://ftp.mozilla.org/pub/mozilla.org/mobile/nightly/latest-mozilla-central-android/en-US/fennec-16.0a1.en-US.android-arm.apk",
+ "repository": "http://hg.mozilla.org/mozilla-central",
+ "pypi_url": "http://people.mozilla.com/~jwood/pypi/",
+ "pywin32_url": "http://people.mozilla.org/~jwood/pypi/pywin32-216.win32-py2.6.exe",
+ "device_name": "tegra-224",
+ "device_package_name": "org.mozilla.fennec",
+ "talos_device_name": "tegra-224",
+ "virtualenv_modules": ["pywin32", "talos"],
+ "exes": { "easy_install": ['d:\\Sources\\mozharness\\build\\venv\\Scripts\\python.exe',
+ 'd:\\Sources\\mozharness\\build\\venv\\scripts\\easy_install-2.6-script.py'], },
+
+ # set graph_server to a real graph server if you want to publish your
+ # results (the device needs to be in the database already or you'll
+ # get errors)
+ "graph_server": "graphs.allizom.org",
+
+ "results_link": "/server/collect.cgi",
+ "talos_suites": ["tsvg"],
+ "tests": ["tsvg"],
+ "talos_config_file": "venv/Lib/site-packages/talos/remote.config",
+
+ # this needs to be set to either your_IP:8000, or an existing webserver
+ # that serves talos.
+# "talos_webserver": "10.251.25.44:8000",
+ "talos_webserver": "bm-remote.build.mozilla.org",
+ "talos_branch": "MobileTest",
+ "disable_chrome": True,
+
+ # adb or sut
+ "device_protocol": "sut",
+
+ # set this to >0 if you want devicemanager output.
+ # beware, this will send binary characters to your terminal
+# "devicemanager_debug_level": 2,
+
+ # set this for adb-over-ip or sut.
+ "device_ip": "10.250.51.64",
+
+ # setting this to tegra250 will add tegra-specific behavior
+ "device_type": "tegra250",
+
+ # enable_automation will run steps that may be undesirable for the
+ # average user.
+ "enable_automation": True,
+
+# "actions": ["check-device"],
+# "no_actions": ["preclean", "pull", "download", "unpack"],
+}
7 mozharness/base/errors.py
View
@@ -88,6 +88,13 @@ class VCSException(Exception):
{'substr': r'''Warning: ''', 'level': WARNING},
]
+ADBErrorList = BaseErrorList + [
+ {'substr': r'''INSTALL_FAILED_INSUFFICIENT_STORAGE''', 'level': ERROR,},
+ {'substr': r'''Android Debug Bridge version''', 'level': ERROR,},
+ {'substr': r'''error: protocol fault''', 'level': ERROR,},
+ {'substr': r'''unable to connect to ''', 'level': ERROR,},
+]
+
JarsignerErrorList = [{
'substr': r'''command not found''',
'level': FATAL
57 mozharness/base/script.py
View
@@ -6,11 +6,8 @@
# ***** END LICENSE BLOCK *****
"""Generic script objects.
-TODO: The various mixins assume that they're used by BaseScript.
-Either every child object will need self.config, or these need some
-work.
-
-TODO: The mixin names kind of need work too?
+script.py, along with config.py and log.py, represents the core of
+mozharness.
"""
import codecs
@@ -369,6 +366,7 @@ def run_command(self, command, cwd=None, error_list=None, parse_at_end=False,
{'regex': re.compile('^Error:'), level=ERROR, contextLines='5:5'},
{'substr': 'THE WORLD IS ENDING', level=FATAL, contextLines='20:'}
]
+ (context_lines isn't written yet)
"""
if error_list is None:
error_list = []
@@ -418,7 +416,8 @@ def run_command(self, command, cwd=None, error_list=None, parse_at_end=False,
def get_output_from_command(self, command, cwd=None,
halt_on_failure=False, env=None,
- silent=False, tmpfile_base_path='tmpfile',
+ silent=False, log_level=INFO,
+ tmpfile_base_path='tmpfile',
return_type='output', save_tmpfiles=False,
throw_exception=False):
"""Similar to run_command, but where run_command is an
@@ -441,7 +440,7 @@ def get_output_from_command(self, command, cwd=None,
level = FATAL
self.log("Can't run command %s in non-existent directory %s!" % \
(command, cwd), level=level)
- return -1
+ return None
self.info("Getting output from command: %s in %s" % (command, cwd))
else:
self.info("Getting output from command: %s" % command)
@@ -463,7 +462,7 @@ def get_output_from_command(self, command, cwd=None,
level = FATAL
self.log("Can't open %s for writing!" % tmp_stdout_filename + \
self.dump_exception(), level=level)
- return -1
+ return None
try:
tmp_stderr = open(tmp_stderr_filename, 'w')
except IOError:
@@ -472,7 +471,7 @@ def get_output_from_command(self, command, cwd=None,
level = FATAL
self.log("Can't open %s for writing!" % tmp_stderr_filename + \
self.dump_exception(), level=level)
- return -1
+ return None
shell = True
if isinstance(command, list):
shell = False
@@ -488,13 +487,13 @@ def get_output_from_command(self, command, cwd=None,
output = self.read_from_file(tmp_stdout_filename,
verbose=False)
if not silent:
- self.info("Output received:")
+ self.log("Output received:", level=log_level)
output_lines = output.rstrip().splitlines()
for line in output_lines:
if not line or line.isspace():
continue
line = line.decode("utf-8")
- self.info(' %s' % line)
+ self.log(' %s' % line, level=log_level)
output = '\n'.join(output_lines)
if os.path.exists(tmp_stderr_filename) and os.path.getsize(tmp_stderr_filename):
return_level = ERROR
@@ -563,12 +562,20 @@ def __init__(self, config_options=None, default_log_level="info", **kwargs):
self.info("Run as %s" % rw_config.command_line)
def _pre_config_lock(self, rw_config):
+ """This empty method can allow for config checking and manipulation
+ before the config lock, when overridden in scripts.
+ """
pass
def _config_lock(self):
+ """After this point, the config is locked and should not be
+ manipulated (based on mozharness.base.config.ReadOnlyDict)
+ """
self.config.lock()
def _possibly_run_method(self, method_name, error_if_missing=False):
+ """This is here for run().
+ """
if hasattr(self, method_name) and callable(getattr(self, method_name)):
return getattr(self, method_name)()
elif error_if_missing:
@@ -611,7 +618,8 @@ def run(self):
self.copy_to_upload_dir(os.path.join(dirs['abs_log_dir'], log_file),
dest=os.path.join('logs', log_file),
short_desc='%s log' % log_name,
- long_desc='%s log' % log_name)
+ long_desc='%s log' % log_name,
+ rotate=True)
sys.exit(self.return_code)
def clobber(self):
@@ -622,6 +630,16 @@ def clobber(self):
self.rmtree(dirs['abs_work_dir'])
def query_abs_dirs(self):
+ """We want to be able to determine where all the important things
+ are. Absolute paths lend themselves well to this, though I wouldn't
+ be surprised if this causes some issues somewhere.
+
+ This should be overridden in any script that has additional dirs
+ to query.
+
+ The query_* methods tend to set self.VAR variables as their
+ runtime cache.
+ """
if self.abs_dirs:
return self.abs_dirs
c = self.config
@@ -633,6 +651,8 @@ def query_abs_dirs(self):
return self.abs_dirs
def dump_config(self, file_path=None):
+ """Dump self.config to localconfig.json
+ """
dirs = self.query_abs_dirs()
if not file_path:
file_path = os.path.join(dirs['abs_log_dir'], "localconfig.json")
@@ -673,6 +693,11 @@ def action_message(self, message):
self.info("#####")
def summary(self):
+ """Print out all the summary lines added via add_summary()
+ throughout the script.
+
+ I'd like to revisit how to do this in a prettier fashion.
+ """
self.action_message("%s summary:" % self.__class__.__name__)
if self.summary_list:
for item in self.summary_list:
@@ -741,16 +766,16 @@ def copy_to_upload_dir(self, target, dest=None, short_desc="unknown",
return -1
if rotate:
# Probably a better way to do this
- oldest_backup = None
+ oldest_backup = 0
backup_regex = re.compile("^%s\.(\d+)$" % dest_file)
for filename in os.listdir(dest_dir):
r = backup_regex.match(filename)
- if r and r.groups()[0] > oldest_backup:
- oldest_backup = r.groups()[0]
+ if r and int(r.groups()[0]) > oldest_backup:
+ oldest_backup = int(r.groups()[0])
for backup_num in range(oldest_backup, 0, -1):
# TODO more error checking?
if backup_num >= max_backups:
- self.rmtree(os.path.join(dest_dir, dest_file, backup_num),
+ self.rmtree(os.path.join(dest_dir, dest_file, str(backup_num)),
log_level=log_level)
else:
self.move(os.path.join(dest_dir, dest_file, '.%d' % backup_num),
7 mozharness/base/vcs/mercurial.py
View
@@ -5,6 +5,9 @@
# You can obtain one at http://mozilla.org/MPL/2.0/.
# ***** END LICENSE BLOCK *****
"""Mercurial VCS support.
+
+Largely copied/ported from
+http://hg.mozilla.org/build/tools/file/cf265ea8fb5e/lib/python/util/hg.py .
"""
import os
@@ -365,7 +368,7 @@ def _ensure_shared_repo_and_revision(self, share_base):
try:
self.pull(repo, shared_repo)
except subprocess.CalledProcessError:
- self.warning("Error pulling changes into %s form %s; clobbering" % (shared_repo, repo))
+ self.warning("Error pulling changes into %s from %s; clobbering" % (shared_repo, repo))
self.dump_exception(level='debug')
self.clone(repo, shared_repo)
else:
@@ -447,7 +450,7 @@ def ensure_repo_and_revision(self):
self.pull(repo, dest)
return self.update(dest, branch=branch, revision=revision)
except subprocess.CalledProcessError:
- self.warning("Error pulling changes into %s form %s; clobbering" % (dest, repo))
+ self.warning("Error pulling changes into %s from %s; clobbering" % (dest, repo))
self.dump_exception(level='debug')
self.rmtree(dest)
elif not os.path.exists(os.path.dirname(dest)):
7 mozharness/base/vcs/vcsbase.py
View
@@ -24,7 +24,12 @@
# VCSMixin {{{1
class VCSMixin(object):
+ """Basic VCS methods that are vcs-agnostic.
+ The vcs_class handles all the vcs-specific tasks.
+ """
def vcs_checkout(self, vcs=None, **kwargs):
+ """ Check out a single repo.
+ """
c = self.config
if not vcs:
if c.get('default_vcs'):
@@ -56,6 +61,8 @@ def vcs_checkout(self, vcs=None, **kwargs):
def vcs_checkout_repos(self, repo_list, parent_dir=None,
tag_override=None, **kwargs):
+ """Check out a list of repos.
+ """
orig_dir = os.getcwd()
c = self.config
if not parent_dir:
708 mozharness/mozilla/testing/device.py
View
@@ -0,0 +1,708 @@
+#!/usr/bin/env python
+# ***** BEGIN LICENSE BLOCK *****
+# 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/.
+# ***** END LICENSE BLOCK *****
+'''Interact with a device via ADB or SUT.
+
+This code is largely from
+http://hg.mozilla.org/build/tools/file/default/sut_tools
+'''
+
+import datetime
+import os
+import re
+import subprocess
+import sys
+import time
+
+from mozharness.base.errors import ADBErrorList
+from mozharness.base.log import LogMixin, DEBUG, FATAL
+from mozharness.base.script import ShellMixin, OSMixin
+
+
+
+# Device flags
+DEVICE_UNREACHABLE = 0x01
+DEVICE_NOT_CONNECTED = 0x02
+DEVICE_MISSING_SDCARD = 0x03
+DEVICE_HOST_ERROR = 0x04
+# DEVICE_UNRECOVERABLE_ERROR?
+DEVICE_NOT_REBOOTED = 0x05
+DEVICE_CANT_REMOVE_DEVROOT = 0x06
+DEVICE_CANT_REMOVE_ETC_HOSTS = 0x07
+DEVICE_CANT_SET_TIME = 0x08
+
+
+
+class DeviceException(Exception):
+ pass
+
+
+
+# BaseDeviceHandler {{{1
+class BaseDeviceHandler(ShellMixin, OSMixin, LogMixin):
+ device_id = None
+ device_root = None
+ default_port = None
+ device_flags = []
+ def __init__(self, log_obj=None, config=None, script_obj=None):
+ super(BaseDeviceHandler, self).__init__()
+ self.config = config
+ self.log_obj = log_obj
+ self.script_obj = script_obj
+
+ def add_device_flag(self, flag):
+ if flag not in self.device_flags:
+ self.device_flags.append(flag)
+
+ def query_device_id(self):
+ if self.device_id:
+ return self.device_id
+ c = self.config
+ device_id = None
+ if c.get('device_id'):
+ device_id = c['device_id']
+ elif c.get('device_ip'):
+ device_id = "%s:%s" % (c['device_ip'],
+ c.get('device_port', self.default_port))
+ self.device_id = device_id
+ return self.device_id
+
+ def query_download_filename(self, file_id=None):
+ pass
+
+ def ping_device(self):
+ pass
+
+ def check_device(self):
+ pass
+
+ def cleanup_device(self, reboot=False):
+ pass
+
+ def reboot_device(self):
+ pass
+
+ def query_device_root(self):
+ pass
+
+ def wait_for_device(self, interval=60, max_attempts=20):
+ pass
+
+ def install_app(self, file_path):
+ pass
+
+
+
+# ADBDeviceHandler {{{1
+class ADBDeviceHandler(BaseDeviceHandler):
+ def __init__(self, **kwargs):
+ super(ADBDeviceHandler, self).__init__(**kwargs)
+ self.default_port = 5555
+
+ def query_device_exe(self, exe_name):
+ return self.query_exe(exe_name, exe_dict="device_exes")
+
+ def _query_config_device_id(self):
+ return BaseDeviceHandler.query_device_id(self)
+
+ def query_device_id(self, auto_connect=True):
+ if self.device_id:
+ return self.device_id
+ device_id = self._query_config_device_id()
+ if device_id:
+ if auto_connect:
+ self.ping_device(auto_connect=True)
+ else:
+ self.info("Trying to find device...")
+ devices = self._query_attached_devices()
+ if not devices:
+ self.add_device_flag(DEVICE_NOT_CONNECTED)
+ self.fatal("No device connected via adb!\nUse 'adb connect' or specify a device_id or device_ip in config!")
+ elif len(devices) > 1:
+ self.warning("""More than one device detected; specify 'device_id' or\n'device_ip' to target a specific device!""")
+ device_id = devices[0]
+ self.info("Found %s." % device_id)
+ self.device_id = device_id
+ return self.device_id
+
+ # maintenance {{{2
+ def ping_device(self, auto_connect=False, silent=False):
+ if auto_connect and not self._query_attached_devices():
+ self.connect_device()
+ if not silent:
+ self.info("Determining device connectivity over adb...")
+ device_id = self.query_device_id()
+ adb = self.query_exe('adb')
+ uptime = self.query_device_exe('uptime')
+ output = self.get_output_from_command([adb, "-s", device_id,
+ "shell", uptime],
+ silent=silent)
+ if str(output).startswith("up time:"):
+ if not silent:
+ self.info("Found %s." % device_id)
+ return True
+ elif auto_connect:
+ # TODO retry?
+ self.connect_device()
+ return self.ping_device()
+ else:
+ if not silent:
+ self.error("Can't find a device.")
+ return False
+
+ def _query_attached_devices(self):
+ devices = []
+ adb = self.query_exe('adb')
+ output = self.get_output_from_command([adb, "devices"])
+ starting_list = False
+ if output is None:
+ self.add_device_flag(DEVICE_HOST_ERROR)
+ self.fatal("Can't get output from 'adb devices'; install the Android SDK!")
+ for line in output:
+ if 'adb: command not found' in line:
+ self.add_device_flag(DEVICE_HOST_ERROR)
+ self.fatal("Can't find adb; install the Android SDK!")
+ if line.startswith("* daemon"):
+ continue
+ if line.startswith("List of devices"):
+ starting_list = True
+ continue
+ # TODO somehow otherwise determine whether this is an actual
+ # device?
+ if starting_list:
+ devices.append(re.split('\s+', line)[0])
+ return devices
+
+ def connect_device(self):
+ self.info("Connecting device...")
+ adb = self.query_exe('adb')
+ cmd = [adb, "connect"]
+ device_id = self._query_config_device_id()
+ if device_id:
+ devices = self._query_attached_devices()
+ if device_id in devices:
+ # TODO is this the right behavior?
+ self.disconnect_device()
+ cmd.append(device_id)
+ # TODO error check
+ self.run_command(cmd, error_list=ADBErrorList)
+
+ def disconnect_device(self):
+ self.info("Disconnecting device...")
+ device_id = self.query_device_id()
+ if device_id:
+ adb = self.query_exe('adb')
+ # TODO error check
+ self.run_command([adb, "-s", device_id,
+ "disconnect"],
+ error_list=ADBErrorList)
+ else:
+ self.info("No device found.")
+
+ def check_device(self):
+ if not self.ping_device(auto_connect=True):
+ self.add_device_flag(DEVICE_NOT_CONNECTED)
+ self.fatal("Can't find device!")
+ if self.query_device_root() is None:
+ self.add_device_flag(DEVICE_NOT_CONNECTED)
+ self.fatal("Can't connect to device!")
+
+ def reboot_device(self):
+ if not self.ping_device(auto_connect=True):
+ self.add_device_flag(DEVICE_NOT_REBOOTED)
+ self.error("Can't reboot disconnected device!")
+ return False
+ device_id = self.query_device_id()
+ self.info("Rebooting device...")
+ adb = self.query_exe('adb')
+ cmd = [adb, "-s", device_id, "reboot"]
+ self.info("Running command (in the background): %s" % cmd)
+ # This won't exit until much later, but we don't need to wait.
+ # However, some error checking would be good.
+ # TODO are we doing something with p?
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ time.sleep(10)
+ self.disconnect_device()
+ status = False
+ try:
+ self.wait_for_device()
+ status = True
+ except DeviceException:
+ self.error("Can't reconnect to device!")
+ return status
+
+ def cleanup_device(self, reboot=False):
+ self.info("Cleaning up device.")
+ c = self.config
+ device_id = self.query_device_id()
+ status = self.remove_device_root()
+ if not status:
+ self.add_device_flag(DEVICE_CANT_REMOVE_DEVROOT)
+ self.fatal("Can't remove device root!")
+ if c.get("enable_automation"):
+ self.remove_etc_hosts()
+ if c.get("device_package_name"):
+ adb = self.query_exe('adb')
+ killall = self.query_device_exe('killall')
+ self.run_command([adb, "-s", device_id, "shell",
+ killall, c["device_package_name"]],
+ error_list=ADBErrorList)
+ self.uninstall_app(c['device_package_name'])
+ if reboot:
+ self.reboot_device()
+
+ # device calls {{{2
+ def query_device_root(self, silent=False):
+ if self.device_root:
+ return self.device_root
+ device_root = None
+ device_id = self.query_device_id()
+ adb = self.query_exe('adb')
+ output = self.get_output_from_command("%s -s %s shell df" % (adb, device_id),
+ silent=silent)
+ # TODO this assumes we're connected; error checking?
+ if output is None or ' not found' in str(output):
+ self.error("Can't get output from 'adb shell df'!\n%s" % output)
+ return None
+ if "/mnt/sdcard" in output:
+ device_root = "/mnt/sdcard/tests"
+ else:
+ device_root = "/data/local/tmp/tests"
+ if not silent:
+ self.info("Device root is %s" % str(device_root))
+ self.device_root = device_root
+ return self.device_root
+
+ # TODO from here on down needs to be copied to Base+SUT
+ def wait_for_device(self, interval=60, max_attempts=20):
+ self.info("Waiting for device to come back...")
+ time.sleep(interval)
+ tries = 0
+ while tries <= max_attempts:
+ tries += 1
+ self.info("Try %d" % tries)
+ if self.ping_device(auto_connect=True, silent=True):
+ return self.ping_device()
+ time.sleep(interval)
+ raise DeviceException, "Remote Device Error: waiting for device timed out."
+
+ def query_device_time(self):
+ device_id = self.query_device_id()
+ adb = self.query_exe('adb')
+ # adb shell 'date' will give a date string
+ date_string = self.get_output_from_command([adb, "-s", device_id,
+ "shell", "date"])
+ # TODO what to do when we error?
+ return date_string
+
+ def set_device_time(self, device_time=None, error_level='error'):
+ # adb shell date UNIXTIMESTAMP will set date
+ device_id = self.query_device_id()
+ if device_time is None:
+ device_time = time.time()
+ self.info(self.query_device_time())
+ adb = self.query_exe('adb')
+ status = self.run_command([adb, "-s", device_id, "shell", "date",
+ str(device_time)],
+ error_list=ADBErrorList)
+ self.info(self.query_device_time())
+ return status
+
+ def query_device_file_exists(self, file_name):
+ device_id = self.query_device_id()
+ adb = self.query_exe('adb')
+ output = self.get_output_from_command([adb, "-s", device_id,
+ "shell", "ls", "-d", file_name])
+ if str(output).rstrip() == file_name:
+ return True
+ return False
+
+ def remove_device_root(self, error_level='error'):
+ device_root = self.query_device_root()
+ device_id = self.query_device_id()
+ if device_root is None:
+ self.add_device_flag(DEVICE_UNREACHABLE)
+ self.fatal("Can't connect to device!")
+ adb = self.query_exe('adb')
+ if self.query_device_file_exists(device_root):
+ self.info("Removing device root %s." % device_root)
+ self.run_command([adb, "-s", device_id, "shell", "rm",
+ "-r", device_root], error_list=ADBErrorList)
+ if self.query_device_file_exists(device_root):
+ self.add_device_flag(DEVICE_CANT_REMOVE_DEVROOT)
+ self.log("Unable to remove device root!", level=error_level)
+ return False
+ return True
+
+ def install_app(self, file_path):
+ c = self.config
+ device_id = self.query_device_id()
+ adb = self.query_exe('adb')
+ uptime = self.query_device_exe('uptime')
+ if c['enable_automation']:
+ self.set_device_time()
+ if self._log_level_at_least(DEBUG):
+ self.run_command([adb, "-s", device_id, "shell", "ps"],
+ error_list=ADBErrorList)
+ # TODO dm.getInfo('memory')
+ if self._log_level_at_least(DEBUG):
+ self.run_command([adb, "-s", "shell", uptime],
+ error_list=ADBErrorList)
+ # TODO getResolution ? # for tegra250:
+ # adb shell getprop persist.tegra.dpy3.mode.width
+ # adb shell getprop persist.tegra.dpy3.mode.height
+ #
+ # for non-tegra250, this ugliness:
+ # adb -s device_id shell screencap /mnt/sdcard/tests/foo.png
+ # adb -s device_id shell ls -l /mnt/sdcard/tests/foo.png
+ # -rw-rw-r-- root sdcard_rw 207187 2011-10-04 18:12 foo.png
+ # adb pull /mnt/sdcard/tests/foo.png
+ # Can do via PIL:
+ # import Image
+ # Image.open("foo.png").size
+ # (1280, 800)
+ # I hate requiring another module just for this, if we can help it.
+ #
+ # adb -s device_id shell am display-size 1024x768
+ # reboot; adb wait-for-device; sleep
+ # (later) adb -s device_id shell am display-size 1680:1050
+ # TODO error checking
+ if not c['enable_automation']:
+ # -s to install on sdcard? Needs to be config driven
+ self.run_command([adb, "-s", device_id, "install", '-r',
+ file_path],
+ error_list=ADBErrorList)
+ else:
+ output = self.get_output_from_command([adb, "-s", device_id,
+ "shell",
+ "ls -d /data/data/%s" % \
+ c['device_package_name']])
+ if output is not None and "No such file" not in output:
+ self.run_command([adb, "-s", device_id, "uninstall",
+ c['device_package_name']],
+ error_list=ADBErrorList)
+ self.run_command([adb, "-s", device_id, "install", '-r',
+ file_path],
+ error_list=ADBErrorList)
+
+ def uninstall_app(self, package_name, package_root="/data/data",
+ error_level="error"):
+ c = self.config
+ device_id = self.query_device_id()
+ self.info("Uninstalling %s..." % package_name)
+ if self.query_device_file_exists('%s/%s' % (package_root, package_name)):
+ adb = self.query_exe('adb')
+ cmd = [adb, "-s", device_id, "uninstall"]
+ if not c.get('enable_automation'):
+ cmd.append("-k")
+ cmd.append(package_name)
+ status = self.run_command(cmd, error_list=ADBErrorList)
+ # TODO is this the right error check?
+ if status:
+ self.log("Failed to uninstall %s!" % package_name,
+ level=error_level)
+
+ # Device-type-specific. {{{2
+ def remove_etc_hosts(self, hosts_file="/system/etc/hosts"):
+ c = self.config
+ if c['device_type'] not in ("tegra250",):
+ self.debug("No need to remove /etc/hosts on a non-Tegra250.")
+ return
+ device_id = self.query_device_id()
+ if self.query_device_file_exists(hosts_file):
+ self.info("Removing %s file." % hosts_file)
+ adb = self.query_exe('adb')
+ self.run_command([adb, "-s", device_id, "shell",
+ "mount", "-o", "remount,rw", "-t", "yaffs2",
+ "/dev/block/mtdblock3", "/system"],
+ error_list=ADBErrorList)
+ self.run_command([adb, "-s", device_id, "shell", "rm",
+ hosts_file])
+ if self.query_device_file_exists(hosts_file):
+ self.add_device_flag(DEVICE_CANT_REMOVE_ETC_HOSTS)
+ self.fatal("Unable to remove %s!" % hosts_file)
+ else:
+ self.debug("%s file doesn't exist; skipping." % hosts_file)
+
+
+
+# SUTDeviceHandler {{{1
+class SUTDeviceHandler(BaseDeviceHandler):
+ def __init__(self, **kwargs):
+ super(SUTDeviceHandler, self).__init__(**kwargs)
+ self.devicemanager = None
+ self.default_port = 20701
+ self.default_heartbeat_port = 20700
+
+ def query_devicemanager(self, error_level=FATAL):
+ if self.devicemanager:
+ return self.devicemanager
+ c = self.config
+ site_packages_path = self.script_obj.query_python_site_packages_path()
+ dm_path = os.path.join(site_packages_path, 'mozdevice')
+ sys.path.append(dm_path)
+ try:
+# import devicemanagerSUT
+ from devicemanagerSUT import DeviceManagerSUT
+ from devicemanagerSUT import DMError
+ self.devicemanager = DeviceManagerSUT(c['device_ip'])
+ # TODO configurable?
+ self.devicemanager.debug = c.get('devicemanager_debug_level', 0)
+ except ImportError, e:
+ self.fatal("Can't import DeviceManagerSUT! %s\nDid you check out talos?" % str(e), level=error_level)
+ return self.devicemanager
+
+ # maintenance {{{2
+ def ping_device(self):
+ #TODO writeme
+ pass
+
+ def check_device(self):
+ self.info("Checking for device root to verify the device is alive.")
+ dev_root = self.query_device_root(strict=True)
+ if not dev_root:
+ self.add_device_flag(DEVICE_UNREACHABLE)
+ self.fatal("Can't get dev_root from devicemanager; is the device up?")
+ self.info("Found a dev_root of %s." % str(dev_root))
+
+ def wait_for_device(self, interval=60, max_attempts=20):
+ self.info("Waiting for device to come back...")
+ time.sleep(interval)
+ success = False
+ attempts = 0
+ while attempts <= max_attempts:
+ attempts += 1
+ self.info("Try %d" % attempts)
+ if self.query_device_root() is not None:
+ success = True
+ break
+ time.sleep(interval)
+ if not success:
+ self.add_device_flag(DEVICE_UNREACHABLE)
+ self.fatal("Waiting for tegra timed out.")
+ else:
+ self.info("Device came back.")
+
+ def cleanup_device(self, reboot=False):
+ c = self.config
+ dev_root = self.query_device_root()
+ dm = self.query_devicemanager()
+ if dm.dirExists(dev_root):
+ self.info("Removing dev_root %s..." % dev_root)
+ status = dm.removeDir(dev_root)
+ if not status:
+ self.add_device_flag(DEVICE_CANT_REMOVE_DEVROOT)
+ self.fatal("Can't remove dev_root!")
+ if c.get("enable_automation"):
+ self.remove_etc_hosts()
+ # TODO I need to abstract this uninstall as we'll need to clean
+ # multiple packages off devices.
+ if c.get("device_package_name"):
+ if dm.dirExists('/data/data/%s' % c['device_package_name']):
+ self.info("Uninstalling %s..." % c['device_package_name'])
+ dm.uninstallAppAndReboot(c['device_package_name'])
+ self.wait_for_device()
+ elif reboot:
+ self.reboot_device()
+
+ # device calls {{{2
+ def query_device_root(self, strict=False):
+ c = self.config
+ dm = self.query_devicemanager()
+ dev_root = dm.getDeviceRoot()
+ if strict and c.get('enable_automation'):
+ if not str(dev_root).startswith("/mnt/sdcard"):
+ self.add_device_flag(DEVICE_MISSING_SDCARD)
+ self.fatal("dev_root from devicemanager [%s] is not correct!" % \
+ str(dev_root))
+ if not dev_root or dev_root == "/tests":
+ return None
+ return dev_root
+
+ def query_device_time(self):
+ dm = self.query_devicemanager()
+ timestamp = int(dm.getCurrentTime()) #epoch time in milliseconds
+ dt = datetime.datetime.utcfromtimestamp(timestamp / 1000)
+ self.info("Current device time is %s" % dt.strftime('%Y/%m/%d %H:%M:%S'))
+ return dt
+
+ def set_device_time(self):
+ dm = self.query_devicemanager()
+ s = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S')
+ self.info("Setting device time to %s" % s)
+ try:
+ dm.sendCMD(['settime %s' % s])
+ return True
+ except DMError, e:
+ self.add_device_flag(DEVICE_CANT_SET_TIME)
+ self.fatal("Exception while setting device time: %s" % str(e))
+
+ def install_app(self, file_path):
+ dev_root = self.query_device_root(strict=True)
+ if not dev_root:
+ self.add_device_flag(DEVICE_UNREACHABLE)
+ # TODO wait_for_device?
+ self.fatal("dev_root %s not correct!" % str(dev_root))
+
+ dm = self.query_devicemanager()
+
+ c = self.config
+ if c.get('enable_automation'):
+ self.query_device_time()
+ self.set_device_time()
+ self.query_device_time()
+ dm.getInfo('process')
+ dm.getInfo('memory')
+ dm.getInfo('uptime')
+
+ # This target needs to not use os.path.join due to differences with win
+ # Paths vs. unix paths.
+ target = "/".join([dev_root, os.path.basename(file_path)])
+ self.info("Installing %s on device..." % file_path)
+ dm.pushFile(file_path, target)
+ # TODO screen resolution
+ # TODO do something with status?
+ status = dm.installApp(target)
+ if status is None:
+ self.info('-'*42)
+ self.info("Sleeping for 90 seconds...")
+ time.sleep(90)
+ self.info('installApp(%s) done - gathering debug info' % target)
+ try:
+ self.info(repr(dm.getInfo('process')))
+ self.info(repr(dm.getInfo('memory')))
+ self.info(repr(dm.getInfo('uptime')))
+ self.info(repr(dm.sendCMD(['exec su -c "logcat -d -v time *:W"'])))
+ except Exception, e:
+ self.info("Exception hit while trying to run logcat: %s" % str(e))
+ self.fatal("Remote Device Error: can't run logcat")
+ else:
+ self.fatal("Remote Device Error: updateApp() call failed - exiting")
+
+
+ def reboot_device(self):
+ dm = self.query_devicemanager()
+ # logcat?
+ self.info("Rebooting device...")
+ status = dm.reboot()
+ if status is None:
+ self.add_device_flag(DEVICE_NOT_REBOOTED)
+ self.fatal("Can't reboot device!")
+ self.wait_for_device()
+ dm.getInfo('uptime')
+
+ # device type specific {{{2
+ def remove_etc_hosts(self, hosts_file="/system/etc/hosts"):
+ c = self.config
+ if c['device_type'] not in ("tegra250",) or True:
+ self.debug("No need to remove /etc/hosts on a non-Tegra250.")
+ return
+ dm = self.query_devicemanager()
+ if dm.fileExists(hosts_file):
+ self.info("Removing %s file." % hosts_file)
+ try:
+ dm.sendCMD(['exec mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system'])
+ dm.sendCMD(['exec rm %s' % hosts_file])
+ except DMError:
+ self.add_device_flag(DEVICE_CANT_REMOVE_ETC_HOSTS)
+ self.fatal("Unable to remove %s!" % hosts_file)
+ if dm.fileExists(hosts_file):
+ self.add_device_flag(DEVICE_CANT_REMOVE_ETC_HOSTS)
+ self.fatal("Unable to remove %s!" % hosts_file)
+ else:
+ self.debug("%s file doesn't exist; skipping." % hosts_file)
+
+
+
+# DeviceMixin {{{1
+DEVICE_PROTOCOL_DICT = {
+ 'adb': ADBDeviceHandler,
+ 'sut': SUTDeviceHandler,
+}
+
+device_config_options = [[
+ ["--device-ip"],
+ {"action": "store",
+ "dest": "device_ip",
+ "help": "Specify the IP address of the device."
+ }
+],[
+ ["--device-port"],
+ {"action": "store",
+ "dest": "device_port",
+ "help": "Specify the IP port of the device."
+ }
+],[
+ ["--device-heartbeat-port"],
+ {"action": "store",
+ "dest": "device_heartbeat_port",
+ "help": "Specify the heartbeat port of the SUT device."
+ }
+],[
+ ["--device-protocol"],
+ {"action": "store",
+ "type": "choice",
+ "dest": "device_protocol",
+ "choices": DEVICE_PROTOCOL_DICT.keys(),
+ "help": "Specify the device communication protocol."
+ }
+],[
+ ["--device-type"],
+ # A bit useless atm, but we can add new device types as we add support
+ # for them.
+ {"action": "store",
+ "type": "choice",
+ "choices": ["non-tegra", "tegra250"],
+ "default": "non-tegra",
+ "dest": "device_type",
+ "help": "Specify the device type."
+ }
+],[
+ ["--devicemanager-path"],
+ {"action": "store",
+ "dest": "devicemanager_path",
+ "help": "Specify the parent dir of devicemanagerSUT.py."
+ }
+]]
+
+class DeviceMixin(object):
+ '''BaseScript mixin, designed to interface with the device.
+
+ '''
+ device_handler = None
+ device_root = None
+
+ def query_device_handler(self):
+ if self.device_handler:
+ return self.device_handler
+ c = self.config
+ device_protocol = c.get('device_protocol')
+ device_class = DEVICE_PROTOCOL_DICT.get(device_protocol)
+ if not device_class:
+ self.fatal("Unknown device_protocol %s; set via --device-protocol!" % str(device_protocol))
+ self.device_handler = device_class(
+ log_obj=self.log_obj,
+ config=self.config,
+ script_obj=self,
+ )
+ return self.device_handler
+
+ def check_device(self):
+ dh = self.query_device_handler()
+ return dh.check_device()
+
+ def cleanup_device(self, **kwargs):
+ dh = self.query_device_handler()
+ return dh.cleanup_device(**kwargs)
+
+ def install_app(self):
+ dh = self.query_device_handler()
+ return dh.install_app(file_path=self.installer_path)
+
+ def reboot_device(self):
+ dh = self.query_device_handler()
+ return dh.reboot_device()
2  mozharness/mozilla/testing/talos.py
View
@@ -190,6 +190,6 @@ def run_tests(self, conf='talos.yml'):
# run talos tests
talos = self.query_python_path('talos')
- command = [talos, '--noisy', talos_conf_path]
+ command = [talos, '--noisy', '--debug', talos_conf_path]
self.return_code = self.run_command(command, cwd=self.workdir,
error_list=TalosErrorList)
10 scripts/configtest.py
View
@@ -7,8 +7,9 @@
"""configtest.py
Verify the .json and .py files in the configs/ directory are well-formed.
-
Further tests to verify validity would be desirable.
+
+This is also a good example script to look at to understand mozharness.
"""
import os
@@ -45,6 +46,10 @@ def __init__(self, require_config_file=False):
require_config_file=require_config_file)
def query_config_files(self):
+ """This query method, much like others, caches its runtime
+ settings in self.VAR so we don't have to figure out config_files
+ multiple times.
+ """
if self.config_files:
return self.config_files
c = self.config
@@ -64,6 +69,9 @@ def query_config_files(self):
return self.config_files
def list_config_files(self):
+ """ Non-default action that is mainly here to demonstrate how
+ non-default actions work in a mozharness script.
+ """
config_files = self.query_config_files()
for config_file in config_files:
self.info(config_file)
196 scripts/device_talosrunner.py
View
@@ -0,0 +1,196 @@
+#!/usr/bin/env python -u
+# ***** BEGIN LICENSE BLOCK *****
+# 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/.
+# ***** END LICENSE BLOCK *****
+"""device_talosrunner.py
+
+Set up and run talos against a device running SUT Agent or ADBD.
+
+WIP.
+"""
+
+import os
+import sys
+import time
+
+sys.path.insert(1, os.path.dirname(sys.path[0]))
+
+from mozharness.base.errors import PythonErrorList
+from mozharness.mozilla.testing.device import device_config_options, DeviceMixin
+from mozharness.mozilla.testing.talos import Talos
+
+# Stop buffering!
+sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+
+# DeviceTalosRunner {{{1
+class DeviceTalosRunner(DeviceMixin, Talos):
+ config_options = Talos.config_options + device_config_options
+
+ def __init__(self, require_config_file=False):
+ super(DeviceTalosRunner, self).__init__(
+ config_options=self.config_options,
+ all_actions=['preclean',
+ 'create-virtualenv',
+ 'check-device',
+ 'read-buildbot-config',
+ 'download',
+ 'unpack',
+ 'pre-cleanup-device',
+ 'install-app',
+ 'generate_config',
+ 'run-tests',
+ 'post-cleanup-device',
+# 'upload',
+# 'notify',
+# 'postclean',
+# 'reboot-host',
+ ],
+ default_actions=['preclean',
+ 'create-virtualenv',
+ 'check-device',
+ 'pre-cleanup-device',
+ 'download',
+ 'unpack',
+ 'install-app',
+ 'generate_config',
+ 'run-tests',
+ 'post-cleanup-device',
+ ],
+ config={'virtualenv_modules': ['talos']},
+ require_config_file=require_config_file,
+ )
+ if not self.installer_path:
+ self.installer_path = os.path.join(self.workdir, 'installer.apk')
+
+ def _pre_config_lock(self, rw_config):
+ super(DeviceTalosRunner, self)._pre_config_lock(rw_config)
+ if 'device_protocol' not in self.config:
+ self.fatal("Must specify --device-protocol!")
+
+ # Helper methods {{{2
+
+ def query_abs_dirs(self):
+ if self.abs_dirs:
+ return self.abs_dirs
+ abs_dirs = super(DeviceTalosRunner, self).query_abs_dirs()
+ abs_dirs['abs_application_dir'] = os.path.join(abs_dirs['abs_work_dir'],
+ 'application')
+ self.abs_dirs = abs_dirs
+ return self.abs_dirs
+
+ # Actions {{{2
+
+ def preclean(self):
+ self.clobber()
+
+ # create_virtualenv defined in VirtualenvMixin
+ # check_device defined in DeviceMixin
+
+ def pre_cleanup_device(self):
+ self.cleanup_device()
+
+ # read_buildbot_config defined in BuildbotMixin
+
+ def download(self):
+ if not self.installer_url:
+ self.installer_url = self.config['installer_url']
+ self._download_installer()
+
+ def unpack(self):
+ # We need a generic extract() again.
+ dirs = self.query_abs_dirs()
+ unzip = self.query_exe("unzip")
+ self.mkdir_p(dirs['abs_application_dir'])
+ self.run_command([unzip, self.installer_path],
+ cwd=dirs['abs_application_dir'])
+ inifile = os.path.join(dirs['abs_application_dir'], 'application.ini')
+ remoteappini = os.path.join(dirs['abs_work_dir'], 'remoteapp.ini')
+ self.info('copying %s to %s' % (inifile, remoteappini))
+ self.run_command(['cp', inifile, remoteappini])
+
+ # TODO install_app defined in DeviceMixin
+
+ def preflight_generate_config(self):
+ if 'install-app' in self.actions:
+ c = self.config
+ time_to_sleep = c.get("post_install_sleep", 60)
+ self.info("Sleeping %d to avoid post-install errors" %
+ time_to_sleep)
+ time.sleep(time_to_sleep)
+
+ super(DeviceTalosRunner, self).preflight_generate_config()
+
+ def generate_config(self, conf='talos.yml', options=None):
+ c = self.config
+ dirs = self.query_abs_dirs()
+ python = self.query_python_path()
+ script_dir = os.path.dirname(python)
+ additional_options = []
+ if c.get('disable_chrome'):
+ additional_options.append("--noChrome")
+ if c['device_protocol'] == 'sut':
+ additional_options.extend(['--remoteDevice', c['device_ip']])
+ additional_options.extend(['--remotePort', c.get('device_port', '20701')])
+ elif c['device_protocol'] == 'adb':
+ additional_options.extend(['--remoteDevice', ''])
+ additional_options.extend(['--remotePort', '-1'])
+ if c.get('start_python_webserver'):
+ additional_options.append('--develop')
+# if c.get('repository'):
+# additional_options.append('repository', c['repository'])
+ script = os.path.join(script_dir, 'remotePerfConfigurator')
+ # TODO set no_chrome based on active tests
+ command = [script,
+ '-v',
+ '-e', c['device_package_name'],
+ '-t', c.get('talos_device_name', c['device_name']),
+ '--branchName', c['talos_branch'],
+ '--resultsServer', c['graph_server'],
+ '--resultsLink', c['results_link'],
+ '--activeTests', ':'.join(c['talos_suites']),
+ '--sampleConfig', c['talos_config_file'],
+ '--output', conf,
+ '--browserWait', '60',
+ '--webServer', c['talos_webserver'],
+ ] + additional_options
+ self.run_command(command, cwd=dirs['abs_work_dir'],
+ error_list=PythonErrorList,
+ halt_on_failure=True)
+
+# def preflight_run_talos(self):
+# #TODO get this un-adb-hardcoded
+# if 'install-app' not in self.actions:
+# c = self.config
+# device_id = self.query_device_id()
+# adb = self.query_exe('adb')
+# kill = self.query_device_exe('kill')
+# procs = self.get_output_from_command([adb, "-s", device_id,
+# 'shell', 'ps'],
+# log_level=DEBUG)
+# if c['device_package_name'] in procs:
+# self.info("Found %s running... attempting to kill." %
+# c['device_package_name'])
+# # TODO this needs to kill the pid
+# # TODO verify it's gone
+# for line in procs.splitlines():
+# line_contents = re.split('\s+', line)
+# if line_contents[-1].startswith(c['device_package_name']):
+# self.run_command([adb, "-s", device_id, 'shell',
+# kill, line_contents[1]],
+# error_list=ADBErrorList)
+
+ def post_cleanup_device(self):
+ c = self.config
+ if c.get('enable_automation'):
+ self.cleanup_device(reboot=True)
+ else:
+ self.info("Nothing to do without enable_automation set.")
+
+ # run_tests() is in Talos
+
+# __main__ {{{1
+if __name__ == '__main__':
+ device_talos_runner = DeviceTalosRunner()
+ device_talos_runner.run()
2  unit.sh
View
@@ -59,7 +59,7 @@ echo "### Running pyflakes"
pyflakes $MOZHARNESS_PY_FILES $SCRIPTS_PY_FILES | grep -v "local variable 'url' is assigned to" | grep -v "redefinition of unused 'json'"
echo "### Running pylint"
-pylint -E -e F -f parseable $MOZHARNESS_PY_FILES $SCRIPTS_PY_FILES 2>&1 | egrep -v '(No config file found, using default configuration|Instance of .* has no .* member)'
+pylint -E -e F -f parseable $MOZHARNESS_PY_FILES $SCRIPTS_PY_FILES 2>&1 | egrep -v '(No config file found, using default configuration|Instance of .* has no .* member|Unable to import .devicemanager|Undefined variable .DMError)'
rm -rf build logs
if [ $OS_TYPE != 'windows' ] ; then
Please sign in to comment.
Something went wrong with that request. Please try again.