Anders Roxell | 65be768 | 2022-12-08 12:02:43 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | import sys |
| 3 | import re |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame^] | 4 | from tap import parser |
Anders Roxell | 65be768 | 2022-12-08 12:02:43 +0100 | [diff] [blame] | 5 | |
| 6 | |
| 7 | def slugify(line): |
| 8 | non_ascii_pattern = r"[^A-Za-z0-9_-]+" |
| 9 | line = re.sub(r"\[\d{1,5}\]", "", line) |
| 10 | return re.sub( |
| 11 | r"_-", "_", re.sub(r"(^_|_$)", "", re.sub(non_ascii_pattern, "_", line)) |
| 12 | ) |
| 13 | |
| 14 | |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame^] | 15 | def parse_nested_tap(string): |
| 16 | results = [] |
| 17 | |
| 18 | def uncomment(line): |
| 19 | # All of the input lines should be comments and begin with #, but let's |
| 20 | # be cautious; don't do anything if the line doesn't begin with #. |
| 21 | if len(line) > 0 and line[0] == "#": |
| 22 | return line[1:].strip() |
| 23 | return line |
| 24 | |
| 25 | def make_name(name, directive, ok, skip): |
| 26 | # Some of this is to maintain compatibility with the old parser. |
| 27 | if name.startswith("selftests:"): |
| 28 | name = name[10:] |
| 29 | if ok and skip and directive.lower().startswith("skip"): |
| 30 | directive = directive[4:] |
Anders Roxell | 65be768 | 2022-12-08 12:02:43 +0100 | [diff] [blame] | 31 | else: |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame^] | 32 | directive = "" |
| 33 | name = f"{name} {directive}".strip() |
| 34 | if name == "": |
| 35 | name = "<unknown>" |
| 36 | return slugify(name) |
| 37 | |
| 38 | def make_result(ok, skip): |
| 39 | return ("skip" if skip else "pass") if ok else "fail" |
| 40 | |
| 41 | output = "" |
| 42 | ps = parser.Parser() |
| 43 | for l in ps.parse_text(string): |
| 44 | if l.category == "test": |
| 45 | results.append( |
| 46 | { |
| 47 | "name": make_name(l.description, l.directive.text, l.ok, l.skip), |
| 48 | "result": make_result(l.ok, l.skip), |
| 49 | "children": parse_nested_tap(output), |
| 50 | } |
| 51 | ) |
| 52 | output = "" |
| 53 | elif l.category == "diagnostic": |
| 54 | output += f"{uncomment(l.text)}\n" |
| 55 | |
| 56 | return results |
| 57 | |
| 58 | |
| 59 | def flatten_results(prefix, results): |
| 60 | ret = [] |
| 61 | for r in results: |
| 62 | test = f"{prefix}{r['name']}" |
| 63 | children = flatten_results(f"{test}_", r["children"]) |
| 64 | ret += children + [{"name": test, "result": r["result"]}] |
| 65 | return ret |
| 66 | |
| 67 | |
| 68 | def make_names_unique(results): |
| 69 | namecounts = {} |
| 70 | for r in results: |
| 71 | name = r["name"] |
| 72 | namecounts[name] = namecounts.get(name, 0) + 1 |
| 73 | if namecounts[name] > 1: |
| 74 | r["name"] += f"_dup{namecounts[name]}" |
| 75 | |
| 76 | |
| 77 | if __name__ == "__main__": |
| 78 | results = parse_nested_tap(sys.stdin.read()) |
| 79 | results = flatten_results("", results) |
| 80 | make_names_unique(results) |
| 81 | for r in results: |
| 82 | print(f"{r['name']} {r['result']}") |