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,
@@ -120,11 +125,103 @@ function getRunArgs({ path, inspectPort }) {
120125 return argv ;
121126}
122127
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+
123220const runningProcesses = new SafeMap ( ) ;
124221const runningSubtests = new SafeMap ( ) ;
125222
126223function runTestFile ( path , root , inspectPort , filesWatcher ) {
127- const subtest = root . createSubtest ( Test , path , async ( t ) => {
224+ const subtest = root . createSubtest ( FileTest , path , async ( t ) => {
128225 const args = getRunArgs ( { path, inspectPort } ) ;
129226 const stdio = [ 'pipe' , 'pipe' , 'pipe' ] ;
130227 const env = { ...process . env } ;
@@ -135,8 +232,7 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
135232
136233 const child = spawn ( process . execPath , args , { signal : t . signal , encoding : 'utf8' , env, stdio } ) ;
137234 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+
140236 let err ;
141237 let stderr = '' ;
142238
@@ -159,6 +255,17 @@ function runTestFile(path, root, inspectPort, filesWatcher) {
159255 } ) ;
160256 }
161257
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+
162269 const { 0 : { 0 : code , 1 : signal } , 1 : stdout } = await SafePromiseAll ( [
163270 once ( child , 'exit' , { signal : t . signal } ) ,
164271 child . stdout . toArray ( { signal : t . signal } ) ,
0 commit comments