Let go tests run without blocking on stdio

This commit is contained in:
Kovid Goyal 2023-08-07 10:56:39 +05:30
parent d6187b5bfc
commit 1347d1ad47
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C

View file

@ -8,10 +8,12 @@
import shutil
import subprocess
import sys
import time
import unittest
from contextlib import contextmanager
from functools import lru_cache
from tempfile import TemporaryDirectory
from threading import Thread
from typing import (
Any,
Callable,
@ -141,18 +143,52 @@ def go_exe() -> str:
return shutil.which('go') or ''
def run_go(packages: Set[str], names: str) -> 'subprocess.Popen[bytes]':
class GoProc(Thread):
def __init__(self, cmd: List[str]):
super().__init__(name='GoProc')
from kitty.constants import kitty_exe
env = os.environ.copy()
env['KITTY_PATH_TO_KITTY_EXE'] = kitty_exe()
self.stdout = b''
self.start_time = time.monotonic()
self.proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)
self.start()
@property
def runtime(self):
return self.end_time - self.start_time
@property
def returncode(self):
return self.proc.returncode
def run(self) -> None:
self.stdout, _ = self.proc.communicate()
self.proc.stdout.close()
def wait(self, timeout=None) -> None:
try:
self.join(timeout)
except KeyboardInterrupt:
self.proc.terminate()
if self.proc.wait(0.1) is None:
self.proc.kill()
self.join()
self.end_time = time.monotonic()
return self.stdout.decode('utf-8', 'replace'), self.proc.returncode
def run_go(packages: Set[str], names: str) -> GoProc:
go = go_exe()
go_pkg_args = [f'kitty/{x}' for x in packages]
cmd = [go, 'test', '-v']
for name in names:
cmd.extend(('-run', name))
cmd += go_pkg_args
env = os.environ.copy()
from kitty.constants import kitty_exe
env['KITTY_PATH_TO_KITTY_EXE'] = kitty_exe()
print(shlex.join(cmd), flush=True)
return subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)
return GoProc(cmd)
def reduce_go_pkgs(module: str, names: Sequence[str]) -> Set[str]:
@ -169,22 +205,18 @@ def reduce_go_pkgs(module: str, names: Sequence[str]) -> Set[str]:
return go_packages
def run_python_tests(args: Any, go_proc: 'Optional[subprocess.Popen[bytes]]' = None) -> None:
def run_python_tests(args: Any, go_proc: 'Optional[GoProc]' = None) -> None:
tests = find_all_tests()
def print_go() -> None:
try:
go_proc.wait()
except KeyboardInterrupt:
go_proc.terminate()
if go_proc.wait(0.1) is None:
go_proc.kill()
stdout, rc = go_proc.wait()
sys.stdout.flush()
sys.stderr.flush()
print(go_proc.stdout.read().decode('utf-8', 'replace'), end='', flush=True)
go_proc.stdout.close()
return go_proc.wait()
if go_proc.returncode == 0 and tests._tests:
print(f'All Go tests succeeded, ran in {go_proc.runtime:.1f} seconds', flush=True)
else:
print(stdout, end='', flush=True)
return rc
if args.module:
tests = filter_tests_by_module(tests, args.module)
@ -197,7 +229,10 @@ def print_go() -> None:
tests = filter_tests_by_name(tests, *args.name)
if not tests._tests and not go_proc:
raise SystemExit('No test named %s found' % args.name)
python_tests_ok = run_cli(tests, args.verbosity)
if tests._tests:
python_tests_ok = run_cli(tests, args.verbosity)
else:
python_tests_ok = True
exit_code = 0 if python_tests_ok else 1
if go_proc:
print_go()
@ -230,7 +265,7 @@ def run_tests(report_env: bool = False) -> None:
type_check()
go_pkgs = reduce_go_pkgs(args.module, args.name)
if go_pkgs:
go_proc: 'Optional[subprocess.Popen[bytes]]' = run_go(go_pkgs, args.name)
go_proc: 'Optional[GoProc]' = run_go(go_pkgs, args.name)
else:
go_proc = None
with env_for_python_tests(report_env):