| OLD | NEW |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 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 datetime | 5 import datetime |
| 6 import mock | 6 import mock |
| 7 import re | 7 import re |
| 8 | 8 |
| 9 import webapp2 | 9 import webapp2 |
| 10 import webtest | 10 import webtest |
| 11 | 11 |
| 12 from google.appengine.api import users | 12 from google.appengine.api import users |
| 13 | 13 |
| 14 from handlers.flake import check_flake | 14 from handlers.flake import check_flake |
| 15 from handlers.flake.check_flake import CheckFlake |
| 16 from model import analysis_status |
| 17 from model.analysis_status import STATUS_TO_DESCRIPTION |
| 18 from model.flake.flake_analysis_request import FlakeAnalysisRequest |
| 15 from model.flake.master_flake_analysis import DataPoint | 19 from model.flake.master_flake_analysis import DataPoint |
| 16 from model.flake.master_flake_analysis import MasterFlakeAnalysis | 20 from model.flake.master_flake_analysis import MasterFlakeAnalysis |
| 17 from model import analysis_status | 21 from waterfall.flake import flake_analysis_service |
| 18 from model.analysis_status import STATUS_TO_DESCRIPTION | |
| 19 from waterfall.test import wf_testcase | 22 from waterfall.test import wf_testcase |
| 20 | 23 |
| 21 | 24 |
| 22 class CheckFlakeTest(wf_testcase.WaterfallTestCase): | 25 class CheckFlakeTest(wf_testcase.WaterfallTestCase): |
| 23 app_module = webapp2.WSGIApplication([ | 26 app_module = webapp2.WSGIApplication([ |
| 24 ('/waterfall/check-flake', check_flake.CheckFlake), | 27 ('/waterfall/check-flake', check_flake.CheckFlake), |
| 25 ], debug=True) | 28 ], debug=True) |
| 26 | 29 |
| 27 def testCorpUserCanScheduleANewAnalysis(self): | 30 def testCorpUserCanScheduleANewAnalysis(self): |
| 28 master_name = 'm' | 31 master_name = 'm' |
| 29 builder_name = 'b' | 32 builder_name = 'b' |
| 30 build_number = '123' | 33 build_number = '123' |
| 31 step_name = 's' | 34 step_name = 's' |
| 32 test_name = 't' | 35 test_name = 't' |
| 33 | 36 |
| 37 analysis = MasterFlakeAnalysis.Create( |
| 38 master_name, builder_name, build_number, step_name, test_name) |
| 39 analysis.Save() |
| 40 |
| 34 self.mock_current_user(user_email='test@google.com') | 41 self.mock_current_user(user_email='test@google.com') |
| 35 | 42 |
| 36 response = self.test_app.get('/waterfall/check-flake', params={ | 43 response = self.test_app.get('/waterfall/check-flake', params={ |
| 37 'master_name': master_name, | 44 'master_name': master_name, |
| 38 'builder_name': builder_name, | 45 'builder_name': builder_name, |
| 39 'build_number': build_number, | 46 'build_number': build_number, |
| 40 'step_name': step_name, | 47 'step_name': step_name, |
| 41 'test_name': test_name}) | 48 'test_name': test_name}) |
| 42 | 49 |
| 43 self.assertEquals(200, response.status_int) | 50 self.assertEquals(200, response.status_int) |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 111 'build_number': 100, | 118 'build_number': 100, |
| 112 'triage_result': 0 | 119 'triage_result': 0 |
| 113 }, | 120 }, |
| 114 'version_number': 1, | 121 'version_number': 1, |
| 115 'show_debug_info': False | 122 'show_debug_info': False |
| 116 } | 123 } |
| 117 | 124 |
| 118 self.assertEquals(200, response.status_int) | 125 self.assertEquals(200, response.status_int) |
| 119 self.assertEqual(expected_check_flake_result, response.json_body) | 126 self.assertEqual(expected_check_flake_result, response.json_body) |
| 120 | 127 |
| 128 def testUnauthorizedUserCannotScheduleNewAnalysis(self): |
| 129 master_name = 'm' |
| 130 builder_name = 'b' |
| 131 build_number = 123 |
| 132 step_name = 's' |
| 133 test_name = 't' |
| 134 |
| 135 self.assertRaisesRegexp( |
| 136 webtest.app.AppError, |
| 137 re.compile('.*401 Unauthorized.*', re.MULTILINE | re.DOTALL), |
| 138 self.test_app.get, |
| 139 '/waterfall/check-flake', |
| 140 params={ |
| 141 'master_name': master_name, |
| 142 'builder_name': builder_name, |
| 143 'build_number': build_number, |
| 144 'step_name': step_name, |
| 145 'test_name': test_name, |
| 146 'format': 'json'}) |
| 147 |
| 148 @mock.patch.object(flake_analysis_service, 'ScheduleAnalysisForFlake', |
| 149 return_value=False) |
| 150 def testRequestExistingAnalysis(self, _): |
| 151 master_name = 'm' |
| 152 builder_name = 'b' |
| 153 build_number = 123 |
| 154 step_name = 's' |
| 155 test_name = 't' |
| 156 success_rate = 0.9 |
| 157 |
| 158 previous_analysis = MasterFlakeAnalysis.Create( |
| 159 master_name, builder_name, build_number - 1, step_name, test_name) |
| 160 data_point = DataPoint() |
| 161 data_point.build_number = int(build_number) |
| 162 data_point.pass_rate = success_rate |
| 163 previous_analysis.data_points.append(data_point) |
| 164 previous_analysis.status = analysis_status.COMPLETED |
| 165 previous_analysis.suspected_flake_build_number = 100 |
| 166 previous_analysis.request_time = datetime.datetime(2016, 10, 01, 12, 10, 00) |
| 167 previous_analysis.start_time = datetime.datetime(2016, 10, 01, 12, 10, 05) |
| 168 previous_analysis.end_time = datetime.datetime(2016, 10, 01, 13, 10, 00) |
| 169 previous_analysis.algorithm_parameters = {'iterations_to_rerun': 100} |
| 170 previous_analysis.Save() |
| 171 |
| 172 previous_request = FlakeAnalysisRequest.Create(test_name, False, None) |
| 173 previous_request.AddBuildStep( |
| 174 master_name, builder_name, build_number, step_name, None) |
| 175 previous_request.analyses.append(previous_analysis.key) |
| 176 previous_request.Save() |
| 177 |
| 178 response = self.test_app.get('/waterfall/check-flake', params={ |
| 179 'master_name': master_name, |
| 180 'builder_name': builder_name, |
| 181 'build_number': build_number, |
| 182 'step_name': step_name, |
| 183 'test_name': test_name, |
| 184 'format': 'json'}) |
| 185 |
| 186 expected_check_flake_result = { |
| 187 'pass_rates': [[int(build_number), success_rate]], |
| 188 'analysis_status': STATUS_TO_DESCRIPTION.get(previous_analysis.status), |
| 189 'master_name': master_name, |
| 190 'builder_name': builder_name, |
| 191 'build_number': int(build_number), |
| 192 'step_name': step_name, |
| 193 'test_name': test_name, |
| 194 'request_time': '2016-10-01 12:10:00 UTC', |
| 195 'task_number': 1, |
| 196 'error': None, |
| 197 'iterations_to_rerun': 100, |
| 198 'pending_time': '00:00:05', |
| 199 'duration': '00:59:55', |
| 200 'suspected_flake': { |
| 201 'build_number': 100, |
| 202 'triage_result': 0 |
| 203 }, |
| 204 'version_number': 1, |
| 205 'show_debug_info': False |
| 206 } |
| 207 |
| 208 self.assertEqual(200, response.status_int) |
| 209 self.assertEqual(expected_check_flake_result, response.json_body) |
| 210 |
| 211 @mock.patch.object(flake_analysis_service, 'ScheduleAnalysisForFlake', |
| 212 return_value=False) |
| 213 def testRequestUnsupportedAnalysis(self, _): |
| 214 master_name = 'm' |
| 215 builder_name = 'b' |
| 216 build_number = 123 |
| 217 step_name = 's' |
| 218 test_name = 't' |
| 219 |
| 220 previous_request = FlakeAnalysisRequest.Create(test_name, False, None) |
| 221 previous_request.AddBuildStep( |
| 222 master_name, builder_name, build_number, step_name, None) |
| 223 previous_request.swarmed = False |
| 224 previous_request.supported = False |
| 225 previous_request.Save() |
| 226 |
| 227 self.assertRaisesRegexp( |
| 228 webtest.app.AppError, |
| 229 re.compile('.*not supported.*', re.MULTILINE | re.DOTALL), |
| 230 self.test_app.get, |
| 231 '/waterfall/check-flake', |
| 232 params={ |
| 233 'master_name': master_name, |
| 234 'builder_name': builder_name, |
| 235 'build_number': build_number, |
| 236 'step_name': step_name, |
| 237 'test_name': test_name, |
| 238 'format': 'json'}) |
| 239 |
| 121 @mock.patch.object(users, 'is_current_user_admin', return_value=True) | 240 @mock.patch.object(users, 'is_current_user_admin', return_value=True) |
| 122 def testGetTriageHistory(self, _): | 241 def testGetTriageHistory(self, _): |
| 123 master_name = 'm' | 242 master_name = 'm' |
| 124 builder_name = 'b' | 243 builder_name = 'b' |
| 125 build_number = '123' | 244 build_number = '123' |
| 126 step_name = 's' | 245 step_name = 's' |
| 127 test_name = 't' | 246 test_name = 't' |
| 128 suspected_flake_build_number = 123 | 247 suspected_flake_build_number = 123 |
| 129 triage_result = 2 | 248 triage_result = 2 |
| 130 user_name = 'test' | 249 user_name = 'test' |
| (...skipping 16 matching lines...) Expand all Loading... |
| 147 | 266 |
| 148 # Because TriagedResult uses auto_now=True, a direct dict comparison will | 267 # Because TriagedResult uses auto_now=True, a direct dict comparison will |
| 149 # always fail. Instead only compare the relevant fields for trige_history. | 268 # always fail. Instead only compare the relevant fields for trige_history. |
| 150 triage_history = response.json_body.get('triage_history') | 269 triage_history = response.json_body.get('triage_history') |
| 151 self.assertEqual(len(triage_history), 1) | 270 self.assertEqual(len(triage_history), 1) |
| 152 self.assertEqual(triage_history[0].get('triage_result'), 'Correct') | 271 self.assertEqual(triage_history[0].get('triage_result'), 'Correct') |
| 153 self.assertEqual(triage_history[0].get('user_name'), user_name) | 272 self.assertEqual(triage_history[0].get('user_name'), user_name) |
| 154 self.assertEqual( | 273 self.assertEqual( |
| 155 triage_history[0].get('suspect_info', {}).get('build_number'), | 274 triage_history[0].get('suspect_info', {}).get('build_number'), |
| 156 suspected_flake_build_number) | 275 suspected_flake_build_number) |
| 276 |
| 277 def testValidateInput(self): |
| 278 self.assertIsNone( |
| 279 CheckFlake()._ValidateInput('m', 'b', '123', 's', 't', None)) |
| 280 self.assertIsNone( |
| 281 CheckFlake()._ValidateInput('m', 'b', '123', 's', 't', '654321')) |
| 282 self.assertEqual( |
| 283 CheckFlake()._ValidateInput( |
| 284 None, 'b', '1', 's', 't', 'a').get('data', {}).get('error_message'), |
| 285 'Master name must be specified') |
| 286 self.assertEqual( |
| 287 CheckFlake()._ValidateInput( |
| 288 'm', None, '1', 's', 't', '').get('data', {}).get('error_message'), |
| 289 'Builder name must be specified') |
| 290 self.assertEqual( |
| 291 CheckFlake()._ValidateInput( |
| 292 'm', 'b', None, 's', 't', '').get('data', {}).get('error_message'), |
| 293 'Build number must be specified as an int') |
| 294 self.assertEqual( |
| 295 CheckFlake()._ValidateInput( |
| 296 'm', 'b', 'a', 's', 't', '').get('data', {}).get('error_message'), |
| 297 'Build number must be specified as an int') |
| 298 self.assertEqual( |
| 299 CheckFlake()._ValidateInput( |
| 300 'm', 'b', '1', None, 't', '').get('data', {}).get('error_message'), |
| 301 'Step name must be specified') |
| 302 self.assertEqual( |
| 303 CheckFlake()._ValidateInput( |
| 304 'm', 'b', '1', 's', None, '').get('data', {}).get('error_message'), |
| 305 'Test name must be specified') |
| 306 self.assertEqual( |
| 307 CheckFlake()._ValidateInput( |
| 308 'm', 'b', '1', 's', 't', 'a').get('data', {}).get('error_message'), |
| 309 'Bug id (optional) must be an int') |
| 310 |
| 311 |
| OLD | NEW |