1290001Sglebius#============================================================
2290001Sglebius#  Author:   John Theofanopoulos
3290001Sglebius#  A simple parser.   Takes the output files generated during the build process and
4290001Sglebius# extracts information relating to the tests.  
5290001Sglebius#
6290001Sglebius#  Notes:
7290001Sglebius#    To capture an output file under VS builds use the following:
8290001Sglebius#      devenv [build instructions]  > Output.txt & type Output.txt
9290001Sglebius# 
10290001Sglebius#    To capture an output file under GCC/Linux builds use the following:
11290001Sglebius#      make | tee Output.txt
12290001Sglebius#
13290001Sglebius#    To use this parser use the following command
14290001Sglebius#    ruby parseOutput.rb [options] [file]
15290001Sglebius#        options:  -xml  : produce a JUnit compatible XML file
16290001Sglebius#        file      :  file to scan for results
17290001Sglebius#============================================================
18290001Sglebius
19290001Sglebius
20290001Sglebiusclass ParseOutput
21290001Sglebius# The following flag is set to true when a test is found or false otherwise.
22290001Sglebius    @testFlag
23290001Sglebius    @xmlOut
24290001Sglebius    @arrayList
25290001Sglebius    @totalTests
26290001Sglebius    @classIndex
27290001Sglebius
28290001Sglebius#   Set the flag to indicate if there will be an XML output file or not  
29290001Sglebius    def setXmlOutput()
30290001Sglebius        @xmlOut = true
31290001Sglebius    end
32290001Sglebius    
33290001Sglebius#  if write our output to XML
34290001Sglebius    def writeXmlOuput()
35290001Sglebius            output = File.open("report.xml", "w")
36290001Sglebius            output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
37290001Sglebius            @arrayList.each do |item|
38290001Sglebius                output << item << "\n"
39290001Sglebius            end
40290001Sglebius            output << "</testsuite>\n"
41290001Sglebius    end
42290001Sglebius    
43290001Sglebius#  This function will try and determine when the suite is changed.   This is
44290001Sglebius# is the name that gets added to the classname parameter.
45290001Sglebius    def  testSuiteVerify(testSuiteName)
46290001Sglebius        if @testFlag == false
47290001Sglebius            @testFlag = true;
48290001Sglebius            # Split the path name 
49290001Sglebius            testName = testSuiteName.split("/")
50290001Sglebius            # Remove the extension
51290001Sglebius            baseName = testName[testName.size - 1].split(".")
52290001Sglebius            @testSuite = "test." + baseName[0]
53290001Sglebius            printf "New Test: %s\n", @testSuite
54290001Sglebius        end
55290001Sglebius    end
56290001Sglebius    
57290001Sglebius
58290001Sglebius# Test was flagged as having passed so format the output
59290001Sglebius    def testPassed(array)
60290001Sglebius        lastItem = array.length - 1
61290001Sglebius        testName = array[lastItem - 1]
62290001Sglebius        testSuiteVerify(array[@className])
63290001Sglebius        printf "%-40s PASS\n", testName
64290001Sglebius        if @xmlOut == true
65290001Sglebius            @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\"/>"
66290001Sglebius        end          
67290001Sglebius    end
68290001Sglebius
69290001Sglebius# Test was flagged as being ingored so format the output
70290001Sglebius    def testIgnored(array)
71290001Sglebius        lastItem = array.length - 1
72290001Sglebius        testName = array[lastItem - 2]
73290001Sglebius        reason = array[lastItem].chomp
74290001Sglebius        testSuiteVerify(array[@className])
75290001Sglebius        printf "%-40s IGNORED\n", testName
76290001Sglebius        if @xmlOut == true
77290001Sglebius            @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
78290001Sglebius            @arrayList.push "            <skipped type=\"TEST IGNORED\"> " + reason + " </skipped>"
79290001Sglebius            @arrayList.push "     </testcase>"
80290001Sglebius        end          
81290001Sglebius    end
82290001Sglebius
83290001Sglebius# Test was flagged as having failed  so format the line
84290001Sglebius    def testFailed(array)
85290001Sglebius        lastItem = array.length - 1
86290001Sglebius        testName = array[lastItem - 2]
87290001Sglebius        reason = array[lastItem].chomp + " at line: " + array[lastItem - 3]
88290001Sglebius        testSuiteVerify(array[@className])
89290001Sglebius        printf "%-40s FAILED\n", testName
90290001Sglebius        if @xmlOut == true
91290001Sglebius            @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
92290001Sglebius            @arrayList.push "            <failure type=\"ASSERT FAILED\"> " + reason + " </failure>"
93290001Sglebius            @arrayList.push "     </testcase>"
94290001Sglebius        end          
95290001Sglebius    end
96290001Sglebius
97290001Sglebius    
98290001Sglebius# Figure out what OS we are running on.   For now we are assuming if it's not Windows it must
99290001Sglebius# be Unix based.  
100290001Sglebius    def detectOS()
101290001Sglebius        myOS = RUBY_PLATFORM.split("-")
102290001Sglebius        if myOS.size == 2
103290001Sglebius            if myOS[1] == "mingw32"
104290001Sglebius                @className = 1
105290001Sglebius            else
106290001Sglebius                @className = 0
107290001Sglebius            end
108290001Sglebius	else
109290001Sglebius                @className = 0
110290001Sglebius        end
111290001Sglebius        
112290001Sglebius    end
113290001Sglebius
114290001Sglebius# Main function used to parse the file that was captured.
115290001Sglebius    def process(name)
116290001Sglebius        @testFlag = false
117290001Sglebius        @arrayList = Array.new
118290001Sglebius
119290001Sglebius        detectOS()
120290001Sglebius
121290001Sglebius        puts "Parsing file: " + name
122290001Sglebius    
123290001Sglebius      
124290001Sglebius        testPass = 0
125290001Sglebius        testFail = 0
126290001Sglebius        testIgnore = 0
127290001Sglebius        puts ""
128290001Sglebius        puts "=================== RESULTS ====================="
129290001Sglebius        puts ""
130290001Sglebius        File.open(name).each do |line|
131290001Sglebius        # Typical test lines look like this:
132290001Sglebius        # <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0
133290001Sglebius        # <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented
134290001Sglebius        # <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS
135290001Sglebius        #
136290001Sglebius        # where path is different on Unix vs Windows devices (Windows leads with a drive letter)
137290001Sglebius            lineArray = line.split(":")
138290001Sglebius            lineSize = lineArray.size
139290001Sglebius            # If we were able to split the line then we can look to see if any of our target words
140290001Sglebius            # were found.  Case is important.
141290001Sglebius            if lineSize >= 4
142290001Sglebius                # Determine if this test passed
143290001Sglebius                if  line.include? ":PASS"
144290001Sglebius                    testPassed(lineArray)
145290001Sglebius                    testPass += 1
146290001Sglebius                elsif line.include? ":FAIL:"
147290001Sglebius                    testFailed(lineArray)
148290001Sglebius                    testFail += 1
149290001Sglebius                elsif line.include? ":IGNORE:"
150290001Sglebius                    testIgnored(lineArray)
151290001Sglebius                    testIgnore += 1
152290001Sglebius                # If none of the keywords are found there are no more tests for this suite so clear
153290001Sglebius                # the test flag
154290001Sglebius                else
155290001Sglebius                    @testFlag = false
156290001Sglebius                end
157290001Sglebius            else
158290001Sglebius                @testFlag = false
159290001Sglebius                end
160290001Sglebius            end
161290001Sglebius        puts ""
162290001Sglebius        puts "=================== SUMMARY ====================="
163290001Sglebius        puts ""
164290001Sglebius        puts "Tests Passed  : " + testPass.to_s
165290001Sglebius        puts "Tests Failed  : " + testFail.to_s
166290001Sglebius        puts "Tests Ignored : " + testIgnore.to_s
167290001Sglebius        @totalTests = testPass + testFail + testIgnore
168290001Sglebius        if @xmlOut == true
169290001Sglebius            heading = "<testsuite tests=\"" +  @totalTests.to_s  + "\" failures=\"" + testFail.to_s + "\""  + " skips=\"" +  testIgnore.to_s + "\">" 
170290001Sglebius            @arrayList.insert(0, heading) 
171290001Sglebius            writeXmlOuput()
172290001Sglebius        end
173290001Sglebius
174290001Sglebius    #  return result
175290001Sglebius    end
176290001Sglebius
177290001Sglebius end
178290001Sglebius
179290001Sglebius# If the command line has no values in, used a default value of Output.txt
180290001SglebiusparseMyFile = ParseOutput.new
181290001Sglebius
182290001Sglebiusif ARGV.size >= 1 
183290001Sglebius    ARGV.each do |a|
184290001Sglebius        if a == "-xml"
185290001Sglebius            parseMyFile.setXmlOutput();
186290001Sglebius        else
187290001Sglebius            parseMyFile.process(a)
188290001Sglebius            break
189290001Sglebius        end
190290001Sglebius    end
191290001Sglebiusend
192