xref: /aosp_15_r20/external/autotest/server/cros/dynamic_suite/reporting.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import collections
6import logging
7
8import common
9
10from autotest_lib.server import site_utils
11from autotest_lib.server.cros.dynamic_suite import job_status
12from autotest_lib.server.cros.dynamic_suite import reporting_utils
13from autotest_lib.server.cros.dynamic_suite import tools
14
15
16class TestBug(object):
17    """
18    Wrap up all information needed to make an intelligent report about an
19    issue. Each TestBug has a search marker associated with it that can be
20    used to find similar reports.
21    """
22
23    def __init__(self, build, chrome_version, suite, result):
24        """
25        @param build: The build type, of the form <board>/<milestone>-<release>.
26                      eg: x86-mario-release/R25-4321.0.0
27        @param chrome_version: The chrome version associated with the build.
28                               eg: 28.0.1498.1
29        @param suite: The name of the suite that this test run is a part of.
30        @param result: The status of the job associated with this issue.
31                       This contains the status, job id, test name, hostname
32                       and reason for issue.
33        """
34        self.build = build
35        self.chrome_version = chrome_version
36        self.suite = suite
37        self.name = tools.get_test_name(build, suite, result.test_name)
38        self.reason = result.reason
39        # The result_owner is used to find results and logs.
40        self.result_owner = result.owner
41        self.hostname = result.hostname
42        self.job_id = result.id
43
44        # Aborts, server/client job failures or a test failure without a
45        # reason field need lab attention. Lab bugs for the aborted case
46        # are disabled till crbug.com/188217 is resolved.
47        self.lab_error = job_status.is_for_infrastructure_fail(result)
48
49        # The owner is who the bug is assigned to.
50        self.owner = ''
51        self.cc = []
52        self.components = []
53
54        if result.is_warn():
55            self.labels = ['Test-Warning']
56            self.status = 'Warning'
57        else:
58            self.labels = []
59            self.status = 'Failure'
60
61
62    def title(self):
63        """Combines information about this bug into a title string."""
64        return '[%s] %s %s on %s' % (self.suite, self.name,
65                                     self.status, self.build)
66
67
68    def summary(self):
69        """Combines information about this bug into a summary string."""
70
71        links = self._get_links_for_failure()
72        template = ('This report is automatically generated to track the '
73                    'following %(status)s:\n'
74                    'Test: %(test)s.\n'
75                    'Suite: %(suite)s.\n'
76                    'Chrome Version: %(chrome_version)s.\n'
77                    'Build: %(build)s.\n\nReason:\n%(reason)s.\n'
78                    'build artifacts: %(build_artifacts)s.\n'
79                    'results log: %(results_log)s.\n'
80                    'status log: %(status_log)s.\n'
81                    'job link: %(job)s.\n\n'
82                    'You may want to check the test history: '
83                    '%(test_history_url)s\n'
84                    'You may also want to check the test retry dashboard in '
85                    'case this is a flakey test: %(retry_url)s\n')
86
87        specifics = {
88            'status': self.status,
89            'test': self.name,
90            'suite': self.suite,
91            'build': self.build,
92            'chrome_version': self.chrome_version,
93            'reason': self.reason,
94            'build_artifacts': links.artifacts,
95            'results_log': links.results,
96            'status_log': links.status_log,
97            'job': links.job,
98            'test_history_url': links.test_history_url,
99            'retry_url': links.retry_url,
100        }
101
102        return template % specifics
103
104
105    # TO-DO(shuqianz) Fix the dedupe failing issue because reason contains
106    # special characters after
107    # https://bugs.chromium.org/p/monorail/issues/detail?id=806 being fixed.
108    def search_marker(self):
109        """Return an Anchor that we can use to dedupe this exact bug."""
110        board = ''
111        try:
112            board = site_utils.ParseBuildName(self.build)[0]
113        except site_utils.ParseBuildNameException as e:
114            logging.error(str(e))
115
116        # Substitute the board name for a placeholder. We try both build and
117        # release board name variants.
118        reason = self.reason
119        if board:
120            for b in (board, board.replace('_', '-')):
121                reason = reason.replace(b, 'BOARD_PLACEHOLDER')
122
123        return "%s{%s,%s,%s}" % ('Test%s' % self.status, self.suite,
124                                 self.name, reason)
125
126
127    def _get_links_for_failure(self):
128        """Returns a named tuple of links related to this failure."""
129        links = collections.namedtuple('links', ('results,'
130                                                 'status_log,'
131                                                 'artifacts,'
132                                                 'job,'
133                                                 'test_history_url,'
134                                                 'retry_url'))
135        return links(reporting_utils.link_result_logs(
136                         self.job_id, self.result_owner, self.hostname),
137                     reporting_utils.link_status_log(
138                         self.job_id, self.result_owner, self.hostname),
139                     reporting_utils.link_build_artifacts(self.build),
140                     reporting_utils.link_job(self.job_id),
141                     reporting_utils.link_test_history(self.name),
142                     reporting_utils.link_retry_url(self.name))
143
144
145ReportResult = collections.namedtuple('ReportResult', ['bug_id', 'update_count'])
146
147
148def send_email(bug, bug_template):
149    """Send email to the owner and cc's to notify the TestBug.
150
151    @param bug: TestBug instance.
152    @param bug_template: A template dictionary specifying the default bug
153                         filing options for failures in this suite.
154    """
155    # TODO(ayatane): Deprecated, no time to untangle imports to delete right now.
156    pass
157