twister: extend reason field in Twister reports

Extended the reason field in Twister report to include
more detailed information for 'Build failure' and
'CMake build failure'

Signed-off-by: Grzegorz Chwierut <grzegorz.chwierut@nordicsemi.no>
This commit is contained in:
Grzegorz Chwierut 2025-02-07 16:05:21 +01:00 committed by Benjamin Cabé
commit cac967db9f

View file

@ -372,7 +372,6 @@ class Reporting:
suite["available_rom"] = available_rom
if instance.status in [TwisterStatus.ERROR, TwisterStatus.FAIL]:
suite['status'] = instance.status
suite["reason"] = instance.reason
# FIXME
if os.path.exists(pytest_log):
suite["log"] = self.process_log(pytest_log)
@ -382,6 +381,11 @@ class Reporting:
suite["log"] = self.process_log(device_log)
else:
suite["log"] = self.process_log(build_log)
suite["reason"] = self.get_detailed_reason(instance.reason, suite["log"])
# update the reason to get more details also in other reports (e.g. junit)
# where build log is not available
instance.reason = suite["reason"]
elif instance.status == TwisterStatus.FILTER:
suite["status"] = TwisterStatus.FILTER
suite["reason"] = instance.reason
@ -798,3 +802,55 @@ class Reporting:
self.json_report(json_platform_file + "_footprint.json",
version=self.env.version, platform=platform.name,
filters=self.json_filters['footprint.json'])
def get_detailed_reason(self, reason: str, log: str) -> str:
if reason == 'CMake build failure':
if error_key := self._parse_cmake_build_failure(log):
return f"{reason} - {error_key}"
elif reason == 'Build failure': # noqa SIM102
if error_key := self._parse_build_failure(log):
return f"{reason} - {error_key}"
return reason
@staticmethod
def _parse_cmake_build_failure(log: str) -> str | None:
last_warning = 'no warning found'
lines = log.splitlines()
for i, line in enumerate(lines):
if "warning: " in line:
last_warning = line
elif "devicetree error: " in line:
return "devicetree error"
elif "fatal error: " in line:
return line[line.index('fatal error: ') :].strip()
elif "error: " in line: # error: Aborting due to Kconfig warnings
if "undefined symbol" in last_warning:
return last_warning[last_warning.index('undefined symbol') :].strip()
return last_warning
elif "CMake Error at" in line:
for next_line in lines[i + 1 :]:
if next_line.strip():
return line + ' ' + next_line
return line
return None
@staticmethod
def _parse_build_failure(log: str) -> str | None:
last_warning = ''
lines = log.splitlines()
for i, line in enumerate(lines):
if "undefined reference" in line:
return line[line.index('undefined reference') :].strip()
elif "error: ld returned" in line:
if last_warning:
return last_warning
elif "overflowed by" in lines[i - 1]:
return "ld.bfd: region overflowed"
elif "ld.bfd: warning: " in lines[i - 1]:
return "ld.bfd:" + lines[i - 1].split("ld.bfd:", 1)[-1]
return line
elif "error: " in line:
return line[line.index('error: ') :].strip()
elif ": in function " in line:
last_warning = line[line.index('in function') :].strip()
return None