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() |
Theodore Grey | 9d6d8c7 | 2025-03-24 18:16:57 -0400 | [diff] [blame] | 43 | test_name = None |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame] | 44 | for l in ps.parse_text(string): |
| 45 | if l.category == "test": |
Theodore Grey | 9d6d8c7 | 2025-03-24 18:16:57 -0400 | [diff] [blame] | 46 | test_name = make_name(l.description, l.directive.text, l.ok, l.skip) |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame] | 47 | results.append( |
| 48 | { |
| 49 | "name": make_name(l.description, l.directive.text, l.ok, l.skip), |
| 50 | "result": make_result(l.ok, l.skip), |
| 51 | "children": parse_nested_tap(output), |
Theodore Grey | 9d6d8c7 | 2025-03-24 18:16:57 -0400 | [diff] [blame] | 52 | "logs": f"{l.directive.text}", |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame] | 53 | } |
| 54 | ) |
| 55 | output = "" |
| 56 | elif l.category == "diagnostic": |
| 57 | output += f"{uncomment(l.text)}\n" |
Theodore Grey | 9d6d8c7 | 2025-03-24 18:16:57 -0400 | [diff] [blame] | 58 | for r in results: |
| 59 | if r["name"] == test_name and not None: |
| 60 | r["logs"] += f"{uncomment(l.text)}\n" |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame] | 61 | |
| 62 | return results |
| 63 | |
| 64 | |
| 65 | def flatten_results(prefix, results): |
| 66 | ret = [] |
| 67 | for r in results: |
| 68 | test = f"{prefix}{r['name']}" |
| 69 | children = flatten_results(f"{test}_", r["children"]) |
Theodore Grey | 9d6d8c7 | 2025-03-24 18:16:57 -0400 | [diff] [blame] | 70 | output = r["logs"] |
| 71 | ret += children + [{"name": test, "result": r["result"], "logs": output}] |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame] | 72 | return ret |
| 73 | |
| 74 | |
| 75 | def make_names_unique(results): |
| 76 | namecounts = {} |
| 77 | for r in results: |
| 78 | name = r["name"] |
| 79 | namecounts[name] = namecounts.get(name, 0) + 1 |
| 80 | if namecounts[name] > 1: |
| 81 | r["name"] += f"_dup{namecounts[name]}" |
| 82 | |
| 83 | |
Theodore Grey | 9d6d8c7 | 2025-03-24 18:16:57 -0400 | [diff] [blame] | 84 | def make_log_files(results): |
| 85 | for r in results: |
| 86 | name = r["name"] |
| 87 | if r["result"] == "fail": |
| 88 | try: |
TheodoreGrey | 9c15816 | 2025-04-08 10:30:10 -0400 | [diff] [blame] | 89 | log_file = open(f"output/{name}.log", "w") |
Theodore Grey | 9d6d8c7 | 2025-03-24 18:16:57 -0400 | [diff] [blame] | 90 | log_file.writelines(r["logs"]) |
| 91 | log_file.close() |
| 92 | except OSError as e: |
| 93 | print(f"Error writing to file output/{name}.log: {e}") |
| 94 | |
| 95 | |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame] | 96 | if __name__ == "__main__": |
| 97 | results = parse_nested_tap(sys.stdin.read()) |
| 98 | results = flatten_results("", results) |
| 99 | make_names_unique(results) |
Theodore Grey | 9d6d8c7 | 2025-03-24 18:16:57 -0400 | [diff] [blame] | 100 | make_log_files(results) |
Ryan Roberts | 6e2cfcd | 2024-02-10 11:45:59 +0000 | [diff] [blame] | 101 | for r in results: |
| 102 | print(f"{r['name']} {r['result']}") |