示例#1
0
    def testUpdateTestLocationAndTagsGPUTest(self, *_):
        flake = Flake(
            normalized_test_name='suite.test',
            normalized_step_name='telemetry_gpu_integration_test',
            tags=['gerrit_project::chromium/src'],
        )
        occurrences = [
            FlakeOccurrence(step_ui_name='context_lost_tests',
                            build_configuration=BuildConfiguration(
                                legacy_master_name='master',
                                luci_builder='builder',
                                legacy_build_number=123,
                            )),
            FlakeOccurrence(
                step_ui_name='webgl_conformance_vulkan_passthrough_tests',
                build_configuration=BuildConfiguration(
                    legacy_master_name='master',
                    luci_builder='builder',
                    legacy_build_number=124,
                )),
        ]
        component_mapping = {
            'base/feature/': 'root>a>b',
            'base/feature/url': 'root>a>b>c',
        }
        watchlist = {
            'feature': 'base/feature',
            'url': r'base/feature/url_test\.cc',
            'other': 'a/b/c',
        }

        expected_tags = sorted([
            'gerrit_project::chromium/src', 'component::Internals>GPU>Testing',
            'component::Blink>WebGL'
        ])

        for tag in flake.tags:
            name = tag.split(TAG_DELIMITER)[0]
            self.assertTrue(name in detect_flake_occurrences.SUPPORTED_TAGS)

        self.assertTrue(
            detect_flake_occurrences._UpdateTestLocationAndTags(
                flake, occurrences, component_mapping, watchlist))
        self.assertEqual(expected_tags, flake.tags)
 def testGetTestLocation(self, *_):
   occurrence = FlakeOccurrence(
       build_configuration=BuildConfiguration(
           legacy_master_name='master',
           luci_builder='builder',
           legacy_build_number=123,
       ),
       step_ui_name='test on Mac',
   )
   self.assertEqual(
       'path/a.cc',
       detect_flake_occurrences._GetTestLocation(occurrence).file_path)
示例#3
0
def _CreateFlake(flake_data, with_component=True):
    """
  Args:
    with_component (bool): Sets flake.component if True, otherwise sets tags.
  """

    luci_project = 'chromium'
    normalized_step_name = 'normalized_step_name'

    flake_issue = _GetOrCreateFlakeIssue(flake_data['bug_id'])

    flake = Flake.Create(normalized_test_name=flake_data['test'],
                         luci_project=luci_project,
                         normalized_step_name=normalized_step_name,
                         test_label_name='test_label')

    if with_component:
        flake.component = flake_data['component']
    else:
        flake.tags = ['component::{}'.format(flake_data['component'])]
    flake.flake_issue_key = flake_issue.key
    flake.flake_counts_last_week = []
    for flake_type, counts in flake_data['counts'].iteritems():
        flake.flake_counts_last_week.append(
            FlakeCountsByType(flake_type=flake_type,
                              occurrence_count=counts[0],
                              impacted_cl_count=counts[1]))
    flake.last_occurred_time = datetime.strptime(
        flake_data['last_occurred_time'], '%Y-W%W-%w')
    flake.put()

    for occurrence_data in flake_data['occurrences']:
        time_happened = datetime.strptime('2018-%d-4' % occurrence_data[2],
                                          '%Y-%W-%w')
        hour = occurrence_data[3]
        time_happened += timedelta(hours=hour)
        occurrence = FlakeOccurrence.Create(
            flake_type=occurrence_data[0],
            build_id=123 + hour,
            step_ui_name='step',
            test_name=flake.normalized_test_name,
            luci_project='chromium',
            luci_bucket='try',
            luci_builder='builder',
            legacy_master_name='master',
            legacy_build_number=42,
            time_happened=time_happened,
            gerrit_cl_id=occurrence_data[1],
            parent_flake_key=flake.key)
        occurrence.put()
示例#4
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)
示例#6
0
    def testUpdateTestLocationAndTagsFromLocation(self, *_):
        flake = Flake(
            normalized_test_name='suite.test',
            tags=[
                'gerrit_project::chromium/src', 'watchlist::old',
                'directory::old', 'component::old', 'parent_component::old',
                'source::old'
            ],
        )
        occurrences = [
            FlakeOccurrence(step_ui_name='browser_tests'),
        ]
        component_mapping = {
            'base/feature/': 'root>a>b',
            'base/feature/url': 'root>a>b>c',
        }
        watchlist = {
            'feature': 'base/feature',
            'url': r'base/feature/url_test\.cc',
            'other': 'a/b/c',
        }

        expected_tags = sorted([
            'gerrit_project::chromium/src',
            'watchlist::feature',
            'watchlist::url',
            'directory::base/feature/',
            'directory::base/',
            'source::base/feature/url_test.cc',
            'component::root>a>b',
            'parent_component::root>a>b',
            'parent_component::root>a',
            'parent_component::root',
        ])

        for tag in flake.tags:
            name = tag.split(TAG_DELIMITER)[0]
            self.assertTrue(name in detect_flake_occurrences.SUPPORTED_TAGS)

        self.assertTrue(
            detect_flake_occurrences._UpdateTestLocationAndTags(
                flake, occurrences, component_mapping, watchlist))
        self.assertEqual(expected_tags, flake.tags)
示例#7
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))
示例#8
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)
示例#9
0
 def testAnalyzeDetectedFlakeOccurrence(self):
     step = 'step1'
     test = 'test1'
     luci_project = 'chromium'
     bug_id = 12345
     flake = Flake.Create(luci_project, step, test, 'l')
     flake_issue = FlakeIssue.Create(luci_project, bug_id)
     flake.flake_issue_key = flake_issue.key
     occurrence = FlakeOccurrence.Create(
         flake_type=FlakeType.CQ_FALSE_REJECTION,
         build_id=111,
         step_ui_name=step,
         test_name=test,
         luci_project=luci_project,
         luci_bucket='try',
         luci_builder='tryserver.chromium.linux',
         legacy_master_name='linux_chromium_rel_ng',
         legacy_build_number=999,
         time_happened=None,
         gerrit_cl_id=98765,
         parent_flake_key=None)
     apis.AnalyzeDetectedFlakeOccurrence(flake, occurrence, bug_id)
     self.assertEqual(1, len(self.taskqueue_requests))
  def testUpdateMetadataForFlakes(self, *_):
    luci_project = 'chromium'
    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.archived = True
    flake.put()
    flake_key = flake.key

    step_ui_name = 'step'
    test_name = 'test'
    luci_bucket = 'try'
    luci_builder = 'luci builder'
    legacy_master_name = 'buildbot master'
    legacy_build_number = 999
    gerrit_cl_id = 98765

    # Flake's last_occurred_time and tags are empty, updated.
    occurrence_1 = FlakeOccurrence.Create(
        flake_type=FlakeType.CQ_FALSE_REJECTION,
        build_id=123,
        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=datetime(2018, 1, 1, 1),
        gerrit_cl_id=gerrit_cl_id,
        parent_flake_key=flake_key,
        tags=['tag1::v1'])
    occurrence_1.put()
    _UpdateFlakeMetadata([occurrence_1])
    flake = flake_key.get()
    self.assertEqual(flake.last_occurred_time, datetime(2018, 1, 1, 1))
    self.assertEqual(flake.tags, ['tag1::v1'])
    self.assertFalse(flake.archived)

    # Flake's last_occurred_time is earlier and tags are different, updated.
    occurrence_2 = FlakeOccurrence.Create(
        flake_type=FlakeType.CQ_FALSE_REJECTION,
        build_id=124,
        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=datetime(2018, 1, 1, 2),
        gerrit_cl_id=gerrit_cl_id,
        parent_flake_key=flake_key,
        tags=['tag2::v2'])
    occurrence_2.put()
    _UpdateFlakeMetadata([occurrence_2])
    flake = flake_key.get()
    self.assertEqual(flake.last_occurred_time, datetime(2018, 1, 1, 2))
    self.assertEqual(flake.tags, ['tag1::v1', 'tag2::v2'])

    # Flake's last_occurred_time is later and tags are the same, not updated.
    occurrence_3 = FlakeOccurrence.Create(
        flake_type=FlakeType.CQ_FALSE_REJECTION,
        build_id=125,
        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=datetime(2018, 1, 1),
        gerrit_cl_id=gerrit_cl_id,
        parent_flake_key=flake_key,
        tags=['tag2::v2'])
    occurrence_3.put()
    _UpdateFlakeMetadata([occurrence_3])
    flake = flake_key.get()
    self.assertEqual(flake.last_occurred_time, datetime(2018, 1, 1, 2))
    self.assertEqual(flake.tags, ['tag1::v1', 'tag2::v2'])
示例#11
0
    def testShowFlake(self, *_):
        flake_issue = FlakeIssue.Create(monorail_project='chromium',
                                        issue_id=900)
        flake_issue.last_updated_time_by_flake_detection = datetime(2018, 1, 1)
        flake_issue.status = 'Assigned'
        flake_issue.last_updated_time_in_monorail = datetime(2018, 1, 1)
        flake_issue.put()

        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.component = 'Mock>Component'
        flake.test_location = TestLocation()
        flake.test_location.file_path = '../../some/test/path/a.cc'
        flake.test_location.line_number = 42
        flake.flake_issue_key = flake_issue.key
        flake.put()

        build_id = 123
        luci_bucket = 'try'
        luci_builder = 'luci builder'
        legacy_master_name = 'buildbot master'
        legacy_build_number = 999
        time_happened = datetime(2018, 1, 1)
        gerrit_cl_id = 98765
        occurrence = FlakeOccurrence.Create(
            flake_type=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)
        occurrence.time_detected = datetime(2018, 1, 1)
        occurrence.put()

        response = self.test_app.get('/flake/detection/ui/show-flake',
                                     params={
                                         'key': flake.key.urlsafe(),
                                         'format': 'json',
                                     },
                                     status=200)

        flake_dict = {
            'flake_issue': {
                'flake_culprit_key':
                None,
                'issue_id':
                900,
                'issue_link':
                ('https://monorail-prod.appspot.com/p/chromium/issues'
                 '/detail?id=900'),
                'last_updated_time_by_flake_detection':
                '2018-01-01 00:00:00',
                'monorail_project':
                'chromium',
                'merge_destination_key':
                None,
                'last_updated_time_in_monorail':
                '2 days, 00:00:00',
                'last_updated_time_with_analysis_results':
                None,
                'create_time_in_monorail':
                None,
                'labels': [],
                'status':
                'Assigned',
            },
            'flake_issue_key':
            flake_issue.key,
            'luci_project':
            'chromium',
            'last_occurred_time':
            None,
            'last_test_location_based_tag_update_time':
            None,
            'normalized_step_name':
            'normalized_step_name',
            'normalized_test_name':
            'normalized_test_name',
            'test_label_name':
            'test_label',
            'false_rejection_count_last_week':
            0,
            'impacted_cl_count_last_week':
            0,
            'archived':
            False,
            'flake_counts_last_week': [
                {
                    'flake_type': 'cq false rejection',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
                {
                    'flake_type': 'cq step level retry',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
                {
                    'flake_type': 'cq hidden flake',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
                {
                    'flake_type': 'ci failed step',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
            ],
            'flake_score_last_week':
            0,
            'component':
            'Mock>Component',
            'tags': [],
            'test_location': {
                'file_path': '../../some/test/path/a.cc',
                'line_number': 42,
            },
            'culprits': [],
            'sample_analysis':
            None,
            'occurrences': [{
                'group_by_field':
                'luci builder',
                'occurrences': [{
                    'build_configuration': {
                        'legacy_build_number': 999,
                        'legacy_master_name': 'buildbot master',
                        'luci_bucket': 'try',
                        'luci_builder': 'luci builder',
                        'luci_project': 'chromium'
                    },
                    'tags': [],
                    'build_id': '123',
                    'flake_type': 'cq false rejection',
                    'gerrit_cl_id': 98765,
                    'step_ui_name': 'step',
                    'test_name': 'test',
                    'time_detected': '2018-01-01 00:00:00 UTC',
                    'time_happened': '2018-01-01 00:00:00 UTC'
                }]
            }]
        }
        self.assertEqual(
            json.dumps(
                {
                    'flake_json':
                    flake_dict,
                    'key':
                    flake.key.urlsafe(),
                    'show_all_occurrences':
                    '',
                    'weights': [('cq false rejection', 100),
                                ('cq step level retry', 10),
                                ('cq hidden flake', 1), ('ci failed step', 10)]
                },
                default=str,
                sort_keys=True,
                indent=2),
            json.dumps(json.loads(response.body), sort_keys=True, indent=2))
    def testFlakeUpdates(self, _):
        luci_project = 'chromium'
        step_ui_name = 'step'
        normalized_step_name = 'normalized_step_name'
        flake1 = Flake.Create(luci_project=luci_project,
                              normalized_step_name=normalized_step_name,
                              normalized_test_name='normalized_test_name_1',
                              test_label_name='test_label1')
        flake1.last_occurred_time = datetime(2018, 9, 1)
        flake1.put()
        flake1_key = flake1.key

        luci_bucket = 'try'
        luci_builder = 'luci builder'
        legacy_master_name = 'buildbot master'
        legacy_build_number = 999

        occurrence1 = FlakeOccurrence.Create(
            flake_type=FlakeType.CQ_FALSE_REJECTION,
            build_id=1,
            step_ui_name=step_ui_name,
            test_name='t1',
            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=datetime(2018, 9, 1),
            gerrit_cl_id=98761,
            parent_flake_key=flake1_key)
        occurrence1.put()

        occurrence2 = FlakeOccurrence.Create(
            flake_type=FlakeType.CQ_FALSE_REJECTION,
            build_id=2,
            step_ui_name=step_ui_name,
            test_name='t1',
            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=datetime(2018, 8, 31),
            gerrit_cl_id=98761,
            parent_flake_key=flake1_key)
        occurrence2.put()

        occurrence3 = FlakeOccurrence.Create(
            flake_type=FlakeType.CQ_FALSE_REJECTION,
            build_id=3,
            step_ui_name=step_ui_name,
            test_name='t2',
            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=datetime(2018, 7, 31),
            gerrit_cl_id=98763,
            parent_flake_key=flake1_key)
        occurrence3.put()

        occurrence5 = FlakeOccurrence.Create(
            flake_type=FlakeType.RETRY_WITH_PATCH,
            build_id=5,
            step_ui_name=step_ui_name,
            test_name='t2',
            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=datetime(2018, 8, 31),
            gerrit_cl_id=98761,
            parent_flake_key=flake1_key)
        occurrence5.put()

        occurrence6 = FlakeOccurrence.Create(
            flake_type=FlakeType.RETRY_WITH_PATCH,
            build_id=6,
            step_ui_name=step_ui_name,
            test_name='t2',
            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=datetime(2018, 8, 31),
            gerrit_cl_id=98766,
            parent_flake_key=flake1_key)
        occurrence6.put()

        occurrence7 = FlakeOccurrence.Create(
            flake_type=FlakeType.RETRY_WITH_PATCH,
            build_id=7,
            step_ui_name=step_ui_name,
            test_name='t2',
            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=datetime(2018, 8, 31),
            gerrit_cl_id=98767,
            parent_flake_key=flake1_key)
        occurrence7.put()

        UpdateFlakeCounts()

        flake1 = flake1_key.get()
        self.assertEqual(5, flake1.false_rejection_count_last_week)
        self.assertEqual(3, flake1.impacted_cl_count_last_week)
        self.assertEqual([
            FlakeCountsByType(flake_type=FlakeType.CQ_FALSE_REJECTION,
                              impacted_cl_count=1,
                              occurrence_count=2),
            FlakeCountsByType(flake_type=FlakeType.RETRY_WITH_PATCH,
                              impacted_cl_count=2,
                              occurrence_count=3)
        ], flake1.flake_counts_last_week)
        self.assertEqual(120, flake1.flake_score_last_week)
def _CreateFlakeOccurrenceFromRow(row, flake_type_enum):
    """Creates a FlakeOccurrence from a row fetched from BigQuery."""
    luci_project = row['luci_project']
    luci_builder = row['luci_builder']
    step_ui_name = row['step_ui_name']
    test_name = row['test_name']
    legacy_master_name = row['legacy_master_name']
    legacy_build_number = row['legacy_build_number']

    normalized_step_name = Flake.NormalizeStepName(
        step_name=step_ui_name,
        master_name=legacy_master_name,
        builder_name=luci_builder,
        build_number=legacy_build_number)
    normalized_test_name = Flake.NormalizeTestName(test_name, step_ui_name)

    flake_id = Flake.GetId(luci_project=luci_project,
                           normalized_step_name=normalized_step_name,
                           normalized_test_name=normalized_test_name)
    flake_key = ndb.Key(Flake, flake_id)

    gerrit_project = row['gerrit_project']
    build_id = row['build_id']
    luci_bucket = row['luci_bucket']
    time_happened = row['test_start_msec']
    gerrit_cl_id = row['gerrit_cl_id']

    # Not add the original test name as a tag here, because all the tags will be
    # merged into Flake model, and there might be 100s of parameterized tests
    # which might lead to too large data for a single Flake entity.
    tags = [
        'gerrit_project::%s' % gerrit_project,
        'luci_project::%s' % luci_project,
        'bucket::%s' % luci_bucket,
        'master::%s' % legacy_master_name,
        'builder::%s' % luci_builder,
        'binary::%s' % normalized_step_name,  # e.g. "tests"
        'test_type::%s' %
        step_ui_name.split(' ', 1)[0],  # e.g. "flavored_tests"
        'step::%s' % step_ui_name,  # e.g. "flavored_tests on Mac 10.13"
        'flake::%s' % normalized_test_name,
    ]

    suite = _GetTestSuiteForOccurrence(row, normalized_test_name,
                                       normalized_step_name)
    if suite:
        tags.append('suite::%s' % suite)
    tags.sort()

    flake_occurrence = FlakeOccurrence.Create(
        flake_type=flake_type_enum,
        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,
        tags=tags)

    return flake_occurrence
    def testGetFlakeInformationNoIssue(self, *_):

        luci_project = 'chromium'
        step_ui_name = 'step'
        test_name = 'test'
        normalized_step_name = 'normalized_step_name'
        normalized_test_name = 'normalized_test_name_3'
        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(2018, 1, 1)
        gerrit_cl_id = 98765
        occurrence = FlakeOccurrence.Create(
            flake_type=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)
        occurrence.time_detected = datetime(2018, 1, 1)
        occurrence.put()

        expected_flake_dict = {
            'luci_project':
            'chromium',
            'normalized_step_name':
            normalized_step_name,
            'normalized_test_name':
            normalized_test_name,
            'test_label_name':
            test_label_name,
            'flake_issue_key':
            None,
            'last_occurred_time':
            None,
            'last_test_location_based_tag_update_time':
            None,
            'false_rejection_count_last_week':
            0,
            'impacted_cl_count_last_week':
            0,
            'archived':
            False,
            'flake_counts_last_week': [
                {
                    'flake_type': 'cq false rejection',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
                {
                    'flake_type': 'cq step level retry',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
                {
                    'flake_type': 'cq hidden flake',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
                {
                    'flake_type': 'ci failed step',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
            ],
            'flake_score_last_week':
            0,
            'component':
            None,
            'test_location':
            None,
            'tags': [],
            'occurrences': [{
                'group_by_field':
                'luci builder',
                'occurrences': [{
                    'flake_type': 'cq false rejection',
                    'build_id': '123',
                    'step_ui_name': step_ui_name,
                    'test_name': test_name,
                    'tags': [],
                    'build_configuration': {
                        'luci_project': 'chromium',
                        'luci_bucket': 'try',
                        'luci_builder': 'luci builder',
                        'legacy_master_name': 'buildbot master',
                        'legacy_build_number': 999
                    },
                    'time_happened': '2018-01-01 00:00:00 UTC',
                    'time_detected': '2018-01-01 00:00:00 UTC',
                    'gerrit_cl_id': gerrit_cl_id
                }]
            }],
        }

        self.assertEqual(expected_flake_dict,
                         flake_detection_utils.GetFlakeInformation(flake, 1))
    def testGetFlakeInformationOldFlakes(self, *_):
        flake_issue = FlakeIssue.Create(monorail_project='chromium',
                                        issue_id=900)
        flake_issue.last_updated_time_by_flake_detection = datetime(2018, 1, 1)
        flake_issue.last_updated_time_in_monorail = datetime(2018, 1, 2)
        flake_issue.status = 'Started'
        flake_issue.put()

        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.component = 'Mock>Component'
        flake.test_location = TestLocation()
        flake.test_location.file_path = '../../some/test/path/a.cc'
        flake.test_location.line_number = 42
        flake.flake_issue_key = flake_issue.key
        flake.put()

        build_id = 123
        luci_bucket = 'try'
        luci_builder = 'luci builder'
        legacy_master_name = 'buildbot master'
        legacy_build_number = 999
        time_happened = datetime(2018, 1, 1)
        gerrit_cl_id = 98765
        occurrence = FlakeOccurrence.Create(
            flake_type=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)
        occurrence.time_detected = datetime(2018, 1, 1)
        occurrence.put()

        occurrence2 = FlakeOccurrence.Create(
            flake_type=FlakeType.RETRY_WITH_PATCH,
            build_id=124,
            step_ui_name=step_ui_name,
            test_name=test_name,
            luci_project=luci_project,
            luci_bucket=luci_bucket,
            luci_builder='luci builder 2',
            legacy_master_name=legacy_master_name,
            legacy_build_number=legacy_build_number,
            time_happened=datetime(2018, 1, 2, 3),
            gerrit_cl_id=gerrit_cl_id,
            parent_flake_key=flake.key)
        occurrence2.time_detected = datetime(2018, 1, 2, 3)
        occurrence2.put()

        occurrence3 = FlakeOccurrence.Create(
            flake_type=FlakeType.CQ_FALSE_REJECTION,
            build_id=125,
            step_ui_name=step_ui_name,
            test_name=test_name,
            luci_project=luci_project,
            luci_bucket=luci_bucket,
            luci_builder='luci builder 2',
            legacy_master_name=legacy_master_name,
            legacy_build_number=legacy_build_number,
            time_happened=datetime(2018, 1, 2, 2),
            gerrit_cl_id=gerrit_cl_id,
            parent_flake_key=flake.key)
        occurrence3.time_detected = datetime(2018, 1, 2, 2)
        occurrence3.put()

        occurrence4 = FlakeOccurrence.Create(
            flake_type=FlakeType.CQ_HIDDEN_FLAKE,
            build_id=126,
            step_ui_name=step_ui_name,
            test_name=test_name,
            luci_project=luci_project,
            luci_bucket=luci_bucket,
            luci_builder='luci builder 2',
            legacy_master_name=legacy_master_name,
            legacy_build_number=legacy_build_number,
            time_happened=datetime(2018, 1, 2, 2),
            gerrit_cl_id=gerrit_cl_id,
            parent_flake_key=flake.key)
        occurrence4.time_detected = datetime(2018, 1, 2, 2)
        occurrence4.put()

        occurrence5 = FlakeOccurrence.Create(
            flake_type=FlakeType.CI_FAILED_STEP,
            build_id=127,
            step_ui_name=step_ui_name,
            test_name=test_name,
            luci_project=luci_project,
            luci_bucket=luci_bucket,
            luci_builder='luci builder 2',
            legacy_master_name=legacy_master_name,
            legacy_build_number=legacy_build_number,
            time_happened=datetime(2018, 1, 2, 2),
            gerrit_cl_id=-1,
            parent_flake_key=flake.key)
        occurrence5.time_detected = datetime(2018, 1, 2, 2)
        occurrence5.put()

        culprit1 = FlakeCulprit.Create('chromium', 'rev1', 123456,
                                       'culprit_url')
        culprit1.put()

        analysis = MasterFlakeAnalysis.Create(legacy_master_name, luci_builder,
                                              legacy_build_number,
                                              step_ui_name, test_name)
        analysis.bug_id = 900
        analysis.culprit_urlsafe_key = culprit1.key.urlsafe()
        analysis.confidence_in_culprit = 0.98
        analysis.put()

        culprit2 = FlakeCulprit.Create('chromium', 'rev2', 123457,
                                       'culprit_url')
        culprit2.put()
        analysis_1 = MasterFlakeAnalysis.Create(legacy_master_name,
                                                luci_builder,
                                                legacy_build_number - 1,
                                                step_ui_name, test_name)
        analysis_1.bug_id = 900
        analysis_1.culprit_urlsafe_key = culprit2.key.urlsafe()
        analysis_1.put()

        expected_flake_dict = {
            'luci_project':
            'chromium',
            'normalized_step_name':
            'normalized_step_name',
            'normalized_test_name':
            'normalized_test_name',
            'test_label_name':
            'test_label',
            'flake_issue_key':
            flake_issue.key,
            'last_occurred_time':
            None,
            'last_test_location_based_tag_update_time':
            None,
            'false_rejection_count_last_week':
            0,
            'impacted_cl_count_last_week':
            0,
            'archived':
            False,
            'flake_counts_last_week': [
                {
                    'flake_type': 'cq false rejection',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
                {
                    'flake_type': 'cq step level retry',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
                {
                    'flake_type': 'cq hidden flake',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
                {
                    'flake_type': 'ci failed step',
                    'impacted_cl_count': 0,
                    'occurrence_count': 0
                },
            ],
            'flake_score_last_week':
            0,
            'flake_issue': {
                'flake_culprit_key':
                None,
                'monorail_project':
                'chromium',
                'issue_id':
                900,
                'last_updated_time_by_flake_detection':
                datetime(2018, 1, 1),
                'issue_link': ('https://monorail-prod.appspot.com/p/chromium/'
                               'issues/detail?id=900'),
                'merge_destination_key':
                None,
                'last_updated_time_in_monorail':
                '366 days, 00:00:00',
                'last_updated_time_with_analysis_results':
                None,
                'create_time_in_monorail':
                None,
                'labels': [],
                'status':
                'Started',
            },
            'component':
            'Mock>Component',
            'test_location': {
                'file_path': '../../some/test/path/a.cc',
                'line_number': 42,
            },
            'tags': [],
            'culprits': [{
                'revision': 'rev1',
                'commit_position': culprit1.commit_position,
                'culprit_key': culprit1.key.urlsafe()
            }],
            'sample_analysis':
            None,
            'occurrences': [{
                'group_by_field':
                'luci builder 2',
                'occurrences': [
                    {
                        'flake_type': 'cq step level retry',
                        'build_id': '124',
                        'step_ui_name': step_ui_name,
                        'test_name': test_name,
                        'tags': [],
                        'build_configuration': {
                            'luci_project': 'chromium',
                            'luci_bucket': 'try',
                            'luci_builder': 'luci builder 2',
                            'legacy_master_name': 'buildbot master',
                            'legacy_build_number': 999
                        },
                        'time_happened': '2018-01-02 03:00:00 UTC',
                        'time_detected': '2018-01-02 03:00:00 UTC',
                        'gerrit_cl_id': gerrit_cl_id
                    },
                    {
                        'flake_type': 'cq false rejection',
                        'build_id': '125',
                        'step_ui_name': step_ui_name,
                        'test_name': test_name,
                        'tags': [],
                        'build_configuration': {
                            'luci_project': 'chromium',
                            'luci_bucket': 'try',
                            'luci_builder': 'luci builder 2',
                            'legacy_master_name': 'buildbot master',
                            'legacy_build_number': 999
                        },
                        'time_happened': '2018-01-02 02:00:00 UTC',
                        'time_detected': '2018-01-02 02:00:00 UTC',
                        'gerrit_cl_id': gerrit_cl_id
                    },
                    {
                        'flake_type': 'ci failed step',
                        'build_id': '127',
                        'step_ui_name': step_ui_name,
                        'test_name': test_name,
                        'tags': [],
                        'build_configuration': {
                            'luci_project': 'chromium',
                            'luci_bucket': 'try',
                            'luci_builder': 'luci builder 2',
                            'legacy_master_name': 'buildbot master',
                            'legacy_build_number': 999
                        },
                        'time_happened': '2018-01-02 02:00:00 UTC',
                        'time_detected': '2018-01-02 02:00:00 UTC',
                        'gerrit_cl_id': -1,
                    },
                    {
                        'flake_type': 'cq hidden flake',
                        'build_id': '126',
                        'step_ui_name': step_ui_name,
                        'test_name': test_name,
                        'tags': [],
                        'build_configuration': {
                            'luci_project': 'chromium',
                            'luci_bucket': 'try',
                            'luci_builder': 'luci builder 2',
                            'legacy_master_name': 'buildbot master',
                            'legacy_build_number': 999
                        },
                        'time_happened': '2018-01-02 02:00:00 UTC',
                        'time_detected': '2018-01-02 02:00:00 UTC',
                        'gerrit_cl_id': gerrit_cl_id,
                    },
                ]
            }, {
                'group_by_field':
                'luci builder',
                'occurrences': [{
                    'flake_type': 'cq false rejection',
                    'build_id': '123',
                    'step_ui_name': step_ui_name,
                    'test_name': test_name,
                    'tags': [],
                    'build_configuration': {
                        'luci_project': 'chromium',
                        'luci_bucket': 'try',
                        'luci_builder': 'luci builder',
                        'legacy_master_name': 'buildbot master',
                        'legacy_build_number': 999
                    },
                    'time_happened': '2018-01-01 00:00:00 UTC',
                    'time_detected': '2018-01-01 00:00:00 UTC',
                    'gerrit_cl_id': gerrit_cl_id
                }]
            }],
        }
        self.assertEqual(expected_flake_dict,
                         flake_detection_utils.GetFlakeInformation(flake, 5))
  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 testFlakesWithCQHiddenFlakes(self, _):
        luci_project = 'chromium'
        normalized_step_name = 'normalized_step_name'

        flake3 = Flake.Create(luci_project=luci_project,
                              normalized_step_name=normalized_step_name,
                              normalized_test_name='normalized_test_name_3',
                              test_label_name='test_label3')
        flake3.last_occurred_time = datetime(2018, 9, 1)
        flake3.false_rejection_count_last_week = 5
        flake3.impacted_cl_count_last_week = 3
        flake3.put()
        flake3_key = flake3.key

        step_ui_name = 'step'
        luci_bucket = 'try'
        luci_builder = 'luci builder'
        legacy_master_name = 'buildbot master'
        legacy_build_number = 999

        occurrence4 = FlakeOccurrence.Create(
            flake_type=FlakeType.CQ_FALSE_REJECTION,
            build_id=4,
            step_ui_name=step_ui_name,
            test_name='t1',
            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=datetime(2018, 8, 31),
            gerrit_cl_id=98764,
            parent_flake_key=flake3_key)
        occurrence4.put()

        for i in xrange(98760, 98790):
            occurrence = FlakeOccurrence.Create(
                flake_type=FlakeType.CQ_HIDDEN_FLAKE,
                build_id=i,
                step_ui_name=step_ui_name,
                test_name='t1',
                luci_project=luci_project,
                luci_bucket=luci_bucket,
                luci_builder=luci_builder,
                legacy_master_name=legacy_master_name,
                legacy_build_number=i,
                time_happened=datetime(2018, 8, 31),
                gerrit_cl_id=i,
                parent_flake_key=flake3_key)
            occurrence.put()

        UpdateFlakeCounts()

        flake3 = flake3_key.get()
        self.assertEqual([
            FlakeCountsByType(flake_type=FlakeType.CQ_FALSE_REJECTION,
                              impacted_cl_count=1,
                              occurrence_count=1),
            FlakeCountsByType(flake_type=FlakeType.CQ_HIDDEN_FLAKE,
                              impacted_cl_count=29,
                              occurrence_count=30)
        ], flake3.flake_counts_last_week)
        self.assertEqual(129, flake3.flake_score_last_week)
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()
示例#19
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
示例#20
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)