def test_cq_status_fetch_detects_flaky_runs_correctly(self): urlfetch_mock = mock.Mock() urlfetch_mock.return_value.content = TEST_CQ_STATUS_RESPONSE with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): cq_status.fetch_cq_status() tasks = self.taskqueue_stub.get_filtered_tasks(queue_names='issue-updates') self.assertEqual(len(tasks), 3) # We only compare select few properties of the created BuildRun entities. build_run_tuples = set() for task in tasks: params = task.extract_params() failure_run = ndb.Key(urlsafe=params['failure_run_key']).get() success_run = ndb.Key(urlsafe=params['success_run_key']).get() self.assertEqual(failure_run.key.parent(), success_run.key.parent()) pbr = failure_run.key.parent().get() build_run_tuple = (pbr.master, pbr.builder, pbr.issue, pbr.patchset, failure_run.buildnumber, success_run.buildnumber) build_run_tuples.add(build_run_tuple) expected_build_runs = set([ ('tryserver.test', 'test-builder', 987654321, 20001, 105, 110), ('tryserver.test', 'test-builder', 123456789, 20001, 109, 101), ('tryserver.test', 'test-builder', 123456789, 20001, 106, 101), ]) self.assertEqual(build_run_tuples, expected_build_runs)
def test_cq_status_fetch_stores_intermediate_status(self): urlfetch_mock = mock.Mock() urlfetch_mock.side_effect = [ self._mock_response({ 'more': True, 'cursor': 'abcd', 'results': [] }), self._mock_response({ 'more': False, 'cursor': None, 'results': [] }), ] with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): with mock.patch( 'status.cq_status.parse_cq_data') as parse_cq_data_mock: parse_cq_data_mock.side_effect = [ None, DeadlineExceededError() ] cq_status.fetch_cq_status() fetch_status = FetchStatus.query().get() self.assertEqual(fetch_status.cursor, 'abcd') self.assertEqual(fetch_status.begin, '1445609862.0') self.assertEqual(fetch_status.end, '1446214662.0') self.assertEqual(fetch_status.done, False)
def test_cq_status_processes_timestamp_in_raw_json(self): urlfetch_mock = mock.Mock() urlfetch_mock.return_value.content = ( '{"more":false,"cursor":"","results":[],"timestamp":"foo"}') with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): with mock.patch('logging.info') as logging_info_mock: cq_status.fetch_cq_status() logging_info_mock.assert_any_call(' current fetch has time of foo')
def test_cq_status_fetch_captures_deadline_exceeded_errors(self): urlfetch_mock = mock.Mock() urlfetch_mock.return_value.content = json.dumps({ 'more': False, 'cursor': None, 'results': []}) with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): with mock.patch('status.cq_status.parse_cq_data') as parse_cq_data_mock: parse_cq_data_mock.side_effect = [DeadlineExceededError()] cq_status.fetch_cq_status()
def test_cq_status_fetch_captures_urllib2_error(self): urlfetch_mock = mock.Mock() urlfetch_mock.side_effect = [ urllib2.URLError('reason'), self._mock_response({'more': False, 'cursor': None, 'results': []}), ] with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): cq_status.fetch_cq_status()
def test_cq_status_fetch_processes_all_flakes(self): urlfetch_mock = mock.Mock() urlfetch_mock.side_effect = [ self._mock_response({'more': True, 'cursor': 'abcd', 'results': []}), self._mock_response({'more': True, 'cursor': 'efgh', 'results': []}), self._mock_response({'more': False, 'cursor': '', 'results': []}), ] with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): cq_status.fetch_cq_status() self.assertEqual(urlfetch_mock.call_count, 3)
def test_cq_status_fetch_creates_tasks_correctly(self): urlfetch_mock = mock.Mock() urlfetch_mock.return_value.content = TEST_CQ_STATUS_RESPONSE with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): cq_status.fetch_cq_status() tasks = self.taskqueue_stub.get_filtered_tasks(queue_names='issue-updates') self.assertEqual(len(tasks), 3) self.assertEqual(tasks[0].url, '/issues/create_flaky_run') self.assertEqual(tasks[1].url, '/issues/create_flaky_run') self.assertEqual(tasks[2].url, '/issues/create_flaky_run')
def test_cq_status_fetch_fetches_one_week_if_no_previous_status(self): urlfetch_mock = mock.Mock() urlfetch_mock.return_value.content = json.dumps({ 'more': False, 'cursor': None, 'results': []}) with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): cq_status.fetch_cq_status() urlfetch_mock.assert_called_once_with( 'https://chromium-cq-status.appspot.com/query?' 'tags=action=verifier_jobs_update&begin=1445609862.0&end=1446214662.0&' 'count=10')
def test_cq_status_fetch_without_begin_end(self): urlfetch_mock = mock.Mock() urlfetch_mock.return_value.content = json.dumps({ 'more': False, 'cursor': None, 'results': []}) FetchStatus(cursor='abcd', done=False).put() with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): cq_status.fetch_cq_status() urlfetch_mock.assert_called_once_with( 'https://chromium-cq-status.appspot.com/query?' 'tags=action=verifier_jobs_update&cursor=abcd&count=10')
def test_cq_status_fetch_stores_intermediate_status(self): urlfetch_mock = mock.Mock() urlfetch_mock.side_effect = [ self._mock_response({'more': True, 'cursor': 'abcd', 'results': []}), self._mock_response({'more': False, 'cursor': None, 'results': []}), ] with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): with mock.patch('status.cq_status.parse_cq_data') as parse_cq_data_mock: parse_cq_data_mock.side_effect = [None, DeadlineExceededError()] cq_status.fetch_cq_status() fetch_status = FetchStatus.query().get() self.assertEqual(fetch_status.cursor, 'abcd') self.assertEqual(fetch_status.begin, '1445609862.0') self.assertEqual(fetch_status.end, '1446214662.0') self.assertEqual(fetch_status.done, False)
def test_fetch_cq_status_handles_and_retries_non_json_replies(self): urlfetch_mock = mock.Mock() urlfetch_mock.side_effect = [ self._mock_response('DeadlineExceededError'), self._mock_response('DeadlineExceededError'), self._mock_response('invalid-json'), self._mock_response('invalid-json'), self._mock_response('invalid-json'), self._mock_response('invalid-json'), # should give up on this one ] with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): with mock.patch('time.sleep'): # No exceptions should be raised - we should just silently fail. cq_status.fetch_cq_status() # Make sure we actually give up after last call. self.assertEqual(urlfetch_mock.call_count, 6)
def test_cq_status_fetch_stats_new_fetch_from_last_build_run(self): urlfetch_mock = mock.Mock() urlfetch_mock.return_value.content = json.dumps({ 'more': False, 'cursor': None, 'results': []}) FetchStatus(cursor='xxx', begin='1', end='2', done=True).put() BuildRun(time_started=datetime.datetime(2015, 10, 30, 12, 17, 42), time_finished=datetime.datetime(2015, 10, 30, 13, 17, 42), buildnumber=0, result=0).put() BuildRun(time_started=datetime.datetime(2015, 10, 30, 11, 17, 42), time_finished=datetime.datetime(2015, 10, 30, 12, 17, 42), buildnumber=0, result=0).put() with mock.patch('google.appengine.api.urlfetch.fetch', urlfetch_mock): cq_status.fetch_cq_status() urlfetch_mock.assert_called_once_with( 'https://chromium-cq-status.appspot.com/query?' 'tags=action=verifier_jobs_update&begin=1446211062.0&end=1446214662.0&' 'count=10')