OLD | NEW |
(Empty) | |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 import logging |
| 6 import re |
| 7 |
| 8 # http://developer.android.com/reference/android/test/InstrumentationTestRunner.
html |
| 9 STATUS_CODE_START = 1 |
| 10 STATUS_CODE_OK = 0 |
| 11 STATUS_CODE_ERROR = -1 |
| 12 STATUS_CODE_FAILURE = -2 |
| 13 |
| 14 # http://developer.android.com/reference/android/app/Activity.html |
| 15 RESULT_CODE_OK = -1 |
| 16 RESULT_CODE_CANCELED = 0 |
| 17 |
| 18 _INSTR_LINE_RE = re.compile('^\s*INSTRUMENTATION_([A-Z_]+): (.*)$') |
| 19 |
| 20 |
| 21 class InstrumentationParser(object): |
| 22 |
| 23 def __init__(self, stream): |
| 24 """An incremental parser for the output of Android instrumentation tests. |
| 25 |
| 26 Example: |
| 27 |
| 28 stream = adb.IterShell('am instrument -r ...') |
| 29 parser = InstrumentationParser(stream) |
| 30 |
| 31 for code, bundle in parser.IterStatus(): |
| 32 # do something with each instrumentation status |
| 33 print 'status:', code, bundle |
| 34 |
| 35 # do something with the final instrumentation result |
| 36 code, bundle = parser.GetResult() |
| 37 print 'result:', code, bundle |
| 38 |
| 39 Args: |
| 40 stream: a sequence of lines as produced by the raw output of an |
| 41 instrumentation test (e.g. by |am instrument -r| or |uiautomator|). |
| 42 """ |
| 43 self._stream = stream |
| 44 self._code = None |
| 45 self._bundle = None |
| 46 |
| 47 def IterStatus(self): |
| 48 """Iterate over statuses as they are produced by the instrumentation test. |
| 49 |
| 50 Yields: |
| 51 A tuple (code, bundle) for each instrumentation status found in the |
| 52 output. |
| 53 """ |
| 54 def join_bundle_values(bundle): |
| 55 for key in bundle: |
| 56 bundle[key] = '\n'.join(bundle[key]) |
| 57 return bundle |
| 58 |
| 59 bundle = {'STATUS': {}, 'RESULT': {}} |
| 60 header = None |
| 61 key = None |
| 62 for line in self._stream: |
| 63 m = _INSTR_LINE_RE.match(line) |
| 64 if m: |
| 65 header, value = m.groups() |
| 66 key = None |
| 67 if header in ['STATUS', 'RESULT'] and '=' in value: |
| 68 key, value = value.split('=', 1) |
| 69 bundle[header][key] = [value] |
| 70 elif header == 'STATUS_CODE': |
| 71 yield int(value), join_bundle_values(bundle['STATUS']) |
| 72 bundle['STATUS'] = {} |
| 73 elif header == 'CODE': |
| 74 self._code = int(value) |
| 75 else: |
| 76 logging.warning('Unknown INSTRUMENTATION_%s line: %s', header, value) |
| 77 elif key is not None: |
| 78 bundle[header][key].append(line) |
| 79 |
| 80 self._bundle = join_bundle_values(bundle['RESULT']) |
| 81 |
| 82 def GetResult(self): |
| 83 """Return the final instrumentation result. |
| 84 |
| 85 Returns: |
| 86 A pair (code, bundle) with the final instrumentation result. The |code| |
| 87 may be None if no instrumentation result was found in the output. |
| 88 |
| 89 Raises: |
| 90 AssertionError if attempting to get the instrumentation result before |
| 91 exhausting |IterStatus| first. |
| 92 """ |
| 93 assert self._bundle is not None, ( |
| 94 'The IterStatus generator must be exhausted before reading the final' |
| 95 ' instrumentation result.') |
| 96 return self._code, self._bundle |
OLD | NEW |