App-Test-Generator
view release on metacpan or search on metacpan
lib/Devel/App/Test/Generator/LCSAJ/Runtime.pm view on Meta::CPAN
BEGIN {
my $targets_env = $ENV{LCSAJ_TARGETS} // '';
$targets_env =~ s/\n//g;
for my $t (split /:/, $targets_env) {
next unless length $t;
# Inline normalisation â cannot call _normalize here since
# BEGIN runs before named subs are compiled when BEGIN
# appears at the top of the file
my $f = $t;
$f =~ s{^.*/blib/lib/}{lib/};
$f =~ s{^.*/lib/}{lib/};
$TARGET{$f} = 1;
}
}
END {
_write_results();
}
# --------------------------------------------------
# _normalize
#
# Purpose: Convert an absolute or build-tree path
# to a canonical lib-relative form so that
# paths recorded at runtime match the
# targets derived from LCSAJ_TARGETS.
#
# Entry: $path - an absolute or relative file path.
#
# Exit: Returns a lib-relative path string,
# e.g. lib/Foo/Bar.pm
#
# Side effects: None.
#
# Notes: Must be defined before the BEGIN block
# that calls it, since BEGIN runs at compile
# time and later subs may not yet be compiled.
#
# Examples:
# /home/user/proj/blib/lib/Foo/Bar.pm -> lib/Foo/Bar.pm
# /home/user/proj/lib/Foo/Bar.pm -> lib/Foo/Bar.pm
# --------------------------------------------------
sub _normalize {
my $f = $_[0];
# Strip everything up to and including blib/lib/ or lib/
$f =~ s{^.*/blib/lib/}{lib/};
$f =~ s{^.*/lib/}{lib/};
return $f;
}
# --------------------------------------------------
# DB::DB
#
# Purpose: Called by the Perl debugger before every
# statement. Records (file, line) hits for
# later LCSAJ coverage analysis.
#
# Entry: No arguments â caller(0) is used to get
# the current file and line number.
#
# Exit: Returns nothing. Updates %HITS in place.
#
# Side effects: Increments %HITS{$norm}{$line}.
#
# Notes: This sub lives in the DB:: package as
# required by Perl's debugger protocol.
# It is called for every statement executed
# while the debugger is active, so it must
# be as fast as possible.
# Internal files and out-of-target files
# are skipped immediately.
# --------------------------------------------------
sub DB::DB {
my (undef, $file, $line) = caller(0);
return unless defined $file && defined $line;
# Resolve symlinks and relative components to a stable absolute path
my $abs = abs_path($file) // $file;
my $norm = _normalize($abs);
# Never record hits inside this module itself â suffix match is used
# so it works regardless of CWD or install prefix
return if $norm =~ m{(?:^|/)Devel/App/Test/Generator/LCSAJ/Runtime\.pm$};
# If a target list was provided, skip files not in it
if(%TARGET) {
return unless $TARGET{$norm};
}
$HITS{$norm}{$line}++;
}
# --------------------------------------------------
# _write_results
#
# Purpose: Serialise %HITS to a per-process JSON
# file in the output directory.
#
# Entry: None. Reads %HITS and $OUT_DIR.
#
# Exit: Returns nothing. Writes a JSON file.
# Returns immediately if %HITS is empty.
#
# Side effects: Creates $OUT_DIR if absent.
# Writes cover_html/lcsaj_hits/hits_PID.json
#
# Notes: Called from END so it runs even when
# prove exits non-zero â mutation tests
# are expected to fail. PID is included
# in the filename so parallel test runs
# produce separate files without collision.
# --------------------------------------------------
sub _write_results {
return unless %HITS;
# Include PID in filename to support parallel test runs
my $out_file = "$OUT_DIR/hits_$$.json";
make_path($OUT_DIR) unless -d $OUT_DIR;
open my $fh, '>', $out_file or croak "Cannot write $out_file: $!";
print $fh encode_json(\%HITS);
close $fh;
}
1;
__END__
=head1 OUTPUT FORMAT
C<cover_html/lcsaj_hits/hits_PID.json> is a JSON object of the form:
( run in 1.583 second using v1.01-cache-2.11-cpan-5a3173703d6 )