This provides a link to the most likely culprit when a test starts failing.
Signed-off-by: Francois Gouget fgouget@codeweavers.com --- winetest/build-patterns | 149 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 3 deletions(-)
diff --git a/winetest/build-patterns b/winetest/build-patterns index a5f98f5f3..dac2eef04 100755 --- a/winetest/build-patterns +++ b/winetest/build-patterns @@ -31,7 +31,7 @@ sub BEGIN } unshift @INC, $1 if ($0 =~ m=^(/.*)/[^/]+$=); } -use vars qw/$workdir $gitdir $patternbuilds $fixed_threshold/; +use vars qw/$workdir $gitdir $gitweb $patternbuilds $fixed_threshold/; require "winetest.conf";
my $name0=$0; @@ -249,6 +249,9 @@ my %reports; # - name # The uniquely identifying test name in the form 'module:unit'. # +# - source +# The source file for that test unit. +# # - colors # A hashtable of colors indexed by failure count. # @@ -303,6 +306,10 @@ my %reports; # # - status # A hashtable of test results indexed by the build name. +# +# - commits +# A hashtable of the commit objects, indexed first buy build name and then +# by commit id. my %tests;
foreach my $build (@sortedbuilds) @@ -337,7 +344,7 @@ foreach my $build (@sortedbuilds) while (my $line = <$fh>) { chomp $line; - my ($testname, $_source, @items) = split / /, $line; + my ($testname, $source, @items) = split / /, $line; if ($testname !~ /:/) { error("found an invalid test unit name ($testname) in '$filename'\n"); @@ -345,7 +352,13 @@ foreach my $build (@sortedbuilds) } $build->{hastest}->{$testname} = 1; my $test = $tests{$testname}; - $tests{$testname} = $test = { name => $testname } if (!$test); + if (!$test) + { + $tests{$testname} = $test = { + name => $testname, + source => $source, + }; + }
foreach my $statreps (@items) { @@ -602,6 +615,80 @@ foreach my $testname (keys %tests) }
+# +# Collect the related commits +# + +# A hashtable of commit objects indexed by their Git commit id. +# Each object has the following fields: +# +# - index +# An integer recording the commit order, with 0 for the newest commit and +# negative numbers for the others. +# +# - id +# The Git commit id. +# +# - build +# The Git commit id of the first build after this commit. +# +# - summary +# The commit summary. +my %commits; + +# A hashtable mapping filenames to a set of matching commit objects indexed by +# Git commit id. +my %path2commits; + +my $cmd = "git log --pretty=oneline --name-status $sortedbuilds[0]->{name}^..$sortedbuilds[-1]->{name}"; +if (open(my $fh, "-|", $cmd)) +{ + my $index = 0; + my ($commit, $build); + foreach my $line (<$fh>) + { + chomp $line; + if ($line =~ /^([0-9a-f]{40}) (.*)$/) + { + my ($commitid, $summary) = ($1, $2); + # The commits are returned in reverse chronological order. So the + # build commit comes first, then the commits that got into it. + $build = $builds{$commitid} if ($builds{$commitid}); + if (!$build) + { + error("$commit does not belong to any build\n"); + next; + } + $commits{$commitid} = $commit = { + index => $index--, + id => $commitid, + build => $build, + summary => $summary, + }; + } + elsif ($line =~ /^R[0-9]+\t(\S+)\t(\S+)$/ or $line =~ /^.\t(\S+)$/) + { + my ($path1, $path2) = ($1, $2); + if (!$commit) + { + error("did not find a commit before: $line\n"); + next; + } + foreach my $filename ($path1, $path2) + { + next if (!$filename); + $path2commits{$filename}->{$commit->{id}} = $commit; + } + } + else + { + error("found an unsupported line type in '$cmd' output: $line\n"); + } + } + close($fh); +} + + # # Compute color gradients # @@ -828,6 +915,54 @@ sub write_pattern_line($$$) print $html "</div> $label\n"; }
+sub index2symbol($) +{ + my ($index) = @_; + my @symbols = ("0".."9", "a".."z", "A".."Z"); + return $symbols[$index % 62]; +} + +sub write_commits($$) +{ + my ($html, $test) = @_; + + print $html "<div class='pattern'>"; + my $i = 0; + my @symbuilds; + foreach my $build (@sortedbuilds) + { + if ($test->{commits}->{$build->{name}}) + { + my $symbol = index2symbol($i++); + push @symbuilds, [$symbol, $build]; + printf $html "<a title='%s'>%s</a>", + short_date($build->{date}), $symbol; + } + else + { + print $html " "; + } + } + print $html "</div> <i>← potentially related commits</i>\n"; + + if (@symbuilds) + { + print $html "\n"; + foreach my $symbuild (@symbuilds) + { + my $first = 1; + my ($symbol, $build) = @$symbuild; + my $buildcommits = $test->{commits}->{$build->{name}}; + foreach my $commit (sort { $a->{index} <=> $b->{index} } values %$buildcommits) + { + print $html $first ? "$symbol." : " "; + $first = undef; + printf $html " <a href='%s/commitdiff/%s' title='%s'>%s</a>\n", $gitweb, $commit->{id}, short_date($build->{date}), $commit->{summary}; + } + } + } +} + sub write_pattern($$$) { my ($html, $test, $pagereports) = @_; @@ -841,6 +976,7 @@ sub write_pattern($$$) next if (!$test->{testreports}->{$reportdir}->{failed}); write_pattern_line($html, $test, $reportdir); } + write_commits($html, $test) if (%{$test->{commits}}); }
sub write_patterns_list($$$$) @@ -915,6 +1051,7 @@ EOF { my $test = $tests{$testname}; $test->{colors} = {}; + $test->{commits} = {};
my $first = @sortedbuilds; my $last = -1; @@ -963,6 +1100,12 @@ EOF %{$test->{newmodes}} ? "newmode" : "regular"; push @{$lists{$listid}->{testnames}}, $testname; + + foreach my $commit (values %{$path2commits{$test->{source}}}) + { + my $buildname = $commit->{build}->{name}; + $test->{commits}->{$buildname}->{$commit->{id}} = $commit; + } }
# Generate the lists index (and up test unit links)