Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 3
0
    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,
                                            constants.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)
Esempio n. 4
0
    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']))
Esempio n. 5
0
    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', constants.WATERFALL_INTERNAL, 1,
            constants.PRE_CQ_LAUNCHER_CONFIG, 'hostname')
        trybot_id = self.fake_db.InsertBuild('banana pre cq',
                                             constants.WATERFALL_INTERNAL, 1,
                                             'banana-pre-cq', 'hostname')
        master_id = self.fake_db.InsertBuild('CQ master',
                                             constants.WATERFALL_INTERNAL, 1,
                                             constants.CQ_MASTER, 'hostname')
        slave_id = self.fake_db.InsertBuild('banana paladin',
                                            constants.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))

        clactions.RecordSubmissionMetrics(
            clactions.CLActionHistory(action_history), strategies)
Esempio n. 6
0
    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))
Esempio n. 7
0
    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))
Esempio n. 8
0
    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())
Esempio n. 9
0
    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.FINAL_STATUS_PASSED}
        failed_status = {'status': constants.FINAL_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'),
                                                constants.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 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])