diff options
author | 2021-08-25 20:01:17 +0000 | |
---|---|---|
committer | 2021-09-08 03:59:40 +0000 | |
commit | f880742582a2876080427fcebb1b2c94787c4e24 (patch) | |
tree | 8f287bdcbe1a7d72cac95d9062ff4108daefebfe | |
parent | 7c16dabfa595cfe38b99c5b0f92aca6eec440de0 (diff) |
Apply pylint to scripts/manifest_check*.py
1. Run pyformat scripts/<script>.py -s 4 --force_quote_type
none -i to fix formatting
2. Annotate #pylint: disable, where straightforward fix is not available
Test: m manifest_check_test
Test: pylint --rcfile tools/repohooks/tools/pylintrc
build/soong/scripts/manifest_check.py
build/soong/scripts/manifest_check_test.py
Bug: 195738175
Change-Id: I9af498c4abd6ac9f8b9df4f93cbdd4424eacff8e
-rwxr-xr-x | scripts/manifest_check.py | 529 | ||||
-rwxr-xr-x | scripts/manifest_check_test.py | 397 |
2 files changed, 495 insertions, 431 deletions
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py index 4ef4399ca..71fe358ff 100755 --- a/scripts/manifest_check.py +++ b/scripts/manifest_check.py @@ -25,7 +25,6 @@ import subprocess import sys from xml.dom import minidom - from manifest import android_ns from manifest import get_children_with_tag from manifest import parse_manifest @@ -33,49 +32,61 @@ from manifest import write_xml class ManifestMismatchError(Exception): - pass + pass def parse_args(): - """Parse commandline arguments.""" - - parser = argparse.ArgumentParser() - parser.add_argument('--uses-library', dest='uses_libraries', - action='append', - help='specify uses-library entries known to the build system') - parser.add_argument('--optional-uses-library', - dest='optional_uses_libraries', - action='append', - help='specify uses-library entries known to the build system with required:false') - parser.add_argument('--enforce-uses-libraries', - dest='enforce_uses_libraries', - action='store_true', - help='check the uses-library entries known to the build system against the manifest') - parser.add_argument('--enforce-uses-libraries-relax', - dest='enforce_uses_libraries_relax', - action='store_true', - help='do not fail immediately, just save the error message to file') - parser.add_argument('--enforce-uses-libraries-status', - dest='enforce_uses_libraries_status', - help='output file to store check status (error message)') - parser.add_argument('--extract-target-sdk-version', - dest='extract_target_sdk_version', - action='store_true', - help='print the targetSdkVersion from the manifest') - parser.add_argument('--dexpreopt-config', - dest='dexpreopt_configs', - action='append', - help='a paths to a dexpreopt.config of some library') - parser.add_argument('--aapt', - dest='aapt', - help='path to aapt executable') - parser.add_argument('--output', '-o', dest='output', help='output AndroidManifest.xml file') - parser.add_argument('input', help='input AndroidManifest.xml file') - return parser.parse_args() + """Parse commandline arguments.""" + + parser = argparse.ArgumentParser() + parser.add_argument( + '--uses-library', + dest='uses_libraries', + action='append', + help='specify uses-library entries known to the build system') + parser.add_argument( + '--optional-uses-library', + dest='optional_uses_libraries', + action='append', + help='specify uses-library entries known to the build system with ' + 'required:false' + ) + parser.add_argument( + '--enforce-uses-libraries', + dest='enforce_uses_libraries', + action='store_true', + help='check the uses-library entries known to the build system against ' + 'the manifest' + ) + parser.add_argument( + '--enforce-uses-libraries-relax', + dest='enforce_uses_libraries_relax', + action='store_true', + help='do not fail immediately, just save the error message to file') + parser.add_argument( + '--enforce-uses-libraries-status', + dest='enforce_uses_libraries_status', + help='output file to store check status (error message)') + parser.add_argument( + '--extract-target-sdk-version', + dest='extract_target_sdk_version', + action='store_true', + help='print the targetSdkVersion from the manifest') + parser.add_argument( + '--dexpreopt-config', + dest='dexpreopt_configs', + action='append', + help='a paths to a dexpreopt.config of some library') + parser.add_argument('--aapt', dest='aapt', help='path to aapt executable') + parser.add_argument( + '--output', '-o', dest='output', help='output AndroidManifest.xml file') + parser.add_argument('input', help='input AndroidManifest.xml file') + return parser.parse_args() def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path): - """Verify that the <uses-library> tags in the manifest match those provided + """Verify that the <uses-library> tags in the manifest match those provided + by the build system. Args: @@ -84,274 +95,294 @@ def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path): optional: optional libs known to the build system relax: if true, suppress error on mismatch and just write it to file is_apk: if the manifest comes from an APK or an XML file - """ - if is_apk: - manifest_required, manifest_optional, tags = extract_uses_libs_apk(manifest) - else: - manifest_required, manifest_optional, tags = extract_uses_libs_xml(manifest) - - # Trim namespace component. Normally Soong does that automatically when it - # handles module names specified in Android.bp properties. However not all - # <uses-library> entries in the manifest correspond to real modules: some of - # the optional libraries may be missing at build time. Therefor this script - # accepts raw module names as spelled in Android.bp/Amdroid.mk and trims the - # optional namespace part manually. - required = trim_namespace_parts(required) - optional = trim_namespace_parts(optional) - - if manifest_required == required and manifest_optional == optional: - return None - - errmsg = ''.join([ - 'mismatch in the <uses-library> tags between the build system and the ' - 'manifest:\n', - '\t- required libraries in build system: [%s]\n' % ', '.join(required), - '\t vs. in the manifest: [%s]\n' % ', '.join(manifest_required), - '\t- optional libraries in build system: [%s]\n' % ', '.join(optional), - '\t vs. in the manifest: [%s]\n' % ', '.join(manifest_optional), - '\t- tags in the manifest (%s):\n' % path, - '\t\t%s\n' % '\t\t'.join(tags), - 'note: the following options are available:\n', - '\t- to temporarily disable the check on command line, rebuild with ', - 'RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" ', - 'and disable AOT-compilation in dexpreopt)\n', - '\t- to temporarily disable the check for the whole product, set ', - 'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n', - '\t- to fix the check, make build system properties coherent with the ' - 'manifest\n', - '\t- see build/make/Changes.md for details\n']) - - if not relax: - raise ManifestMismatchError(errmsg) - - return errmsg - - -MODULE_NAMESPACE = re.compile("^//[^:]+:") - -def trim_namespace_parts(modules): - """Trim the namespace part of each module, if present. Leave only the name.""" - - trimmed = [] - for module in modules: - trimmed.append(MODULE_NAMESPACE.sub('', module)) - return trimmed - - -def extract_uses_libs_apk(badging): - """Extract <uses-library> tags from the manifest of an APK.""" - - pattern = re.compile("^uses-library(-not-required)?:'(.*)'$", re.MULTILINE) - - required = [] - optional = [] - lines = [] - for match in re.finditer(pattern, badging): - lines.append(match.group(0)) - libname = match.group(2) - if match.group(1) == None: - required.append(libname) + """ + if is_apk: + manifest_required, manifest_optional, tags = extract_uses_libs_apk( + manifest) else: - optional.append(libname) - - required = first_unique_elements(required) - optional = first_unique_elements(optional) - tags = first_unique_elements(lines) - return required, optional, tags + manifest_required, manifest_optional, tags = extract_uses_libs_xml( + manifest) + + # Trim namespace component. Normally Soong does that automatically when it + # handles module names specified in Android.bp properties. However not all + # <uses-library> entries in the manifest correspond to real modules: some of + # the optional libraries may be missing at build time. Therefor this script + # accepts raw module names as spelled in Android.bp/Amdroid.mk and trims the + # optional namespace part manually. + required = trim_namespace_parts(required) + optional = trim_namespace_parts(optional) + + if manifest_required == required and manifest_optional == optional: + return None + + #pylint: disable=line-too-long + errmsg = ''.join([ + 'mismatch in the <uses-library> tags between the build system and the ' + 'manifest:\n', + '\t- required libraries in build system: [%s]\n' % ', '.join(required), + '\t vs. in the manifest: [%s]\n' % + ', '.join(manifest_required), + '\t- optional libraries in build system: [%s]\n' % ', '.join(optional), + '\t vs. in the manifest: [%s]\n' % + ', '.join(manifest_optional), + '\t- tags in the manifest (%s):\n' % path, + '\t\t%s\n' % '\t\t'.join(tags), + 'note: the following options are available:\n', + '\t- to temporarily disable the check on command line, rebuild with ', + 'RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" ', + 'and disable AOT-compilation in dexpreopt)\n', + '\t- to temporarily disable the check for the whole product, set ', + 'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n', + '\t- to fix the check, make build system properties coherent with the ' + 'manifest\n', '\t- see build/make/Changes.md for details\n' + ]) + #pylint: enable=line-too-long + + if not relax: + raise ManifestMismatchError(errmsg) + + return errmsg + + +MODULE_NAMESPACE = re.compile('^//[^:]+:') -def extract_uses_libs_xml(xml): - """Extract <uses-library> tags from the manifest.""" - - manifest = parse_manifest(xml) - elems = get_children_with_tag(manifest, 'application') - application = elems[0] if len(elems) == 1 else None - if len(elems) > 1: - raise RuntimeError('found multiple <application> tags') - elif not elems: - if uses_libraries or optional_uses_libraries: - raise ManifestMismatchError('no <application> tag found') - return +def trim_namespace_parts(modules): + """Trim the namespace part of each module, if present. - libs = get_children_with_tag(application, 'uses-library') + Leave only the name. + """ - required = [uses_library_name(x) for x in libs if uses_library_required(x)] - optional = [uses_library_name(x) for x in libs if not uses_library_required(x)] + trimmed = [] + for module in modules: + trimmed.append(MODULE_NAMESPACE.sub('', module)) + return trimmed - # render <uses-library> tags as XML for a pretty error message - tags = [] - for lib in libs: - tags.append(lib.toprettyxml()) - required = first_unique_elements(required) - optional = first_unique_elements(optional) - tags = first_unique_elements(tags) - return required, optional, tags +def extract_uses_libs_apk(badging): + """Extract <uses-library> tags from the manifest of an APK.""" + + pattern = re.compile("^uses-library(-not-required)?:'(.*)'$", re.MULTILINE) + + required = [] + optional = [] + lines = [] + for match in re.finditer(pattern, badging): + lines.append(match.group(0)) + libname = match.group(2) + if match.group(1) is None: + required.append(libname) + else: + optional.append(libname) + + required = first_unique_elements(required) + optional = first_unique_elements(optional) + tags = first_unique_elements(lines) + return required, optional, tags + + +def extract_uses_libs_xml(xml): #pylint: disable=inconsistent-return-statements + """Extract <uses-library> tags from the manifest.""" + + manifest = parse_manifest(xml) + elems = get_children_with_tag(manifest, 'application') + application = elems[0] if len(elems) == 1 else None + if len(elems) > 1: #pylint: disable=no-else-raise + raise RuntimeError('found multiple <application> tags') + elif not elems: + if uses_libraries or optional_uses_libraries: #pylint: disable=undefined-variable + raise ManifestMismatchError('no <application> tag found') + return + + libs = get_children_with_tag(application, 'uses-library') + + required = [uses_library_name(x) for x in libs if uses_library_required(x)] + optional = [ + uses_library_name(x) for x in libs if not uses_library_required(x) + ] + + # render <uses-library> tags as XML for a pretty error message + tags = [] + for lib in libs: + tags.append(lib.toprettyxml()) + + required = first_unique_elements(required) + optional = first_unique_elements(optional) + tags = first_unique_elements(tags) + return required, optional, tags def first_unique_elements(l): - result = [] - [result.append(x) for x in l if x not in result] - return result + result = [] + for x in l: + if x not in result: + result.append(x) + return result def uses_library_name(lib): - """Extract the name attribute of a uses-library tag. + """Extract the name attribute of a uses-library tag. Args: lib: a <uses-library> tag. - """ - name = lib.getAttributeNodeNS(android_ns, 'name') - return name.value if name is not None else "" + """ + name = lib.getAttributeNodeNS(android_ns, 'name') + return name.value if name is not None else '' def uses_library_required(lib): - """Extract the required attribute of a uses-library tag. + """Extract the required attribute of a uses-library tag. Args: lib: a <uses-library> tag. - """ - required = lib.getAttributeNodeNS(android_ns, 'required') - return (required.value == 'true') if required is not None else True + """ + required = lib.getAttributeNodeNS(android_ns, 'required') + return (required.value == 'true') if required is not None else True -def extract_target_sdk_version(manifest, is_apk = False): - """Returns the targetSdkVersion from the manifest. +def extract_target_sdk_version(manifest, is_apk=False): + """Returns the targetSdkVersion from the manifest. Args: manifest: manifest (either parsed XML or aapt dump of APK) is_apk: if the manifest comes from an APK or an XML file - """ - if is_apk: - return extract_target_sdk_version_apk(manifest) - else: - return extract_target_sdk_version_xml(manifest) + """ + if is_apk: #pylint: disable=no-else-return + return extract_target_sdk_version_apk(manifest) + else: + return extract_target_sdk_version_xml(manifest) def extract_target_sdk_version_apk(badging): - """Extract targetSdkVersion tags from the manifest of an APK.""" + """Extract targetSdkVersion tags from the manifest of an APK.""" - pattern = re.compile("^targetSdkVersion?:'(.*)'$", re.MULTILINE) + pattern = re.compile("^targetSdkVersion?:'(.*)'$", re.MULTILINE) - for match in re.finditer(pattern, badging): - return match.group(1) + for match in re.finditer(pattern, badging): + return match.group(1) - raise RuntimeError('cannot find targetSdkVersion in the manifest') + raise RuntimeError('cannot find targetSdkVersion in the manifest') def extract_target_sdk_version_xml(xml): - """Extract targetSdkVersion tags from the manifest.""" + """Extract targetSdkVersion tags from the manifest.""" - manifest = parse_manifest(xml) + manifest = parse_manifest(xml) - # Get or insert the uses-sdk element - uses_sdk = get_children_with_tag(manifest, 'uses-sdk') - if len(uses_sdk) > 1: - raise RuntimeError('found multiple uses-sdk elements') - elif len(uses_sdk) == 0: - raise RuntimeError('missing uses-sdk element') + # Get or insert the uses-sdk element + uses_sdk = get_children_with_tag(manifest, 'uses-sdk') + if len(uses_sdk) > 1: #pylint: disable=no-else-raise + raise RuntimeError('found multiple uses-sdk elements') + elif len(uses_sdk) == 0: + raise RuntimeError('missing uses-sdk element') - uses_sdk = uses_sdk[0] + uses_sdk = uses_sdk[0] - min_attr = uses_sdk.getAttributeNodeNS(android_ns, 'minSdkVersion') - if min_attr is None: - raise RuntimeError('minSdkVersion is not specified') + min_attr = uses_sdk.getAttributeNodeNS(android_ns, 'minSdkVersion') + if min_attr is None: + raise RuntimeError('minSdkVersion is not specified') - target_attr = uses_sdk.getAttributeNodeNS(android_ns, 'targetSdkVersion') - if target_attr is None: - target_attr = min_attr + target_attr = uses_sdk.getAttributeNodeNS(android_ns, 'targetSdkVersion') + if target_attr is None: + target_attr = min_attr - return target_attr.value + return target_attr.value def load_dexpreopt_configs(configs): - """Load dexpreopt.config files and map module names to library names.""" - module_to_libname = {} + """Load dexpreopt.config files and map module names to library names.""" + module_to_libname = {} - if configs is None: - configs = [] + if configs is None: + configs = [] - for config in configs: - with open(config, 'r') as f: - contents = json.load(f) - module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary'] + for config in configs: + with open(config, 'r') as f: + contents = json.load(f) + module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary'] - return module_to_libname + return module_to_libname def translate_libnames(modules, module_to_libname): - """Translate module names into library names using the mapping.""" - if modules is None: - modules = [] + """Translate module names into library names using the mapping.""" + if modules is None: + modules = [] - libnames = [] - for name in modules: - if name in module_to_libname: - name = module_to_libname[name] - libnames.append(name) + libnames = [] + for name in modules: + if name in module_to_libname: + name = module_to_libname[name] + libnames.append(name) - return libnames + return libnames def main(): - """Program entry point.""" - try: - args = parse_args() + """Program entry point.""" + try: + args = parse_args() + + # The input can be either an XML manifest or an APK, they are parsed and + # processed in different ways. + is_apk = args.input.endswith('.apk') + if is_apk: + aapt = args.aapt if args.aapt is not None else 'aapt' + manifest = subprocess.check_output( + [aapt, 'dump', 'badging', args.input]) + else: + manifest = minidom.parse(args.input) + + if args.enforce_uses_libraries: + # Load dexpreopt.config files and build a mapping from module + # names to library names. This is necessary because build system + # addresses libraries by their module name (`uses_libs`, + # `optional_uses_libs`, `LOCAL_USES_LIBRARIES`, + # `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain module names), while + # the manifest addresses libraries by their name. + mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs) + required = translate_libnames(args.uses_libraries, mod_to_lib) + optional = translate_libnames(args.optional_uses_libraries, + mod_to_lib) + + # Check if the <uses-library> lists in the build system agree with + # those in the manifest. Raise an exception on mismatch, unless the + # script was passed a special parameter to suppress exceptions. + errmsg = enforce_uses_libraries(manifest, required, optional, + args.enforce_uses_libraries_relax, + is_apk, args.input) + + # Create a status file that is empty on success, or contains an + # error message on failure. When exceptions are suppressed, + # dexpreopt command command will check file size to determine if + # the check has failed. + if args.enforce_uses_libraries_status: + with open(args.enforce_uses_libraries_status, 'w') as f: + if not errmsg is not None: + f.write('%s\n' % errmsg) + + if args.extract_target_sdk_version: + try: + print(extract_target_sdk_version(manifest, is_apk)) + except: #pylint: disable=bare-except + # Failed; don't crash, return "any" SDK version. This will + # result in dexpreopt not adding any compatibility libraries. + print(10000) + + if args.output: + # XML output is supposed to be written only when this script is + # invoked with XML input manifest, not with an APK. + if is_apk: + raise RuntimeError('cannot save APK manifest as XML') + + with open(args.output, 'wb') as f: + write_xml(f, manifest) + + # pylint: disable=broad-except + except Exception as err: + print('error: ' + str(err), file=sys.stderr) + sys.exit(-1) - # The input can be either an XML manifest or an APK, they are parsed and - # processed in different ways. - is_apk = args.input.endswith('.apk') - if is_apk: - aapt = args.aapt if args.aapt != None else "aapt" - manifest = subprocess.check_output([aapt, "dump", "badging", args.input]) - else: - manifest = minidom.parse(args.input) - - if args.enforce_uses_libraries: - # Load dexpreopt.config files and build a mapping from module names to - # library names. This is necessary because build system addresses - # libraries by their module name (`uses_libs`, `optional_uses_libs`, - # `LOCAL_USES_LIBRARIES`, `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain - # module names), while the manifest addresses libraries by their name. - mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs) - required = translate_libnames(args.uses_libraries, mod_to_lib) - optional = translate_libnames(args.optional_uses_libraries, mod_to_lib) - - # Check if the <uses-library> lists in the build system agree with those - # in the manifest. Raise an exception on mismatch, unless the script was - # passed a special parameter to suppress exceptions. - errmsg = enforce_uses_libraries(manifest, required, optional, - args.enforce_uses_libraries_relax, is_apk, args.input) - - # Create a status file that is empty on success, or contains an error - # message on failure. When exceptions are suppressed, dexpreopt command - # command will check file size to determine if the check has failed. - if args.enforce_uses_libraries_status: - with open(args.enforce_uses_libraries_status, 'w') as f: - if not errmsg == None: - f.write("%s\n" % errmsg) - - if args.extract_target_sdk_version: - try: - print(extract_target_sdk_version(manifest, is_apk)) - except: - # Failed; don't crash, return "any" SDK version. This will result in - # dexpreopt not adding any compatibility libraries. - print(10000) - - if args.output: - # XML output is supposed to be written only when this script is invoked - # with XML input manifest, not with an APK. - if is_apk: - raise RuntimeError('cannot save APK manifest as XML') - - with open(args.output, 'wb') as f: - write_xml(f, manifest) - - # pylint: disable=broad-except - except Exception as err: - print('error: ' + str(err), file=sys.stderr) - sys.exit(-1) if __name__ == '__main__': - main() + main() diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py index e3e8ac468..3be7a30bf 100755 --- a/scripts/manifest_check_test.py +++ b/scripts/manifest_check_test.py @@ -26,202 +26,235 @@ sys.dont_write_bytecode = True def uses_library_xml(name, attr=''): - return '<uses-library android:name="%s"%s />' % (name, attr) + return '<uses-library android:name="%s"%s />' % (name, attr) def required_xml(value): - return ' android:required="%s"' % ('true' if value else 'false') + return ' android:required="%s"' % ('true' if value else 'false') def uses_library_apk(name, sfx=''): - return "uses-library%s:'%s'" % (sfx, name) + return "uses-library%s:'%s'" % (sfx, name) def required_apk(value): - return '' if value else '-not-required' + return '' if value else '-not-required' class EnforceUsesLibrariesTest(unittest.TestCase): - """Unit tests for add_extract_native_libs function.""" - - def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[]): - doc = minidom.parseString(xml) - try: - relax = False - manifest_check.enforce_uses_libraries(doc, uses_libraries, - optional_uses_libraries, relax, False, 'path/to/X/AndroidManifest.xml') - manifest_check.enforce_uses_libraries(apk, uses_libraries, - optional_uses_libraries, relax, True, 'path/to/X/X.apk') - return True - except manifest_check.ManifestMismatchError: - return False - - xml_tmpl = ( - '<?xml version="1.0" encoding="utf-8"?>\n' - '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' - ' <application>\n' - ' %s\n' - ' </application>\n' - '</manifest>\n') - - apk_tmpl = ( - "package: name='com.google.android.something' versionCode='100'\n" - "sdkVersion:'29'\n" - "targetSdkVersion:'29'\n" - "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n" - "%s\n" - "densities: '160' '240' '320' '480' '640' '65534") - - def test_uses_library(self): - xml = self.xml_tmpl % (uses_library_xml('foo')) - apk = self.apk_tmpl % (uses_library_apk('foo')) - matches = self.run_test(xml, apk, uses_libraries=['foo']) - self.assertTrue(matches) - - def test_uses_library_required(self): - xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(True))) - apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(True))) - matches = self.run_test(xml, apk, uses_libraries=['foo']) - self.assertTrue(matches) - - def test_optional_uses_library(self): - xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False))) - apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False))) - matches = self.run_test(xml, apk, optional_uses_libraries=['foo']) - self.assertTrue(matches) - - def test_expected_uses_library(self): - xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False))) - apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False))) - matches = self.run_test(xml, apk, uses_libraries=['foo']) - self.assertFalse(matches) - - def test_expected_optional_uses_library(self): - xml = self.xml_tmpl % (uses_library_xml('foo')) - apk = self.apk_tmpl % (uses_library_apk('foo')) - matches = self.run_test(xml, apk, optional_uses_libraries=['foo']) - self.assertFalse(matches) - - def test_missing_uses_library(self): - xml = self.xml_tmpl % ('') - apk = self.apk_tmpl % ('') - matches = self.run_test(xml, apk, uses_libraries=['foo']) - self.assertFalse(matches) - - def test_missing_optional_uses_library(self): - xml = self.xml_tmpl % ('') - apk = self.apk_tmpl % ('') - matches = self.run_test(xml, apk, optional_uses_libraries=['foo']) - self.assertFalse(matches) - - def test_extra_uses_library(self): - xml = self.xml_tmpl % (uses_library_xml('foo')) - apk = self.apk_tmpl % (uses_library_xml('foo')) - matches = self.run_test(xml, apk) - self.assertFalse(matches) - - def test_extra_optional_uses_library(self): - xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False))) - apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False))) - matches = self.run_test(xml, apk) - self.assertFalse(matches) - - def test_multiple_uses_library(self): - xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'), - uses_library_xml('bar')])) - apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'), - uses_library_apk('bar')])) - matches = self.run_test(xml, apk, uses_libraries=['foo', 'bar']) - self.assertTrue(matches) - - def test_multiple_optional_uses_library(self): - xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)), - uses_library_xml('bar', required_xml(False))])) - apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)), - uses_library_apk('bar', required_apk(False))])) - matches = self.run_test(xml, apk, optional_uses_libraries=['foo', 'bar']) - self.assertTrue(matches) - - def test_order_uses_library(self): - xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'), - uses_library_xml('bar')])) - apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'), - uses_library_apk('bar')])) - matches = self.run_test(xml, apk, uses_libraries=['bar', 'foo']) - self.assertFalse(matches) - - def test_order_optional_uses_library(self): - xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)), - uses_library_xml('bar', required_xml(False))])) - apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)), - uses_library_apk('bar', required_apk(False))])) - matches = self.run_test(xml, apk, optional_uses_libraries=['bar', 'foo']) - self.assertFalse(matches) - - def test_duplicate_uses_library(self): - xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'), - uses_library_xml('foo')])) - apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'), - uses_library_apk('foo')])) - matches = self.run_test(xml, apk, uses_libraries=['foo']) - self.assertTrue(matches) - - def test_duplicate_optional_uses_library(self): - xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)), - uses_library_xml('foo', required_xml(False))])) - apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)), - uses_library_apk('foo', required_apk(False))])) - matches = self.run_test(xml, apk, optional_uses_libraries=['foo']) - self.assertTrue(matches) - - def test_mixed(self): - xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'), - uses_library_xml('bar', required_xml(False))])) - apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'), - uses_library_apk('bar', required_apk(False))])) - matches = self.run_test(xml, apk, uses_libraries=['foo'], - optional_uses_libraries=['bar']) - self.assertTrue(matches) - - def test_mixed_with_namespace(self): - xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'), - uses_library_xml('bar', required_xml(False))])) - apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'), - uses_library_apk('bar', required_apk(False))])) - matches = self.run_test(xml, apk, uses_libraries=['//x/y/z:foo'], - optional_uses_libraries=['//x/y/z:bar']) - self.assertTrue(matches) + """Unit tests for add_extract_native_libs function.""" + + def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[]): #pylint: disable=dangerous-default-value + doc = minidom.parseString(xml) + try: + relax = False + manifest_check.enforce_uses_libraries( + doc, uses_libraries, optional_uses_libraries, relax, False, + 'path/to/X/AndroidManifest.xml') + manifest_check.enforce_uses_libraries(apk, uses_libraries, + optional_uses_libraries, + relax, True, + 'path/to/X/X.apk') + return True + except manifest_check.ManifestMismatchError: + return False + + xml_tmpl = ( + '<?xml version="1.0" encoding="utf-8"?>\n<manifest ' + 'xmlns:android="http://schemas.android.com/apk/res/android">\n ' + '<application>\n %s\n </application>\n</manifest>\n') + + apk_tmpl = ( + "package: name='com.google.android.something' versionCode='100'\n" + "sdkVersion:'29'\n" + "targetSdkVersion:'29'\n" + "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n" + '%s\n' + "densities: '160' '240' '320' '480' '640' '65534") + + def test_uses_library(self): + xml = self.xml_tmpl % (uses_library_xml('foo')) + apk = self.apk_tmpl % (uses_library_apk('foo')) + matches = self.run_test(xml, apk, uses_libraries=['foo']) + self.assertTrue(matches) + + def test_uses_library_required(self): + xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(True))) + apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(True))) + matches = self.run_test(xml, apk, uses_libraries=['foo']) + self.assertTrue(matches) + + def test_optional_uses_library(self): + xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False))) + apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False))) + matches = self.run_test(xml, apk, optional_uses_libraries=['foo']) + self.assertTrue(matches) + + def test_expected_uses_library(self): + xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False))) + apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False))) + matches = self.run_test(xml, apk, uses_libraries=['foo']) + self.assertFalse(matches) + + def test_expected_optional_uses_library(self): + xml = self.xml_tmpl % (uses_library_xml('foo')) + apk = self.apk_tmpl % (uses_library_apk('foo')) + matches = self.run_test(xml, apk, optional_uses_libraries=['foo']) + self.assertFalse(matches) + + def test_missing_uses_library(self): + xml = self.xml_tmpl % ('') + apk = self.apk_tmpl % ('') + matches = self.run_test(xml, apk, uses_libraries=['foo']) + self.assertFalse(matches) + + def test_missing_optional_uses_library(self): + xml = self.xml_tmpl % ('') + apk = self.apk_tmpl % ('') + matches = self.run_test(xml, apk, optional_uses_libraries=['foo']) + self.assertFalse(matches) + + def test_extra_uses_library(self): + xml = self.xml_tmpl % (uses_library_xml('foo')) + apk = self.apk_tmpl % (uses_library_xml('foo')) + matches = self.run_test(xml, apk) + self.assertFalse(matches) + + def test_extra_optional_uses_library(self): + xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False))) + apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False))) + matches = self.run_test(xml, apk) + self.assertFalse(matches) + + def test_multiple_uses_library(self): + xml = self.xml_tmpl % ('\n'.join( + [uses_library_xml('foo'), + uses_library_xml('bar')])) + apk = self.apk_tmpl % ('\n'.join( + [uses_library_apk('foo'), + uses_library_apk('bar')])) + matches = self.run_test(xml, apk, uses_libraries=['foo', 'bar']) + self.assertTrue(matches) + + def test_multiple_optional_uses_library(self): + xml = self.xml_tmpl % ('\n'.join([ + uses_library_xml('foo', required_xml(False)), + uses_library_xml('bar', required_xml(False)) + ])) + apk = self.apk_tmpl % ('\n'.join([ + uses_library_apk('foo', required_apk(False)), + uses_library_apk('bar', required_apk(False)) + ])) + matches = self.run_test( + xml, apk, optional_uses_libraries=['foo', 'bar']) + self.assertTrue(matches) + + def test_order_uses_library(self): + xml = self.xml_tmpl % ('\n'.join( + [uses_library_xml('foo'), + uses_library_xml('bar')])) + apk = self.apk_tmpl % ('\n'.join( + [uses_library_apk('foo'), + uses_library_apk('bar')])) + matches = self.run_test(xml, apk, uses_libraries=['bar', 'foo']) + self.assertFalse(matches) + + def test_order_optional_uses_library(self): + xml = self.xml_tmpl % ('\n'.join([ + uses_library_xml('foo', required_xml(False)), + uses_library_xml('bar', required_xml(False)) + ])) + apk = self.apk_tmpl % ('\n'.join([ + uses_library_apk('foo', required_apk(False)), + uses_library_apk('bar', required_apk(False)) + ])) + matches = self.run_test( + xml, apk, optional_uses_libraries=['bar', 'foo']) + self.assertFalse(matches) + + def test_duplicate_uses_library(self): + xml = self.xml_tmpl % ('\n'.join( + [uses_library_xml('foo'), + uses_library_xml('foo')])) + apk = self.apk_tmpl % ('\n'.join( + [uses_library_apk('foo'), + uses_library_apk('foo')])) + matches = self.run_test(xml, apk, uses_libraries=['foo']) + self.assertTrue(matches) + + def test_duplicate_optional_uses_library(self): + xml = self.xml_tmpl % ('\n'.join([ + uses_library_xml('foo', required_xml(False)), + uses_library_xml('foo', required_xml(False)) + ])) + apk = self.apk_tmpl % ('\n'.join([ + uses_library_apk('foo', required_apk(False)), + uses_library_apk('foo', required_apk(False)) + ])) + matches = self.run_test(xml, apk, optional_uses_libraries=['foo']) + self.assertTrue(matches) + + def test_mixed(self): + xml = self.xml_tmpl % ('\n'.join([ + uses_library_xml('foo'), + uses_library_xml('bar', required_xml(False)) + ])) + apk = self.apk_tmpl % ('\n'.join([ + uses_library_apk('foo'), + uses_library_apk('bar', required_apk(False)) + ])) + matches = self.run_test( + xml, apk, uses_libraries=['foo'], optional_uses_libraries=['bar']) + self.assertTrue(matches) + + def test_mixed_with_namespace(self): + xml = self.xml_tmpl % ('\n'.join([ + uses_library_xml('foo'), + uses_library_xml('bar', required_xml(False)) + ])) + apk = self.apk_tmpl % ('\n'.join([ + uses_library_apk('foo'), + uses_library_apk('bar', required_apk(False)) + ])) + matches = self.run_test( + xml, + apk, + uses_libraries=['//x/y/z:foo'], + optional_uses_libraries=['//x/y/z:bar']) + self.assertTrue(matches) class ExtractTargetSdkVersionTest(unittest.TestCase): - def run_test(self, xml, apk, version): - doc = minidom.parseString(xml) - v = manifest_check.extract_target_sdk_version(doc, is_apk=False) - self.assertEqual(v, version) - v = manifest_check.extract_target_sdk_version(apk, is_apk=True) - self.assertEqual(v, version) - - xml_tmpl = ( - '<?xml version="1.0" encoding="utf-8"?>\n' - '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' - ' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="%s" />\n' - '</manifest>\n') - - apk_tmpl = ( - "package: name='com.google.android.something' versionCode='100'\n" - "sdkVersion:'28'\n" - "targetSdkVersion:'%s'\n" - "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n") - - def test_targert_sdk_version_28(self): - xml = self.xml_tmpl % "28" - apk = self.apk_tmpl % "28" - self.run_test(xml, apk, "28") - - def test_targert_sdk_version_29(self): - xml = self.xml_tmpl % "29" - apk = self.apk_tmpl % "29" - self.run_test(xml, apk, "29") + + def run_test(self, xml, apk, version): + doc = minidom.parseString(xml) + v = manifest_check.extract_target_sdk_version(doc, is_apk=False) + self.assertEqual(v, version) + v = manifest_check.extract_target_sdk_version(apk, is_apk=True) + self.assertEqual(v, version) + + xml_tmpl = ( + '<?xml version="1.0" encoding="utf-8"?>\n<manifest ' + 'xmlns:android="http://schemas.android.com/apk/res/android">\n ' + '<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="%s" ' + '/>\n</manifest>\n') + + apk_tmpl = ( + "package: name='com.google.android.something' versionCode='100'\n" + "sdkVersion:'28'\n" + "targetSdkVersion:'%s'\n" + "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n") + + def test_targert_sdk_version_28(self): + xml = self.xml_tmpl % '28' + apk = self.apk_tmpl % '28' + self.run_test(xml, apk, '28') + + def test_targert_sdk_version_29(self): + xml = self.xml_tmpl % '29' + apk = self.apk_tmpl % '29' + self.run_test(xml, apk, '29') + if __name__ == '__main__': - unittest.main(verbosity=2) + unittest.main(verbosity=2) |