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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: webkit/tools/layout_tests/layout_package/json_results_generator.py
diff --git a/webkit/tools/layout_tests/layout_package/json_results_generator.py b/webkit/tools/layout_tests/layout_package/json_results_generator.py
index 22c5cc62d3ab0c673819aea332157757900b7567..92fc1179ac523e86b40176d073128b0e4abac4f2 100644
--- a/webkit/tools/layout_tests/layout_package/json_results_generator.py
+++ b/webkit/tools/layout_tests/layout_package/json_results_generator.py
@@ -4,7 +4,6 @@
import logging
import os
-import re
import subprocess
import sys
import time
@@ -13,19 +12,17 @@ import xml.dom.minidom
from layout_package import path_utils
from layout_package import test_expectations
-from layout_package import test_failures
sys.path.append(path_utils.PathFromBase('third_party'))
import simplejson
-class JSONResultsGenerator:
+class JSONResultsGenerator(object):
MAX_NUMBER_OF_BUILD_RESULTS_TO_LOG = 750
# Min time (seconds) that will be added to the JSON.
MIN_TIME = 1
JSON_PREFIX = "ADD_RESULTS("
JSON_SUFFIX = ");"
- LAYOUT_TESTS_PATH = "LayoutTests"
PASS_RESULT = "P"
SKIP_RESULT = "X"
NO_DATA_RESULT = "N"
@@ -38,11 +35,10 @@ class JSONResultsGenerator:
CHROME_SVN = "chromeRevision"
TIME = "secondsSinceEpoch"
TESTS = "tests"
- WONTFIX = "wontfixCounts"
- DEFERRED = "deferredCounts"
+
+ FIXABLE_COUNT = "fixableCount"
FIXABLE = "fixableCounts"
ALL_FIXABLE_COUNT = "allFixableCount"
- FIXABLE_COUNT = "fixableCount"
# Note that we omit test_expectations.FAIL from this list because
# it should never show up (it's a legacy input expectation, never
@@ -57,46 +53,44 @@ class JSONResultsGenerator:
}
FAILURE_CHARS = FAILURE_TO_CHAR.values()
- BUILDER_BASE_URL = "http://build.chromium.org/buildbot/layout_test_results/"
RESULTS_FILENAME = "results.json"
- def __init__(self, options, expectations, result_summary,
- individual_test_timings, results_file_base_path, all_tests):
+ def __init__(self, builder_name, build_name, build_number,
+ results_file_base_path, builder_base_url,
+ test_timings, failures, passed_tests, skipped_tests, all_tests):
"""Modifies the results.json file. Grabs it off the archive directory if it
is not found locally.
Args
- options: a dictionary of command line options
- expectations: TestExpectations object with what we expected to get
- result_summary: ResultsSummary object containing failure counts for
- different groups of tests.
- individual_test_times: Map of test name to a tuple containing the
- test_run-time.
+ builder_name: the builder name (e.g. Webkit).
+ build_name: the build name (e.g. webkit-rel).
+ build_number: the build number.
results_file_base_path: Absolute path to the directory containing the
results json file.
- all_tests: List of all the tests that were run.
+ builder_base_url: the URL where we have the archived test results.
+ test_timings: Map of test name to a test_run-time.
+ failures: Map of test name to a failure type (of test_expectations).
+ passed_tests: A set containing all the passed tests.
+ skipped_tests: A set containing all the skipped tests.
+ all_tests: List of all the tests that were run. This should not include
+ skipped tests.
"""
- # Make sure all test paths are relative to the layout test root directory.
- self._failures = {}
- for test in result_summary.failures:
- test_path = self._GetPathRelativeToLayoutTestRoot(test)
- self._failures[test_path] = result_summary.failures[test]
-
- self._all_tests = [self._GetPathRelativeToLayoutTestRoot(test)
- for test in all_tests]
-
- self._expectations = expectations
- self._result_summary = result_summary
-
- self._test_timings = {}
- for test_tuple in individual_test_timings:
- test_path = self._GetPathRelativeToLayoutTestRoot(test_tuple.filename)
- self._test_timings[test_path] = test_tuple.test_run_time
-
- self._options = options
+ self._builder_name = builder_name
+ self._build_name = build_name
+ self._build_number = build_number
+ self._builder_base_url = builder_base_url
self._results_file_path = os.path.join(results_file_base_path,
self.RESULTS_FILENAME)
+ self._test_timings = test_timings
+ self._failures = failures
+ self._passed_tests = passed_tests
+ self._skipped_tests = skipped_tests
+ self._all_tests = all_tests
+
+ self._GenerateJSONOutput()
+ def _GenerateJSONOutput(self):
+ """Generates the JSON output file."""
json = self._GetJSON()
if json:
results_file = open(self._results_file_path, "w")
@@ -119,51 +113,25 @@ class JSONResultsGenerator:
except xml.parsers.expat.ExpatError:
return ""
- def _GetPathRelativeToLayoutTestRoot(self, test):
- """Returns the path of the test relative to the layout test root.
- For example, for:
- src/third_party/WebKit/LayoutTests/fast/forms/foo.html
- We would return
- fast/forms/foo.html
+ def _GetArchivedJSONResults(self):
+ """Reads old results JSON file if it exists.
+ Returns (archived_results, error) tuple where error is None if results
+ were successfully read.
"""
- index = test.find(self.LAYOUT_TESTS_PATH)
- if index is not -1:
- index += len(self.LAYOUT_TESTS_PATH)
-
- if index is -1:
- # Already a relative path.
- relativePath = test
- else:
- relativePath = test[index + 1:]
-
- # Make sure all paths are unix-style.
- return relativePath.replace('\\', '/')
-
- def _GetJSON(self):
- """Gets the results for the results.json file."""
- failures_for_json = {}
- for test in self._failures:
- failures_for_json[test] = ResultAndTime(test, self._all_tests)
- failures_for_json[test].result = self._GetResultsCharForTest(test)
-
- for test in self._test_timings:
- if not test in failures_for_json:
- failures_for_json[test] = ResultAndTime(test, self._all_tests)
- # Floor for now to get time in seconds.
- failures_for_json[test].time = int(self._test_timings[test])
+ results_json = {}
+ old_results = None
+ error = None
- # If results file exists, read it out, put new info in it.
if os.path.exists(self._results_file_path):
old_results_file = open(self._results_file_path, "r")
old_results = old_results_file.read()
- else:
- results_file_url = (self.BUILDER_BASE_URL + self._options.build_name +
- "/" + self.RESULTS_FILENAME)
+ elif self._builder_base_url:
+ # Check if we have the archived JSON file on the buildbot server.
+ results_file_url = (self._builder_base_url +
+ self._build_name + "/" + self.RESULTS_FILENAME)
logging.error("Local results.json file does not exist. Grabbing it off "
"the archive at " + results_file_url)
- error = None
- old_results = None
try:
results_file = urllib2.urlopen(results_file_url)
info = results_file.info()
@@ -176,14 +144,6 @@ class JSONResultsGenerator:
except urllib2.URLError, url_error:
error = url_error
- if error:
- # If there was a URL/HTTPError don't write a results.json
- # file at all as it would lose all the information on the bot.
- logging.error("Archive directory is inaccessible. Not modifying or "
- "clobbering the results.json file: " + str(error))
- return None
-
- builder_name = self._options.builder_name
if old_results:
# Strip the prefix and suffix so we can get the actual JSON object.
old_results = old_results[
@@ -195,58 +155,44 @@ class JSONResultsGenerator:
logging.debug("results.json was not valid JSON. Clobbering.")
# The JSON file is not valid JSON. Just clobber the results.
results_json = {}
-
- if builder_name not in results_json:
- logging.debug("Builder name (%s) is not in the results.json file." %
- builder_name);
else:
logging.debug('Old JSON results do not exist. Starting fresh.');
results_json = {}
+ return results_json, error
+
+ def _GetJSON(self):
+ """Gets the results for the results.json file."""
+ results_json, error = self._GetArchivedJSONResults()
+ if error:
+ # If there was an error don't write a results.json
+ # file at all as it would lose all the information on the bot.
+ logging.error("Archive directory is inaccessible. Not modifying or "
+ "clobbering the results.json file: " + str(error))
+ return None
+
+ builder_name = self._builder_name
+ if results_json and builder_name not in results_json:
+ logging.debug("Builder name (%s) is not in the results.json file." %
+ builder_name);
+
self._ConvertJSONToCurrentVersion(results_json)
if builder_name not in results_json:
results_json[builder_name] = self._CreateResultsForBuilderJSON()
results_for_builder = results_json[builder_name]
- tests = results_for_builder[self.TESTS]
- all_failing_tests = set(self._failures.iterkeys())
- all_failing_tests.update(tests.iterkeys())
-
- self._InsertItemIntoRawList(results_for_builder,
- self._options.build_number, self.BUILD_NUMBERS)
-
- path_to_webkit = path_utils.PathFromBase('third_party', 'WebKit', 'WebCore')
- self._InsertItemIntoRawList(results_for_builder,
- self._GetSVNRevision(path_to_webkit),
- self.WEBKIT_SVN)
- path_to_chrome_base = path_utils.PathFromBase()
- self._InsertItemIntoRawList(results_for_builder,
- self._GetSVNRevision(path_to_chrome_base),
- self.CHROME_SVN)
-
- self._InsertItemIntoRawList(results_for_builder,
- int(time.time()),
- self.TIME)
+ self._InsertGenericMetadata(results_for_builder)
self._InsertFailureSummaries(results_for_builder)
+ # Update the all failing tests with result type and time.
+ tests = results_for_builder[self.TESTS]
+ all_failing_tests = set(self._failures.iterkeys())
+ all_failing_tests.update(tests.iterkeys())
for test in all_failing_tests:
- if test in failures_for_json:
- result_and_time = failures_for_json[test]
- else:
- result_and_time = ResultAndTime(test, self._all_tests)
-
- if test not in tests:
- tests[test] = self._CreateResultsAndTimesJSON()
-
- thisTest = tests[test]
- self._InsertItemRunLengthEncoded(result_and_time.result,
- thisTest[self.RESULTS])
- self._InsertItemRunLengthEncoded(result_and_time.time,
- thisTest[self.TIMES])
- self._NormalizeResultsJSON(thisTest, test, tests)
+ self._InsertTestTimeAndResult(test, tests)
# Specify separators in order to get compact encoding.
results_str = simplejson.dumps(results_json, separators=(',', ':'))
@@ -254,56 +200,35 @@ class JSONResultsGenerator:
def _InsertFailureSummaries(self, results_for_builder):
"""Inserts aggregate pass/failure statistics into the JSON.
+ This method reads self._skipped_tests, self._passed_tests and
+ self._failures and inserts FIXABLE, FIXABLE_COUNT and ALL_FIXABLE_COUNT
+ entries.
Args:
results_for_builder: Dictionary containing the test results for a single
builder.
"""
- summary = self._result_summary
+ # Insert the number of tests that failed.
self._InsertItemIntoRawList(results_for_builder,
- len((set(summary.failures.keys()) |
- summary.tests_by_expectation[test_expectations.SKIP]) &
- summary.tests_by_timeline[test_expectations.NOW]),
+ len(set(self._failures.keys()) | self._skipped_tests),
self.FIXABLE_COUNT)
- self._InsertItemIntoRawList(results_for_builder,
- len(self._expectations.GetTestsWithTimeline(test_expectations.NOW)),
- self.ALL_FIXABLE_COUNT)
- self._InsertItemIntoRawList(results_for_builder,
- self._GetFailureSummaryEntry(test_expectations.DEFER),
- self.DEFERRED)
- self._InsertItemIntoRawList(results_for_builder,
- self._GetFailureSummaryEntry(test_expectations.WONTFIX),
- self.WONTFIX)
- self._InsertItemIntoRawList(results_for_builder,
- self._GetFailureSummaryEntry(test_expectations.NOW),
- self.FIXABLE)
- def _GetFailureSummaryEntry(self, timeline):
- """Creates a summary object to insert into the JSON.
-
- Args:
- summary ResultSummary object with test results
- timeline current test_expectations timeline to build entry for (e.g.,
- test_expectations.NOW, etc.)
- """
+ # Create a pass/skip/failure summary dictionary.
entry = {}
- summary = self._result_summary
- timeline_tests = summary.tests_by_timeline[timeline]
- entry[self.SKIP_RESULT] = len(
- summary.tests_by_expectation[test_expectations.SKIP] & timeline_tests)
- entry[self.PASS_RESULT] = len(
- summary.tests_by_expectation[test_expectations.PASS] & timeline_tests)
- for failure_type in summary.tests_by_expectation.keys():
- if failure_type not in self.FAILURE_TO_CHAR:
- continue
- count = len(summary.tests_by_expectation[failure_type] & timeline_tests)
- entry[self.FAILURE_TO_CHAR[failure_type]] = count
- return entry
-
- def _GetResultsCharForTest(self, test):
- """Returns a single character description of the test result."""
- result = test_failures.DetermineResultType(self._failures[test])
- return self.FAILURE_TO_CHAR[result]
+ entry[self.SKIP_RESULT] = len(self._skipped_tests)
+ entry[self.PASS_RESULT] = len(self._passed_tests)
+ get = entry.get
+ for failure_type in self._failures.values():
+ failure_char = self.FAILURE_TO_CHAR[failure_type]
+ entry[failure_char] = get(failure_char, 0) + 1
+
+ # Insert the pass/skip/failure summary dictionary.
+ self._InsertItemIntoRawList(results_for_builder, entry, self.FIXABLE)
+
+ # Insert the number of all the tests that are supposed to pass.
+ self._InsertItemIntoRawList(results_for_builder,
+ len(self._skipped_tests | self._all_tests),
+ self.ALL_FIXABLE_COUNT)
def _InsertItemIntoRawList(self, results_for_builder, item, key):
"""Inserts the item into the list with the given key in the results for
@@ -341,6 +266,59 @@ class JSONResultsGenerator:
# want the serialized form to be concise.
encoded_results.insert(0, [1, item])
+ def _InsertGenericMetadata(self, results_for_builder):
+ """ Inserts generic metadata (such as version number, current time etc)
+ into the JSON.
+
+ Args:
+ results_for_builder: Dictionary containing the test results for a single
+ builder.
+ """
+ self._InsertItemIntoRawList(results_for_builder,
+ self._build_number, self.BUILD_NUMBERS)
+
+ path_to_webkit = path_utils.PathFromBase('third_party', 'WebKit', 'WebCore')
+ self._InsertItemIntoRawList(results_for_builder,
+ self._GetSVNRevision(path_to_webkit),
+ self.WEBKIT_SVN)
+
+ path_to_chrome_base = path_utils.PathFromBase()
+ self._InsertItemIntoRawList(results_for_builder,
+ self._GetSVNRevision(path_to_chrome_base),
+ self.CHROME_SVN)
+
+ self._InsertItemIntoRawList(results_for_builder,
+ int(time.time()),
+ self.TIME)
+
+ def _InsertTestTimeAndResult(self, test_name, tests):
+ """ Insert a test item with its results to the given tests dictionary.
+
+ Args:
+ tests: Dictionary containing test result entries.
+ """
+
+ result = JSONResultsGenerator.PASS_RESULT
+ time = 0
+
+ if test_name not in self._all_tests:
+ result = JSONResultsGenerator.NO_DATA_RESULT
+
+ if test_name in self._failures:
+ result = self.FAILURE_TO_CHAR[self._failures[test_name]]
+
+ if test_name in self._test_timings:
+ # Floor for now to get time in seconds.
+ time = int(self._test_timings[test_name])
+
+ if test_name not in tests:
+ tests[test_name] = self._CreateResultsAndTimesJSON()
+
+ thisTest = tests[test_name]
+ self._InsertItemRunLengthEncoded(result, thisTest[self.RESULTS])
+ self._InsertItemRunLengthEncoded(time, thisTest[self.TIMES])
+ self._NormalizeResultsJSON(thisTest, test_name, tests)
+
def _ConvertJSONToCurrentVersion(self, results_json):
"""If the JSON does not match the current version, converts it to the
current version and adds in the new version number.
@@ -349,20 +327,6 @@ class JSONResultsGenerator:
results_json[self.VERSION_KEY] == self.VERSION):
return
- if results_json[self.VERSION_KEY] == 2:
- for results_for_builder in results_json.itervalues():
- try:
- test_results = results_for_builder[self.TESTS]
- except:
- continue
-
- for test in test_results:
- # Make sure all paths are relative
- test_path = self._GetPathRelativeToLayoutTestRoot(test)
- if test_path != test:
- test_results[test_path] = test_results[test]
- del test_results[test]
-
results_json[self.VERSION_KEY] = self.VERSION
def _CreateResultsAndTimesJSON(self):
@@ -393,13 +357,13 @@ class JSONResultsGenerator:
return encoded_list[:index]
return encoded_list
- def _NormalizeResultsJSON(self, test, test_path, tests):
+ def _NormalizeResultsJSON(self, test, test_name, tests):
""" Prune tests where all runs pass or tests that no longer exist and
truncate all results to maxNumberOfBuilds.
Args:
test: ResultsAndTimes object for this test.
- test_path: Path to the test.
+ test_name: Name of the test.
tests: The JSON object with all the test results for this builder.
"""
test[self.RESULTS] = self._RemoveItemsOverMaxNumberOfBuilds(
@@ -415,25 +379,9 @@ class JSONResultsGenerator:
# If a test passes every run, but takes > MIN_TIME to run, don't throw away
# the data.
if is_all_no_data or (is_all_pass and max_time <= self.MIN_TIME):
- del tests[test_path]
- else:
- # Remove tests that don't exist anymore.
- full_path = os.path.join(path_utils.LayoutTestsDir(), test_path)
- full_path = os.path.normpath(full_path)
- if not os.path.exists(full_path):
- del tests[test_path]
+ del tests[test_name]
def _IsResultsAllOfType(self, results, type):
"""Returns whether all teh results are of the given type (e.g. all passes).
"""
return len(results) == 1 and results[0][1] == type
-
-class ResultAndTime:
- """A holder for a single result and runtime for a test."""
- def __init__(self, test, all_tests):
- self.time = 0
- # If the test was run, then we don't want to default the result to nodata.
- if test in all_tests:
- self.result = JSONResultsGenerator.PASS_RESULT
- else:
- self.result = JSONResultsGenerator.NO_DATA_RESULT

Powered by Google App Engine
This is Rietveld 408576698