[tools 1/6] testbot: Add tables to track the test failures.

Francois Gouget fgouget at codeweavers.com
Wed Jun 15 11:21:28 CDT 2022


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48912
Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
This patch requires updating the database schema and restarting the
TestBot Engine and web server.

Notes:
- This first patch only introduces the core infrastructure. The other
  parts are:
  - Providing a way to enter known failures.
  - Matching the known failures in the logs and recording the matches
    in the database.
  - Taking the known failures into account in the report emails and
    patches status.
  - Identifying the known failures on the JobDetails page.
  - Update the associated bug information.
- Most bug tracking systems use integers to identify bugs. If we ever
  want to move Wine's bug tracking away from Bugzilla we may have to
  change the type of the BugId field. We may also need a way to know
  which bug tracker a given BugId refers to which may involve adding a
  BugTracker field or replacing the BugId field with the whole bug URL.
- The Jobs::Restart() chunk depends on the patch that turns Itemrefs
  into virtual properties. Without it it would be unable to filter the
  TaskFailures table on the JobId property.
---
 testbot/ddl/update47.sql                |  33 +
 testbot/ddl/winetestbot.sql             |  32 +
 testbot/doc/winetestbot-schema.dia      | 852 +++++++++++++++++++++---
 testbot/lib/WineTestBot/Failures.pm     | 184 +++++
 testbot/lib/WineTestBot/Jobs.pm         |   6 +
 testbot/lib/WineTestBot/TaskFailures.pm | 120 ++++
 testbot/lib/WineTestBot/Tasks.pm        |   4 +
 7 files changed, 1142 insertions(+), 89 deletions(-)
 create mode 100644 testbot/ddl/update47.sql
 create mode 100644 testbot/lib/WineTestBot/Failures.pm
 create mode 100644 testbot/lib/WineTestBot/TaskFailures.pm

diff --git a/testbot/ddl/update47.sql b/testbot/ddl/update47.sql
new file mode 100644
index 000000000..177eadc28
--- /dev/null
+++ b/testbot/ddl/update47.sql
@@ -0,0 +1,33 @@
+USE winetestbot;
+
+CREATE TABLE Failures
+(
+  Id             INT NOT NULL AUTO_INCREMENT,
+  ErrorGroup     VARCHAR(64) NULL,
+  TestUnit       VARCHAR(32) NULL,
+  ConfigRegExp   VARCHAR(64) NULL,
+  FailureRegExp  VARCHAR(256) NOT NULL,
+  Notes          VARCHAR(128) NULL,
+  LastNew        DATETIME NULL,
+  LastOld        DATETIME NULL,
+  BugId          INT NOT NULL,
+  BugStatus      VARCHAR(32) NULL,
+  BugDescription VARCHAR(128) NULL,
+  PRIMARY KEY (Id)
+)
+ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE TaskFailures
+(
+  FailureId      INT         NOT NULL,
+  JobId          INT         NOT NULL,
+  StepNo         INT(2)      NOT NULL,
+  TaskNo         INT(2)      NOT NULL,
+  TaskLog        VARCHAR(32) NOT NULL,
+  NewCount       INT         NULL,
+  OldCount       INT         NULL,
+  FOREIGN KEY(FailureId) REFERENCES Failures(Id),
+  FOREIGN KEY(JobId, StepNo, TaskNo) REFERENCES Tasks(JobId, StepNo, No),
+  PRIMARY KEY (FailureId, JobId, StepNo, TaskNo, TaskLog)
+)
+ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/testbot/ddl/winetestbot.sql b/testbot/ddl/winetestbot.sql
index 2d085247c..0cf70484c 100644
--- a/testbot/ddl/winetestbot.sql
+++ b/testbot/ddl/winetestbot.sql
@@ -164,6 +164,38 @@ CREATE TABLE Tasks
 )
 ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
+CREATE TABLE Failures
+(
+  Id             INT NOT NULL AUTO_INCREMENT,
+  ErrorGroup     VARCHAR(64) NULL,
+  TestUnit       VARCHAR(32) NULL,
+  ConfigRegExp   VARCHAR(64) NULL,
+  FailureRegExp  VARCHAR(256) NOT NULL,
+  Notes          VARCHAR(128) NULL,
+  LastNew        DATETIME NULL,
+  LastOld        DATETIME NULL,
+  BugId          INT NOT NULL,
+  BugStatus      VARCHAR(32) NULL,
+  BugDescription VARCHAR(128) NULL,
+  PRIMARY KEY (Id)
+)
+ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE TaskFailures
+(
+  FailureId      INT         NOT NULL,
+  JobId          INT         NOT NULL,
+  StepNo         INT(2)      NOT NULL,
+  TaskNo         INT(2)      NOT NULL,
+  TaskLog        VARCHAR(32) NOT NULL,
+  NewCount       INT         NULL,
+  OldCount       INT         NULL,
+  FOREIGN KEY(FailureId) REFERENCES Failures(Id),
+  FOREIGN KEY(JobId, StepNo, TaskNo) REFERENCES Tasks(JobId, StepNo, No),
+  PRIMARY KEY (FailureId, JobId, StepNo, TaskNo, TaskLog)
+)
+ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
 CREATE TABLE RecordGroups
 (
   Id           INT      NOT NULL AUTO_INCREMENT,
diff --git a/testbot/doc/winetestbot-schema.dia b/testbot/doc/winetestbot-schema.dia
index b2a686285..effbb9a82 100644
--- a/testbot/doc/winetestbot-schema.dia
+++ b/testbot/doc/winetestbot-schema.dia
@@ -669,7 +669,7 @@
         <dia:point val="-7.87083,-21.5"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="-7.92083,-23;-4.3375,-21.45"/>
+        <dia:rectangle val="-7.92083,-22.9955;-4.3375,-21.45"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
@@ -729,7 +729,7 @@
         <dia:point val="4.6125,-22.35"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="4.5625,-23;11.0958,-22.2834"/>
+        <dia:rectangle val="4.5625,-22.9955;11.0958,-22.2834"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
@@ -789,7 +789,7 @@
         <dia:point val="19.6608,-21.5334"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="19.6108,-22.2;25.4458,-21.4834"/>
+        <dia:rectangle val="19.6108,-22.1955;25.4458,-21.4834"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
@@ -846,16 +846,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O7">
       <dia:attribute name="obj_pos">
-        <dia:point val="-16.8208,-13.3167"/>
+        <dia:point val="-16.8208,-6.6667"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="-16.8208,-13.3167;-7.8208,-10.3167"/>
+        <dia:rectangle val="-16.8208,-6.6667;-7.8208,-3.6667"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="-16.8208,-13.3167"/>
+        <dia:point val="-16.8208,-6.6667"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="9"/>
@@ -962,16 +962,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O8">
       <dia:attribute name="obj_pos">
-        <dia:point val="-16.3208,-2.55002"/>
+        <dia:point val="-16.3208,3.89998"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="-16.3208,-2.55002;-6.1658,5.24998"/>
+        <dia:rectangle val="-16.3208,3.89998;-6.1658,11.7"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="-16.3208,-2.55002"/>
+        <dia:point val="-16.3208,3.89998"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="10.155000000000001"/>
@@ -1216,16 +1216,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O9">
       <dia:attribute name="obj_pos">
-        <dia:point val="-4.57083,-14.9167"/>
+        <dia:point val="-4.57083,-8.2667"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="-4.57083,-14.9167;4.81417,-6.3167"/>
+        <dia:rectangle val="-4.57083,-8.2667;4.81417,0.3333"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="-4.57083,-14.9167"/>
+        <dia:point val="-4.57083,-8.2667"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="9.3850000000000016"/>
@@ -1493,16 +1493,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O10">
       <dia:attribute name="obj_pos">
-        <dia:point val="-2.55417,-2.51669"/>
+        <dia:point val="-2.55417,3.88331"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="-2.55417,-2.51669;12.6058,2.88331"/>
+        <dia:rectangle val="-2.55417,3.88331;12.6058,9.28331"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="-2.55417,-2.51669"/>
+        <dia:point val="-2.55417,3.88331"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="15.16"/>
@@ -1678,16 +1678,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O11">
       <dia:attribute name="obj_pos">
-        <dia:point val="8.24583,-14.8834"/>
+        <dia:point val="8.19583,-8.2334"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="8.24583,-14.8834;22.2508,-6.2834"/>
+        <dia:rectangle val="8.19583,-8.2334;22.2008,0.3666"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="8.24583,-14.8834"/>
+        <dia:point val="8.19583,-8.2334"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="14.004999999999999"/>
@@ -1955,16 +1955,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O12">
       <dia:attribute name="obj_pos">
-        <dia:point val="25.4792,-14.8834"/>
+        <dia:point val="25.4792,-8.2334"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="25.4792,-14.8834;36.0192,-4.6834"/>
+        <dia:rectangle val="25.4792,-8.2334;36.0192,1.9666"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="25.4792,-14.8834"/>
+        <dia:point val="25.4792,-8.2334"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="10.539999999999999"/>
@@ -2278,16 +2278,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O13">
       <dia:attribute name="obj_pos">
-        <dia:point val="25.6708,-2.02919"/>
+        <dia:point val="25.6708,3.87081"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="25.6708,-2.02919;36.5958,12.1708"/>
+        <dia:rectangle val="25.6708,3.87081;36.5958,18.0708"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="25.6708,-2.02919"/>
+        <dia:point val="25.6708,3.87081"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="10.925000000000001"/>
@@ -2716,19 +2716,19 @@
     </dia:object>
     <dia:object type="Database - Reference" version="0" id="O14">
       <dia:attribute name="obj_pos">
-        <dia:point val="-7.8208,-11.6167"/>
+        <dia:point val="-7.8208,-4.9667"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="-7.8708,-12.2667;-4.52083,-11.5667"/>
+        <dia:rectangle val="-7.8708,-5.61225;-4.52083,-4.9167"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="orth_points">
-        <dia:point val="-7.8208,-11.6167"/>
-        <dia:point val="-5.92083,-11.6167"/>
-        <dia:point val="-5.92083,-11.6167"/>
-        <dia:point val="-4.57083,-11.6167"/>
+        <dia:point val="-7.8208,-4.9667"/>
+        <dia:point val="-5.92083,-4.9667"/>
+        <dia:point val="-5.92083,-4.9667"/>
+        <dia:point val="-4.57083,-4.9667"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="0"/>
@@ -2776,19 +2776,19 @@
     </dia:object>
     <dia:object type="Database - Reference" version="0" id="O15">
       <dia:attribute name="obj_pos">
-        <dia:point val="4.81417,-13.2167"/>
+        <dia:point val="4.81417,-6.5667"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="4.76417,-13.8667;8.29583,-13.1334"/>
+        <dia:rectangle val="4.76417,-7.21225;8.24583,-6.4834"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="orth_points">
-        <dia:point val="4.81417,-13.2167"/>
-        <dia:point val="6.4625,-13.2167"/>
-        <dia:point val="6.4625,-13.1834"/>
-        <dia:point val="8.24583,-13.1834"/>
+        <dia:point val="4.81417,-6.5667"/>
+        <dia:point val="6.4625,-6.5667"/>
+        <dia:point val="6.4625,-6.5334"/>
+        <dia:point val="8.19583,-6.5334"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="0"/>
@@ -2836,19 +2836,19 @@
     </dia:object>
     <dia:object type="Database - Reference" version="0" id="O16">
       <dia:attribute name="obj_pos">
-        <dia:point val="22.2508,-12.3834"/>
+        <dia:point val="22.2008,-5.7334"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="22.2008,-13.0334;25.5292,-12.3334"/>
+        <dia:rectangle val="22.1508,-6.37895;25.5292,-5.6834"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="orth_points">
-        <dia:point val="22.2508,-12.3834"/>
-        <dia:point val="23.7334,-12.3834"/>
-        <dia:point val="23.7334,-12.3834"/>
-        <dia:point val="25.4792,-12.3834"/>
+        <dia:point val="22.2008,-5.7334"/>
+        <dia:point val="23.7334,-5.7334"/>
+        <dia:point val="23.7334,-5.7334"/>
+        <dia:point val="25.4792,-5.7334"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="0"/>
@@ -2896,19 +2896,19 @@
     </dia:object>
     <dia:object type="Database - Reference" version="0" id="O17">
       <dia:attribute name="obj_pos">
-        <dia:point val="36.0192,-9.98336"/>
+        <dia:point val="36.0192,-3.3334"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="35.9692,-10.6334;37.6693,-0.27919"/>
+        <dia:rectangle val="35.9692,-3.97895;37.6693,5.62081"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="orth_points">
-        <dia:point val="36.0192,-9.98336"/>
-        <dia:point val="37.6193,-9.98336"/>
-        <dia:point val="37.6193,-0.32919"/>
-        <dia:point val="36.5958,-0.32919"/>
+        <dia:point val="36.0192,-3.3334"/>
+        <dia:point val="37.6193,-3.3334"/>
+        <dia:point val="37.6193,5.57081"/>
+        <dia:point val="36.5958,5.57081"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="0"/>
@@ -2959,7 +2959,7 @@
         <dia:point val="0.1125,-17.85"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="0.0625,-17.9;1.48167,-14.8667"/>
+        <dia:rectangle val="0.0625,-17.9;1.48167,-8.2167"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
@@ -2968,7 +2968,7 @@
         <dia:point val="0.1125,-17.85"/>
         <dia:point val="0.1125,-15.3584"/>
         <dia:point val="0.12167,-15.3584"/>
-        <dia:point val="0.12167,-14.9167"/>
+        <dia:point val="0.12167,-8.2667"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="1"/>
@@ -3016,19 +3016,19 @@
     </dia:object>
     <dia:object type="Database - Reference" version="0" id="O19">
       <dia:attribute name="obj_pos">
-        <dia:point val="-4.57083,-6.8167"/>
+        <dia:point val="-4.57083,-0.1667"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="-18.2807,-7.4667;-4.52083,-0.80002"/>
+        <dia:rectangle val="-18.2807,-0.812247;-4.52083,5.64998"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="orth_points">
-        <dia:point val="-4.57083,-6.8167"/>
-        <dia:point val="-18.2307,-6.8167"/>
-        <dia:point val="-18.2307,-0.85002"/>
-        <dia:point val="-16.3208,-0.85002"/>
+        <dia:point val="-4.57083,-0.1667"/>
+        <dia:point val="-18.2307,-0.1667"/>
+        <dia:point val="-18.2307,5.59998"/>
+        <dia:point val="-16.3208,5.59998"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="0"/>
@@ -3076,19 +3076,19 @@
     </dia:object>
     <dia:object type="Database - Reference" version="0" id="O20">
       <dia:attribute name="obj_pos">
-        <dia:point val="-6.1658,-0.85002"/>
+        <dia:point val="-6.1658,5.59998"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="-6.2158,-1.50002;-2.50417,1.63331"/>
+        <dia:rectangle val="-6.2158,4.95443;-2.50417,8.03331"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="orth_points">
-        <dia:point val="-6.1658,-0.85002"/>
-        <dia:point val="-4.29249,-0.85002"/>
-        <dia:point val="-4.29249,1.58331"/>
-        <dia:point val="-2.55417,1.58331"/>
+        <dia:point val="-6.1658,5.59998"/>
+        <dia:point val="-4.29249,5.59998"/>
+        <dia:point val="-4.29249,7.98331"/>
+        <dia:point val="-2.55417,7.98331"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="0"/>
@@ -3136,19 +3136,19 @@
     </dia:object>
     <dia:object type="Database - Reference" version="0" id="O21">
       <dia:attribute name="obj_pos">
-        <dia:point val="12.6058,-0.81669"/>
+        <dia:point val="12.6058,5.58331"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="12.5558,-1.50002;15.1193,-0.76669"/>
+        <dia:rectangle val="12.5558,4.93776;15.1193,5.64998"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="orth_points">
-        <dia:point val="12.6058,-0.81669"/>
-        <dia:point val="13.7696,-0.81669"/>
-        <dia:point val="13.7696,-0.85002"/>
-        <dia:point val="15.0693,-0.85002"/>
+        <dia:point val="12.6058,5.58331"/>
+        <dia:point val="13.7696,5.58331"/>
+        <dia:point val="13.7696,5.59998"/>
+        <dia:point val="15.0693,5.59998"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="0"/>
@@ -3196,16 +3196,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O22">
       <dia:attribute name="obj_pos">
-        <dia:point val="15.0693,-2.55002"/>
+        <dia:point val="15.0693,3.89998"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="15.0693,-2.55002;24.4543,1.24998"/>
+        <dia:rectangle val="15.0693,3.89998;24.4543,7.69998"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="15.0693,-2.55002"/>
+        <dia:point val="15.0693,3.89998"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="9.3850000000000016"/>
@@ -3335,16 +3335,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O23">
       <dia:attribute name="obj_pos">
-        <dia:point val="14.12,3.7"/>
+        <dia:point val="11.32,12.4"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="14.12,3.7;24.66,8.3"/>
+        <dia:rectangle val="11.32,12.4;21.86,17"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="14.12,3.7"/>
+        <dia:point val="11.32,12.4"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="10.539999999999999"/>
@@ -3497,16 +3497,16 @@
     </dia:object>
     <dia:object type="Database - Table" version="0" id="O24">
       <dia:attribute name="obj_pos">
-        <dia:point val="2.61,3.7125"/>
+        <dia:point val="-0.19,12.3625"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="2.61,3.7125;10.455,6.7125"/>
+        <dia:rectangle val="-0.19,12.3625;7.655,15.3625"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="elem_corner">
-        <dia:point val="2.61,3.7125"/>
+        <dia:point val="-0.19,12.3625"/>
       </dia:attribute>
       <dia:attribute name="elem_width">
         <dia:real val="7.8449999999999998"/>
@@ -3613,19 +3613,19 @@
     </dia:object>
     <dia:object type="Database - Reference" version="0" id="O25">
       <dia:attribute name="obj_pos">
-        <dia:point val="10.455,5.4125"/>
+        <dia:point val="7.655,14.0625"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="10.405,4.75;14.17,5.4625"/>
+        <dia:rectangle val="7.605,13.417;11.37,14.15"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="orth_points">
-        <dia:point val="10.455,5.4125"/>
-        <dia:point val="11.95,5.4125"/>
-        <dia:point val="11.95,5.4"/>
-        <dia:point val="14.12,5.4"/>
+        <dia:point val="7.655,14.0625"/>
+        <dia:point val="9.8693,14.0625"/>
+        <dia:point val="9.8693,14.1"/>
+        <dia:point val="11.32,14.1"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="0"/>
@@ -3673,19 +3673,19 @@
     </dia:object>
     <dia:object type="Database - Reference" version="0" id="O26">
       <dia:attribute name="obj_pos">
-        <dia:point val="8.24583,-12.3834"/>
+        <dia:point val="8.19583,-5.7334"/>
       </dia:attribute>
       <dia:attribute name="obj_bb">
-        <dia:rectangle val="5.87763,-13.0334;8.29583,-11.5334"/>
+        <dia:rectangle val="5.87763,-6.37895;8.24583,-4.8834"/>
       </dia:attribute>
       <dia:attribute name="meta">
         <dia:composite type="dict"/>
       </dia:attribute>
       <dia:attribute name="orth_points">
-        <dia:point val="8.24583,-12.3834"/>
-        <dia:point val="5.92763,-12.3834"/>
-        <dia:point val="5.92763,-11.5834"/>
-        <dia:point val="8.24583,-11.5834"/>
+        <dia:point val="8.19583,-5.7334"/>
+        <dia:point val="5.92763,-5.7334"/>
+        <dia:point val="5.92763,-4.9334"/>
+        <dia:point val="8.19583,-4.9334"/>
       </dia:attribute>
       <dia:attribute name="orth_orient">
         <dia:enum val="0"/>
@@ -3731,5 +3731,679 @@
         <dia:connection handle="1" to="O11" connection="16"/>
       </dia:connections>
     </dia:object>
+    <dia:object type="Database - Table" version="0" id="O27">
+      <dia:attribute name="obj_pos">
+        <dia:point val="25.4168,-19.2167"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="25.4168,-19.2167;34.4168,-12.2167"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="25.4168,-19.2167"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="9"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="7"/>
+      </dia:attribute>
+      <dia:attribute name="name">
+        <dia:string>#TaskFailures#</dia:string>
+      </dia:attribute>
+      <dia:attribute name="comment">
+        <dia:string>##</dia:string>
+      </dia:attribute>
+      <dia:attribute name="visible_comment">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="underline_primary_key">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="tagging_comment">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="bold_primary_keys">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="attributes">
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#FailureId#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#INT#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#JobId#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#INT#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#StepNo#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#INT(2)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#TaskNo#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#INT(2)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#TaskLog#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#VARCHAR(32)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#NewCount#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#INT#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#OldCount#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#INT#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="normal_font">
+        <dia:font family="monospace" style="0" name="Courier"/>
+      </dia:attribute>
+      <dia:attribute name="name_font">
+        <dia:font family="sans" style="80" name="Helvetica-Bold"/>
+      </dia:attribute>
+      <dia:attribute name="comment_font">
+        <dia:font family="sans" style="8" name="Helvetica-Oblique"/>
+      </dia:attribute>
+      <dia:attribute name="normal_font_height">
+        <dia:real val="0.80000000000000004"/>
+      </dia:attribute>
+      <dia:attribute name="name_font_height">
+        <dia:real val="0.99999999999999989"/>
+      </dia:attribute>
+      <dia:attribute name="comment_font_height">
+        <dia:real val="0.69999999999999996"/>
+      </dia:attribute>
+      <dia:attribute name="text_colour">
+        <dia:color val="#000000ff"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000ff"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffffff"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Database - Table" version="0" id="O28">
+      <dia:attribute name="obj_pos">
+        <dia:point val="9.8001,-19.2167"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="9.8001,-19.2167;21.1101,-9.0167"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="elem_corner">
+        <dia:point val="9.8001,-19.2167"/>
+      </dia:attribute>
+      <dia:attribute name="elem_width">
+        <dia:real val="11.309999999999999"/>
+      </dia:attribute>
+      <dia:attribute name="elem_height">
+        <dia:real val="10.199999999999999"/>
+      </dia:attribute>
+      <dia:attribute name="name">
+        <dia:string>#Failures#</dia:string>
+      </dia:attribute>
+      <dia:attribute name="comment">
+        <dia:string>##</dia:string>
+      </dia:attribute>
+      <dia:attribute name="visible_comment">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="underline_primary_key">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="tagging_comment">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="bold_primary_keys">
+        <dia:boolean val="true"/>
+      </dia:attribute>
+      <dia:attribute name="attributes">
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#Id#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#INT#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#ErrorGroup#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#VARCHAR(64)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#TestUnit#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#VARCHAR(32)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#ConfigRegExp#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#VARCHAR(64)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#FailureRegExp#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#VARCHAR(256)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#Notes#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#VARCHAR(128)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#LastNew#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#DATETIME#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#LastOld#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#DATETIME#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#BugId#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#INT#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#BugStatus#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#VARCHAR(32)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+        <dia:composite type="table_attribute">
+          <dia:attribute name="name">
+            <dia:string>#BugDescription#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="type">
+            <dia:string>#VARCHAR(128)#</dia:string>
+          </dia:attribute>
+          <dia:attribute name="comment">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+          <dia:attribute name="primary_key">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="nullable">
+            <dia:boolean val="true"/>
+          </dia:attribute>
+          <dia:attribute name="unique">
+            <dia:boolean val="false"/>
+          </dia:attribute>
+          <dia:attribute name="default_value">
+            <dia:string>##</dia:string>
+          </dia:attribute>
+        </dia:composite>
+      </dia:attribute>
+      <dia:attribute name="normal_font">
+        <dia:font family="monospace" style="0" name="Courier"/>
+      </dia:attribute>
+      <dia:attribute name="name_font">
+        <dia:font family="sans" style="80" name="Helvetica-Bold"/>
+      </dia:attribute>
+      <dia:attribute name="comment_font">
+        <dia:font family="sans" style="8" name="Helvetica-Oblique"/>
+      </dia:attribute>
+      <dia:attribute name="normal_font_height">
+        <dia:real val="0.80000000000000004"/>
+      </dia:attribute>
+      <dia:attribute name="name_font_height">
+        <dia:real val="0.99999999999999989"/>
+      </dia:attribute>
+      <dia:attribute name="comment_font_height">
+        <dia:real val="0.69999999999999996"/>
+      </dia:attribute>
+      <dia:attribute name="text_colour">
+        <dia:color val="#000000ff"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000ff"/>
+      </dia:attribute>
+      <dia:attribute name="fill_colour">
+        <dia:color val="#ffffffff"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+    </dia:object>
+    <dia:object type="Database - Reference" version="0" id="O29">
+      <dia:attribute name="obj_pos">
+        <dia:point val="34.4168,-15.9167"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="34.3668,-16.5622;37.6001,-5.6834"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="34.4168,-15.9167"/>
+        <dia:point val="37.5501,-15.9167"/>
+        <dia:point val="37.5501,-5.7334"/>
+        <dia:point val="36.0192,-5.7334"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="orth_autoroute">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="text_colour">
+        <dia:color val="#000000ff"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000ff"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="corner_radius">
+        <dia:real val="0"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="start_point_desc">
+        <dia:string>#0..n#</dia:string>
+      </dia:attribute>
+      <dia:attribute name="end_point_desc">
+        <dia:string>#1#</dia:string>
+      </dia:attribute>
+      <dia:attribute name="normal_font">
+        <dia:font family="monospace" style="0" name="Courier"/>
+      </dia:attribute>
+      <dia:attribute name="normal_font_height">
+        <dia:real val="0.59999999999999998"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O27" connection="17"/>
+        <dia:connection handle="1" to="O12" connection="15"/>
+      </dia:connections>
+    </dia:object>
+    <dia:object type="Database - Reference" version="0" id="O30">
+      <dia:attribute name="obj_pos">
+        <dia:point val="21.1101,-17.5167"/>
+      </dia:attribute>
+      <dia:attribute name="obj_bb">
+        <dia:rectangle val="21.0601,-18.1622;25.4668,-17.4667"/>
+      </dia:attribute>
+      <dia:attribute name="meta">
+        <dia:composite type="dict"/>
+      </dia:attribute>
+      <dia:attribute name="orth_points">
+        <dia:point val="21.1101,-17.5167"/>
+        <dia:point val="22.6693,-17.5167"/>
+        <dia:point val="22.6693,-17.5167"/>
+        <dia:point val="25.4168,-17.5167"/>
+      </dia:attribute>
+      <dia:attribute name="orth_orient">
+        <dia:enum val="0"/>
+        <dia:enum val="1"/>
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="orth_autoroute">
+        <dia:boolean val="false"/>
+      </dia:attribute>
+      <dia:attribute name="text_colour">
+        <dia:color val="#000000ff"/>
+      </dia:attribute>
+      <dia:attribute name="line_colour">
+        <dia:color val="#000000ff"/>
+      </dia:attribute>
+      <dia:attribute name="line_width">
+        <dia:real val="0.10000000000000001"/>
+      </dia:attribute>
+      <dia:attribute name="line_style">
+        <dia:enum val="0"/>
+        <dia:real val="1"/>
+      </dia:attribute>
+      <dia:attribute name="corner_radius">
+        <dia:real val="0"/>
+      </dia:attribute>
+      <dia:attribute name="end_arrow">
+        <dia:enum val="0"/>
+      </dia:attribute>
+      <dia:attribute name="start_point_desc">
+        <dia:string>#1#</dia:string>
+      </dia:attribute>
+      <dia:attribute name="end_point_desc">
+        <dia:string>#0..n#</dia:string>
+      </dia:attribute>
+      <dia:attribute name="normal_font">
+        <dia:font family="monospace" style="0" name="Courier"/>
+      </dia:attribute>
+      <dia:attribute name="normal_font_height">
+        <dia:real val="0.59999999999999998"/>
+      </dia:attribute>
+      <dia:connections>
+        <dia:connection handle="0" to="O28" connection="13"/>
+        <dia:connection handle="1" to="O27" connection="12"/>
+      </dia:connections>
+    </dia:object>
   </dia:layer>
 </dia:diagram>
diff --git a/testbot/lib/WineTestBot/Failures.pm b/testbot/lib/WineTestBot/Failures.pm
new file mode 100644
index 000000000..b37b9e046
--- /dev/null
+++ b/testbot/lib/WineTestBot/Failures.pm
@@ -0,0 +1,184 @@
+# -*- Mode: Perl; perl-indent-level: 2; indent-tabs-mode: nil -*-
+# Copyright 2022 Francois Gouget
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+use strict;
+
+
+package WineTestBot::Failure;
+
+=head1 NAME
+
+WineTestBot::Failure - Describes a known Wine test failure
+
+=head1 DESCRIPTION
+
+Failure objects document known test failures and link them to the corresponding
+bug describing the issue. They are mainly used to avoid reporting a test
+failure as new when it has in fact happened before despite not being present
+in the reference WineTest logs, either because it is just too rare, or because
+its text is ever changing.
+
+Finally Failure objects are linked to the Task+Log they match by means of the
+TaskFailure objects.
+
+=cut
+
+use WineTestBot::WineTestBotObjects;
+our @ISA = qw(WineTestBot::WineTestBotItem);
+
+
+sub Compare($$)
+{
+  my ($self, $B) = @_;
+
+  # Sort deleted entries last
+  my %StatusOrders = ("deleted" => 1);
+
+  my $Cmp = ($StatusOrders{$self->GetColValue("BugStatus")} || 0) <=> ($StatusOrders{$B->GetColValue("BugStatus")} || 0) ||
+         $self->GetColValue("ErrorGroup") cmp $B->GetColValue("ErrorGroup") ||
+         $self->GetColValue("TestUnit") cmp $B->GetColValue("TestUnit") ||
+         $self->GetColValue("BugDescription") <=> $B->GetColValue("BugDescription");
+  return $Cmp
+}
+
+sub Validate($)
+{
+  my ($self) = @_;
+
+  if ($self->ErrorGroup =~ /^ / or $self->ErrorGroup =~ / $/)
+  {
+    return ("ErrorGroup", "The error group '". $self->ErrorGroup ."' should not have leading or trailing spaces");
+  }
+  if ($self->TestUnit !~ /^[_a-z0-9]*$/)
+  {
+    return ("TestUnit", "The test unit '". $self->TestUnit ."' contains invalid characters");
+  }
+  foreach my $Pair (["ConfigRegExp", "configuration"],
+                    ["FailureRegExp", "failure"])
+  {
+    my ($Field, $Name) = @$Pair;
+    my $RegExp = $self->$Field;
+    my $ErrMessage = eval {
+      if ($RegExp and "" =~ /$RegExp/)
+      {
+        return "The $Name regular expression should not match empty strings";
+      }
+    };
+    $ErrMessage = "The $Name regular expression is invalid" if ($@);
+    return ($Field, $ErrMessage) if ($ErrMessage);
+  }
+  return $self->SUPER::Validate();
+}
+
+
+package WineTestBot::Failures;
+
+=head1 NAME
+
+WineTestBot::Failures - A Failure collection
+
+=head1 DESCRIPTION
+
+This collection contains all known failures.
+
+=cut
+
+use Exporter 'import';
+use WineTestBot::WineTestBotObjects;
+BEGIN
+{
+  our @ISA = qw(WineTestBot::WineTestBotCollection);
+  our @EXPORT = qw(CreateFailures);
+}
+
+use ObjectModel::BasicPropertyDescriptor;
+use ObjectModel::EnumPropertyDescriptor;
+use ObjectModel::DetailrefPropertyDescriptor;
+use WineTestBot::TaskFailures;
+
+
+sub CreateItem($)
+{
+  my ($self) = @_;
+
+  return WineTestBot::Failure->new($self);
+}
+
+my @PropertyDescriptors = (
+  # Rather than using a combination of the other fields, give each entry a
+  # unique id. This allows having multiple entries for a single Wine bug in
+  # case there are too many failures to match for the regular expression to
+  # fit in a single regular expression field. This also avoids using the
+  # regular expression as part of the primary key which would be troublesome
+  # as it may need to be adjusted in case it is buggy or if the test changes.
+  CreateBasicPropertyDescriptor("Id", "Id", 1, 1, "S",  10),
+
+  # Identify the error group the failure can occur in. This is usually a
+  # test module but it may also be a group containing extra errors. Also some
+  # log files have a single nameless error group.
+  CreateBasicPropertyDescriptor("ErrorGroup", "Test module", !1, !1, "A", 64),
+  # For test modules, identify the test unit the failure can occur in. In the
+  # other cases this should be an empty string.
+  CreateBasicPropertyDescriptor("TestUnit", "Test unit", !1, !1, "A", 32),
+
+  # A regular expression to match the configurations the failure can happen in.
+  # A configuration name is of the form 'VMNAME:REPORTNAME'.
+  CreateBasicPropertyDescriptor("ConfigRegExp", "Configurations RegExp", !1, !1, "A", 64),
+
+  # A regular expression to match the troublesome failures.
+  CreateBasicPropertyDescriptor("FailureRegExp", "Failures RegExp", !1, 1, "A", 256),
+
+  # Can be used for documentation when multiple entries are needed to match
+  # all the failures associated with a given bug.
+  # Can also be used to document when a regular expression has been modified,
+  # for instance to match changes in a test.
+  CreateBasicPropertyDescriptor("Notes", "Notes", !1, !1, "A", 128),
+
+  # Record when an entry was last identified as a new or old failure, even
+  # after the corresponding tasks have expired (see the TaskFailures table).
+  CreateBasicPropertyDescriptor("LastNew", "Last new", !1, !1, "DT", 19),
+  CreateBasicPropertyDescriptor("LastOld", "Last old", !1, !1, "DT", 19),
+
+  # Every entry must be associated with a Wine bug.
+  # Note: The 'deleted' bug status means this failure entry should be
+  # deleted as soon as it is not referenced anymore.
+  CreateBasicPropertyDescriptor("BugId", "WineHQ bug id", !1, 1, "N", 10),
+  CreateBasicPropertyDescriptor("BugStatus", "WineHQ bug status", !1, !1, "A", 32),
+  CreateBasicPropertyDescriptor("BugDescription", "WineHQ bug description", !1, !1, "A", 128),
+
+  CreateDetailrefPropertyDescriptor("TaskFailures", "Tasks", \&CreateTaskFailures),
+);
+SetDetailrefKeyPrefix("Failure", @PropertyDescriptors);
+
+=pod
+=over 12
+
+=item C<CreateFailures()>
+
+Creates a collection of Failure objects.
+
+=back
+=cut
+
+sub CreateFailures(;$)
+{
+  my ($ScopeObject) = @_;
+  return WineTestBot::Failures->new("Failures", "Failures", "Failure",
+                                    \@PropertyDescriptors, $ScopeObject);
+}
+
+1;
diff --git a/testbot/lib/WineTestBot/Jobs.pm b/testbot/lib/WineTestBot/Jobs.pm
index 7f15a9c57..3da6ae7b0 100644
--- a/testbot/lib/WineTestBot/Jobs.pm
+++ b/testbot/lib/WineTestBot/Jobs.pm
@@ -89,6 +89,7 @@ use File::Path;
 use WineTestBot::Config;
 use WineTestBot::Branches;
 use WineTestBot::Engine::Notify;
+use WineTestBot::TaskFailures;
 
 
 sub _initialize($$)
@@ -435,6 +436,11 @@ sub Restart($)
   my ($ErrProperty, $ErrMessage) = $self->Save(); # Save it all
   return "$ErrMessage ($ErrProperty)" if ($ErrMessage);
 
+  # Remove the corresponding TaskFailures if any
+  my $TaskFailures = CreateTaskFailures();
+  $TaskFailures->AddFilter("JobId", [$self->Id]);
+  $TaskFailures->DeleteAll();
+
   return undef;
 }
 
diff --git a/testbot/lib/WineTestBot/TaskFailures.pm b/testbot/lib/WineTestBot/TaskFailures.pm
new file mode 100644
index 000000000..bdbfd6e2e
--- /dev/null
+++ b/testbot/lib/WineTestBot/TaskFailures.pm
@@ -0,0 +1,120 @@
+# -*- Mode: Perl; perl-indent-level: 2; indent-tabs-mode: nil -*-
+# Copyright 2022 Francois Gouget
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+use strict;
+
+
+package WineTestBot::TaskFailure;
+
+=head1 NAME
+
+WineTestBot::TaskFailure - Ties a failure to its task+log matches
+
+=head1 DESCRIPTION
+
+A TaskFailure is created when a WineTestBot::Failure matches a line in a
+task's log. It also records whether the matching failures occurred in previous
+logs or not. This makes it possible to detect fixed failures.
+
+=cut
+
+use WineTestBot::WineTestBotObjects;
+our @ISA = qw(WineTestBot::WineTestBotItem);
+
+
+sub Compare($$)
+{
+  my ($self, $B) = @_;
+
+  return $B->Task->Started <=> $self->Task->Started || # newest first by default
+         $self->TaskLog cmp $B->TaskLog;
+}
+
+
+package WineTestBot::TaskFailures;
+
+=head1 NAME
+
+WineTestBot::TaskFailures - A TaskFailure collection
+
+=head1 DESCRIPTION
+
+This collection cross references the known failures with the tasks and logs they
+matched.
+
+=cut
+
+use Exporter 'import';
+use WineTestBot::WineTestBotObjects;
+BEGIN
+{
+  our @ISA = qw(WineTestBot::WineTestBotCollection);
+  our @EXPORT = qw(CreateTaskFailures);
+}
+
+use ObjectModel::BasicPropertyDescriptor;
+use ObjectModel::ItemrefPropertyDescriptor;
+use WineTestBot::Tasks;
+
+
+sub CreateItem($)
+{
+  my ($self) = @_;
+
+  return WineTestBot::TaskFailure->new($self);
+}
+
+my @PropertyDescriptors = (
+  # Identifies the task which has matching failures
+  CreateBasicPropertyDescriptor("JobId",  "Job id", 1, 1, "N", 10),
+  CreateBasicPropertyDescriptor("StepNo", "Step no", 1, 1, "N", 2),
+  CreateBasicPropertyDescriptor("TaskNo", "Task", 1, 1, "N", 2),
+  CreateItemrefPropertyDescriptor("Task", "Task", 1, \&WineTestBot::Tasks::CreateTasks, ["JobId", "StepNo", "TaskNo"]),
+
+  # and more specifically in which of its logs
+  CreateBasicPropertyDescriptor("TaskLog", "Task log",  1,  1, "A", 32),
+
+  # Also store a count of matching new and old failures
+  CreateBasicPropertyDescriptor("NewCount", "Count of matching new failures", !1, !1, "N", 10),
+  CreateBasicPropertyDescriptor("OldCount", "Count of matching old failures", !1, !1, "N", 10),
+);
+SetupItemrefColumns(\@PropertyDescriptors);
+my @FlatPropertyDescriptors = (
+  CreateBasicPropertyDescriptor("FailureId", "Failure id", 1, 1, "N",  10),
+  @PropertyDescriptors
+);
+
+=pod
+=over 12
+
+=item C<CreateTaskFailures()>
+
+Creates a collection of TaskFailure objects.
+
+=back
+=cut
+
+sub CreateTaskFailures(;$$)
+{
+  my ($ScopeObject, $Failure) = @_;
+  return WineTestBot::TaskFailures->new("TaskFailures",
+      "TaskFailures", "TaskFailure",
+      $Failure ? \@PropertyDescriptors : \@FlatPropertyDescriptors,
+      $ScopeObject, $Failure);
+}
+
+1;
diff --git a/testbot/lib/WineTestBot/Tasks.pm b/testbot/lib/WineTestBot/Tasks.pm
index 0360e4ebc..5b87c782b 100644
--- a/testbot/lib/WineTestBot/Tasks.pm
+++ b/testbot/lib/WineTestBot/Tasks.pm
@@ -341,7 +341,9 @@ BEGIN
 use ObjectModel::BasicPropertyDescriptor;
 use ObjectModel::EnumPropertyDescriptor;
 use ObjectModel::ItemrefPropertyDescriptor;
+use ObjectModel::DetailrefPropertyDescriptor;
 use WineTestBot::VMs;
+use WineTestBot::TaskFailures;
 
 
 sub CreateItem($)
@@ -362,8 +364,10 @@ my @PropertyDescriptors = (
   CreateBasicPropertyDescriptor("Started", "Started", !1, !1, "DT", 19),
   CreateBasicPropertyDescriptor("Ended", "Ended", !1, !1, "DT", 19),
   CreateBasicPropertyDescriptor("TestFailures", "Failures", !1, !1, "N", 6),
+  CreateDetailrefPropertyDescriptor("Failures", "Known failures", \&CreateTaskFailures),
 );
 SetupItemrefColumns(\@PropertyDescriptors);
+SetDetailrefKeyPrefix("Task", @PropertyDescriptors);
 my @FlatPropertyDescriptors = (
   CreateBasicPropertyDescriptor("JobId", "Job id", 1, 1, "N", 10),
   CreateBasicPropertyDescriptor("StepNo", "Step no",  1,  1, "N", 2),
-- 
2.30.2




More information about the wine-devel mailing list