| OLD | NEW |
| 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 from datetime import datetime | 5 from datetime import datetime |
| 6 import json | 6 import json |
| 7 import mock |
| 7 import time | 8 import time |
| 8 | 9 |
| 9 from common.waterfall import buildbucket_client | 10 from common.waterfall import buildbucket_client |
| 10 from common.waterfall import failure_type | 11 from common.waterfall import failure_type |
| 11 from common.waterfall import try_job_error | 12 from common.waterfall import try_job_error |
| 12 from model import analysis_status | 13 from model import analysis_status |
| 13 from model.wf_try_job import WfTryJob | 14 from model.wf_try_job import WfTryJob |
| 14 from model.wf_try_job_data import WfTryJobData | 15 from model.wf_try_job_data import WfTryJobData |
| 15 from waterfall import monitor_try_job_pipeline | 16 from waterfall import monitor_try_job_pipeline |
| 16 from waterfall import waterfall_config | 17 from waterfall import waterfall_config |
| 17 from waterfall.monitor_try_job_pipeline import MonitorTryJobPipeline | 18 from waterfall.monitor_try_job_pipeline import MonitorTryJobPipeline |
| 18 from waterfall.test import wf_testcase | 19 from waterfall.test import wf_testcase |
| 19 | 20 |
| 20 | 21 |
| 21 # A counter to enable different responses to requests in a loop. | |
| 22 REQUEST_COUNTER = { | |
| 23 '1': 0, | |
| 24 '2': 0, | |
| 25 '3': 0 | |
| 26 } | |
| 27 | |
| 28 | |
| 29 class MonitorTryJobPipelineTest(wf_testcase.WaterfallTestCase): | 22 class MonitorTryJobPipelineTest(wf_testcase.WaterfallTestCase): |
| 30 | 23 |
| 31 def _MockGetTryJobs(self, build_id): | |
| 32 def Mocked_GetTryJobs(*_): | |
| 33 data = { | |
| 34 '1': [ | |
| 35 { | |
| 36 'build': { | |
| 37 'id': '1', | |
| 38 'url': 'url', | |
| 39 'status': 'COMPLETED', | |
| 40 'result_details_json': json.dumps({ | |
| 41 'properties': { | |
| 42 'report': { | |
| 43 'result': { | |
| 44 'rev1': 'passed', | |
| 45 'rev2': 'failed' | |
| 46 }, | |
| 47 'metadata': { | |
| 48 'regression_range_size': 2 | |
| 49 } | |
| 50 } | |
| 51 } | |
| 52 }) | |
| 53 } | |
| 54 } | |
| 55 ], | |
| 56 '3': [ | |
| 57 { | |
| 58 'build': { | |
| 59 'id': '3', | |
| 60 'url': 'url', | |
| 61 'status': 'STARTED' | |
| 62 } | |
| 63 }, | |
| 64 { | |
| 65 'error': { | |
| 66 'reason': 'BUILD_NOT_FOUND', | |
| 67 'message': 'message', | |
| 68 } | |
| 69 }, | |
| 70 { | |
| 71 'build': { | |
| 72 'id': '3', | |
| 73 'url': 'url', | |
| 74 'status': 'STARTED' | |
| 75 } | |
| 76 }, | |
| 77 { | |
| 78 'error': { | |
| 79 'reason': 'BUILD_NOT_FOUND', | |
| 80 'message': 'message', | |
| 81 } | |
| 82 }, | |
| 83 { | |
| 84 'build': { | |
| 85 'id': '3', | |
| 86 'url': 'url', | |
| 87 'status': 'COMPLETED', | |
| 88 'result_details_json': json.dumps({ | |
| 89 'properties': { | |
| 90 'report': { | |
| 91 'result': { | |
| 92 'rev1': { | |
| 93 'a_test': { | |
| 94 'status': 'passed', | |
| 95 'valid': True | |
| 96 } | |
| 97 }, | |
| 98 'rev2': { | |
| 99 'a_test': { | |
| 100 'status': 'failed', | |
| 101 'valid': True, | |
| 102 'failures': ['test1', 'test2'] | |
| 103 } | |
| 104 } | |
| 105 } | |
| 106 } | |
| 107 } | |
| 108 }) | |
| 109 } | |
| 110 } | |
| 111 ] | |
| 112 } | |
| 113 try_job_results = [] | |
| 114 build_error = data.get(build_id)[REQUEST_COUNTER[build_id]] | |
| 115 if build_error.get('error'): | |
| 116 try_job_results.append(( | |
| 117 buildbucket_client.BuildbucketError(build_error['error']), None)) | |
| 118 else: | |
| 119 try_job_results.append(( | |
| 120 None, buildbucket_client.BuildbucketBuild(build_error['build']))) | |
| 121 REQUEST_COUNTER[build_id] += 1 | |
| 122 return try_job_results | |
| 123 | |
| 124 self.mock(buildbucket_client, 'GetTryJobs', Mocked_GetTryJobs) | |
| 125 | |
| 126 def setUp(self): | 24 def setUp(self): |
| 127 super(MonitorTryJobPipelineTest, self).setUp() | 25 super(MonitorTryJobPipelineTest, self).setUp() |
| 128 self.mock(time, 'sleep', lambda x: None) | 26 self.mock(time, 'sleep', lambda x: None) |
| 129 | 27 |
| 130 def testMicrosecondsToDatetime(self): | 28 def testMicrosecondsToDatetime(self): |
| 131 self.assertEqual( | 29 self.assertEqual( |
| 132 datetime(2016, 2, 1, 22, 59, 34), | 30 datetime(2016, 2, 1, 22, 59, 34), |
| 133 monitor_try_job_pipeline._MicrosecondsToDatetime(1454367574000000)) | 31 monitor_try_job_pipeline._MicrosecondsToDatetime(1454367574000000)) |
| 134 self.assertIsNone(monitor_try_job_pipeline._MicrosecondsToDatetime(None)) | 32 self.assertIsNone(monitor_try_job_pipeline._MicrosecondsToDatetime(None)) |
| 135 | 33 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 self.assertEqual(try_job_data.end_time, datetime(2016, 2, 1, 22, 59, 34)) | 84 self.assertEqual(try_job_data.end_time, datetime(2016, 2, 1, 22, 59, 34)) |
| 187 self.assertEqual(try_job_data.request_time, | 85 self.assertEqual(try_job_data.request_time, |
| 188 datetime(2016, 2, 1, 22, 59, 30)) | 86 datetime(2016, 2, 1, 22, 59, 30)) |
| 189 self.assertEqual(try_job_data.try_job_url, url) | 87 self.assertEqual(try_job_data.try_job_url, url) |
| 190 | 88 |
| 191 monitor_try_job_pipeline._UpdateTryJobMetadata( | 89 monitor_try_job_pipeline._UpdateTryJobMetadata( |
| 192 try_job_data, None, build, None, True) | 90 try_job_data, None, build, None, True) |
| 193 self.assertEqual(try_job_data.error, expected_error_dict) | 91 self.assertEqual(try_job_data.error, expected_error_dict) |
| 194 self.assertEqual(try_job_data.error_code, try_job_error.TIMEOUT) | 92 self.assertEqual(try_job_data.error_code, try_job_error.TIMEOUT) |
| 195 | 93 |
| 196 def testGetTryJobsForCompileSuccess(self): | 94 @mock.patch.object(monitor_try_job_pipeline, 'buildbucket_client') |
| 95 def testGetTryJobsForCompileSuccess(self, mock_module): |
| 197 master_name = 'm' | 96 master_name = 'm' |
| 198 builder_name = 'b' | 97 builder_name = 'b' |
| 199 build_number = 1 | 98 build_number = 1 |
| 200 try_job_id = '1' | 99 try_job_id = '1' |
| 201 regression_range_size = 2 | 100 regression_range_size = 2 |
| 202 | 101 |
| 203 try_job = WfTryJob.Create(master_name, builder_name, build_number) | 102 try_job = WfTryJob.Create(master_name, builder_name, build_number) |
| 204 try_job_data = WfTryJobData.Create(try_job_id) | 103 try_job_data = WfTryJobData.Create(try_job_id) |
| 205 try_job_data.put() | 104 try_job_data.put() |
| 206 try_job.compile_results = [ | 105 try_job.compile_results = [ |
| 207 { | 106 { |
| 208 'report': None, | 107 'report': None, |
| 209 'url': 'url', | 108 'url': 'url', |
| 210 'try_job_id': '1', | 109 'try_job_id': '1', |
| 211 } | 110 } |
| 212 ] | 111 ] |
| 213 try_job.status = analysis_status.RUNNING | 112 try_job.status = analysis_status.RUNNING |
| 214 try_job.put() | 113 try_job.put() |
| 215 self._MockGetTryJobs(try_job_id) | 114 |
| 115 build_response = { |
| 116 'id': '1', |
| 117 'url': 'url', |
| 118 'status': 'COMPLETED', |
| 119 'result_details_json': json.dumps({ |
| 120 'properties': { |
| 121 'report': { |
| 122 'result': { |
| 123 'rev1': 'passed', |
| 124 'rev2': 'failed' |
| 125 }, |
| 126 'metadata': { |
| 127 'regression_range_size': 2 |
| 128 } |
| 129 } |
| 130 } |
| 131 }) |
| 132 } |
| 133 mock_module.GetTryJobs.return_value = [ |
| 134 (None, buildbucket_client.BuildbucketBuild(build_response))] |
| 216 | 135 |
| 217 pipeline = MonitorTryJobPipeline() | 136 pipeline = MonitorTryJobPipeline() |
| 218 compile_result = pipeline.run( | 137 compile_result = pipeline.run( |
| 219 master_name, builder_name, build_number, failure_type.COMPILE, | 138 master_name, builder_name, build_number, failure_type.COMPILE, |
| 220 try_job_id) | 139 try_job_id) |
| 221 | 140 |
| 222 expected_compile_result = { | 141 expected_compile_result = { |
| 223 'report': { | 142 'report': { |
| 224 'result': { | 143 'result': { |
| 225 'rev1': 'passed', | 144 'rev1': 'passed', |
| 226 'rev2': 'failed' | 145 'rev2': 'failed' |
| 227 }, | 146 }, |
| 228 'metadata': { | 147 'metadata': { |
| 229 'regression_range_size': regression_range_size | 148 'regression_range_size': regression_range_size |
| 230 } | 149 } |
| 231 }, | 150 }, |
| 232 'url': 'url', | 151 'url': 'url', |
| 233 'try_job_id': '1', | 152 'try_job_id': '1', |
| 234 } | 153 } |
| 235 | 154 |
| 236 self.assertEqual(expected_compile_result, compile_result) | 155 self.assertEqual(expected_compile_result, compile_result) |
| 237 | 156 |
| 238 try_job = WfTryJob.Get(master_name, builder_name, build_number) | 157 try_job = WfTryJob.Get(master_name, builder_name, build_number) |
| 239 self.assertEqual(expected_compile_result, try_job.compile_results[-1]) | 158 self.assertEqual(expected_compile_result, try_job.compile_results[-1]) |
| 240 self.assertEqual(analysis_status.RUNNING, try_job.status) | 159 self.assertEqual(analysis_status.RUNNING, try_job.status) |
| 241 | 160 |
| 242 try_job_data = WfTryJobData.Get(try_job_id) | 161 try_job_data = WfTryJobData.Get(try_job_id) |
| 243 self.assertEqual(try_job_data.regression_range_size, regression_range_size) | 162 self.assertEqual(try_job_data.regression_range_size, regression_range_size) |
| 244 | 163 |
| 245 def testGetTryJobsForTestSuccess(self): | 164 @mock.patch.object(monitor_try_job_pipeline, 'buildbucket_client') |
| 165 def testGetTryJobsForTestSuccess(self, mock_module): |
| 246 master_name = 'm' | 166 master_name = 'm' |
| 247 builder_name = 'b' | 167 builder_name = 'b' |
| 248 build_number = 1 | 168 build_number = 1 |
| 249 try_job_id = '3' | 169 try_job_id = '3' |
| 250 | 170 |
| 251 try_job = WfTryJob.Create(master_name, builder_name, build_number) | 171 try_job = WfTryJob.Create(master_name, builder_name, build_number) |
| 252 try_job.test_results = [ | 172 try_job.test_results = [ |
| 253 { | 173 { |
| 254 'report': None, | 174 'report': None, |
| 255 'url': 'url', | 175 'url': 'url', |
| 256 'try_job_id': try_job_id, | 176 'try_job_id': try_job_id, |
| 257 } | 177 } |
| 258 ] | 178 ] |
| 259 try_job.status = analysis_status.RUNNING | 179 try_job.status = analysis_status.RUNNING |
| 260 try_job.put() | 180 try_job.put() |
| 261 | 181 |
| 262 try_job_data = WfTryJobData.Create(try_job_id) | 182 try_job_data = WfTryJobData.Create(try_job_id) |
| 263 try_job_data.put() | 183 try_job_data.put() |
| 264 | 184 |
| 265 self._MockGetTryJobs(try_job_id) | 185 data = [ |
| 186 { |
| 187 'build': { |
| 188 'id': '3', |
| 189 'url': 'url', |
| 190 'status': 'STARTED' |
| 191 } |
| 192 }, |
| 193 { |
| 194 'error': { |
| 195 'reason': 'BUILD_NOT_FOUND', |
| 196 'message': 'message', |
| 197 } |
| 198 }, |
| 199 { |
| 200 'build': { |
| 201 'id': '3', |
| 202 'url': 'url', |
| 203 'status': 'STARTED' |
| 204 } |
| 205 }, |
| 206 { |
| 207 'error': { |
| 208 'reason': 'BUILD_NOT_FOUND', |
| 209 'message': 'message', |
| 210 } |
| 211 }, |
| 212 { |
| 213 'build': { |
| 214 'id': '3', |
| 215 'url': 'url', |
| 216 'status': 'COMPLETED', |
| 217 'result_details_json': json.dumps({ |
| 218 'properties': { |
| 219 'report': { |
| 220 'result': { |
| 221 'rev1': { |
| 222 'a_test': { |
| 223 'status': 'passed', |
| 224 'valid': True |
| 225 } |
| 226 }, |
| 227 'rev2': { |
| 228 'a_test': { |
| 229 'status': 'failed', |
| 230 'valid': True, |
| 231 'failures': ['test1', 'test2'] |
| 232 } |
| 233 } |
| 234 } |
| 235 } |
| 236 } |
| 237 }) |
| 238 } |
| 239 } |
| 240 ] |
| 241 |
| 242 mock_module.GetTryJobs.side_effect = [ |
| 243 [(None, buildbucket_client.BuildbucketBuild(data[0]['build']))], |
| 244 [(buildbucket_client.BuildbucketError(data[1]['error']), None)], |
| 245 [(None, buildbucket_client.BuildbucketBuild(data[2]['build']))], |
| 246 [(buildbucket_client.BuildbucketError(data[3]['error']), None)], |
| 247 [(None, buildbucket_client.BuildbucketBuild(data[4]['build']))], |
| 248 ] |
| 266 | 249 |
| 267 pipeline = MonitorTryJobPipeline() | 250 pipeline = MonitorTryJobPipeline() |
| 268 test_result = pipeline.run( | 251 test_result = pipeline.run( |
| 269 master_name, builder_name, build_number, failure_type.TEST, | 252 master_name, builder_name, build_number, failure_type.TEST, |
| 270 try_job_id) | 253 try_job_id) |
| 271 | 254 |
| 272 expected_test_result = { | 255 expected_test_result = { |
| 273 'report': { | 256 'report': { |
| 274 'result': { | 257 'result': { |
| 275 'rev1': { | 258 'rev1': { |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 462 } | 445 } |
| 463 | 446 |
| 464 expected_error_dict = { | 447 expected_error_dict = { |
| 465 'message': 'No result report was found.', | 448 'message': 'No result report was found.', |
| 466 'reason': MonitorTryJobPipeline.UNKNOWN | 449 'reason': MonitorTryJobPipeline.UNKNOWN |
| 467 } | 450 } |
| 468 | 451 |
| 469 self.assertEqual( | 452 self.assertEqual( |
| 470 monitor_try_job_pipeline._GetError(build_response, None, False), | 453 monitor_try_job_pipeline._GetError(build_response, None, False), |
| 471 (expected_error_dict, try_job_error.UNKNOWN)) | 454 (expected_error_dict, try_job_error.UNKNOWN)) |
| OLD | NEW |