15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/python2.4
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright 2008, The Android Open Source Project
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License");
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# you may not use this file except in compliance with the License.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# You may obtain a copy of the License at
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#     http://www.apache.org/licenses/LICENSE-2.0
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# distributed under the License is distributed on an "AS IS" BASIS,
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# See the License for the specific language governing permissions and
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# limitations under the License.
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Module that assists in parsing the output of "am instrument" commands run on
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)the device."""
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import string
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def ParseAmInstrumentOutput(result):
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Given the raw output of an "am instrument" command that targets and
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InstrumentationTestRunner, return structured data.
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result (string): Raw output of "am instrument"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Return
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (test_results, inst_finished_bundle)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_results (list of am_output_parser.TestResult)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inst_finished_bundle (dict): Key/value pairs contained in the bundle that is
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    code of the Instrumentation process, any error codes reported by the
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    activity manager, and any results explicity added by the instrumentation
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    code.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  re_status_code = re.compile(r'INSTRUMENTATION_STATUS_CODE: (?P<status_code>-?\d)$')
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  test_results = []
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inst_finished_bundle = {}
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result_block_string = ""
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for line in result.splitlines():
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result_block_string += line + '\n'
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if "INSTRUMENTATION_STATUS_CODE:" in line:
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      test_result = TestResult(result_block_string)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if test_result.GetStatusCode() == 1: # The test started
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pass
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif test_result.GetStatusCode() in [0, -1, -2]:
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test_results.append(test_result)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        pass
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result_block_string = ""
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if "INSTRUMENTATION_CODE:" in line:
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      inst_finished_bundle = _ParseInstrumentationFinishedBundle(result_block_string)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result_block_string = ""
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (test_results, inst_finished_bundle)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _ParseInstrumentationFinishedBundle(result):
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Given the raw output of "am instrument" returns a dictionary of the
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  key/value pairs from the bundle passed into
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ActivityManager.finishInstrumentation().
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result (string): Raw output of "am instrument"
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Return:
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inst_finished_bundle (dict): Key/value pairs contained in the bundle that is
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    code of the Instrumentation process, any error codes reported by the
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    activity manager, and any results explicity added by the instrumentation
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    code.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  re_result = re.compile(r'INSTRUMENTATION_RESULT: ([^=]+)=(.*)$')
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  re_code = re.compile(r'INSTRUMENTATION_CODE: (\-?\d)$')
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result_dict = {}
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  key = ''
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  val = ''
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_tag = ''
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for line in result.split('\n'):
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    line = line.strip(string.whitespace)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if re_result.match(line):
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_tag = 'INSTRUMENTATION_RESULT'
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      key = re_result.search(line).group(1).strip(string.whitespace)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if key.startswith('performance.'):
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = key[len('performance.'):]
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      val = re_result.search(line).group(2).strip(string.whitespace)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try:
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        result_dict[key] = float(val)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      except ValueError:
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        result_dict[key] = val
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      except TypeError:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        result_dict[key] = val
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif re_code.match(line):
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_tag = 'INSTRUMENTATION_CODE'
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      key = 'code'
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      val = re_code.search(line).group(1).strip(string.whitespace)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result_dict[key] = val
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif 'INSTRUMENTATION_ABORTED:' in line:
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_tag = 'INSTRUMENTATION_ABORTED'
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      key = 'INSTRUMENTATION_ABORTED'
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      val = ''
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result_dict[key] = val
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif last_tag == 'INSTRUMENTATION_RESULT':
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result_dict[key] += '\n' + line
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not result_dict.has_key('code'):
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result_dict['code'] = '0'
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result_dict['shortMsg'] = "No result returned from instrumentation"
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result_dict
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class TestResult(object):
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A class that contains information about a single test result."""
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, result_block_string):
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      result_block_string (string): Is a single "block" of output. A single
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "block" would be either a "test started" status report, or a "test
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      finished" status report.
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._test_name = None
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._status_code = None
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._failure_reason = None
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._fields_map = {}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    re_status_code = re.search(r'INSTRUMENTATION_STATUS_CODE: '
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        '(?P<status_code>1|0|-1|-2)', result_block_string)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    re_fields = re.compile(r'INSTRUMENTATION_STATUS: '
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        '(?P<key>[\w.]+)=(?P<value>.*?)(?=\nINSTRUMENTATION_STATUS)', re.DOTALL)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for field in re_fields.finditer(result_block_string):
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      key, value = (field.group('key').strip(), field.group('value').strip())
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if key.startswith('performance.'):
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = key[len('performance.'):]
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._fields_map[key] = value
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._fields_map.setdefault('class')
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._fields_map.setdefault('test')
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._test_name = '%s:%s' % (self._fields_map['class'],
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 self._fields_map['test'])
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._status_code = int(re_status_code.group('status_code'))
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'stack' in self._fields_map:
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._failure_reason = self._fields_map['stack']
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetTestName(self):
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._test_name
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetStatusCode(self):
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._status_code
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetFailureReason(self):
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._failure_reason
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetResultFields(self):
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._fields_map
170