12b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project#!/usr/bin/python2.4
22b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project#
32b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project#
42b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project# Copyright 2008, The Android Open Source Project
52b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project#
62b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project# Licensed under the Apache License, Version 2.0 (the "License");
72b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project# you may not use this file except in compliance with the License.
82b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project# You may obtain a copy of the License at
92b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project#
102b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project#     http://www.apache.org/licenses/LICENSE-2.0
112b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project#
122b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project# Unless required by applicable law or agreed to in writing, software
132b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project# distributed under the License is distributed on an "AS IS" BASIS,
142b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
152b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project# See the License for the specific language governing permissions and
162b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project# limitations under the License.
172b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
182b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project"""Module that assists in parsing the output of "am instrument" commands run on
192b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Projectthe device."""
202b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
212b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Projectimport re
222b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Projectimport string
232b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
242b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
252b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Projectdef ParseAmInstrumentOutput(result):
262b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  """Given the raw output of an "am instrument" command that targets and
272b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  InstrumentationTestRunner, return structured data.
282b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
292b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  Args:
302b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    result (string): Raw output of "am instrument"
312b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
322b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  Return
332b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  (test_results, inst_finished_bundle)
342b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
352b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  test_results (list of am_output_parser.TestResult)
362b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  inst_finished_bundle (dict): Key/value pairs contained in the bundle that is
372b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return
382b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    code of the Instrumentation process, any error codes reported by the
392b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    activity manager, and any results explicity added by the instrumentation
402b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    code.
412b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  """
422b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
432b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  re_status_code = re.compile(r'INSTRUMENTATION_STATUS_CODE: (?P<status_code>-?\d)$')
442b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  test_results = []
452b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  inst_finished_bundle = {}
462b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
472b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  result_block_string = ""
482b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  for line in result.splitlines():
492b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    result_block_string += line + '\n'
502b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
512b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    if "INSTRUMENTATION_STATUS_CODE:" in line:
522b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      test_result = TestResult(result_block_string)
532b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      if test_result.GetStatusCode() == 1: # The test started
542b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project        pass
552b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      elif test_result.GetStatusCode() in [0, -1, -2]:
562b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project        test_results.append(test_result)
572b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      else:
582b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project        pass
592b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      result_block_string = ""
602b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    if "INSTRUMENTATION_CODE:" in line:
612b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      inst_finished_bundle = _ParseInstrumentationFinishedBundle(result_block_string)
622b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      result_block_string = ""
632b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
642b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  return (test_results, inst_finished_bundle)
652b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
662b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
672b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Projectdef _ParseInstrumentationFinishedBundle(result):
682b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  """Given the raw output of "am instrument" returns a dictionary of the
692b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  key/value pairs from the bundle passed into
702b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  ActivityManager.finishInstrumentation().
712b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
722b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  Args:
732b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    result (string): Raw output of "am instrument"
742b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
752b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  Return:
762b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  inst_finished_bundle (dict): Key/value pairs contained in the bundle that is
772b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return
782b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    code of the Instrumentation process, any error codes reported by the
792b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    activity manager, and any results explicity added by the instrumentation
802b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    code.
812b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  """
822b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
8316eac2a6dcba235179003e399d0df14acb50fea6Jack Wang  re_result = re.compile(r'INSTRUMENTATION_RESULT: ([^=]+)=(.*)$')
842b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  re_code = re.compile(r'INSTRUMENTATION_CODE: (\-?\d)$')
852b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  result_dict = {}
862b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  key = ''
872b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  val = ''
882b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  last_tag = ''
892b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
902b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  for line in result.split('\n'):
912b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    line = line.strip(string.whitespace)
922b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    if re_result.match(line):
932b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      last_tag = 'INSTRUMENTATION_RESULT'
942b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      key = re_result.search(line).group(1).strip(string.whitespace)
952b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      if key.startswith('performance.'):
962b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project        key = key[len('performance.'):]
972b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      val = re_result.search(line).group(2).strip(string.whitespace)
982b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      try:
992b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project        result_dict[key] = float(val)
1002b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      except ValueError:
1012b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project        result_dict[key] = val
1022b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      except TypeError:
1032b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project        result_dict[key] = val
1042b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    elif re_code.match(line):
1052b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      last_tag = 'INSTRUMENTATION_CODE'
1062b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      key = 'code'
1072b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      val = re_code.search(line).group(1).strip(string.whitespace)
1082b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      result_dict[key] = val
1092b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    elif 'INSTRUMENTATION_ABORTED:' in line:
1102b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      last_tag = 'INSTRUMENTATION_ABORTED'
1112b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      key = 'INSTRUMENTATION_ABORTED'
1122b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      val = ''
1132b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      result_dict[key] = val
1142b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    elif last_tag == 'INSTRUMENTATION_RESULT':
1152b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      result_dict[key] += '\n' + line
1162b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
1172b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  if not result_dict.has_key('code'):
1182b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    result_dict['code'] = '0'
1192b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    result_dict['shortMsg'] = "No result returned from instrumentation"
1202b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
1212b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  return result_dict
1222b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
1232b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
1242b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Projectclass TestResult(object):
1252b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  """A class that contains information about a single test result."""
1262b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
1272b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  def __init__(self, result_block_string):
1282b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    """
1292b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    Args:
1302b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      result_block_string (string): Is a single "block" of output. A single
1312b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      "block" would be either a "test started" status report, or a "test
1322b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project      finished" status report.
1332b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    """
1342b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
1352b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    self._test_name = None
1362b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    self._status_code = None
1372b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    self._failure_reason = None
13816eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    self._fields_map = {}
1392b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
14016eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    re_status_code = re.search(r'INSTRUMENTATION_STATUS_CODE: '
14116eac2a6dcba235179003e399d0df14acb50fea6Jack Wang        '(?P<status_code>1|0|-1|-2)', result_block_string)
14216eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    re_fields = re.compile(r'INSTRUMENTATION_STATUS: '
14316eac2a6dcba235179003e399d0df14acb50fea6Jack Wang        '(?P<key>[\w.]+)=(?P<value>.*?)(?=\nINSTRUMENTATION_STATUS)', re.DOTALL)
14416eac2a6dcba235179003e399d0df14acb50fea6Jack Wang
14516eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    for field in re_fields.finditer(result_block_string):
14616eac2a6dcba235179003e399d0df14acb50fea6Jack Wang      key, value = (field.group('key').strip(), field.group('value').strip())
14716eac2a6dcba235179003e399d0df14acb50fea6Jack Wang      if key.startswith('performance.'):
14816eac2a6dcba235179003e399d0df14acb50fea6Jack Wang        key = key[len('performance.'):]
14916eac2a6dcba235179003e399d0df14acb50fea6Jack Wang      self._fields_map[key] = value
15016eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    self._fields_map.setdefault('class')
15116eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    self._fields_map.setdefault('test')
15216eac2a6dcba235179003e399d0df14acb50fea6Jack Wang
15316eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    self._test_name = '%s:%s' % (self._fields_map['class'],
15416eac2a6dcba235179003e399d0df14acb50fea6Jack Wang                                 self._fields_map['test'])
15516eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    self._status_code = int(re_status_code.group('status_code'))
15616eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    if 'stack' in self._fields_map:
15716eac2a6dcba235179003e399d0df14acb50fea6Jack Wang      self._failure_reason = self._fields_map['stack']
1582b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
1592b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  def GetTestName(self):
1602b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    return self._test_name
1612b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
1622b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  def GetStatusCode(self):
1632b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    return self._status_code
1642b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project
1652b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project  def GetFailureReason(self):
1662b83cbdb14fcf0307e87b67d46ba17aab4c22a28The Android Open Source Project    return self._failure_reason
16716eac2a6dcba235179003e399d0df14acb50fea6Jack Wang
16816eac2a6dcba235179003e399d0df14acb50fea6Jack Wang  def GetResultFields(self):
16916eac2a6dcba235179003e399d0df14acb50fea6Jack Wang    return self._fields_map
170