Skip to content

Commit 03a463e

Browse files
committed
Add 'genhhtml --validate ..' option - to check for broken hyperlinks in HTML report.
Signed-off-by: Henry Cox <henry.cox@mediatek.com>
1 parent 99382ed commit 03a463e

File tree

4 files changed

+150
-8
lines changed

4 files changed

+150
-8
lines changed

bin/genhtml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6319,6 +6319,7 @@ my %genhtml_rc_opts = (
63196319

63206320
my $save;
63216321
my $serialize;
6322+
my $validateHTML = exists($ENV{LCOV_VALIDATE});
63226323

63236324
my %genhtml_options = ("output-directory|o=s" => \$output_directory,
63246325
"header-title=s" => \$header_title,
@@ -6371,7 +6372,8 @@ my %genhtml_options = ("output-directory|o=s" => \$output_directory,
63716372
"show-navigation" => \$show_tla,
63726373
"show-proportion" => \$show_functionProportions,
63736374
"merge-aliases" => \$merge_function_aliases,
6374-
"suppress-aliases" => \$suppress_function_aliases,);
6375+
"suppress-aliases" => \$suppress_function_aliases,
6376+
'validate' => \$validateHTML,);
63756377
# Parse command line options
63766378
if (!lcovutil::parseOptions(\%genhtml_rc_opts, \%genhtml_options)) {
63776379
print(STDERR "Use $tool_name --help to get usage information\n");
@@ -6720,6 +6722,10 @@ lcovutil::cleanup_callbacks();
67206722
lcovutil::save_profile(File::Spec->catfile($output_directory, 'genhtml'),
67216723
File::Spec->catfile($output_directory, 'profile.html'));
67226724

6725+
if (0 == $exit_status && $validateHTML) {
6726+
ValidateHTML->new($output_directory, '.' . $html_ext);
6727+
}
6728+
67236729
# exit with non-zero status if --keep-going and some errors detected
67246730
$exit_status = 1
67256731
if (0 == $exit_status && lcovutil::saw_error());

lib/lcovutil.pm

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2727,6 +2727,133 @@ sub parse_w3cdtf($)
27272727
time_zone => $tz,);
27282728
}
27292729

2730+
package HTML_fileData;
2731+
2732+
use constant {
2733+
NAME => 0,
2734+
PARENT => 1,
2735+
HREFS => 2,
2736+
ANCHORS => 3,
2737+
};
2738+
2739+
sub new
2740+
{
2741+
my ($class, $parentDir, $filename) = @_;
2742+
2743+
my $self = [$parentDir, $filename, [], {}];
2744+
2745+
my $name = File::Spec->catfile($parentDir, $filename);
2746+
2747+
open(HTML, '<', $name) or die("unable to open $name: $!");
2748+
while (<HTML>) {
2749+
if (/<(a|span) .*id=\"([^\"]+)\"/) {
2750+
lcovutil::ignorable_error($lcovutil::ERROR_USAGE,
2751+
"\"$name\":$.: duplicate anchor '$2' original at " .
2752+
$self->[ANCHORS]->{$2} . '.')
2753+
if exists($self->[ANCHORS]->{$2});
2754+
$self->[ANCHORS]->{$2} = $.;
2755+
} elsif (/<a .*href=\"([^#\"]+)(#([^\"]+))?\"/) {
2756+
next if 'http' eq substr($1, 0, 4);
2757+
push(@{$self->[HREFS]}, [$., $1, $3]); # lineNo, filename, anchor
2758+
} elsif (/<frame .*src=\"([^\"]+)\"/) {
2759+
push(@{$self->[HREFS]}, [$., $1, $3]); # lineNo, filename, anchor
2760+
}
2761+
}
2762+
close(HTML) or die("unable to close $name: $!");
2763+
2764+
return bless $self, $class;
2765+
}
2766+
2767+
sub verifyAnchor
2768+
{
2769+
my ($self, $anchor) = @_;
2770+
2771+
return exists($self->[ANCHORS]->{$anchor});
2772+
}
2773+
2774+
sub hrefs
2775+
{
2776+
my $self = shift;
2777+
return $self->[HREFS];
2778+
}
2779+
2780+
package ValidateHTML;
2781+
2782+
sub new
2783+
{
2784+
my ($class, $topDir, $htmlExt) = @_;
2785+
my $self = {};
2786+
2787+
$htmlExt = '.html' unless defined($htmlExt);
2788+
2789+
my @dirstack = ($topDir);
2790+
my %visited;
2791+
while (@dirstack) {
2792+
my $top = pop(@dirstack);
2793+
die("unexpected link $top") if -l $top;
2794+
opendir(my $dh, $top) or die("can't open directory $top: $!");
2795+
while (my $e = readdir($dh)) {
2796+
next if $e eq '.' || $e eq '..';
2797+
my $p = File::Spec->catfile($top, $e);
2798+
die("unexpected link $p") if -l $p;
2799+
if (-d $p) {
2800+
die("already visited $p") if exists($visited{$p});
2801+
$visited{$p} = [$top, $e];
2802+
push(@dirstack, $p);
2803+
} elsif (-f $p &&
2804+
$p =~ /.+$htmlExt$/) {
2805+
die("duplicate file $p??") if exists($self->{$p});
2806+
lcovutil::info(1, "schedule $p\n");
2807+
$self->{$p} = HTML_fileData->new($top, $e);
2808+
}
2809+
}
2810+
closedir($dh);
2811+
}
2812+
my %fileReferred;
2813+
while (my ($filename, $data) = each(%$self)) {
2814+
my $dir = File::Basename::dirname($filename);
2815+
lcovutil::info(1, "verify $filename:\n");
2816+
foreach my $href (@{$data->hrefs()}) {
2817+
my ($lineNo, $link, $anchor) = @$href;
2818+
my $path = File::Spec->catfile($dir, $link);
2819+
$path = File::Spec->abs2rel(Cwd::realpath($path), $main::cwd)
2820+
unless exists($self->{$path});
2821+
lcovutil::info(1,
2822+
" $lineNo: $link" . ($anchor ? "#$anchor" : '') . "\n");
2823+
unless (exists($self->{$path})) {
2824+
lcovutil::ignorable_error($lcovutil::ERROR_PATH,
2825+
"\"$filename\":$lineNo: non-existent file '$link'.");
2826+
next;
2827+
}
2828+
if (exists($fileReferred{$path})) {
2829+
# keep only one use
2830+
push(@{$fileReferred{$path}}, $filename)
2831+
if ($fileReferred{$path}->[-1] ne $filename);
2832+
} else {
2833+
$fileReferred{$path} = [$filename];
2834+
}
2835+
2836+
if (defined($anchor)) {
2837+
my $a = $self->{$path};
2838+
unless ($a->verifyAnchor($anchor)) {
2839+
lcovutil::ignorable_error($lcovutil::ERROR_PATH,
2840+
"\"$filename\":$lineNo: \"$link#$anchor\" doesn't point to valid anchor."
2841+
);
2842+
}
2843+
}
2844+
}
2845+
}
2846+
2847+
while (my ($filename, $data) = each(%$self)) {
2848+
lcovutil::ignorable_error($lcovutil::ERROR_UNUSED,
2849+
"HTML file \"$filename\" is not referenced.")
2850+
unless (exists($fileReferred{$filename}) ||
2851+
($topDir eq File::Basename::dirname($filename) &&
2852+
"index$htmlExt" eq File::Basename::basename($filename)));
2853+
}
2854+
return bless $self, $class;
2855+
}
2856+
27302857
package CoverageCriteria;
27312858

27322859
our @coverageCriteriaScript;

man/genhtml.1

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ genhtml \- Generate HTML view from LCOV coverage data files
1515
.br
1616
.RB [ \-q | \-\-quiet ]
1717
.RB [ \-v | \-\-verbose ]
18-
.RB [ \-\-debug ]
18+
.br
19+
.RB [ \-\-debug ] [ \-\-validate ]
1920
.br
2021
.RB [ \-s | \-\-show\-details ]
2122
.br
@@ -886,6 +887,14 @@ Decreased verbosity will suppress 'progress' messages for example - while error
886887
.RS
887888
Increment 'debug messages' verbosity. This is useful primarily to developers who want to enhance the lcov tool suite.
888889

890+
.RE
891+
.B \-\-validate
892+
.RS
893+
Check the generated HTML to verify that there are no dead hyperlinks and no unused files in the output directory.
894+
The checks can also be enabled by setting environment variable
895+
.B LCOV_VALIDATE = 1.
896+
This option is primarily intended for use by developers who modify the HTML report.
897+
889898
.RE
890899
.B \-\-flat
891900
.br

tests/gendiffcov/insensitive/insensitive.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,8 @@ rm -f TeSt.cpp
263263
264264
# check annotation failure message...
265265
# check that this works with test names
266-
echo genhtml $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATATE --show-owners all --show-noncode -o differential ./current.info --ignore source $IGNORE --rc check_existence_before_callback=0
267-
$COVER $GENHTML_TOOL $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATE --show-owners all --show-noncode -o differential ./current.info $GENHTML_PORT --ignore source $IGNORE --rc check_existence_before_callback=0 2>&1 | tee fail.log
266+
echo genhtml $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATATE --show-owners all --show-noncode -o differential2 ./current.info --ignore source $IGNORE --rc check_existence_before_callback=0
267+
$COVER $GENHTML_TOOL $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATE --show-owners all --show-noncode -o differential2 ./current.info $GENHTML_PORT --ignore source $IGNORE --rc check_existence_before_callback=0 2>&1 | tee fail.log
268268
if [ 0 == ${PIPESTATUS[0]} ] ; then
269269
echo "ERROR: expected annotation error but didn't find"
270270
if [ 0 == $KEEP_GOING ] ; then
@@ -278,8 +278,8 @@ if [ 0 != $? ] ; then
278278
fi
279279
280280
# just ignore the version check error this time..
281-
echo genhtml $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATATE --show-owners all --show-noncode -o differential ./current.info --ignore-source,annotate,version $IGNORE
282-
$COVER $GENHTML_TOOL $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATE --show-owners all --show-noncode -o differential ./current.info $GENHTML_PORT --ignore source,annotate,version $IGNORE 2>&1 | tee fail2.log
281+
echo genhtml $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATATE --show-owners all --show-noncode -o differential3 ./current.info --ignore-source,annotate,version $IGNORE
282+
$COVER $GENHTML_TOOL $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATE --show-owners all --show-noncode -o differential3 ./current.info $GENHTML_PORT --ignore source,annotate,version $IGNORE 2>&1 | tee fail2.log
283283
if [ 0 == ${PIPESTATUS[0]} ] ; then
284284
echo "ERROR: expected synthesize error but didn't find"
285285
if [ 0 == $KEEP_GOING ] ; then
@@ -297,8 +297,8 @@ if [ 0 != $? ] ; then
297297
exit 1
298298
fi
299299
300-
echo genhtml $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATATE --show-owners all --show-noncode -o differential ./current.info --ignore-source,annotate,version --synthesize $IGNORE
301-
$COVER $GENHTML_TOOL $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATE --show-owners all --show-noncode -o differential ./current.info $GENHTML_PORT --ignore source,annotate,version --synthesize $IGNORE 2>&1 | tee fail3.log
300+
echo genhtml $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATATE --show-owners all --show-noncode -o differential4 ./current.info --ignore-source,annotate,version --synthesize $IGNORE
301+
$COVER $GENHTML_TOOL $DIFFCOV_OPTS --baseline-file ./baseline.info --diff-file diff.txt --annotate-script $ANNOTATE --show-owners all --show-noncode -o differential4 ./current.info $GENHTML_PORT --ignore source,annotate,version --synthesize $IGNORE 2>&1 | tee fail3.log
302302
if [ 0 != ${PIPESTATUS[0]} ] ; then
303303
echo "ERROR: unexpected synthesize error"
304304
if [ 0 == $KEEP_GOING ] ; then

0 commit comments

Comments
 (0)