Changeset 247569 in webkit


Ignore:
Timestamp:
Jul 18, 2019 12:03:17 PM (5 years ago)
Author:
aakash_jain@apple.com
Message:

[ews-build] Add build step to AnalyzeLayoutTestsResults
https://bugs.webkit.org/show_bug.cgi?id=199877

Reviewed by Jonathan Bedard.

Logic is ported from webkitpy/tool/bot/patchanalysistask.py::_retry_layout_tests()

  • BuildSlaveSupport/ews-build/steps.py:

(RunWebKitTestsWithoutPatch.evaluateCommand): invoke AnalyzeLayoutTestsResults step.
(AnalyzeLayoutTestsResults): Build step to analyze layout-test results.
(AnalyzeLayoutTestsResults.report_failure):
(AnalyzeLayoutTestsResults.report_pre_existing_failures):
(AnalyzeLayoutTestsResults.retry_build):
(AnalyzeLayoutTestsResults._results_failed_different_tests):
(AnalyzeLayoutTestsResults._report_flaky_tests):
(AnalyzeLayoutTestsResults.start):

  • BuildSlaveSupport/ews-build/steps_unittest.py: Added unit-tests.
Location:
trunk/Tools
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/BuildSlaveSupport/ews-build/steps.py

    r247515 r247569  
    992992    def evaluateCommand(self, cmd):
    993993        rc = shell.Test.evaluateCommand(self, cmd)
    994         self.build.addStepsAfterCurrentStep([ArchiveTestResults(), UploadTestResults(identifier='clean-tree'), ExtractTestResults(identifier='clean-tree')])
     994        self.build.addStepsAfterCurrentStep([ArchiveTestResults(), UploadTestResults(identifier='clean-tree'), ExtractTestResults(identifier='clean-tree'), AnalyzeLayoutTestsResults()])
    995995        return rc
    996996
     
    10061006            self.setProperty('clean_tree_run_failures', clean_tree_results.failing_tests)
    10071007        self._parseRunWebKitTestsOutput(logText)
     1008
     1009
     1010class AnalyzeLayoutTestsResults(buildstep.BuildStep):
     1011    name = 'analyze-layout-tests-results'
     1012    description = ['analyze-layout-test-results']
     1013    descriptionDone = ['analyze-layout-tests-results']
     1014
     1015    def report_failure(self, new_failures):
     1016        self.finished(FAILURE)
     1017        self.build.results = FAILURE
     1018        pluralSuffix = 's' if len(new_failures) > 1 else ''
     1019        new_failures_string = ', '.join([failure_name for failure_name in new_failures])
     1020        message = 'Found {} new Test failure{}: {}'.format(len(new_failures), pluralSuffix, new_failures_string)
     1021        self.descriptionDone = message
     1022        self.build.buildFinished([message], FAILURE)
     1023        return defer.succeed(None)
     1024
     1025    def report_pre_existing_failures(self, clean_tree_failures):
     1026        self.finished(SUCCESS)
     1027        self.build.results = SUCCESS
     1028        self.descriptionDone = 'Passed layout tests'
     1029        pluralSuffix = 's' if len(clean_tree_failures) > 1 else ''
     1030        message = 'Found {} pre-existing test failure{}'.format(len(clean_tree_failures), pluralSuffix)
     1031        self.build.buildFinished([message], SUCCESS)
     1032        return defer.succeed(None)
     1033
     1034    def retry_build(self, message=''):
     1035        self.finished(RETRY)
     1036        message = 'Unable to confirm if test failures are introduced by patch, retrying build'
     1037        self.descriptionDone = message
     1038        self.build.buildFinished([message], RETRY)
     1039        return defer.succeed(None)
     1040
     1041    def _results_failed_different_tests(self, first_results_failing_tests, second_results_failing_tests):
     1042        return first_results_failing_tests != second_results_failing_tests
     1043
     1044    def _report_flaky_tests(self, flaky_tests):
     1045        #TODO: implement this
     1046        pass
     1047
     1048    def start(self):
     1049        first_results_did_exceed_test_failure_limit = self.getProperty('first_results_exceed_failure_limit')
     1050        first_results_failing_tests = set(self.getProperty('first_run_failures', []))
     1051        second_results_did_exceed_test_failure_limit = self.getProperty('second_results_exceed_failure_limit')
     1052        second_results_failing_tests = set(self.getProperty('second_run_failures', []))
     1053        clean_tree_results_did_exceed_test_failure_limit = self.getProperty('clean_tree_results_exceed_failure_limit')
     1054        clean_tree_results_failing_tests = set(self.getProperty('clean_tree_run_failures', []))
     1055
     1056        if first_results_did_exceed_test_failure_limit and second_results_did_exceed_test_failure_limit:
     1057            if (len(first_results_failing_tests) - len(clean_tree_results_failing_tests)) <= 5:
     1058                # If we've made it here, then many tests are failing with the patch applied, but
     1059                # if the clean tree is also failing many tests, even if it's not quite as many,
     1060                # then we can't be certain that the discrepancy isn't due to flakiness, and hence we must defer judgement.
     1061                return self.retry_build()
     1062            return self.report_failure(first_results_failing_tests)
     1063
     1064        if second_results_did_exceed_test_failure_limit:
     1065            if clean_tree_results_did_exceed_test_failure_limit:
     1066                return self.retry_build()
     1067            failures_introduced_by_patch = first_results_failing_tests - clean_tree_results_failing_tests
     1068            if failures_introduced_by_patch:
     1069                return self.report_failure(failures_introduced_by_patch)
     1070            return self.retry_build()
     1071
     1072        if first_results_did_exceed_test_failure_limit:
     1073            if clean_tree_results_did_exceed_test_failure_limit:
     1074                return self.retry_build()
     1075            failures_introduced_by_patch = second_results_failing_tests - clean_tree_results_failing_tests
     1076            if failures_introduced_by_patch:
     1077                return self.report_failure(failures_introduced_by_patch)
     1078            return self.retry_build()
     1079
     1080        if self._results_failed_different_tests(first_results_failing_tests, second_results_failing_tests):
     1081            tests_that_only_failed_first = first_results_failing_tests.difference(second_results_failing_tests)
     1082            self._report_flaky_tests(tests_that_only_failed_first)
     1083
     1084            tests_that_only_failed_second = second_results_failing_tests.difference(first_results_failing_tests)
     1085            self._report_flaky_tests(tests_that_only_failed_second)
     1086
     1087            tests_that_consistently_failed = first_results_failing_tests.intersection(second_results_failing_tests)
     1088            if tests_that_consistently_failed:
     1089                if clean_tree_results_did_exceed_test_failure_limit:
     1090                    return self.retry_build()
     1091                failures_introduced_by_patch = tests_that_consistently_failed - clean_tree_results_failing_tests
     1092                if failures_introduced_by_patch:
     1093                    return self.report_failure(failures_introduced_by_patch)
     1094
     1095            # At this point we know that at least one test flaked, but no consistent failures
     1096            # were introduced. This is a bit of a grey-zone.
     1097            return self.retry_build()
     1098
     1099        if clean_tree_results_did_exceed_test_failure_limit:
     1100            return self.retry_build()
     1101        failures_introduced_by_patch = first_results_failing_tests - clean_tree_results_failing_tests
     1102        if failures_introduced_by_patch:
     1103            return self.report_failure(failures_introduced_by_patch)
     1104
     1105        # At this point, we know that the first and second runs had the exact same failures,
     1106        # and that those failures are all present on the clean tree, so we can say with certainty
     1107        # that the patch is good.
     1108        return self.report_pre_existing_failures(clean_tree_results_failing_tests)
    10081109
    10091110
  • trunk/Tools/BuildSlaveSupport/ews-build/steps_unittest.py

    r247515 r247569  
    3535from twisted.trial import unittest
    3636
    37 from steps import (AnalyzeAPITestsResults, AnalyzeCompileWebKitResults, ApplyPatch, ArchiveBuiltProduct, ArchiveTestResults,
     37from steps import (AnalyzeAPITestsResults, AnalyzeCompileWebKitResults, AnalyzeLayoutTestsResults, ApplyPatch, ArchiveBuiltProduct, ArchiveTestResults,
    3838                   CheckOutSource, CheckOutSpecificRevision, CheckPatchRelevance, CheckStyle, CleanBuild, CleanUpGitIndexLock, CleanWorkingDirectory,
    3939                   CompileJSCOnly, CompileJSCOnlyToT, CompileWebKit, CompileWebKitToT, ConfigureBuild,
     
    11351135        return self.runStep()
    11361136
     1137
     1138class TestAnalyzeLayoutTestsResults(BuildStepMixinAdditions, unittest.TestCase):
     1139    def setUp(self):
     1140        self.longMessage = True
     1141        return self.setUpBuildStep()
     1142
     1143    def tearDown(self):
     1144        return self.tearDownBuildStep()
     1145
     1146    def configureStep(self):
     1147        self.setupStep(AnalyzeLayoutTestsResults())
     1148        self.setProperty('first_results_exceed_failure_limit', False)
     1149        self.setProperty('second_results_exceed_failure_limit', False)
     1150        self.setProperty('clean_tree_results_exceed_failure_limit', False)
     1151        self.setProperty('clean_tree_run_failures', [])
     1152
     1153    def test_failure_introduced_by_patch(self):
     1154        self.configureStep()
     1155        self.setProperty('first_run_failures', ["jquery/offset.html"])
     1156        self.setProperty('second_run_failures', ["jquery/offset.html"])
     1157        self.expectOutcome(result=FAILURE, state_string='Found 1 new Test failure: jquery/offset.html (failure)')
     1158        return self.runStep()
     1159
     1160    def test_failure_on_clean_tree(self):
     1161        self.configureStep()
     1162        self.setProperty('first_run_failures', ["jquery/offset.html"])
     1163        self.setProperty('second_run_failures', ["jquery/offset.html"])
     1164        self.setProperty('clean_tree_run_failures', ["jquery/offset.html"])
     1165        self.expectOutcome(result=SUCCESS, state_string='Passed layout tests')
     1166        return self.runStep()
     1167
     1168    def test_flaky_and_consistent_failures_without_clean_tree_failures(self):
     1169        self.configureStep()
     1170        self.setProperty('first_run_failures', ['test1', 'test2'])
     1171        self.setProperty('second_run_failures', ['test1'])
     1172        self.expectOutcome(result=FAILURE, state_string='Found 1 new Test failure: test1 (failure)')
     1173        return self.runStep()
     1174
     1175    def test_flaky_and_inconsistent_failures_without_clean_tree_failures(self):
     1176        self.configureStep()
     1177        self.setProperty('first_run_failures', ['test1', 'test2'])
     1178        self.setProperty('second_run_failures', ['test3'])
     1179        self.expectOutcome(result=RETRY, state_string='Unable to confirm if test failures are introduced by patch, retrying build (retry)')
     1180        return self.runStep()
     1181
     1182    def test_flaky_and_inconsistent_failures_with_clean_tree_failures(self):
     1183        self.configureStep()
     1184        self.setProperty('first_run_failures', ['test1', 'test2'])
     1185        self.setProperty('second_run_failures', ['test3'])
     1186        self.setProperty('clean_tree_run_failures', ['test1', 'test2', 'test3'])
     1187        self.expectOutcome(result=RETRY, state_string='Unable to confirm if test failures are introduced by patch, retrying build (retry)')
     1188        return self.runStep()
     1189
     1190    def test_flaky_and_consistent_failures_with_clean_tree_failures(self):
     1191        self.configureStep()
     1192        self.setProperty('first_run_failures', ['test1', 'test2'])
     1193        self.setProperty('second_run_failures', ['test1'])
     1194        self.setProperty('clean_tree_run_failures', ['test1', 'test2'])
     1195        self.expectOutcome(result=RETRY, state_string='Unable to confirm if test failures are introduced by patch, retrying build (retry)')
     1196        return self.runStep()
     1197
     1198    def test_first_run_exceed_failure_limit(self):
     1199        self.configureStep()
     1200        self.setProperty('first_results_exceed_failure_limit', True)
     1201        self.setProperty('first_run_failures',  ['test{}'.format(i) for i in range(0, 30)])
     1202        self.setProperty('second_run_failures', [])
     1203        self.expectOutcome(result=RETRY, state_string='Unable to confirm if test failures are introduced by patch, retrying build (retry)')
     1204        return self.runStep()
     1205
     1206    def test_second_run_exceed_failure_limit(self):
     1207        self.configureStep()
     1208        self.setProperty('first_run_failures', [])
     1209        self.setProperty('second_results_exceed_failure_limit', True)
     1210        self.setProperty('second_run_failures',  ['test{}'.format(i) for i in range(0, 30)])
     1211        self.expectOutcome(result=RETRY, state_string='Unable to confirm if test failures are introduced by patch, retrying build (retry)')
     1212        return self.runStep()
     1213
     1214    def test_clean_tree_exceed_failure_limit(self):
     1215        self.configureStep()
     1216        self.setProperty('first_run_failures', ['test1'])
     1217        self.setProperty('second_run_failures', ['test1'])
     1218        self.setProperty('clean_tree_results_exceed_failure_limit', True)
     1219        self.setProperty('clean_tree_run_failures',  ['test{}'.format(i) for i in range(0, 30)])
     1220        self.expectOutcome(result=RETRY, state_string='Unable to confirm if test failures are introduced by patch, retrying build (retry)')
     1221        return self.runStep()
     1222
     1223    def test_clean_tree_has_lot_of_failures(self):
     1224        self.configureStep()
     1225        self.setProperty('first_results_exceed_failure_limit', True)
     1226        self.setProperty('first_run_failures', ['test{}'.format(i) for i in range(0, 30)])
     1227        self.setProperty('second_results_exceed_failure_limit', True)
     1228        self.setProperty('second_run_failures', ['test{}'.format(i) for i in range(0, 30)])
     1229        self.setProperty('clean_tree_run_failures', ['test{}'.format(i) for i in range(0, 27)])
     1230        self.expectOutcome(result=RETRY, state_string='Unable to confirm if test failures are introduced by patch, retrying build (retry)')
     1231        return self.runStep()
     1232
     1233    def test_clean_tree_has_some_failures(self):
     1234        self.configureStep()
     1235        self.setProperty('first_results_exceed_failure_limit', True)
     1236        self.setProperty('first_run_failures', ['test{}'.format(i) for i in range(0, 30)])
     1237        self.setProperty('second_results_exceed_failure_limit', True)
     1238        self.setProperty('second_run_failures', ['test{}'.format(i) for i in range(0, 30)])
     1239        self.setProperty('clean_tree_run_failures', ['test{}'.format(i) for i in range(0, 10)])
     1240        self.expectOutcome(result=FAILURE, state_string='Found 30 new Test failures: test1, test0, test3, test2, test5, test4, test7, test6, test9, test8, test24, test25, test26, test27, test20, test21, test22, test23, test28, test29, test19, test18, test11, test10, test13, test12, test15, test14, test17, test16 (failure)')
     1241        return self.runStep()
    11371242
    11381243class TestCheckOutSpecificRevision(BuildStepMixinAdditions, unittest.TestCase):
  • trunk/Tools/ChangeLog

    r247567 r247569  
     12019-07-18  Aakash Jain  <aakash_jain@apple.com>
     2
     3        [ews-build] Add build step to AnalyzeLayoutTestsResults
     4        https://bugs.webkit.org/show_bug.cgi?id=199877
     5
     6        Reviewed by Jonathan Bedard.
     7
     8        Logic is ported from webkitpy/tool/bot/patchanalysistask.py::_retry_layout_tests()
     9
     10        * BuildSlaveSupport/ews-build/steps.py:
     11        (RunWebKitTestsWithoutPatch.evaluateCommand): invoke AnalyzeLayoutTestsResults step.
     12        (AnalyzeLayoutTestsResults): Build step to analyze layout-test results.
     13        (AnalyzeLayoutTestsResults.report_failure):
     14        (AnalyzeLayoutTestsResults.report_pre_existing_failures):
     15        (AnalyzeLayoutTestsResults.retry_build):
     16        (AnalyzeLayoutTestsResults._results_failed_different_tests):
     17        (AnalyzeLayoutTestsResults._report_flaky_tests):
     18        (AnalyzeLayoutTestsResults.start):
     19        * BuildSlaveSupport/ews-build/steps_unittest.py: Added unit-tests.
     20
    1212019-07-18  Alex Christensen  <achristensen@webkit.org>
    222
Note: See TracChangeset for help on using the changeset viewer.