Commit e8963b4f authored by Maria Glukhova's avatar Maria Glukhova

Improve AndroidManifest.xml comparison for APK files.

Instead of comparing both decoded and undecoded AndroidManifest.xml file,
try to first compare decoded one and resort to comparing undecoded ones
only if no difference found in the former.
parent fcbcafa1
......@@ -28,7 +28,9 @@ from diffoscope.difference import Difference
from .utils.file import File
from .utils.archive import Archive
from .utils.compare import compare_files
from .zip import Zipinfo, ZipinfoVerbose
from .missing_file import MissingFile
logger = logging.getLogger(__name__)
......@@ -45,6 +47,8 @@ class ApkContainer(Archive):
get_temporary_directory().name,
os.path.basename(self.source.name),
)
self._andmanifest = None
self._andmanifest_orig = None
logger.debug("Extracting %s to %s", self.source.name, self._unpacked)
......@@ -72,12 +76,30 @@ class ApkContainer(Archive):
continue
relpath = abspath[len(self._unpacked)+1:]
if filename == 'AndroidManifest.xml':
containing_dir = root[len(self._unpacked)+1:]
if containing_dir == 'original':
self._andmanifest_orig = relpath
if containing_dir == '':
self._andmanifest = relpath
continue
current_dir.append(relpath)
self._members.extend(current_dir)
return self
def get_android_manifest(self):
return self.get_member(self._andmanifest) \
if self._andmanifest else None
def get_original_android_manifest(self):
if self._andmanifest_orig:
return self.get_member(self._andmanifest_orig)
return MissingFile('/dev/null', self._andmanifest_orig)
def close_archive(self):
pass
......@@ -88,6 +110,37 @@ class ApkContainer(Archive):
src_path = os.path.join(self._unpacked, member_name)
return src_path
def compare_manifests(self, other):
my_android_manifest = self.get_android_manifest()
other_android_manifest = other.get_android_manifest()
comment = None
diff_manifests = None
if my_android_manifest and other_android_manifest:
diff_manifests = compare_files(my_android_manifest,
other_android_manifest)
if diff_manifests is None:
comment = 'No difference found for decoded AndroidManifest.xml'
else:
comment = 'No decoded AndroidManifest.xml found ' + \
'for one of the APK files.'
if diff_manifests:
return diff_manifests
diff_manifests = compare_files(self.get_original_android_manifest(),
other.get_original_android_manifest())
if diff_manifests is not None:
diff_manifests.add_comment(comment)
return diff_manifests
def compare(self, other, source=None):
differences = []
try:
differences.append(self.compare_manifests(other))
except AttributeError: # no apk-specific methods, e.g. MissingArchive
pass
differences.extend(super().compare(other, source=source))
return differences
class ApkFile(File):
RE_FILE_TYPE = re.compile(r'^(Java|Zip) archive data.*\b')
RE_FILE_EXTENSION = re.compile(r'\.apk$')
......
......@@ -27,6 +27,7 @@ from utils.nonexisting import assert_non_existing
apk1 = load_fixture('test1.apk')
apk2 = load_fixture('test2.apk')
apk3 = load_fixture('test3.apk')
def test_identification(apk1):
assert isinstance(apk1, ApkFile)
......@@ -39,6 +40,10 @@ def test_no_differences(apk1):
def differences(apk1, apk2):
return apk1.compare(apk2).details
@pytest.fixture
def differences2(apk1, apk3):
return apk1.compare(apk3).details
@skip_unless_tools_exist('apktool', 'zipinfo')
def test_compare_non_existing(monkeypatch, apk1):
assert_non_existing(monkeypatch, apk1)
......@@ -52,12 +57,26 @@ def test_zipinfo(differences):
@skip_unless_tools_exist('apktool', 'zipinfo')
def test_android_manifest(differences):
assert differences[2].source1 == 'AndroidManifest.xml'
assert differences[2].source2 == 'AndroidManifest.xml'
assert differences[1].source1 == 'AndroidManifest.xml'
assert differences[1].source2 == 'AndroidManifest.xml'
expected_diff = get_data('apk_manifest_expected_diff')
assert differences[2].unified_diff == expected_diff
assert differences[1].unified_diff == expected_diff
@skip_unless_tools_exist('apktool', 'zipinfo')
def test_apk_metadata_source(differences):
assert differences[1].source1 == 'APK metadata'
assert differences[1].source2 == 'APK metadata'
assert differences[2].source1 == 'APK metadata'
assert differences[2].source2 == 'APK metadata'
@skip_unless_tools_exist('apktool', 'zipinfo')
def test_skip_undecoded_android_manifest(differences):
assert not any(difference.source1 == 'original/AndroidManifest.xml'
for difference in differences)
assert not any(difference.source2 == 'original/AndroidManifest.xml'
for difference in differences)
@skip_unless_tools_exist('apktool', 'zipinfo')
def test_no_android_manifest(differences2):
assert differences2[1].source1 == 'original/AndroidManifest.xml'
assert differences2[1].source2 == '/dev/null'
assert differences2[1].comment == 'No decoded AndroidManifest.xml ' \
'found for one of the APK files.'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment