summaryrefslogtreecommitdiff
path: root/scripts/code_cov_parse_info
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/code_cov_parse_info')
-rwxr-xr-xscripts/code_cov_parse_info440
1 files changed, 387 insertions, 53 deletions
diff --git a/scripts/code_cov_parse_info b/scripts/code_cov_parse_info
index 206145ef..77291eb6 100755
--- a/scripts/code_cov_parse_info
+++ b/scripts/code_cov_parse_info
@@ -9,6 +9,8 @@ use Pod::Man;
my $prefix = qr ".*?(linux)\w*/";
+my $title = "";
+
my %used_func;
my %all_func;
my %all_branch;
@@ -284,8 +286,12 @@ sub write_filtered_file($)
my $filtered = "";
- foreach my $testname(sort keys %test_names) {
- $filtered .= "TN:$testname\n";
+ if ($title eq "") {
+ foreach my $testname(sort keys %test_names) {
+ $filtered .= "TN:$testname\n";
+ }
+ } else {
+ $filtered .= "TN:$title\n";
}
# Generates filtered data
@@ -390,68 +396,80 @@ sub print_code_coverage($$$)
}
}
-sub print_summary()
+my %stats;
+
+sub gen_stats()
{
- # Output per-line coverage statistics
- my $line_count = 0;
- my $line_reached = 0;
+ # per-line coverage statistics
+ $stats{"line_count"} = 0;
+ $stats{"line_reached"} = 0;
foreach my $source (keys(%all_line)) {
next if (!$used_source{$source});
foreach my $where (keys(%{$all_line{$source}})) {
- $line_count++;
- $line_reached++ if ($all_line{$source}{$where} != 0);
+ $stats{"line_count"}++;
+ $stats{"line_reached"}++ if ($all_line{$source}{$where} != 0);
}
}
- if ($line_count) {
- my $percent = 100. * $line_reached / $line_count;
- printf " lines......: %.1f%% (%d of %d lines)\n",
- $percent, $line_reached, $line_count;
- } else {
- print "No line coverage data.\n";
- }
- # Output per-function coverage statistics
- my $func_count = 0;
- my $func_used = 0;
+ # per-function coverage statistics
+ $stats{"func_count"} = 0;
+ $stats{"func_used"} = 0;
foreach my $func (keys(%all_func)) {
foreach my $file (keys(%{$all_func{$func}})) {
- $func_count++;
+ $stats{"func_count"}++;
if ($used_func{$func}) {
if ($used_func{$func}->{$file}) {
- $func_used++;
+ $stats{"func_used"}++;
}
}
}
}
- if ($func_count) {
- my $percent = 100. * $func_used / $func_count;
- printf " functions..: %.1f%% (%d of %d functions)\n",
- $percent, $func_used, $func_count;
- } else {
- print "No functions reported. Wrong filters?\n";
- return;
- }
-
- # Output per-branch coverage statistics
- my $branch_count = 0;
- my $branch_reached = 0;
+ # per-branch coverage statistics
+ $stats{"branch_count"} = 0;
+ $stats{"branch_reached"} = 0;
foreach my $source (keys(%all_branch)) {
next if (!$used_source{$source});
foreach my $where (keys(%{$all_branch{$source}})) {
- $branch_count++;
- $branch_reached++ if ($all_branch{$source}{$where} != 0);
+ $stats{"branch_count"}++;
+ $stats{"branch_reached"}++ if ($all_branch{$source}{$where} != 0);
}
}
- if ($branch_count) {
- my $percent = 100. * $branch_reached / $branch_count;
+
+ # per-file coverage stats
+ $stats{"all_files"} = scalar keys(%files);
+ $stats{"filtered_files"} = scalar keys(%record);
+ $stats{"used_files"} = scalar keys(%used_source);
+}
+
+sub print_summary()
+{
+ if ($stats{"line_count"}) {
+ my $percent = 100. * $stats{"line_reached"} / $stats{"line_count"};
+ printf " lines......: %.1f%% (%d of %d lines)\n",
+ $percent, $stats{"line_reached"}, $stats{"line_count"};
+ } else {
+ print "No line coverage data.\n";
+ }
+
+ if ($stats{"func_count"}) {
+ my $percent = 100. * $stats{"func_used"} / $stats{"func_count"};
+ printf " functions..: %.1f%% (%d of %d functions)\n",
+ $percent, $stats{"func_used"}, $stats{"func_count"};
+ } else {
+ print "No functions reported. Wrong filters?\n";
+ return;
+ }
+
+ if ($stats{"branch_count"}) {
+ my $percent = 100. * $stats{"branch_reached"} / $stats{"branch_count"};
printf " branches...: %.1f%% (%d of %d branches)\n",
- $percent, $branch_reached, $branch_count;
+ $percent, $stats{"branch_reached"}, $stats{"branch_count"};
} else {
print "No branch coverage data.\n";
}
@@ -514,6 +532,254 @@ sub open_filter_file($$$)
return $filter;
}
+my $gen_report;
+my $css_file;
+my $html_prolog;
+my $html_epilog;
+my %report;
+
+sub generate_report()
+{
+ my $percent;
+ my $prolog = "";
+ my $epilog = "";
+ my @info_files = sort(keys %report);
+
+ $title = "Code coverage results" if ($title eq "");
+
+ if ($html_prolog) {
+ open IN, $html_prolog or die "Can't open prolog file";
+ $prolog .= $_ while (<IN>);
+ close IN;
+ }
+
+ if ($html_epilog) {
+ open IN, $html_epilog or die "Can't open epilog file";
+ $epilog .= $_ while (<IN>);
+ close IN;
+ }
+
+ # Re-generate the hashes used to report stats in order to procuce the
+ # Total results
+
+ %used_func = ();
+ %all_func = ();
+ %all_branch = ();
+ %all_line = ();
+ %used_source = ();
+ %files = ();
+ %test_names = ();
+
+ foreach my $f (@info_files) {
+ foreach my $source (keys(%{$report{$f}{"all_line"}})) {
+ $used_source{$source} = 1 if ($report{$f}{"used_source"});
+ foreach my $where (keys(%{$report{$f}{"all_line"}{$source}})) {
+ $all_line{$source}{$where} += $report{$f}{"all_line"}{$source}{$where};
+ }
+ }
+ foreach my $func (keys(%{$report{$f}{"all_func"}})) {
+ foreach my $file (keys(%{$report{$f}{"all_func"}{$func}})) {
+ $all_func{$func}{$file}->{ln} = $report{$f}{"all_func"}{$func}{$file}->{ln};
+ $used_func{$func}->{$file} = 1 if ($report{$f}{"used_func"}{$func}->{$file});
+ }
+ }
+ foreach my $source (keys(%{$report{$f}{"all_branch"}})) {
+ foreach my $where (keys(%{$report{$f}{"all_branch"}{$source}})) {
+ $all_branch{$source}{"$where"} += $report{$f}{"all_branch"}{$source}{$where};
+ }
+ }
+ for my $source(keys(%{$report{$f}{"files"}})) {
+ $files{$source} = 1;
+ $used_source{$source} = 1 if ($report{$f}{"used_source"}{$source});
+ }
+ for my $test(keys(%{$report{$f}{"test_names"}})) {
+ $test_names{$test} = 1;
+ }
+ }
+ gen_stats();
+
+ # Colors for the html output
+
+ my $red = "style=\"background-color:#ffb3b3\"";
+ my $yellow = "style=\"background-color:#ffffb3\"";
+ my $green = "style=\"background-color:#d9ffd9\"";
+
+ # Open report file
+
+ open OUT, ">$gen_report" or die "Can't open $gen_report";
+
+ print OUT "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
+
+ print OUT "<html lang=\"en\">\n\n";
+ print OUT "<head>\n";
+ print OUT " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n";
+ print OUT " <title>$title</title>\n";
+ print OUT " <link rel=\"stylesheet\" type=\"text/css\" href=\"$css_file\">\n" if ($css_file);
+ print OUT "</head>\n\n<body>\n$prolog";
+
+ print OUT " <h1>$title</h1>\n";
+
+ print OUT " <h2>Summary</h2>\n";
+ # Generates a table containing the code coverage statistics per input
+
+ print OUT "<table width=\"100%\" border=1 cellspacing=0 cellpadding=0>\n <tr>\n";
+ print OUT " <th></th>\n";
+ foreach my $f (@info_files) {
+ print OUT " <th>$f</th>\n";
+ }
+ print OUT " <th>TOTAL</th>\n";
+ print OUT " <th>Total count</th>\n";
+ print OUT " </tr><tr>\n";
+
+ print OUT " <td><b>Functions</b></td>\n";
+ foreach my $f (@info_files) {
+ my %st = %{$report{$f}{"stats"}};
+ if ($st{"func_count"}) {
+ $percent = 100. * $st{"func_used"} / $st{"func_count"};
+
+ printf OUT " <td>%.1f%%</td>\n", $percent;
+ } else {
+ print OUT " <td>N. A.</td>\n";
+ }
+ }
+ if ($stats{"func_count"}) {
+ $percent = 100. * $stats{"func_used"} / $stats{"func_count"};
+
+ printf OUT " <td>%.1f%%</td>\n", $percent;
+ } else {
+ print OUT " <td>N. A.</td>\n";
+ }
+ print OUT " <td>" . $stats{"func_count"} . "</td>";
+ print OUT " </tr><tr>\n";
+
+ print OUT " <td><b>Branches</b></td>\n";
+ foreach my $f (@info_files) {
+ my %st = %{$report{$f}{"stats"}};
+ if ($st{"branch_count"}) {
+ $percent = 100. * $st{"branch_reached"} / $st{"branch_count"};
+
+ printf OUT " <td>%.1f%%</td>\n", $percent;
+ } else {
+ print OUT " <td>N. A.</td>\n";
+ }
+ }
+ if ($stats{"branch_count"}) {
+ $percent = 100. * $stats{"branch_reached"} / $stats{"branch_count"};
+
+ printf OUT " <td>%.1f%%</td>\n", $percent;
+ } else {
+ print OUT " <td>N. A.</td>\n";
+ }
+ print OUT " <td>" . $stats{"branch_count"} . "</td>";
+ print OUT " </tr><tr>\n";
+
+ print OUT " <td><b>Lines</b></td>\n";
+ foreach my $f (@info_files) {
+ my %st = %{$report{$f}{"stats"}};
+
+ if ($st{"line_count"}) {
+ $percent = 100. * $st{"line_reached"} / $st{"line_count"};
+
+ printf OUT " <td>%.1f%%</td>\n", $percent;
+ } else {
+ print OUT " <td>N. A.</td>\n";
+ }
+ }
+ if ($stats{"line_count"}) {
+ $percent = 100. * $stats{"line_reached"} / $stats{"line_count"};
+
+ printf OUT " <td>%.1f%%</td>\n", $percent;
+ } else {
+ print OUT " <td>N. A.</td>\n";
+ }
+ print OUT " <td>" . $stats{"line_count"} . "</td>";
+
+ # If there are more than one tests per file, report them
+ my $total = scalar(keys %test_names);
+ if ($total > 1) {
+ print OUT " </tr><tr>\n";
+ print OUT " <td><b>Number of tests</b></td>\n";
+ foreach my $f (@info_files) {
+ my $count = scalar(keys %{$report{$f}{"test_names"}});
+
+ if ($count == 0) {
+ print OUT " <td $red>$count</td>\n";
+ } elsif ($count < $total) {
+ print OUT " <td $yellow>$count</td>\n";
+ } else {
+ print OUT " <td $green>$count</td>\n";
+ }
+ }
+ print OUT " <td $green\>$total</td>\n";
+
+ }
+ print OUT " </tr>\n</table><p/>\n\n";
+
+ if ($total > 1) {
+ print OUT "<h2>Tests coverage</h2>\n";
+
+ print OUT "<table width=\"100%\" border=1 cellspacing=0 cellpadding=0>\n <tr>\n";
+ print OUT " <th>Test name</th>\n";
+ foreach my $f (@info_files) {
+ print OUT " <th>$f</th>\n";
+ }
+
+ foreach my $t (sort keys(%test_names)) {
+ print OUT " </tr><tr>\n";
+ printf OUT " <td>%s</td>\n", $t;
+ foreach my $f (@info_files) {
+ if (%{$report{$f}{"test_names"}}{$t}) {
+ print OUT " <td $green>YES</td>\n";
+ } else {
+ print OUT " <td $red>NO</td>\n";
+ }
+ }
+ }
+ print OUT "</tr></table>\n";
+ }
+
+
+ # Generates a table containing per-function detailed data
+
+ print OUT "<h2>Functions coverage</h2>\n";
+ print OUT "<table width=\"100%\" border=1 cellspacing=0 cellpadding=0>\n <tr>\n";
+ print OUT " <th>Function</th>\n";
+ print OUT " <th>Used?</th>\n";
+ foreach my $f (@info_files) {
+ print OUT " <th>$f</th>\n";
+ }
+ print OUT " <th>File</th>\n";
+
+ foreach my $func (sort keys(%all_func)) {
+ my @keys = sort keys(%{$all_func{$func}});
+ foreach my $file (@keys) {
+ print OUT " </tr><tr>\n";
+ print OUT " <td>$func</td>\n";
+ if ($used_func{$func}->{$file}) {
+ print OUT " <td $green>YES</td>\n";
+ } else {
+ print OUT " <td $red>NO</td>\n";
+ }
+ foreach my $f (@info_files) {
+ if ($report{$f}{"used_func"}{$func}->{$file}) {
+ print OUT " <td $green>YES</td>\n";
+ } else {
+ print OUT " <td $red>NO</td>\n";
+ }
+ }
+ $file =~ s,$prefix,linux/,;
+ print OUT " <td>$file</td>\n";
+ }
+ }
+ print OUT "</tr></table>\n";
+
+ print OUT "$epilog</body>\n";
+
+ # Close the file and exit
+
+ close OUT;
+}
+
#
# Argument handling
#
@@ -548,6 +814,11 @@ GetOptions(
"exclude-source=s" => \@src_exclude_regexes,
"show-files|show_files" => \$show_files,
"show-lines|show_lines" => \$show_lines,
+ "report|r=s" => \$gen_report,
+ "css-file|css|c=s" => \$css_file,
+ "title|t=s" => \$title,
+ "html-prolog|prolog=s" => \$html_prolog,
+ "html-epilog|epilog=s" => \$html_epilog,
"help" => \$help,
"man" => \$man,
) or pod2usage(2);
@@ -561,7 +832,9 @@ if ($#ARGV < 0) {
}
# At least one action should be specified
-pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused);
+pod2usage(1) if (!$print_used && !$filter && !$stat && !$print_unused && !$gen_report);
+
+pod2usage(1) if ($gen_report && ($print_used || $filter || $stat || $print_unused));
my $filter_str = "";
my $has_filter;
@@ -605,39 +878,67 @@ if ($ignore_unused) {
foreach my $f (@ARGV) {
parse_info_data($f);
+
+ if ($gen_report) {
+ $f =~ s,.*/,,;
+ $f =~ s/\.info$//;
+
+ gen_stats();
+
+ $report{$f}{"stats"} = { %stats };
+ $report{$f}{"all_func"} = { %all_func };
+ $report{$f}{"used_func"} = { %used_func };
+ $report{$f}{"all_branch"} = { %all_branch };
+ $report{$f}{"all_line"} = { %all_line };
+ $report{$f}{"used_source"} = { %used_source };
+ $report{$f}{"files"} = { %files };
+ $report{$f}{"test_names"} = { %test_names };
+
+ %used_func = ();
+ %all_func = ();
+ %all_branch = ();
+ %all_line = ();
+ %used_source = ();
+ %files = ();
+ %test_names = ();
+ }
}
-print_code_coverage($print_used, $print_unused, $show_lines);
+if ($gen_report) {
+ generate_report();
+ exit 0;
+}
-print_summary() if ($stat);
+gen_stats();
-my $all_files = scalar keys(%files);
+die "Nothing counted. Wrong input files?" if (!$stats{"all_files"});
-die "Nothing counted. Wrong input files?" if (!$all_files);
+print_code_coverage($print_used, $print_unused, $show_lines);
-if ($has_filter) {
- my $all_files = scalar keys(%files);
- my $filtered_files = scalar keys(%record);
- my $used_files = scalar keys(%used_source);
+print_summary() if ($stat);
- my $percent = 100. * $used_files / $all_files;
+if ($has_filter) {
+ my $percent = 100. * $stats{"used_files"} / $stats{"all_files"};
$filter_str =~ s/(.*),/$1 and/;
printf "Filters......:%s.\n", $filter_str;
printf "Source files.: %.2f%% (%d of %d total)",
- $percent, $used_files, $all_files;
+ $percent, $stats{"used_files"}, $stats{"all_files"};
- if ($used_files != $filtered_files) {
- my $percent_filtered = 100. * $used_files / $filtered_files;
+ if ($stats{"used_files"} != $stats{"filtered_files"}) {
+ my $percent_filtered = 100. * $stats{"used_files"} / $stats{"filtered_files"};
printf ", %.2f%% (%d of %d filtered)",
- $percent_filtered, $used_files, $filtered_files;
+ $percent_filtered, $stats{"used_files"}, $stats{"filtered_files"};
}
print "\n";
} else {
printf "Source files: %d\n", scalar keys(%files) if($stat);
}
+my $ntests=scalar(%test_names);
+printf "Number of tests: %d\n", $ntests if ($ntests > 1);
+
if ($show_files) {
for my $f(sort keys %used_source) {
print "\t$f\n";
@@ -658,8 +959,10 @@ Parses lcov data from .info files.
code_cov_parse_info <options> [input file(s)]
-At least one of the options B<--stat>, B<--print> and/or B<--output>
-should be used.
+At least one of the output options should be used, e g.
+B<--stat>, B<--print>, B<--print-unused>, B<--report> and/or B<--output>.
+
+Also, B<--report> can't be used together with other output options.
=head1 OPTIONS
@@ -688,6 +991,37 @@ Prints the functions that were never reached.
The function coverage report is affected by the applied filters.
+=item B<--report> B<[output file]> or B<-r> B<[output file]>
+
+Generates an html report containing per-test and total statistics.
+
+The function coverage report is affected by the applied filters.
+
+=item B<--css-file> B<[css file]> or B<--css> B<[css file]> or B<-c> B<[css file]
+
+Adds an optional css file to the html report.
+Used only with B<--report>.
+
+=item B<--title> B<[title] or B<-t> B<[title]
+
+If used with B<--report>, it defines the title for the for the html report.
+
+If used with B<--output>, it replaces the test names with the title. This
+is useful when merging reports from multiple tests into a summarized file.
+If not used, the B<[output file]> will contain all test names on its
+beginning.
+
+Used with B<--report> AND B<--output>.
+
+=item B<--html-prolog> B<[html file] or B<--prolog> B<[html file]
+
+Adds a prolog at the beginning of the body of the html report.
+Used only with B<--report>.
+
+=item B<--html-epilog> B<[html file] or B<--epilog> B<[html file]
+
+Adds an epilog before the end of the body of the html report.
+Used only with B<--report>.
=item B<--show-lines> or B<--show_lines>