import glob import json from collections import defaultdict from datetime import datetime statuses = ['passed', 'failed', 'broken', 'skipped', 'unknown'] # Structure : results[file][test_func]['status_counts'] results = defaultdict(lambda: defaultdict(lambda: { 'status_counts': defaultdict(int) })) totals = defaultdict(int) errors_by_file = defaultdict(list) # file -> list of (func, status, msg) for f in glob.glob('allure-results/*-result.json'): with open(f) as j: data = json.load(j) status = data.get('status', 'unknown').lower() full_name = data.get('fullName', 'unknown') if '#' in full_name: file_part, func_part = full_name.split('#', 1) else: file_part, func_part = full_name, 'unknown' file_name = file_part.replace('.', '/').strip() + '.py' test_name = func_part.strip() # Update count results[file_name][test_name]['status_counts'][status] += 1 totals[status] += 1 # Collect individual error messages for failed/broken if status in ['failed', 'broken']: msg = data.get('statusDetails', {}).get('message', '').strip() if msg: errors_by_file[file_name].append((test_name, status, msg)) # Write markdown with open('ci-reports/markdown/test-summary.md', 'w') as out: out.write('# Test Summary\n\n') for file in sorted(results): file_totals = defaultdict(int) out.write(f'## {file}\n\n') out.write('| Test Function | Passed | Failed | Broken | Skipped | Unknown |\n') out.write('|---------------|--------|--------|--------|---------|---------|\n') for test_func in sorted(results[file]): row = [test_func] counts = results[file][test_func]['status_counts'] for s in statuses: count = counts.get(s, 0) file_totals[s] += count row.append(str(count)) out.write('| ' + ' | '.join(row) + ' |\n') # Totals per file total_row = ['**Total**'] + [str(file_totals[s]) for s in statuses] out.write('| ' + ' | '.join(total_row) + ' |\n\n') # Failure details if errors_by_file[file]: out.write('### Failure Details\n\n') grouped = defaultdict(list) for func_name, status, msg in errors_by_file[file]: grouped[func_name].append((status, msg)) for func_name in sorted(grouped): out.write(f'#### {func_name}\n') for i, (status, msg) in enumerate(grouped[func_name], 1): safe_msg = msg.replace('\n', ' ').replace('|', '¦') out.write(f'- **{status.capitalize()} #{i}**: `{safe_msg}`\n') out.write('\n') # Global totals out.write('## Total Summary\n\n') out.write('| Status | Count |\n') out.write('|---------|-------|\n') for s in statuses: out.write(f'| {s.capitalize():<7} | {totals[s]} |\n') # Optional timestamp out.write(f'\n_Last updated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}_\n')