22
33// Testcase to check reporting of uv handles.
44const common = require ( '../common' ) ;
5+ const tmpdir = require ( '../common/tmpdir' ) ;
6+ const path = require ( 'path' ) ;
57if ( common . isIBMi )
68 common . skip ( 'IBMi does not support fs.watch()' ) ;
79
8- if ( process . argv [ 2 ] === 'child' ) {
9- // Exit on loss of parent process
10- const exit = ( ) => process . exit ( 2 ) ;
11- process . on ( 'disconnect' , exit ) ;
10+ // This is quite similar to common.PIPE except that it uses an extended prefix
11+ // of "\\?\pipe" on windows.
12+ const PIPE = ( ( ) => {
13+ const localRelative = path . relative ( process . cwd ( ) , `${ tmpdir . path } /` ) ;
14+ const pipePrefix = common . isWindows ? '\\\\?\\pipe\\' : localRelative ;
15+ const pipeName = `node-test.${ process . pid } .sock` ;
16+ return path . join ( pipePrefix , pipeName ) ;
17+ } ) ( ) ;
1218
19+ function createFsHandle ( childData ) {
1320 const fs = require ( 'fs' ) ;
14- const http = require ( 'http' ) ;
15- const spawn = require ( 'child_process' ) . spawn ;
16-
1721 // Watching files should result in fs_event/fs_poll uv handles.
1822 let watcher ;
1923 try {
@@ -22,59 +26,129 @@ if (process.argv[2] === 'child') {
2226 // fs.watch() unavailable
2327 }
2428 fs . watchFile ( __filename , ( ) => { } ) ;
29+ childData . skip_fs_watch = watcher === undefined ;
30+
31+ return ( ) => {
32+ if ( watcher ) watcher . close ( ) ;
33+ fs . unwatchFile ( __filename ) ;
34+ } ;
35+ }
2536
37+ function createChildProcessHandle ( childData ) {
38+ const spawn = require ( 'child_process' ) . spawn ;
2639 // Child should exist when this returns as child_process.pid must be set.
27- const child_process = spawn ( process . execPath ,
28- [ '-e' , "process.stdin.on('data', (x) => " +
29- 'console.log(x.toString()));' ] ) ;
40+ const cp = spawn ( process . execPath ,
41+ [ '-e' , "process.stdin.on('data', (x) => " +
42+ 'console.log(x.toString()));' ] ) ;
43+ childData . pid = cp . pid ;
44+
45+ return ( ) => {
46+ cp . kill ( ) ;
47+ } ;
48+ }
3049
50+ function createTimerHandle ( ) {
3151 const timeout = setInterval ( ( ) => { } , 1000 ) ;
3252 // Make sure the timer doesn't keep the test alive and let
3353 // us check we detect unref'd handles correctly.
3454 timeout . unref ( ) ;
55+ return ( ) => {
56+ clearInterval ( timeout ) ;
57+ } ;
58+ }
59+
60+ function createTcpHandle ( childData ) {
61+ const http = require ( 'http' ) ;
3562
63+ return new Promise ( ( resolve ) => {
64+ // Simple server/connection to create tcp uv handles.
65+ const server = http . createServer ( ( req , res ) => {
66+ req . on ( 'end' , ( ) => {
67+ resolve ( ( ) => {
68+ res . writeHead ( 200 , { 'Content-Type' : 'text/plain' } ) ;
69+ res . end ( ) ;
70+ server . close ( ) ;
71+ } ) ;
72+ } ) ;
73+ req . resume ( ) ;
74+ } ) ;
75+ server . listen ( ( ) => {
76+ childData . tcp_address = server . address ( ) ;
77+ http . get ( { port : server . address ( ) . port } ) ;
78+ } ) ;
79+ } ) ;
80+ }
81+
82+ function createUdpHandle ( childData ) {
3683 // Datagram socket for udp uv handles.
3784 const dgram = require ( 'dgram' ) ;
38- const udp_socket = dgram . createSocket ( 'udp4' ) ;
39- const connected_udp_socket = dgram . createSocket ( 'udp4' ) ;
40- udp_socket . bind ( { } , common . mustCall ( ( ) => {
41- connected_udp_socket . connect ( udp_socket . address ( ) . port ) ;
42- } ) ) ;
85+ const udpSocket = dgram . createSocket ( 'udp4' ) ;
86+ const connectedUdpSocket = dgram . createSocket ( 'udp4' ) ;
87+
88+ return new Promise ( ( resolve ) => {
89+ udpSocket . bind ( { } , common . mustCall ( ( ) => {
90+ connectedUdpSocket . connect ( udpSocket . address ( ) . port ) ;
4391
44- // Simple server/connection to create tcp uv handles.
45- const server = http . createServer ( ( req , res ) => {
46- req . on ( 'end' , ( ) => {
47- // Generate the report while the connection is active.
48- console . log ( JSON . stringify ( process . report . getReport ( ) , null , 2 ) ) ;
49- child_process . kill ( ) ;
50-
51- res . writeHead ( 200 , { 'Content-Type' : 'text/plain' } ) ;
52- res . end ( ) ;
53-
54- // Tidy up to allow process to exit cleanly.
55- server . close ( ( ) => {
56- if ( watcher ) watcher . close ( ) ;
57- fs . unwatchFile ( __filename ) ;
58- connected_udp_socket . close ( ) ;
59- udp_socket . close ( ) ;
60- process . removeListener ( 'disconnect' , exit ) ;
92+ childData . udp_address = udpSocket . address ( ) ;
93+ resolve ( ( ) => {
94+ connectedUdpSocket . close ( ) ;
95+ udpSocket . close ( ) ;
96+ } ) ;
97+ } ) ) ;
98+ } ) ;
99+ }
100+
101+ function createNamedPipeHandle ( childData ) {
102+ const net = require ( 'net' ) ;
103+ const sockPath = PIPE ;
104+ return new Promise ( ( resolve ) => {
105+ const server = net . createServer ( ( socket ) => {
106+ childData . pipe_sock_path = server . address ( ) ;
107+ resolve ( ( ) => {
108+ socket . end ( ) ;
109+ server . close ( ) ;
61110 } ) ;
62111 } ) ;
63- req . resume ( ) ;
112+ server . listen (
113+ sockPath ,
114+ ( ) => {
115+ net . connect ( sockPath , ( socket ) => { } ) ;
116+ } ) ;
64117 } ) ;
65- server . listen ( ( ) => {
66- const data = { pid : child_process . pid ,
67- tcp_address : server . address ( ) ,
68- udp_address : udp_socket . address ( ) ,
69- skip_fs_watch : ( watcher === undefined ) } ;
70- process . send ( data ) ;
71- http . get ( { port : server . address ( ) . port } ) ;
118+ }
119+
120+ async function child ( ) {
121+ // Exit on loss of parent process
122+ const exit = ( ) => process . exit ( 2 ) ;
123+ process . on ( 'disconnect' , exit ) ;
124+
125+ const childData = { } ;
126+ const disposes = await Promise . all ( [
127+ createFsHandle ( childData ) ,
128+ createChildProcessHandle ( childData ) ,
129+ createTimerHandle ( childData ) ,
130+ createTcpHandle ( childData ) ,
131+ createUdpHandle ( childData ) ,
132+ createNamedPipeHandle ( childData ) ,
133+ ] ) ;
134+ process . send ( childData ) ;
135+
136+ // Generate the report while the connection is active.
137+ console . log ( JSON . stringify ( process . report . getReport ( ) , null , 2 ) ) ;
138+
139+ // Tidy up to allow process to exit cleanly.
140+ disposes . forEach ( ( it ) => {
141+ it ( ) ;
72142 } ) ;
143+ process . removeListener ( 'disconnect' , exit ) ;
144+ }
145+
146+ if ( process . argv [ 2 ] === 'child' ) {
147+ child ( ) ;
73148} else {
74149 const helper = require ( '../common/report.js' ) ;
75150 const fork = require ( 'child_process' ) . fork ;
76151 const assert = require ( 'assert' ) ;
77- const tmpdir = require ( '../common/tmpdir' ) ;
78152 tmpdir . refresh ( ) ;
79153 const options = { encoding : 'utf8' , silent : true , cwd : tmpdir . path } ;
80154 const child = fork ( __filename , [ 'child' ] , options ) ;
@@ -86,11 +160,11 @@ if (process.argv[2] === 'child') {
86160 const report_msg = 'Report files were written: unexpectedly' ;
87161 child . stdout . on ( 'data' , ( chunk ) => { stdout += chunk ; } ) ;
88162 child . on ( 'exit' , common . mustCall ( ( code , signal ) => {
163+ assert . strictEqual ( stderr . trim ( ) , '' ) ;
89164 assert . deepStrictEqual ( code , 0 , 'Process exited unexpectedly with code: ' +
90165 `${ code } ` ) ;
91166 assert . deepStrictEqual ( signal , null , 'Process should have exited cleanly,' +
92167 ` but did not: ${ signal } ` ) ;
93- assert . strictEqual ( stderr . trim ( ) , '' ) ;
94168
95169 const reports = helper . findReports ( child . pid , tmpdir . path ) ;
96170 assert . deepStrictEqual ( reports , [ ] , report_msg , reports ) ;
@@ -116,6 +190,7 @@ if (process.argv[2] === 'child') {
116190 const expected_filename = `${ prefix } ${ __filename } ` ;
117191 const found_tcp = [ ] ;
118192 const found_udp = [ ] ;
193+ const found_named_pipe = [ ] ;
119194 // Functions are named to aid debugging when they are not called.
120195 const validators = {
121196 fs_event : common . mustCall ( function fs_event_validator ( handle ) {
@@ -133,6 +208,21 @@ if (process.argv[2] === 'child') {
133208 } ) ,
134209 pipe : common . mustCallAtLeast ( function pipe_validator ( handle ) {
135210 assert ( handle . is_referenced ) ;
211+ // Pipe handles. The report should contain three pipes:
212+ // 1. The server's listening pipe.
213+ // 2. The inbound pipe making the request.
214+ // 3. The outbound pipe sending the response.
215+ //
216+ // There is no way to distinguish inbound and outbound in a cross
217+ // platform manner, so we just check inbound here.
218+ const sockPath = child_data . pipe_sock_path ;
219+ if ( handle . localEndpoint === sockPath ) {
220+ if ( handle . writable === false ) {
221+ found_named_pipe . push ( 'listening' ) ;
222+ }
223+ } else if ( handle . remoteEndpoint === sockPath ) {
224+ found_named_pipe . push ( 'inbound' ) ;
225+ }
136226 } ) ,
137227 process : common . mustCall ( function process_validator ( handle ) {
138228 assert . strictEqual ( handle . pid , child_data . pid ) ;
@@ -172,7 +262,7 @@ if (process.argv[2] === 'child') {
172262 assert ( handle . is_referenced ) ;
173263 } , 2 ) ,
174264 } ;
175- console . log ( report . libuv ) ;
265+
176266 for ( const entry of report . libuv ) {
177267 if ( validators [ entry . type ] ) validators [ entry . type ] ( entry ) ;
178268 }
@@ -182,6 +272,9 @@ if (process.argv[2] === 'child') {
182272 for ( const socket of [ 'connected' , 'unconnected' ] ) {
183273 assert ( found_udp . includes ( socket ) , `${ socket } UDP socket was not found` ) ;
184274 }
275+ for ( const socket of [ 'listening' , 'inbound' ] ) {
276+ assert ( found_named_pipe . includes ( socket ) , `${ socket } named pipe socket was not found` ) ;
277+ }
185278
186279 // Common report tests.
187280 helper . validateContent ( stdout ) ;
0 commit comments