@@ -85,22 +85,22 @@ def add_counter(self, key: str, test: str = '') -> None:
8585 def print_counters (self , indent : int = 0 ):
8686 for key , value in self .counters .items ():
8787 print (f'{ " " * indent } { value .quantity :4} { key } ' )
88- if value .subcounters . counters :
88+ if value .has_subcounters () :
8989 value .subcounters .print_counters (indent + 4 )
9090
9191 def sort_by_quantity (self ):
9292 self .counters = dict (
9393 sorted (self .counters .items (), key = lambda item : item [1 ].quantity , reverse = True )
9494 )
9595 for value in self .counters .values ():
96- if value .subcounters . counters :
96+ if value .has_subcounters () :
9797 value .subcounters .sort_by_quantity ()
9898
9999 def get_next_entry (self , depth : int = 0 , max_depth : int = 10 ):
100100 for key , value in self .counters .items ():
101101 # limit number of test files to 100 to not exceed CSV cell limit
102102 yield depth , value .quantity , key , ', ' .join (value .tests [0 :100 ])
103- if value .subcounters . counters and depth < max_depth :
103+ if value .has_subcounters () and depth < max_depth :
104104 yield from value .subcounters .get_next_entry (depth + 1 , max_depth )
105105
106106 def _flatten (self ):
@@ -110,7 +110,7 @@ def _flatten(self):
110110 do not contain any further nested subcounters.
111111 """
112112 for key , value in self .counters .items ():
113- if value .subcounters . counters :
113+ if value .has_subcounters () :
114114 yield from value .subcounters ._flatten ()
115115 else :
116116 yield key , value
@@ -130,6 +130,9 @@ def append(self, test: str = ''):
130130 if test :
131131 self .tests .append (test )
132132
133+ def has_subcounters (self ):
134+ return bool (self .subcounters .counters )
135+
133136
134137class TwisterReports :
135138 def __init__ (self ):
@@ -161,41 +164,44 @@ def parse_testsuite(self, testsuite):
161164 if ts_status not in ('error' , 'failed' ):
162165 return
163166
164- ts_reason = testsuite .get ('reason' ) or 'Unknown reason'
165- self .errors .add_counter (ts_reason )
166167 ts_platform = testsuite .get ('platform' ) or 'Unknown platform'
167168 self .platforms .add_counter (ts_platform )
169+ ts_reason = testsuite .get ('reason' ) or 'Unknown reason'
168170 ts_log = testsuite .get ('log' )
169171 test_identifier = f'{ testsuite .get ("platform" )} :{ testsuite .get ("name" )} '
170172
171- matched = self ._parse_ts_error_log (
172- self .errors .counters [ts_reason ].subcounters , ts_reason , ts_log , test_identifier
173- )
173+ # CMake and Build failures are treated separately.
174+ # Extract detailed information to group errors. Keep the parsing methods
175+ # to allow for further customization and keep backward compatibility.
176+ if ts_reason .startswith ('CMake build failure' ):
177+ reason = 'CMake build failure'
178+ self .errors .add_counter (reason )
179+ error_key = ts_reason .split (reason , 1 )[- 1 ].lstrip (' -' )
180+ if not error_key :
181+ error_key = self ._parse_cmake_build_failure (ts_log )
182+ self .errors .counters [reason ].subcounters .add_counter (error_key , test_identifier )
183+ ts_reason = reason
184+ elif ts_reason .startswith ('Build failure' ):
185+ reason = 'Build failure'
186+ self .errors .add_counter (reason )
187+ error_key = ts_reason .split (reason , 1 )[- 1 ].lstrip (' -' )
188+ if not error_key :
189+ error_key = self ._parse_build_failure (ts_log )
190+ self .errors .counters [reason ].subcounters .add_counter (error_key , test_identifier )
191+ ts_reason = reason
192+ else :
193+ self .errors .add_counter (ts_reason )
174194
175195 # Process testcases
176196 for tc in testsuite .get ('testcases' , []):
177197 tc_reason = tc .get ('reason' )
178198 tc_log = tc .get ('log' )
179199 if tc_reason and tc_log :
180200 self .errors .counters [ts_reason ].subcounters .add_counter (tc_reason , test_identifier )
181- matched = True
182201
183- if not matched :
202+ if not self . errors . counters [ ts_reason ]. has_subcounters () :
184203 self .errors .counters [ts_reason ].tests .append (test_identifier )
185204
186- def _parse_ts_error_log (
187- self , counters : Counters , reason : str , log : str , test : str = ''
188- ) -> bool :
189- if reason == 'CMake build failure' :
190- if error_key := self ._parse_cmake_build_failure (log ):
191- counters .add_counter (error_key , test )
192- return True
193- elif reason == 'Build failure' : # noqa SIM102
194- if error_key := self ._parse_build_failure (log ):
195- counters .add_counter (error_key , test )
196- return True
197- return False
198-
199205 def _parse_cmake_build_failure (self , log : str ) -> str | None :
200206 last_warning = 'no warning found'
201207 lines = log .splitlines ()
@@ -263,32 +269,23 @@ def parse_testsuite(self, testsuite):
263269 if ts_status not in ('error' , 'failed' ):
264270 return
265271
266- ts_reason = testsuite .get ('reason' ) or 'Unknown reason'
267- self .errors .add_counter (ts_reason )
268272 ts_log = testsuite .get ('log' )
269273 test_identifier = f'{ testsuite .get ("platform" )} :{ testsuite .get ("name" )} '
270- self ._parse_log_with_error_paterns (
271- self .errors .counters [ts_reason ].subcounters , ts_log , test_identifier
272- )
274+ if key := self ._parse_log_with_error_paterns (ts_log ):
275+ self .errors .add_counter (key , test_identifier )
273276 # Process testcases
274277 for tc in testsuite .get ('testcases' , []):
275- tc_reason = tc .get ('reason' )
276278 tc_log = tc .get ('log' )
277- if tc_reason and tc_log :
278- self .errors .counters [ts_reason ].subcounters .add_counter (tc_reason )
279- self ._parse_log_with_error_paterns (
280- self .errors .counters [ts_reason ].subcounters .counters [tc_reason ].subcounters ,
281- tc_log ,
282- test_identifier ,
283- )
284-
285- def _parse_log_with_error_paterns (self , counters : Counters , log : str , test : str = '' ):
279+ if tc_log and (key := self ._parse_log_with_error_paterns (tc_log )):
280+ self .errors .add_counter (key , test_identifier )
281+
282+ def _parse_log_with_error_paterns (self , log : str ) -> str | None :
286283 for line in log .splitlines ():
287284 for error_pattern in self .error_patterns :
288285 if error_pattern in line :
289286 logger .debug (f'Matched: { error_pattern } in { line } ' )
290- counters . add_counter ( error_pattern , test )
291- return
287+ return error_pattern
288+ return None
292289
293290
294291class EnhancedJSONEncoder (json .JSONEncoder ):
@@ -363,7 +360,7 @@ def main():
363360 if not reports .errors .counters :
364361 return
365362
366- if args .platforms :
363+ if args .platforms and reports . platforms . counters :
367364 print ('\n Errors per platform:' )
368365 reports .platforms .print_counters ()
369366
0 commit comments