Support safe installation into existing GPT partitions and Btrfs layouts
This commit is contained in:
+319
-1
@@ -43,6 +43,12 @@ class FakeProbe:
|
||||
def physical_disk(self, path):
|
||||
return path == '/dev/vda'
|
||||
|
||||
def verify_identity(self, path, node):
|
||||
return None
|
||||
|
||||
def partition_number(self, path, node):
|
||||
return {'/dev/vda1': 1, '/dev/vda2': 2, '/dev/vda3': 3}.get(path, 0)
|
||||
|
||||
|
||||
class FakeRunner:
|
||||
def __init__(self, root, fail=None):
|
||||
@@ -68,11 +74,313 @@ class FakeRunner:
|
||||
(self.root / 'etc/systemd/system/getty@tty1.service.d/90-hydra-live.conf').touch()
|
||||
(self.root / 'etc/sudoers.d/90-hydra-live').touch()
|
||||
(self.root / 'boot/vmlinuz-6.12.1').touch()
|
||||
if argv[0] == 'unsquashfs' and '-lln' in argv:
|
||||
return 'drwxr-xr-x 0/0 0 2026-10-13 12:00 squashfs-root\n-rw-r--r-- 0/0 2048 2026-10-13 12:00 squashfs-root/file\n'
|
||||
if argv[0] == 'blkid':
|
||||
return '11111111-2222-3333-4444-555555555555\n'
|
||||
if 'PARTUUID' in argv:
|
||||
number = '1' if argv[-1].endswith('1') else '2'
|
||||
return 'aaaaaaaa-bbbb-cccc-dddd-' + number.zfill(12) + '\n'
|
||||
return 'ABCD-1234\n' if argv[-1].endswith('1') else '11111111-2222-3333-4444-555555555555\n'
|
||||
return ''
|
||||
|
||||
|
||||
def partition_topology(*, sibling_mounted=False):
|
||||
esp = disk('/dev/vda1', kind='part')
|
||||
root = disk('/dev/vda2', kind='part')
|
||||
sibling = disk('/dev/vda3', kind='part', mounts=['/other'] if sibling_mounted else [])
|
||||
for node, start, size, number in ((esp, 2048, 1024**3, 1),
|
||||
(root, 2099200, 8*1024**3, 2),
|
||||
(sibling, 18876416, 4*1024**3, 3)):
|
||||
node.update(start=start, size=size, partuuid=f'aaaaaaaa-bbbb-cccc-dddd-{number:012d}',
|
||||
parttype='c12a7328-f81f-11d2-ba4b-00a0c93ec93b' if number == 1 else
|
||||
'0fc63daf-8483-4772-8e79-3d69d8477de4',
|
||||
fstype='vfat' if number == 1 else 'ext4', uuid='ABCD-1234' if number == 1 else
|
||||
f'11111111-2222-3333-4444-{number:012d}')
|
||||
return topology(disk(children=[esp, root, sibling]))
|
||||
|
||||
|
||||
def partition_request(**values):
|
||||
return i.Request(None, 'emil', root_partition='/dev/vda2', esp_partition='/dev/vda1',
|
||||
**values)
|
||||
|
||||
|
||||
class PartitionGuardTests(unittest.TestCase):
|
||||
def test_explicit_partitions_allow_unrelated_mounted_sibling(self):
|
||||
self.assertEqual(i.validate_request(partition_request(), FakeProbe(partition_topology(sibling_mounted=True))), '/dev/vda2')
|
||||
|
||||
def test_modes_and_confirmations_are_unambiguous(self):
|
||||
for changes in ({'disk': '/dev/vda'}, {'esp_partition': None}, {'root_fs': 'xfs'},
|
||||
{'confirm_erase': '/dev/vda'}, {'confirm_format': '/dev/vda3', 'execute': True}):
|
||||
request = partition_request()
|
||||
for key, value in changes.items():
|
||||
setattr(request, key, value)
|
||||
with self.subTest(changes=changes), self.assertRaises(i.InstallError):
|
||||
i.validate_request(request, FakeProbe(partition_topology()))
|
||||
self.assertEqual(i.validate_request(partition_request(execute=True, confirm_format='/dev/vda2'),
|
||||
FakeProbe(partition_topology())), '/dev/vda2')
|
||||
|
||||
def test_selected_mount_holder_alias_overlap_or_bad_metadata_refused(self):
|
||||
for mutation in ('mount', 'holder', 'same', 'bounds', 'overlap', 'missing', 'not-fat', 'not-esp', 'nested'):
|
||||
topo = partition_topology()
|
||||
req = partition_request()
|
||||
if mutation == 'mount': topo.protect('/dev/vda2')
|
||||
if mutation == 'holder':
|
||||
topo.nodes['/dev/holder'] = disk('/dev/holder', kind='crypt')
|
||||
topo.edges['/dev/holder'] = set(); topo.parents['/dev/holder'] = set()
|
||||
topo.link('/dev/holder', '/dev/vda2')
|
||||
if mutation == 'same': req.esp_partition = req.root_partition
|
||||
if mutation == 'bounds': topo.nodes['/dev/vda2']['size'] = 40*1024**3
|
||||
if mutation == 'overlap': topo.nodes['/dev/vda2']['start'] = 2048
|
||||
if mutation == 'missing': del topo.nodes['/dev/vda2']['partuuid']
|
||||
if mutation == 'not-fat': topo.nodes['/dev/vda1']['fstype'] = 'ext4'
|
||||
if mutation == 'not-esp': topo.nodes['/dev/vda1']['parttype'] = '0fc63daf-8483-4772-8e79-3d69d8477de4'
|
||||
if mutation == 'nested': topo.parents['/dev/vda2'].add('/dev/vda3')
|
||||
with self.subTest(mutation=mutation), self.assertRaises(i.InstallError):
|
||||
i.validate_request(req, FakeProbe(topo))
|
||||
|
||||
def test_malformed_optional_storage_metadata_is_rejected(self):
|
||||
for field, value in (('partuuid', 123), ('uuid', []), ('parttype', {}), ('start', True)):
|
||||
nodes = partition_topology().nodes
|
||||
nodes['/dev/vda1'][field] = value
|
||||
with self.subTest(field=field), self.assertRaises(i.InstallError):
|
||||
topo = topology(nodes['/dev/vda'])
|
||||
i.validate_request(partition_request(), FakeProbe(topo))
|
||||
|
||||
def test_btrfs_loop_backing_members_preserve_eligible_root(self):
|
||||
nodes = list(partition_topology().nodes.values())[:1]
|
||||
loop = disk('/dev/loop0', kind='loop', mounts=['/scratch']); loop['maj:min']='7:0'
|
||||
media = disk('/dev/sr0', kind='rom', mounts=['/live']); media['maj:min']='11:0'
|
||||
nodes.extend([loop,media])
|
||||
btrfs={'source':'/dev/vda3[/home]', 'target':'/other', 'fstype':'btrfs','maj:min':'0:37','options':'rw'}
|
||||
scratch={'source':'/dev/loop0','target':'/scratch','fstype':'ext4','maj:min':'7:0','options':'rw'}
|
||||
live={'source':'/dev/sr0','target':'/live','fstype':'iso9660','maj:min':'11:0','options':'ro'}
|
||||
class ReadOnlyRunner:
|
||||
def run(self, argv):
|
||||
if argv[0]=='lsblk': return json.dumps({'blockdevices':nodes})
|
||||
if argv[0]=='btrfs':
|
||||
return 'Label: none uuid: abc\n Total devices 1 FS bytes used 4096\n devid 1 size 999 used 1 path /dev/vda3\n'
|
||||
return json.dumps({'filesystems':[live] if '--target' in argv else [btrfs,scratch,live]})
|
||||
probe=i.SystemProbe(ReadOnlyRunner())
|
||||
probe.physical_disk=lambda path: path=='/dev/vda'
|
||||
probe.verify_identity=lambda path,node: None
|
||||
probe.partition_number=lambda path,node: 1 if path.endswith('1') else 2
|
||||
with patch.object(Path,'is_file',return_value=True), patch.object(i.SystemProbe,'_loop_backing_identity',return_value='0:37'):
|
||||
topo=probe.snapshot(i.DEFAULT_PAYLOAD)
|
||||
probe.snapshot=lambda payload:topo
|
||||
self.assertIn('/dev/vda3',topo.parents['/dev/loop0'])
|
||||
self.assertEqual(i.validate_request(partition_request(),probe),'/dev/vda2')
|
||||
|
||||
def test_partition_plan_formats_only_root_and_preserves_efi(self):
|
||||
req = partition_request()
|
||||
plan = i.render_plan(req, '/dev/vda2', '/mnt/target')
|
||||
self.assertIn('mkfs.ext4', plan)
|
||||
self.assertIn('--no-nvram', plan)
|
||||
self.assertNotIn('--removable', plan)
|
||||
self.assertNotIn('parted', plan)
|
||||
self.assertNotIn('mkfs.vfat', plan)
|
||||
self.assertIn('--confirm-format /dev/vda2', plan)
|
||||
|
||||
def test_btrfs_dry_run_includes_real_mount_layout(self):
|
||||
plan=i.render_plan(partition_request(root_fs='btrfs'),'/dev/vda2','/mnt/target')
|
||||
self.assertIn('subvol=@root',plan)
|
||||
self.assertIn('subvol=@home',plan)
|
||||
self.assertIn('subvol=@snapshots',plan)
|
||||
self.assertLess(plan.index('ro,nosuid,nodev,noexec'),plan.index('mkfs.btrfs'))
|
||||
|
||||
def test_cli_accepts_partition_mode_without_disk(self):
|
||||
output=io.StringIO()
|
||||
with patch.object(i,'SystemProbe',return_value=FakeProbe(partition_topology())), contextlib.redirect_stdout(output):
|
||||
self.assertEqual(i.main(['--root-partition','/dev/vda2','--esp-partition','/dev/vda1','--user','emil']),0)
|
||||
self.assertIn('format ONLY /dev/vda2',output.getvalue())
|
||||
|
||||
def test_btrfs_mount_protects_all_members_without_protecting_siblings(self):
|
||||
topo = partition_topology(sibling_mounted=True)
|
||||
class ReadOnlyRunner:
|
||||
def run(self, argv):
|
||||
return ("Label: none uuid: 11111111-2222-3333-4444-555555555555\n"
|
||||
"\tTotal devices 1 FS bytes used 4096\n"
|
||||
"\tdevid 1 size 4294967296 used 4096 path /dev/vda3\n")
|
||||
probe = i.SystemProbe(ReadOnlyRunner())
|
||||
probe._block_source({'source':'/dev/vda3[/home]', 'target':'/other', 'fstype':'btrfs',
|
||||
'maj:min':'0:37', 'options':'rw'}, topo)
|
||||
self.assertIn('/dev/vda3', topo.protected)
|
||||
self.assertEqual(i.validate_request(partition_request(), FakeProbe(topo)), '/dev/vda2')
|
||||
|
||||
def test_multi_member_btrfs_mount_protects_selected_target(self):
|
||||
class ReadOnlyRunner:
|
||||
def run(self, argv):
|
||||
return ('Label: none uuid: abc\n Total devices 2 FS bytes used 4096\n'
|
||||
' devid 1 size 999 used 1 path /dev/vda3\n'
|
||||
' devid 2 size 999 used 1 path /dev/vda2\n')
|
||||
topo = partition_topology()
|
||||
i.SystemProbe(ReadOnlyRunner())._block_source(
|
||||
{'source':'/dev/vda3', 'target':'/other', 'fstype':'btrfs', 'maj:min':'0:37'}, topo)
|
||||
with self.assertRaises(i.InstallError):
|
||||
i.validate_request(partition_request(), FakeProbe(topo))
|
||||
|
||||
def test_conflicting_duplicate_partition_metadata_is_rejected(self):
|
||||
node = disk('/dev/vda1', kind='part', pkname='/dev/vda')
|
||||
other = dict(node, start=9999)
|
||||
with self.assertRaises(i.InstallError):
|
||||
topology(disk(children=[dict(node, start=2048)]), other)
|
||||
|
||||
def test_btrfs_source_not_in_reported_members_is_rejected(self):
|
||||
class ReadOnlyRunner:
|
||||
def run(self, argv):
|
||||
return 'Label: none uuid: abc\n Total devices 1 FS bytes used 4096\n devid 1 size 999 used 1 path /dev/vda3\n'
|
||||
with self.assertRaises(i.InstallError):
|
||||
i.SystemProbe(ReadOnlyRunner())._block_source(
|
||||
{'source':'/dev/vda2', 'target':'/other', 'fstype':'btrfs', 'maj:min':'0:37'}, partition_topology())
|
||||
|
||||
def test_incomplete_btrfs_members_fail_closed(self):
|
||||
class ReadOnlyRunner:
|
||||
def run(self, argv):
|
||||
return 'Label: none uuid: abc\n Total devices 2 FS bytes used 4096\n devid 1 size 999 used 1 path /dev/vda3\n'
|
||||
with self.assertRaises(i.InstallError):
|
||||
i.SystemProbe(ReadOnlyRunner())._block_source(
|
||||
{'source':'/dev/vda3', 'target':'/other', 'fstype':'btrfs', 'maj:min':'0:37'}, partition_topology())
|
||||
|
||||
|
||||
class PartitionPipelineTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory(); self.addCleanup(self.tmp.cleanup)
|
||||
self.root = Path(self.tmp.name)/'target'; self.root.mkdir()
|
||||
self.probe = FakeProbe(partition_topology(sibling_mounted=True))
|
||||
self.request = partition_request(execute=True, confirm_format='/dev/vda2')
|
||||
|
||||
def install(self, runner):
|
||||
i.install(self.request, runner, self.probe, self.root, 'test-password', io.StringIO())
|
||||
|
||||
def test_existing_mode_formats_root_only_and_records_owned_layout(self):
|
||||
runner = FakeRunner(self.root)
|
||||
self.install(runner)
|
||||
self.assertFalse(any(c[0] in ('parted', 'partprobe', 'mkfs.vfat') for c in runner.calls))
|
||||
self.assertEqual([c[-1] for c in runner.calls if c[0].startswith('mkfs.')], ['/dev/vda2'])
|
||||
self.assertFalse(any('--removable' in c for c in runner.calls))
|
||||
self.assertFalse(any(c[0] == 'efibootmgr' for c in runner.calls))
|
||||
ro = next(n for n,c in enumerate(runner.calls) if c[0]=='mount' and 'ro,nosuid,nodev,noexec' in c)
|
||||
format_at = next(n for n,c in enumerate(runner.calls) if c[0]=='mkfs.ext4')
|
||||
self.assertLess(ro, format_at)
|
||||
marker = json.loads((self.root/'etc/hydra/storage.json').read_text())
|
||||
self.assertEqual(marker['format'], 1); self.assertEqual(marker['filesystem'], 'ext4')
|
||||
self.assertEqual(marker['owned_efi_path'], 'EFI/HydraOS')
|
||||
self.assertEqual(stat.S_IMODE((self.root/'etc/hydra/storage.json').stat().st_mode), 0o600)
|
||||
self.assertNotIn('root_subvolume', marker)
|
||||
self.assertIn('GRUB_DISABLE_OS_PROBER=true', (self.root/'etc/default/grub').read_text())
|
||||
|
||||
def test_esp_collision_or_insufficient_space_prevents_formatting(self):
|
||||
for problem in ('collision', 'space'):
|
||||
runner = FakeRunner(self.root)
|
||||
real_run = runner.run
|
||||
def run(argv, **kw):
|
||||
result = real_run(argv, **kw)
|
||||
if argv[0]=='mount' and 'ro,nosuid,nodev,noexec' in argv and problem=='collision':
|
||||
(Path(argv[-1])/'EFI/HydraOS').mkdir(parents=True)
|
||||
return result
|
||||
runner.run = run
|
||||
context = patch('os.statvfs', return_value=SimpleNamespace(f_bavail=1, f_frsize=4096)) if problem=='space' else contextlib.nullcontext()
|
||||
with self.subTest(problem=problem), context, self.assertRaises(i.InstallError):
|
||||
self.install(runner)
|
||||
self.assertFalse(any(c[0].startswith('mkfs.') for c in runner.calls))
|
||||
|
||||
def test_esp_preflight_failure_never_recursively_removes_foreign_files(self):
|
||||
runner=FakeRunner(self.root); original_run=runner.run; sentinels=[]
|
||||
def run(argv, **kw):
|
||||
result=original_run(argv, **kw)
|
||||
if argv[0]=='mount' and 'ro,nosuid,nodev,noexec' in argv:
|
||||
sentinel=Path(argv[-1])/'EFI/HydraOS/foreign'
|
||||
sentinel.parent.mkdir(parents=True); sentinel.write_bytes(b'keep')
|
||||
sentinels.append(sentinel)
|
||||
return result
|
||||
runner.run=run
|
||||
with self.assertRaises(i.InstallError): self.install(runner)
|
||||
self.assertEqual(sentinels[0].read_bytes(), b'keep')
|
||||
self.assertFalse(any(c[0].startswith('mkfs.') for c in runner.calls))
|
||||
|
||||
def test_identity_change_after_esp_preflight_prevents_formatting(self):
|
||||
runner = FakeRunner(self.root)
|
||||
original_snapshot = self.probe.snapshot
|
||||
def snapshot(payload):
|
||||
topo = original_snapshot(payload)
|
||||
if self.probe.calls >= 2: topo.nodes['/dev/vda2']['maj:min'] = '252:9999'
|
||||
return topo
|
||||
self.probe.snapshot = snapshot
|
||||
with self.assertRaises(i.InstallError): self.install(runner)
|
||||
self.assertFalse(any(c[0].startswith('mkfs.') for c in runner.calls))
|
||||
|
||||
def test_btrfs_root_uses_named_subvolumes_and_boot_arguments(self):
|
||||
self.request.root_fs = 'btrfs'
|
||||
runner = FakeRunner(self.root); self.install(runner)
|
||||
self.assertIn(['mkfs.btrfs', '-f', '-L', 'HYDRA_ROOT', '/dev/vda2'], runner.calls)
|
||||
subvolumes = [Path(c[-1]).name for c in runner.calls if c[:3]==['btrfs','subvolume','create']]
|
||||
self.assertEqual(subvolumes, ['@root','@home','@snapshots'])
|
||||
fstab=(self.root/'etc/fstab').read_text()
|
||||
for name in ('@root','@home','@snapshots'): self.assertIn('subvol='+name, fstab)
|
||||
self.assertNotIn('subvolid', fstab)
|
||||
self.assertIn('rootflags=subvol=@root', (self.root/'etc/default/grub').read_text())
|
||||
self.assertIn('rootflags=subvol=@root', (self.root/'etc/dracut.conf.d/90-hydra-installed.conf').read_text())
|
||||
self.assertEqual(json.loads((self.root/'etc/hydra/storage.json').read_text())['root_subvolume'],'@root')
|
||||
self.assertEqual(runner.calls[-1], ['umount', str(self.root)])
|
||||
|
||||
def test_foreign_efi_files_are_compared_after_loader_install(self):
|
||||
runner=FakeRunner(self.root)
|
||||
original_run=runner.run
|
||||
def run(argv, **kw):
|
||||
result=original_run(argv, **kw)
|
||||
if argv[0]=='mount' and argv[-2]=='/dev/vda1':
|
||||
foreign=Path(argv[-1])/'EFI/BOOT/BOOTX64.EFI'
|
||||
foreign.parent.mkdir(parents=True, exist_ok=True); foreign.write_bytes(b'foreign-loader')
|
||||
if 'grub-install' in argv:
|
||||
(self.root/'boot/efi/EFI/BOOT/BOOTX64.EFI').write_bytes(b'changed-loader')
|
||||
return result
|
||||
runner.run=run
|
||||
with self.assertRaisesRegex(i.InstallError, 'Foreign ESP files changed'):
|
||||
self.install(runner)
|
||||
|
||||
def test_payload_capacity_failure_occurs_before_root_format(self):
|
||||
runner=FakeRunner(self.root); original_run=runner.run
|
||||
def run(argv, **kw):
|
||||
if '-lln' in argv:
|
||||
return '-rw-r--r-- 0/0 10737418240 2026-10-13 12:00 squashfs-root/large\n'
|
||||
return original_run(argv, **kw)
|
||||
runner.run=run
|
||||
with self.assertRaisesRegex(i.InstallError, 'capacity'):
|
||||
self.install(runner)
|
||||
self.assertFalse(any(c[0].startswith('mkfs.') for c in runner.calls))
|
||||
|
||||
def test_btrfs_extraction_failure_cleans_home_snapshots_and_root(self):
|
||||
self.request.root_fs='btrfs'; runner=FakeRunner(self.root,'unsquashfs-extract')
|
||||
with self.assertRaises(i.InstallError): self.install(runner)
|
||||
self.assertEqual(runner.calls[-3:], [['umount',str(self.root/'.snapshots')],
|
||||
['umount',str(self.root/'home')], ['umount',str(self.root)]])
|
||||
|
||||
def test_registration_creates_only_new_entry(self):
|
||||
self.request.register_boot_entry=True
|
||||
runner=FakeRunner(self.root); self.install(runner)
|
||||
commands=[c for c in runner.calls if c[0]=='efibootmgr']
|
||||
self.assertEqual(len(commands),1)
|
||||
self.assertIn('--create-only', commands[0]); self.assertNotIn('--bootorder',commands[0])
|
||||
self.assertIn('/dev/vda',commands[0]); self.assertIn('1',commands[0])
|
||||
|
||||
|
||||
|
||||
class PayloadCapacityTests(unittest.TestCase):
|
||||
def test_capacity_reserves_blocks_for_many_small_inodes(self):
|
||||
listing='-rw-r--r-- 0/0 1 2026-10-13 12:00 squashfs-root/tiny\n' * 500000
|
||||
self.assertGreater(i.storage.payload_capacity(listing), 4*1024**3)
|
||||
|
||||
def test_payload_special_inodes_do_not_need_device_capacity(self):
|
||||
listing=('drwxr-xr-x 0/0 39 2026-10-13 12:00 squashfs-root\n'
|
||||
'crw-rw-rw- 0/0 1, 3 2026-10-13 12:00 squashfs-root/dev/null\n'
|
||||
'brw-rw---- 0/0 8, 0 2026-10-13 12:00 squashfs-root/dev/disk\n')
|
||||
self.assertEqual(i.storage.payload_capacity(listing),4*1024**3)
|
||||
|
||||
def test_unrecognized_payload_listing_fails_closed(self):
|
||||
for listing in ('', '-rw-r--r-- root/root unknown file', 'device entry cannot be measured'):
|
||||
with self.subTest(listing=listing), self.assertRaises(i.InstallError):
|
||||
i.storage.payload_capacity(listing)
|
||||
|
||||
|
||||
class GuardTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.assertIsNotNone(i, 'installer module must implement the guarded installer')
|
||||
@@ -182,6 +490,14 @@ class PipelineTests(unittest.TestCase):
|
||||
self.assertIn('6.12.1', dracut)
|
||||
self.assertEqual(runner.calls[-1], ['umount', str(self.root)])
|
||||
|
||||
def test_whole_disk_installs_owned_loader_and_fallback(self):
|
||||
runner=FakeRunner(self.root); self.install(runner)
|
||||
boot_commands=[c for c in runner.calls if 'grub-install' in c]
|
||||
self.assertEqual(len(boot_commands),2)
|
||||
self.assertNotIn('--removable',boot_commands[0])
|
||||
self.assertIn('--removable',boot_commands[1])
|
||||
self.assertEqual(json.loads((self.root/'etc/hydra/storage.json').read_text())['owned_efi_path'], 'EFI/HydraOS')
|
||||
|
||||
def test_partition_format_and_extract_failures_abort_and_cleanup_only_mounted(self):
|
||||
for failing in ('parted', 'mkfs.vfat', 'mkfs.ext4', 'unsquashfs-extract'):
|
||||
with self.subTest(failing=failing):
|
||||
@@ -452,6 +768,7 @@ class RelocatedLiveLoopTests(unittest.TestCase):
|
||||
self.assertEqual(syscall.call_args.args, (91, 0x4C05, bytes(232)))
|
||||
self.assertFalse(any('/LiveOS/squashfs.img' in args for args in calls))
|
||||
probe.snapshot = lambda payload: topo
|
||||
probe.verify_identity = lambda path, node: None
|
||||
self.assertEqual(i.validate_request(i.Request('/dev/vda', 'hydratest'), probe), '/dev/vda')
|
||||
probe.physical_disk = lambda path: path == '/dev/sr0'
|
||||
with self.assertRaises(i.InstallError):
|
||||
@@ -522,6 +839,7 @@ class NestedLoopAncestryTests(unittest.TestCase):
|
||||
side_effect=lambda path, expected: identities[path]):
|
||||
topo = probe.snapshot(i.DEFAULT_PAYLOAD)
|
||||
probe.snapshot = lambda payload: topo
|
||||
probe.verify_identity = lambda path, node: None
|
||||
return probe, topo
|
||||
|
||||
def test_reverse_order_nested_loop_refuses_physical_backing(self):
|
||||
|
||||
Reference in New Issue
Block a user