blob: a064a89b1d9a574a6a63855922e203eaea782eea [file] [log] [blame]
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utilities to generate content of bugs to be logged."""
import abc
import textwrap
from google.appengine.ext import ndb
from gae_libs.appengine_util import IsStaging
from model import entity_util
from model.flake.flake import DEFAULT_COMPONENT
from model.flake.flake import Flake
from model.flake.flake_issue import FlakeIssue
from monorail_api import CustomizedField
from services import git
from services import issue_constants
from services import monitoring
from services import swarming
from waterfall import buildbot
# TODO(crbug.com/902408): Once underlying data models for Flake, FlakeIssue,
# MasterFlakeAnalysis, etc. are updated to associate with each other for bug
# deduplication on a 1-bug-per-culprit level, FlakyTestIssueGenerator,
# FlakeAnalysisIssueGenerator, and FlakeDetectionIssueGenerator should all be
# merged into a single bug-filing entry point capable of handling the various
# bug updates.
# The link to the flake culprit page to encapsulate all impacted analyses by
# a common culprit.
_CULPRIT_LINK_TEMPLATE = ('https://analysis.chromium.org'
'/p/chromium/flake-portal/analysis/culprit?key={}')
# The base template for updating a bug with culprit findings.
_RESULT_WITH_CULPRIT_TEMPLATE = textwrap.dedent("""
Findit identified the culprit r{commit_position} as introducing flaky test(s)
summarized in {culprit_link}
Please revert the culprit or disable the test(s) asap. If you are the owner,
please fix!
If the culprit above is wrong, please file a bug using this link:
{wrong_result_link}
Automatically posted by the findit-for-me app (https://goo.gl/6D5FZh).""")
# The link to include with bug updates about wrong findings for users to
# report.
_WRONG_CULPRIT_LINK_TEMPLATE = (
'https://bugs.chromium.org/p/chromium/issues/entry?'
'status=Unconfirmed&'
'labels=Pri-1,Test-Findit-Wrong&'
'components=Tools%3ETest%3EFindIt%3EFlakiness&'
'summary=%5BFindit%5D%20Flake%20Analyzer%20-%20Wrong%20culprit%20'
'r{commit_position}&comment=Link%20to%20Culprit%3A%20{culprit_link}')
# The base template for completed analyses without findings. Currently not yet
# used as analyses without findings don't update bugs.
# TODO(crbug.com/902408): Still update bugs when there are no findings so
# sheriffs or developers can disable or delete tests at their discretion.
_UNKNOWN_CULPRIT_TEMPLATE = textwrap.dedent("""
Flaky test: {test_name}
Sample failed build due to flakiness: {build_link}
Test output log: {test_output_log_link}
Analysis: {analysis_link}
This flake is either longstanding, has low flakiness, or is not reproducible.
Automatically posted by the findit-for-me app (https://goo.gl/6D5FZh).""")
# Flake detection bug templates.
_FLAKE_DETECTION_BUG_DESCRIPTION = textwrap.dedent("""
{test_name} is flaky.
{num_occurrences} flake occurrences of this test have been detected within the
past 24 hours. List of all flake occurrences can be found at:
{flake_url}.
Unless the culprit CL is found and reverted, please disable this test first
within 30 minutes then find an appropriate owner.
{previous_tracking_bug_text}
{footer}""")
# The base template for a detected flaky test before analysis.
_FLAKE_DETECTION_BUG_COMMENT = textwrap.dedent("""
{test_name} is flaky.
{num_occurrences} new flake occurrences of this test have been detected. List
of all flake occurrences can be found at:
{flake_url}.
{previous_tracking_bug_text}
{footer}""")
_FLAKE_DETECTION_WRONG_RESULTS_BUG_LINK = (
'https://bugs.chromium.org/p/chromium/issues/entry?'
'status=Unconfirmed&labels=Pri-1,Test-Flake-Detection-Wrong,Type-Bug&'
'components=Infra%3ETest%3EFlakiness&'
'summary=Flake%20Detection%20-%20Wrong%20result%3A%20'
'{summary}&comment=Link%20to%20flake%20details%3A%20{flake_link}'
'%0A%0AIssue%20Description:%0A%0A')
_FLAKE_DETECTION_PREVIOUS_TRACKING_BUG = (
'\nThis flaky test was previously tracked in bug {}.\n')
_FLAKE_DETECTION_FOOTER_TEMPLATE = textwrap.dedent(
"""If the result above is wrong, please file a bug using this link:
{wrong_results_bug_link}
Automatically posted by Flake Portal (https://goo.gl/Ne6KtC).""")
############### Below are bug templates for flake groups. ###############
# Bug template for a group of detected flakes.
_FLAKE_DETECTION_GROUP_BUG_DESCRIPTION = textwrap.dedent("""
Tests in {canonical_step_name} is flaky.
{num_occurrences} flake occurrences of tests below have been detected within
the past 24 hours:
{flake_list}
Please try to find and revert the culprit if the culprit is obvious.
Otherwise please find an appropriate owner.
{previous_tracking_bug_text}
""")
# Template for the comment immediately after the bug is created.
_FLAKE_DETECTION_GROUP_BUG_LINK_COMMENT = textwrap.dedent("""
List of all flake occurrences can be found at:
{flakes_url}.
{footer}""")
_FLAKE_DETECTION_GROUP_BUG_COMMENT = textwrap.dedent("""
{num_occurrences} new flake occurrences of tests in this bug have been detected
within the past 24 hours.
List of all flake occurrences can be found at:
{flake_url}.
{previous_tracking_bug_text}
{footer}""")
_DUPLICATE_FLAKE_BUG_COMMENT = textwrap.dedent("""
This flake has been identified as being introduced in r{commit_position}.
See {culprit_url} for details.
In the case that the finding is wrong, please unmerge the bug.
""")
def _GenerateAnalysisLink(analysis):
"""Returns a link to Findit's result page of a MasterFlakeAnalysis."""
return ('https://analysis.chromium.org'
'/p/chromium/flake-portal/analysis/culprit?key={}').format(
analysis.key.urlsafe())
def _GenerateCulpritLink(culprit_urlsafe_key):
"""Returns a link to a FlakeCulprit page."""
return _CULPRIT_LINK_TEMPLATE.format(culprit_urlsafe_key)
def _GenerateWrongCulpritLink(culprit):
"""Returns the test with a link to file a bug agasinst a wrong result."""
return _WRONG_CULPRIT_LINK_TEMPLATE.format(
commit_position=culprit.commit_position,
culprit_link=_GenerateCulpritLink(culprit.key.urlsafe()))
def _GenerateTestOutputLogLink(analysis):
"""Generates a link to the swarming task to be surfaced to the bug.
Args:
analysis (MasterFlakeAnalysis): The analysis whose data points and swarming
tasks will be queried for surfacing to the bug.
Returns:
url (str): The url to the swarming task.
"""
task_id = analysis.GetRepresentativeSwarmingTaskId()
assert task_id, 'Representative task id unexpectedly not found!'
return swarming.GetSwarmingTaskUrl(task_id)
def _GenerateMessageText(analysis):
"""Generates the text to create or update a bug with depending on results.
Args:
analysis (MasterFlakeAnalysis): The completed analysis with results to
determine what to update the bug with.
Returns:
(str): The text to upodate the bug with.
"""
# Culprit identified.
if analysis.culprit_urlsafe_key:
culprit = ndb.Key(urlsafe=analysis.culprit_urlsafe_key).get()
assert culprit, 'Culprit is unexpectedly missing.'
culprit_link = _GenerateCulpritLink(analysis.culprit_urlsafe_key)
wrong_result_link = _GenerateWrongCulpritLink(culprit)
return _RESULT_WITH_CULPRIT_TEMPLATE.format(
commit_position=culprit.commit_position,
culprit_link=culprit_link,
wrong_result_link=wrong_result_link)
# Culprit not identified.
analysis_link = _GenerateAnalysisLink(analysis)
build_link = buildbot.CreateBuildbucketUrl(analysis.original_master_name,
analysis.original_builder_name,
analysis.original_build_number)
test_output_log_link = _GenerateTestOutputLogLink(analysis)
return _UNKNOWN_CULPRIT_TEMPLATE.format(
test_name=analysis.original_test_name,
build_link=build_link,
test_output_log_link=test_output_log_link,
analysis_link=analysis_link)
def _GetAutoAssignOwner(analysis):
"""Determines the best owner for the culprit of an analysis.
Rules for determining an owner:
1. None if no culprit.
2. Return the culprit CL author if @chromium.org or @google.com
3. TODO(crbug.com913032): Fallback to the reviewer(s) and check for
@chromium.org or @google.com.
Args:
analysis (MasterFlakeAnalysis): The analysis for whose results are to be
used to update the bug with.
Returns:
owner (str): The best-guess owner or None if not determined.
"""
if not analysis.culprit_urlsafe_key:
# No culprit, so no owner.
return None
culprit = entity_util.GetEntityFromUrlsafeKey(analysis.culprit_urlsafe_key)
assert culprit, (
'Culprit missing unexpectedly when trying to get owner for bug!')
author = git.GetAuthor(culprit.revision)
if not author:
return None
email = author.email
if email.endswith('@chromium.org') or email.endswith('@google.com'):
return email
return None
def GenerateDuplicateComment(culprit):
return _DUPLICATE_FLAKE_BUG_COMMENT.format(
commit_position=culprit.commit_position,
culprit_url=_CULPRIT_LINK_TEMPLATE.format(culprit.key.urlsafe()))
class BaseFlakeIssueGenerator(object):
"""Encapsulates details needed to create or update a Monorail issue."""
__metaclass__ = abc.ABCMeta
def __init__(self):
"""Initiates a BaseFlakeIssueGenerator object."""
# Id of the previous issue that was tracking this flaky test.
self._previous_tracking_bug_id = None
@abc.abstractmethod
def GetDescription(self):
"""Gets description for the issue to be created.
Returns:
A string representing the description.
"""
return
@abc.abstractmethod
def GetComment(self):
"""Gets a comment to post an update to the issue.
Returns:
A string representing the comment.
"""
return
@abc.abstractmethod
def ShouldRestoreChromiumSheriffLabel(self):
"""Returns True if the Sheriff label should be restored when updating bugs.
This value should be set based on whether the results of the service are
actionable. For example, for Flake Detection, once it detects new
occurrences of a flaky test, it is immediately actionable that Sheriffs
should disable the test ASAP. However, for Flake Analyzer, when the
confidence is low, the analysis results mostly only serve as FYI
information, so it would be too noisy to notify Sheriffs on every bug.
Returns:
A boolean indicates whether the Sheriff label should be restored.
"""
return
@abc.abstractmethod
def GetLabels(self):
"""Gets labels for the issue to be created.
Returns:
A list of string representing the labels.
"""
return
def _GetCommonFlakyTestLabel(self):
"""Returns a list of comment labels used for flaky tests related issues.
Args:
A list of string representing the labels.
"""
return [
issue_constants.SHERIFF_CHROMIUM_LABEL, issue_constants.TYPE_BUG_LABEL,
issue_constants.FLAKY_TEST_LABEL
]
def GetAutoAssignOwner(self):
"""Gets the owner to assign the issue to.
Can be None, in which case the owner field should not be affected.
"""
return None
def GetComponents(self):
"""Gets the components of reported flakes."""
return []
def GetStatus(self):
"""Gets status for the issue to be created.
Returns:
A string representing the status, for example: Untriaged.
"""
return 'Untriaged'
@abc.abstractmethod
def GetSummary(self):
"""Gets summary for the issue to be created.
Returns:
A string representing the summary.
"""
return
@abc.abstractmethod
def GetFlakyTestCustomizedField(self):
"""Gets customized fields for the issue to be created.
Returns:
A CustomizedField field.
"""
return
def GetPriority(self):
"""Gets priority for the issue to be created.
Defaults to P1 for all flaky tests related bugs.
Returns:
A string representing the priority of the issue. (e.g Pri-1, Pri-2)
"""
return 'Pri-1'
def GetMonorailProject(self):
"""Gets the name of the Monorail project the issue is for.
Returns:
A string representing the Monorail project.
"""
return 'chromium'
def GetPreviousTrackingBugId(self):
"""Gets the id of the previous issue that was tracking this flaky test.
Returns:
A string representing the Id of the issue.
"""
return self._previous_tracking_bug_id
def SetPreviousTrackingBugId(self, previous_tracking_bug_id):
"""Sets the id of the previous issue that was tracking this flaky test.
Args:
previous_tracking_bug_id: Id of the issue that was tracking this test.
"""
self._previous_tracking_bug_id = previous_tracking_bug_id
def OnIssueCreated(self):
"""Called when an issue was created successfully."""
return
def OnIssueUpdated(self):
"""Called when an issue was updated successfully."""
return
class FlakyTestIssueGenerator(BaseFlakeIssueGenerator):
"""Encapsulates details needed to create or update a Monorail issue."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def GetStepName(self):
"""Gets the name of the step to create or update issue for.
Returns:
A String representing the step name.
"""
return
@abc.abstractmethod
def GetTestName(self):
"""Gets a name that can be used to identify a flaky test.
Returns:
A string representing the test name.
"""
return
@abc.abstractmethod
def GetTestLabelName(self):
"""Gets a label of the test that is used for display purpose.
Returns:
A label for the flaky test.
"""
return
def GetSummary(self):
"""Gets summary for the issue to be created.
Returns:
A string representing the summary.
"""
return '%s is flaky' % self.GetTestLabelName()
def GetFlakyTestCustomizedField(self):
"""Gets Flaky-Test customized fields for the issue to be created.
Returns:
A CustomizedField field whose value is the test name.
"""
return CustomizedField(issue_constants.FLAKY_TEST_CUSTOMIZED_FIELD,
self.GetTestName())
class FlakeAnalysisIssueGenerator(FlakyTestIssueGenerator):
"""Encapsulates the details of issues filed by Flake Analyzer."""
def __init__(self, analysis):
super(FlakeAnalysisIssueGenerator, self).__init__()
self._analysis = analysis
def GetAutoAssignOwner(self):
return _GetAutoAssignOwner(self._analysis)
def GetStepName(self):
return Flake.LegacyNormalizeStepName(
step_name=self._analysis.step_name,
master_name=self._analysis.master_name,
builder_name=self._analysis.builder_name,
build_number=self._analysis.build_number)
def GetTestName(self):
return Flake.NormalizeTestName(self._analysis.test_name,
self._analysis.step_name)
def GetTestLabelName(self):
# Issues are filed with the test label name.
return Flake.GetTestLabelName(self._analysis.test_name,
self._analysis.step_name)
def GetMonorailProject(self):
# Currently, flake analysis only works on Chromium project.
return 'chromium'
def GetComponents(self):
flake = self._analysis.flake_key.get() if self._analysis.flake_key else None
component = flake.GetComponent() if flake else None
return [component] if component else []
def GetDescription(self):
return _GenerateMessageText(self._analysis)
def GetComment(self):
return _GenerateMessageText(self._analysis)
def ShouldRestoreChromiumSheriffLabel(self):
# Analysis results are not always immediately actionable, so don't restore
# Sheriff label to avoid being too noisy.
return False
def GetLabels(self):
priority = self.GetPriority()
flaky_test_labels = self._GetCommonFlakyTestLabel()
flaky_test_labels.append(priority)
flaky_test_labels.append(issue_constants.FINDIT_ANALYZED_LABEL_TEXT)
if self.GetAutoAssignOwner() or self.GetComponents():
flaky_test_labels.remove(issue_constants.SHERIFF_CHROMIUM_LABEL)
return flaky_test_labels
def OnIssueCreated(self):
monitoring.OnIssueChange('created', 'flake')
def OnIssueUpdated(self):
monitoring.OnIssueChange('update', 'flake')
class FlakeDetectionIssueGenerator(FlakyTestIssueGenerator):
"""Encapsulates the details of issues filed by Flake Detection."""
def __init__(self, flake, num_occurrences):
super(FlakeDetectionIssueGenerator, self).__init__()
self._flake = flake
self._num_occurrences = num_occurrences
def GetStepName(self):
return self._flake.normalized_step_name
def GetTestName(self):
return self._flake.normalized_test_name
def GetTestLabelName(self):
return self._flake.test_label_name
def GetMonorailProject(self):
return FlakeIssue.GetMonorailProjectFromLuciProject(
self._flake.luci_project)
def GetComponents(self):
component = self._flake.GetComponent()
return [component] if component != DEFAULT_COMPONENT else []
def GetDescription(self):
previous_tracking_bug_id = self.GetPreviousTrackingBugId()
previous_tracking_bug_text = _FLAKE_DETECTION_PREVIOUS_TRACKING_BUG.format(
previous_tracking_bug_id) if previous_tracking_bug_id else ''
description = _FLAKE_DETECTION_BUG_DESCRIPTION.format(
test_name=self._flake.test_label_name,
num_occurrences=self._num_occurrences,
flake_url=self._GetLinkForFlake(),
previous_tracking_bug_text=previous_tracking_bug_text,
footer=self._GetFooter())
return description
def GetComment(self):
previous_tracking_bug_id = self.GetPreviousTrackingBugId()
previous_tracking_bug_text = _FLAKE_DETECTION_PREVIOUS_TRACKING_BUG.format(
previous_tracking_bug_id) if previous_tracking_bug_id else ''
comment = _FLAKE_DETECTION_BUG_COMMENT.format(
test_name=self._flake.test_label_name,
num_occurrences=self._num_occurrences,
flake_url=self._GetLinkForFlake(),
previous_tracking_bug_text=previous_tracking_bug_text,
footer=self._GetFooter())
return comment
def ShouldRestoreChromiumSheriffLabel(self):
# Because of flake exoneration, we no longer require sheriffs to triage
# flake bugs.
return False
def GetLabels(self):
flaky_test_labels = self._GetCommonFlakyTestLabel()
priority = self.GetPriority()
flaky_test_labels.append(priority)
flaky_test_labels.append(issue_constants.FLAKE_DETECTION_LABEL_TEXT)
if self.GetComponents():
flaky_test_labels.remove(issue_constants.SHERIFF_CHROMIUM_LABEL)
return flaky_test_labels
def OnIssueCreated(self):
monitoring.OnFlakeDetectionCreateOrUpdateIssues('create',
self.GetStepName())
def OnIssueUpdated(self):
monitoring.OnFlakeDetectionCreateOrUpdateIssues('updated',
self.GetStepName())
def _GetLinkForFlake(self):
"""Given a flake, gets a link to the flake on flake detection UI.
Returns:
A link to the flake on flake detection UI.
"""
hostname = 'analysis.chromium.org'
if IsStaging():
hostname = 'findit-for-me-staging.appspot.com'
url_template = (
'https://%s/p/chromium/flake-portal/flakes/occurrences?key=%s')
return url_template % (hostname, self._flake.key.urlsafe())
def _GetFooter(self):
"""Gets the footer for the bug description of comment.
Returns:
A string representing footer.
"""
wrong_results_bug_link = _FLAKE_DETECTION_WRONG_RESULTS_BUG_LINK.format(
summary=self._flake.normalized_test_name,
flake_link=self._GetLinkForFlake())
return _FLAKE_DETECTION_FOOTER_TEMPLATE.format(
wrong_results_bug_link=wrong_results_bug_link)
class FlakeDetectionGroupIssueGenerator(BaseFlakeIssueGenerator):
"""Encapsulates the details of issues filed by Flake Detection for a flake
group.
This issue_generator can be used for 2 cases:
1. A group of new flakes are detected, and we want to create a bug for this
group. In this case, by our heuristic rules, all flakes are in the same
binary name and test suite, and all happen in the same builds (meaning
they have the same occurrence_count).
2. A group of old flakes are still happening so we want to update their bug.
In this case, all our heuristic rules may not apply since other flakes may
be merged together to one bug automatically or manually.
"""
def __init__(self,
flakes,
num_occurrences,
canonical_step_name=None,
flake_issue=None,
flakes_with_same_occurrences=True):
"""
Args:
flakes (list): a list of Flake entities in the same group for one bug.
num_occurrences (int): Number of occurrence for each flake.
1. If create a bug for a group, by heuristic rule the occurrence should
be the same for all flakes.
2. If updating a bug, numbers might be different, in that case we will
use the smallest number(but still qualified to update the bug) of
occurrences within the group.
canonical_step_name (str): The flakes in a group should be in the same
step name.
flake_issue (FlakeIssue): The FlakeIssue entity for the shared bug of the
group.
flakes_with_same_occurrences (bool): Flag for if flakes in the group have
the same occurrences count. Bug comment should be adjusted based on the
value of this flag.
"""
super(FlakeDetectionGroupIssueGenerator, self).__init__()
self._flakes = flakes
self._num_occurrences = num_occurrences
self._canonical_step_name = canonical_step_name
self._flake_issue = flake_issue
self._flakes_with_same_occurrences = flakes_with_same_occurrences
def _GenerateFlakeList(self):
return '\n'.join([flake.test_label_name for flake in self._flakes])
def _GetNumOccurrences(self):
"""Returns processed num occurrences.
If self._flakes_with_same_occurrences, we can simply use the
self._num_occurrences; otherwise self._num_occurrences should be the
smallest count within the group.
"""
return (self._num_occurrences if self._flakes_with_same_occurrences else
'%d+' % self._num_occurrences)
def _GetLinkForFlakes(self):
"""Given a FlakeIssue, gets a link to all the flake linked to the bug on
flake detection UI.
"""
assert self._flake_issue, (
'FlakeIssue required to generate a comment on a group bug.')
hostname = 'analysis.chromium.org'
if IsStaging():
hostname = 'findit-for-me-staging.appspot.com'
url_template = 'https://%s/p/chromium/flake-portal/flakes?bug_id=%d'
if self._flake_issue.monorail_project != 'chromium':
url_template = '{}&monorail_project={}'.format(
url_template, self._flake_issue.monorail_project)
return url_template % (hostname, self._flake_issue.issue_id)
def _GetIssueSummaryForWrongResultLink(self):
if self._canonical_step_name:
return 'Tests in {}'.format(self._canonical_step_name)
return self._flake_issue.issue_id if self._flake_issue else None
def _GetFooter(self):
"""Gets the footer for the bug description of comment.
Returns:
A string representing footer.
"""
wrong_results_bug_link = _FLAKE_DETECTION_WRONG_RESULTS_BUG_LINK.format(
summary=self._GetIssueSummaryForWrongResultLink(),
flake_link=self._GetLinkForFlakes())
return _FLAKE_DETECTION_FOOTER_TEMPLATE.format(
wrong_results_bug_link=wrong_results_bug_link)
def GetMonorailProject(self):
return FlakeIssue.GetMonorailProjectFromLuciProject(
self._flakes[0].luci_project)
def GetComponents(self):
"""Assigns multiple components to bugs of flake groups."""
components = [flake.GetComponent() for flake in self._flakes]
return list(set(components) - {DEFAULT_COMPONENT})
def GetSummary(self):
return 'Flakes are found in {canonical_step_name}.'.format(
canonical_step_name=self._canonical_step_name)
def GetDescription(self):
previous_tracking_bug_id = self.GetPreviousTrackingBugId()
previous_tracking_bug_text = _FLAKE_DETECTION_PREVIOUS_TRACKING_BUG.format(
previous_tracking_bug_id) if previous_tracking_bug_id else ''
return _FLAKE_DETECTION_GROUP_BUG_DESCRIPTION.format(
canonical_step_name=self._canonical_step_name,
num_occurrences=self._GetNumOccurrences(),
flake_list=self._GenerateFlakeList(),
previous_tracking_bug_text=previous_tracking_bug_text)
def GetFirstCommentWhenBugJustCreated(self):
"""Generates the first comment we'll post to the newly created bug.
In order to provide a url to grouped flakes, an issue id is needed to query
for Flakes with FlakeIssues with that same id which is unavailable at bug
creation time.
"""
return _FLAKE_DETECTION_GROUP_BUG_LINK_COMMENT.format(
flakes_url=self._GetLinkForFlakes(), footer=self._GetFooter())
def GetComment(self):
previous_tracking_bug_id = self.GetPreviousTrackingBugId()
previous_tracking_bug_text = _FLAKE_DETECTION_PREVIOUS_TRACKING_BUG.format(
previous_tracking_bug_id) if previous_tracking_bug_id else ''
return _FLAKE_DETECTION_GROUP_BUG_COMMENT.format(
num_occurrences=self._GetNumOccurrences(),
flake_url=self._GetLinkForFlakes(),
previous_tracking_bug_text=previous_tracking_bug_text,
footer=self._GetFooter())
def GetFlakyTestCustomizedField(self):
return None
def SetFlakeIssue(self, flake_issue):
"""Sets flake_issue for the group when the bug has been created."""
self._flake_issue = flake_issue
def ShouldRestoreChromiumSheriffLabel(self):
# Flake Detection always requires Chromium Sheriff's attentions to disable
# flaky tests when new occurrences are detected.
return True
def GetLabels(self):
flaky_test_labels = self._GetCommonFlakyTestLabel()
priority = self.GetPriority()
flaky_test_labels.append(priority)
flaky_test_labels.append(issue_constants.FLAKE_DETECTION_LABEL_TEXT)
if self.GetComponents():
flaky_test_labels.remove(issue_constants.SHERIFF_CHROMIUM_LABEL)
return flaky_test_labels
def _GetNormalizedStepName(self):
return self._flakes[0].normalized_step_name
def OnIssueCreated(self):
monitoring.OnFlakeDetectionCreateOrUpdateIssues(
'create', self._GetNormalizedStepName())
def OnIssueUpdated(self):
monitoring.OnFlakeDetectionCreateOrUpdateIssues(
'updated', self._GetNormalizedStepName())