11#!/usr/bin/env python3
22
3- import os
4- import sys
3+ from collections import defaultdict
54import json
5+ import os
66import re
7+ import sys
78
89SKIP = - 1 # Test was not performed
910PASS = 0 # (PASS) Compiled successfully
@@ -75,9 +76,9 @@ def track(self, test_entry):
7576 self .boards .add (test_entry .board )
7677 self .sketches .add (test_entry .sketch )
7778
78- ARTIFACT_TESTS = {} # { artifact: TestGroup() }
79- BOARD_TESTS = {} # { board: TestGroup() }
80- SKETCH_TESTS = {} # { artifact: { sketch: TestGroup() } }
79+ ARTIFACT_TESTS = defaultdict ( TestGroup ) # { artifact: TestGroup() }
80+ BOARD_TESTS = defaultdict ( TestGroup ) # { board: TestGroup() }
81+ SKETCH_TESTS = defaultdict ( defaultdict ( TestGroup )) # { artifact: { sketch: TestGroup() } }
8182
8283def log_test (artifact , board , sketch , exceptions , status , issues , job_link = None ):
8384 """
@@ -96,18 +97,8 @@ def log_test(artifact, board, sketch, exceptions, status, issues, job_link=None)
9697 test_entry = TestEntry (artifact , board , sketch , status , issues )
9798
9899 # Track in global structures
99- if not artifact in ARTIFACT_TESTS :
100- ARTIFACT_TESTS [artifact ] = TestGroup ()
101100 ARTIFACT_TESTS [artifact ].track (test_entry )
102-
103- if not board in BOARD_TESTS :
104- BOARD_TESTS [board ] = TestGroup (link = job_link )
105101 BOARD_TESTS [board ].track (test_entry )
106-
107- if not artifact in SKETCH_TESTS :
108- SKETCH_TESTS [artifact ] = {}
109- if not sketch in SKETCH_TESTS [artifact ]:
110- SKETCH_TESTS [artifact ][sketch ] = TestGroup ()
111102 SKETCH_TESTS [artifact ][sketch ].track (test_entry )
112103
113104def print_test_matrix (artifact , artifact_boards , title , sketch_filter = lambda x : True ):
@@ -136,7 +127,7 @@ def print_test_matrix(artifact, artifact_boards, title, sketch_filter=lambda x:
136127 if not sketch_filter (res ):
137128 continue
138129
139- # (1 ................) (2....) (3................) (4.)
130+ # (1. ................) (2....) (3................) (4.)
140131 match = re .search (r'(libraries|examples)/([^/]+)/(examples/|extras/)?(.*)' , sketch )
141132 if match :
142133 group = match .group (2 )
@@ -212,6 +203,69 @@ def print_test_matrix(artifact, artifact_boards, title, sketch_filter=lambda x:
212203
213204 print ("</table></blockquote></details>\n " )
214205
206+ BOARD_MEM_REPORTS = defaultdict (dict ) # { board: { region: [used, total] } }
207+ BOARD_CONFIGS = defaultdict (dict ) # { board: { config_symbol: value } }
208+ REGIONS_FOR_SOC = defaultdict (set ) # { soc: set(regions) }
209+
210+ def print_mem_report (artifact , artifact_boards ):
211+ BASE_COLOR = 0x60
212+ DELTA_COLOR = 0xff - BASE_COLOR
213+
214+ def color_cmd (percent ):
215+ color_amt = int (DELTA_COLOR * percent )
216+ return f"\\ color{{#{ BASE_COLOR + color_amt :02x} { 0xff - color_amt :02x} { BASE_COLOR :02x} }}"
217+
218+ def color_entry (values ):
219+ if not values :
220+ return ""
221+
222+ percent = values [0 ] / values [1 ]
223+ return f"${{{ color_cmd (percent )} \\ frac{{{ values [0 ]} }}{{{ values [1 ]} }}\\ space({ percent * 100 :0.1f} \\ \\ %)}}$"
224+
225+ print ("<table><tr>" , end = '' )
226+ print ("<th rowspan='2' colspan='2'>Board</th>" , end = '' )
227+ print ("<th rowspan='2'>SoC</th>" , end = '' )
228+ print ("<th rowspan='2'>FLASH</th>" , end = '' )
229+ print ("<th rowspan='2'>RAM</th>" , end = '' )
230+ print ("<th colspan='2'>User heaps</th>" , end = '' )
231+ print ("</tr>" )
232+ print ("<tr><th>SYS</th><th>LIBC</th><th>LLEXT</th><th>MBEDTLS</th></tr>" )
233+
234+ for soc , board in sorted ((ALL_BOARD_DATA [board ]['soc' ], board ) for board in artifact_boards ):
235+ max_pct = max ([ (BOARD_MEM_REPORTS [board ][r ][0 ] / BOARD_MEM_REPORTS [board ][r ][1 ]) for r in ('FLASH' , 'RAM' ) ])
236+ icon = ':warning:' if max_pct > 0.90 else ''
237+ board_str = board .replace ('_' , '\\ \\ _' )
238+
239+ row = [
240+ icon ,
241+ f"${{{ color_cmd (max_pct )} \\ texttt{{{ board_str } }}}}$" ,
242+ f"<code>{ soc } </code>" ,
243+ color_entry (BOARD_MEM_REPORTS [board ]['FLASH' ]),
244+ color_entry (BOARD_MEM_REPORTS [board ]['RAM' ]),
245+ f"${{{ BOARD_CONFIGS [board ].get ('CONFIG_HEAP_MEM_POOL_SIZE' , 0 ) } }}$" ,
246+ f"${{{ BOARD_CONFIGS [board ]['CONFIG_SRAM_SIZE' ]* 1024 - BOARD_MEM_REPORTS [board ]['RAM' ][0 ] } }}$" ,
247+ f"${{{ BOARD_CONFIGS [board ]['CONFIG_LLEXT_HEAP_SIZE' ]* 1024 } }}$" ,
248+ f"${{{ BOARD_CONFIGS [board ].get ('CONFIG_MBEDTLS_HEAP_SIZE' , '-' ) } }}$"
249+ ]
250+
251+ print ("<tr>" )
252+ col_aligns = ['center' , 'left' , 'center' , 'right' , 'right' , 'right' , 'right' , 'right' , 'right' ]
253+ for index , cell in enumerate (row ):
254+ print (f"<td align='{ col_aligns [index ]} '>\n \n { cell } \n \n </td>" )
255+ print ("</tr>" )
256+ print ("</table>" )
257+
258+ print ("<details><summary>Raw data</summary><blockquote>\n " )
259+ print ("<table>" )
260+ for soc , board in sorted ((ALL_BOARD_DATA [board ]['soc' ], board ) for board in artifact_boards ):
261+ print (f"<td><code>{ soc } </code></td>" )
262+ print (f"<td><code>{ board } </code></td>" )
263+ print (f"<td><pre>" )
264+ for r in REGIONS_BY_SOC [soc ]:
265+ print (f"{ r :>20} { data [board ].get (r , ['' ,'' ])[0 ]:8} { data [board ].get (r , ['' ,'' ])[1 ]:8} " )
266+ print ("</pre></td></tr>" )
267+ print ("</table></blockquote></details>" )
268+
215269# --- Main Logic ---
216270
217271# Environment Variable Checks
@@ -224,13 +278,45 @@ def print_test_matrix(artifact, artifact_boards, title, sketch_filter=lambda x:
224278 sys .exit (0 )
225279
226280ALL_BOARD_DATA = json .loads (ALL_BOARD_DATA_STR )
281+ ALL_BOARD_DATA = { b ['board' ]: b for b in ALL_BOARD_DATA }
227282
228- for board_data in ALL_BOARD_DATA :
283+ for board_data in ALL_BOARD_DATA . values () :
229284 # Extract common fields
230285 artifact = board_data ['artifact' ]
231286 board = board_data ['board' ]
232287 variant = board_data ['variant' ]
233288 subarch = board_data ['subarch' ]
289+
290+ # get board's config settings
291+ report_file = f"firmware/zephyr-{ variant } .config"
292+ try :
293+ with open (report_file , 'r' ) as f :
294+ for line in f :
295+ if line .startswith ('#' ) or '=' not in line :
296+ continue
297+ sym , val = line .split ('=' , 1 )
298+ if val .startswith ('"' ):
299+ BOARD_CONFIGS [board ][sym ] = val .strip ().strip ('"' )
300+ else :
301+ BOARD_CONFIGS [board ][sym ] = int (val )
302+ except Exception as e :
303+ log_test (artifact , board , 'CI test' , [], FAILURE , f"Error reading config file: { e } " )
304+ continue # Skip to the next board
305+
306+ soc = BOARD_CONFIGS [board ]['CONFIG_SOC' ]
307+ board_data ['soc' ] = soc
308+
309+ # get board's memory report
310+ report_file = f"firmware/zephyr-{ variant } .meminfo"
311+ try :
312+ with open (report_file , 'r' ) as f :
313+ report_data = json .load (f )
314+ except Exception as e :
315+ log_test (artifact , board , 'CI test' , [], FAILURE , f"Error reading mem report file: { e } " )
316+ continue # Skip to the next board
317+
318+ BOARD_MEM_REPORTS [board ] = { region .replace (':' ,'' ): [used , total ] for region , used , total in report_data }
319+ REGIONS_FOR_SOC [soc ].update (BOARD_MEM_REPORTS [board ].keys ())
234320
235321 # Get list of expected errors for this board/variant
236322 exceptions = []
@@ -242,16 +328,16 @@ def print_test_matrix(artifact, artifact_boards, title, sketch_filter=lambda x:
242328 exceptions .append (re .compile (f"^(ArduinoCore-zephyr/)?{ sketch_pattern } " ))
243329
244330 # Get raw data from report file
245- REPORT_FILE = f"arduino-{ subarch } -{ board } .json"
246- if not os .path .exists (REPORT_FILE ):
247- log_test (artifact , board , 'CI test' , exceptions , FAILURE , "Report file not found." )
331+ report_file = f"arduino-{ subarch } -{ board } .json"
332+ if not os .path .exists (report_file ):
333+ log_test (artifact , board , 'CI test' , [] , FAILURE , "Report file not found." )
248334 continue # Skip to the next board
249335
250336 try :
251- with open (REPORT_FILE , 'r' ) as f :
337+ with open (report_file , 'r' ) as f :
252338 report_data = json .load (f )
253339 except Exception as e :
254- log_test (artifact , board , 'CI test' , exceptions , FAILURE , f"Error reading report file: { e } " )
340+ log_test (artifact , board , 'CI test' , [] , FAILURE , f"Error reading report file: { e } " )
255341 continue # Skip to the next board
256342
257343 # Extract data from the report file
@@ -260,7 +346,7 @@ def print_test_matrix(artifact, artifact_boards, title, sketch_filter=lambda x:
260346
261347 reports = report_data .get ('boards' , [{}])[0 ].get ('sketches' , [])
262348 if not reports :
263- log_test (artifact , board , 'CI test' , exceptions , FAILURE , "Test report is empty, check CI log." , job_link )
349+ log_test (artifact , board , 'CI test' , [] , FAILURE , "Test report is empty, check CI log." , job_link )
264350 continue # Skip to the next board
265351
266352 # Iterate through individual sketch reports
@@ -283,15 +369,6 @@ def print_test_matrix(artifact, artifact_boards, title, sketch_filter=lambda x:
283369
284370artifacts = ARTIFACT_TESTS .keys ()
285371
286- # Load memory usage reports if they exist
287- ARTIFACT_MEM_REPORTS = {}
288- for artifact in artifacts :
289- try :
290- lines = open (f'mem-report-{ artifact } .md' ).readlines ()
291- ARTIFACT_MEM_REPORTS [artifact ] = lines
292- except :
293- pass
294-
295372# Begin output of the report
296373# --------------------------
297374
@@ -374,11 +451,9 @@ def print_test_matrix(artifact, artifact_boards, title, sketch_filter=lambda x:
374451 print_test_matrix (artifact , artifact_boards , "tests" , sketch_filter = lambda res : res .status in (PASS , WARNING ))
375452 print ("</blockquote></details>\n " )
376453
377- if artifact in ARTIFACT_MEM_REPORTS :
378- print (f"<details><summary>Memory usage report for <code>{ artifact } </code></summary><blockquote>\n " )
379- for line in ARTIFACT_MEM_REPORTS [artifact ]:
380- print (line .strip ())
381- print ("\n </blockquote></details>\n " )
454+ print (f"<details><summary>Memory usage report for <code>{ artifact } </code></summary><blockquote>" )
455+ print_mem_report (artifact , artifact_boards )
456+ print ("</blockquote></details>" )
382457
383458if not ci_run_passed :
384459 sys .exit (1 )
0 commit comments