blob: f490a6b764f5447c3a5b9f64b16eed3cc99bf75f [file] [log] [blame]
# 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
from collections import OrderedDict
from blinkpy.common.host_mock import MockHost
from blinkpy.common.system.filesystem_mock import MockFileSystem
from blinkpy.common.system.log_testing import LoggingTestCase
from blinkpy.web_tests.update_expectations import ExpectationsRemover
from blinkpy.web_tests.builder_list import BuilderList
from blinkpy.web_tests.port.factory import PortFactory
from blinkpy.web_tests.port.test import WEB_TEST_DIR
from blinkpy.web_tests.update_expectations import main
from blinkpy.tool.commands.flaky_tests import FlakyTests
class FakeBotTestExpectations(object):
def __init__(self, results_by_path):
self._results = {}
# Make the results distinct like the real BotTestExpectations.
for path, results in results_by_path.iteritems():
self._results[path] = list(set(results))
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.
For example, if the bot results for mytest.html are:
PASS PASS FAIL PASS TIMEOUT
then |all_results_by_builder| would be:
{
'WebKit Linux Trusty' : {
'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, all_build_types=None, all_systems=None):
super(FakePortFactory, self).__init__(host)
self._all_build_types = all_build_types or ()
self._all_systems = all_systems or ()
self._configuration_specifier_macros = {
'mac': ['mac10.10'],
'win': ['win7'],
'linux': ['trusty']
}
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.
"""
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_dict = self._configuration_specifier_macros
return port
class MockWebBrowser(object):
def __init__(self):
self.opened_url = None
def open(self, url):
self.opened_url = url
class UpdateTestExpectationsTest(LoggingTestCase):
FLAKE_TYPE = 'flake'
FAIL_TYPE = 'fail'
def setUp(self):
super(UpdateTestExpectationsTest, self).setUp()
self._mock_web_browser = MockWebBrowser()
self._host = MockHost()
self._port = self._host.port_factory.get('test', None)
self._expectation_factory = FakeBotTestExpectationsFactory()
self._port.configuration_specifier_macros_dict = {
'mac': ['mac10.10'],
'win': ['win7'],
'linux': ['trusty']
}
filesystem = self._host.filesystem
self._write_tests_into_filesystem(filesystem)
def tearDown(self):
super(UpdateTestExpectationsTest, self).tearDown()
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(WEB_TEST_DIR, test)
filesystem.write_binary_file(path, '')
def _create_expectations_remover(self, type_flag='all', remove_missing=False):
return ExpectationsRemover(
self._host,
self._port,
self._expectation_factory,
self._mock_web_browser,
type_flag,
remove_missing)
def _assert_expectations_match(self, expectations, expected_string):
self.assertIsNotNone(expectations)
stringified_expectations = '\n'.join(
x.to_string() for x in expectations)
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_flake_mode_doesnt_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 ]"""
self._expectations_remover = (
self._create_expectations_remover(self.FLAKE_TYPE))
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'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._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(
updated_expectations, test_expectations_before)
def test_fail_mode_doesnt_remove_non_fails(self):
"""Tests that lines that aren't failing are not touched.
Lines are failing if they contain only 'Failure', 'Timeout', or
'Crash' results.
"""
test_expectations_before = """
# Even though the results show all passing, none of the
# expectations are failing so we shouldn't remove any.
Bug(test) test/a.html [ Pass ]
Bug(test) test/b.html [ Failure Pass ]
Bug(test) test/c.html [ Failure Pass Timeout ]"""
self._expectations_remover = (
self._create_expectations_remover(self.FAIL_TYPE))
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'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._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(
updated_expectations, test_expectations_before)
def test_dont_remove_directory_flake(self):
"""Tests that flake lines with directories are untouched."""
test_expectations_before = """
# This expectation is for a whole directory.
Bug(test) test/ [ Failure Pass ]"""
self._expectations_remover = (
self._create_expectations_remover(self.FLAKE_TYPE))
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'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._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(
updated_expectations, test_expectations_before)
def test_dont_remove_directory_fail(self):
"""Tests that fail lines with directories are untouched."""
test_expectations_before = """
# This expectation is for a whole directory.
Bug(test) test/ [ Failure ]"""
self._expectations_remover = (
self._create_expectations_remover(self.FAIL_TYPE))
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'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._expectations_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._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS'],
'test/b.html': ['PASS', 'IMAGE'],
}
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(
updated_expectations, test_expectations_before)
def test_all_failure_result_types(self):
"""Tests that all failure types are treated as failure."""
test_expectations_before = (
"""Bug(test) test/a.html [ Failure Pass ]
Bug(test) test/b.html [ Failure Pass ]
Bug(test) test/c.html [ Failure Pass ]
Bug(test) test/d.html [ Failure Pass ]
# Remove these two since CRASH and TIMEOUT aren't considered
# Failure.
Bug(test) test/e.html [ Failure Pass ]
Bug(test) test/f.html [ Failure Pass ]""")
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'IMAGE'],
'test/b.html': ['PASS', 'TEXT'],
'test/c.html': ['PASS', 'IMAGE+TEXT'],
'test/d.html': ['PASS', 'AUDIO'],
'test/e.html': ['PASS', 'CRASH'],
'test/f.html': ['PASS', 'TIMEOUT'],
}
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, (
"""Bug(test) test/a.html [ Failure Pass ]
Bug(test) test/b.html [ Failure Pass ]
Bug(test) test/c.html [ Failure Pass ]
Bug(test) test/d.html [ Failure Pass ]"""))
def test_fail_mode_all_fail_types_removed(self):
"""Tests that all types of fail expectation are removed in fail mode.
Fail expectation types include Failure, Timeout, and Crash.
"""
test_expectations_before = (
"""Bug(test) test/a.html [ Failure ]
Bug(test) test/b.html [ Timeout ]
Bug(test) test/c.html [ Crash ]
Bug(test) test/d.html [ Failure ]""")
self._expectations_remover = (
self._create_expectations_remover(self.FAIL_TYPE))
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS'],
}
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
# The line with test/d.html is not removed since
# --remove-missing is false by default; lines for
# tests with no actual results are kept.
self._assert_expectations_match(
updated_expectations,
'Bug(test) test/d.html [ Failure ]')
def test_basic_one_builder(self):
"""Tests basic functionality with a single builder.
Test that expectations with results from a single bot showing the
expected failure isn't occurring should be removed. Results with failures
of the expected type shouldn't be removed but other kinds of failures
allow removal.
"""
test_expectations_before = (
"""# Remove these two since they're passing all runs.
Bug(test) test/a.html [ Failure Pass ]
Bug(test) test/b.html [ Failure ]
# Remove these two since the failure is not a Timeout
Bug(test) test/c.html [ Pass Timeout ]
Bug(test) test/d.html [ Timeout ]
# Keep since we have both crashes and passes.
Bug(test) test/e.html [ Crash Pass ]""")
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'IMAGE', 'PASS'],
'test/d.html': ['PASS', 'IMAGE', 'PASS'],
'test/e.html': ['PASS', 'CRASH', 'PASS'],
}
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, (
"""# Keep since we have both crashes and passes.
Bug(test) test/e.html [ Crash Pass ]"""))
def test_flake_mode_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._expectations_remover = (
self._create_expectations_remover(self.FLAKE_TYPE))
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['IMAGE', 'IMAGE', 'IMAGE'],
}
}
updated_expectations = (
self._expectations_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_remove_none_met(self):
"""Tests that expectations with no matching result are removed.
Expectations that are failing in a different way than specified should
be removed, even if there is no passing result.
"""
test_expectations_before = (
"""# Remove all since CRASH and TIMEOUT aren't considered Failure.
Bug(test) test/a.html [ Failure Pass ]
Bug(test) test/b.html [ Failure Pass ]
Bug(test) test/c.html [ Failure ]
Bug(test) test/d.html [ Failure ]""")
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['CRASH'],
'test/b.html': ['TIMEOUT'],
'test/c.html': ['CRASH'],
'test/d.html': ['TIMEOUT'],
}
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, (''))
def test_empty_test_expectations(self):
"""Running on an empty TestExpectations file outputs an empty file."""
test_expectations_before = ''
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
}
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, '')
def test_basic_multiple_builders(self):
"""Tests basic functionality with multiple builders."""
test_expectations_before = (
"""# Remove these two since they're passing on both builders.
Bug(test) test/a.html [ Failure Pass ]
Bug(test) test/b.html [ Failure ]
# Keep these two since they're failing on the Mac builder.
Bug(test) test/c.html [ Failure Pass ]
Bug(test) test/d.html [ Failure ]
# Keep these two since they're failing on the Linux builder.
Bug(test) test/e.html [ Failure Pass ]
Bug(test) test/f.html [ Failure ]""")
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
'WebKit Mac10.10': {
'port_name': 'mac-mac10.10',
'specifiers': ['Mac10.10', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('mac10.10', 'x86'),
('trusty', 'x86_64'))
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
'test/e.html': ['AUDIO', 'AUDIO', 'AUDIO'],
'test/f.html': ['AUDIO', 'AUDIO', 'AUDIO'],
},
'WebKit Mac10.10': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'IMAGE'],
'test/d.html': ['PASS', 'PASS', 'IMAGE'],
'test/e.html': ['PASS', 'PASS', 'PASS'],
'test/f.html': ['PASS', 'PASS', 'PASS'],
},
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, (
"""# Keep these two since they're failing on the Mac builder.
Bug(test) test/c.html [ Failure Pass ]
Bug(test) test/d.html [ Failure ]
# Keep these two since they're failing on the Linux builder.
Bug(test) test/e.html [ Failure Pass ]
Bug(test) test/f.html [ Failure ]"""))
def test_multiple_builders_and_platform_specifiers(self):
"""Tests correct operation with platform specifiers."""
test_expectations_before = (
"""# Keep these two since they're failing in the Mac10.10 results.
Bug(test) [ Mac ] test/a.html [ Failure Pass ]
Bug(test) [ Mac ] test/b.html [ Failure ]
# Keep these two since they're failing on the Windows builder.
Bug(test) [ Linux Win ] test/c.html [ Failure Pass ]
Bug(test) [ Linux Win ] test/d.html [ Failure ]
# Remove these two since they're passing on both Linux and Windows builders.
Bug(test) [ Linux Win ] test/e.html [ Failure Pass ]
Bug(test) [ Linux Win ] test/f.html [ Failure ]
# Remove these two since they're passing on Mac results
Bug(test) [ Mac ] test/g.html [ Failure Pass ]
Bug(test) [ Mac ] test/h.html [ Failure ]""")
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
'WebKit Mac10.10': {
'port_name': 'mac-mac10.10',
'specifiers': ['Mac10.10', 'Release']
},
'WebKit Mac10.11': {
'port_name': 'mac-mac10.11',
'specifiers': ['Mac10.11', 'Release']
},
'WebKit Win7': {
'port_name': 'win-win7',
'specifiers': ['Win7', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (
('mac10.10', 'x86'),
('mac10.11', 'x86'),
('trusty', 'x86_64'),
('win7', 'x86'),
)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
'test/e.html': ['PASS', 'PASS', 'PASS'],
'test/f.html': ['PASS', 'PASS', 'PASS'],
'test/g.html': ['IMAGE', 'PASS', 'PASS'],
'test/h.html': ['IMAGE', 'PASS', 'PASS'],
},
'WebKit Mac10.10': {
'test/a.html': ['PASS', 'PASS', 'IMAGE'],
'test/b.html': ['PASS', 'PASS', 'IMAGE'],
'test/c.html': ['PASS', 'IMAGE', 'PASS'],
'test/d.html': ['PASS', 'IMAGE', 'PASS'],
'test/e.html': ['PASS', 'IMAGE', 'PASS'],
'test/f.html': ['PASS', 'IMAGE', 'PASS'],
'test/g.html': ['PASS', 'PASS', 'PASS'],
'test/h.html': ['PASS', 'PASS', 'PASS'],
},
'WebKit Mac10.11': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
'test/e.html': ['PASS', 'PASS', 'PASS'],
'test/f.html': ['PASS', 'PASS', 'PASS'],
'test/g.html': ['PASS', 'PASS', 'PASS'],
'test/h.html': ['PASS', 'PASS', 'PASS'],
},
'WebKit Win7': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['IMAGE', 'PASS', 'PASS'],
'test/d.html': ['IMAGE', 'PASS', 'PASS'],
'test/e.html': ['PASS', 'PASS', 'PASS'],
'test/f.html': ['PASS', 'PASS', 'PASS'],
'test/g.html': ['IMAGE', 'PASS', 'PASS'],
'test/h.html': ['IMAGE', 'PASS', 'PASS'],
},
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, (
"""# Keep these two since they're failing in the Mac10.10 results.
Bug(test) [ Mac ] test/a.html [ Failure Pass ]
Bug(test) [ Mac ] test/b.html [ Failure ]
# Keep these two since they're failing on the Windows builder.
Bug(test) [ Linux Win ] test/c.html [ Failure Pass ]
Bug(test) [ Linux Win ] test/d.html [ Failure ]"""))
def test_debug_release_specifiers(self):
"""Tests correct operation of Debug/Release specifiers."""
test_expectations_before = (
"""# Keep these two since they fail in debug.
Bug(test) [ Linux ] test/a.html [ Failure Pass ]
Bug(test) [ Linux ] test/b.html [ Failure ]
# Remove these two since failure is in Release, Debug is all PASS.
Bug(test) [ Debug ] test/c.html [ Failure Pass ]
Bug(test) [ Debug ] test/d.html [ Failure ]
# Keep these two since they fail in Linux Release.
Bug(test) [ Release ] test/e.html [ Failure Pass ]
Bug(test) [ Release ] test/f.html [ Failure ]
# Remove these two since the Release Linux builder is all passing.
Bug(test) [ Release Linux ] test/g.html [ Failure Pass ]
Bug(test) [ Release Linux ] test/h.html [ Failure ]
# Remove these two since all the Linux builders PASS.
Bug(test) [ Linux ] test/i.html [ Failure Pass ]
Bug(test) [ Linux ] test/j.html [ Failure ]""")
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Win7': {
'port_name': 'win-win7',
'specifiers': ['Win7', 'Release']
},
'WebKit Win7 (dbg)': {
'port_name': 'win-win7',
'specifiers': ['Win7', 'Debug']
},
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
'WebKit Linux Trusty (dbg)': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Debug']
},
})
self._port.all_build_types = ('release', 'debug')
self._port.all_systems = (('win7', 'x86'),
('trusty', 'x86_64'))
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'IMAGE', 'PASS'],
'test/d.html': ['PASS', 'IMAGE', 'PASS'],
'test/e.html': ['PASS', 'IMAGE', 'PASS'],
'test/f.html': ['PASS', 'IMAGE', 'PASS'],
'test/g.html': ['PASS', 'PASS', 'PASS'],
'test/h.html': ['PASS', 'PASS', 'PASS'],
'test/i.html': ['PASS', 'PASS', 'PASS'],
'test/j.html': ['PASS', 'PASS', 'PASS'],
},
'WebKit Linux Trusty (dbg)': {
'test/a.html': ['PASS', 'IMAGE', 'PASS'],
'test/b.html': ['PASS', 'IMAGE', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
'test/e.html': ['PASS', 'PASS', 'PASS'],
'test/f.html': ['PASS', 'PASS', 'PASS'],
'test/g.html': ['IMAGE', 'PASS', 'PASS'],
'test/h.html': ['IMAGE', 'PASS', 'PASS'],
'test/i.html': ['PASS', 'PASS', 'PASS'],
'test/j.html': ['PASS', 'PASS', 'PASS'],
},
'WebKit Win7 (dbg)': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
'test/e.html': ['PASS', 'PASS', 'PASS'],
'test/f.html': ['PASS', 'PASS', 'PASS'],
'test/g.html': ['PASS', 'IMAGE', 'PASS'],
'test/h.html': ['PASS', 'IMAGE', 'PASS'],
'test/i.html': ['PASS', 'PASS', 'PASS'],
'test/j.html': ['PASS', 'PASS', 'PASS'],
},
'WebKit Win7': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'IMAGE'],
'test/d.html': ['PASS', 'PASS', 'IMAGE'],
'test/e.html': ['PASS', 'PASS', 'PASS'],
'test/f.html': ['PASS', 'PASS', 'PASS'],
'test/g.html': ['PASS', 'IMAGE', 'PASS'],
'test/h.html': ['PASS', 'IMAGE', 'PASS'],
'test/i.html': ['PASS', 'PASS', 'PASS'],
'test/j.html': ['PASS', 'PASS', 'PASS'],
},
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, (
"""# Keep these two since they fail in debug.
Bug(test) [ Linux ] test/a.html [ Failure Pass ]
Bug(test) [ Linux ] test/b.html [ Failure ]
# Keep these two since they fail in Linux Release.
Bug(test) [ Release ] test/e.html [ Failure Pass ]
Bug(test) [ Release ] test/f.html [ Failure ]"""))
def test_preserve_comments_and_whitespace(self):
"""Tests that comments and whitespace are preserved appropriately.
Comments and whitespace should be kept unless all the tests grouped
below a comment are removed. In that case the comment block should also
be removed.
Ex:
# This comment applies to the below tests.
Bug(test) test/a.html [ Failure Pass ]
Bug(test) test/b.html [ Failure Pass ]
# <some prose>
# This is another comment.
Bug(test) test/c.html [ Failure Pass ]
Assuming we removed a.html and c.html we get:
# This comment applies to the below tests.
Bug(test) test/b.html [ Failure Pass ]
# <some prose>
"""
test_expectations_before = """
# Comment A - Keep since these aren't part of any test.
# Comment B - Keep since these aren't part of any test.
# Comment C - Remove since it's a block belonging to a
# Comment D - and a is removed.
Bug(test) test/a.html [ Failure Pass ]
# Comment E - Keep since it's below a.
# Comment F - Keep since only b is removed
Bug(test) test/b.html [ Failure Pass ]
Bug(test) test/c.html [ Failure Pass ]
# Comment G - Should be removed since both d and e will be removed.
Bug(test) test/d.html [ Failure Pass ]
Bug(test) test/e.html [ Failure Pass ]"""
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'IMAGE', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
'test/e.html': ['PASS', 'PASS', 'PASS'],
}
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, (
"""
# Comment A - Keep since these aren't part of any test.
# Comment B - Keep since these aren't part of any test.
# Comment E - Keep since it's below a.
# Comment F - Keep since only b is removed
Bug(test) test/c.html [ Failure Pass ]"""))
def test_lines_with_no_results_on_builders_kept_by_default(self):
"""Tests the case where there are lines with no results on the builders.
A test that has no results returned from the builders means that all
runs passed or were skipped. This might be because the test is skipped
because it's not in the SmokeTests file (e.g. on Android); or it might
potentially be that the test is legitimately no longer run anywhere.
In the former case, we may want to keep the line; but it may also be
useful to be able to remove it.
"""
test_expectations_before = """
# A Skip expectation probably won't have any results but we
# shouldn't consider those passing so this line should remain.
Bug(test) test/a.html [ Skip ]
# The lines below should be kept since the flag for removing
# such results (--remove-missing) is not passed.
Bug(test) test/b.html [ Failure Timeout ]
Bug(test) test/c.html [ Failure Pass ]
Bug(test) test/d.html [ Pass Timeout ]
Bug(test) test/e.html [ Crash Pass ]"""
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {}
}
self._expectations_remover = self._create_expectations_remover()
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, test_expectations_before)
def test_lines_with_no_results_on_builders_can_be_removed(self):
"""Tests that we remove a line that has no results on the builders.
In this test, we simulate what would happen when --remove-missing
is passed.
"""
test_expectations_before = """
# A Skip expectation probably won't have any results but we
# shouldn't consider those passing so this line should remain.
Bug(test) test/a.html [ Skip ]
# The lines below should be removed since the flag for removing
# such results (--remove-missing) is passed.
Bug(test) test/b.html [ Failure Timeout ]
Bug(test) test/c.html [ Failure Pass ]
Bug(test) test/d.html [ Pass Timeout ]
Bug(test) test/e.html [ Crash Pass ]"""
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {}
}
self._expectations_remover = self._create_expectations_remover(
remove_missing=True)
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self._assert_expectations_match(updated_expectations, """
# A Skip expectation probably won't have any results but we
# shouldn't consider those passing so this line should remain.
Bug(test) test/a.html [ Skip ]""")
def test_missing_builders_for_some_configurations(self):
"""Tests the behavior when there are no builders for some configurations.
We don't necessarily expect to have builders for all configurations,
so as long as a test appears to not match the expectation on all
matching configurations that have builders, then it can be removed,
even if there are extra configurations with no existing builders.
"""
# Set the logging level used for assertLog to allow us to check
# messages with a "debug" severity level.
self.set_logging_level(logging.DEBUG)
test_expectations_before = """
# There are no builders that match this configuration at all.
Bug(test) [ Win ] test/a.html [ Failure Pass ]
# This matches the existing linux release builder and
# also linux debug, which has no builder.
Bug(test) [ Linux ] test/b.html [ Failure Pass ]
# This one is marked as Failing and there are some matching
# configurations with no builders, but for all configurations
# with existing builders it is passing.
Bug(test) test/c.html [ Failure ]
# This one is marked as flaky and there are some matching
# configurations with no builders, but for all configurations
# with existing builders, it is non-flaky.
Bug(test) test/d.html [ Failure Pass ]
# This one only matches the existing linux release builder,
# and it's still flaky, so it shouldn't be removed.
Bug(test) [ Linux Release ] test/e.html [ Failure Pass ]
# No message should be emitted for this one because it's not
# marked as flaky or failing, so we don't need to check builder
# results.
Bug(test) test/f.html [ Pass ]"""
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release', 'debug')
self._port.all_systems = (
('win7', 'x86'),
('trusty', 'x86_64'),
)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
'test/e.html': ['PASS', 'IMAGE', 'PASS'],
'test/f.html': ['PASS', 'IMAGE', 'PASS'],
}
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self.assertLog([
'DEBUG: No builder with config <win7, x86, release>\n',
'DEBUG: No builder with config <win7, x86, debug>\n',
'WARNING: No matching builders for line, deleting line.\n',
'INFO: Deleting line "Bug(test) [ Win ] test/a.html [ Failure Pass ]"\n',
'DEBUG: No builder with config <trusty, x86_64, debug>\n',
'DEBUG: Checked builders:\n WebKit Linux Trusty\n',
'INFO: Deleting line "Bug(test) [ Linux ] test/b.html [ Failure Pass ]"\n',
'DEBUG: No builder with config <trusty, x86_64, debug>\n',
'DEBUG: No builder with config <win7, x86, release>\n',
'DEBUG: No builder with config <win7, x86, debug>\n',
'DEBUG: Checked builders:\n WebKit Linux Trusty\n',
'INFO: Deleting line "Bug(test) test/c.html [ Failure ]"\n',
'DEBUG: No builder with config <trusty, x86_64, debug>\n',
'DEBUG: No builder with config <win7, x86, release>\n',
'DEBUG: No builder with config <win7, x86, debug>\n',
'DEBUG: Checked builders:\n WebKit Linux Trusty\n',
'INFO: Deleting line "Bug(test) test/d.html [ Failure Pass ]"\n',
])
self._assert_expectations_match(
updated_expectations,
"""
# This one only matches the existing linux release builder,
# and it's still flaky, so it shouldn't be removed.
Bug(test) [ Linux Release ] test/e.html [ Failure Pass ]
# No message should be emitted for this one because it's not
# marked as flaky or failing, so we don't need to check builder
# results.
Bug(test) test/f.html [ Pass ]""")
def test_log_missing_results(self):
"""Tests that we emit the appropriate error for missing results.
If the results dictionary we download from the builders is missing the
results from one of the builders we matched we should have logged an
error.
"""
test_expectations_before = """
Bug(test) [ Linux ] test/a.html [ Failure Pass ]
# This line won't emit an error since the Linux Release results
# exist.
Bug(test) [ Linux Release ] test/b.html [ Failure Pass ]
Bug(test) [ Release ] test/c.html [ Failure ]
# This line is not flaky or failing so we shouldn't even check the
# results.
Bug(test) [ Linux ] test/d.html [ Pass ]"""
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
'WebKit Linux Trusty (dbg)': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Debug']
},
'WebKit Win7': {
'port_name': 'win-win7',
'specifiers': ['Win7', 'Release']
},
'WebKit Win7 (dbg)': {
'port_name': 'win-win7',
'specifiers': ['Win7', 'Debug']
},
})
# Two warnings and two errors should be emitted:
# (1) A warning since the results don't contain anything for the Linux
# (dbg) builder
# (2) A warning since the results don't contain anything for the Win
# release builder
# (3) The first line needs and is missing results for Linux (dbg).
# (4) The third line needs and is missing results for Win Release.
self._port.all_build_types = ('release', 'debug')
self._port.all_systems = (('win7', 'x86'),
('trusty', 'x86_64'))
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'IMAGE', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
},
'WebKit Win7 (dbg)': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
},
}
updated_expectations = (
self._expectations_remover.get_updated_test_expectations())
self.assertLog([
'WARNING: Downloaded results are missing results for builder "WebKit Linux Trusty (dbg)"\n',
'WARNING: Downloaded results are missing results for builder "WebKit Win7"\n',
'ERROR: Failed to find results for builder "WebKit Linux Trusty (dbg)"\n',
'ERROR: Failed to find results for builder "WebKit Win7"\n',
])
# Also make sure we didn't remove any lines if some builders were
# missing.
self._assert_expectations_match(
updated_expectations, test_expectations_before)
def test_harness_updates_file(self):
"""Tests that the call harness updates the TestExpectations file."""
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
'WebKit Linux Trusty (dbg)': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Debug']
},
})
# Setup the mock host and port.
host = self._host
host.port_factory = FakePortFactory(
host,
all_build_types=('release', 'debug'),
all_systems=(('trusty', 'x86_64'),))
# Write out a fake TestExpectations file.
test_expectation_path = (
host.port_factory.get().path_to_generic_test_expectations_file())
test_expectations = """
# Remove since passing on both bots.
Bug(test) [ Linux ] test/a.html [ Failure Pass ]
# Keep since there's a failure on release bot.
Bug(test) [ Linux Release ] test/b.html [ Failure Pass ]
# Remove since it's passing on both builders.
Bug(test) test/c.html [ Failure ]
# Keep since there's a failure on debug bot.
Bug(test) [ Linux ] test/d.html [ Failure ]"""
files = {
test_expectation_path: test_expectations
}
host.filesystem = MockFileSystem(files)
self._write_tests_into_filesystem(host.filesystem)
# Write out the fake builder bot results.
expectation_factory = FakeBotTestExpectationsFactory()
expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'IMAGE', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
},
'WebKit Linux Trusty (dbg)': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
'test/d.html': ['IMAGE', 'PASS', 'PASS'],
},
}
main(host, expectation_factory, [])
self.assertEqual(host.filesystem.files[test_expectation_path], (
""" # Keep since there's a failure on release bot.
Bug(test) [ Linux Release ] test/b.html [ Failure Pass ]
# Keep since there's a failure on debug bot.
Bug(test) [ Linux ] test/d.html [ Failure ]"""))
def test_harness_no_expectations(self):
"""Tests behavior when TestExpectations file doesn't exist.
Tests that a warning is outputted if the TestExpectations file
doesn't exist.
"""
# Set up 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))
return_code = main(host, expectation_factory, [])
self.assertEqual(return_code, 1)
self.assertLog([
"WARNING: Didn't find generic expectations file at: %s\n" % test_expectation_path
])
self.assertFalse(host.filesystem.isfile(test_expectation_path))
def test_harness_remove_all(self):
"""Tests that removing all expectations doesn't delete the file.
Make sure we're prepared for the day when we exterminated flakes.
"""
self._define_builders({
'WebKit Linux Trusty': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
'WebKit Linux Trusty (dbg)': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Debug']
},
})
# Set up the mock host and port.
host = self._host
host.port_factory = FakePortFactory(
host,
all_build_types=('release', 'debug'),
all_systems=(('trusty', 'x86_64'),))
# Write out a fake TestExpectations file.
test_expectation_path = (
host.port_factory.get().path_to_generic_test_expectations_file())
test_expectations = """
# Remove since passing on both bots.
Bug(test) [ Linux ] test/a.html [ Failure Pass ]"""
files = {
test_expectation_path: test_expectations
}
host.filesystem = MockFileSystem(files)
self._write_tests_into_filesystem(host.filesystem)
# Write out the fake builder bot results.
expectation_factory = FakeBotTestExpectationsFactory()
expectation_factory.all_results_by_builder = {
'WebKit Linux Trusty': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
},
'WebKit Linux Trusty (dbg)': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
},
}
main(host, expectation_factory, [])
self.assertTrue(host.filesystem.isfile(test_expectation_path))
self.assertEqual(host.filesystem.files[test_expectation_path], '')
def test_show_results(self):
"""Tests that passing --show-results shows the removed results.
--show-results opens the removed tests in the layout dashboard using
the default browser. This tests mocks the webbrowser.open function and
checks that it was called with the correct URL.
"""
test_expectations_before = (
"""# Remove this since it's passing all runs.
Bug(test) test/a.html [ Failure Pass ]
# Remove this since, although there's a failure, it's not a timeout.
Bug(test) test/b.html [ Pass Timeout ]
# Keep since we have both crashes and passes.
Bug(test) test/c.html [ Crash Pass ]
# Remove since it's passing all runs.
Bug(test) test/d.html [ Failure ]""")
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'IMAGE', 'PASS'],
'test/c.html': ['PASS', 'CRASH', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
}
}
self._expectations_remover.show_removed_results()
self.assertEqual(
FlakyTests.FLAKINESS_DASHBOARD_URL
% 'test/a.html,test/b.html,test/d.html',
self._mock_web_browser.opened_url)
def test_flake_mode_suggested_commit_description(self):
"""Tests display of the suggested commit message.
"""
test_expectations_before = (
"""# Remove this since it's passing all runs.
crbug.com/1111 test/a.html [ Failure Pass ]
# Remove this since, although there's a failure, it's not a timeout.
crbug.com/2222 test/b.html [ Pass Timeout ]
# Keep since it's not a flake
crbug.com/3333 test/c.html [ Failure ]""")
self._expectations_remover = (
self._create_expectations_remover(self.FLAKE_TYPE))
self._define_builders({
'WebKit Linux': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'IMAGE', 'PASS'],
'test/c.html': ['PASS', 'PASS', 'PASS'],
}
}
self._expectations_remover.print_suggested_commit_description()
self.assertLog([
'INFO: Deleting line "crbug.com/1111 test/a.html [ Failure Pass ]"\n',
'INFO: Deleting line "crbug.com/2222 test/b.html [ Pass Timeout ]"\n',
'INFO: Suggested commit description:\n'
'Remove flake TestExpectations which are not failing in the specified way.\n\n'
'This change was made by the update_expectations.py script.\n\n'
'Recent test results history:\n'
'https://test-results.appspot.com/dashboards/flakiness_dashboard.html'
'#testType=webkit_layout_tests&tests=test/a.html,test/b.html\n\n'
'Bug: 1111, 2222\n'
])
def test_fail_mode_suggested_commit_description(self):
"""Tests display of the suggested commit message.
"""
test_expectations_before = (
"""# Keep since it's not a fail.
crbug.com/1111 test/a.html [ Failure Pass ]
# Remove since it's passing all runs.
crbug.com/2222 test/b.html [ Failure ]""")
self._expectations_remover = (
self._create_expectations_remover(self.FAIL_TYPE))
self._define_builders({
'WebKit Linux': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'PASS', 'PASS'],
}
}
self._expectations_remover.print_suggested_commit_description()
self.assertLog([
'INFO: Deleting line "crbug.com/2222 test/b.html [ Failure ]"\n',
'INFO: Suggested commit description:\n'
'Remove fail TestExpectations which are not failing in the specified way.\n\n'
'This change was made by the update_expectations.py script.\n\n'
'Recent test results history:\n'
'https://test-results.appspot.com/dashboards/flakiness_dashboard.html'
'#testType=webkit_layout_tests&tests=test/b.html\n\n'
'Bug: 2222\n'
])
def test_suggested_commit_description(self):
"""Tests display of the suggested commit message.
"""
test_expectations_before = (
"""# Remove this since it's passing all runs.
crbug.com/1111 test/a.html [ Failure Pass ]
# Remove this since, although there's a failure, it's not a timeout.
crbug.com/1111 test/b.html [ Pass Timeout ]
# Keep since we have both crashes and passes.
crbug.com/2222 test/c.html [ Crash Pass ]
# Remove since it's passing all runs.
crbug.com/3333 test/d.html [ Failure ]""")
self._expectations_remover = self._create_expectations_remover()
self._define_builders({
'WebKit Linux': {
'port_name': 'linux-trusty',
'specifiers': ['Trusty', 'Release']
},
})
self._port.all_build_types = ('release',)
self._port.all_systems = (('trusty', 'x86_64'),)
self._parse_expectations(test_expectations_before)
self._expectation_factory.all_results_by_builder = {
'WebKit Linux': {
'test/a.html': ['PASS', 'PASS', 'PASS'],
'test/b.html': ['PASS', 'IMAGE', 'PASS'],
'test/c.html': ['PASS', 'CRASH', 'PASS'],
'test/d.html': ['PASS', 'PASS', 'PASS'],
}
}
self._expectations_remover.print_suggested_commit_description()
self.assertLog([
'INFO: Deleting line "crbug.com/1111 test/a.html [ Failure Pass ]"\n',
'INFO: Deleting line "crbug.com/1111 test/b.html [ Pass Timeout ]"\n',
'INFO: Deleting line "crbug.com/3333 test/d.html [ Failure ]"\n',
'INFO: Suggested commit description:\n'
'Remove TestExpectations which are not failing in the specified way.\n\n'
'This change was made by the update_expectations.py script.\n\n'
'Recent test results history:\n'
'https://test-results.appspot.com/dashboards/flakiness_dashboard.html'
'#testType=webkit_layout_tests&tests=test/a.html,test/b.html,test/d.html\n\n'
'Bug: 1111, 3333\n'
])