Files
ElyOffice/scripts/test-map.py
T

96 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Writes docs/test-map.md: each PRD id, its tasks, their tests."""
import os
import re
import subprocess
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
LATER = ("MAIL", "AGENT")
# Where each area's tests live, named or not.
WHERE = {
"UX": "crates/ely-ui/tests、shell.sh、doc-shell.sh、settings.sh",
"FILE": "crates/ely-files、crates/ely-office、recovery.sh、autosave.sh",
"LOK": "crates/ely-lok/src/client/tests、crates/ely-lok/src/view/tests、lokview.sh",
"DOC": "apps/ely-doc/src/tests、doc-props.sh",
"TABLE": "apps/ely-table/src/tests、theme.sh",
"SHOW": "apps/ely-show/src/tests",
"PDF": "apps/ely-pdf/src/tests、crates/ely-pdf-engine/tests、pdf-recovery.sh",
"TREE": "apps/ely-tree/src/tests",
"MARK": "apps/ely-mark 单测、mark-*.sh",
"PERF": "tests/perf/bench.py、lag.sh",
"QA": "见 docs/qa.md",
}
def read(name):
with open(os.path.join(ROOT, name)) as text:
return text.read()
def ids():
"""PRD table rows: id and its requirement."""
for line in read("PRD.md").splitlines():
if m := re.match(r"\| ([A-Z]+-\d+) \| (.+?) \|", line):
yield m.group(1), m.group(2).split("|")[0].strip()
def cites(line):
"""PRD ids a line names: single, lists, ranges."""
cited = set()
for prefix, rest in re.findall(r"([A-Z]+)-(\d+(?:(?: ?至 ?|、)(?:[A-Z]+-)?\d+)*)", line):
numbers = re.split(r"(?: ?至 ?|、)(?:[A-Z]+-)?", rest)
ranges = re.findall(r"(?: ?至 ?|、)", rest)
n = [int(x) for x in numbers]
cited.add(n[0])
for kind, (a, b) in zip(ranges, zip(n, n[1:])):
cited.update(range(a, b + 1) if "至" in kind else [b])
yield from (f"{prefix}-{k:02d}" for k in sorted(cited))
cited = set()
def tasks():
"""Task lines and decisions: a label, the PRD ids, the line."""
for line in read("tasks/TASKS.md").splitlines():
if m := re.match(r"- \[(.)\] (C[\d.]+[a-z]?) ", line):
yield m.group(2) + ("" if m.group(1) == "x" else f"({m.group(1)})"), set(cites(line)), line
for line in read("agents.md").splitlines():
if m := re.match(r"- (\d{4}-\d\d-\d\d)[^:]*:.*?((C[\d.]+)", line):
yield f"决策 {m.group(2)}", set(cites(line)), line
def known():
"""Test function and script names present in the tree."""
found = subprocess.run(["grep", "-rhoE", r"fn [a-z0-9_]+\(", "crates", "apps", "--include=*.rs"],
cwd=ROOT, capture_output=True, text=True).stdout
names = {name[3:-1] for name in found.split()}
names |= {f for f in os.listdir(os.path.join(ROOT, "tests", "scripts"))}
return names
def main():
present = known()
rows = ["# 功能编号与测试对照", "", "对应 PRD §22。由 `scripts/test-map.py` 从 PRD、`tasks/TASKS.md` 与 `agents.md` 生成:每个编号列出引用它的任务与决策,其中写明、且在代码里确有其名的测试(单测函数名或 `tests/scripts` 的脚本),以及该领域测试所在之处。邮件与 Agent 属第二批,不列。", "",
"| 编号 | 要求 | 任务 | 记下的测试 | 测试所在 |", "| --- | --- | --- | --- | --- |"]
listed = list(tasks())
for id_, need in ids():
if id_.startswith(LATER):
continue
mine = [(label, line) for label, cited, line in listed if id_ in cited]
named = []
for _, line in mine:
for name in re.findall(r"`([a-z0-9_.-]+)`|([a-z0-9-]+\.sh)", line):
name = name[0] or name[1]
if name in present and name not in named:
named.append(name)
labels = list(dict.fromkeys(label for label, _ in mine))
where = WHERE[id_.split("-")[0]]
rows.append(f"| {id_} | {need} | {'、'.join(labels) or '无'} | {'、'.join(f'`{n}`' for n in named) or '—'} | {where} |")
with open(os.path.join(ROOT, "docs", "test-map.md"), "w") as out:
out.write("\n".join(rows) + "\n")
print(f"docs/test-map.md: {len(rows) - 6} ids")
if __name__ == "__main__":
main()