@@ -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 ):
0 commit comments