Reports.pm 9.43 KB
Newer Older
1
2
3
4
5
6
7
package TBBTestSuite::Reports;

use warnings;
use strict;
use FindBin;
use File::Temp;
use File::Path qw(make_path);
8
use File::Copy;
boklm's avatar
boklm committed
9
use File::Slurp;
10
11
use Template;
use File::Spec;
boklm's avatar
boklm committed
12
use JSON;
13
use YAML;
boklm's avatar
boklm committed
14
use TBBTestSuite::Common qw(exit_error as_array);
15
use TBBTestSuite::Options qw($options);
boklm's avatar
boklm committed
16
use TBBTestSuite::Tests;
boklm's avatar
boklm committed
17
18
use Email::Simple;
use Email::Sender::Simple qw(try_to_sendmail);
19

20
21
22
23
our (@ISA, @EXPORT_OK);
BEGIN {
    require Exporter;
    @ISA       = qw(Exporter);
boklm's avatar
boklm committed
24
    @EXPORT_OK = qw(load_report report_dir report_path save_report);
25
26
}

boklm's avatar
boklm committed
27
my %reports;
28
my %summaries;
boklm's avatar
boklm committed
29

30
31
my %template_functions = (
    is_test_error => \&TBBTestSuite::Tests::is_test_error,
boklm's avatar
boklm committed
32
    is_test_warning => \&TBBTestSuite::Tests::is_test_warning,
33
    is_test_known => \&TBBTestSuite::Tests::is_test_known,
boklm's avatar
boklm committed
34
    test_by_name => \&TBBTestSuite::Tests::test_by_name,
35
36
);

37
sub report_dir {
38
    my ($report) = @_;
39
40
    my $rdir = $options->{'reports-dir'} . '/r';
    make_path($rdir) unless -d $rdir;
41
    if ($report->{options}{name}) {
42
43
44
        my $reportdir = "$rdir/$report->{options}{name}";
        make_path($reportdir) unless -d $reportdir;
        return $reportdir;
45
    }
46
    my $reportdir = File::Temp::newdir(
47
        'XXXXXX',
boklm's avatar
boklm committed
48
        DIR => $rdir,
49
        CLEANUP => 0)->dirname;
50
    (undef, undef, $report->{options}{name})
51
52
53
54
55
56
                = File::Spec->splitpath($reportdir);
    return $reportdir;
}

sub report_path {
    report_dir($_[0]) . '/' . $_[1];
57
58
}

59
60
61
62
63
64
65
66
67
68
sub copy_static {
    my $staticdir = "$options->{'reports-dir'}/static";
    mkdir $staticdir unless -d $staticdir;
    foreach my $file_src (glob "$FindBin::Bin/static/*") {
        my (undef, undef, $file) = File::Spec->splitpath($file_src);
        my $file_dst = "$staticdir/$file";
        copy($file_src, $file_dst) unless -f $file_dst;
    }
}

69
70
sub make_report {
    my ($report) = @_;
71
    copy_static;
72
73
    my $template = Template->new(
        ENCODING => 'utf8',
74
75
        INCLUDE_PATH => "$FindBin::Bin/tmpl:" . report_dir($report),
        OUTPUT_PATH => report_dir($report),
76
    );
77
78
79
80
81
82
    foreach my $tbbfile (keys %{$report->{tbbfiles}}) {
        my $type = $report->{tbbfiles}{$tbbfile}{type};
        my $testsuite = $TBBTestSuite::Tests::testsuite_types{$type};
        $testsuite->{pre_makereport}($report, $tbbfile)
                if $testsuite->{pre_makereport};
    }
83
84
    my %r = ( %template_functions, %$report );
    $template->process('screenshots.html', \%r, 'screenshots.html',
85
                       binmode => ':utf8');
86
    $template->process('testrun_report.html', \%r, 'index.html',
87
                       binmode => ':utf8');
88
    foreach my $tbbfile (keys %{$report->{tbbfiles}}) {
89
        $template->process('report.html', { %r, tbbfile => $tbbfile },
90
91
92
93
94
                "$tbbfile.html", binmode => ':utf8')
                || exit_error "Template Error:\n" . $template->error;
    }
}

95
96
97
sub report_type {
    my ($report) = @_;
    foreach my $tbbfile (values %{$report->{tbbfiles}}) {
98
99
100
        return 'browserbundle' if not defined $tbbfile->{type};
        return 'browserbundle' if $tbbfile->{type} eq 'tbbfile';
        return $tbbfile->{type};
101
102
103
104
    }
    return 'browserbundle';
}

105
sub make_reports_index {
106
107
108
109
110
111
112
113
114
    my ($changed_report) = @_;
    my %pre_reports_index;
    my @changed_tags;
    my @changed_type;
    if ($changed_report) {
        @changed_tags = $changed_report->{options}{tags} ?
                        @{$changed_report->{options}{tags}} : ();
        push @changed_type, report_type($changed_report);
    }
115
    copy_static;
116
117
118
119
120
    my $template = Template->new(
        ENCODING => 'utf8',
        INCLUDE_PATH => "$FindBin::Bin/tmpl",
        OUTPUT_PATH => $options->{'reports-dir'},
    );
boklm's avatar
boklm committed
121
    foreach my $dir (glob "$options->{'reports-dir'}/r/*") {
boklm's avatar
boklm committed
122
        my $resfile = "$dir/report.yml";
123
124
        next unless -f $resfile;
        my (undef, undef, $name) = File::Spec->splitpath($dir);
125
        load_report_summary($name);
126
127
128
    }
    my @reports_by_time =
        sort { $reports{$b}->{time} <=> $reports{$a}->{time} } keys %reports;
boklm's avatar
boklm committed
129
    my %reports_by_tag;
130
    my %reports_by_type;
131
132
    foreach my $report (keys %summaries) {
        my $type = $summaries{$report}->{type};
133
        push @{$reports_by_type{$type}}, $report;
134
        my $tags = as_array($summaries{$report}->{options}{tags} // []);
boklm's avatar
boklm committed
135
        foreach my $tag (@$tags) {
boklm's avatar
boklm committed
136
137
            push @{$reports_by_tag{$type}->{$tag}}, $report;
        }
boklm's avatar
boklm committed
138
    }
139
    my $vars = {
140
        %template_functions,
141
        reports => \%reports,
boklm's avatar
boklm committed
142
        reports_list => \@reports_by_time,
143
        reports_by_type => \%reports_by_type,
144
        reports_by_tag => \%reports_by_tag,
145
    };
146
147
    $template->process('reports_index.html', $vars, 'index.html')
                || exit_error "Template Error:\n" . $template->error;
boklm's avatar
boklm committed
148
    $template->process('tests_index.html', { %$vars, tests =>
149
150
            \@TBBTestSuite::Tests::tests }, 'tests.html')
                || exit_error "Template Error:\n" . $template->error;
151
    foreach my $type ($changed_report ? @changed_type : keys %reports_by_type) {
152
        my @s = sort { $summaries{$b}->{time} <=> $summaries{$a}->{time} }
153
                @{$reports_by_type{$type}};
154
        @s = @s[0..19] if @s > 20;
155
        load_reports_for_index(\%pre_reports_index, @s);
boklm's avatar
boklm committed
156
        my $title = "Last 10 reports";
157
        $template->process("reports_index_$type.html",
boklm's avatar
boklm committed
158
          { %$vars, reports_list => \@s, title => $title }, "index-$type.html")
159
                || exit_error "Template Error:\n" . $template->error;
160
    }
161
162
163
    foreach my $type ($changed_report ? @changed_type : keys %reports_by_tag) {
        foreach my $tag ($changed_report ? @changed_tags
                                : keys %{$reports_by_tag{$type}}) {
164
            my @s = sort { $summaries{$b}->{time} <=> $summaries{$a}->{time} }
boklm's avatar
boklm committed
165
                @{$reports_by_tag{$type}->{$tag}};
166
            load_reports_for_index(\%pre_reports_index, @s);
boklm's avatar
boklm committed
167
            my $title = "Reports for $tag";
168
            $template->process("reports_index_$type.html",
boklm's avatar
boklm committed
169
170
                    { %$vars, reports_list => \@s, title => $title, },
                    "index-$type-$tag.html")
boklm's avatar
boklm committed
171
172
173
                        || exit_error "Template Error:\n" . $template->error;
        }
    }
174
175
}

boklm's avatar
boklm committed
176
177
178
179
180
181
182
sub text_report {
    my ($report) = @_;
    my $res;
    my $template = Template->new(
        ENCODING => 'utf8',
        INCLUDE_PATH => "$FindBin::Bin/tmpl",
    );
183
184
    $template->process('testrun_report.txt', { %template_functions, %$report },
                        \$res, binmode => ':utf8')
boklm's avatar
boklm committed
185
186
187
188
                || exit_error "Template Error:\n" . $template->error;
    return $res;
}

boklm's avatar
boklm committed
189
190
sub email_report {
    my ($report) = @_;
191
    exit_error 'email-to is not defined' unless @{$report->{options}{'email-to'}};
boklm's avatar
boklm committed
192
193
194
195
196
    my ($subject, $body);
    my $template = Template->new(
        ENCODING => 'utf8',
        INCLUDE_PATH => "$FindBin::Bin/tmpl",
    );
197
198
    my %r = ( %template_functions, %$report );
    $template->process(\$report->{options}{'email-subject'}, \%r, \$subject,
boklm's avatar
boklm committed
199
200
                       binmode => ':utf8')
                || exit_error "Template Error:\n" . $template->error;
201
    $template->process('testrun_report.txt', \%r, \$body,
boklm's avatar
boklm committed
202
203
                       binmode => ':utf8')
                || exit_error "Template Error:\n" . $template->error;
204
    foreach my $email_to (@{$report->{options}{'email-to'}}) {
boklm's avatar
boklm committed
205
206
        my $email = Email::Simple->create(
            header => [
207
                From    => $report->{options}{'email-from'},
boklm's avatar
boklm committed
208
209
210
211
212
213
214
215
216
217
218
                To      => $email_to,
                Subject => $subject,
            ],
            body => $body,
        );
        if (!try_to_sendmail($email)) {
            print STDERR "Warning: Error sending email to $email_to\n";
        }
    }
}

219
220
sub load_report {
    my ($report_name) = @_;
boklm's avatar
boklm committed
221
    return $reports{$report_name} if exists $reports{$report_name};
222
223
    my $reportfile = "$options->{'reports-dir'}/r/$report_name/report.yml";
    return undef unless -f $reportfile;
boklm's avatar
boklm committed
224
    return $reports{$report_name} = YAML::LoadFile($reportfile);
225
226
}

227
228
229
230
231
232
233
234
235
sub load_reports_for_index {
    my ($pre_reports_index, @reports) = @_;
    foreach my $rname (@reports) {
        my $r = load_report($rname);
        my $testsuite = $TBBTestSuite::Tests::testsuite_types{report_type($r)};
        if ($testsuite->{pre_reports_index} && !$pre_reports_index->{$rname}) {
            $pre_reports_index->{$rname} = 1;
            $testsuite->{pre_reports_index}(\%reports, $reports{$rname});
        }
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
    }
}

sub load_report_summary {
    my ($report_name) = @_;
    return $summaries{$report_name} if exists $summaries{$report_name};
    my $summaryfile = "$options->{'reports-dir'}/r/$report_name/summary.json";
    if (!-f $summaryfile) {
        $summaries{$report_name} = report_summary(load_report($report_name));
        save_report_summary($reports{$report_name});
        return $summaries{$report_name};
    }
    return $summaries{$report_name} = decode_json read_file $summaryfile;
}

boklm's avatar
boklm committed
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
sub report_summary {
    my ($report) = @_;
    my %res = (
        type => report_type($report),
        time => 1,
    );
    foreach my $o (qw(report_format time success)) {
        $res{$o} = $report->{$o} if $report->{$o};
    }
    $res{options}->{tags} = $report->{options}{tags} // [];
    my $tbbver = $report->{options}{tbbversion};
    push @{$res{options}->{tags}}, $tbbver if $tbbver;
    return \%res;
}

sub save_report_summary {
    my ($report) = @_;
    write_file(report_path($report, 'summary.json'),
               encode_json(report_summary($report)));
}

boklm's avatar
boklm committed
272
273
sub save_report {
    my ($report) = @_;
boklm's avatar
boklm committed
274
    save_report_summary($report);
boklm's avatar
boklm committed
275
276
277
    YAML::DumpFile(report_path($report, 'report.yml'), $report);
}

278
1;