xref: /aosp_15_r20/tools/asuite/atest/test_runners/event_handler.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2019, The Android Open Source Project
2*c2e18aaaSAndroid Build Coastguard Worker#
3*c2e18aaaSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*c2e18aaaSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*c2e18aaaSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*c2e18aaaSAndroid Build Coastguard Worker#
7*c2e18aaaSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
8*c2e18aaaSAndroid Build Coastguard Worker#
9*c2e18aaaSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*c2e18aaaSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*c2e18aaaSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*c2e18aaaSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*c2e18aaaSAndroid Build Coastguard Worker# limitations under the License.
14*c2e18aaaSAndroid Build Coastguard Worker
15*c2e18aaaSAndroid Build Coastguard Worker"""Atest test event handler class."""
16*c2e18aaaSAndroid Build Coastguard Worker
17*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import print_function
18*c2e18aaaSAndroid Build Coastguard Worker
19*c2e18aaaSAndroid Build Coastguard Workerfrom collections import deque
20*c2e18aaaSAndroid Build Coastguard Workerfrom datetime import timedelta
21*c2e18aaaSAndroid Build Coastguard Workerimport logging
22*c2e18aaaSAndroid Build Coastguard Workerimport time
23*c2e18aaaSAndroid Build Coastguard Worker
24*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_execution_info
25*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_utils
26*c2e18aaaSAndroid Build Coastguard Workerfrom atest import result_reporter
27*c2e18aaaSAndroid Build Coastguard Workerfrom atest.test_runners import test_runner_base
28*c2e18aaaSAndroid Build Coastguard Worker
29*c2e18aaaSAndroid Build Coastguard Worker
30*c2e18aaaSAndroid Build Coastguard WorkerEVENT_NAMES = {
31*c2e18aaaSAndroid Build Coastguard Worker    'module_started': 'TEST_MODULE_STARTED',
32*c2e18aaaSAndroid Build Coastguard Worker    'module_ended': 'TEST_MODULE_ENDED',
33*c2e18aaaSAndroid Build Coastguard Worker    'run_started': 'TEST_RUN_STARTED',
34*c2e18aaaSAndroid Build Coastguard Worker    'run_ended': 'TEST_RUN_ENDED',
35*c2e18aaaSAndroid Build Coastguard Worker    # Next three are test-level events
36*c2e18aaaSAndroid Build Coastguard Worker    'test_started': 'TEST_STARTED',
37*c2e18aaaSAndroid Build Coastguard Worker    'test_failed': 'TEST_FAILED',
38*c2e18aaaSAndroid Build Coastguard Worker    'test_ended': 'TEST_ENDED',
39*c2e18aaaSAndroid Build Coastguard Worker    'invocation_ended': 'INVOCATION_ENDED',
40*c2e18aaaSAndroid Build Coastguard Worker    # Last two failures are runner-level, not test-level.
41*c2e18aaaSAndroid Build Coastguard Worker    # Invocation failure is broader than run failure.
42*c2e18aaaSAndroid Build Coastguard Worker    'run_failed': 'TEST_RUN_FAILED',
43*c2e18aaaSAndroid Build Coastguard Worker    'invocation_failed': 'INVOCATION_FAILED',
44*c2e18aaaSAndroid Build Coastguard Worker    'test_ignored': 'TEST_IGNORED',
45*c2e18aaaSAndroid Build Coastguard Worker    'test_assumption_failure': 'TEST_ASSUMPTION_FAILURE',
46*c2e18aaaSAndroid Build Coastguard Worker    'log_association': 'LOG_ASSOCIATION',
47*c2e18aaaSAndroid Build Coastguard Worker}
48*c2e18aaaSAndroid Build Coastguard Worker
49*c2e18aaaSAndroid Build Coastguard WorkerEVENT_PAIRS = {
50*c2e18aaaSAndroid Build Coastguard Worker    EVENT_NAMES['module_started']: EVENT_NAMES['module_ended'],
51*c2e18aaaSAndroid Build Coastguard Worker    EVENT_NAMES['run_started']: EVENT_NAMES['run_ended'],
52*c2e18aaaSAndroid Build Coastguard Worker    EVENT_NAMES['test_started']: EVENT_NAMES['test_ended'],
53*c2e18aaaSAndroid Build Coastguard Worker}
54*c2e18aaaSAndroid Build Coastguard WorkerSTART_EVENTS = list(EVENT_PAIRS.keys())
55*c2e18aaaSAndroid Build Coastguard WorkerEND_EVENTS = list(EVENT_PAIRS.values())
56*c2e18aaaSAndroid Build Coastguard WorkerTEST_NAME_TEMPLATE = '%s#%s'
57*c2e18aaaSAndroid Build Coastguard Worker
58*c2e18aaaSAndroid Build Coastguard Worker# time in millisecond.
59*c2e18aaaSAndroid Build Coastguard WorkerONE_SECOND = 1000
60*c2e18aaaSAndroid Build Coastguard WorkerONE_MINUTE = 60000
61*c2e18aaaSAndroid Build Coastguard WorkerONE_HOUR = 3600000
62*c2e18aaaSAndroid Build Coastguard Worker
63*c2e18aaaSAndroid Build Coastguard WorkerCONNECTION_STATE = {
64*c2e18aaaSAndroid Build Coastguard Worker    'current_test': None,
65*c2e18aaaSAndroid Build Coastguard Worker    'test_run_name': None,
66*c2e18aaaSAndroid Build Coastguard Worker    'last_failed': None,
67*c2e18aaaSAndroid Build Coastguard Worker    'last_ignored': None,
68*c2e18aaaSAndroid Build Coastguard Worker    'last_assumption_failed': None,
69*c2e18aaaSAndroid Build Coastguard Worker    'current_group': None,
70*c2e18aaaSAndroid Build Coastguard Worker    'current_group_total': None,
71*c2e18aaaSAndroid Build Coastguard Worker    'test_count': 0,
72*c2e18aaaSAndroid Build Coastguard Worker    'test_start_time': None,
73*c2e18aaaSAndroid Build Coastguard Worker}
74*c2e18aaaSAndroid Build Coastguard Worker
75*c2e18aaaSAndroid Build Coastguard Worker
76*c2e18aaaSAndroid Build Coastguard Workerclass EventHandleError(Exception):
77*c2e18aaaSAndroid Build Coastguard Worker  """Raised when handle event error."""
78*c2e18aaaSAndroid Build Coastguard Worker
79*c2e18aaaSAndroid Build Coastguard Worker
80*c2e18aaaSAndroid Build Coastguard Workerclass EventHandler:
81*c2e18aaaSAndroid Build Coastguard Worker  """Test Event handle class."""
82*c2e18aaaSAndroid Build Coastguard Worker
83*c2e18aaaSAndroid Build Coastguard Worker  def __init__(self, reporter, name):
84*c2e18aaaSAndroid Build Coastguard Worker    self.reporter = reporter
85*c2e18aaaSAndroid Build Coastguard Worker    self.runner_name = name
86*c2e18aaaSAndroid Build Coastguard Worker    self.state = CONNECTION_STATE.copy()
87*c2e18aaaSAndroid Build Coastguard Worker    self.event_stack = deque()
88*c2e18aaaSAndroid Build Coastguard Worker    self.log_associations = []
89*c2e18aaaSAndroid Build Coastguard Worker    self.run_num = 0
90*c2e18aaaSAndroid Build Coastguard Worker
91*c2e18aaaSAndroid Build Coastguard Worker  def _module_started(self, event_data):
92*c2e18aaaSAndroid Build Coastguard Worker    if atest_execution_info.PREPARE_END_TIME is None:
93*c2e18aaaSAndroid Build Coastguard Worker      atest_execution_info.PREPARE_END_TIME = time.time()
94*c2e18aaaSAndroid Build Coastguard Worker    self.state['current_group'] = event_data['moduleName']
95*c2e18aaaSAndroid Build Coastguard Worker    self.state['last_failed'] = None
96*c2e18aaaSAndroid Build Coastguard Worker    self.state['current_test'] = None
97*c2e18aaaSAndroid Build Coastguard Worker
98*c2e18aaaSAndroid Build Coastguard Worker  def _run_started(self, event_data):
99*c2e18aaaSAndroid Build Coastguard Worker    # Technically there can be more than one run per module.
100*c2e18aaaSAndroid Build Coastguard Worker    self.run_num = event_data.get('runAttempt', 0)
101*c2e18aaaSAndroid Build Coastguard Worker    self.state['test_run_name'] = event_data.setdefault('runName', '')
102*c2e18aaaSAndroid Build Coastguard Worker    self.state['current_group_total'] = event_data['testCount']
103*c2e18aaaSAndroid Build Coastguard Worker    self.state['test_count'] = 0
104*c2e18aaaSAndroid Build Coastguard Worker    self.state['last_failed'] = None
105*c2e18aaaSAndroid Build Coastguard Worker    self.state['current_test'] = None
106*c2e18aaaSAndroid Build Coastguard Worker
107*c2e18aaaSAndroid Build Coastguard Worker  def _test_started(self, event_data):
108*c2e18aaaSAndroid Build Coastguard Worker    name = TEST_NAME_TEMPLATE % (
109*c2e18aaaSAndroid Build Coastguard Worker        event_data['className'],
110*c2e18aaaSAndroid Build Coastguard Worker        event_data['testName'],
111*c2e18aaaSAndroid Build Coastguard Worker    )
112*c2e18aaaSAndroid Build Coastguard Worker    self.state['current_test'] = name
113*c2e18aaaSAndroid Build Coastguard Worker    self.state['test_count'] += 1
114*c2e18aaaSAndroid Build Coastguard Worker    self.state['test_start_time'] = event_data['start_time']
115*c2e18aaaSAndroid Build Coastguard Worker
116*c2e18aaaSAndroid Build Coastguard Worker  def _test_failed(self, event_data):
117*c2e18aaaSAndroid Build Coastguard Worker    self.state['last_failed'] = {
118*c2e18aaaSAndroid Build Coastguard Worker        'name': TEST_NAME_TEMPLATE % (
119*c2e18aaaSAndroid Build Coastguard Worker            event_data['className'],
120*c2e18aaaSAndroid Build Coastguard Worker            event_data['testName'],
121*c2e18aaaSAndroid Build Coastguard Worker        ),
122*c2e18aaaSAndroid Build Coastguard Worker        'trace': event_data['trace'],
123*c2e18aaaSAndroid Build Coastguard Worker    }
124*c2e18aaaSAndroid Build Coastguard Worker
125*c2e18aaaSAndroid Build Coastguard Worker  def _test_ignored(self, event_data):
126*c2e18aaaSAndroid Build Coastguard Worker    name = TEST_NAME_TEMPLATE % (
127*c2e18aaaSAndroid Build Coastguard Worker        event_data['className'],
128*c2e18aaaSAndroid Build Coastguard Worker        event_data['testName'],
129*c2e18aaaSAndroid Build Coastguard Worker    )
130*c2e18aaaSAndroid Build Coastguard Worker    self.state['last_ignored'] = name
131*c2e18aaaSAndroid Build Coastguard Worker
132*c2e18aaaSAndroid Build Coastguard Worker  def _test_assumption_failure(self, event_data):
133*c2e18aaaSAndroid Build Coastguard Worker    name = TEST_NAME_TEMPLATE % (
134*c2e18aaaSAndroid Build Coastguard Worker        event_data['className'],
135*c2e18aaaSAndroid Build Coastguard Worker        event_data['testName'],
136*c2e18aaaSAndroid Build Coastguard Worker    )
137*c2e18aaaSAndroid Build Coastguard Worker    self.state['last_assumption_failed'] = name
138*c2e18aaaSAndroid Build Coastguard Worker
139*c2e18aaaSAndroid Build Coastguard Worker  def _run_failed(self, event_data):
140*c2e18aaaSAndroid Build Coastguard Worker    # Module and Test Run probably started, but failure occurred.
141*c2e18aaaSAndroid Build Coastguard Worker    self.reporter.process_test_result(
142*c2e18aaaSAndroid Build Coastguard Worker        test_runner_base.TestResult(
143*c2e18aaaSAndroid Build Coastguard Worker            runner_name=self.runner_name,
144*c2e18aaaSAndroid Build Coastguard Worker            group_name=self.state['current_group'],
145*c2e18aaaSAndroid Build Coastguard Worker            test_name=self.state['current_test'],
146*c2e18aaaSAndroid Build Coastguard Worker            status=test_runner_base.ERROR_STATUS,
147*c2e18aaaSAndroid Build Coastguard Worker            details=event_data['reason'],
148*c2e18aaaSAndroid Build Coastguard Worker            test_count=self.state['test_count'],
149*c2e18aaaSAndroid Build Coastguard Worker            test_time='',
150*c2e18aaaSAndroid Build Coastguard Worker            runner_total=None,
151*c2e18aaaSAndroid Build Coastguard Worker            group_total=self.state['current_group_total'],
152*c2e18aaaSAndroid Build Coastguard Worker            additional_info={},
153*c2e18aaaSAndroid Build Coastguard Worker            test_run_name=self.state['test_run_name'],
154*c2e18aaaSAndroid Build Coastguard Worker        )
155*c2e18aaaSAndroid Build Coastguard Worker    )
156*c2e18aaaSAndroid Build Coastguard Worker
157*c2e18aaaSAndroid Build Coastguard Worker  def _invocation_failed(self, event_data):
158*c2e18aaaSAndroid Build Coastguard Worker    # Broadest possible failure. May not even start the module/test run.
159*c2e18aaaSAndroid Build Coastguard Worker    self.reporter.process_test_result(
160*c2e18aaaSAndroid Build Coastguard Worker        test_runner_base.TestResult(
161*c2e18aaaSAndroid Build Coastguard Worker            runner_name=self.runner_name,
162*c2e18aaaSAndroid Build Coastguard Worker            group_name=self.state['current_group'],
163*c2e18aaaSAndroid Build Coastguard Worker            test_name=self.state['current_test'],
164*c2e18aaaSAndroid Build Coastguard Worker            status=test_runner_base.ERROR_STATUS,
165*c2e18aaaSAndroid Build Coastguard Worker            details=event_data['cause'],
166*c2e18aaaSAndroid Build Coastguard Worker            test_count=self.state['test_count'],
167*c2e18aaaSAndroid Build Coastguard Worker            test_time='',
168*c2e18aaaSAndroid Build Coastguard Worker            runner_total=None,
169*c2e18aaaSAndroid Build Coastguard Worker            group_total=self.state['current_group_total'],
170*c2e18aaaSAndroid Build Coastguard Worker            additional_info={},
171*c2e18aaaSAndroid Build Coastguard Worker            test_run_name=self.state['test_run_name'],
172*c2e18aaaSAndroid Build Coastguard Worker        )
173*c2e18aaaSAndroid Build Coastguard Worker    )
174*c2e18aaaSAndroid Build Coastguard Worker
175*c2e18aaaSAndroid Build Coastguard Worker  def _invocation_ended(self, event_data):
176*c2e18aaaSAndroid Build Coastguard Worker    self.reporter.device_count = event_data.get('device_count', None)
177*c2e18aaaSAndroid Build Coastguard Worker
178*c2e18aaaSAndroid Build Coastguard Worker  # pylint: disable=unused-argument
179*c2e18aaaSAndroid Build Coastguard Worker  def _run_ended(self, event_data):
180*c2e18aaaSAndroid Build Coastguard Worker    # Renew ResultReport if is module level(reporter.silent=False)
181*c2e18aaaSAndroid Build Coastguard Worker    if not self.reporter.silent:
182*c2e18aaaSAndroid Build Coastguard Worker      self.reporter.set_current_iteration_summary(self.run_num)
183*c2e18aaaSAndroid Build Coastguard Worker      self.reporter = result_reporter.ResultReporter(silent=False)
184*c2e18aaaSAndroid Build Coastguard Worker
185*c2e18aaaSAndroid Build Coastguard Worker  def _module_ended(self, event_data):
186*c2e18aaaSAndroid Build Coastguard Worker    pass
187*c2e18aaaSAndroid Build Coastguard Worker
188*c2e18aaaSAndroid Build Coastguard Worker  def _test_ended(self, event_data):
189*c2e18aaaSAndroid Build Coastguard Worker    name = TEST_NAME_TEMPLATE % (
190*c2e18aaaSAndroid Build Coastguard Worker        event_data['className'],
191*c2e18aaaSAndroid Build Coastguard Worker        event_data['testName'],
192*c2e18aaaSAndroid Build Coastguard Worker    )
193*c2e18aaaSAndroid Build Coastguard Worker    test_time = ''
194*c2e18aaaSAndroid Build Coastguard Worker    if self.state['test_start_time']:
195*c2e18aaaSAndroid Build Coastguard Worker      test_time = self._calc_duration(
196*c2e18aaaSAndroid Build Coastguard Worker          event_data['end_time'] - self.state['test_start_time']
197*c2e18aaaSAndroid Build Coastguard Worker      )
198*c2e18aaaSAndroid Build Coastguard Worker    if self.state['last_failed'] and name == self.state['last_failed']['name']:
199*c2e18aaaSAndroid Build Coastguard Worker      status = test_runner_base.FAILED_STATUS
200*c2e18aaaSAndroid Build Coastguard Worker      trace = self.state['last_failed']['trace']
201*c2e18aaaSAndroid Build Coastguard Worker      self.state['last_failed'] = None
202*c2e18aaaSAndroid Build Coastguard Worker    elif (
203*c2e18aaaSAndroid Build Coastguard Worker        self.state['last_assumption_failed']
204*c2e18aaaSAndroid Build Coastguard Worker        and name == self.state['last_assumption_failed']
205*c2e18aaaSAndroid Build Coastguard Worker    ):
206*c2e18aaaSAndroid Build Coastguard Worker      status = test_runner_base.ASSUMPTION_FAILED
207*c2e18aaaSAndroid Build Coastguard Worker      self.state['last_assumption_failed'] = None
208*c2e18aaaSAndroid Build Coastguard Worker      trace = None
209*c2e18aaaSAndroid Build Coastguard Worker    elif self.state['last_ignored'] and name == self.state['last_ignored']:
210*c2e18aaaSAndroid Build Coastguard Worker      status = test_runner_base.IGNORED_STATUS
211*c2e18aaaSAndroid Build Coastguard Worker      self.state['last_ignored'] = None
212*c2e18aaaSAndroid Build Coastguard Worker      trace = None
213*c2e18aaaSAndroid Build Coastguard Worker    else:
214*c2e18aaaSAndroid Build Coastguard Worker      status = test_runner_base.PASSED_STATUS
215*c2e18aaaSAndroid Build Coastguard Worker      trace = None
216*c2e18aaaSAndroid Build Coastguard Worker
217*c2e18aaaSAndroid Build Coastguard Worker    default_event_keys = ['className', 'end_time', 'testName']
218*c2e18aaaSAndroid Build Coastguard Worker    additional_info = {}
219*c2e18aaaSAndroid Build Coastguard Worker    for event_key in event_data.keys():
220*c2e18aaaSAndroid Build Coastguard Worker      if event_key not in default_event_keys:
221*c2e18aaaSAndroid Build Coastguard Worker        additional_info[event_key] = event_data.get(event_key, None)
222*c2e18aaaSAndroid Build Coastguard Worker
223*c2e18aaaSAndroid Build Coastguard Worker    self.reporter.process_test_result(
224*c2e18aaaSAndroid Build Coastguard Worker        test_runner_base.TestResult(
225*c2e18aaaSAndroid Build Coastguard Worker            runner_name=self.runner_name,
226*c2e18aaaSAndroid Build Coastguard Worker            group_name=self.state['current_group'],
227*c2e18aaaSAndroid Build Coastguard Worker            test_name=name,
228*c2e18aaaSAndroid Build Coastguard Worker            status=status,
229*c2e18aaaSAndroid Build Coastguard Worker            details=trace,
230*c2e18aaaSAndroid Build Coastguard Worker            test_count=self.state['test_count'],
231*c2e18aaaSAndroid Build Coastguard Worker            test_time=test_time,
232*c2e18aaaSAndroid Build Coastguard Worker            runner_total=None,
233*c2e18aaaSAndroid Build Coastguard Worker            additional_info=additional_info,
234*c2e18aaaSAndroid Build Coastguard Worker            group_total=self.state['current_group_total'],
235*c2e18aaaSAndroid Build Coastguard Worker            test_run_name=self.state['test_run_name'],
236*c2e18aaaSAndroid Build Coastguard Worker        )
237*c2e18aaaSAndroid Build Coastguard Worker    )
238*c2e18aaaSAndroid Build Coastguard Worker
239*c2e18aaaSAndroid Build Coastguard Worker  def _log_association(self, event_data):
240*c2e18aaaSAndroid Build Coastguard Worker    event_data.setdefault('time', time.time())
241*c2e18aaaSAndroid Build Coastguard Worker    self.log_associations.append(event_data)
242*c2e18aaaSAndroid Build Coastguard Worker
243*c2e18aaaSAndroid Build Coastguard Worker  switch_handler = {
244*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['module_started']: _module_started,
245*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['run_started']: _run_started,
246*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['test_started']: _test_started,
247*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['test_failed']: _test_failed,
248*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['test_ignored']: _test_ignored,
249*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['test_assumption_failure']: _test_assumption_failure,
250*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['run_failed']: _run_failed,
251*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['invocation_failed']: _invocation_failed,
252*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['invocation_ended']: _invocation_ended,
253*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['test_ended']: _test_ended,
254*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['run_ended']: _run_ended,
255*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['module_ended']: _module_ended,
256*c2e18aaaSAndroid Build Coastguard Worker      EVENT_NAMES['log_association']: _log_association,
257*c2e18aaaSAndroid Build Coastguard Worker  }
258*c2e18aaaSAndroid Build Coastguard Worker
259*c2e18aaaSAndroid Build Coastguard Worker  def process_event(self, event_name, event_data):
260*c2e18aaaSAndroid Build Coastguard Worker    """Process the events of the test run and call reporter with results.
261*c2e18aaaSAndroid Build Coastguard Worker
262*c2e18aaaSAndroid Build Coastguard Worker    Args:
263*c2e18aaaSAndroid Build Coastguard Worker        event_name: A string of the event name.
264*c2e18aaaSAndroid Build Coastguard Worker        event_data: A dict of event data.
265*c2e18aaaSAndroid Build Coastguard Worker    """
266*c2e18aaaSAndroid Build Coastguard Worker    logging.debug('Processing %s %s', event_name, event_data)
267*c2e18aaaSAndroid Build Coastguard Worker    if event_name in START_EVENTS:
268*c2e18aaaSAndroid Build Coastguard Worker      self.event_stack.append(event_name)
269*c2e18aaaSAndroid Build Coastguard Worker    elif event_name in END_EVENTS:
270*c2e18aaaSAndroid Build Coastguard Worker      self._check_events_are_balanced(event_name, self.reporter, event_data)
271*c2e18aaaSAndroid Build Coastguard Worker    if event_name in self.switch_handler:
272*c2e18aaaSAndroid Build Coastguard Worker      self.switch_handler[event_name](self, event_data)
273*c2e18aaaSAndroid Build Coastguard Worker    else:
274*c2e18aaaSAndroid Build Coastguard Worker      # TODO(b/128875503): Implement the mechanism to inform not handled
275*c2e18aaaSAndroid Build Coastguard Worker      # TF event.
276*c2e18aaaSAndroid Build Coastguard Worker      logging.debug('Event[%s] is not processable.', event_name)
277*c2e18aaaSAndroid Build Coastguard Worker
278*c2e18aaaSAndroid Build Coastguard Worker  def _check_events_are_balanced(
279*c2e18aaaSAndroid Build Coastguard Worker      self, end_event_name, reporter, end_event_data
280*c2e18aaaSAndroid Build Coastguard Worker  ):
281*c2e18aaaSAndroid Build Coastguard Worker    """Check whether the Start events and End events are balanced.
282*c2e18aaaSAndroid Build Coastguard Worker
283*c2e18aaaSAndroid Build Coastguard Worker    When imbalance events are detected, and we understand the case of imbalance,
284*c2e18aaaSAndroid Build Coastguard Worker    the events will handled without throwing error; otherwise EventHandleError
285*c2e18aaaSAndroid Build Coastguard Worker    will raise.
286*c2e18aaaSAndroid Build Coastguard Worker
287*c2e18aaaSAndroid Build Coastguard Worker    Args:
288*c2e18aaaSAndroid Build Coastguard Worker        end_event_name: A string of the event name.
289*c2e18aaaSAndroid Build Coastguard Worker        reporter: A ResultReporter instance.
290*c2e18aaaSAndroid Build Coastguard Worker        end_event_data: A dict of event data.
291*c2e18aaaSAndroid Build Coastguard Worker
292*c2e18aaaSAndroid Build Coastguard Worker    Raises:
293*c2e18aaaSAndroid Build Coastguard Worker        EventHandleError if we can't handle the imbalance of START/END events.
294*c2e18aaaSAndroid Build Coastguard Worker    """
295*c2e18aaaSAndroid Build Coastguard Worker    start_event = self.event_stack.pop() if self.event_stack else None
296*c2e18aaaSAndroid Build Coastguard Worker
297*c2e18aaaSAndroid Build Coastguard Worker    def _handle_crashed_test(message):
298*c2e18aaaSAndroid Build Coastguard Worker      atest_utils.print_and_log_error(message)
299*c2e18aaaSAndroid Build Coastguard Worker
300*c2e18aaaSAndroid Build Coastguard Worker      self.reporter.process_test_result(
301*c2e18aaaSAndroid Build Coastguard Worker          test_runner_base.TestResult(
302*c2e18aaaSAndroid Build Coastguard Worker              runner_name=self.runner_name,
303*c2e18aaaSAndroid Build Coastguard Worker              group_name=self.state['current_group'],
304*c2e18aaaSAndroid Build Coastguard Worker              test_name=self.state['current_test'],
305*c2e18aaaSAndroid Build Coastguard Worker              status=test_runner_base.ERROR_STATUS,
306*c2e18aaaSAndroid Build Coastguard Worker              details=message,
307*c2e18aaaSAndroid Build Coastguard Worker              test_count=self.state['test_count'],
308*c2e18aaaSAndroid Build Coastguard Worker              test_time='',
309*c2e18aaaSAndroid Build Coastguard Worker              runner_total=None,
310*c2e18aaaSAndroid Build Coastguard Worker              group_total=self.state['current_group_total'],
311*c2e18aaaSAndroid Build Coastguard Worker              additional_info={},
312*c2e18aaaSAndroid Build Coastguard Worker              test_run_name=self.state['test_run_name'],
313*c2e18aaaSAndroid Build Coastguard Worker          )
314*c2e18aaaSAndroid Build Coastguard Worker      )
315*c2e18aaaSAndroid Build Coastguard Worker
316*c2e18aaaSAndroid Build Coastguard Worker    if not start_event or EVENT_PAIRS[start_event] != end_event_name:
317*c2e18aaaSAndroid Build Coastguard Worker      # Here bubble up the failed trace in the situation having
318*c2e18aaaSAndroid Build Coastguard Worker      # TEST_FAILED but never receiving TEST_ENDED.
319*c2e18aaaSAndroid Build Coastguard Worker      if self.state['last_failed'] and (
320*c2e18aaaSAndroid Build Coastguard Worker          start_event == EVENT_NAMES['test_started']
321*c2e18aaaSAndroid Build Coastguard Worker      ):
322*c2e18aaaSAndroid Build Coastguard Worker        self.reporter.process_test_result(
323*c2e18aaaSAndroid Build Coastguard Worker            test_runner_base.TestResult(
324*c2e18aaaSAndroid Build Coastguard Worker                runner_name=self.runner_name,
325*c2e18aaaSAndroid Build Coastguard Worker                group_name=self.state['current_group'],
326*c2e18aaaSAndroid Build Coastguard Worker                test_name=self.state['last_failed']['name'],
327*c2e18aaaSAndroid Build Coastguard Worker                status=test_runner_base.FAILED_STATUS,
328*c2e18aaaSAndroid Build Coastguard Worker                details=self.state['last_failed']['trace'],
329*c2e18aaaSAndroid Build Coastguard Worker                test_count=self.state['test_count'],
330*c2e18aaaSAndroid Build Coastguard Worker                test_time='',
331*c2e18aaaSAndroid Build Coastguard Worker                runner_total=None,
332*c2e18aaaSAndroid Build Coastguard Worker                group_total=self.state['current_group_total'],
333*c2e18aaaSAndroid Build Coastguard Worker                additional_info={},
334*c2e18aaaSAndroid Build Coastguard Worker                test_run_name=self.state['test_run_name'],
335*c2e18aaaSAndroid Build Coastguard Worker            )
336*c2e18aaaSAndroid Build Coastguard Worker        )
337*c2e18aaaSAndroid Build Coastguard Worker        # Even though we have proceessed the test result here, we still consider
338*c2e18aaaSAndroid Build Coastguard Worker        # this case unhandled as we don't have a full understanding about the cause.
339*c2e18aaaSAndroid Build Coastguard Worker        # So we don't return here.
340*c2e18aaaSAndroid Build Coastguard Worker        raise EventHandleError(
341*c2e18aaaSAndroid Build Coastguard Worker            'Error: Test failed without receiving a test end event'
342*c2e18aaaSAndroid Build Coastguard Worker        )
343*c2e18aaaSAndroid Build Coastguard Worker      elif (
344*c2e18aaaSAndroid Build Coastguard Worker          end_event_name == EVENT_NAMES['module_ended']
345*c2e18aaaSAndroid Build Coastguard Worker          and start_event == EVENT_NAMES['run_started']
346*c2e18aaaSAndroid Build Coastguard Worker      ):
347*c2e18aaaSAndroid Build Coastguard Worker        _handle_crashed_test(
348*c2e18aaaSAndroid Build Coastguard Worker            'Test run started but did not end. This often happens when the test'
349*c2e18aaaSAndroid Build Coastguard Worker            ' binary/app such as android instrumentation app process died.'
350*c2e18aaaSAndroid Build Coastguard Worker            ' Test count might be inaccurate.'
351*c2e18aaaSAndroid Build Coastguard Worker        )
352*c2e18aaaSAndroid Build Coastguard Worker        return
353*c2e18aaaSAndroid Build Coastguard Worker      elif (
354*c2e18aaaSAndroid Build Coastguard Worker          end_event_name == EVENT_NAMES['run_ended']
355*c2e18aaaSAndroid Build Coastguard Worker          and start_event == EVENT_NAMES['test_started']
356*c2e18aaaSAndroid Build Coastguard Worker      ):
357*c2e18aaaSAndroid Build Coastguard Worker        _handle_crashed_test(
358*c2e18aaaSAndroid Build Coastguard Worker            'Test started but did not end. This often happens when the test'
359*c2e18aaaSAndroid Build Coastguard Worker            ' binary/app such as android instrumentation app process died.'
360*c2e18aaaSAndroid Build Coastguard Worker            ' Test count might be inaccurate.'
361*c2e18aaaSAndroid Build Coastguard Worker        )
362*c2e18aaaSAndroid Build Coastguard Worker        return
363*c2e18aaaSAndroid Build Coastguard Worker      else:
364*c2e18aaaSAndroid Build Coastguard Worker        raise EventHandleError(
365*c2e18aaaSAndroid Build Coastguard Worker            'Error: Saw %s Start event and %s End event. These should be equal!'
366*c2e18aaaSAndroid Build Coastguard Worker            % (start_event, end_event_name)
367*c2e18aaaSAndroid Build Coastguard Worker        )
368*c2e18aaaSAndroid Build Coastguard Worker
369*c2e18aaaSAndroid Build Coastguard Worker  @staticmethod
370*c2e18aaaSAndroid Build Coastguard Worker  def _calc_duration(duration):
371*c2e18aaaSAndroid Build Coastguard Worker    """Convert duration from ms to 3h2m43.034s.
372*c2e18aaaSAndroid Build Coastguard Worker
373*c2e18aaaSAndroid Build Coastguard Worker    Args:
374*c2e18aaaSAndroid Build Coastguard Worker        duration: millisecond
375*c2e18aaaSAndroid Build Coastguard Worker
376*c2e18aaaSAndroid Build Coastguard Worker    Returns:
377*c2e18aaaSAndroid Build Coastguard Worker        string in h:m:s, m:s, s or millis, depends on the duration.
378*c2e18aaaSAndroid Build Coastguard Worker    """
379*c2e18aaaSAndroid Build Coastguard Worker    delta = timedelta(milliseconds=duration)
380*c2e18aaaSAndroid Build Coastguard Worker    timestamp = str(delta).split(':')  # hh:mm:microsec
381*c2e18aaaSAndroid Build Coastguard Worker
382*c2e18aaaSAndroid Build Coastguard Worker    if duration < ONE_SECOND:
383*c2e18aaaSAndroid Build Coastguard Worker      return '({}ms)'.format(duration)
384*c2e18aaaSAndroid Build Coastguard Worker    if duration < ONE_MINUTE:
385*c2e18aaaSAndroid Build Coastguard Worker      return '({:.3f}s)'.format(float(timestamp[2]))
386*c2e18aaaSAndroid Build Coastguard Worker    if duration < ONE_HOUR:
387*c2e18aaaSAndroid Build Coastguard Worker      return '({0}m{1:.3f}s)'.format(timestamp[1], float(timestamp[2]))
388*c2e18aaaSAndroid Build Coastguard Worker    return '({0}h{1}m{2:.3f}s)'.format(
389*c2e18aaaSAndroid Build Coastguard Worker        timestamp[0], timestamp[1], float(timestamp[2])
390*c2e18aaaSAndroid Build Coastguard Worker    )
391