Skip to content

Commit 1daec9a

Browse files
pmarchinitargos
authored andcommitted
test_runner: avoid coverage report partial file names
Co-author: Medhansh404 <21ucs126@lnmiit.ac.in> PR-URL: #54379 Fixes: #51299 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
1 parent c8b7a64 commit 1daec9a

19 files changed

+544
-156
lines changed

lib/internal/test_runner/utils.js

Lines changed: 118 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
'use strict';
22
const {
33
ArrayPrototypeFlatMap,
4+
ArrayPrototypeForEach,
45
ArrayPrototypeJoin,
56
ArrayPrototypeMap,
7+
ArrayPrototypePop,
68
ArrayPrototypePush,
79
ArrayPrototypeReduce,
810
ArrayPrototypeSome,
@@ -24,7 +26,7 @@ const {
2426
} = primordials;
2527

2628
const { AsyncResource } = require('async_hooks');
27-
const { relative } = require('path');
29+
const { relative, sep } = require('path');
2830
const { createWriteStream } = require('fs');
2931
const { pathToFileURL } = require('internal/url');
3032
const { createDeferredPromise } = require('internal/util');
@@ -409,6 +411,36 @@ const kColumns = ['line %', 'branch %', 'funcs %'];
409411
const kColumnsKeys = ['coveredLinePercent', 'coveredBranchPercent', 'coveredFunctionPercent'];
410412
const kSeparator = ' | ';
411413

414+
function buildFileTree(summary) {
415+
const tree = { __proto__: null };
416+
let treeDepth = 1;
417+
let longestFile = 0;
418+
419+
ArrayPrototypeForEach(summary.files, (file) => {
420+
let longestPart = 0;
421+
const parts = StringPrototypeSplit(relative(summary.workingDirectory, file.path), sep);
422+
let current = tree;
423+
424+
ArrayPrototypeForEach(parts, (part, index) => {
425+
if (!current[part]) {
426+
current[part] = { __proto__: null };
427+
}
428+
current = current[part];
429+
// If this is the last part, add the file to the tree
430+
if (index === parts.length - 1) {
431+
current.file = file;
432+
}
433+
// Keep track of the longest part for padding
434+
longestPart = MathMax(longestPart, part.length);
435+
});
436+
437+
treeDepth = MathMax(treeDepth, parts.length);
438+
longestFile = MathMax(longestPart, longestFile);
439+
});
440+
441+
return { __proto__: null, tree, treeDepth, longestFile };
442+
}
443+
412444
function getCoverageReport(pad, summary, symbol, color, table) {
413445
const prefix = `${pad}${symbol}`;
414446
let report = `${color}${prefix}start of coverage report\n`;
@@ -418,11 +450,19 @@ function getCoverageReport(pad, summary, symbol, color, table) {
418450
let uncoveredLinesPadLength;
419451
let tableWidth;
420452

453+
// Create a tree of file paths
454+
const { tree, treeDepth, longestFile } = buildFileTree(summary);
421455
if (table) {
422-
// Get expected column sizes
423-
filePadLength = table && ArrayPrototypeReduce(summary.files, (acc, file) =>
424-
MathMax(acc, relative(summary.workingDirectory, file.path).length), 0);
456+
// Calculate expected column sizes based on the tree
457+
filePadLength = table && longestFile;
458+
filePadLength += (treeDepth - 1);
459+
if (color) {
460+
filePadLength += 2;
461+
}
425462
filePadLength = MathMax(filePadLength, 'file'.length);
463+
if (filePadLength > (process.stdout.columns / 2)) {
464+
filePadLength = MathFloor(process.stdout.columns / 2);
465+
}
426466
const fileWidth = filePadLength + 2;
427467

428468
columnPadLengths = ArrayPrototypeMap(kColumns, (column) => (table ? MathMax(column.length, 6) : 0));
@@ -435,26 +475,17 @@ function getCoverageReport(pad, summary, symbol, color, table) {
435475

436476
tableWidth = fileWidth + columnsWidth + uncoveredLinesWidth;
437477

438-
// Fit with sensible defaults
439478
const availableWidth = (process.stdout.columns || Infinity) - prefix.length;
440479
const columnsExtras = tableWidth - availableWidth;
441480
if (table && columnsExtras > 0) {
442-
// Ensure file name is sufficiently visible
443-
const minFilePad = MathMin(8, filePadLength);
444-
filePadLength -= MathFloor(columnsExtras * 0.2);
445-
filePadLength = MathMax(filePadLength, minFilePad);
446-
447-
// Get rest of available space, subtracting margins
481+
filePadLength = MathMin(availableWidth * 0.5, filePadLength);
448482
uncoveredLinesPadLength = MathMax(availableWidth - columnsWidth - (filePadLength + 2) - 2, 1);
449-
450-
// Update table width
451483
tableWidth = availableWidth;
452484
} else {
453485
uncoveredLinesPadLength = Infinity;
454486
}
455487
}
456488

457-
458489
function getCell(string, width, pad, truncate, coverage) {
459490
if (!table) return string;
460491

@@ -469,35 +500,85 @@ function getCoverageReport(pad, summary, symbol, color, table) {
469500
return result;
470501
}
471502

472-
// Head
473-
if (table) report += addTableLine(prefix, tableWidth);
474-
report += `${prefix}${getCell('file', filePadLength, StringPrototypePadEnd, truncateEnd)}${kSeparator}` +
475-
`${ArrayPrototypeJoin(ArrayPrototypeMap(kColumns, (column, i) => getCell(column, columnPadLengths[i], StringPrototypePadStart)), kSeparator)}${kSeparator}` +
476-
`${getCell('uncovered lines', uncoveredLinesPadLength, false, truncateEnd)}\n`;
477-
if (table) report += addTableLine(prefix, tableWidth);
503+
function writeReportLine({ file, depth = 0, coveragesColumns, fileCoverage, uncoveredLines }) {
504+
const fileColumn = `${prefix}${StringPrototypeRepeat(' ', depth)}${getCell(file, filePadLength - depth, StringPrototypePadEnd, truncateStart, fileCoverage)}`;
505+
const coverageColumns = ArrayPrototypeJoin(ArrayPrototypeMap(coveragesColumns, (coverage, j) => {
506+
const coverageText = typeof coverage === 'number' ? NumberPrototypeToFixed(coverage, 2) : coverage;
507+
return getCell(coverageText, columnPadLengths[j], StringPrototypePadStart, false, coverage);
508+
}), kSeparator);
478509

479-
// Body
480-
for (let i = 0; i < summary.files.length; ++i) {
481-
const file = summary.files[i];
482-
const relativePath = relative(summary.workingDirectory, file.path);
510+
const uncoveredLinesColumn = getCell(uncoveredLines, uncoveredLinesPadLength, false, truncateEnd);
483511

484-
let fileCoverage = 0;
485-
const coverages = ArrayPrototypeMap(kColumnsKeys, (columnKey) => {
486-
const percent = file[columnKey];
487-
fileCoverage += percent;
488-
return percent;
489-
});
490-
fileCoverage /= kColumnsKeys.length;
512+
return `${fileColumn}${kSeparator}${coverageColumns}${kSeparator}${uncoveredLinesColumn}\n`;
513+
}
491514

492-
report += `${prefix}${getCell(relativePath, filePadLength, StringPrototypePadEnd, truncateStart, fileCoverage)}${kSeparator}` +
493-
`${ArrayPrototypeJoin(ArrayPrototypeMap(coverages, (coverage, j) => getCell(NumberPrototypeToFixed(coverage, 2), columnPadLengths[j], StringPrototypePadStart, false, coverage)), kSeparator)}${kSeparator}` +
494-
`${getCell(formatUncoveredLines(getUncoveredLines(file.lines), table), uncoveredLinesPadLength, false, truncateEnd)}\n`;
515+
function printCoverageBodyTree(tree, depth = 0) {
516+
for (const key in tree) {
517+
if (tree[key].file) {
518+
const file = tree[key].file;
519+
const fileName = ArrayPrototypePop(StringPrototypeSplit(file.path, sep));
520+
521+
let fileCoverage = 0;
522+
const coverages = ArrayPrototypeMap(kColumnsKeys, (columnKey) => {
523+
const percent = file[columnKey];
524+
fileCoverage += percent;
525+
return percent;
526+
});
527+
fileCoverage /= kColumnsKeys.length;
528+
529+
const uncoveredLines = formatUncoveredLines(getUncoveredLines(file.lines), table);
530+
531+
report += writeReportLine({
532+
__proto__: null,
533+
file: fileName,
534+
depth: depth,
535+
coveragesColumns: coverages,
536+
fileCoverage: fileCoverage,
537+
uncoveredLines: uncoveredLines,
538+
});
539+
} else {
540+
report += writeReportLine({
541+
__proto__: null,
542+
file: key,
543+
depth: depth,
544+
coveragesColumns: ArrayPrototypeMap(columnPadLengths, () => ''),
545+
fileCoverage: undefined,
546+
uncoveredLines: '',
547+
});
548+
printCoverageBodyTree(tree[key], depth + 1);
549+
}
550+
}
495551
}
496552

497-
// Foot
553+
// -------------------------- Coverage Report --------------------------
554+
if (table) report += addTableLine(prefix, tableWidth);
555+
556+
// Print the header
557+
report += writeReportLine({
558+
__proto__: null,
559+
file: 'file',
560+
coveragesColumns: kColumns,
561+
fileCoverage: undefined,
562+
uncoveredLines: 'uncovered lines',
563+
});
564+
565+
if (table) report += addTableLine(prefix, tableWidth);
566+
567+
// Print the body
568+
printCoverageBodyTree(tree);
569+
498570
if (table) report += addTableLine(prefix, tableWidth);
499-
report += `${prefix}${getCell('all files', filePadLength, StringPrototypePadEnd, truncateEnd)}${kSeparator}` +
500-
`${ArrayPrototypeJoin(ArrayPrototypeMap(kColumnsKeys, (columnKey, j) => getCell(NumberPrototypeToFixed(summary.totals[columnKey], 2), columnPadLengths[j], StringPrototypePadStart, false, summary.totals[columnKey])), kSeparator)} |\n`;
571+
572+
// Print the footer
573+
const allFilesCoverages = ArrayPrototypeMap(kColumnsKeys, (columnKey) => summary.totals[columnKey]);
574+
report += writeReportLine({
575+
__proto__: null,
576+
file: 'all files',
577+
coveragesColumns: allFilesCoverages,
578+
fileCoverage: undefined,
579+
uncoveredLines: '',
580+
});
581+
501582
if (table) report += addTableLine(prefix, tableWidth);
502583

503584
report += `${prefix}end of coverage report\n`;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
// Here we can't import common module as the coverage will be different based on the system
3+
4+
// Empty functions that don't do anything
5+
function doNothing1() {
6+
// Not implemented
7+
}
8+
9+
function doNothing2() {
10+
// No logic here
11+
}
12+
13+
function unusedFunction1() {
14+
// Intentionally left empty
15+
}
16+
17+
function unusedFunction2() {
18+
// Another empty function
19+
}
20+
21+
// Unused variables
22+
const unusedVariable1 = 'This is never used';
23+
const unusedVariable2 = 42;
24+
let unusedVariable3;
25+
26+
// Empty class with no methods
27+
class UnusedClass {
28+
constructor() {
29+
// Constructor does nothing
30+
}
31+
}
32+
33+
// Empty object literal
34+
const emptyObject = {};
35+
36+
// Empty array
37+
const emptyArray = [];
38+
39+
// Function with parameters but no body
40+
function doNothingWithParams(param1, param2) {
41+
// No implementation
42+
}
43+
44+
// Function that returns nothing
45+
function returnsNothing() {
46+
// No return statement
47+
}
48+
49+
// Another unused function
50+
function unusedFunction3() {
51+
// More empty code
52+
}

test/fixtures/test-runner/output/coverage-width-100-uncovered-lines.snapshot

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ ok 1 - Coverage Print Fixed Width 100
1515
# duration_ms *
1616
# start of coverage report
1717
# --------------------------------------------------------------------------------------------------
18-
# file | line % | branch % | funcs % | uncovered lines
18+
# file | line % | branch % | funcs % | uncovered lines
1919
# --------------------------------------------------------------------------------------------------
20-
# …ap/a.js | 55.77 | 100.00 | 0.00 | 5-7 9-11 13-15 17-19 29-30 40-42 45-47 50-52
21-
# …ap/b.js | 45.45 | 100.00 | 0.00 | 5-7 9-11
22-
# …ines.js | 50.99 | 42.86 | 1.92 | 5-7 9-11 13-15 17-19 29-30 40-42 45-47 50-52 55-57 59-6…
23-
# …nes.mjs | 100.00 | 100.00 | 100.00 |
20+
# test | | | |
21+
# fixtures | | | |
22+
# test-runner | | | |
23+
# coverage-snap | | | |
24+
# a.js | 55.77 | 100.00 | 0.00 | 5-7 9-11 13-15 17-19 …
25+
# b.js | 45.45 | 100.00 | 0.00 | 5-7 9-11
26+
# many-uncovered-lines.js | 50.99 | 42.86 | 1.92 | 5-7 9-11 13-15 17-19 …
27+
# output | | | |
28+
# coverage-width-100-uncovered-lines.mjs | 100.00 | 100.00 | 100.00 |
2429
# --------------------------------------------------------------------------------------------------
25-
# all fil… | 52.80 | 60.00 | 1.61 |
30+
# all files | 52.80 | 60.00 | 1.61 |
2631
# --------------------------------------------------------------------------------------------------
2732
# end of coverage report

test/fixtures/test-runner/output/coverage-width-100.snapshot

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@ ok 1 - Coverage Print Fixed Width 100
1515
# duration_ms *
1616
# start of coverage report
1717
# --------------------------------------------------------------------------------------------------
18-
# file | line % | branch % | funcs % | uncovered lines
18+
# file | line % | branch % | funcs % | uncovered lines
1919
# --------------------------------------------------------------------------------------------------
20-
# test/fixtures/test-runner/coverage-snap/a.js | 55.77 | 100.00 | 0.00 | 5-7 9-11 13-15 …
21-
# test/fixtures/test-runner/coverage-snap/b.js | 45.45 | 100.00 | 0.00 | 5-7 9-11
22-
# …tures/test-runner/output/coverage-width-100.mjs | 100.00 | 100.00 | 100.00 |
20+
# test | | | |
21+
# fixtures | | | |
22+
# test-runner | | | |
23+
# coverage-snap | | | |
24+
# a.js | 55.77 | 100.00 | 0.00 | 5-7 9-11 13-15 17-19 29-30 40-42 45-4…
25+
# b.js | 45.45 | 100.00 | 0.00 | 5-7 9-11
26+
# output | | | |
27+
# coverage-width-100.mjs | 100.00 | 100.00 | 100.00 |
2328
# --------------------------------------------------------------------------------------------------
24-
# all files | 60.81 | 100.00 | 0.00 |
29+
# all files | 60.81 | 100.00 | 0.00 |
2530
# --------------------------------------------------------------------------------------------------
2631
# end of coverage report

test/fixtures/test-runner/output/coverage-width-150-uncovered-lines.snapshot

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ ok 1 - Coverage Print Fixed Width 150
1515
# duration_ms *
1616
# start of coverage report
1717
# ----------------------------------------------------------------------------------------------------------------------------------------------------
18-
# file | line % | branch % | funcs % | uncovered lines
18+
# file | line % | branch % | funcs % | uncovered lines
1919
# ----------------------------------------------------------------------------------------------------------------------------------------------------
20-
# …ap/a.js | 55.77 | 100.00 | 0.00 | 5-7 9-11 13-15 17-19 29-30 40-42 45-47 50-52
21-
# …ap/b.js | 45.45 | 100.00 | 0.00 | 5-7 9-11
22-
# …ines.js | 50.99 | 42.86 | 1.92 | 5-7 9-11 13-15 17-19 29-30 40-42 45-47 50-52 55-57 59-61 63-65 67-69 91-93 96-98 100-102 104-106 111-112 …
23-
# …nes.mjs | 100.00 | 100.00 | 100.00 |
20+
# test | | | |
21+
# fixtures | | | |
22+
# test-runner | | | |
23+
# coverage-snap | | | |
24+
# a.js | 55.77 | 100.00 | 0.00 | 5-7 9-11 13-15 17-19 29-30 40-42 45-47 50-52
25+
# b.js | 45.45 | 100.00 | 0.00 | 5-7 9-11
26+
# many-uncovered-lines.js | 50.99 | 42.86 | 1.92 | 5-7 9-11 13-15 17-19 29-30 40-42 45-47 50-52 55-57 59-61 63-65 67-69 91…
27+
# output | | | |
28+
# coverage-width-150-uncovered-lines.mjs | 100.00 | 100.00 | 100.00 |
2429
# ----------------------------------------------------------------------------------------------------------------------------------------------------
25-
# all fil… | 52.80 | 60.00 | 1.61 |
30+
# all files | 52.80 | 60.00 | 1.61 |
2631
# ----------------------------------------------------------------------------------------------------------------------------------------------------
2732
# end of coverage report

test/fixtures/test-runner/output/coverage-width-150.snapshot

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,18 @@ ok 1 - Coverage Print Fixed Width 150
1414
# todo 0
1515
# duration_ms *
1616
# start of coverage report
17-
# -------------------------------------------------------------------------------------------------------------------------------------
18-
# file | line % | branch % | funcs % | uncovered lines
19-
# -------------------------------------------------------------------------------------------------------------------------------------
20-
# test/fixtures/test-runner/coverage-snap/a.js | 55.77 | 100.00 | 0.00 | 5-7 9-11 13-15 17-19 29-30 40-42 45-47 50-52
21-
# test/fixtures/test-runner/coverage-snap/b.js | 45.45 | 100.00 | 0.00 | 5-7 9-11
22-
# test/fixtures/test-runner/output/coverage-width-150.mjs | 100.00 | 100.00 | 100.00 |
23-
# -------------------------------------------------------------------------------------------------------------------------------------
24-
# all files | 60.81 | 100.00 | 0.00 |
25-
# -------------------------------------------------------------------------------------------------------------------------------------
17+
# --------------------------------------------------------------------------------------------------------
18+
# file | line % | branch % | funcs % | uncovered lines
19+
# --------------------------------------------------------------------------------------------------------
20+
# test | | | |
21+
# fixtures | | | |
22+
# test-runner | | | |
23+
# coverage-snap | | | |
24+
# a.js | 55.77 | 100.00 | 0.00 | 5-7 9-11 13-15 17-19 29-30 40-42 45-47 50-52
25+
# b.js | 45.45 | 100.00 | 0.00 | 5-7 9-11
26+
# output | | | |
27+
# coverage-width-150.mjs | 100.00 | 100.00 | 100.00 |
28+
# --------------------------------------------------------------------------------------------------------
29+
# all files | 60.81 | 100.00 | 0.00 |
30+
# --------------------------------------------------------------------------------------------------------
2631
# end of coverage report
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Flags: --experimental-test-coverage
2+
// here we can't import common module as the coverage will be different based on the system
3+
// Unused imports are here in order to populate the coverage report
4+
import * as a from '../coverage-snap/b.js';
5+
import * as b from '../coverage-snap/a.js';
6+
import * as c from '../coverage-snap/a-very-long-long-long-sub-dir/c.js';
7+
8+
import { test } from 'node:test';
9+
10+
process.stdout.columns = 40;
11+
12+
test(`Coverage Print Fixed Width ${process.stdout.columns}`);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy