in mozci/push.py [0:0]
def _is_classified_as_cause(self, first_appearance_push, classifications):
"""Checks a 'fixed by commit' classification to figure out what push it references.
Returns:
bool or None: True, if the classification references this push.
False, if the classification references another push.
None, if it is not clear what the classification references.
"""
fixed_by_commit_classification_notes = set(
n[:12]
for c, n in classifications
if c == "fixed by commit"
if n is not None
)
if len(fixed_by_commit_classification_notes) == 0:
return None
# If the failure was classified as fixed by commit, and the fixing commit
# is a backout of the current push, it is definitely a regression of the
# current push.
# If the failure was classified as fixed by commit, and the fixing commit
# is a backout of another push, it is definitely not a regression of the
# current push.
# Unless some condition holds which makes us doubt about the correctness of the
# classification.
# - the backout commit also backs out one of the commits of this push;
# - the other push backed-out by the commit which is mentioned in the classification
# is landed after the push where the failure occurs (so, it can't have caused it);
# - the backout push also contains a commit backing out one of the commits of this push.
for classification_note in fixed_by_commit_classification_notes:
fix_hgmo = HgRev.create(classification_note, branch=self.branch)
try:
fix_hgmo.backouts
except PushNotFound:
logger.warning(
f"Classification note ({classification_note}) references a revision which does not exist on push {first_appearance_push.rev}"
)
return None
self_fix = None
other_fixes = set()
# If the backout commit also backs out one of the commits of this push, then
# we can consider it as a regression of this push.
# NOTE: this should never happen in practice because of current development
# practices.
for backout, backedouts in fix_hgmo.backouts.items():
if backout[:12] != classification_note[:12]:
continue
if any(
rev[:12] in {backedout[:12] for backedout in backedouts}
for rev in self.revs
):
self_fix = backout[:12]
break
# Otherwise, if the backout push also contains the backout commit of this push,
# we can consider it as a regression of this push.
if self.backedout:
for backout in fix_hgmo.backouts:
if backout[:12] == self.backedoutby[:12]:
self_fix = backout[:12]
break
# If one of the commits in the backout push is a bustage fix, then we could
# consider it as a regression of this push.
if self_fix is None:
for bug in self.bugs:
if bug in fix_hgmo.bugs_without_backouts:
self_fix = fix_hgmo.bugs_without_backouts[bug][:12]
break
# Otherwise, as long as the commit which was backed-out was landed **before**
# the appearance of this failure, we can be sure it was its cause and so
# the current push is not at fault.
# TODO: We should actually check if the failure was already happening in the parents
# and compare the backout push ID with the the ID of first parent where it failed.
for backout, backedouts in fix_hgmo.backouts.items():
if self.backedout and backout[:12] == self.backedoutby[:12]:
continue
if any(
HgRev.create(backedout, branch=self.branch).pushid
<= first_appearance_push.id
for backedout in backedouts
):
other_fixes.add(backout[:12])
# If the backout push contains a bustage fix of another push, then we could
# consider it as a regression of another push.
if len(fix_hgmo.bugs_without_backouts) > 0:
other_parent = first_appearance_push
for other_parent in other_parent._iterate_parents(MAX_DEPTH):
if other_parent != self:
for bug in other_parent.bugs:
if bug in fix_hgmo.bugs_without_backouts:
other_fixes.add(
fix_hgmo.bugs_without_backouts[bug][:12]
)
if self_fix and other_fixes:
# If the classification points to a commit in the middle of the backout push and not the backout push head,
# we can consider the classification to be correct.
if (
self_fix != fix_hgmo.pushhead[:12]
and classification_note[:12] == self_fix
and classification_note[:12] not in other_fixes
):
return True
elif any(
other_fix != fix_hgmo.pushhead[:12]
and classification_note[:12] == other_fix
and classification_note[:12] != self_fix
for other_fix in other_fixes
):
return False
return None
if self_fix:
return True
if other_fixes:
return False
return None