Reports.pm 10.7 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;
boklm's avatar
boklm committed
13
use YAML::Syck;
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);
boklm's avatar
boklm committed
19
use DateTime;
20

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

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

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

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

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

60
61
62
63
64
65
66
67
68
69
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;
    }
}

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

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

boklm's avatar
boklm committed
106
107
108
109
110
sub date_month {
    my $dt = DateTime->from_epoch(epoch => $_[0]);
    return $dt->year . '-' . sprintf("%02d", $dt->month);
}

111
sub make_reports_index {
112
113
114
115
    my ($changed_report) = @_;
    my %pre_reports_index;
    my @changed_tags;
    my @changed_type;
boklm's avatar
boklm committed
116
    my @changed_month;
117
118
119
120
    if ($changed_report) {
        @changed_tags = $changed_report->{options}{tags} ?
                        @{$changed_report->{options}{tags}} : ();
        push @changed_type, report_type($changed_report);
boklm's avatar
boklm committed
121
        push @changed_month, date_month($changed_report->{time});
122
    }
123
    copy_static;
124
125
126
127
128
    my $template = Template->new(
        ENCODING => 'utf8',
        INCLUDE_PATH => "$FindBin::Bin/tmpl",
        OUTPUT_PATH => $options->{'reports-dir'},
    );
boklm's avatar
boklm committed
129
    foreach my $dir (glob "$options->{'reports-dir'}/r/*") {
boklm's avatar
boklm committed
130
        my $resfile = "$dir/report.yml";
131
132
        next unless -f $resfile;
        my (undef, undef, $name) = File::Spec->splitpath($dir);
133
        load_report_summary($name);
134
135
136
    }
    my @reports_by_time =
        sort { $reports{$b}->{time} <=> $reports{$a}->{time} } keys %reports;
boklm's avatar
boklm committed
137
    my %reports_by_tag;
138
    my %reports_by_type;
boklm's avatar
boklm committed
139
    my %reports_by_month;
140
141
    foreach my $report (keys %summaries) {
        my $type = $summaries{$report}->{type};
142
        push @{$reports_by_type{$type}}, $report;
143
        my $tags = as_array($summaries{$report}->{options}{tags} // []);
boklm's avatar
boklm committed
144
        foreach my $tag (@$tags) {
boklm's avatar
boklm committed
145
146
            push @{$reports_by_tag{$type}->{$tag}}, $report;
        }
boklm's avatar
boklm committed
147
148
        my $month = date_month($summaries{$report}->{time});
        push @{$reports_by_month{$type}->{$month}}, $report;
boklm's avatar
boklm committed
149
    }
150
    my $vars = {
151
        %template_functions,
152
        reports => \%reports,
boklm's avatar
boklm committed
153
        reports_list => \@reports_by_time,
154
        reports_by_type => \%reports_by_type,
155
        reports_by_tag => \%reports_by_tag,
boklm's avatar
boklm committed
156
        reports_by_month => \%reports_by_month,
boklm's avatar
boklm committed
157
        testsuite_types => \%TBBTestSuite::Tests::testsuite_types,
158
    };
159
160
    $template->process('reports_index.html', $vars, 'index.html')
                || exit_error "Template Error:\n" . $template->error;
161
    foreach my $type ($changed_report ? @changed_type : keys %reports_by_type) {
162
        my @s = sort { $summaries{$b}->{time} <=> $summaries{$a}->{time} }
163
                @{$reports_by_type{$type}};
164
        @s = @s[0..19] if @s > 20;
165
        load_reports_for_index(\%pre_reports_index, @s);
boklm's avatar
boklm committed
166
        my $title = "Last 20 reports";
167
        $template->process("reports_index_$type.html",
boklm's avatar
boklm committed
168
          { %$vars, reports_list => \@s, title => $title }, "index-$type.html")
169
                || exit_error "Template Error:\n" . $template->error;
boklm's avatar
boklm committed
170
171
172
173
        my ($t) = values %{$reports{$s[0]}->{tbbfiles}};
        $template->process('tests_index.html', { %$vars, testsuite_type => $type,
                tests => $t->{tests} }, "tests-$type.html")
                || exit_error "Template Error:\n" . $template->error;
174
    }
175
176
177
    foreach my $type ($changed_report ? @changed_type : keys %reports_by_tag) {
        foreach my $tag ($changed_report ? @changed_tags
                                : keys %{$reports_by_tag{$type}}) {
178
            my @s = sort { $summaries{$b}->{time} <=> $summaries{$a}->{time} }
boklm's avatar
boklm committed
179
                @{$reports_by_tag{$type}->{$tag}};
180
            load_reports_for_index(\%pre_reports_index, @s);
boklm's avatar
boklm committed
181
            my $title = "Reports for $tag";
182
            $template->process("reports_index_$type.html",
boklm's avatar
boklm committed
183
184
                    { %$vars, reports_list => \@s, title => $title, },
                    "index-$type-$tag.html")
boklm's avatar
boklm committed
185
186
187
                        || exit_error "Template Error:\n" . $template->error;
        }
    }
boklm's avatar
boklm committed
188
189
190
191
192
193
194
195
196
197
198
199
200
    foreach my $type ($changed_report ? @changed_type : keys %reports_by_month) {
        foreach my $month ($changed_report ? @changed_month
                                : keys %{$reports_by_month{$type}}) {
            my @s = sort { $summaries{$b}->{time} <=> $summaries{$a}->{time} }
                @{$reports_by_month{$type}->{$month}};
            load_reports_for_index(\%pre_reports_index, @s);
            my $title = "$month reports";
            $template->process("reports_index_$type.html",
                    { %$vars, reports_list => \@s, title => $title, },
                    "index-$type-$month.html")
                        || exit_error "Template Error:\n" . $template->error;
            }
    }
201
202
}

boklm's avatar
boklm committed
203
204
205
206
207
208
209
sub text_report {
    my ($report) = @_;
    my $res;
    my $template = Template->new(
        ENCODING => 'utf8',
        INCLUDE_PATH => "$FindBin::Bin/tmpl",
    );
210
211
    $template->process('testrun_report.txt', { %template_functions, %$report },
                        \$res, binmode => ':utf8')
boklm's avatar
boklm committed
212
213
214
215
                || exit_error "Template Error:\n" . $template->error;
    return $res;
}

boklm's avatar
boklm committed
216
217
sub email_report {
    my ($report) = @_;
218
    exit_error 'email-to is not defined' unless @{$report->{options}{'email-to'}};
boklm's avatar
boklm committed
219
220
221
222
223
    my ($subject, $body);
    my $template = Template->new(
        ENCODING => 'utf8',
        INCLUDE_PATH => "$FindBin::Bin/tmpl",
    );
224
225
    my %r = ( %template_functions, %$report );
    $template->process(\$report->{options}{'email-subject'}, \%r, \$subject,
boklm's avatar
boklm committed
226
227
                       binmode => ':utf8')
                || exit_error "Template Error:\n" . $template->error;
228
    $template->process('testrun_report.txt', \%r, \$body,
boklm's avatar
boklm committed
229
230
                       binmode => ':utf8')
                || exit_error "Template Error:\n" . $template->error;
231
    foreach my $email_to (@{$report->{options}{'email-to'}}) {
boklm's avatar
boklm committed
232
233
        my $email = Email::Simple->create(
            header => [
234
                From    => $report->{options}{'email-from'},
boklm's avatar
boklm committed
235
236
237
238
239
240
241
242
243
244
245
                To      => $email_to,
                Subject => $subject,
            ],
            body => $body,
        );
        if (!try_to_sendmail($email)) {
            print STDERR "Warning: Error sending email to $email_to\n";
        }
    }
}

246
247
sub load_report {
    my ($report_name) = @_;
boklm's avatar
boklm committed
248
    return $reports{$report_name} if exists $reports{$report_name};
249
250
    my $reportfile = "$options->{'reports-dir'}/r/$report_name/report.yml";
    return undef unless -f $reportfile;
boklm's avatar
boklm committed
251
    return $reports{$report_name} = YAML::Syck::LoadFile($reportfile);
252
253
}

254
255
256
257
258
259
260
261
262
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});
        }
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
    }
}

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
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
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
299
300
sub save_report {
    my ($report) = @_;
boklm's avatar
boklm committed
301
    save_report_summary($report);
boklm's avatar
boklm committed
302
    YAML::Syck::DumpFile(report_path($report, 'report.yml'), $report);
boklm's avatar
boklm committed
303
304
}

305
1;