Commit 27be3f4c authored by Ximin Luo's avatar Ximin Luo

comparators: add a test for fallback_recognizes and improve the behaviour

parent 7b8b9ae7
......@@ -101,8 +101,8 @@ def is_header_valid(buf, size, offset=0):
class CbfsFile(File):
CONTAINER_CLASS = CbfsContainer
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
size = os.stat(file.path).st_size
if size < CBFS_HEADER_SIZE or size > CBFS_MAXIMUM_FILE_SIZE:
return False
......
......@@ -125,8 +125,8 @@ class DebFile(File):
class Md5sumsFile(File):
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
return isinstance(file, ArchiveMember) and \
file.name == './md5sums' and \
isinstance(file.container.source, ArchiveMember) and \
......@@ -179,8 +179,8 @@ class DebTarContainer(TarContainer):
class DebDataTarFile(File):
CONTAINER_CLASS = DebTarContainer
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
return isinstance(file, ArchiveMember) and \
isinstance(file.container.source, ArchiveMember) and \
DebContainer.RE_DATA_TAR.match(file.container.source.name) and \
......
......@@ -31,8 +31,8 @@ logger = logging.getLogger(__name__)
class Device(File):
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
return file.is_device()
def get_device(self):
......
......@@ -146,8 +146,8 @@ def compare_directories(path1, path2, source=None):
class Directory(object):
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
return file.is_directory()
@classmethod
......
......@@ -312,8 +312,8 @@ class ElfSection(File):
def fuzzy_hash(self):
return None
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
# No file should be recognized as an elf section
return False
......
......@@ -71,8 +71,8 @@ class HiFile(File):
"""
RE_FILE_EXTENSION = re.compile(r'\.(p_|dyn_)?hi$')
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
if not HiFile.RE_FILE_EXTENSION.search(file.name):
return False
......
......@@ -34,8 +34,8 @@ class MissingFile(File):
Represents a missing file when comparing containers.
"""
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
if isinstance(file, FilesystemFile) and not os.path.lexists(file.name):
assert Config().new_file, '%s does not exist' % file.name
return True
......
......@@ -68,8 +68,8 @@ class RdsReader(Command):
class RdsFile(File):
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
if check_rds_extension(file) or \
file.container and \
check_rds_extension(file.container.source):
......
......@@ -29,8 +29,8 @@ logger = logging.getLogger(__name__)
class Symlink(File):
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
return file.is_symlink()
@property
......
......@@ -162,14 +162,27 @@ class File(object, metaclass=abc.ABCMeta):
The default test returns True if the file matches these tests:
cls.FALLBACK_FILE_EXTENSION_SUFFIX AND
cls.FALLBACK_FILE_TYPE_HEADER_PREFIX
(cls.FALLBACK_FILE_EXTENSION_SUFFIX AND cls.FILE_EXTENSION_SUFFIX) AND
(cls.FALLBACK_FILE_TYPE_HEADER_PREFIX AND cls.FILE_TYPE_HEADER_PREFIX)
We also AND-compare with the non-fallback versions to ensure that
subclasses don't "accidentally match" (e.g. IpkFile vs GzipFile).
"""
if cls.recognizes.__func__ != File.recognizes.__func__:
# If the class has overridden the default recognizes() then the
# logic below about AND-comparing with the non-fallback versions is
# not valid, they have to re-implement it
return False
all_tests = [test for test in (
(cls.FALLBACK_FILE_EXTENSION_SUFFIX,
str.endswith, file.name),
(cls.FILE_EXTENSION_SUFFIX,
str.endswith, file.name),
(cls.FALLBACK_FILE_TYPE_HEADER_PREFIX,
bytes.startswith, file.file_header),
(cls.FILE_TYPE_HEADER_PREFIX,
bytes.startswith, file.file_header),
) if test[0]] # filter out undefined tests, inc. file_type_tests if it's empty
return _run_tests(all, all_tests) if all_tests else False
......
......@@ -56,3 +56,14 @@ def specialize(file):
logger.debug("Unidentified file. Magic says: %s", file.magic_file_type)
return file
def is_direct_instance(file, cls):
# is_direct_instance(<GzipFile>, GzipFile) == True, but
# is_direct_instance(<IpkFile>, GzipFile) == False
if not isinstance(file, cls):
return False
for c in ComparatorManager().classes:
if c != cls and isinstance(file, c):
return False
return True
......@@ -151,8 +151,8 @@ class MozillaZipContainer(ZipContainer):
class MozillaZipFile(File):
CONTAINER_CLASS = MozillaZipContainer
@staticmethod
def recognizes(file):
@classmethod
def recognizes(cls, file):
# Mozilla-optimized ZIPs start with a 32-bit little endian integer
# indicating the amount of data to preload, followed by the ZIP
# central directory (with a PK\x01\x02 signature)
......
......@@ -24,17 +24,26 @@ from diffoscope.config import Config
from diffoscope.comparators.gzip import GzipFile
from diffoscope.comparators.binary import FilesystemFile
from diffoscope.comparators.missing_file import MissingFile
from diffoscope.comparators.utils.specialize import specialize
from diffoscope.comparators.utils.specialize import specialize, is_direct_instance
from ..utils.data import load_fixture, get_data
gzip1 = load_fixture('test1.gz')
gzip2 = load_fixture('test2.gz')
gzip3 = load_fixture('debian-bug-876316-control.tar.gz')
def test_identification(gzip1):
assert isinstance(gzip1, GzipFile)
assert is_direct_instance(gzip1, GzipFile)
def test_fallback_recognizes(gzip3):
# the below always-True assertion is just to document the fact that we
# should identify it correctly regardless of any bugs in file(1)
assert ("gzip" not in gzip3.magic_file_type or
"gzip" in gzip3.magic_file_type)
assert is_direct_instance(gzip3, GzipFile)
def test_no_differences(gzip1):
......
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