11#!/usr/bin/env python3
2+
3+ """
4+ GNU Test Results Analyzer and Aggregator
5+
6+ This script analyzes and aggregates test results from the GNU test suite.
7+ It parses JSON files containing test results (PASS/FAIL/SKIP/ERROR) and:
8+ 1. Counts the number of tests in each result category
9+ 2. Can aggregate results from multiple JSON files with priority ordering
10+ 3. Outputs shell export statements for use in GitHub Actions workflows
11+
12+ Priority order for aggregation (highest to lowest):
13+ - PASS: Takes precedence over all other results (best outcome)
14+ - FAIL: Takes precedence over ERROR and SKIP
15+ - ERROR: Takes precedence over SKIP
16+ - SKIP: Lowest priority
17+
18+ Usage:
19+ - Single file:
20+ python analyze-gnu-results.py test-results.json
21+
22+ - Multiple files (with aggregation):
23+ python analyze-gnu-results.py file1.json file2.json
24+
25+ - With output file for aggregated results:
26+ python analyze-gnu-results.py -o=output.json file1.json file2.json
27+
28+ Output:
29+ Prints shell export statements for TOTAL, PASS, FAIL, SKIP, XPASS, and ERROR
30+ that can be evaluated in a shell environment.
31+ """
232import json
333import sys
34+ from collections import defaultdict
35+
36+
37+ def get_priority (result ):
38+ """Return a priority value for result status (lower is higher priority)"""
39+ priorities = {
40+ "PASS" : 0 , # PASS is highest priority (best result)
41+ "FAIL" : 1 , # FAIL is second priority
42+ "ERROR" : 2 , # ERROR is third priority
43+ "SKIP" : 3 , # SKIP is lowest priority
44+ }
45+ return priorities .get (result , 4 ) # Unknown states have lowest priority
46+
47+
48+ def aggregate_results (json_files ):
49+ """
50+ Aggregate test results from multiple JSON files.
51+ Prioritizes results in the order: SKIP > ERROR > FAIL > PASS
52+ """
53+ # Combined results dictionary
54+ combined_results = defaultdict (dict )
55+
56+ # Process each JSON file
57+ for json_file in json_files :
58+ try :
59+ with open (json_file , "r" ) as f :
60+ data = json .load (f )
61+
62+ # For each utility and its tests
63+ for utility , tests in data .items ():
64+ for test_name , result in tests .items ():
65+ # If this test hasn't been seen yet, add it
66+ if test_name not in combined_results [utility ]:
67+ combined_results [utility ][test_name ] = result
68+ else :
69+ # If it has been seen, apply priority rules
70+ current_priority = get_priority (
71+ combined_results [utility ][test_name ]
72+ )
73+ new_priority = get_priority (result )
74+
75+ # Lower priority value means higher precedence
76+ if new_priority < current_priority :
77+ combined_results [utility ][test_name ] = result
78+ except FileNotFoundError :
79+ print (f"Warning: File '{ json_file } ' not found." , file = sys .stderr )
80+ continue
81+ except json .JSONDecodeError :
82+ print (f"Warning: '{ json_file } ' is not a valid JSON file." , file = sys .stderr )
83+ continue
84+
85+ return combined_results
486
587
688def analyze_test_results (json_data ):
89+ """
90+ Analyze test results from GNU test suite JSON data.
91+ Counts PASS, FAIL, SKIP results for all tests.
92+ """
793 # Counters for test results
894 total_tests = 0
995 pass_count = 0
1096 fail_count = 0
1197 skip_count = 0
12- error_count = 0 # Although not in the JSON, included for compatibility
98+ xpass_count = 0 # Not in JSON data but included for compatibility
99+ error_count = 0 # Not in JSON data but included for compatibility
13100
14101 # Analyze each utility's tests
15102 for utility , tests in json_data .items ():
@@ -22,47 +109,74 @@ def analyze_test_results(json_data):
22109 fail_count += 1
23110 elif result == "SKIP" :
24111 skip_count += 1
112+ elif result == "ERROR" :
113+ error_count += 1
114+ elif result == "XPASS" :
115+ xpass_count += 1
25116
26117 # Return the statistics
27118 return {
28119 "TOTAL" : total_tests ,
29120 "PASS" : pass_count ,
30121 "FAIL" : fail_count ,
31122 "SKIP" : skip_count ,
123+ "XPASS" : xpass_count ,
32124 "ERROR" : error_count ,
33125 }
34126
35127
36128def main ():
37- # Check if a file argument was provided
38- if len (sys .argv ) != 2 :
39- print ("Usage: python script.py <json_file>" )
129+ """
130+ Main function to process JSON files and export variables.
131+ Supports both single file analysis and multi-file aggregation.
132+ """
133+ # Check if file arguments were provided
134+ if len (sys .argv ) < 2 :
135+ print ("Usage: python analyze-gnu-results.py <json> [json ...]" )
136+ print (" For multiple files, results will be aggregated" )
137+ print (" Priority SKIP > ERROR > FAIL > PASS" )
40138 sys .exit (1 )
41139
42- json_file = sys .argv [1 ]
140+ json_files = sys .argv [1 :]
141+ output_file = None
43142
44- try :
45- # Parse the JSON data from the specified file
46- with open ( json_file , "r" ) as file :
47- json_data = json . load ( file )
143+ # Check if the first argument is an output file (starts with -)
144+ if json_files [ 0 ]. startswith ( "-o=" ):
145+ output_file = json_files [ 0 ][ 3 :]
146+ json_files = json_files [ 1 :]
48147
49- # Analyze the results
148+ # Process the files
149+ if len (json_files ) == 1 :
150+ # Single file analysis
151+ try :
152+ with open (json_files [0 ], "r" ) as file :
153+ json_data = json .load (file )
154+ results = analyze_test_results (json_data )
155+ except FileNotFoundError :
156+ print (f"Error: File '{ json_files [0 ]} ' not found." , file = sys .stderr )
157+ sys .exit (1 )
158+ except json .JSONDecodeError :
159+ print (
160+ f"Error: '{ json_files [0 ]} ' is not a valid JSON file." , file = sys .stderr
161+ )
162+ sys .exit (1 )
163+ else :
164+ # Multiple files - aggregate them
165+ json_data = aggregate_results (json_files )
50166 results = analyze_test_results (json_data )
51167
52- # Export the results as environment variables
53- # For use in shell, print export statements
54- print (f"export TOTAL={ results ['TOTAL' ]} " )
55- print (f"export PASS={ results ['PASS' ]} " )
56- print (f"export SKIP={ results ['SKIP' ]} " )
57- print (f"export FAIL={ results ['FAIL' ]} " )
58- print (f"export ERROR={ results ['ERROR' ]} " )
168+ # Save aggregated data if output file is specified
169+ if output_file :
170+ with open (output_file , "w" ) as f :
171+ json .dump (json_data , f , indent = 2 )
59172
60- except FileNotFoundError :
61- print (f"Error: File '{ json_file } ' not found." , file = sys .stderr )
62- sys .exit (1 )
63- except json .JSONDecodeError :
64- print (f"Error: '{ json_file } ' is not a valid JSON" , file = sys .stderr )
65- sys .exit (1 )
173+ # Print export statements for shell evaluation
174+ print (f"export TOTAL={ results ['TOTAL' ]} " )
175+ print (f"export PASS={ results ['PASS' ]} " )
176+ print (f"export SKIP={ results ['SKIP' ]} " )
177+ print (f"export FAIL={ results ['FAIL' ]} " )
178+ print (f"export XPASS={ results ['XPASS' ]} " )
179+ print (f"export ERROR={ results ['ERROR' ]} " )
66180
67181
68182if __name__ == "__main__" :
0 commit comments