#!/usr/bin/env vpython3 # Copyright 2021 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # pylint: disable=protected-access import datetime import os import sys import tempfile import unittest import unittest.mock as mock from pyfakefs import fake_filesystem_unittest # pylint:disable=import-error from flake_suppressor_common import common_typing as ct from flake_suppressor_common import expectations from flake_suppressor_common import unittest_utils as uu # Note for all tests in this class: We can safely check the contents of the file # at the end despite potentially having multiple added lines because Python 3.7+ # guarantees that dictionaries remember insertion order, so there is no risk of # the order of modification changing. @unittest.skipIf(sys.version_info[0] != 3, 'Python 3-only') class IterateThroughResultsForUserUnittest(fake_filesystem_unittest.TestCase): def setUp(self) -> None: self._new_stdout = open(os.devnull, 'w') self.setUpPyfakefs() self._expectations = uu.UnitTestExpectationProcessor() # Redirect stdout since the tested function prints a lot. self._old_stdout = sys.stdout sys.stdout = self._new_stdout self._input_patcher = mock.patch.object(expectations.ExpectationProcessor, 'PromptUserForExpectationAction') self._input_mock = self._input_patcher.start() self.addCleanup(self._input_patcher.stop) self.result_map = { 'pixel_integration_test': { 'foo_test': { tuple(['win']): ['a'], tuple(['mac']): ['b'], }, 'bar_test': { tuple(['win']): ['c'], }, }, } self.expectation_file = os.path.join(uu.ABSOLUTE_EXPECTATION_FILE_DIRECTORY, 'pixel_expectations.txt') uu.CreateFile(self, self.expectation_file) expectation_file_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ android ] some_test [ Failure ] """ with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) self._expectation_file_patcher = mock.patch.object( uu.UnitTestExpectationProcessor, 'GetExpectationFileForSuite') self._expectation_file_mock = self._expectation_file_patcher.start() self._expectation_file_mock.return_value = self.expectation_file self.addCleanup(self._expectation_file_patcher.stop) def tearDown(self) -> None: sys.stdout = self._old_stdout self._new_stdout.close() def testIterateThroughResultsForUserIgnoreNoGroupByTags(self) -> None: """Tests that everything appears to function with ignore and no group.""" self._input_mock.return_value = (None, None) self._expectations.IterateThroughResultsForUser(self.result_map, False, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ android ] some_test [ Failure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testIterateThroughResultsForUserIgnoreGroupByTags(self) -> None: """Tests that everything appears to function with ignore and grouping.""" self._input_mock.return_value = (None, None) self._expectations.IterateThroughResultsForUser(self.result_map, True, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ android ] some_test [ Failure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testIterateThroughResultsForUserRetryNoGroupByTags(self) -> None: """Tests that everything appears to function with retry and no group.""" self._input_mock.return_value = ('RetryOnFailure', '') self._expectations.IterateThroughResultsForUser(self.result_map, False, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ android ] some_test [ Failure ] [ win ] foo_test [ RetryOnFailure ] [ mac ] foo_test [ RetryOnFailure ] [ win ] bar_test [ RetryOnFailure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testIterateThroughResultsForUserRetryGroupByTags(self) -> None: """Tests that everything appears to function with retry and grouping.""" self._input_mock.return_value = ('RetryOnFailure', 'crbug.com/1') self._expectations.IterateThroughResultsForUser(self.result_map, True, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] crbug.com/1 [ win ] foo_test [ RetryOnFailure ] crbug.com/1 [ win ] bar_test [ RetryOnFailure ] [ mac ] some_test [ Failure ] crbug.com/1 [ mac ] foo_test [ RetryOnFailure ] [ android ] some_test [ Failure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testIterateThroughResultsForUserFailNoGroupByTags(self) -> None: """Tests that everything appears to function with failure and no group.""" self._input_mock.return_value = ('Failure', 'crbug.com/1') self._expectations.IterateThroughResultsForUser(self.result_map, False, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ android ] some_test [ Failure ] crbug.com/1 [ win ] foo_test [ Failure ] crbug.com/1 [ mac ] foo_test [ Failure ] crbug.com/1 [ win ] bar_test [ Failure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testIterateThroughResultsForUserFailGroupByTags(self) -> None: """Tests that everything appears to function with failure and grouping.""" self._input_mock.return_value = ('Failure', '') self._expectations.IterateThroughResultsForUser(self.result_map, True, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ win ] foo_test [ Failure ] [ win ] bar_test [ Failure ] [ mac ] some_test [ Failure ] [ mac ] foo_test [ Failure ] [ android ] some_test [ Failure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testIterateThroughResultsForUserNoIncludeAllTags(self) -> None: """Tests that everything appears to function without including all tags""" self.result_map = { 'pixel_integration_test': { 'foo_test': { tuple(['win', 'win10']): ['a'], tuple(['mac']): ['b'], }, 'bar_test': { tuple(['win']): ['c'], }, }, } self._input_mock.return_value = ('RetryOnFailure', '') self._expectations.IterateThroughResultsForUser(self.result_map, False, False) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ android ] some_test [ Failure ] [ win10 ] foo_test [ RetryOnFailure ] [ mac ] foo_test [ RetryOnFailure ] [ win ] bar_test [ RetryOnFailure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) @unittest.skipIf(sys.version_info[0] != 3, 'Python 3-only') class IterateThroughResultsWithThresholdsUnittest( fake_filesystem_unittest.TestCase): def setUp(self) -> None: self.setUpPyfakefs() self._expectations = uu.UnitTestExpectationProcessor() self.result_map = { 'pixel_integration_test': { 'foo_test': { tuple(['win']): ['a'], tuple(['mac']): ['b'], }, 'bar_test': { tuple(['win']): ['c'], }, }, } self.expectation_file = os.path.join(uu.ABSOLUTE_EXPECTATION_FILE_DIRECTORY, 'pixel_expectations.txt') uu.CreateFile(self, self.expectation_file) expectation_file_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ android ] some_test [ Failure ] """ with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) self._expectation_file_patcher = mock.patch.object( uu.UnitTestExpectationProcessor, 'GetExpectationFileForSuite') self._expectation_file_mock = self._expectation_file_patcher.start() self._expectation_file_mock.return_value = self.expectation_file self.addCleanup(self._expectation_file_patcher.stop) def testGroupByTags(self) -> None: """Tests that threshold-based expectations work when grouping by tags.""" result_counts = { tuple(['win']): { # We expect this to be ignored since it has a 1% flake rate. 'foo_test': 100, # We expect this to be RetryOnFailure since it has a 25% flake rate. 'bar_test': 4, }, tuple(['mac']): { # We expect this to be Failure since it has a 50% flake rate. 'foo_test': 2 } } self._expectations.IterateThroughResultsWithThresholds( self.result_map, True, result_counts, 0.02, 0.5, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ win ] bar_test [ RetryOnFailure ] [ mac ] some_test [ Failure ] [ mac ] foo_test [ Failure ] [ android ] some_test [ Failure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testNoGroupByTags(self) -> None: """Tests that threshold-based expectations work when not grouping by tags""" result_counts = { tuple(['win']): { # We expect this to be ignored since it has a 1% flake rate. 'foo_test': 100, # We expect this to be RetryOnFailure since it has a 25% flake rate. 'bar_test': 4, }, tuple(['mac']): { # We expect this to be Failure since it has a 50% flake rate. 'foo_test': 2 } } self._expectations.IterateThroughResultsWithThresholds( self.result_map, False, result_counts, 0.02, 0.5, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ android ] some_test [ Failure ] [ mac ] foo_test [ Failure ] [ win ] bar_test [ RetryOnFailure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testNoIncludeAllTags(self) -> None: """Tests that threshold-based expectations work when filtering tags.""" self.result_map = { 'pixel_integration_test': { 'foo_test': { tuple(['win', 'win10']): ['a'], tuple(['mac']): ['b'], }, 'bar_test': { tuple(['win', 'win10']): ['c'], }, }, } result_counts = { tuple(['win', 'win10']): { # We expect this to be ignored since it has a 1% flake rate. 'foo_test': 100, # We expect this to be RetryOnFailure since it has a 25% flake rate. 'bar_test': 4, }, tuple(['mac']): { # We expect this to be Failure since it has a 50% flake rate. 'foo_test': 2 } } self._expectations.IterateThroughResultsWithThresholds( self.result_map, False, result_counts, 0.02, 0.5, False) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ android ] some_test [ Failure ] [ mac ] foo_test [ Failure ] [ win10 ] bar_test [ RetryOnFailure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) @unittest.skipIf(sys.version_info[0] != 3, 'Python 3-only') class CreateExpectationsForAllResultsUnittest(fake_filesystem_unittest.TestCase ): def setUp(self) -> None: self.setUpPyfakefs() self._expectations = uu.UnitTestExpectationProcessor() self.result_map = { 'pixel_integration_test': { 'foo_test': { tuple(['win']): [ ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date.today() - datetime.timedelta(days=2), False, ['Pass']), ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date.today() - datetime.timedelta(days=3), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date.today(), False, ['Pass']), ], tuple(['mac']): [ ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date.today() - datetime.timedelta(days=1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date.today(), False, ['Pass']), ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date.today() - datetime.timedelta(days=3), False, ['Pass']), ], }, 'bar_test': { tuple(['win']): [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/4444', datetime.date.today(), False, ['Pass']), ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/5555', datetime.date.today() - datetime.timedelta(days=1), False, ['Pass']), ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/6666', datetime.date.today() - datetime.timedelta(days=2), False, ['Pass']), ], }, 'baz_test': { # This test config causes build fail on less than 2 consecutive # days, and thus should not exist in the output. tuple(['win']): [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/7777', datetime.date.today(), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/8888', datetime.date.today(), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/9999', datetime.date.today(), False, ['Pass']), ], tuple(['mac']): [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/7777', datetime.date.today(), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/8888', datetime.date.today(), False, ['Pass']), ], }, 'wpt_test': { # Test for same test in all builders over threshold. tuple(['win']): [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1234', datetime.date.today(), False, ['Pass']), ], tuple(['mac']): [ ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2345', datetime.date.today() - datetime.timedelta(days=1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3456', datetime.date.today(), False, ['Pass']), ], }, }, } self.build_fail_total_number_threshold = 3 self.build_fail_consecutive_day_threshold = 2 self.build_fail_recent_day_threshold = 1 self.expectation_file = os.path.join(uu.ABSOLUTE_EXPECTATION_FILE_DIRECTORY, 'pixel_expectations.txt') uu.CreateFile(self, self.expectation_file) expectation_file_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure Pass ] [ mac ] some_test [ Failure Pass ] [ android ] some_test [ Failure Pass ] """ with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) self._expectation_file_patcher = mock.patch.object( uu.UnitTestExpectationProcessor, 'GetExpectationFileForSuite') self._expectation_file_mock = self._expectation_file_patcher.start() self._expectation_file_mock.return_value = self.expectation_file self.addCleanup(self._expectation_file_patcher.stop) def testGroupByTags(self) -> None: """Tests that threshold-based expectations work when grouping by tags.""" self._expectations.CreateExpectationsForAllResults( self.result_map, True, True, self.build_fail_total_number_threshold, self.build_fail_consecutive_day_threshold, self.build_fail_recent_day_threshold) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure Pass ] [ win ] foo_test [ Failure Pass ] [ win ] bar_test [ Failure Pass ] [ win ] wpt_test [ Failure Pass ] [ mac ] some_test [ Failure Pass ] [ mac ] foo_test [ Failure Pass ] [ mac ] wpt_test [ Failure Pass ] [ android ] some_test [ Failure Pass ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testNoGroupByTags(self) -> None: """Tests that threshold-based expectations work when not grouping by tags""" self._expectations.CreateExpectationsForAllResults( self.result_map, False, True, self.build_fail_total_number_threshold, self.build_fail_consecutive_day_threshold, self.build_fail_recent_day_threshold) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure Pass ] [ mac ] some_test [ Failure Pass ] [ android ] some_test [ Failure Pass ] [ win ] foo_test [ Failure Pass ] [ mac ] foo_test [ Failure Pass ] [ win ] bar_test [ Failure Pass ] [ win ] wpt_test [ Failure Pass ] [ mac ] wpt_test [ Failure Pass ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testNoIncludeAllTags(self) -> None: """Tests that threshold-based expectations work when filtering tags.""" self.result_map = { 'pixel_integration_test': { 'foo_test': { tuple(['win', 'win10']): [ ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date.today() - datetime.timedelta(days=2), False, ['Pass']), ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date.today() - datetime.timedelta(days=3), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date.today(), False, ['Pass']), ], tuple(['mac']): [ ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date.today() - datetime.timedelta(days=1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date.today(), False, ['Pass']), ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date.today() - datetime.timedelta(days=3), False, ['Pass']), ], }, 'bar_test': { tuple(['win', 'win10']): [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/4444', datetime.date.today(), False, ['Pass']), ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/5555', datetime.date.today() - datetime.timedelta(days=1), False, ['Pass']), ct.ResultTupleType( ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/6666', datetime.date.today() - datetime.timedelta(days=2), False, ['Pass']), ], }, 'baz_test': { # This test config causes build fail on less than 2 consecutive # days, and thus should not exist in the output. tuple(['win']): [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/7777', datetime.date.today(), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/8888', datetime.date.today(), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/9999', datetime.date.today(), False, ['Pass']), ], tuple(['mac']): [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/7777', datetime.date.today(), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/8888', datetime.date.today(), False, ['Pass']), ], }, }, } self._expectations.CreateExpectationsForAllResults( self.result_map, False, False, self.build_fail_total_number_threshold, self.build_fail_consecutive_day_threshold, self.build_fail_recent_day_threshold) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure Pass ] [ mac ] some_test [ Failure Pass ] [ android ] some_test [ Failure Pass ] [ win10 ] foo_test [ Failure Pass ] [ mac ] foo_test [ Failure Pass ] [ win10 ] bar_test [ Failure Pass ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) @unittest.skipIf(sys.version_info[0] != 3, 'Python 3-only') class FindFailuresInSameConditionUnittest(unittest.TestCase): def setUp(self) -> None: self._expectations = uu.UnitTestExpectationProcessor() self.result_map = { 'pixel_integration_test': { 'foo_test': { tuple(['win']): ['a'], tuple(['mac']): ['a', 'b'], }, 'bar_test': { tuple(['win']): ['a', 'b', 'c'], tuple(['mac']): ['a', 'b', 'c', 'd'], }, }, 'webgl_conformance_integration_test': { 'foo_test': { tuple(['win']): ['a', 'b', 'c', 'd', 'e'], tuple(['mac']): ['a', 'b', 'c', 'd', 'e', 'f'], }, 'bar_test': { tuple(['win']): ['a', 'b', 'c', 'd', 'e', 'f', 'g'], tuple(['mac']): ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'], }, }, } def testFindFailuresInSameTest(self) -> None: other_failures = self._expectations.FindFailuresInSameTest( self.result_map, 'pixel_integration_test', 'foo_test', tuple(['win'])) self.assertEqual(other_failures, [(tuple(['mac']), 2)]) def testFindFailuresInSameConfig(self) -> None: typ_tag_ordered_result_map = self._expectations._ReorderMapByTypTags( self.result_map) other_failures = self._expectations.FindFailuresInSameConfig( typ_tag_ordered_result_map, 'pixel_integration_test', 'foo_test', tuple(['win'])) expected_other_failures = [ ('pixel_integration_test.bar_test', 3), ('webgl_conformance_integration_test.foo_test', 5), ('webgl_conformance_integration_test.bar_test', 7), ] self.assertEqual(len(other_failures), len(expected_other_failures)) self.assertEqual(set(other_failures), set(expected_other_failures)) @unittest.skipIf(sys.version_info[0] != 3, 'Python 3-only') class ModifyFileForResultUnittest(fake_filesystem_unittest.TestCase): def setUp(self) -> None: self.setUpPyfakefs() self._expectations = uu.UnitTestExpectationProcessor() self.expectation_file = os.path.join(uu.ABSOLUTE_EXPECTATION_FILE_DIRECTORY, 'expectation.txt') uu.CreateFile(self, self.expectation_file) self._expectation_file_patcher = mock.patch.object( uu.UnitTestExpectationProcessor, 'GetExpectationFileForSuite') self._expectation_file_mock = self._expectation_file_patcher.start() self.addCleanup(self._expectation_file_patcher.stop) self._expectation_file_mock.return_value = self.expectation_file def testNoGroupByTags(self) -> None: """Tests that not grouping by tags appends to the end.""" expectation_file_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] """ with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) self._expectations.ModifyFileForResult('some_file', 'some_test', ('win', 'win10'), '', 'Failure', False, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ win win10 ] some_test [ Failure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testGroupByTagsNoMatch(self) -> None: """Tests that grouping by tags but finding no match appends to the end.""" expectation_file_contents = uu.TAG_HEADER + """\ [ mac ] some_test [ Failure ] """ with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) self._expectations.ModifyFileForResult('some_file', 'some_test', ('win', 'win10'), '', 'Failure', True, True) expected_contents = uu.TAG_HEADER + """\ [ mac ] some_test [ Failure ] [ win win10 ] some_test [ Failure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) def testGroupByTagsMatch(self) -> None: """Tests that grouping by tags and finding a match adds mid-file.""" expectation_file_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] """ with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) self._expectations.ModifyFileForResult('some_file', 'foo_test', ('win', 'win10'), '', 'Failure', True, True) expected_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ win ] foo_test [ Failure ] [ mac ] some_test [ Failure ] """ with open(self.expectation_file) as infile: self.assertEqual(infile.read(), expected_contents) @unittest.skipIf(sys.version_info[0] != 3, 'Python 3-only') class FilterToMostSpecificTagTypeUnittest(fake_filesystem_unittest.TestCase): def setUp(self) -> None: self._expectations = uu.UnitTestExpectationProcessor() self.setUpPyfakefs() with tempfile.NamedTemporaryFile(delete=False) as tf: self.expectation_file = tf.name def testBasic(self): """Tests that only the most specific tags are kept.""" expectation_file_contents = """\ # tags: [ tag1_least_specific tag1_middle_specific tag1_most_specific ] # tags: [ tag2_least_specific tag2_middle_specific tag2_most_specific ]""" with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) tags = ('tag1_least_specific', 'tag1_most_specific', 'tag2_middle_specific', 'tag2_least_specific') filtered_tags = self._expectations.FilterToMostSpecificTypTags( tags, self.expectation_file) self.assertEqual(filtered_tags, ('tag1_most_specific', 'tag2_middle_specific')) def testSingleTags(self) -> None: """Tests that functionality works as expected with single tags.""" expectation_file_contents = """\ # tags: [ tag1_most_specific ] # tags: [ tag2_most_specific ]""" with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) tags = ('tag1_most_specific', 'tag2_most_specific') filtered_tags = self._expectations.FilterToMostSpecificTypTags( tags, self.expectation_file) self.assertEqual(filtered_tags, tags) def testUnusedTags(self) -> None: """Tests that functionality works as expected with extra/unused tags.""" expectation_file_contents = """\ # tags: [ tag1_least_specific tag1_middle_specific tag1_most_specific ] # tags: [ tag2_least_specific tag2_middle_specific tag2_most_specific ] # tags: [ some_unused_tag ]""" with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) tags = ('tag1_least_specific', 'tag1_most_specific', 'tag2_middle_specific', 'tag2_least_specific') filtered_tags = self._expectations.FilterToMostSpecificTypTags( tags, self.expectation_file) self.assertEqual(filtered_tags, ('tag1_most_specific', 'tag2_middle_specific')) def testMultiline(self) -> None: """Tests that functionality works when tags cover multiple lines.""" expectation_file_contents = """\ # tags: [ tag1_least_specific # tag1_middle_specific # tag1_most_specific ] # tags: [ tag2_least_specific # tag2_middle_specific tag2_most_specific ]""" with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) tags = ('tag1_least_specific', 'tag1_middle_specific', 'tag1_most_specific', 'tag2_middle_specific', 'tag2_least_specific') filtered_tags = self._expectations.FilterToMostSpecificTypTags( tags, self.expectation_file) self.assertEqual(filtered_tags, ('tag1_most_specific', 'tag2_middle_specific')) def testMissingTags(self) -> None: """Tests that a file not having all tags is an error.""" expectation_file_contents = """\ # tags: [ tag1_least_specific tag1_middle_specific ] # tags: [ tag2_least_specific tag2_middle_specific tag2_most_specific ]""" with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) tags = ('tag1_least_specific', 'tag1_most_specific', 'tag2_middle_specific', 'tag2_least_specific') with self.assertRaises(RuntimeError): self._expectations.FilterToMostSpecificTypTags(tags, self.expectation_file) @unittest.skipIf(sys.version_info[0] != 3, 'Python 3-only') class FindBestInsertionLineForExpectationUnittest( fake_filesystem_unittest.TestCase): def setUp(self) -> None: self.setUpPyfakefs() self._expectations = uu.UnitTestExpectationProcessor() self.expectation_file = os.path.join(uu.ABSOLUTE_EXPECTATION_FILE_DIRECTORY, 'expectation.txt') uu.CreateFile(self, self.expectation_file) expectation_file_contents = uu.TAG_HEADER + """\ [ win ] some_test [ Failure ] [ mac ] some_test [ Failure ] [ win release ] bar_test [ Failure ] [ win ] foo_test [ Failure ] [ chromeos ] some_test [ Failure ] """ with open(self.expectation_file, 'w') as outfile: outfile.write(expectation_file_contents) def testNoMatchingTags(self) -> None: """Tests behavior when there are no expectations with matching tags.""" insertion_line, tags = ( self._expectations.FindBestInsertionLineForExpectation( tuple(['android']), self.expectation_file)) self.assertEqual(insertion_line, -1) self.assertEqual(tags, set()) def testMatchingTagsLastEntryChosen(self) -> None: """Tests that the last matching line is chosen.""" insertion_line, tags = ( self._expectations.FindBestInsertionLineForExpectation( tuple(['win']), self.expectation_file)) # We expect "[ win ] foo_test [ Failure ]" to be chosen expected_line = len(uu.TAG_HEADER.splitlines()) + 6 self.assertEqual(insertion_line, expected_line) self.assertEqual(tags, set(['win'])) def testMatchingTagsClosestMatchChosen(self) -> None: """Tests that the closest tag match is chosen.""" insertion_line, tags = ( self._expectations.FindBestInsertionLineForExpectation( ('win', 'release'), self.expectation_file)) # We expect "[ win release ] bar_test [ Failure ]" to be chosen expected_line = len(uu.TAG_HEADER.splitlines()) + 5 self.assertEqual(insertion_line, expected_line) self.assertEqual(tags, set(['win', 'release'])) class AssertCheckoutIsUpToDateUnittest(unittest.TestCase): def setUp(self) -> None: self._expectations = uu.UnitTestExpectationProcessor() self._origin_patcher = mock.patch( 'flake_suppressor_common.expectations.ExpectationProcessor.' 'GetOriginExpectationFileContents') self._origin_mock = self._origin_patcher.start() self.addCleanup(self._origin_patcher.stop) self._local_patcher = mock.patch( 'flake_suppressor_common.expectations.' + 'ExpectationProcessor.GetLocalCheckoutExpectationFileContents') self._local_mock = self._local_patcher.start() self.addCleanup(self._local_patcher.stop) def testContentsMatch(self) -> None: """Tests the happy path where the contents match.""" self._origin_mock.return_value = { 'foo.txt': 'foo_content', 'bar.txt': 'bar_content', } self._local_mock.return_value = { 'bar.txt': 'bar_content', 'foo.txt': 'foo_content', } self._expectations.AssertCheckoutIsUpToDate() def testContentsDoNotMatch(self) -> None: """Tests that mismatched contents results in a failure.""" self._origin_mock.return_value = { 'foo.txt': 'foo_content', 'bar.txt': 'bar_content', } # Differing keys. self._local_mock.return_value = { 'bar.txt': 'bar_content', 'foo2.txt': 'foo_content', } with self.assertRaises(RuntimeError): self._expectations.AssertCheckoutIsUpToDate() # Differing values. self._local_mock.return_value = { 'bar.txt': 'bar_content', 'foo.txt': 'foo_content2', } with self.assertRaises(RuntimeError): self._expectations.AssertCheckoutIsUpToDate() class OverFailedBuildThresholdUnittest(unittest.TestCase): def setUp(self) -> None: self.build_fail_total_number_threshold = 3 def testOverThreshold(self) -> None: """Tests functionality when |result_tuple_list| passes |build_fail_total_number_threshold|. True is expected output on these inputs. """ result_tuple_list = [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date(2021, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date(2023, 1, 1), False, ['Pass']), ] self.assertTrue( expectations.OverFailedBuildThreshold( result_tuple_list, self.build_fail_total_number_threshold)) def testUnderThreshold(self) -> None: """Tests functionality when |result_tuple_list| cannot pass |build_fail_total_number_threshold|. False is expected output on these inputs. """ result_tuple_list = [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date(2022, 1, 2), False, ['Pass']), ] self.assertFalse( expectations.OverFailedBuildThreshold( result_tuple_list, self.build_fail_total_number_threshold)) result_tuple_list = [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date(2022, 1, 2), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date(2022, 1, 3), False, ['Pass']), ] self.assertFalse( expectations.OverFailedBuildThreshold( result_tuple_list, self.build_fail_total_number_threshold)) class OverFailedBuildByConsecutiveDayThresholdUnittest(unittest.TestCase): def setUp(self) -> None: self.build_fail_consecutive_day_threshold = 3 def testOverThreshold(self) -> None: """Tests functionality when |result_tuple_list| passes |build_fail_consecutive_day_threshold|. True is expected output on these inputs. """ result_tuple_list = [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date(2022, 1, 2), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date(2022, 1, 3), False, ['Pass']), ] self.assertTrue( expectations.OverFailedBuildByConsecutiveDayThreshold( result_tuple_list, self.build_fail_consecutive_day_threshold)) def testUnderThreshold(self) -> None: """Tests functionality when |result_tuple_list| cannot pass |build_fail_consecutive_day_threshold|. False is expected output on these inputs. """ result_tuple_list = [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date(2022, 1, 1), False, ['Pass']), ] self.assertFalse( expectations.OverFailedBuildByConsecutiveDayThreshold( result_tuple_list, self.build_fail_consecutive_day_threshold)) result_tuple_list = [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date(2022, 1, 2), False, ['Pass']), ] self.assertFalse( expectations.OverFailedBuildByConsecutiveDayThreshold( result_tuple_list, self.build_fail_consecutive_day_threshold)) result_tuple_list = [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date(2022, 1, 2), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date(2022, 1, 4), False, ['Pass']), ] self.assertFalse( expectations.OverFailedBuildByConsecutiveDayThreshold( result_tuple_list, self.build_fail_consecutive_day_threshold)) class FailedBuildWithinRecentDayThresholdUnittest(unittest.TestCase): def setUp(self) -> None: self.build_fail_recent_day_threshold = 3 def testWithinThreshold(self) -> None: """Tests functionality when |result_tuple_list| has build fail within |build_fail_recent_day_threshold|. True is expected output on these inputs. """ result_tuple_list = [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date.today(), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date.today(), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date.today(), False, ['Pass']), ] self.assertTrue( expectations.FailedBuildWithinRecentDayThreshold( result_tuple_list, self.build_fail_recent_day_threshold)) def testBeyondThreshold(self) -> None: """Tests functionality when |result_tuple_list| has no build fail within |build_fail_recent_day_threshold|. False is expected output on these inputs. """ result_tuple_list = [ ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/1111', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/2222', datetime.date(2022, 1, 1), False, ['Pass']), ct.ResultTupleType(ct.ResultStatus.FAIL, 'http://ci.chromium.org/b/3333', datetime.date(2022, 1, 1), False, ['Pass']), ] self.assertFalse( expectations.FailedBuildWithinRecentDayThreshold( result_tuple_list, self.build_fail_recent_day_threshold)) if __name__ == '__main__': unittest.main(verbosity=2)