Merge pull request #4026 from therearesomewhocallmetim/py_perm_apk_search_2

Better AAPT search
This commit is contained in:
burivuh 2016-08-11 20:47:49 +04:00 committed by GitHub
commit 87ce64bf91

View file

@ -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__":