def testNotNeedANewWaterfallTryJobForOtherType(self, mock_fn):
        master_name = 'm'
        builder_name = 'b'
        build_number = 223
        failure_info = {
            'master_name': master_name,
            'builder_name': builder_name,
            'build_number': build_number,
            'failed_steps': {},
            'builds': {
                '222': {
                    'blame_list': ['222-1'],
                    'chromium_revision': '222-1'
                },
                '223': {
                    'blame_list': ['223-1', '223-2', '223-3'],
                    'chromium_revision': '223-3'
                }
            },
            'failure_type': failure_type.UNKNOWN
        }

        mock_fn.return_value = False
        expected_try_job_key = WfTryJob.Create(master_name, builder_name,
                                               build_number).key

        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None)

        self.assertFalse(need_try_job)
        self.assertEqual(expected_try_job_key, try_job_key)
    def testBailOutForTryJobWithOutdatedTimestamp(self, mock_fn):
        master_name = 'master1'
        builder_name = 'builder1'
        build_number = 223
        WfAnalysis.Create(master_name, builder_name, build_number).put()
        failure_info = {
            'master_name': master_name,
            'builder_name': builder_name,
            'build_number': build_number,
            'failed_steps': {
                'compile': {
                    'current_failure': 221,
                    'first_failure': 221,
                    'last_pass': 220
                }
            },
            'failure_type': failure_type.COMPILE
        }

        yesterday = datetime.utcnow() - timedelta(days=1)
        build = WfBuild.Create(master_name, builder_name, build_number)
        build.start_time = yesterday
        build.put()

        mock_fn.return_value = True

        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None)

        self.assertFalse(need_try_job)
        self.assertIsNone(try_job_key)
    def testNotNeedANewWaterfallTryJobIfNoNewFailure(self, mock_fn):
        master_name = 'm'
        builder_name = 'b'
        build_number = 223
        failure_info = {
            'failed_steps': {
                'a': {
                    'current_failure': 223,
                    'first_failure': 222,
                    'last_pass': 221,
                    'tests': {
                        'a.t2': {
                            'current_failure': 223,
                            'first_failure': 222,
                            'last_pass': 221
                        }
                    }
                }
            },
            'failure_type': failure_type.TEST
        }

        analysis = WfAnalysis.Create(master_name, builder_name, build_number)
        analysis.failure_result_map = {'a': {'a.t2': 'm/b/222'}}
        analysis.put()

        mock_fn.return_value = False
        expected_try_job_key = WfTryJob.Create(master_name, builder_name,
                                               build_number).key

        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None)

        self.assertFalse(need_try_job)
        self.assertEqual(expected_try_job_key, try_job_key)
    def testForceTryJob(self):
        master_name = 'm'
        builder_name = 'b'
        build_number = 223
        failure_info = {
            'failed_steps': {
                'a': {
                    'current_failure': 223,
                    'first_failure': 223,
                    'last_pass': 222,
                    'tests': {
                        'a.t2': {
                            'current_failure': 223,
                            'first_failure': 223,
                            'last_pass': 222
                        }
                    }
                }
            },
            'builds': {
                '222': {
                    'blame_list': ['222-1'],
                    'chromium_revision': '222-1'
                },
                '223': {
                    'blame_list': ['223-1', '223-2', '223-3'],
                    'chromium_revision': '223-3'
                }
            },
            'failure_type': failure_type.TEST
        }

        try_job = WfTryJob.Create(master_name, builder_name, build_number)
        try_job.compile_results = [['rev', 'failed']]
        try_job.status = analysis_status.COMPLETED
        try_job.put()

        analysis = WfAnalysis.Create(master_name, builder_name, build_number)
        analysis.failure_result_map = {'a': {'a.t2': 'm/b/223'}}
        analysis.put()

        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None,
            True)

        self.assertTrue(need_try_job)
        self.assertEqual(try_job_key, try_job.key)
    def testNotNeedANewWaterfallTryJobIfNotFirstTimeFailure(self, mock_fn):
        master_name = 'm'
        builder_name = 'b'
        build_number = 223
        WfAnalysis.Create(master_name, builder_name, build_number).put()
        failure_info = {
            'master_name': master_name,
            'builder_name': builder_name,
            'build_number': build_number,
            'failed_steps': {
                'compile': {
                    'current_failure': 223,
                    'first_failure': 221,
                    'last_pass': 220
                }
            },
            'builds': {
                '220': {
                    'blame_list': ['220-1', '220-2'],
                    'chromium_revision': '220-2'
                },
                '221': {
                    'blame_list': ['221-1', '221-2'],
                    'chromium_revision': '221-2'
                },
                '222': {
                    'blame_list': ['222-1'],
                    'chromium_revision': '222-1'
                },
                '223': {
                    'blame_list': ['223-1', '223-2', '223-3'],
                    'chromium_revision': '223-3'
                }
            },
            'failure_type': failure_type.COMPILE
        }

        WfAnalysis.Create(master_name, builder_name, build_number).put()
        mock_fn.return_value = False
        expected_key = WfTryJob.Create(master_name, builder_name,
                                       build_number).key
        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None)

        self.assertFalse(need_try_job)
        self.assertEqual(expected_key, try_job_key)
    def testNotNeedANewWaterfallTryJobIfBuilderIsNotSupportedYet(self):
        master_name = 'master3'
        builder_name = 'builder3'
        build_number = 223
        WfAnalysis.Create(master_name, builder_name, build_number).put()
        failure_info = {
            'master_name': master_name,
            'builder_name': builder_name,
            'build_number': build_number,
            'failed_steps': {
                'compile': {
                    'current_failure': 221,
                    'first_failure': 221,
                    'last_pass': 220
                }
            },
            'builds': {
                '220': {
                    'blame_list': ['220-1', '220-2'],
                    'chromium_revision': '220-2'
                },
                '221': {
                    'blame_list': ['221-1', '221-2'],
                    'chromium_revision': '221-2'
                },
                '222': {
                    'blame_list': ['222-1'],
                    'chromium_revision': '222-1'
                },
                '223': {
                    'blame_list': ['223-1', '223-2', '223-3'],
                    'chromium_revision': '223-3'
                }
            },
            'failure_type': failure_type.COMPILE
        }

        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None)

        self.assertFalse(need_try_job)
        self.assertIsNone(try_job_key)
    def testNeedANewWaterfallTryJob(self, mock_fn):
        master_name = 'm'
        builder_name = 'b'
        build_number = 223
        failure_info = {
            'master_name': master_name,
            'builder_name': builder_name,
            'build_number': build_number,
            'failed_steps': {
                'compile': {
                    'current_failure': 223,
                    'first_failure': 223,
                    'last_pass': 222
                }
            },
            'builds': {
                '222': {
                    'blame_list': ['222-1'],
                    'chromium_revision': '222-1'
                },
                '223': {
                    'blame_list': ['223-1', '223-2', '223-3'],
                    'chromium_revision': '223-3'
                }
            },
            'failure_type': failure_type.COMPILE
        }

        analysis = WfAnalysis.Create(master_name, builder_name, build_number)
        analysis.failure_result_map = {'compile': 'm/b/223'}
        analysis.put()

        mock_fn.return_value = False

        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None)

        self.assertTrue(need_try_job)
        self.assertIsNotNone(try_job_key)
    def testNotNeedANewWaterfallTryJobIfOneWithResultExists(self, mock_fn):
        master_name = 'm'
        builder_name = 'b'
        build_number = 223
        failure_info = {
            'failed_steps': {
                'compile': {
                    'current_failure': 223,
                    'first_failure': 223,
                    'last_pass': 220
                }
            },
            'builds': {
                '222': {
                    'blame_list': ['222-1'],
                    'chromium_revision': '222-1'
                },
                '223': {
                    'blame_list': ['223-1', '223-2', '223-3'],
                    'chromium_revision': '223-3'
                }
            },
            'failure_type': failure_type.COMPILE
        }

        try_job = WfTryJob.Create(master_name, builder_name, build_number)
        try_job.compile_results = [['rev', 'failed']]
        try_job.status = analysis_status.COMPLETED
        try_job.put()

        WfAnalysis.Create(master_name, builder_name, build_number).put()

        mock_fn.return_value = False

        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None)

        self.assertFalse(need_try_job)
        self.assertEqual(try_job_key, try_job.key)
    def testBailOutForTestTryJob(self, mock_fn):
        master_name = 'master2'
        builder_name = 'builder2'
        build_number = 223
        WfAnalysis.Create(master_name, builder_name, build_number).put()
        failure_info = {
            'master_name': master_name,
            'builder_name': builder_name,
            'build_number': build_number,
            'failed_steps': {
                'a_test': {}
            },
            'failure_type': failure_type.TEST
        }

        mock_fn.return_value = False
        expected_try_job_key = WfTryJob.Create(master_name, builder_name,
                                               build_number).key
        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None)

        self.assertFalse(need_try_job)
        self.assertEqual(expected_try_job_key, try_job_key)
    def run(self, master_name, builder_name, build_number, failure_info,
            signals, heuristic_result, build_completed, force_try_job):
        """Starts a try job if one is needed for the given failure."""

        if not build_completed:  # Only start try-jobs for completed builds.
            return

        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, signals,
            heuristic_result, force_try_job)

        if not need_try_job:
            return

        try_job_type = failure_info['failure_type']
        last_pass = _GetLastPass(build_number, failure_info, try_job_type)
        if last_pass is None:  # pragma: no cover
            logging.warning(
                'Couldn"t start try job for build %s, %s, %d because'
                ' last_pass is not found.', master_name, builder_name,
                build_number)
            return

        good_revision = failure_info['builds'][str(
            last_pass)]['chromium_revision']
        bad_revision = failure_info['builds'][str(
            build_number)]['chromium_revision']
        suspected_revisions = _GetSuspectsFromHeuristicResult(heuristic_result)

        if try_job_type == failure_type.COMPILE:
            compile_targets = try_job_util.GetFailedTargetsFromSignals(
                signals, master_name, builder_name)
            dimensions = waterfall_config.GetTrybotDimensions(
                master_name, builder_name)
            cache_name = swarming_util.GetCacheName(master_name, builder_name)
            try_job_id = yield ScheduleCompileTryJobPipeline(
                master_name, builder_name, build_number, good_revision,
                bad_revision, try_job_type, compile_targets,
                suspected_revisions, cache_name, dimensions)
        else:
            # If try_job_type is other type, the pipeline has returned.
            # So here the try_job_type is failure_type.TEST.

            # Waits and gets the swarming tasks' results.
            task_results = []
            for step_name, step_failure in failure_info[
                    'failed_steps'].iteritems():
                step_has_first_time_failure = _HasFirstTimeFailure(
                    step_failure.get('tests', {}), build_number)
                if not step_has_first_time_failure:
                    continue
                task_result = yield ProcessSwarmingTaskResultPipeline(
                    master_name, builder_name, build_number, step_name)
                task_results.append(task_result)

            yield UpdateAnalysisWithFlakeInfoPipeline(master_name,
                                                      builder_name,
                                                      build_number,
                                                      *task_results)

            parent_mastername = failure_info.get(
                'parent_mastername') or master_name
            parent_buildername = failure_info.get('parent_buildername') or (
                builder_name)
            dimensions = waterfall_config.GetTrybotDimensions(
                parent_mastername, parent_buildername)
            cache_name = swarming_util.GetCacheName(parent_mastername,
                                                    parent_buildername)

            try_job_id = yield ScheduleTestTryJobPipeline(
                master_name, builder_name, build_number, good_revision,
                bad_revision, try_job_type, suspected_revisions, cache_name,
                dimensions, *task_results)

        try_job_result = yield MonitorTryJobPipeline(try_job_key.urlsafe(),
                                                     try_job_type, try_job_id)

        yield IdentifyTryJobCulpritPipeline(master_name, builder_name,
                                            build_number, try_job_type,
                                            try_job_id, try_job_result)
    def testNeedANewWaterfallTryJobIfTestFailureSwarming(self, mock_fn):
        master_name = 'm'
        builder_name = 'b'
        build_number = 223
        failure_info = {
            'failed_steps': {
                'a': {
                    'current_failure': 223,
                    'first_failure': 222,
                    'last_pass': 221,
                    'tests': {
                        'a.PRE_t1': {
                            'current_failure': 223,
                            'first_failure': 223,
                            'last_pass': 221,
                            'base_test_name': 'a.t1'
                        },
                        'a.t2': {
                            'current_failure': 223,
                            'first_failure': 222,
                            'last_pass': 221
                        },
                        'a.t3': {
                            'current_failure': 223,
                            'first_failure': 223,
                            'last_pass': 222
                        }
                    }
                },
                'b': {
                    'current_failure': 223,
                    'first_failure': 222,
                    'last_pass': 221,
                    'tests': {
                        'b.t1': {
                            'current_failure': 223,
                            'first_failure': 222,
                            'last_pass': 221
                        },
                        'b.t2': {
                            'current_failure': 223,
                            'first_failure': 222,
                            'last_pass': 221
                        }
                    }
                }
            },
            'builds': {
                '222': {
                    'blame_list': ['222-1'],
                    'chromium_revision': '222-1'
                },
                '223': {
                    'blame_list': ['223-1', '223-2', '223-3'],
                    'chromium_revision': '223-3'
                }
            },
            'failure_type': failure_type.TEST
        }

        analysis = WfAnalysis.Create(master_name, builder_name, build_number)
        analysis.failure_result_map = {
            'a': {
                'a.PRE_t1': 'm/b/223',
                'a.t2': 'm/b/222',
                'a.t3': 'm/b/223'
            },
            'b': {
                'b.t1': 'm/b/222',
                'b.t2': 'm/b/222'
            }
        }
        analysis.put()

        mock_fn.return_value = False

        need_try_job, try_job_key = try_job_util.NeedANewWaterfallTryJob(
            master_name, builder_name, build_number, failure_info, None, None)

        self.assertTrue(need_try_job)
        self.assertIsNotNone(try_job_key)