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

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

Issue 2119303003: Create the harness and shell of update_test_expectations (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@updateTestExpectations1
Patch Set: Create the harness and shell of update_test_expectations 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_unittest.py
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations_unittest.py
new file mode 100644
index 0000000000000000000000000000000000000000..655d6a4b9301680fc5f4afe50bfdf8ab05df3e02
--- /dev/null
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/update_test_expectations_unittest.py
@@ -0,0 +1,330 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import unittest
+import StringIO
+from collections import OrderedDict
qyearsley 2016/07/08 00:06:21 Nit: In most of the files, there's a blank line be
bokan 2016/07/08 14:29:49 Done.
+from webkitpy.common.host_mock import MockHost
+from webkitpy.common.system.filesystem_mock import MockFileSystem
+from webkitpy.layout_tests.builder_list import BuilderList
+from webkitpy.layout_tests.port.factory import PortFactory
+from webkitpy.layout_tests.port.test import LAYOUT_TEST_DIR
+from webkitpy.layout_tests.update_test_expectations import main
+from webkitpy.layout_tests.update_test_expectations import RemoveFlakesOMatic
+
+logger = logging.getLogger()
+logger.level = logging.DEBUG
+
+
+class FakeBotTestExpectations(object):
+
+ def __init__(self, results_by_path):
+ self._results = results_by_path
+
+ def all_results_by_path(self):
+ return self._results
+
+
+class FakeBotTestExpectationsFactory(object):
+
+ def __init__(self):
+ """
+ The distinct results seen in at least one run of the test.
+ E.g. if the bot results for mytest.html are:
+ PASS PASS FAIL PASS TIMEOUT
+ then _all_results_by_builder would be:
+ {
+ 'WebKit Linux' : {
+ 'mytest.html': ['FAIL', 'PASS', 'TIMEOUT']
+ }
+ }
+ """
+ self._all_results_by_builder = {}
+
+ def expectations_for_builder(self, builder):
+ if builder not in self._all_results_by_builder:
+ return None
+
+ return FakeBotTestExpectations(self._all_results_by_builder[builder])
+
+
+class FakePortFactory(PortFactory):
+
+ def __init__(self, host):
+ super(FakePortFactory, self).__init__(host)
+ self._all_build_types = ()
+ self._all_systems = ()
+ self._configuration_specifier_macros = {
+ 'mac': ['mac10.10'],
+ 'win': ['win7'],
+ 'linux': ['precise']
+ }
+
+ def get(self, port_name=None, options=None, **kwargs):
+ """Returns an object implementing the Port interface. This
+ fake object will always return the 'test' port factory."""
qyearsley 2016/07/08 00:06:21 I personally think that docstrings with a one-line
bokan 2016/07/08 14:29:49 Done.
+ port = super(FakePortFactory, self).get('test', None)
+ port._all_build_types = self._all_build_types
+ port._all_systems = self._all_systems
+ port._configuration_specifier_macros = (
+ self._configuration_specifier_macros)
Dirk Pranke 2016/07/06 21:51:44 as suggested in the earlier CL, update these names
bokan 2016/07/07 22:08:32 Done.
+ return port
+
+
+class UpdateTestExpectationsTest(unittest.TestCase):
+
+ def setUp(self):
+ self._host = MockHost()
+ self._port = self._host.port_factory.get('test', None)
+ self._expectation_factory = FakeBotTestExpectationsFactory()
+ self._flake_remover = RemoveFlakesOMatic(self._host,
+ self._port,
+ self._expectation_factory)
+ self._port._configuration_specifier_macros = {
+ 'mac': ['mac10.10'],
+ 'win': ['win7'],
+ 'linux': ['precise']
+ }
+ filesystem = self._host.filesystem
+ self._write_tests_into_filesystem(filesystem)
+
+ self._log_output = StringIO.StringIO()
+ self._stream_handler = logging.StreamHandler(self._log_output)
+ logger.addHandler(self._stream_handler)
+
+ def tearDown(self):
+ logger.removeHandler(self._stream_handler)
+ self._log_output.close()
+
+ def _write_tests_into_filesystem(self, filesystem):
+ test_list = ['test/a.html',
+ 'test/b.html',
+ 'test/c.html',
+ 'test/d.html',
+ 'test/e.html',
+ 'test/f.html',
+ 'test/g.html']
+ for test in test_list:
+ path = filesystem.join(LAYOUT_TEST_DIR, test)
+ filesystem.write_binary_file(path, '')
+
+ def _assert_expectations_match(self, expectations, expected_string):
+ self.assertIsNotNone(expectations)
+ stringified_expectations = "\n".join(
+ x.to_string(None) for x in expectations)
qyearsley 2016/07/06 23:18:04 Note: I wonder if it would be nice to change TestE
bokan 2016/07/07 22:08:32 It turns out this is the only use of TestExpectati
+ expected_string = "\n".join(
+ x.strip() for x in expected_string.split("\n"))
+ self.assertEqual(stringified_expectations, expected_string)
+
+ def _parse_expectations(self, expectations):
+ """Parses a TestExpectation file given as string.
+
+ This function takes a string representing the contents of the
+ TestExpectations file and parses it, producing the TestExpectations
+ object and sets it on the Port object where the script will read it
+ from.
+
+ Args:
+ expectations: A string containing the contents of the
+ TestExpectations file to use.
+ """
+ expectations_dict = OrderedDict()
+ expectations_dict['expectations'] = expectations
+ self._port.expectations_dict = lambda: expectations_dict
+
+ def _define_builders(self, builders_dict):
+ """Defines the available builders for the test.
+
+ Args:
+ builders_dict: A dictionary containing builder names to their
+ attributes, see BuilderList.__init__ for the format.
+ """
+ self._host.builders = BuilderList(builders_dict)
+
+ def test_dont_remove_non_flakes(self):
+ """Tests that lines that aren't flaky are not touched.
+
+ Lines are flaky if they contain a PASS as well as at least one other
+ failing result.
+ """
+ test_expectations_before = """
+ # Even though the results show all passing, none of the
+ # expectations are flaky so we shouldn't remove any.
+ Bug(test) test/a.html [ Pass ]
+ Bug(test) test/b.html [ Timeout ]
+ Bug(test) test/c.html [ Failure Timeout ]
+ Bug(test) test/d.html [ Rebaseline ]
+ Bug(test) test/e.html [ NeedsManualRebaseline ]
+ Bug(test) test/f.html [ NeedsRebaseline ]"""
+
+ self._define_builders({
+ "WebKit Linux": {
+ "port_name": "linux-precise",
+ "specifiers": ['Precise', 'Release']
+ },
+ })
+ self._port._all_build_types = ('release',)
+ self._port._all_systems = (('precise', 'x86_64'),)
+
+ self._parse_expectations(test_expectations_before)
+ self._expectation_factory._all_results_by_builder = {
+ 'WebKit Linux': {
+ "test/a.html": ["PASS", "PASS"],
+ "test/b.html": ["PASS", "PASS"],
+ "test/c.html": ["PASS", "PASS"],
+ "test/d.html": ["PASS", "PASS"],
+ "test/e.html": ["PASS", "PASS"],
+ "test/f.html": ["PASS", "PASS"],
+ }
+ }
+ updated_expectations = (
+ self._flake_remover.get_updated_test_expectations())
+ self._assert_expectations_match(
+ updated_expectations, test_expectations_before)
+
+ def test_dont_remove_skip(self):
+ """Tests that lines with Skip are untouched.
+
+ If a line is marked as Skip, it will eventually contain no results,
+ which is indistinguishable from "All Passing" so don't remove since we
+ don't know what the results actually are.
+ """
+ test_expectations_before = """
+ # Skip expectations should never be removed.
+ Bug(test) test/a.html [ Skip ]
+ Bug(test) test/b.html [ Skip ]
+ Bug(test) test/c.html [ Skip ]"""
+
+ self._define_builders({
+ "WebKit Linux": {
+ "port_name": "linux-precise",
+ "specifiers": ['Precise', 'Release']
+ },
+ })
+ self._port._all_build_types = ('release',)
+ self._port._all_systems = (('precise', 'x86_64'),)
+
+ self._parse_expectations(test_expectations_before)
+ self._expectation_factory._all_results_by_builder = {
+ 'WebKit Linux': {
+ "test/a.html": ["PASS", "PASS"],
+ "test/b.html": ["PASS", "IMAGE"],
+ }
+ }
+ updated_expectations = (
+ self._flake_remover.get_updated_test_expectations())
+ self._assert_expectations_match(
+ updated_expectations, test_expectations_before)
+
+ def test_dont_remove_rebaselines(self):
+ """Tests that lines with rebaseline expectations are untouched.
+ """
qyearsley 2016/07/06 23:18:04 Nit: end quote can go on the line above.
bokan 2016/07/07 22:08:32 Done. Does this go for multiline comments too or j
qyearsley 2016/07/08 00:06:21 The Python style guide for Blink just say to use P
bokan 2016/07/08 14:29:49 Thanks!
+ test_expectations_before = """
+ # Even though the results show all passing, none of the
qyearsley 2016/07/08 00:06:21 I assume it's not a problem that these lines start
bokan 2016/07/08 14:29:49 I think this is easier to read and the assert_expe
+ # expectations are flaky so we shouldn't remove any.
+ Bug(test) test/a.html [ Failure NeedsRebaseline Pass ]
+ Bug(test) test/b.html [ Failure Pass Rebaseline ]
+ Bug(test) test/c.html [ Failure NeedsManualRebaseline Pass ]"""
+
+ self._define_builders({
+ "WebKit Linux": {
+ "port_name": "linux-precise",
+ "specifiers": ['Precise', 'Release']
+ },
+ })
+ self._port._all_build_types = ('release',)
+ self._port._all_systems = (('precise', 'x86_64'),)
+
+ self._parse_expectations(test_expectations_before)
+ self._expectation_factory._all_results_by_builder = {
+ 'WebKit Linux': {
+ "test/a.html": ["PASS", "PASS"],
+ "test/b.html": ["PASS", "PASS"],
+ "test/c.html": ["PASS", "PASS"]
+ }
+ }
+ updated_expectations = (
+ self._flake_remover.get_updated_test_expectations())
+ self._assert_expectations_match(
+ updated_expectations, test_expectations_before)
+
+ def test_all_failure_case(self):
+ """Tests that results with all failures are not treated as non-flaky."""
+ test_expectations_before = (
+ """# Keep since it's all failures.
+ Bug(test) test/a.html [ Failure Pass ]""")
+
+ self._define_builders({
+ "WebKit Linux": {
+ "port_name": "linux-precise",
+ "specifiers": ['Precise', 'Release']
+ },
+ })
+ self._port._all_build_types = ('release',)
+ self._port._all_systems = (('precise', 'x86_64'),)
+
+ self._parse_expectations(test_expectations_before)
+ self._expectation_factory._all_results_by_builder = {
+ 'WebKit Linux': {
+ "test/a.html": ["IMAGE", "IMAGE", "IMAGE"],
+ }
+ }
+ updated_expectations = (
+ self._flake_remover.get_updated_test_expectations())
+ self._assert_expectations_match(updated_expectations, (
+ """# Keep since it's all failures.
+ Bug(test) test/a.html [ Failure Pass ]"""))
+
+ def test_empty_test_expectations(self):
+ """Running on an empty TestExpectations file outputs an empty file."""
+ test_expectations_before = ""
+
+ self._define_builders({
+ "WebKit Linux": {
+ "port_name": "linux-precise",
+ "specifiers": ['Precise', 'Release']
+ },
+ })
+ self._port._all_build_types = ('release',)
+ self._port._all_systems = (('precise', 'x86_64'),)
+
+ self._parse_expectations(test_expectations_before)
+ self._expectation_factory._all_results_by_builder = {
+ 'WebKit Linux': {
+ "test/a.html": ["PASS", "PASS", "PASS"],
+ }
+ }
+ updated_expectations = (
+ self._flake_remover.get_updated_test_expectations())
+ self._assert_expectations_match(updated_expectations, "")
+
+ def test_harness_no_expectations(self):
+ """Tests that a warning is outputted if the TestExpectations file
+ doesn't exist.
+ """
+
+ # Setup the mock host and port.
+ host = MockHost()
+ host.port_factory = FakePortFactory(host)
+
+ # Write the test file but not the TestExpectations file.
+ test_expectation_path = (
+ host.port_factory.get().path_to_generic_test_expectations_file())
+ host.filesystem = MockFileSystem()
+ self._write_tests_into_filesystem(host.filesystem)
+
+ # Write out the fake builder bot results.
+ expectation_factory = FakeBotTestExpectationsFactory()
+ expectation_factory._all_results_by_builder = {}
+
+ self.assertFalse(host.filesystem.isfile(test_expectation_path))
+
+ main(host, expectation_factory, [])
+
+ expected_warning = (
+ "Didn't find generic expectations file at: " +
+ test_expectation_path + "\n")
+ self.assertEqual(self._log_output.getvalue(), expected_warning)
+ self.assertFalse(host.filesystem.isfile(test_expectation_path))

Powered by Google App Engine
This is Rietveld 408576698