@@ -410,6 +410,8 @@ async def wait_preload(
410410 preload_unfinished = preload_dispvm
411411 for _ in range (timeout ):
412412 for qube in preload_unfinished .copy ():
413+ if qube not in self .app .domains :
414+ break
413415 if self .app .domains [qube ].preload_complete .is_set ():
414416 logger .info ("preload completed for '%s'" , qube )
415417 preload_unfinished .remove (qube )
@@ -1104,6 +1106,53 @@ def mock_open_mem_threshold(file, *args, **kwargs):
11041106 )
11051107 self .assertEqual (1 , len (self .disp_base .get_feat_preload ()))
11061108
1109+ def test_012_preload_low_mem_early_startup (self ):
1110+ """Test preloading with low memory on early startup"""
1111+ self .loop .run_until_complete (
1112+ self ._test_012_preload_low_mem_early_startup ()
1113+ )
1114+
1115+ async def _test_012_preload_low_mem_early_startup (self ):
1116+ # pylint: disable=unspecified-encoding
1117+ logger .info ("start" )
1118+ unpatched_open = open
1119+
1120+ def mock_open_mem_raise (file , * args , ** kwargs ):
1121+ if file == qubes .config .qmemman_avail_mem_file :
1122+ raise FileNotFoundError (2 , "No such file or directory" , file )
1123+ return unpatched_open (file , * args , ** kwargs )
1124+
1125+ preload_max = 2
1126+ with patch ("builtins.open" , side_effect = mock_open_mem_raise ):
1127+ logger .info ("insufficient memory reserve (early startup failure)" )
1128+ old_memory = self .disp_base .memory
1129+ old_dispvms = [
1130+ qube .name
1131+ for qube in self .app .domains
1132+ if getattr (qube , "is_preload" , False )
1133+ ]
1134+ self .disp_base .memory = 999999999999999999999
1135+ self .disp_base .features ["preload-dispvm-max" ] = str (preload_max )
1136+ await self .wait_preload (
1137+ preload_max , fail_on_timeout = False , timeout = 15
1138+ )
1139+ self .assertEqual (0 , len (self .disp_base .get_feat_preload ()))
1140+ new_dispvms = [
1141+ qube .name
1142+ for qube in self .app .domains
1143+ if getattr (qube , "is_preload" , False )
1144+ ]
1145+ self .assertEqual (new_dispvms , old_dispvms )
1146+
1147+ # Nothing will be done here, just to prepare to the next test.
1148+ self .disp_base .features ["preload-dispvm-max" ] = "0"
1149+ with patch ("builtins.open" , side_effect = mock_open_mem_raise ):
1150+ logger .info ("enough memory reserve but no avail-mem file" )
1151+ self .disp_base .memory = old_memory
1152+ self .disp_base .features ["preload-dispvm-max" ] = str (preload_max )
1153+ await self .wait_preload (preload_max )
1154+ self .assertEqual (2 , len (self .disp_base .get_feat_preload ()))
1155+
11071156 logger .info ("end" )
11081157
11091158 def test_015_preload_race_more (self ):
0 commit comments