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
3 # found in the LICENSE file.
5 """Gathers information about APKs."""
14 class ApkInfo(object):
15 """Helper class for inspecting APKs."""
16 _PROGUARD_PATH
= os
.path
.join(os
.environ
['ANDROID_SDK_ROOT'],
17 'tools/proguard/bin/proguard.sh')
18 if not os
.path
.exists(_PROGUARD_PATH
):
19 _PROGUARD_PATH
= os
.path
.join(os
.environ
['ANDROID_BUILD_TOP'],
20 'external/proguard/bin/proguard.sh')
21 _PROGUARD_CLASS_RE
= re
.compile(r
'\s*?- Program class:\s*([\S]+)$')
22 _PROGUARD_METHOD_RE
= re
.compile(r
'\s*?- Method:\s*(\S*)[(].*$')
23 _PROGUARD_ANNOTATION_RE
= re
.compile(r
'\s*?- Annotation \[L(\S*);\]:$')
24 _PROGUARD_ANNOTATION_CONST_RE
= re
.compile(r
'\s*?- Constant element value.*$')
25 _PROGUARD_ANNOTATION_VALUE_RE
= re
.compile(r
'\s*?- \S+? \[(.*)\]$')
26 _AAPT_PACKAGE_NAME_RE
= re
.compile(r
'package: .*name=\'(\S
*)\'')
28 def __init__(self, apk_path, jar_path):
29 if not os.path.exists(apk_path):
30 raise Exception('%s not found
, please build it
' % apk_path)
31 self._apk_path = apk_path
32 if not os.path.exists(jar_path):
33 raise Exception('%s not found
, please build it
' % jar_path)
34 self._jar_path = jar_path
35 self._annotation_map = collections.defaultdict(list)
36 self._test_methods = []
39 def _Initialize(self):
40 proguard_output = cmd_helper.GetCmdOutput([self._PROGUARD_PATH,
41 '-injars
', self._jar_path,
52 qualified_method = None
53 for line in proguard_output:
54 m = self._PROGUARD_CLASS_RE.match(line)
56 clazz = m.group(1).replace('/', '.') # Change package delim.
59 m = self._PROGUARD_METHOD_RE.match(line)
63 qualified_method = clazz + '#' + method
64 if method
.startswith('test') and clazz
.endswith('Test'):
65 self
._test
_methods
+= [qualified_method
]
67 m
= self
._PROGUARD
_ANNOTATION
_RE
.match(line
)
69 assert qualified_method
70 annotation
= m
.group(1).split('/')[-1] # Ignore the annotation package.
71 self
._annotation
_map
[qualified_method
].append(annotation
)
75 assert qualified_method
77 m
= self
._PROGUARD
_ANNOTATION
_CONST
_RE
.match(line
)
81 m
= self
._PROGUARD
_ANNOTATION
_VALUE
_RE
.match(line
)
84 self
._annotation
_map
[qualified_method
].append(
85 annotation
+ ':' + value
)
88 def _GetAnnotationMap(self
):
89 return self
._annotation
_map
91 def _IsTestMethod(self
, test
):
92 class_name
, method
= test
.split('#')
93 return class_name
.endswith('Test') and method
.startswith('test')
98 def GetPackageName(self
):
99 """Returns the package name of this APK."""
100 aapt_output
= cmd_helper
.GetCmdOutput(
101 ['aapt', 'dump', 'badging', self
._apk
_path
]).split('\n')
102 for line
in aapt_output
:
103 m
= self
._AAPT
_PACKAGE
_NAME
_RE
.match(line
)
106 raise Exception('Failed to determine package name of %s' % self
._apk
_path
)
108 def GetTestAnnotations(self
, test
):
109 """Returns a list of all annotations for the given |test|. May be empty."""
110 if not self
._IsTestMethod
(test
):
112 return self
._GetAnnotationMap
()[test
]
114 def _AnnotationsMatchFilters(self
, annotation_filter_list
, annotations
):
115 """Checks if annotations match any of the filters."""
116 if not annotation_filter_list
:
118 for annotation_filter
in annotation_filter_list
:
119 filters
= annotation_filter
.split('=')
120 if len(filters
) == 2:
122 value_list
= filters
[1].split(',')
123 for value
in value_list
:
124 if key
+ ':' + value
in annotations
:
126 elif annotation_filter
in annotations
:
130 def GetAnnotatedTests(self
, annotation_filter_list
):
131 """Returns a list of all tests that match the given annotation filters."""
132 return [test
for test
, annotations
in self
._GetAnnotationMap
().iteritems()
133 if self
._IsTestMethod
(test
) and self
._AnnotationsMatchFilters
(
134 annotation_filter_list
, annotations
)]
136 def GetTestMethods(self
):
137 """Returns a list of all test methods in this apk as Class#testMethod."""
138 return self
._test
_methods
141 def IsPythonDrivenTest(test
):
142 return 'pythonDrivenTests' in test