Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: third_party/WebKit/Tools/Scripts/webkitpy/w3c/update_w3c_test_expectations.py

Issue 2136723002: Create test expectations line automatically from failing test results (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Refactored script to pass in Host object to main use its attributes Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 '''A script to modify TestExpectations lines based layout test failures in try j obs.
qyearsley 2016/07/18 22:55:29 Usually triple-double-quotes (""") are used for do
6
7 This script outputs a list of test expectation lines to add to a 'TestExpectatio ns' file
8 by retrieving the try job results for the current CL.
9 '''
10
11 import logging
12
13 from webkitpy.common.net import buildbot
14 from webkitpy.common.net import rietveld
15
16
17 _log = logging.getLogger(__name__)
18
19
20 def main(host, port):
21 expectations_file = port.path_to_generic_test_expectations_file()
22 expectations_line_adder = W3CExpectationsLineAdder(host)
23 issue_number = expectations_line_adder.get_issue_number()
24 try_bots = expectations_line_adder.get_try_bots()
25 try_jobs_info = expectations_line_adder.get_try_jobs_information(issue_numbe r, try_bots)
26 line_expectations_dict = {}
27 if not try_jobs_info:
28 print 'No Try Job information was collected.'
29 return 1
30 for try_job in try_jobs_info:
31 builder_name = try_job[0]
32 build_number = try_job[1]
33 builder = buildbot.Builder(builder_name, expectations_line_adder.get_bui ld_bot)
34 build = buildbot.Build(builder, build_number)
35 platform_results_dict = expectations_line_adder.get_failing_results_dict (builder, build)
36 line_expectations_dict = expectations_line_adder.merge_dicts(line_expect ations_dict, platform_results_dict)
37 for platform_results_dicts in line_expectations_dict.values():
38 platform_results_dicts = expectations_line_adder.merge_same_valued_keys( platform_results_dicts)
39 line_list = expectations_line_adder.create_line_list(line_expectations_dict)
40 expectations_line_adder.write_to_test_expectations(host, expectations_file, line_list)
41
42
43 class W3CExpectationsLineAdder(object):
44
45 def __init__(self, host):
46 self._host = host
47
48 def get_build_bot(self):
49 return self._host.buildbot
50
51 def get_try_jobs_information(self, issue_number, try_bots):
52 return rietveld.latest_try_jobs(issue_number, try_bots, self._host.web)
53
54 def get_issue_number(self):
55 return self._host._scm.get_issue_number()
56
57 def get_try_bots(self):
58 return self._host.builders.all_try_builder_names()
59
60 def _generate_results_dict(self, platform, result_list):
61 test_dict = {}
62 if '-' in platform:
63 platform = platform[platform.find('-') + 1:].capitalize()
64 for result in result_list:
65 test_dict[result.test_name()] = {
66 platform: {
67 'expected': result.expected_results(),
68 'actual': result.actual_results(),
69 'bug': 'crbug.com/626703'
70 }}
71 return test_dict
72
73 def get_failing_results_dict(self, builder, build):
74 ''' returns a dict of dicts with the format
75 {'key': {'expected': results, 'actual': results, 'bug': bug_url, ...}}
76 '''
77 layout_test_results = builder.fetch_layout_test_results(build.results_ur l())
78 builder_name = layout_test_results.builder_name()
79 platform = self._host.builders.port_name_for_builder_name(builder_name)
80 result_list = layout_test_results.didnt_run_as_expected_results()
81 failing_results_dict = self._generate_results_dict(platform, result_list )
82 return failing_results_dict
83
84 def merge_dicts(self, final, temp, path=None):
85 path = path or []
86 for key in temp:
87 if key in final:
88 if (isinstance(final[key], dict)) and isinstance(temp[key], dict ):
89 self.merge_dicts(final[key], temp[key], path + [str(key)])
90 elif final[key] == temp[key]:
91 pass
92 else:
93 raise Exception('conflict at %s' % '.'.join(path))
94 else:
95 final[key] = temp[key]
96 return final
97
98 def merge_same_valued_keys(self, dictionary):
99 '''This function takes a dictionary of dictionaries and creates a new tu ple key
qyearsley 2016/07/18 22:55:29 You could omit "This function", and just start wit
100 if two or more values match. Example: {
qyearsley 2016/07/18 22:55:28 You should add a blank line after the first senten
101 'one': {'foo': 'bar'},
102 'two': {'foo': 'bar'},
103 'three': {'foo': bar'}
104 } is converted to
105 {('one', 'two', 'three'): {'foo': 'bar'}}
106 '''
107 matching_value_keys = set()
108 keys = dictionary.keys()
109 isLastItem = False
110 for index, item in enumerate(keys):
111 if isLastItem:
112 break
113 for i in range(index + 1, len(keys)):
114 next_item = keys[i]
115 if dictionary[item] == dictionary[next_item]:
116 matching_value_keys.update([item, next_item])
117 dictionary[tuple(matching_value_keys)] = dictionary[item]
118 isLastItem = next_item == keys[-1]
119 del dictionary[item]
120 del dictionary[next_item]
121 return dictionary
122
123 def get_expectations(self, results):
124 expectations = []
125 failure_expectations = ['TEXT', 'FAIL', 'IMAGE+TEXT', 'IMAGE']
126 pass_crash_timeout = ['TIMEOUT', 'CRASH', 'PASS']
127 if results['expected'] in pass_crash_timeout and results['actual'] in fa ilure_expectations:
128 expectations.append('Failure')
129 if results['expected'] in failure_expectations and results['actual'] in pass_crash_timeout:
130 expectations.append(results['actual'].capitalize())
131 if results['expected'] in pass_crash_timeout and results['actual'] in pa ss_crash_timeout:
132 expectations.append(results['actual'].capitalize())
133 expectations.append(results['expected'].capitalize())
134 return expectations
135
136 def create_line_list(self, dictionary):
137 '''Returns a list of test expectations lines with the format
138 ['BUG_URL [PLATFORM(S)] TEST_MAME [EXPECTATION(S)]']
139 '''
140 line_list = []
141 for key, value in dictionary.iteritems():
142 test_name = key
143 for key2 in value:
144 platform = []
145 bug = []
146 expectations = []
147 if isinstance(key2, tuple):
148 platform = list(key2)
149 else:
150 platform.append(key2)
151 bug.append(value[key2]['bug'])
152 expectations = self.get_expectations(value[key2])
153 line = '%s [ %s ] %s [ %s ]' % (bug[0], ' '.join(platform), test _name, ' '.join(expectations))
154 line_list.append(str(line))
155 return line_list
156
157 def write_to_test_expectations(self, host, path, line_list):
158 '''Writes to test expectations file on the filesystem. Checks the file f or
159 '#Tests added from W3C auto import bot' and writes expectation lines dir ectly under it. If not found,
160 it writes to the end of the file.
161 '''
162 file_contents = host.filesystem.read_text_file(path)
163 w3c_comment_line_index = file_contents.find('# Tests added from W3C auto import bot')
164 all_lines = ''
165 for line in line_list:
166 all_lines += str(line) + '\n'
167 all_lines = all_lines[:-1]
168 if w3c_comment_line_index == -1:
169 file_contents += '\n\n# Tests added from W3C auto import bot\n'
170 file_contents += all_lines
171 else:
172 end_of_comment_line = (file_contents[w3c_comment_line_index:].find(' \n')) + w3c_comment_line_index
173 new_data = file_contents[: end_of_comment_line + 1] + all_lines + fi le_contents[end_of_comment_line:]
174 file_contents = new_data
175 host.filesystem.write_text_file(path, file_contents)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698