blob: 31148e3a44e255198b779a6031ea78cf70fc74e1 [file] [log] [blame]
#!/usr/bin/perl
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) 2019 Cyril Hrubis <chrubis@suse.cz>
# Copyright (c) 2020-2021 Petr Vorel <pvorel@suse.cz>
use strict;
use warnings;
use JSON qw(decode_json);
use Cwd qw(abs_path);
use File::Basename qw(dirname);
use constant OUTDIR => dirname(abs_path($0));
# tags which expect git tree, also need constant for URL
our @TAGS_GIT = ("linux-git", "linux-stable-git", "glibc-git");
# tags should map these in lib/tst_test.c
use constant LINUX_GIT_URL => "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=";
use constant LINUX_STABLE_GIT_URL => "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=";
use constant GLIBC_GIT_URL => "https://sourceware.org/git/?p=glibc.git;a=commit;h=";
use constant CVE_DB_URL => "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-";
sub load_json
{
my ($fname, $mode) = @_;
local $/;
open(my $fh, '<', $fname) or die("Can't open $fname $!");
return <$fh>;
}
sub log_info
{
my $msg = shift;
print STDERR "INFO: $msg\n";
}
sub log_warn
{
my $msg = shift;
print STDERR "WARN: $msg\n";
}
sub print_asciidoc_page
{
my ($fh, $json, $title, $content) = @_;
print $fh <<EOL;
// -*- mode:doc; -*-
// vim: set syntax=asciidoc:
$title
$content
EOL
}
sub tag_url {
my ($tag, $value, $scm_url_base) = @_;
if ($tag eq "fname") {
return $scm_url_base . $value;
}
if ($tag eq "CVE") {
return CVE_DB_URL . $value;
}
# *_GIT_URL
my $key = tag2env($tag) . "_URL";
if (defined($constant::declared{"main::$key"})) {
return eval("main::$key") . $value;
}
die("unknown constant '$key' for tag $tag, define it!");
}
sub bold
{
return "*$_[0]*";
}
sub code
{
return "+$_[0]+";
}
sub hr
{
return "\n\n'''\n\n";
}
sub html_a
{
my ($url, $text) = @_;
# escape ]
$text =~ s/([]])/\\$1/g;
return "$url\[$text\]";
}
sub h1
{
return "== $_[0]\n";
}
sub h2
{
return "=== $_[0]\n";
}
sub h3
{
return "==== $_[0]\n";
}
sub label
{
return "[[$_[0]]]\n";
}
sub paragraph
{
return "$_[0]\n\n";
}
sub reference
{
my ($link, %args) = @_;
$args{text} //= $link;
$args{delimiter} //= "";
return "xref:$link\[$args{text}\]$args{delimiter}\n";
}
sub table
{
return "|===\n";
}
sub table_escape
{
my $out = $_[0];
$out =~ s/\|/\\|/g;
return $out;
}
sub print_defined
{
my ($key, $val, $val2) = @_;
if (defined($val)) {
return paragraph(bold($key) . ": " . $val . (defined($val2) ? " $val2" : ""));
}
}
sub content_about
{
my $json = shift;
my $content;
$content .= print_defined("URL", $json->{'url'});
$content .= print_defined("Version", $json->{'version'});
$content .= print_defined("Default timeout", $json->{'timeout'}, "seconds");
return $content;
}
sub uniq {
my %seen;
grep !$seen{$_}++, @_;
}
sub get_test_names
{
my @names = @{$_[0]};
my ($letter, $prev_letter);
my $content;
for my $name (sort @names) {
$letter = substr($name, 0, 1);
if (defined($prev_letter) && $letter ne $prev_letter) {
$content .= "\n";
}
$content .= reference($name, delimiter => " ");
$prev_letter = $letter;
}
$content .= "\n";
return $content;
}
sub get_test_letters
{
my @names = @{$_[0]};
my $letter;
my $prev_letter = "";
my $content;
for (@names) {
$_ = substr($_, 0, 1);
}
@names = uniq(@names);
for my $letter (@names) {
$content .= reference($letter);
}
$content .= "\n";
return $content;
}
sub tag2title
{
my $tag = shift;
return code(".$tag");
}
sub get_filters
{
my $json = shift;
my %data;
while (my ($k, $v) = each %{$json->{'tests'}}) {
for my $j (keys %{$v}) {
next if ($j eq 'fname' || $j eq 'doc');
$data{$j} = () unless (defined($data{$j}));
if ($j eq 'tags') {
for my $tags (@{$v}{'tags'}) {
for my $tag (@$tags) {
my $k2 = $$tag[0];
my $v2 = $$tag[1];
$data{$j}{$k2} = () unless (defined($data{$j}{$k2}));
push @{$data{$j}{$k2}}, $k unless grep{$_ eq $k} @{$data{$j}{$k2}};
}
}
} else {
push @{$data{$j}}, $k unless grep{$_ eq $k} @{$data{$j}};
}
}
}
return \%data;
}
sub content_filter
{
my $k = $_[0];
my $title = $_[1];
my $desc = $_[2];
my $h = $_[3];
my ($letter, $prev_letter, $content);
$content = label($k);
$content .= $title;
$content .= paragraph("Tests containing $desc flag.");
$content .= get_test_names(\@{$h});
return $content;
}
sub content_filters
{
my $json = shift;
my $data = get_filters($json);
my %h = %$data;
my $content;
for my $k (sort keys %$data) {
my $title = tag2title($k);
if (ref($h{$k}) eq 'HASH') {
$content .= label($k);
$content .= h2($title);
for my $k2 (sort keys %{$h{$k}}) {
my $title2 = code($k2);
$content .= content_filter($k2, h3($title2), "$title $title2", $h{$k}{$k2});
}
} else {
$content .= content_filter($k, h2($title), $title, \@{$h{$k}});
}
}
return $content;
}
sub tag2env
{
my $tag = shift;
$tag =~ s/-/_/g;
return uc($tag);
}
sub detect_git
{
my %data;
for my $tag (@TAGS_GIT) {
my $env = tag2env($tag);
unless (defined $ENV{$env} && $ENV{$env}) {
log_warn("git repository $tag not defined. Define it in \$$env");
next;
}
unless (-d $ENV{$env}) {
log_warn("\$$env does not exit ('$ENV{$env}')");
next;
}
if (system("which git >/dev/null")) {
log_warn("git not in \$PATH ('$ENV{'PATH'}')");
next;
}
chdir($ENV{$env});
if (!system("git log -1 > /dev/null")) {
log_info("using '$ENV{$env}' as $env repository");
$data{$tag} = $ENV{$env};
} else {
log_warn("git failed, git not installed or \$$env is not a git repository? ('$ENV{$env}')");
}
chdir(OUTDIR);
}
return \%data;
}
sub content_all_tests
{
my $json = shift;
my @names = sort keys %{$json->{'tests'}};
my $letters = paragraph(get_test_letters(\@names));
my $git_url = detect_git();
my $tmp = undef;
my $printed = "";
my $content;
$content .= paragraph("Total $#names tests.");
$content .= $letters;
$content .= get_test_names(\@names);
for my $name (@names) {
my $letter = substr($name, 0, 1);
if ($printed ne $letter) {
$content .= label($letter);
$content .= h2($letter);
$printed = $letter;
}
$content .= hr() if (defined($tmp));
$content .= label($name);
$content .= h3($name);
$content .= $letters;
if (defined($json->{'scm_url_base'}) &&
defined($json->{'tests'}{$name}{fname})) {
$content .= paragraph(html_a(tag_url("fname", $json->{'tests'}{$name}{fname},
$json->{'scm_url_base'}), "source"));
}
if (defined $json->{'tests'}{$name}{doc}) {
for my $doc (@{$json->{'tests'}{$name}{doc}}) {
# fix formatting for asciidoc [DOCUMENTATION] => *Documentation*
if ($doc =~ s/^\[(.*)\]$/$1/) {
$doc = paragraph(bold(ucfirst(lc($doc))));
}
$content .= "$doc\n";
}
$content .= "\n";
}
if ($json->{'tests'}{$name}{timeout}) {
if ($json->{'tests'}{$name}{timeout} eq -1) {
$content .= paragraph("Test timeout is disabled");
} else {
$content .= paragraph("Test timeout is $json->{'tests'}{$name}{timeout} seconds");
}
} else {
$content .= paragraph("Test timeout defaults to $json->{'timeout'} seconds");
}
my $tmp2 = undef;
for my $k (sort keys %{$json->{'tests'}{$name}}) {
my $v = $json->{'tests'}{$name}{$k};
next if ($k eq "tags" || $k eq "fname" || $k eq "doc");
if (!defined($tmp2)) {
$content .= table . "|Key|Value\n\n"
}
$content .= "|" . reference($k, text => tag2title($k)) . "\n|";
if (ref($v) eq 'ARRAY') {
# two dimensional array
if (ref(@$v[0]) eq 'ARRAY') {
for my $v2 (@$v) {
$content .= paragraph(table_escape(join(' ', @$v2)));
}
} else {
# one dimensional array
$content .= table_escape(join(', ', @$v));
}
} else {
# plain content
$content .= table_escape($v);
}
$content .= "\n";
$tmp2 = 1;
}
if (defined($tmp2)) {
$content .= table . "\n";
}
$tmp2 = undef;
my %commits;
my @sorted_tags = sort { $a->[0] cmp $b->[0] } @{$json->{'tests'}{$name}{tags} // []};
for my $tag (@sorted_tags) {
if (!defined($tmp2)) {
$content .= table . "|Tags|Info\n"
}
my $k = @$tag[0];
my $v = @$tag[1];
if (defined($$git_url{$k})) {
$commits{$k} = () unless (defined($commits{$k}));
unless (defined($commits{$k}{$v})) {
chdir($$git_url{$k});
$commits{$k}{$v} = `git log --pretty=format:'%s' -1 $v`;
chdir(OUTDIR);
}
$v .= ' ("' . $commits{$k}{$v} . '")';
}
$v = html_a(tag_url($k, @$tag[1]), $v);
$content .= "\n|" . reference($k) . "\n|$v\n";
$tmp2 = 1;
}
if (defined($tmp2)) {
$content .= table . "\n";
}
$tmp = 1;
}
return $content;
}
my $json = decode_json(load_json($ARGV[0]));
my $config = [
{
file => "about.txt",
title => h2("About $json->{'testsuite'}"),
content => \&content_about,
},
{
file => "filters.txt",
title => h1("Test filtered by used flags"),
content => \&content_filters,
},
{
file => "all-tests.txt",
title => h1("All tests"),
content => \&content_all_tests,
},
];
sub print_asciidoc_main
{
my $config = shift;
my $file = "metadata.txt";
my $content;
open(my $fh, '>', $file) or die("Can't open $file $!");
$content = <<EOL;
:doctype: inline
:sectanchors:
:toc:
EOL
for my $c (@{$config}) {
$content .= "include::$c->{'file'}\[\]\n";
}
print_asciidoc_page($fh, $json, h1($json->{'testsuite_short'} . " test catalog"), $content);
}
for my $c (@{$config}) {
open(my $fh, '>', $c->{'file'}) or die("Can't open $c->{'file'} $!");
print_asciidoc_page($fh, $json, $c->{'title'}, $c->{'content'}->($json));
}
print_asciidoc_main($config);