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

Unified Diff: third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations.py

Issue 2125633002: Fill out implementation of UpdateTestExpectations script (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@updateTestExpectations2
Patch Set: 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations.py
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations.py
index ed1be7297a62b05e1315ba677989d72c965d3e50..0fb450abee8a9f72fb010cd7a09bd1880e86e76c 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations.py
@@ -62,6 +62,111 @@ class RemoveFlakesOMatic(object):
self._expectations_factory = bot_test_expectations_factory
self.builder_results_by_path = {}
+ def _can_delete_line(self, test_expectation_line):
+ """Returns whether a given line in the expectations can be removed.
+
+ Uses results from builder bots to determine if a given line is stale and
+ can safely be removed from the TestExpectations file. (i.e. remove if
+ the bots show that it's not flaky.) There's also some rules about when
qyearsley 2016/07/08 17:55:03 There's also some rules -> There are also some rul
bokan 2016/07/08 21:13:00 Done.
+ not to remove lines (e.g. never remove lines with Rebaseline
+ expectations, don't remove non-flaky expectations, etc.)
+
+ Args:
+ test_expectation_line (TestExpectationLine): A line in the test
+ expectation file to test for possible removal.
+
+ Returns: True if the line can be removed, False otherwise.
qyearsley 2016/07/08 17:55:04 To be consistent with the docstrings below, this c
bokan 2016/07/08 21:13:00 Done.
+ """
+ expectations = test_expectation_line.expectations
+ if len(expectations) < 2:
+ return False
+
+ if self._has_unstrippable_expectations(expectations):
+ return False
+
+ if not self._has_pass_expectation(expectations):
+ return False
qyearsley 2016/07/08 17:55:03 Is "PASS" an "unstrippable" expectation? Would it
bokan 2016/07/08 21:13:00 No, it's a bit subtle but has_unstrippable_expecta
+
+ # The line can be deleted if the only expectation on the line that appears in the actual
+ # results is the PASS expectation.
+ for config in test_expectation_line.matching_configurations:
+ builder_name = self._host.builders.builder_name_for_specifiers(config.version, config.build_type)
+
+ if not builder_name:
+ _log.error('Failed to get builder for config [%s, %s, %s]' % (config.version, config.architecture, config.build_type))
qyearsley 2016/07/08 17:55:03 For logging methods, if you give more than one arg
bokan 2016/07/08 21:13:00 Done.
+ # TODO(bokan): Matching configurations often give us bots that don't have a
+ # builder in builders.py's exact_matches. Should we ignore those or be conservative
+ # and assume we need these expectations to make a decision?
+ return False
+
+ if builder_name not in self.builder_results_by_path.keys():
+ _log.error('Failed to find results for builder "%s"' % builder_name)
+ return False
+
+ results_by_path = self.builder_results_by_path[builder_name]
+
+ # No results means the tests were all skipped or all results are passing.
+ if test_expectation_line.path not in results_by_path.keys():
+ continue
+
+ results_for_single_test = results_by_path[test_expectation_line.path]
+
+ if self._expectations_that_were_met(test_expectation_line, results_for_single_test) != set(['PASS']):
+ return False
+
+ return True
+
+ def _has_pass_expectation(self, expectations):
+ return 'PASS' in expectations
+
+ def _expectations_that_were_met(self, test_expectation_line, results_for_single_test):
+ """Returns the set of expectations that appear in the given results.
+
+ e.g. If the test expectations is:
+ bug(test) fast/test.html [Crash Failure Pass]
+
+ And the results are ['TEXT', 'PASS', 'PASS', 'TIMEOUT']
+
+ This method would return [Pass Failure]
qyearsley 2016/07/08 17:55:03 Maybe the "flowiness" of this section could be imp
bokan 2016/07/08 21:13:00 Done.
+
+ Args:
+ test_expectation_line: A TestExpectationLine object
+ results_for_single_test: A list of result strings.
+ e.g. ['IMAGE', 'IMAGE', 'PASS']
+
+ Returns:
+ A set containing expectations that occured in the results.
+ """
+ # TODO(bokan): Does this not exist in a more central place?
qyearsley 2016/07/08 17:55:03 Good question, it seems like it should, so if it d
bokan 2016/07/08 21:13:00 Ok, I'll leave this TODO here for now and either f
+ def replace_failing_with_fail(expectation):
+ if expectation in ('TEXT', 'IMAGE', 'IMAGE+TEXT', 'AUDIO'):
+ return 'FAIL'
+ else:
+ return expectation
+
+ actual_results = {replace_failing_with_fail(r) for r in results_for_single_test}
+
+ return set(test_expectation_line.expectations) & actual_results
+
+ def _has_unstrippable_expectations(self, expectations):
+ """ Returns whether any of the given expectations are considered unstrippable.
qyearsley 2016/07/08 17:55:04 Extra space character can be removed.
bokan 2016/07/08 21:13:00 Done.
+
+ Unstrippable expectations are those which should stop a line from being
+ removed regardless of builder bot results.
+
+ Args:
+ expectations: A list of string expectations.
+ E.g. ['PASS', 'FAIL' 'CRASH']
+
+ Returns:
+ True if at least one of the expectations is unstrippable. False
+ otherwise.
+ """
+ unstrippable_expectations = ('REBASELINE', 'NEEDSREBASELINE',
+ 'NEEDSMANUALREBASELINE', 'SLOW',
+ 'SKIP')
+ return any(s in expectations for s in unstrippable_expectations)
+
def get_updated_test_expectations(self):
"""Filters out passing lines from TestExpectations file.
@@ -72,7 +177,53 @@ class RemoveFlakesOMatic(object):
Returns: A TestExpectations object with the passing lines filtered out.
"""
test_expectations = TestExpectations(self._port, include_overrides=False).expectations()
- # TODO: Filter the expectations based on results.
+
+ self.builder_results_by_path = {}
+ for builder_name in self._host.builders.all_builder_names():
+ expectations_for_builder = (
+ self._expectations_factory.expectations_for_builder(builder_name)
+ )
+
+ if not expectations_for_builder:
+ # This is not fatal since we may not need to check these
+ # results. If we do need these results we'll log an error later
+ # when trying to check against them.
+ _log.warn('Downloaded results are missing results for builder "%s"' % builder_name)
+ continue
+
+ self.builder_results_by_path[builder_name] = (
+ expectations_for_builder.all_results_by_path()
+ )
qyearsley 2016/07/08 17:55:04 This block of code (lines 181-196), which builds u
bokan 2016/07/08 21:13:00 Done.
+
+ expectations_to_remove = []
+
+ for expectation in test_expectations:
+ if self._can_delete_line(expectation):
+ expectations_to_remove.append(expectation)
+
+ for expectation in expectations_to_remove:
+ index = test_expectations.index(expectation)
+ test_expectations.remove(expectation)
+
+ # Remove associated comments and whitespace if we've removed the last expectation under
+ # a comment block. Only remove a comment block if it's not separated from the test
+ # expectation line by whitespace.
+ if index == len(test_expectations) or test_expectations[index].is_whitespace() or test_expectations[index].is_comment():
+ removed_whitespace = False
+ while index and test_expectations[index - 1].is_whitespace():
+ index = index - 1
qyearsley 2016/07/08 17:55:03 `index = index - 1` can also be written as `index
bokan 2016/07/08 21:13:00 Done.
+ test_expectations.pop(index)
+ removed_whitespace = True
+
+ if not removed_whitespace:
+ while index and test_expectations[index - 1].is_comment():
+ index = index - 1
+ test_expectations.pop(index)
+
+ while index and test_expectations[index - 1].is_whitespace():
+ index = index - 1
+ test_expectations.pop(index)
qyearsley 2016/07/08 17:55:03 This block of code (removing associated comments a
bokan 2016/07/08 21:13:00 Done.
+
return test_expectations
def write_test_expectations(self, test_expectations, test_expectations_file):

Powered by Google App Engine
This is Rietveld 408576698