scripts/test_macos_os_files.py
Rama main · réplica de lectura del repositorio original.
"""Test the exact guest file-restoration script embedded in the signed helper."""
import hashlib
import os
from pathlib import Path
import tempfile
import types
import unittest
SOURCE = Path(__file__).resolve().parents[1] / 'micromanager-macos/Sources/FirecrabMicroManager/OSFileRestore.swift'
SCRIPT = SOURCE.read_text().split('#"""\n', 1)[1].rsplit('"""#', 1)[0]
GUEST = types.ModuleType('os_file_restore')
exec(compile(SCRIPT, str(SOURCE), 'exec'), GUEST.__dict__)
class OSFileRestoreTests(unittest.TestCase):
def setUp(self):
self.temporary = tempfile.TemporaryDirectory()
self.addCleanup(self.temporary.cleanup)
self.root = Path(self.temporary.name) / 'target'
self.reference = Path(self.temporary.name) / 'reference'
for root in [self.root, self.reference]:
(root / 'var/lib/dpkg/info').mkdir(parents=True)
(root / 'usr/lib/example').mkdir(parents=True)
(root / 'var/lib/dpkg/status').write_text(
'Package: example\nStatus: install ok installed\nArchitecture: arm64\nVersion: 1\n\n')
self.binary = '/usr/lib/example/library.so.1'
self.link = '/usr/lib/example/library.so'
payload = b'checked Debian package contents'
(self.reference / self.binary.lstrip('/')).write_bytes(payload)
(self.reference / self.link.lstrip('/')).symlink_to('library.so.1')
digest = hashlib.md5(payload).hexdigest()
(self.root / 'var/lib/dpkg/info/example:arm64.list').write_text(self.binary + '\n' + self.link + '\n')
(self.root / 'var/lib/dpkg/info/example:arm64.md5sums').write_text(digest + ' ' + self.binary.lstrip('/') + '\n')
def restore(self):
return GUEST.restore_missing(self.root, self.reference)
def test_missing_binary_and_symlink_are_restored_and_repeat_is_noop(self):
self.assertEqual(self.restore(), 2)
self.assertEqual((self.root / self.binary.lstrip('/')).read_bytes(), b'checked Debian package contents')
self.assertEqual(os.readlink(self.root / self.link.lstrip('/')), 'library.so.1')
self.assertEqual(self.restore(), 0)
self.assertEqual((self.reference / self.binary.lstrip('/')).read_bytes(), b'checked Debian package contents')
def test_existing_files_and_configuration_are_preserved(self):
existing = self.root / self.binary.lstrip('/')
existing.write_bytes(b'local override')
config = self.root / 'etc/example.conf'
config.parent.mkdir()
config.write_text('local configuration')
manifest = self.root / 'var/lib/dpkg/info/example:arm64.list'
manifest.write_text(manifest.read_text() + '/etc/example.conf\n')
self.assertEqual(self.restore(), 1)
self.assertEqual(existing.read_bytes(), b'local override')
self.assertEqual(config.read_text(), 'local configuration')
def test_reference_version_mismatch_fails_before_replacing_missing_file(self):
path = self.reference / 'var/lib/dpkg/status'
path.write_text(path.read_text().replace('Version: 1', 'Version: 2'))
with self.assertRaisesRegex(RuntimeError, 'version does not match'):
self.restore()
self.assertFalse((self.root / self.binary.lstrip('/')).exists())
def test_reference_checksum_mismatch_and_missing_source_fail(self):
path = self.reference / self.binary.lstrip('/')
path.write_bytes(b'wrong file')
with self.assertRaisesRegex(RuntimeError, 'checksum'):
self.restore()
path.unlink()
with self.assertRaisesRegex(RuntimeError, 'cannot restore'):
self.restore()
def test_missing_parent_directory_is_created_without_recursive_copy(self):
(self.root / 'usr/lib/example').rmdir()
(self.reference / 'usr/lib/example/unlisted').write_bytes(b'do not copy')
self.assertEqual(self.restore(), 2)
self.assertFalse((self.root / 'usr/lib/example/unlisted').exists())
def test_absolute_reference_symlinks_stay_within_the_reference(self):
(self.reference / 'lib').symlink_to('/usr/lib')
self.assertEqual(GUEST.rooted_path(self.reference, '/lib/example/library.so.1'),
self.reference / 'usr/lib/example/library.so.1')
def test_destination_symlink_into_application_data_is_rejected(self):
(self.root / 'usr/lib/example').rmdir()
(self.root / 'var/lib/application').mkdir()
(self.root / 'usr/lib/example').symlink_to('/var/lib/application')
with self.assertRaisesRegex(RuntimeError, 'leaves OS binaries'):
self.restore()
self.assertEqual(list((self.root / 'var/lib/application').iterdir()), [])
def test_missing_package_manifests_use_only_the_same_version_cache(self):
for suffix in ['.list', '.md5sums']:
source = self.root / ('var/lib/dpkg/info/example:arm64' + suffix)
(self.reference / 'var/lib/dpkg/info' / source.name).write_bytes(source.read_bytes())
source.unlink()
self.assertEqual(self.restore(), 4)
self.assertTrue((self.root / 'var/lib/dpkg/info/example:arm64.md5sums').is_file())
self.assertEqual(self.restore(), 0)
def test_checksum_matched_orphan_restores_the_installed_version(self):
manifest = self.root / 'var/lib/dpkg/info/example:arm64.list'
manifest.write_text(self.binary + '\n')
lost = self.root / 'lost+found'
lost.mkdir()
source = self.reference / self.binary.lstrip('/')
(lost / '#1234').write_bytes(source.read_bytes())
source.write_bytes(b'different cached version')
status = self.reference / 'var/lib/dpkg/status'
status.write_text(status.read_text().replace('Version: 1', 'Version: 2'))
self.assertEqual(self.restore(), 1)
self.assertEqual((self.root / self.binary.lstrip('/')).read_bytes(), (lost / '#1234').read_bytes())
self.assertTrue((lost / '#1234').exists())
def test_package_mode_is_restored_after_owner_changes(self):
source = self.reference / self.binary.lstrip('/')
source.chmod(0o4755)
self.assertEqual(self.restore(), 2)
self.assertEqual((self.root / self.binary.lstrip('/')).stat().st_mode & 0o7777, 0o4755)
def test_parent_traversal_is_rejected(self):
(self.root / 'var/lib/dpkg/info/example:arm64.list').write_text('/usr/lib/../../etc/unsafe\n')
with self.assertRaisesRegex(RuntimeError, 'invalid package path'):
self.restore()
if __name__ == '__main__':
unittest.main()