fix: verify work during continuously hidden guest intervals
This commit is contained in:
+64
-9
@@ -35,6 +35,51 @@ def counter(manager, name):
|
||||
return values[0]
|
||||
|
||||
|
||||
def focus_events(log):
|
||||
"""Only keyboard focus, excluding pointer-enter and old window titles."""
|
||||
return re.findall(r'wl_keyboard#\d+\.(enter|leave)\(', log.read_text())
|
||||
|
||||
|
||||
def fresh_focus_transition(source_before, target_before, source_after, target_after):
|
||||
return (len(source_after) > len(source_before) and source_after[-1] == 'leave'
|
||||
and len(target_after) > len(target_before) and target_after[-1] == 'enter')
|
||||
|
||||
|
||||
def measure_hidden_interval(read_counter, hidden_log, visible_log, duration=.5):
|
||||
hidden_focus, visible_focus = focus_events(hidden_log), focus_events(visible_log)
|
||||
assert hidden_focus and hidden_focus[-1] == 'leave', 'sampled guest is not hidden'
|
||||
assert visible_focus and visible_focus[-1] == 'enter', 'other guest is not focused'
|
||||
|
||||
def unchanged():
|
||||
assert (focus_events(hidden_log) == hidden_focus and
|
||||
focus_events(visible_log) == visible_focus), 'focus changed during hidden interval'
|
||||
|
||||
start = time.monotonic()
|
||||
before = read_counter()
|
||||
unchanged()
|
||||
while time.monotonic() - start < duration:
|
||||
time.sleep(min(.05, duration))
|
||||
unchanged()
|
||||
after = read_counter()
|
||||
unchanged()
|
||||
assert after != before, 'hidden guest stopped working during continuously hidden interval'
|
||||
return {'before': before, 'after': after,
|
||||
'elapsed_seconds': time.monotonic() - start, 'focus_unchanged': True}
|
||||
|
||||
|
||||
def classify_compositor_errors(log):
|
||||
expected = []
|
||||
for line in log.splitlines():
|
||||
if '[ERROR]' not in line:
|
||||
continue
|
||||
if re.search(r'\[xwayland/sockets\.c:\d+\] Failed to bind socket '
|
||||
r'@?/tmp/\.X11-unix/X\d+: Address already in use$', line):
|
||||
expected.append(line)
|
||||
else:
|
||||
raise AssertionError('Unexpected compositor error: ' + line)
|
||||
return expected
|
||||
|
||||
|
||||
def main():
|
||||
cli = argparse.ArgumentParser(description=__doc__)
|
||||
cli.add_argument('--viewer', default=os.environ.get('HYDRA_VIEWER', 'remote-viewer'))
|
||||
@@ -74,7 +119,7 @@ def main():
|
||||
(config / 'environment').touch()
|
||||
env = dict(os.environ, XDG_RUNTIME_DIR=str(xdg), WLR_BACKENDS='headless',
|
||||
WLR_RENDERER='pixman', WLR_XWAYLAND='/bin/false', XDG_DATA_DIRS='/nonexistent',
|
||||
GSETTINGS_BACKEND='memory')
|
||||
GSETTINGS_BACKEND='memory', LABWC_UPDATE_ACTIVATION_ENV='0')
|
||||
for key in ('DISPLAY', 'WAYLAND_DISPLAY'):
|
||||
env.pop(key, None)
|
||||
manager = Runtime(root / 'config', root / 'state', root / 'run')
|
||||
@@ -111,6 +156,15 @@ def main():
|
||||
def chord(code):
|
||||
keys(f'down 29\ndown 56\ndown 42\ndown {code}\nup {code}\nup 42\nup 56\nup 29')
|
||||
|
||||
def switch(key, source_log, target_log):
|
||||
source_before, target_before = focus_events(source_log), focus_events(target_log)
|
||||
chord(key)
|
||||
wait_for(lambda: fresh_focus_transition(source_before, target_before,
|
||||
focus_events(source_log), focus_events(target_log)),
|
||||
'reserved switch did not produce fresh keyboard leave/enter')
|
||||
return {'from_events': len(focus_events(source_log)),
|
||||
'to_events': len(focus_events(target_log)), 'keycode': key}
|
||||
|
||||
def attach(name, number):
|
||||
vl = (root / f'{name}-{number}.log').open('w'); streams.append(vl)
|
||||
previous = dict(os.environ)
|
||||
@@ -150,11 +204,10 @@ def main():
|
||||
chord(23) # I: test-only ToggleKeybinds.
|
||||
keys('down 88\nup 88')
|
||||
assert not unreserved.exists(), 'keybinding inhibition did not suppress ordinary F12'
|
||||
a_leave = alog.read_text().count('.leave(')
|
||||
chord(106) # Right: production reserved host switch.
|
||||
wait_for(lambda: alog.read_text().count('.leave(') > a_leave and '.enter(' in blog.read_text(),
|
||||
'reserved switch failed with inhibition')
|
||||
chord(105) # Left: return to inhibited alpha.
|
||||
switch(106, alog, blog) # Right: beta focused, alpha continuously hidden.
|
||||
hidden_intervals = {'alpha': measure_hidden_interval(lambda: counter(manager, 'alpha'), alog, blog)}
|
||||
switch(105, blog, alog) # Left: alpha focused, beta continuously hidden.
|
||||
hidden_intervals['beta'] = measure_hidden_interval(lambda: counter(manager, 'beta'), blog, alog)
|
||||
chord(1) # Escape: production recovery binding still works.
|
||||
wait_for(recovery.exists, 'reserved recovery failed with inhibition')
|
||||
dialog = 'set_title("Select USB devices for redirection")'
|
||||
@@ -164,8 +217,7 @@ def main():
|
||||
wait_for(lambda: dialog in alog.read_text(), 'manual USB selector did not open', timeout=3)
|
||||
keys('up 22\nup 42\nup 56\nup 29')
|
||||
keys('down 1\nup 1') # Close selector.
|
||||
chord(106)
|
||||
chord(105)
|
||||
post_usb_switches = [switch(106, alog, blog), switch(105, blog, alog)]
|
||||
assert alpha.poll() is None and beta.poll() is None
|
||||
assert identities == {name: manager.paths(name).pid.read_text().strip() for name in names}, 'switch restarted a guest'
|
||||
assert alog.read_text().count('set_title("HydraOS: alpha")') == 1, 'switch reconnected alpha'
|
||||
@@ -191,7 +243,10 @@ def main():
|
||||
time.sleep(1)
|
||||
disk_boot_counter = counter(manager, 'alpha')
|
||||
assert disk_boot_counter > 0
|
||||
result.update(manual_usb_dialog=True, standard_us_keymap=True,
|
||||
expected_compositor_noise = classify_compositor_errors((root / 'labwc.log').read_text())
|
||||
result.update(hidden_guest_intervals=hidden_intervals, post_usb_switches=post_usb_switches,
|
||||
expected_compositor_noise=expected_compositor_noise,
|
||||
manual_usb_dialog=True, standard_us_keymap=True,
|
||||
native_labwc_inhibition=True, reserved_switch_and_recovery=True,
|
||||
qemu_survives_viewer_and_compositor=True, independent_lifecycle=True,
|
||||
guest_counters_before=before, guest_counters_after=after,
|
||||
|
||||
Reference in New Issue
Block a user