diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/ValidationKit/testanalysis | |
parent | Initial commit. (diff) | |
download | virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/ValidationKit/testanalysis')
-rw-r--r-- | src/VBox/ValidationKit/testanalysis/Makefile.kmk | 35 | ||||
-rw-r--r-- | src/VBox/ValidationKit/testanalysis/__init__.py | 31 | ||||
-rwxr-xr-x | src/VBox/ValidationKit/testanalysis/diff.py | 95 | ||||
-rwxr-xr-x | src/VBox/ValidationKit/testanalysis/reader.py | 291 | ||||
-rwxr-xr-x | src/VBox/ValidationKit/testanalysis/reporting.py | 302 | ||||
-rwxr-xr-x | src/VBox/ValidationKit/testanalysis/tst-a1.py | 100 |
6 files changed, 854 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/testanalysis/Makefile.kmk b/src/VBox/ValidationKit/testanalysis/Makefile.kmk new file mode 100644 index 00000000..3cdc1c86 --- /dev/null +++ b/src/VBox/ValidationKit/testanalysis/Makefile.kmk @@ -0,0 +1,35 @@ +# $Id: Makefile.kmk $ +## @file +# VirtualBox Validation Kit - Python Test Driver. +# + +# +# Copyright (C) 2010-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL) only, as it comes in the "COPYING.CDDL" file of the +# VirtualBox OSE distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + + +VBOX_VALIDATIONKIT_PYTHON_SOURCES += $(wildcard $(PATH_SUB_CURRENT)/*.py) + +$(evalcall def_vbox_validationkit_process_python_sources) +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/ValidationKit/testanalysis/__init__.py b/src/VBox/ValidationKit/testanalysis/__init__.py new file mode 100644 index 00000000..650cba1d --- /dev/null +++ b/src/VBox/ValidationKit/testanalysis/__init__.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# $Id: __init__.py $ + +""" +Test analysis package +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2019 Oracle Corporation + +This file is part of VirtualBox Open Source Edition (OSE), as +available from http://www.virtualbox.org. This file is free software; +you can redistribute it and/or modify it under the terms of the GNU +General Public License (GPL) as published by the Free Software +Foundation, in version 2 as it comes in the "COPYING" file of the +VirtualBox OSE distribution. VirtualBox OSE is distributed in the +hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL) only, as it comes in the "COPYING.CDDL" file of the +VirtualBox OSE distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. +""" +__version__ = "$Revision: 127855 $" +__all__ = ["reader", "diff", "reporting"] + diff --git a/src/VBox/ValidationKit/testanalysis/diff.py b/src/VBox/ValidationKit/testanalysis/diff.py new file mode 100755 index 00000000..f75897d0 --- /dev/null +++ b/src/VBox/ValidationKit/testanalysis/diff.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# $Id: diff.py $ + +""" +Diff two test sets. +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2019 Oracle Corporation + +This file is part of VirtualBox Open Source Edition (OSE), as +available from http://www.virtualbox.org. This file is free software; +you can redistribute it and/or modify it under the terms of the GNU +General Public License (GPL) as published by the Free Software +Foundation, in version 2 as it comes in the "COPYING" file of the +VirtualBox OSE distribution. VirtualBox OSE is distributed in the +hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL) only, as it comes in the "COPYING.CDDL" file of the +VirtualBox OSE distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. +""" +__version__ = "$Revision: 127855 $" +__all__ = ['BaselineDiff', ]; + + +def _findBaselineTest(oBaseline, oTest): + """ Recursively finds the test in oBaseline corresponding to oTest. """ + if oTest.oParent is None: + return oBaseline; + oBaseline = _findBaselineTest(oBaseline, oTest.oParent); + if oBaseline is not None: + for oBaseTest in oBaseline.aoChildren: + if oBaseTest.sName == oTest.sName: + return oBaseTest; + return None; + +def _findBaselineTestValue(oBaseline, oValue): + """ Finds the baseline value corresponding to oValue. """ + oBaseTest = _findBaselineTest(oBaseline, oValue.oTest); + if oBaseTest is not None: + for oBaseValue in oBaseTest.aoValues: + if oBaseValue.sName == oValue.sName: + return oBaseValue; + return None; + +def baselineDiff(oTestTree, oBaseline): + """ + Diffs oTestTree against oBaseline, adding diff info to oTestTree. + Returns oTestTree on success, None on failure (complained already). + """ + + aoStack = []; + aoStack.append((oTestTree, 0)); + while len(aoStack) > 0: + oCurTest, iChild = aoStack.pop(); + + # depth first + if iChild < len(oCurTest.aoChildren): + aoStack.append((oCurTest, iChild + 1)); + aoStack.append((oCurTest.aoChildren[iChild], 0)); + continue; + + # do value diff. + for i in range(len(oCurTest.aoValues)): + oBaseVal = _findBaselineTestValue(oBaseline, oCurTest.aoValues[i]); + if oBaseVal is not None: + try: + lBase = long(oBaseVal.sValue); + lTest = long(oCurTest.aoValues[i].sValue); + except: + try: + if oBaseVal.sValue == oCurTest.aoValues[i].sValue: + oCurTest.aoValues[i].sValue += '|'; + else: + oCurTest.aoValues[i].sValue += '|%s' % (oBaseVal.sValue); + except: + oCurTest.aoValues[i].sValue += '|%s' % (oBaseVal.sValue); + else: + if lTest > lBase: + oCurTest.aoValues[i].sValue += '|+%u' % (lTest - lBase); + elif lTest < lBase: + oCurTest.aoValues[i].sValue += '|-%u' % (lBase - lTest); + else: + oCurTest.aoValues[i].sValue += '|0'; + else: + oCurTest.aoValues[i].sValue += '|N/A'; + + return oTestTree; diff --git a/src/VBox/ValidationKit/testanalysis/reader.py b/src/VBox/ValidationKit/testanalysis/reader.py new file mode 100755 index 00000000..f46b3924 --- /dev/null +++ b/src/VBox/ValidationKit/testanalysis/reader.py @@ -0,0 +1,291 @@ +# -*- coding: utf-8 -*- +# $Id: reader.py $ + +""" +XML reader module. + +This produces a test result tree that can be processed and passed to +reporting. +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2019 Oracle Corporation + +This file is part of VirtualBox Open Source Edition (OSE), as +available from http://www.virtualbox.org. This file is free software; +you can redistribute it and/or modify it under the terms of the GNU +General Public License (GPL) as published by the Free Software +Foundation, in version 2 as it comes in the "COPYING" file of the +VirtualBox OSE distribution. VirtualBox OSE is distributed in the +hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL) only, as it comes in the "COPYING.CDDL" file of the +VirtualBox OSE distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. +""" +__version__ = "$Revision: 127855 $" +__all__ = ['ParseTestResult', ] + +# Standard python imports. +import os +import traceback + +# pylint: disable=C0111 + +class Value(object): + """ + Represents a value. Usually this is benchmark result or parameter. + """ + def __init__(self, oTest, hsAttrs): + self.oTest = oTest; + self.sName = hsAttrs['name']; + self.sUnit = hsAttrs['unit']; + self.sTimestamp = hsAttrs['timestamp']; + self.sValue = ''; + self.sDiff = None; + + # parsing + + def addData(self, sData): + self.sValue += sData; + + # debug + + def printValue(self, cIndent): + print '%sValue: name=%s timestamp=%s unit=%s value="%s"' \ + % (''.ljust(cIndent*2), self.sName, self.sTimestamp, self.sUnit, self.sValue); + + +class Test(object): + """ + Nested test result. + """ + def __init__(self, oParent, hsAttrs): + self.aoChildren = []; + self.aoValues = []; + self.oParent = oParent; + self.sName = hsAttrs['name']; + self.sStartTS = hsAttrs['timestamp']; + self.sEndTS = None; + self.sStatus = None; + self.cErrors = -1; + self.sStatusDiff = None; + self.cErrorsDiff = None; + + # parsing + + def addChild(self, oChild): + self.aoChildren.append(oChild); + return oChild; + + def addValue(self, hsAttrs): + oValue = hsAttrs['value']; + self.aoValues.append(oValue); + return oValue; + + def markPassed(self, hsAttrs): + try: self.sEndTS = hsAttrs['timestamp']; + except: pass; + self.sStatus = 'passed'; + self.cErrors = 0; + + def markSkipped(self, hsAttrs): + try: self.sEndTS = hsAttrs['timestamp']; + except: pass; + self.sStatus = 'skipped'; + self.cErrors = 0; + + def markFailed(self, hsAttrs): + try: self.sEndTS = hsAttrs['timestamp']; + except: pass; + self.sStatus = 'failed'; + self.cErrors = int(hsAttrs['errors']); + + def markEnd(self, hsAttrs): + try: self.sEndTS = hsAttrs['timestamp']; + except: pass; + if self.sStatus is None: + self.sStatus = 'end'; + self.cErrors = 0; + + def mergeInIncludedTest(self, oTest): + """ oTest will be robbed. """ + if oTest is not None: + for oChild in oTest.aoChildren: + oChild.oParent = self; + self.aoChildren.append(oChild); + for oValue in oTest.aoValues: + oValue.oTest = self; + self.aoValues.append(oValue); + oTest.aoChildren = []; + oTest.aoValues = []; + + # debug + + def printTree(self, iLevel = 0): + print '%sTest: name=%s start=%s end=%s' % (''.ljust(iLevel*2), self.sName, self.sStartTS, self.sEndTS); + for oChild in self.aoChildren: + oChild.printTree(iLevel + 1); + for oValue in self.aoValues: + oValue.printValue(iLevel + 1); + + # getters / queries + + def getFullNameWorker(self, cSkipUpper): + if self.oParent is None: + return (self.sName, 0); + sName, iLevel = self.oParent.getFullNameWorker(cSkipUpper); + if iLevel < cSkipUpper: + sName = self.sName; + else: + sName += ', ' + self.sName; + return (sName, iLevel + 1); + + def getFullName(self, cSkipUpper = 2): + return self.getFullNameWorker(cSkipUpper)[0]; + + def matchFilters(self, asFilters): + """ + Checks if the all of the specified filter strings are substrings + of the full test name. Returns True / False. + """ + sName = self.getFullName(); + for sFilter in asFilters: + if sName.find(sFilter) < 0: + return False; + return True; + + # manipulation + + def filterTestsWorker(self, asFilters): + # depth first + i = 0; + while i < len(self.aoChildren): + if self.aoChildren[i].filterTestsWorker(asFilters): + i += 1; + else: + self.aoChildren[i].oParent = None; + del self.aoChildren[i]; + + # If we have children, they must've matched up. + if len(self.aoChildren) != 0: + return True; + return self.matchFilters(asFilters); + + def filterTests(self, asFilters): + if len(asFilters) > 0: + self.filterTestsWorker(asFilters) + return self; + + +class XmlLogReader(object): + """ + XML log reader class. + """ + + def __init__(self, sXmlFile): + self.sXmlFile = sXmlFile; + self.oRoot = Test(None, {'name': 'root', 'timestamp': ''}); + self.oTest = self.oRoot; + self.iLevel = 0; + self.oValue = None; + + def parse(self): + try: + oFile = open(self.sXmlFile, 'r'); + except: + traceback.print_exc(); + return False; + + from xml.parsers.expat import ParserCreate + oParser = ParserCreate(); + oParser.StartElementHandler = self.handleElementStart; + oParser.CharacterDataHandler = self.handleElementData; + oParser.EndElementHandler = self.handleElementEnd; + try: + oParser.ParseFile(oFile); + except: + traceback.print_exc(); + oFile.close(); + return False; + oFile.close(); + return True; + + def handleElementStart(self, sName, hsAttrs): + #print '%s%s: %s' % (''.ljust(self.iLevel * 2), sName, str(hsAttrs)); + if sName == 'Test' or sName == 'SubTest': + self.iLevel += 1; + self.oTest = self.oTest.addChild(Test(self.oTest, hsAttrs)); + elif sName == 'Value': + self.oValue = self.oTest.addValue(hsAttrs); + elif sName == 'End': + self.oTest.markEnd(hsAttrs); + elif sName == 'Passed': + self.oTest.markPassed(hsAttrs); + elif sName == 'Skipped': + self.oTest.markSkipped(hsAttrs); + elif sName == 'Failed': + self.oTest.markFailed(hsAttrs); + elif sName == 'Include': + self.handleInclude(hsAttrs); + else: + print 'Unknonwn element "%s"' % (sName); + + def handleElementData(self, sData): + if self.oValue is not None: + self.oValue.addData(sData); + elif sData.strip() != '': + print 'Unexpected data "%s"' % (sData); + return True; + + def handleElementEnd(self, sName): + if sName == 'Test' or sName == 'Subtest': + self.iLevel -= 1; + self.oTest = self.oTest.oParent; + elif sName == 'Value': + self.oValue = None; + return True; + + def handleInclude(self, hsAttrs): + # relative or absolute path. + sXmlFile = hsAttrs['filename']; + if not os.path.isabs(sXmlFile): + sXmlFile = os.path.join(os.path.dirname(self.sXmlFile), sXmlFile); + + # Try parse it. + oSub = parseTestResult(sXmlFile); + if oSub is None: + print 'error: failed to parse include "%s"' % (sXmlFile); + else: + # Skip the root and the next level before merging it the subtest and + # values in to the current test. The reason for this is that the + # include is the output of some sub-program we've run and we don't need + # the extra test level it automatically adds. + # + # More benchmark heuristics: Walk down until we find more than one + # test or values. + oSub2 = oSub; + while len(oSub2.aoChildren) == 1 and len(oSub2.aoValues) == 0: + oSub2 = oSub2.aoChildren[0]; + if len(oSub2.aoValues) == 0: + oSub2 = oSub; + self.oTest.mergeInIncludedTest(oSub2); + return True; + +def parseTestResult(sXmlFile): + """ + Parses the test results in the XML. + Returns result tree. + Returns None on failure. + """ + oXlr = XmlLogReader(sXmlFile); + if oXlr.parse(): + return oXlr.oRoot; + return None; + diff --git a/src/VBox/ValidationKit/testanalysis/reporting.py b/src/VBox/ValidationKit/testanalysis/reporting.py new file mode 100755 index 00000000..3ff334ce --- /dev/null +++ b/src/VBox/ValidationKit/testanalysis/reporting.py @@ -0,0 +1,302 @@ +# -*- coding: utf-8 -*- +# $Id: reporting.py $ + +""" +Test Result Report Writer. + +This takes a processed test result tree and creates a HTML, re-structured text, +or normal text report from it. +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2019 Oracle Corporation + +This file is part of VirtualBox Open Source Edition (OSE), as +available from http://www.virtualbox.org. This file is free software; +you can redistribute it and/or modify it under the terms of the GNU +General Public License (GPL) as published by the Free Software +Foundation, in version 2 as it comes in the "COPYING" file of the +VirtualBox OSE distribution. VirtualBox OSE is distributed in the +hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL) only, as it comes in the "COPYING.CDDL" file of the +VirtualBox OSE distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. +""" +__version__ = "$Revision: 127855 $" +__all__ = ['HtmlReport', 'RstReport', 'TextReport']; + + +def tryAddThousandSeparators(sPotentialInterger): + """ Apparently, python 3.0(/3.1) has(/will have) support for this...""" + # Try convert the string/value to a long. + try: + lVal = long(sPotentialInterger); + lVal = long(sPotentialInterger); + except: + return sPotentialInterger; + + # Convert it back to a string (paranoia) and build up the new string. + sOld = str(lVal); + chSign = ''; + if sOld[0] == '-': + chSign = '-'; + sOld = sOld[1:]; + elif sPotentialInterger[0] == '+': + chSign = '+'; + cchDigits = len(sOld); + iDigit = 0 + sNewVal = ''; + while iDigit < cchDigits: + if (iDigit % 3) == 0 and iDigit > 0: + sNewVal = ' ' + sNewVal; + sNewVal = sOld[cchDigits - iDigit - 1] + sNewVal; + iDigit += 1; + return chSign + sNewVal; + + +class Table(object): + """ + A table as a header as well as data rows, thus this class. + """ + def __init__(self, oTest, fSplitDiff): + self.aasRows = []; + self.asHeader = ['Test',]; + self.asUnits = ['',]; + for oValue in oTest.aoValues: + self.asHeader.append(oValue.sName); + self.asUnits.append(oValue.sUnit); + self.addRow(oTest, fSplitDiff); + + def addRow(self, oTest, fSplitDiff): + """Adds a row.""" + asRow = [oTest.getFullName(),]; + for oValue in oTest.aoValues: + asRow.append(oValue.sValue); + if not fSplitDiff: + self.aasRows.append(asRow); + else: + # Split cells into multiple rows on '|'. Omit the first column. + iRow = 0; + asThisRow = [asRow[0], ]; + fMoreTodo = True; + while fMoreTodo: + for i in range(1, len(asRow)): + asSplit = asRow[i].split('|'); + asThisRow.append(asSplit[0]); + asRow[i] = '|'.join(asSplit[1:]) + self.aasRows.append(asThisRow); + + # Done? + fMoreTodo = False; + for i in range(1, len(asRow)): + if len(asRow[i]): + fMoreTodo = True; + asThisRow = ['', ]; + iRow += 1; + + # Readability hack: Add an extra row if there are diffs. + if iRow > 1: + asRow[0] = ''; + self.aasRows.append(asRow); + + return True; + + def hasTheSameHeadingAsTest(self, oTest): + """ Checks if the test values has the same heading.""" + i = 1; + for oValue in oTest.aoValues: + if self.asHeader[i] != oValue.sName: + return False; + if self.asUnits[i] != oValue.sUnit: + return False; + i += 1; + return True; + + def hasTheSameHeadingAsTable(self, oTable): + """ Checks if the other table has the same heading.""" + if len(oTable.asHeader) != len(self.asHeader): + return False; + for i in range(len(self.asHeader)): + if self.asHeader[i] != oTable.asHeader[i]: + return False; + if self.asUnits[i] != oTable.asUnits[i]: + return False; + return True; + + def appendTable(self, oTable): + """ Append the rows in oTable. oTable has the same heading as us. """ + self.aasRows.extend(oTable.aasRows); + return True; + + # manipulation and stuff + + def optimizeUnit(self): + """ Turns bytes into KB, MB or GB. """ + ## @todo + pass; + + def addThousandSeparators(self): + """ Adds thousand separators to make numbers more readable. """ + for iRow in range(len(self.aasRows)): + for iColumn in range(1, len(self.aasRows[iRow])): + asValues = self.aasRows[iRow][iColumn].split('|'); + for i in range(len(asValues)): + asValues[i] = tryAddThousandSeparators(asValues[i]); + self.aasRows[iRow][iColumn] = '|'.join(asValues); + return True; + + def getRowWidths(self): + """Figure out the column withs.""" + # Header is first. + acchColumns = []; + for i in range(len(self.asHeader)): + cch = 1; + asWords = self.asHeader[i].split(); + for s in asWords: + if len(s) > cch: + cch = len(s); + if i > 0 and len(self.asUnits[i]) > cch: + cch = len(self.asUnits[i]); + acchColumns.append(cch); + + # Check out all cells. + for asColumns in self.aasRows: + for i in range(len(asColumns)): + if len(asColumns[i]) > acchColumns[i]: + acchColumns[i] = len(asColumns[i]); + return acchColumns; + + +def tabelizeTestResults(oTest, fSplitDiff): + """ + Break the test results down into a list of tables containing the values. + + TODO: Handle passed / failed stuff too. Not important for benchmarks. + """ + # Pass 1 + aoTables = []; + aoStack = []; + aoStack.append((oTest, 0)); + while len(aoStack) > 0: + oCurTest, iChild = aoStack.pop(); + + # depth first + if iChild < len(oCurTest.aoChildren): + aoStack.append((oCurTest, iChild + 1)); + aoStack.append((oCurTest.aoChildren[iChild], 0)); + continue; + + # values -> row + if len(oCurTest.aoValues) > 0: + if len(aoTables) > 0 and aoTables[len(aoTables) - 1].hasTheSameHeadingAsTest(oCurTest): + aoTables[len(aoTables) - 1].addRow(oCurTest, fSplitDiff); + else: + aoTables.append(Table(oCurTest, fSplitDiff)); + + # Pass 2 - Combine tables with the same heading. + aoTables2 = []; + for oTable in aoTables: + for i in range(len(aoTables2)): + if aoTables2[i].hasTheSameHeadingAsTable(oTable): + aoTables2[i].appendTable(oTable); + oTable = None; + break; + if oTable is not None: + aoTables2.append(oTable); + + return aoTables2; + +def produceHtmlReport(oTest): + """ + Produce an HTML report on stdout (via print). + """ + print 'not implemented: %s' % (oTest); + return False; + +def produceReStructuredTextReport(oTest): + """ + Produce a ReStructured text report on stdout (via print). + """ + print 'not implemented: %s' % (oTest); + return False; + +def produceTextReport(oTest): + """ + Produce a text report on stdout (via print). + """ + + # + # Report header. + # + ## @todo later + + # + # Tabelize the results and display the tables. + # + aoTables = tabelizeTestResults(oTest, True) + for oTable in aoTables: + ## @todo do max/min on the columns where we can do [GMK]B(/s). + oTable.addThousandSeparators(); + acchColumns = oTable.getRowWidths(); + + # The header. + # This is a bit tedious and the solution isn't entirely elegant due + # to the pick-it-up-as-you-go-along python skills. + aasHeader = []; + aasHeader.append([]); + for i in range(len(oTable.asHeader)): + aasHeader[0].append(''); + + for iColumn in range(len(oTable.asHeader)): + asWords = oTable.asHeader[iColumn].split(); + iLine = 0; + for s in asWords: + if len(aasHeader[iLine][iColumn]) <= 0: + aasHeader[iLine][iColumn] = s; + elif len(s) + 1 + len(aasHeader[iLine][iColumn]) <= acchColumns[iColumn]: + aasHeader[iLine][iColumn] += ' ' + s; + else: + iLine += 1; + if iLine >= len(aasHeader): # There must be a better way to do this... + aasHeader.append([]); + for i in range(len(oTable.asHeader)): + aasHeader[iLine].append(''); + aasHeader[iLine][iColumn] = s; + + for asLine in aasHeader: + sLine = ''; + for i in range(len(asLine)): + if i > 0: sLine += ' '; + sLine += asLine[i].center(acchColumns[i]); + print sLine; + + # Units. + sLine = ''; + for i in range(len(oTable.asUnits)): + if i > 0: sLine += ' '; + sLine += oTable.asUnits[i].center(acchColumns[i]); + print sLine; + + # Separator line. + sLine = ''; + for i in range(len(oTable.asHeader)): + if i > 0: sLine += ' ' + sLine += '=' * acchColumns[i]; + print sLine; + + # The rows. + for asColumns in oTable.aasRows: + sText = asColumns[0].ljust(acchColumns[0]); + for i in range(1, len(asColumns)): + sText += ' ' + asColumns[i].rjust(acchColumns[i]); + print sText; + + return None; + diff --git a/src/VBox/ValidationKit/testanalysis/tst-a1.py b/src/VBox/ValidationKit/testanalysis/tst-a1.py new file mode 100755 index 00000000..47b2a565 --- /dev/null +++ b/src/VBox/ValidationKit/testanalysis/tst-a1.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# $Id: tst-a1.py $ + +""" +Analyzer Experiment 1. +""" + +__copyright__ = \ +""" +Copyright (C) 2010-2019 Oracle Corporation + +This file is part of VirtualBox Open Source Edition (OSE), as +available from http://www.virtualbox.org. This file is free software; +you can redistribute it and/or modify it under the terms of the GNU +General Public License (GPL) as published by the Free Software +Foundation, in version 2 as it comes in the "COPYING" file of the +VirtualBox OSE distribution. VirtualBox OSE is distributed in the +hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + +The contents of this file may alternatively be used under the terms +of the Common Development and Distribution License Version 1.0 +(CDDL) only, as it comes in the "COPYING.CDDL" file of the +VirtualBox OSE distribution, in which case the provisions of the +CDDL are applicable instead of those of the GPL. + +You may elect to license modified versions of this file under the +terms and conditions of either the GPL or the CDDL or both. +""" +__version__ = "$Revision: 127855 $" + + +import os.path +import sys + +# Only the main script needs to modify the path. +try: __file__ +except: __file__ = sys.argv[0]; +g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))); +sys.path.append(g_ksValidationKitDir); + +# Validation Kit imports. +from testanalysis import reader ## @todo fix testanalysis/__init__.py. +from testanalysis import reporting +from testanalysis import diff + + +def usage(): + """ Display usage """ + print 'usage: %s [options] <testresults.xml> [baseline.xml]' % (sys.argv[0]); + print '' + print 'options:' + print ' --filter <test-sub-string>' + return 1; + +def main(asArgs): + """ C styl main(). """ + # Parse arguments + sTestFile = None; + sBaseFile = None; + asFilters = []; + iArg = 1; + while iArg < len(asArgs): + if asArgs[iArg] == '--filter': + iArg += 1; + asFilters.append(asArgs[iArg]); + elif asArgs[iArg].startswith('--help'): + return usage(); + elif asArgs[iArg].startswith('--'): + print 'syntax error: unknown option "%s"' % (asArgs[iArg]); + return usage(); + elif sTestFile is None: + sTestFile = asArgs[iArg]; + elif sBaseFile is None: + sBaseFile = asArgs[iArg]; + else: + print 'syntax error: too many file names: %s' % (asArgs[iArg]) + return usage(); + iArg += 1; + + # Down to business + oTestTree = reader.parseTestResult(sTestFile); + if oTestTree is None: + return 1; + oTestTree = oTestTree.filterTests(asFilters) + + if sBaseFile is not None: + oBaseline = reader.parseTestResult(sBaseFile); + if oBaseline is None: + return 1; + oTestTree = diff.baselineDiff(oTestTree, oBaseline); + if oTestTree is None: + return 1; + + reporting.produceTextReport(oTestTree); + return 0; + +if __name__ == '__main__': + sys.exit(main(sys.argv)); + |