Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(589)

Side by Side Diff: scripts/slave/recipe_modules/auto_bisect/bisector_test.py

Issue 940123005: Adding ability to bisect recipe to bisect into dependency repos. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@hax
Patch Set: Addressing feedback. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import os 5 import os
6 import sys 6 import sys
7 import unittest 7 import unittest
8 8
9 # TODO(robertocn): Use abspath for these, to prevent relative path errors. 9 # TODO(robertocn): Use abspath for these, to prevent relative path errors.
10 _RECIPE_MODULES_DIR = os.path.join(os.path.dirname(__file__), os.path.pardir) 10 _RECIPE_MODULES_DIR = os.path.join(os.path.dirname(__file__), os.path.pardir)
11 # For the importing of mock. 11 # For the importing of mock.
12 _ROOT_DIR = os.path.join(os.path.dirname(__file__), os.path.pardir, 12 _ROOT_DIR = os.path.join(os.path.dirname(__file__), os.path.pardir,
13 os.path.pardir, os.path.pardir, os.path.pardir) 13 os.path.pardir, os.path.pardir, os.path.pardir)
14 14
15 sys.path.append(_RECIPE_MODULES_DIR) 15 sys.path.append(_RECIPE_MODULES_DIR)
16 sys.path.append(os.path.join(_ROOT_DIR, 'third_party', 'mock-1.0.1')) 16 sys.path.append(os.path.join(_ROOT_DIR, 'third_party', 'mock-1.0.1'))
17 17
18 import mock 18 import mock
19 19
20 from auto_bisect.bisector import Bisector 20 from auto_bisect.bisector import Bisector
21 21
22
23 class MockRevisionClass(object): # pragma: no cover
24 def __init__(self, rev_string, bisector):
25 self.commit_pos = int(rev_string)
26 self.revision_string = rev_string
27 self.bisector = bisector
28 self.previous_revision = None
29 self.next_revision = None
30 self.values = []
31
32 def get_next_url(self):
33 if self.in_progress:
34 return 'mockurl'
35 return None
36
37
22 class BisectorTest(unittest.TestCase): # pragma: no cover 38 class BisectorTest(unittest.TestCase): # pragma: no cover
23 def setUp(self): 39 def setUp(self):
24 self.bisect_config = { 40 self.bisect_config = {
25 'test_type': 'perf', 41 'test_type': 'perf',
26 'command': 'tools/perf/run_benchmark -v ' 42 'command': 'tools/perf/run_benchmark -v '
27 '--browser=release page_cycler.intl_ar_fa_he', 43 '--browser=release page_cycler.intl_ar_fa_he',
28 'good_revision': '306475', 44 'good_revision': '306475',
29 'bad_revision': '306478', 45 'bad_revision': '306478',
30 'metric': 'warm_times/page_load_time', 46 'metric': 'warm_times/page_load_time',
31 'repeat_count': '2', 47 'repeat_count': '2',
32 'max_time_minutes': '5', 48 'max_time_minutes': '5',
33 'truncate_percent': '25', 49 'truncate_percent': '25',
34 'bug_id': '425582', 50 'bug_id': '425582',
35 'gs_bucket': 'chrome-perf', 51 'gs_bucket': 'chrome-perf',
36 'builder_host': 'master4.golo.chromium.org', 52 'builder_host': 'master4.golo.chromium.org',
37 'builder_port': '8341', 53 'builder_port': '8341',
38 'dummy_builds': True, 54 'dummy_builds': True,
39 } 55 }
40 self.dummy_api = mock.Mock() 56 self.dummy_api = mock.Mock()
41 57
42 class MockRevisionClass(object):
43 def __init__(self, rev_string, bisector):
44 self.commit_pos = int(rev_string)
45 self.revision_string = rev_string
46 self.bisector = bisector
47 self.previous_revision = None
48 self.next_revision = None
49 self.values = []
50 58
51 def get_next_url(self):
52 if self.in_progress:
53 return 'mockurl'
54 return None
55 59
56 def test_create_bisector(self): 60 def test_create_bisector(self):
57 new_bisector = Bisector(self.dummy_api, self.bisect_config, 61 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
58 self.MockRevisionClass)
59 # Check the proper revision range is initialized 62 # Check the proper revision range is initialized
60 self.assertEqual(4, len(new_bisector.revisions)) 63 self.assertEqual(4, len(bisector.revisions))
61 a, b, c, d = new_bisector.revisions 64 a, b, c, d = bisector.revisions
62 # Check that revisions are properly chained 65 # Check that revisions are properly chained
63 self.assertEqual(a, b.previous_revision) 66 self.assertEqual(a, b.previous_revision)
64 self.assertEqual(b, c.previous_revision) 67 self.assertEqual(b, c.previous_revision)
65 self.assertEqual(c, d.previous_revision) 68 self.assertEqual(c, d.previous_revision)
66 self.assertEqual(d, c.next_revision) 69 self.assertEqual(d, c.next_revision)
67 self.assertEqual(c, b.next_revision) 70 self.assertEqual(c, b.next_revision)
68 self.assertEqual(b, a.next_revision) 71 self.assertEqual(b, a.next_revision)
69 72
70 # Check the ends are grounded 73 # Check the ends are grounded
71 self.assertIsNone(a.previous_revision) 74 self.assertIsNone(a.previous_revision)
72 self.assertIsNone(d.next_revision) 75 self.assertIsNone(d.next_revision)
73 76
74 # Check the reference range is set with correct 'goodness' values 77 # Check the reference range is set with correct 'goodness' values
75 self.assertTrue(a.good) 78 self.assertTrue(a.good)
76 self.assertTrue(d.bad) 79 self.assertTrue(d.bad)
77 80
78 def test_improvement_direction_default(self): 81 def test_improvement_direction_default(self):
79 # By default, no improvement direction should be set 82 # By default, no improvement direction should be set
80 new_bisector = Bisector(self.dummy_api, self.bisect_config, 83 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
81 self.MockRevisionClass) 84 self.assertIsNone(bisector.improvement_direction)
82 self.assertIsNone(new_bisector.improvement_direction)
83 85
84 def test_improvement_direction_greater_is_better(self): 86 def test_improvement_direction_greater_is_better(self):
85 # Improvement up, bad > good: should fail 87 # Improvement up, bad > good: should fail
86 self.bisect_config['improvement_direction'] = 1 88 self.bisect_config['improvement_direction'] = 1
87 new_bisector = Bisector(self.dummy_api, self.bisect_config, 89 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
88 self.MockRevisionClass) 90 bisector.good_rev.mean_value = 10
89 new_bisector.good_rev.mean_value = 10 91 bisector.bad_rev.mean_value = 100
90 new_bisector.bad_rev.mean_value = 100 92 self.assertFalse(bisector.check_improvement_direction())
91 self.assertFalse(new_bisector.check_improvement_direction()) 93 self.assertIn('direction of improvement', ''.join(bisector.warnings))
92 self.assertIn('direction of improvement', ''.join(new_bisector.warnings))
93 94
94 # Improvement up, bad < good: should not fail 95 # Improvement up, bad < good: should not fail
95 self.bisect_config['improvement_direction'] = 1 96 self.bisect_config['improvement_direction'] = 1
96 new_bisector = Bisector(self.dummy_api, self.bisect_config, 97 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
97 self.MockRevisionClass) 98 bisector.good_rev.mean_value = 100
98 new_bisector.good_rev.mean_value = 100 99 bisector.bad_rev.mean_value = 10
99 new_bisector.bad_rev.mean_value = 10 100 self.assertTrue(bisector.check_improvement_direction())
100 self.assertTrue(new_bisector.check_improvement_direction()) 101 self.assertNotIn('direction of improvement', ''.join(bisector.warnings))
101 self.assertNotIn('direction of improvement', ''.join(new_bisector.warnings))
102 102
103 def test_improvement_direction_lower_is_better(self): 103 def test_improvement_direction_lower_is_better(self):
104 # Improvement down, bad < good: should fail 104 # Improvement down, bad < good: should fail
105 self.bisect_config['improvement_direction'] = -1 105 self.bisect_config['improvement_direction'] = -1
106 new_bisector = Bisector(self.dummy_api, self.bisect_config, 106 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
107 self.MockRevisionClass) 107 bisector.good_rev.mean_value = 100
108 new_bisector.good_rev.mean_value = 100 108 bisector.bad_rev.mean_value = 10
109 new_bisector.bad_rev.mean_value = 10 109 self.assertFalse(bisector.check_improvement_direction())
110 self.assertFalse(new_bisector.check_improvement_direction()) 110 self.assertIn('direction of improvement', ''.join(bisector.warnings))
111 self.assertIn('direction of improvement', ''.join(new_bisector.warnings))
112 111
113 # Improvement down, bad > good: should not fail 112 # Improvement down, bad > good: should not fail
114 self.bisect_config['improvement_direction'] = -1 113 self.bisect_config['improvement_direction'] = -1
115 new_bisector = Bisector(self.dummy_api, self.bisect_config, 114 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
116 self.MockRevisionClass) 115 bisector.good_rev.mean_value = 10
117 new_bisector.good_rev.mean_value = 10 116 bisector.bad_rev.mean_value = 100
118 new_bisector.bad_rev.mean_value = 100 117 self.assertTrue(bisector.check_improvement_direction())
119 self.assertTrue(new_bisector.check_improvement_direction()) 118 self.assertNotIn('direction of improvement', ''.join(bisector.warnings))
120 self.assertNotIn('direction of improvement', ''.join(new_bisector.warnings))
121 119
122 def test_check_regression_confidence_default(self): 120 def test_check_regression_confidence_default(self):
123 # Test default required confidence (default may change) 121 # Test default required confidence (default may change)
124 mock_score = self.dummy_api.m.math_utils.confidence_score 122 mock_score = self.dummy_api.m.math_utils.confidence_score
125 # A confidence score of 0 should not satisfy any default 123 # A confidence score of 0 should not satisfy any default
126 mock_score.return_value = 0 124 mock_score.return_value = 0
127 new_bisector = Bisector(self.dummy_api, self.bisect_config, 125 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
128 self.MockRevisionClass) 126 self.assertFalse(bisector.check_regression_confidence())
129 self.assertFalse(new_bisector.check_regression_confidence()) 127 self.assertTrue(bisector.failed_confidence)
130 self.assertTrue(new_bisector.failed_confidence)
131 128
132 # A confidence score of 100 should satisfy any default 129 # A confidence score of 100 should satisfy any default
133 mock_score.return_value = 100 130 mock_score.return_value = 100
134 new_bisector = Bisector(self.dummy_api, self.bisect_config, 131 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
135 self.MockRevisionClass) 132 self.assertTrue(bisector.check_regression_confidence())
136 self.assertTrue(new_bisector.check_regression_confidence()) 133 self.assertFalse(bisector.failed_confidence)
137 self.assertFalse(new_bisector.failed_confidence)
138 134
139 def test_check_regression_confidence_not_required(self): 135 def test_check_regression_confidence_not_required(self):
140 # When confidence is not required, confidence_score should not be called 136 # When confidence is not required, confidence_score should not be called
141 mock_score = self.dummy_api.m.math_utils.confidence_score 137 mock_score = self.dummy_api.m.math_utils.confidence_score
142 self.bisect_config['required_regression_confidence'] = None 138 self.bisect_config['required_regression_confidence'] = None
143 new_bisector = Bisector(self.dummy_api, self.bisect_config, 139 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
144 self.MockRevisionClass) 140 self.assertTrue(bisector.check_regression_confidence())
145 self.assertTrue(new_bisector.check_regression_confidence())
146 self.assertFalse(mock_score.called) 141 self.assertFalse(mock_score.called)
147 142
148 def test_check_regression_confidence_arbitrary(self): 143 def test_check_regression_confidence_arbitrary(self):
149 mock_score = self.dummy_api.m.math_utils.confidence_score 144 mock_score = self.dummy_api.m.math_utils.confidence_score
150 self.bisect_config['required_regression_confidence'] = 99 145 self.bisect_config['required_regression_confidence'] = 99
151 # A confidence score of 98.5 should not satisfy the required 99 146 # A confidence score of 98.5 should not satisfy the required 99
152 mock_score.return_value = 98.5 147 mock_score.return_value = 98.5
153 new_bisector = Bisector(self.dummy_api, self.bisect_config, 148 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
154 self.MockRevisionClass) 149 self.assertFalse(bisector.check_regression_confidence())
155 self.assertFalse(new_bisector.check_regression_confidence()) 150 self.assertTrue(bisector.failed_confidence)
156 self.assertTrue(new_bisector.failed_confidence)
157 151
158 # A confidence score of 99.5 should satisfy the required 99 152 # A confidence score of 99.5 should satisfy the required 99
159 mock_score.return_value = 99.5 153 mock_score.return_value = 99.5
160 new_bisector = Bisector(self.dummy_api, self.bisect_config, 154 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
161 self.MockRevisionClass) 155 self.assertTrue(bisector.check_regression_confidence())
162 self.assertTrue(new_bisector.check_regression_confidence()) 156 self.assertFalse(bisector.failed_confidence)
163 self.assertFalse(new_bisector.failed_confidence)
164 157
165 def test_wait_for_all(self): 158 def test_wait_for_all(self):
166 def mock_update_status(s): 159 def mock_update_status(s):
167 if getattr(s, 'mock_verified', False): 160 if getattr(s, 'mock_verified', False):
168 s.in_progress = False 161 s.in_progress = False
169 return 162 return
170 s.mock_verified = True 163 s.mock_verified = True
171 s.tested = True 164 s.tested = True
172 165
173 # Plug in mock update_status method 166 # Plug in mock update_status method
174 with mock.patch( 167 with mock.patch(
175 'bisector_test.BisectorTest.MockRevisionClass.update_status', 168 'bisector_test.MockRevisionClass.update_status',
176 mock_update_status): 169 mock_update_status):
177 new_bisector = Bisector(self.dummy_api, self.bisect_config, 170 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
178 self.MockRevisionClass) 171 for r in bisector.revisions:
179 for r in new_bisector.revisions:
180 r.in_progress = True 172 r.in_progress = True
181 new_bisector.wait_for_all(new_bisector.revisions) 173 bisector.wait_for_all(bisector.revisions)
182 # Verify that all revisions in list where verified by mock_update_status 174 # Verify that all revisions in list where verified by mock_update_status
183 self.assertTrue(all([r.mock_verified for r in new_bisector.revisions])) 175 self.assertTrue(all([r.mock_verified for r in bisector.revisions]))
184 176
185 def test_wait_for_any(self): 177 def test_wait_for_any(self):
186 # Creating placeholder for the patch 178 # Creating placeholder for the patch
187 self.MockRevisionClass.update_status = None 179 MockRevisionClass.update_status = None
188 with mock.patch( 180 with mock.patch(
189 'bisector_test.BisectorTest.MockRevisionClass.update_status'): 181 'bisector_test.MockRevisionClass.update_status'):
190 new_bisector = Bisector(self.dummy_api, self.bisect_config, 182 bisector = Bisector(self.dummy_api, self.bisect_config, MockRevisionClass)
191 self.MockRevisionClass) 183 for r in bisector.revisions:
192 for r in new_bisector.revisions:
193 r.tested = False 184 r.tested = False
194 r.in_progress = True 185 r.in_progress = True
195 new_bisector.revisions[0].tested = True 186 bisector.revisions[0].tested = True
196 finished_revision = new_bisector.wait_for_any(new_bisector.revisions) 187 finished_revision = bisector.wait_for_any(bisector.revisions)
197 self.assertEqual(new_bisector.revisions[0], finished_revision) 188 self.assertEqual(bisector.revisions[0], finished_revision)
189
190
191 class BisectorAbortTest(unittest.TestCase): # pragma: no cover
192 def setUp(self):
193 self.bisect_config = {
194 'test_type': 'perf',
195 'command': 'tools/perf/run_benchmark -v '
196 '--browser=release page_cycler.intl_ar_fa_he',
197 'good_revision': '306475',
198 'bad_revision': '306478',
199 'metric': 'warm_times/page_load_time',
200 'repeat_count': '2',
201 'max_time_minutes': '5',
202 'truncate_percent': '25',
203 'bug_id': '425582',
204 'gs_bucket': 'chrome-perf',
205 'builder_host': 'master4.golo.chromium.org',
206 'builder_port': '8341',
207 'dummy_builds': True,
208 }
209 self.dummy_api = mock.Mock()
210 self.called_abort = False
211 self.aborted_once = False
198 212
199 def test_abort_unnecessary_jobs(self): 213 def test_abort_unnecessary_jobs(self):
200 global aborted_once, called_abort 214 def mock_abort(_):
201 called_abort = False 215 self.called_abort = True
202 aborted_once = False 216 if self.aborted_once:
217 raise RuntimeError('Only one abort expected')
218 self.aborted_once = True
203 219
204 def mock_abort(s): 220 MockRevisionClass.abort = None
205 global aborted_once, called_abort 221 MockRevisionClass.update_status = None
206 called_abort = True
207 if aborted_once:
208 raise Exception('Only one abort expected')
209 aborted_once = True
210
211 self.MockRevisionClass.abort = None
212 self.MockRevisionClass.update_status = None
213 with mock.patch( 222 with mock.patch(
214 'bisector_test.BisectorTest.MockRevisionClass.update_status'): 223 'bisector_test.MockRevisionClass.update_status'):
215 with mock.patch('bisector_test.BisectorTest.MockRevisionClass.abort', 224 with mock.patch('bisector_test.MockRevisionClass.abort',
216 mock_abort) as abort_patch: 225 mock_abort):
217 new_bisector = Bisector(self.dummy_api, self.bisect_config, 226 bisector = Bisector(self.dummy_api, self.bisect_config,
218 self.MockRevisionClass) 227 MockRevisionClass)
219 r = new_bisector.revisions 228 r = bisector.revisions
220 r[0].good = True 229 r[0].good = True
221 r[0].bad = False 230 r[0].bad = False
222 r[0].tested = True 231 r[0].tested = True
223 r[0].in_progress = False 232 r[0].in_progress = False
224 233
225 r[1].in_progress = True 234 r[1].in_progress = True
226 r[1].tested = False 235 r[1].tested = False
227 236
228 r[2].good = True 237 r[2].good = True
229 r[2].bad = False 238 r[2].bad = False
230 r[2].tested = True 239 r[2].tested = True
231 r[2].in_progress = False 240 r[2].in_progress = False
232 241
233 r[3].bad = True 242 r[3].bad = True
234 r[3].good = False 243 r[3].good = False
235 r[3].tested = True 244 r[3].tested = True
236 r[3].in_progress = False 245 r[3].in_progress = False
237 246
238 try: 247 try:
239 new_bisector.abort_unnecessary_jobs() 248 bisector.abort_unnecessary_jobs()
240 except: 249 except RuntimeError:
241 self.fail('Expected to call abort only once') 250 self.fail('Expected to call abort only once')
242 self.assertTrue(called_abort) 251 self.assertTrue(self.called_abort)
243 252
244 # Verifying the side effects of updating the candidate range 253 # Verifying the side effects of updating the candidate range
245 self.assertEqual(r[2], new_bisector.lkgr) 254 self.assertEqual(r[2], bisector.lkgr)
246 self.assertEqual(r[3], new_bisector.fkbr) 255 self.assertEqual(r[3], bisector.fkbr)
247
248 # TODO: Test check_bisect_finished
249 256
250 257
258 # TODO(robertocn): Add test for bisector.check_bisect_finished.
251 if __name__ == '__main__': 259 if __name__ == '__main__':
252 unittest.main() # pragma: no cover 260 unittest.main() # pragma: no cover
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698