blob: 6c4a32b7af60933dc5eeceb71a176f46af44a820 [file] [log] [blame]
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Classes to manage HWTest result."""
from __future__ import print_function
import collections
import os
from chromite.lib import constants
from chromite.lib import cros_logging as logging
from chromite.lib import git
from chromite.lib import patch as cros_patch
HWTEST_RESULT_COLUMNS = ['id', 'build_id', 'test_name', 'status']
_hwTestResult = collections.namedtuple('_hwTestResult',
HWTEST_RESULT_COLUMNS)
class HWTestResult(_hwTestResult):
"""Class to present HWTest status."""
@classmethod
def FromReport(cls, build_id, test_name, status):
"""Get a HWTestResult instance from a result report."""
return HWTestResult(None, build_id, test_name, status)
@classmethod
def FromEntry(cls, entry):
"""Get a HWTestResult instance from cidb entry."""
return HWTestResult(entry.id, entry.build_id, entry.test_name, entry.status)
@classmethod
def NormalizeTestName(cls, test_name):
"""Normalize test name.
Normalization examples:
Suite job -> None
cheets_CTS.com.android.cts.dram -> cheets_CTS
security_NetworkListeners -> security_NetworkListeners
Args:
test_name: The test name string to normalize.
Returns:
Test name after normalization.
"""
if test_name == 'Suite job':
return None
names = test_name.split('.')
return names[0]
class HWTestResultManager(object):
"""Class to manage HWTest results."""
@classmethod
def GetHWTestResultsFromCIDB(cls, db, build_ids, test_statues=None):
"""Get HWTest results for given builds from CIDB.
Args:
db: An instance of cidb.CIDBConnection.
build_ids: A list of build_ids (strings) to get HWTest results.
test_statues: A set of HWTest statuses (stirngs) to get. If not None,
only return HWTests in test_statues.
Returns:
A list of HWTestResult instances.
"""
results = db.GetHWTestResultsForBuilds(build_ids)
if test_statues is None:
return results
return [x for x in results if x.status in test_statues]
@classmethod
def GetFailedHWTestsFromCIDB(cls, db, build_ids):
"""Get test names of failed HWTests from CIDB.
Args:
db: An instance of cidb.CIDBConnection
build_ids: A list of build_ids (strings) to get failed HWTests.
Returns:
A list of normalized HWTest names (strings).
"""
# TODO: probably only count 'fail' and exclude abort' and 'other' results?
hwtest_results = cls.GetHWTestResultsFromCIDB(
db, build_ids, test_statues=constants.HWTEST_STATUES_NOT_PASSED)
failed_tests = set([HWTestResult.NormalizeTestName(result.test_name)
for result in hwtest_results])
failed_tests.discard(None)
logging.info('Found failed tests: %s ', failed_tests)
return failed_tests
@classmethod
def GetFailedHwtestsAffectedByChange(cls, change, manifest, failed_hwtests):
"""Get failed HWtests which could be affected by the given change.
Args:
change: An instance of cros_patch.GerritPatch.
manifest: An instance of ManifestCheckout.
failed_hwtests: A list of normalized name of failed hwtests got from CIDB
(see the return type of HWTestResultManager.GetFailedHWTestsFromCIDB).
Returns:
A list of failed hwtest names (strings) which are likely to be affected by
the change.
"""
assert failed_hwtests
checkout = change.GetCheckout(manifest)
assigned_failed_hwtests = set()
if checkout:
git_repo = checkout.GetPath(absolute=True)
for path in change.GetDiffStatus(git_repo):
root, _ = os.path.splitext(path)
touched_dirs = root.split(os.sep)
for touched_dir in touched_dirs:
if touched_dir in failed_hwtests:
logging.info('Change %s changed path %s which may be relevant to '
'the failed HWTest %s', change, path, touched_dir)
assigned_failed_hwtests.add(touched_dir)
return assigned_failed_hwtests
@classmethod
def FindHWTestFailureSuspects(cls, changes, build_root, failed_hwtests):
"""Find suspects for HWTest failures.
Args:
changes: A list of cros_patch.GerritPatch instances.
build_root: The path to the build root.
failed_hwtests: A list of names of failed hwtests got from CIDB (see the
return type of HWTestResultManager.GetFailedHWTestsFromCIDB), or None.
Returns:
A pair of suspects and no_assignee_hwtests. suspects is a set of
cros_patch.GerritPatch instances. no_assignee_hwtests is True when there
are failed hwtests without assigned suspects; else False.
"""
suspects = set()
no_assignee_hwtests = False
if not failed_hwtests:
logging.info('No failed HWTests, skip finding HWTest failure suspects')
return suspects, no_assignee_hwtests
manifest = git.ManifestCheckout.Cached(build_root)
hwtests_with_assignee = set()
for change in changes:
assigned = cls.GetFailedHwtestsAffectedByChange(
change, manifest, failed_hwtests)
if assigned:
hwtests_with_assignee.update(assigned)
suspects.add(change)
if suspects:
logging.info('Found suspects for HWTest failures: %s',
cros_patch.GetChangesAsString(suspects))
hwtests_without_assignee = failed_hwtests - hwtests_with_assignee
if hwtests_without_assignee:
logging.info('Didn\'t find changes to blame for failed HWtests: %s',
hwtests_without_assignee)
no_assignee_hwtests = True
return suspects, no_assignee_hwtests