| #!/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 |