blob: 7ade73976edf69c930f08546d4ae2d0f5b02b2c2 [file] [log] [blame]
# pylint: disable=g-direct-third-party-import
# Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""AndroidManifest checks for android_instrumentation_test.
Ensures that the targetPackage of the instrumentation APK references
the correct target package name.
"""
import os
import sys
import xml.etree.ElementTree as ET
from third_party.py import gflags
gflags.DEFINE_string("instrumentation_manifest", None,
"AndroidManifest.xml of the instrumentation APK")
gflags.DEFINE_string("target_manifest", None,
"AndroidManifest.xml of the target APK")
gflags.DEFINE_string("output", None, "Output of the check")
FLAGS = gflags.FLAGS
class ManifestError(Exception):
"""Raised when there is a problem with an AndroidManifest.xml."""
# There might be more than one <instrumentation> tag to use different
# test runners, so we need to extract the targetPackage attribute values
# from all of them and check that they are the same.
def _ExtractTargetPackageToInstrument(xml_content, path):
"""Extract the targetPackage value from the <instrumentation> tag."""
# https://developer.android.com/guide/topics/manifest/manifest-element.html
# xmlns:android is the required namespace in an Android manifest.
tree = ET.ElementTree(ET.fromstring(xml_content))
package_key = "{http://schemas.android.com/apk/res/android}targetPackage"
instrumentation_elems = tree.iterfind(
".//instrumentation[@{0}]".format(package_key))
package_names = set(e.attrib[package_key] for e in instrumentation_elems)
if not package_names:
raise ManifestError("No <instrumentation> tag containing "
"the targetPackage attribute is found in the "
"manifest at %s" % path)
if len(package_names) > 1:
raise ManifestError(
"The <instrumentation> tags in the manifest at %s do not "
"reference the same target package: %s" % (path, list(package_names)))
return package_names.pop()
def _ExtractTargetPackageName(xml_content, path):
"""Extract the package name value from the root <manifest> tag."""
tree = ET.ElementTree(ET.fromstring(xml_content))
root = tree.getroot()
if "package" in root.attrib:
return root.attrib["package"]
else:
raise ManifestError("The <manifest> tag in the manifest at %s needs to "
"specify the package name using the 'package' "
"attribute." % path)
def _ValidateManifestPackageNames(instr_manifest_content, instr_manifest_path,
target_manifest_content,
target_manifest_path):
"""Diff the package names and throw a ManifestError if not identical."""
target_package_to_instrument = _ExtractTargetPackageToInstrument(
instr_manifest_content, instr_manifest_path)
target_package_name = _ExtractTargetPackageName(target_manifest_content,
target_manifest_path)
if target_package_to_instrument != target_package_name:
raise ManifestError(
"The targetPackage specified in the instrumentation manifest at "
"{instr_manifest_path} ({target_package_to_instrument}) does not match "
"the package name of the target manifest at {target_manifest_path} "
"({target_package_name})".format(
instr_manifest_path=instr_manifest_path,
target_package_to_instrument=target_package_to_instrument,
target_manifest_path=target_manifest_path,
target_package_name=target_package_name))
return target_package_to_instrument, target_package_name
def main():
FLAGS(sys.argv)
instr_manifest_path = FLAGS.instrumentation_manifest
target_manifest_path = FLAGS.target_manifest
output_path = FLAGS.output
dirname = os.path.dirname(output_path)
if not os.path.exists(dirname):
os.makedirs(dirname)
with open(instr_manifest_path, "r") as f:
instr_manifest = f.read()
with open(target_manifest_path, "r") as f:
target_manifest = f.read()
try:
package_to_instrument, package_name = _ValidateManifestPackageNames(
instr_manifest, instr_manifest_path, target_manifest,
target_manifest_path)
except ManifestError as e:
sys.exit(e.message)
with open(output_path, "w") as f:
f.write("target_package={0}\n".format(package_to_instrument))
f.write("package_name={0}\n".format(package_name))
if __name__ == "__main__":
main()