| #!/usr/bin/env vpython3 |
| # Copyright 2020 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from __future__ import print_function |
| |
| import datetime |
| import os |
| import sys |
| import tempfile |
| import unittest |
| |
| if sys.version_info[0] == 2: |
| import mock |
| else: |
| import unittest.mock as mock |
| |
| from pyfakefs import fake_filesystem_unittest |
| |
| from unexpected_passes_common import data_types |
| from unexpected_passes_common import expectations |
| from unexpected_passes_common import unittest_utils as uu |
| |
| FAKE_EXPECTATION_FILE_CONTENTS = """\ |
| # tags: [ win linux ] |
| # results: [ Failure RetryOnFailure Skip Pass ] |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| crbug.com/5678 crbug.com/6789 [ win ] foo/another/test [ RetryOnFailure ] |
| |
| [ linux ] foo/test [ Failure ] |
| |
| crbug.com/2345 [ linux ] bar/* [ RetryOnFailure ] |
| crbug.com/3456 [ linux ] some/bad/test [ Skip ] |
| crbug.com/4567 [ linux ] some/good/test [ Pass ] |
| """ |
| |
| SECONDARY_FAKE_EXPECTATION_FILE_CONTENTS = """\ |
| # tags: [ mac ] |
| # results: [ Failure ] |
| |
| crbug.com/4567 [ mac ] foo/test [ Failure ] |
| """ |
| |
| FAKE_EXPECTATION_FILE_CONTENTS_WITH_TYPO = """\ |
| # tags: [ win linux ] |
| # results: [ Failure RetryOnFailure Skip ] |
| crbug.com/1234 [ wine ] foo/test [ Failure ] |
| |
| [ linux ] foo/test [ Failure ] |
| |
| crbug.com/2345 [ linux ] bar/* [ RetryOnFailure ] |
| crbug.com/3456 [ linux ] some/bad/test [ Skip ] |
| """ |
| |
| FAKE_EXPECTATION_FILE_CONTENTS_WITH_COMPLEX_TAGS = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| |
| |
| class CreateTestExpectationMapUnittest(unittest.TestCase): |
| def setUp(self) -> None: |
| self.instance = expectations.Expectations() |
| |
| self._expectation_content = {} |
| self._content_patcher = mock.patch.object( |
| self.instance, '_GetNonRecentExpectationContent') |
| self._content_mock = self._content_patcher.start() |
| self.addCleanup(self._content_patcher.stop) |
| |
| def SideEffect(filepath, _): |
| return self._expectation_content[filepath] |
| |
| self._content_mock.side_effect = SideEffect |
| |
| def testExclusiveOr(self) -> None: |
| """Tests that only one input can be specified.""" |
| with self.assertRaises(AssertionError): |
| self.instance.CreateTestExpectationMap(None, None, |
| datetime.timedelta(days=0)) |
| with self.assertRaises(AssertionError): |
| self.instance.CreateTestExpectationMap('foo', ['bar'], |
| datetime.timedelta(days=0)) |
| |
| def testExpectationFile(self) -> None: |
| """Tests reading expectations from an expectation file.""" |
| filename = '/tmp/foo' |
| self._expectation_content[filename] = FAKE_EXPECTATION_FILE_CONTENTS |
| expectation_map = self.instance.CreateTestExpectationMap( |
| filename, None, datetime.timedelta(days=0)) |
| # Skip expectations should be omitted, but everything else should be |
| # present. |
| # yapf: disable |
| expected_expectation_map = { |
| filename: { |
| data_types.Expectation( |
| 'foo/test', ['win'], ['Failure'], 'crbug.com/1234'): {}, |
| data_types.Expectation( |
| 'foo/another/test', ['win'], ['RetryOnFailure'], |
| 'crbug.com/5678 crbug.com/6789'): {}, |
| data_types.Expectation('foo/test', ['linux'], ['Failure']): {}, |
| data_types.Expectation( |
| 'bar/*', ['linux'], ['RetryOnFailure'], 'crbug.com/2345'): {}, |
| }, |
| } |
| # yapf: enable |
| self.assertEqual(expectation_map, expected_expectation_map) |
| self.assertIsInstance(expectation_map, data_types.TestExpectationMap) |
| |
| def testMultipleExpectationFiles(self) -> None: |
| """Tests reading expectations from multiple files.""" |
| filename1 = '/tmp/foo' |
| filename2 = '/tmp/bar' |
| expectation_files = [filename1, filename2] |
| self._expectation_content[filename1] = FAKE_EXPECTATION_FILE_CONTENTS |
| self._expectation_content[ |
| filename2] = SECONDARY_FAKE_EXPECTATION_FILE_CONTENTS |
| |
| expectation_map = self.instance.CreateTestExpectationMap( |
| expectation_files, None, datetime.timedelta(days=0)) |
| # yapf: disable |
| expected_expectation_map = { |
| expectation_files[0]: { |
| data_types.Expectation( |
| 'foo/test', ['win'], ['Failure'], 'crbug.com/1234'): {}, |
| data_types.Expectation( |
| 'foo/another/test', ['win'], ['RetryOnFailure'], |
| 'crbug.com/5678 crbug.com/6789'): {}, |
| data_types.Expectation('foo/test', ['linux'], ['Failure']): {}, |
| data_types.Expectation( |
| 'bar/*', ['linux'], ['RetryOnFailure'], 'crbug.com/2345'): {}, |
| }, |
| expectation_files[1]: { |
| data_types.Expectation( |
| 'foo/test', ['mac'], ['Failure'], 'crbug.com/4567'): {}, |
| } |
| } |
| # yapf: enable |
| self.assertEqual(expectation_map, expected_expectation_map) |
| self.assertIsInstance(expectation_map, data_types.TestExpectationMap) |
| |
| def testIndividualTests(self) -> None: |
| """Tests reading expectations from a list of tests.""" |
| expectation_map = self.instance.CreateTestExpectationMap( |
| None, ['foo/test', 'bar/*'], datetime.timedelta(days=0)) |
| expected_expectation_map = { |
| '': { |
| data_types.Expectation('foo/test', [], ['RetryOnFailure']): {}, |
| data_types.Expectation('bar/*', [], ['RetryOnFailure']): {}, |
| }, |
| } |
| self.assertEqual(expectation_map, expected_expectation_map) |
| self.assertIsInstance(expectation_map, data_types.TestExpectationMap) |
| |
| |
| class GetNonRecentExpectationContentUnittest(unittest.TestCase): |
| def setUp(self) -> None: |
| self.instance = uu.CreateGenericExpectations() |
| self._output_patcher = mock.patch( |
| 'unexpected_passes_common.expectations.subprocess.check_output') |
| self._output_mock = self._output_patcher.start() |
| self.addCleanup(self._output_patcher.stop) |
| |
| def testBasic(self) -> None: |
| """Tests that only expectations that are old enough are kept.""" |
| today_date = datetime.date.today() |
| yesterday_date = today_date - datetime.timedelta(days=1) |
| older_date = today_date - datetime.timedelta(days=2) |
| today_str = today_date.isoformat() |
| yesterday_str = yesterday_date.isoformat() |
| older_str = older_date.isoformat() |
| # pylint: disable=line-too-long |
| blame_output = """\ |
| 5f03bc04975c04 (Some R. Author {today_date} 00:00:00 +0000 1)# tags: [ tag1 ] |
| 98637cd80f8c15 (Some R. Author {yesterday_date} 00:00:00 +0000 2)# tags: [ tag2 ] |
| 3fcadac9d861d0 (Some R. Author {older_date} 00:00:00 +0000 3)# results: [ Failure ] |
| 5f03bc04975c04 (Some R. Author {today_date} 00:00:00 +0000 4) |
| 5f03bc04975c04 (Some R. Author {today_date} 00:00:00 +0000 5)crbug.com/1234 [ tag1 ] testname [ Failure ] |
| 98637cd80f8c15 (Some R. Author {yesterday_date} 00:00:00 +0000 6)[ tag2 ] testname [ Failure ] # Comment |
| 3fcadac9d861d0 (Some R. Author {older_date} 00:00:00 +0000 7)[ tag1 ] othertest [ Failure ] |
| 5f03bc04975c04 (Some R. Author {today_date} 00:00:00 +0000 8)crbug.com/2345 testname [ Failure ] |
| 3fcadac9d861d0 (Some R. Author {older_date} 00:00:00 +0000 9)crbug.com/3456 othertest [ Failure ]""" |
| # pylint: enable=line-too-long |
| blame_output = blame_output.format(today_date=today_str, |
| yesterday_date=yesterday_str, |
| older_date=older_str) |
| self._output_mock.return_value = blame_output.encode('utf-8') |
| |
| expected_content = """\ |
| # tags: [ tag1 ] |
| # tags: [ tag2 ] |
| # results: [ Failure ] |
| |
| [ tag1 ] othertest [ Failure ] |
| crbug.com/3456 othertest [ Failure ]""" |
| self.assertEqual( |
| self.instance._GetNonRecentExpectationContent( |
| '', datetime.timedelta(days=1)), expected_content) |
| |
| def testNegativeGracePeriod(self) -> None: |
| """Tests that setting a negative grace period disables filtering.""" |
| today_date = datetime.date.today() |
| yesterday_date = today_date - datetime.timedelta(days=1) |
| older_date = today_date - datetime.timedelta(days=2) |
| today_str = today_date.isoformat() |
| yesterday_str = yesterday_date.isoformat() |
| older_str = older_date.isoformat() |
| # pylint: disable=line-too-long |
| blame_output = """\ |
| 5f03bc04975c04 (Some R. Author {today_date} 00:00:00 +0000 1)# tags: [ tag1 ] |
| 98637cd80f8c15 (Some R. Author {yesterday_date} 00:00:00 +0000 2)# tags: [ tag2 ] |
| 3fcadac9d861d0 (Some R. Author {older_date} 00:00:00 +0000 3)# results: [ Failure ] |
| 5f03bc04975c04 (Some R. Author {today_date} 00:00:00 +0000 4) |
| 5f03bc04975c04 (Some R. Author {today_date} 00:00:00 +0000 5)crbug.com/1234 [ tag1 ] testname [ Failure ] |
| 98637cd80f8c15 (Some R. Author {yesterday_date} 00:00:00 +0000 6)[ tag2 ] testname [ Failure ] # Comment |
| 3fcadac9d861d0 (Some R. Author {older_date} 00:00:00 +0000 7)[ tag1 ] othertest [ Failure ]""" |
| # pylint: enable=line-too-long |
| blame_output = blame_output.format(today_date=today_str, |
| yesterday_date=yesterday_str, |
| older_date=older_str) |
| self._output_mock.return_value = blame_output.encode('utf-8') |
| |
| expected_content = """\ |
| # tags: [ tag1 ] |
| # tags: [ tag2 ] |
| # results: [ Failure ] |
| |
| crbug.com/1234 [ tag1 ] testname [ Failure ] |
| [ tag2 ] testname [ Failure ] # Comment |
| [ tag1 ] othertest [ Failure ]""" |
| self.assertEqual( |
| self.instance._GetNonRecentExpectationContent( |
| '', datetime.timedelta(days=-1)), expected_content) |
| |
| |
| class RemoveExpectationsFromFileUnittest(fake_filesystem_unittest.TestCase): |
| def setUp(self) -> None: |
| self.instance = uu.CreateGenericExpectations() |
| self.header = self.instance._GetExpectationFileTagHeader(None) |
| self.setUpPyfakefs() |
| with tempfile.NamedTemporaryFile(delete=False) as f: |
| self.filename = f.name |
| |
| def testExpectationRemoval(self) -> None: |
| """Tests that expectations are properly removed from a file.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| [ linux ] bar/test [ RetryOnFailure ] |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('bar/test', ['linux'], ['RetryOnFailure']) |
| ] |
| |
| expected_contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set(['crbug.com/1234'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testRemovalWithMultipleBugs(self) -> None: |
| """Tests removal of expectations with multiple associated bugs.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/1234 crbug.com/3456 crbug.com/4567 [ win ] foo/test [ Failure ] |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| [ linux ] bar/test [ RetryOnFailure ] |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/1234 crbug.com/3456 crbug.com/4567'), |
| ] |
| expected_contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| [ linux ] bar/test [ RetryOnFailure ] |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual( |
| removed_urls, |
| set(['crbug.com/1234', 'crbug.com/3456', 'crbug.com/4567'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testGeneralBlockComments(self) -> None: |
| """Tests that expectations in a disable block comment are not removed.""" |
| contents = self.header + """ |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:disable-general |
| crbug.com/2345 [ win ] foo/test [ Failure ] |
| crbug.com/3456 [ win ] foo/test [ Failure ] |
| # finder:enable-general |
| crbug.com/4567 [ win ] foo/test [ Failure ] |
| """ |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/2345'), |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/3456'), |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/4567'), |
| ] |
| expected_contents = self.header + """ |
| # finder:disable-general |
| crbug.com/2345 [ win ] foo/test [ Failure ] |
| crbug.com/3456 [ win ] foo/test [ Failure ] |
| # finder:enable-general |
| """ |
| for removal_type in (expectations.RemovalType.STALE, |
| expectations.RemovalType.UNUSED): |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, removal_type) |
| self.assertEqual(removed_urls, set(['crbug.com/1234', 'crbug.com/4567'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testStaleBlockComments(self) -> None: |
| """Tests that stale expectations in a stale disable block are not removed""" |
| contents = self.header + """ |
| crbug.com/1234 [ win ] not_stale [ Failure ] |
| crbug.com/1234 [ win ] before_block [ Failure ] |
| # finder:disable-stale |
| crbug.com/2345 [ win ] in_block [ Failure ] |
| # finder:enable-stale |
| crbug.com/3456 [ win ] after_block [ Failure ] |
| """ |
| stale_expectations = [ |
| data_types.Expectation('before_block', ['win'], 'Failure', |
| 'crbug.com/1234'), |
| data_types.Expectation('in_block', ['win'], 'Failure', |
| 'crbug.com/2345'), |
| data_types.Expectation('after_block', ['win'], 'Failure', |
| 'crbug.com/3456'), |
| ] |
| expected_contents = self.header + """ |
| crbug.com/1234 [ win ] not_stale [ Failure ] |
| # finder:disable-stale |
| crbug.com/2345 [ win ] in_block [ Failure ] |
| # finder:enable-stale |
| """ |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set(['crbug.com/1234', 'crbug.com/3456'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testUnusedBlockComments(self) -> None: |
| """Tests that stale expectations in unused disable blocks are not removed""" |
| contents = self.header + """ |
| crbug.com/1234 [ win ] not_unused [ Failure ] |
| crbug.com/1234 [ win ] before_block [ Failure ] |
| # finder:disable-unused |
| crbug.com/2345 [ win ] in_block [ Failure ] |
| # finder:enable-unused |
| crbug.com/3456 [ win ] after_block [ Failure ] |
| """ |
| unused_expectations = [ |
| data_types.Expectation('before_block', ['win'], 'Failure', |
| 'crbug.com/1234'), |
| data_types.Expectation('in_block', ['win'], 'Failure', |
| 'crbug.com/2345'), |
| data_types.Expectation('after_block', ['win'], 'Failure', |
| 'crbug.com/3456'), |
| ] |
| expected_contents = self.header + """ |
| crbug.com/1234 [ win ] not_unused [ Failure ] |
| # finder:disable-unused |
| crbug.com/2345 [ win ] in_block [ Failure ] |
| # finder:enable-unused |
| """ |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| unused_expectations, self.filename, expectations.RemovalType.UNUSED) |
| self.assertEqual(removed_urls, set(['crbug.com/1234', 'crbug.com/3456'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testMismatchedBlockComments(self) -> None: |
| """Tests that block comments for the wrong removal type do nothing.""" |
| contents = self.header + """ |
| crbug.com/1234 [ win ] do_not_remove [ Failure ] |
| # finder:disable-stale |
| crbug.com/2345 [ win ] disabled_stale [ Failure ] |
| # finder:enable-stale |
| # finder:disable-unused |
| crbug.com/3456 [ win ] disabled_unused [ Failure ] |
| # finder:enable-unused |
| crbug.com/4567 [ win ] also_do_not_remove [ Failure ] |
| """ |
| expectations_to_remove = [ |
| data_types.Expectation('disabled_stale', ['win'], 'Failure', |
| 'crbug.com/2345'), |
| data_types.Expectation('disabled_unused', ['win'], 'Failure', |
| 'crbug.com/3456'), |
| ] |
| |
| expected_contents = self.header + """ |
| crbug.com/1234 [ win ] do_not_remove [ Failure ] |
| # finder:disable-stale |
| crbug.com/2345 [ win ] disabled_stale [ Failure ] |
| # finder:enable-stale |
| crbug.com/4567 [ win ] also_do_not_remove [ Failure ] |
| """ |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| expectations_to_remove, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set(['crbug.com/3456'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| expected_contents = self.header + """ |
| crbug.com/1234 [ win ] do_not_remove [ Failure ] |
| # finder:disable-unused |
| crbug.com/3456 [ win ] disabled_unused [ Failure ] |
| # finder:enable-unused |
| crbug.com/4567 [ win ] also_do_not_remove [ Failure ] |
| """ |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| expectations_to_remove, self.filename, expectations.RemovalType.UNUSED) |
| self.assertEqual(removed_urls, set(['crbug.com/2345'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testInlineGeneralComments(self) -> None: |
| """Tests that expectations with inline disable comments are not removed.""" |
| contents = self.header + """ |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| crbug.com/2345 [ win ] foo/test [ Failure ] # finder:disable-general |
| crbug.com/3456 [ win ] foo/test [ Failure ] |
| """ |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/2345'), |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/3456'), |
| ] |
| expected_contents = self.header + """ |
| crbug.com/2345 [ win ] foo/test [ Failure ] # finder:disable-general |
| """ |
| for removal_type in (expectations.RemovalType.STALE, |
| expectations.RemovalType.UNUSED): |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, removal_type) |
| self.assertEqual(removed_urls, set(['crbug.com/1234', 'crbug.com/3456'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testInlineStaleComments(self) -> None: |
| """Tests that expectations with inline stale disable comments not removed""" |
| contents = self.header + """ |
| crbug.com/1234 [ win ] not_disabled [ Failure ] |
| crbug.com/2345 [ win ] stale_disabled [ Failure ] # finder:disable-stale |
| crbug.com/3456 [ win ] unused_disabled [ Failure ] # finder:disable-unused |
| """ |
| stale_expectations = [ |
| data_types.Expectation('not_disabled', ['win'], 'Failure', |
| 'crbug.com/1234'), |
| data_types.Expectation('stale_disabled', ['win'], 'Failure', |
| 'crbug.com/2345'), |
| data_types.Expectation('unused_disabled', ['win'], 'Failure', |
| 'crbug.com/3456') |
| ] |
| expected_contents = self.header + """ |
| crbug.com/2345 [ win ] stale_disabled [ Failure ] # finder:disable-stale |
| """ |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set(['crbug.com/1234', 'crbug.com/3456'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testInlineUnusedComments(self) -> None: |
| """Tests that expectations with inline unused comments not removed""" |
| contents = self.header + """ |
| crbug.com/1234 [ win ] not_disabled [ Failure ] |
| crbug.com/2345 [ win ] stale_disabled [ Failure ] # finder:disable-stale |
| crbug.com/3456 [ win ] unused_disabled [ Failure ] # finder:disable-unused |
| """ |
| stale_expectations = [ |
| data_types.Expectation('not_disabled', ['win'], 'Failure', |
| 'crbug.com/1234'), |
| data_types.Expectation('stale_disabled', ['win'], 'Failure', |
| 'crbug.com/2345'), |
| data_types.Expectation('unused_disabled', ['win'], 'Failure', |
| 'crbug.com/3456') |
| ] |
| expected_contents = self.header + """ |
| crbug.com/3456 [ win ] unused_disabled [ Failure ] # finder:disable-unused |
| """ |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.UNUSED) |
| self.assertEqual(removed_urls, set(['crbug.com/1234', 'crbug.com/2345'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testGetDisableReasonFromComment(self): |
| """Tests that the disable reason can be pulled from a line.""" |
| self.assertEqual( |
| expectations._GetDisableReasonFromComment( |
| '# finder:disable-general foo'), 'foo') |
| self.assertEqual( |
| expectations._GetDisableReasonFromComment( |
| 'crbug.com/1234 [ win ] bar/test [ Failure ] ' |
| '# finder:disable-general foo'), 'foo') |
| |
| def testGroupBlockAllRemovable(self): |
| """Tests that a group with all members removable is removed.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| # finder:group-start some group name |
| [ linux ] bar/test [ RetryOnFailure ] |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:group-end |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('bar/test', ['linux'], ['RetryOnFailure']), |
| ] |
| |
| expected_contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set(['crbug.com/1234'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testLargeGroupBlockAllRemovable(self): |
| """Tests that a large group with all members removable is removed.""" |
| # This test exists because we've had issues that passed tests with |
| # relatively small groups, but failed on larger ones. |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| # finder:group-start some group name |
| [ linux ] a [ RetryOnFailure ] |
| [ linux ] b [ RetryOnFailure ] |
| [ linux ] c [ RetryOnFailure ] |
| [ linux ] d [ RetryOnFailure ] |
| [ linux ] e [ RetryOnFailure ] |
| [ linux ] f [ RetryOnFailure ] |
| [ linux ] g [ RetryOnFailure ] |
| [ linux ] h [ RetryOnFailure ] |
| [ linux ] i [ RetryOnFailure ] |
| [ linux ] j [ RetryOnFailure ] |
| [ linux ] k [ RetryOnFailure ] |
| # finder:group-end |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('a', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('b', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('c', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('d', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('e', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('f', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('g', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('h', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('i', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('j', ['linux'], ['RetryOnFailure']), |
| data_types.Expectation('k', ['linux'], ['RetryOnFailure']), |
| ] |
| |
| expected_contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set([])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testNestedGroupAndNarrowingAllRemovable(self): |
| """Tests that a disable block within a group can be properly removed.""" |
| contents = self.header + """ |
| crbug.com/2345 [ win ] baz/test [ Failure ] |
| |
| # Description |
| # finder:group-start name |
| # finder:disable-narrowing |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| crbug.com/1234 [ win ] bar/test [ Failure ] |
| # finder:enable-narrowing |
| # finder:group-end |
| |
| crbug.com/3456 [ linux ] foo/test [ Failure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('bar/test', ['win'], ['Failure'], |
| 'crbug.com/1234'), |
| ] |
| |
| expected_contents = self.header + """ |
| crbug.com/2345 [ win ] baz/test [ Failure ] |
| |
| |
| crbug.com/3456 [ linux ] foo/test [ Failure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set(['crbug.com/1234'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testGroupBlockNotAllRemovable(self): |
| """Tests that a group with not all members removable is not removed.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| # finder:group-start some group name |
| [ linux ] bar/test [ RetryOnFailure ] |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:group-end |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('bar/test', ['linux'], ['RetryOnFailure']) |
| ] |
| |
| expected_contents = contents |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set()) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testGroupSplitAllRemovable(self): |
| """Tests that a split group with all members removable is removed.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| # finder:group-start some group name |
| [ linux ] bar/test [ RetryOnFailure ] |
| # finder:group-end |
| |
| # finder:group-start some group name |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:group-end |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('bar/test', ['linux'], ['RetryOnFailure']) |
| ] |
| |
| expected_contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set(['crbug.com/1234'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testGroupSplitNotAllRemovable(self): |
| """Tests that a split group without all members removable is not removed.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| # finder:group-start some group name |
| [ linux ] bar/test [ RetryOnFailure ] |
| # finder:group-end |
| |
| # finder:group-start some group name |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:group-end |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('bar/test', ['linux'], ['RetryOnFailure']) |
| ] |
| |
| expected_contents = contents |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set()) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testGroupMultipleGroupsAllRemovable(self): |
| """Tests that multiple groups with all members removable are removed.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| # finder:group-start some group name |
| [ linux ] bar/test [ RetryOnFailure ] |
| # finder:group-end |
| |
| # finder:group-start another group name |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:group-end |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('bar/test', ['linux'], ['RetryOnFailure']) |
| ] |
| |
| expected_contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set(['crbug.com/1234'])) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testGroupMultipleGroupsSomeRemovable(self): |
| """Tests that multiple groups are handled separately.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| # finder:group-start some group name |
| [ linux ] bar/test [ RetryOnFailure ] |
| # finder:group-end |
| |
| # finder:group-start another group name |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| # finder:group-end |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['win'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('bar/test', ['linux'], ['RetryOnFailure']) |
| ] |
| |
| expected_contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| |
| |
| # finder:group-start another group name |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| # finder:group-end |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set()) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testNestedGroupStart(self): |
| """Tests that nested groups are disallowed.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| # finder:group-start some group name |
| [ linux ] bar/test [ RetryOnFailure ] |
| # finder:group-start another group name |
| # finder:group-end |
| # finder:group-end |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| with self.assertRaisesRegex(RuntimeError, |
| 'that is inside another group block'): |
| self.instance.RemoveExpectationsFromFile([], self.filename, |
| expectations.RemovalType.STALE) |
| |
| def testOrphanedGroupEnd(self): |
| """Tests that orphaned group ends are disallowed.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| # finder:group-end |
| [ linux ] bar/test [ RetryOnFailure ] |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| with self.assertRaisesRegex(RuntimeError, 'without a group start comment'): |
| self.instance.RemoveExpectationsFromFile([], self.filename, |
| expectations.RemovalType.STALE) |
| |
| def testNoGroupName(self): |
| """Tests that unnamed groups are disallowed.""" |
| contents = self.header + """ |
| |
| # This is a test comment |
| crbug.com/2345 [ win ] foo/test [ RetryOnFailure ] |
| |
| # Another comment |
| # finder:group-start |
| # finder:group-end |
| [ linux ] bar/test [ RetryOnFailure ] |
| [ win ] bar/test [ RetryOnFailure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| with self.assertRaisesRegex(RuntimeError, 'did not have a group name'): |
| self.instance.RemoveExpectationsFromFile([], self.filename, |
| expectations.RemovalType.STALE) |
| |
| def testRemoveCommentBlockSimpleTrailingWhitespace(self): |
| """Tests stale comment removal in a simple case with trailing whitespace.""" |
| contents = self.header + """ |
| # Comment line 1 |
| # Comment line 2 |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['linux'], ['Failure'], |
| 'crbug.com/1234'), |
| ] |
| |
| expected_contents = self.header + """ |
| |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, {'crbug.com/1234'}) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testRemoveCommentBlockSimpleTrailingComment(self): |
| """Tests stale comment removal in a simple case with trailing comment.""" |
| contents = self.header + """ |
| # Comment line 1 |
| # Comment line 2 |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['linux'], ['Failure'], |
| 'crbug.com/1234'), |
| ] |
| |
| expected_contents = self.header + """ |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, {'crbug.com/1234'}) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testRemoveCommentBlockSimpleEndOfFile(self): |
| """Tests stale comment removal in a simple case at file end.""" |
| contents = self.header + """ |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| |
| # Comment line 1 |
| # Comment line 2 |
| crbug.com/1234 [ linux ] foo/test [ Failure ]""" |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['linux'], ['Failure'], |
| 'crbug.com/1234'), |
| ] |
| |
| expected_contents = self.header + """ |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, {'crbug.com/1234'}) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testRemoveCommentBlockWithAnnotations(self): |
| """Tests stale comment removal with annotations on both ends.""" |
| contents = self.header + """ |
| # Comment line 1 |
| # Comment line 2 |
| # finder:disable-unused |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| # finder:enable-unused |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['linux'], ['Failure'], |
| 'crbug.com/1234'), |
| ] |
| |
| expected_contents = self.header + """ |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, {'crbug.com/1234'}) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testRemoveCommentBlockWithMissingTrailingAnnotation(self): |
| """Tests stale comment removal with a missing trailing annotation.""" |
| contents = self.header + """ |
| # Comment line 1 |
| # Comment line 2 |
| # finder:disable-unused |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:enable-unused |
| |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['linux'], ['Failure'], |
| 'crbug.com/1234'), |
| ] |
| |
| expected_contents = self.header + """ |
| # Comment line 1 |
| # Comment line 2 |
| # finder:disable-unused |
| |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:enable-unused |
| |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, {'crbug.com/1234'}) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testRemoveCommentBlockWithMissingStartAnnotation(self): |
| """Tests stale comment removal with a missing start annotation.""" |
| contents = self.header + """ |
| # finder:disable-unused |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # Comment line 1 |
| # Comment line 2 |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| # finder:enable-unused |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['linux'], ['Failure'], |
| 'crbug.com/1234'), |
| ] |
| |
| expected_contents = self.header + """ |
| # finder:disable-unused |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:enable-unused |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, {'crbug.com/1234'}) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testRemoveCommentBlockMultipleExpectations(self): |
| """Tests stale comment removal with multiple expectations in a block.""" |
| contents = self.header + """ |
| # Comment line 1 |
| # Comment line 2 |
| # finder:disable-unused |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| crbug.com/3456 [ mac ] foo/test [ Failure ] |
| # finder:enable-unused |
| |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['linux'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('foo/test', ['mac'], ['Failure'], |
| 'crbug.com/3456'), |
| ] |
| |
| expected_contents = self.header + """ |
| |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, {'crbug.com/1234', 'crbug.com/3456'}) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testRemoveCommentBlockMultipleBlocks(self): |
| """Tests stale comment removal with expectations in multiple blocks.""" |
| contents = self.header + """ |
| # Comment line 1 |
| # Comment line 2 |
| # finder:disable-unused |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| # finder:enable-unused |
| |
| # Comment line 4 |
| # finder:disable-unused |
| crbug.com/3456 [ mac ] foo/test [ Failure ] |
| # finder:enable-unused |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| stale_expectations = [ |
| data_types.Expectation('foo/test', ['linux'], ['Failure'], |
| 'crbug.com/1234'), |
| data_types.Expectation('foo/test', ['mac'], ['Failure'], |
| 'crbug.com/3456'), |
| ] |
| |
| expected_contents = self.header + """ |
| |
| # Comment line 3 |
| crbug.com/2345 [ win ] bar/test [ Failure ] |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, {'crbug.com/1234', 'crbug.com/3456'}) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testRemoveStaleAnnotationBlocks(self): |
| """Tests removal of annotation blocks not associated with removals.""" |
| contents = self.header + """ |
| # finder:disable-general |
| # finder:enable-general |
| |
| # finder:disable-stale |
| |
| # finder:enable-stale |
| |
| # finder:disable-unused |
| # comment |
| # finder:enable-unused |
| |
| # finder:disable-narrowing description |
| # comment |
| # finder:enable-narrowing |
| |
| # finder:group-start name |
| # finder:group-end |
| """ |
| |
| stale_expectations = [] |
| |
| expected_contents = self.header + """ |
| |
| |
| |
| |
| """ |
| |
| with open(self.filename, 'w') as f: |
| f.write(contents) |
| |
| removed_urls = self.instance.RemoveExpectationsFromFile( |
| stale_expectations, self.filename, expectations.RemovalType.STALE) |
| self.assertEqual(removed_urls, set()) |
| with open(self.filename) as f: |
| self.assertEqual(f.read(), expected_contents) |
| |
| def testGroupNameExtraction(self): |
| """Tests that group names are properly extracted.""" |
| group_name = expectations._GetGroupNameFromCommentLine( |
| '# finder:group-start group name') |
| self.assertEqual(group_name, 'group name') |
| |
| |
| class GetDisableAnnotatedExpectationsFromFileUnittest(unittest.TestCase): |
| def setUp(self) -> None: |
| self.instance = uu.CreateGenericExpectations() |
| |
| def testNestedBlockComments(self) -> None: |
| """Tests that nested disable block comments throw exceptions.""" |
| contents = """ |
| # finder:disable-general |
| # finder:disable-general |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:enable-general |
| # finder:enable-general |
| """ |
| with self.assertRaises(RuntimeError): |
| self.instance._GetDisableAnnotatedExpectationsFromFile( |
| 'expectation_file', contents) |
| |
| contents = """ |
| # finder:disable-general |
| # finder:disable-stale |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:enable-stale |
| # finder:enable-general |
| """ |
| with self.assertRaises(RuntimeError): |
| self.instance._GetDisableAnnotatedExpectationsFromFile( |
| 'expectation_file', contents) |
| |
| contents = """ |
| # finder:enable-general |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| """ |
| with self.assertRaises(RuntimeError): |
| self.instance._GetDisableAnnotatedExpectationsFromFile( |
| 'expectation_file', contents) |
| |
| def testBlockComments(self) -> None: |
| """Tests that disable block comments are properly parsed.""" |
| contents = """ |
| # finder:disable-general general-reason |
| crbug.com/1234 [ win ] foo/test [ Failure ] |
| # finder:enable-general |
| |
| # finder:disable-stale |
| crbug.com/1234 [ mac ] foo/test [ Failure ] |
| # finder:enable-stale |
| |
| # finder:disable-unused unused reason |
| crbug.com/1234 [ linux ] foo/test [ Failure ] |
| # finder:enable-unused |
| |
| # finder:disable-narrowing |
| crbug.com/1234 [ win ] bar/test [ Failure ] |
| # finder:enable-narrowing |
| |
| crbug.com/1234 [ mac ] bar/test [ Failure ] |
| """ |
| annotated_expectations = ( |
| self.instance._GetDisableAnnotatedExpectationsFromFile( |
| 'expectation_file', contents)) |
| self.assertEqual(len(annotated_expectations), 4) |
| self.assertEqual( |
| annotated_expectations[data_types.Expectation('foo/test', ['win'], |
| 'Failure', |
| 'crbug.com/1234')], |
| ('-general', 'general-reason')) |
| self.assertEqual( |
| annotated_expectations[data_types.Expectation('foo/test', ['mac'], |
| 'Failure', |
| 'crbug.com/1234')], |
| ('-stale', '')) |
| self.assertEqual( |
| annotated_expectations[data_types.Expectation('foo/test', ['linux'], |
| 'Failure', |
| 'crbug.com/1234')], |
| ('-unused', 'unused reason')) |
| self.assertEqual( |
| annotated_expectations[data_types.Expectation('bar/test', ['win'], |
| 'Failure', |
| 'crbug.com/1234')], |
| ('-narrowing', '')) |
| |
| def testInlineComments(self) -> None: |
| """Tests that inline disable comments are properly parsed.""" |
| # pylint: disable=line-too-long |
| contents = """ |
| crbug.com/1234 [ win ] foo/test [ Failure ] # finder:disable-general general-reason |
| |
| crbug.com/1234 [ mac ] foo/test [ Failure ] # finder:disable-stale |
| |
| crbug.com/1234 [ linux ] foo/test [ Failure ] # finder:disable-unused unused reason |
| |
| crbug.com/1234 [ win ] bar/test [ Failure ] # finder:disable-narrowing |
| |
| crbug.com/1234 [ mac ] bar/test [ Failure ] |
| """ |
| # pylint: enable=line-too-long |
| annotated_expectations = ( |
| self.instance._GetDisableAnnotatedExpectationsFromFile( |
| 'expectation_file', contents)) |
| self.assertEqual(len(annotated_expectations), 4) |
| self.assertEqual( |
| annotated_expectations[data_types.Expectation('foo/test', ['win'], |
| 'Failure', |
| 'crbug.com/1234')], |
| ('-general', 'general-reason')) |
| self.assertEqual( |
| annotated_expectations[data_types.Expectation('foo/test', ['mac'], |
| 'Failure', |
| 'crbug.com/1234')], |
| ('-stale', '')) |
| self.assertEqual( |
| annotated_expectations[data_types.Expectation('foo/test', ['linux'], |
| 'Failure', |
| 'crbug.com/1234')], |
| ('-unused', 'unused reason')) |
| self.assertEqual( |
| annotated_expectations[data_types.Expectation('bar/test', ['win'], |
| 'Failure', |
| 'crbug.com/1234')], |
| ('-narrowing', '')) |
| |
| |
| class GetExpectationLineUnittest(unittest.TestCase): |
| def setUp(self) -> None: |
| self.instance = uu.CreateGenericExpectations() |
| |
| def testNoMatchingExpectation(self) -> None: |
| """Tests that the case of no matching expectation is handled.""" |
| expectation = data_types.Expectation('foo', ['win'], 'Failure') |
| line, line_number = self.instance._GetExpectationLine( |
| expectation, FAKE_EXPECTATION_FILE_CONTENTS, 'expectation_file') |
| self.assertIsNone(line) |
| self.assertIsNone(line_number) |
| |
| def testMatchingExpectation(self) -> None: |
| """Tests that matching expectations are found.""" |
| expectation = data_types.Expectation('foo/test', ['win'], 'Failure', |
| 'crbug.com/1234') |
| line, line_number = self.instance._GetExpectationLine( |
| expectation, FAKE_EXPECTATION_FILE_CONTENTS, 'expectation_file') |
| self.assertEqual(line, 'crbug.com/1234 [ win ] foo/test [ Failure ]') |
| self.assertEqual(line_number, 3) |
| |
| |
| class FilterToMostSpecificTypTagsUnittest(fake_filesystem_unittest.TestCase): |
| def setUp(self) -> None: |
| self._expectations = uu.CreateGenericExpectations() |
| self.setUpPyfakefs() |
| with tempfile.NamedTemporaryFile(delete=False, mode='w') as f: |
| self.filename = f.name |
| |
| def testBasic(self) -> None: |
| """Tests that only the most specific tags are kept.""" |
| expectation_file_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| """ |
| with open(self.filename, 'w') as outfile: |
| outfile.write(expectation_file_contents) |
| tags = frozenset(['win', 'nvidia', 'nvidia-0x1111', 'release']) |
| filtered_tags = self._expectations._FilterToMostSpecificTypTags( |
| tags, self.filename) |
| self.assertEqual(filtered_tags, set(['win', 'nvidia-0x1111', 'release'])) |
| |
| def testSingleTags(self) -> None: |
| """Tests that functionality works with single tags.""" |
| expectation_file_contents = """\ |
| # tags: [ tag1_most_specific ] |
| # tags: [ tag2_most_specific ]""" |
| with open(self.filename, 'w') as outfile: |
| outfile.write(expectation_file_contents) |
| |
| tags = frozenset(['tag1_most_specific', 'tag2_most_specific']) |
| filtered_tags = self._expectations._FilterToMostSpecificTypTags( |
| tags, self.filename) |
| 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.filename, 'w') as outfile: |
| outfile.write(expectation_file_contents) |
| |
| tags = frozenset([ |
| 'tag1_least_specific', 'tag1_most_specific', 'tag2_middle_specific', |
| 'tag2_least_specific' |
| ]) |
| filtered_tags = self._expectations._FilterToMostSpecificTypTags( |
| tags, self.filename) |
| self.assertEqual(filtered_tags, |
| set(['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.filename, 'w') as outfile: |
| outfile.write(expectation_file_contents) |
| |
| tags = frozenset([ |
| 'tag1_least_specific', 'tag1_most_specific', 'tag2_middle_specific', |
| 'tag2_least_specific' |
| ]) |
| with self.assertRaisesRegex(RuntimeError, r'.*tag1_most_specific.*'): |
| self._expectations._FilterToMostSpecificTypTags(tags, self.filename) |
| |
| def testTagsLowerCased(self) -> None: |
| """Tests that found tags are lower cased to match internal tags.""" |
| expectation_file_contents = """\ |
| # tags: [ Win Win10 |
| # Linux |
| # Mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| """ |
| with open(self.filename, 'w') as outfile: |
| outfile.write(expectation_file_contents) |
| tags = frozenset(['win', 'win10', 'nvidia', 'release']) |
| filtered_tags = self._expectations._FilterToMostSpecificTypTags( |
| tags, self.filename) |
| self.assertEqual(filtered_tags, set(['win10', 'nvidia', 'release'])) |
| |
| |
| class NarrowSemiStaleExpectationScopeUnittest(fake_filesystem_unittest.TestCase |
| ): |
| def setUp(self) -> None: |
| self.setUpPyfakefs() |
| self.instance = uu.CreateGenericExpectations() |
| |
| with tempfile.NamedTemporaryFile(delete=False, mode='w') as f: |
| f.write(FAKE_EXPECTATION_FILE_CONTENTS_WITH_COMPLEX_TAGS) |
| self.filename = f.name |
| |
| def testEmptyExpectationMap(self) -> None: |
| """Tests that scope narrowing with an empty map is a no-op.""" |
| urls = self.instance.NarrowSemiStaleExpectationScope( |
| data_types.TestExpectationMap({})) |
| self.assertEqual(urls, set()) |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), |
| FAKE_EXPECTATION_FILE_CONTENTS_WITH_COMPLEX_TAGS) |
| |
| def testWildcard(self) -> None: |
| """Regression test to ensure that wildcards are modified correctly.""" |
| file_contents = """\ |
| # tags: [ win ] |
| # tags: [ amd intel ] |
| # results: [ Failure ] |
| |
| crbug.com/1234 [ win ] foo/bar* [ Failure ] |
| """ |
| with open(self.filename, 'w') as f: |
| f.write(file_contents) |
| |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['win', 'intel'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/bar*', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| }), |
| }) |
| # yap: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win ] |
| # tags: [ amd intel ] |
| # results: [ Failure ] |
| |
| crbug.com/1234 [ intel win ] foo/bar* [ Failure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, {'crbug.com/1234'}) |
| |
| def testMultipleSteps(self) -> None: |
| """Tests that scope narrowing works across multiple steps.""" |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['win', 'intel'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ intel win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testMultipleBuilders(self) -> None: |
| """Tests that scope narrowing works across multiple builders.""" |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['win', 'intel'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_amd_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| }), |
| 'win_intel_builder': |
| data_types.StepBuildStatsMap({ |
| 'intel': intel_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ intel win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testMultipleExpectations(self) -> None: |
| """Tests that scope narrowing works across multiple expectations.""" |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd'])) |
| failed_amd_stats = data_types.BuildStats() |
| failed_amd_stats.AddFailedBuild('1', frozenset(['win', 'amd'])) |
| multi_amd_stats = data_types.BuildStats() |
| multi_amd_stats.AddFailedBuild('1', frozenset(['win', 'amd', 'debug'])) |
| multi_amd_stats.AddFailedBuild('1', frozenset(['win', 'amd', 'release'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['win', 'intel'])) |
| debug_stats = data_types.BuildStats() |
| debug_stats.AddFailedBuild('1', frozenset(['linux', 'debug'])) |
| release_stats = data_types.BuildStats() |
| release_stats.AddPassedBuild(frozenset(['linux', 'release'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| # These two expectations are here to ensure that our continue logic |
| # works as expected when we hit cases we can't handle, i.e. that |
| # later expectations are still handled properly. |
| data_types.Expectation('bar/test', ['win'], 'Failure', ''): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'win1': amd_stats, |
| 'win2': failed_amd_stats, |
| }), |
| }), |
| data_types.Expectation('baz/test', ['win'], 'Failure', ''): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'win1': amd_stats, |
| 'win2': multi_amd_stats, |
| }), |
| }), |
| data_types.Expectation( |
| 'foo/test', ['linux'], 'RetryOnFailure', 'crbug.com/2345'): |
| data_types.BuilderStepMap({ |
| 'linux_builder': |
| data_types.StepBuildStatsMap({ |
| 'debug': debug_stats, |
| 'release': release_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ intel win ] foo/test [ Failure ] |
| crbug.com/2345 [ debug linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234', 'crbug.com/2345'])) |
| |
| def testMultipleOutputLines(self) -> None: |
| """Tests that scope narrowing works with multiple output lines.""" |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['win', 'intel'])) |
| nvidia_stats = data_types.BuildStats() |
| nvidia_stats.AddFailedBuild('1', frozenset(['win', 'nvidia'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_amd_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| }), |
| 'win_intel_builder': |
| data_types.StepBuildStatsMap({ |
| 'intel': intel_stats, |
| }), |
| 'win_nvidia_builder': |
| data_types.StepBuildStatsMap({ |
| 'nvidia': nvidia_stats, |
| }) |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ intel win ] foo/test [ Failure ] |
| crbug.com/1234 [ nvidia win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testMultipleTagSets(self) -> None: |
| """Tests that multiple tag sets result in a scope narrowing no-op.""" |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd', 'release'])) |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd', 'debug'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['win', 'intel'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), |
| FAKE_EXPECTATION_FILE_CONTENTS_WITH_COMPLEX_TAGS) |
| self.assertEqual(urls, set()) |
| |
| def testAmbiguousTags(self): |
| """Tests that ambiguous tag sets result in a scope narrowing no-op.""" |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd'])) |
| bad_amd_stats = data_types.BuildStats() |
| bad_amd_stats.AddFailedBuild('1', frozenset(['win', 'amd'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['win', 'intel'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| 'bad_amd': bad_amd_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), |
| FAKE_EXPECTATION_FILE_CONTENTS_WITH_COMPLEX_TAGS) |
| self.assertEqual(urls, set()) |
| |
| def testRemoveCommonTags(self) -> None: |
| """Tests that scope narrowing removes common/redundant tags.""" |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd', 'desktop'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['win', 'intel', 'desktop'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ intel win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, {'crbug.com/1234'}) |
| |
| def testConsolidateKnownOverlappingTags(self) -> None: |
| """Tests that scope narrowing consolidates known overlapping tags.""" |
| |
| # This specific example emulates a dual GPU machine where we remove the |
| # integrated GPU tag. |
| def SideEffect(tags): |
| return tags - {'intel'} |
| |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd'])) |
| nvidia_dgpu_intel_igpu_stats = data_types.BuildStats() |
| nvidia_dgpu_intel_igpu_stats.AddFailedBuild( |
| '1', frozenset(['win', 'nvidia', 'intel'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'dual_gpu': nvidia_dgpu_intel_igpu_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| with mock.patch.object(self.instance, |
| '_ConsolidateKnownOverlappingTags', |
| side_effect=SideEffect): |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ nvidia win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testFilterToSpecificTags(self) -> None: |
| """Tests that scope narrowing filters to the most specific tags.""" |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd', 'amd-0x1111'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['win', 'intel', 'intel-0x2222'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ intel-0x2222 win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testSupersetsRemoved(self) -> None: |
| """Tests that superset tags (i.e. conflicts) are omitted.""" |
| # These stats are set up so that the raw new tag sets are: |
| # [{win, amd}, {win, amd, debug}] since the passed Intel build also has |
| # "release". Thus, if we aren't correctly filtering out supersets, we'll |
| # end up with [ amd win ] and [ amd debug win ] in the expectation file |
| # instead of just [ amd win ]. |
| amd_release_stats = data_types.BuildStats() |
| amd_release_stats.AddFailedBuild('1', frozenset(['win', 'amd', 'release'])) |
| amd_debug_stats = data_types.BuildStats() |
| amd_debug_stats.AddFailedBuild('1', frozenset(['win', 'amd', 'debug'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddPassedBuild(frozenset(['win', 'intel', 'release'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd_release': amd_release_stats, |
| 'amd_debug': amd_debug_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ amd win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testNoPassingOverlap(self): |
| """Tests that scope narrowing works with no overlap between passing tags.""" |
| # There is no commonality between [ amd debug ] and [ intel release ], so |
| # the resulting expectation we generate should just be the tags from the |
| # failed build. |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['win', 'amd', 'debug'])) |
| intel_stats_debug = data_types.BuildStats() |
| intel_stats_debug.AddFailedBuild('1', frozenset(['win', 'intel', 'debug'])) |
| intel_stats_release = data_types.BuildStats() |
| intel_stats_release.AddPassedBuild(frozenset(['win', 'intel', 'release'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel_debug': intel_stats_debug, |
| 'intel_release': intel_stats_release, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ debug intel win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testMultipleOverlap(self): |
| """Tests that scope narrowing works with multiple potential overlaps.""" |
| # [ win intel debug ], [ win intel release ], and [ win amd debug ] each |
| # have 2/3 tags overlapping with each other, so we expect one pair to be |
| # simplified and the other to remain the same. |
| intel_debug_stats = data_types.BuildStats() |
| intel_debug_stats.AddFailedBuild('1', frozenset(['win', 'intel', 'debug'])) |
| intel_release_stats = data_types.BuildStats() |
| intel_release_stats.AddFailedBuild('1', |
| frozenset(['win', 'intel', 'release'])) |
| amd_debug_stats = data_types.BuildStats() |
| amd_debug_stats.AddFailedBuild('1', frozenset(['win', 'amd', 'debug'])) |
| amd_release_stats = data_types.BuildStats() |
| amd_release_stats.AddPassedBuild(frozenset(['win', 'amd', 'release'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['win'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'win_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd_debug': amd_debug_stats, |
| 'amd_release': amd_release_stats, |
| 'intel_debug': intel_debug_stats, |
| 'intel_release': intel_release_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| # Python sets are not stable between different processes due to random hash |
| # seeds that are on by default. Since there are two valid ways to simplify |
| # the tags we provided, this means that the test is flaky if we only check |
| # for one due to the non-deterministic order the tags are processed, so |
| # instead, accept either valid output. |
| # |
| # Random hash seeds can be disabled by setting PYTHONHASHSEED, but that |
| # requires that we either ensure that this test is always run with that set |
| # (difficult/error-prone), or we manually set the seed and recreate the |
| # process (hacky). Simply accepting either valid value instead of trying to |
| # force a certain order seems like the better approach. |
| expected_contents1 = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ amd debug win ] foo/test [ Failure ] |
| crbug.com/1234 [ intel win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| expected_contents2 = """\ |
| # tags: [ win win10 |
| # linux |
| # mac ] |
| # tags: [ nvidia nvidia-0x1111 |
| # intel intel-0x2222 |
| # amd amd-0x3333] |
| # tags: [ release debug ] |
| # results: [ Failure RetryOnFailure ] |
| |
| crbug.com/1234 [ debug win ] foo/test [ Failure ] |
| crbug.com/1234 [ intel release win ] foo/test [ Failure ] |
| crbug.com/2345 [ linux ] foo/test [ RetryOnFailure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertIn(infile.read(), (expected_contents1, expected_contents2)) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testMultipleOverlapRepeatedIntersection(self): |
| """Edge case where intersection checks need to be repeated to work.""" |
| original_contents = """\ |
| # tags: [ mac |
| # win ] |
| # tags: [ amd amd-0x3333 |
| # intel intel-0x2222 intel-0x4444 |
| # nvidia nvidia-0x1111 ] |
| # results: [ Failure ] |
| |
| crbug.com/1234 foo/test [ Failure ] |
| """ |
| with open(self.filename, 'w') as outfile: |
| outfile.write(original_contents) |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddFailedBuild('1', frozenset(['mac', 'amd', 'amd-0x3333'])) |
| intel_stats_1 = data_types.BuildStats() |
| intel_stats_1.AddFailedBuild('1', |
| frozenset(['mac', 'intel', 'intel-0x2222'])) |
| intel_stats_2 = data_types.BuildStats() |
| intel_stats_2.AddFailedBuild('1', |
| frozenset(['mac', 'intel', 'intel-0x4444'])) |
| nvidia_stats = data_types.BuildStats() |
| nvidia_stats.AddPassedBuild(frozenset(['win', 'nvidia', 'nvidia-0x1111'])) |
| |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', [], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'mixed_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel_1': intel_stats_1, |
| 'intel_2': intel_stats_2, |
| 'nvidia': nvidia_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ mac |
| # win ] |
| # tags: [ amd amd-0x3333 |
| # intel intel-0x2222 intel-0x4444 |
| # nvidia nvidia-0x1111 ] |
| # results: [ Failure ] |
| |
| crbug.com/1234 [ mac ] foo/test [ Failure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testBlockDisableAnnotation(self) -> None: |
| """Tests that narrowing is skipped if block annotations are present.""" |
| original_contents = """\ |
| # tags: [ mac ] |
| # tags: [ amd intel ] |
| # results: [ Failure ] |
| |
| crbug.com/1234 [ mac ] foo/test [ Failure ] |
| # finder:disable-narrowing |
| crbug.com/2345 [ mac ] bar/test [ Failure ] |
| # finder:enable-narrowing |
| """ |
| with open(self.filename, 'w') as outfile: |
| outfile.write(original_contents) |
| |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['mac', 'amd'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['mac', 'intel'])) |
| |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['mac'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'mac_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| data_types.Expectation( |
| 'bar/test', ['mac'], 'Failure', 'crbug.com/2345'): |
| data_types.BuilderStepMap({ |
| 'mac_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ mac ] |
| # tags: [ amd intel ] |
| # results: [ Failure ] |
| |
| crbug.com/1234 [ intel mac ] foo/test [ Failure ] |
| # finder:disable-narrowing |
| crbug.com/2345 [ mac ] bar/test [ Failure ] |
| # finder:enable-narrowing |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| def testNoOverlapsInNarrowedExpectations(self): |
| """Tests that scope narrowing does not produce overlapping tag sets.""" |
| original_contents = """\ |
| # tags: [ Linux |
| # Mac Mac10.15 Mac11 Mac11-arm64 Mac12 Mac12-arm64 |
| # Win Win10.20h2 Win11 ] |
| # tags: [ Release Debug ] |
| # results: [ Failure ] |
| |
| crbug.com/874695 foo/test [ Failure ] |
| """ |
| with open(self.filename, 'w') as outfile: |
| outfile.write(original_contents) |
| |
| linux_debug_stats = data_types.BuildStats() |
| linux_debug_stats.AddPassedBuild(frozenset(['debug', 'linux'])) |
| linux_release_stats = data_types.BuildStats() |
| linux_release_stats.AddFailedBuild('1', frozenset(['linux', 'release'])) |
| mac10_release_stats = data_types.BuildStats() |
| mac10_release_stats.AddFailedBuild( |
| '1', frozenset(['mac', 'mac10.15', 'release'])) |
| mac11_arm_release_stats = data_types.BuildStats() |
| mac11_arm_release_stats.AddFailedBuild( |
| '1', frozenset(['mac', 'mac11-arm64', 'release'])) |
| mac11_release_stats = data_types.BuildStats() |
| mac11_release_stats.AddFailedBuild('1', |
| frozenset(['mac', 'mac11', 'release'])) |
| mac12_arm_release_stats = data_types.BuildStats() |
| mac12_arm_release_stats.AddFailedBuild( |
| '1', frozenset(['mac', 'mac12-arm64', 'release'])) |
| mac12_debug_stats = data_types.BuildStats() |
| mac12_debug_stats.AddFailedBuild('1', frozenset(['debug', 'mac', 'mac12'])) |
| mac12_release_stats = data_types.BuildStats() |
| mac12_release_stats.AddFailedBuild('1', |
| frozenset(['mac', 'mac12', 'release'])) |
| win10_release_stats = data_types.BuildStats() |
| win10_release_stats.AddFailedBuild( |
| '1', frozenset(['release', 'win', 'win10.20h2'])) |
| win11_release_stats = data_types.BuildStats() |
| win11_release_stats.AddFailedBuild('1', |
| frozenset(['release', 'win', 'win11'])) |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', |
| [], 'Failure', 'crbug.com/874695'): |
| data_types.BuilderStepMap({ |
| 'Linux Tests (dbg)(1)': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': linux_debug_stats, |
| }), |
| 'Mac10.15 Tests': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': mac10_release_stats, |
| }), |
| 'mac11-arm64-rel-tests': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': mac11_arm_release_stats, |
| }), |
| 'Mac11 Tests': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': mac11_release_stats, |
| }), |
| 'mac12-arm64-rel-tests': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': mac12_arm_release_stats, |
| }), |
| 'Mac12 Tests (dbg)': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': mac12_debug_stats, |
| }), |
| 'Mac12 Tests': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': mac12_release_stats, |
| }), |
| 'Linux Tests': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': linux_release_stats, |
| }), |
| 'WebKit Win10': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': win10_release_stats, |
| }), |
| 'Win11 Tests x64': |
| data_types.StepBuildStatsMap({ |
| 'blink_web_tests': win11_release_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| # Python sets are not stable between different processes due to random hash |
| # seeds that are on by default. Since there are two valid ways to simplify |
| # the tags we provided, this means that the test is flaky if we only check |
| # for one due to the non-deterministic order the tags are processed, so |
| # instead, accept either valid output. |
| # |
| # Random hash seeds can be disabled by setting PYTHONHASHSEED, but that |
| # requires that we either ensure that this test is always run with that set |
| # (difficult/error-prone), or we manually set the seed and recreate the |
| # process (hacky). Simply accepting either valid value instead of trying to |
| # force a certain order seems like the better approach. |
| expected_contents1 = """\ |
| # tags: [ Linux |
| # Mac Mac10.15 Mac11 Mac11-arm64 Mac12 Mac12-arm64 |
| # Win Win10.20h2 Win11 ] |
| # tags: [ Release Debug ] |
| # results: [ Failure ] |
| |
| crbug.com/874695 [ debug mac12 ] foo/test [ Failure ] |
| crbug.com/874695 [ release ] foo/test [ Failure ] |
| """ |
| expected_contents2 = """\ |
| # tags: [ Linux |
| # Mac Mac10.15 Mac11 Mac11-arm64 Mac12 Mac12-arm64 |
| # Win Win10.20h2 Win11 ] |
| # tags: [ Release Debug ] |
| # results: [ Failure ] |
| |
| crbug.com/874695 [ linux release ] foo/test [ Failure ] |
| crbug.com/874695 [ mac ] foo/test [ Failure ] |
| crbug.com/874695 [ release win ] foo/test [ Failure ] |
| """ |
| with open(self.filename) as infile: |
| self.assertIn(infile.read(), (expected_contents1, expected_contents2)) |
| self.assertEqual(urls, set(['crbug.com/874695'])) |
| |
| def testInlineDisableAnnotation(self) -> None: |
| """Tests that narrowing is skipped if inline annotations are present.""" |
| original_contents = """\ |
| # tags: [ mac ] |
| # tags: [ amd intel ] |
| # results: [ Failure ] |
| |
| crbug.com/1234 [ mac ] foo/test [ Failure ] |
| crbug.com/2345 [ mac ] bar/test [ Failure ] # finder:disable-narrowing |
| """ |
| with open(self.filename, 'w') as outfile: |
| outfile.write(original_contents) |
| |
| amd_stats = data_types.BuildStats() |
| amd_stats.AddPassedBuild(frozenset(['mac', 'amd'])) |
| intel_stats = data_types.BuildStats() |
| intel_stats.AddFailedBuild('1', frozenset(['mac', 'intel'])) |
| |
| # yapf: disable |
| test_expectation_map = data_types.TestExpectationMap({ |
| self.filename: |
| data_types.ExpectationBuilderMap({ |
| data_types.Expectation( |
| 'foo/test', ['mac'], 'Failure', 'crbug.com/1234'): |
| data_types.BuilderStepMap({ |
| 'mac_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| data_types.Expectation( |
| 'bar/test', ['mac'], 'Failure', 'crbug.com/2345'): |
| data_types.BuilderStepMap({ |
| 'mac_builder': |
| data_types.StepBuildStatsMap({ |
| 'amd': amd_stats, |
| 'intel': intel_stats, |
| }), |
| }), |
| }), |
| }) |
| # yapf: enable |
| urls = self.instance.NarrowSemiStaleExpectationScope(test_expectation_map) |
| expected_contents = """\ |
| # tags: [ mac ] |
| # tags: [ amd intel ] |
| # results: [ Failure ] |
| |
| crbug.com/1234 [ intel mac ] foo/test [ Failure ] |
| crbug.com/2345 [ mac ] bar/test [ Failure ] # finder:disable-narrowing |
| """ |
| with open(self.filename) as infile: |
| self.assertEqual(infile.read(), expected_contents) |
| self.assertEqual(urls, set(['crbug.com/1234'])) |
| |
| |
| class FindOrphanedBugsUnittest(fake_filesystem_unittest.TestCase): |
| def CreateFile(self, *args, **kwargs) -> None: |
| # TODO(crbug.com/1156806): Remove this and just use fs.create_file() when |
| # Catapult is updated to a newer version of pyfakefs that is compatible with |
| # Chromium's version. |
| if hasattr(self.fs, 'create_file'): |
| self.fs.create_file(*args, **kwargs) |
| else: |
| self.fs.CreateFile(*args, **kwargs) |
| |
| def setUp(self) -> None: |
| expectations_dir = os.path.join(os.path.dirname(__file__), 'expectations') |
| self.setUpPyfakefs() |
| self.instance = expectations.Expectations() |
| self.filepath_patcher = mock.patch.object( |
| self.instance, |
| 'GetExpectationFilepaths', |
| return_value=[os.path.join(expectations_dir, 'real_expectations.txt')]) |
| self.filepath_mock = self.filepath_patcher.start() |
| self.addCleanup(self.filepath_patcher.stop) |
| |
| real_contents = 'crbug.com/1\ncrbug.com/2' |
| skipped_contents = 'crbug.com/4' |
| self.CreateFile(os.path.join(expectations_dir, 'real_expectations.txt'), |
| contents=real_contents) |
| self.CreateFile(os.path.join(expectations_dir, 'fake.txt'), |
| contents=skipped_contents) |
| |
| def testNoOrphanedBugs(self) -> None: |
| bugs = ['crbug.com/1', 'crbug.com/2'] |
| self.assertEqual(self.instance.FindOrphanedBugs(bugs), set()) |
| |
| def testOrphanedBugs(self) -> None: |
| bugs = ['crbug.com/1', 'crbug.com/3', 'crbug.com/4'] |
| self.assertEqual(self.instance.FindOrphanedBugs(bugs), |
| set(['crbug.com/3', 'crbug.com/4'])) |
| |
| |
| if __name__ == '__main__': |
| unittest.main(verbosity=2) |