101 lines
3.7 KiB
Python
101 lines
3.7 KiB
Python
import json
|
|
import os
|
|
from pathlib import Path
|
|
from collections import defaultdict
|
|
import matplotlib.pyplot as plt
|
|
|
|
RESULTS_DIR = Path("allure-results")
|
|
REPORT_DIR = Path("ci-reports")
|
|
REPORT_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
def parse_allure_results():
|
|
results = []
|
|
for file in RESULTS_DIR.glob("*.json"):
|
|
if file.name.startswith("result"):
|
|
with open(file) as f:
|
|
results.append(json.load(f))
|
|
return results
|
|
|
|
def summarize_results(results):
|
|
summary = {"passed": 0, "failed": 0, "skipped": 0, "total": 0}
|
|
durations = []
|
|
grouped = defaultdict(list)
|
|
|
|
for res in results:
|
|
status = res.get("status", "unknown")
|
|
summary["total"] += 1
|
|
summary[status] += 1
|
|
time = res.get("stop", 0) - res.get("start", 0)
|
|
durations.append((res["name"], time / 1000))
|
|
grouped[res["fullName"]].append({
|
|
"name": res["name"],
|
|
"status": status,
|
|
"time": time / 1000,
|
|
"parameters": res.get("parameters", []),
|
|
})
|
|
return summary, durations, grouped
|
|
|
|
def generate_donut_chart(summary):
|
|
labels = ["Passed", "Failed", "Skipped"]
|
|
sizes = [summary["passed"], summary["failed"], summary["skipped"]]
|
|
colors = ["#8BC34A", "#F44336", "#FFC107"]
|
|
total = sum(sizes)
|
|
|
|
fig, ax = plt.subplots()
|
|
ax.pie(sizes, labels=None, colors=colors, startangle=90,
|
|
wedgeprops=dict(width=0.3))
|
|
ax.text(0, 0, f'{summary["passed"]/total*100:.1f}%', ha='center', va='center', fontsize=20)
|
|
plt.savefig(REPORT_DIR / "summary.png", bbox_inches='tight')
|
|
plt.close()
|
|
|
|
def generate_timeline_chart(durations):
|
|
names, times = zip(*sorted(durations, key=lambda x: x[1], reverse=True))
|
|
plt.figure(figsize=(10, max(4, len(times) * 0.3)))
|
|
plt.barh(names, times, color="#2196F3")
|
|
plt.xlabel("Duration (s)")
|
|
plt.title("Test Execution Timeline")
|
|
plt.tight_layout()
|
|
plt.savefig(REPORT_DIR / "timeline.png")
|
|
plt.close()
|
|
|
|
def format_parameters(params):
|
|
return ", ".join(f"{p['name']}={p['value']}" for p in params)
|
|
|
|
def write_report(summary, grouped):
|
|
with open(REPORT_DIR / "report.md", "w") as f:
|
|
f.write(f"# 🧪 Test Summary\n\n")
|
|
f.write(f"\n\n")
|
|
f.write(f"\n\n")
|
|
f.write(f"**Total:** {summary['total']} | ✅ Passed: {summary['passed']} | ❌ Failed: {summary['failed']} | ⚠️ Skipped: {summary['skipped']}\n\n")
|
|
f.write("<details>\n<summary><strong>📋 Test Details</strong></summary>\n\n")
|
|
for suite, tests in grouped.items():
|
|
f.write(f"<details>\n<summary><code>{suite}</code></summary>\n\n")
|
|
for test in tests:
|
|
emoji = "✅" if test["status"] == "passed" else "❌" if test["status"] == "failed" else "⚠️"
|
|
params = format_parameters(test["parameters"])
|
|
f.write(f"- {emoji} `{test['name']}` ({test['time']}s)")
|
|
if params:
|
|
f.write(f" \n ↪️ *Params:* {params}")
|
|
f.write("\n")
|
|
f.write("</details>\n\n")
|
|
f.write("</details>\n")
|
|
|
|
def write_failures(grouped):
|
|
with open(REPORT_DIR / "failures.md", "w") as f:
|
|
f.write("# ❌ Failed Tests\n\n")
|
|
for suite, tests in grouped.items():
|
|
for test in tests:
|
|
if test["status"] == "failed":
|
|
f.write(f"- `{suite}::{test['name']}`\n")
|
|
|
|
def main():
|
|
results = parse_allure_results()
|
|
summary, durations, grouped = summarize_results(results)
|
|
generate_donut_chart(summary)
|
|
generate_timeline_chart(durations)
|
|
write_report(summary, grouped)
|
|
write_failures(grouped)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|