diff --git a/kitty_tests/main.py b/kitty_tests/main.py index d468264ed..7113295b5 100644 --- a/kitty_tests/main.py +++ b/kitty_tests/main.py @@ -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):