PR #2 opened by Kacper Michajłow (kasper93) URL: https://code.ffmpeg.org/FFmpeg/fateserver/pulls/2 Patch URL: https://code.ffmpeg.org/FFmpeg/fateserver/pulls/2.patch
From e2901da35bbf0e61d42df13dc3288593e25407dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Thu, 30 Jul 2026 01:03:02 +0200 Subject: [PATCH 1/6] FATE: accept a "desc" prefix on any sort key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prefix shared the 10 character budget of the key it reverses, so the links the index itself generates for the Comment and Arch columns were rejected with "Invalid sort value". Signed-off-by: Kacper Michajłow <[email protected]> --- FATE.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/FATE.pm b/FATE.pm index d74fd00..804dcd2 100644 --- a/FATE.pm +++ b/FATE.pm @@ -489,7 +489,9 @@ sub safeparam($$) { } sub safeparam_sort() { - return safeparam_opt qr/[a-z]{1,10}(?:\/\/[a-z]{1,10})*/, 'sort'; + # The "desc" prefix reversing a key must not eat into its length budget. + my $key = qr/(?:desc)?[a-z]{1,10}/; + return safeparam_opt qr/$key(?:\/\/$key)*/, 'sort'; } sub safeparam_slot() { -- 2.52.0 From 4af3b82eba59fff9de0950c02ca7a4fe4ecd0d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Thu, 30 Jul 2026 01:03:26 +0200 Subject: [PATCH 2/6] index: keep the sort direction per key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loop variable aliases the array element, so stripping "desc" edited @sort in place and left $sdir reversed for every remaining key and every later comparison. Signed-off-by: Kacper Michajłow <[email protected]> --- index.cgi | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/index.cgi b/index.cgi index 04b6cbd..4373679 100755 --- a/index.cgi +++ b/index.cgi @@ -84,7 +84,6 @@ $allfail = 100 * $allfail / @reps; my $warn = 100 - $allpass - $allfail; my @sort = ('subarch', 'os', 'cc', 'comment', 'slot'); -my $sdir = 1; # default to ascending sorting defined $sort and unshift @sort, split /\/\//, $sort; $sort ||= $sort[0]; @@ -94,15 +93,13 @@ sub nscmp { } sub repcmp { - my $r; - for my $s (@sort) { - if ($s =~ /^desc/) { - $s =~ s/^desc//; - $sdir = -1; - } - last if $r = $sdir * nscmp $$a{$s}, $$b{$s}; + for my $key (@sort) { + # $key aliases the array element, so do not edit it in place. + (my $s = $key) =~ s/^desc//; + my $r = ($s eq $key ? 1 : -1) * nscmp $$a{$s}, $$b{$s}; + return $r if $r; } - return $r; + return 0; }; sub lsort { -- 2.52.0 From 4c227e8de941374ac04c2bc2be84b861894a6844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Thu, 30 Jul 2026 01:04:06 +0200 Subject: [PATCH 3/6] index: let the Arch column sort in both directions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "arch" is rewritten to "subarch" before lsort compares it against the current key, so the header never matched and never offered the reverse order. Signed-off-by: Kacper Michajłow <[email protected]> --- index.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.cgi b/index.cgi index 4373679..9c9fe6c 100755 --- a/index.cgi +++ b/index.cgi @@ -237,7 +237,7 @@ end 'tr'; start 'tr'; start 'th'; lsort 'Time', 'descdate'; end 'th'; start 'th'; lsort 'Rev', 'rev'; end 'th'; -start 'th'; lsort 'Arch', 'arch'; end 'th'; +start 'th'; lsort 'Arch', 'subarch'; end 'th'; start 'th'; lsort 'OS', 'os'; end 'th'; start 'th'; lsort 'Compiler', 'cc'; end 'th'; start 'th'; lsort 'Comment', 'comment'; end 'th'; -- 2.52.0 From 275ab9a6144fed081e807658d725ccfd901a04df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Thu, 30 Jul 2026 01:05:53 +0200 Subject: [PATCH 4/6] index: allow "/" and "%" in search criteria MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CGI already percent-decodes each parameter, so decoding the query again turned an escaped "/" inside a value back into the "//" separator and tore the criterion in two. Pass one criterion per parameter instead: CGI splits on "&" before decoding, so a value can no longer be mistaken for a separator. Escape values as bytes, as uri_escape_utf8 double-encodes the bytes CGI and the report files actually hand out. Signed-off-by: Kacper Michajłow <[email protected]> --- index.cgi | 86 +++++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/index.cgi b/index.cgi index 9c9fe6c..1df8c89 100755 --- a/index.cgi +++ b/index.cgi @@ -20,7 +20,7 @@ use warnings; use lib "/var/www/fateweb"; -use CGI qw/param/; +use CGI qw/param multi_param/; use HTML::Entities; use FATE; use Time::Zone; @@ -28,12 +28,17 @@ use URI::Escape; cgi_path_is_trustworthy; -# Format for /?query= : /?query=type:value//type:value// (URI encoded). -# Trailing // does not matter (i.e. may be added). -# @queries contains an array of 'type:value' strings. -# Every member of @queries can be further parsed with another simple -# split(/:/, $this_query, 2); -my @queries = split(/\/\//, uri_unescape scalar param 'query') if (param 'query'); +# Format for /?query= : /?query=type:value, repeated once per criterion, as an +# array of [type, value] pairs. CGI splits the parameters before decoding +# them, so a value may contain any character; decoding again here would let a +# '/' in a value act as a separator. +my @queries; +for my $this_query (multi_param 'query') { + my ($type, $text) = split(/:/, $this_query, 2); + defined $text or next; + ($type) = $type =~ /^([a-z]{1,16})\z/ or next; + push @queries, [$type, $text]; +} my $sort = safeparam_sort; $sort = "subarch" if defined($sort) && $sort eq "arch"; @@ -55,8 +60,8 @@ for my $slot (@slots) { my $not_matched = 0; $$rep{subarch} = $$rep{arch} if not $$rep{subarch}; for my $this_query (@queries) { - my ($type, $text) = split(/:/, $this_query, 2); - $not_matched = 1 if ($$rep{$type} ne $text); + my ($type, $text) = @$this_query; + $not_matched = 1 if not defined $$rep{$type} or $$rep{$type} ne $text; } next if $not_matched; @@ -102,16 +107,29 @@ sub repcmp { return 0; }; -sub lsort { - my $params = ''; +# Every parameter except 'query' and, optionally, $skip, in HTTP format. +# 'query' may occur several times and is regenerated by query_params(). +sub other_params { + my ($skip) = @_; + my @params; for my $thisparam (param) { next if $thisparam =~ /[^a-z0-9_]/; - next if $thisparam =~ 'sort'; - $params .= '&' if $params ne ''; - $params .= "$thisparam=" . uri_escape(param($thisparam)); + next if $thisparam eq 'query'; + next if defined $skip and $thisparam eq $skip; + push @params, "$thisparam=" . uri_escape(scalar param($thisparam)); } - $params .= '&' if $params; + return @params; +} + +# Serialise [type, value] pairs as repeated 'query' parameters. Values are +# bytes, as CGI and the report files give them, so escape them as bytes. +sub query_params { + return map "query=" . uri_escape("$$_[0]:" . ($$_[1] // '')), @_; +} + +sub lsort { my ($text, $key) = @_; + my @params = (other_params('sort'), query_params(@queries)); my $newkey = ''; if ($sort eq $key) { # $key = $sort @@ -130,39 +148,21 @@ sub lsort { } $key = $newkey if $newkey ne ''; - anchor $text, href => "?${params}sort=$key"; + push @params, "sort=$key"; + anchor $text, href => '?' . join '&', @params; } sub category { my ($category, $rep) = @_; - my $head_printed = 0; - # $params will contain parameters else than query, if any, in HTTP format. - my $params = ''; - for my $thisparam (param) { - next if $thisparam =~ /[^a-z0-9_]/; - next if $thisparam eq 'query'; - $params .= '&' if $params ne ''; - $params .= "$thisparam=" . uri_escape(param($thisparam)); - } - my $head = ($params ? '&' : '') . 'query='; - - if (@queries) { - for my $this_query (@queries) { - my ($type, $text) = split(/:/, $this_query, 2); - if ($type ne $category) { - $params .= $head if (!$head_printed); - $params .= $this_query . '//'; - $head_printed = 1; - } - } - } - $params .= $head if (!$head_printed); - $params .= "$category:" . uri_escape_utf8 "$$rep{$category}" . '//'; - $head_printed = 1; # for the sake of completeness + # Keep every search criterion except the one for $category, which the + # value in this cell replaces. + my @params = (other_params(), + query_params(grep($$_[0] ne $category, @queries), + [$category, $$rep{$category}])); start 'td'; - anchor $$rep{$category}, href => "?$params"; + anchor $$rep{$category}, href => '?' . join '&', @params; end 'td'; } @@ -197,8 +197,8 @@ if (@queries) { start 'p'; print 'Search patterns: '; for my $this_query (@queries) { - my ($type, $text) = split(/:/, $this_query, 2); - print "$type: $text; "; + my ($type, $text) = @$this_query; + print encode_entities("$type: $text", '<>&"'), '; '; } anchor 'clear all.', href => ""; end 'p'; -- 2.52.0 From 6ac7749560c28606c26625dcf2319496a938e06c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Thu, 30 Jul 2026 01:06:26 +0200 Subject: [PATCH 5/6] index: round the percentages in the failometer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They land in a CSS width and a tooltip, where 17 significant digits are of no use. Signed-off-by: Kacper Michajłow <[email protected]> --- index.cgi | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/index.cgi b/index.cgi index 1df8c89..25ed088 100755 --- a/index.cgi +++ b/index.cgi @@ -84,9 +84,17 @@ for my $slot (@slots) { "<a href=\"\">Clear all search criteria.</a>" : 'No data in $fatedir.'; -$allpass = 100 * $allpass / @reps; -$allfail = 100 * $allfail / @reps; -my $warn = 100 - $allpass - $allfail; +# Percentage of $n out of $total, rounded to one decimal for display. +sub pct { + my ($n, $total) = @_; + return int(1000 * $n / $total + 0.5) / 10; +} + +my $nreps = @reps; +my $warn = $nreps - $allpass - $allfail; +$allpass = pct($allpass, $nreps); +$allfail = pct($allfail, $nreps); +$warn = pct($warn, $nreps); my @sort = ('subarch', 'os', 'cc', 'comment', 'slot'); defined $sort and unshift @sort, split /\/\//, $sort; -- 2.52.0 From 4b23f6da3b3b1119958b7f2c0011a1ed7028d5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Thu, 30 Jul 2026 01:06:59 +0200 Subject: [PATCH 6/6] index, history: show a failure to build tests as a failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A run where every test passed but the status is non-zero failed to build the tests, which report.cgi already flags as a failure. Signed-off-by: Kacper Michajłow <[email protected]> --- history.cgi | 3 ++- index.cgi | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/history.cgi b/history.cgi index 5e4f007..71ecdbb 100755 --- a/history.cgi +++ b/history.cgi @@ -76,7 +76,8 @@ for my $date ((sort { $b cmp $a } @reps)[0..49]) { td $$rep{nwarn}; if ($npass) { $rtext = "$npass / $ntest"; - $rclass = $$rep{status}==0? 'pass' : $npass? 'warn' : 'fail'; + $rclass = !$$rep{status} ? 'pass' : + $npass < $ntest ? 'warn' : 'fail'; } elsif (!$ntest and !$$rep{status}) { $rtext = "build only"; $rclass = $$rep{status}? 'fail' : 'pass'; diff --git a/index.cgi b/index.cgi index 25ed088..d38b999 100755 --- a/index.cgi +++ b/index.cgi @@ -293,7 +293,8 @@ for my $rep (sort repcmp @reps) { td $$rep{comment}, class => 'comment'; if ($npass) { $rtext = "$npass / $ntest"; - $rclass = $$rep{status}==0? 'pass' : $npass? 'warn' : 'fail'; + $rclass = !$$rep{status} ? 'pass' : + $npass < $ntest ? 'warn' : 'fail'; } elsif (!$ntest and !$$rep{status}) { $rtext = "build only"; $rclass = $$rep{status}? 'fail' : 'pass'; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
