NAME
Devel::XSCover::DB - the C coverage written into Devel::Cover's database
SYNOPSIS
use Devel::XSCover::DB;
my $writer = Devel::XSCover::DB->new(dir => '.', db => 'cover_db');
my ($files, $generated) = $writer->merge(@objects);
my $entries = $writer->entries($files, $generated, @maps);
my $run = $writer->write($entries);
DESCRIPTION
Turns what the gcov reader and the XS map found into one Devel::Cover run with three criteria for every C and XS file, statements, branches and subroutines, and conditions too when the compiler reported them. cover then reads it beside the Perl runs of the same test run.
METHODS
new
my $writer = Devel::XSCover::DB->new(%options);
Options: dir, db (relative to dir unless absolute), macro_branches (every gcov block as a branch, see entries), dry and silent.
merge
my ($files, $generated) = $writer->merge(@objects);
Merges the objects of the reader into one hash of files and one of generated files. A file seen from several objects, as a header with inline functions is, becomes one entry: the union of its lines with the counts summed, its functions summed by name and line, and the arms of its blocks summed where the same block was seen twice.
entries
my $entries = $writer->entries($files, $generated, @maps);
A map that carries an inverse table first moves every record of that XS file from the line gcov reported to the line meant, since xsubpp drops a comment line from an XSUB body without a #line.
The criteria per file. Every executable line is a statement, but a line of nothing but braces (and perhaps else), which clang credits with the code after a return inside a macro, is not. A branch is a decision of the source, an if, while, for or switch, with one arm per way out of it, as Devel::XSCover::Flow read them from the flow graph: a decision of two arms is one branch with the source line as its text, the arm into the body first, and one of more arms is one branch per arm, arm k of N, each the arm against the sum of the others. A file the flow graph did not cover, as with a gcov older than 9, gets its branches from gcov's blocks instead, one per block with two or more arms in gcov's order, leaving out a block on a line whose text holds no decision (no if, else, while, for, do, switch, ?, && or || outside comments), which is a macro's own, as GvSVn or SvTRUE expand to: nothing in the source can take its other arm. macro_branches keeps every block of every file that way instead. Every C function is a subroutine on its first line under its C name. An XSUB is a subroutine on its declaration line in the XS under each of its Perl names, with the count of its wrapper from the generated C, and an XSUB whose wrapper the compiler never saw, under a preprocessor condition the build left out, is not a subroutine of this build. A decision that gcc 14's condition coverage reported becomes one condition per term, term k of the source line, with two counts: whether the term's true outcome and whether its false outcome each decided the result at least once. gcc counts the terms of the expression as the preprocessor left it, so a decision with more terms than its source holds (one more than the && and || over the lines its parentheses span, one for a plain if or a ?) has a macro's own among them, as HvNAME_get expands to, and is left out: nothing in the source tells its terms from the macro's. A line with no decision in its text is a macro's altogether. macro_branches keeps them all.
write
my $run = $writer->write($entries);
Writes one run under the database, with the file paths relative to dir as cover expects them, and returns a hash with the run's path and the counts of files, statements, branches and subroutines written. A file that is not under dir is skipped with a warning.