Add support for webkitpy tests EWS
[WebKit-https.git] / Tools / Scripts / webkitpy / tool / bot / retrylogic_unittest.py
1 # Copyright (C) 2017 Apple Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1.  Redistributions of source code must retain the above copyright
7 #     notice, this list of conditions and the following disclaimer.
8 # 2.  Redistributions in binary form must reproduce the above copyright
9 #     notice, this list of conditions and the following disclaimer in the
10 #     documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23 import logging
24 import unittest
25
26 from webkitpy.common.net.generictestresults import BindingsTestResults
27 from webkitpy.common.net.jsctestresults import JSCTestResults
28 from webkitpy.common.system.executive import ScriptError
29 from webkitpy.tool.bot.patchanalysistask import *
30 from webkitpy.tool.commands.earlywarningsystem import AbstractEarlyWarningSystem
31 from webkitpy.tool.mocktool import MockTool
32
33 _log = logging.getLogger(__name__)
34
35
36 class MockPatchAnalysisTask(PatchAnalysisTask):
37     def __init__(self, delegate, patch, patches_passed_all_tests):
38         self._delegate = delegate
39         self._patch = patch
40         self._script_error = None
41         self._results_archive_from_patch_test_run = None
42         self._results_from_patch_test_run = None
43         self.error = None
44         self._patches_passed_all_tests = patches_passed_all_tests
45         self._test_run_count = 0
46         self.failure_status_id = 0
47
48     def _test(self):
49         self._test_run_count += 1
50
51         if self._patches_passed_all_tests.pop() == True:
52             return True
53         self._script_error = ScriptError('Regression test')
54         return False
55
56     def _build_and_test_without_patch(self):
57         self._test_run_count += 1
58         return True
59
60     def validate(self):
61         return True
62
63     def test_run_count(self):
64         return self._test_run_count
65
66
67 # This is the delegate to be used with MockPatchAnalysisTask, above.
68 class MockJSCEarlyWarningSystem(AbstractEarlyWarningSystem):
69     def __init__(self, first_test_results, second_test_results, clean_test_results):
70         AbstractEarlyWarningSystem.__init__(self)
71         self._group = 'jsc'
72         self._results_in_order = [clean_test_results, second_test_results, first_test_results]
73
74     def test_results(self):
75         return self._results_in_order.pop()
76
77
78 class JSCEarlyWarningSystemTest(unittest.TestCase):
79     def _results_indicate_all_passed(self, results):
80         if results == None:
81             return False
82         return results.all_passed()
83
84     def _create_task(self, first_test_results, second_test_results, clean_test_results):
85         queue = MockJSCEarlyWarningSystem(first_test_results, second_test_results, clean_test_results)
86         tool = MockTool(log_executive=True)
87         patch = tool.bugs.fetch_attachment(10000)
88         patches_passed_all_tests = map(self._results_indicate_all_passed, [second_test_results, first_test_results])
89         return MockPatchAnalysisTask(queue, patch, patches_passed_all_tests)
90
91     def test_success_case(self):
92         first_test_results = JSCTestResults(True, [])
93         second_test_results = JSCTestResults(True, [])
94         clean_test_results = JSCTestResults(True, [])
95         task = self._create_task(first_test_results, second_test_results, clean_test_results)
96
97         return_value = task._test_patch()
98         self.assertEqual(task.test_run_count(), 1)
99         self.assertTrue(return_value)
100
101     def test_test_failure(self):
102         first_test_results = JSCTestResults(True, ['Fail.js'])
103         second_test_results = JSCTestResults(True, ['Fail.js'])
104         clean_test_results = JSCTestResults(True, [])
105         task = self._create_task(first_test_results, second_test_results, clean_test_results)
106
107         with self.assertRaises(ScriptError):
108             return_value = task._test_patch()
109         self.assertEqual(task.test_run_count(), 3)
110
111     def test_fix(self):
112         first_test_results = JSCTestResults(True, [])
113         second_test_results = JSCTestResults(True, [])
114         clean_test_results = JSCTestResults(True, ['Fail.js'])
115         task = self._create_task(first_test_results, second_test_results, clean_test_results)
116
117         return_value = task._test_patch()
118         self.assertEqual(task.test_run_count(), 1)
119         self.assertTrue(return_value)
120
121     def test_ineffective_patch(self):
122         first_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
123         second_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
124         clean_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
125         task = self._create_task(first_test_results, second_test_results, clean_test_results)
126
127         return_value = task._test_patch()
128         self.assertEqual(task.test_run_count(), 3)
129         self.assertTrue(return_value)
130
131     def test_partially_effective_patch(self):
132         first_test_results = JSCTestResults(True, ['failure2.js'])
133         second_test_results = JSCTestResults(True, ['failure2.js'])
134         clean_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
135         task = self._create_task(first_test_results, second_test_results, clean_test_results)
136
137         return_value = task._test_patch()
138         self.assertEqual(task.test_run_count(), 3)
139         self.assertTrue(return_value)
140
141     def test_different_test_failures_in_patch_and_tree(self):
142         first_test_results = JSCTestResults(False, [])
143         second_test_results = JSCTestResults(False, [])
144         clean_test_results = JSCTestResults(True, ['failure1.js', 'failure2.js'])
145         task = self._create_task(first_test_results, second_test_results, clean_test_results)
146
147         with self.assertRaises(ScriptError):
148             return_value = task._test_patch()
149         self.assertEqual(task.test_run_count(), 3)
150
151     def test_first_results_could_not_be_read(self):
152         first_test_results = None
153         second_test_results = JSCTestResults(True, [])
154         clean_test_results = JSCTestResults(True, [])
155         task = self._create_task(first_test_results, second_test_results, clean_test_results)
156
157         return_value = task._test_patch()
158         self.assertEqual(task.test_run_count(), 1)
159         self.assertFalse(return_value)
160
161     def test_second_results_could_not_be_read(self):
162         first_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
163         second_test_results = None
164         clean_test_results = JSCTestResults(True, [])
165         task = self._create_task(first_test_results, second_test_results, clean_test_results)
166
167         return_value = task._test_patch()
168         self.assertEqual(task.test_run_count(), 2)
169         self.assertFalse(return_value)
170
171     def test_clean_results_could_not_be_read(self):
172         first_test_results = JSCTestResults(True, ['failure2.js'])
173         second_test_results = JSCTestResults(True, ['failure2.js'])
174         clean_test_results = None
175         task = self._create_task(first_test_results, second_test_results, clean_test_results)
176
177         return_value = task._test_patch()
178         self.assertEqual(task.test_run_count(), 3)
179         self.assertFalse(return_value)
180
181     def test_flaky_results_on_clean_tree_pass(self):
182         first_test_results = JSCTestResults(True, ['failure2.js'])
183         second_test_results = JSCTestResults(True, [])
184         clean_test_results = JSCTestResults(True, [])
185         task = self._create_task(first_test_results, second_test_results, clean_test_results)
186
187         return_value = task._test_patch()
188         self.assertTrue(return_value)
189         self.assertEqual(task.test_run_count(), 2)
190
191     def test_flaky_results_on_clean_tree_pass_v2(self):
192         first_test_results = JSCTestResults(True, [])
193         second_test_results = JSCTestResults(True, ['failure2.js'])
194         clean_test_results = JSCTestResults(True, [])
195         task = self._create_task(first_test_results, second_test_results, clean_test_results)
196
197         return_value = task._test_patch()
198         self.assertTrue(return_value)
199         self.assertEqual(task.test_run_count(), 1)
200
201     def test_flaky_results_on_clean_tree_failure(self):
202         first_test_results = JSCTestResults(False, ['failure1.js', 'failure2.js'])
203         second_test_results = JSCTestResults(True, ['failure2.js'])
204         clean_test_results = JSCTestResults(True, [])
205         task = self._create_task(first_test_results, second_test_results, clean_test_results)
206
207         with self.assertRaises(ScriptError):
208             task._test_patch()
209         self.assertEqual(task.test_run_count(), 3)
210
211     def test_flaky_results_on_red_tree_pass(self):
212         first_test_results = JSCTestResults(True, ['failure1.js'])
213         second_test_results = JSCTestResults(True, ['failure1.js', 'failure2.js'])
214         clean_test_results = JSCTestResults(True, ['failure1.js'])
215         task = self._create_task(first_test_results, second_test_results, clean_test_results)
216
217         return_value = task._test_patch()
218         self.assertTrue(return_value)
219         self.assertEqual(task.test_run_count(), 3)
220
221
222 class MockBindingsEarlyWarningSystem(AbstractEarlyWarningSystem):
223     def __init__(self, first_test_results, clean_test_results):
224         AbstractEarlyWarningSystem.__init__(self)
225         self._group = 'bindings'
226         self._results_in_order = [clean_test_results, first_test_results]
227
228     def test_results(self):
229         return self._results_in_order.pop()
230
231
232 class BindingsEarlyWarningSystemTest(unittest.TestCase):
233     def _results_indicate_all_passed(self, results):
234         if results == None:
235             return False
236         return results.all_passed()
237
238     def _create_task(self, first_test_results, clean_test_results):
239         queue = MockBindingsEarlyWarningSystem(first_test_results, clean_test_results)
240         tool = MockTool(log_executive=True)
241         patch = tool.bugs.fetch_attachment(10000)
242         patches_passed_all_tests = [self._results_indicate_all_passed(first_test_results)]
243         return MockPatchAnalysisTask(queue, patch, patches_passed_all_tests)
244
245     def test_success_case(self):
246         first_test_results = BindingsTestResults([])
247         clean_test_results = BindingsTestResults([])
248         task = self._create_task(first_test_results, clean_test_results)
249
250         return_value = task._test_patch()
251         self.assertEqual(task.test_run_count(), 1)
252         self.assertTrue(return_value)
253
254     def test_test_failure(self):
255         first_test_results = BindingsTestResults(['TestMapLike.idl'])
256         clean_test_results = BindingsTestResults([])
257         task = self._create_task(first_test_results, clean_test_results)
258
259         with self.assertRaises(ScriptError):
260             return_value = task._test_patch()
261         self.assertEqual(task.test_run_count(), 2)
262
263     def test_fix(self):
264         first_test_results = BindingsTestResults([])
265         clean_test_results = BindingsTestResults(['TestMapLike.idl'])
266         task = self._create_task(first_test_results, clean_test_results)
267
268         return_value = task._test_patch()
269         self.assertEqual(task.test_run_count(), 1)
270         self.assertTrue(return_value)
271
272     def test_ineffective_patch(self):
273         first_test_results = BindingsTestResults(['TestMapLike.idl'])
274         clean_test_results = BindingsTestResults(['TestMapLike.idl'])
275         task = self._create_task(first_test_results, clean_test_results)
276
277         return_value = task._test_patch()
278         self.assertEqual(task.test_run_count(), 2)
279         self.assertTrue(return_value)
280
281     def test_partially_effective_patch(self):
282         first_test_results = BindingsTestResults(['TestMapLike.idl'])
283         clean_test_results = BindingsTestResults(['TestMapLike.idl', 'TestNode.idl'])
284         task = self._create_task(first_test_results, clean_test_results)
285
286         return_value = task._test_patch()
287         self.assertEqual(task.test_run_count(), 2)
288         self.assertTrue(return_value)
289
290     def test_different_test_failures_in_patch_and_tree(self):
291         first_test_results = BindingsTestResults(['TestNode.idl'])
292         clean_test_results = BindingsTestResults(['TestMapLike.idl'])
293         task = self._create_task(first_test_results, clean_test_results)
294
295         with self.assertRaises(ScriptError):
296             return_value = task._test_patch()
297         self.assertEqual(task.test_run_count(), 2)