Using asyncio for test subprocesses.
"Support for running tests in a subprocess."
+import asyncio
import os
import subprocess
+import sys
import traceback
from cosmic_ray.work_item import TestOutcome
+async def _run_tests(command, timeout):
+ # We want to avoid writing pyc files in case our changes happen too fast for Python to
+ # notice them. If the timestamps between two changes are too small, Python won't recompile
+ # the source.
+ env = dict(os.environ)
+ env['PYTHONDONTWRITEBUTECODE'] = '1'
+
+ try:
+ proc = await asyncio.create_subprocess_shell(
+ command,
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.STDOUT,
+ text=True,
+ env=env)
+ except Exception: # pylint: disable=W0703
+ return (TestOutcome.INCOMPETENT, traceback.format_exc())
+
+ try:
+ outs, errs = await asyncio.wait_for(proc.communicate(), timeout)
+
+ assert proc.returncode is not None
+
+ if proc.returncode == 0:
+ return (TestOutcome.SURVIVED, outs)
+ else:
+ return (TestOutcome.KILLED, outs)
+
+ except asyncio.TimeoutError:
+ proc.terminate()
+ return (TestOutcome.KILLED, 'timeout')
+
+ except Exception: # pylint: disable=W0703
+ proc.terminate()
+ return (TestOutcome.INCOMPETENT, traceback.format_exc())
+
+ finally:
+ await proc.wait()
+
+
def run_tests(command, timeout=None):
"""Run test command in a subprocess.
@@ -14,7 +55,7 @@ def run_tests(command, timeout=None):
it exits with any other code, we assume a test failed. If the call to launch
the subprocess throws an exception, we consider the test 'incompetent'.
- Tests which time out are considered 'incompetent' as well.
+ Tests which time out are considered 'killed' as well.
Args:
command (str): The command to execute.
@@ -24,26 +65,8 @@ def run_tests(command, timeout=None):
containing the output of the command.
"""
- # We want to avoid writing pyc files in case our changes happen too fast for Python to
- # notice them. If the timestamps between two changes are too small, Python won't recompile
- # the source.
- env = dict(os.environ)
- env['PYTHONDONTWRITEBUTECODE'] = '1'
+ if sys.platform == "win32":
+ asyncio.set_event_loop_policy(
+ asyncio.WindowsProactorEventLoopPolicy())
- try:
- proc = subprocess.run(
- command,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- check=True,
- universal_newlines=True,
- timeout=timeout,
- shell=True,
- env=env,
- )
-
- return (TestOutcome.SURVIVED, proc.stdout)
- except subprocess.CalledProcessError as exc:
- return (TestOutcome.KILLED, exc.output)
- except Exception: # pylint: disable=W0703
- return (TestOutcome.INCOMPETENT, traceback.format_exc())
+ return asyncio.run(_run_tests(command, timeout))
