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