| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """A class to keep track of devices across builds and report state.""" | 7 """A class to keep track of devices across builds and report state.""" |
| 8 import logging | 8 import logging |
| 9 import optparse | 9 import optparse |
| 10 import os | 10 import os |
| 11 import smtplib | 11 import smtplib |
| 12 import sys | 12 import sys |
| 13 import re | 13 import re |
| 14 | 14 |
| 15 from pylib import buildbot_report | 15 from pylib import buildbot_report |
| 16 from pylib import android_commands | 16 from pylib import android_commands |
| 17 from pylib.cmd_helper import GetCmdOutput | 17 from pylib.cmd_helper import GetCmdOutput |
| 18 | 18 |
| 19 | 19 |
| 20 def DeviceInfo(serial): | 20 def DeviceInfo(serial): |
| 21 """Gathers info on a device via various adb calls. | 21 """Gathers info on a device via various adb calls. |
| 22 | 22 |
| 23 Args: | 23 Args: |
| 24 serial: The serial of the attached device to construct info about. | 24 serial: The serial of the attached device to construct info about. |
| 25 | 25 |
| 26 Returns: | 26 Returns: |
| 27 Tuple of device type, build id and report as a string. | 27 Tuple of device type, build id, report as a string, error messages, and |
| 28 boolean indicating whether or not device can be used for testing. |
| 28 """ | 29 """ |
| 29 | 30 |
| 30 def AdbShellCmd(cmd): | 31 def AdbShellCmd(cmd): |
| 31 return GetCmdOutput('adb -s %s shell %s' % (serial, cmd), | 32 return GetCmdOutput('adb -s %s shell %s' % (serial, cmd), |
| 32 shell=True).strip() | 33 shell=True).strip() |
| 33 | 34 |
| 34 device_type = AdbShellCmd('getprop ro.build.product') | 35 device_type = AdbShellCmd('getprop ro.build.product') |
| 35 device_build = AdbShellCmd('getprop ro.build.id') | 36 device_build = AdbShellCmd('getprop ro.build.id') |
| 36 device_product_name = AdbShellCmd('getprop ro.product.name') | 37 device_product_name = AdbShellCmd('getprop ro.product.name') |
| 37 | 38 |
| 38 setup_wizard_disabled = AdbShellCmd( | 39 setup_wizard_disabled = AdbShellCmd( |
| 39 'getprop ro.setupwizard.mode') == 'DISABLED' | 40 'getprop ro.setupwizard.mode') == 'DISABLED' |
| 40 battery = AdbShellCmd('dumpsys battery') | 41 battery = AdbShellCmd('dumpsys battery') |
| 42 install_output = GetCmdOutput(['build/android/adb_install_apk.py', '--apk', |
| 43 'build/android/CheckInstallApk-debug.apk']) |
| 44 install_speed_found = re.findall('(\d+) KB/s', install_output) |
| 45 if install_speed_found: |
| 46 install_speed = int(install_speed_found[0]) |
| 47 else: |
| 48 install_speed = 'Unknown' |
| 41 if 'Error' in battery: | 49 if 'Error' in battery: |
| 42 ac_power = 'Unknown' | 50 ac_power = 'Unknown' |
| 43 battery_level = 'Unknown' | 51 battery_level = 'Unknown' |
| 44 battery_temp = 'Unknown' | 52 battery_temp = 'Unknown' |
| 45 else: | 53 else: |
| 46 ac_power = re.findall('AC powered: (\w+)', battery)[0] | 54 ac_power = re.findall('AC powered: (\w+)', battery)[0] |
| 47 battery_level = int(re.findall('level: (\d+)', battery)[0]) | 55 battery_level = int(re.findall('level: (\d+)', battery)[0]) |
| 48 battery_temp = float(re.findall('temperature: (\d+)', battery)[0]) / 10 | 56 battery_temp = float(re.findall('temperature: (\d+)', battery)[0]) / 10 |
| 49 report = ['Device %s (%s)' % (serial, device_type), | 57 report = ['Device %s (%s)' % (serial, device_type), |
| 50 ' Build: %s (%s)' % (device_build, | 58 ' Build: %s (%s)' % (device_build, |
| 51 AdbShellCmd('getprop ro.build.fingerprint')), | 59 AdbShellCmd('getprop ro.build.fingerprint')), |
| 52 ' Battery: %s%%' % battery_level, | 60 ' Battery: %s%%' % battery_level, |
| 53 ' Battery temp: %s' % battery_temp, | 61 ' Battery temp: %s' % battery_temp, |
| 54 ' IMEI slice: %s' % AdbShellCmd('dumpsys iphonesubinfo ' | 62 ' IMEI slice: %s' % AdbShellCmd('dumpsys iphonesubinfo ' |
| 55 '| grep Device' | 63 '| grep Device' |
| 56 "| awk '{print $4}'")[-6:], | 64 "| awk '{print $4}'")[-6:], |
| 57 ' Wifi IP: %s' % AdbShellCmd('getprop dhcp.wlan0.ipaddress'), | 65 ' Wifi IP: %s' % AdbShellCmd('getprop dhcp.wlan0.ipaddress'), |
| 66 ' Install Speed: %s KB/s' % install_speed, |
| 58 ''] | 67 ''] |
| 59 | 68 |
| 60 errors = [] | 69 errors = [] |
| 61 if battery_level < 5: | 70 if battery_level < 5: |
| 62 errors += ['Device critically low in battery.'] | 71 errors += ['Device critically low in battery. Do not use for testing.'] |
| 63 if not setup_wizard_disabled: | 72 if not setup_wizard_disabled: |
| 64 errors += ['Setup wizard not disabled. Was it provisioned correctly?'] | 73 errors += ['Setup wizard not disabled. Was it provisioned correctly?'] |
| 65 if device_product_name == 'mantaray' and ac_power != 'true': | 74 if device_product_name == 'mantaray' and ac_power != 'true': |
| 66 errors += ['Mantaray device not connected to AC power.'] | 75 errors += ['Mantaray device not connected to AC power.'] |
| 76 if install_speed < 800: |
| 77 errors += ['Device install speed too low. Do not use for testing.'] |
| 67 | 78 |
| 68 return device_type, device_build, '\n'.join(report), errors | 79 # TODO(navabi): Determine if device status check step should fail on slow |
| 80 # install speed. The original CL caused the step to fail but was reverted |
| 81 # because it caused too many early failures. Determine if it was just flake. |
| 82 # Also, do not fail on 'Unknown' caused by offline device, because other |
| 83 # devices can still be used for tests. |
| 84 fail_step = (battery_level == 'Unknown' or battery_level >= 5) |
| 85 return device_type, device_build, '\n'.join(report), errors, fail_step |
| 69 | 86 |
| 70 | 87 |
| 71 def CheckForMissingDevices(options, adb_online_devs): | 88 def CheckForMissingDevices(options, adb_online_devs): |
| 72 """Uses file of previous online devices to detect broken phones. | 89 """Uses file of previous online devices to detect broken phones. |
| 73 | 90 |
| 74 Args: | 91 Args: |
| 75 options: out_dir parameter of options argument is used as the base | 92 options: out_dir parameter of options argument is used as the base |
| 76 directory to load and update the cache file. | 93 directory to load and update the cache file. |
| 77 adb_online_devs: A list of serial numbers of the currently visible | 94 adb_online_devs: A list of serial numbers of the currently visible |
| 78 and online attached devices. | 95 and online attached devices. |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 160 help='Directory where the device path is stored', | 177 help='Directory where the device path is stored', |
| 161 default=os.path.join(os.path.dirname(__file__), '..', | 178 default=os.path.join(os.path.dirname(__file__), '..', |
| 162 '..', 'out')) | 179 '..', 'out')) |
| 163 | 180 |
| 164 options, args = parser.parse_args() | 181 options, args = parser.parse_args() |
| 165 if args: | 182 if args: |
| 166 parser.error('Unknown options %s' % args) | 183 parser.error('Unknown options %s' % args) |
| 167 devices = android_commands.GetAttachedDevices() | 184 devices = android_commands.GetAttachedDevices() |
| 168 types, builds, reports, errors = [], [], [], [] | 185 types, builds, reports, errors = [], [], [], [] |
| 169 if devices: | 186 if devices: |
| 170 types, builds, reports, errors = zip(*[DeviceInfo(dev) for dev in devices]) | 187 types, builds, reports, errors, fail_step_lst = zip(*[DeviceInfo(dev) |
| 188 for dev in devices]) |
| 171 | 189 |
| 172 err_msg = CheckForMissingDevices(options, devices) or [] | 190 err_msg = CheckForMissingDevices(options, devices) or [] |
| 173 | 191 |
| 174 unique_types = list(set(types)) | 192 unique_types = list(set(types)) |
| 175 unique_builds = list(set(builds)) | 193 unique_builds = list(set(builds)) |
| 176 | 194 |
| 177 buildbot_report.PrintMsg('Online devices: %d. Device types %s, builds %s' | 195 buildbot_report.PrintMsg('Online devices: %d. Device types %s, builds %s' |
| 178 % (len(devices), unique_types, unique_builds)) | 196 % (len(devices), unique_types, unique_builds)) |
| 179 print '\n'.join(reports) | 197 print '\n'.join(reports) |
| 180 | 198 |
| 181 for serial, dev_errors in zip(devices, errors): | 199 for serial, dev_errors in zip(devices, errors): |
| 182 if dev_errors: | 200 if dev_errors: |
| 183 err_msg += ['%s errors:' % serial] | 201 err_msg += ['%s errors:' % serial] |
| 184 err_msg += [' %s' % error for error in dev_errors] | 202 err_msg += [' %s' % error for error in dev_errors] |
| 185 | 203 |
| 186 if err_msg: | 204 if err_msg: |
| 187 buildbot_report.PrintWarning() | 205 buildbot_report.PrintWarning() |
| 188 msg = '\n'.join(err_msg) | 206 msg = '\n'.join(err_msg) |
| 189 print msg | 207 print msg |
| 190 SendDeviceStatusAlert(msg) | 208 SendDeviceStatusAlert(msg) |
| 191 | 209 |
| 210 if False in fail_step_lst: |
| 211 # TODO(navabi): Build fails on device status check step if there exists any |
| 212 # devices with critically low battery or install speed. Remove those devices |
| 213 # from testing, allowing build to continue with good devices. |
| 214 return 1 |
| 215 |
| 192 if not devices: | 216 if not devices: |
| 193 return 1 | 217 return 1 |
| 194 | 218 |
| 195 | 219 |
| 196 if __name__ == '__main__': | 220 if __name__ == '__main__': |
| 197 sys.exit(main()) | 221 sys.exit(main()) |
| OLD | NEW |