Skip to content

Commit 9e2edc0

Browse files
authored
ci: add conformance report summary (#2)
* ci: add conformance report * report: clarify ported vs translated counts
1 parent d580cd1 commit 9e2edc0

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

bin/ci.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ if [[ "${PYGRAPHISTRY_INSTALL:-0}" == "1" ]]; then
2626
fi
2727

2828
pytest tests/cypher_tck -xvs
29+
python -m tests.cypher_tck.report

tests/cypher_tck/report.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from __future__ import annotations
2+
3+
import os
4+
from collections import Counter, defaultdict
5+
from typing import Dict, List, Optional, Tuple
6+
7+
from tests.cypher_tck.scenarios import SCENARIOS
8+
9+
10+
def _feature_parts(feature_path: str) -> Tuple[str, str]:
11+
parts = feature_path.split("/")
12+
if "features" in parts:
13+
idx = parts.index("features")
14+
group = parts[idx + 1] if idx + 1 < len(parts) else "unknown"
15+
area = parts[idx + 2] if idx + 2 < len(parts) else "unknown"
16+
return group, f"{group}/{area}"
17+
return "unknown", "unknown"
18+
19+
20+
def _percent(value: int, total: int) -> str:
21+
if total == 0:
22+
return "n/a"
23+
return f"{(value / total) * 100:.1f}%"
24+
25+
26+
def _table_rows(
27+
counts: Dict[str, Counter], top_n: Optional[int] = None
28+
) -> List[str]:
29+
items = sorted(
30+
counts.items(),
31+
key=lambda item: item[1].get("total", 0),
32+
reverse=True,
33+
)
34+
if top_n is not None:
35+
items = items[:top_n]
36+
rows = []
37+
for name, counter in items:
38+
rows.append(
39+
f"| {name} | {counter.get('total', 0)} | "
40+
f"{counter.get('supported', 0)} | {counter.get('xfail', 0)} | "
41+
f"{counter.get('skip', 0)} |"
42+
)
43+
return rows
44+
45+
46+
def build_report() -> str:
47+
total = len(SCENARIOS)
48+
status_counts = Counter(scenario.status for scenario in SCENARIOS)
49+
gfql_defined = sum(1 for scenario in SCENARIOS if scenario.gfql is not None)
50+
missing_gfql = total - gfql_defined
51+
supported_defined = sum(
52+
1
53+
for scenario in SCENARIOS
54+
if scenario.status == "supported" and scenario.gfql is not None
55+
)
56+
translated_xfail = sum(
57+
1
58+
for scenario in SCENARIOS
59+
if scenario.status == "xfail" and scenario.gfql is not None
60+
)
61+
translated_skip = sum(
62+
1
63+
for scenario in SCENARIOS
64+
if scenario.status == "skip" and scenario.gfql is not None
65+
)
66+
supported_missing = sum(
67+
1
68+
for scenario in SCENARIOS
69+
if scenario.status == "supported" and scenario.gfql is None
70+
)
71+
72+
supported_count = status_counts.get("supported", 0)
73+
xfail_count = status_counts.get("xfail", 0)
74+
skip_count = status_counts.get("skip", 0)
75+
other_count = total - supported_count - xfail_count - skip_count
76+
77+
group_counts: Dict[str, Counter] = defaultdict(Counter)
78+
area_counts: Dict[str, Counter] = defaultdict(Counter)
79+
xfail_tags = Counter()
80+
81+
for scenario in SCENARIOS:
82+
group, area = _feature_parts(scenario.feature_path)
83+
for bucket in (group_counts[group], area_counts[area]):
84+
bucket["total"] += 1
85+
bucket[scenario.status] += 1
86+
if scenario.status == "xfail":
87+
xfail_tags.update(scenario.tags)
88+
89+
lines = [
90+
"GFQL conformance report (tck-gfql)",
91+
"",
92+
f"Scenarios represented (ported): {total}",
93+
f"GFQL translated (non-None): {gfql_defined} ({_percent(gfql_defined, total)})",
94+
f"GFQL missing: {missing_gfql} ({_percent(missing_gfql, total)})",
95+
f"Translated + expected pass (supported): {supported_defined}",
96+
f"Translated but xfail: {translated_xfail}",
97+
f"Translated but skip: {translated_skip}",
98+
f"Supported but missing GFQL: {supported_missing}",
99+
f"Status counts: supported {supported_count}, "
100+
f"xfail {xfail_count}, "
101+
f"skip {skip_count}, "
102+
f"other {other_count}",
103+
"",
104+
"By feature group:",
105+
"| group | total | supported | xfail | skip |",
106+
"|---|---:|---:|---:|---:|",
107+
]
108+
109+
lines.extend(_table_rows(group_counts))
110+
111+
lines.extend(
112+
[
113+
"",
114+
"Top feature areas (by scenario count):",
115+
"| feature | total | supported | xfail | skip |",
116+
"|---|---:|---:|---:|---:|",
117+
]
118+
)
119+
lines.extend(_table_rows(area_counts, top_n=10))
120+
121+
lines.append("")
122+
lines.append("Top xfail tags:")
123+
if xfail_tags:
124+
for tag, count in xfail_tags.most_common(10):
125+
lines.append(f"- {tag}: {count}")
126+
else:
127+
lines.append("- none")
128+
129+
return "\n".join(lines) + "\n"
130+
131+
132+
def main() -> None:
133+
report = build_report()
134+
print(report)
135+
summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
136+
if summary_path:
137+
with open(summary_path, "a", encoding="utf-8") as summary:
138+
summary.write(report)
139+
140+
141+
if __name__ == "__main__":
142+
main()

0 commit comments

Comments
 (0)