Skip to content

Commit

Permalink
Merge pull request #10 from vidstige/better-tests
Browse files Browse the repository at this point in the history
BSD support
  • Loading branch information
vidstige authored Jan 20, 2024
2 parents 6afc7dc + 16efcd4 commit 284b096
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 23 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ __pycache__/

/*.egg-info

/test_data/
/test_data/file0.txt
/test_data/file1.bin
/test_data/test.a

/dist/
/build/
26 changes: 18 additions & 8 deletions ar/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,22 @@ def lookup(data, offset):
return data[start:end - 1].decode()


def load(stream):
actual = stream.read(len(MAGIC))
if actual != MAGIC:
raise ArchiveError(f"Unexpected magic: {actual}")
ENTRY_FORMAT = '16s12s6s6s8s10sbb'


fmt = '16s12s6s6s8s10sbb'
def load(stream):
magic = stream.read(len(MAGIC))
if not isinstance(magic, bytes):
raise ArchiveError("Stream must be binary")
if magic != MAGIC:
raise ArchiveError(f"Unexpected magic: {magic!r}")

lookup_data = None
while True:
buffer = stream.read(struct.calcsize(fmt))
if len(buffer) < struct.calcsize(fmt):
buffer = stream.read(struct.calcsize(ENTRY_FORMAT))
if len(buffer) < struct.calcsize(ENTRY_FORMAT):
break
name, timestamp, owner, group, mode, size, _, _ = struct.unpack(fmt, buffer)
name, timestamp, owner, group, mode, size, _, _ = struct.unpack(ENTRY_FORMAT, buffer)
del timestamp, owner, group, mode
name = name.decode().rstrip()
size = int(size.decode().rstrip())
Expand All @@ -103,6 +106,13 @@ def load(stream):
offset = stream.tell()
stream.seek(pad(size, 2), 1)
yield ArPath(expanded_name, offset, size)
elif name.startswith('#1/'):
# BSD long filenames
name_length = int(name[len('#1/'):])
long_name = stream.read(name_length).rstrip(b'\00').decode()
offset = stream.tell()
stream.seek(pad(size - name_length, 2), 1)
yield ArPath(long_name, offset, size - name_length)
else:
offset = stream.tell()
stream.seek(pad(size, 2), 1)
Expand Down
15 changes: 15 additions & 0 deletions ar/tests/test_bad_archive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from pathlib import Path

import pytest

from ar import Archive, ArchiveError


BAD_ARCHIVE = Path('test_data/bad.a')


def test_bad_file():
with BAD_ARCHIVE.open('rb') as f:
with pytest.raises(ArchiveError) as exception_info:
Archive(f)
assert str(exception_info.value) == "Unexpected magic: b'nope, no'"
46 changes: 46 additions & 0 deletions ar/tests/test_bsd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# pylint: disable=redefined-outer-name
import subprocess
from pathlib import Path

from ar import Archive, ArchiveError


ARCHIVE = Path('test_data/bsd.a')


def test_list():
with ARCHIVE.open('rb') as f:
archive = Archive(f)
actual = [entry.name for entry in archive]
expected = ['file0.txt', 'file1.bin', 'long-filename.txt']
assert expected == actual


def test_read_content():
with ARCHIVE.open('rb') as f:
archive = Archive(f)
file0 = archive.open('file0.txt')
assert file0.read(1) == 'H'
assert file0.read() == 'ello'


def test_read_content_long_filename():
with ARCHIVE.open('rb') as f:
archive = Archive(f)
file0 = archive.open('long-filename.txt')
assert file0.read(1) == 'l'
assert file0.read() == 'ong filename\n'

def test_read_binary():
with ARCHIVE.open('rb') as f:
archive = Archive(f)
file0 = archive.open('file1.bin', 'rb')
assert file0.read() == b'\xc3\x28'


def test_seek_basic():
with ARCHIVE.open('rb') as f:
archive = Archive(f)
file0 = archive.open('file0.txt')
file0.seek(1)
assert file0.read(3) == 'ell'
37 changes: 37 additions & 0 deletions ar/tests/test_linux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# pylint: disable=redefined-outer-name
import subprocess
from pathlib import Path

from ar import Archive, ArchiveError


ARCHIVE = Path('test_data/linux.a')


def test_list():
with ARCHIVE.open('rb') as f:
archive = Archive(f)
assert ['file0.txt', 'file1.bin'] == [entry.name for entry in archive]


def test_read_content():
with ARCHIVE.open('rb') as f:
archive = Archive(f)
file0 = archive.open('file0.txt')
assert file0.read(1) == 'H'
assert file0.read() == 'ello'


def test_read_binary():
with ARCHIVE.open('rb') as f:
archive = Archive(f)
file0 = archive.open('file1.bin', 'rb')
assert file0.read() == b'\xc3\x28'


def test_seek_basic():
with ARCHIVE.open('rb') as f:
archive = Archive(f)
file0 = archive.open('file0.txt')
file0.seek(1)
assert file0.read(3) == 'ell'
14 changes: 0 additions & 14 deletions ar/tests/test_ar.py → ar/tests/test_roundtrip.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@ def simple_archive():
return TEST_DATA / 'test.a'


@pytest.fixture
def bad_archive():
path = TEST_DATA / 'bad.a'
path.write_bytes(b'nope, not an ar file')
return path


def test_list(simple_archive):
with simple_archive.open('rb') as f:
archive = Archive(f)
Expand Down Expand Up @@ -55,10 +48,3 @@ def test_seek_basic(simple_archive):
file0 = archive.open('file0.txt')
file0.seek(1)
assert file0.read(3) == 'ell'


def test_bad_file(bad_archive):
with bad_archive.open('rb') as f:
with pytest.raises(ArchiveError) as exception_info:
Archive(f)
assert str(exception_info.value) == "Unexpected magic: b'nope, no'"
15 changes: 15 additions & 0 deletions ar/tests/test_wrong_mody.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from pathlib import Path

import pytest

from ar import Archive, ArchiveError


BAD_ARCHIVE = Path('test_data/bad.a')


def test_bad_file():
with BAD_ARCHIVE.open('rt') as f:
with pytest.raises(ArchiveError) as exception_info:
Archive(f)
assert str(exception_info.value) == "Stream must be binary"
1 change: 1 addition & 0 deletions test_data/bad.a
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nope, not an ar file
Binary file added test_data/bsd.a
Binary file not shown.
5 changes: 5 additions & 0 deletions test_data/linux.a
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
!<arch>
file0.txt/ 0 0 0 644 5 `
Hello
file1.bin/ 0 0 0 644 2 `
�(

0 comments on commit 284b096

Please sign in to comment.