test: Remove polling loop from test_runner
This commit is contained in:
1 file changed
+46
-33
@@ -14,6 +14,7 @@ For a description of arguments recognized by test scripts, see
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from concurrent import futures
|
||||||
import configparser
|
import configparser
|
||||||
import csv
|
import csv
|
||||||
import datetime
|
import datetime
|
||||||
@@ -703,15 +704,15 @@ class TestHandler:
|
|||||||
"""
|
"""
|
||||||
Trigger the test scripts passed in via the list.
|
Trigger the test scripts passed in via the list.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, use_term_control):
|
def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, use_term_control):
|
||||||
assert num_tests_parallel >= 1
|
assert num_tests_parallel >= 1
|
||||||
|
self.executor = futures.ThreadPoolExecutor(max_workers=num_tests_parallel)
|
||||||
self.num_jobs = num_tests_parallel
|
self.num_jobs = num_tests_parallel
|
||||||
self.tests_dir = tests_dir
|
self.tests_dir = tests_dir
|
||||||
self.tmpdir = tmpdir
|
self.tmpdir = tmpdir
|
||||||
self.test_list = test_list
|
self.test_list = test_list
|
||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.jobs = []
|
self.jobs = {}
|
||||||
self.use_term_control = use_term_control
|
self.use_term_control = use_term_control
|
||||||
|
|
||||||
def done(self):
|
def done(self):
|
||||||
@@ -728,47 +729,59 @@ class TestHandler:
|
|||||||
test_argv = test.split()
|
test_argv = test.split()
|
||||||
testdir = "{}/{}_{}".format(self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)
|
testdir = "{}/{}_{}".format(self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)
|
||||||
tmpdir_arg = ["--tmpdir={}".format(testdir)]
|
tmpdir_arg = ["--tmpdir={}".format(testdir)]
|
||||||
self.jobs.append((test,
|
|
||||||
time.time(),
|
def proc_wait(task):
|
||||||
subprocess.Popen([sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg,
|
task[2].wait()
|
||||||
text=True,
|
return task
|
||||||
stdout=log_stdout,
|
|
||||||
stderr=log_stderr),
|
task = [
|
||||||
testdir,
|
test,
|
||||||
log_stdout,
|
time.time(),
|
||||||
log_stderr))
|
subprocess.Popen(
|
||||||
|
[sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg,
|
||||||
|
text=True,
|
||||||
|
stdout=log_stdout,
|
||||||
|
stderr=log_stderr,
|
||||||
|
),
|
||||||
|
testdir,
|
||||||
|
log_stdout,
|
||||||
|
log_stderr,
|
||||||
|
]
|
||||||
|
fut = self.executor.submit(proc_wait, task)
|
||||||
|
self.jobs[fut] = test
|
||||||
if not self.jobs:
|
if not self.jobs:
|
||||||
raise IndexError('pop from empty list')
|
raise IndexError('pop from empty list')
|
||||||
|
|
||||||
# Print remaining running jobs when all jobs have been started.
|
# Print remaining running jobs when all jobs have been started.
|
||||||
if not self.test_list:
|
if not self.test_list:
|
||||||
print("Remaining jobs: [{}]".format(", ".join(j[0] for j in self.jobs)))
|
print("Remaining jobs: [{}]".format(", ".join(sorted(self.jobs.values()))))
|
||||||
|
|
||||||
dot_count = 0
|
dot_count = 0
|
||||||
while True:
|
while True:
|
||||||
# Return all procs that have finished, if any. Otherwise sleep until there is one.
|
# Return all procs that have finished, if any. Otherwise sleep until there is one.
|
||||||
time.sleep(.5)
|
procs = futures.wait(self.jobs.keys(), timeout=.5, return_when=futures.FIRST_COMPLETED)
|
||||||
|
self.jobs = {fut: self.jobs[fut] for fut in procs.not_done}
|
||||||
ret = []
|
ret = []
|
||||||
for job in self.jobs:
|
for job in procs.done:
|
||||||
(name, start_time, proc, testdir, log_out, log_err) = job
|
(name, start_time, proc, testdir, log_out, log_err) = job.result()
|
||||||
if proc.poll() is not None:
|
|
||||||
log_out.seek(0), log_err.seek(0)
|
log_out.seek(0), log_err.seek(0)
|
||||||
[stdout, stderr] = [log_file.read().decode('utf-8') for log_file in (log_out, log_err)]
|
[stdout, stderr] = [log_file.read().decode('utf-8') for log_file in (log_out, log_err)]
|
||||||
log_out.close(), log_err.close()
|
log_out.close(), log_err.close()
|
||||||
skip_reason = None
|
skip_reason = None
|
||||||
if proc.returncode == TEST_EXIT_PASSED and stderr == "":
|
if proc.returncode == TEST_EXIT_PASSED and stderr == "":
|
||||||
status = "Passed"
|
status = "Passed"
|
||||||
elif proc.returncode == TEST_EXIT_SKIPPED:
|
elif proc.returncode == TEST_EXIT_SKIPPED:
|
||||||
status = "Skipped"
|
status = "Skipped"
|
||||||
skip_reason = re.search(r"Test Skipped: (.*)", stdout).group(1)
|
skip_reason = re.search(r"Test Skipped: (.*)", stdout).group(1)
|
||||||
else:
|
else:
|
||||||
status = "Failed"
|
status = "Failed"
|
||||||
self.jobs.remove(job)
|
|
||||||
if self.use_term_control:
|
if self.use_term_control:
|
||||||
clearline = '\r' + (' ' * dot_count) + '\r'
|
clearline = '\r' + (' ' * dot_count) + '\r'
|
||||||
print(clearline, end='', flush=True)
|
print(clearline, end='', flush=True)
|
||||||
dot_count = 0
|
dot_count = 0
|
||||||
ret.append((TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr, skip_reason))
|
ret.append((TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr, skip_reason))
|
||||||
if ret:
|
if ret:
|
||||||
return ret
|
return ret
|
||||||
if self.use_term_control:
|
if self.use_term_control:
|
||||||
|
|||||||
Reference in new issue
Block a user