# 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 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 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 tag containing " "the targetPackage attribute is found in the " "manifest at %s" % path) if len(package_names) > 1: raise ManifestError( "The 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 tag.""" tree = ET.ElementTree(ET.fromstring(xml_content)) root = tree.getroot() if "package" in root.attrib: return root.attrib["package"] else: raise ManifestError("The 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()