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 19 20class ParseOutput 21# The following flag is set to true when a test is found or false otherwise. 22 @testFlag 23 @xmlOut 24 @arrayList 25 @totalTests 26 @classIndex 27 28# Set the flag to indicate if there will be an XML output file or not 29 def setXmlOutput() 30 @xmlOut = true 31 end 32 33# if write our output to XML 34 def writeXmlOuput() 35 output = File.open("report.xml", "w") 36 output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 37 @arrayList.each do |item| 38 output << item << "\n" 39 end 40 output << "</testsuite>\n" 41 end 42 43# This function will try and determine when the suite is changed. This is 44# is the name that gets added to the classname parameter. 45 def testSuiteVerify(testSuiteName) 46 if @testFlag == false 47 @testFlag = true; 48 # Split the path name 49 testName = testSuiteName.split("/") 50 # Remove the extension 51 baseName = testName[testName.size - 1].split(".") 52 @testSuite = "test." + baseName[0] 53 printf "New Test: %s\n", @testSuite 54 end 55 end 56 57 58# Test was flagged as having passed so format the output 59 def testPassed(array) 60 lastItem = array.length - 1 61 testName = array[lastItem - 1] 62 testSuiteVerify(array[@className]) 63 printf "%-40s PASS\n", testName 64 if @xmlOut == true 65 @arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\"/>" 66 end 67 end 68 69# Test was flagged as being ingored so format the output 70 def testIgnored(array) 71 lastItem = array.length - 1 72 testName = array[lastItem - 2] 73 reason = array[lastItem].chomp 74 testSuiteVerify(array[@className]) 75 printf "%-40s IGNORED\n", testName 76 if @xmlOut == true 77 @arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">" 78 @arrayList.push " <skipped type=\"TEST IGNORED\"> " + reason + " </skipped>" 79 @arrayList.push " </testcase>" 80 end 81 end 82 83# Test was flagged as having failed so format the line 84 def testFailed(array) 85 lastItem = array.length - 1 86 testName = array[lastItem - 2] 87 reason = array[lastItem].chomp + " at line: " + array[lastItem - 3] 88 testSuiteVerify(array[@className]) 89 printf "%-40s FAILED\n", testName 90 if @xmlOut == true 91 @arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">" 92 @arrayList.push " <failure type=\"ASSERT FAILED\"> " + reason + " </failure>" 93 @arrayList.push " </testcase>" 94 end 95 end 96 97 98# Figure out what OS we are running on. For now we are assuming if it's not Windows it must 99# be Unix based. 100 def detectOS() 101 myOS = RUBY_PLATFORM.split("-") 102 if myOS.size == 2 103 if myOS[1] == "mingw32" 104 @className = 1 105 else 106 @className = 0 107 end 108 else 109 @className = 0 110 end 111 112 end 113 114# Main function used to parse the file that was captured. 115 def process(name) 116 @testFlag = false 117 @arrayList = Array.new 118 119 detectOS() 120 121 puts "Parsing file: " + name 122 123 124 testPass = 0 125 testFail = 0 126 testIgnore = 0 127 puts "" 128 puts "=================== RESULTS =====================" 129 puts "" 130 File.open(name).each do |line| 131 # Typical test lines look like this: 132 # <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0 133 # <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented 134 # <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS 135 # 136 # where path is different on Unix vs Windows devices (Windows leads with a drive letter) 137 lineArray = line.split(":") 138 lineSize = lineArray.size 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 lineSize >= 4 142 # Determine if this test passed 143 if line.include? ":PASS" 144 testPassed(lineArray) 145 testPass += 1 146 elsif line.include? ":FAIL:" 147 testFailed(lineArray) 148 testFail += 1 149 elsif line.include? ":IGNORE:" 150 testIgnored(lineArray) 151 testIgnore += 1 152 # If none of the keywords are found there are no more tests for this suite so clear 153 # the test flag 154 else 155 @testFlag = false 156 end 157 else 158 @testFlag = false 159 end 160 end 161 puts "" 162 puts "=================== SUMMARY =====================" 163 puts "" 164 puts "Tests Passed : " + testPass.to_s 165 puts "Tests Failed : " + testFail.to_s 166 puts "Tests Ignored : " + testIgnore.to_s 167 @totalTests = testPass + testFail + testIgnore 168 if @xmlOut == true 169 heading = "<testsuite tests=\"" + @totalTests.to_s + "\" failures=\"" + testFail.to_s + "\"" + " skips=\"" + testIgnore.to_s + "\">" 170 @arrayList.insert(0, heading) 171 writeXmlOuput() 172 end 173 174 # return result 175 end 176 177 end 178 179# If the command line has no values in, used a default value of Output.txt 180parseMyFile = ParseOutput.new 181 182if ARGV.size >= 1 183 ARGV.each do |a| 184 if a == "-xml" 185 parseMyFile.setXmlOutput(); 186 else 187 parseMyFile.process(a) 188 break 189 end 190 end 191end 192