#!/usr/bin/python2.4 # # # Copyright 2008, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Module that assists in parsing the output of "am instrument" commands run on the device.""" import re import string def ParseAmInstrumentOutput(result): """Given the raw output of an "am instrument" command that targets and InstrumentationTestRunner, return structured data. Args: result (string): Raw output of "am instrument" Return (test_results, inst_finished_bundle) test_results (list of am_output_parser.TestResult) inst_finished_bundle (dict): Key/value pairs contained in the bundle that is passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return code of the Instrumentation process, any error codes reported by the activity manager, and any results explicity added by the instrumentation code. """ re_status_code = re.compile(r'INSTRUMENTATION_STATUS_CODE: (?P<status_code>-?\d)$') test_results = [] inst_finished_bundle = {} result_block_string = "" for line in result.splitlines(): result_block_string += line + '\n' if "INSTRUMENTATION_STATUS_CODE:" in line: test_result = TestResult(result_block_string) if test_result.GetStatusCode() == 1: # The test started pass elif test_result.GetStatusCode() in [0, -1, -2]: test_results.append(test_result) else: pass result_block_string = "" if "INSTRUMENTATION_CODE:" in line: inst_finished_bundle = _ParseInstrumentationFinishedBundle(result_block_string) result_block_string = "" return (test_results, inst_finished_bundle) def _ParseInstrumentationFinishedBundle(result): """Given the raw output of "am instrument" returns a dictionary of the key/value pairs from the bundle passed into ActivityManager.finishInstrumentation(). Args: result (string): Raw output of "am instrument" Return: inst_finished_bundle (dict): Key/value pairs contained in the bundle that is passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return code of the Instrumentation process, any error codes reported by the activity manager, and any results explicity added by the instrumentation code. """ re_result = re.compile(r'INSTRUMENTATION_RESULT: ([^=]+)=(.*)$') re_code = re.compile(r'INSTRUMENTATION_CODE: (\-?\d)$') result_dict = {} key = '' val = '' last_tag = '' for line in result.split('\n'): line = line.strip(string.whitespace) if re_result.match(line): last_tag = 'INSTRUMENTATION_RESULT' key = re_result.search(line).group(1).strip(string.whitespace) if key.startswith('performance.'): key = key[len('performance.'):] val = re_result.search(line).group(2).strip(string.whitespace) try: result_dict[key] = float(val) except ValueError: result_dict[key] = val except TypeError: result_dict[key] = val elif re_code.match(line): last_tag = 'INSTRUMENTATION_CODE' key = 'code' val = re_code.search(line).group(1).strip(string.whitespace) result_dict[key] = val elif 'INSTRUMENTATION_ABORTED:' in line: last_tag = 'INSTRUMENTATION_ABORTED' key = 'INSTRUMENTATION_ABORTED' val = '' result_dict[key] = val elif last_tag == 'INSTRUMENTATION_RESULT': result_dict[key] += '\n' + line if not result_dict.has_key('code'): result_dict['code'] = '0' result_dict['shortMsg'] = "No result returned from instrumentation" return result_dict class TestResult(object): """A class that contains information about a single test result.""" def __init__(self, result_block_string): """ Args: result_block_string (string): Is a single "block" of output. A single "block" would be either a "test started" status report, or a "test finished" status report. """ self._test_name = None self._status_code = None self._failure_reason = None self._fields_map = {} re_status_code = re.search(r'INSTRUMENTATION_STATUS_CODE: ' '(?P<status_code>1|0|-1|-2)', result_block_string) re_fields = re.compile(r'INSTRUMENTATION_STATUS: ' '(?P<key>[\w.]+)=(?P<value>.*?)(?=\nINSTRUMENTATION_STATUS)', re.DOTALL) for field in re_fields.finditer(result_block_string): key, value = (field.group('key').strip(), field.group('value').strip()) if key.startswith('performance.'): key = key[len('performance.'):] self._fields_map[key] = value self._fields_map.setdefault('class') self._fields_map.setdefault('test') self._test_name = '%s:%s' % (self._fields_map['class'], self._fields_map['test']) self._status_code = int(re_status_code.group('status_code')) if 'stack' in self._fields_map: self._failure_reason = self._fields_map['stack'] def GetTestName(self): return self._test_name def GetStatusCode(self): return self._status_code def GetFailureReason(self): return self._failure_reason def GetResultFields(self): return self._fields_map