| # -*- coding: utf-8 -*- |
| # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Unittests for builder_status_lib.""" |
| |
| from __future__ import print_function |
| |
| import mock |
| |
| from chromite.cbuildbot import build_status_unittest |
| from chromite.lib.const import waterfall |
| from chromite.lib import buildbucket_lib |
| from chromite.lib import builder_status_lib |
| from chromite.lib import cidb |
| from chromite.lib import config_lib |
| from chromite.lib import constants |
| from chromite.lib import cros_test_lib |
| from chromite.lib import fake_cidb |
| from chromite.lib import failure_message_lib |
| from chromite.lib import failure_message_lib_unittest |
| from chromite.lib import metadata_lib |
| |
| |
| bb_infos = build_status_unittest.BuildbucketInfos |
| cidb_infos = build_status_unittest.CIDBStatusInfos |
| failure_msg_helper = failure_message_lib_unittest.FailureMessageHelper |
| stage_failure_helper = failure_message_lib_unittest.StageFailureHelper |
| |
| |
| def ConstructFailureMessages(build_config): |
| """Helper method to construct failure messages.""" |
| entry_1 = stage_failure_helper.GetStageFailure( |
| build_config=build_config, failure_id=1) |
| entry_2 = stage_failure_helper.GetStageFailure( |
| build_config=build_config, failure_id=2, outer_failure_id=1) |
| entry_3 = stage_failure_helper.GetStageFailure( |
| build_config=build_config, failure_id=3, outer_failure_id=1) |
| failure_entries = [entry_1, entry_2, entry_3] |
| failure_messages = ( |
| failure_message_lib.FailureMessageManager.ConstructStageFailureMessages( |
| failure_entries)) |
| |
| return failure_messages |
| |
| |
| class BuilderStatusLibTests(cros_test_lib.MockTestCase): |
| """Tests for builder_status_lib.""" |
| |
| def testGetSlavesAbortedBySelfDestructedMaster(self): |
| """Test GetSlavesAbortedBySelfDestructedMaster with aborted slaves.""" |
| db = fake_cidb.FakeCIDBConnection() |
| cidb.CIDBConnectionFactory.SetupMockCidb(db) |
| master_build_id = db.InsertBuild( |
| 'master', waterfall.WATERFALL_SWARMING, 1, 'master', 'bot_hostname', |
| buildbucket_id='0') |
| |
| self.assertEqual( |
| set(), |
| builder_status_lib.GetSlavesAbortedBySelfDestructedMaster( |
| master_build_id, db)) |
| |
| slave_build_id_1 = db.InsertBuild( |
| 'slave_1', waterfall.WATERFALL_SWARMING, 1, 'slave_1', 'bot_hostname', |
| master_build_id=master_build_id, buildbucket_id='1') |
| slave_build_id_2 = db.InsertBuild( |
| 'slave_2', waterfall.WATERFALL_SWARMING, 2, 'slave_2', 'bot_hostname', |
| master_build_id=master_build_id, buildbucket_id='2') |
| db.InsertBuild( |
| 'slave_3', waterfall.WATERFALL_SWARMING, 3, 'slave_3', 'bot_hostname', |
| master_build_id=master_build_id, buildbucket_id='3') |
| for slave_build_id in (slave_build_id_1, slave_build_id_2): |
| db.InsertBuildMessage( |
| master_build_id, |
| message_type=constants.MESSAGE_TYPE_IGNORED_REASON, |
| message_subtype=constants.MESSAGE_SUBTYPE_SELF_DESTRUCTION, |
| message_value=str(slave_build_id)) |
| self.assertEqual( |
| {'slave_1', 'slave_2'}, |
| builder_status_lib.GetSlavesAbortedBySelfDestructedMaster( |
| master_build_id, db)) |
| |
| |
| # pylint: disable=protected-access |
| class BuilderStatusManagerTest(cros_test_lib.MockTestCase): |
| """Tests for BuilderStatusManager.""" |
| |
| def setUp(self): |
| self.db = fake_cidb.FakeCIDBConnection() |
| |
| def testCreateBuildFailureMessageWithMessages(self): |
| """Test CreateBuildFailureMessage with stage failure messages.""" |
| overlays = constants.PRIVATE_OVERLAYS |
| dashboard_url = 'http://fake_dashboard_url' |
| slave = 'cyan-paladin' |
| failure_messages = ConstructFailureMessages(slave) |
| |
| build_msg = ( |
| builder_status_lib.BuilderStatusManager.CreateBuildFailureMessage( |
| slave, overlays, dashboard_url, failure_messages)) |
| |
| self.assertTrue('stage failed' in build_msg.message_summary) |
| self.assertTrue(build_msg.internal) |
| self.assertEqual(build_msg.builder, slave) |
| |
| def testCreateBuildFailureMessageWithoutMessages(self): |
| """Test CreateBuildFailureMessage without stage failure messages.""" |
| overlays = constants.PUBLIC_OVERLAYS |
| dashboard_url = 'http://fake_dashboard_url' |
| slave = 'cyan-paladin' |
| |
| build_msg = ( |
| builder_status_lib.BuilderStatusManager.CreateBuildFailureMessage( |
| slave, overlays, dashboard_url, None)) |
| |
| self.assertTrue('cbuildbot failed' in build_msg.message_summary) |
| self.assertFalse(build_msg.internal) |
| self.assertEqual(build_msg.builder, slave) |
| |
| def testCreateBuildFailureMessageWhenCanceled(self): |
| """Test CreateBuildFailureMessage with no stage failure and canceled""" |
| overlays = constants.PRIVATE_OVERLAYS |
| dashboard_url = 'http://fake_dashboard_url' |
| slave = 'cyan-paladin' |
| |
| build_msg = ( |
| builder_status_lib.BuilderStatusManager.CreateBuildFailureMessage( |
| slave, overlays, dashboard_url, None, |
| aborted_by_self_destruction=True)) |
| |
| self.assertTrue('aborted by self-destruction' in build_msg.message_summary) |
| self.assertFalse('cbuildbot failed' in build_msg.message_summary) |
| self.assertEqual(build_msg.builder, slave) |
| |
| def testCreateBuildFailureMessageSupersedesCancellation(self): |
| """Test CreateBuildFailureMessage with a stage failure when canceled""" |
| overlays = constants.PRIVATE_OVERLAYS |
| dashboard_url = 'http://fake_dashboard_url' |
| slave = 'cyan-paladin' |
| failure_messages = ConstructFailureMessages(slave) |
| |
| build_msg = ( |
| builder_status_lib.BuilderStatusManager.CreateBuildFailureMessage( |
| slave, overlays, dashboard_url, failure_messages, |
| aborted_by_self_destruction=True)) |
| |
| self.assertFalse('canceled by master' in build_msg.message_summary) |
| self.assertFalse('cbuildbot failed' in build_msg.message_summary) |
| self.assertEqual(build_msg.builder, slave) |
| |
| |
| class SlaveBuilderStatusTest(cros_test_lib.MockTestCase): |
| """Tests for SlaveBuilderStatus.""" |
| |
| def setUp(self): |
| self.db = fake_cidb.FakeCIDBConnection() |
| self.master_build_id = 0 |
| self.site_config = config_lib.GetConfig() |
| self.config = self.site_config['master-paladin'] |
| self.metadata = metadata_lib.CBuildbotMetadata() |
| self.db = fake_cidb.FakeCIDBConnection() |
| self.buildbucket_client = mock.Mock() |
| self.slave_1 = 'cyan-paladin' |
| self.slave_2 = 'auron-paladin' |
| self.builders_array = [self.slave_1, self.slave_2] |
| |
| def testGetAllSlaveBuildbucketInfo(self): |
| """Test GetAllSlaveBuildbucketInfo.""" |
| |
| # Test completed builds. |
| buildbucket_info_dict = { |
| 'build1': buildbucket_lib.BuildbucketInfo( |
| 'id_1', 1, 0, None, None, None), |
| 'build2': buildbucket_lib.BuildbucketInfo( |
| 'id_2', 1, 0, None, None, None) |
| } |
| self.PatchObject(buildbucket_lib, 'GetScheduledBuildDict', |
| return_value=buildbucket_info_dict) |
| |
| expected_status = 'COMPLETED' |
| expected_result = 'SUCCESS' |
| expected_url = 'fake_url' |
| content = { |
| 'build': { |
| 'status': expected_status, |
| 'result': expected_result, |
| 'url': expected_url |
| } |
| } |
| |
| self.buildbucket_client.GetBuildRequest.return_value = content |
| updated_buildbucket_info_dict = ( |
| builder_status_lib.SlaveBuilderStatus.GetAllSlaveBuildbucketInfo( |
| self.buildbucket_client, buildbucket_info_dict)) |
| |
| self.assertEqual(updated_buildbucket_info_dict['build1'].status, |
| expected_status) |
| self.assertEqual(updated_buildbucket_info_dict['build1'].result, |
| expected_result) |
| self.assertEqual(updated_buildbucket_info_dict['build1'].url, |
| expected_url) |
| self.assertEqual(updated_buildbucket_info_dict['build2'].status, |
| expected_status) |
| self.assertEqual(updated_buildbucket_info_dict['build2'].result, |
| expected_result) |
| self.assertEqual(updated_buildbucket_info_dict['build1'].url, |
| expected_url) |
| |
| # Test started builds. |
| expected_status = 'STARTED' |
| expected_result = None |
| content = { |
| 'build': { |
| 'status': 'STARTED' |
| } |
| } |
| self.buildbucket_client.GetBuildRequest.return_value = content |
| updated_buildbucket_info_dict = ( |
| builder_status_lib.SlaveBuilderStatus.GetAllSlaveBuildbucketInfo( |
| self.buildbucket_client, buildbucket_info_dict)) |
| |
| self.assertEqual(updated_buildbucket_info_dict['build1'].status, |
| expected_status) |
| self.assertEqual(updated_buildbucket_info_dict['build1'].result, |
| expected_result) |
| self.assertEqual(updated_buildbucket_info_dict['build2'].status, |
| expected_status) |
| self.assertEqual(updated_buildbucket_info_dict['build2'].result, |
| expected_result) |
| |
| # Test BuildbucketResponseException failures. |
| self.buildbucket_client.GetBuildRequest.side_effect = ( |
| buildbucket_lib.BuildbucketResponseException) |
| updated_buildbucket_info_dict = ( |
| builder_status_lib.SlaveBuilderStatus.GetAllSlaveBuildbucketInfo( |
| self.buildbucket_client, buildbucket_info_dict)) |
| self.assertIsNone(updated_buildbucket_info_dict['build1'].status) |
| self.assertIsNone(updated_buildbucket_info_dict['build2'].status) |
| |
| def _InsertMasterSlaveBuildsToCIDB(self): |
| """Insert master and slave builds into fake_cidb.""" |
| master = self.db.InsertBuild('master', waterfall.WATERFALL_SWARMING, 1, |
| 'master', 'host1') |
| slave1 = self.db.InsertBuild('slave1', waterfall.WATERFALL_SWARMING, 2, |
| 'slave1', 'host1', master_build_id=0, |
| buildbucket_id='id_1', status='fail') |
| slave2 = self.db.InsertBuild('slave2', waterfall.WATERFALL_SWARMING, 3, |
| 'slave2', 'host1', master_build_id=0, |
| buildbucket_id='id_2', status='fail') |
| return master, slave1, slave2 |
| |
| def testGetAllSlaveCIDBStatusInfo(self): |
| """GetAllSlaveCIDBStatusInfo without Buildbucket info.""" |
| _, slave1_id, slave2_id = self._InsertMasterSlaveBuildsToCIDB() |
| |
| expected_status = { |
| 'slave1': builder_status_lib.CIDBStatusInfo(slave1_id, 'fail', 2), |
| 'slave2': builder_status_lib.CIDBStatusInfo(slave2_id, 'fail', 3) |
| } |
| |
| cidb_status = ( |
| builder_status_lib.SlaveBuilderStatus.GetAllSlaveCIDBStatusInfo( |
| self.db, self.master_build_id, None)) |
| self.assertDictEqual(cidb_status, expected_status) |
| |
| cidb_status = ( |
| builder_status_lib.SlaveBuilderStatus.GetAllSlaveCIDBStatusInfo( |
| self.db, self.master_build_id, None)) |
| self.assertDictEqual(cidb_status, expected_status) |
| |
| def testGetAllSlaveCIDBStatusInfoWithBuildbucket(self): |
| """GetAllSlaveCIDBStatusInfo with Buildbucket info.""" |
| _, slave1_id, slave2_id = self._InsertMasterSlaveBuildsToCIDB() |
| |
| buildbucket_info_dict = { |
| 'slave1': build_status_unittest.BuildbucketInfos.GetStartedBuild( |
| bb_id='id_1'), |
| 'slave2': build_status_unittest.BuildbucketInfos.GetStartedBuild( |
| bb_id='id_2') |
| } |
| |
| expected_status = { |
| 'slave1': builder_status_lib.CIDBStatusInfo(slave1_id, 'fail', 2), |
| 'slave2': builder_status_lib.CIDBStatusInfo(slave2_id, 'fail', 3) |
| } |
| |
| cidb_status = ( |
| builder_status_lib.SlaveBuilderStatus.GetAllSlaveCIDBStatusInfo( |
| self.db, self.master_build_id, buildbucket_info_dict)) |
| self.assertDictEqual(cidb_status, expected_status) |
| |
| cidb_status = ( |
| builder_status_lib.SlaveBuilderStatus.GetAllSlaveCIDBStatusInfo( |
| self.db, self.master_build_id, buildbucket_info_dict)) |
| self.assertDictEqual(cidb_status, expected_status) |
| |
| def testGetAllSlaveCIDBStatusInfoWithRetriedBuilds(self): |
| """GetAllSlaveCIDBStatusInfo doesn't return retried builds.""" |
| self._InsertMasterSlaveBuildsToCIDB() |
| self.db.InsertBuild('slave1', waterfall.WATERFALL_SWARMING, 3, |
| 'slave1', 'host1', master_build_id=0, |
| buildbucket_id='id_3', status='inflight') |
| |
| buildbucket_info_dict = { |
| 'slave1': build_status_unittest.BuildbucketInfos.GetStartedBuild( |
| bb_id='id_3'), |
| 'slave2': build_status_unittest.BuildbucketInfos.GetStartedBuild( |
| bb_id='id_4') |
| } |
| |
| cidb_status = ( |
| builder_status_lib.SlaveBuilderStatus.GetAllSlaveCIDBStatusInfo( |
| self.db, self.master_build_id, buildbucket_info_dict)) |
| self.assertEqual(set(cidb_status.keys()), set(['slave1'])) |
| self.assertEqual(cidb_status['slave1'].status, 'inflight') |
| |
| |
| def ConstructBuilderStatusManager(self, |
| master_build_id=None, |
| db=None, |
| config=None, |
| metadata=None, |
| buildbucket_client=None, |
| builders_array=None, |
| dry_run=True): |
| if master_build_id is None: |
| master_build_id = self.master_build_id |
| if db is None: |
| db = self.db |
| if config is None: |
| config = self.config |
| if metadata is None: |
| metadata = self.metadata |
| if buildbucket_client is None: |
| buildbucket_client = self.buildbucket_client |
| if builders_array is None: |
| builders_array = self.builders_array |
| |
| return builder_status_lib.SlaveBuilderStatus( |
| master_build_id, db, config, metadata, buildbucket_client, |
| builders_array, dry_run) |
| |
| def testGetSlaveFailures(self): |
| """Test _GetSlaveFailures.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_InitSlaveInfo') |
| entry_1 = stage_failure_helper.GetStageFailure( |
| build_config=self.slave_1, failure_id=1) |
| entry_2 = stage_failure_helper.GetStageFailure( |
| build_config=self.slave_1, failure_id=2, outer_failure_id=1) |
| entry_3 = stage_failure_helper.GetStageFailure( |
| build_config=self.slave_2, failure_id=3) |
| failure_entries = [entry_1, entry_2, entry_3] |
| mock_db = mock.Mock() |
| mock_db.GetSlaveFailures.return_value = failure_entries |
| manager = self.ConstructBuilderStatusManager(db=mock_db) |
| slave_failures_dict = manager._GetSlaveFailures(None) |
| |
| self.assertItemsEqual(slave_failures_dict.keys(), |
| [self.slave_1, self.slave_2]) |
| self.assertEqual(len(slave_failures_dict[self.slave_1]), 1) |
| self.assertEqual(len(slave_failures_dict[self.slave_2]), 1) |
| self.assertTrue(isinstance(slave_failures_dict[self.slave_1][0], |
| failure_message_lib.CompoundFailureMessage)) |
| self.assertTrue(isinstance(slave_failures_dict[self.slave_2][0], |
| failure_message_lib.StageFailureMessage)) |
| |
| def testGetSlavesAbortedBySelfDestructionReturnsEmptySet(self): |
| """Test GetSlavesAbortedBySelfDestruction returns an empty set.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_InitSlaveInfo') |
| cidb_info_dict = cidb_infos.GetFullCIDBStatusInfo() |
| manager = self.ConstructBuilderStatusManager() |
| |
| aborted_slaves = manager._GetSlavesAbortedBySelfDestruction(cidb_info_dict) |
| self.assertEqual(aborted_slaves, set()) |
| |
| def testGetSlavesAbortedBySelfDestructionWithAbortedBuilds(self): |
| """Test GetSlavesAbortedBySelfDestruction with aborted builds.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_InitSlaveInfo') |
| cidb_info_dict = cidb_infos.GetFullCIDBStatusInfo() |
| self.db.InsertBuildMessage( |
| self.master_build_id, |
| message_type=constants.MESSAGE_TYPE_IGNORED_REASON, |
| message_subtype=constants.MESSAGE_SUBTYPE_SELF_DESTRUCTION, |
| message_value=str(3)) |
| manager = self.ConstructBuilderStatusManager() |
| |
| aborted_slaves = manager._GetSlavesAbortedBySelfDestruction(cidb_info_dict) |
| self.assertEqual(aborted_slaves, {'completed_failure'}) |
| |
| def testInitSlaveInfo(self): |
| """Test _InitSlaveInfo with Buildbucket info.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, |
| '_GetSlaveFailures') |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, |
| 'GetAllSlaveBuildbucketInfo', |
| return_value={self.slave_1: bb_infos.GetSuccessBuild()}) |
| manager = self.ConstructBuilderStatusManager() |
| |
| self.assertItemsEqual(manager.builders_array, [self.slave_1]) |
| self.assertIsNotNone(manager.buildbucket_info_dict) |
| |
| def testGetStatusWithBuildbucket(self): |
| """Test _GetStatus with Buildbucket info.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_InitSlaveInfo') |
| cidb_info_dict = cidb_infos.GetFullCIDBStatusInfo() |
| buildbucket_info_dict = bb_infos.GetFullBuildbucketInfoDict() |
| manager = self.ConstructBuilderStatusManager() |
| |
| status_1 = manager._GetStatus( |
| 'scheduled', cidb_info_dict, buildbucket_info_dict) |
| self.assertEqual(status_1, constants.BUILDER_STATUS_MISSING) |
| status_2 = manager._GetStatus( |
| 'started', cidb_info_dict, buildbucket_info_dict) |
| self.assertEqual(status_2, constants.BUILDER_STATUS_INFLIGHT) |
| status_3 = manager._GetStatus( |
| 'completed_success', cidb_info_dict, buildbucket_info_dict) |
| self.assertEqual(status_3, constants.BUILDER_STATUS_PASSED) |
| status_4 = manager._GetStatus( |
| 'completed_failure', cidb_info_dict, buildbucket_info_dict) |
| self.assertEqual(status_4, constants.BUILDER_STATUS_FAILED) |
| status_5 = manager._GetStatus( |
| 'completed_canceled', cidb_info_dict, buildbucket_info_dict) |
| self.assertEqual(status_5, constants.BUILDER_STATUS_FAILED) |
| |
| def test_GetDashboardUrlWithBuildbucket(self): |
| """Test _GetDashboardUrl with Buildbucket info.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_InitSlaveInfo') |
| manager = self.ConstructBuilderStatusManager() |
| cidb_info_dict = { |
| self.slave_1: cidb_infos.GetPassedBuild(build_id=1, build_number=100)} |
| buildbucket_info_dict = { |
| self.slave_1: bb_infos.GetSuccessBuild(url='http://buildbucket_url'), |
| self.slave_2: bb_infos.GetSuccessBuild(url='http://buildbucket_url'), |
| } |
| |
| dashboard_url = manager._GetDashboardUrl( |
| self.slave_1, cidb_info_dict, buildbucket_info_dict) |
| self.assertEqual( |
| dashboard_url, |
| 'http://buildbucket_url') |
| |
| dashboard_url = manager._GetDashboardUrl( |
| self.slave_2, cidb_info_dict, buildbucket_info_dict) |
| self.assertEqual(dashboard_url, 'http://buildbucket_url') |
| |
| def testGetDashboardUrlWithoutBuildbucket(self): |
| """Test _GetDashboardUrl without Buildbucket info.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_InitSlaveInfo') |
| manager = self.ConstructBuilderStatusManager() |
| |
| dashboard_url = manager._GetDashboardUrl(self.slave_1, {}, None) |
| self.assertIsNone(dashboard_url) |
| |
| def testGetMessageOnFailedBuilds(self): |
| """Test _GetMessage on failed builds.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_InitSlaveInfo') |
| mock_create_msg = self.PatchObject(builder_status_lib.BuilderStatusManager, |
| 'CreateBuildFailureMessage') |
| manager = self.ConstructBuilderStatusManager() |
| failure_messages = ConstructFailureMessages(self.slave_1) |
| slave_failures_dict = {self.slave_1: failure_messages} |
| aborted_slaves = {self.slave_1, self.slave_2} |
| manager._GetMessage( |
| self.slave_1, constants.BUILDER_STATUS_FAILED, 'dashboard_url', |
| slave_failures_dict, aborted_slaves) |
| |
| mock_create_msg.assert_called_with( |
| self.slave_1, mock.ANY, 'dashboard_url', |
| slave_failures_dict.get(self.slave_1), aborted_by_self_destruction=True) |
| |
| def testGetMessageOnNotFailedBuilds(self): |
| """Test _GetMessage on not failed builds.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_InitSlaveInfo') |
| mock_create_msg = self.PatchObject(builder_status_lib.BuilderStatusManager, |
| 'CreateBuildFailureMessage') |
| manager = self.ConstructBuilderStatusManager() |
| manager._GetMessage( |
| self.slave_1, constants.BUILDER_STATUS_PASSED, 'dashboard_url', {}, |
| set()) |
| |
| mock_create_msg.assert_not_called() |
| |
| def testGetBuilderStatusForBuild(self): |
| """Test GetBuilderStatusForBuild.""" |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_InitSlaveInfo') |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_GetStatus') |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_GetDashboardUrl') |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '_GetMessage') |
| manager = self.ConstructBuilderStatusManager() |
| self.assertIsNotNone(manager.GetBuilderStatusForBuild(self.slave_1)) |
| |
| def testCancelBuilds(self): |
| """Test CancelBuilds.""" |
| buildbucket_id_1 = '100' |
| buildbucket_id_2 = '200' |
| buildbucket_ids = [buildbucket_id_1, buildbucket_id_2] |
| |
| cancel_mock = self.buildbucket_client.CancelBatchBuildsRequest |
| |
| cancel_mock.return_value = dict() |
| builder_status_lib.CancelBuilds(buildbucket_ids, self.buildbucket_client) |
| |
| self.assertEqual(cancel_mock.call_count, 1) |
| |
| def testCancelNoBuilds(self): |
| """Test CancelBuilds with no builds to cancel.""" |
| cancel_mock = self.buildbucket_client.CancelBatchBuildsRequest |
| |
| builder_status_lib.CancelBuilds([], self.buildbucket_client) |
| self.assertEqual(cancel_mock.call_count, 0) |
| |
| def testCancelWithBuildbucketClient(self): |
| """Test Buildbucket client cancels successfully during CancelBuilds.""" |
| buildbucket_id_1 = '100' |
| buildbucket_id_2 = '200' |
| buildbucket_ids = [buildbucket_id_1, buildbucket_id_2] |
| |
| self.PatchObject(buildbucket_lib, 'GetServiceAccount', |
| return_value=True) |
| send_request_mock = self.PatchObject(buildbucket_lib.BuildbucketClient, |
| 'SendBuildbucketRequest', |
| return_value=dict()) |
| buildbucket_client = buildbucket_lib.BuildbucketClient( |
| mock.Mock(), mock.Mock()) |
| |
| builder_status_lib.CancelBuilds(buildbucket_ids, buildbucket_client) |
| |
| self.assertEqual(send_request_mock.call_count, 1) |
| |
| def testAbortedBySelfDestruction(self): |
| """Test that self-destructive aborts in CIDB are recognized.""" |
| slave = 'cyan-paladin' |
| builder_number = 37 |
| |
| slave_id = self.db.InsertBuild(slave, waterfall.WATERFALL_SWARMING, |
| builder_number, slave, 'bot_hostname', |
| master_build_id=self.master_build_id) |
| self.db.InsertBuildMessage( |
| self.master_build_id, |
| message_type=constants.MESSAGE_TYPE_IGNORED_REASON, |
| message_subtype=constants.MESSAGE_SUBTYPE_SELF_DESTRUCTION, |
| message_value=str(slave_id)) |
| |
| self.assertTrue( |
| builder_status_lib.BuilderStatusManager.AbortedBySelfDestruction( |
| self.db, slave_id, self.master_build_id)) |
| |
| def testNotAbortedBySelfDestruction(self): |
| """Test that aborts in CIDB are only flagged if they happened.""" |
| slave = 'cyan-paladin' |
| builder_number = 37 |
| |
| slave_id = self.db.InsertBuild(slave, waterfall.WATERFALL_SWARMING, |
| builder_number, slave, 'bot_hostname', |
| master_build_id=self.master_build_id) |
| self.db.InsertBuildMessage(self.master_build_id, |
| message_value=slave_id) |
| builder_number = 1 |
| |
| self.db.InsertBuild(slave, waterfall.WATERFALL_SWARMING, |
| builder_number, slave, 'bot_hostname') |
| self.db.InsertBuildMessage(slave) |
| |
| self.assertFalse( |
| builder_status_lib.BuilderStatusManager.AbortedBySelfDestruction( |
| self.db, slave_id, self.master_build_id)) |
| |
| def testAbortedBySelfDestructionOnBuildWithoutMaster(self): |
| """AbortedBySelfDestruction returns False on builds without master.""" |
| self.assertFalse( |
| builder_status_lib.BuilderStatusManager.AbortedBySelfDestruction( |
| self.db, 1, None)) |
| |
| |
| class BuilderStatusesFetcherTests(cros_test_lib.MockTestCase): |
| """Tests for BuilderStatusesFetcher.""" |
| |
| def setUp(self): |
| self.build_id = 0 |
| self.db = fake_cidb.FakeCIDBConnection() |
| self.site_config = config_lib.GetConfig() |
| self.config = self.site_config['master-paladin'] |
| self.slave_config = self.site_config['eve-paladin'] |
| self.message = 'build message' |
| self.metadata = metadata_lib.CBuildbotMetadata() |
| self.buildbucket_client = mock.Mock() |
| self.slave_1 = 'cyan-paladin' |
| self.slave_2 = 'auron-paladin' |
| self.builders_array = [self.slave_1, self.slave_2] |
| |
| def CreateBuilderStatusesFetcher( |
| self, build_id=None, db=None, success=True, message=None, config=None, |
| metadata=None, buildbucket_client=None, builders_array=None, |
| dry_run=True): |
| build_id = build_id or self.build_id |
| db = db or self.db |
| message = message or self.message |
| config = config or self.config |
| metadata = metadata or self.metadata |
| buildbucket_client = buildbucket_client or self.buildbucket_client |
| builders_array = (builders_array if builders_array is not None |
| else self.builders_array) |
| self.PatchObject(buildbucket_lib, 'FetchCurrentSlaveBuilders', |
| return_value=builders_array) |
| builder_statuses_fetcher = builder_status_lib.BuilderStatusesFetcher( |
| build_id, db, success, message, config, metadata, buildbucket_client, |
| builders_array=builders_array, dry_run=dry_run) |
| |
| return builder_statuses_fetcher |
| |
| def _PatchesForGetSlaveBuilderStatus(self, status_dict): |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, '__init__', |
| return_value=None) |
| message_mock = mock.Mock() |
| message_mock.BuildFailureMessageToStr.return_value = 'failure_message_str' |
| build_statuses = { |
| x: builder_status_lib.BuilderStatus( |
| status_dict[x].status, message_mock) for x in status_dict |
| } |
| self.PatchObject(builder_status_lib.SlaveBuilderStatus, |
| 'GetBuilderStatusForBuild', |
| side_effect=lambda config: build_statuses[config]) |
| |
| def testFetchLocalBuilderStatus(self): |
| """Test _FetchLocalBuilderStatus.""" |
| fetcher = self.CreateBuilderStatusesFetcher() |
| |
| local_builder_status = fetcher._FetchLocalBuilderStatus() |
| self.assertTrue('master-paladin' in local_builder_status.keys()) |
| |
| def testFetchSlaveBuilderStatusesWithEmptySlaveList(self): |
| """Test _FetchSlaveBuilderStatuses with an empty slave list.""" |
| self._PatchesForGetSlaveBuilderStatus({}) |
| fetcher = self.CreateBuilderStatusesFetcher(builders_array=[]) |
| statuses = fetcher._FetchSlaveBuilderStatuses() |
| self.assertEqual(statuses, {}) |
| |
| def testFetchSlaveBuildersStatusesWithSlaveList(self): |
| """Test _FetchSlaveBuilderStatuses with a slave list.""" |
| status_dict = { |
| 'build1': build_status_unittest.CIDBStatusInfos.GetFailedBuild( |
| build_id=1), |
| 'build2': build_status_unittest.CIDBStatusInfos.GetPassedBuild( |
| build_id=2), |
| 'build3': build_status_unittest.CIDBStatusInfos.GetInflightBuild( |
| build_id=3) |
| } |
| self._PatchesForGetSlaveBuilderStatus(status_dict) |
| fetcher = self.CreateBuilderStatusesFetcher( |
| builders_array=['build1', 'build2', 'build3']) |
| statuses = fetcher._FetchSlaveBuilderStatuses() |
| self.assertTrue(statuses['build1'].Failed()) |
| self.assertTrue(statuses['build2'].Passed()) |
| self.assertTrue(statuses['build3'].Inflight()) |
| |
| def testGetBuilderStatusesOnSlaves(self): |
| """Test GetBuilderStatuses on slaves.""" |
| fetcher = self.CreateBuilderStatusesFetcher( |
| config=self.slave_config, builders_array=[]) |
| |
| important_statuses, experimental_statuses = fetcher.GetBuilderStatuses() |
| self.assertEqual(len(important_statuses), 1) |
| self.assertEqual(len(experimental_statuses), 0) |
| self.assertTrue(important_statuses['eve-paladin'].Passed()) |
| |
| def testGetBuilderStatusesOnMasters(self): |
| """Test GetBuilderStatuses on masters.""" |
| status_dict = { |
| 'build1': build_status_unittest.CIDBStatusInfos.GetFailedBuild( |
| build_id=1), |
| 'build2': build_status_unittest.CIDBStatusInfos.GetPassedBuild( |
| build_id=2), |
| 'build3': build_status_unittest.CIDBStatusInfos.GetInflightBuild( |
| build_id=3) |
| } |
| self._PatchesForGetSlaveBuilderStatus(status_dict) |
| fetcher = self.CreateBuilderStatusesFetcher( |
| builders_array=status_dict.keys()) |
| |
| important, experimental = fetcher.GetBuilderStatuses() |
| self.assertItemsEqual(['build1', 'build2', 'build3', 'master-paladin'], |
| important.keys()) |
| self.assertItemsEqual([], experimental.keys()) |
| |
| # Update the experimental_builders in metadata |
| self.metadata.UpdateWithDict({ |
| constants.METADATA_EXPERIMENTAL_BUILDERS: ['build1'] |
| }) |
| important, experimental = fetcher.GetBuilderStatuses() |
| self.assertItemsEqual(['build2', 'build3', 'master-paladin'], |
| important.keys()) |
| self.assertItemsEqual(['build1'], experimental.keys()) |
| |
| def _CreateBuilderStatusDict(self): |
| passed = builder_status_lib.BuilderStatus( |
| constants.BUILDER_STATUS_PASSED, None) |
| failed = builder_status_lib.BuilderStatus( |
| constants.BUILDER_STATUS_FAILED, mock.Mock()) |
| inflight = builder_status_lib.BuilderStatus( |
| constants.BUILDER_STATUS_INFLIGHT, mock.Mock()) |
| missing = builder_status_lib.BuilderStatus( |
| constants.BUILDER_STATUS_MISSING, None) |
| return {'passed': passed, |
| 'failed': failed, |
| 'inflight': inflight, |
| 'missing': missing} |
| |
| def testGetFailingBuilds(self): |
| """Test GetFailingBuilds.""" |
| statuses = self._CreateBuilderStatusDict() |
| self.assertEqual( |
| builder_status_lib.BuilderStatusesFetcher.GetFailingBuilds(statuses), |
| {'failed'}) |
| |
| def testGetInflightBuilds(self): |
| """Test GetInflightBuilds.""" |
| statuses = self._CreateBuilderStatusDict() |
| self.assertEqual( |
| builder_status_lib.BuilderStatusesFetcher.GetInflightBuilds(statuses), |
| {'inflight'}) |
| |
| def testGetNostatBuilds(self): |
| """Test GetNostatBuilds.""" |
| statuses = self._CreateBuilderStatusDict() |
| self.assertEqual( |
| builder_status_lib.BuilderStatusesFetcher.GetNostatBuilds(statuses), |
| {'missing'}) |