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

Side by Side Diff: webkit/tools/layout_tests/layout_package/json_results_generator.py

Issue 550027: Refactor layout test scripts to get the dashboard work for other types (Closed)
Patch Set: fix the nits Created 10 years, 11 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
1 # Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import logging 5 import logging
6 import os 6 import os
7 import re
8 import subprocess 7 import subprocess
9 import sys 8 import sys
10 import time 9 import time
11 import urllib2 10 import urllib2
12 import xml.dom.minidom 11 import xml.dom.minidom
13 12
14 from layout_package import path_utils 13 from layout_package import path_utils
15 from layout_package import test_expectations 14 from layout_package import test_expectations
16 from layout_package import test_failures
17 15
18 sys.path.append(path_utils.PathFromBase('third_party')) 16 sys.path.append(path_utils.PathFromBase('third_party'))
19 import simplejson 17 import simplejson
20 18
21 class JSONResultsGenerator: 19 class JSONResultsGenerator(object):
22 20
23 MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG = 750 21 MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG = 750
24 # Min time (seconds) that will be added to the JSON. 22 # Min time (seconds) that will be added to the JSON.
25 MIN_TIME = 1 23 MIN_TIME = 1
26 JSON_PREFIX = "ADD_RESULTS(" 24 JSON_PREFIX = "ADD_RESULTS("
27 JSON_SUFFIX = ");" 25 JSON_SUFFIX = ");"
28 LAYOUT_TESTS_PATH = "LayoutTests"
29 PASS_RESULT = "P" 26 PASS_RESULT = "P"
30 SKIP_RESULT = "X" 27 SKIP_RESULT = "X"
31 NO_DATA_RESULT = "N" 28 NO_DATA_RESULT = "N"
32 VERSION = 3 29 VERSION = 3
33 VERSION_KEY = "version" 30 VERSION_KEY = "version"
34 RESULTS = "results" 31 RESULTS = "results"
35 TIMES = "times" 32 TIMES = "times"
36 BUILD_NUMBERS = "buildNumbers" 33 BUILD_NUMBERS = "buildNumbers"
37 WEBKIT_SVN = "webkitRevision" 34 WEBKIT_SVN = "webkitRevision"
38 CHROME_SVN = "chromeRevision" 35 CHROME_SVN = "chromeRevision"
39 TIME = "secondsSinceEpoch" 36 TIME = "secondsSinceEpoch"
40 TESTS = "tests" 37 TESTS = "tests"
41 WONTFIX = "wontfixCounts" 38
42 DEFERRED = "deferredCounts" 39 FIXABLE_COUNT = "fixableCount"
43 FIXABLE = "fixableCounts" 40 FIXABLE = "fixableCounts"
44 ALL_FIXABLE_COUNT = "allFixableCount" 41 ALL_FIXABLE_COUNT = "allFixableCount"
45 FIXABLE_COUNT = "fixableCount"
46 42
47 # Note that we omit test_expectations.FAIL from this list because 43 # Note that we omit test_expectations.FAIL from this list because
48 # it should never show up (it's a legacy input expectation, never 44 # it should never show up (it's a legacy input expectation, never
49 # an output expectation). 45 # an output expectation).
50 FAILURE_TO_CHAR = { 46 FAILURE_TO_CHAR = {
51 test_expectations.CRASH : "C", 47 test_expectations.CRASH : "C",
52 test_expectations.TIMEOUT : "T", 48 test_expectations.TIMEOUT : "T",
53 test_expectations.IMAGE : "I", 49 test_expectations.IMAGE : "I",
54 test_expectations.TEXT : "F", 50 test_expectations.TEXT : "F",
55 test_expectations.MISSING : "O", 51 test_expectations.MISSING : "O",
56 test_expectations.IMAGE_PLUS_TEXT : "Z" 52 test_expectations.IMAGE_PLUS_TEXT : "Z"
57 } 53 }
58 FAILURE_CHARS = FAILURE_TO_CHAR.values() 54 FAILURE_CHARS = FAILURE_TO_CHAR.values()
59 55
60 BUILDER_BASE_URL = "http://build.chromium.org/buildbot/layout_test_results/"
61 RESULTS_FILENAME = "results.json" 56 RESULTS_FILENAME = "results.json"
62 57
63 def __init__(self, options, expectations, result_summary, 58 def __init__(self, builder_name, build_name, build_number,
64 individual_test_timings, results_file_base_path, all_tests): 59 results_file_base_path, builder_base_url,
60 test_timings, failures, passed_tests, skipped_tests, all_tests):
65 """Modifies the results.json file. Grabs it off the archive directory if it 61 """Modifies the results.json file. Grabs it off the archive directory if it
66 is not found locally. 62 is not found locally.
67 63
68 Args 64 Args
69 options: a dictionary of command line options 65 builder_name: the builder name (e.g. Webkit).
70 expectations: TestExpectations object with what we expected to get 66 build_name: the build name (e.g. webkit-rel).
71 result_summary: ResultsSummary object containing failure counts for 67 build_number: the build number.
72 different groups of tests.
73 individual_test_times: Map of test name to a tuple containing the
74 test_run-time.
75 results_file_base_path: Absolute path to the directory containing the 68 results_file_base_path: Absolute path to the directory containing the
76 results json file. 69 results json file.
77 all_tests: List of all the tests that were run. 70 builder_base_url: the URL where we have the archived test results.
71 test_timings: Map of test name to a test_run-time.
72 failures: Map of test name to a failure type (of test_expectations).
73 passed_tests: A set containing all the passed tests.
74 skipped_tests: A set containing all the skipped tests.
75 all_tests: List of all the tests that were run. This should not include
76 skipped tests.
78 """ 77 """
79 # Make sure all test paths are relative to the layout test root directory. 78 self._builder_name = builder_name
80 self._failures = {} 79 self._build_name = build_name
81 for test in result_summary.failures: 80 self._build_number = build_number
82 test_path = self._GetPathRelativeToLayoutTestRoot(test) 81 self._builder_base_url = builder_base_url
83 self._failures[test_path] = result_summary.failures[test]
84
85 self._all_tests = [self._GetPathRelativeToLayoutTestRoot(test)
86 for test in all_tests]
87
88 self._expectations = expectations
89 self._result_summary = result_summary
90
91 self._test_timings = {}
92 for test_tuple in individual_test_timings:
93 test_path = self._GetPathRelativeToLayoutTestRoot(test_tuple.filename)
94 self._test_timings[test_path] = test_tuple.test_run_time
95
96 self._options = options
97 self._results_file_path = os.path.join(results_file_base_path, 82 self._results_file_path = os.path.join(results_file_base_path,
98 self.RESULTS_FILENAME) 83 self.RESULTS_FILENAME)
84 self._test_timings = test_timings
85 self._failures = failures
86 self._passed_tests = passed_tests
87 self._skipped_tests = skipped_tests
88 self._all_tests = all_tests
99 89
90 self._GenerateJSONOutput()
91
92 def _GenerateJSONOutput(self):
93 """Generates the JSON output file."""
100 json = self._GetJSON() 94 json = self._GetJSON()
101 if json: 95 if json:
102 results_file = open(self._results_file_path, "w") 96 results_file = open(self._results_file_path, "w")
103 results_file.write(json) 97 results_file.write(json)
104 results_file.close() 98 results_file.close()
105 99
106 def _GetSVNRevision(self, in_directory=None): 100 def _GetSVNRevision(self, in_directory=None):
107 """Returns the svn revision for the given directory. 101 """Returns the svn revision for the given directory.
108 102
109 Args: 103 Args:
110 in_directory: The directory where svn is to be run. 104 in_directory: The directory where svn is to be run.
111 """ 105 """
112 output = subprocess.Popen(["svn", "info", "--xml"], 106 output = subprocess.Popen(["svn", "info", "--xml"],
113 cwd=in_directory, 107 cwd=in_directory,
114 shell=(sys.platform == 'win32'), 108 shell=(sys.platform == 'win32'),
115 stdout=subprocess.PIPE).communicate()[0] 109 stdout=subprocess.PIPE).communicate()[0]
116 try : 110 try :
117 dom = xml.dom.minidom.parseString(output) 111 dom = xml.dom.minidom.parseString(output)
118 return dom.getElementsByTagName('entry')[0].getAttribute('revision'); 112 return dom.getElementsByTagName('entry')[0].getAttribute('revision');
119 except xml.parsers.expat.ExpatError: 113 except xml.parsers.expat.ExpatError:
120 return "" 114 return ""
121 115
122 def _GetPathRelativeToLayoutTestRoot(self, test): 116 def _GetArchivedJSONResults(self):
123 """Returns the path of the test relative to the layout test root. 117 """Reads old results JSON file if it exists.
124 For example, for: 118 Returns (archived_results, error) tuple where error is None if results
125 src/third_party/WebKit/LayoutTests/fast/forms/foo.html 119 were successfully read.
126 We would return
127 fast/forms/foo.html
128 """ 120 """
129 index = test.find(self.LAYOUT_TESTS_PATH) 121 results_json = {}
130 if index is not -1: 122 old_results = None
131 index += len(self.LAYOUT_TESTS_PATH) 123 error = None
132 124
133 if index is -1:
134 # Already a relative path.
135 relativePath = test
136 else:
137 relativePath = test[index + 1:]
138
139 # Make sure all paths are unix-style.
140 return relativePath.replace('\\', '/')
141
142 def _GetJSON(self):
143 """Gets the results for the results.json file."""
144 failures_for_json = {}
145 for test in self._failures:
146 failures_for_json[test] = ResultAndTime(test, self._all_tests)
147 failures_for_json[test].result = self._GetResultsCharForTest(test)
148
149 for test in self._test_timings:
150 if not test in failures_for_json:
151 failures_for_json[test] = ResultAndTime(test, self._all_tests)
152 # Floor for now to get time in seconds.
153 failures_for_json[test].time = int(self._test_timings[test])
154
155 # If results file exists, read it out, put new info in it.
156 if os.path.exists(self._results_file_path): 125 if os.path.exists(self._results_file_path):
157 old_results_file = open(self._results_file_path, "r") 126 old_results_file = open(self._results_file_path, "r")
158 old_results = old_results_file.read() 127 old_results = old_results_file.read()
159 else: 128 elif self._builder_base_url:
160 results_file_url = (self.BUILDER_BASE_URL + self._options.build_name + 129 # Check if we have the archived JSON file on the buildbot server.
161 "/" + self.RESULTS_FILENAME) 130 results_file_url = (self._builder_base_url +
131 self._build_name + "/" + self.RESULTS_FILENAME)
162 logging.error("Local results.json file does not exist. Grabbing it off " 132 logging.error("Local results.json file does not exist. Grabbing it off "
163 "the archive at " + results_file_url) 133 "the archive at " + results_file_url)
164 134
165 error = None
166 old_results = None
167 try: 135 try:
168 results_file = urllib2.urlopen(results_file_url) 136 results_file = urllib2.urlopen(results_file_url)
169 info = results_file.info() 137 info = results_file.info()
170 old_results = results_file.read() 138 old_results = results_file.read()
171 except urllib2.HTTPError, http_error: 139 except urllib2.HTTPError, http_error:
172 # A non-4xx status code means the bot is hosed for some reason and we 140 # A non-4xx status code means the bot is hosed for some reason and we
173 # can't grab the results.json file off of it. 141 # can't grab the results.json file off of it.
174 if (http_error.code < 400 and http_error.code >= 500): 142 if (http_error.code < 400 and http_error.code >= 500):
175 error = http_error 143 error = http_error
176 except urllib2.URLError, url_error: 144 except urllib2.URLError, url_error:
177 error = url_error 145 error = url_error
178 146
179 if error:
180 # If there was a URL/HTTPError don't write a results.json
181 # file at all as it would lose all the information on the bot.
182 logging.error("Archive directory is inaccessible. Not modifying or "
183 "clobbering the results.json file: " + str(error))
184 return None
185
186 builder_name = self._options.builder_name
187 if old_results: 147 if old_results:
188 # Strip the prefix and suffix so we can get the actual JSON object. 148 # Strip the prefix and suffix so we can get the actual JSON object.
189 old_results = old_results[ 149 old_results = old_results[
190 len(self.JSON_PREFIX) : len(old_results) - len(self.JSON_SUFFIX)] 150 len(self.JSON_PREFIX) : len(old_results) - len(self.JSON_SUFFIX)]
191 151
192 try: 152 try:
193 results_json = simplejson.loads(old_results) 153 results_json = simplejson.loads(old_results)
194 except: 154 except:
195 logging.debug("results.json was not valid JSON. Clobbering.") 155 logging.debug("results.json was not valid JSON. Clobbering.")
196 # The JSON file is not valid JSON. Just clobber the results. 156 # The JSON file is not valid JSON. Just clobber the results.
197 results_json = {} 157 results_json = {}
198
199 if builder_name not in results_json:
200 logging.debug("Builder name (%s) is not in the results.json file." %
201 builder_name);
202 else: 158 else:
203 logging.debug('Old JSON results do not exist. Starting fresh.'); 159 logging.debug('Old JSON results do not exist. Starting fresh.');
204 results_json = {} 160 results_json = {}
205 161
162 return results_json, error
163
164 def _GetJSON(self):
165 """Gets the results for the results.json file."""
166 results_json, error = self._GetArchivedJSONResults()
167 if error:
168 # If there was an error don't write a results.json
169 # file at all as it would lose all the information on the bot.
170 logging.error("Archive directory is inaccessible. Not modifying or "
171 "clobbering the results.json file: " + str(error))
172 return None
173
174 builder_name = self._builder_name
175 if results_json and builder_name not in results_json:
176 logging.debug("Builder name (%s) is not in the results.json file." %
177 builder_name);
178
206 self._ConvertJSONToCurrentVersion(results_json) 179 self._ConvertJSONToCurrentVersion(results_json)
207 180
208 if builder_name not in results_json: 181 if builder_name not in results_json:
209 results_json[builder_name] = self._CreateResultsForBuilderJSON() 182 results_json[builder_name] = self._CreateResultsForBuilderJSON()
210 183
211 results_for_builder = results_json[builder_name] 184 results_for_builder = results_json[builder_name]
185
186 self._InsertGenericMetadata(results_for_builder)
187
188 self._InsertFailureSummaries(results_for_builder)
189
190 # Update the all failing tests with result type and time.
212 tests = results_for_builder[self.TESTS] 191 tests = results_for_builder[self.TESTS]
213 all_failing_tests = set(self._failures.iterkeys()) 192 all_failing_tests = set(self._failures.iterkeys())
214 all_failing_tests.update(tests.iterkeys()) 193 all_failing_tests.update(tests.iterkeys())
215
216 self._InsertItemIntoRawList(results_for_builder,
217 self._options.build_number, self.BUILD_NUMBERS)
218
219 path_to_webkit = path_utils.PathFromBase('third_party', 'WebKit', 'WebCore')
220 self._InsertItemIntoRawList(results_for_builder,
221 self._GetSVNRevision(path_to_webkit),
222 self.WEBKIT_SVN)
223
224 path_to_chrome_base = path_utils.PathFromBase()
225 self._InsertItemIntoRawList(results_for_builder,
226 self._GetSVNRevision(path_to_chrome_base),
227 self.CHROME_SVN)
228
229 self._InsertItemIntoRawList(results_for_builder,
230 int(time.time()),
231 self.TIME)
232
233 self._InsertFailureSummaries(results_for_builder)
234
235 for test in all_failing_tests: 194 for test in all_failing_tests:
236 if test in failures_for_json: 195 self._InsertTestTimeAndResult(test, tests)
237 result_and_time = failures_for_json[test]
238 else:
239 result_and_time = ResultAndTime(test, self._all_tests)
240
241 if test not in tests:
242 tests[test] = self._CreateResultsAndTimesJSON()
243
244 thisTest = tests[test]
245 self._InsertItemRunLengthEncoded(result_and_time.result,
246 thisTest[self.RESULTS])
247 self._InsertItemRunLengthEncoded(result_and_time.time,
248 thisTest[self.TIMES])
249 self._NormalizeResultsJSON(thisTest, test, tests)
250 196
251 # Specify separators in order to get compact encoding. 197 # Specify separators in order to get compact encoding.
252 results_str = simplejson.dumps(results_json, separators=(',', ':')) 198 results_str = simplejson.dumps(results_json, separators=(',', ':'))
253 return self.JSON_PREFIX + results_str + self.JSON_SUFFIX 199 return self.JSON_PREFIX + results_str + self.JSON_SUFFIX
254 200
255 def _InsertFailureSummaries(self, results_for_builder): 201 def _InsertFailureSummaries(self, results_for_builder):
256 """Inserts aggregate pass/failure statistics into the JSON. 202 """Inserts aggregate pass/failure statistics into the JSON.
203 This method reads self._skipped_tests, self._passed_tests and
204 self._failures and inserts FIXABLE, FIXABLE_COUNT and ALL_FIXABLE_COUNT
205 entries.
257 206
258 Args: 207 Args:
259 results_for_builder: Dictionary containing the test results for a single 208 results_for_builder: Dictionary containing the test results for a single
260 builder. 209 builder.
261 """ 210 """
262 summary = self._result_summary 211 # Insert the number of tests that failed.
263 self._InsertItemIntoRawList(results_for_builder, 212 self._InsertItemIntoRawList(results_for_builder,
264 len((set(summary.failures.keys()) | 213 len(set(self._failures.keys()) | self._skipped_tests),
265 summary.tests_by_expectation[test_expectations.SKIP]) &
266 summary.tests_by_timeline[test_expectations.NOW]),
267 self.FIXABLE_COUNT) 214 self.FIXABLE_COUNT)
215
216 # Create a pass/skip/failure summary dictionary.
217 entry = {}
218 entry[self.SKIP_RESULT] = len(self._skipped_tests)
219 entry[self.PASS_RESULT] = len(self._passed_tests)
220 get = entry.get
221 for failure_type in self._failures.values():
222 failure_char = self.FAILURE_TO_CHAR[failure_type]
223 entry[failure_char] = get(failure_char, 0) + 1
224
225 # Insert the pass/skip/failure summary dictionary.
226 self._InsertItemIntoRawList(results_for_builder, entry, self.FIXABLE)
227
228 # Insert the number of all the tests that are supposed to pass.
268 self._InsertItemIntoRawList(results_for_builder, 229 self._InsertItemIntoRawList(results_for_builder,
269 len(self._expectations.GetTestsWithTimeline(test_expectations.NOW)), 230 len(self._skipped_tests | self._all_tests),
270 self.ALL_FIXABLE_COUNT) 231 self.ALL_FIXABLE_COUNT)
271 self._InsertItemIntoRawList(results_for_builder,
272 self._GetFailureSummaryEntry(test_expectations.DEFER),
273 self.DEFERRED)
274 self._InsertItemIntoRawList(results_for_builder,
275 self._GetFailureSummaryEntry(test_expectations.WONTFIX),
276 self.WONTFIX)
277 self._InsertItemIntoRawList(results_for_builder,
278 self._GetFailureSummaryEntry(test_expectations.NOW),
279 self.FIXABLE)
280
281 def _GetFailureSummaryEntry(self, timeline):
282 """Creates a summary object to insert into the JSON.
283
284 Args:
285 summary ResultSummary object with test results
286 timeline current test_expectations timeline to build entry for (e.g.,
287 test_expectations.NOW, etc.)
288 """
289 entry = {}
290 summary = self._result_summary
291 timeline_tests = summary.tests_by_timeline[timeline]
292 entry[self.SKIP_RESULT] = len(
293 summary.tests_by_expectation[test_expectations.SKIP] & timeline_tests)
294 entry[self.PASS_RESULT] = len(
295 summary.tests_by_expectation[test_expectations.PASS] & timeline_tests)
296 for failure_type in summary.tests_by_expectation.keys():
297 if failure_type not in self.FAILURE_TO_CHAR:
298 continue
299 count = len(summary.tests_by_expectation[failure_type] & timeline_tests)
300 entry[self.FAILURE_TO_CHAR[failure_type]] = count
301 return entry
302
303 def _GetResultsCharForTest(self, test):
304 """Returns a single character description of the test result."""
305 result = test_failures.DetermineResultType(self._failures[test])
306 return self.FAILURE_TO_CHAR[result]
307 232
308 def _InsertItemIntoRawList(self, results_for_builder, item, key): 233 def _InsertItemIntoRawList(self, results_for_builder, item, key):
309 """Inserts the item into the list with the given key in the results for 234 """Inserts the item into the list with the given key in the results for
310 this builder. Creates the list if no such list exists. 235 this builder. Creates the list if no such list exists.
311 236
312 Args: 237 Args:
313 results_for_builder: Dictionary containing the test results for a single 238 results_for_builder: Dictionary containing the test results for a single
314 builder. 239 builder.
315 item: Number or string to insert into the list. 240 item: Number or string to insert into the list.
316 key: Key in results_for_builder for the list to insert into. 241 key: Key in results_for_builder for the list to insert into.
(...skipping 17 matching lines...) Expand all
334 """ 259 """
335 if len(encoded_results) and item == encoded_results[0][1]: 260 if len(encoded_results) and item == encoded_results[0][1]:
336 num_results = encoded_results[0][0] 261 num_results = encoded_results[0][0]
337 if num_results <= self.MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG: 262 if num_results <= self.MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG:
338 encoded_results[0][0] = num_results + 1 263 encoded_results[0][0] = num_results + 1
339 else: 264 else:
340 # Use a list instead of a class for the run-length encoding since we 265 # Use a list instead of a class for the run-length encoding since we
341 # want the serialized form to be concise. 266 # want the serialized form to be concise.
342 encoded_results.insert(0, [1, item]) 267 encoded_results.insert(0, [1, item])
343 268
269 def _InsertGenericMetadata(self, results_for_builder):
270 """ Inserts generic metadata (such as version number, current time etc)
271 into the JSON.
272
273 Args:
274 results_for_builder: Dictionary containing the test results for a single
275 builder.
276 """
277 self._InsertItemIntoRawList(results_for_builder,
278 self._build_number, self.BUILD_NUMBERS)
279
280 path_to_webkit = path_utils.PathFromBase('third_party', 'WebKit', 'WebCore')
281 self._InsertItemIntoRawList(results_for_builder,
282 self._GetSVNRevision(path_to_webkit),
283 self.WEBKIT_SVN)
284
285 path_to_chrome_base = path_utils.PathFromBase()
286 self._InsertItemIntoRawList(results_for_builder,
287 self._GetSVNRevision(path_to_chrome_base),
288 self.CHROME_SVN)
289
290 self._InsertItemIntoRawList(results_for_builder,
291 int(time.time()),
292 self.TIME)
293
294 def _InsertTestTimeAndResult(self, test_name, tests):
295 """ Insert a test item with its results to the given tests dictionary.
296
297 Args:
298 tests: Dictionary containing test result entries.
299 """
300
301 result = JSONResultsGenerator.PASS_RESULT
302 time = 0
303
304 if test_name not in self._all_tests:
305 result = JSONResultsGenerator.NO_DATA_RESULT
306
307 if test_name in self._failures:
308 result = self.FAILURE_TO_CHAR[self._failures[test_name]]
309
310 if test_name in self._test_timings:
311 # Floor for now to get time in seconds.
312 time = int(self._test_timings[test_name])
313
314 if test_name not in tests:
315 tests[test_name] = self._CreateResultsAndTimesJSON()
316
317 thisTest = tests[test_name]
318 self._InsertItemRunLengthEncoded(result, thisTest[self.RESULTS])
319 self._InsertItemRunLengthEncoded(time, thisTest[self.TIMES])
320 self._NormalizeResultsJSON(thisTest, test_name, tests)
321
344 def _ConvertJSONToCurrentVersion(self, results_json): 322 def _ConvertJSONToCurrentVersion(self, results_json):
345 """If the JSON does not match the current version, converts it to the 323 """If the JSON does not match the current version, converts it to the
346 current version and adds in the new version number. 324 current version and adds in the new version number.
347 """ 325 """
348 if (self.VERSION_KEY in results_json and 326 if (self.VERSION_KEY in results_json and
349 results_json[self.VERSION_KEY] == self.VERSION): 327 results_json[self.VERSION_KEY] == self.VERSION):
350 return 328 return
351 329
352 if results_json[self.VERSION_KEY] == 2:
353 for results_for_builder in results_json.itervalues():
354 try:
355 test_results = results_for_builder[self.TESTS]
356 except:
357 continue
358
359 for test in test_results:
360 # Make sure all paths are relative
361 test_path = self._GetPathRelativeToLayoutTestRoot(test)
362 if test_path != test:
363 test_results[test_path] = test_results[test]
364 del test_results[test]
365
366 results_json[self.VERSION_KEY] = self.VERSION 330 results_json[self.VERSION_KEY] = self.VERSION
367 331
368 def _CreateResultsAndTimesJSON(self): 332 def _CreateResultsAndTimesJSON(self):
369 results_and_times = {} 333 results_and_times = {}
370 results_and_times[self.RESULTS] = [] 334 results_and_times[self.RESULTS] = []
371 results_and_times[self.TIMES] = [] 335 results_and_times[self.TIMES] = []
372 return results_and_times 336 return results_and_times
373 337
374 def _CreateResultsForBuilderJSON(self): 338 def _CreateResultsForBuilderJSON(self):
375 results_for_builder = {} 339 results_for_builder = {}
(...skipping 10 matching lines...) Expand all
386 """ 350 """
387 num_builds = 0 351 num_builds = 0
388 index = 0 352 index = 0
389 for result in encoded_list: 353 for result in encoded_list:
390 num_builds = num_builds + result[0] 354 num_builds = num_builds + result[0]
391 index = index + 1 355 index = index + 1
392 if num_builds > self.MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG: 356 if num_builds > self.MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG:
393 return encoded_list[:index] 357 return encoded_list[:index]
394 return encoded_list 358 return encoded_list
395 359
396 def _NormalizeResultsJSON(self, test, test_path, tests): 360 def _NormalizeResultsJSON(self, test, test_name, tests):
397 """ Prune tests where all runs pass or tests that no longer exist and 361 """ Prune tests where all runs pass or tests that no longer exist and
398 truncate all results to maxNumberOfBuilds. 362 truncate all results to maxNumberOfBuilds.
399 363
400 Args: 364 Args:
401 test: ResultsAndTimes object for this test. 365 test: ResultsAndTimes object for this test.
402 test_path: Path to the test. 366 test_name: Name of the test.
403 tests: The JSON object with all the test results for this builder. 367 tests: The JSON object with all the test results for this builder.
404 """ 368 """
405 test[self.RESULTS] = self._RemoveItemsOverMaxNumberOfBuilds( 369 test[self.RESULTS] = self._RemoveItemsOverMaxNumberOfBuilds(
406 test[self.RESULTS]) 370 test[self.RESULTS])
407 test[self.TIMES] = self._RemoveItemsOverMaxNumberOfBuilds(test[self.TIMES]) 371 test[self.TIMES] = self._RemoveItemsOverMaxNumberOfBuilds(test[self.TIMES])
408 372
409 is_all_pass = self._IsResultsAllOfType(test[self.RESULTS], self.PASS_RESULT) 373 is_all_pass = self._IsResultsAllOfType(test[self.RESULTS], self.PASS_RESULT)
410 is_all_no_data = self._IsResultsAllOfType(test[self.RESULTS], 374 is_all_no_data = self._IsResultsAllOfType(test[self.RESULTS],
411 self.NO_DATA_RESULT) 375 self.NO_DATA_RESULT)
412 max_time = max([time[1] for time in test[self.TIMES]]) 376 max_time = max([time[1] for time in test[self.TIMES]])
413 377
414 # Remove all passes/no-data from the results to reduce noise and filesize. 378 # Remove all passes/no-data from the results to reduce noise and filesize.
415 # If a test passes every run, but takes > MIN_TIME to run, don't throw away 379 # If a test passes every run, but takes > MIN_TIME to run, don't throw away
416 # the data. 380 # the data.
417 if is_all_no_data or (is_all_pass and max_time <= self.MIN_TIME): 381 if is_all_no_data or (is_all_pass and max_time <= self.MIN_TIME):
418 del tests[test_path] 382 del tests[test_name]
419 else:
420 # Remove tests that don't exist anymore.
421 full_path = os.path.join(path_utils.LayoutTestsDir(), test_path)
422 full_path = os.path.normpath(full_path)
423 if not os.path.exists(full_path):
424 del tests[test_path]
425 383
426 def _IsResultsAllOfType(self, results, type): 384 def _IsResultsAllOfType(self, results, type):
427 """Returns whether all teh results are of the given type (e.g. all passes). 385 """Returns whether all teh results are of the given type (e.g. all passes).
428 """ 386 """
429 return len(results) == 1 and results[0][1] == type 387 return len(results) == 1 and results[0][1] == type
430
431 class ResultAndTime:
432 """A holder for a single result and runtime for a test."""
433 def __init__(self, test, all_tests):
434 self.time = 0
435 # If the test was run, then we don't want to default the result to nodata.
436 if test in all_tests:
437 self.result = JSONResultsGenerator.PASS_RESULT
438 else:
439 self.result = JSONResultsGenerator.NO_DATA_RESULT
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698