Index: /trunk/src/VBox/ValidationKit/testmanager/core/base.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/base.py	(revision 65156)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/base.py	(revision 65157)
@@ -1188,6 +1188,7 @@
     ## @name The value type.
     ## @{
-    ksType_UInt   = 'uint';     ##< unsigned integer value.
-    ksType_String = 'string';   ##< string value.
+    ksType_UInt    = 'uint';     ##< unsigned integer value.
+    ksType_UIntNil = 'uint-nil'; ##< unsigned integer value, with nil.
+    ksType_String  = 'string';   ##< string value.
     ## @}
 
@@ -1245,4 +1246,6 @@
         if oCriterion.sType == FilterCriterion.ksType_UInt:
             oCriterion.aoSelected = oDisp.getListOfIntParams(oCriterion.sVarNm, iMin = 0, aiDefaults = []);
+        elif oCriterion.sType == FilterCriterion.ksType_UIntNil:
+            oCriterion.aoSelected = oDisp.getListOfIntParams(oCriterion.sVarNm, iMin = -1, aiDefaults = []);
         elif oCriterion.sType == FilterCriterion.ksType_String:
             oCriterion.aoSelected = oDisp.getListOfStrParams(oCriterion.sVarNm, asDefaults = []);
Index: /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py	(revision 65156)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py	(revision 65157)
@@ -729,5 +729,6 @@
 
         # Failure reasons
-        oCrit = FilterCriterion('Failure reasons', sVarNm = 'fr', sTable = 'TestResultFailures', sColumn = 'idFailureReason');
+        oCrit = FilterCriterion('Failure reasons', sVarNm = 'fr', sType = FilterCriterion.ksType_UIntNil,
+                                sTable = 'TestResultFailures', sColumn = 'idFailureReason');
         self.aCriteria.append(oCrit);
         assert self.aCriteria[self.kiFailReasons] is oCrit;
@@ -846,16 +847,28 @@
             else:
                 assert len(oCrit.asTables) == 1;
-                if iCrit == self.kiMemory:
-                    sQuery += '%s   AND (%s.%s / 1024)' % (sExtraIndent, oCrit.asTables[0], oCrit.sColumn,);
-                else:
-                    sQuery += '%s   AND %s.%s' % (sExtraIndent, oCrit.asTables[0], oCrit.sColumn,);
-                if not oCrit.fInverted:
-                    sQuery += ' IN (';
-                else:
-                    sQuery += ' NOT IN (';
-                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';
+                sQuery += '%s   AND (' % (sExtraIndent,);
+
+                if oCrit.sType != FilterCriterion.ksType_UIntNil or max(oCrit.aoSelected) != -1:
+                    if iCrit == self.kiMemory:
+                        sQuery += '(%s.%s / 1024)' % (oCrit.asTables[0], oCrit.sColumn,);
+                    else:
+                        sQuery += '%s.%s' % (oCrit.asTables[0], oCrit.sColumn,);
+                    if not oCrit.fInverted:
+                        sQuery += ' IN (';
+                    else:
+                        sQuery += ' NOT IN (';
+                    if oCrit.sType == FilterCriterion.ksType_String:
+                        sQuery += ', '.join('\'%s\'' % (sValue,) for sValue in oCrit.aoSelected) + ')';
+                    else:
+                        sQuery += ', '.join(str(iValue) for iValue in oCrit.aoSelected if iValue != -1) + ')';
+
+                if    oCrit.sType == FilterCriterion.ksType_UIntNil \
+                  and -1 in oCrit.aoSelected:
+                    if sQuery[-1] != '(': sQuery += ' OR ';
+                    sQuery += '%s.%s IS NULL' % (oCrit.asTables[0], oCrit.sColumn,);
+
+                if iCrit == self.kiFailReasons:
+                    sQuery += '%s    AND TestSets.enmStatus >= \'failure\'::TestStatus_T\n' % (sExtraIndent,);
+                sQuery += ')\n';
             if oCrit.oSub is not None:
                 sQuery += self._getWhereWorker(iCrit | (((iCrit >> 8) + 1) << 8), oCrit.oSub, sExtraIndent, iOmit);
@@ -1621,10 +1634,13 @@
             oReportModel = DummyReportModel();
 
-        def workerDoFetch(oMissingLogicType, sNameAttr = 'sName', fIdIsName = False, idxHover = -1):
+        def workerDoFetch(oMissingLogicType, sNameAttr = 'sName', fIdIsName = False, idxHover = -1,
+                          idNull = -1, sNullDesc = '<NULL>'):
             """ Does the tedious result fetching and handling of missing bits. """
             dLeft = { oValue: 1 for oValue in oCrit.aoSelected };
             oCrit.aoPossible = [];
             for aoRow in self._oDb.fetchAll():
-                oCrit.aoPossible.append(FilterCriterionValueAndDescription(aoRow[0], aoRow[1], aoRow[2],
+                oCrit.aoPossible.append(FilterCriterionValueAndDescription(aoRow[0] if aoRow[0] is not None else idNull,
+                                                                           aoRow[1] if aoRow[1] is not None else sNullDesc,
+                                                                           aoRow[2],
                                                                            aoRow[idxHover] if idxHover >= 0 else None));
                 if aoRow[0] in dLeft:
@@ -1955,5 +1971,5 @@
                           '                COUNT(TestSets.idTestSet) as cTimes\n'
                           '         FROM   TestSets\n'
-                          '         INNER JOIN TestResultFailures\n'
+                          '         LEFT OUTER JOIN TestResultFailures\n'
                           '                 ON     TestResultFailures.idTestSet = TestSets.idTestSet\n'
                           '                    AND TestResultFailures.tsExpire  = \'infinity\'::TIMESTAMP\n' +
@@ -1961,13 +1977,15 @@
                           ''.join('                , %s\n' % (sTable,) for sTable in oReportModel.getExtraSubjectTables()) +
                           '         WHERE  ' + self._getTimePeriodQueryPart(tsNow, sPeriod, '        ') +
+                          '            AND TestSets.enmStatus >= \'failure\'::TestStatus_T\n' +
                           oFilter.getWhereConditions(iOmit = TestResultFilter.kiFailReasons) +
                           oReportModel.getExtraSubjectWhereExpr() +
                           '         GROUP BY TestResultFailures.idFailureReason\n'
                           '       ) AS FailureReasonIDs\n'
-                          '       INNER JOIN FailureReasons\n'
+                          '       LEFT OUTER JOIN FailureReasons\n'
                           '               ON FailureReasons.idFailureReason = FailureReasonIDs.idFailureReason\n'
                           '              AND FailureReasons.tsExpire        = \'infinity\'::TIMESTAMP\n'
-                          'ORDER BY FailureReasons.sShort\n' );
-        workerDoFetch(FailureReasonLogic, 'sShort');
+                          'ORDER BY FailureReasons.idFailureReason IS NULL DESC,\n'
+                          '         FailureReasons.sShort\n' );
+        workerDoFetch(FailureReasonLogic, 'sShort', sNullDesc = 'Not given');
 
         # Error counts.
