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