VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/analysis/reader.py@ 93603

Last change on this file since 93603 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.9 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: reader.py 93115 2022-01-01 11:31:46Z vboxsync $
3
4"""
5XML reader module.
6
7This produces a test result tree that can be processed and passed to
8reporting.
9"""
10
11__copyright__ = \
12"""
13Copyright (C) 2010-2022 Oracle Corporation
14
15This file is part of VirtualBox Open Source Edition (OSE), as
16available from http://www.virtualbox.org. This file is free software;
17you can redistribute it and/or modify it under the terms of the GNU
18General Public License (GPL) as published by the Free Software
19Foundation, in version 2 as it comes in the "COPYING" file of the
20VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22
23The contents of this file may alternatively be used under the terms
24of the Common Development and Distribution License Version 1.0
25(CDDL) only, as it comes in the "COPYING.CDDL" file of the
26VirtualBox OSE distribution, in which case the provisions of the
27CDDL are applicable instead of those of the GPL.
28
29You may elect to license modified versions of this file under the
30terms and conditions of either the GPL or the CDDL or both.
31"""
32__version__ = "$Revision: 93115 $"
33__all__ = [ 'parseTestResult', ]
34
35# Standard python imports.
36import os
37import traceback
38
39# pylint: disable=missing-docstring
40
41class Value(object):
42 """
43 Represents a value. Usually this is benchmark result or parameter.
44 """
45 def __init__(self, oTest, hsAttrs):
46 self.oTest = oTest;
47 self.sName = hsAttrs['name'];
48 self.sUnit = hsAttrs['unit'];
49 self.sTimestamp = hsAttrs['timestamp'];
50 self.sValue = '';
51 self.sDiff = None;
52
53 # parsing
54
55 def addData(self, sData):
56 self.sValue += sData;
57
58 # debug
59
60 def printValue(self, cIndent):
61 print('%sValue: name=%s timestamp=%s unit=%s value="%s"'
62 % (''.ljust(cIndent*2), self.sName, self.sTimestamp, self.sUnit, self.sValue));
63
64
65class Test(object):
66 """
67 Nested test result.
68 """
69 def __init__(self, oParent, hsAttrs):
70 self.aoChildren = [];
71 self.aoValues = [];
72 self.oParent = oParent;
73 self.sName = hsAttrs['name'];
74 self.sStartTS = hsAttrs['timestamp'];
75 self.sEndTS = None;
76 self.sStatus = None;
77 self.cErrors = -1;
78 self.sStatusDiff = None;
79 self.cErrorsDiff = None;
80
81 # parsing
82
83 def addChild(self, oChild):
84 self.aoChildren.append(oChild);
85 return oChild;
86
87 def addValue(self, hsAttrs):
88 oValue = hsAttrs['value'];
89 self.aoValues.append(oValue);
90 return oValue;
91
92 def markPassed(self, hsAttrs):
93 try: self.sEndTS = hsAttrs['timestamp'];
94 except: pass;
95 self.sStatus = 'passed';
96 self.cErrors = 0;
97
98 def markSkipped(self, hsAttrs):
99 try: self.sEndTS = hsAttrs['timestamp'];
100 except: pass;
101 self.sStatus = 'skipped';
102 self.cErrors = 0;
103
104 def markFailed(self, hsAttrs):
105 try: self.sEndTS = hsAttrs['timestamp'];
106 except: pass;
107 self.sStatus = 'failed';
108 self.cErrors = int(hsAttrs['errors']);
109
110 def markEnd(self, hsAttrs):
111 try: self.sEndTS = hsAttrs['timestamp'];
112 except: pass;
113 if self.sStatus is None:
114 self.sStatus = 'end';
115 self.cErrors = 0;
116
117 def mergeInIncludedTest(self, oTest):
118 """ oTest will be robbed. """
119 if oTest is not None:
120 for oChild in oTest.aoChildren:
121 oChild.oParent = self;
122 self.aoChildren.append(oChild);
123 for oValue in oTest.aoValues:
124 oValue.oTest = self;
125 self.aoValues.append(oValue);
126 oTest.aoChildren = [];
127 oTest.aoValues = [];
128
129 # debug
130
131 def printTree(self, iLevel = 0):
132 print('%sTest: name=%s start=%s end=%s' % (''.ljust(iLevel*2), self.sName, self.sStartTS, self.sEndTS));
133 for oChild in self.aoChildren:
134 oChild.printTree(iLevel + 1);
135 for oValue in self.aoValues:
136 oValue.printValue(iLevel + 1);
137
138 # getters / queries
139
140 def getFullNameWorker(self, cSkipUpper):
141 if self.oParent is None:
142 return (self.sName, 0);
143 sName, iLevel = self.oParent.getFullNameWorker(cSkipUpper);
144 if iLevel < cSkipUpper:
145 sName = self.sName;
146 else:
147 sName += ', ' + self.sName;
148 return (sName, iLevel + 1);
149
150 def getFullName(self, cSkipUpper = 2):
151 return self.getFullNameWorker(cSkipUpper)[0];
152
153 def matchFilters(self, asFilters):
154 """
155 Checks if the all of the specified filter strings are substrings
156 of the full test name. Returns True / False.
157 """
158 sName = self.getFullName();
159 for sFilter in asFilters:
160 if sName.find(sFilter) < 0:
161 return False;
162 return True;
163
164 # manipulation
165
166 def filterTestsWorker(self, asFilters):
167 # depth first
168 i = 0;
169 while i < len(self.aoChildren):
170 if self.aoChildren[i].filterTestsWorker(asFilters):
171 i += 1;
172 else:
173 self.aoChildren[i].oParent = None;
174 del self.aoChildren[i];
175
176 # If we have children, they must've matched up.
177 if self.aoChildren:
178 return True;
179 return self.matchFilters(asFilters);
180
181 def filterTests(self, asFilters):
182 if asFilters:
183 self.filterTestsWorker(asFilters)
184 return self;
185
186
187class XmlLogReader(object):
188 """
189 XML log reader class.
190 """
191
192 def __init__(self, sXmlFile):
193 self.sXmlFile = sXmlFile;
194 self.oRoot = Test(None, {'name': 'root', 'timestamp': ''});
195 self.oTest = self.oRoot;
196 self.iLevel = 0;
197 self.oValue = None;
198
199 def parse(self):
200 try:
201 oFile = open(self.sXmlFile, 'r');
202 except:
203 traceback.print_exc();
204 return False;
205
206 from xml.parsers.expat import ParserCreate
207 oParser = ParserCreate();
208 oParser.StartElementHandler = self.handleElementStart;
209 oParser.CharacterDataHandler = self.handleElementData;
210 oParser.EndElementHandler = self.handleElementEnd;
211 try:
212 oParser.ParseFile(oFile);
213 except:
214 traceback.print_exc();
215 oFile.close();
216 return False;
217 oFile.close();
218 return True;
219
220 def handleElementStart(self, sName, hsAttrs):
221 #print '%s%s: %s' % (''.ljust(self.iLevel * 2), sName, str(hsAttrs));
222 if sName in ('Test', 'SubTest',):
223 self.iLevel += 1;
224 self.oTest = self.oTest.addChild(Test(self.oTest, hsAttrs));
225 elif sName == 'Value':
226 self.oValue = self.oTest.addValue(hsAttrs);
227 elif sName == 'End':
228 self.oTest.markEnd(hsAttrs);
229 elif sName == 'Passed':
230 self.oTest.markPassed(hsAttrs);
231 elif sName == 'Skipped':
232 self.oTest.markSkipped(hsAttrs);
233 elif sName == 'Failed':
234 self.oTest.markFailed(hsAttrs);
235 elif sName == 'Include':
236 self.handleInclude(hsAttrs);
237 else:
238 print('Unknonwn element "%s"' % (sName,));
239
240 def handleElementData(self, sData):
241 if self.oValue is not None:
242 self.oValue.addData(sData);
243 elif sData.strip() != '':
244 print('Unexpected data "%s"' % (sData,));
245 return True;
246
247 def handleElementEnd(self, sName):
248 if sName in ('Test', 'Subtest',):
249 self.iLevel -= 1;
250 self.oTest = self.oTest.oParent;
251 elif sName == 'Value':
252 self.oValue = None;
253 return True;
254
255 def handleInclude(self, hsAttrs):
256 # relative or absolute path.
257 sXmlFile = hsAttrs['filename'];
258 if not os.path.isabs(sXmlFile):
259 sXmlFile = os.path.join(os.path.dirname(self.sXmlFile), sXmlFile);
260
261 # Try parse it.
262 oSub = parseTestResult(sXmlFile);
263 if oSub is None:
264 print('error: failed to parse include "%s"' % (sXmlFile,));
265 else:
266 # Skip the root and the next level before merging it the subtest and
267 # values in to the current test. The reason for this is that the
268 # include is the output of some sub-program we've run and we don't need
269 # the extra test level it automatically adds.
270 #
271 # More benchmark heuristics: Walk down until we find more than one
272 # test or values.
273 oSub2 = oSub;
274 while len(oSub2.aoChildren) == 1 and not oSub2.aoValues:
275 oSub2 = oSub2.aoChildren[0];
276 if not oSub2.aoValues:
277 oSub2 = oSub;
278 self.oTest.mergeInIncludedTest(oSub2);
279 return True;
280
281def parseTestResult(sXmlFile):
282 """
283 Parses the test results in the XML.
284 Returns result tree.
285 Returns None on failure.
286 """
287 oXlr = XmlLogReader(sXmlFile);
288 if oXlr.parse():
289 return oXlr.oRoot;
290 return None;
291
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette