Add reversible old-identity migration and freeze corrected release identities
这个提交包含在:
1 父节点
47f6e55ecd
当前提交
767bba8ac1
6 个文件已更改
+283
-26
没有与搜索条件相匹配的文件
@@ -0,0 +1,91 @@
|
||||
import importlib.util, json, pathlib, plistlib, subprocess, tempfile, unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
spec = importlib.util.spec_from_file_location('product_identity', ROOT / 'scripts/product_identity.py')
|
||||
identity = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(identity)
|
||||
|
||||
class IdentityMigrationTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory(prefix='beautygirl-identity-test.')
|
||||
self.volume = pathlib.Path(self.tmp.name)
|
||||
self.legacy = identity.PRODUCT['legacyProducts'][0]
|
||||
self.old = self.volume / pathlib.Path(self.legacy['installationPath']).relative_to('/')
|
||||
(self.old / 'Contents/PlugIns' / self.legacy['pluginName'] / 'Contents').mkdir(parents=True)
|
||||
(self.old / 'Contents/Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier': self.legacy['bundleIdentifier']}))
|
||||
(self.old / 'Contents/PlugIns' / self.legacy['pluginName'] / 'Contents/Info.plist').write_bytes(plistlib.dumps({'ProPlugPlugInList': [{'uuid': identity.PRODUCT['filterUUID']}]}))
|
||||
self.signature = patch.object(identity.subprocess, 'run', return_value=subprocess.CompletedProcess([], 0, '', 'TeamIdentifier=B3W25FX5H5\n'))
|
||||
self.signature.start()
|
||||
self.migration = identity.LegacyMigration(self.volume / 'backups', volume=self.volume)
|
||||
|
||||
def tearDown(self):
|
||||
self.signature.stop()
|
||||
self.tmp.cleanup()
|
||||
|
||||
def test_success_removes_only_validated_old_product(self):
|
||||
original = (self.old / 'Contents/Info.plist').read_bytes()
|
||||
unrelated = self.old.parent / 'Unrelated.app'
|
||||
unrelated.mkdir()
|
||||
calls = []
|
||||
self.migration.relocate(calls.append)
|
||||
self.assertFalse(self.old.exists())
|
||||
self.assertEqual(len(calls), 1)
|
||||
self.assertEqual((self.migration.moved[0][2] / 'Contents/Info.plist').read_bytes(), original)
|
||||
self.assertEqual(len(self.migration.cleanup()), 1)
|
||||
self.assertTrue(unrelated.exists())
|
||||
|
||||
def test_failed_new_install_restores_old_bytes_and_registration(self):
|
||||
original = (self.old / 'Contents/Info.plist').read_bytes()
|
||||
self.migration.relocate(lambda p: None)
|
||||
calls = []
|
||||
self.migration.rollback(calls.append)
|
||||
self.assertEqual((self.old / 'Contents/Info.plist').read_bytes(), original)
|
||||
self.assertEqual(calls, [self.old / 'Contents/PlugIns' / self.legacy['pluginName']])
|
||||
|
||||
def test_wrong_bundle_id_is_never_moved(self):
|
||||
(self.old / 'Contents/Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier': 'com.unrelated.product'}))
|
||||
with self.assertRaisesRegex(RuntimeError, 'identity mismatch'):
|
||||
self.migration.relocate(lambda p: self.fail('Must not deregister another product'))
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_wrong_filter_uuid_is_never_moved(self):
|
||||
(self.old / 'Contents/PlugIns' / self.legacy['pluginName'] / 'Contents/Info.plist').write_bytes(plistlib.dumps({'ProPlugPlugInList': []}))
|
||||
with self.assertRaisesRegex(RuntimeError, 'filter UUID'):
|
||||
self.migration.relocate(lambda p: self.fail('Must not deregister another filter'))
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_invalid_signature_keeps_original(self):
|
||||
with patch.object(identity.subprocess, 'run', side_effect=subprocess.CalledProcessError(1, 'codesign')):
|
||||
with self.assertRaises(subprocess.CalledProcessError):
|
||||
self.migration.relocate(lambda p: self.fail('Unverified APP must remain'))
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_existing_backup_cannot_be_overwritten(self):
|
||||
backup = self.volume / 'backups' / ('legacy-' + self.legacy['name'] + '.app')
|
||||
backup.mkdir(parents=True)
|
||||
with self.assertRaisesRegex(RuntimeError, 'already exists'):
|
||||
self.migration.relocate(lambda p: self.fail('Recovery copy must be preserved'))
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_failed_deregistration_keeps_old_installation(self):
|
||||
def unavailable(path):
|
||||
raise RuntimeError('registry unavailable')
|
||||
with self.assertRaisesRegex(RuntimeError, 'registry unavailable'):
|
||||
self.migration.relocate(unavailable)
|
||||
self.assertTrue(self.old.exists())
|
||||
|
||||
def test_project_and_legacy_template_share_stable_filter_identity(self):
|
||||
p = identity.PRODUCT
|
||||
pbx = (ROOT / p['xcodeProject'] / 'project.pbxproj').read_text()
|
||||
self.assertEqual(pbx.count('PRODUCT_BUNDLE_IDENTIFIER = ' + p['bundleIdentifier'] + ';'), 2)
|
||||
self.assertEqual(pbx.count('PRODUCT_BUNDLE_IDENTIFIER = ' + p['pluginBundleIdentifier'] + ';'), 2)
|
||||
self.assertNotIn('beatygril', pbx)
|
||||
plugin = plistlib.loads((ROOT / p['sourceDirectory'] / 'Plugin/Info.plist').read_bytes())
|
||||
self.assertEqual(plugin['ProPlugPlugInList'][0]['className'], p['pluginClass'])
|
||||
self.assertEqual(plugin['ProPlugPlugInList'][0]['uuid'], p['filterUUID'])
|
||||
self.assertEqual(plugin['CFBundleShortVersionString'], p['version'])
|
||||
self.assertIn('pluginUUID="' + p['filterUUID'] + '"', (ROOT / 'Templates/大鱼人像美化/大鱼人像美化.moef').read_text())
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
在新工单中引用
屏蔽一个用户