xref: /aosp_15_r20/external/deqp/scripts/log/log_to_xml.py (revision 35238bce31c2a825756842865a792f8cf7f89930)
1*35238bceSAndroid Build Coastguard Worker# -*- coding: utf-8 -*-
2*35238bceSAndroid Build Coastguard Worker
3*35238bceSAndroid Build Coastguard Worker#-------------------------------------------------------------------------
4*35238bceSAndroid Build Coastguard Worker# drawElements Quality Program utilities
5*35238bceSAndroid Build Coastguard Worker# --------------------------------------
6*35238bceSAndroid Build Coastguard Worker#
7*35238bceSAndroid Build Coastguard Worker# Copyright 2015 The Android Open Source Project
8*35238bceSAndroid Build Coastguard Worker#
9*35238bceSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
10*35238bceSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
11*35238bceSAndroid Build Coastguard Worker# You may obtain a copy of the License at
12*35238bceSAndroid Build Coastguard Worker#
13*35238bceSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
14*35238bceSAndroid Build Coastguard Worker#
15*35238bceSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
16*35238bceSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
17*35238bceSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18*35238bceSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
19*35238bceSAndroid Build Coastguard Worker# limitations under the License.
20*35238bceSAndroid Build Coastguard Worker#
21*35238bceSAndroid Build Coastguard Worker#-------------------------------------------------------------------------
22*35238bceSAndroid Build Coastguard Worker
23*35238bceSAndroid Build Coastguard Workerimport os
24*35238bceSAndroid Build Coastguard Workerimport sys
25*35238bceSAndroid Build Coastguard Workerimport codecs
26*35238bceSAndroid Build Coastguard Workerimport xml.dom.minidom
27*35238bceSAndroid Build Coastguard Workerimport xml.sax
28*35238bceSAndroid Build Coastguard Workerimport xml.sax.handler
29*35238bceSAndroid Build Coastguard Workerfrom log_parser import BatchResultParser, StatusCode
30*35238bceSAndroid Build Coastguard Worker
31*35238bceSAndroid Build Coastguard WorkerSTYLESHEET_FILENAME = "testlog.xsl"
32*35238bceSAndroid Build Coastguard WorkerLOG_VERSION = '0.3.2'
33*35238bceSAndroid Build Coastguard Worker
34*35238bceSAndroid Build Coastguard Workerclass BuildXMLLogHandler(xml.sax.handler.ContentHandler):
35*35238bceSAndroid Build Coastguard Worker    def __init__ (self, doc):
36*35238bceSAndroid Build Coastguard Worker        self.doc = doc
37*35238bceSAndroid Build Coastguard Worker        self.elementStack = []
38*35238bceSAndroid Build Coastguard Worker        self.rootElements = []
39*35238bceSAndroid Build Coastguard Worker
40*35238bceSAndroid Build Coastguard Worker    def getRootElements (self):
41*35238bceSAndroid Build Coastguard Worker        return self.rootElements
42*35238bceSAndroid Build Coastguard Worker
43*35238bceSAndroid Build Coastguard Worker    def pushElement (self, elem):
44*35238bceSAndroid Build Coastguard Worker        if len(self.elementStack) == 0:
45*35238bceSAndroid Build Coastguard Worker            self.rootElements.append(elem)
46*35238bceSAndroid Build Coastguard Worker        else:
47*35238bceSAndroid Build Coastguard Worker            self.getCurElement().appendChild(elem)
48*35238bceSAndroid Build Coastguard Worker        self.elementStack.append(elem)
49*35238bceSAndroid Build Coastguard Worker
50*35238bceSAndroid Build Coastguard Worker    def popElement (self):
51*35238bceSAndroid Build Coastguard Worker        self.elementStack.pop()
52*35238bceSAndroid Build Coastguard Worker
53*35238bceSAndroid Build Coastguard Worker    def getCurElement (self):
54*35238bceSAndroid Build Coastguard Worker        if len(self.elementStack) > 0:
55*35238bceSAndroid Build Coastguard Worker            return self.elementStack[-1]
56*35238bceSAndroid Build Coastguard Worker        else:
57*35238bceSAndroid Build Coastguard Worker            return None
58*35238bceSAndroid Build Coastguard Worker
59*35238bceSAndroid Build Coastguard Worker    def startDocument (self):
60*35238bceSAndroid Build Coastguard Worker        pass
61*35238bceSAndroid Build Coastguard Worker
62*35238bceSAndroid Build Coastguard Worker    def endDocument (self):
63*35238bceSAndroid Build Coastguard Worker        pass
64*35238bceSAndroid Build Coastguard Worker
65*35238bceSAndroid Build Coastguard Worker    def startElement (self, name, attrs):
66*35238bceSAndroid Build Coastguard Worker        elem = self.doc.createElement(name)
67*35238bceSAndroid Build Coastguard Worker        for name in attrs.getNames():
68*35238bceSAndroid Build Coastguard Worker            value = attrs.getValue(name)
69*35238bceSAndroid Build Coastguard Worker            elem.setAttribute(name, value)
70*35238bceSAndroid Build Coastguard Worker        self.pushElement(elem)
71*35238bceSAndroid Build Coastguard Worker
72*35238bceSAndroid Build Coastguard Worker    def endElement (self, name):
73*35238bceSAndroid Build Coastguard Worker        self.popElement()
74*35238bceSAndroid Build Coastguard Worker
75*35238bceSAndroid Build Coastguard Worker    def characters (self, content):
76*35238bceSAndroid Build Coastguard Worker        # Discard completely empty content
77*35238bceSAndroid Build Coastguard Worker        if len(content.strip()) == 0:
78*35238bceSAndroid Build Coastguard Worker            return
79*35238bceSAndroid Build Coastguard Worker
80*35238bceSAndroid Build Coastguard Worker        # Append as text node (not pushed to stack)
81*35238bceSAndroid Build Coastguard Worker        if self.getCurElement() != None:
82*35238bceSAndroid Build Coastguard Worker            txt = self.doc.createTextNode(content)
83*35238bceSAndroid Build Coastguard Worker            self.getCurElement().appendChild(txt)
84*35238bceSAndroid Build Coastguard Worker
85*35238bceSAndroid Build Coastguard Workerclass LogErrorHandler(xml.sax.handler.ErrorHandler):
86*35238bceSAndroid Build Coastguard Worker    def __init__ (self):
87*35238bceSAndroid Build Coastguard Worker        pass
88*35238bceSAndroid Build Coastguard Worker
89*35238bceSAndroid Build Coastguard Worker    def error (self, err):
90*35238bceSAndroid Build Coastguard Worker        #print("error(%s)" % str(err))
91*35238bceSAndroid Build Coastguard Worker        pass
92*35238bceSAndroid Build Coastguard Worker
93*35238bceSAndroid Build Coastguard Worker    def fatalError (self, err):
94*35238bceSAndroid Build Coastguard Worker        #print("fatalError(%s)" % str(err))
95*35238bceSAndroid Build Coastguard Worker        pass
96*35238bceSAndroid Build Coastguard Worker
97*35238bceSAndroid Build Coastguard Worker    def warning (self, warn):
98*35238bceSAndroid Build Coastguard Worker        #print("warning(%s)" % str(warn))
99*35238bceSAndroid Build Coastguard Worker        pass
100*35238bceSAndroid Build Coastguard Worker
101*35238bceSAndroid Build Coastguard Workerdef findFirstElementByName (nodes, name):
102*35238bceSAndroid Build Coastguard Worker    for node in nodes:
103*35238bceSAndroid Build Coastguard Worker        if node.nodeName == name:
104*35238bceSAndroid Build Coastguard Worker            return node
105*35238bceSAndroid Build Coastguard Worker        chFound = findFirstElementByName(node.childNodes, name)
106*35238bceSAndroid Build Coastguard Worker        if chFound != None:
107*35238bceSAndroid Build Coastguard Worker            return chFound
108*35238bceSAndroid Build Coastguard Worker    return None
109*35238bceSAndroid Build Coastguard Worker
110*35238bceSAndroid Build Coastguard Worker# Normalizes potentially broken (due to crash for example) log data to XML element tree
111*35238bceSAndroid Build Coastguard Workerdef normalizeToXml (result, doc):
112*35238bceSAndroid Build Coastguard Worker    handler = BuildXMLLogHandler(doc)
113*35238bceSAndroid Build Coastguard Worker    errHandler = LogErrorHandler()
114*35238bceSAndroid Build Coastguard Worker
115*35238bceSAndroid Build Coastguard Worker    xml.sax.parseString(result.log, handler, errHandler)
116*35238bceSAndroid Build Coastguard Worker
117*35238bceSAndroid Build Coastguard Worker    rootNodes = handler.getRootElements()
118*35238bceSAndroid Build Coastguard Worker
119*35238bceSAndroid Build Coastguard Worker    # Check if we have TestCaseResult
120*35238bceSAndroid Build Coastguard Worker    testCaseResult = findFirstElementByName(rootNodes, 'TestCaseResult')
121*35238bceSAndroid Build Coastguard Worker    if testCaseResult == None:
122*35238bceSAndroid Build Coastguard Worker        # Create TestCaseResult element
123*35238bceSAndroid Build Coastguard Worker        testCaseResult = doc.createElement('TestCaseResult')
124*35238bceSAndroid Build Coastguard Worker        testCaseResult.setAttribute('CasePath', result.name)
125*35238bceSAndroid Build Coastguard Worker        testCaseResult.setAttribute('CaseType', 'SelfValidate') # \todo [pyry] Not recoverable..
126*35238bceSAndroid Build Coastguard Worker        testCaseResult.setAttribute('Version', LOG_VERSION)
127*35238bceSAndroid Build Coastguard Worker        rootNodes.append(testCaseResult)
128*35238bceSAndroid Build Coastguard Worker
129*35238bceSAndroid Build Coastguard Worker    # Check if we have Result element
130*35238bceSAndroid Build Coastguard Worker    resultElem = findFirstElementByName(rootNodes, 'Result')
131*35238bceSAndroid Build Coastguard Worker    if resultElem == None:
132*35238bceSAndroid Build Coastguard Worker        # Create result element
133*35238bceSAndroid Build Coastguard Worker        resultElem = doc.createElement('Result')
134*35238bceSAndroid Build Coastguard Worker        resultElem.setAttribute('StatusCode', result.statusCode)
135*35238bceSAndroid Build Coastguard Worker        resultElem.appendChild(doc.createTextNode(result.statusDetails))
136*35238bceSAndroid Build Coastguard Worker        testCaseResult.appendChild(resultElem)
137*35238bceSAndroid Build Coastguard Worker
138*35238bceSAndroid Build Coastguard Worker    return rootNodes
139*35238bceSAndroid Build Coastguard Worker
140*35238bceSAndroid Build Coastguard Workerdef logToXml (logFilePath, outFilePath):
141*35238bceSAndroid Build Coastguard Worker    # Initialize Xml Document
142*35238bceSAndroid Build Coastguard Worker    dstDoc = xml.dom.minidom.Document()
143*35238bceSAndroid Build Coastguard Worker    batchResultNode = dstDoc.createElement('BatchResult')
144*35238bceSAndroid Build Coastguard Worker    batchResultNode.setAttribute("FileName", os.path.basename(logFilePath))
145*35238bceSAndroid Build Coastguard Worker    dstDoc.appendChild(batchResultNode)
146*35238bceSAndroid Build Coastguard Worker
147*35238bceSAndroid Build Coastguard Worker    # Initialize dictionary for counting status codes
148*35238bceSAndroid Build Coastguard Worker    countByStatusCode = {}
149*35238bceSAndroid Build Coastguard Worker    for code in StatusCode.STATUS_CODES:
150*35238bceSAndroid Build Coastguard Worker        countByStatusCode[code] = 0
151*35238bceSAndroid Build Coastguard Worker
152*35238bceSAndroid Build Coastguard Worker    # Write custom headers
153*35238bceSAndroid Build Coastguard Worker    out = codecs.open(outFilePath, "wb", encoding="utf-8")
154*35238bceSAndroid Build Coastguard Worker    out.write("<?xml version=\"1.0\"?>\n")
155*35238bceSAndroid Build Coastguard Worker    out.write("<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n" % STYLESHEET_FILENAME)
156*35238bceSAndroid Build Coastguard Worker
157*35238bceSAndroid Build Coastguard Worker    summaryElem = dstDoc.createElement('ResultTotals')
158*35238bceSAndroid Build Coastguard Worker    batchResultNode.appendChild(summaryElem)
159*35238bceSAndroid Build Coastguard Worker
160*35238bceSAndroid Build Coastguard Worker    # Print the first line manually <BatchResult FileName=something.xml>
161*35238bceSAndroid Build Coastguard Worker    out.write(dstDoc.toprettyxml().splitlines()[1])
162*35238bceSAndroid Build Coastguard Worker    out.write("\n")
163*35238bceSAndroid Build Coastguard Worker
164*35238bceSAndroid Build Coastguard Worker    parser = BatchResultParser()
165*35238bceSAndroid Build Coastguard Worker    parser.init(logFilePath)
166*35238bceSAndroid Build Coastguard Worker    logFile = open(logFilePath, 'rb')
167*35238bceSAndroid Build Coastguard Worker
168*35238bceSAndroid Build Coastguard Worker    result = parser.getNextTestCaseResult(logFile)
169*35238bceSAndroid Build Coastguard Worker    while result is not None:
170*35238bceSAndroid Build Coastguard Worker
171*35238bceSAndroid Build Coastguard Worker        countByStatusCode[result.statusCode] += 1
172*35238bceSAndroid Build Coastguard Worker        rootNodes = normalizeToXml(result, dstDoc)
173*35238bceSAndroid Build Coastguard Worker
174*35238bceSAndroid Build Coastguard Worker        for node in rootNodes:
175*35238bceSAndroid Build Coastguard Worker
176*35238bceSAndroid Build Coastguard Worker            # Do not append TestResults to dstDoc to save memory.
177*35238bceSAndroid Build Coastguard Worker            # Instead print them directly to the file and add tabs manually.
178*35238bceSAndroid Build Coastguard Worker            for line in node.toprettyxml().splitlines():
179*35238bceSAndroid Build Coastguard Worker                out.write("\t" + line + "\n")
180*35238bceSAndroid Build Coastguard Worker
181*35238bceSAndroid Build Coastguard Worker        result = parser.getNextTestCaseResult(logFile)
182*35238bceSAndroid Build Coastguard Worker
183*35238bceSAndroid Build Coastguard Worker    # Calculate the totals to add at the end of the Xml file
184*35238bceSAndroid Build Coastguard Worker    for code in StatusCode.STATUS_CODES:
185*35238bceSAndroid Build Coastguard Worker        summaryElem.setAttribute(code, "%d" % countByStatusCode[code])
186*35238bceSAndroid Build Coastguard Worker    summaryElem.setAttribute('All', "%d" % sum(countByStatusCode.values()))
187*35238bceSAndroid Build Coastguard Worker
188*35238bceSAndroid Build Coastguard Worker    # Print the test totals and finish the Xml Document"
189*35238bceSAndroid Build Coastguard Worker    for line in dstDoc.toprettyxml().splitlines()[2:]:
190*35238bceSAndroid Build Coastguard Worker        out.write(line + "\n")
191*35238bceSAndroid Build Coastguard Worker
192*35238bceSAndroid Build Coastguard Worker    out.close()
193*35238bceSAndroid Build Coastguard Worker    logFile.close()
194*35238bceSAndroid Build Coastguard Worker
195*35238bceSAndroid Build Coastguard Workerif __name__ == "__main__":
196*35238bceSAndroid Build Coastguard Worker    if len(sys.argv) != 3:
197*35238bceSAndroid Build Coastguard Worker        print("%s: [test log] [dst file]" % sys.argv[0])
198*35238bceSAndroid Build Coastguard Worker        sys.exit(-1)
199*35238bceSAndroid Build Coastguard Worker
200*35238bceSAndroid Build Coastguard Worker    logToXml(sys.argv[1], sys.argv[2])
201