On Tue, Dec 15, 2015 at 04:31:46PM +0100, Jérémy Bobbio wrote:
> Mike Hommey:
> > It would be useful for diffoscope to output differences in omni.ja files as
> > for other Zip files, instead of ending up with a diff of an hexdump.
> > 
> > The attached patch implements a minimal support for this. It however doesn't
> > look at the difference in the `preload` value.
> 
> Great! I think it's fine to just skip the preload value. It will show up
> in the fallback binary comparison if that's the only remaining difference.
> 
> I was going to merge this, but actually I have to ask: would you be kind
> enough to amend the test suite as well?

Attached.

Mike
diff --git a/diffoscope/comparators/__init__.py 
b/diffoscope/comparators/__init__.py
index ed24f63..d308b6e 100644
--- a/diffoscope/comparators/__init__.py
+++ b/diffoscope/comparators/__init__.py
@@ -72,7 +72,7 @@ from diffoscope.comparators.symlink import Symlink
 from diffoscope.comparators.text import TextFile
 from diffoscope.comparators.tar import TarFile
 from diffoscope.comparators.xz import XzFile
-from diffoscope.comparators.zip import ZipFile
+from diffoscope.comparators.zip import ZipFile, MozillaZipFile
 
 
 def bail_if_non_existing(*paths):
@@ -153,6 +153,7 @@ FILE_CLASSES = (
     TarFile,
     XzFile,
     ZipFile,
+    MozillaZipFile,
     ImageFile,
     CbfsFile,
     )
diff --git a/diffoscope/comparators/zip.py b/diffoscope/comparators/zip.py
index ecdc77b..42c9a9f 100644
--- a/diffoscope/comparators/zip.py
+++ b/diffoscope/comparators/zip.py
@@ -111,3 +111,54 @@ class ZipFile(File):
         zipinfo_difference = Difference.from_command(Zipinfo, self.path, 
other.path) or \
                              Difference.from_command(ZipinfoVerbose, 
self.path, other.path)
         return [zipinfo_difference]
+
+
+class MozillaZipCommandMixin(object):
+    def wait(self):
+        # zipinfo emits an error when reading Mozilla-optimized ZIPs,
+        # which is fine to ignore.
+        super(Zipinfo, self).wait()
+        return 0
+
+
+class MozillaZipinfo(MozillaZipCommandMixin, Zipinfo): pass
+
+
+class MozillaZipinfoVerbose(MozillaZipCommandMixin, ZipinfoVerbose): pass
+
+
+class MozillaZipContainer(ZipContainer):
+    def open_archive(self):
+        # This is gross: Monkeypatch zipfile._EndRecData to work with
+        # Mozilla-optimized ZIPs
+        _orig_EndRecData = zipfile._EndRecData
+        def _EndRecData(fh):
+            endrec = _orig_EndRecData(fh)
+            if endrec:
+                endrec[zipfile._ECD_LOCATION] = (endrec[zipfile._ECD_OFFSET] +
+                                                 endrec[zipfile._ECD_SIZE])
+            return endrec
+        zipfile._EndRecData = _EndRecData
+        result = super(MozillaZipContainer, self).open_archive()
+        zipfile._EndRecData = _orig_EndRecData
+        return result
+
+
+class MozillaZipFile(File):
+    CONTAINER_CLASS = MozillaZipContainer
+
+    @staticmethod
+    def recognizes(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)
+        with open(file.path, 'rb') as f:
+            preload = f.read(4)
+            if len(preload) == 4:
+                signature = f.read(4)
+                return signature == b'PK\x01\x02'
+
+    def compare_details(self, other, source=None):
+        zipinfo_difference = Difference.from_command(MozillaZipinfo, 
self.path, other.path) or \
+                             Difference.from_command(MozillaZipinfoVerbose, 
self.path, other.path)
+        return [zipinfo_difference]
diff --git a/tests/comparators/test_zip.py b/tests/comparators/test_zip.py
index d921b79..57255bc 100644
--- a/tests/comparators/test_zip.py
+++ b/tests/comparators/test_zip.py
@@ -21,7 +21,7 @@ import os.path
 import pytest
 from diffoscope.comparators import specialize
 from diffoscope.comparators.binary import FilesystemFile, NonExistingFile
-from diffoscope.comparators.zip import ZipFile
+from diffoscope.comparators.zip import ZipFile, MozillaZipFile
 from diffoscope.config import Config
 from conftest import tool_missing
 
@@ -65,3 +65,46 @@ def test_compare_non_existing(monkeypatch, zip1):
     difference = zip1.compare(NonExistingFile('/nonexisting', zip1))
     assert difference.source2 == '/nonexisting'
     assert difference.details[-1].source2 == '/dev/null'
+
+TEST_MOZZIP1_PATH = os.path.join(os.path.dirname(__file__), 
'../data/test1.mozzip')
+TEST_MOZZIP2_PATH = os.path.join(os.path.dirname(__file__), 
'../data/test2.mozzip')
+
+@pytest.fixture
+def mozzip1():
+    return specialize(FilesystemFile(TEST_MOZZIP1_PATH))
+
+@pytest.fixture
+def mozzip2():
+    return specialize(FilesystemFile(TEST_MOZZIP2_PATH))
+
+def test_mozzip_identification(mozzip1):
+    assert isinstance(mozzip1, MozillaZipFile)
+
+def test_mozzip_no_differences(mozzip1):
+    difference = mozzip1.compare(mozzip1)
+    assert difference is None
+
+@pytest.fixture
+def mozzip_differences(mozzip1, mozzip2):
+    return mozzip1.compare(mozzip2).details
+
+@pytest.mark.skipif(tool_missing('zipinfo'), reason='missing zip')
+def test_mozzip_metadata(mozzip_differences):
+    expected_diff = open(os.path.join(os.path.dirname(__file__), 
'../data/mozzip_zipinfo_expected_diff')).read()
+    diff = mozzip_differences[0].unified_diff
+    assert (diff.replace(TEST_MOZZIP1_PATH, 'test1.mozzip')
+                .replace(TEST_MOZZIP2_PATH, 'test2.mozzip')) == expected_diff
+
+@pytest.mark.skipif(tool_missing('zipinfo'), reason='missing zip')
+def test_mozzip_compressed_files(mozzip_differences):
+    assert mozzip_differences[1].source1 == 'dir/text'
+    assert mozzip_differences[1].source2 == 'dir/text'
+    expected_diff = open(os.path.join(os.path.dirname(__file__), 
'../data/text_ascii_expected_diff')).read()
+    assert mozzip_differences[1].unified_diff == expected_diff
+
+@pytest.mark.skipif(tool_missing('zipinfo'), reason='missing zip')
+def test_mozzip_compare_non_existing(monkeypatch, mozzip1):
+    monkeypatch.setattr(Config.general, 'new_file', True)
+    difference = mozzip1.compare(NonExistingFile('/nonexisting', mozzip1))
+    assert difference.source2 == '/nonexisting'
+    assert difference.details[-1].source2 == '/dev/null'
diff --git a/tests/data/mozzip_zipinfo_expected_diff 
b/tests/data/mozzip_zipinfo_expected_diff
new file mode 100644
index 0000000..9cc8134
--- /dev/null
+++ b/tests/data/mozzip_zipinfo_expected_diff
@@ -0,0 +1,15 @@
+@@ -1,8 +1,8 @@
+-Zip file size: 409 bytes, number of entries: 1
+-warning [test1.mozzip]:  329 extra bytes at beginning or within zipfile
++Zip file size: 552 bytes, number of entries: 1
++warning [test2.mozzip]:  472 extra bytes at beginning or within zipfile
+   (attempting to process anyway)
+-error [test1.mozzip]:  reported length of central directory is
+-  -329 bytes too long (Atari STZip zipfile?  J.H.Holm ZIPSPLIT 1.1
++error [test2.mozzip]:  reported length of central directory is
++  -472 bytes too long (Atari STZip zipfile?  J.H.Holm ZIPSPLIT 1.1
+   zipfile?).  Compensating...
+--rw-r--r--  2.0 unx      446 b- defX 10-Jan-01 00:00 dir/text
+-1 file, 446 bytes uncompressed, 269 bytes compressed:  39.7%
++-rw-r--r--  2.0 unx      671 b- defX 10-Jan-01 00:00 dir/text
++1 file, 671 bytes uncompressed, 412 bytes compressed:  38.6%
diff --git a/tests/data/test1.mozzip b/tests/data/test1.mozzip
new file mode 100644
index 
0000000000000000000000000000000000000000..d43bb74faacf04cc23be9de208935bc54bad0a49
GIT binary patch
literal 409
zcmZQzU|<OFW@Hj!7GYpw-~jRzZC<E7b>?McVAu!5AQ2c?(ii|_reqfBm!wvd1bDNu
zfn*qg&<sekfH*+2m|1X}1k-Ce;cTI^AxF#m^<BCL9&1gCUZ>gMapR`s-$<tZ)BLBr
z^PCQB`d#~FrKrql?IQi!q>BonZ=0^%zLb(>r#Q1&d*j`OtrufvB{O=(Z%v<Z?c(!;
z(`L-vD|sj2iT0C)WnS8=?#y=JlGh8bk=nAc%}ne2W67mgxaH23ZrPVp)wZuhZI(oJ
z^K>QCufa39d%J&MTCd{H<E+|q{lk^>427rG9gp^Kx+5}2&hOlsoEcWj>atH;DOc3k
zRDTqFZt}(@=tWf5k)VH^t_t>fb;~7gnYAbybIlLmvwsp_@O_D9M}JR~qcNWj>TEMt
xIyz%+<n3iUJU7aoJUwBq^xv}NMGCJk?cBX}dakpX0$bnL<5ypQ{lSPmAOW4Vm!bdw

literal 0
HcmV?d00001

diff --git a/tests/data/test2.mozzip b/tests/data/test2.mozzip
new file mode 100644
index 
0000000000000000000000000000000000000000..b0b88fb85ea11b17fd9e90a3a46cac4149d1ccd3
GIT binary patch
literal 552
zcmZQzU|<OFW@Hj!7GYpw-~jRzZ2~ek#m!-4V3^Ou02ToXg29r;03e-`S)^Z*T2T_<
z&B_K6W&}brAk6~e0L@}%!EF*uukWO@c_M}aE${2Pg7g};ND3A`7E{&M^C)N#esm%G
zdrs7+^GDvs>E`=ai!ZwKX5;ERU$5FIYzvAydaKGYdSdi*rkhu|tb#9{SFEZ1tZTZT
zaVn#hO_|>MQ-vQk@Of?ObX)0OAZ8!pr^nEpnaSCF`dZZlDOJx+Rtd~A`SSSX!n55L
zCaVTa6JN|~&%VQ}FkfV5_vHMQZ$2a~T4*MFSLnwX+fxe^<&G9D6S{phQY19wnYXz2
z%|Avn*6pm_;muH=rStHluA`a6rL*cg)26;s*mmxGjBq*sJ9$6d+t1E#?0@Wea8-|n
z<$v`jjXG<}M5I;}E96|MO|CuoPo`2jM|iPuhM|!Ae(~JNF3DOpN3YFRSvUESimIv0
zY3Z|f?p@Xj<mjCG+eTOSykQ2D(D8-;cSJ=x?Wnwbzjt~;nfcK(MW5=f-Q!AY?ei<#
zey29(o&DW=_s-9M7^w99o8QMH5p(|?G?9s#yKnObr=Oc@{;y54G5%)ya!PIHS@96z
tZ|Wy^H3u)T*%7XPqVey+_3h4^KU)7OxLW=?I=C)$AD6w(Lw)S=3IK1S*~tI^

literal 0
HcmV?d00001

-- 
2.6.1

_______________________________________________
Reproducible-builds mailing list
Reproducible-builds@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/reproducible-builds

Reply via email to