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