1289715Sglebius#============================================================
2289715Sglebius#  Author:   John Theofanopoulos
3289715Sglebius#  A simple parser.   Takes the output files generated during the build process and
4289715Sglebius# extracts information relating to the tests.  
5289715Sglebius#
6289715Sglebius#  Notes:
7289715Sglebius#    To capture an output file under VS builds use the following:
8289715Sglebius#      devenv [build instructions]  > Output.txt & type Output.txt
9289715Sglebius# 
10289715Sglebius#    To capture an output file under GCC/Linux builds use the following:
11289715Sglebius#      make | tee Output.txt
12289715Sglebius#
13289715Sglebius#    To use this parser use the following command
14289715Sglebius#    ruby parseOutput.rb [options] [file]
15289715Sglebius#        options:  -xml  : produce a JUnit compatible XML file
16289715Sglebius#        file      :  file to scan for results
17289715Sglebius#============================================================
18289715Sglebius
19289715Sglebius
20289715Sglebiusclass ParseOutput
21289715Sglebius# The following flag is set to true when a test is found or false otherwise.
22289715Sglebius    @testFlag
23289715Sglebius    @xmlOut
24289715Sglebius    @arrayList
25289715Sglebius    @totalTests
26289715Sglebius    @classIndex
27289715Sglebius
28289715Sglebius#   Set the flag to indicate if there will be an XML output file or not  
29289715Sglebius    def setXmlOutput()
30289715Sglebius        @xmlOut = true
31289715Sglebius    end
32289715Sglebius    
33289715Sglebius#  if write our output to XML
34289715Sglebius    def writeXmlOuput()
35289715Sglebius            output = File.open("report.xml", "w")
36289715Sglebius            output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
37289715Sglebius            @arrayList.each do |item|
38289715Sglebius                output << item << "\n"
39289715Sglebius            end
40289715Sglebius            output << "</testsuite>\n"
41289715Sglebius    end
42289715Sglebius    
43289715Sglebius#  This function will try and determine when the suite is changed.   This is
44289715Sglebius# is the name that gets added to the classname parameter.
45289715Sglebius    def  testSuiteVerify(testSuiteName)
46289715Sglebius        if @testFlag == false
47289715Sglebius            @testFlag = true;
48289715Sglebius            # Split the path name 
49289715Sglebius            testName = testSuiteName.split("/")
50289715Sglebius            # Remove the extension
51289715Sglebius            baseName = testName[testName.size - 1].split(".")
52289715Sglebius            @testSuite = "test." + baseName[0]
53289715Sglebius            printf "New Test: %s\n", @testSuite
54289715Sglebius        end
55289715Sglebius    end
56289715Sglebius    
57289715Sglebius
58289715Sglebius# Test was flagged as having passed so format the output
59289715Sglebius    def testPassed(array)
60289715Sglebius        lastItem = array.length - 1
61289715Sglebius        testName = array[lastItem - 1]
62289715Sglebius        testSuiteVerify(array[@className])
63289715Sglebius        printf "%-40s PASS\n", testName
64289715Sglebius        if @xmlOut == true
65289715Sglebius            @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\"/>"
66289715Sglebius        end          
67289715Sglebius    end
68289715Sglebius
69289715Sglebius# Test was flagged as being ingored so format the output
70289715Sglebius    def testIgnored(array)
71289715Sglebius        lastItem = array.length - 1
72289715Sglebius        testName = array[lastItem - 2]
73289715Sglebius        reason = array[lastItem].chomp
74289715Sglebius        testSuiteVerify(array[@className])
75289715Sglebius        printf "%-40s IGNORED\n", testName
76289715Sglebius        if @xmlOut == true
77289715Sglebius            @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
78289715Sglebius            @arrayList.push "            <skipped type=\"TEST IGNORED\"> " + reason + " </skipped>"
79289715Sglebius            @arrayList.push "     </testcase>"
80289715Sglebius        end          
81289715Sglebius    end
82289715Sglebius
83289715Sglebius# Test was flagged as having failed  so format the line
84289715Sglebius    def testFailed(array)
85289715Sglebius        lastItem = array.length - 1
86289715Sglebius        testName = array[lastItem - 2]
87289715Sglebius        reason = array[lastItem].chomp + " at line: " + array[lastItem - 3]
88289715Sglebius        testSuiteVerify(array[@className])
89289715Sglebius        printf "%-40s FAILED\n", testName
90289715Sglebius        if @xmlOut == true
91289715Sglebius            @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
92289715Sglebius            @arrayList.push "            <failure type=\"ASSERT FAILED\"> " + reason + " </failure>"
93289715Sglebius            @arrayList.push "     </testcase>"
94289715Sglebius        end          
95289715Sglebius    end
96289715Sglebius
97289715Sglebius    
98289715Sglebius# Figure out what OS we are running on.   For now we are assuming if it's not Windows it must
99289715Sglebius# be Unix based.  
100289715Sglebius    def detectOS()
101289715Sglebius        myOS = RUBY_PLATFORM.split("-")
102289715Sglebius        if myOS.size == 2
103289715Sglebius            if myOS[1] == "mingw32"
104289715Sglebius                @className = 1
105289715Sglebius            else
106289715Sglebius                @className = 0
107289715Sglebius            end
108289715Sglebius	else
109289715Sglebius                @className = 0
110289715Sglebius        end
111289715Sglebius        
112289715Sglebius    end
113289715Sglebius
114289715Sglebius# Main function used to parse the file that was captured.
115289715Sglebius    def process(name)
116289715Sglebius        @testFlag = false
117289715Sglebius        @arrayList = Array.new
118289715Sglebius
119289715Sglebius        detectOS()
120289715Sglebius
121289715Sglebius        puts "Parsing file: " + name
122289715Sglebius    
123289715Sglebius      
124289715Sglebius        testPass = 0
125289715Sglebius        testFail = 0
126289715Sglebius        testIgnore = 0
127289715Sglebius        puts ""
128289715Sglebius        puts "=================== RESULTS ====================="
129289715Sglebius        puts ""
130289715Sglebius        File.open(name).each do |line|
131289715Sglebius        # Typical test lines look like this:
132289715Sglebius        # <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0
133289715Sglebius        # <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented
134289715Sglebius        # <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS
135289715Sglebius        #
136289715Sglebius        # where path is different on Unix vs Windows devices (Windows leads with a drive letter)
137289715Sglebius            lineArray = line.split(":")
138289715Sglebius            lineSize = lineArray.size
139289715Sglebius            # If we were able to split the line then we can look to see if any of our target words
140289715Sglebius            # were found.  Case is important.
141289715Sglebius            if lineSize >= 4
142289715Sglebius                # Determine if this test passed
143289715Sglebius                if  line.include? ":PASS"
144289715Sglebius                    testPassed(lineArray)
145289715Sglebius                    testPass += 1
146289715Sglebius                elsif line.include? ":FAIL:"
147289715Sglebius                    testFailed(lineArray)
148289715Sglebius                    testFail += 1
149289715Sglebius                elsif line.include? ":IGNORE:"
150289715Sglebius                    testIgnored(lineArray)
151289715Sglebius                    testIgnore += 1
152289715Sglebius                # If none of the keywords are found there are no more tests for this suite so clear
153289715Sglebius                # the test flag
154289715Sglebius                else
155289715Sglebius                    @testFlag = false
156289715Sglebius                end
157289715Sglebius            else
158289715Sglebius                @testFlag = false
159289715Sglebius                end
160289715Sglebius            end
161289715Sglebius        puts ""
162289715Sglebius        puts "=================== SUMMARY ====================="
163289715Sglebius        puts ""
164289715Sglebius        puts "Tests Passed  : " + testPass.to_s
165289715Sglebius        puts "Tests Failed  : " + testFail.to_s
166289715Sglebius        puts "Tests Ignored : " + testIgnore.to_s
167289715Sglebius        @totalTests = testPass + testFail + testIgnore
168289715Sglebius        if @xmlOut == true
169289715Sglebius            heading = "<testsuite tests=\"" +  @totalTests.to_s  + "\" failures=\"" + testFail.to_s + "\""  + " skips=\"" +  testIgnore.to_s + "\">" 
170289715Sglebius            @arrayList.insert(0, heading) 
171289715Sglebius            writeXmlOuput()
172289715Sglebius        end
173289715Sglebius
174289715Sglebius    #  return result
175289715Sglebius    end
176289715Sglebius
177289715Sglebius end
178289715Sglebius
179289715Sglebius# If the command line has no values in, used a default value of Output.txt
180289715SglebiusparseMyFile = ParseOutput.new
181289715Sglebius
182289715Sglebiusif ARGV.size >= 1 
183289715Sglebius    ARGV.each do |a|
184289715Sglebius        if a == "-xml"
185289715Sglebius            parseMyFile.setXmlOutput();
186289715Sglebius        else
187289715Sglebius            parseMyFile.process(a)
188289715Sglebius            break
189289715Sglebius        end
190289715Sglebius    end
191289715Sglebiusend
192