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

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

Issue 1766583002: Added update_test_expectations script to remove non-flaky test expectations. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 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
new file mode 100644
index 0000000000000000000000000000000000000000..a1741197f80a3255f6587f3da2e315cae51593d2
--- /dev/null
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations.py
@@ -0,0 +1,126 @@
+import optparse
+
+from webkitpy.layout_tests.models.test_expectations import TestExpectations
+from webkitpy.layout_tests.layout_package.bot_test_expectations import BotTestExpectationsFactory
+from webkitpy.layout_tests.port import builders
+
+
+def main(host, argv):
+ parser = optparse.OptionParser(usage='%prog ')
+ # parser.add_option('-f', '--forward', action='store', type='int',
+ # help='group times by first N directories of test')
+ # parser.add_option('-b', '--backward', action='store', type='int',
+ # help='group times by last N directories of test')
+ # parser.add_option('--fastest', action='store', type='float',
+ # help='print a list of tests that will take N % of the time')
+
+ epilog = """
+ Test test test"""
+ parser.epilog = '\n'.join(s.lstrip() for s in epilog.splitlines())
+
+ options, args = parser.parse_args(argv)
qyearsley 2016/03/07 19:09:53 This could be switched to argparse, the successor
bokan 2016/03/07 22:59:15 Thanks, done.
+
+ remove_flakes_o_matic = RemoveFlakesOMatic()
+ remove_flakes_o_matic.execute(host)
qyearsley 2016/03/07 19:09:53 Possible refactoring: Maybe RemoveFlakesOMatic.exe
bokan 2016/03/07 22:59:15 My patch is based on Ojan's CL which originally us
+
+
+class RemoveFlakesOMatic:
qyearsley 2016/03/07 19:09:53 What do instances of this class represent? Could t
bokan 2016/03/07 22:59:16 Addressed above.
+ def __init__(self):
+ self.expectations_factory = BotTestExpectationsFactory()
+
+ def _can_delete_line(self, test_expectation_line):
qyearsley 2016/03/07 19:09:53 test_expectation_line is an instance of test_expec
bokan 2016/03/07 22:59:15 Done.
+ expectations = test_expectation_line.expectations
+ if len(expectations) < 2:
+ return False
qyearsley 2016/03/07 19:09:53 What does an expectation list of length 0 or 1 ind
bokan 2016/03/07 22:59:15 It means the test isn't flaky and this script (rig
+
+ if self._has_unstripable_expectations(expectations):
+ return False
+
+ if not self._has_pass_expectation(expectations):
+ return False
+
+ # 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 = builders.builder_name_for_specifiers(config.version, config.build_type)
+
+ if not builder_name:
+ # TODO(bokan): We probably want to report a problem if we can't get turn the
+ # specifiers into a bot.
+ # TODO(bokan): Matching configurations often give us bots that don't have a
bokan 2016/03/04 03:44:10 There seems to be more matching_configurations tha
+ # builder in builders.py's exact_matches. Should we ignore those or be conservative
+ # and assume those need these expectations?
+ return False
+
+ if builder_name not in self.builder_results_by_path.keys():
+ # TODO(bokan): We probably want to report a problem if we can't get results for
+ # a bot.
+ return False
+
+ results_by_path = self.builder_results_by_path[builder_name]
+
+ # No results means the test was all skipped or all results are passing.
+ if test_expectation_line.path not in results_by_path.keys():
+ continue
+
+ results_for_test = results_by_path[test_expectation_line.path]
+
+ if self._met_expectations(test_expectation_line, results_for_test) != set(['PASS']):
+ return False
+
+ return True
+
+ def _has_pass_expectation(self, expectations):
+ return 'PASS' in expectations
+
+ def _met_expectations(self, test_expectation_line, results_for_test):
+ # TODO(bokan): Does this not exist in a more central place?
bokan 2016/03/04 03:44:10 Ditto here, is there a function that turns "failur
+ def replace_failing_with_fail(expectation):
+ if expectation in ('TEXT', 'IMAGE', 'IMAGE+TEXT', 'AUDIO'):
+ return 'FAIL'
+ else:
+ return expectation
+
+ test_results = set(results_for_test)
qyearsley 2016/03/07 19:09:53 This line could be removed without changing the ou
bokan 2016/03/07 22:59:15 Yes, you're right, removed.
+
+ actual_results = set(map(replace_failing_with_fail, test_results))
qyearsley 2016/03/07 19:09:54 Possible rephrasing: actual_results = {replac
bokan 2016/03/07 22:59:15 Done.
+
+ return set(test_expectation_line.expectations) & actual_results
+
+ def _has_unstripable_expectations(self, expectations):
qyearsley 2016/03/07 19:09:53 unstripable -> unstrippable
bokan 2016/03/07 22:59:15 Done.
+ return ('REBASELINE' in expectations
+ or 'NEEDSREBASLINE' in expectations
+ or 'NEEDSMANUALREBASELINE' in expectations
+ or 'SLOW' in expectations)
qyearsley 2016/03/07 19:09:53 Possible alternate phrasing: passing_statuses
bokan 2016/03/07 22:59:15 Done.
+
+ def execute(self, host):
+ port = host.port_factory.get()
+ test_expectations = TestExpectations(port, include_overrides=False)._expectations
qyearsley 2016/03/07 19:09:53 1. What is the class of the objects in test_expect
bokan 2016/03/07 22:59:15 test_expectations is a list of TestExpectationLine
+
+ self.builder_results_by_path = {}
+ for builder_name in builders.all_builder_names():
+ self.builder_results_by_path[builder_name] = (
+ self.expectations_factory.expectations_for_builder(builder_name).all_results_by_path()
+ )
+ pass
+
+ 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 if we've removed the last expectation under a comment block.
+ if index == len(test_expectations) or test_expectations[index].is_whitespace_or_comment():
+ while index and test_expectations[index - 1].is_whitespace_or_comment():
+ index = index - 1
+ test_expectations.pop(index)
+
+ expectations_file = port.path_to_generic_test_expectations_file()
bokan 2016/03/04 03:44:10 Presumably we also want to update ASAN and other b
+ host.filesystem.write_text_file(
+ expectations_file,
+ TestExpectations.list_to_string(test_expectations, reconstitute_only_these=[]))

Powered by Google App Engine
This is Rietveld 408576698