# Copyright 2001-2018, Paul Johnson (paul@pjcj.net) # This software is free. It is licensed under the same terms as Perl itself. # The latest version of this software should be available from my homepage: # http://www.pjcj.net package Devel::Cover::Report::Text; use strict; use warnings; our $VERSION = '1.31'; # VERSION use Devel::Cover::DB; sub print_runs { my ($db, $options) = @_; for my $r (sort {$a->{start} <=> $b->{start}} $db->runs) { print "Run: ", $r->run, "\n"; print "Perl version: ", $r->perl, "\n"; print "OS: ", $r->OS, "\n"; print "Start: ", scalar gmtime $r->start , "\n"; print "Finish: ", scalar gmtime $r->finish, "\n"; print "\n"; # use Devel::Cover::Dumper; print Dumper $r; } } sub print_statement { my ($db, $file, $options) = @_; my $cover = $db->cover; print "$file\n\n"; my $f = $cover->file($file); my $fmt = "%-5s %3s "; my @args = ("line", "err"); for my $ann (@{$options->{annotations}}) { for my $a (0 .. $ann->count - 1) { $fmt .= "%-" . $ann->width($a) . "s "; push @args, $ann->header($a); } } my %cr; @cr{$db->criteria} = $db->criteria_short; for my $c ($db->criteria) { if ($options->{show}{$c}) { $fmt .= "%6s "; push @args, $cr{$c}; } } $fmt .= " %s\n"; push @args, "code"; printf $fmt, @args; my $autoloader = 0; open F, $file or warn("Unable to open $file: $!\n"), return; LINE: while (defined(my $l = )) { chomp $l; my $n = $.; $autoloader ||= $l =~ /use\s+AutoLoader/; my %criteria; for my $c ($db->criteria) { next unless $options->{show}{$c}; my $criterion = $f->$c(); if ($criterion) { my $l = $criterion->location($n); $criteria{$c} = $l ? [@$l] : $l; } } my $more = 1; while ($more) { my @args = ($n, ""); my $error = 0; for my $ann (@{$options->{annotations}}) { for my $a (0 .. $ann->count - 1) { push @args, substr $ann->text($file, $n, $a), 0, $ann->width($a); $error ||= $ann->error($file, $n, $a); } } $more = 0; for my $c ($db->criteria) { next unless $options->{show}{$c}; my $o = shift @{$criteria{$c}}; $more ||= @{$criteria{$c}}; my $value = $o ? ($c =~ /statement|sub|pod|time/) ? $o->covered : $o->percentage : ""; $value = "-" . $value if $o && $o->uncoverable; push @args, $value; $error ||= $o->error if $o; } $args[1] = "***" if $error; push @args, $l; # print join(", ", map { "[$_]" } @args), "\n"; printf $fmt, @args; last LINE if !$autoloader && $l =~ /^__(END|DATA)__/; $n = $l = ""; } } close F or die "Unable to close $file: $!"; print "\n\n"; } sub print_branches { my ($db, $file, $options) = @_; my $branches = $db->cover->file($file)->branch; return unless $branches; print "Branches\n"; print "--------\n\n"; my $tpl = "%-5s %3s %6s %6s %6s %s\n"; printf $tpl, "line", "err", "%", "true", "false", "branch"; printf $tpl, "-----", "---", ("------") x 3, "------"; for my $location (sort { $a <=> $b } $branches->items) { my $n = 0; for my $b (@{$branches->location($location)}) { printf $tpl, $n ? "" : $location, $b->error ? "***" : "", ($b->uncoverable ? "-" : "") . $b->percentage, map (($b->uncoverable($_) ? "-" : "") . ($b->covered($_) || 0), 0 .. $b->total - 1), $b->text; $n++; } } print "\n\n"; } sub print_conditions { my ($db, $file, $options) = @_; my $conditions = $db->cover->file($file)->condition; return unless $conditions; my $template = sub { "%-5s %3s %6s " . ( "%6s " x shift ) . " %s\n" }; my %r; for my $location (sort { $a <=> $b } $conditions->items) { my %seen; for my $c (@{$conditions->location($location)}) { push @{$r{$c->type}}, [ $c, $seen{$c->type}++ ? "" : $location ]; } } print "Conditions\n"; print "----------\n\n"; my %seen; for my $type (sort keys %r) { my $tpl; for (@{$r{$type}}) { my ($c, $location) = @$_; unless ($seen{$type}++) { my $headers = $c->headers; my $nh = @$headers; $tpl = $template->($nh); (my $t = $type) =~ s/_/ /g; print "$t conditions\n\n"; printf $tpl, "line", "err", "%", @$headers, "expr"; printf $tpl, "-----", "---", ("------") x ($nh + 1), "----"; } printf $tpl, $location, $c->error ? "***" : "", ($c->uncoverable ? "-" : "") . $c->percentage, map (($c->uncoverable($_) ? "-" : "") . ($c->covered($_) || 0), 0 .. $c->total - 1), $c->text; } print "\n"; } print "\n"; } sub print_subroutines { my ($db, $file, $options) = @_; my $dfil = $db->cover->file($file); my $subs = $dfil->subroutine or return; my $pods = $options->{show}{pod} && $dfil->pod; my $maxh = 8; my $maxc = 5; my $maxp = 3; my $maxs = 10; my %subs; for my $location ($subs->items) { my $l = $subs->location($location); my $d = $pods && $pods->location($location); for my $sub (@$l) { my $h = "$file:$location"; my $c = ($sub->uncoverable ? "-" : "") . $sub->covered; my $e = $pods && shift @$d; my $p = $e ? ($e->uncoverable ? "-" : "") . $e->covered : ""; my $s = $sub->name; $maxh = length $h if length $h > $maxh; $maxc = length $c if length $c > $maxc; $maxp = length $p if $p && length $p > $maxp; $maxs = length $s if length $s > $maxs; push @{$subs{$sub->covered ? "covered" : "uncovered"}{$s}}, [$c, $pods ? $p : (), $h]; } } my $template = "%-${maxs}s %${maxc}s "; $template .= "%${maxp}s " if $pods; $template .= "%-${maxh}s\n"; for my $type (sort keys %subs) { print ucfirst $type, " Subroutines\n"; print "-" x (12 + length $type), "\n\n"; printf $template, "Subroutine", "Count", $pods ? "Pod" : (), "Location"; printf $template, "-" x $maxs, "-" x $maxc, $pods ? "-" x $maxp : (), "-" x $maxh; for my $s (sort keys %{$subs{$type}}) { printf $template, $s, @$_ for sort {$a->[-1] cmp $b->[-1]} @{$subs{$type}{$s}}; } print "\n"; } print "\n"; } sub report { my ($pkg, $db, $options) = @_; print_runs($db, $options); for my $file (@{$options->{file}}) { print_statement ($db, $file, $options) if $options->{show}{statement}; print_branches ($db, $file, $options) if $options->{show}{branch}; print_conditions ($db, $file, $options) if $options->{show}{condition}; print_subroutines($db, $file, $options) if $options->{show}{subroutine} || $options->{show}{pod}; } } 1 __END__ =head1 NAME Devel::Cover::Report::Text - Text backend for Devel::Cover =head1 VERSION version 1.31 =head1 SYNOPSIS cover -report text =head1 DESCRIPTION This module provides a textual reporting mechanism for coverage data. It is designed to be called from the C program. =head1 SEE ALSO Devel::Cover =head1 BUGS Huh? =head1 LICENCE Copyright 2001-2018, Paul Johnson (paul@pjcj.net) This software is free. It is licensed under the same terms as Perl itself. The latest version of this software should be available from my homepage: http://www.pjcj.net =cut