Skip to content

Commit cedec3b

Browse files
committed
appmenus: update menus when template_for_dispvms changes
Setting or clearing the template_for_dispvms property did not trigger a menu refresh. This caused the "Disposable:" submenu to appear or disappear only after manually running qvm-appmenus --update. Add a property-set:template_for_dispvms handler in the extension, following the same pattern as the existing label and provides_network handlers. Also add regression tests verifying that appmenus_create correctly creates dispvm entries when the property is enabled, and removes them when it is cleared. For QubesOS/qubes-issues#9194
1 parent 3667bff commit cedec3b

2 files changed

Lines changed: 101 additions & 0 deletions

File tree

qubesappmenus/tests.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,99 @@ def test_007_created_dispvm(self):
392392
self.assertIn(b'X-Qubes-NonDispvmExec=', content)
393393
self.assertNotIn(b'X-Qubes-DispvmExec=', content)
394394

395+
def test_008_appmenus_update_when_template_for_dispvms_enabled(self):
396+
"""Dispvm menu entries appear after template_for_dispvms is set True.
397+
398+
Regression test for QubesOS/qubes-issues#9194: setting
399+
template_for_dispvms on a VM must regenerate menus so the
400+
"Disposable:" submenu shows up.
401+
"""
402+
tpl = TestVM('test-inst-tpl',
403+
klass='TemplateVM',
404+
virt_mode='pvh',
405+
updateable=True,
406+
provides_network=False,
407+
label=self.app.labels[1])
408+
self.ext.appmenus_init(tpl)
409+
appvm = TestVM('test-inst-dvm',
410+
klass='AppVM',
411+
template=tpl,
412+
virt_mode='pvh',
413+
updateable=False,
414+
provides_network=False,
415+
template_for_dispvms=False,
416+
label=self.app.labels[1])
417+
self.ext.appmenus_init(appvm)
418+
with open(os.path.join(self.ext.templates_dirs(tpl)[0],
419+
'evince.desktop'), 'wb') as f:
420+
f.write(importlib.resources.files(
421+
__package__).joinpath(
422+
'test-data/evince.desktop.template').read_bytes())
423+
424+
# First create without dispvm — no "Disposable:" directory entry
425+
self.ext.appmenus_create(appvm, refresh_cache=False)
426+
appmenus_dir = self.ext.appmenus_dir(appvm)
427+
dispvm_dir = os.path.join(appmenus_dir,
428+
'qubes-dispvm-directory_test_dinst_ddvm.directory')
429+
self.assertPathNotExists(dispvm_dir)
430+
431+
# Now simulate setting template_for_dispvms=True + appmenus-dispvm
432+
appvm.template_for_dispvms = True
433+
appvm.features['appmenus-dispvm'] = '1'
434+
self.ext.appmenus_create(appvm, refresh_cache=False)
435+
436+
self.assertPathExists(dispvm_dir)
437+
dispvm_evince = os.path.join(appmenus_dir,
438+
'org.qubes-os.dispvm._test_dinst_ddvm.evince.desktop')
439+
self.assertPathExists(dispvm_evince)
440+
441+
def test_009_appmenus_update_when_template_for_dispvms_disabled(self):
442+
"""Dispvm menu entries are removed after template_for_dispvms is cleared.
443+
444+
Regression test for QubesOS/qubes-issues#9194: clearing
445+
template_for_dispvms must regenerate menus so stale
446+
"Disposable:" entries are removed.
447+
"""
448+
tpl = TestVM('test-inst-tpl2',
449+
klass='TemplateVM',
450+
virt_mode='pvh',
451+
updateable=True,
452+
provides_network=False,
453+
label=self.app.labels[1])
454+
self.ext.appmenus_init(tpl)
455+
appvm = TestVM('test-inst-dvm2',
456+
klass='AppVM',
457+
template=tpl,
458+
virt_mode='pvh',
459+
updateable=False,
460+
provides_network=False,
461+
template_for_dispvms=True,
462+
label=self.app.labels[1])
463+
appvm.features['appmenus-dispvm'] = '1'
464+
self.ext.appmenus_init(appvm)
465+
with open(os.path.join(self.ext.templates_dirs(tpl)[0],
466+
'evince.desktop'), 'wb') as f:
467+
f.write(importlib.resources.files(
468+
__package__).joinpath(
469+
'test-data/evince.desktop.template').read_bytes())
470+
471+
# First create as dispvm template — "Disposable:" entries should exist
472+
self.ext.appmenus_create(appvm, refresh_cache=False)
473+
appmenus_dir = self.ext.appmenus_dir(appvm)
474+
dispvm_dir = os.path.join(appmenus_dir,
475+
'qubes-dispvm-directory_test_dinst_ddvm2.directory')
476+
self.assertPathExists(dispvm_dir)
477+
478+
# Simulate clearing template_for_dispvms
479+
appvm.template_for_dispvms = False
480+
del appvm.features['appmenus-dispvm']
481+
self.ext.appmenus_create(appvm, refresh_cache=False)
482+
483+
self.assertPathNotExists(dispvm_dir)
484+
dispvm_evince = os.path.join(appmenus_dir,
485+
'org.qubes-os.dispvm._test_dinst_ddvm2.evince.desktop')
486+
self.assertPathNotExists(dispvm_evince)
487+
395488
def test_100_get_appmenus(self):
396489
self.maxDiff = None
397490
def _run(service, **kwargs):

qubesappmenusext/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ def provides_network_setter(self, vm, event, **kwargs):
171171
self.vm_tasks[vm.name].append(
172172
asyncio.ensure_future(self.update_appmenus(vm)))
173173

174+
@qubes.ext.handler('property-set:template_for_dispvms')
175+
def template_for_dispvms_setter(self, vm, event, **kwargs):
176+
if vm.app.vmm.offline_mode:
177+
return
178+
self.collect_done_tasks(vm)
179+
self.vm_tasks[vm.name].append(
180+
asyncio.ensure_future(self.update_appmenus(vm)))
181+
174182
@qubes.ext.handler('property-set:guivm')
175183
def provides_network_setter(self, vm, event, name, newvalue, oldvalue=None):
176184
if vm.app.vmm.offline_mode:

0 commit comments

Comments
 (0)