Skip to content

Commit 56ce83b

Browse files
MoLowRafaelGSS
authored andcommitted
test_runner: report entryFile in TestStream events
Signed-off-by: Moshe Atlov <moshe@atlow.co.il> PR-URL: #64309 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
1 parent 2a51f8b commit 56ce83b

7 files changed

Lines changed: 131 additions & 11 deletions

File tree

‎doc/api/test.md‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3438,6 +3438,10 @@ added:
34383438
- v18.9.0
34393439
- v16.19.0
34403440
changes:
3441+
- version: REPLACEME
3442+
pr-url: https://github.com/nodejs/node/pull/64309
3443+
description: Added `entryFile` to events forwarded from child processes
3444+
when tests run with process isolation.
34413445
- version: v26.3.0
34423446
pr-url: https://github.com/nodejs/node/pull/63435
34433447
description: Added `parentId` to test events that carry a `testId`.
@@ -3522,6 +3526,10 @@ Emitted when code coverage is enabled and all tests have completed.
35223526
*`cause` {Error} The actual error thrown by the test.
35233527
*`type` {string|undefined} The type of the test, used to denote whether
35243528
this is a suite.
3529+
*`entryFile` {string|undefined} The path of the test file that was
3530+
executed as the entry point of the child process that emitted this event.
3531+
Only present when tests run with process isolation. May differ from
3532+
`file` when the test is defined in a module imported by the entry file.
35253533
*`file` {string|undefined} The path of the test file,
35263534
`undefined` if test was run through the REPL.
35273535
*`line` {number|undefined} The line number where the test is defined, or
@@ -3551,6 +3559,10 @@ The corresponding declaration ordered events are `'test:pass'` and `'test:fail'`
35513559
*`data` {Object}
35523560
*`column` {number|undefined} The column number where the test is defined, or
35533561
`undefined` if the test was run through the REPL.
3562+
*`entryFile` {string|undefined} The path of the test file that was
3563+
executed as the entry point of the child process that emitted this event.
3564+
Only present when tests run with process isolation. May differ from
3565+
`file` when the test is defined in a module imported by the entry file.
35543566
*`file` {string|undefined} The path of the test file,
35553567
`undefined` if test was run through the REPL.
35563568
*`line` {number|undefined} The line number where the test is defined, or
@@ -3577,6 +3589,10 @@ defined. The corresponding declaration ordered event is `'test:start'`.
35773589
*`data` {Object}
35783590
*`column` {number|undefined} The column number where the test is defined, or
35793591
`undefined` if the test was run through the REPL.
3592+
*`entryFile` {string|undefined} The path of the test file that was
3593+
executed as the entry point of the child process that emitted this event.
3594+
Only present when tests run with process isolation. May differ from
3595+
`file` when the test is defined in a module imported by the entry file.
35803596
*`file` {string|undefined} The path of the test file,
35813597
`undefined` if test was run through the REPL.
35823598
*`line` {number|undefined} The line number where the test is defined, or
@@ -3598,6 +3614,10 @@ defined.
35983614
*`data` {Object}
35993615
*`column` {number|undefined} The column number where the test is defined, or
36003616
`undefined` if the test was run through the REPL.
3617+
*`entryFile` {string|undefined} The path of the test file that was
3618+
executed as the entry point of the child process that emitted this event.
3619+
Only present when tests run with process isolation. May differ from
3620+
`file` when the test is defined in a module imported by the entry file.
36013621
*`file` {string|undefined} The path of the test file,
36023622
`undefined` if test was run through the REPL.
36033623
*`line` {number|undefined} The line number where the test is defined, or
@@ -3630,6 +3650,10 @@ Emitted when a test is enqueued for execution.
36303650
this is a suite.
36313651
*`attempt` {number|undefined} The attempt number of the test run,
36323652
present only when using the [`--test-rerun-failures`][] flag.
3653+
*`entryFile` {string|undefined} The path of the test file that was
3654+
executed as the entry point of the child process that emitted this event.
3655+
Only present when tests run with process isolation. May differ from
3656+
`file` when the test is defined in a module imported by the entry file.
36333657
*`file` {string|undefined} The path of the test file,
36343658
`undefined` if test was run through the REPL.
36353659
*`line` {number|undefined} The line number where the test is defined, or
@@ -3693,6 +3717,10 @@ since the parent runner only knows about file-level tests. When using
36933717
present only when using the [`--test-rerun-failures`][] flag.
36943718
*`passed_on_attempt` {number|undefined} The attempt number the test passed on,
36953719
present only when using the [`--test-rerun-failures`][] flag.
3720+
*`entryFile` {string|undefined} The path of the test file that was
3721+
executed as the entry point of the child process that emitted this event.
3722+
Only present when tests run with process isolation. May differ from
3723+
`file` when the test is defined in a module imported by the entry file.
36963724
*`file` {string|undefined} The path of the test file,
36973725
`undefined` if test was run through the REPL.
36983726
*`line` {number|undefined} The line number where the test is defined, or
@@ -3722,6 +3750,10 @@ The corresponding execution ordered event is `'test:complete'`.
37223750
*`data` {Object}
37233751
*`column` {number|undefined} The column number where the test is defined, or
37243752
`undefined` if the test was run through the REPL.
3753+
*`entryFile` {string|undefined} The path of the test file that was
3754+
executed as the entry point of the child process that emitted this event.
3755+
Only present when tests run with process isolation. May differ from
3756+
`file` when the test is defined in a module imported by the entry file.
37253757
*`file` {string|undefined} The path of the test file,
37263758
`undefined` if test was run through the REPL.
37273759
*`line` {number|undefined} The line number where the test is defined, or
@@ -3738,6 +3770,10 @@ defined.
37383770
*`data` {Object}
37393771
*`column` {number|undefined} The column number where the test is defined, or
37403772
`undefined` if the test was run through the REPL.
3773+
*`entryFile` {string|undefined} The path of the test file that was
3774+
executed as the entry point of the child process that emitted this event.
3775+
Only present when tests run with process isolation. May differ from
3776+
`file` when the test is defined in a module imported by the entry file.
37413777
*`file` {string|undefined} The path of the test file,
37423778
`undefined` if test was run through the REPL.
37433779
*`line` {number|undefined} The line number where the test is defined, or
@@ -3762,6 +3798,9 @@ The corresponding execution ordered event is `'test:dequeue'`.
37623798
### Event: `'test:stderr'`
37633799

37643800
*`data` {Object}
3801+
*`entryFile` {string|undefined} The path of the test file that was
3802+
executed as the entry point of the child process that emitted this event.
3803+
Only present when tests run with process isolation.
37653804
*`file` {string} The path of the test file.
37663805
*`message` {string} The message written to `stderr`.
37673806

@@ -3773,6 +3812,9 @@ defined.
37733812
### Event: `'test:stdout'`
37743813

37753814
*`data` {Object}
3815+
*`entryFile` {string|undefined} The path of the test file that was
3816+
executed as the entry point of the child process that emitted this event.
3817+
Only present when tests run with process isolation.
37763818
*`file` {string} The path of the test file.
37773819
*`message` {string} The message written to `stdout`.
37783820

‎lib/internal/test_runner/runner.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ class FileTest extends Test {
295295
ArrayPrototypeIncludes(kDiagnosticsFilterArgs,StringPrototypeSlice(comment,0,firstSpaceIndex));
296296
}
297297
#handleReportItem(item){
298+
// The name is empty when a single child process runs all test files.
299+
if(this.name!==''){
300+
item.data.entryFile=this.loc.file;
301+
}
298302
constisTopLevel=item.data.nesting===0;
299303
if(isTopLevel){
300304
if(item.type==='test:plan'&&this.#skipReporting()){
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import{test}from'node:test';
2+
import{runShared}from'./helper.mjs';
3+
test('backup A',async(t)=>{awaitrunShared(t,'A');});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import{test}from'node:test';
2+
import{runShared}from'./helper.mjs';
3+
test('backup B',async(t)=>{awaitrunShared(t,'B');});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
exportasyncfunctionrunShared(t,target){
2+
awaitt.test(`restore ${target}`,async()=>{});
3+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import'../common/index.mjs';
2+
import*asfixturesfrom'../common/fixtures.mjs';
3+
import{describe,it,run}from'node:test';
4+
importassertfrom'node:assert';
5+
6+
constaPath=fixtures.path('test-runner','entry-file','a.test.mjs');
7+
constbPath=fixtures.path('test-runner','entry-file','b.test.mjs');
8+
consthelperPath=fixtures.path('test-runner','entry-file','helper.mjs');
9+
10+
asyncfunctioncollectEvents(options){
11+
constevents=[];
12+
conststream=run({files: [aPath,bPath], ...options});
13+
stream.on('test:fail',()=>{});
14+
forawait(consteventofstream){
15+
events.push(event);
16+
}
17+
returnevents;
18+
}
19+
20+
describe('entryFile attribution in reporter events',{concurrency: false},()=>{
21+
it('stamps entryFile on events forwarded from child processes',async()=>{
22+
constevents=awaitcollectEvents({isolation: 'process'});
23+
constchecked={__proto__: null,A: 0,B: 0};
24+
25+
for(const{ type, data }ofevents){
26+
if(data?.name==='restore A'||data?.name==='restore B'){
27+
consttarget=data.name==='restore A' ? 'A' : 'B';
28+
constexpectedEntry=target==='A' ? aPath : bPath;
29+
assert.strictEqual(data.file,helperPath,
30+
`${type} file should be the definition site`);
31+
assert.strictEqual(data.entryFile,expectedEntry,
32+
`${type} entryFile should be the entry file`);
33+
checked[target]++;
34+
}
35+
}
36+
37+
// Each subtest emits at least enqueue/dequeue/start/pass/complete.
38+
assert.ok(checked.A>=4,`expected events for restore A, got ${checked.A}`);
39+
assert.ok(checked.B>=4,`expected events for restore B, got ${checked.B}`);
40+
});
41+
42+
it('stamps entryFile on top-level tests forwarded from child processes',async()=>{
43+
constevents=awaitcollectEvents({isolation: 'process'});
44+
constpass=events.filter(({ type })=>type==='test:pass');
45+
constbackupA=pass.find(({ data })=>data.name==='backup A');
46+
constbackupB=pass.find(({ data })=>data.name==='backup B');
47+
assert.strictEqual(backupA.data.entryFile,aPath);
48+
assert.strictEqual(backupB.data.entryFile,bPath);
49+
});
50+
51+
it('does not stamp entryFile with isolation none',async()=>{
52+
constevents=awaitcollectEvents({isolation: 'none'});
53+
for(const{ data }ofevents){
54+
if(data?.name==='restore A'||data?.name==='restore B'){
55+
assert.strictEqual(data.entryFile,undefined);
56+
}
57+
}
58+
});
59+
});

‎test/parallel/test-runner-v8-deserializer.mjs‎

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { describe, it, beforeEach } from 'node:test';
55
importassertfrom'node:assert';
66
import{finished}from'node:stream/promises';
77
import{DefaultSerializer}from'node:v8';
8+
import{resolve}from'node:path';
89
importserializerfrom'internal/test_runner/reporter/v8-serializer';
910
importrunnerfrom'internal/test_runner/runner';
1011

@@ -14,10 +15,15 @@ async function toArray(chunks) {
1415
returnarr;
1516
}
1617

18+
constentryFile=resolve('filetest');
1719
constdiagnosticEvent={
1820
type: 'test:diagnostic',
1921
data: {nesting: 0,details: {},message: 'diagnostic'},
2022
};
23+
constreportedDiagnosticEvent={
24+
type: 'test:diagnostic',
25+
data: { ...diagnosticEvent.data, entryFile },
26+
};
2127
constchunks=awaittoArray(serializer([diagnosticEvent]));
2228
constdefaultSerializer=newDefaultSerializer();
2329
defaultSerializer.writeHeader();
@@ -67,28 +73,28 @@ describe('v8 deserializer', common.mustCall(() => {
6773
it('should deserialize a chunk with no serialization',async()=>{
6874
constreported=awaitcollectReported([Buffer.from('unknown')]);
6975
assert.deepStrictEqual(reported,[
70-
{data: {__proto__: null,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
76+
{data: {__proto__: null,entryFile,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
7177
]);
7278
});
7379

7480
it('should deserialize a serialized chunk',async()=>{
7581
constreported=awaitcollectReported(chunks);
76-
assert.deepStrictEqual(reported,[diagnosticEvent]);
82+
assert.deepStrictEqual(reported,[reportedDiagnosticEvent]);
7783
});
7884

7985
it('should deserialize a serialized chunk after non-serialized chunk',async()=>{
8086
constreported=awaitcollectReported([Buffer.concat([Buffer.from('unknown'), ...chunks])]);
8187
assert.deepStrictEqual(reported,[
82-
{data: {__proto__: null,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
83-
diagnosticEvent,
88+
{data: {__proto__: null,entryFile,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
89+
reportedDiagnosticEvent,
8490
]);
8591
});
8692

8793
it('should deserialize a serialized chunk before non-serialized output',async()=>{
8894
constreported=awaitcollectReported([Buffer.concat([ ...chunks,Buffer.from('unknown')])]);
8995
assert.deepStrictEqual(reported,[
90-
diagnosticEvent,
91-
{data: {__proto__: null,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
96+
reportedDiagnosticEvent,
97+
{data: {__proto__: null,entryFile,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
9298
]);
9399
});
94100

@@ -131,7 +137,7 @@ describe('v8 deserializer', common.mustCall(() => {
131137
oversizedLengthHeader,
132138
...chunks,
133139
]);
134-
assert.deepStrictEqual(reported.at(-1),diagnosticEvent);
140+
assert.deepStrictEqual(reported.at(-1),reportedDiagnosticEvent);
135141
assert.strictEqual(reported.filter((event)=>event.type==='test:diagnostic').length,1);
136142
assert.strictEqual(collectStdout(reported),oversizedLengthStdout);
137143
});
@@ -152,7 +158,7 @@ describe('v8 deserializer', common.mustCall(() => {
152158
constdata=chunks[0];
153159
constreported=awaitcollectReported([data.subarray(0,i),data.subarray(i)]);
154160
assert.deepStrictEqual(reported,[
155-
diagnosticEvent,
161+
reportedDiagnosticEvent,
156162
]);
157163
});
158164

@@ -163,9 +169,9 @@ describe('v8 deserializer', common.mustCall(() => {
163169
Buffer.concat([data.subarray(i),Buffer.from('unknown')]),
164170
]);
165171
assert.deepStrictEqual(reported,[
166-
{data: {__proto__: null,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
167-
diagnosticEvent,
168-
{data: {__proto__: null,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
172+
{data: {__proto__: null,entryFile,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
173+
reportedDiagnosticEvent,
174+
{data: {__proto__: null,entryFile,file: 'filetest',message: 'unknown'},type: 'test:stdout'},
169175
]);
170176
}
171177
);

0 commit comments

Comments
 (0)