Hash :
e3c7134c
Author :
Date :
2019-04-16T17:12:03
Add script to collect information from tests running on bots. Add scripts/generate_deqp_stats.py, which takes in a bot name and prints out a struct with the number of tests run/passed/failed/etc for each test suite that ran on the bot. Bug: angleproject:3369 Change-Id: Ie8086e1d0fb6b141afa388ad44711a798a3bbcd1 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1570111 Commit-Queue: Jonah Ryan-Davis <jonahr@google.com> Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
#!/usr/bin/python2
#
# Copyright 2019 The ANGLE Project Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# generate_deqp_stats.py:
# Checks output of deqp testers and generates stats using the GDocs API
import re
import datetime
import os
import subprocess
import sys
BOT_NAMES = [
'Win10 FYI dEQP Release (NVIDIA)',
'Win10 FYI dEQP Release (Intel HD 630)',
'Win7 FYI dEQP Release (AMD)',
'Win7 FYI x64 dEQP Release (NVIDIA)',
'Mac FYI dEQP Release Intel',
'Mac FYI dEQP Release AMD',
'Linux FYI dEQP Release (Intel HD 630)',
'Linux FYI dEQP Release (NVIDIA)',
'Android FYI dEQP Release (Nexus 5X)',
'Android FYI 32 dEQP Vk Release (Pixel XL)',
'Android FYI 64 dEQP Vk Release (Pixel XL)',
'Android FYI 32 dEQP Vk Release (Pixel 2)',
'Android FYI 64 dEQP Vk Release (Pixel 2)',
]
BOT_NAME_PREFIX = 'chromium/ci/'
INFO_TAG = '*RESULT'
# Returns a struct with info about the latest successful build given a bot name
# Info contains the build_name, time, date, and angle_revision, if available.
# Uses: bb ls '<botname>' -n 1 -status success -A
def get_latest_success_build_info(bot_name):
bb = subprocess.Popen(
['bb', 'ls', bot_name, '-n', '1', '-status', 'success', '-A'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = bb.communicate()
if err:
raise ValueError("Unexpected error from bb ls: '" + err + "'")
if not out:
raise ValueError("Unexpected empty result from bb ls of bot '" + bot_name +
"'")
if 'SUCCESS' not in out:
raise ValueError("Unexpected result from bb ls: '" + out + "'")
info = {}
for line in out.splitlines():
# The first line holds the build name
if 'build_name' not in info:
info['build_name'] = line.strip().split("'")[1]
if 'Created' in line:
info['time'] = re.findall(r'[0-9]{1,2}:[0-9]{2}:[0-9]{2}',
line.split(',', 1)[0])[0]
info['date'] = datetime.datetime.now().strftime('%y/%m/%d')
if 'parent_got_angle_revision' in line:
info['angle_revision'] = filter(str.isalnum, line.split(':')[1])
if 'build_name' not in info:
raise ValueError("Could not find build_name from bot '" + bot_name + "'")
return info
# Returns a list of step names that we're interested in given a build name. We
# are interested in step names starting with 'angle_'.
# Uses: bb get '<build_name>' -steps
# May raise an exception.
def get_step_names(build_name):
bb = subprocess.Popen(['bb', 'get', build_name, '-steps'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = bb.communicate()
if err:
raise ValueError("Unexpected error from bb get: '" + err + "'")
step_names = []
for line in out.splitlines():
if 'Step "angle_' not in line:
continue
step_names.append(line.split('"')[1])
return step_names
# Performs some heuristic validation of the step_info struct returned from a
# single step log. Returns True if valid, False if invalid. May write to stderr
def validate_step_info(step_info, build_name, step_name):
print_name = "'" + build_name + "': '" + step_name + "'"
if not step_info:
sys.stderr.write('WARNING: Step info empty for ' + print_name + '\n')
return False
if 'Total' in step_info:
partial_sum_keys = [
'Passed', 'Failed', 'Skipped', 'Not Supported', 'Exception', 'Crashed'
]
partial_sum_values = [
int(step_info[key]) for key in partial_sum_keys if key in step_info
]
computed_total = sum(partial_sum_values)
if step_info['Total'] != computed_total:
sys.stderr.write('WARNING: Step info does not sum to total for ' +
print_name + ' | Total: ' + str(step_info['Total']) +
' - Computed total: ' + str(computed_total) + '\n')
return True
# Returns a struct containing parsed info from a given step log. The info is
# parsed by looking for lines with the following format in stdout:
# '[TESTSTATS]: <key>: <value>''
# May write to stderr
# Uses: bb log '<build_name>' '<step_name>'
def get_step_info(build_name, step_name):
bb = subprocess.Popen(['bb', 'log', build_name, step_name],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = bb.communicate()
if err:
sys.stderr.write("WARNING: Unexpected error from bb log '" + build_name +
"' '" + step_name + "': '" + err + "'")
return None
step_info = {}
for line in out.splitlines():
if INFO_TAG not in line:
continue
found_stat = True
line_columns = line.split(INFO_TAG, 1)[1].split(':')
if len(line_columns) is not 3:
sys.stderr.write("WARNING: Line improperly formatted: '" + line + "'\n")
continue
key = line_columns[1].strip()
if key not in step_info:
step_info[key] = 0
val = int(filter(str.isdigit, line_columns[2]))
if val is not None:
step_info[key] += val
else:
step_info[key] += ', ' + line_columns[2]
if validate_step_info(step_info, build_name, step_name):
return step_info
return None
# Returns the info for each step run on a given bot_name.
def get_bot_info(bot_name):
info = get_latest_success_build_info(bot_name)
info['step_names'] = get_step_names(info['build_name'])
for step_name in info['step_names']:
info[step_name] = get_step_info(info['build_name'], step_name)
return info
def main():
info = {}
for bot_name in BOT_NAMES:
try:
info[bot_name] = get_bot_info(BOT_NAME_PREFIX + bot_name)
except Exception as error:
sys.stderr.write('ERROR: %s\n' % str(error))
print(str(info))
if __name__ == '__main__':
sys.exit(main())