Index: /trunk/src/VBox/ValidationKit/testmanager/core/testbox.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testbox.py	(revision 65083)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testbox.py	(revision 65084)
@@ -339,33 +339,53 @@
         return dErrors;
 
-    def formatPythonVersion(self):
-        """
-        Unbuttons the version number and formats it as a version string.
-        """
-        if self.iPythonHexVersion is None:
+    @staticmethod
+    def formatPythonVersionEx(iPythonHexVersion):
+        """ Unbuttons the version number and formats it as a version string. """
+        if iPythonHexVersion is None:
             return 'N/A';
         return 'v%d.%d.%d.%d' \
-            % (  self.iPythonHexVersion >> 24,
-                (self.iPythonHexVersion >> 16) & 0xff,
-                (self.iPythonHexVersion >>  8) & 0xff,
-                 self.iPythonHexVersion        & 0xff);
+            % (  iPythonHexVersion >> 24,
+                (iPythonHexVersion >> 16) & 0xff,
+                (iPythonHexVersion >>  8) & 0xff,
+                 iPythonHexVersion        & 0xff);
+
+    def formatPythonVersion(self):
+        """ Unbuttons the version number and formats it as a version string. """
+        return self.formatPythonVersionEx(self.iPythonHexVersion);
+
+
+    @staticmethod
+    def getCpuFamilyEx(lCpuRevision):
+        """ Returns the CPU family for a x86 or amd64 testboxes."""
+        if lCpuRevision is None:
+            return 0;
+        return (lCpuRevision >> 24 & 0xff);
 
     def getCpuFamily(self):
         """ Returns the CPU family for a x86 or amd64 testboxes."""
-        if self.lCpuRevision is None:
+        return self.getCpuFamilyEx(self.lCpuRevision);
+
+    @staticmethod
+    def getCpuModelEx(lCpuRevision):
+        """ Returns the CPU model for a x86 or amd64 testboxes."""
+        if lCpuRevision is None:
             return 0;
-        return (self.lCpuRevision >> 24 & 0xff);
+        return (lCpuRevision >> 8 & 0xffff);
 
     def getCpuModel(self):
         """ Returns the CPU model for a x86 or amd64 testboxes."""
-        if self.lCpuRevision is None:
+        return self.getCpuModelEx(self.lCpuRevision);
+
+    @staticmethod
+    def getCpuSteppingEx(lCpuRevision):
+        """ Returns the CPU stepping for a x86 or amd64 testboxes."""
+        if lCpuRevision is None:
             return 0;
-        return (self.lCpuRevision >> 8 & 0xffff);
+        return (lCpuRevision & 0xff);
 
     def getCpuStepping(self):
         """ Returns the CPU stepping for a x86 or amd64 testboxes."""
-        if self.lCpuRevision is None:
-            return 0;
-        return (self.lCpuRevision & 0xff);
+        return self.getCpuSteppingEx(self.lCpuRevision);
+
 
     # The following is a translation of the g_aenmIntelFamily06 array in CPUMR3CpuId.cpp:
@@ -436,16 +456,17 @@
     };
 
-    def queryCpuMicroarch(self):
+    @staticmethod
+    def queryCpuMicroarchEx(lCpuRevision, sCpuVendor):
         """ Try guess the microarch name for the cpu.  Returns None if we cannot. """
-        if self.lCpuRevision is None or self.sCpuVendor is None:
+        if lCpuRevision is None or sCpuVendor is None:
             return None;
-        uFam = self.getCpuFamily();
-        uMod = self.getCpuModel();
-        if self.sCpuVendor == 'GenuineIntel':
+        uFam = TestBoxData.getCpuFamilyEx(lCpuRevision);
+        uMod = TestBoxData.getCpuModelEx(lCpuRevision);
+        if sCpuVendor == 'GenuineIntel':
             if uFam == 6:
-                return self.kdIntelFamily06.get(uMod, None);
+                return TestBoxData.kdIntelFamily06.get(uMod, None);
             if uFam == 15:
-                return self.kdIntelFamily15.get(uMod, None);
-        elif self.sCpuVendor == 'AuthenticAMD':
+                return TestBoxData.kdIntelFamily15.get(uMod, None);
+        elif sCpuVendor == 'AuthenticAMD':
             if uFam == 0xf:
                 if uMod < 0x10:                             return 'K8_130nm';
@@ -464,5 +485,5 @@
             if uFam == 0x16:
                 return 'Jaguar';
-        elif self.sCpuVendor == 'CentaurHauls':
+        elif sCpuVendor == 'CentaurHauls':
             if uFam == 0x05:
                 if uMod == 0x01: return 'Centaur_C6';
@@ -473,19 +494,31 @@
                 if uMod == 0x05: return 'VIA_C3_M2';
                 if uMod == 0x06: return 'VIA_C3_C5A';
-                if uMod == 0x07: return 'VIA_C3_C5B' if self.getCpuStepping() < 8 else 'VIA_C3_C5C';
+                if uMod == 0x07: return 'VIA_C3_C5B' if TestBoxData.getCpuSteppingEx(lCpuRevision) < 8 else 'VIA_C3_C5C';
                 if uMod == 0x08: return 'VIA_C3_C5N';
-                if uMod == 0x09: return 'VIA_C3_C5XL' if self.getCpuStepping() < 8 else 'VIA_C3_C5P';
+                if uMod == 0x09: return 'VIA_C3_C5XL' if TestBoxData.getCpuSteppingEx(lCpuRevision) < 8 else 'VIA_C3_C5P';
                 if uMod == 0x0a: return 'VIA_C7_C5J';
                 if uMod == 0x0f: return 'VIA_Isaiah';
         return None;
 
+    def queryCpuMicroarch(self):
+        """ Try guess the microarch name for the cpu.  Returns None if we cannot. """
+        return self.queryCpuMicroarchEx(self.lCpuRevision, self.sCpuVendor);
+
+    @staticmethod
+    def getPrettyCpuVersionEx(lCpuRevision, sCpuVendor):
+        """ Pretty formatting of the family/model/stepping with microarch optimizations. """
+        if lCpuRevision is None or sCpuVendor is None:
+            return u'<none>';
+        sMarch = TestBoxData.queryCpuMicroarchEx(lCpuRevision, sCpuVendor);
+        if sMarch is not None:
+            return '%s m%02X s%02X' \
+                 % (sMarch, TestBoxData.getCpuModelEx(lCpuRevision), TestBoxData.getCpuSteppingEx(lCpuRevision));
+        return 'fam%02X m%02X s%02X' \
+             % ( TestBoxData.getCpuFamilyEx(lCpuRevision), TestBoxData.getCpuModelEx(lCpuRevision),
+                 TestBoxData.getCpuSteppingEx(lCpuRevision));
+
     def getPrettyCpuVersion(self):
         """ Pretty formatting of the family/model/stepping with microarch optimizations. """
-        if self.lCpuRevision is None or self.sCpuVendor is None:
-            return u'<none>';
-        sMarch = self.queryCpuMicroarch();
-        if sMarch is not None:
-            return '%s m%02X s%02X' % (sMarch, self.getCpuModel(), self.getCpuStepping());
-        return 'fam%02X m%02X s%02X' % (self.getCpuFamily(), self.getCpuModel(), self.getCpuStepping());
+        return self.getPrettyCpuVersionEx(self.lCpuRevision, self.sCpuVendor);
 
     def getArchBitString(self):
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py	(revision 65083)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py	(revision 65084)
@@ -646,16 +646,32 @@
     """
 
-    kiTestStatus        =  0;
-    kiBranches          =  1;
-    kiSchedGroups       =  2;
-    kiTestBoxes         =  3;
-    kiTestCases         =  4;
-    kiTestCaseVars      =  5;
-    kiRevisions         =  6;
-    kiCpuArches         =  7;
-    kiCpuVendors        =  8;
-    kiOses              =  9;
-    kiOsVersions        = 10;
-    kiFailReasons       = 11;
+    kiTestStatus            =  0;
+    kiBranches              =  1;
+    kiSchedGroups           =  2;
+    kiTestBoxes             =  3;
+    kiTestCases             =  4;
+    kiTestCaseVars          =  5;
+    kiRevisions             =  6;
+    kiCpuArches             =  7;
+    kiCpuVendors            =  8;
+    kiCpuRevisions          =  9;
+    kiCpuCounts             = 10;
+    kiMemory                = 11;
+    kiMisc                  = 12;
+    kiOses                  = 13;
+    kiOsVersions            = 14;
+    kiPythonVersions        = 15;
+    kiFailReasons           = 16;
+
+    kiMisc_NestedPaging     =  0;
+    kiMisc_NoNestedPaging   =  1;
+    kiMisc_RawMode          =  2;
+    kiMisc_NoRawMode        =  3;
+    kiMisc_64BitGuest       =  4;
+    kiMisc_No64BitGuest     =  5;
+    kiMisc_HwVirt           =  6;
+    kiMisc_NoHwVirt         =  7;
+    kiMisc_IoMmu            =  8;
+    kiMisc_NoIoMmu          =  9;
 
     def __init__(self):
@@ -699,4 +715,32 @@
         assert self.aCriteria[self.kiCpuVendors] is oCrit;
 
+        oCrit = FilterCriterion('CPU revisions', sVarNm = 'cr', sTable = 'TestBoxesWithStrings', sColumn = 'lCpuRevision');
+        self.aCriteria.append(oCrit);
+        assert self.aCriteria[self.kiCpuRevisions] is oCrit;
+
+        oCrit = FilterCriterion('CPU counts', sVarNm = 'cc', sTable = 'TestBoxesWithStrings', sColumn = 'cCpus');
+        self.aCriteria.append(oCrit);
+        assert self.aCriteria[self.kiCpuCounts] is oCrit;
+
+        oCrit = FilterCriterion('Memory', sVarNm = 'mb', sTable = 'TestBoxesWithStrings', sColumn = 'cMbMemory');
+        self.aCriteria.append(oCrit);
+        assert self.aCriteria[self.kiMemory] is oCrit;
+
+        oCrit = FilterCriterion('Misc', sVarNm = 'cf', sTable = 'TestBoxesWithStrings', sColumn = 'it_is_complicated');
+        oCrit.aoPossible = [
+            FilterCriterionValueAndDescription(self.kiMisc_NestedPaging,      "req nested paging"),
+            FilterCriterionValueAndDescription(self.kiMisc_NoNestedPaging,    "w/o nested paging"),
+            #FilterCriterionValueAndDescription(self.kiMisc_RawMode,           "req raw-mode"), - not implemented yet.
+            #FilterCriterionValueAndDescription(self.kiMisc_NoRawMode,         "w/o raw-mode"), - not implemented yet.
+            FilterCriterionValueAndDescription(self.kiMisc_64BitGuest,        "req 64-bit guests"),
+            FilterCriterionValueAndDescription(self.kiMisc_No64BitGuest,      "w/o 64-bit guests"),
+            FilterCriterionValueAndDescription(self.kiMisc_HwVirt,            "req VT-x / AMD-V"),
+            FilterCriterionValueAndDescription(self.kiMisc_NoHwVirt,          "w/o VT-x / AMD-V"),
+            #FilterCriterionValueAndDescription(self.kiMisc_IoMmu,             "req I/O MMU"), - not implemented yet.
+            #FilterCriterionValueAndDescription(self.kiMisc_NoIoMmu,           "w/o I/O MMU"), - not implemented yet.
+        ];
+        self.aCriteria.append(oCrit);
+        assert self.aCriteria[self.kiMisc] is oCrit;
+
         oCrit = FilterCriterion('OSes', sVarNm = 'os', sTable = 'TestBoxesWithStrings', sColumn = 'idStrOs');
         self.aCriteria.append(oCrit);
@@ -707,8 +751,24 @@
         assert self.aCriteria[self.kiOsVersions] is oCrit;
 
+        oCrit = FilterCriterion('Python', sVarNm = 'py', sTable = 'TestBoxesWithStrings', sColumn = 'iPythonHexVersion');
+        self.aCriteria.append(oCrit);
+        assert self.aCriteria[self.kiPythonVersions] is oCrit;
+
         oCrit = FilterCriterion('Failure reasons', sVarNm = 'fr', sTable = 'TestResultFailures', sColumn = 'idFailureReason');
         self.aCriteria.append(oCrit);
         assert self.aCriteria[self.kiFailReasons] is oCrit;
 
+    kdMiscConditions = {
+        kiMisc_NestedPaging:    'TestBoxesWithStrings.fCpuNestedPaging IS TRUE',
+        kiMisc_NoNestedPaging:  'TestBoxesWithStrings.fCpuNestedPaging IS FALSE',
+        kiMisc_RawMode:         'TestBoxesWithStrings.fRawMode IS TRUE',
+        kiMisc_NoRawMode:       'TestBoxesWithStrings.fRawMode IS NOT TRUE',
+        kiMisc_64BitGuest:      'TestBoxesWithStrings.fCpu64BitGuest IS TRUE',
+        kiMisc_No64BitGuest:    'TestBoxesWithStrings.fCpu64BitGuest IS FALSE',
+        kiMisc_HwVirt:          'TestBoxesWithStrings.fCpuHwVirt IS TRUE',
+        kiMisc_NoHwVirt:        'TestBoxesWithStrings.fCpuHwVirt IS FALSE',
+        kiMisc_IoMmu:           'TestBoxesWithStrings.fChipsetIoMmu IS TRUE',
+        kiMisc_NoIoMmu:         'TestBoxesWithStrings.fChipsetIoMmu IS FALSE',
+    };
 
     def getWhereConditions(self, sExtraIndent = '', iOmit = -1):
@@ -720,9 +780,17 @@
         for iCrit, oCrit in enumerate(self.aCriteria):
             if oCrit.sState == FilterCriterion.ksState_Selected and iCrit != iOmit:
-                sQuery += '%s   AND %s.%s IN (' % (sExtraIndent, oCrit.sTable, oCrit.sColumn,);
-                if oCrit.sType == FilterCriterion.ksType_String:
-                    sQuery += ', '.join('\'%s\'' % (sValue,) for sValue in oCrit.aoSelected) + ')\n';
+                if iCrit == self.kiMisc:
+                    for iValue in oCrit.aoSelected:
+                        if iValue in self.kdMiscConditions:
+                            sQuery += '%s   AND %s\n' % (sExtraIndent, self.kdMiscConditions[iValue],);
                 else:
-                    sQuery += ', '.join(str(iValue) for iValue in oCrit.aoSelected) + ')\n';
+                    if iCrit == self.kiMemory:
+                        sQuery += '%s   AND (%s.%s / 1024) IN (' % (sExtraIndent, oCrit.sTable, oCrit.sColumn,);
+                    else:
+                        sQuery += '%s   AND %s.%s IN (' % (sExtraIndent, oCrit.sTable, oCrit.sColumn,);
+                    if oCrit.sType == FilterCriterion.ksType_String:
+                        sQuery += ', '.join('\'%s\'' % (sValue,) for sValue in oCrit.aoSelected) + ')\n';
+                    else:
+                        sQuery += ', '.join(str(iValue) for iValue in oCrit.aoSelected) + ')\n';
         return sQuery;
 
@@ -1475,5 +1543,6 @@
                 if fIdIsName:
                     for idMissing in dLeft:
-                        oCrit.aoPossible.append(FilterCriterionValueAndDescription(idMissing, idMissing, fIrrelevant = True));
+                        oCrit.aoPossible.append(FilterCriterionValueAndDescription(idMissing, str(idMissing),
+                                                                                   fIrrelevant = True));
                 else:
                     oMissingLogic = oMissingLogicType(self._oDb);
@@ -1555,5 +1624,5 @@
                           'GROUP BY TestBoxesWithStrings.idStrOs, TestBoxesWithStrings.sOs\n'
                           'ORDER BY TestBoxesWithStrings.sOs\n' );
-        workerDoFetch(TestBoxLogic, 'sOs');
+        workerDoFetch(None, fIdIsName = True);
 
         # Testbox OS versions .
@@ -1575,5 +1644,5 @@
                           'GROUP BY TestBoxesWithStrings.idStrOsVersion, TestBoxesWithStrings.sOsVersion\n'
                           'ORDER BY TestBoxesWithStrings.sOsVersion\n' );
-        workerDoFetch(TestBoxLogic, 'sOsVersion');
+        workerDoFetch(None, fIdIsName = True);
 
         # Testbox CPU/OS architectures.
@@ -1595,5 +1664,5 @@
                           'GROUP BY TestBoxesWithStrings.idStrCpuArch, TestBoxesWithStrings.sCpuArch\n'
                           'ORDER BY TestBoxesWithStrings.sCpuArch\n' );
-        workerDoFetch(TestBoxLogic, 'sCpuArch');
+        workerDoFetch(None, fIdIsName = True);
 
         # Testbox CPU vendors.
@@ -1609,5 +1678,5 @@
                           oFilter.getWhereConditions(iOmit = TestResultFilter.kiCpuVendors) +
                           oReportModel.getExtraSubjectWhereExpr() +
-                          '         GROUP BY TestSets.idGEnTestBox'
+                          '         GROUP BY TestSets.idGenTestBox'
                           '       ) AS TestBoxGenIDs\n'
                           '       LEFT OUTER JOIN TestBoxesWithStrings\n'
@@ -1615,5 +1684,91 @@
                           'GROUP BY TestBoxesWithStrings.idStrCpuVendor, TestBoxesWithStrings.sCpuVendor\n'
                           'ORDER BY TestBoxesWithStrings.sCpuVendor\n' );
-        workerDoFetch(TestBoxLogic, 'sCpuVendor');
+        workerDoFetch(None, fIdIsName = True);
+
+        # Testbox CPU revisions.
+        oCrit = oFilter.aCriteria[TestResultFilter.kiCpuRevisions];
+        self._oDb.execute('SELECT TestBoxesWithStrings.lCpuRevision,\n'
+                          '       TestBoxesWithStrings.sCpuVendor,\n'
+                          '       SUM(TestBoxGenIDs.cTimes)\n'
+                          'FROM   ( SELECT TestSets.idGenTestBox,\n'
+                          '                COUNT(TestSets.idTestSet) AS cTimes\n'
+                          '         FROM   TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiCpuRevisions) +
+                          ''.join('                , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+                          '         WHERE  ' + self._getTimePeriodQueryPart(tsNow, sPeriod, '        ') +
+                          oFilter.getWhereConditions(iOmit = TestResultFilter.kiCpuRevisions) +
+                          oReportModel.getExtraSubjectWhereExpr() +
+                          '         GROUP BY TestSets.idGenTestBox'
+                          '       ) AS TestBoxGenIDs\n'
+                          '       LEFT OUTER JOIN TestBoxesWithStrings\n'
+                          '                    ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+                          'GROUP BY TestBoxesWithStrings.lCpuRevision, TestBoxesWithStrings.sCpuVendor\n'
+                          'ORDER BY TestBoxesWithStrings.sCpuVendor DESC, TestBoxesWithStrings.lCpuRevision DESC\n' );
+        workerDoFetch(None, fIdIsName = True);
+        for oCur in oCrit.aoPossible:
+            oCur.sDesc = TestBoxData.getPrettyCpuVersionEx(oCur.oValue, oCur.sDesc).replace('_', ' ');
+
+        # Testbox CPU core/thread counts.
+        oCrit = oFilter.aCriteria[TestResultFilter.kiCpuCounts];
+        self._oDb.execute('SELECT TestBoxesWithStrings.cCpus,\n'
+                          '       CAST(TestBoxesWithStrings.cCpus AS TEXT),\n'
+                          '       SUM(TestBoxGenIDs.cTimes)\n'
+                          'FROM   ( SELECT TestSets.idGenTestBox,\n'
+                          '                COUNT(TestSets.idTestSet) AS cTimes\n'
+                          '         FROM   TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiCpuCounts) +
+                          ''.join('                , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+                          '         WHERE  ' + self._getTimePeriodQueryPart(tsNow, sPeriod, '        ') +
+                          oFilter.getWhereConditions(iOmit = TestResultFilter.kiCpuCounts) +
+                          oReportModel.getExtraSubjectWhereExpr() +
+                          '         GROUP BY TestSets.idGenTestBox'
+                          '       ) AS TestBoxGenIDs\n'
+                          '       LEFT OUTER JOIN TestBoxesWithStrings\n'
+                          '                    ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+                          'GROUP BY TestBoxesWithStrings.cCpus\n'
+                          'ORDER BY TestBoxesWithStrings.cCpus\n' );
+        workerDoFetch(None, fIdIsName = True);
+
+        # Testbox memory.
+        oCrit = oFilter.aCriteria[TestResultFilter.kiMemory];
+        self._oDb.execute('SELECT TestBoxesWithStrings.cMbMemory / 1024,\n'
+                          '       NULL,\n'
+                          '       SUM(TestBoxGenIDs.cTimes)\n'
+                          'FROM   ( SELECT TestSets.idGenTestBox,\n'
+                          '                COUNT(TestSets.idTestSet) AS cTimes\n'
+                          '         FROM   TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiMemory) +
+                          ''.join('                , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+                          '         WHERE  ' + self._getTimePeriodQueryPart(tsNow, sPeriod, '        ') +
+                          oFilter.getWhereConditions(iOmit = TestResultFilter.kiMemory) +
+                          oReportModel.getExtraSubjectWhereExpr() +
+                          '         GROUP BY TestSets.idGenTestBox'
+                          '       ) AS TestBoxGenIDs\n'
+                          '       LEFT OUTER JOIN TestBoxesWithStrings\n'
+                          '                    ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+                          'GROUP BY TestBoxesWithStrings.cMbMemory / 1024\n'
+                          'ORDER BY 1\n' );
+        workerDoFetch(None, fIdIsName = True);
+        for oCur in oCrit.aoPossible:
+            oCur.sDesc = '%u GB' % (oCur.oValue,);
+
+        # Testbox python versions .
+        oCrit = oFilter.aCriteria[TestResultFilter.kiPythonVersions];
+        self._oDb.execute('SELECT TestBoxesWithStrings.iPythonHexVersion,\n'
+                          '       NULL,\n'
+                          '       SUM(TestBoxGenIDs.cTimes)\n'
+                          'FROM   ( SELECT TestSets.idGenTestBox     AS idGenTestBox,\n'
+                          '                COUNT(TestSets.idTestSet) AS cTimes\n'
+                          '         FROM   TestSets\n' + oFilter.getTableJoins(iOmit = TestResultFilter.kiPythonVersions) +
+                          ''.join('                , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
+                          '         WHERE  ' + self._getTimePeriodQueryPart(tsNow, sPeriod, '        ') +
+                          oFilter.getWhereConditions(iOmit = TestResultFilter.kiPythonVersions) +
+                          oReportModel.getExtraSubjectWhereExpr() +
+                          '         GROUP BY TestSets.idGenTestBox\n'
+                          '       ) AS TestBoxGenIDs\n'
+                          '       LEFT OUTER JOIN TestBoxesWithStrings\n'
+                          '                    ON TestBoxesWithStrings.idGenTestBox = TestBoxGenIDs.idGenTestBox\n'
+                          'GROUP BY TestBoxesWithStrings.iPythonHexVersion\n'
+                          'ORDER BY TestBoxesWithStrings.iPythonHexVersion\n' );
+        workerDoFetch(None, fIdIsName = True);
+        for oCur in oCrit.aoPossible:
+            oCur.sDesc = TestBoxData.formatPythonVersionEx(oCur.oValue);
 
         # Testcases (see getTestCases).
