def testActions(self): """Test that InsertCLActions accepts 0-, 1-, and multi-item lists.""" db = self._PrepareDatabase() build_id = db.InsertBuild('my builder', 'chromiumos', _random(), 'my config', 'my bot hostname') a1 = clactions.CLAction.FromGerritPatchAndAction( metadata_lib.GerritPatchTuple(1, 1, True), constants.CL_ACTION_PICKED_UP) a2 = clactions.CLAction.FromGerritPatchAndAction( metadata_lib.GerritPatchTuple(1, 1, True), constants.CL_ACTION_PICKED_UP) a3 = clactions.CLAction.FromGerritPatchAndAction( metadata_lib.GerritPatchTuple(1, 1, True), constants.CL_ACTION_PICKED_UP) db.InsertCLActions(build_id, []) db.InsertCLActions(build_id, [a1]) db.InsertCLActions(build_id, [a2, a3]) action_count = db._GetEngine().execute( 'select count(*) from clActionTable').fetchall()[0][0] self.assertEqual(action_count, 3) # Test that all known CL action types can be inserted fakepatch = metadata_lib.GerritPatchTuple(1, 1, True) all_actions_list = [ clactions.CLAction.FromGerritPatchAndAction(fakepatch, action) for action in constants.CL_ACTIONS ] db.InsertCLActions(build_id, all_actions_list)
def testGetRequeuedOrSpeculative(self): """Tests GetRequeuedOrSpeculative function.""" change = metadata_lib.GerritPatchTuple(1, 1, False) speculative_change = metadata_lib.GerritPatchTuple(2, 2, False) changes = [change, speculative_change] build_id = self.fake_db.InsertBuild('n', 'w', 1, 'c', 'h') # A fresh change should not be marked requeued. A fresh specualtive # change should be marked as speculative. action_history = self.fake_db.GetActionsForChanges(changes) a = clactions.GetRequeuedOrSpeculative(change, action_history, False) self.assertEqual(a, None) a = clactions.GetRequeuedOrSpeculative(speculative_change, action_history, True) self.assertEqual(a, constants.CL_ACTION_SPECULATIVE) self._Act(build_id, speculative_change, a) # After picking up either change, neither should need an additional # requeued or speculative action. self._Act(build_id, speculative_change, constants.CL_ACTION_PICKED_UP) self._Act(build_id, change, constants.CL_ACTION_PICKED_UP) action_history = self.fake_db.GetActionsForChanges(changes) a = clactions.GetRequeuedOrSpeculative(change, action_history, False) self.assertEqual(a, None) a = clactions.GetRequeuedOrSpeculative(speculative_change, action_history, True) self.assertEqual(a, None) # After being rejected, both changes need an action (requeued and # speculative accordingly). self._Act(build_id, speculative_change, constants.CL_ACTION_KICKED_OUT) self._Act(build_id, change, constants.CL_ACTION_KICKED_OUT) action_history = self.fake_db.GetActionsForChanges(changes) a = clactions.GetRequeuedOrSpeculative(change, action_history, False) self.assertEqual(a, constants.CL_ACTION_REQUEUED) self._Act(build_id, change, a) a = clactions.GetRequeuedOrSpeculative(speculative_change, action_history, True) self.assertEqual(a, constants.CL_ACTION_SPECULATIVE) self._Act(build_id, speculative_change, a) # Once a speculative change becomes un-speculative, it needs a REQUEUD # action. action_history = self.fake_db.GetActionsForChanges(changes) a = clactions.GetRequeuedOrSpeculative(speculative_change, action_history, False) self.assertEqual(a, constants.CL_ACTION_REQUEUED) self._Act(build_id, speculative_change, a)
def testMultiprocessSafety(self): m = multiprocessing.Manager() metadata = metadata_lib.CBuildbotMetadata(multiprocess_manager=m) key_dict = {'key1': 1, 'key2': 2} starting_dict = { 'key1': 1, 'key2': '2', 'key3': key_dict, 'cl_actions': [('a', 1), ('b', 2)], 'board-metadata': { 'board-1': {'info': 432}, }, } # Test that UpdateWithDict is process-safe parallel.RunParallelSteps([lambda: metadata.UpdateWithDict(starting_dict)]) ending_dict = metadata.GetDict() self.assertEqual(starting_dict, ending_dict) # Test that UpdateKeyDictWithDict is process-safe parallel.RunParallelSteps([lambda: metadata.UpdateKeyDictWithDict( 'key3', key_dict)]) ending_dict = metadata.GetDict() self.assertEqual(starting_dict, ending_dict) # Test that RecordCLAction is process-safe fake_change = metadata_lib.GerritPatchTuple(12345, 1, False) fake_action = ('asdf,') parallel.RunParallelSteps([lambda: metadata.RecordCLAction(fake_change, fake_action)]) ending_dict = metadata.GetDict() # Assert that an action was recorded. self.assertEqual(len(starting_dict['cl_actions']) + 1, len(ending_dict['cl_actions']))
def testGetCLPreCQStatus(self): change = metadata_lib.GerritPatchTuple(1, 1, False) # Initial pre-CQ status of a change is None. self.assertEqual(self._GetCLStatus(change), None) # Builders can update the CL's pre-CQ status. build_id = self.fake_db.InsertBuild(constants.PRE_CQ_LAUNCHER_NAME, waterfall.WATERFALL_INTERNAL, 1, constants.PRE_CQ_LAUNCHER_CONFIG, 'bot-hostname') self._Act(build_id, change, constants.CL_ACTION_PRE_CQ_WAITING) self.assertEqual(self._GetCLStatus(change), constants.CL_STATUS_WAITING) self._Act(build_id, change, constants.CL_ACTION_PRE_CQ_INFLIGHT) self.assertEqual(self._GetCLStatus(change), constants.CL_STATUS_INFLIGHT) # Recording a cl action that is not a valid pre-cq status should leave # pre-cq status unaffected. self._Act(build_id, change, 'polenta') self.assertEqual(self._GetCLStatus(change), constants.CL_STATUS_INFLIGHT) self._Act(build_id, change, constants.CL_ACTION_PRE_CQ_RESET) self.assertEqual(self._GetCLStatus(change), None)
def testGetCancelledPreCQBuilds(self): """Test GetCancelledPreCQBuilds.""" c1 = metadata_lib.GerritPatchTuple(1, 1, False) build_id = self.fake_db.InsertBuild('n', 'w', 1, 'c', 'h') a1 = clactions.CLAction.FromGerritPatchAndAction( c1, constants.CL_ACTION_TRYBOT_CANCELLED, reason='binhost-pre-cq') a2 = clactions.CLAction.FromGerritPatchAndAction( c1, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='binhost-pre-cq', buildbucket_id='1') a3 = clactions.CLAction.FromGerritPatchAndAction( c1, constants.CL_ACTION_TRYBOT_CANCELLED, reason='binhost-pre-cq', buildbucket_id='2') cl_actions = [a1, a2, a3] self.fake_db.InsertCLActions(build_id, cl_actions) action_history = self.fake_db.GetActionsForChanges([c1]) builds = clactions.GetCancelledPreCQBuilds(action_history) self.assertEqual(len(builds), 1) self.assertEqual(builds.pop().buildbucket_id, '2')
def _PopulateFakeCidbWithTestData(self, cq): """Generate test data and insert it in the the fake cidb object. Args: cq: Whether this is a CQ run. If False, this is a Pre-CQ run. """ # Mock patches for test data. c1p1 = metadata_lib.GerritPatchTuple(1, 1, False) c2p1 = metadata_lib.GerritPatchTuple(2, 1, True) c2p2 = metadata_lib.GerritPatchTuple(2, 2, True) c3p1 = metadata_lib.GerritPatchTuple(3, 1, True) c3p2 = metadata_lib.GerritPatchTuple(3, 2, True) c4p1 = metadata_lib.GerritPatchTuple(4, 1, True) c4p2 = metadata_lib.GerritPatchTuple(4, 2, True) # Mock builder status dictionaries passed_status = {'status': constants.BUILDER_STATUS_PASSED} failed_status = {'status': constants.BUILDER_STATUS_FAILED} t = datetime.datetime.now() delta = datetime.timedelta(hours=1) bot_config = (constants.CQ_MASTER if cq else constants.PRE_CQ_DEFAULT_CONFIGS[0]) # pylint: disable=bad-continuation test_metadata = [ # Build 1 picks up no patches. metadata_lib.CBuildbotMetadata().UpdateWithDict({ 'build-number': 1, 'bot-config': bot_config, 'results': [], 'status': passed_status }), # Build 2 picks up c1p1 and does nothing. metadata_lib.CBuildbotMetadata().UpdateWithDict({ 'build-number': 2, 'bot-config': bot_config, 'results': [], 'status': failed_status, 'changes': [c1p1._asdict()] }).RecordCLAction(c1p1, constants.CL_ACTION_PICKED_UP, t + delta), # Build 3 picks up c1p1 and c2p1 and rejects both. # c3p1 is not included in the run because it fails to apply. metadata_lib.CBuildbotMetadata().UpdateWithDict({ 'build-number': 3, 'bot-config': bot_config, 'results': [], 'status': failed_status, 'changes': [c1p1._asdict(), c2p1._asdict()] }).RecordCLAction(c1p1, constants.CL_ACTION_PICKED_UP, t + delta).RecordCLAction( c2p1, constants.CL_ACTION_PICKED_UP, t + delta).RecordCLAction( c1p1, constants.CL_ACTION_KICKED_OUT, t + delta).RecordCLAction( c2p1, constants.CL_ACTION_KICKED_OUT, t + delta).RecordCLAction( c3p1, constants.CL_ACTION_KICKED_OUT, t + delta), # Build 4 picks up c4p1 and does nothing with it. # c4p2 isn't picked up because it fails to apply. metadata_lib.CBuildbotMetadata().UpdateWithDict({ 'build-number': 3, 'bot-config': bot_config, 'results': [], 'status': failed_status, 'changes': [c4p1._asdict()] }).RecordCLAction(c4p1, constants.CL_ACTION_PICKED_UP, t + delta).RecordCLAction( c4p2, constants.CL_ACTION_KICKED_OUT, t + delta), ] if cq: test_metadata += [ # Build 4 picks up c1p1, c2p2, c3p2, c4p1 and submits the first three. # c4p2 is submitted without being tested. # So c1p1 should be detected as a 1-time rejected good patch, # and c2p1 should be detected as a possibly bad patch. metadata_lib.CBuildbotMetadata().UpdateWithDict({ 'build-number': 4, 'bot-config': bot_config, 'results': [], 'status': passed_status, 'changes': [c1p1._asdict(), c2p2._asdict()] }).RecordCLAction( c1p1, constants.CL_ACTION_PICKED_UP, t + delta).RecordCLAction( c2p2, constants.CL_ACTION_PICKED_UP, t + delta).RecordCLAction( c3p2, constants.CL_ACTION_PICKED_UP, t + delta).RecordCLAction( c4p1, constants.CL_ACTION_PICKED_UP, t + delta).RecordCLAction( c1p1, constants.CL_ACTION_SUBMITTED, t + delta).RecordCLAction( c2p2, constants.CL_ACTION_SUBMITTED, t + delta).RecordCLAction( c3p2, constants.CL_ACTION_SUBMITTED, t + delta).RecordCLAction( c4p2, constants.CL_ACTION_SUBMITTED, t + delta), ] else: test_metadata += [ metadata_lib.CBuildbotMetadata().UpdateWithDict({ 'build-number': 5, 'bot-config': bot_config, 'results': [], 'status': failed_status, 'changes': [c4p1._asdict()] }).RecordCLAction(c4p1, constants.CL_ACTION_PICKED_UP, t + delta).RecordCLAction( c4p1, constants.CL_ACTION_KICKED_OUT, t + delta), metadata_lib.CBuildbotMetadata().UpdateWithDict({ 'build-number': 6, 'bot-config': bot_config, 'results': [], 'status': failed_status, 'changes': [c4p1._asdict()] }).RecordCLAction(c1p1, constants.CL_ACTION_PICKED_UP, t + delta).RecordCLAction( c1p1, constants.CL_ACTION_KICKED_OUT, t + delta) ] # pylint: enable=bad-continuation # test_metadata should not be guaranteed to be ordered by build number # so shuffle it, but use the same seed each time so that unit test is # deterministic. random.seed(0) random.shuffle(test_metadata) for m in test_metadata: build_id = self.fake_db.InsertBuild(m.GetValue('bot-config'), waterfall.WATERFALL_INTERNAL, m.GetValue('build-number'), m.GetValue('bot-config'), 'bot-hostname') m.UpdateWithDict({'build_id': build_id}) actions = [] for action_metadata in m.GetDict()['cl_actions']: actions.append( clactions.CLAction.FromMetadataEntry(action_metadata)) self.fake_db.InsertCLActions(build_id, actions)
def testRecordSubmissionMetrics(self): """Test that we correctly compute a CL's handling time.""" change = metadata_lib.GerritPatchTuple(1, 1, False) launcher_id = self.fake_db.InsertBuild( 'launcher', waterfall.WATERFALL_INTERNAL, 1, constants.PRE_CQ_LAUNCHER_CONFIG, 'hostname') trybot_id = self.fake_db.InsertBuild( 'banana pre cq', waterfall.WATERFALL_INTERNAL, 1, 'banana-pre-cq', 'hostname') master_id = self.fake_db.InsertBuild( 'CQ master', waterfall.WATERFALL_INTERNAL, 1, constants.CQ_MASTER, 'hostname') slave_id = self.fake_db.InsertBuild( 'banana paladin', waterfall.WATERFALL_INTERNAL, 1, 'banana-paladin', 'hostname') start_time = datetime.datetime.now() c = itertools.count() def next_time(): return start_time + datetime.timedelta(seconds=c.next()) def a(build_id, action, reason=None): self._Act(build_id, change, action, reason=reason, timestamp=next_time()) strategies = {} # Change is screened, picked up, and rejected by the pre-cq, # non-speculatively. a(launcher_id, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, reason='banana-pre-cq') a(launcher_id, constants.CL_ACTION_SCREENED_FOR_PRE_CQ) a(launcher_id, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='banana-pre-cq') a(trybot_id, constants.CL_ACTION_PICKED_UP) a(trybot_id, constants.CL_ACTION_KICKED_OUT) # Change is re-marked by developer, picked up again by pre-cq, verified, # and marked as passed. a(launcher_id, constants.CL_ACTION_REQUEUED) a(launcher_id, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='banana-pre-cq') a(trybot_id, constants.CL_ACTION_PICKED_UP) a(trybot_id, constants.CL_ACTION_VERIFIED) a(launcher_id, constants.CL_ACTION_PRE_CQ_FULLY_VERIFIED) a(launcher_id, constants.CL_ACTION_PRE_CQ_PASSED) # Change is picked up by the CQ and rejected. a(master_id, constants.CL_ACTION_PICKED_UP) a(slave_id, constants.CL_ACTION_PICKED_UP) a(master_id, constants.CL_ACTION_KICKED_OUT) # Change is re-marked, picked up by the CQ, and forgiven. a(launcher_id, constants.CL_ACTION_REQUEUED) a(master_id, constants.CL_ACTION_PICKED_UP) a(slave_id, constants.CL_ACTION_PICKED_UP) a(master_id, constants.CL_ACTION_FORGIVEN) # Change is re-marked, picked up by the CQ, and forgiven. a(master_id, constants.CL_ACTION_PICKED_UP) a(slave_id, constants.CL_ACTION_PICKED_UP) a(master_id, constants.CL_ACTION_SUBMITTED) strategies[change] = constants.STRATEGY_CQ_SUCCESS action_history = self.fake_db.GetActionsForChanges([change]) clactions_metrics.RecordSubmissionMetrics( clactions.CLActionHistory(action_history), strategies)
def testGetCLActionCount(self): c1p1 = metadata_lib.GerritPatchTuple(1, 1, False) c1p2 = metadata_lib.GerritPatchTuple(1, 2, False) precq_build_id = self.fake_db.InsertBuild( constants.PRE_CQ_LAUNCHER_NAME, constants.WATERFALL_INTERNAL, 1, constants.PRE_CQ_LAUNCHER_CONFIG, 'bot-hostname') melon_build_id = self.fake_db.InsertBuild( 'melon builder name', constants.WATERFALL_INTERNAL, 1, 'melon-config-name', 'grape-bot-hostname') # Count should be zero before any actions are recorded. action_history = self.fake_db.GetActionsForChanges([c1p1]) self.assertEqual( 0, clactions.GetCLActionCount( c1p1, validation_pool.CQ_PIPELINE_CONFIGS, constants.CL_ACTION_KICKED_OUT, action_history)) # Record 3 failures for c1p1, and some other actions. Only count the # actions from builders in validation_pool.CQ_PIPELINE_CONFIGS. self.fake_db.InsertCLActions( precq_build_id, [clactions.CLAction.FromGerritPatchAndAction( c1p1, constants.CL_ACTION_KICKED_OUT)]) self.fake_db.InsertCLActions( precq_build_id, [clactions.CLAction.FromGerritPatchAndAction( c1p1, constants.CL_ACTION_PICKED_UP)]) self.fake_db.InsertCLActions( precq_build_id, [clactions.CLAction.FromGerritPatchAndAction( c1p1, constants.CL_ACTION_KICKED_OUT)]) self.fake_db.InsertCLActions( melon_build_id, [clactions.CLAction.FromGerritPatchAndAction( c1p1, constants.CL_ACTION_KICKED_OUT)]) action_history = self.fake_db.GetActionsForChanges([c1p1]) self.assertEqual( 2, clactions.GetCLActionCount( c1p1, validation_pool.CQ_PIPELINE_CONFIGS, constants.CL_ACTION_KICKED_OUT, action_history)) # Record a failure for c1p2. Now the latest patches failure count should be # 1 (true weather we pass c1p1 or c1p2), whereas the total failure count # should be 3. self.fake_db.InsertCLActions( precq_build_id, [clactions.CLAction.FromGerritPatchAndAction( c1p2, constants.CL_ACTION_KICKED_OUT)]) action_history = self.fake_db.GetActionsForChanges([c1p1]) self.assertEqual( 1, clactions.GetCLActionCount( c1p1, validation_pool.CQ_PIPELINE_CONFIGS, constants.CL_ACTION_KICKED_OUT, action_history)) self.assertEqual( 1, clactions.GetCLActionCount( c1p2, validation_pool.CQ_PIPELINE_CONFIGS, constants.CL_ACTION_KICKED_OUT, action_history)) self.assertEqual( 3, clactions.GetCLActionCount( c1p2, validation_pool.CQ_PIPELINE_CONFIGS, constants.CL_ACTION_KICKED_OUT, action_history, latest_patchset_only=False))
def testGetCLPreCQCategoriesAndPendingCLs(self): c1 = metadata_lib.GerritPatchTuple(1, 1, False) c2 = metadata_lib.GerritPatchTuple(2, 2, False) c3 = metadata_lib.GerritPatchTuple(3, 3, False) c4 = metadata_lib.GerritPatchTuple(4, 4, False) c5 = metadata_lib.GerritPatchTuple(5, 5, False) launcher_build_id = self.fake_db.InsertBuild( constants.PRE_CQ_LAUNCHER_NAME, constants.WATERFALL_INTERNAL, 1, constants.PRE_CQ_LAUNCHER_CONFIG, 'bot hostname 1') pineapple_build_id = self.fake_db.InsertBuild( 'pineapple', constants.WATERFALL_TRYBOT, 87, 'pineapple-pre-cq', 'pineapple hostname') guava_build_id = self.fake_db.InsertBuild( 'guava', constants.WATERFALL_TRYBOT, 7, 'guava-pre-cq', 'guava hostname') # c1 has 3 pending verifications, but only 1 inflight and 1 # launching, so it is not busy/inflight. self._Act(launcher_build_id, c1, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'pineapple-pre-cq') self._Act(launcher_build_id, c1, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'banana-pre-cq') self._Act(launcher_build_id, c1, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'guava-pre-cq') self._Act(launcher_build_id, c1, constants.CL_ACTION_TRYBOT_LAUNCHING, 'banana-pre-cq') self._Act(pineapple_build_id, c1, constants.CL_ACTION_PICKED_UP) # c2 has 3 pending verifications, 1 inflight and 1 launching, and 1 passed, # so it is busy. self._Act(launcher_build_id, c2, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'pineapple-pre-cq') self._Act(launcher_build_id, c2, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'banana-pre-cq') self._Act(launcher_build_id, c2, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'guava-pre-cq') self._Act(launcher_build_id, c2, constants.CL_ACTION_TRYBOT_LAUNCHING, 'banana-pre-cq') self._Act(pineapple_build_id, c2, constants.CL_ACTION_PICKED_UP) self._Act(guava_build_id, c2, constants.CL_ACTION_VERIFIED) # c3 has 2 pending verifications, both passed, so it is passed. self._Act(launcher_build_id, c3, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'pineapple-pre-cq') self._Act(launcher_build_id, c3, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'guava-pre-cq') self._Act(pineapple_build_id, c3, constants.CL_ACTION_VERIFIED) self._Act(guava_build_id, c3, constants.CL_ACTION_VERIFIED) # c4 has 2 pending verifications: one is inflight and the other # passed. It is considered inflight and busy. self._Act(launcher_build_id, c4, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'pineapple-pre-cq') self._Act(launcher_build_id, c4, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'guava-pre-cq') self._Act(pineapple_build_id, c4, constants.CL_ACTION_PICKED_UP) self._Act(guava_build_id, c4, constants.CL_ACTION_VERIFIED) # c5 has not even been screened. changes = [c1, c2, c3, c4, c5] action_history = self.fake_db.GetActionsForChanges(changes) progress_map = clactions.GetPreCQProgressMap(changes, action_history) self.assertEqual(({c2, c4}, {c4}, {c3}), clactions.GetPreCQCategories(progress_map)) # Among changes c1, c2, c3, only the guava-pre-cq config is pending. The # other configs are either inflight, launching, or passed everywhere. screened_changes = set(changes).intersection(progress_map) self.assertEqual({'guava-pre-cq'}, clactions.GetPreCQConfigsToTest(screened_changes, progress_map))
def testGetCLPreCQProgress(self): change = metadata_lib.GerritPatchTuple(1, 1, False) s = lambda: clactions.GetCLPreCQProgress( change, self.fake_db.GetActionsForChanges([change])) self.assertEqual({}, s()) # Simulate the pre-cq-launcher screening changes for pre-cq configs # to test with. launcher_build_id = self.fake_db.InsertBuild( constants.PRE_CQ_LAUNCHER_NAME, constants.WATERFALL_INTERNAL, 1, constants.PRE_CQ_LAUNCHER_CONFIG, 'bot hostname 1') self._Act(launcher_build_id, change, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'pineapple-pre-cq') self._Act(launcher_build_id, change, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, 'banana-pre-cq') configs = ['banana-pre-cq', 'pineapple-pre-cq'] self.assertEqual(configs, sorted(s().keys())) for c in configs: self.assertEqual(constants.CL_PRECQ_CONFIG_STATUS_PENDING, s()[c][0]) # Simulate a prior build rejecting change self._Act(launcher_build_id, change, constants.CL_ACTION_KICKED_OUT, 'pineapple-pre-cq') self.assertEqual(constants.CL_PRECQ_CONFIG_STATUS_FAILED, s()['pineapple-pre-cq'][0]) # Simulate the pre-cq-launcher launching tryjobs for all pending configs. for c in configs: self._Act(launcher_build_id, change, constants.CL_ACTION_TRYBOT_LAUNCHING, c) for c in configs: self.assertEqual(constants.CL_PRECQ_CONFIG_STATUS_LAUNCHED, s()[c][0]) # Simulate the tryjobs launching, and picking up the changes. banana_build_id = self.fake_db.InsertBuild( 'banana', constants.WATERFALL_TRYBOT, 12, 'banana-pre-cq', 'banana hostname') pineapple_build_id = self.fake_db.InsertBuild( 'pineapple', constants.WATERFALL_TRYBOT, 87, 'pineapple-pre-cq', 'pineapple hostname') self._Act(banana_build_id, change, constants.CL_ACTION_PICKED_UP) self._Act(pineapple_build_id, change, constants.CL_ACTION_PICKED_UP) for c in configs: self.assertEqual(constants.CL_PRECQ_CONFIG_STATUS_INFLIGHT, s()[c][0]) # Simulate the changes being retried. self._Act(banana_build_id, change, constants.CL_ACTION_FORGIVEN) self._Act(launcher_build_id, change, constants.CL_ACTION_FORGIVEN, 'pineapple-pre-cq') for c in configs: self.assertEqual(constants.CL_PRECQ_CONFIG_STATUS_PENDING, s()[c][0]) # Simulate the changes being rejected, either by the configs themselves # or by the pre-cq-launcher. self._Act(banana_build_id, change, constants.CL_ACTION_KICKED_OUT) self._Act(launcher_build_id, change, constants.CL_ACTION_KICKED_OUT, 'pineapple-pre-cq') for c in configs: self.assertEqual(constants.CL_PRECQ_CONFIG_STATUS_FAILED, s()[c][0]) # Simulate the tryjobs verifying the changes. self._Act(banana_build_id, change, constants.CL_ACTION_VERIFIED) self._Act(pineapple_build_id, change, constants.CL_ACTION_VERIFIED) for c in configs: self.assertEqual(constants.CL_PRECQ_CONFIG_STATUS_VERIFIED, s()[c][0]) # Simulate the pre-cq status being reset. self._Act(launcher_build_id, change, constants.CL_ACTION_PRE_CQ_RESET) self.assertEqual({}, s())
def testGetOldPreCQBuildActions(self): """Test GetOldPreCQBuildActions.""" c1 = metadata_lib.GerritPatchTuple(1, 1, False) c2 = metadata_lib.GerritPatchTuple(1, 2, False) changes = [c1, c2] build_id = self.fake_db.InsertBuild('n', 'w', 1, 'c', 'h') a1 = clactions.CLAction.FromGerritPatchAndAction( c1, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='binhost-pre-cq', timestamp=datetime.datetime.now() - datetime.timedelta(hours=5)) a2 = clactions.CLAction.FromGerritPatchAndAction( c1, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='binhost-pre-cq', timestamp=datetime.datetime.now() - datetime.timedelta(hours=4), buildbucket_id='1') a3 = clactions.CLAction.FromGerritPatchAndAction( c1, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='binhost-pre-cq', timestamp=datetime.datetime.now() - datetime.timedelta(hours=3), buildbucket_id='2') a4 = clactions.CLAction.FromGerritPatchAndAction( c1, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='pbinhost-pre-cq', timestamp=datetime.datetime.now() - datetime.timedelta(hours=3), buildbucket_id='3') a5 = clactions.CLAction.FromGerritPatchAndAction( c1, constants.CL_ACTION_TRYBOT_CANCELLED, reason='binhost-pre-cq', timestamp=datetime.datetime.now() - datetime.timedelta(hours=3), buildbucket_id='3') a6 = clactions.CLAction.FromGerritPatchAndAction( c1, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='binhost-pre-cq', timestamp=datetime.datetime.now() - datetime.timedelta(hours=1), buildbucket_id='4') a7 = clactions.CLAction.FromGerritPatchAndAction( c2, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='binhost-pre-cq', timestamp=datetime.datetime.now(), buildbucket_id='5') cl_actions = [a1, a2, a3, a4, a5, a6, a7] self.fake_db.InsertCLActions(build_id, cl_actions) action_history = self.fake_db.GetActionsForChanges(changes) timestamp = datetime.datetime.now() - datetime.timedelta(hours=2) c1_old_actions = clactions.GetOldPreCQBuildActions( c1, action_history, timestamp) c2_old_actions = clactions.GetOldPreCQBuildActions( c2, action_history, timestamp) self.assertTrue(len(c1_old_actions) == 0) self.assertTrue(len(c2_old_actions) == 1) self.assertEqual([c.buildbucket_id for c in c2_old_actions], [a6.buildbucket_id]) c1_old_actions = clactions.GetOldPreCQBuildActions( c1, action_history) c2_old_actions = clactions.GetOldPreCQBuildActions( c2, action_history) self.assertTrue(len(c1_old_actions) == 0) self.assertTrue(len(c2_old_actions) == 3) self.assertEqual([c.buildbucket_id for c in c2_old_actions], [a2.buildbucket_id, a3.buildbucket_id, a6.buildbucket_id])
def testGetCLHandlingTime(self): """Test that we correctly compute a CL's handling time.""" change = metadata_lib.GerritPatchTuple(1, 1, False) launcher_id = self.fake_db.InsertBuild( 'launcher', waterfall.WATERFALL_INTERNAL, 1, constants.PRE_CQ_LAUNCHER_CONFIG, 'hostname') trybot_id = self.fake_db.InsertBuild('banana pre cq', waterfall.WATERFALL_INTERNAL, 1, 'banana-pre-cq', 'hostname') master_id = self.fake_db.InsertBuild('CQ master', waterfall.WATERFALL_INTERNAL, 1, constants.CQ_MASTER, 'hostname') slave_id = self.fake_db.InsertBuild('banana paladin', waterfall.WATERFALL_INTERNAL, 1, 'banana-paladin', 'hostname') start_time = datetime.datetime.now() c = itertools.count() def next_time(): return start_time + datetime.timedelta(seconds=c.next()) def a(build_id, action, reason=None): self._Act(build_id, change, action, reason=reason, timestamp=next_time()) strategies = {} # Change is screened, picked up, and rejected by the pre-cq, # non-speculatively. a(launcher_id, constants.CL_ACTION_VALIDATION_PENDING_PRE_CQ, reason='banana-pre-cq') a(launcher_id, constants.CL_ACTION_SCREENED_FOR_PRE_CQ) a(launcher_id, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='banana-pre-cq') a(trybot_id, constants.CL_ACTION_PICKED_UP) a(trybot_id, constants.CL_ACTION_KICKED_OUT) # Change is re-marked by developer, picked up again by pre-cq, verified, and # marked as passed. a(launcher_id, constants.CL_ACTION_REQUEUED) a(launcher_id, constants.CL_ACTION_TRYBOT_LAUNCHING, reason='banana-pre-cq') a(trybot_id, constants.CL_ACTION_PICKED_UP) a(trybot_id, constants.CL_ACTION_VERIFIED) a(launcher_id, constants.CL_ACTION_PRE_CQ_FULLY_VERIFIED) a(launcher_id, constants.CL_ACTION_PRE_CQ_PASSED) # Change is picked up by the CQ and rejected. a(master_id, constants.CL_ACTION_PICKED_UP) a(slave_id, constants.CL_ACTION_PICKED_UP) a(master_id, constants.CL_ACTION_KICKED_OUT) # Change is re-marked, picked up by the CQ, and forgiven. a(launcher_id, constants.CL_ACTION_REQUEUED) a(master_id, constants.CL_ACTION_PICKED_UP) a(slave_id, constants.CL_ACTION_PICKED_UP) a(master_id, constants.CL_ACTION_FORGIVEN) # Change is re-marked, picked up by the CQ, and forgiven. a(master_id, constants.CL_ACTION_PICKED_UP) a(slave_id, constants.CL_ACTION_PICKED_UP) a(master_id, constants.CL_ACTION_SUBMITTED) strategies[change] = constants.STRATEGY_CQ_SUCCESS action_history = self.fake_db.GetActionsForChanges([change]) # Note: There are 2 ticks in the total handling time that are not accounted # for in the sub-times. These are the time between VALIDATION_PENDING and # SCREENED, and the time between FULLY_VERIFIED and PASSED. self.assertEqual(18, clactions.GetCLHandlingTime(change, action_history)) self.assertEqual(7, clactions.GetPreCQTime(change, action_history)) self.assertEqual(3, clactions.GetCQWaitTime(change, action_history)) self.assertEqual(6, clactions.GetCQRunTime(change, action_history)) self.assertEqual(3, clactions.GetCQAttemptsCount(change, action_history))