junit_utils.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import os
  2. import glob
  3. import sys
  4. from xml.etree import ElementTree as ET
  5. def get_or_create_properties(testcase):
  6. props = testcase.find("properties")
  7. if props is None:
  8. props = ET.Element("properties")
  9. testcase.append(props)
  10. return props
  11. def add_junit_link_property(testcase, name, url):
  12. add_junit_property(testcase, f"url:{name}", url)
  13. def add_junit_property(testcase, name, value):
  14. props = get_or_create_properties(testcase)
  15. # remove existing property if exists
  16. for item in props.findall("property"):
  17. if item.get('name') == name:
  18. props.remove(item)
  19. break
  20. props.append(ET.Element("property", dict(name=name, value=value)))
  21. def add_junit_log_property(testcase, url):
  22. add_junit_link_property(testcase, "Log", url)
  23. def get_property_value(testcase, name):
  24. props = testcase.find("properties")
  25. if props is None:
  26. return None
  27. for prop in props.findall("property"):
  28. if prop.attrib["name"] == name:
  29. return prop.attrib["value"]
  30. def create_error_testsuite(testcases):
  31. n = str(len(testcases))
  32. suite = ET.Element("testsuite", dict(tests=n, errors=n))
  33. suite.extend(testcases)
  34. root = ET.Element("testsuites", dict(tests=n, errors=n))
  35. root.append(suite)
  36. return ET.ElementTree(root)
  37. def create_error_testcase(shardname, classname, name, log_fn=None, log_url=None):
  38. testcase = ET.Element("testcase", dict(classname=classname, name=name))
  39. add_junit_property(testcase, "shard", shardname)
  40. if log_url:
  41. add_junit_log_property(testcase, log_url)
  42. err = ET.Element("error", dict(type="error"))
  43. if log_fn:
  44. with open(log_fn, "rt") as fp:
  45. err.text = fp.read(4096)
  46. testcase.append(err)
  47. return testcase
  48. def suite_case_iterator(root):
  49. for suite in root.findall("testsuite"):
  50. for case in suite.findall("testcase"):
  51. cls, method = case.attrib["classname"], case.attrib["name"]
  52. yield suite, case, cls, method
  53. def iter_xml_files(folder_or_file):
  54. if os.path.isfile(folder_or_file):
  55. files = [folder_or_file]
  56. else:
  57. files = glob.glob(os.path.join(folder_or_file, "*.xml"))
  58. for fn in files:
  59. try:
  60. tree = ET.parse(fn)
  61. except ET.ParseError as e:
  62. print(f"Unable to parse {fn}: {e}", file=sys.stderr)
  63. continue
  64. root = tree.getroot()
  65. if root.tag == "testsuite":
  66. suites = [root]
  67. elif root.tag == "testsuites":
  68. suites = root.findall("testsuite")
  69. else:
  70. raise ValueError(f"Invalid root tag {root.tag}")
  71. for suite in suites:
  72. for case in suite.findall("testcase"):
  73. yield fn, suite, case
  74. def is_faulty_testcase(testcase):
  75. return testcase.find("failure") is not None or testcase.find("error") is not None