parse_output.rb 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #============================================================
  2. # Author: John Theofanopoulos
  3. # A simple parser. Takes the output files generated during the build process and
  4. # extracts information relating to the tests.
  5. #
  6. # Notes:
  7. # To capture an output file under VS builds use the following:
  8. # devenv [build instructions] > Output.txt & type Output.txt
  9. #
  10. # To capture an output file under GCC/Linux builds use the following:
  11. # make | tee Output.txt
  12. #
  13. # To use this parser use the following command
  14. # ruby parseOutput.rb [options] [file]
  15. # options: -xml : produce a JUnit compatible XML file
  16. # file : file to scan for results
  17. #============================================================
  18. class ParseOutput
  19. def initialize
  20. @test_flag = false
  21. @xml_out = false
  22. @array_list = false
  23. @total_tests = false
  24. @class_index = false
  25. end
  26. # Set the flag to indicate if there will be an XML output file or not
  27. def set_xml_output
  28. @xml_out = true
  29. end
  30. # if write our output to XML
  31. def write_xml_output
  32. output = File.open('report.xml', 'w')
  33. output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  34. @array_list.each do |item|
  35. output << item << "\n"
  36. end
  37. output << "</testsuite>\n"
  38. end
  39. # This function will try and determine when the suite is changed. This is
  40. # is the name that gets added to the classname parameter.
  41. def test_suite_verify(test_suite_name)
  42. return if @test_flag
  43. @test_flag = true
  44. # Split the path name
  45. test_name = test_suite_name.split('/')
  46. # Remove the extension
  47. base_name = test_name[test_name.size - 1].split('.')
  48. @test_suite = 'test.' + base_name[0]
  49. printf "New Test: %s\n", @test_suite
  50. end
  51. # Test was flagged as having passed so format the output
  52. def test_passed(array)
  53. last_item = array.length - 1
  54. test_name = array[last_item - 1]
  55. test_suite_verify(array[@class_name])
  56. printf "%-40s PASS\n", test_name
  57. return unless @xml_out
  58. @array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '"/>'
  59. end
  60. # Test was flagged as having passed so format the output.
  61. # This is using the Unity fixture output and not the original Unity output.
  62. def test_passed_unity_fixture(array)
  63. test_suite = array[0].sub('TEST(', '')
  64. test_suite = test_suite.sub(',', '')
  65. test_name = array[1].sub(')', '')
  66. return unless @xml_out
  67. @array_list.push ' <testcase classname="' + test_suite + '" name="' + test_name + '"/>'
  68. end
  69. # Test was flagged as being ingored so format the output
  70. def test_ignored(array)
  71. last_item = array.length - 1
  72. test_name = array[last_item - 2]
  73. reason = array[last_item].chomp
  74. test_suite_verify(array[@class_name])
  75. printf "%-40s IGNORED\n", test_name
  76. if test_name.start_with? 'TEST('
  77. array2 = test_name.split(' ')
  78. @test_suite = array2[0].sub('TEST(', '')
  79. @test_suite = @test_suite.sub(',', '')
  80. test_name = array2[1].sub(')', '')
  81. end
  82. return unless @xml_out
  83. @array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '">'
  84. @array_list.push ' <skipped type="TEST IGNORED"> ' + reason + ' </skipped>'
  85. @array_list.push ' </testcase>'
  86. end
  87. # Test was flagged as having failed so format the line
  88. def test_failed(array)
  89. last_item = array.length - 1
  90. test_name = array[last_item - 2]
  91. reason = array[last_item].chomp + ' at line: ' + array[last_item - 3]
  92. test_suite_verify(array[@class_name])
  93. printf "%-40s FAILED\n", test_name
  94. if test_name.start_with? 'TEST('
  95. array2 = test_name.split(' ')
  96. @test_suite = array2[0].sub('TEST(', '')
  97. @test_suite = @test_suite.sub(',', '')
  98. test_name = array2[1].sub(')', '')
  99. end
  100. return unless @xml_out
  101. @array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '">'
  102. @array_list.push ' <failure type="ASSERT FAILED"> ' + reason + ' </failure>'
  103. @array_list.push ' </testcase>'
  104. end
  105. # Figure out what OS we are running on. For now we are assuming if it's not Windows it must
  106. # be Unix based.
  107. def detect_os
  108. os = RUBY_PLATFORM.split('-')
  109. @class_name = if os.size == 2
  110. if os[1] == 'mingw32'
  111. 1
  112. else
  113. 0
  114. end
  115. else
  116. 0
  117. end
  118. end
  119. # Main function used to parse the file that was captured.
  120. def process(name)
  121. @test_flag = false
  122. @array_list = []
  123. detect_os
  124. puts 'Parsing file: ' + name
  125. test_pass = 0
  126. test_fail = 0
  127. test_ignore = 0
  128. puts ''
  129. puts '=================== RESULTS ====================='
  130. puts ''
  131. File.open(name).each do |line|
  132. # Typical test lines look like this:
  133. # <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0
  134. # <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented
  135. # <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS
  136. #
  137. # where path is different on Unix vs Windows devices (Windows leads with a drive letter)
  138. line_array = line.split(':')
  139. # If we were able to split the line then we can look to see if any of our target words
  140. # were found. Case is important.
  141. if (line_array.size >= 4) || (line.start_with? 'TEST(')
  142. # Determine if this test passed
  143. if line.include? ':PASS'
  144. test_passed(line_array)
  145. test_pass += 1
  146. elsif line.include? ':FAIL:'
  147. test_failed(line_array)
  148. test_fail += 1
  149. elsif line.include? ':IGNORE:'
  150. test_ignored(line_array)
  151. test_ignore += 1
  152. elsif line.start_with? 'TEST('
  153. if line.include? ' PASS'
  154. line_array = line.split(' ')
  155. test_passed_unity_fixture(line_array)
  156. test_pass += 1
  157. end
  158. # If none of the keywords are found there are no more tests for this suite so clear
  159. # the test flag
  160. else
  161. @test_flag = false
  162. end
  163. else
  164. @test_flag = false
  165. end
  166. end
  167. puts ''
  168. puts '=================== SUMMARY ====================='
  169. puts ''
  170. puts 'Tests Passed : ' + test_pass.to_s
  171. puts 'Tests Failed : ' + test_fail.to_s
  172. puts 'Tests Ignored : ' + test_ignore.to_s
  173. @total_tests = test_pass + test_fail + test_ignore
  174. return unless @xml_out
  175. heading = '<testsuite tests="' + @total_tests.to_s + '" failures="' + test_fail.to_s + '"' + ' skips="' + test_ignore.to_s + '">'
  176. @array_list.insert(0, heading)
  177. write_xml_output
  178. end
  179. end
  180. # If the command line has no values in, used a default value of Output.txt
  181. parse_my_file = ParseOutput.new
  182. if ARGV.size >= 1
  183. ARGV.each do |a|
  184. if a == '-xml'
  185. parse_my_file.set_xml_output
  186. else
  187. parse_my_file.process(a)
  188. break
  189. end
  190. end
  191. end