| Differences between
and this patch
- a/Tools/ChangeLog +77 lines
Lines 1-3 a/Tools/ChangeLog_sec1
1
2021-10-19  Carlos Alberto Lopez Perez  <clopez@igalia.com>
2
3
        [EWS][GTK][WPE] Add a new Class for running layout tests on the EWS for the GTK and WPE ports.
4
        https://bugs.webkit.org/show_bug.cgi?id=231999
5
6
        Reviewed by NOBODY (OOPS!).
7
8
        This patch adds a new class to the EWS for running layout tests for the GTK and WPE ports.
9
        It will be used initially for the GTK port and later for the WPE one.
10
        Mac/iOS ports will continue to use the current default EWS, so no behaviour change for them.
11
12
        This new class is specially designed to:
13
        A. Work with a tree that is not always green (or even that is often quite red).
14
        B. To not report any false positive to the patch author.
15
        C. To allow patch authors to use this EWS to get new expectations for patches that need lot of new re-baselines (like a WPT import)
16
17
        The very simplified logic of how this works is the following:
18
        1. Run layout tests with patch (abort early at 500 unexpected failures)
19
        2. Run layout tests with patch 10 times for each test that failed consistently (non-flaky) on step 1.
20
        3. Run layout tests without patch 10 times for each test that failed consistently (non-flaky) on step 2.
21
22
        Then report to the patch author only the new consistent failures (tests that failed always with the patch and passed always without the patch, retrying 10 times)
23
        Any flaky test found is only reported to the bot watchers.
24
25
        For an explainer about why this is needed, and more details about the flow diagram and several design considerations, please check:
26
        https://people.igalia.com/clopez/wkbug/231999/explainer.html
27
28
        * CISupport/ews-build/config.json:
29
        * CISupport/ews-build/factories.py:
30
        (GTKTestsFactory):
31
        * CISupport/ews-build/layout_test_failures.py:
32
        (LayoutTestFailures.__init__):
33
        (LayoutTestFailures.results_from_string):
34
        (LayoutTestFailures.results_from_string.get_failing_tests):
35
        * CISupport/ews-build/steps.py:
36
        (BufferLogHeaderObserver):
37
        (BufferLogHeaderObserver.__init__):
38
        (BufferLogHeaderObserver.headerReceived):
39
        (BufferLogHeaderObserver.getHeaders):
40
        (BugzillaMixin.send_email_for_infrastructure_issue):
41
        (Trigger.propertiesToPassToTriggers):
42
        (RunWebKitTests.setLayoutTestCommand):
43
        (RunWebKitTests.start):
44
        (RunWebKitTests.commandComplete):
45
        (ReRunWebKitTests.evaluateCommand):
46
        (ReRunWebKitTests.commandComplete):
47
        (RunWebKitTestsWithoutPatch.commandComplete):
48
        (AnalyzeLayoutTestsResults.report_failure):
49
        (AnalyzeLayoutTestsResults.send_email_for_flaky_failure):
50
        (AnalyzeLayoutTestsResults.send_email_for_new_test_failures):
51
        (AnalyzeLayoutTestsResults.start):
52
        (RunWebKit1Tests.start):
53
        (RunWebKitTestsRedTree):
54
        (RunWebKitTestsRedTree._did_command_timed_out):
55
        (RunWebKitTestsRedTree.evaluateCommand):
56
        (RunWebKitTestsRepeatFailuresRedTree):
57
        (RunWebKitTestsRepeatFailuresRedTree.__init__):
58
        (RunWebKitTestsRepeatFailuresRedTree.setLayoutTestCommand):
59
        (RunWebKitTestsRepeatFailuresRedTree.evaluateCommand):
60
        (RunWebKitTestsRepeatFailuresRedTree.commandComplete):
61
        (RunWebKitTestsRepeatFailuresRedTree.start):
62
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree):
63
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.__init__):
64
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.setLayoutTestCommand):
65
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.evaluateCommand):
66
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.commandComplete):
67
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.start):
68
        (RunWebKitTestsWithoutPatchRedTree):
69
        (RunWebKitTestsWithoutPatchRedTree.evaluateCommand):
70
        (AnalyzeLayoutTestsResultsRedTree):
71
        (AnalyzeLayoutTestsResultsRedTree.report_success):
72
        (AnalyzeLayoutTestsResultsRedTree.report_warning):
73
        (AnalyzeLayoutTestsResultsRedTree.report_infrastructure_issue_and_maybe_retry_build):
74
        (AnalyzeLayoutTestsResultsRedTree.send_email_for_pre_existent_failures):
75
        (AnalyzeLayoutTestsResultsRedTree.start):
76
        * CISupport/ews-build/steps_unittest.py:
77
1
2021-10-19  Jonathan Bedard  <jbedard@apple.com>
78
2021-10-19  Jonathan Bedard  <jbedard@apple.com>
2
79
3
        [webkitscmpy] Override http url with ssh url
80
        [webkitscmpy] Override http url with ssh url
- a/Tools/CISupport/ews-build/config.json -2 / +8 lines
Lines 108-120 a/Tools/CISupport/ews-build/config.json_sec1
108
      "name": "GTK-Build-EWS", "shortname": "gtk", "icon": "buildOnly",
108
      "name": "GTK-Build-EWS", "shortname": "gtk", "icon": "buildOnly",
109
      "factory": "GTKBuildFactory", "platform": "gtk",
109
      "factory": "GTKBuildFactory", "platform": "gtk",
110
      "configuration": "release", "architectures": ["x86_64"],
110
      "configuration": "release", "architectures": ["x86_64"],
111
      "triggers": ["api-tests-gtk-ews"],
111
      "triggers": ["api-tests-gtk-ews", "gtk-wk2-tests-ews"],
112
      "workernames": ["igalia1-gtk-wk2-ews", "igalia2-gtk-wk2-ews", "aperez-gtk-ews"]
112
      "workernames": ["igalia1-gtk-wk2-ews", "igalia2-gtk-wk2-ews", "aperez-gtk-ews"]
113
    },
113
    },
114
    {
114
    {
115
      "name": "GTK-WK2-Tests-EWS", "shortname": "gtk-wk2", "icon": "testOnly",
115
      "name": "GTK-WK2-Tests-EWS", "shortname": "gtk-wk2", "icon": "testOnly",
116
      "factory": "GTKTestsFactory", "platform": "gtk",
116
      "factory": "GTKTestsFactory", "platform": "gtk",
117
      "configuration": "release", "architectures": ["x86_64"],
117
      "configuration": "release", "architectures": ["x86_64"],
118
      "triggered_by": ["gtk-build-ews"],
118
      "workernames": ["igalia5-gtk-wk2-ews", "igalia6-gtk-wk2-ews", "igalia7-gtk-wk2-ews", "igalia8-gtk-wk2-ews", "igalia9-gtk-wk2-ews"]
119
      "workernames": ["igalia5-gtk-wk2-ews", "igalia6-gtk-wk2-ews", "igalia7-gtk-wk2-ews", "igalia8-gtk-wk2-ews", "igalia9-gtk-wk2-ews"]
119
    },
120
    },
120
    {
121
    {
Lines 307-312 a/Tools/CISupport/ews-build/config.json_sec2
307
      "name": "API-Tests-GTK-EWS", "shortname": "api-gtk", "icon": "testOnly",
308
      "name": "API-Tests-GTK-EWS", "shortname": "api-gtk", "icon": "testOnly",
308
      "factory": "APITestsFactory", "platform": "gtk",
309
      "factory": "APITestsFactory", "platform": "gtk",
309
      "configuration": "release", "architectures": ["x86_64"],
310
      "configuration": "release", "architectures": ["x86_64"],
311
      "triggered_by": ["gtk-build-ews"],
310
      "workernames": ["igalia3-gtk-wk2-ews", "igalia4-gtk-wk2-ews"]
312
      "workernames": ["igalia3-gtk-wk2-ews", "igalia4-gtk-wk2-ews"]
311
    },
313
    },
312
    {
314
    {
Lines 384-395 a/Tools/CISupport/ews-build/config.json_sec3
384
      "type": "Triggerable", "name": "api-tests-mac-ews",
386
      "type": "Triggerable", "name": "api-tests-mac-ews",
385
      "builderNames": ["API-Tests-macOS-EWS"]
387
      "builderNames": ["API-Tests-macOS-EWS"]
386
    },
388
    },
389
    {
390
      "type": "Triggerable", "name": "gtk-build-ews",
391
      "builderNames": ["GTK-Build-EWS"]
392
    },
387
    {
393
    {
388
      "type": "Triggerable", "name": "api-tests-gtk-ews",
394
      "type": "Triggerable", "name": "api-tests-gtk-ews",
389
      "builderNames": ["API-Tests-GTK-EWS"]
395
      "builderNames": ["API-Tests-GTK-EWS"]
390
    },
396
    },
391
    {
397
    {
392
      "type": "Try_Userpass", "name": "gtk-wk2-tests-ews-disabled", "port": 5558,
398
      "type": "Triggerable", "name": "gtk-wk2-tests-ews",
393
      "builderNames": ["GTK-WK2-Tests-EWS"]
399
      "builderNames": ["GTK-WK2-Tests-EWS"]
394
    },
400
    },
395
    {
401
    {
- a/Tools/CISupport/ews-build/factories.py -2 / +2 lines
Lines 30-36 from steps import (ApplyPatch, ApplyWatchList, CheckOutSource, CheckOutSpecificR a/Tools/CISupport/ews-build/factories.py_sec1
30
                   InstallGtkDependencies, InstallWpeDependencies, KillOldProcesses, PrintConfiguration, PushCommitToWebKitRepo,
30
                   InstallGtkDependencies, InstallWpeDependencies, KillOldProcesses, PrintConfiguration, PushCommitToWebKitRepo,
31
                   RunAPITests, RunBindingsTests, RunBuildWebKitOrgUnitTests, RunBuildbotCheckConfigForBuildWebKit, RunBuildbotCheckConfigForEWS,
31
                   RunAPITests, RunBindingsTests, RunBuildWebKitOrgUnitTests, RunBuildbotCheckConfigForBuildWebKit, RunBuildbotCheckConfigForEWS,
32
                   RunEWSUnitTests, RunResultsdbpyTests, RunJavaScriptCoreTests, RunWebKit1Tests, RunWebKitPerlTests, RunWebKitPyPython2Tests,
32
                   RunEWSUnitTests, RunResultsdbpyTests, RunJavaScriptCoreTests, RunWebKit1Tests, RunWebKitPerlTests, RunWebKitPyPython2Tests,
33
                   RunWebKitPyPython3Tests, RunWebKitTests, RunWebKitTestsInStressMode, RunWebKitTestsInStressGuardmallocMode,
33
                   RunWebKitPyPython3Tests, RunWebKitTests, RunWebKitTestsRedTree, RunWebKitTestsInStressMode, RunWebKitTestsInStressGuardmallocMode,
34
                   SetBuildSummary, ShowIdentifier, TriggerCrashLogSubmission, UpdateWorkingDirectory,
34
                   SetBuildSummary, ShowIdentifier, TriggerCrashLogSubmission, UpdateWorkingDirectory,
35
                   ValidatePatch, ValidateChangeLogAndReviewer, ValidateCommiterAndReviewer, WaitForCrashCollection,
35
                   ValidatePatch, ValidateChangeLogAndReviewer, ValidateCommiterAndReviewer, WaitForCrashCollection,
36
                   InstallBuiltProduct, VerifyGitHubIntegrity)
36
                   InstallBuiltProduct, VerifyGitHubIntegrity)
Lines 257-263 class GTKBuildFactory(BuildFactory): a/Tools/CISupport/ews-build/factories.py_sec2
257
257
258
258
259
class GTKTestsFactory(TestFactory):
259
class GTKTestsFactory(TestFactory):
260
    LayoutTestClass = RunWebKitTests
260
    LayoutTestClass = RunWebKitTestsRedTree
261
261
262
262
263
class WPEFactory(Factory):
263
class WPEFactory(Factory):
- a/Tools/CISupport/ews-build/layout_test_failures.py -2 / +6 lines
Lines 27-34 class LayoutTestFailures(object): a/Tools/CISupport/ews-build/layout_test_failures.py_sec1
27
    _JSON_PREFIX = "ADD_RESULTS("
27
    _JSON_PREFIX = "ADD_RESULTS("
28
    _JSON_SUFFIX = ");"
28
    _JSON_SUFFIX = ");"
29
29
30
    def __init__(self, failing_tests, did_exceed_test_failure_limit):
30
    def __init__(self, failing_tests, flaky_tests, did_exceed_test_failure_limit):
31
        self.failing_tests = failing_tests
31
        self.failing_tests = failing_tests
32
        self.flaky_tests = flaky_tests
32
        self.did_exceed_test_failure_limit = did_exceed_test_failure_limit
33
        self.did_exceed_test_failure_limit = did_exceed_test_failure_limit
33
34
34
    @classmethod
35
    @classmethod
Lines 56-68 class LayoutTestFailures(object): a/Tools/CISupport/ews-build/layout_test_failures.py_sec2
56
        json_dict = json.loads(content_string)
57
        json_dict = json.loads(content_string)
57
58
58
        failing_tests = []
59
        failing_tests = []
60
        flaky_tests = []
59
61
60
        def get_failing_tests(test, result):
62
        def get_failing_tests(test, result):
61
            if result.get('report') in ['REGRESSION', 'MISSING']:
63
            if result.get('report') in ['REGRESSION', 'MISSING']:
62
                failing_tests.append(test)
64
                failing_tests.append(test)
65
            elif result.get('report') in ['FLAKY']:
66
                flaky_tests.append(test)
63
67
64
        cls.parse_full_results_json(json_dict['tests'], get_failing_tests)
68
        cls.parse_full_results_json(json_dict['tests'], get_failing_tests)
65
        return cls(failing_tests, json_dict.get('interrupted', False))
69
        return cls(failing_tests, flaky_tests, json_dict.get('interrupted', False))
66
70
67
    @classmethod
71
    @classmethod
68
    def parse_full_results_json(cls, tree, handler, prefix=''):
72
    def parse_full_results_json(cls, tree, handler, prefix=''):
- a/Tools/CISupport/ews-build/steps.py -9 / +333 lines
Lines 56-61 WithProperties = properties.WithProperties a/Tools/CISupport/ews-build/steps.py_sec1
56
Interpolate = properties.Interpolate
56
Interpolate = properties.Interpolate
57
57
58
58
59
class BufferLogHeaderObserver(logobserver.BufferLogObserver):
60
61
    def __init__(self, **kwargs):
62
        self.headers = []
63
        super(BufferLogHeaderObserver, self).__init__(**kwargs)
64
65
    def headerReceived(self, data):
66
        self.headers.append(data)
67
68
    def getHeaders(self):
69
        return self._get(self.headers)
70
71
59
class ConfigureBuild(buildstep.BuildStep):
72
class ConfigureBuild(buildstep.BuildStep):
60
    name = 'configure-build'
73
    name = 'configure-build'
61
    description = ['configuring build']
74
    description = ['configuring build']
Lines 734-739 class BugzillaMixin(object): a/Tools/CISupport/ews-build/steps.py_sec2
734
            return False
747
            return False
735
        return True
748
        return True
736
749
750
    def send_email_for_infrastructure_issue(self, infrastructure_issue_text):
751
        try:
752
            builder_name = self.getProperty('buildername', '')
753
            worker_name = self.getProperty('workername', '')
754
            build_url = '{}#/builders/{}/builds/{}'.format(self.master.config.buildbotURL, self.build._builderid, self.build.number)
755
            email_subject = 'Infrastructure issue at {}'.format(builder_name)
756
            email_text = 'The following infrastructure issue happened at:\n\n'
757
            email_text += '    - Build : {}\n'.format(build_url)
758
            email_text += '    - Builder : {}\n'.format(builder_name)
759
            email_text += '    - Worker : {}\n'.format(worker_name)
760
            email_text += '    - Issue: {}\n'.format(infrastructure_issue_text)
761
            send_email_to_bot_watchers(email_subject, email_text, builder_name, 'infrastructure-{}'.format(builder_name))
762
        except Exception as e:
763
            print('Error in sending email for infrastructure issue: {}'.format(e))
764
737
    def get_bugzilla_api_key(self):
765
    def get_bugzilla_api_key(self):
738
        try:
766
        try:
739
            passwords = json.load(open('passwords.json'))
767
            passwords = json.load(open('passwords.json'))
Lines 1170-1175 class Trigger(trigger.Trigger): a/Tools/CISupport/ews-build/steps.py_sec3
1170
            properties_to_pass['ews_revision'] = properties.Property('got_revision')
1198
            properties_to_pass['ews_revision'] = properties.Property('got_revision')
1171
        if self.triggers:
1199
        if self.triggers:
1172
            properties_to_pass['triggers'] = self.triggers
1200
            properties_to_pass['triggers'] = self.triggers
1201
        properties_to_pass['retry_count'] = properties.Property('retry_count', default=0)
1173
        return properties_to_pass
1202
        return properties_to_pass
1174
1203
1175
1204
Lines 2210-2216 class RunWebKitTests(shell.Test): a/Tools/CISupport/ews-build/steps.py_sec4
2210
        if patch_author in ['webkit-wpt-import-bot@igalia.com']:
2239
        if patch_author in ['webkit-wpt-import-bot@igalia.com']:
2211
            self.setCommand(self.command + ['imported/w3c/web-platform-tests'])
2240
            self.setCommand(self.command + ['imported/w3c/web-platform-tests'])
2212
        else:
2241
        else:
2213
            self.setCommand(self.command + ['--exit-after-n-failures', self.EXIT_AFTER_FAILURES, '--skip-failing-tests'])
2242
            if self.EXIT_AFTER_FAILURES is not None:
2243
                self.setCommand(self.command + ['--exit-after-n-failures', '{}'.format(self.EXIT_AFTER_FAILURES)])
2244
            self.setCommand(self.command + ['--skip-failing-tests'])
2214
2245
2215
        if additionalArguments:
2246
        if additionalArguments:
2216
            self.setCommand(self.command + additionalArguments)
2247
            self.setCommand(self.command + additionalArguments)
Lines 2243-2250 class RunWebKitTests(shell.Test): a/Tools/CISupport/ews-build/steps.py_sec5
2243
                    list_retry_tests = sorted(first_results_failing_tests.union(second_results_failing_tests))
2274
                    list_retry_tests = sorted(first_results_failing_tests.union(second_results_failing_tests))
2244
                    self.setCommand(self.command + list_retry_tests)
2275
                    self.setCommand(self.command + list_retry_tests)
2245
2276
2246
    def start(self):
2277
    def start(self, BufferLogObserverClass=logobserver.BufferLogObserver):
2247
        self.log_observer = logobserver.BufferLogObserver(wantStderr=True)
2278
        self.log_observer = BufferLogObserverClass(wantStderr=True)
2248
        self.addLogObserver('stdio', self.log_observer)
2279
        self.addLogObserver('stdio', self.log_observer)
2249
        self.log_observer_json = logobserver.BufferLogObserver()
2280
        self.log_observer_json = logobserver.BufferLogObserver()
2250
        self.addLogObserver('json', self.log_observer_json)
2281
        self.addLogObserver('json', self.log_observer_json)
Lines 2306-2311 class RunWebKitTests(shell.Test): a/Tools/CISupport/ews-build/steps.py_sec6
2306
        if first_results:
2337
        if first_results:
2307
            self.setProperty('first_results_exceed_failure_limit', first_results.did_exceed_test_failure_limit)
2338
            self.setProperty('first_results_exceed_failure_limit', first_results.did_exceed_test_failure_limit)
2308
            self.setProperty('first_run_failures', sorted(first_results.failing_tests))
2339
            self.setProperty('first_run_failures', sorted(first_results.failing_tests))
2340
            self.setProperty('first_run_flakies', sorted(first_results.flaky_tests))
2309
            if first_results.failing_tests:
2341
            if first_results.failing_tests:
2310
                self._addToLog(self.test_failures_log_name, '\n'.join(first_results.failing_tests))
2342
                self._addToLog(self.test_failures_log_name, '\n'.join(first_results.failing_tests))
2311
        self._parseRunWebKitTestsOutput(logText)
2343
        self._parseRunWebKitTestsOutput(logText)
Lines 2417-2422 class ReRunWebKitTests(RunWebKitTests): a/Tools/CISupport/ews-build/steps.py_sec7
2417
        first_results_failing_tests = set(self.getProperty('first_run_failures', []))
2449
        first_results_failing_tests = set(self.getProperty('first_run_failures', []))
2418
        second_results_did_exceed_test_failure_limit = self.getProperty('second_results_exceed_failure_limit')
2450
        second_results_did_exceed_test_failure_limit = self.getProperty('second_results_exceed_failure_limit')
2419
        second_results_failing_tests = set(self.getProperty('second_run_failures', []))
2451
        second_results_failing_tests = set(self.getProperty('second_run_failures', []))
2452
        # FIXME: here it can be a good idea to also use the info from second_run_flakies and first_run_flakies
2420
        tests_that_consistently_failed = first_results_failing_tests.intersection(second_results_failing_tests)
2453
        tests_that_consistently_failed = first_results_failing_tests.intersection(second_results_failing_tests)
2421
        flaky_failures = first_results_failing_tests.union(second_results_failing_tests) - first_results_failing_tests.intersection(second_results_failing_tests)
2454
        flaky_failures = first_results_failing_tests.union(second_results_failing_tests) - first_results_failing_tests.intersection(second_results_failing_tests)
2422
        flaky_failures = sorted(list(flaky_failures))[:self.NUM_FAILURES_TO_DISPLAY]
2455
        flaky_failures = sorted(list(flaky_failures))[:self.NUM_FAILURES_TO_DISPLAY]
Lines 2455-2460 class ReRunWebKitTests(RunWebKitTests): a/Tools/CISupport/ews-build/steps.py_sec8
2455
        if second_results:
2488
        if second_results:
2456
            self.setProperty('second_results_exceed_failure_limit', second_results.did_exceed_test_failure_limit)
2489
            self.setProperty('second_results_exceed_failure_limit', second_results.did_exceed_test_failure_limit)
2457
            self.setProperty('second_run_failures', sorted(second_results.failing_tests))
2490
            self.setProperty('second_run_failures', sorted(second_results.failing_tests))
2491
            self.setProperty('second_run_flakies', sorted(second_results.flaky_tests))
2458
            if second_results.failing_tests:
2492
            if second_results.failing_tests:
2459
                self._addToLog(self.test_failures_log_name, '\n'.join(second_results.failing_tests))
2493
                self._addToLog(self.test_failures_log_name, '\n'.join(second_results.failing_tests))
2460
        self._parseRunWebKitTestsOutput(logText)
2494
        self._parseRunWebKitTestsOutput(logText)
Lines 2494-2499 class RunWebKitTestsWithoutPatch(RunWebKitTests): a/Tools/CISupport/ews-build/steps.py_sec9
2494
        if clean_tree_results:
2528
        if clean_tree_results:
2495
            self.setProperty('clean_tree_results_exceed_failure_limit', clean_tree_results.did_exceed_test_failure_limit)
2529
            self.setProperty('clean_tree_results_exceed_failure_limit', clean_tree_results.did_exceed_test_failure_limit)
2496
            self.setProperty('clean_tree_run_failures', clean_tree_results.failing_tests)
2530
            self.setProperty('clean_tree_run_failures', clean_tree_results.failing_tests)
2531
            self.setProperty('clean_tree_run_flakies', sorted(clean_tree_results.flaky_tests))
2497
            if clean_tree_results.failing_tests:
2532
            if clean_tree_results.failing_tests:
2498
                self._addToLog(self.test_failures_log_name, '\n'.join(clean_tree_results.failing_tests))
2533
                self._addToLog(self.test_failures_log_name, '\n'.join(clean_tree_results.failing_tests))
2499
        self._parseRunWebKitTestsOutput(logText)
2534
        self._parseRunWebKitTestsOutput(logText)
Lines 2505-2522 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec10
2505
    descriptionDone = ['analyze-layout-tests-results']
2540
    descriptionDone = ['analyze-layout-tests-results']
2506
    NUM_FAILURES_TO_DISPLAY = 10
2541
    NUM_FAILURES_TO_DISPLAY = 10
2507
2542
2508
    def report_failure(self, new_failures):
2543
    def report_failure(self, new_failures, exceed_failure_limit=False):
2509
        self.finished(FAILURE)
2544
        self.finished(FAILURE)
2510
        self.build.results = FAILURE
2545
        self.build.results = FAILURE
2511
        if not new_failures:
2546
        if not new_failures:
2512
            message = 'Found unexpected failure with patch'
2547
            message = 'Found unexpected failure with patch'
2513
        else:
2548
        else:
2514
            pluralSuffix = 's' if len(new_failures) > 1 else ''
2549
            pluralSuffix = 's' if len(new_failures) > 1 else ''
2550
            if exceed_failure_limit:
2551
                message = 'Failure limit exceed. At least found'
2552
            else:
2553
                message = 'Found'
2515
            new_failures_string = ', '.join(sorted(new_failures)[:self.NUM_FAILURES_TO_DISPLAY])
2554
            new_failures_string = ', '.join(sorted(new_failures)[:self.NUM_FAILURES_TO_DISPLAY])
2516
            message = 'Found {} new test failure{}: {}'.format(len(new_failures), pluralSuffix, new_failures_string)
2555
            message += ' {} new test failure{}: {}'.format(len(new_failures), pluralSuffix, new_failures_string)
2517
            if len(new_failures) > self.NUM_FAILURES_TO_DISPLAY:
2556
            if len(new_failures) > self.NUM_FAILURES_TO_DISPLAY:
2518
                message += ' ...'
2557
                message += ' ...'
2519
            self.send_email_for_new_test_failures(new_failures)
2558
            self.send_email_for_new_test_failures(new_failures, exceed_failure_limit)
2520
        self.descriptionDone = message
2559
        self.descriptionDone = message
2521
        self.setProperty('build_finish_summary', message)
2560
        self.setProperty('build_finish_summary', message)
2522
2561
Lines 2573-2579 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec11
2573
    def _results_failed_different_tests(self, first_results_failing_tests, second_results_failing_tests):
2612
    def _results_failed_different_tests(self, first_results_failing_tests, second_results_failing_tests):
2574
        return first_results_failing_tests != second_results_failing_tests
2613
        return first_results_failing_tests != second_results_failing_tests
2575
2614
2576
    def send_email_for_flaky_failure(self, test_name):
2615
    def send_email_for_flaky_failure(self, test_name, step_str=None):
2577
        try:
2616
        try:
2578
            builder_name = self.getProperty('buildername', '')
2617
            builder_name = self.getProperty('buildername', '')
2579
            worker_name = self.getProperty('workername', '')
2618
            worker_name = self.getProperty('workername', '')
Lines 2582-2587 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec12
2582
2621
2583
            email_subject = 'Flaky test: {}'.format(test_name)
2622
            email_subject = 'Flaky test: {}'.format(test_name)
2584
            email_text = 'Flaky test: {}\n\nBuild: {}\n\nBuilder: {}\n\nWorker: {}\n\nHistory: {}'.format(test_name, build_url, builder_name, worker_name, history_url)
2623
            email_text = 'Flaky test: {}\n\nBuild: {}\n\nBuilder: {}\n\nWorker: {}\n\nHistory: {}'.format(test_name, build_url, builder_name, worker_name, history_url)
2624
            if step_str:
2625
                email_text += '\nThis test was flaky on the steps: {}'.format(step_str)
2585
            send_email_to_bot_watchers(email_subject, email_text, builder_name, 'flaky-{}'.format(test_name))
2626
            send_email_to_bot_watchers(email_subject, email_text, builder_name, 'flaky-{}'.format(test_name))
2586
        except Exception as e:
2627
        except Exception as e:
2587
            print('Error in sending email for flaky failure: {}'.format(e))
2628
            print('Error in sending email for flaky failure: {}'.format(e))
Lines 2599-2605 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec13
2599
        except Exception as e:
2640
        except Exception as e:
2600
            print('Error in sending email for pre-existing failure: {}'.format(e))
2641
            print('Error in sending email for pre-existing failure: {}'.format(e))
2601
2642
2602
    def send_email_for_new_test_failures(self, test_names):
2643
    def send_email_for_new_test_failures(self, test_names, exceed_failure_limit=False):
2603
        try:
2644
        try:
2604
            patch_id = self.getProperty('patch_id', '')
2645
            patch_id = self.getProperty('patch_id', '')
2605
            if not self.should_send_email(patch_id):
2646
            if not self.should_send_email(patch_id):
Lines 2621-2626 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec14
2621
            email_text += ' while testing <a href="{}">Patch {}</a>'.format(Bugzilla.patch_url(patch_id), patch_id)
2662
            email_text += ' while testing <a href="{}">Patch {}</a>'.format(Bugzilla.patch_url(patch_id), patch_id)
2622
            email_text += ' for <a href="{}">Bug {}</a>.'.format(Bugzilla.bug_url(bug_id), bug_id)
2663
            email_text += ' for <a href="{}">Bug {}</a>.'.format(Bugzilla.bug_url(bug_id), bug_id)
2623
            email_text += '\n\nFull details are available at: {}\n\nPatch author: {}'.format(build_url, patch_author)
2664
            email_text += '\n\nFull details are available at: {}\n\nPatch author: {}'.format(build_url, patch_author)
2665
            if exceed_failure_limit:
2666
                email_text += '\n\nAditionally the failure limit has been exceeded, so the test suite has been terminated early. It is likely that there would be more failures than the ones listed below.'
2624
            email_text += '\n\nLayout test failure{}:\n{}'.format(pluralSuffix, test_names_string)
2667
            email_text += '\n\nLayout test failure{}:\n{}'.format(pluralSuffix, test_names_string)
2625
            email_text += '\n\nTo unsubscrible from these notifications or to provide any feedback please email aakash_jain@apple.com'
2668
            email_text += '\n\nTo unsubscrible from these notifications or to provide any feedback please email aakash_jain@apple.com'
2626
            self._addToLog('stdio', 'Sending email notification to {}'.format(patch_author))
2669
            self._addToLog('stdio', 'Sending email notification to {}'.format(patch_author))
Lines 2647-2653 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec15
2647
            clean_tree_run_status = self.getProperty('clean_tree_run_status', FAILURE)
2690
            clean_tree_run_status = self.getProperty('clean_tree_run_status', FAILURE)
2648
            if clean_tree_run_status == SUCCESS:
2691
            if clean_tree_run_status == SUCCESS:
2649
                return self.report_failure(set())
2692
                return self.report_failure(set())
2650
            # TODO: email EWS admins
2693
            self.send_email_for_infrastructure_issue('Both first and second layout-test runs with patch generated no list of results but exited with error, and the clean_tree without patch retry also failed.')
2651
            return self.retry_build('Unexpected infrastructure issue, retrying build')
2694
            return self.retry_build('Unexpected infrastructure issue, retrying build')
2652
2695
2653
        if first_results_did_exceed_test_failure_limit and second_results_did_exceed_test_failure_limit:
2696
        if first_results_did_exceed_test_failure_limit and second_results_did_exceed_test_failure_limit:
Lines 2674-2679 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec16
2674
                return self.report_failure(failures_introduced_by_patch)
2717
                return self.report_failure(failures_introduced_by_patch)
2675
            return self.retry_build()
2718
            return self.retry_build()
2676
2719
2720
        # FIXME: Here it could be a good idea to also use the info of results.flaky_tests from the runs
2677
        if self._results_failed_different_tests(first_results_failing_tests, second_results_failing_tests):
2721
        if self._results_failed_different_tests(first_results_failing_tests, second_results_failing_tests):
2678
            tests_that_only_failed_first = first_results_failing_tests.difference(second_results_failing_tests)
2722
            tests_that_only_failed_first = first_results_failing_tests.difference(second_results_failing_tests)
2679
            self._report_flaky_tests(tests_that_only_failed_first)
2723
            self._report_flaky_tests(tests_that_only_failed_first)
Lines 2712-2717 class RunWebKit1Tests(RunWebKitTests): a/Tools/CISupport/ews-build/steps.py_sec17
2712
        return RunWebKitTests.start(self)
2756
        return RunWebKitTests.start(self)
2713
2757
2714
2758
2759
# This is a specialized class designed to cope with a tree that is not always green.
2760
# It tries hard to avoid reporting any false positive, so it will only report new
2761
# consistent failures (fail always with the patch and pass always without it).
2762
class RunWebKitTestsRedTree(RunWebKitTests):
2763
    EXIT_AFTER_FAILURES = 500
2764
2765
    def _did_command_timed_out(self, logHeadersText):
2766
        timed_out_line_start = 'command timed out: {} seconds elapsed running'.format(self.MAX_SECONDS_STEP_RUN)
2767
        timed_out_line_end = 'attempting to kill'
2768
        for line in logHeadersText.splitlines():
2769
            line = line.strip()
2770
            if line.startswith(timed_out_line_start) and line.endswith(timed_out_line_end):
2771
                return True
2772
        return False
2773
2774
    def evaluateCommand(self, cmd):
2775
        first_results_failing_tests = set(self.getProperty('first_run_failures', []))
2776
        first_results_flaky_tests = set(self.getProperty('first_run_flakies', []))
2777
        rc = self.evaluateResult(cmd)
2778
        next_steps = [ArchiveTestResults(), UploadTestResults(), ExtractTestResults()]
2779
        if first_results_failing_tests:
2780
            next_steps.extend([ValidatePatch(verifyBugClosed=False, addURLs=False), KillOldProcesses(), RunWebKitTestsRepeatFailuresRedTree()])
2781
        elif first_results_flaky_tests:
2782
            next_steps.append(AnalyzeLayoutTestsResultsRedTree())
2783
        elif rc == SUCCESS or rc == WARNINGS:
2784
            next_steps = None
2785
            message = 'Passed layout tests'
2786
            self.descriptionDone = message
2787
            self.build.results = SUCCESS
2788
            self.setProperty('build_summary', message)
2789
        else:
2790
            # We have a failure return code, but not a list of failed or flaky tests.
2791
            # So retry re-running the _whole_ layout tests without-patch to see if
2792
            # this unexpected failure was pre-existent. If the failure was not pre-existent,
2793
            # then we would not report a list of failed test, just a generic "unknown" failure.
2794
            self.setProperty('patchFailedTests', True)
2795
            next_steps.extend([UnApplyPatchIfRequired(), CompileWebKitWithoutPatch(retry_build_on_failure=True), ValidatePatch(verifyBugClosed=False, addURLs=False), RunWebKitTestsWithoutPatchRedTree()])
2796
        if next_steps:
2797
            self.build.addStepsAfterCurrentStep(next_steps)
2798
        return rc
2799
2800
2801
class RunWebKitTestsRepeatFailuresRedTree(RunWebKitTestsRedTree):
2802
    name = 'layout-tests-repeat-failures'
2803
    NUM_REPEATS_PER_TEST = 10
2804
    EXIT_AFTER_FAILURES = None
2805
    MAX_SECONDS_STEP_RUN = 18000  # 5h
2806
2807
    def __init__(self, **kwargs):
2808
        super(RunWebKitTestsRepeatFailuresRedTree, self).__init__(maxTime=self.MAX_SECONDS_STEP_RUN, **kwargs)
2809
2810
    def setLayoutTestCommand(self):
2811
        super(RunWebKitTestsRepeatFailuresRedTree, self).setLayoutTestCommand()
2812
        first_results_failing_tests = set(self.getProperty('first_run_failures', []))
2813
        self.setCommand(self.command + ['--repeat-each=%s' % self.NUM_REPEATS_PER_TEST] + sorted(first_results_failing_tests))
2814
2815
    def evaluateCommand(self, cmd):
2816
        with_patch_repeat_failures_results_nonflaky_failures = set(self.getProperty('with_patch_repeat_failures_results_nonflaky_failures', []))
2817
        with_patch_repeat_failures_results_flakies = set(self.getProperty('with_patch_repeat_failures_results_flakies', []))
2818
        with_patch_repeat_failures_timedout = self.getProperty('with_patch_repeat_failures_timedout', False)
2819
        first_results_flaky_tests = set(self.getProperty('first_run_flakies', []))
2820
        rc = self.evaluateResult(cmd)
2821
        self.setProperty('with_patch_repeat_failures_retcode', rc)
2822
        next_steps = [ArchiveTestResults(), UploadTestResults(identifier='repeat-failures'), ExtractTestResults(identifier='repeat-failures')]
2823
        if with_patch_repeat_failures_results_nonflaky_failures or with_patch_repeat_failures_timedout:
2824
            self.setProperty('patchFailedTests', True)
2825
            next_steps.extend([ValidatePatch(verifyBugClosed=False, addURLs=False), KillOldProcesses(), UnApplyPatchIfRequired(), CompileWebKitWithoutPatch(retry_build_on_failure=True),
2826
                               ValidatePatch(verifyBugClosed=False, addURLs=False), RunWebKitTestsRepeatFailuresWithoutPatchRedTree()])
2827
        else:
2828
            next_steps.append(AnalyzeLayoutTestsResultsRedTree())
2829
        if next_steps:
2830
            self.build.addStepsAfterCurrentStep(next_steps)
2831
        return rc
2832
2833
    def commandComplete(self, cmd):
2834
        shell.Test.commandComplete(self, cmd)
2835
        logText = self.log_observer.getStdout() + self.log_observer.getStderr()
2836
        logTextJson = self.log_observer_json.getStdout()
2837
        with_patch_repeat_failures_results = LayoutTestFailures.results_from_string(logTextJson)
2838
        if with_patch_repeat_failures_results:
2839
            self.setProperty('with_patch_repeat_failures_results_exceed_failure_limit', with_patch_repeat_failures_results.did_exceed_test_failure_limit)
2840
            self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', sorted(with_patch_repeat_failures_results.failing_tests))
2841
            self.setProperty('with_patch_repeat_failures_results_flakies', sorted(with_patch_repeat_failures_results.flaky_tests))
2842
            if with_patch_repeat_failures_results.failing_tests:
2843
                self._addToLog(self.test_failures_log_name, '\n'.join(with_patch_repeat_failures_results.failing_tests))
2844
        command_timedout = self._did_command_timed_out(self.log_observer.getHeaders())
2845
        self.setProperty('with_patch_repeat_failures_timedout', command_timedout)
2846
        self._parseRunWebKitTestsOutput(logText)
2847
2848
    def start(self):
2849
        # buildbot messages about timeout reached appear on the header stream of BufferLog
2850
        return super(RunWebKitTestsRepeatFailuresRedTree, self).start(BufferLogObserverClass=BufferLogHeaderObserver)
2851
2852
2853
class RunWebKitTestsRepeatFailuresWithoutPatchRedTree(RunWebKitTestsRedTree):
2854
    name = 'layout-tests-repeat-failures-without-patch'
2855
    NUM_REPEATS_PER_TEST = 10
2856
    EXIT_AFTER_FAILURES = None
2857
    MAX_SECONDS_STEP_RUN = 10800  # 3h
2858
2859
    def __init__(self, **kwargs):
2860
        super(RunWebKitTestsRepeatFailuresWithoutPatchRedTree, self).__init__(maxTime=self.MAX_SECONDS_STEP_RUN, **kwargs)
2861
2862
    def setLayoutTestCommand(self):
2863
        super(RunWebKitTestsRepeatFailuresWithoutPatchRedTree, self).setLayoutTestCommand()
2864
        with_patch_nonflaky_failures = set(self.getProperty('with_patch_repeat_failures_results_nonflaky_failures', []))
2865
        first_run_failures = set(self.getProperty('first_run_failures', []))
2866
        with_patch_repeat_failures_timedout = self.getProperty('with_patch_repeat_failures_timedout', False)
2867
        failures_to_repeat = first_run_failures if with_patch_repeat_failures_timedout else with_patch_nonflaky_failures
2868
        self.setProperty('xdebug_timeout_previous', with_patch_repeat_failures_timedout)
2869
        # Pass '--skipped=always' to ensure that any test passed via command line arguments
2870
        # is skipped anyways if is marked as such on the Expectation files or if is marked
2871
        # as failure (since we are passing also '--skip-failing-tests'). That way we ensure
2872
        # to report the case of a patch removing an expectation that still fails with it.
2873
        self.setCommand(self.command + ['--repeat-each=%s' % self.NUM_REPEATS_PER_TEST, '--skipped=always'] + sorted(failures_to_repeat))
2874
2875
    def evaluateCommand(self, cmd):
2876
        rc = shell.Test.evaluateCommand(self, cmd)
2877
        self.build.addStepsAfterCurrentStep([ArchiveTestResults(), UploadTestResults(identifier='repeat-failures-without-patch'), ExtractTestResults(identifier='repeat-failures-without-patch'), AnalyzeLayoutTestsResultsRedTree()])
2878
        return rc
2879
2880
    def commandComplete(self, cmd):
2881
        shell.Test.commandComplete(self, cmd)
2882
        logText = self.log_observer.getStdout() + self.log_observer.getStderr()
2883
        logTextJson = self.log_observer_json.getStdout()
2884
        without_patch_repeat_failures_results = LayoutTestFailures.results_from_string(logTextJson)
2885
        if without_patch_repeat_failures_results:
2886
            self.setProperty('without_patch_repeat_failures_results_exceed_failure_limit', without_patch_repeat_failures_results.did_exceed_test_failure_limit)
2887
            self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', sorted(without_patch_repeat_failures_results.failing_tests))
2888
            self.setProperty('without_patch_repeat_failures_results_flakies', sorted(without_patch_repeat_failures_results.flaky_tests))
2889
            if without_patch_repeat_failures_results.failing_tests:
2890
                self._addToLog(self.test_failures_log_name, '\n'.join(without_patch_repeat_failures_results.failing_tests))
2891
        command_timedout = self._did_command_timed_out(self.log_observer.getHeaders())
2892
        self.setProperty('without_patch_repeat_failures_timedout', command_timedout)
2893
        self._parseRunWebKitTestsOutput(logText)
2894
2895
    def start(self):
2896
        # buildbot messages about timeout reached appear on the header stream of BufferLog
2897
        return super(RunWebKitTestsRepeatFailuresWithoutPatchRedTree, self).start(BufferLogObserverClass=BufferLogHeaderObserver)
2898
2899
2900
class RunWebKitTestsWithoutPatchRedTree(RunWebKitTestsWithoutPatch):
2901
    EXIT_AFTER_FAILURES = 500
2902
2903
    def evaluateCommand(self, cmd):
2904
        rc = shell.Test.evaluateCommand(self, cmd)
2905
        self.build.addStepsAfterCurrentStep([ArchiveTestResults(), UploadTestResults(identifier='clean-tree'), ExtractTestResults(identifier='clean-tree'), AnalyzeLayoutTestsResultsRedTree()])
2906
        self.setProperty('clean_tree_run_status', rc)
2907
        return rc
2908
2909
2910
class AnalyzeLayoutTestsResultsRedTree(AnalyzeLayoutTestsResults):
2911
    MAX_RETRY = 3
2912
2913
    def report_success(self):
2914
        self.finished(SUCCESS)
2915
        self.build.results = SUCCESS
2916
        self.descriptionDone = 'Passed layout tests'
2917
        message = ''
2918
        self.setProperty('build_summary', message)
2919
        return defer.succeed(None)
2920
2921
    def report_warning(self, message):
2922
        self.finished(WARNINGS)
2923
        self.build.results = WARNINGS
2924
        self.descriptionDone = message
2925
        self.setProperty('build_summary', message)
2926
        return defer.succeed(None)
2927
2928
    def report_infrastructure_issue_and_maybe_retry_build(self, message):
2929
        retry_count = int(self.getProperty('retry_count', 0))
2930
        if retry_count >= self.MAX_RETRY:
2931
            message += '\nReached the maximum number of retries ({}). Unable to determine if patch is bad or there is a pre-existent infrastructure issue.'.format(self.MAX_RETRY)
2932
            self.send_email_for_infrastructure_issue(message)
2933
            return self.report_warning(message)
2934
        message += "\nRetrying build [retry count is {} of {}]".format(retry_count, self.MAX_RETRY)
2935
        self.setProperty('retry_count', retry_count + 1)
2936
        self.send_email_for_infrastructure_issue(message)
2937
        return self.retry_build(message='Unexpected infrastructure issue: {}'.format(message))
2938
2939
    def send_email_for_pre_existent_failures(self, test_names):
2940
        try:
2941
            builder_name = self.getProperty('buildername', '')
2942
            worker_name = self.getProperty('workername', '')
2943
            build_url = '{}#/builders/{}/builds/{}'.format(self.master.config.buildbotURL, self.build._builderid, self.build.number)
2944
            number_failures = len(test_names)
2945
            pluralSuffix = 's' if number_failures > 1 else ''
2946
2947
            email_subject = 'Info about {} pre-existent failure{} at {}'.format(number_failures, pluralSuffix, builder_name)
2948
            email_test = 'Info about pre-existent (non-flaky) test failure{} at EWS:\n'.format(pluralSuffix)
2949
            email_text = '    - Build : {}\n'.format(build_url)
2950
            email_text = '    - Builder : {}\n'.format(builder_name)
2951
            email_text = '    - Worker : {}\n'.format(worker_name)
2952
            for test_name in sorted(test_names):
2953
                history_url = '{}?suite=layout-tests&test={}'.format(RESULTS_DB_URL, test_name)
2954
                email_text += '\n- {} (<a href="{}">test history</a>)'.format(test_name, history_url)
2955
            send_email_to_bot_watchers(email_subject, email_text, builder_name, 'preexisting-{}'.format(test_name))
2956
        except Exception as e:
2957
            print('Error in sending email for flaky failure: {}'.format(e))
2958
2959
    def start(self):
2960
        # Run with patch, running the whole layout test suite
2961
        first_results_exceed_failure_limit = self.getProperty('first_results_exceed_failure_limit', False)
2962
        first_run_failures = set(self.getProperty('first_run_failures', []))
2963
        first_run_flakies = set(self.getProperty('first_run_flakies', []))
2964
2965
        # Run with patch, running first_run_failures 10 times each test
2966
        with_patch_repeat_failures_results_exceed_failure_limit = self.getProperty('with_patch_repeat_failures_results_exceed_failure_limit', False)
2967
        with_patch_repeat_failures_results_nonflaky_failures = set(self.getProperty('with_patch_repeat_failures_results_nonflaky_failures', []))
2968
        with_patch_repeat_failures_results_flakies = set(self.getProperty('with_patch_repeat_failures_results_flakies', []))
2969
        with_patch_repeat_failures_timedout = self.getProperty('with_patch_repeat_failures_timedout', False)
2970
2971
        # Run without patch, running with_patch_repeat_failures_results_nonflaky_failures 10 times each test
2972
        without_patch_repeat_failures_results_exceed_failure_limit = self.getProperty('without_patch_repeat_failures_results_exceed_failure_limit', False)
2973
        without_patch_repeat_failures_results_nonflaky_failures = set(self.getProperty('without_patch_repeat_failures_results_nonflaky_failures', []))
2974
        without_patch_repeat_failures_results_flakies = set(self.getProperty('without_patch_repeat_failures_results_flakies', []))
2975
        without_patch_repeat_failures_timedout = self.getProperty('without_patch_repeat_failures_timedout', False)
2976
2977
        # Check for unexpected situations and warn EWS bot watchers or author potentially retrying the build.
2978
        if (not first_run_failures) and (not first_run_flakies):
2979
            # If we've made it here, the first_run failed, which means there should be either test failures or flakies
2980
            # Otherwise there is some unexpected issue.
2981
            # The clean tree run that only happens when the first run gives error code without generating a list of failures or flakies
2982
            clean_tree_run_failures = set(self.getProperty('clean_tree_run_failures', []))
2983
            clean_tree_run_flakies = set(self.getProperty('clean_tree_run_flakies', []))
2984
            clean_tree_run_status = self.getProperty('clean_tree_run_status', FAILURE)
2985
2986
            # If the clean-tree run generated some results then we assume this patch broke the script run-webkit-tests or something like that
2987
            if (clean_tree_run_status in [SUCCESS, WARNINGS]) or clean_tree_run_failures or clean_tree_run_flakies:
2988
                return self.report_failure(set(), first_results_exceed_failure_limit)
2989
            return self.report_infrastructure_issue_and_maybe_retry_build('The layout-test run with patch generated no list of results but exited with error, and the clean_tree without patch run did the samething.')
2990
2991
        if with_patch_repeat_failures_results_exceed_failure_limit or without_patch_repeat_failures_results_exceed_failure_limit:
2992
            return self.report_infrastructure_issue_and_maybe_retry_build('One of the steps for retrying the failed tests has exited early, but this steps should run without "--exit-after-n-failures" switch, so they should not exit early.')
2993
2994
        if without_patch_repeat_failures_timedout:
2995
            return self.report_infrastructure_issue_and_maybe_retry_build('The step "layout-tests-repeat-failures-without-patch" was interrumped because it reached the timeout.')
2996
2997
        if with_patch_repeat_failures_timedout:
2998
            # The patch is causing the step 'layout-tests-repeat-failures-with-patch" to timeout (but the step 'layout-tests-repeat-failures-without-patch' doesn't timeout)
2999
            # Likely the patch is adding so many failures that is causing the timeout to be reached. There is no point in repeating this run (it would happen the same on next runs and consume lot of time)
3000
            # Mark the patch as bad and report the tests that failured on the first run (without the ones that passed on the repeat).
3001
            # This may cause some flaky tests to be reported as false positives (since the step to discard flakies with patch couldn't finish), but is the best we can do here.
3002
            likely_new_non_flaky_failures = first_run_failures - without_patch_repeat_failures_results_nonflaky_failures.union(without_patch_repeat_failures_results_flakies)
3003
            self.send_email_for_infrastructure_issue('The step "layout-tests-repeat-failures-with-patch" reached the timeout but the step "layout-tests-repeat-failures-without-patch" ended. Not trying to repeat this. Reporting {} failures from the first run.'.format(len(likely_new_non_flaky_failures)))
3004
            return self.report_failure(likely_new_non_flaky_failures, first_results_exceed_failure_limit)
3005
3006
        # The step with_patch_repeat_failures generated no list of failures or flakies, which should only happen when the return code of the step is SUCESS or WARNINGS.
3007
        if not with_patch_repeat_failures_results_nonflaky_failures and not with_patch_repeat_failures_results_flakies:
3008
            with_patch_repeat_failures_retcode = self.getProperty('with_patch_repeat_failures_retcode', FAILURE)
3009
            if with_patch_repeat_failures_retcode not in [SUCCESS, WARNINGS]:
3010
                return self.report_infrastructure_issue_and_maybe_retry_build('The step "layout-tests-repeat-failures" failed to generate any list of failures or flakies and returned an error code.')
3011
3012
        # Warn EWS bot watchers about flakies
3013
        all_flaky_failures = first_run_flakies.union(with_patch_repeat_failures_results_flakies).union(without_patch_repeat_failures_results_flakies)
3014
        for flaky_failure in all_flaky_failures:
3015
            step_names = []
3016
            if flaky_failure in without_patch_repeat_failures_results_flakies:
3017
                step_names.append('layout-tests-repeat-failures-without-patch')
3018
            if flaky_failure in with_patch_repeat_failures_results_flakies:
3019
                step_names.append('layout-tests-repeat-failures (with patch)')
3020
            if flaky_failure in first_run_flakies:
3021
                step_names.append('layout-tests (with patch)')
3022
            step_names_str = '"{}"'.format('", "'.join(step_names))
3023
            self.send_email_for_flaky_failure(flaky_failure, step_names_str)
3024
3025
        # Warn EWS bot watchers about pre-existent non-flaky failures (if any), but
3026
        # send only one e-mail with all the tests to avoid sending too much e-mails.
3027
        pre_existent_non_flaky_failures = without_patch_repeat_failures_results_nonflaky_failures - with_patch_repeat_failures_results_nonflaky_failures.union(all_flaky_failures)
3028
        if pre_existent_non_flaky_failures:
3029
            self.send_email_for_pre_existent_failures(pre_existent_non_flaky_failures)
3030
3031
        # Finally check if there are new consitent (non-flaky) failures caused by the patch and warn the patch author settting the status for the build.
3032
        new_non_flaky_failures = with_patch_repeat_failures_results_nonflaky_failures - without_patch_repeat_failures_results_nonflaky_failures.union(without_patch_repeat_failures_results_flakies)
3033
        if new_non_flaky_failures:
3034
            return self.report_failure(new_non_flaky_failures, first_results_exceed_failure_limit)
3035
3036
        return self.report_success()
3037
3038
2715
class ArchiveBuiltProduct(shell.ShellCommand):
3039
class ArchiveBuiltProduct(shell.ShellCommand):
2716
    command = ['python3', 'Tools/CISupport/built-product-archive',
3040
    command = ['python3', 'Tools/CISupport/built-product-archive',
2717
               WithProperties('--platform=%(fullPlatform)s'), WithProperties('--%(configuration)s'), 'archive']
3041
               WithProperties('--platform=%(fullPlatform)s'), WithProperties('--%(configuration)s'), 'archive']
- a/Tools/CISupport/ews-build/steps_unittest.py -6 / +333 lines
Lines 53-59 from steps import (AnalyzeAPITestsResults, AnalyzeCompileWebKitResults, AnalyzeJ a/Tools/CISupport/ews-build/steps_unittest.py_sec1
53
                   RunBuildbotCheckConfigForBuildWebKit, RunBuildbotCheckConfigForEWS, RunEWSUnitTests, RunResultsdbpyTests,
53
                   RunBuildbotCheckConfigForBuildWebKit, RunBuildbotCheckConfigForEWS, RunEWSUnitTests, RunResultsdbpyTests,
54
                   RunJavaScriptCoreTests, RunJSCTestsWithoutPatch, RunWebKit1Tests, RunWebKitPerlTests, RunWebKitPyPython2Tests,
54
                   RunJavaScriptCoreTests, RunJSCTestsWithoutPatch, RunWebKit1Tests, RunWebKitPerlTests, RunWebKitPyPython2Tests,
55
                   RunWebKitPyPython3Tests, RunWebKitTests, RunWebKitTestsInStressMode, RunWebKitTestsInStressGuardmallocMode,
55
                   RunWebKitPyPython3Tests, RunWebKitTests, RunWebKitTestsInStressMode, RunWebKitTestsInStressGuardmallocMode,
56
                   RunWebKitTestsWithoutPatch, TestWithFailureCount, ShowIdentifier,
56
                   RunWebKitTestsWithoutPatch, RunWebKitTestsRedTree, RunWebKitTestsRepeatFailuresRedTree, RunWebKitTestsRepeatFailuresWithoutPatchRedTree,
57
                   RunWebKitTestsWithoutPatchRedTree, AnalyzeLayoutTestsResultsRedTree, TestWithFailureCount, ShowIdentifier,
57
                   Trigger, TransferToS3, UnApplyPatchIfRequired, UpdateWorkingDirectory, UploadBuiltProduct,
58
                   Trigger, TransferToS3, UnApplyPatchIfRequired, UpdateWorkingDirectory, UploadBuiltProduct,
58
                   UploadTestResults, ValidateChangeLogAndReviewer, ValidateCommiterAndReviewer, ValidatePatch, VerifyGitHubIntegrity)
59
                   UploadTestResults, ValidateChangeLogAndReviewer, ValidateCommiterAndReviewer, ValidatePatch, VerifyGitHubIntegrity)
59
60
Lines 1601-1608 class TestAnalyzeJSCTestsResults(BuildStepMixinAdditions, unittest.TestCase): a/Tools/CISupport/ews-build/steps_unittest.py_sec2
1601
        self.setProperty('jsc_rerun_binary_failures', [])
1602
        self.setProperty('jsc_rerun_binary_failures', [])
1602
        self.setProperty('jsc_clean_tree_stress_test_failures', [])
1603
        self.setProperty('jsc_clean_tree_stress_test_failures', [])
1603
        self.setProperty('jsc_clean_tree_binary_failures', [])
1604
        self.setProperty('jsc_clean_tree_binary_failures', [])
1604
        AnalyzeJSCTestsResults.send_email_for_flaky_failure = lambda self, test: None
1605
        AnalyzeJSCTestsResults.send_email_for_flaky_failure = lambda *args: None
1605
        AnalyzeJSCTestsResults.send_email_for_pre_existing_failure = lambda self, test: None
1606
        AnalyzeJSCTestsResults.send_email_for_pre_existing_failure = lambda *args: None
1606
1607
1607
    def test_single_new_stress_failure(self):
1608
    def test_single_new_stress_failure(self):
1608
        self.configureStep()
1609
        self.configureStep()
Lines 1951-1957 class TestReRunWebKitTests(TestRunWebKitTests): a/Tools/CISupport/ews-build/steps_unittest.py_sec3
1951
        self.setupStep(ReRunWebKitTests())
1952
        self.setupStep(ReRunWebKitTests())
1952
        self.property_exceed_failure_limit = 'second_results_exceed_failure_limit'
1953
        self.property_exceed_failure_limit = 'second_results_exceed_failure_limit'
1953
        self.property_failures = 'second_run_failures'
1954
        self.property_failures = 'second_run_failures'
1954
        ReRunWebKitTests.send_email_for_flaky_failure = lambda self, test: None
1955
        ReRunWebKitTests.send_email_for_flaky_failure = lambda *args: None
1955
1956
1956
    def test_flaky_failures_in_first_run(self):
1957
    def test_flaky_failures_in_first_run(self):
1957
        self.configureStep()
1958
        self.configureStep()
Lines 2351-2358 class TestAnalyzeLayoutTestsResults(BuildStepMixinAdditions, unittest.TestCase): a/Tools/CISupport/ews-build/steps_unittest.py_sec4
2351
        return self.tearDownBuildStep()
2352
        return self.tearDownBuildStep()
2352
2353
2353
    def configureStep(self):
2354
    def configureStep(self):
2354
        AnalyzeLayoutTestsResults.send_email_for_flaky_failure = lambda self, test: None
2355
        AnalyzeLayoutTestsResults.send_email_for_flaky_failure = lambda *args: None
2355
        AnalyzeLayoutTestsResults.send_email_for_pre_existing_failure = lambda self, test: None
2356
        AnalyzeLayoutTestsResults.send_email_for_pre_existing_failure = lambda *args: None
2356
        self.setupStep(AnalyzeLayoutTestsResults())
2357
        self.setupStep(AnalyzeLayoutTestsResults())
2357
        self.setProperty('first_results_exceed_failure_limit', False)
2358
        self.setProperty('first_results_exceed_failure_limit', False)
2358
        self.setProperty('second_results_exceed_failure_limit', False)
2359
        self.setProperty('second_results_exceed_failure_limit', False)
Lines 2543-2548 class TestAnalyzeLayoutTestsResults(BuildStepMixinAdditions, unittest.TestCase): a/Tools/CISupport/ews-build/steps_unittest.py_sec5
2543
        return self.runStep()
2544
        return self.runStep()
2544
2545
2545
2546
2547
class TestRunWebKitTestsRedTree(BuildStepMixinAdditions, unittest.TestCase):
2548
    def setUp(self):
2549
        self.longMessage = True
2550
        self.jsonFileName = 'layout-test-results/full_results.json'
2551
        return self.setUpBuildStep()
2552
2553
    def tearDown(self):
2554
        return self.tearDownBuildStep()
2555
2556
    def configureStep(self):
2557
        self.setupStep(RunWebKitTestsRedTree())
2558
2559
    def test_success(self):
2560
        self.configureStep()
2561
        self.setProperty('fullPlatform', 'gtk')
2562
        self.setProperty('configuration', 'release')
2563
        self.expectRemoteCommands(
2564
            ExpectShell(workdir='wkdir',
2565
                        logfiles={'json': self.jsonFileName},
2566
                        logEnviron=False,
2567
                        command=['python',
2568
                                 'Tools/Scripts/run-webkit-tests',
2569
                                 '--no-build', '--no-show-results', '--no-new-test-results', '--clobber-old-results',
2570
                                 '--release', '--results-directory', 'layout-test-results', '--debug-rwt-logging',
2571
                                 '--exit-after-n-failures', '500', '--skip-failing-tests']
2572
                        )
2573
            + 0,
2574
        )
2575
        self.expectOutcome(result=SUCCESS, state_string='Passed layout tests')
2576
        return self.runStep()
2577
2578
2579
class TestRunWebKitTestsRepeatFailuresRedTree(BuildStepMixinAdditions, unittest.TestCase):
2580
    def setUp(self):
2581
        self.longMessage = True
2582
        self.jsonFileName = 'layout-test-results/full_results.json'
2583
        return self.setUpBuildStep()
2584
2585
    def tearDown(self):
2586
        return self.tearDownBuildStep()
2587
2588
    def configureStep(self):
2589
        self.setupStep(RunWebKitTestsRepeatFailuresRedTree())
2590
2591
    def test_success(self):
2592
        self.configureStep()
2593
        self.setProperty('fullPlatform', 'gtk')
2594
        self.setProperty('configuration', 'release')
2595
        first_run_failures = ['fast/css/test1.html', 'imported/test/test2.html', 'fast/svg/test3.svg']
2596
        first_run_flakies = ['fast/css/flaky1.html', 'imported/test/flaky2.html', 'fast/svg/flaky3.svg']
2597
        self.setProperty('first_run_failures', first_run_failures)
2598
        self.setProperty('first_run_flakies', first_run_flakies)
2599
        self.expectRemoteCommands(
2600
            ExpectShell(workdir='wkdir',
2601
                        logfiles={'json': self.jsonFileName},
2602
                        logEnviron=False,
2603
                        maxTime=18000,
2604
                        command=['python',
2605
                                 'Tools/Scripts/run-webkit-tests',
2606
                                 '--no-build', '--no-show-results', '--no-new-test-results', '--clobber-old-results',
2607
                                 '--release', '--results-directory', 'layout-test-results', '--debug-rwt-logging',
2608
                                 '--skip-failing-tests', '--repeat-each=10'] + sorted(first_run_failures)
2609
                        )
2610
            + 0,
2611
        )
2612
        self.expectOutcome(result=SUCCESS, state_string='layout-tests')
2613
        return self.runStep()
2614
2615
2616
class TestRunWebKitTestsRepeatFailuresWithoutPatchRedTree(BuildStepMixinAdditions, unittest.TestCase):
2617
    def setUp(self):
2618
        self.longMessage = True
2619
        self.jsonFileName = 'layout-test-results/full_results.json'
2620
        return self.setUpBuildStep()
2621
2622
    def tearDown(self):
2623
        return self.tearDownBuildStep()
2624
2625
    def configureStep(self):
2626
        self.setupStep(RunWebKitTestsRepeatFailuresWithoutPatchRedTree())
2627
2628
    def test_success(self):
2629
        self.configureStep()
2630
        self.setProperty('fullPlatform', 'gtk')
2631
        self.setProperty('configuration', 'release')
2632
        first_run_failures = ['fast/css/test1.html', 'imported/test/test2.html', 'fast/svg/test3.svg']
2633
        first_run_flakies = ['fast/css/flaky1.html', 'imported/test/flaky2.html', 'fast/svg/flaky3.svg']
2634
        with_patch_repeat_failures_results_nonflaky_failures = ['fast/css/test1.html']
2635
        with_patch_repeat_failures_results_flakies = ['imported/test/test2.html', 'fast/svg/test3.svg']
2636
        self.setProperty('first_run_failures', first_run_failures)
2637
        self.setProperty('first_run_flakies', first_run_flakies)
2638
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', with_patch_repeat_failures_results_nonflaky_failures)
2639
        self.setProperty('with_patch_repeat_failures_results_flakies', with_patch_repeat_failures_results_flakies)
2640
        self.expectRemoteCommands(
2641
            ExpectShell(workdir='wkdir',
2642
                        logfiles={'json': self.jsonFileName},
2643
                        logEnviron=False,
2644
                        maxTime=10800,
2645
                        command=['python',
2646
                                 'Tools/Scripts/run-webkit-tests',
2647
                                 '--no-build', '--no-show-results', '--no-new-test-results', '--clobber-old-results',
2648
                                 '--release', '--results-directory', 'layout-test-results', '--debug-rwt-logging',
2649
                                 '--skip-failing-tests', '--repeat-each=10', '--skipped=always'] + sorted(with_patch_repeat_failures_results_nonflaky_failures)
2650
                        )
2651
            + 0,
2652
        )
2653
        self.expectOutcome(result=SUCCESS, state_string='layout-tests')
2654
        return self.runStep()
2655
2656
    def test_step_with_patch_did_timeout(self):
2657
        self.configureStep()
2658
        self.setProperty('fullPlatform', 'gtk')
2659
        self.setProperty('configuration', 'release')
2660
        first_run_failures = ['fast/css/test1.html', 'imported/test/test2.html', 'fast/svg/test3.svg']
2661
        first_run_flakies = ['fast/css/flaky1.html', 'imported/test/flaky2.html', 'fast/svg/flaky3.svg']
2662
        with_patch_repeat_failures_results_nonflaky_failures = ['fast/css/test1.html']
2663
        with_patch_repeat_failures_results_flakies = ['imported/test/test2.html', 'fast/svg/test3.svg']
2664
        self.setProperty('first_run_failures', first_run_failures)
2665
        self.setProperty('first_run_flakies', first_run_flakies)
2666
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', with_patch_repeat_failures_results_nonflaky_failures)
2667
        self.setProperty('with_patch_repeat_failures_results_flakies', with_patch_repeat_failures_results_flakies)
2668
        self.setProperty('with_patch_repeat_failures_timedout', True)
2669
        self.expectRemoteCommands(
2670
            ExpectShell(workdir='wkdir',
2671
                        logfiles={'json': self.jsonFileName},
2672
                        logEnviron=False,
2673
                        maxTime=10800,
2674
                        command=['python',
2675
                                 'Tools/Scripts/run-webkit-tests',
2676
                                 '--no-build', '--no-show-results', '--no-new-test-results', '--clobber-old-results',
2677
                                 '--release', '--results-directory', 'layout-test-results', '--debug-rwt-logging',
2678
                                 '--skip-failing-tests', '--repeat-each=10', '--skipped=always'] + sorted(first_run_failures)
2679
                        )
2680
            + 0,
2681
        )
2682
        self.expectOutcome(result=SUCCESS, state_string='layout-tests')
2683
        return self.runStep()
2684
2685
2686
class TestAnalyzeLayoutTestsResultsRedTree(BuildStepMixinAdditions, unittest.TestCase):
2687
    def setUp(self):
2688
        self.longMessage = True
2689
        return self.setUpBuildStep()
2690
2691
    def tearDown(self):
2692
        return self.tearDownBuildStep()
2693
2694
    def configureStep(self):
2695
        AnalyzeLayoutTestsResults.send_email_for_flaky_failure = lambda *args: None
2696
        AnalyzeLayoutTestsResults.send_email_for_pre_existing_failure = lambda *args: None
2697
        self.setupStep(AnalyzeLayoutTestsResultsRedTree())
2698
2699
    def test_failure_introduced_by_patch_clean_tree_green(self):
2700
        self.configureStep()
2701
        self.setProperty('fullPlatform', 'gtk')
2702
        self.setProperty('configuration', 'release')
2703
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html"])
2704
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2705
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html"])
2706
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2707
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', [])
2708
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2709
        self.expectOutcome(result=FAILURE, state_string='Found 1 new test failure: test/failure1.html (failure)')
2710
        return self.runStep()
2711
2712
    def test_failure_introduced_by_patch_clean_tree_red(self):
2713
        self.configureStep()
2714
        self.setProperty('fullPlatform', 'gtk')
2715
        self.setProperty('configuration', 'release')
2716
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2717
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2718
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2719
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2720
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2721
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2722
        self.expectOutcome(result=FAILURE, state_string='Found 1 new test failure: test/failure1.html (failure)')
2723
        return self.runStep()
2724
2725
    def test_pre_existent_failures(self):
2726
        self.configureStep()
2727
        self.setProperty('first_run_failures', ["test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2728
        self.setProperty('first_run_flakies', ["test/pre-existent/flaky2.html", "test/pre-existent/flaky3.html"])
2729
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2730
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2731
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2732
        self.setProperty('without_patch_repeat_failures_results_flakies', [])
2733
        self.expectOutcome(result=SUCCESS, state_string='Passed layout tests')
2734
        return self.runStep()
2735
2736
    def test_pre_existent_flakies(self):
2737
        self.configureStep()
2738
        self.setProperty('first_run_failures', ["test/pre-existent/flaky1.html"])
2739
        self.setProperty('first_run_flakies', ["test/pre-existent/flaky2.html", "test/pre-existent/flaky3.html"])
2740
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', [])
2741
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky1.html"])
2742
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', [])
2743
        self.setProperty('without_patch_repeat_failures_results_flakies', [])
2744
        self.expectOutcome(result=SUCCESS, state_string='Passed layout tests')
2745
        return self.runStep()
2746
2747
    def test_first_step_gives_unexpected_failure_and_clean_tree_pass(self):
2748
        self.configureStep()
2749
        self.setProperty('fullPlatform', 'gtk')
2750
        self.setProperty('configuration', 'release')
2751
        self.setProperty('first_run_failures', [])
2752
        self.setProperty('first_run_flakies', [])
2753
        self.setProperty('clean_tree_run_failures', [])
2754
        self.setProperty('clean_tree_run_flakies', ['test/pre-existent/flaky.html'])
2755
        self.setProperty('clean_tree_run_status', WARNINGS)
2756
        self.expectOutcome(result=FAILURE, state_string='Found unexpected failure with patch (failure)')
2757
        return self.runStep()
2758
2759
    def test_first_step_gives_unexpected_failure_and_clean_tree_unexpected_failure(self):
2760
        self.configureStep()
2761
        self.setProperty('fullPlatform', 'gtk')
2762
        self.setProperty('configuration', 'release')
2763
        self.setProperty('first_run_failures', [])
2764
        self.setProperty('first_run_flakies', [])
2765
        self.setProperty('clean_tree_run_failures', [])
2766
        self.setProperty('clean_tree_run_flakies', [])
2767
        self.setProperty('clean_tree_run_status', FAILURE)
2768
        self.expectOutcome(result=RETRY, state_string='Unexpected infrastructure issue: The layout-test run with patch generated no list of results but exited with error, and the clean_tree without patch run did the samething.\nRetrying build [retry count is 0 of 3] (retry)')
2769
        return self.runStep()
2770
2771
    def test_step_retry_with_patch_exits_early_error(self):
2772
        self.configureStep()
2773
        self.setProperty('fullPlatform', 'gtk')
2774
        self.setProperty('configuration', 'release')
2775
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2776
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2777
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2778
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2779
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2780
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2781
        self.setProperty('with_patch_repeat_failures_results_exceed_failure_limit', True)
2782
        self.expectOutcome(result=RETRY, state_string='Unexpected infrastructure issue: One of the steps for retrying the failed tests has exited early, but this steps should run without "--exit-after-n-failures" switch, so they should not exit early.\nRetrying build [retry count is 0 of 3] (retry)')
2783
        return self.runStep()
2784
2785
    def test_step_retry_without_patch_exits_early_error(self):
2786
        self.configureStep()
2787
        self.setProperty('fullPlatform', 'gtk')
2788
        self.setProperty('configuration', 'release')
2789
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2790
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2791
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2792
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2793
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2794
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2795
        self.setProperty('without_patch_repeat_failures_results_exceed_failure_limit', True)
2796
        self.expectOutcome(result=RETRY, state_string='Unexpected infrastructure issue: One of the steps for retrying the failed tests has exited early, but this steps should run without "--exit-after-n-failures" switch, so they should not exit early.\nRetrying build [retry count is 0 of 3] (retry)')
2797
        return self.runStep()
2798
2799
    def test_step_retry_with_patch_timeouts(self):
2800
        self.configureStep()
2801
        self.setProperty('fullPlatform', 'gtk')
2802
        self.setProperty('configuration', 'release')
2803
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2804
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2805
        self.setProperty('with_patch_repeat_failures_timedout', True)
2806
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2807
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2808
        self.expectOutcome(result=FAILURE, state_string='Found 2 new test failures: test/failure1.html, test/failure2.html (failure)')
2809
        return self.runStep()
2810
2811
    def test_step_retry_with_patch_unexpected_error(self):
2812
        self.configureStep()
2813
        self.setProperty('fullPlatform', 'gtk')
2814
        self.setProperty('configuration', 'release')
2815
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2816
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2817
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', [])
2818
        self.setProperty('without_patch_repeat_failures_results_flakies', [])
2819
        self.setProperty('with_patch_repeat_failures_retcode', FAILURE)
2820
        self.expectOutcome(result=RETRY, state_string='Unexpected infrastructure issue: The step "layout-tests-repeat-failures" failed to generate any list of failures or flakies and returned an error code.\nRetrying build [retry count is 0 of 3] (retry)')
2821
        return self.runStep()
2822
2823
    def test_step_retry_without_patch_timeouts(self):
2824
        self.configureStep()
2825
        self.setProperty('fullPlatform', 'gtk')
2826
        self.setProperty('configuration', 'release')
2827
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2828
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2829
        self.setProperty('with_patch_repeat_failures_timedout', True)
2830
        self.setProperty('without_patch_repeat_failures_timedout', True)
2831
        self.expectOutcome(result=RETRY, state_string='Unexpected infrastructure issue: The step "layout-tests-repeat-failures-without-patch" was interrumped because it reached the timeout.\nRetrying build [retry count is 0 of 3] (retry)')
2832
        return self.runStep()
2833
2834
    def test_step_retry_with_patch_timeouts_and_without_patch_timeouts(self):
2835
        self.configureStep()
2836
        self.setProperty('fullPlatform', 'gtk')
2837
        self.setProperty('configuration', 'release')
2838
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2839
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2840
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2841
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2842
        self.setProperty('without_patch_repeat_failures_timedout', True)
2843
        self.expectOutcome(result=RETRY, state_string='Unexpected infrastructure issue: The step "layout-tests-repeat-failures-without-patch" was interrumped because it reached the timeout.\nRetrying build [retry count is 0 of 3] (retry)')
2844
        return self.runStep()
2845
2846
    def test_retry_third_time(self):
2847
        self.configureStep()
2848
        self.setProperty('fullPlatform', 'gtk')
2849
        self.setProperty('configuration', 'release')
2850
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2851
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2852
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2853
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2854
        self.setProperty('without_patch_repeat_failures_timedout', True)
2855
        self.setProperty('retry_count', 2)
2856
        self.expectOutcome(result=RETRY, state_string='Unexpected infrastructure issue: The step "layout-tests-repeat-failures-without-patch" was interrumped because it reached the timeout.\nRetrying build [retry count is 2 of 3] (retry)')
2857
        return self.runStep()
2858
2859
    def test_retry_finish(self):
2860
        self.configureStep()
2861
        self.setProperty('fullPlatform', 'gtk')
2862
        self.setProperty('configuration', 'release')
2863
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2864
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2865
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2866
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2867
        self.setProperty('without_patch_repeat_failures_timedout', True)
2868
        self.setProperty('retry_count', 3)
2869
        self.expectOutcome(result=WARNINGS, state_string='The step "layout-tests-repeat-failures-without-patch" was interrumped because it reached the timeout.\nReached the maximum number of retries (3). Unable to determine if patch is bad or there is a pre-existent infrastructure issue. (warnings)')
2870
        return self.runStep()
2871
2872
2546
class TestCheckOutSpecificRevision(BuildStepMixinAdditions, unittest.TestCase):
2873
class TestCheckOutSpecificRevision(BuildStepMixinAdditions, unittest.TestCase):
2547
    def setUp(self):
2874
    def setUp(self):
2548
        self.longMessage = True
2875
        self.longMessage = True

Return to Bug 231999