fix: preserve guest network identities and refresh session profiles
This commit is contained in:
@@ -65,6 +65,15 @@ class ProfilesTests(unittest.TestCase):
|
||||
with self.subTest(field=field, value=value), self.assertRaises(ValueError):
|
||||
Profile('guest', self.iso, **{field: value})
|
||||
|
||||
def test_explicit_mac_roundtrip_and_validation(self):
|
||||
dest = self.root / 'guest.toml'
|
||||
save_profile(dest, Profile('guest', mac='02:ab:cd:ef:01:23'))
|
||||
self.assertEqual(load_profile(dest).mac, '02:ab:cd:ef:01:23')
|
||||
for value in ('ff:00:00:00:00:01', '00:00:00:00:00:01',
|
||||
'02:00:00:00:00:zz', '02:00:00:00:00', True):
|
||||
with self.subTest(mac=value), self.assertRaises(ValueError):
|
||||
Profile('guest', mac=value)
|
||||
|
||||
def test_refresh_vertical_descriptor_boundaries(self):
|
||||
for value in (0, 50000, 255000):
|
||||
path = self.root / 'guest.toml'
|
||||
|
||||
+41
-1
@@ -88,6 +88,46 @@ class RuntimeTests(unittest.TestCase):
|
||||
except VMError:
|
||||
pass
|
||||
|
||||
def test_mac_persisted_distinct_and_stable_across_restarts(self):
|
||||
self.runtime.add(Profile('other', network='bridge'))
|
||||
other = self.runtime.profile('other')
|
||||
guest = self.runtime.profile('guest')
|
||||
self.assertIsNotNone(guest.mac)
|
||||
self.assertNotEqual(guest.mac, other.mac)
|
||||
octet = int(guest.mac.split(':')[0], 16)
|
||||
self.assertEqual(octet & 3, 2) # locally administered, unicast
|
||||
def nic(profile, runtime):
|
||||
return next(arg for arg in build_qemu_argv(profile, runtime.paths(profile.name))
|
||||
if arg.startswith('virtio-net-pci,'))
|
||||
expected = nic(guest, self.runtime)
|
||||
self.runtime.start('guest')
|
||||
self.runtime.stop('guest', force=True)
|
||||
self.runtime.start('guest')
|
||||
self.runtime.stop('guest', force=True)
|
||||
self.assertEqual(nic(self.runtime.profile('guest'), self.runtime), expected)
|
||||
changed_runtime = Runtime(self.runtime.config, self.runtime.state, self.root / 'different-run')
|
||||
self.assertEqual(nic(changed_runtime.profile('guest'), changed_runtime), expected)
|
||||
self.runtime.media('guest', None)
|
||||
self.assertEqual(self.runtime.profile('guest').mac, guest.mac)
|
||||
|
||||
def test_legacy_profiles_gain_stable_owner_specific_argv_mac(self):
|
||||
file = self.runtime.config / 'legacy.toml'
|
||||
save_profile(file, Profile('legacy', network='bridge'))
|
||||
original = file.read_bytes()
|
||||
legacy = self.runtime.profile('legacy')
|
||||
self.assertIsNone(legacy.mac)
|
||||
paths = self.runtime.paths('legacy')
|
||||
def mac():
|
||||
return next(arg for arg in build_qemu_argv(legacy, paths) if arg.startswith('virtio-net-pci,'))
|
||||
with patch('os.getuid', return_value=1000):
|
||||
first = mac()
|
||||
self.assertEqual(mac(), first)
|
||||
self.assertNotEqual(first, next(arg for arg in build_qemu_argv(
|
||||
legacy, self.runtime.paths('other')) if arg.startswith('virtio-net-pci,')))
|
||||
with patch('os.getuid', return_value=1001):
|
||||
self.assertNotEqual(mac(), first)
|
||||
self.assertEqual(file.read_bytes(), original)
|
||||
|
||||
def test_disk_boot_omits_media_and_requests_display(self):
|
||||
profile = Profile('guest', width=1920, height=1080, refresh_millihz=144000,
|
||||
graphics='software')
|
||||
@@ -145,7 +185,7 @@ class RuntimeTests(unittest.TestCase):
|
||||
self.assertIn('disable-agent-file-xfer=on', spice)
|
||||
self.assertIn('run,,comma', spice)
|
||||
self.assertIn('virtio-blk-pci,drive=disk', argv)
|
||||
self.assertIn('virtio-net-pci,netdev=net', argv)
|
||||
self.assertTrue(any(arg.startswith('virtio-net-pci,netdev=net,mac=') for arg in argv))
|
||||
self.assertNotIn('-vnc', argv)
|
||||
self.assertNotIn('-virtfs', argv)
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ class SessionTests(unittest.TestCase):
|
||||
self.assertEqual(tree.findall('./mouse/context/mousebind/action'), [])
|
||||
self.assertEqual(tree.findtext('./libinput/device/tap'), 'yes')
|
||||
|
||||
def test_empty_session_has_one_workspace_without_empty_names(self):
|
||||
tree = ET.fromstring(session.build_labwc_config([], ['foot']))
|
||||
self.assertEqual(tree.find('./desktops').get('number'), '1')
|
||||
self.assertIsNone(tree.find('./desktops/names'))
|
||||
|
||||
def test_viewer_argv_is_private_safe_and_manual_usb_only(self):
|
||||
paths = self.manager.paths('alpha')
|
||||
argv = session.build_viewer_argv(paths, '/tool path/viewer')
|
||||
@@ -173,6 +178,45 @@ else:
|
||||
self.assertEqual(main(['_session-helper', 'recover', str(manifest)]), 1)
|
||||
self.assertIn('manifest', error.getvalue().lower())
|
||||
|
||||
def test_empty_launch_refresh_rebuilds_profile_snapshot(self):
|
||||
manager = Runtime(self.root / 'empty-config', self.root / 'empty-state', self.root / 'empty-run')
|
||||
capture = self.root / 'refresh-capture.json'
|
||||
compositor = self.root / 'refresh-labwc'
|
||||
compositor.write_text('#!' + os.sys.executable + '\n' + r'''import json, os, subprocess, sys
|
||||
from pathlib import Path
|
||||
if '--version' in sys.argv:
|
||||
print('labwc 0.20.2')
|
||||
sys.exit(0)
|
||||
config = Path(sys.argv[sys.argv.index('-C') + 1])
|
||||
manifest = config.parent / 'session.json'
|
||||
data = json.loads(manifest.read_text())
|
||||
capture = Path(os.environ['CAPTURE'])
|
||||
seen = json.loads(capture.read_text()) if capture.exists() else []
|
||||
seen.append({'names': data['names'], 'xml': (config / 'rc.xml').read_text()})
|
||||
capture.write_text(json.dumps(seen))
|
||||
if len(seen) == 1:
|
||||
launcher = os.environ['HYDRA_SOURCE_LAUNCHER']
|
||||
for name in ('first', 'second'):
|
||||
subprocess.run([sys.executable, launcher, '--config', data['config'], '--state', data['state'],
|
||||
'--runtime', data['runtime'], 'add', name], check=True)
|
||||
subprocess.run([sys.executable, launcher, '_session-helper', 'recover', str(manifest)],
|
||||
input=':refresh\n', text=True, check=True)
|
||||
sys.exit(67)
|
||||
sys.exit(0)
|
||||
''')
|
||||
compositor.chmod(0o700)
|
||||
with patch.dict(os.environ, {'HYDRA_LABWC': str(compositor), 'HYDRA_TERMINAL': str(compositor),
|
||||
'HYDRA_BRIGHTNESS': str(compositor), 'CAPTURE': str(capture),
|
||||
'HYDRA_SOURCE_LAUNCHER': str(Path('bin/hydra').resolve())}), contextlib.redirect_stdout(io.StringIO()):
|
||||
self.assertEqual(session.launch_session(manager), 0)
|
||||
seen = json.loads(capture.read_text())
|
||||
self.assertEqual([entry['names'] for entry in seen], [[], ['first', 'second']])
|
||||
tree = ET.fromstring(seen[1]['xml'])
|
||||
self.assertEqual(tree.find('desktops').get('number'), '2')
|
||||
self.assertEqual([rule.get('title') for rule in tree.findall('./windowRules/windowRule')],
|
||||
['HydraOS: first', 'HydraOS: second'])
|
||||
self.assertFalse(manager.paths('first').disk.exists())
|
||||
|
||||
def test_no_running_vms_opens_recovery_without_start_or_stop(self):
|
||||
terminal = self.root / 'terminal'
|
||||
capture = self.root / 'recovery-argv.json'
|
||||
|
||||
Reference in New Issue
Block a user