summaryrefslogtreecommitdiff
path: root/systrace/catapult/devil/devil/utils
diff options
context:
space:
mode:
Diffstat (limited to 'systrace/catapult/devil/devil/utils')
-rw-r--r--systrace/catapult/devil/devil/utils/__init__.py23
-rwxr-xr-xsystrace/catapult/devil/devil/utils/battor_device_mapping.py309
-rw-r--r--systrace/catapult/devil/devil/utils/cmd_helper.py36
-rwxr-xr-xsystrace/catapult/devil/devil/utils/find_usb_devices.py4
-rwxr-xr-xsystrace/catapult/devil/devil/utils/find_usb_devices_test.py161
-rw-r--r--systrace/catapult/devil/devil/utils/lazy/weak_constant.py23
-rw-r--r--systrace/catapult/devil/devil/utils/lazy/weak_constant_test.py70
-rw-r--r--systrace/catapult/devil/devil/utils/logging_common.py23
-rwxr-xr-xsystrace/catapult/devil/devil/utils/markdown.py12
-rw-r--r--systrace/catapult/devil/devil/utils/reraiser_thread.py30
-rwxr-xr-xsystrace/catapult/devil/devil/utils/reset_usb.py5
-rw-r--r--systrace/catapult/devil/devil/utils/run_tests_helper.py4
-rw-r--r--systrace/catapult/devil/devil/utils/test/data/test_serial_map.json1
-rw-r--r--systrace/catapult/devil/devil/utils/timeout_retry.py3
-rwxr-xr-xsystrace/catapult/devil/devil/utils/update_mapping.py47
15 files changed, 212 insertions, 539 deletions
diff --git a/systrace/catapult/devil/devil/utils/__init__.py b/systrace/catapult/devil/devil/utils/__init__.py
index ff84988..e69de29 100644
--- a/systrace/catapult/devil/devil/utils/__init__.py
+++ b/systrace/catapult/devil/devil/utils/__init__.py
@@ -1,23 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-import sys
-
-def _JoinPath(*path_parts):
- return os.path.abspath(os.path.join(*path_parts))
-
-
-def _AddDirToPythonPath(*path_parts):
- path = _JoinPath(*path_parts)
- if os.path.isdir(path) and path not in sys.path:
- # Some call sites that use Telemetry assume that sys.path[0] is the
- # directory containing the script, so we add these extra paths to right
- # after sys.path[0].
- sys.path.insert(1, path)
-
-_CATAPULT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
- os.path.pardir, os.path.pardir, os.path.pardir)
-
-_AddDirToPythonPath(_CATAPULT_DIR, 'common', 'battor')
diff --git a/systrace/catapult/devil/devil/utils/battor_device_mapping.py b/systrace/catapult/devil/devil/utils/battor_device_mapping.py
deleted file mode 100755
index 8cabb83..0000000
--- a/systrace/catapult/devil/devil/utils/battor_device_mapping.py
+++ /dev/null
@@ -1,309 +0,0 @@
-#!/usr/bin/python
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-'''
-This script provides tools to map BattOrs to phones.
-
-Phones are identified by the following string:
-
-"Phone serial number" - Serial number of the phone. This can be
-obtained via 'adb devices' or 'usb-devices', and is not expected
-to change for a given phone.
-
-BattOrs are identified by the following two strings:
-
-"BattOr serial number" - Serial number of the BattOr. This can be
-obtained via 'usb-devices', and is not expected to change for
-a given BattOr.
-
-"BattOr path" - The path of the form '/dev/ttyUSB*' that is used
-to communicate with the BattOr (the battor_agent binary takes
-this BattOr path as a parameter). The BattOr path is frequently
-reassigned by the OS, most often when the device is disconnected
-and then reconnected. Thus, the BattOr path cannot be expected
-to be stable.
-
-In a typical application, the user will require the BattOr path
-for the BattOr that is plugged into a given phone. For instance,
-the user will be running tracing on a particular phone, and will
-need to know which BattOr path to use to communicate with the BattOr
-to get the corresponding power trace.
-
-Getting this mapping requires two steps: (1) determining the
-mapping between phone serial numbers and BattOr serial numbers, and
-(2) getting the BattOr path corresponding to a given BattOr serial
-number.
-
-For step (1), we generate a JSON file giving this mapping. This
-JSON file consists of a list of items of the following form:
-[{'phone': <phone serial 1>, 'battor': <battor serial 1>},
-{'phone': <phone serial 2>, 'battor': <battor serial 2>}, ...]
-
-The default way to generate this JSON file is using the function
-GenerateSerialMapFile, which generates a mapping based on assuming
-that the system has two identical USB hubs connected to it, and
-the phone plugged into physical port number 1 on one hub corresponds
-to the BattOr plugged into physical port number 1 on the other hub,
-and similarly with physical port numbers 2, 3, etc. This generates
-the map file based on the structure at the time GenerateSerialMapFile called.
-Note that after the map file is generated, port numbers are no longer used;
-the user could move around the devices in the ports without affecting
-which phone goes with which BattOr. (Thus, if the user wanted to update the
-mapping to match the new port connections, the user would have to
-re-generate this file.)
-
-The script update_mapping.py will do this updating from the command line.
-
-If the user wanted to specify a custom mapping, the user could instead
-create the JSON file manually. (In this case, hubs would not be necessary
-and the physical ports connected would be irrelevant.)
-
-Step (2) is conducted through the function GetBattOrPathFromPhoneSerial,
-which takes a serial number mapping generated via step (1) and a phone
-serial number, then gets the corresponding BattOr serial number from the
-map and determines its BattOr path (e.g. /dev/ttyUSB0). Since BattOr paths
-can change if devices are connected and disconnected (even if connected
-or disconnected via the same port) this function should be called to
-determine the BattOr path every time before connecting to the BattOr.
-
-Note that if there is only one BattOr connected to the system, then
-GetBattOrPathFromPhoneSerial will always return that BattOr and will ignore
-the mapping file. Thus, if the user never has more than one BattOr connected
-to the system, the user will not need to generate mapping files.
-'''
-
-
-import json
-import collections
-
-from battor import battor_error
-from devil.utils import find_usb_devices
-from devil.utils import usb_hubs
-
-
-def GetBattOrList(device_tree_map):
- return [x for x in find_usb_devices.GetTTYList()
- if IsBattOr(x, device_tree_map)]
-
-
-def IsBattOr(tty_string, device_tree_map):
- (bus, device) = find_usb_devices.GetBusDeviceFromTTY(tty_string)
- node = device_tree_map[bus].FindDeviceNumber(device)
- return '0403:6001' in node.desc
-
-
-def GetBattOrSerialNumbers(device_tree_map):
- for x in find_usb_devices.GetTTYList():
- if IsBattOr(x, device_tree_map):
- (bus, device) = find_usb_devices.GetBusDeviceFromTTY(x)
- devnode = device_tree_map[bus].FindDeviceNumber(device)
- yield devnode.serial
-
-
-def ReadSerialMapFile(filename):
- """Reads JSON file giving phone-to-battor serial number map.
-
- Parses a JSON file consisting of a list of items of the following form:
- [{'phone': <phone serial 1>, 'battor': <battor serial 1>},
- {'phone': <phone serial 2>, 'battor': <battor serial 2>}, ...]
-
- indicating which phone serial numbers should be matched with
- which BattOr serial numbers. Returns dictionary of the form:
-
- {<phone serial 1>: <BattOr serial 1>,
- <phone serial 2>: <BattOr serial 2>}
-
- Args:
- filename: Name of file to read.
- """
- result = {}
- with open(filename, 'r') as infile:
- in_dict = json.load(infile)
- for x in in_dict:
- result[x['phone']] = x['battor']
- return result
-
-def WriteSerialMapFile(filename, serial_map):
- """Writes a map of phone serial numbers to BattOr serial numbers to file.
-
- Writes a JSON file consisting of a list of items of the following form:
- [{'phone': <phone serial 1>, 'battor': <battor serial 1>},
- {'phone': <phone serial 2>, 'battor': <battor serial 2>}, ...]
-
- indicating which phone serial numbers should be matched with
- which BattOr serial numbers. Mapping is based on the physical port numbers
- of the hubs that the BattOrs and phones are connected to.
-
- Args:
- filename: Name of file to write.
- serial_map: Serial map {phone: battor}
- """
- result = []
- for (phone, battor) in serial_map.iteritems():
- result.append({'phone': phone, 'battor': battor})
- with open(filename, 'w') as outfile:
- json.dump(result, outfile)
-
-def GenerateSerialMap(hub_types=None):
- """Generates a map of phone serial numbers to BattOr serial numbers.
-
- Generates a dict of:
- {<phone serial 1>: <battor serial 1>,
- <phone serial 2>: <battor serial 2>}
- indicating which phone serial numbers should be matched with
- which BattOr serial numbers. Mapping is based on the physical port numbers
- of the hubs that the BattOrs and phones are connected to.
-
- Args:
- hub_types: List of hub types to check for. If not specified, checks
- for all defined hub types. (see usb_hubs.py for details)
- """
- if hub_types:
- hub_types = [usb_hubs.GetHubType(x) for x in hub_types]
- else:
- hub_types = usb_hubs.ALL_HUBS
-
- devtree = find_usb_devices.GetBusNumberToDeviceTreeMap()
-
- # List of serial numbers in the system that represent BattOrs.
- battor_serials = list(GetBattOrSerialNumbers(devtree))
-
- # If there's only one BattOr in the system, then a serial number ma
- # is not necessary.
- if len(battor_serials) == 1:
- return {}
-
- # List of dictionaries, one for each hub, that maps the physical
- # port number to the serial number of that hub. For instance, in a 2
- # hub system, this could return [{1:'ab', 2:'cd'}, {1:'jkl', 2:'xyz'}]
- # where 'ab' and 'cd' are the phone serial numbers and 'jkl' and 'xyz'
- # are the BattOr serial numbers.
- port_to_serial = find_usb_devices.GetAllPhysicalPortToSerialMaps(
- hub_types, device_tree_map=devtree)
-
- class serials(object):
- def __init__(self):
- self.phone = None
- self.battor = None
-
- # Map of {physical port number: [phone serial #, BattOr serial #]. This
- # map is populated by executing the code below. For instance, in the above
- # example, after the code below is executed, port_to_devices would equal
- # {1: ['ab', 'jkl'], 2: ['cd', 'xyz']}
- port_to_devices = collections.defaultdict(serials)
- for hub in port_to_serial:
- for (port, serial) in hub.iteritems():
- if serial in battor_serials:
- if port_to_devices[port].battor is not None:
- raise battor_error.BattOrError('Multiple BattOrs on same port number')
- else:
- port_to_devices[port].battor = serial
- else:
- if port_to_devices[port].phone is not None:
- raise battor_error.BattOrError('Multiple phones on same port number')
- else:
- port_to_devices[port].phone = serial
-
- # Turn the port_to_devices map into a map of the form
- # {phone serial number: BattOr serial number}.
- result = {}
- for pair in port_to_devices.values():
- if pair.phone is None:
- continue
- if pair.battor is None:
- raise battor_error.BattOrError(
- 'Phone detected with no corresponding BattOr')
- result[pair.phone] = pair.battor
- return result
-
-def GenerateSerialMapFile(filename, hub_types=None):
- """Generates a serial map file and writes it."""
- WriteSerialMapFile(filename, GenerateSerialMap(hub_types))
-
-def _PhoneToPathMap(serial, serial_map, devtree):
- """Maps phone serial number to TTY path, assuming serial map is provided."""
- try:
- battor_serial = serial_map[serial]
- except KeyError:
- raise battor_error.BattOrError('Serial number not found in serial map.')
- for tree in devtree.values():
- for node in tree.AllNodes():
- if isinstance(node, find_usb_devices.USBDeviceNode):
- if node.serial == battor_serial:
- bus_device_to_tty = find_usb_devices.GetBusDeviceToTTYMap()
- bus_device = (node.bus_num, node.device_num)
- try:
- return bus_device_to_tty[bus_device]
- except KeyError:
- raise battor_error.BattOrError(
- 'Device with given serial number not a BattOr '
- '(does not have TTY path)')
-
-
-def GetBattOrPathFromPhoneSerial(serial, serial_map=None,
- serial_map_file=None):
- """Gets the TTY path (e.g. '/dev/ttyUSB0') to communicate with the BattOr.
-
- (1) If serial_map is given, it is treated as a dictionary mapping
- phone serial numbers to BattOr serial numbers. This function will get the
- TTY path for the given BattOr serial number.
-
- (2) If serial_map_file is given, it is treated as the name of a
- phone-to-BattOr mapping file (generated with GenerateSerialMapFile)
- and this will be loaded and used as the dict to map port numbers to
- BattOr serial numbers.
-
- You can only give one of serial_map and serial_map_file.
-
- Args:
- serial: Serial number of phone connected on the same physical port that
- the BattOr is connected to.
- serial_map: Map of phone serial numbers to BattOr serial numbers, given
- as a dictionary.
- serial_map_file: Map of phone serial numbers to BattOr serial numbers,
- given as a file.
- hub_types: List of hub types to check for. Used only if serial_map_file
- is None.
-
- Returns:
- Device string used to communicate with device.
-
- Raises:
- ValueError: If serial number is not given.
- BattOrError: If BattOr not found or unexpected USB topology.
- """
- # If there's only one BattOr connected to the system, just use that one.
- # This allows for use on, e.g., a developer's workstation with no hubs.
- devtree = find_usb_devices.GetBusNumberToDeviceTreeMap()
- all_battors = GetBattOrList(devtree)
- if len(all_battors) == 1:
- return '/dev/' + all_battors[0]
-
- if not serial:
- raise battor_error.BattOrError(
- 'Two or more BattOrs connected, no serial provided')
-
- if serial_map and serial_map_file:
- raise ValueError('Cannot specify both serial_map and serial_map_file')
-
- if serial_map_file:
- serial_map = ReadSerialMapFile(serial_map_file)
-
- tty_string = _PhoneToPathMap(serial, serial_map, devtree)
-
- if not tty_string:
- raise battor_error.BattOrError(
- 'No device with given serial number detected.')
-
- if IsBattOr(tty_string, devtree):
- return '/dev/' + tty_string
- else:
- raise battor_error.BattOrError(
- 'Device with given serial number is not a BattOr.')
-
-if __name__ == '__main__':
- # Main function for testing purposes
- print GenerateSerialMap()
diff --git a/systrace/catapult/devil/devil/utils/cmd_helper.py b/systrace/catapult/devil/devil/utils/cmd_helper.py
index b477c70..3c4a06e 100644
--- a/systrace/catapult/devil/devil/utils/cmd_helper.py
+++ b/systrace/catapult/devil/devil/utils/cmd_helper.py
@@ -4,6 +4,7 @@
"""A wrapper for subprocess to make calling shell commands easier."""
+import codecs
import logging
import os
import pipes
@@ -15,11 +16,16 @@ import subprocess
import sys
import time
+from devil import base_error
logger = logging.getLogger(__name__)
_SafeShellChars = frozenset(string.ascii_letters + string.digits + '@%_-+=:,./')
+# Cache the string-escape codec to ensure subprocess can find it
+# later. Return value doesn't matter.
+codecs.lookup('string-escape')
+
def SingleQuote(s):
"""Return an shell-escaped version of the string using single quotes.
@@ -152,12 +158,12 @@ def _ValidateAndLogCommand(args, cwd, shell):
else:
if shell:
raise Exception('array args must be run with shell=False')
- args = ' '.join(SingleQuote(c) for c in args)
+ args = ' '.join(SingleQuote(str(c)) for c in args)
if cwd is None:
cwd = ''
else:
cwd = ':' + cwd
- logger.info('[host]%s> %s', cwd, args)
+ logger.debug('[host]%s> %s', cwd, args)
return args
@@ -187,6 +193,27 @@ def GetCmdStatusAndOutput(args, cwd=None, shell=False, env=None):
return (status, stdout)
+def StartCmd(args, cwd=None, shell=False, env=None):
+ """Starts a subprocess and returns a handle to the process.
+
+ Args:
+ args: A string or a sequence of program arguments. The program to execute is
+ the string or the first item in the args sequence.
+ cwd: If not None, the subprocess's current directory will be changed to
+ |cwd| before it's executed.
+ shell: Whether to execute args as a shell command. Must be True if args
+ is a string and False if args is a sequence.
+ env: If not None, a mapping that defines environment variables for the
+ subprocess.
+
+ Returns:
+ A process handle from subprocess.Popen.
+ """
+ _ValidateAndLogCommand(args, cwd, shell)
+ return Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ shell=shell, cwd=cwd, env=env)
+
+
def GetCmdStatusOutputAndError(args, cwd=None, shell=False, env=None):
"""Executes a subprocess and returns its exit code, output, and errors.
@@ -210,11 +237,11 @@ def GetCmdStatusOutputAndError(args, cwd=None, shell=False, env=None):
return (pipe.returncode, stdout, stderr)
-class TimeoutError(Exception):
+class TimeoutError(base_error.BaseError):
"""Module-specific timeout exception."""
def __init__(self, output=None):
- super(TimeoutError, self).__init__()
+ super(TimeoutError, self).__init__('Timeout')
self._output = output
@property
@@ -226,6 +253,7 @@ def _IterProcessStdoutFcntl(
process, iter_timeout=None, timeout=None, buffer_size=4096,
poll_interval=1):
"""An fcntl-based implementation of _IterProcessStdout."""
+ # pylint: disable=too-many-nested-blocks
import fcntl
try:
# Enable non-blocking reads from the child's stdout.
diff --git a/systrace/catapult/devil/devil/utils/find_usb_devices.py b/systrace/catapult/devil/devil/utils/find_usb_devices.py
index 74b888d..b6dcc70 100755
--- a/systrace/catapult/devil/devil/utils/find_usb_devices.py
+++ b/systrace/catapult/devil/devil/utils/find_usb_devices.py
@@ -452,9 +452,9 @@ def GetBusDeviceFromTTY(tty_string):
for line in _GetTtyUSBInfo(tty_string).splitlines():
bus_match = _BUS_NUM_REGEX.match(line)
device_match = _DEVICE_NUM_REGEX.match(line)
- if bus_match and bus_num == None:
+ if bus_match and bus_num is None:
bus_num = int(bus_match.group(1))
- if device_match and device_num == None:
+ if device_match and device_num is None:
device_num = int(device_match.group(1))
if bus_num is None or device_num is None:
raise ValueError('Info not found')
diff --git a/systrace/catapult/devil/devil/utils/find_usb_devices_test.py b/systrace/catapult/devil/devil/utils/find_usb_devices_test.py
index e8b00c8..c22f21b 100755
--- a/systrace/catapult/devil/devil/utils/find_usb_devices_test.py
+++ b/systrace/catapult/devil/devil/utils/find_usb_devices_test.py
@@ -17,23 +17,21 @@ Bus 001:
Bus 002:
1: Device 011 "quux"
2: Device 020 "My Test HUB" #hub 1
-2:1: Device 021 "battor_p7_h1_t0" #physical port 7 on hub 1, on ttyUSB0
-2:3: Device 022 "battor_p5_h1_t1" #physical port 5 on hub 1, on ttyUSB1
+2:1: Device 021 "usb_device_p7_h1_t0" #physical port 7 on hub 1, on ttyUSB0
+2:3: Device 022 "usb_device_p5_h1_t1" #physical port 5 on hub 1, on ttyUSB1
2:4: Device 023 "My Test Internal HUB" #internal section of hub 1
-2:4:2: Device 024 "battor_p3_h1_t2" #physical port 3 on hub 1, on ttyUSB2
+2:4:2: Device 024 "usb_device_p3_h1_t2" #physical port 3 on hub 1, on ttyUSB2
2:4:3: Device 026 "Not a Battery Monitor" #physical port 1 on hub 1, on ttyUSB3
-2:4:4: Device 025 "battor_p1_h1_t3" #physical port 1 on hub 1, on ttyUSB3
+2:4:4: Device 025 "usb_device_p1_h1_t3" #physical port 1 on hub 1, on ttyUSB3
3: Device 100 "My Test HUB" #hub 2
3:4: Device 101 "My Test Internal HUB" #internal section of hub 2
-3:4:4: Device 102 "battor_p1_h2_t4" #physical port 1 on hub 2, on ttyusb4
+3:4:4: Device 102 "usb_device_p1_h2_t4" #physical port 1 on hub 2, on ttyusb4
"""
import logging
-import os
import unittest
from devil import devil_env
-from devil.utils import battor_device_mapping
from devil.utils import find_usb_devices
from devil.utils import lsusb
from devil.utils import usb_hubs
@@ -51,15 +49,15 @@ DEVLIST = [(1, 11, 'foo'),
(1, 13, 'baz'),
(2, 11, 'quux'),
(2, 20, 'My Test HUB'),
- (2, 21, 'ID 0403:6001 battor_p7_h1_t0'),
- (2, 22, 'ID 0403:6001 battor_p5_h1_t1'),
+ (2, 21, 'ID 0403:6001 usb_device_p7_h1_t0'),
+ (2, 22, 'ID 0403:6001 usb_device_p5_h1_t1'),
(2, 23, 'My Test Internal HUB'),
- (2, 24, 'ID 0403:6001 battor_p3_h1_t2'),
- (2, 25, 'ID 0403:6001 battor_p1_h1_t3'),
+ (2, 24, 'ID 0403:6001 usb_device_p3_h1_t2'),
+ (2, 25, 'ID 0403:6001 usb_device_p1_h1_t3'),
(2, 26, 'Not a Battery Monitor'),
(2, 100, 'My Test HUB'),
(2, 101, 'My Test Internal HUB'),
- (2, 102, 'ID 0403:6001 battor_p1_h1_t4')]
+ (2, 102, 'ID 0403:6001 usb_device_p1_h1_t4')]
LSUSB_OUTPUT = [
{'bus': b, 'device': d, 'desc': t, 'id': (1000*b)+d}
@@ -82,14 +80,14 @@ T: Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 11 Spd=000 MxCh=00
T: Bus=02 Lev=00 Prnt=00 Port=01 Cnt=00 Dev#= 20 Spd=000 MxCh=00
T: Bus=02 Lev=00 Prnt=20 Port=00 Cnt=00 Dev#= 21 Spd=000 MxCh=00
-S: SerialNumber=BattOr0
+S: SerialNumber=UsbDevice0
T: Bus=02 Lev=00 Prnt=20 Port=02 Cnt=00 Dev#= 22 Spd=000 MxCh=00
-S: SerialNumber=BattOr1
+S: SerialNumber=UsbDevice1
T: Bus=02 Lev=00 Prnt=20 Port=03 Cnt=00 Dev#= 23 Spd=000 MxCh=00
T: Bus=02 Lev=00 Prnt=23 Port=01 Cnt=00 Dev#= 24 Spd=000 MxCh=00
-S: SerialNumber=BattOr2
+S: SerialNumber=UsbDevice2
T: Bus=02 Lev=00 Prnt=23 Port=03 Cnt=00 Dev#= 25 Spd=000 MxCh=00
-S: SerialNumber=BattOr3
+S: SerialNumber=UsbDevice3
T: Bus=02 Lev=00 Prnt=23 Port=02 Cnt=00 Dev#= 26 Spd=000 MxCh=00
T: Bus=02 Lev=00 Prnt=00 Port=02 Cnt=00 Dev#=100 Spd=000 MxCh=00
@@ -103,15 +101,15 @@ Bus 001 Device 012: FAST bar
Bus 001 Device 013: baz
Bus 002 Device 011: quux
Bus 002 Device 020: My Test HUB
-Bus 002 Device 021: ID 0403:6001 battor_p7_h1_t0
-Bus 002 Device 022: ID 0403:6001 battor_p5_h1_t1
+Bus 002 Device 021: ID 0403:6001 usb_device_p7_h1_t0
+Bus 002 Device 022: ID 0403:6001 usb_device_p5_h1_t1
Bus 002 Device 023: My Test Internal HUB
-Bus 002 Device 024: ID 0403:6001 battor_p3_h1_t2
-Bus 002 Device 025: ID 0403:6001 battor_p1_h1_t3
+Bus 002 Device 024: ID 0403:6001 usb_device_p3_h1_t2
+Bus 002 Device 025: ID 0403:6001 usb_device_p1_h1_t3
Bus 002 Device 026: Not a Battery Monitor
Bus 002 Device 100: My Test HUB
Bus 002 Device 101: My Test Internal HUB
-Bus 002 Device 102: ID 0403:6001 battor_p1_h1_t4
+Bus 002 Device 102: ID 0403:6001 usb_device_p1_h1_t4
'''
LIST_TTY_OUTPUT = '''
@@ -213,17 +211,6 @@ class USBScriptTest(unittest.TestCase):
lsusb.raw_lsusb = mock.Mock(
return_value=RAW_LSUSB_OUTPUT)
- def testIsBattOr(self):
- bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
- self.assertTrue(battor_device_mapping.IsBattOr('ttyUSB3', bd))
- self.assertFalse(battor_device_mapping.IsBattOr('ttyUSB5', bd))
-
- def testGetBattOrs(self):
- bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
- self.assertEquals(battor_device_mapping.GetBattOrList(bd),
- ['ttyUSB0', 'ttyUSB1', 'ttyUSB2',
- 'ttyUSB3', 'ttyUSB4'])
-
def testGetTTYDevices(self):
pp = find_usb_devices.GetAllPhysicalPortToTTYMaps([TEST_HUB])
result = list(pp)
@@ -247,131 +234,49 @@ class USBScriptTest(unittest.TestCase):
def testGetSerialMapping(self):
pp = find_usb_devices.GetAllPhysicalPortToSerialMaps([TEST_HUB])
result = list(pp)
- self.assertEquals(result[0], {7:'BattOr0',
- 5:'BattOr1',
- 3:'BattOr2',
- 1:'BattOr3'})
+ self.assertEquals(result[0], {7:'UsbDevice0',
+ 5:'UsbDevice1',
+ 3:'UsbDevice2',
+ 1:'UsbDevice3'})
self.assertEquals(result[1], {})
def testFastDeviceDescriptions(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap()
dev_foo = bd[1].FindDeviceNumber(11)
dev_bar = bd[1].FindDeviceNumber(12)
- dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
+ dev_usb_device_p7_h1_t0 = bd[2].FindDeviceNumber(21)
self.assertEquals(dev_foo.desc, 'FAST foo')
self.assertEquals(dev_bar.desc, 'FAST bar')
- self.assertEquals(dev_battor_p7_h1_t0.desc,
- 'ID 0403:6001 battor_p7_h1_t0')
+ self.assertEquals(dev_usb_device_p7_h1_t0.desc,
+ 'ID 0403:6001 usb_device_p7_h1_t0')
def testDeviceDescriptions(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
dev_foo = bd[1].FindDeviceNumber(11)
dev_bar = bd[1].FindDeviceNumber(12)
- dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
+ dev_usb_device_p7_h1_t0 = bd[2].FindDeviceNumber(21)
self.assertEquals(dev_foo.desc, 'foo')
self.assertEquals(dev_bar.desc, 'bar')
- self.assertEquals(dev_battor_p7_h1_t0.desc,
- 'ID 0403:6001 battor_p7_h1_t0')
+ self.assertEquals(dev_usb_device_p7_h1_t0.desc,
+ 'ID 0403:6001 usb_device_p7_h1_t0')
def testDeviceInformation(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
dev_foo = bd[1].FindDeviceNumber(11)
dev_bar = bd[1].FindDeviceNumber(12)
- dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
+ dev_usb_device_p7_h1_t0 = bd[2].FindDeviceNumber(21)
self.assertEquals(dev_foo.info['id'], 1011)
self.assertEquals(dev_bar.info['id'], 1012)
- self.assertEquals(dev_battor_p7_h1_t0.info['id'], 2021)
+ self.assertEquals(dev_usb_device_p7_h1_t0.info['id'], 2021)
def testSerialNumber(self):
bd = find_usb_devices.GetBusNumberToDeviceTreeMap(fast=False)
dev_foo = bd[1].FindDeviceNumber(11)
dev_bar = bd[1].FindDeviceNumber(12)
- dev_battor_p7_h1_t0 = bd[2].FindDeviceNumber(21)
+ dev_usb_device_p7_h1_t0 = bd[2].FindDeviceNumber(21)
self.assertEquals(dev_foo.serial, 'FooSerial')
self.assertEquals(dev_bar.serial, 'BarSerial')
- self.assertEquals(dev_battor_p7_h1_t0.serial, 'BattOr0')
-
- def testBattOrDictMapping(self):
- map_dict = {'Phone1':'BattOr1', 'Phone2':'BattOr2', 'Phone3':'BattOr3'}
- a1 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
- 'Phone1', serial_map=map_dict)
- a2 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
- 'Phone2', serial_map=map_dict)
- a3 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
- 'Phone3', serial_map=map_dict)
- self.assertEquals(a1, '/dev/ttyUSB1')
- self.assertEquals(a2, '/dev/ttyUSB2')
- self.assertEquals(a3, '/dev/ttyUSB3')
-
- def testBattOrDictFromFileMapping(self):
- try:
- map_dict = {'Phone1':'BattOr1', 'Phone2':'BattOr2', 'Phone3':'BattOr3'}
- curr_dir = os.path.dirname(os.path.realpath(__file__))
- filename = os.path.join(curr_dir, 'test', 'data', 'test_write_map.json')
- battor_device_mapping.WriteSerialMapFile(filename, map_dict)
- a1 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
- 'Phone1', serial_map_file=filename)
- a2 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
- 'Phone2', serial_map_file=filename)
- a3 = battor_device_mapping.GetBattOrPathFromPhoneSerial(
- 'Phone3', serial_map_file=filename)
- finally:
- os.remove(filename)
- self.assertEquals(a1, '/dev/ttyUSB1')
- self.assertEquals(a2, '/dev/ttyUSB2')
- self.assertEquals(a3, '/dev/ttyUSB3')
-
- def testReadSerialMapFile(self):
- curr_dir = os.path.dirname(os.path.realpath(__file__))
- map_dict = battor_device_mapping.ReadSerialMapFile(
- os.path.join(curr_dir, 'test', 'data', 'test_serial_map.json'))
- self.assertEquals(len(map_dict.keys()), 3)
- self.assertEquals(map_dict['Phone1'], 'BattOr1')
- self.assertEquals(map_dict['Phone2'], 'BattOr2')
- self.assertEquals(map_dict['Phone3'], 'BattOr3')
-
-original_PPTSM = find_usb_devices.GetAllPhysicalPortToSerialMaps
-original_PPTTM = find_usb_devices.GetAllPhysicalPortToTTYMaps
-original_GBL = battor_device_mapping.GetBattOrList
-original_GBNDM = find_usb_devices.GetBusNumberToDeviceTreeMap
-original_IB = battor_device_mapping.IsBattOr
-original_GBSM = battor_device_mapping.GetBattOrSerialNumbers
-
-def setup_battor_test(serial, tty, battor, bser=None):
- serial_mapper = mock.Mock(return_value=serial)
- tty_mapper = mock.Mock(return_value=tty)
- battor_lister = mock.Mock(return_value=battor)
- devtree = mock.Mock(return_value=None)
- is_battor = mock.Mock(side_effect=lambda x, y: x in battor)
- battor_serials = mock.Mock(return_value=bser)
- find_usb_devices.GetAllPhysicalPortToSerialMaps = serial_mapper
- find_usb_devices.GetAllPhysicalPortToTTYMaps = tty_mapper
- battor_device_mapping.GetBattOrList = battor_lister
- find_usb_devices.GetBusNumberToDeviceTreeMap = devtree
- battor_device_mapping.IsBattOr = is_battor
- battor_device_mapping.GetBattOrSerialNumbers = battor_serials
-
-class BattOrMappingTest(unittest.TestCase):
- def tearDown(self):
- find_usb_devices.GetAllPhysicalPortToSerialMaps = original_PPTSM
- find_usb_devices.GetAllPhysicalPortToTTYMaps = original_PPTTM
- battor_device_mapping.GetBattOrList = original_GBL
- find_usb_devices.GetBusNumberToDeviceTreeMap = original_GBNDM
- battor_device_mapping.IsBattOr = original_IB
- battor_device_mapping.GetBattOrSerialNumbers = original_GBSM
-
- def test_generate_serial_map(self):
- setup_battor_test([{1:'Phn1', 2:'Phn2', 3:'Phn3'},
- {1:'Bat1', 2:'Bat2', 3:'Bat3'}],
- [{},
- {1:'ttyUSB0', 2:'ttyUSB1', 3:'ttyUSB2'}],
- ['ttyUSB0', 'ttyUSB1', 'ttyUSB2'],
- ['Bat1', 'Bat2', 'Bat3'])
- result = battor_device_mapping.GenerateSerialMap()
- self.assertEqual(len(result), 3)
- self.assertEqual(result['Phn1'], 'Bat1')
- self.assertEqual(result['Phn2'], 'Bat2')
- self.assertEqual(result['Phn3'], 'Bat3')
+ self.assertEquals(dev_usb_device_p7_h1_t0.serial, 'UsbDevice0')
if __name__ == "__main__":
diff --git a/systrace/catapult/devil/devil/utils/lazy/weak_constant.py b/systrace/catapult/devil/devil/utils/lazy/weak_constant.py
index 3558f29..24ad940 100644
--- a/systrace/catapult/devil/devil/utils/lazy/weak_constant.py
+++ b/systrace/catapult/devil/devil/utils/lazy/weak_constant.py
@@ -4,6 +4,9 @@
import threading
+from devil.utils import reraiser_thread
+from devil.utils import timeout_retry
+
class WeakConstant(object):
"""A thread-safe, lazily initialized object.
@@ -13,17 +16,27 @@ class WeakConstant(object):
"""
def __init__(self, initializer):
- self._initialized = False
+ self._initialized = threading.Event()
self._initializer = initializer
self._lock = threading.Lock()
self._val = None
def read(self):
"""Get the object, creating it if necessary."""
- if self._initialized:
+ if self._initialized.is_set():
return self._val
with self._lock:
- if not self._initialized:
- self._val = self._initializer()
- self._initialized = True
+ if not self._initialized.is_set():
+ # We initialize the value on a separate thread to protect
+ # from holding self._lock indefinitely in the event that
+ # self._initializer hangs.
+ initializer_thread = reraiser_thread.ReraiserThread(
+ self._initializer)
+ initializer_thread.start()
+ timeout_retry.WaitFor(
+ lambda: initializer_thread.join(1) or not initializer_thread.isAlive(),
+ wait_period=0)
+ self._val = initializer_thread.GetReturnValue()
+ self._initialized.set()
+
return self._val
diff --git a/systrace/catapult/devil/devil/utils/lazy/weak_constant_test.py b/systrace/catapult/devil/devil/utils/lazy/weak_constant_test.py
new file mode 100644
index 0000000..643351d
--- /dev/null
+++ b/systrace/catapult/devil/devil/utils/lazy/weak_constant_test.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# pylint: disable=protected-access
+
+import time
+import unittest
+
+from devil import devil_env
+from devil.utils import lazy
+from devil.utils import timeout_retry
+
+with devil_env.SysPath(devil_env.PYMOCK_PATH):
+ import mock
+
+
+class DynamicSideEffect(object):
+ """A helper object for handling a sequence of single-use side effects."""
+
+ def __init__(self, side_effects):
+ self._side_effects = iter(side_effects or [])
+
+ def __call__(self):
+ val = next(self._side_effects)()
+ if isinstance(val, Exception):
+ raise val
+ return val
+
+
+class WeakConstantTest(unittest.TestCase):
+
+ def testUninitialized(self):
+ """Ensure that the first read calls the initializer."""
+ initializer = mock.Mock(return_value='initializer called')
+ test_constant = lazy.WeakConstant(initializer)
+ self.assertEquals(
+ 'initializer called',
+ test_constant.read())
+ initializer.assert_called_once_with()
+
+ def testInitialized(self):
+ """Ensure that reading doesn't reinitialize the value."""
+ initializer = mock.Mock(return_value='initializer called')
+ test_constant = lazy.WeakConstant(initializer)
+ test_constant._initialized.set()
+ test_constant._val = 'initializer not called'
+ self.assertEquals(
+ 'initializer not called',
+ test_constant.read())
+ self.assertFalse(initializer.mock_calls) # assert not called
+
+ def testFirstCallHangs(self):
+ """Ensure that reading works even if the first initializer call hangs."""
+ dyn = DynamicSideEffect([
+ lambda: time.sleep(10),
+ lambda: 'second try worked!'
+ ])
+
+ initializer = mock.Mock(side_effect=dyn)
+ test_constant = lazy.WeakConstant(initializer)
+ self.assertEquals(
+ 'second try worked!',
+ timeout_retry.Run(test_constant.read, 1, 1))
+ initializer.assert_has_calls([mock.call(), mock.call()])
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/systrace/catapult/devil/devil/utils/logging_common.py b/systrace/catapult/devil/devil/utils/logging_common.py
index 5aea3c6..ab364a2 100644
--- a/systrace/catapult/devil/devil/utils/logging_common.py
+++ b/systrace/catapult/devil/devil/utils/logging_common.py
@@ -8,13 +8,32 @@ import time
def AddLoggingArguments(parser):
- parser.add_argument(
+ """Adds standard logging flags to the parser.
+
+ After parsing args, remember to invoke InitializeLogging() with the parsed
+ args, to configure the log level.
+ """
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument(
'-v', '--verbose', action='count', default=0,
help='Log more. Use multiple times for even more logging.')
+ group.add_argument(
+ '-q', '--quiet', action='count', default=0,
+ help=('Log less (suppress output). Use multiple times for even less '
+ 'output.'))
def InitializeLogging(args, handler=None):
- if args.verbose == 0:
+ """Initialized the log level based on commandline flags.
+
+ This expects to be given an "args" object with the options defined by
+ AddLoggingArguments().
+ """
+ if args.quiet >= 2:
+ log_level = logging.CRITICAL
+ elif args.quiet == 1:
+ log_level = logging.ERROR
+ elif args.verbose == 0:
log_level = logging.WARNING
elif args.verbose == 1:
log_level = logging.INFO
diff --git a/systrace/catapult/devil/devil/utils/markdown.py b/systrace/catapult/devil/devil/utils/markdown.py
index 54e7ed5..ba66664 100755
--- a/systrace/catapult/devil/devil/utils/markdown.py
+++ b/systrace/catapult/devil/devil/utils/markdown.py
@@ -3,6 +3,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+from __future__ import print_function
+
import argparse
import imp
import os
@@ -182,7 +184,7 @@ def md_module(module_obj, module_path=None, module_link=None):
A list of markdown-formatted lines.
"""
def should_doc(name):
- return (type(module_obj.__dict__[name]) != types.ModuleType
+ return (not isinstance(module_obj.__dict__[name], types.ModuleType)
and not name.startswith('_'))
stuff_to_doc = sorted(
@@ -193,9 +195,9 @@ def md_module(module_obj, module_path=None, module_link=None):
functions_to_doc = []
for s in stuff_to_doc:
- if type(s) == types.TypeType:
+ if isinstance(s, types.TypeType):
classes_to_doc.append(s)
- elif type(s) == types.FunctionType:
+ elif isinstance(s, types.FunctionType):
functions_to_doc.append(s)
command = ['devil/utils/markdown.py']
@@ -221,7 +223,7 @@ def md_module(module_obj, module_path=None, module_link=None):
for f in functions_to_doc:
content += md_function(f)
- print '\n'.join(content)
+ print('\n'.join(content))
return 0
@@ -243,7 +245,7 @@ def md_class(class_obj):
content.extend(md_docstring(class_obj.__doc__))
def should_doc(name, obj):
- return (type(obj) == types.FunctionType
+ return (isinstance(obj, types.FunctionType)
and (name.startswith('__') or not name.startswith('_')))
methods_to_doc = sorted(
diff --git a/systrace/catapult/devil/devil/utils/reraiser_thread.py b/systrace/catapult/devil/devil/utils/reraiser_thread.py
index 56d95f3..6e6c810 100644
--- a/systrace/catapult/devil/devil/utils/reraiser_thread.py
+++ b/systrace/catapult/devil/devil/utils/reraiser_thread.py
@@ -11,12 +11,14 @@ import threading
import time
import traceback
+from devil import base_error
from devil.utils import watchdog_timer
-class TimeoutError(Exception):
+class TimeoutError(base_error.BaseError):
"""Module-specific timeout exception."""
- pass
+ def __init__(self, message):
+ super(TimeoutError, self).__init__(message)
def LogThreadStack(thread, error_log_func=logging.critical):
@@ -47,10 +49,13 @@ class ReraiserThread(threading.Thread):
func: callable to call on a new thread.
args: list of positional arguments for callable, defaults to empty.
kwargs: dictionary of keyword arguments for callable, defaults to empty.
- name: thread name, defaults to Thread-N.
+ name: thread name, defaults to the function name.
"""
- if not name and func.__name__ != '<lambda>':
- name = func.__name__
+ if not name:
+ if hasattr(func, '__name__') and func.__name__ != '<lambda>':
+ name = func.__name__
+ else:
+ name = 'anonymous'
super(ReraiserThread, self).__init__(name=name)
if not args:
args = []
@@ -64,10 +69,17 @@ class ReraiserThread(threading.Thread):
self._exc_info = None
self._thread_group = None
- def ReraiseIfException(self):
- """Reraise exception if an exception was raised in the thread."""
- if self._exc_info:
- raise self._exc_info[0], self._exc_info[1], self._exc_info[2]
+ if sys.version_info < (3,):
+ # pylint: disable=exec-used
+ exec('''def ReraiseIfException(self):
+ """Reraise exception if an exception was raised in the thread."""
+ if self._exc_info:
+ raise self._exc_info[0], self._exc_info[1], self._exc_info[2]''')
+ else:
+ def ReraiseIfException(self):
+ """Reraise exception if an exception was raised in the thread."""
+ if self._exc_info:
+ raise self._exc_info[1]
def GetReturnValue(self):
"""Reraise exception if present, otherwise get the return value."""
diff --git a/systrace/catapult/devil/devil/utils/reset_usb.py b/systrace/catapult/devil/devil/utils/reset_usb.py
index 0335227..404a44c 100755
--- a/systrace/catapult/devil/devil/utils/reset_usb.py
+++ b/systrace/catapult/devil/devil/utils/reset_usb.py
@@ -3,12 +3,15 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import sys
+if sys.platform == 'win32':
+ raise ImportError('devil.utils.reset_usb only supported on unix systems.')
+
import argparse
import fcntl
import logging
import os
import re
-import sys
if __name__ == '__main__':
sys.path.append(
diff --git a/systrace/catapult/devil/devil/utils/run_tests_helper.py b/systrace/catapult/devil/devil/utils/run_tests_helper.py
index 7f71b65..0b9dd47 100644
--- a/systrace/catapult/devil/devil/utils/run_tests_helper.py
+++ b/systrace/catapult/devil/devil/utils/run_tests_helper.py
@@ -14,7 +14,7 @@ CustomFormatter = logging_common.CustomFormatter
_WrappedLoggingArgs = collections.namedtuple(
- '_WrappedLoggingArgs', ['verbose'])
+ '_WrappedLoggingArgs', ['verbose', 'quiet'])
def SetLogLevel(verbose_count, add_handler=True):
@@ -25,5 +25,5 @@ def SetLogLevel(verbose_count, add_handler=True):
add_handler: If true, adds a handler with |CustomFormatter|.
"""
logging_common.InitializeLogging(
- _WrappedLoggingArgs(verbose_count),
+ _WrappedLoggingArgs(verbose_count, 0),
handler=None if add_handler else logging.NullHandler())
diff --git a/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json b/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json
deleted file mode 100644
index f068281..0000000
--- a/systrace/catapult/devil/devil/utils/test/data/test_serial_map.json
+++ /dev/null
@@ -1 +0,0 @@
-[{"phone": "Phone1", "battor": "BattOr1"}, {"phone": "Phone2", "battor": "BattOr2"}, {"phone": "Phone3", "battor": "BattOr3"}]
diff --git a/systrace/catapult/devil/devil/utils/timeout_retry.py b/systrace/catapult/devil/devil/utils/timeout_retry.py
index 2327b6b..d662c1d 100644
--- a/systrace/catapult/devil/devil/utils/timeout_retry.py
+++ b/systrace/catapult/devil/devil/utils/timeout_retry.py
@@ -109,7 +109,8 @@ def WaitFor(condition, wait_period=5, max_tries=None):
# pylint: disable=no-member
timeout_thread_group.GetRemainingTime(wait_period,
suffix=' waiting for condition %r' % condition_name)
- time.sleep(wait_period)
+ if wait_period:
+ time.sleep(wait_period)
return None
diff --git a/systrace/catapult/devil/devil/utils/update_mapping.py b/systrace/catapult/devil/devil/utils/update_mapping.py
deleted file mode 100755
index 6666b9b..0000000
--- a/systrace/catapult/devil/devil/utils/update_mapping.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import argparse
-import sys
-
-from devil.utils import battor_device_mapping
-
-def parse_options():
- """Parses and checks the command-line options.
-
- Returns:
- A tuple containing the options structure.
- """
- usage = 'Usage: ./update_mapping.py [options]'
- desc = ('Example: ./update_mapping.py -o mapping.json.\n'
- 'This script generates and stores a file that gives the\n'
- 'mapping between phone serial numbers and BattOr serial numbers\n'
- 'Mapping is based on which physical ports on the USB hubs the\n'
- 'devices are plugged in to. For instance, if there are two hubs,\n'
- 'the phone connected to port N on the first hub is mapped to the\n'
- 'BattOr connected to port N on the second hub, for each N.')
- parser = argparse.ArgumentParser(usage=usage, description=desc)
- parser.add_argument('-o', '--output', dest='out_file',
- default='mapping.json', type=str,
- action='store', help='mapping file name')
- parser.add_argument('-u', '--hub', dest='hub_types',
- action='append', choices=['plugable_7port',
- 'plugable_7port_usb3_part2',
- 'plugable_7port_usb3_part3'],
- help='USB hub types.')
- options = parser.parse_args()
- if not options.hub_types:
- options.hub_types = ['plugable_7port', 'plugable_7port_usb3_part2',
- 'plugable_7port_usb3_part3']
- return options
-
-def main():
- options = parse_options()
- battor_device_mapping.GenerateSerialMapFile(options.out_file,
- options.hub_types)
-
-if __name__ == "__main__":
- sys.exit(main())