Commit 8bf4418c authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent f2beeb95
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2018 Nexedi SA and Contributors.
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
"""runTestSuite - run neotest under Nexedi testing infrastructure.
neotest must be on $PATH.
"""
# XXX split -> nxdtest + NXDTestfile (name=?)
from erp5.util.taskdistribution import TaskDistributor
from subprocess import Popen, PIPE
from time import time, strftime, gmtime
import os, sys, threading, argparse, logging, traceback, re
def main():
# testnode executes us giving URL to master results collecting instance and other details
# https://lab.nexedi.com/nexedi/erp5/blob/744f3fde/erp5/util/testnode/UnitTestRunner.py#L137
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--master_url', help='The URL of Master controling many suites')
parser.add_argument('--revision', help='The revision to test', default='dummy_revision')
parser.add_argument('--test_suite', help='The test suite name')
parser.add_argument('--test_suite_title', help='The test suite title')
parser.add_argument('--test_node_title', help='The test node title')
parser.add_argument('--project_title', help='The project title')
parser.add_argument('--verbose', action='store_true', help='increase output verbosity')
args = parser.parse_args()
# if verbose -> log to stderr
logger = None
if args.verbose:
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger()
# connect to master and create 'test result' object with list of tests to run
tool = TaskDistributor(portal_url = args.master_url, logger = logger)
test_result = tool.createTestResult(
revision = args.revision,
test_name_list = ['test-go', 'test-py', 'bench-local'],
node_title = args.test_node_title,
test_title = args.test_suite_title or args.test_suite,
project_title = args.project_title)
if test_result is None:
# a test run for given name and revision has already been completed
return
# make sure we get output from subprocesses without delay.
# go does not buffer stdout/stderr by default, but python does for stdout.
# tell python not to buffer anything.
os.environ['PYTHONUNBUFFERED'] = 'y'
# run the tests
devnull = open(os.devnull)
while 1:
# ask master for next test to run; stop if no more.
test_result_line = test_result.start()
if test_result_line is None:
break
# run `neotest <test-name>`
testname = test_result_line.name
argv = ['neotest', testname]
tstart = time()
try:
# NOTE runs with unchanged cwd. Instance wrapper cares to set cwd before running us.
# bufsize=1 means 'line buffered'
p = Popen(argv, stdin=devnull, stdout=PIPE, stderr=PIPE, bufsize=1)
except:
stdout, stderr = '', traceback.format_exc()
sys.stderr.write(stderr)
ok = False
else:
# tee >stdout,stderr so we can also see in testnode logs
# (explicit teeing instead of p.communicate() to be able to see incremental progress)
buf_out = []
buf_err = []
tout = threading.Thread(target=tee, args=(p.stdout, sys.stdout, buf_out))
terr = threading.Thread(target=tee, args=(p.stderr, sys.stderr, buf_err))
tout.start()
terr.start()
tout.join(); stdout = ''.join(buf_out)
terr.join(); stderr = ''.join(buf_err)
p.wait()
ok = (p.returncode == 0)
# default status dict just by exit code
status = {
'test_count': 1,
'error_count': (0 if ok else 1),
'failure_count': 0,
'skip_count': 0,
#html_test_result
}
# postprocess output, if we can
summaryf = globals().get(testname.replace('-', '_') + '_summary')
if summaryf is not None:
try:
summary = summaryf(stdout)
except:
bad = traceback.format_exc()
sys.stderr.write(bad)
stderr += bad
status['error_count'] += 1
else:
status.update(summary)
tend = time()
# report result of test run back to master
test_result_line.stop(
command = ' '.join(argv),
duration = tend - tstart,
date = strftime("%Y/%m/%d %H:%M:%S", gmtime(tend)),
stdout = stdout,
stderr = stderr,
**status
)
# tee, similar to tee(1) utility, copies data from fin to fout appending them to buf.
def tee(fin, fout, buf):
while 1:
# NOTE use raw os.read because it does not wait for full data to be available.
# ( we could use fin.readline(), but there are cases when e.g. progress
# is reported via printing consequent dots on the same line and
# readline() won't work for that.
#
# besides when a lot of output is available it would be a waste to
# read/flush it line-by-line. )
data = os.read(fin.fileno(), 4096)
if not(data):
return # EOF
fout.write(data)
fout.flush()
buf.append(data)
# xint converts number from neo/py test output to integer
def xint(s):
s = s.strip()
if s == '.':
return 0
else:
return int(s)
# extract summary from neo/py test run
def test_py_summary(stdout):
# Test Module | run | unexpected | expected | skipped | time
# ...
# Summary | 366 | . | 9 | . | 353.47s
m = re.search(r'^\s*summary.*$', stdout, re.M | re.I)
assert m is not None, "could not find summary line"
summary = m.group(0)
_, nrun, nfail, nxfail, nskip, _ = summary.split('|')
return {
'test_count': xint(nrun),
'error_count': xint(nfail),
'failure_count': xint(nxfail),
'skip_count': xint(nskip),
}
if __name__ == '__main__':
main()
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
neotest must be on $PATH. neotest must be on $PATH.
""" """
# XXX split -> nxdtest + NXDTestfile (name=?)
from erp5.util.taskdistribution import TaskDistributor from erp5.util.taskdistribution import TaskDistributor
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from time import time, strftime, gmtime from time import time, strftime, gmtime
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment