feat: add archive plugin for zip, tar and gzip comments
This commit is contained in:
parent
d2e43e7839
commit
0a19339b98
7 changed files with 302 additions and 17 deletions
139
tests/test_archive.py
Normal file
139
tests/test_archive.py
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
import gzip
|
||||
import io
|
||||
import struct
|
||||
import tarfile
|
||||
import zipfile
|
||||
import zlib
|
||||
|
||||
import pytest
|
||||
|
||||
import orvanitag.orvani as orvani
|
||||
from orvanitag.entry import Entry
|
||||
from orvanitag.orvani import OrvaniTag
|
||||
from orvanitag.errors import UnsupportedFile
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cwd(tmp_path, monkeypatch):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
return tmp_path
|
||||
|
||||
|
||||
def make_tar(path, **kwargs):
|
||||
with tarfile.open(path, 'w', **kwargs) as archive:
|
||||
data = b'packed'
|
||||
info = tarfile.TarInfo('a.txt')
|
||||
info.size = len(data)
|
||||
archive.addfile(info, io.BytesIO(data))
|
||||
|
||||
|
||||
def members(path):
|
||||
with tarfile.open(path) as archive:
|
||||
return {member.name: archive.extractfile(member).read() for member in archive.getmembers()}
|
||||
|
||||
|
||||
def roundtrip(cwd, name, *tags):
|
||||
"""Writes the tags, reads them back into a fresh entry and clears them again"""
|
||||
OrvaniTag().add(name, *tags)
|
||||
OrvaniTag().write_metadata(name, 'archive')
|
||||
expected = (cwd / orvani.CONTEXT_FILE).read_text()
|
||||
(cwd / orvani.CONTEXT_FILE).unlink()
|
||||
OrvaniTag().read_metadata(name)
|
||||
assert (cwd / orvani.CONTEXT_FILE).read_text() == expected
|
||||
|
||||
|
||||
def test_zip_comment(cwd):
|
||||
with zipfile.ZipFile(cwd / 'a.zip', 'w') as z:
|
||||
z.writestr('a.txt', 'packed')
|
||||
roundtrip(cwd, 'a.zip', '+backup', '#old')
|
||||
with zipfile.ZipFile(cwd / 'a.zip') as z:
|
||||
assert z.comment == b'otag: +backup #old'
|
||||
assert z.read('a.txt') == b'packed'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ['a.gz', 'a.tar.gz', 'a.tgz'])
|
||||
def test_gzip_comment_and_clear_restores_the_file(cwd, name):
|
||||
with gzip.GzipFile(cwd / name, 'wb', mtime=0) as file:
|
||||
file.write(b'packed')
|
||||
before = (cwd / name).read_bytes()
|
||||
|
||||
roundtrip(cwd, name, '#фантастика', 'a note')
|
||||
assert b'otag: #\xd1\x84' in (cwd / name).read_bytes()
|
||||
assert gzip.decompress((cwd / name).read_bytes()) == b'packed'
|
||||
|
||||
OrvaniTag().write_metadata(name, 'archive', clear=True)
|
||||
assert (cwd / name).read_bytes() == before
|
||||
|
||||
|
||||
def test_gzip_keeps_extra_field_and_drops_header_crc(cwd):
|
||||
header = b'\x1f\x8b\x08' + bytes([2 | 4 | 8]) + bytes(6) + struct.pack('<H', 4) + b'ab\0\0' + b'a.txt\0'
|
||||
deflate = zlib.compressobj(wbits=-15)
|
||||
body = deflate.compress(b'packed') + deflate.flush() + struct.pack('<II', zlib.crc32(b'packed'), 6)
|
||||
(cwd / 'a.gz').write_bytes(header + struct.pack('<H', zlib.crc32(header) & 0xffff) + body)
|
||||
|
||||
roundtrip(cwd, 'a.gz', '#lofi')
|
||||
data = (cwd / 'a.gz').read_bytes()
|
||||
assert data == b'\x1f\x8b\x08' + bytes([4 | 8 | 16]) + header[4:] + b'otag: #lofi\0' + body
|
||||
assert gzip.decompress(data) == b'packed'
|
||||
|
||||
|
||||
def test_tar_global_header_and_clear_restores_the_file(cwd):
|
||||
make_tar(cwd / 'a.tar')
|
||||
before = (cwd / 'a.tar').read_bytes()
|
||||
|
||||
roundtrip(cwd, 'a.tar', '+backup', 'a note')
|
||||
with tarfile.open(cwd / 'a.tar') as archive:
|
||||
assert archive.pax_headers == {'comment': 'otag: +backup a note'}
|
||||
assert members(cwd / 'a.tar') == {'a.txt': b'packed'}
|
||||
|
||||
OrvaniTag().add('a.tar', '#more')
|
||||
OrvaniTag().write_metadata('a.tar', 'archive')
|
||||
with tarfile.open(cwd / 'a.tar') as archive:
|
||||
assert archive.pax_headers == {'comment': 'otag: +backup #more a note'}
|
||||
|
||||
OrvaniTag().write_metadata('a.tar', 'archive', clear=True)
|
||||
assert (cwd / 'a.tar').read_bytes() == before
|
||||
|
||||
|
||||
def test_tar_keeps_other_global_records(cwd):
|
||||
make_tar(cwd / 'a.tar', pax_headers={'mtime': '1'})
|
||||
OrvaniTag().add('a.tar', '#lofi')
|
||||
OrvaniTag().write_metadata('a.tar', 'archive')
|
||||
OrvaniTag().write_metadata('a.tar', 'archive', clear=True)
|
||||
with tarfile.open(cwd / 'a.tar') as archive:
|
||||
assert archive.pax_headers == {'mtime': '1'}
|
||||
assert members(cwd / 'a.tar') == {'a.txt': b'packed'}
|
||||
|
||||
|
||||
def foreign_zip(path):
|
||||
with zipfile.ZipFile(path, 'w') as z:
|
||||
z.comment = b'foreign'
|
||||
|
||||
|
||||
def foreign_gzip(path):
|
||||
path.write_bytes(b'\x1f\x8b\x08' + bytes([16]) + bytes(6) + b'foreign\0' + gzip.compress(b'packed', mtime=0)[10:])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name, make', [
|
||||
('a.tar', lambda path: make_tar(path, pax_headers={'comment': '0123abcd'})), # like git archive
|
||||
('a.gz', foreign_gzip),
|
||||
('a.zip', foreign_zip),
|
||||
])
|
||||
def test_foreign_comments_are_kept(cwd, name, make):
|
||||
make(cwd / name)
|
||||
before = (cwd / name).read_bytes()
|
||||
assert OrvaniTag().read_metadata(name) == Entry()
|
||||
OrvaniTag().add(name, '#lofi')
|
||||
with pytest.raises(UnsupportedFile, match='used by another tool'):
|
||||
OrvaniTag().write_metadata(name, 'archive')
|
||||
OrvaniTag().write_metadata(name, 'archive', clear=True)
|
||||
assert (cwd / name).read_bytes() == before
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ['a.tar', 'a.gz'])
|
||||
def test_other_files_are_refused_unchanged(cwd, name):
|
||||
(cwd / name).write_bytes(gzip.compress(b'packed') if name == 'a.tar' else b'plain text')
|
||||
OrvaniTag().add(name, '#lofi')
|
||||
with pytest.raises(UnsupportedFile):
|
||||
OrvaniTag().write_metadata(name, 'archive')
|
||||
assert OrvaniTag().scan('.', False)[1]
|
||||
Loading…
Add table
Add a link
Reference in a new issue