Ejemplo n.º 1
0
    def testCreate(self):
        luci_project = 'chromium'
        step_ui_name = 'step'
        test_name = 'test'

        normalized_step_name = 'normalized_step_name'
        normalized_test_name = 'normalized_test_name'
        test_label_name = 'test_label'
        flake = Flake.Create(luci_project=luci_project,
                             normalized_step_name=normalized_step_name,
                             normalized_test_name=normalized_test_name,
                             test_label_name=test_label_name)
        flake.put()

        build_id = 123
        luci_bucket = 'try'
        luci_builder = 'luci builder'
        legacy_master_name = 'buildbot master'
        legacy_build_number = 999
        time_happened = datetime.datetime(2018, 1, 1)
        gerrit_cl_id = 98765

        cq_false_rejection_occurrence = FlakeOccurrence.Create(
            FlakeType.CQ_FALSE_REJECTION,
            build_id=build_id,
            step_ui_name=step_ui_name,
            test_name=test_name,
            luci_project=luci_project,
            luci_bucket=luci_bucket,
            luci_builder=luci_builder,
            legacy_master_name=legacy_master_name,
            legacy_build_number=legacy_build_number,
            time_happened=time_happened,
            gerrit_cl_id=gerrit_cl_id,
            parent_flake_key=flake.key)
        cq_false_rejection_occurrence.put()

        retry_with_patch_occurrence = FlakeOccurrence.Create(
            FlakeType.RETRY_WITH_PATCH,
            build_id=build_id,
            step_ui_name=step_ui_name,
            test_name=test_name,
            luci_project=luci_project,
            luci_bucket=luci_bucket,
            luci_builder=luci_builder,
            legacy_master_name=legacy_master_name,
            legacy_build_number=legacy_build_number,
            time_happened=time_happened,
            gerrit_cl_id=gerrit_cl_id,
            parent_flake_key=flake.key)
        retry_with_patch_occurrence.put()

        fetched_flake_occurrences = FlakeOccurrence.query().fetch()
        self.assertEqual(2, len(fetched_flake_occurrences))
        self.assertIn(cq_false_rejection_occurrence, fetched_flake_occurrences)
        self.assertIn(retry_with_patch_occurrence, fetched_flake_occurrences)
        self.assertIsNotNone(fetched_flake_occurrences[0].time_detected)
        self.assertIsNotNone(fetched_flake_occurrences[1].time_detected)
Ejemplo n.º 2
0
def _FetchFlakeOccurrences(flake, flake_type, max_occurrence_count):
    """Fetches flake occurrences of a certain type within a time range.

  Args:
    flake(Flake): Flake object for a flaky test.
    flake_type(FlakeType): Type of the occurrences.
    max_occurrence_count(int): Maximum number of occurrences to fetch.

  Returns:
    (list): A list of occurrences.
  """
    start_date = time_util.GetDateDaysBeforeNow(days=constants.DAYS_IN_A_WEEK)
    occurrences_query = FlakeOccurrence.query(ancestor=flake.key).filter(
        ndb.AND(FlakeOccurrence.flake_type == flake_type,
                FlakeOccurrence.time_happened >
                start_date)).order(-FlakeOccurrence.time_happened)

    occurrences_query_old_flakes = FlakeOccurrence.query(
        ancestor=flake.key).filter(FlakeOccurrence.flake_type == flake_type
                                   ).order(-FlakeOccurrence.time_happened)

    if max_occurrence_count:
        flake_occurrences = occurrences_query.fetch(max_occurrence_count)

        # No occurrences in the past 7 days.
        if not flake_occurrences:
            flake_occurrences = occurrences_query_old_flakes.fetch(
                max_occurrence_count)
    else:
        flake_occurrences = occurrences_query.fetch()

        # No occurrences in the past 7 days.
        if not flake_occurrences:
            flake_occurrences = occurrences_query_old_flakes.fetch()

    return flake_occurrences
  def testStoreDetectedCIFlakes(self, *_):
    master_name = 'm'
    builder_name = 'b'
    build_number = 123
    build = WfBuild.Create(master_name, builder_name, build_number)
    build.build_id = '87654321'
    build.put()

    flaky_tests = {'s': ['t1', 't2']}

    detect_flake_occurrences.StoreDetectedCIFlakes(master_name, builder_name,
                                                   build_number, flaky_tests)

    flake = Flake.Get('chromium', 'normalized_step_name', 't1')
    self.assertIsNotNone(flake)

    occurrences = FlakeOccurrence.query(ancestor=flake.key).fetch()
    self.assertEqual(1, len(occurrences))
    self.assertEqual(FlakeType.CI_FAILED_STEP, occurrences[0].flake_type)
Ejemplo n.º 4
0
    def testDetectCQHiddenFlakes(self, mocked_get_client, *_):
        query_response = self._GetEmptyFlakeQueryResponse()
        query_response['pageToken'] = 'token'
        test_name1 = 'suite.test'
        test_name2 = 'suite.test_1'

        self._AddRowToFlakeQueryResponse(query_response=query_response,
                                         step_ui_name='step_ui_name',
                                         test_name=test_name1,
                                         gerrit_cl_id='10000')

        self._AddRowToFlakeQueryResponse(query_response=query_response,
                                         step_ui_name='step_ui_name',
                                         test_name=test_name1,
                                         gerrit_cl_id='10001',
                                         build_id='124',
                                         test_start_msec='1')

        self._AddRowToFlakeQueryResponse(query_response=query_response,
                                         luci_builder='another_builder',
                                         step_ui_name='step_ui_name',
                                         test_name=test_name1,
                                         gerrit_cl_id='10001',
                                         build_id='125')

        self._AddRowToFlakeQueryResponse(query_response=query_response,
                                         step_ui_name='step_ui_name',
                                         test_name=test_name2,
                                         gerrit_cl_id='10000')
        mocked_client = mock.Mock()
        mocked_get_client.return_value = mocked_client
        mocked_client.jobs().query().execute.return_value = query_response

        query_response_2 = self._GetEmptyFlakeQueryResponse()
        mocked_client.jobs().getQueryResults().execute.side_effect = [
            query_response, query_response_2
        ]

        detect_flake_occurrences.QueryAndStoreHiddenFlakes()

        all_flake_occurrences = FlakeOccurrence.query().fetch()
        self.assertEqual(4, len(all_flake_occurrences))
Ejemplo n.º 5
0
def _AddDistinctCLsToCounters(counters, flake_info_dict, start, end,
                              save_test_report):
    occurrences_query = FlakeOccurrence.query(
        projection=[FlakeOccurrence.flake_type, FlakeOccurrence.gerrit_cl_id
                    ]).filter(
                        ndb.AND(FlakeOccurrence.time_happened >= start,
                                FlakeOccurrence.time_happened < end))

    cursor = None
    more = True
    while more:
        occurrences, cursor, more = occurrences_query.fetch_page(
            500, start_cursor=cursor)

        for occurrence in occurrences:
            flake_key = occurrence.key.parent()
            luci_project = flake_info_dict.get(flake_key,
                                               {}).get('luci_project')
            component = flake_info_dict.get(flake_key, {}).get('component')
            test = flake_info_dict.get(flake_key, {}).get('test')

            # Broken data, bails out on this occurrence.
            if not luci_project or not counters.get(luci_project):
                continue
            if not component or not counters[luci_project].get(component):
                continue
            if save_test_report and (
                    not test
                    or not counters[luci_project][component].get(test)):
                continue

            flake_type = occurrence.flake_type
            cl = occurrence.gerrit_cl_id
            counters[luci_project]['_impacted_cls'][flake_type].add(cl)
            counters[luci_project][component]['_impacted_cls'][flake_type].add(
                cl)
            if save_test_report:
                counters[luci_project][component][test]['_impacted_cls'][
                    flake_type].add(cl)

    _UpdateDuplicatedClsBetweenTypes(counters)
Ejemplo n.º 6
0
def GetFlakeInformation(flake, max_occurrence_count, with_occurrences=True):
    """Gets information for a detected flakes.
  Gets occurrences of the flake and the attached monorail issue.

  Args:
    flake(Flake): Flake object for a flaky test.
    max_occurrence_count(int): Maximum number of occurrences to fetch.
    with_occurrences(bool): If the flake must be with occurrences or not.
      For flakes reported by Flake detection, there should always be
      occurrences, but it's not always true for flakes reported by
      Flake Analyzer, ignore those flakes for now.
  Returns:
    flake_dict(dict): A dict of information for the test. Including data from
    its Flake entity, its flake issue information and information of all its
    flake occurrences.
  """
    occurrences = []
    if (max_occurrence_count and not flake.archived
            and flake.flake_score_last_week > 0):
        # On-going flakes, queries the ones in the past-week.
        for flake_type in [
                FlakeType.CQ_FALSE_REJECTION, FlakeType.RETRY_WITH_PATCH,
                FlakeType.CI_FAILED_STEP, FlakeType.CQ_HIDDEN_FLAKE
        ]:
            occurrences.extend(
                _FetchFlakeOccurrences(flake, flake_type,
                                       max_occurrence_count -
                                       len(occurrences)))
            if len(occurrences) >= max_occurrence_count:
                # Bails out if the number of occurrences with higher impact has hit
                # the cap.
                break

        # Makes sure occurrences are sorted by time_happened in descending order,
        # regardless of types.
        occurrences.sort(key=lambda x: x.time_happened, reverse=True)

    # Falls back to query all recent occurrences.
    occurrences = occurrences or FlakeOccurrence.query(
        ancestor=flake.key).order(-FlakeOccurrence.time_happened).fetch(
            max_occurrence_count)

    if not occurrences and with_occurrences:
        # Flake must be with occurrences, but there is no occurrence, bail out.
        return None

    flake_dict = flake.to_dict()
    flake_dict['occurrences'] = _GetGroupedOccurrencesByBuilder(occurrences)
    flake_dict['flake_counts_last_week'] = _GetFlakeCountsList(
        flake.flake_counts_last_week)

    flake_issue = GetFlakeIssue(flake)
    if flake_issue and flake_issue.status and flake_issue.status in OPEN_STATUSES:
        flake_dict['flake_issue'] = flake_issue.to_dict()
        flake_dict['flake_issue']['issue_link'] = FlakeIssue.GetLinkForIssue(
            flake_issue.monorail_project, flake_issue.issue_id)
        flake_dict['flake_issue'][
            'last_updated_time_in_monorail'] = _GetLastUpdatedTimeDelta(
                flake_issue)

        flake_dict['culprits'], flake_dict['sample_analysis'] = (
            _GetFlakeAnalysesResults(flake_issue.issue_id))
    return flake_dict
  def testProcessBuildForFlakes(self, mock_metadata, mock_build,
                                mock_normalized_test_name, mock_lable_name, *_):
    flake_type_enum = FlakeType.CQ_FALSE_REJECTION
    build_id = 123
    luci_project = 'luci_project'
    luci_bucket = 'luci_bucket'
    luci_builder = 'luci_builder'
    legacy_master_name = 'legacy_master_name'
    start_time = datetime(2019, 3, 6)
    end_time = datetime(2019, 3, 6, 0, 0, 10)

    findit_step = Step()
    findit_step.name = 'FindIt Flakiness'
    step1 = Step()
    step1.name = 'step1 (with patch)'
    step1.start_time.FromDatetime(start_time)
    step1.end_time.FromDatetime(end_time)
    builder = BuilderID(
        project=luci_project,
        bucket=luci_bucket,
        builder=luci_builder,
    )
    build = Build(id=build_id, builder=builder, number=build_id)
    build.steps.extend([findit_step, step1])
    build.input.properties['mastername'] = legacy_master_name
    build.input.properties['patch_project'] = 'chromium/src'
    mock_change = build.input.gerrit_changes.add()
    mock_change.host = 'mock.gerrit.host'
    mock_change.change = 12345
    mock_change.patchset = 1
    mock_build.return_value = build

    def _MockTestName(test_name, _step_ui_name):  # pylint: disable=unused-argument
      return test_name

    mock_normalized_test_name.side_effect = _MockTestName
    mock_lable_name.side_effect = _MockTestName

    flakiness_metadata = {
        'Failing With Patch Tests That Caused Build Failure': {
            'step1 (with patch)': ['s1_t1', 's1_t2']
        },
        'Step Layer Flakiness': {}
    }
    mock_metadata.return_value = flakiness_metadata

    # Flake object for s2_t1 exists.
    flake1 = Flake.Create(
        luci_project=luci_project,
        normalized_step_name='step1',
        normalized_test_name='s1_t1',
        test_label_name='s1_t1')
    flake1.put()

    detect_flake_occurrences.ProcessBuildForFlakes(
        detect_flake_occurrences.DetectFlakesFromFlakyCQBuildParam(
            build_id=build_id,
            flake_type_desc=FLAKE_TYPE_DESCRIPTIONS[flake_type_enum]))

    flake1_occurrence_num = FlakeOccurrence.query(ancestor=flake1.key).count()
    self.assertEqual(1, flake1_occurrence_num)

    flake2 = Flake.Get(luci_project, 'step1', 's1_t2')
    self.assertIsNotNone(flake2)
def _QueryTypedFlakeOccurrences(flake, start_date, flake_type):
    return FlakeOccurrence.query(ancestor=flake.key).filter(
        ndb.AND(FlakeOccurrence.flake_type == flake_type,
                FlakeOccurrence.time_happened > start_date)).fetch()
Ejemplo n.º 9
0
def GetFlakesWithEnoughOccurrences():
    """Queries Datastore and returns flakes that has enough occurrences.

  The most intuitive algorithm is to fetch all flakes first, and then for each
  flake, fetch its recent and unreported flake occurrences, but it has
  performance implications when there are a lot of flakes (too many RPC calls)
  because a large number of calls are wasted on flakes that don't even have any
  recent and unreported flake occurrence.

  So, instead, this algorithm first fetches all recent and unreported flake
  occurrences, and then by looking up their parents to figure out the subset of
  flakes that need to be fetched.

  Returns:
    A list of tuples whose first element is a flake entity, second element is
    number of corresponding recent and unreported occurrences, third element is
    the flake issue the flake links to if exist. And this list is sorted by each
    flake's flake_score_last_week in descending order.
  """
    utc_one_day_ago = time_util.GetUTCNow() - datetime.timedelta(days=1)
    occurrences = FlakeOccurrence.query(
        ndb.AND(
            FlakeOccurrence.flake_type.IN(
                [FlakeType.CQ_FALSE_REJECTION, FlakeType.RETRY_WITH_PATCH]),
            FlakeOccurrence.time_happened > utc_one_day_ago)).fetch()

    logging.info(
        'There are %d non cq hidden occurrences within the past 24h.' %
        len(occurrences))

    flake_key_to_occurrences = defaultdict(list)
    for occurrence in occurrences:
        flake_key_to_occurrences[occurrence.key.parent()].append(occurrence)

    unique_flake_keys = flake_key_to_occurrences.keys()
    flakes = ndb.get_multi(unique_flake_keys)

    key_to_flake = dict(zip(unique_flake_keys, flakes))

    # Filter out occurrences that have already been reported according to the
    # last update time of the associated flake issue.
    flake_key_to_enough_unreported_occurrences = {}
    for flake_key, occurrences in flake_key_to_occurrences.iteritems():
        if not key_to_flake[flake_key]:
            logging.error('Flake not found for key %s', flake_key.urlsafe())
            continue

        if not _FlakeHasEnoughOccurrences(occurrences):
            continue

        flake_issue = GetFlakeIssue(flake_key.get())
        last_updated_time_by_flake_detection = (
            flake_issue.last_updated_time_by_flake_detection
            if flake_issue else None)
        if (last_updated_time_by_flake_detection
                and last_updated_time_by_flake_detection > utc_one_day_ago):
            # An issue can be updated at most once in any 24h window avoid noises.
            continue

        flake_key_to_enough_unreported_occurrences[flake_key] = {
            'occurrences': occurrences,
            'flake_issue': flake_issue
        }

    flake_tuples_to_report = [
        (key_to_flake[flake_key], info['occurrences'], info['flake_issue'])
        for flake_key, info in
        flake_key_to_enough_unreported_occurrences.iteritems()
    ]

    return sorted(flake_tuples_to_report,
                  key=lambda tup: tup[0].flake_score_last_week,
                  reverse=True)