Skip to content

Commit 98598f0

Browse files
committed
test_runner: add reporters
1 parent 2cad517 commit 98598f0

File tree

10 files changed

+186
-65
lines changed

10 files changed

+186
-65
lines changed

‎lib/internal/main/test_runner.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const{
66
const{ getOptionValue }=require('internal/options');
77
const{ isUsingInspector }=require('internal/util/inspector');
88
const{ run }=require('internal/test_runner/runner');
9+
const{ getTestReporter }=require('internal/test_runner/utils');
910
const{exitCodes: { kGenericUserError }}=internalBinding('errors');
1011

1112
prepareMainThreadExecution(false);
@@ -21,7 +22,7 @@ if (isUsingInspector()){
2122
inspectPort=process.debugPort;
2223
}
2324

24-
consttapStream=run({ concurrency, inspectPort,watch: getOptionValue('--watch')});
25+
consttapStream=getTestReporter(run({ concurrency, inspectPort,watch: getOptionValue('--watch')}));
2526
tapStream.pipe(process.stdout);
2627
tapStream.once('test:fail',()=>{
2728
process.exitCode=kGenericUserError;

‎lib/internal/test_runner/harness.js‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const{exitCodes:{kGenericUserError } } = internalBinding('errors');
1818
const{ kEmptyObject }=require('internal/util');
1919
const{ getOptionValue }=require('internal/options');
2020
const{ kCancelledByParent, Test, ItTest, Suite }=require('internal/test_runner/test');
21+
const{ getTestReporter }=require('internal/test_runner/utils');
2122
const{bigint: hrtime}=process.hrtime;
2223

2324
constisTestRunnerCli=getOptionValue('--test');
@@ -119,8 +120,9 @@ let globalRoot;
119120
functiongetGlobalRoot(){
120121
if(!globalRoot){
121122
globalRoot=createTestTree();
122-
globalRoot.reporter.pipe(process.stdout);
123-
globalRoot.reporter.once('test:fail',()=>{
123+
constreporter=getTestReporter(globalRoot.reporter);
124+
reporter.pipe(process.stdout);
125+
reporter.once('test:fail',()=>{
124126
process.exitCode=kGenericUserError;
125127
});
126128
}

‎lib/internal/test_runner/runner.js‎

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ const{validateArray, validateBoolean } = require('internal/validators');
3333
const{ getInspectPort, isUsingInspector, isInspectorMessage }=require('internal/util/inspector');
3434
const{ kEmptyObject }=require('internal/util');
3535
const{ createTestTree }=require('internal/test_runner/harness');
36-
const{kDefaultIndent,kSubtestsFailed, Test }=require('internal/test_runner/test');
36+
const{ kSubtestsFailed, Test }=require('internal/test_runner/test');
3737
const{ TapParser }=require('internal/test_runner/tap_parser');
38+
const{ kDefaultIndent }=require('internal/test_runner/tap_stream');
3839
const{ TokenKind }=require('internal/test_runner/tap_lexer');
3940

4041
const{
@@ -49,6 +50,7 @@ const{
4950
}=internalBinding('errors');
5051

5152
constkFilterArgs=['--test','--watch'];
53+
constkFilterArgValues=['--test-reporter'];
5254

5355
// TODO(cjihrig): Replace this with recursive readdir once it lands.
5456
functionprocessPath(path,testFiles,options){
@@ -112,8 +114,10 @@ function createTestFileList(){
112114
returnArrayPrototypeSort(ArrayFrom(testFiles));
113115
}
114116

115-
functionfilterExecArgv(arg){
116-
return!ArrayPrototypeIncludes(kFilterArgs,arg);
117+
functionfilterExecArgv(arg,i,arr){
118+
return!ArrayPrototypeIncludes(kFilterArgs,arg)&&
119+
ArrayPrototypeIncludes(kFilterArgValues,arg)&&
120+
!ArrayPrototypeIncludes(kFilterArgValues,arr[i-1]);
117121
}
118122

119123
functiongetRunArgs({ path, inspectPort }){
@@ -128,9 +132,10 @@ function getRunArgs({path, inspectPort }){
128132
classFileTestextendsTest{
129133
#buffer =[];
130134
#handleReportItem({ kind, node, nesting =0}){
131-
constindent=StringPrototypeRepeat(kDefaultIndent,nesting+1);
135+
nesting+=1;
132136

133137
constdetails=(diagnostic)=>{
138+
constindent=StringPrototypeRepeat(kDefaultIndent,nesting);
134139
return(
135140
diagnostic&&{
136141
__proto__: null,
@@ -149,11 +154,11 @@ class FileTest extends Test{
149154
break;
150155

151156
caseTokenKind.TAP_PLAN:
152-
this.reporter.plan(indent,node.end-node.start+1);
157+
this.reporter.plan(nesting,node.end-node.start+1);
153158
break;
154159

155160
caseTokenKind.TAP_SUBTEST_POINT:
156-
this.reporter.subtest(indent,node.name);
161+
this.reporter.subtest(nesting,node.name);
157162
break;
158163

159164
caseTokenKind.TAP_TEST_POINT:
@@ -172,15 +177,15 @@ class FileTest extends Test{
172177

173178
if(pass){
174179
this.reporter.ok(
175-
indent,
180+
nesting,
176181
node.id,
177182
node.description,
178183
details(node.diagnostics),
179184
directive
180185
);
181186
}else{
182187
this.reporter.fail(
183-
indent,
188+
nesting,
184189
node.id,
185190
node.description,
186191
details(node.diagnostics),
@@ -190,15 +195,15 @@ class FileTest extends Test{
190195
break;
191196

192197
caseTokenKind.COMMENT:
193-
if(indent===kDefaultIndent){
198+
if(nesting===1){
194199
// Ignore file top level diagnostics
195200
break;
196201
}
197-
this.reporter.diagnostic(indent,node.comment);
202+
this.reporter.diagnostic(nesting,node.comment);
198203
break;
199204

200205
caseTokenKind.UNKNOWN:
201-
this.reporter.diagnostic(indent,node.value);
206+
this.reporter.diagnostic(nesting,node.value);
202207
break;
203208
}
204209
}

‎lib/internal/test_runner/tap_stream.js‎

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ const{
99
StringPrototypeReplaceAll,
1010
StringPrototypeToUpperCase,
1111
StringPrototypeSplit,
12+
StringPrototypeRepeat,
1213
RegExpPrototypeSymbolReplace,
1314
}=primordials;
1415
const{ inspectWithNoCustomRetry }=require('internal/errors');
1516
constReadable=require('internal/streams/readable');
1617
const{ isError, kEmptyObject }=require('internal/util');
18+
constkDefaultIndent=' ';// 4 spaces
1719
constkFrameStartRegExp=/^{4}at/;
1820
constkLineBreakRegExp=/\n|\r\n/;
1921
constkDefaultTAPVersion=13;
@@ -30,8 +32,8 @@ class TapStream extends Readable{
3032
#buffer;
3133
#canPush;
3234

33-
constructor(){
34-
super();
35+
constructor(options=kEmptyObject){
36+
super(options);
3537
this.#buffer =[];
3638
this.#canPush =true;
3739
}
@@ -40,34 +42,30 @@ class TapStream extends Readable{
4042
this.#canPush =true;
4143

4244
while(this.#buffer.length>0){
43-
constline=ArrayPrototypeShift(this.#buffer);
45+
constchunk=ArrayPrototypeShift(this.#buffer);
4446

45-
if(!this.#tryPush(line)){
47+
if(!this.#tryPush(chunk)){
4648
return;
4749
}
4850
}
4951
}
5052

51-
bail(message){
52-
this.#tryPush(`Bail out!${message ? ` ${tapEscape(message)}` : ''}\n`);
53+
fail(nesting,testNumber,name,details,directive){
54+
this.#emit('test:fail',{__proto__: null, name, nesting, testNumber, details, ...directive});
55+
this.#test(nesting,testNumber,'not ok',name,directive);
56+
this.#details(nesting,details);
5357
}
5458

55-
fail(indent,testNumber,name,details,directive){
56-
this.emit('test:fail',{__proto__: null, name, testNumber, details, ...directive});
57-
this.#test(indent,testNumber,'not ok',name,directive);
58-
this.#details(indent,details);
59+
ok(nesting,testNumber,name,details,directive){
60+
this.#emit('test:pass',{__proto__: null, name, nesting, testNumber, details, ...directive});
61+
this.#test(nesting,testNumber,'ok',name,directive);
62+
this.#details(nesting,details);
5963
}
6064

61-
ok(indent,testNumber,name,details,directive){
62-
this.emit('test:pass',{__proto__: null, name, testNumber, details, ...directive});
63-
this.#test(indent,testNumber,'ok',name,directive);
64-
this.#details(indent,details);
65-
}
66-
67-
plan(indent,count,explanation){
65+
plan(nesting,count,explanation){
6866
constexp=`${explanation ? ` # ${tapEscape(explanation)}` : ''}`;
6967

70-
this.#tryPush(`${indent}1..${count}${exp}\n`);
68+
this.#tryPushString(`${this.#indent(nesting)}1..${count}${exp}\n`);
7169
}
7270

7371
getSkip(reason){
@@ -78,32 +76,42 @@ class TapStream extends Readable{
7876
return{__proto__: null,todo: reason};
7977
}
8078

81-
subtest(indent,name){
82-
this.#tryPush(`${indent}# Subtest: ${tapEscape(name)}\n`);
79+
subtest(nesting,name){
80+
this.#emit('test:subtest',{ nesting, name });
81+
this.#tryPushString(`${this.#indent(nesting)}# Subtest: ${tapEscape(name)}\n`);
8382
}
8483

85-
#details(indent,data=kEmptyObject){
84+
#details(nesting,data=kEmptyObject){
8685
const{ error, duration, yaml }=data;
86+
constindent=this.#indent(nesting);
8787
letdetails=`${indent} ---\n`;
8888

8989
details+=`${yaml ? yaml : ''}`;
9090
details+=jsToYaml(indent,'duration_ms',duration);
9191
details+=jsToYaml(indent,null,error);
9292
details+=`${indent} ...\n`;
93-
this.#tryPush(details);
93+
this.#tryPushString(details);
9494
}
9595

96-
diagnostic(indent,message){
97-
this.emit('test:diagnostic',message);
98-
this.#tryPush(`${indent}# ${tapEscape(message)}\n`);
96+
diagnostic(nesting,message){
97+
this.#emit('test:diagnostic',message);
98+
this.#tryPushString(`${this.#indent(nesting)}# ${tapEscape(message)}\n`);
9999
}
100100

101101
version(spec=kDefaultTAPVersion){
102-
this.#tryPush(`TAP version ${spec}\n`);
102+
this.#tryPushString(`TAP version ${spec}\n`);
103+
}
104+
105+
#indent(nesting){
106+
returnStringPrototypeRepeat(kDefaultIndent,nesting);
103107
}
104108

105-
#test(indent,testNumber,status,name,directive=kEmptyObject){
106-
letline=`${indent}${status}${testNumber}`;
109+
#test(nesting,testNumber,status,name,directive=kEmptyObject){
110+
if(this._readableState.objectMode){
111+
// early return
112+
return;
113+
}
114+
letline=`${this.#indent(nesting)}${status}${testNumber}`;
107115

108116
if(name){
109117
line+=` ${tapEscape(`- ${name}`)}`;
@@ -115,7 +123,23 @@ class TapStream extends Readable{
115123

116124
line+='\n';
117125

118-
this.#tryPush(line);
126+
this.#tryPushString(line);
127+
}
128+
129+
#emit(type,data){
130+
this.emit(type,data);
131+
this.#tryPushObject({ type, data });
132+
}
133+
134+
#tryPushString(str){
135+
if(!this._readableState.objectMode){
136+
this.#tryPush(str);
137+
}
138+
}
139+
#tryPushObject(obj){
140+
if(this._readableState.objectMode){
141+
this.#tryPush(obj);
142+
}
119143
}
120144

121145
#tryPush(message){
@@ -261,4 +285,4 @@ function isAssertionLike(value){
261285
returnvalue&&typeofvalue==='object'&&'expected'invalue&&'actual'invalue;
262286
}
263287

264-
module.exports={ TapStream };
288+
module.exports={ TapStream, kDefaultIndent};

‎lib/internal/test_runner/test.js‎

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ const kTestCodeFailure = 'testCodeFailure'
6363
constkTestTimeoutFailure='testTimeoutFailure';
6464
constkHookFailure='hookFailed';
6565
constkDefaultTimeout=null;
66-
constkDefaultIndent=' ';// 4 spaces
6766
constnoop=FunctionPrototype;
6867
constisTestRunner=getOptionValue('--test');
68+
consthasReporters=Boolean(getOptionValue('--test-reporter'));
6969
consttestOnlyFlag=!isTestRunner&&getOptionValue('--test-only');
7070
consttestNamePatternFlag=isTestRunner ? null :
7171
getOptionValue('--test-name-pattern');
@@ -171,18 +171,18 @@ class Test extends AsyncResource{
171171

172172
if(parent===null){
173173
this.concurrency=1;
174-
this.indent='';
174+
this.nesting=0;
175175
this.only=testOnlyFlag;
176-
this.reporter=newTapStream();
176+
this.reporter=newTapStream({objectMode: hasReporters});
177177
this.runOnlySubtests=this.only;
178178
this.testNumber=0;
179179
this.timeout=kDefaultTimeout;
180180
}else{
181-
constindent=parent.parent===null ? parent.indent :
182-
parent.indent+kDefaultIndent;
181+
constnesting=parent.parent===null ? parent.nesting :
182+
parent.nesting+1;
183183

184184
this.concurrency=parent.concurrency;
185-
this.indent=indent;
185+
this.nesting=nesting;
186186
this.only=only??!parent.runOnlySubtests;
187187
this.reporter=parent.reporter;
188188
this.runOnlySubtests=!this.only;
@@ -607,19 +607,19 @@ class Test extends AsyncResource{
607607
this.parent.processPendingSubtests();
608608
}elseif(!this.reported){
609609
this.reported=true;
610-
this.reporter.plan(this.indent,this.subtests.length);
610+
this.reporter.plan(this.nesting,this.subtests.length);
611611

612612
for(leti=0;i<this.diagnostics.length;i++){
613-
this.reporter.diagnostic(this.indent,this.diagnostics[i]);
613+
this.reporter.diagnostic(this.nesting,this.diagnostics[i]);
614614
}
615615

616-
this.reporter.diagnostic(this.indent,`tests ${this.subtests.length}`);
617-
this.reporter.diagnostic(this.indent,`pass ${counters.passed}`);
618-
this.reporter.diagnostic(this.indent,`fail ${counters.failed}`);
619-
this.reporter.diagnostic(this.indent,`cancelled ${counters.cancelled}`);
620-
this.reporter.diagnostic(this.indent,`skipped ${counters.skipped}`);
621-
this.reporter.diagnostic(this.indent,`todo ${counters.todo}`);
622-
this.reporter.diagnostic(this.indent,`duration_ms ${this.#duration()}`);
616+
this.reporter.diagnostic(this.nesting,`tests ${this.subtests.length}`);
617+
this.reporter.diagnostic(this.nesting,`pass ${counters.passed}`);
618+
this.reporter.diagnostic(this.nesting,`fail ${counters.failed}`);
619+
this.reporter.diagnostic(this.nesting,`cancelled ${counters.cancelled}`);
620+
this.reporter.diagnostic(this.nesting,`skipped ${counters.skipped}`);
621+
this.reporter.diagnostic(this.nesting,`todo ${counters.todo}`);
622+
this.reporter.diagnostic(this.nesting,`duration_ms ${this.#duration()}`);
623623
this.reporter.push(null);
624624
}
625625
}
@@ -655,7 +655,7 @@ class Test extends AsyncResource{
655655

656656
report(){
657657
if(this.subtests.length>0){
658-
this.reporter.plan(this.subtests[0].indent,this.subtests.length);
658+
this.reporter.plan(this.subtests[0].nesting,this.subtests.length);
659659
}else{
660660
this.reportSubtest();
661661
}
@@ -669,14 +669,14 @@ class Test extends AsyncResource{
669669
}
670670

671671
if(this.passed){
672-
this.reporter.ok(this.indent,this.testNumber,this.name,details,directive);
672+
this.reporter.ok(this.nesting,this.testNumber,this.name,details,directive);
673673
}else{
674674
details.error=this.error;
675-
this.reporter.fail(this.indent,this.testNumber,this.name,details,directive);
675+
this.reporter.fail(this.nesting,this.testNumber,this.name,details,directive);
676676
}
677677

678678
for(leti=0;i<this.diagnostics.length;i++){
679-
this.reporter.diagnostic(this.indent,this.diagnostics[i]);
679+
this.reporter.diagnostic(this.nesting,this.diagnostics[i]);
680680
}
681681
}
682682

@@ -686,7 +686,7 @@ class Test extends AsyncResource{
686686
}
687687
this.#reportedSubtest =true;
688688
this.parent.reportSubtest();
689-
this.reporter.subtest(this.indent,this.name);
689+
this.reporter.subtest(this.nesting,this.name);
690690
}
691691
}
692692

@@ -790,7 +790,6 @@ class Suite extends Test{
790790
module.exports={
791791
ItTest,
792792
kCancelledByParent,
793-
kDefaultIndent,
794793
kSubtestsFailed,
795794
kTestCodeFailure,
796795
kUnwrapErrors,

0 commit comments

Comments
(0)