summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-04-22 19:14:52 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-04-22 19:14:52 +0000
commitb02e400b63849991c1590a3572e711740758583f (patch)
tree517eed03d0bb1f1d4b05039c6dcd9baef611f82f
parent5f512359af5d02c037f85887bf5a428af05d4fb3 (diff)
downloadmeta-virtualization-b02e400b63849991c1590a3572e711740758583f.tar.gz
tests: fix memres start hanging in subprocess.run
memres start spawns background processes (QEMU VM, idle watchdog) that persist after the vrunner script exits. When invoked via subprocess.run(capture_output=True), these background processes inherit the pipe file descriptors, preventing communicate() from returning until all pipe holders exit — which can be 30+ minutes (the idle timeout). Fix by using Popen with: - stdin=subprocess.DEVNULL (no inherited stdin pipe) - file-based stdout (no pipe FDs to inherit) - start_new_session=True (new process group, so wait() only waits for the parent script, not the background children) This matches the behavior when running from a shell, where the daemon processes are fully detached from the caller's FD table. Applied to both VdkrRunner and VpdmnRunner memres_start methods. Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
-rw-r--r--tests/conftest.py60
1 files changed, 58 insertions, 2 deletions
diff --git a/tests/conftest.py b/tests/conftest.py
index 56047929..8e1e245f 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -444,7 +444,35 @@ class VdkrRunner:
444 if port_forwards: 444 if port_forwards:
445 for pf in port_forwards: 445 for pf in port_forwards:
446 args.extend(["-p", pf]) 446 args.extend(["-p", pf])
447 return self.run(*args, timeout=timeout) 447 # memres start spawns background processes (QEMU VM, idle watchdog)
448 # that can inherit pipe FDs from subprocess.run(capture_output=True),
449 # causing communicate() to hang indefinitely. Use Popen with
450 # file-based output, DEVNULL stdin, and start_new_session to fully
451 # isolate the daemon process tree from the test harness.
452 cmd = [str(self.binary)]
453 if self._needs_arch_flag:
454 cmd.extend(["--arch", self.arch])
455 cmd.extend(["--state-dir", str(self.state_dir)])
456 cmd.extend(args)
457 import tempfile
458 with tempfile.TemporaryFile(mode='w+') as out:
459 proc = subprocess.Popen(
460 cmd, env=self.env,
461 stdin=subprocess.DEVNULL,
462 stdout=out, stderr=subprocess.STDOUT,
463 start_new_session=True,
464 )
465 try:
466 proc.wait(timeout=timeout)
467 except subprocess.TimeoutExpired:
468 proc.kill()
469 proc.wait()
470 raise
471 out.seek(0)
472 output = out.read()
473 result = subprocess.CompletedProcess(
474 cmd, proc.returncode, stdout=output, stderr="")
475 return result
448 476
449 def memres_stop(self, timeout=30): 477 def memres_stop(self, timeout=30):
450 """Stop memory resident mode.""" 478 """Stop memory resident mode."""
@@ -705,7 +733,35 @@ class VpdmnRunner:
705 if port_forwards: 733 if port_forwards:
706 for pf in port_forwards: 734 for pf in port_forwards:
707 args.extend(["-p", pf]) 735 args.extend(["-p", pf])
708 return self.run(*args, timeout=timeout) 736 # memres start spawns background processes (QEMU VM, idle watchdog)
737 # that can inherit pipe FDs from subprocess.run(capture_output=True),
738 # causing communicate() to hang indefinitely. Use Popen with
739 # file-based output, DEVNULL stdin, and start_new_session to fully
740 # isolate the daemon process tree from the test harness.
741 cmd = [str(self.binary)]
742 if self._needs_arch_flag:
743 cmd.extend(["--arch", self.arch])
744 cmd.extend(["--state-dir", str(self.state_dir)])
745 cmd.extend(args)
746 import tempfile
747 with tempfile.TemporaryFile(mode='w+') as out:
748 proc = subprocess.Popen(
749 cmd, env=self.env,
750 stdin=subprocess.DEVNULL,
751 stdout=out, stderr=subprocess.STDOUT,
752 start_new_session=True,
753 )
754 try:
755 proc.wait(timeout=timeout)
756 except subprocess.TimeoutExpired:
757 proc.kill()
758 proc.wait()
759 raise
760 out.seek(0)
761 output = out.read()
762 result = subprocess.CompletedProcess(
763 cmd, proc.returncode, stdout=output, stderr="")
764 return result
709 765
710 def memres_stop(self, timeout=30): 766 def memres_stop(self, timeout=30):
711 """Stop memory resident mode.""" 767 """Stop memory resident mode."""