···
from contextlib import contextmanager
from typing import Any, Callable, ContextManager, Dict, Iterator, List, Optional, Union
···
polling_conditions: List[PollingCondition]
+
race_timer: threading.Timer
···
keep_vm_state: bool = False,
+
global_timeout: int = 24 * 60 * 60 * 7,
+
self.global_timeout = global_timeout
+
self.race_timer = threading.Timer(global_timeout, self.terminate_test)
···
def __exit__(self, *_: Any) -> None:
with rootlog.nested("cleanup"):
+
self.race_timer.cancel()
for machine in self.machines:
···
def run_tests(self) -> None:
"""Run the test script (for non-interactive test runs)"""
+
f"Test will time out and terminate in {self.global_timeout} seconds"
+
self.race_timer.start()
# TODO: Collect coverage data
for machine in self.machines:
···
with rootlog.nested("wait for all VMs to finish"):
for machine in self.machines:
machine.wait_for_shutdown()
+
self.race_timer.cancel()
+
def terminate_test(self) -> None:
+
# This will be usually running in another thread than
+
# the thread actually executing the test script.
+
with rootlog.nested("timeout reached; test terminating..."):
+
for machine in self.machines:
+
# As we cannot `sys.exit` from another thread
+
# We can at least force the main thread to get SIGTERM'ed.
+
# This will prevent any user who caught all the exceptions
+
# to swallow them and prevent itself from terminating.
+
os.kill(os.getpid(), signal.SIGTERM)
def create_machine(self, args: Dict[str, Any]) -> Machine: