CGI-Info
view release on metacpan or search on metacpan
scripts/generate_index.pl view on Meta::CPAN
'generate_mutant_tests=s' => \$mutant_test_dir,
'generate_test=s' => \$generate_test,
'generate_fuzz' => \$generate_fuzz,
) or die "Usage: $0 [--generate_mutant_tests=DIR] [--generate_test=mutant] [--generate_fuzz]";
# -------------------------------
# Dependency correlation analysis
# -------------------------------
my $MAX_REPORTS_PER_GRADE = 20; # safety rail
my $ENABLE_DEP_ANALYSIS = 1;
# Read and decode data
my $cover_db = eval { decode_json(read_file($config{cover_db})) };
my $mutation_db = eval { decode_json(read_file($config{mutation_db})) };
# --------------------------------------------------
# Compute coverage percentage from only our own files,
# excluding absolute paths (installed CPAN modules)
# which inflate Devel::Cover's pre-aggregated Total.
# --------------------------------------------------
my ($coverage_pct, $badge_color) = (0, 'red');
if($cover_db->{summary}) {
my ($sum, $count) = (0, 0);
for my $f (keys %{ $cover_db->{summary} }) {
next if $f eq 'Total';
next if $f =~ /^\//; # skip absolute paths
$sum += $cover_db->{summary}{$f}{total}{percentage} // 0;
$count++;
}
if($count) {
my $pct = _own_file_coverage_pct($cover_db->{summary});
$coverage_pct = defined $pct ? int($pct) : 0;
$badge_color = $coverage_pct > $config{med_threshold} ? 'brightgreen'
: $coverage_pct > $config{low_threshold} ? 'yellow'
: 'red';
}
}
Readonly my $coverage_badge_url => "https://img.shields.io/badge/coverage-${coverage_pct}%25-${badge_color}";
# Start HTML
my @html; # build in array, join later
push @html, <<"HTML";
<!DOCTYPE html>
<html>
<head>
<title>$config{package_name} Coverage Report</title>
<style>
body { font-family: sans-serif; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.low { background-color: #fdd; }
.med { background-color: #ffd; }
.high { background-color: #dfd; }
.badges img { margin-right: 10px; }
.disabled-icon {
opacity: 0.4;
cursor: default;
}
.icon-link {
text-decoration: none;
}
.icon-link:hover {
opacity: 0.7;
cursor: pointer;
}
.coverage-badge {
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
color: white;
font-size: 0.9em;
}
.badge-good { background-color: #4CAF50; }
.badge-warn { background-color: #FFC107; }
.badge-bad { background-color: #F44336; }
.summary-row {
font-weight: bold;
background-color: #f0f0f0;
}
td.positive { color: green; font-weight: bold; }
td.negative { color: red; font-weight: bold; }
td.neutral { color: gray; }
/* Show cursor points on the headers to show that they are clickable */
th { background-color: #f2f2f2; cursor: pointer; }
th.sortable {
cursor: pointer;
user-select: none;
white-space: nowrap;
}
th .arrow {
color: #aaa; /* dimmed for inactive */
font-weight: normal;
}
th .arrow.active {
color: #000; /* dark for active */
font-weight: bold;
}
.sparkline {
display: inline-block;
vertical-align: middle;
}
tr.cpan-fail td {
background-color: #fdd;
}
tr.cpan-unknown td {
background-color: #eee;
color: #666;
}
tr.cpan-na td {
background-color: #ffffde;
color: #666;
}
.new-failure {
background: #c00;
color: #fff;
font-weight: bold;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.85em;
}
.notice {
padding: 8px 12px;
margin: 10px 0;
border-radius: 4px;
font-size: 0.95em;
}
.notice strong {
font-weight: bold;
}
.notice.perl-version-cliff {
background-color: #fff3cd; /* soft amber */
border: 1px solid #ffeeba;
color: #856404;
}
.notice.perl-version-cliff a {
color: #533f03;
text-decoration: underline;
}
.notice.perl-version-cliff a:hover {
text-decoration: none;
}
.notice.locale-cliff {
border-left: 4px solid #d97706;
background: #fffbeb;
padding: 0.5em 1em;
}
scripts/generate_index.pl view on Meta::CPAN
Theme-aware and readable in light & dark modes
-------------------------------------------------- */
.suggested-test {
margin-top: 6px;
margin-bottom: 12px;
/* Use theme variables instead of hardcoded colors */
background: var(--bg);
color: var(--text);
padding: 8px;
border-radius: 4px;
/* Subtle border for visual separation */
border: 1px solid var(--border);
}
/* Label styling */
.suggest-label {
font-weight: bold;
margin-bottom: 4px;
}
/* Ensure the test code block inherits readable colors */
.suggested-test pre {
background: transparent; /* Prevent nested dark blocks */
color: inherit; /* Match theme text color */
margin: 0;
font-family: monospace;
}
pre { line-height: 1.4; }
pre > details {
margin: 0.2em 0;
}
pre > details:first-child {
margin-top: 0;
}
pre > details:last-child {
margin-bottom: 0;
}
pre details,
pre summary,
pre ul,
pre li {
white-space: normal;
margin: 0;
padding: 0;
line-height: 1.2;
}
.nav { margin-bottom: 1em; }
.toggle {
float: right;
cursor: pointer;
padding: 6px 10px;
border: 1px solid var(--border);
border-radius: 4px;
}
/* Tooltip container */
.tooltip {
position: relative;
cursor: help;
}
/* Tooltip bubble */
.tooltip:hover::after {
content: attr(data-tooltip);
position: absolute;
left: 0;
top: 100%;
background: var(--table-header);
color: var(--table-header-text);
padding: 6px 10px;
white-space: normal;
max-width: 300px;
min-width: 30ch;
font-size: 12px;
border-radius: 6px;
z-index: 1000;
margin-left: 10ch; /* move tooltip ~10 characters to the right */
}
.mutant-details {
margin-left: 2em;
font-size: 0.9em;
}
/* Indent the list of mutations that displays when expanding by 8 characters */
pre details.mutant-details ul {
padding-left: 8ch;
margin: 0.2em 0;
}
.mutant-details summary {
cursor: pointer;
font-weight: bold;
}
.lcsaj-dot {
color: #5555ff;
font-size: 10px;
margin-right: 3px;
}
.lcsaj-dot-uncovered {
color: #cc4444;
font-size: 10px;
margin-right: 3px;
}
.lcsaj-tip {
position: relative;
display: inline-block;
}
.lcsaj-tip .lcsaj-tip-text {
visibility: hidden;
background-color: #333;
color: #fff;
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
white-space: nowrap;
position: fixed;
z-index: 9999;
pointer-events: none;
}
.lcsaj-tip:hover .lcsaj-tip-text {
visibility: visible;
}
</style>
</head>
<body>
<button class="toggle" onclick="toggleTheme()">ð Toggle Theme</button>
};
}
sub _mutant_file_footer {
return qq{
<script>
function toggleTheme() {
const html = document.documentElement;
const current = html.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
}
(function() {
const saved = localStorage.getItem('theme');
if (saved) {
document.documentElement.setAttribute('data-theme', saved);
}
( run in 1.320 second using v1.01-cache-2.11-cpan-56fb94df46f )