2
2
const {
3
3
ArrayFrom,
4
4
ArrayPrototypeFilter,
5
+ ArrayPrototypeForEach,
5
6
ArrayPrototypeIncludes,
6
7
ArrayPrototypeJoin,
7
8
ArrayPrototypePush,
@@ -14,6 +15,7 @@ const {
14
15
SafePromiseAllSettledReturnVoid,
15
16
SafeMap,
16
17
SafeSet,
18
+ StringPrototypeRepeat,
17
19
} = primordials ;
18
20
19
21
const { spawn } = require ( 'child_process' ) ;
@@ -31,7 +33,10 @@ const { validateArray, validateBoolean } = require('internal/validators');
31
33
const { getInspectPort, isUsingInspector, isInspectorMessage } = require ( 'internal/util/inspector' ) ;
32
34
const { kEmptyObject } = require ( 'internal/util' ) ;
33
35
const { createTestTree } = require ( 'internal/test_runner/harness' ) ;
34
- const { kSubtestsFailed, Test } = require ( 'internal/test_runner/test' ) ;
36
+ const { kDefaultIndent, kSubtestsFailed, Test } = require ( 'internal/test_runner/test' ) ;
37
+ const { TapParser } = require ( 'internal/test_runner/tap_parser' ) ;
38
+ const { TokenKind } = require ( 'internal/test_runner/tap_lexer' ) ;
39
+
35
40
const {
36
41
isSupportedFileType,
37
42
doesPathMatchFilter,
@@ -120,11 +125,103 @@ function getRunArgs({ path, inspectPort }) {
120
125
return argv ;
121
126
}
122
127
128
+ class FileTest extends Test {
129
+ #buffer = [ ] ;
130
+ #handleReportItem( { kind, node, nesting = 0 } ) {
131
+ const indent = StringPrototypeRepeat ( kDefaultIndent , nesting + 1 ) ;
132
+
133
+ const details = ( diagnostic ) => {
134
+ return (
135
+ diagnostic && {
136
+ __proto__ : null ,
137
+ yaml :
138
+ `${ indent } ` +
139
+ ArrayPrototypeJoin ( diagnostic , `\n${ indent } ` ) +
140
+ '\n' ,
141
+ }
142
+ ) ;
143
+ } ;
144
+
145
+ switch ( kind ) {
146
+ case TokenKind . TAP_VERSION :
147
+ // TODO(manekinekko): handle TAP version coming from the parser.
148
+ // this.reporter.version(node.version);
149
+ break ;
150
+
151
+ case TokenKind . TAP_PLAN :
152
+ this . reporter . plan ( indent , node . end - node . start + 1 ) ;
153
+ break ;
154
+
155
+ case TokenKind . TAP_SUBTEST_POINT :
156
+ this . reporter . subtest ( indent , node . name ) ;
157
+ break ;
158
+
159
+ case TokenKind . TAP_TEST_POINT :
160
+ // eslint-disable-next-line no-case-declarations
161
+ const { todo, skip, pass } = node . status ;
162
+ // eslint-disable-next-line no-case-declarations
163
+ let directive ;
164
+
165
+ if ( skip ) {
166
+ directive = this . reporter . getSkip ( node . reason ) ;
167
+ } else if ( todo ) {
168
+ directive = this . reporter . getTodo ( node . reason ) ;
169
+ } else {
170
+ directive = kEmptyObject ;
171
+ }
172
+
173
+ if ( pass ) {
174
+ this . reporter . ok (
175
+ indent ,
176
+ node . id ,
177
+ node . description ,
178
+ details ( node . diagnostics ) ,
179
+ directive
180
+ ) ;
181
+ } else {
182
+ this . reporter . fail (
183
+ indent ,
184
+ node . id ,
185
+ node . description ,
186
+ details ( node . diagnostics ) ,
187
+ directive
188
+ ) ;
189
+ }
190
+ break ;
191
+
192
+ case TokenKind . COMMENT :
193
+ if ( indent === kDefaultIndent ) {
194
+ // Ignore file top level diagnostics
195
+ break ;
196
+ }
197
+ this . reporter . diagnostic ( indent , node . comment ) ;
198
+ break ;
199
+
200
+ case TokenKind . UNKNOWN :
201
+ this . reporter . diagnostic ( indent , node . value ) ;
202
+ break ;
203
+ }
204
+ }
205
+ addToReport ( ast ) {
206
+ if ( ! this . isClearToSend ( ) ) {
207
+ ArrayPrototypePush ( this . #buffer, ast ) ;
208
+ return ;
209
+ }
210
+ this . reportSubtest ( ) ;
211
+ this . #handleReportItem( ast ) ;
212
+ }
213
+ report ( ) {
214
+ this . reportSubtest ( ) ;
215
+ ArrayPrototypeForEach ( this . #buffer, ( ast ) => this . #handleReportItem( ast ) ) ;
216
+ super . report ( ) ;
217
+ }
218
+ }
219
+
123
220
const runningProcesses = new SafeMap ( ) ;
124
221
const runningSubtests = new SafeMap ( ) ;
125
222
126
223
function runTestFile ( path , root , inspectPort , filesWatcher ) {
127
- const subtest = root . createSubtest ( Test , path , async ( t ) => {
224
+ const subtest = root . createSubtest ( FileTest , path , async ( t ) => {
128
225
const args = getRunArgs ( { path, inspectPort } ) ;
129
226
const stdio = [ 'pipe' , 'pipe' , 'pipe' ] ;
130
227
const env = { ...process . env } ;
@@ -135,8 +232,7 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
135
232
136
233
const child = spawn ( process . execPath , args , { signal : t . signal , encoding : 'utf8' , env, stdio } ) ;
137
234
runningProcesses . set ( path , child ) ;
138
- // TODO(cjihrig): Implement a TAP parser to read the child's stdout
139
- // instead of just displaying it all if the child fails.
235
+
140
236
let err ;
141
237
let stderr = '' ;
142
238
@@ -159,6 +255,17 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
159
255
} ) ;
160
256
}
161
257
258
+ const parser = new TapParser ( ) ;
259
+ child . stderr . pipe ( parser ) . on ( 'data' , ( ast ) => {
260
+ if ( ast . lexeme && isInspectorMessage ( ast . lexeme ) ) {
261
+ process . stderr . write ( ast . lexeme + '\n' ) ;
262
+ }
263
+ } ) ;
264
+
265
+ child . stdout . pipe ( parser ) . on ( 'data' , ( ast ) => {
266
+ subtest . addToReport ( ast ) ;
267
+ } ) ;
268
+
162
269
const { 0 : { 0 : code , 1 : signal } , 1 : stdout } = await SafePromiseAll ( [
163
270
once ( child , 'exit' , { signal : t . signal } ) ,
164
271
child . stdout . toArray ( { signal : t . signal } ) ,
0 commit comments