Fix mojo_ tools on the first run after adb kill-server. Fixes domokit/devtools#47. R=qsr@chromium.org BUG= Review URL: https://codereview.chromium.org/1381863004 . Cr-Mirrored-From: https://github.com/domokit/mojo Cr-Mirrored-Commit: c3e3eac077c5cbb6250e50d3630f8c45b1fce693
diff --git a/devtoolslib/android_shell.py b/devtoolslib/android_shell.py index a4a7d16..2833138 100644 --- a/devtoolslib/android_shell.py +++ b/devtoolslib/android_shell.py
@@ -38,6 +38,9 @@ _MOJO_SHELL_PACKAGE_NAME = 'org.chromium.mojo.shell' +# Used to parse the output of `adb devices`. +_ADB_DEVICES_HEADER = 'List of devices attached' + _logger = logging.getLogger() @@ -63,6 +66,24 @@ netstat_output = subprocess.check_output(['netstat']) return _find_available_port(netstat_output) +def parse_adb_devices_output(adb_devices_output): + """Parses the output of the `adb devices` command, returning a dictionary + mapping device id to the status of the device, as printed by `adb devices`. + """ + # Split into lines skipping empty ones. + lines = [line.strip() for line in adb_devices_output.split('\n') + if line.strip()] + + if _ADB_DEVICES_HEADER not in lines: + return None + + # The header can be preceeded by output informing of adb server being spawned, + # but all non-empty lines after the header describe connected devices. + device_specs = lines[lines.index(_ADB_DEVICES_HEADER) + 1:] + split_specs = [spec.split() for spec in device_specs] + return {split_spec[0]: split_spec[1] for split_spec in split_specs + if len(split_spec) == 2} + class AndroidShell(Shell): """Wrapper around Mojo shell running on an Android device. @@ -213,25 +234,24 @@ """ adb_devices_output = subprocess.check_output( self._adb_command(['devices'])) - # Skip the header line, strip empty lines at the end. - device_list = [line.strip() for line in adb_devices_output.split('\n')[1:] - if line.strip()] + devices = parse_adb_devices_output(adb_devices_output) + + if not devices: + return False, 'No devices connected.' if self.target_device: - if any([line.startswith(self.target_device) and - line.endswith('device') for line in device_list]): + if (self.target_device in devices and + devices[self.target_device] == 'device'): return True, None else: - return False, 'Cannot connect to the selected device.' + return False, ('Cannot connect to the selected device, status: ' + + devices[self.target_device]) - if len(device_list) > 1: + if len(devices) > 1: return False, ('More than one device connected and target device not ' 'specified.') - if not len(device_list): - return False, 'No devices connected.' - - if not device_list[0].endswith('device'): + if not devices.itervalues().next() == 'device': return False, 'Connected device is not available.' if require_root and not self._run_adb_as_root():
diff --git a/devtoolslib/android_shell_unittest.py b/devtoolslib/android_shell_unittest.py new file mode 100644 index 0000000..caf2483 --- /dev/null +++ b/devtoolslib/android_shell_unittest.py
@@ -0,0 +1,61 @@ +# 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. + +"""Tests for the Android shell abstraction.""" + +import imp +import os.path +import sys +import unittest + +try: + imp.find_module("devtoolslib") +except ImportError: + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from devtoolslib.android_shell import parse_adb_devices_output + +class AndroidShellTest(unittest.TestCase): + """Tests the Android shell abstraction.""" + + def test_parse_adb_devices_output(self): + """Tests parsing of the `adb devices` output.""" + one_device_output = """\ +List of devices attached +42424242 device + +""" + results = parse_adb_devices_output(one_device_output) + self.assertEquals(1, len(results)) + self.assertTrue('42424242' in results) + self.assertEquals('device', results['42424242']) + + multi_devices_output = """\ +List of devices attached +42424242 device +42424243 offline +42424244 device + +""" + results = parse_adb_devices_output(multi_devices_output) + self.assertEquals(3, len(results)) + self.assertTrue('42424242' in results) + self.assertEquals('device', results['42424242']) + self.assertTrue('42424243' in results) + self.assertEquals('offline', results['42424243']) + self.assertTrue('42424244' in results) + self.assertEquals('device', results['42424244']) + + # Output produced when adb server needs to be started. + adb_server_startup_output = """\ +* daemon not running. starting it now on port 5037 * +* daemon started successfully * +List of devices attached +42424242 device + +""" + results = parse_adb_devices_output(adb_server_startup_output) + self.assertEquals(1, len(results)) + self.assertTrue('42424242' in results) + self.assertEquals('device', results['42424242'])