summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit/testanalysis/reporting.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/ValidationKit/testanalysis/reporting.py
parentInitial commit. (diff)
downloadvirtualbox-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/reporting.py')
-rwxr-xr-xsrc/VBox/ValidationKit/testanalysis/reporting.py302
1 files changed, 302 insertions, 0 deletions
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;
+