mtest: create TestRun object early on

This will provide a way to pass more information from the TestHarness
local variables to the SingleTestRunner and use them outside the
run_test function.  For example, the name could be used to report
progress while the tests are running.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
pull/8001/head
Paolo Bonzini 4 years ago
parent 2f836e3acc
commit 30741a0f20
  1. 99
      mesonbuild/mtest.py

@ -166,6 +166,8 @@ class TestException(MesonException):
@enum.unique @enum.unique
class TestResult(enum.Enum): class TestResult(enum.Enum):
PENDING = 'PENDING'
RUNNING = 'RUNNING'
OK = 'OK' OK = 'OK'
TIMEOUT = 'TIMEOUT' TIMEOUT = 'TIMEOUT'
INTERRUPT = 'INTERRUPT' INTERRUPT = 'INTERRUPT'
@ -450,40 +452,50 @@ class JunitBuilder:
class TestRun: class TestRun:
@classmethod def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str]):
def make_gtest(cls, test: TestSerialisation, test_env: T.Dict[str, str], self.res = TestResult.PENDING
returncode: int, starttime: float, duration: float, self.test = test
stdo: T.Optional[str], stde: T.Optional[str], self.results = list() # type: T.List[TestResult]
cmd: T.Optional[T.List[str]]) -> 'TestRun': self.returncode = 0
filename = '{}.xml'.format(test.name) self.starttime = None # type: T.Optional[float]
if test.workdir: self.duration = None # type: T.Optional[float]
filename = os.path.join(test.workdir, filename) self.stdo = None # type: T.Optional[str]
self.stde = None # type: T.Optional[str]
self.cmd = None # type: T.Optional[T.List[str]]
self.env = dict() # type: T.Dict[str, str]
self.should_fail = test.should_fail
self.project = test.project_name
self.junit = None # type: T.Optional[et.ElementTree]
def start(self) -> None:
self.res = TestResult.RUNNING
self.starttime = time.time()
def complete_gtest(self, returncode: int,
stdo: T.Optional[str], stde: T.Optional[str],
cmd: T.List[str]) -> None:
filename = '{}.xml'.format(self.test.name)
if self.test.workdir:
filename = os.path.join(self.test.workdir, filename)
tree = et.parse(filename) tree = et.parse(filename)
return cls.make_exitcode( self.complete_exitcode(returncode, stdo, stde, cmd, junit=tree)
test, test_env, returncode, starttime, duration, stdo, stde, cmd,
junit=tree)
@classmethod def complete_exitcode(self, returncode: int,
def make_exitcode(cls, test: TestSerialisation, test_env: T.Dict[str, str], stdo: T.Optional[str], stde: T.Optional[str],
returncode: int, starttime: float, duration: float, cmd: T.List[str],
stdo: T.Optional[str], stde: T.Optional[str], **kwargs: T.Any) -> None:
cmd: T.Optional[T.List[str]], **kwargs: T.Any) -> 'TestRun':
if returncode == GNU_SKIP_RETURNCODE: if returncode == GNU_SKIP_RETURNCODE:
res = TestResult.SKIP res = TestResult.SKIP
elif returncode == GNU_ERROR_RETURNCODE: elif returncode == GNU_ERROR_RETURNCODE:
res = TestResult.ERROR res = TestResult.ERROR
elif test.should_fail: elif self.should_fail:
res = TestResult.EXPECTEDFAIL if bool(returncode) else TestResult.UNEXPECTEDPASS res = TestResult.EXPECTEDFAIL if bool(returncode) else TestResult.UNEXPECTEDPASS
else: else:
res = TestResult.FAIL if bool(returncode) else TestResult.OK res = TestResult.FAIL if bool(returncode) else TestResult.OK
return cls(test, test_env, res, [], returncode, starttime, duration, stdo, stde, cmd, **kwargs) self.complete(res, [], returncode, stdo, stde, cmd, **kwargs)
@classmethod def complete_tap(self, returncode: int, stdo: str, stde: str, cmd: T.List[str]) -> None:
def make_tap(cls, test: TestSerialisation, test_env: T.Dict[str, str],
returncode: int, starttime: float, duration: float,
stdo: str, stde: str,
cmd: T.Optional[T.List[str]]) -> 'TestRun':
res = None # type: T.Optional[TestResult] res = None # type: T.Optional[TestResult]
results = [] # type: T.List[TestResult] results = [] # type: T.List[TestResult]
failed = False failed = False
@ -510,30 +522,25 @@ class TestRun:
if all(t is TestResult.SKIP for t in results): if all(t is TestResult.SKIP for t in results):
# This includes the case where num_tests is zero # This includes the case where num_tests is zero
res = TestResult.SKIP res = TestResult.SKIP
elif test.should_fail: elif self.should_fail:
res = TestResult.EXPECTEDFAIL if failed else TestResult.UNEXPECTEDPASS res = TestResult.EXPECTEDFAIL if failed else TestResult.UNEXPECTEDPASS
else: else:
res = TestResult.FAIL if failed else TestResult.OK res = TestResult.FAIL if failed else TestResult.OK
return cls(test, test_env, res, results, returncode, starttime, duration, stdo, stde, cmd) self.complete(res, results, returncode, stdo, stde, cmd)
def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str], def complete(self, res: TestResult, results: T.List[TestResult],
res: TestResult, results: T.List[TestResult], returncode: returncode: int,
int, starttime: float, duration: float,
stdo: T.Optional[str], stde: T.Optional[str], stdo: T.Optional[str], stde: T.Optional[str],
cmd: T.Optional[T.List[str]], *, junit: T.Optional[et.ElementTree] = None): cmd: T.List[str], *, junit: T.Optional[et.ElementTree] = None) -> None:
assert isinstance(res, TestResult) assert isinstance(res, TestResult)
self.res = res self.res = res
self.results = results # May be an empty list self.results = results
self.returncode = returncode self.returncode = returncode
self.starttime = starttime self.duration = time.time() - self.starttime
self.duration = duration
self.stdo = stdo self.stdo = stdo
self.stde = stde self.stde = stde
self.cmd = cmd self.cmd = cmd
self.env = test_env
self.should_fail = test.should_fail
self.project = test.project_name
self.junit = junit self.junit = junit
def get_log(self) -> str: def get_log(self) -> str:
@ -645,6 +652,7 @@ class SingleTestRunner:
self.test_env = test_env self.test_env = test_env
self.env = env self.env = env
self.options = options self.options = options
self.runobj = TestRun(test, test_env)
def _get_cmd(self) -> T.Optional[T.List[str]]: def _get_cmd(self) -> T.Optional[T.List[str]]:
if self.test.fname[0].endswith('.jar'): if self.test.fname[0].endswith('.jar'):
@ -668,14 +676,16 @@ class SingleTestRunner:
async def run(self) -> TestRun: async def run(self) -> TestRun:
cmd = self._get_cmd() cmd = self._get_cmd()
self.runobj.start()
if cmd is None: if cmd is None:
skip_stdout = 'Not run because can not execute cross compiled binaries.' skip_stdout = 'Not run because can not execute cross compiled binaries.'
return TestRun(self.test, self.test_env, TestResult.SKIP, [], GNU_SKIP_RETURNCODE, time.time(), 0.0, skip_stdout, None, None) self.runobj.complete(TestResult.SKIP, [], GNU_SKIP_RETURNCODE, skip_stdout, None, None)
else: else:
wrap = TestHarness.get_wrapper(self.options) wrap = TestHarness.get_wrapper(self.options)
if self.options.gdb: if self.options.gdb:
self.test.timeout = None self.test.timeout = None
return await self._run_cmd(wrap + cmd + self.test.cmd_args + self.options.test_args) await self._run_cmd(wrap + cmd + self.test.cmd_args + self.options.test_args)
return self.runobj
async def _run_subprocess(self, args: T.List[str], *, timeout: T.Optional[int], async def _run_subprocess(self, args: T.List[str], *, timeout: T.Optional[int],
stdout: T.IO, stderr: T.IO, stdout: T.IO, stderr: T.IO,
@ -762,9 +772,7 @@ class SingleTestRunner:
return p.returncode or 0, result, additional_error return p.returncode or 0, result, additional_error
async def _run_cmd(self, cmd: T.List[str]) -> TestRun: async def _run_cmd(self, cmd: T.List[str]) -> None:
starttime = time.time()
if self.test.extra_paths: if self.test.extra_paths:
self.env['PATH'] = os.pathsep.join(self.test.extra_paths + ['']) + self.env['PATH'] self.env['PATH'] = os.pathsep.join(self.test.extra_paths + ['']) + self.env['PATH']
winecmd = [] winecmd = []
@ -813,8 +821,6 @@ class SingleTestRunner:
stderr=stderr, stderr=stderr,
env=self.env, env=self.env,
cwd=self.test.workdir) cwd=self.test.workdir)
endtime = time.time()
duration = endtime - starttime
if additional_error is None: if additional_error is None:
if stdout is None: if stdout is None:
stdo = '' stdo = ''
@ -830,17 +836,16 @@ class SingleTestRunner:
stdo = "" stdo = ""
stde = additional_error stde = additional_error
if result: if result:
return TestRun(self.test, self.test_env, result, [], returncode, starttime, duration, stdo, stde, cmd) self.runobj.complete(result, [], returncode, stdo, stde, cmd)
else: else:
if self.test.protocol is TestProtocol.EXITCODE: if self.test.protocol is TestProtocol.EXITCODE:
return TestRun.make_exitcode(self.test, self.test_env, returncode, starttime, duration, stdo, stde, cmd) self.runobj.complete_exitcode(returncode, stdo, stde, cmd)
elif self.test.protocol is TestProtocol.GTEST: elif self.test.protocol is TestProtocol.GTEST:
return TestRun.make_gtest(self.test, self.test_env, returncode, starttime, duration, stdo, stde, cmd) self.runobj.complete_gtest(returncode, stdo, stde, cmd)
else: else:
if self.options.verbose: if self.options.verbose:
print(stdo, end='') print(stdo, end='')
return TestRun.make_tap(self.test, self.test_env, returncode, starttime, duration, stdo, stde, cmd) self.runobj.complete_tap(returncode, stdo, stde, cmd)
class TestHarness: class TestHarness:
def __init__(self, options: argparse.Namespace): def __init__(self, options: argparse.Namespace):

Loading…
Cancel
Save