forked from organicmaps/organicmaps
Merge pull request #4026 from therearesomewhocallmetim/py_perm_apk_search_2
Better AAPT search
This commit is contained in:
commit
87ce64bf91
1 changed files with 96 additions and 43 deletions
|
@ -1,12 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import unittest
|
||||
|
||||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
from os.path import abspath, join, dirname
|
||||
from os import listdir
|
||||
import sys
|
||||
import unittest
|
||||
from os import listdir, path
|
||||
from os.path import abspath, dirname, isfile
|
||||
|
||||
EXPECTED_PERMISSIONS = {
|
||||
"uses-permission: android.permission.WRITE_EXTERNAL_STORAGE",
|
||||
|
@ -27,13 +27,17 @@ EXPECTED_PERMISSIONS = {
|
|||
"uses-permission: com.amazon.device.messaging.permission.RECEIVE",
|
||||
}
|
||||
|
||||
SPLIT_RE = re.compile("[\.\-]")
|
||||
APK_RE = re.compile(".*universal(?!.*?unaligned).*$")
|
||||
PROP_RE = re.compile("(?!\s*?#).*$")
|
||||
CLEAN_PERM_RE = re.compile("(name='|'$)", re.MULTILINE)
|
||||
AAPT_VERSION_PREFIX_LEN = len("Android Asset Packaging Tool, v")
|
||||
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.level = logging.DEBUG
|
||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||
|
||||
def new_lines(iterable):
|
||||
return "\n".join(iterable)
|
||||
|
||||
|
||||
def exec_shell(executable, flags):
|
||||
spell = ["{0} {1}".format(executable, flags)]
|
||||
|
@ -52,8 +56,17 @@ def exec_shell(executable, flags):
|
|||
class TestPermissions(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.omim_path = self.find_omim_path()
|
||||
self.aapt = self.find_aapt()
|
||||
self.aapt = self.find_aapt(self.get_build_tools_version())
|
||||
self.apk = self.find_apks()
|
||||
self.permissions = self.get_actual_permissions()
|
||||
self.failure_description = self.get_failure_description()
|
||||
|
||||
|
||||
def get_build_tools_version(self):
|
||||
return self.get_property(
|
||||
"propBuildToolsVersion",
|
||||
path.join(self.omim_path, "android", "gradle.properties")
|
||||
)
|
||||
|
||||
|
||||
def contains_correct_files(self, my_path):
|
||||
|
@ -66,57 +79,97 @@ class TestPermissions(unittest.TestCase):
|
|||
|
||||
|
||||
def find_omim_path(self):
|
||||
my_path = abspath(join(dirname(__file__), "..", ".."))
|
||||
my_path = abspath(path.join(dirname(__file__), "..", ".."))
|
||||
if self.contains_correct_files(my_path):
|
||||
return my_path
|
||||
raise IOError("Couldn't find a candidate for the project root path")
|
||||
|
||||
|
||||
def find_apks(self):
|
||||
apk_path = join(self.omim_path, "android", "build", "outputs", "apk")
|
||||
apk = filter(
|
||||
lambda s: "universal" in s and "unaligned" not in s,
|
||||
listdir(apk_path)
|
||||
)[0]
|
||||
apk_path = join(apk_path, apk)
|
||||
apk_path = path.join(self.omim_path, "android", "build", "outputs", "apk")
|
||||
on_disk = listdir(apk_path)
|
||||
print(on_disk)
|
||||
apks = filter(APK_RE.match, on_disk)
|
||||
if not apks:
|
||||
raise IOError("Couldn't find an APK")
|
||||
apk_path = path.join(apk_path, apks[0])
|
||||
logging.info("Using apk at {}".format(apk_path))
|
||||
|
||||
return apk_path
|
||||
|
||||
|
||||
def find_aapt(self):
|
||||
local_props_path = join(self.omim_path, "android", "local.properties")
|
||||
logging.info("local props: {}".format(local_props_path))
|
||||
with open(local_props_path) as props:
|
||||
for line in filter(lambda s: s != "" and not s.startswith("#"), map(lambda s: s.strip(), props)):
|
||||
if line.startswith("sdk.dir"):
|
||||
path = line.split("=")[1].strip()
|
||||
aapt_path = join(path, "platforms", "android-6", "tools", "aapt")
|
||||
logging.info("Using aapt at {}".format(aapt_path))
|
||||
return aapt_path
|
||||
logging.info("Couldn't find aapt")
|
||||
return None
|
||||
def get_property(self, prop, props_path):
|
||||
if not isfile(props_path):
|
||||
raise IOError("Properties file does not exist: {}".format(props_path))
|
||||
|
||||
logging.info("local props: {}".format(props_path))
|
||||
with open(props_path) as props:
|
||||
for line in filter(PROP_RE.match, props):
|
||||
if line.startswith(prop):
|
||||
return line.split("=")[1].strip()
|
||||
|
||||
raise IOError("Couldn't find property {} in file {}".format(prop, props_path))
|
||||
|
||||
|
||||
def find_aapt(self, build_tools_version):
|
||||
local_props_path = path.join(self.omim_path, "android", "local.properties")
|
||||
sdk_path = self.get_property("sdk.dir", local_props_path)
|
||||
aapt_path = self.find_aapt_in_platforms(sdk_path, build_tools_version)
|
||||
|
||||
logging.info("Using aapt at {}".format(aapt_path))
|
||||
return aapt_path
|
||||
|
||||
|
||||
def find_aapt_in_platforms(self, platforms_path, build_tools_version):
|
||||
aapts = {}
|
||||
candidates = exec_shell("find", "{} -name aapt".format(platforms_path))
|
||||
for c in candidates:
|
||||
if "build-tools/{}".format(build_tools_version) in c:
|
||||
return c
|
||||
|
||||
try:
|
||||
version = tuple(map(int, self.get_aapt_version(c)))
|
||||
aapts[version] = c
|
||||
except ValueError:
|
||||
# Do nothing, because aapt version contains non-numeric symbols
|
||||
pass
|
||||
|
||||
max_version = sorted(aapts.iterkeys(), reverse=True)[0]
|
||||
logging.info("Max aapt version: {}".format(max_version))
|
||||
|
||||
return aapts[max_version]
|
||||
|
||||
|
||||
def get_aapt_version(self, candidate):
|
||||
return SPLIT_RE.split(exec_shell(candidate, "v")[0][AAPT_VERSION_PREFIX_LEN:])
|
||||
|
||||
|
||||
def get_actual_permissions(self):
|
||||
permissions = exec_shell(self.aapt, "dump permissions {0}".format(self.apk))
|
||||
permissions = filter(
|
||||
lambda s: s.startswith("permission:") or s.startswith("uses-permission:"),
|
||||
permissions
|
||||
)
|
||||
|
||||
return set(
|
||||
map(lambda s: CLEAN_PERM_RE.sub("", s), permissions)
|
||||
)
|
||||
|
||||
|
||||
def get_failure_description(self):
|
||||
join_by_new_lines = lambda iterable: "\n".join(iterable)
|
||||
|
||||
return "Expected: {}\n\nActual: {}\n\nAdded: {}\n\nRemoved: {}".format(
|
||||
join_by_new_lines(EXPECTED_PERMISSIONS),
|
||||
join_by_new_lines(self.permissions),
|
||||
join_by_new_lines(self.permissions - EXPECTED_PERMISSIONS),
|
||||
join_by_new_lines(EXPECTED_PERMISSIONS - self.permissions)
|
||||
)
|
||||
|
||||
|
||||
def test_permissions(self):
|
||||
"""Check whether we have added or removed any permissions"""
|
||||
permissions = exec_shell(self.aapt, "dump permissions {0}".format(self.apk))
|
||||
permissions = set(
|
||||
filter(
|
||||
lambda s: s.startswith("permission:") or s.startswith("uses-permission:"),
|
||||
permissions
|
||||
)
|
||||
)
|
||||
|
||||
description = "Expected: {}\n\nActual: {}\n\n:Added: {}\n\nRemoved: {}".format(
|
||||
new_lines(EXPECTED_PERMISSIONS),
|
||||
new_lines(permissions),
|
||||
new_lines(permissions - EXPECTED_PERMISSIONS),
|
||||
new_lines(EXPECTED_PERMISSIONS - permissions)
|
||||
)
|
||||
|
||||
self.assertEqual(EXPECTED_PERMISSIONS, permissions, description)
|
||||
self.assertEqual(EXPECTED_PERMISSIONS, self.permissions, self.failure_description)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Add table
Reference in a new issue