@@ -69,6 +69,11 @@ def setUp(self):
6969 self .template = self .app .add_new_vm (
7070 qubes .vm .templatevm .TemplateVM , name = "test-template" , label = "red"
7171 )
72+ self .template_alt = self .app .add_new_vm (
73+ qubes .vm .templatevm .TemplateVM ,
74+ name = "test-template-alt" ,
75+ label = "red" ,
76+ )
7277 self .appvm = self .app .add_new_vm (
7378 qubes .vm .appvm .AppVM ,
7479 name = "test-vm" ,
@@ -98,9 +103,14 @@ def cleanup_dispvm(self):
98103 if hasattr (self , "dispvm" ):
99104 self .dispvm .close ()
100105 del self .dispvm
106+ if hasattr (self , "dispvm_alt" ):
107+ self .dispvm_alt .close ()
108+ del self .dispvm_alt
101109 self .template .close ()
110+ self .template_alt .close ()
102111 self .appvm .close ()
103112 del self .template
113+ del self .template_alt
104114 del self .appvm
105115 self .app .domains .clear ()
106116 self .app .pools .clear ()
@@ -241,7 +251,7 @@ def test_011_dvm_preload_del_max(self, mock_remove_preload_excess):
241251 del self .adminvm .features ["preload-dispvm-max" ]
242252 self .appvm .features ["preload-dispvm-max" ] = ""
243253 del self .appvm .features ["preload-dispvm-max" ]
244- mock_remove_preload_excess .assert_called_once_with (0 )
254+ mock_remove_preload_excess .assert_called_once_with (0 , reason = mock . ANY )
245255
246256 @mock .patch ("qubes.events.Emitter.fire_event_async" )
247257 def test_012_dvm_preload_set_max (self , mock_events ):
@@ -251,6 +261,11 @@ def test_012_dvm_preload_set_max(self, mock_events):
251261 "domain-preload-dispvm-start" , reason = mock .ANY
252262 )
253263
264+ mock_events .reset_mock ()
265+ self .appvm .template_for_dispvms = False
266+ self .appvm .features ["preload-dispvm-max" ] = "2"
267+ mock_events .assert_not_called ()
268+
254269 def test_013_dvm_preload_get_treshold (self ):
255270 cases = [None , False , "0" , "2" , "1000" ]
256271 self .assertEqual (self .appvm .get_feat_preload_threshold (), 0 )
@@ -260,6 +275,101 @@ def test_013_dvm_preload_get_treshold(self):
260275 threshold = self .appvm .get_feat_preload_threshold ()
261276 self .assertEqual (threshold , int (value or 0 ) * 1024 ** 2 )
262277
278+ @mock .patch ("qubes.events.Emitter.fire_event_async" )
279+ @mock .patch (
280+ "qubes.vm.mix.dvmtemplate.DVMTemplateMixin.remove_preload_excess"
281+ )
282+ def test_030_dvm_preload_set_template (self , mock_remove , mock_events ):
283+ # Don't try to preload if max is not set.
284+ mock_events .side_effect = self .mock_coro
285+ self .appvm .template = self .template_alt
286+ mock_events .assert_not_called ()
287+ mock_remove .assert_called_once_with (0 , reason = mock .ANY )
288+
289+ # Try to remove and preload if max is set and template has changed.
290+ mock_remove .reset_mock ()
291+ self .appvm .features ["preload-dispvm-max" ] = "1"
292+ mock_events .reset_mock ()
293+ self .appvm .template = self .template
294+ mock_remove .assert_called_once_with (0 , reason = mock .ANY )
295+ mock_events .assert_called_once_with (
296+ "domain-preload-dispvm-start" , reason = mock .ANY
297+ )
298+
299+ # Don't change anything if template hasn't changed.
300+ mock_remove .reset_mock ()
301+ mock_events .reset_mock ()
302+ self .appvm .template = self .template
303+ mock_remove .assert_not_called ()
304+ mock_events .assert_not_called ()
305+
306+ @mock .patch ("qubes.events.Emitter.fire_event_async" )
307+ @mock .patch (
308+ "qubes.vm.mix.dvmtemplate.DVMTemplateMixin.remove_preload_excess"
309+ )
310+ def test_040_dvm_preload_set_template_for_dispvms (
311+ self , mock_remove , mock_events
312+ ):
313+ # Remove preloads when disabling property.
314+ mock_events .side_effect = self .mock_coro
315+ self .appvm .template_for_dispvms = False
316+ mock_events .assert_not_called ()
317+ mock_remove .assert_called_once_with (0 , reason = mock .ANY )
318+
319+ # Preload when enabling property.
320+ self .appvm .features ["preload-dispvm-max" ] = "1"
321+ mock_events .reset_mock ()
322+ mock_remove .reset_mock ()
323+ self .appvm .template_for_dispvms = True
324+ mock_remove .assert_not_called ()
325+ mock_events .assert_called_once_with (
326+ "domain-preload-dispvm-start" , reason = mock .ANY
327+ )
328+
329+ # Try to disable property if it has dependents.
330+ mock_events .reset_mock ()
331+ self .dispvm = self .app .add_new_vm (
332+ qubes .vm .dispvm .DispVM ,
333+ name = "test-dispvm" ,
334+ template = self .appvm ,
335+ label = "red" ,
336+ dispid = 42 ,
337+ )
338+ self .dispvm_alt = self .app .add_new_vm (
339+ qubes .vm .dispvm .DispVM ,
340+ name = "test-dispvm-alt" ,
341+ template = self .appvm ,
342+ label = "red" ,
343+ dispid = 43 ,
344+ )
345+ with self .assertRaises (qubes .exc .QubesVMInUseError ):
346+ self .appvm .template_for_dispvms = False
347+ mock_remove .assert_not_called ()
348+ mock_events .assert_not_called ()
349+
350+ # Disabling property when not all dependents are preloads
351+ self .appvm .features ["preload-dispvm-max" ] = 1
352+ self .appvm .features ["preload-dispvm" ] = self .dispvm .name
353+ mock_events .reset_mock ()
354+ mock_remove .reset_mock ()
355+ with self .assertRaises (qubes .exc .QubesVMInUseError ):
356+ self .appvm .template_for_dispvms = False
357+ mock_remove .assert_not_called ()
358+ mock_events .assert_not_called ()
359+
360+ # Disabling property when all dependents are preloads
361+ self .appvm .features ["preload-dispvm-max" ] = 2
362+ mock_events .reset_mock ()
363+ mock_remove .reset_mock ()
364+ self .appvm .features ["preload-dispvm" ] = (
365+ self .dispvm .name + " " + self .dispvm_alt .name
366+ )
367+ mock_events .reset_mock ()
368+ mock_remove .reset_mock ()
369+ self .appvm .template_for_dispvms = False
370+ mock_remove .assert_called_once_with (0 , reason = mock .ANY )
371+ mock_events .assert_not_called ()
372+
263373 def test_100_get_preload_templates (self ):
264374 print (qubes .vm .dispvm .get_preload_templates (self .app ))
265375 self .appvm .features ["supported-rpc.qubes.WaitForRunningSystem" ] = True
0 commit comments