| Differences between
and this patch
- a/Tools/ChangeLog +79 lines
Lines 1-3 a/Tools/ChangeLog_sec1
1
2021-12-01  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
        The patch also add 3 new workers to the queue (to a total of 8) to speed up testing.
29
30
        * CISupport/ews-build/config.json:
31
        * CISupport/ews-build/factories.py:
32
        (GTKTestsFactory):
33
        * CISupport/ews-build/layout_test_failures.py:
34
        (LayoutTestFailures.__init__):
35
        (LayoutTestFailures.results_from_string):
36
        (LayoutTestFailures.results_from_string.get_failing_tests):
37
        * CISupport/ews-build/steps.py:
38
        (BufferLogHeaderObserver):
39
        (BufferLogHeaderObserver.__init__):
40
        (BufferLogHeaderObserver.headerReceived):
41
        (BufferLogHeaderObserver.getHeaders):
42
        (BugzillaMixin.send_email_for_infrastructure_issue):
43
        (Trigger.propertiesToPassToTriggers):
44
        (RunWebKitTests.setLayoutTestCommand):
45
        (RunWebKitTests.start):
46
        (RunWebKitTests.commandComplete):
47
        (ReRunWebKitTests.evaluateCommand):
48
        (ReRunWebKitTests.commandComplete):
49
        (RunWebKitTestsWithoutPatch.commandComplete):
50
        (AnalyzeLayoutTestsResults.report_failure):
51
        (AnalyzeLayoutTestsResults.send_email_for_flaky_failure):
52
        (AnalyzeLayoutTestsResults.send_email_for_new_test_failures):
53
        (AnalyzeLayoutTestsResults.start):
54
        (RunWebKit1Tests.start):
55
        (RunWebKitTestsRedTree):
56
        (RunWebKitTestsRedTree._did_command_timed_out):
57
        (RunWebKitTestsRedTree.evaluateCommand):
58
        (RunWebKitTestsRepeatFailuresRedTree):
59
        (RunWebKitTestsRepeatFailuresRedTree.__init__):
60
        (RunWebKitTestsRepeatFailuresRedTree.setLayoutTestCommand):
61
        (RunWebKitTestsRepeatFailuresRedTree.evaluateCommand):
62
        (RunWebKitTestsRepeatFailuresRedTree.commandComplete):
63
        (RunWebKitTestsRepeatFailuresRedTree.start):
64
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree):
65
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.__init__):
66
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.setLayoutTestCommand):
67
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.evaluateCommand):
68
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.commandComplete):
69
        (RunWebKitTestsRepeatFailuresWithoutPatchRedTree.start):
70
        (RunWebKitTestsWithoutPatchRedTree):
71
        (RunWebKitTestsWithoutPatchRedTree.evaluateCommand):
72
        (AnalyzeLayoutTestsResultsRedTree):
73
        (AnalyzeLayoutTestsResultsRedTree.report_success):
74
        (AnalyzeLayoutTestsResultsRedTree.report_warning):
75
        (AnalyzeLayoutTestsResultsRedTree.report_infrastructure_issue_and_maybe_retry_build):
76
        (AnalyzeLayoutTestsResultsRedTree.send_email_for_pre_existent_failures):
77
        (AnalyzeLayoutTestsResultsRedTree.start):
78
        * CISupport/ews-build/steps_unittest.py:
79
1
2021-12-01  Alex Christensen  <achristensen@webkit.org>
80
2021-12-01  Alex Christensen  <achristensen@webkit.org>
2
81
3
        Add csp-report resource type to WKContentRuleList
82
        Add csp-report resource type to WKContentRuleList
- a/Tools/CISupport/ews-build/config.json -3 / +12 lines
Lines 16-21 a/Tools/CISupport/ews-build/config.json_sec1
16
    { "name": "igalia7-gtk-wk2-ews", "platform": "gtk" },
16
    { "name": "igalia7-gtk-wk2-ews", "platform": "gtk" },
17
    { "name": "igalia8-gtk-wk2-ews", "platform": "gtk" },
17
    { "name": "igalia8-gtk-wk2-ews", "platform": "gtk" },
18
    { "name": "igalia9-gtk-wk2-ews", "platform": "gtk" },
18
    { "name": "igalia9-gtk-wk2-ews", "platform": "gtk" },
19
    { "name": "igalia10-gtk-wk2-ews", "platform": "gtk" },
20
    { "name": "igalia11-gtk-wk2-ews", "platform": "gtk" },
21
    { "name": "igalia12-gtk-wk2-ews", "platform": "gtk" },
19
    { "name": "aperez-gtk-ews", "platform": "gtk" },
22
    { "name": "aperez-gtk-ews", "platform": "gtk" },
20
    { "name": "igalia-wpe-ews", "platform": "wpe" },
23
    { "name": "igalia-wpe-ews", "platform": "wpe" },
21
    { "name": "aperez-wpe-ews", "platform": "wpe" },
24
    { "name": "aperez-wpe-ews", "platform": "wpe" },
Lines 111-124 a/Tools/CISupport/ews-build/config.json_sec2
111
      "name": "GTK-Build-EWS", "shortname": "gtk", "icon": "buildOnly",
114
      "name": "GTK-Build-EWS", "shortname": "gtk", "icon": "buildOnly",
112
      "factory": "GTKBuildFactory", "platform": "gtk",
115
      "factory": "GTKBuildFactory", "platform": "gtk",
113
      "configuration": "release", "architectures": ["x86_64"],
116
      "configuration": "release", "architectures": ["x86_64"],
114
      "triggers": ["api-tests-gtk-ews"],
117
      "triggers": ["api-tests-gtk-ews", "gtk-wk2-tests-ews"],
115
      "workernames": ["igalia1-gtk-wk2-ews", "igalia2-gtk-wk2-ews", "aperez-gtk-ews"]
118
      "workernames": ["igalia1-gtk-wk2-ews", "igalia2-gtk-wk2-ews", "aperez-gtk-ews"]
116
    },
119
    },
117
    {
120
    {
118
      "name": "GTK-WK2-Tests-EWS", "shortname": "gtk-wk2", "icon": "testOnly",
121
      "name": "GTK-WK2-Tests-EWS", "shortname": "gtk-wk2", "icon": "testOnly",
119
      "factory": "GTKTestsFactory", "platform": "gtk",
122
      "factory": "GTKTestsFactory", "platform": "gtk",
120
      "configuration": "release", "architectures": ["x86_64"],
123
      "configuration": "release", "architectures": ["x86_64"],
121
      "workernames": ["igalia5-gtk-wk2-ews", "igalia6-gtk-wk2-ews", "igalia7-gtk-wk2-ews", "igalia8-gtk-wk2-ews", "igalia9-gtk-wk2-ews"]
124
      "triggered_by": ["gtk-build-ews"],
125
      "workernames": ["igalia5-gtk-wk2-ews", "igalia6-gtk-wk2-ews", "igalia7-gtk-wk2-ews", "igalia8-gtk-wk2-ews", "igalia9-gtk-wk2-ews", "igalia10-gtk-wk2-ews", "igalia11-gtk-wk2-ews", "igalia12-gtk-wk2-ews"]
122
    },
126
    },
123
    {
127
    {
124
      "name": "iOS-15-Build-EWS", "shortname": "ios", "icon": "buildOnly",
128
      "name": "iOS-15-Build-EWS", "shortname": "ios", "icon": "buildOnly",
Lines 310-315 a/Tools/CISupport/ews-build/config.json_sec3
310
      "name": "API-Tests-GTK-EWS", "shortname": "api-gtk", "icon": "testOnly",
314
      "name": "API-Tests-GTK-EWS", "shortname": "api-gtk", "icon": "testOnly",
311
      "factory": "APITestsFactory", "platform": "gtk",
315
      "factory": "APITestsFactory", "platform": "gtk",
312
      "configuration": "release", "architectures": ["x86_64"],
316
      "configuration": "release", "architectures": ["x86_64"],
317
      "triggered_by": ["gtk-build-ews"],
313
      "workernames": ["igalia3-gtk-wk2-ews", "igalia4-gtk-wk2-ews"]
318
      "workernames": ["igalia3-gtk-wk2-ews", "igalia4-gtk-wk2-ews"]
314
    },
319
    },
315
    {
320
    {
Lines 387-398 a/Tools/CISupport/ews-build/config.json_sec4
387
      "type": "Triggerable", "name": "api-tests-mac-ews",
392
      "type": "Triggerable", "name": "api-tests-mac-ews",
388
      "builderNames": ["API-Tests-macOS-EWS"]
393
      "builderNames": ["API-Tests-macOS-EWS"]
389
    },
394
    },
395
    {
396
      "type": "Triggerable", "name": "gtk-build-ews",
397
      "builderNames": ["GTK-Build-EWS"]
398
    },
390
    {
399
    {
391
      "type": "Triggerable", "name": "api-tests-gtk-ews",
400
      "type": "Triggerable", "name": "api-tests-gtk-ews",
392
      "builderNames": ["API-Tests-GTK-EWS"]
401
      "builderNames": ["API-Tests-GTK-EWS"]
393
    },
402
    },
394
    {
403
    {
395
      "type": "Try_Userpass", "name": "gtk-wk2-tests-ews-disabled", "port": 5558,
404
      "type": "Triggerable", "name": "gtk-wk2-tests-ews",
396
      "builderNames": ["GTK-WK2-Tests-EWS"]
405
      "builderNames": ["GTK-WK2-Tests-EWS"]
397
    },
406
    },
398
    {
407
    {
- 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 / +345 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().__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 735-740 class BugzillaMixin(object): a/Tools/CISupport/ews-build/steps.py_sec2
735
            return False
748
            return False
736
        return True
749
        return True
737
750
751
    def send_email_for_infrastructure_issue(self, infrastructure_issue_text):
752
        try:
753
            builder_name = self.getProperty('buildername', '')
754
            worker_name = self.getProperty('workername', '')
755
            build_url = '{}#/builders/{}/builds/{}'.format(self.master.config.buildbotURL, self.build._builderid, self.build.number)
756
            email_subject = 'Infrastructure issue at {}'.format(builder_name)
757
            email_text = 'The following infrastructure issue happened at:\n\n'
758
            email_text += '    - Build : {}\n'.format(build_url)
759
            email_text += '    - Builder : {}\n'.format(builder_name)
760
            email_text += '    - Worker : {}\n'.format(worker_name)
761
            email_text += '    - Issue: {}\n'.format(infrastructure_issue_text)
762
            send_email_to_bot_watchers(email_subject, email_text, builder_name, 'infrastructure-{}'.format(builder_name))
763
        except Exception as e:
764
            print('Error in sending email for infrastructure issue: {}'.format(e))
765
738
    def get_bugzilla_api_key(self):
766
    def get_bugzilla_api_key(self):
739
        try:
767
        try:
740
            passwords = json.load(open('passwords.json'))
768
            passwords = json.load(open('passwords.json'))
Lines 1171-1176 class Trigger(trigger.Trigger): a/Tools/CISupport/ews-build/steps.py_sec3
1171
            properties_to_pass['ews_revision'] = properties.Property('got_revision')
1199
            properties_to_pass['ews_revision'] = properties.Property('got_revision')
1172
        if self.triggers:
1200
        if self.triggers:
1173
            properties_to_pass['triggers'] = self.triggers
1201
            properties_to_pass['triggers'] = self.triggers
1202
        properties_to_pass['retry_count'] = properties.Property('retry_count', default=0)
1174
        return properties_to_pass
1203
        return properties_to_pass
1175
1204
1176
1205
Lines 2193-2199 class RunWebKitTests(shell.Test): a/Tools/CISupport/ews-build/steps.py_sec4
2193
        if patch_author in ['webkit-wpt-import-bot@igalia.com']:
2222
        if patch_author in ['webkit-wpt-import-bot@igalia.com']:
2194
            self.setCommand(self.command + ['imported/w3c/web-platform-tests'])
2223
            self.setCommand(self.command + ['imported/w3c/web-platform-tests'])
2195
        else:
2224
        else:
2196
            self.setCommand(self.command + ['--exit-after-n-failures', self.EXIT_AFTER_FAILURES, '--skip-failing-tests'])
2225
            if self.EXIT_AFTER_FAILURES is not None:
2226
                self.setCommand(self.command + ['--exit-after-n-failures', '{}'.format(self.EXIT_AFTER_FAILURES)])
2227
            self.setCommand(self.command + ['--skip-failing-tests'])
2197
2228
2198
        if additionalArguments:
2229
        if additionalArguments:
2199
            self.setCommand(self.command + additionalArguments)
2230
            self.setCommand(self.command + additionalArguments)
Lines 2201-2208 class RunWebKitTests(shell.Test): a/Tools/CISupport/ews-build/steps.py_sec5
2201
        if self.ENABLE_GUARD_MALLOC:
2232
        if self.ENABLE_GUARD_MALLOC:
2202
            self.setCommand(self.command + ['--guard-malloc'])
2233
            self.setCommand(self.command + ['--guard-malloc'])
2203
2234
2204
    def start(self):
2235
    def start(self, BufferLogObserverClass=logobserver.BufferLogObserver):
2205
        self.log_observer = logobserver.BufferLogObserver(wantStderr=True)
2236
        self.log_observer = BufferLogObserverClass(wantStderr=True)
2206
        self.addLogObserver('stdio', self.log_observer)
2237
        self.addLogObserver('stdio', self.log_observer)
2207
        self.log_observer_json = logobserver.BufferLogObserver()
2238
        self.log_observer_json = logobserver.BufferLogObserver()
2208
        self.addLogObserver('json', self.log_observer_json)
2239
        self.addLogObserver('json', self.log_observer_json)
Lines 2264-2269 class RunWebKitTests(shell.Test): a/Tools/CISupport/ews-build/steps.py_sec6
2264
        if first_results:
2295
        if first_results:
2265
            self.setProperty('first_results_exceed_failure_limit', first_results.did_exceed_test_failure_limit)
2296
            self.setProperty('first_results_exceed_failure_limit', first_results.did_exceed_test_failure_limit)
2266
            self.setProperty('first_run_failures', sorted(first_results.failing_tests))
2297
            self.setProperty('first_run_failures', sorted(first_results.failing_tests))
2298
            self.setProperty('first_run_flakies', sorted(first_results.flaky_tests))
2267
            if first_results.failing_tests:
2299
            if first_results.failing_tests:
2268
                self._addToLog(self.test_failures_log_name, '\n'.join(first_results.failing_tests))
2300
                self._addToLog(self.test_failures_log_name, '\n'.join(first_results.failing_tests))
2269
        self._parseRunWebKitTestsOutput(logText)
2301
        self._parseRunWebKitTestsOutput(logText)
Lines 2375-2380 class ReRunWebKitTests(RunWebKitTests): a/Tools/CISupport/ews-build/steps.py_sec7
2375
        first_results_failing_tests = set(self.getProperty('first_run_failures', []))
2407
        first_results_failing_tests = set(self.getProperty('first_run_failures', []))
2376
        second_results_did_exceed_test_failure_limit = self.getProperty('second_results_exceed_failure_limit')
2408
        second_results_did_exceed_test_failure_limit = self.getProperty('second_results_exceed_failure_limit')
2377
        second_results_failing_tests = set(self.getProperty('second_run_failures', []))
2409
        second_results_failing_tests = set(self.getProperty('second_run_failures', []))
2410
        # FIXME: here it can be a good idea to also use the info from second_run_flakies and first_run_flakies
2378
        tests_that_consistently_failed = first_results_failing_tests.intersection(second_results_failing_tests)
2411
        tests_that_consistently_failed = first_results_failing_tests.intersection(second_results_failing_tests)
2379
        flaky_failures = first_results_failing_tests.union(second_results_failing_tests) - first_results_failing_tests.intersection(second_results_failing_tests)
2412
        flaky_failures = first_results_failing_tests.union(second_results_failing_tests) - first_results_failing_tests.intersection(second_results_failing_tests)
2380
        flaky_failures = sorted(list(flaky_failures))[:self.NUM_FAILURES_TO_DISPLAY]
2413
        flaky_failures = sorted(list(flaky_failures))[:self.NUM_FAILURES_TO_DISPLAY]
Lines 2413-2418 class ReRunWebKitTests(RunWebKitTests): a/Tools/CISupport/ews-build/steps.py_sec8
2413
        if second_results:
2446
        if second_results:
2414
            self.setProperty('second_results_exceed_failure_limit', second_results.did_exceed_test_failure_limit)
2447
            self.setProperty('second_results_exceed_failure_limit', second_results.did_exceed_test_failure_limit)
2415
            self.setProperty('second_run_failures', sorted(second_results.failing_tests))
2448
            self.setProperty('second_run_failures', sorted(second_results.failing_tests))
2449
            self.setProperty('second_run_flakies', sorted(second_results.flaky_tests))
2416
            if second_results.failing_tests:
2450
            if second_results.failing_tests:
2417
                self._addToLog(self.test_failures_log_name, '\n'.join(second_results.failing_tests))
2451
                self._addToLog(self.test_failures_log_name, '\n'.join(second_results.failing_tests))
2418
        self._parseRunWebKitTestsOutput(logText)
2452
        self._parseRunWebKitTestsOutput(logText)
Lines 2452-2457 class RunWebKitTestsWithoutPatch(RunWebKitTests): a/Tools/CISupport/ews-build/steps.py_sec9
2452
        if clean_tree_results:
2486
        if clean_tree_results:
2453
            self.setProperty('clean_tree_results_exceed_failure_limit', clean_tree_results.did_exceed_test_failure_limit)
2487
            self.setProperty('clean_tree_results_exceed_failure_limit', clean_tree_results.did_exceed_test_failure_limit)
2454
            self.setProperty('clean_tree_run_failures', clean_tree_results.failing_tests)
2488
            self.setProperty('clean_tree_run_failures', clean_tree_results.failing_tests)
2489
            self.setProperty('clean_tree_run_flakies', sorted(clean_tree_results.flaky_tests))
2455
            if clean_tree_results.failing_tests:
2490
            if clean_tree_results.failing_tests:
2456
                self._addToLog(self.test_failures_log_name, '\n'.join(clean_tree_results.failing_tests))
2491
                self._addToLog(self.test_failures_log_name, '\n'.join(clean_tree_results.failing_tests))
2457
        self._parseRunWebKitTestsOutput(logText)
2492
        self._parseRunWebKitTestsOutput(logText)
Lines 2482-2499 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec10
2482
    descriptionDone = ['analyze-layout-tests-results']
2517
    descriptionDone = ['analyze-layout-tests-results']
2483
    NUM_FAILURES_TO_DISPLAY = 10
2518
    NUM_FAILURES_TO_DISPLAY = 10
2484
2519
2485
    def report_failure(self, new_failures):
2520
    def report_failure(self, new_failures, exceed_failure_limit=False):
2486
        self.finished(FAILURE)
2521
        self.finished(FAILURE)
2487
        self.build.results = FAILURE
2522
        self.build.results = FAILURE
2488
        if not new_failures:
2523
        if not new_failures:
2489
            message = 'Found unexpected failure with patch'
2524
            message = 'Found unexpected failure with patch'
2490
        else:
2525
        else:
2491
            pluralSuffix = 's' if len(new_failures) > 1 else ''
2526
            pluralSuffix = 's' if len(new_failures) > 1 else ''
2527
            if exceed_failure_limit:
2528
                message = 'Failure limit exceed. At least found'
2529
            else:
2530
                message = 'Found'
2492
            new_failures_string = ', '.join(sorted(new_failures)[:self.NUM_FAILURES_TO_DISPLAY])
2531
            new_failures_string = ', '.join(sorted(new_failures)[:self.NUM_FAILURES_TO_DISPLAY])
2493
            message = 'Found {} new test failure{}: {}'.format(len(new_failures), pluralSuffix, new_failures_string)
2532
            message += ' {} new test failure{}: {}'.format(len(new_failures), pluralSuffix, new_failures_string)
2494
            if len(new_failures) > self.NUM_FAILURES_TO_DISPLAY:
2533
            if len(new_failures) > self.NUM_FAILURES_TO_DISPLAY:
2495
                message += ' ...'
2534
                message += ' ...'
2496
            self.send_email_for_new_test_failures(new_failures)
2535
            self.send_email_for_new_test_failures(new_failures, exceed_failure_limit)
2497
        self.descriptionDone = message
2536
        self.descriptionDone = message
2498
        self.setProperty('build_finish_summary', message)
2537
        self.setProperty('build_finish_summary', message)
2499
2538
Lines 2550-2556 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec11
2550
    def _results_failed_different_tests(self, first_results_failing_tests, second_results_failing_tests):
2589
    def _results_failed_different_tests(self, first_results_failing_tests, second_results_failing_tests):
2551
        return first_results_failing_tests != second_results_failing_tests
2590
        return first_results_failing_tests != second_results_failing_tests
2552
2591
2553
    def send_email_for_flaky_failure(self, test_name):
2592
    def send_email_for_flaky_failure(self, test_name, step_str=None):
2554
        try:
2593
        try:
2555
            builder_name = self.getProperty('buildername', '')
2594
            builder_name = self.getProperty('buildername', '')
2556
            worker_name = self.getProperty('workername', '')
2595
            worker_name = self.getProperty('workername', '')
Lines 2559-2564 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec12
2559
2598
2560
            email_subject = 'Flaky test: {}'.format(test_name)
2599
            email_subject = 'Flaky test: {}'.format(test_name)
2561
            email_text = 'Flaky test: {}\n\nBuild: {}\n\nBuilder: {}\n\nWorker: {}\n\nHistory: {}'.format(test_name, build_url, builder_name, worker_name, history_url)
2600
            email_text = 'Flaky test: {}\n\nBuild: {}\n\nBuilder: {}\n\nWorker: {}\n\nHistory: {}'.format(test_name, build_url, builder_name, worker_name, history_url)
2601
            if step_str:
2602
                email_text += '\nThis test was flaky on the steps: {}'.format(step_str)
2562
            send_email_to_bot_watchers(email_subject, email_text, builder_name, 'flaky-{}'.format(test_name))
2603
            send_email_to_bot_watchers(email_subject, email_text, builder_name, 'flaky-{}'.format(test_name))
2563
        except Exception as e:
2604
        except Exception as e:
2564
            print('Error in sending email for flaky failure: {}'.format(e))
2605
            print('Error in sending email for flaky failure: {}'.format(e))
Lines 2576-2582 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec13
2576
        except Exception as e:
2617
        except Exception as e:
2577
            print('Error in sending email for pre-existing failure: {}'.format(e))
2618
            print('Error in sending email for pre-existing failure: {}'.format(e))
2578
2619
2579
    def send_email_for_new_test_failures(self, test_names):
2620
    def send_email_for_new_test_failures(self, test_names, exceed_failure_limit=False):
2580
        try:
2621
        try:
2581
            patch_id = self.getProperty('patch_id', '')
2622
            patch_id = self.getProperty('patch_id', '')
2582
            if not self.should_send_email(patch_id):
2623
            if not self.should_send_email(patch_id):
Lines 2598-2603 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec14
2598
            email_text += ' while testing <a href="{}">Patch {}</a>'.format(Bugzilla.patch_url(patch_id), patch_id)
2639
            email_text += ' while testing <a href="{}">Patch {}</a>'.format(Bugzilla.patch_url(patch_id), patch_id)
2599
            email_text += ' for <a href="{}">Bug {}</a>.'.format(Bugzilla.bug_url(bug_id), bug_id)
2640
            email_text += ' for <a href="{}">Bug {}</a>.'.format(Bugzilla.bug_url(bug_id), bug_id)
2600
            email_text += '\n\nFull details are available at: {}\n\nPatch author: {}'.format(build_url, patch_author)
2641
            email_text += '\n\nFull details are available at: {}\n\nPatch author: {}'.format(build_url, patch_author)
2642
            if exceed_failure_limit:
2643
                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.'
2601
            email_text += '\n\nLayout test failure{}:\n{}'.format(pluralSuffix, test_names_string)
2644
            email_text += '\n\nLayout test failure{}:\n{}'.format(pluralSuffix, test_names_string)
2602
            email_text += '\n\nTo unsubscribe from these notifications or to provide any feedback please email aakash_jain@apple.com'
2645
            email_text += '\n\nTo unsubscribe from these notifications or to provide any feedback please email aakash_jain@apple.com'
2603
            self._addToLog('stdio', 'Sending email notification to {}'.format(patch_author))
2646
            self._addToLog('stdio', 'Sending email notification to {}'.format(patch_author))
Lines 2624-2630 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec15
2624
            clean_tree_run_status = self.getProperty('clean_tree_run_status', FAILURE)
2667
            clean_tree_run_status = self.getProperty('clean_tree_run_status', FAILURE)
2625
            if clean_tree_run_status == SUCCESS:
2668
            if clean_tree_run_status == SUCCESS:
2626
                return self.report_failure(set())
2669
                return self.report_failure(set())
2627
            # TODO: email EWS admins
2670
            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.')
2628
            return self.retry_build('Unexpected infrastructure issue, retrying build')
2671
            return self.retry_build('Unexpected infrastructure issue, retrying build')
2629
2672
2630
        if first_results_did_exceed_test_failure_limit and second_results_did_exceed_test_failure_limit:
2673
        if first_results_did_exceed_test_failure_limit and second_results_did_exceed_test_failure_limit:
Lines 2651-2656 class AnalyzeLayoutTestsResults(buildstep.BuildStep, BugzillaMixin): a/Tools/CISupport/ews-build/steps.py_sec16
2651
                return self.report_failure(failures_introduced_by_patch)
2694
                return self.report_failure(failures_introduced_by_patch)
2652
            return self.retry_build()
2695
            return self.retry_build()
2653
2696
2697
        # FIXME: Here it could be a good idea to also use the info of results.flaky_tests from the runs
2654
        if self._results_failed_different_tests(first_results_failing_tests, second_results_failing_tests):
2698
        if self._results_failed_different_tests(first_results_failing_tests, second_results_failing_tests):
2655
            tests_that_only_failed_first = first_results_failing_tests.difference(second_results_failing_tests)
2699
            tests_that_only_failed_first = first_results_failing_tests.difference(second_results_failing_tests)
2656
            self._report_flaky_tests(tests_that_only_failed_first)
2700
            self._report_flaky_tests(tests_that_only_failed_first)
Lines 2689-2694 class RunWebKit1Tests(RunWebKitTests): a/Tools/CISupport/ews-build/steps.py_sec17
2689
        return RunWebKitTests.start(self)
2733
        return RunWebKitTests.start(self)
2690
2734
2691
2735
2736
# This is a specialized class designed to cope with a tree that is not always green.
2737
# It tries hard to avoid reporting any false positive, so it will only report new
2738
# consistent failures (fail always with the patch and pass always without it).
2739
class RunWebKitTestsRedTree(RunWebKitTests):
2740
    EXIT_AFTER_FAILURES = 500
2741
2742
    def _did_command_timed_out(self, logHeadersText):
2743
        timed_out_line_start = 'command timed out: {} seconds elapsed running'.format(self.MAX_SECONDS_STEP_RUN)
2744
        timed_out_line_end = 'attempting to kill'
2745
        for line in logHeadersText.splitlines():
2746
            line = line.strip()
2747
            if line.startswith(timed_out_line_start) and line.endswith(timed_out_line_end):
2748
                return True
2749
        return False
2750
2751
    def evaluateCommand(self, cmd):
2752
        first_results_failing_tests = set(self.getProperty('first_run_failures', []))
2753
        first_results_flaky_tests = set(self.getProperty('first_run_flakies', []))
2754
        rc = self.evaluateResult(cmd)
2755
        next_steps = [ArchiveTestResults(), UploadTestResults(), ExtractTestResults()]
2756
        if first_results_failing_tests:
2757
            next_steps.extend([ValidatePatch(verifyBugClosed=False, addURLs=False), KillOldProcesses(), RunWebKitTestsRepeatFailuresRedTree()])
2758
        elif first_results_flaky_tests:
2759
            next_steps.append(AnalyzeLayoutTestsResultsRedTree())
2760
        elif rc == SUCCESS or rc == WARNINGS:
2761
            next_steps = None
2762
            message = 'Passed layout tests'
2763
            self.descriptionDone = message
2764
            self.build.results = SUCCESS
2765
            self.setProperty('build_summary', message)
2766
        else:
2767
            # We have a failure return code, but not a list of failed or flaky tests.
2768
            # So retry re-running the _whole_ layout tests without-patch to see if
2769
            # this unexpected failure was pre-existent. If the failure was not pre-existent,
2770
            # then we would not report a list of failed test, just a generic "unknown" failure.
2771
            self.setProperty('patchFailedTests', True)
2772
            next_steps.extend([UnApplyPatchIfRequired(), CompileWebKitWithoutPatch(retry_build_on_failure=True), ValidatePatch(verifyBugClosed=False, addURLs=False), RunWebKitTestsWithoutPatchRedTree()])
2773
        if next_steps:
2774
            self.build.addStepsAfterCurrentStep(next_steps)
2775
        return rc
2776
2777
2778
class RunWebKitTestsRepeatFailuresRedTree(RunWebKitTestsRedTree):
2779
    name = 'layout-tests-repeat-failures'
2780
    NUM_REPEATS_PER_TEST = 10
2781
    EXIT_AFTER_FAILURES = None
2782
    MAX_SECONDS_STEP_RUN = 18000  # 5h
2783
2784
    def __init__(self, **kwargs):
2785
        super().__init__(maxTime=self.MAX_SECONDS_STEP_RUN, **kwargs)
2786
2787
    def setLayoutTestCommand(self):
2788
        super().setLayoutTestCommand()
2789
        first_results_failing_tests = set(self.getProperty('first_run_failures', []))
2790
        self.setCommand(self.command + ['--repeat-each=%s' % self.NUM_REPEATS_PER_TEST] + sorted(first_results_failing_tests))
2791
2792
    def evaluateCommand(self, cmd):
2793
        with_patch_repeat_failures_results_nonflaky_failures = set(self.getProperty('with_patch_repeat_failures_results_nonflaky_failures', []))
2794
        with_patch_repeat_failures_results_flakies = set(self.getProperty('with_patch_repeat_failures_results_flakies', []))
2795
        with_patch_repeat_failures_timedout = self.getProperty('with_patch_repeat_failures_timedout', False)
2796
        first_results_flaky_tests = set(self.getProperty('first_run_flakies', []))
2797
        rc = self.evaluateResult(cmd)
2798
        self.setProperty('with_patch_repeat_failures_retcode', rc)
2799
        next_steps = [ArchiveTestResults(), UploadTestResults(identifier='repeat-failures'), ExtractTestResults(identifier='repeat-failures')]
2800
        if with_patch_repeat_failures_results_nonflaky_failures or with_patch_repeat_failures_timedout:
2801
            self.setProperty('patchFailedTests', True)
2802
            next_steps.extend([ValidatePatch(verifyBugClosed=False, addURLs=False), KillOldProcesses(), UnApplyPatchIfRequired(), CompileWebKitWithoutPatch(retry_build_on_failure=True),
2803
                               ValidatePatch(verifyBugClosed=False, addURLs=False), RunWebKitTestsRepeatFailuresWithoutPatchRedTree()])
2804
        else:
2805
            next_steps.append(AnalyzeLayoutTestsResultsRedTree())
2806
        if next_steps:
2807
            self.build.addStepsAfterCurrentStep(next_steps)
2808
        return rc
2809
2810
    def commandComplete(self, cmd):
2811
        shell.Test.commandComplete(self, cmd)
2812
        logText = self.log_observer.getStdout() + self.log_observer.getStderr()
2813
        logTextJson = self.log_observer_json.getStdout()
2814
        with_patch_repeat_failures_results = LayoutTestFailures.results_from_string(logTextJson)
2815
        if with_patch_repeat_failures_results:
2816
            self.setProperty('with_patch_repeat_failures_results_exceed_failure_limit', with_patch_repeat_failures_results.did_exceed_test_failure_limit)
2817
            self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', sorted(with_patch_repeat_failures_results.failing_tests))
2818
            self.setProperty('with_patch_repeat_failures_results_flakies', sorted(with_patch_repeat_failures_results.flaky_tests))
2819
            if with_patch_repeat_failures_results.failing_tests:
2820
                self._addToLog(self.test_failures_log_name, '\n'.join(with_patch_repeat_failures_results.failing_tests))
2821
        command_timedout = self._did_command_timed_out(self.log_observer.getHeaders())
2822
        self.setProperty('with_patch_repeat_failures_timedout', command_timedout)
2823
        self._parseRunWebKitTestsOutput(logText)
2824
2825
    def start(self):
2826
        # buildbot messages about timeout reached appear on the header stream of BufferLog
2827
        return super().start(BufferLogObserverClass=BufferLogHeaderObserver)
2828
2829
2830
class RunWebKitTestsRepeatFailuresWithoutPatchRedTree(RunWebKitTestsRedTree):
2831
    name = 'layout-tests-repeat-failures-without-patch'
2832
    NUM_REPEATS_PER_TEST = 10
2833
    EXIT_AFTER_FAILURES = None
2834
    MAX_SECONDS_STEP_RUN = 10800  # 3h
2835
2836
    def __init__(self, **kwargs):
2837
        super().__init__(maxTime=self.MAX_SECONDS_STEP_RUN, **kwargs)
2838
2839
    def setLayoutTestCommand(self):
2840
        super().setLayoutTestCommand()
2841
        with_patch_nonflaky_failures = set(self.getProperty('with_patch_repeat_failures_results_nonflaky_failures', []))
2842
        first_run_failures = set(self.getProperty('first_run_failures', []))
2843
        with_patch_repeat_failures_timedout = self.getProperty('with_patch_repeat_failures_timedout', False)
2844
        failures_to_repeat = first_run_failures if with_patch_repeat_failures_timedout else with_patch_nonflaky_failures
2845
        # Pass '--skipped=always' to ensure that any test passed via command line arguments
2846
        # is skipped anyways if is marked as such on the Expectation files or if is marked
2847
        # as failure (since we are passing also '--skip-failing-tests'). That way we ensure
2848
        # to report the case of a patch removing an expectation that still fails with it.
2849
        self.setCommand(self.command + ['--repeat-each=%s' % self.NUM_REPEATS_PER_TEST, '--skipped=always'] + sorted(failures_to_repeat))
2850
2851
    def evaluateCommand(self, cmd):
2852
        rc = self.evaluateResult(cmd)
2853
        self.setProperty('without_patch_repeat_failures_retcode', rc)
2854
        self.build.addStepsAfterCurrentStep([ArchiveTestResults(), UploadTestResults(identifier='repeat-failures-without-patch'), ExtractTestResults(identifier='repeat-failures-without-patch'), AnalyzeLayoutTestsResultsRedTree()])
2855
        return rc
2856
2857
    def commandComplete(self, cmd):
2858
        shell.Test.commandComplete(self, cmd)
2859
        logText = self.log_observer.getStdout() + self.log_observer.getStderr()
2860
        logTextJson = self.log_observer_json.getStdout()
2861
        without_patch_repeat_failures_results = LayoutTestFailures.results_from_string(logTextJson)
2862
        if without_patch_repeat_failures_results:
2863
            self.setProperty('without_patch_repeat_failures_results_exceed_failure_limit', without_patch_repeat_failures_results.did_exceed_test_failure_limit)
2864
            self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', sorted(without_patch_repeat_failures_results.failing_tests))
2865
            self.setProperty('without_patch_repeat_failures_results_flakies', sorted(without_patch_repeat_failures_results.flaky_tests))
2866
            if without_patch_repeat_failures_results.failing_tests:
2867
                self._addToLog(self.test_failures_log_name, '\n'.join(without_patch_repeat_failures_results.failing_tests))
2868
        command_timedout = self._did_command_timed_out(self.log_observer.getHeaders())
2869
        self.setProperty('without_patch_repeat_failures_timedout', command_timedout)
2870
        self._parseRunWebKitTestsOutput(logText)
2871
2872
    def start(self):
2873
        # buildbot messages about timeout reached appear on the header stream of BufferLog
2874
        return super().start(BufferLogObserverClass=BufferLogHeaderObserver)
2875
2876
2877
class RunWebKitTestsWithoutPatchRedTree(RunWebKitTestsWithoutPatch):
2878
    EXIT_AFTER_FAILURES = 500
2879
2880
    def evaluateCommand(self, cmd):
2881
        rc = shell.Test.evaluateCommand(self, cmd)
2882
        self.build.addStepsAfterCurrentStep([ArchiveTestResults(), UploadTestResults(identifier='clean-tree'), ExtractTestResults(identifier='clean-tree'), AnalyzeLayoutTestsResultsRedTree()])
2883
        self.setProperty('clean_tree_run_status', rc)
2884
        return rc
2885
2886
2887
class AnalyzeLayoutTestsResultsRedTree(AnalyzeLayoutTestsResults):
2888
    MAX_RETRY = 3
2889
2890
    def report_success(self):
2891
        self.finished(SUCCESS)
2892
        self.build.results = SUCCESS
2893
        self.descriptionDone = 'Passed layout tests'
2894
        message = ''
2895
        self.setProperty('build_summary', message)
2896
        return defer.succeed(None)
2897
2898
    def report_warning(self, message):
2899
        self.finished(WARNINGS)
2900
        self.build.results = WARNINGS
2901
        self.descriptionDone = message
2902
        self.setProperty('build_summary', message)
2903
        return defer.succeed(None)
2904
2905
    def report_infrastructure_issue_and_maybe_retry_build(self, message):
2906
        retry_count = int(self.getProperty('retry_count', 0))
2907
        if retry_count >= self.MAX_RETRY:
2908
            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)
2909
            self.send_email_for_infrastructure_issue(message)
2910
            return self.report_warning(message)
2911
        message += "\nRetrying build [retry count is {} of {}]".format(retry_count, self.MAX_RETRY)
2912
        self.setProperty('retry_count', retry_count + 1)
2913
        self.send_email_for_infrastructure_issue(message)
2914
        return self.retry_build(message='Unexpected infrastructure issue: {}'.format(message))
2915
2916
    def send_email_for_pre_existent_failures(self, test_names):
2917
        try:
2918
            builder_name = self.getProperty('buildername', '')
2919
            worker_name = self.getProperty('workername', '')
2920
            build_url = '{}#/builders/{}/builds/{}'.format(self.master.config.buildbotURL, self.build._builderid, self.build.number)
2921
            number_failures = len(test_names)
2922
            pluralSuffix = 's' if number_failures > 1 else ''
2923
2924
            email_subject = 'Info about {} pre-existent failure{} at {}'.format(number_failures, pluralSuffix, builder_name)
2925
            email_test = 'Info about pre-existent (non-flaky) test failure{} at EWS:\n'.format(pluralSuffix)
2926
            email_text = '    - Build : {}\n'.format(build_url)
2927
            email_text = '    - Builder : {}\n'.format(builder_name)
2928
            email_text = '    - Worker : {}\n'.format(worker_name)
2929
            for test_name in sorted(test_names):
2930
                history_url = '{}?suite=layout-tests&test={}'.format(RESULTS_DB_URL, test_name)
2931
                email_text += '\n- {} (<a href="{}">test history</a>)'.format(test_name, history_url)
2932
            send_email_to_bot_watchers(email_subject, email_text, builder_name, 'preexisting-{}'.format(test_name))
2933
        except Exception as e:
2934
            print('Error in sending email for flaky failure: {}'.format(e))
2935
2936
    def start(self):
2937
        # Run with patch, running the whole layout test suite
2938
        first_results_exceed_failure_limit = self.getProperty('first_results_exceed_failure_limit', False)
2939
        first_run_failures = set(self.getProperty('first_run_failures', []))
2940
        first_run_flakies = set(self.getProperty('first_run_flakies', []))
2941
2942
        # Run with patch, running first_run_failures 10 times each test
2943
        with_patch_repeat_failures_results_exceed_failure_limit = self.getProperty('with_patch_repeat_failures_results_exceed_failure_limit', False)
2944
        with_patch_repeat_failures_results_nonflaky_failures = set(self.getProperty('with_patch_repeat_failures_results_nonflaky_failures', []))
2945
        with_patch_repeat_failures_results_flakies = set(self.getProperty('with_patch_repeat_failures_results_flakies', []))
2946
        with_patch_repeat_failures_timedout = self.getProperty('with_patch_repeat_failures_timedout', False)
2947
2948
        # Run without patch, running with_patch_repeat_failures_results_nonflaky_failures 10 times each test
2949
        without_patch_repeat_failures_results_exceed_failure_limit = self.getProperty('without_patch_repeat_failures_results_exceed_failure_limit', False)
2950
        without_patch_repeat_failures_results_nonflaky_failures = set(self.getProperty('without_patch_repeat_failures_results_nonflaky_failures', []))
2951
        without_patch_repeat_failures_results_flakies = set(self.getProperty('without_patch_repeat_failures_results_flakies', []))
2952
        without_patch_repeat_failures_timedout = self.getProperty('without_patch_repeat_failures_timedout', False)
2953
2954
        # Check for unexpected situations and warn EWS bot watchers or author potentially retrying the build.
2955
        if (not first_run_failures) and (not first_run_flakies):
2956
            # If we've made it here, the first_run failed, which means there should be either test failures or flakies
2957
            # Otherwise there is some unexpected issue.
2958
            # The clean tree run that only happens when the first run gives error code without generating a list of failures or flakies
2959
            clean_tree_run_failures = set(self.getProperty('clean_tree_run_failures', []))
2960
            clean_tree_run_flakies = set(self.getProperty('clean_tree_run_flakies', []))
2961
            clean_tree_run_status = self.getProperty('clean_tree_run_status', FAILURE)
2962
2963
            # If we are not on the last retry, then try to retry the whole testing with the hope it was a random infrastructure error
2964
            retry_count = int(self.getProperty('retry_count', 0))
2965
            if retry_count < self.MAX_RETRY:
2966
                return self.report_infrastructure_issue_and_maybe_retry_build('The layout-test run with patch generated no list of results and exited with error, retrying with the hope it was a random infrastructure error.')
2967
            # Otherwise report and error or a warning (since we already gave it enough retries for the issue to not be caused by a random infrastructure error)
2968
            # If the clean-tree run generated some results then we assume this patch broke the script run-webkit-tests or something like that
2969
            if (clean_tree_run_status in [SUCCESS, WARNINGS]) or clean_tree_run_failures or clean_tree_run_flakies:
2970
                return self.report_failure(set(), first_results_exceed_failure_limit)
2971
            # This will end the testing as retry_count will be now self.MAX_RETRY and a warning will be reported.
2972
            return self.report_infrastructure_issue_and_maybe_retry_build('The layout-test run with patch generated no list of results and exited with error, and the clean_tree without patch run did the same thing.')
2973
2974
        if with_patch_repeat_failures_results_exceed_failure_limit or without_patch_repeat_failures_results_exceed_failure_limit:
2975
            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.')
2976
2977
        if without_patch_repeat_failures_timedout:
2978
            return self.report_infrastructure_issue_and_maybe_retry_build('The step "layout-tests-repeat-failures-without-patch" was interrumped because it reached the timeout.')
2979
2980
        if with_patch_repeat_failures_timedout:
2981
            # 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)
2982
            # 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)
2983
            # Mark the patch as bad and report the tests that failured on the first run (without the ones that passed on the repeat).
2984
            # 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.
2985
            likely_new_non_flaky_failures = first_run_failures - without_patch_repeat_failures_results_nonflaky_failures.union(without_patch_repeat_failures_results_flakies)
2986
            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)))
2987
            return self.report_failure(likely_new_non_flaky_failures, first_results_exceed_failure_limit)
2988
2989
        # 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.
2990
        if not with_patch_repeat_failures_results_nonflaky_failures and not with_patch_repeat_failures_results_flakies:
2991
            with_patch_repeat_failures_retcode = self.getProperty('with_patch_repeat_failures_retcode', FAILURE)
2992
            if with_patch_repeat_failures_retcode not in [SUCCESS, WARNINGS]:
2993
                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.')
2994
2995
        # Check the same for the step without_patch_repeat_failures
2996
        if not without_patch_repeat_failures_results_nonflaky_failures and not without_patch_repeat_failures_results_flakies:
2997
            without_patch_repeat_failures_retcode = self.getProperty('without_patch_repeat_failures_retcode', FAILURE)
2998
            if without_patch_repeat_failures_retcode not in [SUCCESS, WARNINGS]:
2999
                return self.report_infrastructure_issue_and_maybe_retry_build('The step "layout-tests-repeat-failures-without-patch" failed to generate any list of failures or flakies and returned an error code.')
3000
3001
        # Warn EWS bot watchers about flakies
3002
        all_flaky_failures = first_run_flakies.union(with_patch_repeat_failures_results_flakies).union(without_patch_repeat_failures_results_flakies)
3003
        for flaky_failure in all_flaky_failures:
3004
            step_names = []
3005
            if flaky_failure in without_patch_repeat_failures_results_flakies:
3006
                step_names.append('layout-tests-repeat-failures-without-patch')
3007
            if flaky_failure in with_patch_repeat_failures_results_flakies:
3008
                step_names.append('layout-tests-repeat-failures (with patch)')
3009
            if flaky_failure in first_run_flakies:
3010
                step_names.append('layout-tests (with patch)')
3011
            step_names_str = '"{}"'.format('", "'.join(step_names))
3012
            self.send_email_for_flaky_failure(flaky_failure, step_names_str)
3013
3014
        # Warn EWS bot watchers about pre-existent non-flaky failures (if any), but
3015
        # send only one e-mail with all the tests to avoid sending too much e-mails.
3016
        pre_existent_non_flaky_failures = without_patch_repeat_failures_results_nonflaky_failures - with_patch_repeat_failures_results_nonflaky_failures.union(all_flaky_failures)
3017
        if pre_existent_non_flaky_failures:
3018
            self.send_email_for_pre_existent_failures(pre_existent_non_flaky_failures)
3019
3020
        # 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.
3021
        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)
3022
        if new_non_flaky_failures:
3023
            return self.report_failure(new_non_flaky_failures, first_results_exceed_failure_limit)
3024
3025
        return self.report_success()
3026
3027
2692
class ArchiveBuiltProduct(shell.ShellCommand):
3028
class ArchiveBuiltProduct(shell.ShellCommand):
2693
    command = ['python3', 'Tools/CISupport/built-product-archive',
3029
    command = ['python3', 'Tools/CISupport/built-product-archive',
2694
               WithProperties('--platform=%(fullPlatform)s'), WithProperties('--%(configuration)s'), 'archive']
3030
               WithProperties('--platform=%(fullPlatform)s'), WithProperties('--%(configuration)s'), 'archive']
- a/Tools/CISupport/ews-build/steps_unittest.py -6 / +364 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 1581-1588 class TestAnalyzeJSCTestsResults(BuildStepMixinAdditions, unittest.TestCase): a/Tools/CISupport/ews-build/steps_unittest.py_sec2
1581
        self.setProperty('jsc_clean_tree_stress_test_failures', [])
1582
        self.setProperty('jsc_clean_tree_stress_test_failures', [])
1582
        self.setProperty('jsc_clean_tree_binary_failures', [])
1583
        self.setProperty('jsc_clean_tree_binary_failures', [])
1583
        self.setProperty('jsc_clean_tree_flaky_and_passed', {})
1584
        self.setProperty('jsc_clean_tree_flaky_and_passed', {})
1584
        AnalyzeJSCTestsResults.send_email_for_flaky_failure = lambda self, test: None
1585
        AnalyzeJSCTestsResults.send_email_for_flaky_failure = lambda *args: None
1585
        AnalyzeJSCTestsResults.send_email_for_pre_existing_failure = lambda self, test: None
1586
        AnalyzeJSCTestsResults.send_email_for_pre_existing_failure = lambda *args: None
1586
1587
1587
    def test_single_new_stress_failure(self):
1588
    def test_single_new_stress_failure(self):
1588
        self.configureStep()
1589
        self.configureStep()
Lines 1915-1921 class TestReRunWebKitTests(TestRunWebKitTests): a/Tools/CISupport/ews-build/steps_unittest.py_sec3
1915
        self.setupStep(ReRunWebKitTests())
1916
        self.setupStep(ReRunWebKitTests())
1916
        self.property_exceed_failure_limit = 'second_results_exceed_failure_limit'
1917
        self.property_exceed_failure_limit = 'second_results_exceed_failure_limit'
1917
        self.property_failures = 'second_run_failures'
1918
        self.property_failures = 'second_run_failures'
1918
        ReRunWebKitTests.send_email_for_flaky_failure = lambda self, test: None
1919
        ReRunWebKitTests.send_email_for_flaky_failure = lambda *args: None
1919
1920
1920
    def test_flaky_failures_in_first_run(self):
1921
    def test_flaky_failures_in_first_run(self):
1921
        self.configureStep()
1922
        self.configureStep()
Lines 2318-2325 class TestAnalyzeLayoutTestsResults(BuildStepMixinAdditions, unittest.TestCase): a/Tools/CISupport/ews-build/steps_unittest.py_sec4
2318
        return self.tearDownBuildStep()
2319
        return self.tearDownBuildStep()
2319
2320
2320
    def configureStep(self):
2321
    def configureStep(self):
2321
        AnalyzeLayoutTestsResults.send_email_for_flaky_failure = lambda self, test: None
2322
        AnalyzeLayoutTestsResults.send_email_for_flaky_failure = lambda *args: None
2322
        AnalyzeLayoutTestsResults.send_email_for_pre_existing_failure = lambda self, test: None
2323
        AnalyzeLayoutTestsResults.send_email_for_pre_existing_failure = lambda *args: None
2323
        self.setupStep(AnalyzeLayoutTestsResults())
2324
        self.setupStep(AnalyzeLayoutTestsResults())
2324
        self.setProperty('first_results_exceed_failure_limit', False)
2325
        self.setProperty('first_results_exceed_failure_limit', False)
2325
        self.setProperty('second_results_exceed_failure_limit', False)
2326
        self.setProperty('second_results_exceed_failure_limit', False)
Lines 2519-2524 class TestAnalyzeLayoutTestsResults(BuildStepMixinAdditions, unittest.TestCase): a/Tools/CISupport/ews-build/steps_unittest.py_sec5
2519
        return self.runStep()
2520
        return self.runStep()
2520
2521
2521
2522
2523
class TestRunWebKitTestsRedTree(BuildStepMixinAdditions, unittest.TestCase):
2524
    def setUp(self):
2525
        self.longMessage = True
2526
        self.jsonFileName = 'layout-test-results/full_results.json'
2527
        return self.setUpBuildStep()
2528
2529
    def tearDown(self):
2530
        return self.tearDownBuildStep()
2531
2532
    def configureStep(self):
2533
        self.setupStep(RunWebKitTestsRedTree())
2534
2535
    def test_success(self):
2536
        self.configureStep()
2537
        self.setProperty('fullPlatform', 'gtk')
2538
        self.setProperty('configuration', 'release')
2539
        self.expectRemoteCommands(
2540
            ExpectShell(workdir='wkdir',
2541
                        logfiles={'json': self.jsonFileName},
2542
                        logEnviron=False,
2543
                        command=['python',
2544
                                 'Tools/Scripts/run-webkit-tests',
2545
                                 '--no-build', '--no-show-results', '--no-new-test-results', '--clobber-old-results',
2546
                                 '--release', '--results-directory', 'layout-test-results', '--debug-rwt-logging',
2547
                                 '--exit-after-n-failures', '500', '--skip-failing-tests']
2548
                        )
2549
            + 0,
2550
        )
2551
        self.expectOutcome(result=SUCCESS, state_string='Passed layout tests')
2552
        return self.runStep()
2553
2554
2555
class TestRunWebKitTestsRepeatFailuresRedTree(BuildStepMixinAdditions, unittest.TestCase):
2556
    def setUp(self):
2557
        self.longMessage = True
2558
        self.jsonFileName = 'layout-test-results/full_results.json'
2559
        return self.setUpBuildStep()
2560
2561
    def tearDown(self):
2562
        return self.tearDownBuildStep()
2563
2564
    def configureStep(self):
2565
        self.setupStep(RunWebKitTestsRepeatFailuresRedTree())
2566
2567
    def test_success(self):
2568
        self.configureStep()
2569
        self.setProperty('fullPlatform', 'gtk')
2570
        self.setProperty('configuration', 'release')
2571
        first_run_failures = ['fast/css/test1.html', 'imported/test/test2.html', 'fast/svg/test3.svg']
2572
        first_run_flakies = ['fast/css/flaky1.html', 'imported/test/flaky2.html', 'fast/svg/flaky3.svg']
2573
        self.setProperty('first_run_failures', first_run_failures)
2574
        self.setProperty('first_run_flakies', first_run_flakies)
2575
        self.expectRemoteCommands(
2576
            ExpectShell(workdir='wkdir',
2577
                        logfiles={'json': self.jsonFileName},
2578
                        logEnviron=False,
2579
                        maxTime=18000,
2580
                        command=['python',
2581
                                 'Tools/Scripts/run-webkit-tests',
2582
                                 '--no-build', '--no-show-results', '--no-new-test-results', '--clobber-old-results',
2583
                                 '--release', '--results-directory', 'layout-test-results', '--debug-rwt-logging',
2584
                                 '--skip-failing-tests', '--repeat-each=10'] + sorted(first_run_failures)
2585
                        )
2586
            + 0,
2587
        )
2588
        self.expectOutcome(result=SUCCESS, state_string='layout-tests')
2589
        return self.runStep()
2590
2591
2592
class TestRunWebKitTestsRepeatFailuresWithoutPatchRedTree(BuildStepMixinAdditions, unittest.TestCase):
2593
    def setUp(self):
2594
        self.longMessage = True
2595
        self.jsonFileName = 'layout-test-results/full_results.json'
2596
        return self.setUpBuildStep()
2597
2598
    def tearDown(self):
2599
        return self.tearDownBuildStep()
2600
2601
    def configureStep(self):
2602
        self.setupStep(RunWebKitTestsRepeatFailuresWithoutPatchRedTree())
2603
2604
    def test_success(self):
2605
        self.configureStep()
2606
        self.setProperty('fullPlatform', 'gtk')
2607
        self.setProperty('configuration', 'release')
2608
        first_run_failures = ['fast/css/test1.html', 'imported/test/test2.html', 'fast/svg/test3.svg']
2609
        first_run_flakies = ['fast/css/flaky1.html', 'imported/test/flaky2.html', 'fast/svg/flaky3.svg']
2610
        with_patch_repeat_failures_results_nonflaky_failures = ['fast/css/test1.html']
2611
        with_patch_repeat_failures_results_flakies = ['imported/test/test2.html', 'fast/svg/test3.svg']
2612
        self.setProperty('first_run_failures', first_run_failures)
2613
        self.setProperty('first_run_flakies', first_run_flakies)
2614
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', with_patch_repeat_failures_results_nonflaky_failures)
2615
        self.setProperty('with_patch_repeat_failures_results_flakies', with_patch_repeat_failures_results_flakies)
2616
        self.expectRemoteCommands(
2617
            ExpectShell(workdir='wkdir',
2618
                        logfiles={'json': self.jsonFileName},
2619
                        logEnviron=False,
2620
                        maxTime=10800,
2621
                        command=['python',
2622
                                 'Tools/Scripts/run-webkit-tests',
2623
                                 '--no-build', '--no-show-results', '--no-new-test-results', '--clobber-old-results',
2624
                                 '--release', '--results-directory', 'layout-test-results', '--debug-rwt-logging',
2625
                                 '--skip-failing-tests', '--repeat-each=10', '--skipped=always'] + sorted(with_patch_repeat_failures_results_nonflaky_failures)
2626
                        )
2627
            + 0,
2628
        )
2629
        self.expectOutcome(result=SUCCESS, state_string='layout-tests')
2630
        return self.runStep()
2631
2632
    def test_step_with_patch_did_timeout(self):
2633
        self.configureStep()
2634
        self.setProperty('fullPlatform', 'gtk')
2635
        self.setProperty('configuration', 'release')
2636
        first_run_failures = ['fast/css/test1.html', 'imported/test/test2.html', 'fast/svg/test3.svg']
2637
        first_run_flakies = ['fast/css/flaky1.html', 'imported/test/flaky2.html', 'fast/svg/flaky3.svg']
2638
        with_patch_repeat_failures_results_nonflaky_failures = ['fast/css/test1.html']
2639
        with_patch_repeat_failures_results_flakies = ['imported/test/test2.html', 'fast/svg/test3.svg']
2640
        self.setProperty('first_run_failures', first_run_failures)
2641
        self.setProperty('first_run_flakies', first_run_flakies)
2642
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', with_patch_repeat_failures_results_nonflaky_failures)
2643
        self.setProperty('with_patch_repeat_failures_results_flakies', with_patch_repeat_failures_results_flakies)
2644
        self.setProperty('with_patch_repeat_failures_timedout', True)
2645
        self.expectRemoteCommands(
2646
            ExpectShell(workdir='wkdir',
2647
                        logfiles={'json': self.jsonFileName},
2648
                        logEnviron=False,
2649
                        maxTime=10800,
2650
                        command=['python',
2651
                                 'Tools/Scripts/run-webkit-tests',
2652
                                 '--no-build', '--no-show-results', '--no-new-test-results', '--clobber-old-results',
2653
                                 '--release', '--results-directory', 'layout-test-results', '--debug-rwt-logging',
2654
                                 '--skip-failing-tests', '--repeat-each=10', '--skipped=always'] + sorted(first_run_failures)
2655
                        )
2656
            + 0,
2657
        )
2658
        self.expectOutcome(result=SUCCESS, state_string='layout-tests')
2659
        return self.runStep()
2660
2661
2662
class TestAnalyzeLayoutTestsResultsRedTree(BuildStepMixinAdditions, unittest.TestCase):
2663
    def setUp(self):
2664
        self.longMessage = True
2665
        return self.setUpBuildStep()
2666
2667
    def tearDown(self):
2668
        return self.tearDownBuildStep()
2669
2670
    def configureStep(self):
2671
        AnalyzeLayoutTestsResults.send_email_for_flaky_failure = lambda *args: None
2672
        AnalyzeLayoutTestsResults.send_email_for_pre_existing_failure = lambda *args: None
2673
        self.setupStep(AnalyzeLayoutTestsResultsRedTree())
2674
2675
    def test_failure_introduced_by_patch_clean_tree_green(self):
2676
        self.configureStep()
2677
        self.setProperty('fullPlatform', 'gtk')
2678
        self.setProperty('configuration', 'release')
2679
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html"])
2680
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2681
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html"])
2682
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2683
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', [])
2684
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2685
        self.expectOutcome(result=FAILURE, state_string='Found 1 new test failure: test/failure1.html (failure)')
2686
        return self.runStep()
2687
2688
    def test_failure_introduced_by_patch_clean_tree_red(self):
2689
        self.configureStep()
2690
        self.setProperty('fullPlatform', 'gtk')
2691
        self.setProperty('configuration', 'release')
2692
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2693
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2694
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2695
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2696
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2697
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2698
        self.expectOutcome(result=FAILURE, state_string='Found 1 new test failure: test/failure1.html (failure)')
2699
        return self.runStep()
2700
2701
    def test_pre_existent_failures(self):
2702
        self.configureStep()
2703
        self.setProperty('first_run_failures', ["test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2704
        self.setProperty('first_run_flakies', ["test/pre-existent/flaky2.html", "test/pre-existent/flaky3.html"])
2705
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2706
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2707
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2708
        self.setProperty('without_patch_repeat_failures_results_flakies', [])
2709
        self.expectOutcome(result=SUCCESS, state_string='Passed layout tests')
2710
        return self.runStep()
2711
2712
    def test_pre_existent_flakies(self):
2713
        self.configureStep()
2714
        self.setProperty('first_run_failures', ["test/pre-existent/flaky1.html"])
2715
        self.setProperty('first_run_flakies', ["test/pre-existent/flaky2.html", "test/pre-existent/flaky3.html"])
2716
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', [])
2717
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky1.html"])
2718
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', [])
2719
        self.setProperty('without_patch_repeat_failures_results_flakies', [])
2720
        self.setProperty('without_patch_repeat_failures_retcode', SUCCESS)
2721
        self.expectOutcome(result=SUCCESS, state_string='Passed layout tests')
2722
        return self.runStep()
2723
2724
    def test_first_step_gives_unexpected_failure_and_clean_tree_pass_last_try(self):
2725
        self.configureStep()
2726
        self.setProperty('fullPlatform', 'gtk')
2727
        self.setProperty('configuration', 'release')
2728
        self.setProperty('first_run_failures', [])
2729
        self.setProperty('first_run_flakies', [])
2730
        self.setProperty('retry_count', AnalyzeLayoutTestsResultsRedTree.MAX_RETRY)
2731
        self.setProperty('clean_tree_run_failures', [])
2732
        self.setProperty('clean_tree_run_flakies', ['test/pre-existent/flaky.html'])
2733
        self.setProperty('clean_tree_run_status', WARNINGS)
2734
        self.expectOutcome(result=FAILURE, state_string='Found unexpected failure with patch (failure)')
2735
        return self.runStep()
2736
2737
    def test_first_step_gives_unexpected_failure_and_clean_tree_unexpected_failure_last_try(self):
2738
        self.configureStep()
2739
        self.setProperty('fullPlatform', 'gtk')
2740
        self.setProperty('configuration', 'release')
2741
        self.setProperty('first_run_failures', [])
2742
        self.setProperty('first_run_flakies', [])
2743
        self.setProperty('retry_count', AnalyzeLayoutTestsResultsRedTree.MAX_RETRY)
2744
        self.setProperty('clean_tree_run_failures', [])
2745
        self.setProperty('clean_tree_run_flakies', [])
2746
        self.setProperty('clean_tree_run_status', FAILURE)
2747
        self.expectOutcome(result=WARNINGS, state_string='The layout-test run with patch generated no list of results and exited with error, and the clean_tree without patch run did the same thing.\nReached the maximum number of retries (3). Unable to determine if patch is bad or there is a pre-existent infrastructure issue. (warnings)')
2748
        return self.runStep()
2749
2750
    def test_first_step_gives_unexpected_failure_retry(self):
2751
        self.configureStep()
2752
        self.setProperty('fullPlatform', 'gtk')
2753
        self.setProperty('configuration', 'release')
2754
        self.setProperty('first_run_failures', [])
2755
        self.setProperty('first_run_flakies', [])
2756
        self.setProperty('clean_tree_run_failures', [])
2757
        self.setProperty('retry_count', AnalyzeLayoutTestsResultsRedTree.MAX_RETRY - 1)
2758
        self.setProperty('clean_tree_run_flakies', ['test/pre-existent/flaky.html'])
2759
        self.setProperty('clean_tree_run_status', WARNINGS)
2760
        self.expectOutcome(result=RETRY, state_string='Unexpected infrastructure issue: The layout-test run with patch generated no list of results and exited with error, retrying with the hope it was a random infrastructure error.\nRetrying build [retry count is 2 of 3] (retry)')
2761
        return self.runStep()
2762
2763
    def test_step_retry_with_patch_exits_early_error(self):
2764
        self.configureStep()
2765
        self.setProperty('fullPlatform', 'gtk')
2766
        self.setProperty('configuration', 'release')
2767
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2768
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2769
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2770
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2771
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2772
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2773
        self.setProperty('with_patch_repeat_failures_results_exceed_failure_limit', True)
2774
        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)')
2775
        return self.runStep()
2776
2777
    def test_step_retry_without_patch_exits_early_error(self):
2778
        self.configureStep()
2779
        self.setProperty('fullPlatform', 'gtk')
2780
        self.setProperty('configuration', 'release')
2781
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2782
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2783
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2784
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2785
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2786
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2787
        self.setProperty('without_patch_repeat_failures_results_exceed_failure_limit', True)
2788
        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)')
2789
        return self.runStep()
2790
2791
    def test_step_retry_with_patch_timeouts(self):
2792
        self.configureStep()
2793
        self.setProperty('fullPlatform', 'gtk')
2794
        self.setProperty('configuration', 'release')
2795
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2796
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2797
        self.setProperty('with_patch_repeat_failures_timedout', True)
2798
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', ["test/pre-existent/failure.html"])
2799
        self.setProperty('without_patch_repeat_failures_results_flakies', ["test/pre-existent/flaky.html"])
2800
        self.expectOutcome(result=FAILURE, state_string='Found 2 new test failures: test/failure1.html, test/failure2.html (failure)')
2801
        return self.runStep()
2802
2803
    def test_step_retry_with_patch_unexpected_error(self):
2804
        self.configureStep()
2805
        self.setProperty('fullPlatform', 'gtk')
2806
        self.setProperty('configuration', 'release')
2807
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2808
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2809
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', [])
2810
        self.setProperty('with_patch_repeat_failures_results_flakies', [])
2811
        self.setProperty('with_patch_repeat_failures_retcode', FAILURE)
2812
        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)')
2813
        return self.runStep()
2814
2815
    def test_step_retry_without_patch_unexpected_error(self):
2816
        self.configureStep()
2817
        self.setProperty('fullPlatform', 'gtk')
2818
        self.setProperty('configuration', 'release')
2819
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2820
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2821
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/failure2.html"])
2822
        self.setProperty('with_patch_repeat_failures_results_flakies', [])
2823
        self.setProperty('with_patch_repeat_failures_retcode', FAILURE)
2824
        self.setProperty('without_patch_repeat_failures_results_nonflaky_failures', [])
2825
        self.setProperty('without_patch_repeat_failures_results_flakies', [])
2826
        self.setProperty('without_patch_repeat_failures_retcode', FAILURE)
2827
        self.expectOutcome(result=RETRY, state_string='Unexpected infrastructure issue: The step "layout-tests-repeat-failures-without-patch" failed to generate any list of failures or flakies and returned an error code.\nRetrying build [retry count is 0 of 3] (retry)')
2828
        return self.runStep()
2829
2830
    def test_step_retry_without_patch_timeouts(self):
2831
        self.configureStep()
2832
        self.setProperty('fullPlatform', 'gtk')
2833
        self.setProperty('configuration', 'release')
2834
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2835
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2836
        self.setProperty('with_patch_repeat_failures_timedout', True)
2837
        self.setProperty('without_patch_repeat_failures_timedout', True)
2838
        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)')
2839
        return self.runStep()
2840
2841
    def test_step_retry_with_patch_timeouts_and_without_patch_timeouts(self):
2842
        self.configureStep()
2843
        self.setProperty('fullPlatform', 'gtk')
2844
        self.setProperty('configuration', 'release')
2845
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2846
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2847
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2848
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2849
        self.setProperty('without_patch_repeat_failures_timedout', True)
2850
        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)')
2851
        return self.runStep()
2852
2853
    def test_retry_third_time(self):
2854
        self.configureStep()
2855
        self.setProperty('fullPlatform', 'gtk')
2856
        self.setProperty('configuration', 'release')
2857
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2858
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2859
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2860
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2861
        self.setProperty('without_patch_repeat_failures_timedout', True)
2862
        self.setProperty('retry_count', 2)
2863
        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)')
2864
        return self.runStep()
2865
2866
    def test_retry_finish(self):
2867
        self.configureStep()
2868
        self.setProperty('fullPlatform', 'gtk')
2869
        self.setProperty('configuration', 'release')
2870
        self.setProperty('first_run_failures', ["test/failure1.html", "test/failure2.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2871
        self.setProperty('first_run_flakies', ["test/flaky1.html", "test/flaky2.html"])
2872
        self.setProperty('with_patch_repeat_failures_results_nonflaky_failures', ["test/failure1.html", "test/pre-existent/failure.html", "test/pre-existent/flaky.html"])
2873
        self.setProperty('with_patch_repeat_failures_results_flakies', ["test/failure2.html"])
2874
        self.setProperty('without_patch_repeat_failures_timedout', True)
2875
        self.setProperty('retry_count', 3)
2876
        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)')
2877
        return self.runStep()
2878
2879
2522
class TestCheckOutSpecificRevision(BuildStepMixinAdditions, unittest.TestCase):
2880
class TestCheckOutSpecificRevision(BuildStepMixinAdditions, unittest.TestCase):
2523
    def setUp(self):
2881
    def setUp(self):
2524
        self.longMessage = True
2882
        self.longMessage = True

Return to Bug 231999