ESP32-basiertes Kraftort-Suchger�t mit GPS, LED-Ring und PlatformIO-Firmware.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
6.6KB

  1. #! python3
  2. # =========================================================================
  3. # Unity - A Test Framework for C
  4. # ThrowTheSwitch.org
  5. # Copyright (c) 2007-25 Mike Karlesky, Mark VanderVoord, & Greg Williams
  6. # SPDX-License-Identifier: MIT
  7. # =========================================================================
  8. import sys
  9. import os
  10. from glob import glob
  11. import argparse
  12. from pyparsing import *
  13. from junit_xml import TestSuite, TestCase
  14. class UnityTestSummary:
  15. def __init__(self):
  16. self.report = ''
  17. self.total_tests = 0
  18. self.failures = 0
  19. self.ignored = 0
  20. self.targets = 0
  21. self.root = None
  22. self.output = None
  23. self.test_suites = dict()
  24. def run(self):
  25. # Clean up result file names
  26. results = []
  27. for target in self.targets:
  28. results.append(target.replace('\\', '/'))
  29. # Dig through each result file, looking for details on pass/fail:
  30. for result_file in results:
  31. lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n')))
  32. if len(lines) == 0:
  33. raise Exception("Empty test result file: %s" % result_file)
  34. # define an expression for your file reference
  35. entry_one = Combine(
  36. oneOf(list(alphas)) + ':/' +
  37. Word(alphanums + '_-./'))
  38. entry_two = Word(printables + ' ', excludeChars=':')
  39. entry = entry_one | entry_two
  40. delimiter = Literal(':').suppress()
  41. # Format of a result line is `[file_name]:line:test_name:RESULT[:msg]`
  42. tc_result_line = Group(ZeroOrMore(entry.setResultsName('tc_file_name'))
  43. + delimiter + entry.setResultsName('tc_line_nr')
  44. + delimiter + entry.setResultsName('tc_name')
  45. + delimiter + entry.setResultsName('tc_status') +
  46. Optional(delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line")
  47. eol = LineEnd().suppress()
  48. sol = LineStart().suppress()
  49. blank_line = sol + eol
  50. # Format of the summary line is `# Tests # Failures # Ignored`
  51. tc_summary_line = Group(Word(nums).setResultsName("num_of_tests") + "Tests" + Word(nums).setResultsName(
  52. "num_of_fail") + "Failures" + Word(nums).setResultsName("num_of_ignore") + "Ignored").setResultsName(
  53. "tc_summary")
  54. tc_end_line = Or(Literal("FAIL"), Literal('Ok')).setResultsName("tc_result")
  55. # run it and see...
  56. pp1 = tc_result_line | Optional(tc_summary_line | tc_end_line)
  57. pp1.ignore(blank_line | OneOrMore("-"))
  58. result = list()
  59. for l in lines:
  60. result.append((pp1.parseString(l)).asDict())
  61. # delete empty results
  62. result = filter(None, result)
  63. tc_list = list()
  64. for r in result:
  65. if 'tc_line' in r:
  66. tmp_tc_line = r['tc_line']
  67. # get only the file name which will be used as the classname
  68. if 'tc_file_name' in tmp_tc_line:
  69. file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0]
  70. else:
  71. file_name = result_file.strip("./")
  72. tmp_tc = TestCase(name=tmp_tc_line['tc_name'], classname=file_name)
  73. if 'tc_status' in tmp_tc_line:
  74. if str(tmp_tc_line['tc_status']) == 'IGNORE':
  75. if 'tc_msg' in tmp_tc_line:
  76. tmp_tc.add_skipped_info(message=tmp_tc_line['tc_msg'],
  77. output=r'[File]={0}, [Line]={1}'.format(
  78. tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr']))
  79. else:
  80. tmp_tc.add_skipped_info(message=" ")
  81. elif str(tmp_tc_line['tc_status']) == 'FAIL':
  82. if 'tc_msg' in tmp_tc_line:
  83. tmp_tc.add_failure_info(message=tmp_tc_line['tc_msg'],
  84. output=r'[File]={0}, [Line]={1}'.format(
  85. tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr']))
  86. else:
  87. tmp_tc.add_failure_info(message=" ")
  88. tc_list.append((str(result_file), tmp_tc))
  89. for k, v in tc_list:
  90. try:
  91. self.test_suites[k].append(v)
  92. except KeyError:
  93. self.test_suites[k] = [v]
  94. ts = []
  95. for suite_name in self.test_suites:
  96. ts.append(TestSuite(suite_name, self.test_suites[suite_name]))
  97. with open(self.output, 'w') as f:
  98. TestSuite.to_file(f, ts, prettyprint='True', encoding='utf-8')
  99. return self.report
  100. def set_targets(self, target_array):
  101. self.targets = target_array
  102. def set_root_path(self, path):
  103. self.root = path
  104. def set_output(self, output):
  105. self.output = output
  106. if __name__ == '__main__':
  107. uts = UnityTestSummary()
  108. parser = argparse.ArgumentParser(description=
  109. """Takes as input the collection of *.testpass and *.testfail result
  110. files, and converts them to a JUnit formatted XML.""")
  111. parser.add_argument('targets_dir', metavar='result_file_directory',
  112. type=str, nargs='?', default='./',
  113. help="""The location of your results files.
  114. Defaults to current directory if not specified.""")
  115. parser.add_argument('root_path', nargs='?',
  116. default='os.path.split(__file__)[0]',
  117. help="""Helpful for producing more verbose output if
  118. using relative paths.""")
  119. parser.add_argument('--output', '-o', type=str, default="result.xml",
  120. help="""The name of the JUnit-formatted file (XML).""")
  121. args = parser.parse_args()
  122. if args.targets_dir[-1] != '/':
  123. args.targets_dir+='/'
  124. targets = list(map(lambda x: x.replace('\\', '/'), glob(args.targets_dir + '*.test*')))
  125. if len(targets) == 0:
  126. raise Exception("No *.testpass or *.testfail files found in '%s'" % args.targets_dir)
  127. uts.set_targets(targets)
  128. # set the root path
  129. uts.set_root_path(args.root_path)
  130. # set output
  131. uts.set_output(args.output)
  132. # run the summarizer
  133. print(uts.run())