twister: pytest: Capture and log error messages from subprocess

Updated the Pytest-Harness methods to capture and log error messages from
the subprocess, when pytest command fails.
Ensured that error messages are logged with `--inline-log` option
and placed in twister log reports.

Signed-off-by: Grzegorz Chwierut <grzegorz.chwierut@nordicsemi.no>
This commit is contained in:
Grzegorz Chwierut 2024-08-12 16:07:14 +02:00 committed by Anas Nashif
commit 913434e83d

View file

@ -16,6 +16,7 @@ import time
import shutil import shutil
import json import json
from pytest import ExitCode
from twisterlib.reports import ReportStatus from twisterlib.reports import ReportStatus
from twisterlib.error import ConfigurationError from twisterlib.error import ConfigurationError
from twisterlib.environment import ZEPHYR_BASE, PYTEST_PLUGIN_INSTALLED from twisterlib.environment import ZEPHYR_BASE, PYTEST_PLUGIN_INSTALLED
@ -354,6 +355,7 @@ class Pytest(Harness):
self.report_file = os.path.join(self.running_dir, 'report.xml') self.report_file = os.path.join(self.running_dir, 'report.xml')
self.pytest_log_file_path = os.path.join(self.running_dir, 'twister_harness.log') self.pytest_log_file_path = os.path.join(self.running_dir, 'twister_harness.log')
self.reserved_serial = None self.reserved_serial = None
self._output = []
def pytest_run(self, timeout): def pytest_run(self, timeout):
try: try:
@ -488,7 +490,7 @@ class Pytest(Harness):
env=env env=env
) as proc: ) as proc:
try: try:
reader_t = threading.Thread(target=self._output_reader, args=(proc, self), daemon=True) reader_t = threading.Thread(target=self._output_reader, args=(proc,), daemon=True)
reader_t.start() reader_t.start()
reader_t.join(timeout) reader_t.join(timeout)
if reader_t.is_alive(): if reader_t.is_alive():
@ -502,6 +504,13 @@ class Pytest(Harness):
self.status = TwisterStatus.FAIL self.status = TwisterStatus.FAIL
proc.kill() proc.kill()
if proc.returncode in (ExitCode.INTERRUPTED, ExitCode.USAGE_ERROR, ExitCode.INTERNAL_ERROR):
self.status = TwisterStatus.ERROR
self.instance.reason = f'Pytest error - return code {proc.returncode}'
with open(self.pytest_log_file_path, 'w') as log_file:
log_file.write(shlex.join(cmd) + '\n\n')
log_file.write('\n'.join(self._output))
@staticmethod @staticmethod
def _update_command_with_env_dependencies(cmd): def _update_command_with_env_dependencies(cmd):
''' '''
@ -524,14 +533,15 @@ class Pytest(Harness):
return cmd, env return cmd, env
@staticmethod def _output_reader(self, proc):
def _output_reader(proc, harness): self._output = []
while proc.stdout.readable() and proc.poll() is None: while proc.stdout.readable() and proc.poll() is None:
line = proc.stdout.readline().decode().strip() line = proc.stdout.readline().decode().strip()
if not line: if not line:
continue continue
self._output.append(line)
logger.debug('PYTEST: %s', line) logger.debug('PYTEST: %s', line)
harness.parse_record(line) self.parse_record(line)
proc.communicate() proc.communicate()
def _update_test_status(self): def _update_test_status(self):