Francois Gouget : testbot: Match the failures with the test logs.
Alexandre Julliard
julliard at winehq.org
Wed Jun 15 15:31:50 CDT 2022
Module: tools
Branch: master
Commit: 1b238d27711cf9b62fb5f304d19610c0472c7b05
URL: https://source.winehq.org/git/tools.git/?a=commit;h=1b238d27711cf9b62fb5f304d19610c0472c7b05
Author: Francois Gouget <fgouget at codeweavers.com>
Date: Wed Jun 15 18:21:35 2022 +0200
testbot: Match the failures with the test logs.
This augments the .errors files with information mapping errors to known
failures.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48912
Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>
---
testbot/lib/WineTestBot/LogUtils.pm | 180 +++++++++++++++++++++++++++++++++++-
1 file changed, 178 insertions(+), 2 deletions(-)
diff --git a/testbot/lib/WineTestBot/LogUtils.pm b/testbot/lib/WineTestBot/LogUtils.pm
index 2e3de83b..b8aaaa43 100644
--- a/testbot/lib/WineTestBot/LogUtils.pm
+++ b/testbot/lib/WineTestBot/LogUtils.pm
@@ -36,7 +36,10 @@ our @EXPORT = qw(GetLogFileNames GetLogLabel
use Algorithm::Diff;
use File::Basename;
+use ObjectModel::Collection; # For CombineKey()
use WineTestBot::Config; # For $MaxUnitSize
+use WineTestBot::Failures;
+use WineTestBot::Tasks; # FIXME Hack for SaveLogFailures()
use WineTestBot::Utils; # For LocaleName()
@@ -187,6 +190,12 @@ sub _AddLogError($$$;$$)
Returns a hashtable containing a summary of the task log:
=over
+=item LogName
+The log file basename.
+
+=item LogPath
+The full log file path.
+
=item Type
'tests' if the task ran Wine tests and 'build' otherwise.
@@ -913,6 +922,10 @@ copy of that line (with CR/LF converted to a simple LF).
The format for new error lines is identical to that for old errors but with a
different type.
+=item f <errindex> <failureid1>...
+Failure lines contain the index of the error in the error group and a list of
+the known failures they match (normally at most one).
+
=back
=back
=cut
@@ -981,6 +994,10 @@ sub LoadLogErrorsFromFh($$)
{
_AddLogError($LogInfo, $LogInfo->{CurGroup}, $Value, $Property, "new");
}
+ elsif ($Type eq "f")
+ {
+ $LogInfo->{CurGroup}->{Failures}->{$Property} = [ split / /, $Value ];
+ }
else
{
$LogInfo->{BadLog} = "$LogInfo->{LineNo}: Found an unknown line type ($Type)";
@@ -1058,8 +1075,12 @@ sub _WriteLogErrorsToFh($$)
print $Fh "g $Group->{LineNo} $GroupName\n";
foreach my $Index (0..$#{$Group->{Errors}})
{
+ my $LineNo = $Group->{LineNos}->[$Index];
my $IsNew = $Group->{IsNew}->[$Index] ? "n" : "o";
- print $Fh "$IsNew $Group->{LineNos}->[$Index] $Group->{Errors}->[$Index]\n";
+ print $Fh "$IsNew $LineNo $Group->{Errors}->[$Index]\n";
+
+ my $Failures = $Group->{Failures}->{$Index};
+ print $Fh "f $Index @$Failures\n" if ($Failures);
}
}
}
@@ -1299,6 +1320,158 @@ sub TagNewErrors($$)
}
}
+=pod
+=over 12
+
+=item C<MatchLogFailures()>
+
+Checks the errors against known failures.
+
+The $LogInfo structure is augmented with the following fields:
+=over
+
+=item ErrGroups
+=over
+
+=item Failures
+A hashtable mapping error indices to the list of matching known
+failure ids.
+
+=back
+=back
+
+Returns a hashtable containing a summary of the log failures:
+=over
+
+=item LogName
+The log file basename.
+
+=item Collection
+A collection containing the relevant Failure objects.
+
+=item Failures
+A hashtable indexed by the failure ids. Each entry contains:
+
+=over
+
+=item Failure
+The failure object.
+
+=item NewCount
+A count errors matched by this known failure that were tagged as new.
+
+=item OldCount
+A count errors matched by this known failure that were tagged as old.
+
+=back
+
+=back
+=back
+=cut
+
+sub MatchLogFailures($$)
+{
+ my ($LogInfo, $Task) = @_;
+
+ my $LogFailures = {
+ Task => $Task,
+ LogName => $LogInfo->{LogName}
+ };
+ return $LogFailures if (!$LogInfo->{ErrCount});
+
+ my %FailureTree;
+ my $ConfigName = $Task->VMName .":$LogInfo->{LogName}";
+
+ $LogFailures->{Collection} = CreateFailures();
+ foreach my $Failure (@{$LogFailures->{Collection}->GetItems()})
+ {
+ # Ignore failures that don't apply to this configuration
+ my $ConfigRegExp = $Failure->ConfigRegExp;
+ my $Match = eval { $ConfigRegExp and $ConfigName =~ /$ConfigRegExp/ };
+ next if (!$Match);
+
+ my $UnitFailures = $FailureTree{$Failure->ErrorGroup}->{$Failure->TestUnit} ||= [];
+ push @$UnitFailures, $Failure;
+ }
+
+ foreach my $GroupName (@{$LogInfo->{ErrGroupNames}})
+ {
+ next if (!$FailureTree{$GroupName});
+
+ my $Group = $LogInfo->{ErrGroups}->{$GroupName};
+ foreach my $ErrIndex (0..$#{$Group->{Errors}})
+ {
+ my $Line = $Group->{Errors}->[$ErrIndex];
+ my $TestUnit = $Line =~ /^([_a-z0-9]+)\.c:\d+:/ ? $1 : "";
+ my $UnitFailures = $FailureTree{$GroupName}->{$TestUnit};
+ next if (!$UnitFailures);
+
+ foreach my $UnitFailure (@$UnitFailures)
+ {
+ my $RegExp = $UnitFailure->FailureRegExp;
+ my $Match = eval { $RegExp and $Line =~ /$RegExp/ };
+ next if (!$Match);
+
+ my $LineFailures = $Group->{Failures}->{$ErrIndex} ||= [];
+ push @$LineFailures, $UnitFailure->Id;
+
+ my $LogFailure = $LogFailures->{Failures}->{$UnitFailure->Id};
+ if (!$LogFailure)
+ {
+ $LogFailure = $LogFailures->{Failures}->{$UnitFailure->Id} =
+ { Failure => $UnitFailure };
+ }
+ my $Count = $Group->{IsNew}->[$ErrIndex] ? "NewCount" : "OldCount";
+ $LogFailure->{$Count}++;
+ }
+ }
+ }
+
+ return $LogFailures;
+}
+
+=pod
+=over 12
+
+=item C<SaveLogFailures()>
+
+Updates the TaskFailures objects for the task and log specified by the
+$LogFailures structure.
+
+Note that this implies deleting any preexisting TaskFailure to avoid leaving
+obsolete data.
+
+=back
+=cut
+
+sub SaveLogFailures($)
+{
+ my ($LogFailures) = @_;
+
+ # FIXME $Task->Failures->AddFilter() cannot be undone and impacts every
+ # future use of $Task->Failures. So add the filter on a throw away
+ # clone to not end up with a nonsensical filter.
+ my $TaskFailures = $LogFailures->{Task}->Failures->Clone();
+ $TaskFailures->AddFilter("TaskLog", [$LogFailures->{LogName}]);
+ my $ErrMessage = $TaskFailures->DeleteAll();
+ return $ErrMessage if (defined $ErrMessage);
+ return undef if (!$LogFailures->{Failures});
+
+ foreach my $LogFailure (values %{$LogFailures->{Failures}})
+ {
+ my $TaskFailure = $LogFailure->{Failure}->TaskFailures->Add();
+ my $OldKey = $TaskFailure->GetKey();
+ $TaskFailure->Task($LogFailures->{Task});
+ $TaskFailure->TaskLog($LogFailures->{LogName});
+ $TaskFailure->KeyChanged($OldKey, $TaskFailure->GetKey());
+ $TaskFailure->NewCount($LogFailure->{NewCount});
+ $TaskFailure->OldCount($LogFailure->{OldCount});
+ }
+
+ (my $_ErrKey, my $_ErrProperty, $ErrMessage) = $LogFailures->{Collection}->Save();
+ return $ErrMessage;
+}
+
#
# Log errors caching [Part 2]
@@ -1322,8 +1495,11 @@ sub CreateLogErrorsCache($;$)
# Don't mark the errors as new if there is no reference WineTest report
# as this would cause false positives.
}
+ my $LogFailures = MatchLogFailures($LogInfo, $Task) if ($Task);
- return _SaveLogErrors($LogInfo);
+ my $ErrMessage = _SaveLogErrors($LogInfo);
+ $ErrMessage ||= SaveLogFailures($LogFailures) if ($Task);
+ return $ErrMessage;
}
More information about the wine-cvs
mailing list