| OLD | NEW | 
|    1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |    1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|    2 # Use of this source code is governed by a BSD-style license that can be |    2 # Use of this source code is governed by a BSD-style license that can be | 
|    3 # found in the LICENSE file. |    3 # found in the LICENSE file. | 
|    4  |    4  | 
|    5 """Gathers information about APKs.""" |    5 """Gathers information about JARs such as test methods and annotations.""" | 
|    6  |    6  | 
|    7 import collections |    7 import collections | 
|    8 import logging |    8 import logging | 
|    9 import os |    9 import os | 
|   10 import pickle |   10 import pickle | 
|   11 import re |   11 import re | 
|   12  |   12  | 
|   13 import cmd_helper |   13 # TODO(frankf): Move cmd_helper to utils. | 
 |   14 from pylib import cmd_helper | 
|   14  |   15  | 
|   15 # If you change the cached output of proguard, increment this number |   16 # If you change the cached output of proguard, increment this number | 
|   16 PICKLE_FORMAT_VERSION = 1 |   17 PICKLE_FORMAT_VERSION = 1 | 
|   17  |   18  | 
|   18 def GetPackageNameForApk(apk_path): |  | 
|   19   """Returns the package name of the apk file.""" |  | 
|   20   aapt_output = cmd_helper.GetCmdOutput( |  | 
|   21       ['aapt', 'dump', 'badging', apk_path]).split('\n') |  | 
|   22   package_name_re = re.compile(r'package: .*name=\'(\S*)\'') |  | 
|   23   for line in aapt_output: |  | 
|   24     m = package_name_re.match(line) |  | 
|   25     if m: |  | 
|   26       return m.group(1) |  | 
|   27   raise Exception('Failed to determine package name of %s' % apk_path) |  | 
|   28  |   19  | 
 |   20 class JarInfo(object): | 
 |   21   """Helper class for inspecting Jars.""" | 
|   29  |   22  | 
|   30 class ApkInfo(object): |   23   def __init__(self, jar_path): | 
|   31   """Helper class for inspecting APKs.""" |  | 
|   32  |  | 
|   33   def __init__(self, apk_path, jar_path): |  | 
|   34     self._PROGUARD_PATH = os.path.join(os.environ['ANDROID_SDK_ROOT'], |   24     self._PROGUARD_PATH = os.path.join(os.environ['ANDROID_SDK_ROOT'], | 
|   35                                        'tools/proguard/bin/proguard.sh') |   25                                        'tools/proguard/bin/proguard.sh') | 
|   36     if not os.path.exists(self._PROGUARD_PATH): |   26     if not os.path.exists(self._PROGUARD_PATH): | 
|   37       self._PROGUARD_PATH = os.path.join(os.environ['ANDROID_BUILD_TOP'], |   27       self._PROGUARD_PATH = os.path.join(os.environ['ANDROID_BUILD_TOP'], | 
|   38                                          'external/proguard/bin/proguard.sh') |   28                                          'external/proguard/bin/proguard.sh') | 
|   39     self._PROGUARD_CLASS_RE = re.compile(r'\s*?- Program class:\s*([\S]+)$') |   29     self._PROGUARD_CLASS_RE = re.compile(r'\s*?- Program class:\s*([\S]+)$') | 
|   40     self._PROGUARD_METHOD_RE = re.compile(r'\s*?- Method:\s*(\S*)[(].*$') |   30     self._PROGUARD_METHOD_RE = re.compile(r'\s*?- Method:\s*(\S*)[(].*$') | 
|   41     self._PROGUARD_ANNOTATION_RE = re.compile(r'\s*?- Annotation \[L(\S*);\]:$') |   31     self._PROGUARD_ANNOTATION_RE = re.compile(r'\s*?- Annotation \[L(\S*);\]:$') | 
|   42     self._PROGUARD_ANNOTATION_CONST_RE = ( |   32     self._PROGUARD_ANNOTATION_CONST_RE = ( | 
|   43         re.compile(r'\s*?- Constant element value.*$')) |   33         re.compile(r'\s*?- Constant element value.*$')) | 
|   44     self._PROGUARD_ANNOTATION_VALUE_RE = re.compile(r'\s*?- \S+? \[(.*)\]$') |   34     self._PROGUARD_ANNOTATION_VALUE_RE = re.compile(r'\s*?- \S+? \[(.*)\]$') | 
|   45  |   35  | 
|   46     if not os.path.exists(apk_path): |  | 
|   47       raise Exception('%s not found, please build it' % apk_path) |  | 
|   48     self._apk_path = apk_path |  | 
|   49     if not os.path.exists(jar_path): |   36     if not os.path.exists(jar_path): | 
|   50       raise Exception('%s not found, please build it' % jar_path) |   37       raise Exception('%s not found, please build it' % jar_path) | 
|   51     self._jar_path = jar_path |   38     self._jar_path = jar_path | 
|   52     self._annotation_map = collections.defaultdict(list) |   39     self._annotation_map = collections.defaultdict(list) | 
|   53     self._pickled_proguard_name = self._jar_path + '-proguard.pickle' |   40     self._pickled_proguard_name = self._jar_path + '-proguard.pickle' | 
|   54     self._test_methods = [] |   41     self._test_methods = [] | 
|   55     self._Initialize() |  | 
|   56  |  | 
|   57   def _Initialize(self): |  | 
|   58     if not self._GetCachedProguardData(): |   42     if not self._GetCachedProguardData(): | 
|   59       self._GetProguardData() |   43       self._GetProguardData() | 
|   60  |   44  | 
|   61   def _GetCachedProguardData(self): |   45   def _GetCachedProguardData(self): | 
|   62     if (os.path.exists(self._pickled_proguard_name) and |   46     if (os.path.exists(self._pickled_proguard_name) and | 
|   63         (os.path.getmtime(self._pickled_proguard_name) > |   47         (os.path.getmtime(self._pickled_proguard_name) > | 
|   64          os.path.getmtime(self._jar_path))): |   48          os.path.getmtime(self._jar_path))): | 
|   65       logging.info('Loading cached proguard output from %s', |   49       logging.info('Loading cached proguard output from %s', | 
|   66                    self._pickled_proguard_name) |   50                    self._pickled_proguard_name) | 
|   67       try: |   51       try: | 
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  135     with open(self._pickled_proguard_name, 'w') as f: |  119     with open(self._pickled_proguard_name, 'w') as f: | 
|  136       f.write(pickle.dumps(d)) |  120       f.write(pickle.dumps(d)) | 
|  137  |  121  | 
|  138   def _GetAnnotationMap(self): |  122   def _GetAnnotationMap(self): | 
|  139     return self._annotation_map |  123     return self._annotation_map | 
|  140  |  124  | 
|  141   def _IsTestMethod(self, test): |  125   def _IsTestMethod(self, test): | 
|  142     class_name, method = test.split('#') |  126     class_name, method = test.split('#') | 
|  143     return class_name.endswith('Test') and method.startswith('test') |  127     return class_name.endswith('Test') and method.startswith('test') | 
|  144  |  128  | 
|  145   def GetApkPath(self): |  | 
|  146     return self._apk_path |  | 
|  147  |  | 
|  148   def GetPackageName(self): |  | 
|  149     """Returns the package name of this APK.""" |  | 
|  150     return GetPackageNameForApk(self._apk_path) |  | 
|  151  |  | 
|  152   def GetTestAnnotations(self, test): |  129   def GetTestAnnotations(self, test): | 
|  153     """Returns a list of all annotations for the given |test|. May be empty.""" |  130     """Returns a list of all annotations for the given |test|. May be empty.""" | 
|  154     if not self._IsTestMethod(test): |  131     if not self._IsTestMethod(test): | 
|  155       return [] |  132       return [] | 
|  156     return self._GetAnnotationMap()[test] |  133     return self._GetAnnotationMap()[test] | 
|  157  |  134  | 
|  158   def _AnnotationsMatchFilters(self, annotation_filter_list, annotations): |  135   def _AnnotationsMatchFilters(self, annotation_filter_list, annotations): | 
|  159     """Checks if annotations match any of the filters.""" |  136     """Checks if annotations match any of the filters.""" | 
|  160     if not annotation_filter_list: |  137     if not annotation_filter_list: | 
|  161       return True |  138       return True | 
|  162     for annotation_filter in annotation_filter_list: |  139     for annotation_filter in annotation_filter_list: | 
|  163       filters = annotation_filter.split('=') |  140       filters = annotation_filter.split('=') | 
|  164       if len(filters) == 2: |  141       if len(filters) == 2: | 
|  165         key = filters[0] |  142         key = filters[0] | 
|  166         value_list = filters[1].split(',') |  143         value_list = filters[1].split(',') | 
|  167         for value in value_list: |  144         for value in value_list: | 
|  168           if key + ':' + value in annotations: |  145           if key + ':' + value in annotations: | 
|  169             return True |  146             return True | 
|  170       elif annotation_filter in annotations: |  147       elif annotation_filter in annotations: | 
|  171         return True |  148         return True | 
|  172     return False |  149     return False | 
|  173  |  150  | 
|  174   def GetAnnotatedTests(self, annotation_filter_list): |  151   def GetAnnotatedTests(self, annotation_filter_list): | 
|  175     """Returns a list of all tests that match the given annotation filters.""" |  152     """Returns a list of all tests that match the given annotation filters.""" | 
|  176     return [test for test, annotations in self._GetAnnotationMap().iteritems() |  153     return [test for test, annotations in self._GetAnnotationMap().iteritems() | 
|  177             if self._IsTestMethod(test) and self._AnnotationsMatchFilters( |  154             if self._IsTestMethod(test) and self._AnnotationsMatchFilters( | 
|  178                 annotation_filter_list, annotations)] |  155                 annotation_filter_list, annotations)] | 
|  179  |  156  | 
|  180   def GetTestMethods(self): |  157   def GetTestMethods(self): | 
|  181     """Returns a list of all test methods in this apk as Class#testMethod.""" |  158     """Returns a list of all test methods in this jar as Class#testMethod.""" | 
|  182     return self._test_methods |  159     return self._test_methods | 
|  183  |  160  | 
|  184   @staticmethod |  161   @staticmethod | 
|  185   def IsPythonDrivenTest(test): |  162   def IsPythonDrivenTest(test): | 
|  186     return 'pythonDrivenTests' in test |  163     return 'pythonDrivenTests' in test | 
| OLD | NEW |