blob: 7ade73976edf69c930f08546d4ae2d0f5b02b2c2 [file] [log] [blame]
jingwen741dbc02017-12-19 09:30:45 -08001# pylint: disable=g-direct-third-party-import
2# Copyright 2017 The Bazel Authors. All rights reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""AndroidManifest checks for android_instrumentation_test.
16
17Ensures that the targetPackage of the instrumentation APK references
18the correct target package name.
19"""
20
21import os
22import sys
23
24import xml.etree.ElementTree as ET
25from third_party.py import gflags
26
27gflags.DEFINE_string("instrumentation_manifest", None,
28 "AndroidManifest.xml of the instrumentation APK")
29gflags.DEFINE_string("target_manifest", None,
30 "AndroidManifest.xml of the target APK")
31gflags.DEFINE_string("output", None, "Output of the check")
32
33FLAGS = gflags.FLAGS
34
35
36class ManifestError(Exception):
37 """Raised when there is a problem with an AndroidManifest.xml."""
38
39
40# There might be more than one <instrumentation> tag to use different
41# test runners, so we need to extract the targetPackage attribute values
42# from all of them and check that they are the same.
43def _ExtractTargetPackageToInstrument(xml_content, path):
44 """Extract the targetPackage value from the <instrumentation> tag."""
45
46 # https://developer.android.com/guide/topics/manifest/manifest-element.html
47 # xmlns:android is the required namespace in an Android manifest.
48 tree = ET.ElementTree(ET.fromstring(xml_content))
49 package_key = "{http://schemas.android.com/apk/res/android}targetPackage"
50 instrumentation_elems = tree.iterfind(
51 ".//instrumentation[@{0}]".format(package_key))
52
53 package_names = set(e.attrib[package_key] for e in instrumentation_elems)
54
55 if not package_names:
56 raise ManifestError("No <instrumentation> tag containing "
57 "the targetPackage attribute is found in the "
58 "manifest at %s" % path)
59
60 if len(package_names) > 1:
61 raise ManifestError(
62 "The <instrumentation> tags in the manifest at %s do not "
63 "reference the same target package: %s" % (path, list(package_names)))
64
65 return package_names.pop()
66
67
68def _ExtractTargetPackageName(xml_content, path):
69 """Extract the package name value from the root <manifest> tag."""
70 tree = ET.ElementTree(ET.fromstring(xml_content))
71 root = tree.getroot()
72 if "package" in root.attrib:
73 return root.attrib["package"]
74 else:
75 raise ManifestError("The <manifest> tag in the manifest at %s needs to "
76 "specify the package name using the 'package' "
77 "attribute." % path)
78
79
80def _ValidateManifestPackageNames(instr_manifest_content, instr_manifest_path,
81 target_manifest_content,
82 target_manifest_path):
83 """Diff the package names and throw a ManifestError if not identical."""
84 target_package_to_instrument = _ExtractTargetPackageToInstrument(
85 instr_manifest_content, instr_manifest_path)
86 target_package_name = _ExtractTargetPackageName(target_manifest_content,
87 target_manifest_path)
88
89 if target_package_to_instrument != target_package_name:
90 raise ManifestError(
91 "The targetPackage specified in the instrumentation manifest at "
92 "{instr_manifest_path} ({target_package_to_instrument}) does not match "
93 "the package name of the target manifest at {target_manifest_path} "
94 "({target_package_name})".format(
95 instr_manifest_path=instr_manifest_path,
96 target_package_to_instrument=target_package_to_instrument,
97 target_manifest_path=target_manifest_path,
98 target_package_name=target_package_name))
99
100 return target_package_to_instrument, target_package_name
101
102
103def main():
104 FLAGS(sys.argv)
105
106 instr_manifest_path = FLAGS.instrumentation_manifest
107 target_manifest_path = FLAGS.target_manifest
108 output_path = FLAGS.output
109 dirname = os.path.dirname(output_path)
110 if not os.path.exists(dirname):
111 os.makedirs(dirname)
112
113 with open(instr_manifest_path, "r") as f:
114 instr_manifest = f.read()
115
116 with open(target_manifest_path, "r") as f:
117 target_manifest = f.read()
118
119 try:
120 package_to_instrument, package_name = _ValidateManifestPackageNames(
121 instr_manifest, instr_manifest_path, target_manifest,
122 target_manifest_path)
123 except ManifestError as e:
124 sys.exit(e.message)
125
126 with open(output_path, "w") as f:
127 f.write("target_package={0}\n".format(package_to_instrument))
128 f.write("package_name={0}\n".format(package_name))
129
130
131if __name__ == "__main__":
132 main()