def AnalyzeTestFailure(failure_info, change_logs, deps_info, failure_signals):
  """Analyzes given failure signals, and figure out culprits of test failure.

  Args:
    failure_info (TestFailureInfo): Output of pipeline
      DetectFirstFailurePipeline.
    change_logs (dict): Output of pipeline PullChangelogPipeline.
    deps_info (dict): Output of pipeline ExtractDEPSInfoPipeline.
    failure_signals (dict): Output of pipeline ExtractSignalPipeline.

  Returns:
    A dict with the following form:
    {
      'failures': [
        {
          'step_name': 'compile',
          'supported': True
          'first_failure': 230,
          'last_pass': 229,
          'suspected_cls': [
            {
              'build_number': 230,
              'repo_name': 'chromium',
              'revision': 'a_git_hash',
              'commit_position': 56789,
              'score': 11,
              'hints': {
                'add a/b/x.cc': 5,
                'delete a/b/y.cc': 5,
                'modify e/f/z.cc': 1,
                ...
              }
            },
            ...
          ],
        },
        ...
      ]
    }

    And a list of suspected_cls format as below:
    [
        {
            'repo_name': 'chromium',
            'revision': 'r98_1',
            'commit_position': None,
            'url': None,
            'failures': {
                'b': ['Unittest2.Subtest1', 'Unittest3.Subtest2']
            },
            'top_score': 4
        },
        ...
    ]
  """
  analysis_result = {'failures': []}

  if not failure_signals:
    logging.debug('No failure signals when analyzing a test failure.')
    return analysis_result, []

  failed_steps = failure_info.failed_steps
  builds = failure_info.builds

  cl_failure_map = defaultdict(build_failure_analysis.CLInfo)

  for step_name, step_failure_info in failed_steps.iteritems():
    is_test_level = step_failure_info.tests is not None

    failed_build_number = step_failure_info.current_failure
    start_build_number = (
        build_failure_analysis.GetLowerBoundForAnalysis(step_failure_info))
    step_analysis_result = (
        build_failure_analysis.InitializeStepLevelResult(
            step_name, step_failure_info))

    if is_test_level:
      step_analysis_result['tests'] = []
      tests = step_failure_info.tests or {}
      for test_name, test_failure in tests.iteritems():
        test_analysis_result = {
            'test_name': test_name,
            'first_failure': test_failure.first_failure,
            'last_pass': test_failure.last_pass,
            'suspected_cls': [],
        }
        step_analysis_result['tests'].append(test_analysis_result)

    if step_analysis_result['supported']:
      step_failure_signal = FailureSignal.FromDict(failure_signals[step_name])
      for build_number, build in builds.iteritems():
        if (build_number > failed_build_number or
            build_number < start_build_number):
          continue

        for revision in build.blame_list:
          # TODO(crbug/842980): Deprecate blame_list in builds.
          if not change_logs.get(revision):
            continue

          if is_test_level:
            # Checks files at test level.
            for test_analysis_result in step_analysis_result['tests']:
              test_name = test_analysis_result['test_name']
              test_signal = FailureSignal.FromDict(
                  failure_signals[step_name]['tests'].get(test_name) or {})

              _AnalyzeTestFailureOnOneBuild(build_number, step_name, test_name,
                                            test_signal, change_logs[revision],
                                            deps_info, test_analysis_result,
                                            cl_failure_map)

          # Checks Files on step level using step level signals
          # regardless of test level signals so we can make sure
          # no duplicate justifications added to the step result.
          _AnalyzeTestFailureOnOneBuild(
              build_number,
              step_name,
              None,
              step_failure_signal,
              change_logs[revision],
              deps_info,
              step_analysis_result,
              cl_failure_map,
              has_lower_level_info=is_test_level)

    # TODO(stgao): sort CLs by score.
    analysis_result['failures'].append(step_analysis_result)

  suspected_cls = build_failure_analysis.ConvertCLFailureMapToList(
      cl_failure_map)

  return analysis_result, suspected_cls
Ejemplo n.º 2
0
 def testGetLowerBoundForAnalysisStructuredLastPass(self):
     failure_info = TestFailedStep(last_pass=120,
                                   current_failure=121,
                                   first_failure=121)
     self.assertEqual(
         121, build_failure_analysis.GetLowerBoundForAnalysis(failure_info))
Ejemplo n.º 3
0
def AnalyzeCompileFailure(failure_info, change_logs, deps_info,
                          failure_signals):
  """Analyzes given failure signals, and figure out culprits of compile failure.

  Args:
    failure_info (CompileFailureInfo): Output of pipeline
      DetectFirstFailurePipeline.
    change_logs (dict): Output of pipeline PullChangelogPipeline.
    deps_info (dict): Output of pipeline ExtractDEPSInfoPipeline.
    failure_signals (dict): Output of pipeline ExtractSignalPipeline.

  Returns:
    A dict with the following form:
    {
      'failures': [
        {
          'step_name': 'compile',
          'supported': True
          'first_failure': 230,
          'last_pass': 229,
          'suspected_cls': [
            {
              'build_number': 230,
              'repo_name': 'chromium',
              'revision': 'a_git_hash',
              'commit_position': 56789,
              'score': 11,
              'hints': {
                'add a/b/x.cc': 5,
                'delete a/b/y.cc': 5,
                'modify e/f/z.cc': 1,
                ...
              }
            },
            ...
          ],
        },
        ...
      ]
    }

    And a list of suspected_cls format as below:
    [
        {
            'repo_name': 'chromium',
            'revision': 'r98_1',
            'commit_position': None,
            'url': None,
            'failures': {
                'b': ['Unittest2.Subtest1', 'Unittest3.Subtest2']
            },
            'top_score': 4
        },
        ...
    ]
  """
  analysis_result = {'failures': []}
  cl_failure_map = defaultdict(build_failure_analysis.CLInfo)

  step_name = constants.COMPILE_STEP_NAME

  if not failure_signals:
    logging.debug('No failure signals when analyzing a compile failure.')
    return analysis_result, []

  if step_name not in failure_info.failed_steps:
    logging.debug('No failed compile step when analyzing a compile failure.')
    return analysis_result, []

  builds = failure_info.builds
  compile_failure_info = failure_info.failed_steps[step_name]

  failed_build_number = compile_failure_info.current_failure
  start_build_number = build_failure_analysis.GetLowerBoundForAnalysis(
      compile_failure_info)
  step_analysis_result = build_failure_analysis.InitializeStepLevelResult(
      step_name, compile_failure_info)

  if not step_analysis_result['supported']:
    return analysis_result, []

  failure_signal = FailureSignal.FromDict(failure_signals[step_name])
  _Analyze(start_build_number, failed_build_number, builds, step_name,
           failure_signal, change_logs, deps_info, step_analysis_result,
           cl_failure_map)

  if waterfall_config.GetDownloadBuildDataSettings().get(
      'use_ninja_output_log'):
    step_analysis_result['new_compile_suspected_cls'] = []
    _Analyze(
        start_build_number,
        failed_build_number,
        builds,
        step_name,
        failure_signal,
        change_logs,
        deps_info,
        step_analysis_result,
        cl_failure_map,
        use_ninja_output=True)

    if (not step_analysis_result['suspected_cls'] and
        step_analysis_result.get('new_compile_suspected_cls')):
      step_analysis_result['use_ninja_dependencies'] = True
      step_analysis_result['suspected_cls'] = step_analysis_result[
          'new_compile_suspected_cls']
      for new_suspected_cl_dict in step_analysis_result['suspected_cls']:
        # Top score for new heuristic is always 2.
        build_failure_analysis.SaveFailureToMap(
            cl_failure_map, new_suspected_cl_dict, step_name, None, 2)

  # TODO(stgao): sort CLs by score.
  analysis_result['failures'].append(step_analysis_result)

  suspected_cls = build_failure_analysis.ConvertCLFailureMapToList(
      cl_failure_map)

  return analysis_result, suspected_cls