22const {
33 ArrayFrom,
44 ArrayPrototypeFilter,
5+ ArrayPrototypeForEach,
56 ArrayPrototypeIncludes,
67 ArrayPrototypeJoin,
78 ArrayPrototypePush,
@@ -14,6 +15,7 @@ const {
1415 SafePromiseAllSettledReturnVoid,
1516 SafeMap,
1617 SafeSet,
18+ StringPrototypeRepeat,
1719} = primordials ;
1820
1921const { spawn } = require ( 'child_process' ) ;
@@ -31,7 +33,10 @@ const { validateArray, validateBoolean } = require('internal/validators');
3133const { getInspectPort, isUsingInspector, isInspectorMessage } = require ( 'internal/util/inspector' ) ;
3234const { kEmptyObject } = require ( 'internal/util' ) ;
3335const { 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+
3540const {
3641 isSupportedFileType,
3742 doesPathMatchFilter,
@@ -119,11 +124,103 @@ function getRunArgs({ path, inspectPort }) {
119124 return argv ;
120125}
121126
127+ class FileTest extends Test {
128+ #buffer = [ ] ;
129+ #handleReportItem( { kind, node, nesting = 0 } ) {
130+ const indent = StringPrototypeRepeat ( kDefaultIndent , nesting + 1 ) ;
131+
132+ const details = ( diagnostic ) => {
133+ return (
134+ diagnostic && {
135+ __proto__ : null ,
136+ yaml :
137+ `${ indent } ` +
138+ ArrayPrototypeJoin ( diagnostic , `\n${ indent } ` ) +
139+ '\n' ,
140+ }
141+ ) ;
142+ } ;
143+
144+ switch ( kind ) {
145+ case TokenKind . TAP_VERSION :
146+ // TODO(manekinekko): handle TAP version coming from the parser.
147+ // this.reporter.version(node.version);
148+ break ;
149+
150+ case TokenKind . TAP_PLAN :
151+ this . reporter . plan ( indent , node . end - node . start + 1 ) ;
152+ break ;
153+
154+ case TokenKind . TAP_SUBTEST_POINT :
155+ this . reporter . subtest ( indent , node . name ) ;
156+ break ;
157+
158+ case TokenKind . TAP_TEST_POINT :
159+ // eslint-disable-next-line no-case-declarations
160+ const { todo, skip, pass } = node . status ;
161+ // eslint-disable-next-line no-case-declarations
162+ let directive ;
163+
164+ if ( skip ) {
165+ directive = this . reporter . getSkip ( node . reason ) ;
166+ } else if ( todo ) {
167+ directive = this . reporter . getTodo ( node . reason ) ;
168+ } else {
169+ directive = kEmptyObject ;
170+ }
171+
172+ if ( pass ) {
173+ this . reporter . ok (
174+ indent ,
175+ node . id ,
176+ node . description ,
177+ details ( node . diagnostics ) ,
178+ directive
179+ ) ;
180+ } else {
181+ this . reporter . fail (
182+ indent ,
183+ node . id ,
184+ node . description ,
185+ details ( node . diagnostics ) ,
186+ directive
187+ ) ;
188+ }
189+ break ;
190+
191+ case TokenKind . COMMENT :
192+ if ( indent === kDefaultIndent ) {
193+ // Ignore file top level diagnostics
194+ break ;
195+ }
196+ this . reporter . diagnostic ( indent , node . comment ) ;
197+ break ;
198+
199+ case TokenKind . UNKNOWN :
200+ this . reporter . diagnostic ( indent , node . value ) ;
201+ break ;
202+ }
203+ }
204+ addToReport ( ast ) {
205+ if ( ! this . isClearToSend ( ) ) {
206+ ArrayPrototypePush ( this . #buffer, ast ) ;
207+ return ;
208+ }
209+ this . reportSubtest ( ) ;
210+ this . #handleReportItem( ast ) ;
211+ }
212+ report ( ) {
213+ this . reportSubtest ( ) ;
214+ ArrayPrototypeForEach ( this . #buffer, ( ast ) => this . #handleReportItem( ast ) ) ;
215+ super . report ( ) ;
216+ }
217+ }
218+
122219const runningProcesses = new SafeMap ( ) ;
123220const runningSubtests = new SafeMap ( ) ;
124221
125222function runTestFile ( path , root , inspectPort , filesWatcher ) {
126- const subtest = root . createSubtest ( Test , path , async ( t ) => {
223+ const subtest = root . createSubtest ( FileTest , path , async ( t ) => {
127224 const args = getRunArgs ( { path, inspectPort } ) ;
128225 const stdio = [ 'pipe' , 'pipe' , 'pipe' ] ;
129226 const env = { ...process . env } ;
@@ -134,8 +231,7 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
134231
135232 const child = spawn ( process . execPath , args , { signal : t . signal , encoding : 'utf8' , env, stdio } ) ;
136233 runningProcesses . set ( path , child ) ;
137- // TODO(cjihrig): Implement a TAP parser to read the child's stdout
138- // instead of just displaying it all if the child fails.
234+
139235 let err ;
140236 let stderr = '' ;
141237
@@ -158,6 +254,17 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
158254 } ) ;
159255 }
160256
257+ const parser = new TapParser ( ) ;
258+ child . stderr . pipe ( parser ) . on ( 'data' , ( ast ) => {
259+ if ( ast . lexeme && isInspectorMessage ( ast . lexeme ) ) {
260+ process . stderr . write ( ast . lexeme + '\n' ) ;
261+ }
262+ } ) ;
263+
264+ child . stdout . pipe ( parser ) . on ( 'data' , ( ast ) => {
265+ subtest . addToReport ( ast ) ;
266+ } ) ;
267+
161268 const { 0 : { 0 : code , 1 : signal } , 1 : stdout } = await SafePromiseAll ( [
162269 once ( child , 'exit' , { signal : t . signal } ) ,
163270 child . stdout . toArray ( { signal : t . signal } ) ,
0 commit comments