|
1 | 1 | import xml.etree.ElementTree as ET
|
2 | 2 | import sys
|
3 | 3 | import re
|
| 4 | +from collections import defaultdict |
4 | 5 |
|
5 | 6 | def create_testcase_element(testclass, testname, stdout, identifier, got, expected, status, url):
|
| 7 | + # Create the testcase XML element with the class and test name attributes |
6 | 8 | testcase = ET.Element("testcase", classname=testclass, name=testname)
|
7 |
| - |
8 |
| - description = f"Test {identifier} with URL: {url}" |
9 |
| - |
| 9 | + |
10 | 10 | if status == "PASS":
|
| 11 | + # If the test passed, add system-out with a clickable link and details |
11 | 12 | system_out = ET.SubElement(testcase, "system-out")
|
12 | 13 | system_out.text = f"<![CDATA[\n<a href=\"{url}\">Test Report</a>\n\nAssertion: {stdout}\nExpected: {expected}\nActual: {got}\n]]>"
|
13 | 14 | else: # status == "FAIL"
|
| 15 | + # If the test failed, add a failure element with details and a clickable link |
14 | 16 | failure_message = f"Test failed: Expected '{expected}' but got '{got}'"
|
15 | 17 | failure = ET.SubElement(testcase, "failure", message=failure_message, type="AssertionError")
|
16 | 18 | failure.text = f"<![CDATA[\nAssertionError: {failure_message}\n]]>"
|
17 | 19 | system_out = ET.SubElement(testcase, "system-out")
|
18 | 20 | system_out.text = f"<![CDATA[\n<a href=\"{url}\">Test Report</a>\n\nAssertion: {stdout}\nExpected: {expected}\nActual: {got}\n]]>"
|
19 |
| - |
| 21 | + |
20 | 22 | return testcase
|
21 | 23 |
|
22 | 24 | def parse_test_line(line):
|
| 25 | + # Split the line into parts based on the table format |
23 | 26 | parts = re.split(r'\s*\|\s*(?![^()]*\))', line.strip())
|
24 | 27 | if len(parts) < 7:
|
25 | 28 | raise ValueError(f"Line does not have the expected number of parts: {len(parts)} found")
|
26 | 29 |
|
27 |
| - full_identifier = parts[1].strip() # The second field contains the test class and name |
| 30 | + full_identifier = parts[1].strip() # The second field contains the test package/class and name |
28 | 31 | status = parts[2].strip() # The third field contains the pass/fail status
|
29 | 32 | url = re.search(r'\((.*?)\)', parts[3]).group(1).strip() # Extract the URL inside the parentheses
|
30 | 33 | stdout = parts[4].strip() # The fifth field contains the assertion
|
31 |
| - got = parts[5].strip() |
32 |
| - expected = parts[6].strip() |
| 34 | + got = parts[5].strip() # The sixth field contains the actual result |
| 35 | + expected = parts[6].strip() # The seventh field contains the expected result |
33 | 36 |
|
34 | 37 | try:
|
35 |
| - testclass, testname = full_identifier.rsplit('.', 1) |
36 |
| - if '.' in testclass: |
37 |
| - testname = f"{testclass.split('.')[-1]}.{testname}" # Combine to form the correct testname |
38 |
| - if not testclass or not testname: |
39 |
| - raise ValueError("Test class or test name is empty after splitting.") |
| 38 | + # Split the identifier into the package, class, and test names |
| 39 | + testpackage, testname = full_identifier.split('.', 1) |
| 40 | + if not testpackage or not testname: |
| 41 | + raise ValueError("Test package or test name is empty after splitting.") |
40 | 42 | except ValueError as e:
|
41 | 43 | raise ValueError(f"Identifier does not contain the expected format: {full_identifier}. Error: {str(e)}")
|
42 | 44 |
|
43 |
| - return testclass, testname, stdout, full_identifier, got, expected, status, url |
| 45 | + return testpackage, testname, stdout, full_identifier, got, expected, status, url |
44 | 46 |
|
45 | 47 | def generate_junit_xml(input_file):
|
46 | 48 | testsuites = ET.Element("testsuites")
|
47 |
| - testsuite = ET.Element("testsuite", name="Metta Tests") |
48 |
| - testsuites.append(testsuite) |
| 49 | + packages_dict = defaultdict(list) # Dictionary to group test cases by their testpackage |
49 | 50 |
|
50 | 51 | with open(input_file, 'r') as file:
|
51 | 52 | for line in file:
|
52 | 53 | parts = None
|
53 | 54 | if line.startswith("|"):
|
54 | 55 | try:
|
55 | 56 | parts = re.split(r'\s*\|\s*(?![^()]*\))', line.strip())
|
56 |
| - testclass, testname, stdout, full_identifier, got, expected, status, url = parse_test_line(line) |
57 |
| - testcase = create_testcase_element(testclass, testname, stdout, full_identifier, got, expected, status, url) |
58 |
| - testsuite.append(testcase) |
59 |
| - print(f"Processing {testclass}.{testname}: {status}", file=sys.stderr) |
| 57 | + testpackage, testname, stdout, full_identifier, got, expected, status, url = parse_test_line(line) |
| 58 | + testcase = create_testcase_element(testpackage, testname, stdout, full_identifier, got, expected, status, url) |
| 59 | + packages_dict[testpackage].append(testcase) |
| 60 | + print(f"Processing {testpackage}.{testname}: {status}", file=sys.stderr) |
60 | 61 | except ValueError as e:
|
61 | 62 | print(f"Skipping line due to error: {e}\nLine: {line}\nParts: {parts}", file=sys.stderr)
|
62 | 63 |
|
| 64 | + # Create a testsuite for each testpackage group |
| 65 | + for testpackage, testcases in packages_dict.items(): |
| 66 | + testsuite = ET.Element("testsuite", name=testpackage) |
| 67 | + for testcase in testcases: |
| 68 | + testsuite.append(testcase) |
| 69 | + testsuites.append(testsuite) |
| 70 | + |
| 71 | + # Generate the XML tree and return it as a string |
63 | 72 | tree = ET.ElementTree(testsuites)
|
64 | 73 | return ET.tostring(testsuites, encoding="utf-8", xml_declaration=True).decode("utf-8")
|
65 | 74 |
|
66 | 75 | if __name__ == "__main__":
|
67 | 76 | if len(sys.argv) != 2:
|
68 |
| - print("Usage: python generate_junit.py <input_file>") |
| 77 | + print("Usage: python scripts/into_junit.py <input_file>") |
69 | 78 | sys.exit(1)
|
70 | 79 |
|
71 | 80 | input_file = sys.argv[1]
|
|
0 commit comments