Home | History | Annotate | Download | only in testrunner
      1 #!/usr/bin/python2.4
      2 #
      3 #
      4 # Copyright 2008, The Android Open Source Project
      5 #
      6 # Licensed under the Apache License, Version 2.0 (the "License");
      7 # you may not use this file except in compliance with the License.
      8 # You may obtain a copy of the License at
      9 #
     10 #     http://www.apache.org/licenses/LICENSE-2.0
     11 #
     12 # Unless required by applicable law or agreed to in writing, software
     13 # distributed under the License is distributed on an "AS IS" BASIS,
     14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 # See the License for the specific language governing permissions and
     16 # limitations under the License.
     17 
     18 """Module that assists in parsing the output of "am instrument" commands run on
     19 the device."""
     20 
     21 import re
     22 import string
     23 
     24 
     25 def ParseAmInstrumentOutput(result):
     26   """Given the raw output of an "am instrument" command that targets and
     27   InstrumentationTestRunner, return structured data.
     28 
     29   Args:
     30     result (string): Raw output of "am instrument"
     31 
     32   Return
     33   (test_results, inst_finished_bundle)
     34   
     35   test_results (list of am_output_parser.TestResult)
     36   inst_finished_bundle (dict): Key/value pairs contained in the bundle that is
     37     passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return
     38     code of the Instrumentation process, any error codes reported by the
     39     activity manager, and any results explicity added by the instrumentation
     40     code.
     41   """
     42 
     43   re_status_code = re.compile(r'INSTRUMENTATION_STATUS_CODE: (?P<status_code>-?\d)$')
     44   test_results = []
     45   inst_finished_bundle = {}
     46 
     47   result_block_string = ""
     48   for line in result.splitlines():
     49     result_block_string += line + '\n'
     50 
     51     if "INSTRUMENTATION_STATUS_CODE:" in line:
     52       test_result = TestResult(result_block_string)
     53       if test_result.GetStatusCode() == 1: # The test started
     54         pass
     55       elif test_result.GetStatusCode() in [0, -1, -2]:
     56         test_results.append(test_result)
     57       else:
     58         pass
     59       result_block_string = ""
     60     if "INSTRUMENTATION_CODE:" in line:
     61       inst_finished_bundle = _ParseInstrumentationFinishedBundle(result_block_string)
     62       result_block_string = ""
     63 
     64   return (test_results, inst_finished_bundle)
     65 
     66 
     67 def _ParseInstrumentationFinishedBundle(result):
     68   """Given the raw output of "am instrument" returns a dictionary of the
     69   key/value pairs from the bundle passed into 
     70   ActivityManager.finishInstrumentation().
     71 
     72   Args:
     73     result (string): Raw output of "am instrument"
     74 
     75   Return:
     76   inst_finished_bundle (dict): Key/value pairs contained in the bundle that is
     77     passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return
     78     code of the Instrumentation process, any error codes reported by the
     79     activity manager, and any results explicity added by the instrumentation
     80     code.
     81   """
     82 
     83   re_result = re.compile(r'INSTRUMENTATION_RESULT: ([^=]+)=(.*)$')
     84   re_code = re.compile(r'INSTRUMENTATION_CODE: (\-?\d)$')
     85   result_dict = {}
     86   key = ''
     87   val = ''
     88   last_tag = ''
     89 
     90   for line in result.split('\n'):
     91     line = line.strip(string.whitespace)
     92     if re_result.match(line):
     93       last_tag = 'INSTRUMENTATION_RESULT'
     94       key = re_result.search(line).group(1).strip(string.whitespace)
     95       if key.startswith('performance.'):
     96         key = key[len('performance.'):]
     97       val = re_result.search(line).group(2).strip(string.whitespace)
     98       try:
     99         result_dict[key] = float(val)
    100       except ValueError:
    101         result_dict[key] = val
    102       except TypeError:
    103         result_dict[key] = val
    104     elif re_code.match(line):
    105       last_tag = 'INSTRUMENTATION_CODE'
    106       key = 'code'
    107       val = re_code.search(line).group(1).strip(string.whitespace)
    108       result_dict[key] = val
    109     elif 'INSTRUMENTATION_ABORTED:' in line:
    110       last_tag = 'INSTRUMENTATION_ABORTED'
    111       key = 'INSTRUMENTATION_ABORTED'
    112       val = ''
    113       result_dict[key] = val
    114     elif last_tag == 'INSTRUMENTATION_RESULT':
    115       result_dict[key] += '\n' + line
    116 
    117   if not result_dict.has_key('code'):
    118     result_dict['code'] = '0'
    119     result_dict['shortMsg'] = "No result returned from instrumentation"
    120 
    121   return result_dict
    122 
    123 
    124 class TestResult(object):
    125   """A class that contains information about a single test result."""
    126 
    127   def __init__(self, result_block_string):
    128     """
    129     Args:
    130       result_block_string (string): Is a single "block" of output. A single
    131       "block" would be either a "test started" status report, or a "test
    132       finished" status report.
    133     """
    134 
    135     self._test_name = None
    136     self._status_code = None
    137     self._failure_reason = None
    138     self._fields_map = {}
    139 
    140     re_status_code = re.search(r'INSTRUMENTATION_STATUS_CODE: '
    141         '(?P<status_code>1|0|-1|-2)', result_block_string)
    142     re_fields = re.compile(r'INSTRUMENTATION_STATUS: '
    143         '(?P<key>[\w.]+)=(?P<value>.*?)(?=\nINSTRUMENTATION_STATUS)', re.DOTALL)
    144 
    145     for field in re_fields.finditer(result_block_string):
    146       key, value = (field.group('key').strip(), field.group('value').strip())
    147       if key.startswith('performance.'):
    148         key = key[len('performance.'):]
    149       self._fields_map[key] = value
    150     self._fields_map.setdefault('class')
    151     self._fields_map.setdefault('test')
    152 
    153     self._test_name = '%s:%s' % (self._fields_map['class'],
    154                                  self._fields_map['test'])
    155     self._status_code = int(re_status_code.group('status_code'))
    156     if 'stack' in self._fields_map:
    157       self._failure_reason = self._fields_map['stack']
    158 
    159   def GetTestName(self):
    160     return self._test_name
    161 
    162   def GetStatusCode(self):
    163     return self._status_code
    164 
    165   def GetFailureReason(self):
    166     return self._failure_reason
    167 
    168   def GetResultFields(self):
    169     return self._fields_map
    170