From f8781cce92e6a7e770048282361267790386735b Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 22 Feb 2021 13:01:35 -0500 Subject: [PATCH 1/2] Remove subprocess usage from discovery This commit removes the usage of subprocess from the discovery/list test phase in TestProcessor. Previously to get a list of tests from discovery we would subprocess out to a new python interpreter calling the subunit runner with the list command and capture stdout from process and parse that sububit stream to get the test ids. However, this was not strictly necessary as we're not doing anything in parallel, so we should be able to just call the subunit_runner directly inline and use an in memory buffer instead of stdout. This has the advantage of not interfering with sys.stdout, in the previous method if anything besides the subunit_runner was printing to stdout it would cause a parser error because of the non-subunit data mixed into the stream. --- stestr/test_processor.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/stestr/test_processor.py b/stestr/test_processor.py index 67de043..570a995 100644 --- a/stestr/test_processor.py +++ b/stestr/test_processor.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from functools import partial import io import os import re @@ -24,6 +25,8 @@ from stestr import results from stestr import scheduler from stestr import selection +from stestr.subunit_runner import program as sub_prog +from stestr.subunit_runner import run as sub_run from stestr import testlist @@ -213,27 +216,30 @@ def list_tests(self): :return: A list of test ids. """ - run_proc = self._start_process(self.list_cmd) - out, err = run_proc.communicate() - if run_proc.returncode != 0: + discovery_buffer = io.BytesIO() + runner = sub_run.SubunitTestRunner + list_cmd = [x.strip('"') for x in self.list_cmd.split(' ')[2:-1] if x] + try: + sub_prog.TestProgram(module=None, argv=list_cmd, + testRunner=partial(runner, + stdout=discovery_buffer)) + except Exception: sys.stdout.write("\n=========================\n" "Failures during discovery" "\n=========================\n") new_out = io.BytesIO() v2.ByteStreamToStreamResult( - io.BytesIO(out), 'stdout').run( - results.CatFiles(new_out)) + discovery_buffer, 'stdout').run(results.CatFiles(new_out)) out = new_out.getvalue() if out: sys.stdout.write(out.decode('utf8')) - if err: - sys.stderr.write(err.decode('utf8')) sys.stdout.write("\n" + "=" * 80 + "\n" "The above traceback was encountered during " "test discovery which imports all the found test" " modules in the specified test_path.\n") exit(100) - ids = testlist.parse_enumeration(out) + ids = testlist.parse_enumeration(discovery_buffer.getvalue()) + discovery_buffer.close() return ids def run_tests(self): From 48363a6ee651eff38e4f3b58ff7181ccc043f567 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 22 Feb 2021 13:42:25 -0500 Subject: [PATCH 2/2] Raise instead of exit in subunit_runner discovery --- stestr/subunit_runner/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stestr/subunit_runner/run.py b/stestr/subunit_runner/run.py index 93fae58..dfa379f 100644 --- a/stestr/subunit_runner/run.py +++ b/stestr/subunit_runner/run.py @@ -64,7 +64,7 @@ def list(self, test, loader=None): result.status(file_name="import errors", runnable=False, file_bytes=failed_descr, mime_type="text/plain;charset=utf8") - sys.exit(2) + raise Exception("listing test %s errored" % test) def _list(self, test): test_ids, errors = program.list_test(test)