def test_heartbeats(self): def test(): NUM_BUILDS = 200 # More than batch size. self.integrator._leases = { str(i): { 'key': LEASE_KEY, 'build_request': Mock(), } for i in xrange(NUM_BUILDS) } test_finished = defer.Deferred() def assert_heartbeat_sent(): try: updated_builds = set() for _, kwargs in self.buildbucket.api.heartbeat_batch.call_args_list: for hb in kwargs['body']['heartbeats']: updated_builds.add(int(hb['build_id'])) self.assertEqual(updated_builds, set(xrange(NUM_BUILDS))) finally: test_finished.callback(None) # Finish test. reactor.callLater(0.001, assert_heartbeat_sent) return test_finished with self.create_integrator(): run_deferred(test())
def test_no_builds(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = {'builds': []} run_deferred(self.integrator.poll_builds()) self.assertFalse(self.buildbot.add_build_request.called) self.assertTrue(self.buildbucket.api.peek.called) self.assertFalse(self.buildbucket.api.lease.called)
def test_polling_with_cursor(self): with self.create_integrator(): peek_response1 = { 'builds': [self.buildbucket_build_rel], 'next_cursor': '123', } peek_response2 = { 'builds': [self.buildbucket_build_dbg], } self.buildbucket.api.peek.side_effect = [ peek_response1, peek_response2 ] run_deferred(self.integrator.poll_builds()) self.assertEqual(self.buildbucket.api.peek.call_count, 2) _, last_peek_call_kwargs = self.buildbucket.api.peek.call_args self.assertEqual(last_peek_call_kwargs['start_cursor'], peek_response1['next_cursor']) self.assert_leased(self.buildbucket_build_rel['id']) self.assert_leased(self.buildbucket_build_dbg['id']) # Assert added two buildsets. self.assertEqual(self.buildbot.add_build_request.call_count, 2) self.assert_added_build_request(self.buildbucket_build_rel['id'], 'Release', self.ssid, buildset=BUILDSET) self.assert_added_build_request(self.buildbucket_build_dbg['id'], 'Debug', self.ssid, buildset=BUILDSET)
def test_build_with_properties(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = { 'builds': [{ 'id': '1', 'bucket': BUCKET, 'parameters_json': json.dumps({ 'builder_name': 'Release', 'properties': { 'a': 'b', } }), }], } run_deferred(self.integrator.poll_builds()) self.assert_leased('1') self.assert_added_build_request('1', 'Release', self.ssid, properties={'a': 'b'})
def test_invalid_build_def(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = { 'builds': [{ 'id': '1', }], } run_deferred(self.integrator.poll_builds()) self.assert_buildbucket_lease_was_called('1') self.buildbucket.api.fail.assert_called_once_with( id='1', body={ 'lease_key': LEASE_KEY, 'failure_reason': 'INVALID_BUILD_DEFINITION', 'result_details_json': json.dumps( { 'error': { 'message': ('Build parameters (parameters_json) are not set' ), } }, sort_keys=True) }) self.assertFalse(self.buildbot.add_build_request.called)
def test_clean_completed_build_requests(self): with self.create_integrator(): def make_build_request(complete, has_builds): result = Mock() result.is_failed.return_value = complete result.has_builds.return_value = has_builds return result self.integrator._leases = { '1': { 'lease_key': LEASE_KEY, 'build_request': make_build_request(True, False), }, '2': { 'lease_key': LEASE_KEY, 'build_request': make_build_request(False, False), }, '3': { 'lease_key': LEASE_KEY, 'build_request': make_build_request(True, True), }, } run_deferred(self.integrator.clean_completed_build_requests()) self.assertEqual(1, self.buildbucket.api.cancel.call_count) self.buildbucket.api.cancel.assert_called_once_with(id='1')
def test_heartbeats(self): def test(): NUM_BUILDS = 200 # More than batch size. self.integrator._leases = { str(i): { 'key': LEASE_KEY, 'build_request': Mock(), } for i in xrange(NUM_BUILDS) } test_finished = defer.Deferred() def assert_heartbeat_sent(): try: updated_builds = set() for _, kwargs in self.buildbucket.api.heartbeat_batch.call_args_list: for hb in kwargs['body']['heartbeats']: updated_builds.add(int(hb['build_id'])) self.assertEqual(updated_builds, set(xrange(NUM_BUILDS))) finally: test_finished.callback(None) # Finish test. reactor.callLater(0.001, assert_heartbeat_sent) return test_finished with self.create_integrator(): run_deferred(test())
def test_clean_completed_build_requests(self): with self.create_integrator(): def make_build_request(complete, has_builds): result = Mock() result.is_failed.return_value = complete result.has_builds.return_value = has_builds return result self.integrator._leases = { '1': { 'lease_key': LEASE_KEY, 'build_request': make_build_request(True, False), }, '2': { 'lease_key': LEASE_KEY, 'build_request': make_build_request(False, False), }, '3': { 'lease_key': LEASE_KEY, 'build_request': make_build_request(True, True), }, } run_deferred(self.integrator.clean_completed_build_requests()) self.assertEqual(1, self.buildbucket.api.cancel.call_count) self.buildbucket.api.cancel.assert_called_once_with(id='1')
def test_polling_with_cursor(self): with self.create_integrator(): peek_response1 = { 'builds': [self.buildbucket_build_rel], 'next_cursor': '123', } peek_response2 = { 'builds': [self.buildbucket_build_dbg], } self.buildbucket.api.peek.side_effect = [peek_response1, peek_response2] run_deferred(self.integrator.poll_builds()) self.assertEqual(self.buildbucket.api.peek.call_count, 2) _, last_peek_call_kwargs = self.buildbucket.api.peek.call_args self.assertEqual( last_peek_call_kwargs['start_cursor'], peek_response1['next_cursor']) self.assert_leased(self.buildbucket_build_rel['id']) self.assert_leased(self.buildbucket_build_dbg['id']) # Assert added two buildsets. self.assertEqual(self.buildbot.add_build_request.call_count, 2) self.assert_added_build_request( self.buildbucket_build_rel['id'], 'Release', self.ssid, buildset=BUILDSET) self.assert_added_build_request( self.buildbucket_build_dbg['id'], 'Debug', self.ssid, buildset=BUILDSET)
def test_no_builds(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = {'builds': []} run_deferred(self.integrator.poll_builds()) self.assertFalse(self.buildbot.add_build_request.called) self.assertTrue(self.buildbucket.api.peek.called) self.assertFalse(self.buildbucket.api.lease.called)
def test_build_with_hook_fails_if_valueerror(self): def hook(params, build): raise ValueError("Test Failure.") with self.create_integrator(build_params_hook=hook): self.buildbucket.api.peek.return_value = { 'builds': [{ 'id': '1', 'bucket': BUCKET, 'parameters_json': json.dumps({ 'builder_name': 'Release', }), }], } run_deferred(self.integrator.poll_builds()) self.buildbucket.api.fail.assert_called_once_with( id='1', body={ 'lease_key': LEASE_KEY, 'failure_reason': 'INVALID_BUILD_DEFINITION', 'result_details_json': json.dumps({ 'error': { 'message': 'Test Failure.', }, }), })
def test_build_with_hook_fails_if_valueerror(self): def hook(params, build): raise ValueError("Test Failure.") with self.create_integrator(build_params_hook=hook): self.buildbucket.api.peek.return_value = { 'builds': [{ 'id': '1', 'bucket': BUCKET, 'parameters_json': json.dumps({ 'builder_name': 'Release', }), }], } run_deferred(self.integrator.poll_builds()) self.buildbucket.api.fail.assert_called_once_with( id='1', body={ 'lease_key': LEASE_KEY, 'failure_reason': 'INVALID_BUILD_DEFINITION', 'result_details_json': json.dumps({ 'error': { 'message': 'Test Failure.', }, }), } )
def test_build_with_properties_hook(self): def hook(params, build): build['bucket'] = 'INVALID' params['builder_name'] = 'Debug' params['properties']['foo'] = 'bar' build = { 'id': '1', 'bucket': BUCKET, 'parameters_json': json.dumps({ 'builder_name': 'Release', 'properties': { 'a': 'b', } }), } with self.create_integrator(build_params_hook=hook): self.buildbucket.api.peek.return_value = { 'builds': [build], } run_deferred(self.integrator.poll_builds()) self.assert_leased('1') self.assert_added_build_request( '1', 'Debug', self.ssid, properties={'a': 'b', 'foo': 'bar'}) # Modifications to 'build' in the hook should not be persisted. self.assertEqual(build['bucket'], BUCKET)
def test_max_one_lease(self): bb = fake_buildbot(one_slave=True) with self.create_integrator(bb, max_lease_count=1): with self.mock_build_peek(): run_deferred(self.integrator.poll_builds()) # Assert only one build was leased and scheduled. self.assertEqual(bb.add_build_request.call_count, 1) self.assertEqual(self.buildbucket.api.lease.call_count, 1)
def test_nothing_is_scheduled_if_builders_do_not_have_connected_slaves(self): with self.create_integrator(): self.buildbot.get_connected_slaves.return_value = [] run_deferred(self.integrator.poll_builds()) self.assertFalse(self.buildbucket.api.peek.called) self.assertFalse(self.buildbot.add_build_request.called)
def test_max_one_lease(self): bb = fake_buildbot(one_slave=True) with self.create_integrator(bb, max_lease_count=1): with self.mock_build_peek(): run_deferred(self.integrator.poll_builds()) # Assert only one build was leased and scheduled. self.assertEqual(bb.add_build_request.call_count, 1) self.assertEqual(self.buildbucket.api.lease.call_count, 1)
def test_nothing_is_scheduled_if_builders_do_not_have_connected_slaves( self): with self.create_integrator(): self.buildbot.get_connected_slaves.return_value = [] run_deferred(self.integrator.poll_builds()) self.assertFalse(self.buildbucket.api.peek.called) self.assertFalse(self.buildbot.add_build_request.called)
def test_error_in_peek_response(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = { 'error': { 'reason': 'PROBLEM', 'message': 'Something is bad', } } run_deferred(self.integrator.poll_builds()) self.assertFalse(self.buildbucket.api.lease.called)
def test_build_retried(self): with self.create_integrator(): build = self.mock_existing_build() # A build is marked during master stop. self.integrator.stop() run_deferred(self.integrator.on_build_finished(build, 'RETRY')) # Do not delete lease for RETRY builds. self.assertTrue(build.id in self.integrator._leases) self.assertFalse(self.buildbucket.api.fail.called)
def test_error_in_peek_response(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = { 'error': { 'reason': 'PROBLEM', 'message': 'Something is bad', } } run_deferred(self.integrator.poll_builds()) self.assertFalse(self.buildbucket.api.lease.called)
def test_build_retried(self): with self.create_integrator(): build = self.mock_existing_build() # A build is marked during master stop. self.integrator.stop() run_deferred(self.integrator.on_build_finished(build, 'RETRY')) # Do not delete lease for RETRY builds. self.assertTrue(build.id in self.integrator._leases) self.assertFalse(self.buildbucket.api.fail.called)
def test_honor_max_lease_count(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = { 'builds': [ self.buildbucket_build_rel, self.buildbucket_build_dbg, self.buildbucket_build_dbg ], } run_deferred(self.integrator.poll_builds()) # Assert only two builds were leased. self.assertEqual(self.buildbucket.api.lease.call_count, 2)
def test_build_was_not_leased(self): with self.create_integrator(), self.mock_build_peek(): self.buildbucket.api.lease.return_value = { 'error': { 'reason': 'CANNOT_LEASE_BUILD', 'message': 'Sorry', } } self.buildbucket.api.lease.side_effect = None run_deferred(self.integrator.poll_builds()) self.assertFalse(self.buildbot.add_build_request.called)
def test_build_was_not_leased(self): with self.create_integrator(), self.mock_build_peek(): self.buildbucket.api.lease.return_value = { 'error': { 'reason': 'CANNOT_LEASE_BUILD', 'message': 'Sorry', } } self.buildbucket.api.lease.side_effect = None run_deferred(self.integrator.poll_builds()) self.assertFalse(self.buildbot.add_build_request.called)
def test_honor_max_lease_count(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = { 'builds': [ self.buildbucket_build_rel, self.buildbucket_build_dbg, self.buildbucket_build_dbg ], } run_deferred(self.integrator.poll_builds()) # Assert only two builds were leased. self.assertEqual(self.buildbucket.api.lease.call_count, 2)
def test_build_not_scheduled_if_builder_name_is_wrong(self): with self.create_integrator(): bb = self.integrator.buildbot bb.add_build_request.return_value = (1, 1) # No Debug builder. del bb.get_builders.return_value['Debug'] with self.mock_build_peek(): run_deferred(self.integrator.poll_builds()) # Assert added one buildset of two. self.assertEqual(bb.add_build_request.call_count, 1)
def test_build_not_scheduled_if_builder_name_is_wrong(self): with self.create_integrator(): bb = self.integrator.buildbot bb.add_build_request.return_value = (1, 1) # No Debug builder. del bb.get_builders.return_value['Debug'] with self.mock_build_peek(): run_deferred(self.integrator.poll_builds()) # Assert added one buildset of two. self.assertEqual(bb.add_build_request.call_count, 1)
def test_lease_for_build_request_expired(self): with self.create_integrator(): build_request = Mock() self.integrator._leases = { '1': { 'key': LEASE_KEY, 'build_request': build_request, }, } with self.mock_heartbeat_lease_expired('1'): run_deferred(self.integrator.send_heartbeats()) self.assertTrue(build_request.cancel.called)
def test_build_succeeded(self): with self.create_integrator(): build = self.mock_existing_build() self.buildbucket.api.succeed.return_value = {} run_deferred(self.integrator.on_build_finished(build, 'SUCCESS')) self.assertFalse(build.id in self.integrator._leases) self.buildbucket.api.succeed.assert_called_once_with( id=build.id, body={ 'lease_key': LEASE_KEY, 'result_details_json': build.expected_result_details_json, })
def test_lease_for_build_request_expired(self): with self.create_integrator(): build_request = Mock() self.integrator._leases = { '1': { 'key': LEASE_KEY, 'build_request': build_request, }, } with self.mock_heartbeat_lease_expired('1'): run_deferred(self.integrator.send_heartbeats()) self.assertTrue(build_request.cancel.called)
def test_build_succeeded(self): with self.create_integrator(): build = self.mock_existing_build() self.buildbucket.api.succeed.return_value = {} run_deferred(self.integrator.on_build_finished(build, 'SUCCESS')) self.assertFalse(build.id in self.integrator._leases) self.buildbucket.api.succeed.assert_called_once_with( id=build.id, body={ 'lease_key': LEASE_KEY, 'result_details_json': build.expected_result_details_json, } )
def test_all_scheduled(self): with self.create_integrator(): with self.mock_build_peek(): run_deferred(self.integrator.poll_builds()) self.assert_leased(self.buildbucket_build_rel['id']) self.assert_leased(self.buildbucket_build_dbg['id']) # Assert added two buildsets. self.assertEqual(self.buildbot.add_build_request.call_count, 2) self.assert_added_build_request( self.buildbucket_build_rel['id'], 'Release', self.ssid, buildset=BUILDSET) self.assert_added_build_request( self.buildbucket_build_dbg['id'], 'Debug', self.ssid, buildset=BUILDSET)
def test_get_change_without_id(self): cache = self.buildbot.change_cache cache.get.return_value = None result = run_deferred(self.store.get_change({ 'author': { 'email': '*****@*****.**', }, 'message': 'Hello world', })) expected_info = {'change_id': None} self.buildbot.add_change_to_db.assert_called_once_with( author='*****@*****.**', files=[], comments='Hello world', revision='', when_timestamp=None, branch=None, category=common.CHANGE_CATEGORY, revlink='', properties={ common.INFO_PROPERTY: (expected_info, 'Change'), }, repository='', project='', ) self.assertEqual(result, self.buildbot.get_change_by_id.return_value)
def test_minimalistic_build(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = { 'builds': [{ 'id': '1', 'bucket': BUCKET, 'parameters_json': json.dumps({ 'builder_name': 'Release', }), }], } run_deferred(self.integrator.poll_builds()) self.assert_leased('1') self.assert_added_build_request('1', 'Release', self.ssid)
def test_get_change(self): cache = self.buildbot.change_cache cache.get.return_value = None result = run_deferred(self.store.get_change(self.buildbucket_change)) info = json.dumps({ common.BUILDBUCKET_CHANGE_ID_PROPERTY: '1', }, sort_keys=True) self.buildbot.add_change_to_db.assert_called_once_with( author=self.buildbucket_change['author']['email'], files=[], comments=self.buildbucket_change['message'], revision=self.buildbucket_change['revision'], when_timestamp=datetime.datetime(2014, 12, 22), branch=self.buildbucket_change['branch'], category=common.CHANGE_CATEGORY, revlink=self.buildbucket_change['url'], properties={ common.INFO_PROPERTY: (info, 'Change'), }, repository=self.buildbucket_change.get('repo_url'), project=self.buildbucket_change.get('project'), ) self.assertEqual(result, self.buildbot.get_change_by_id.return_value)
def test_all_scheduled(self): with self.create_integrator(): with self.mock_build_peek(): run_deferred(self.integrator.poll_builds()) self.assert_leased(self.buildbucket_build_rel['id']) self.assert_leased(self.buildbucket_build_dbg['id']) # Assert added two buildsets. self.assertEqual(self.buildbot.add_build_request.call_count, 2) self.assert_added_build_request(self.buildbucket_build_rel['id'], 'Release', self.ssid, buildset=BUILDSET) self.assert_added_build_request(self.buildbucket_build_dbg['id'], 'Debug', self.ssid, buildset=BUILDSET)
def test_get_source_stamp_with_cache(self): ssid = Mock() ss_cache = { (self.buildbucket_change['id'],): ssid, } result = run_deferred( self.store.get_source_stamp([self.buildbucket_change], cache=ss_cache)) self.assertFalse(self.buildbot.change_cache.get.called) self.assertFalse(self.buildbot.insert_source_stamp_to_db.called) self.assertEqual(result, ssid)
def test_build_with_properties_hook(self): def hook(params, build): build['bucket'] = 'INVALID' params['builder_name'] = 'Debug' params['properties']['foo'] = 'bar' build = { 'id': '1', 'bucket': BUCKET, 'parameters_json': json.dumps({ 'builder_name': 'Release', 'properties': { 'a': 'b', } }), } with self.create_integrator(build_params_hook=hook): self.buildbucket.api.peek.return_value = { 'builds': [build], } run_deferred(self.integrator.poll_builds()) self.assert_leased('1') self.assert_added_build_request('1', 'Debug', self.ssid, properties={ 'a': 'b', 'foo': 'bar' }) # Modifications to 'build' in the hook should not be persisted. self.assertEqual(build['bucket'], BUCKET)
def test_find_change_in_db(self): change = Mock() rev = 123 change_id = 'abc' change.properties.getProperty.return_value = {'change_id': change_id} self.buildbot.get_change_by_id.return_value = change self.buildbot.find_changes_by_revision.return_value = [change.changeid] result = run_deferred(self.store._find_change_in_db((rev, change_id))) self.assertEqual(result, change) change.properties.getProperty.assert_any_call(common.INFO_PROPERTY) self.buildbot.find_changes_by_revision.assert_called_once_with(rev) self.buildbot.get_change_by_id.assert_called_once_with(change.changeid)
def test_invalid_build_def(self): with self.create_integrator(): self.buildbucket.api.peek.return_value = { 'builds': [{ 'id': '1', }], } run_deferred(self.integrator.poll_builds()) self.assert_buildbucket_lease_was_called('1') self.buildbucket.api.fail.assert_called_once_with( id='1', body={ 'lease_key': LEASE_KEY, 'failure_reason': 'INVALID_BUILD_DEFINITION', 'result_details_json': json.dumps({ 'error': { 'message': ( 'Build parameters (parameters_json) are not set'), } }, sort_keys=True) }) self.assertFalse(self.buildbot.add_build_request.called)
def test_get_source_stamp(self): result = run_deferred( self.store.get_source_stamp([self.buildbucket_change])) cache = self.buildbot.change_cache cache.get.assert_called_once_with( (self.buildbucket_change['revision'], self.buildbucket_change['id'])) bb_change = cache.get.return_value self.buildbot.insert_source_stamp_to_db.assert_called_once_with( branch=bb_change.branch, revision=bb_change.revision, repository=bb_change.repository, project=bb_change.project, changeids=[bb_change.number], ) self.assertEqual( result, self.buildbot.insert_source_stamp_to_db.return_value)
def test_build_skipped(self): with self.create_integrator(): build = self.mock_existing_build() run_deferred(self.integrator.on_build_finished(build, 'SKIPPED')) self.assertFalse(build.id in self.integrator._leases) self.assertFalse(self.buildbucket.api.fail.called)
def test_lease_for_build_expired(self): with self.create_integrator(): build = self.mock_existing_build() with self.mock_heartbeat_lease_expired(build.id): run_deferred(self.integrator.send_heartbeats()) self.assertTrue(self.buildbot.stop_build.called)
def test_get_change_with_cached_value(self): cache = self.buildbot.change_cache result = run_deferred(self.store.get_change(self.buildbucket_change)) self.assertEqual(result, cache.get.return_value) self.assertFalse(self.buildbot.add_change_to_db.called)
def test_build_skipped(self): with self.create_integrator(): build = self.mock_existing_build() run_deferred(self.integrator.on_build_finished(build, 'SKIPPED')) self.assertFalse(build.id in self.integrator._leases) self.assertFalse(self.buildbucket.api.fail.called)
def test_lease_for_build_expired(self): with self.create_integrator(): build = self.mock_existing_build() with self.mock_heartbeat_lease_expired(build.id): run_deferred(self.integrator.send_heartbeats()) self.assertTrue(self.buildbot.stop_build.called)