def testNormalizeTestName(self): self.assertEqual('suite.test', Flake.NormalizeTestName('suite.test')) self.assertEqual('suite.test', Flake.NormalizeTestName('a/suite.test/0', )) self.assertEqual('suite.test', Flake.NormalizeTestName('suite.test/1')) self.assertEqual('suite.test', Flake.NormalizeTestName('suite.test/*')) self.assertEqual('suite.test', Flake.NormalizeTestName('*/suite.test/*')) self.assertEqual('suite.test', Flake.NormalizeTestName('suite.PRE_PRE_test')) self.assertEqual('suite.test', Flake.NormalizeTestName('a/suite.PRE_PRE_test/0')) self.assertEqual('a/b/c/d.html', Flake.NormalizeTestName('a/b/c/d.html')) self.assertEqual('a/b/c/d.html', Flake.NormalizeTestName('a/b/c/d.html?1000-2000')) self.assertEqual('a/b/c/d.html', Flake.NormalizeTestName('a/b/c/d.html?*'))
def testNormalizeTestNameWithStepName(self): self.assertEqual('suite.test', Flake.NormalizeTestName('a/suite.test/1')) self.assertEqual('a.html', Flake.NormalizeTestName('a/b.html')) self.assertEqual( 'a/b.html', Flake.NormalizeTestName('a/b.html', 'webkit_layout_tests'))
def GetFlake(luci_project, original_step_name, original_test_name, master_name, builder_name, build_number): """Returns an existing Flake or creates one as needed. Args: luci_project (str): The project being analyzed, e.g. 'chromium'. original_step_name (str): The original step name which may contain hardware information and 'with(out) patch' etc. suffixes. original_test_name (str): The original test name. master_name (str): Master name of the build of the step. builder_name (str): Builder name of the build of the step. build_number (int): Build number of the build of the step. """ normalized_step_name = Flake.LegacyNormalizeStepName( original_step_name, master_name, builder_name, build_number) normalized_test_name = Flake.NormalizeTestName(original_test_name, original_step_name) flake = Flake.Get(luci_project, normalized_step_name, normalized_test_name) if not flake: # pragma: no branch label = Flake.GetTestLabelName(original_test_name, original_step_name) flake = Flake.Create(luci_project, normalized_step_name, normalized_test_name, label) flake.put() return flake
def testGetTestLabelName(self): self.assertEqual( 'suite.test', Flake.GetTestLabelName('suite.test', 'base_unittests')) self.assertEqual( 'suite.test/*', Flake.GetTestLabelName('suite.test/1', 'base_unittests')) self.assertEqual( '*/suite.test/*', Flake.GetTestLabelName('a/suite.test/0', 'base_unittests')) self.assertEqual( 'suite.*test', Flake.GetTestLabelName('suite.PRE_PRE_test', 'base_unittests')) self.assertEqual( '*/suite.*test/*', Flake.GetTestLabelName('a/suite.PRE_PRE_test/0', 'base_unittests')) self.assertEqual( 'a/b.html', Flake.NormalizeTestName('a/b.html', 'webkit_layout_tests')) self.assertEqual( 'a/b.html?*', Flake.GetTestLabelName('a/b.html?1000-2000', 'webkit_layout_tests'))
def _CreateLocalTests(row, local_tests, component_mapping, watchlists): """Creates a LuciTest key-test variant pair for a row fetched from BigQuery. Args: row: A row of query results. local_tests (dict): LuciTest entities in local memory in the format {LuciTest.key: {'disabled_test_variants : set(), issue_keys: set()}, mutated by this function. component_mapping (dict): Mapping from directories to crbug components. watchlists (dict): Mapping from directories to watchlists. """ build_id = row['build_id'] builder_name = row['builder_name'] step_name = row['step_name'] test_name = row['test_name'] bugs = row['bugs'] if int(build_id) == 1: # To filter out tests results with invalid build_id. # TODO (crbug.com/999215): Remove this check after test-results is fixed. logging.info( 'Failed to define test variant for build_id: %s, row is %r', build_id, row) return normalized_step_name = Flake.NormalizeStepName(build_id, step_name) normalized_test_name = Flake.NormalizeTestName(test_name, step_name) test_key = LuciTest.CreateKey(_DEFAULT_LUCI_PROJECT, normalized_step_name, normalized_test_name) if not local_tests.get(test_key): local_tests[test_key] = { 'disabled_test_variants': set(), 'issue_keys': set(), 'tags': set() } local_tests[test_key]['tags'].update( _GetNewTestTags(local_tests[test_key]['tags'], step_name, test_name, normalized_step_name, normalized_test_name, build_id, component_mapping, watchlists)) disabled_variant = _CreateDisabledVariant(build_id, builder_name, step_name) local_tests[test_key]['disabled_test_variants'].add(disabled_variant) local_tests[test_key]['issue_keys'].update(_CreateIssueKeys(bugs))
def _CreateFlakeFromRow(row): """Creates a Flake entity from a row fetched from BigQuery.""" luci_project = row['luci_project'] luci_builder = row['luci_builder'] legacy_master_name = row['legacy_master_name'] legacy_build_number = row['legacy_build_number'] step_ui_name = row['step_ui_name'] test_name = row['test_name'] 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) test_label_name = Flake.GetTestLabelName(test_name, step_ui_name) return Flake.Create(luci_project=luci_project, normalized_step_name=normalized_step_name, normalized_test_name=normalized_test_name, test_label_name=test_label_name)
def GetFlakesByFilter(flake_filter, luci_project, cursor, direction, page_size=None): # pragma: no cover. """Gets flakes by the given filter, then sorts them by the flake score. Args: flake_filter (str): It could be a test name, or a tag-based filter in the following forms: * tag::value * tag1::value1@tag2::value2 * tag1::value1@-tag2:value2 luci_project (str): The Luci project that the flakes are for. cursor (None or str): The cursor provides a cursor in the current query results, allowing you to retrieve the next set based on the offset. direction (str): Either previous or next. page_size (int): Limit of results required in one page. Returns: (flakes, prev_cursor, cursor, grouping_search, error_message) flakes (list): A list of Flakes filtered by tags. prev_cursor (str): The urlsafe encoding of the cursor, which is at the top position of entities of the current page. cursor (str): The urlsafe encoding of the cursor, which is at the bottom position of entities of the current page. grouping_search (bool): Whether it is a group searching. error_message (str): An error message if there is one; otherwise None. """ logging.info('Searching filter: %s', flake_filter) flakes = [] error_message = None grouping_search = True filters = [f.strip() for f in flake_filter.split('@') if f.strip()] # The resulted flakes are those: # * Match all of positive filters # * Not match any of negative filters positive_filters = [] negative_filters = [] invalid_filters = [] for f in filters: parts = [p.strip() for p in f.split(TAG_DELIMITER)] if len(parts) != 2 or not parts[1]: invalid_filters.append(f) continue if parts[0] == _TEST_FILTER_NAME: # Search for a specific test. grouping_search = False flakes = Flake.query( Flake.normalized_test_name == Flake.NormalizeTestName(parts[1]) ).filter(Flake.luci_project == luci_project).fetch() return flakes, '', '', grouping_search, error_message negative = False if parts[0][0] == '-': parts[0] = parts[0][1:] negative = True if parts[0] not in SUPPORTED_TAGS: invalid_filters.append(f) continue if negative: negative_filters.append(TAG_DELIMITER.join(parts)) else: positive_filters.append(TAG_DELIMITER.join(parts)) if invalid_filters: error_message = 'Unsupported tag filters: %s' % ', '.join( invalid_filters) return flakes, '', '', grouping_search, error_message if not positive_filters: # At least one positive filter should be given. error_message = 'At least one positive filter required' return flakes, '', '', grouping_search, error_message logging.info('Positive filters: %r', positive_filters) logging.info('Negative filters: %r', negative_filters) query = Flake.query( ndb.AND(Flake.luci_project == luci_project, Flake.archived == False)) # pylint: disable=singleton-comparison for tag in positive_filters: query = query.filter(Flake.tags == tag) query = query.filter(Flake.flake_score_last_week > 0) minimum_flake_count_in_page = max( 1, page_size / 2) if page_size else DEFAULT_PAGE_SIZE / 2 while True: results, prev_cursor, cursor = dashboard_util.GetPagedResults( query, order_properties=[ (Flake.flake_score_last_week, dashboard_util.DESC), (Flake.last_occurred_time, dashboard_util.DESC), (Flake.normalized_step_name, dashboard_util.ASC), (Flake.test_label_name, dashboard_util.ASC), ], cursor=cursor, direction=direction, page_size=page_size or DEFAULT_PAGE_SIZE) for result in results: if negative_filters and any(t in result.tags for t in negative_filters): continue flakes.append(result) if ((direction == dashboard_util.PREVIOUS and prev_cursor == '') or cursor == '' or len(flakes) >= minimum_flake_count_in_page): # No more results or gets enough flakes on a page. # Ideally we expect the page shows the same amount of flakes as the # page_size suggests, but in the case with negative_filters, the number of # flakes left after filtering out negative_filters is unknown. # Uses minimum_flake_count_in_page to cap the flake count in one page from # 0.5 page_size to 1.5 page_size. break return flakes, prev_cursor, cursor, grouping_search, error_message
def GetTestName(self): return Flake.NormalizeTestName(self._analysis.test_name, self._analysis.step_name)
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