58 lines
2.8 KiB
Python
58 lines
2.8 KiB
Python
"""Verify staging/archive/manifest equality, then extract into a fresh empty directory."""
|
|
from pathlib import Path, PurePosixPath
|
|
import hashlib, json, stat, zipfile
|
|
|
|
root = Path(__file__).resolve().parent / 'package46'
|
|
archives = list((root / 'dist').glob('*.zip'))
|
|
assert len(archives) == 1
|
|
archive = archives[0]
|
|
sha = lambda data: hashlib.sha256(data).hexdigest()
|
|
checksum_lines = Path(str(archive) + '.sha256').read_text().splitlines()
|
|
assert len(checksum_lines) == 1
|
|
expected, checksum_name = checksum_lines[0].split(' ', 1)
|
|
assert checksum_name == archive.name
|
|
assert sha(archive.read_bytes()) == expected
|
|
stage = archive.with_suffix('')
|
|
assert stage.is_dir()
|
|
prefix = stage.name + '/'
|
|
|
|
def inventory(directory):
|
|
return {p.relative_to(directory).as_posix(): sha(p.read_bytes())
|
|
for p in directory.rglob('*') if p.is_file()}
|
|
|
|
stage_inventory = inventory(stage)
|
|
manifest = {}
|
|
for line in (stage / 'SHA256SUMS.txt').read_text().splitlines():
|
|
digest, name = line.split(' ', 1)
|
|
path = PurePosixPath(name)
|
|
assert name and not path.is_absolute() and '..' not in path.parts and '\\' not in name and ':' not in name
|
|
assert name not in manifest
|
|
assert stage_inventory[name] == digest
|
|
manifest[name] = digest
|
|
assert set(stage_inventory) == set(manifest) | {'SHA256SUMS.txt'}
|
|
target = root / 'extracted'
|
|
assert target.is_dir() and not list(target.iterdir()), 'Never overwrite a previous extraction'
|
|
with zipfile.ZipFile(archive) as z:
|
|
names = z.namelist()
|
|
assert len(names) == len({n.casefold() for n in names}) == len(stage_inventory)
|
|
assert set(names) == {prefix + n for n in stage_inventory}
|
|
for entry in z.infolist():
|
|
path = PurePosixPath(entry.filename)
|
|
assert not path.is_absolute() and '..' not in path.parts and '\\' not in entry.filename and ':' not in entry.filename
|
|
assert entry.filename.startswith(prefix) and not entry.is_dir()
|
|
assert not stat.S_ISLNK(entry.external_attr >> 16)
|
|
assert sha(z.read(entry)) == stage_inventory[entry.filename[len(prefix):]]
|
|
assert z.testzip() is None
|
|
z.extractall(target)
|
|
bundle = target / stage.name
|
|
assert inventory(bundle) == stage_inventory
|
|
package = json.loads((bundle / 'package.json').read_text(encoding='utf-8-sig'))
|
|
assert package['version'] == '0.2.0-preview.save-diagnostics.20261001'
|
|
assert package['target'] == 'x86_64-pc-windows-gnullvm'
|
|
report = {'status': 'PASS', 'archive': archive.name, 'archive_bytes': archive.stat().st_size,
|
|
'archive_sha256': expected, 'files': len(stage_inventory), 'bundle': str(bundle),
|
|
'stage_archive_extraction_exact': True, 'sha256': stage_inventory}
|
|
with (root / 'extraction-check.json').open('x', encoding='utf-8') as f:
|
|
json.dump(report, f, indent=2); f.write('\n')
|
|
print(json.dumps({k: v for k, v in report.items() if k != 'sha256'}, indent=2))
|