2
2
3
3
// Testcase to check reporting of uv handles.
4
4
const common = require ( '../common' ) ;
5
+ const tmpdir = require ( '../common/tmpdir' ) ;
6
+ const path = require ( 'path' ) ;
5
7
if ( common . isIBMi )
6
8
common . skip ( 'IBMi does not support fs.watch()' ) ;
7
9
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
+ } ) ( ) ;
12
18
19
+ function createFsHandle ( childData ) {
13
20
const fs = require ( 'fs' ) ;
14
- const http = require ( 'http' ) ;
15
- const spawn = require ( 'child_process' ) . spawn ;
16
-
17
21
// Watching files should result in fs_event/fs_poll uv handles.
18
22
let watcher ;
19
23
try {
@@ -22,59 +26,129 @@ if (process.argv[2] === 'child') {
22
26
// fs.watch() unavailable
23
27
}
24
28
fs . watchFile ( __filename , ( ) => { } ) ;
29
+ childData . skip_fs_watch = watcher === undefined ;
30
+
31
+ return ( ) => {
32
+ if ( watcher ) watcher . close ( ) ;
33
+ fs . unwatchFile ( __filename ) ;
34
+ } ;
35
+ }
25
36
37
+ function createChildProcessHandle ( childData ) {
38
+ const spawn = require ( 'child_process' ) . spawn ;
26
39
// 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
+ }
30
49
50
+ function createTimerHandle ( ) {
31
51
const timeout = setInterval ( ( ) => { } , 1000 ) ;
32
52
// Make sure the timer doesn't keep the test alive and let
33
53
// us check we detect unref'd handles correctly.
34
54
timeout . unref ( ) ;
55
+ return ( ) => {
56
+ clearInterval ( timeout ) ;
57
+ } ;
58
+ }
59
+
60
+ function createTcpHandle ( childData ) {
61
+ const http = require ( 'http' ) ;
35
62
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 ) {
36
83
// Datagram socket for udp uv handles.
37
84
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 ) ;
43
91
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 ( ) ;
61
110
} ) ;
62
111
} ) ;
63
- req . resume ( ) ;
112
+ server . listen (
113
+ sockPath ,
114
+ ( ) => {
115
+ net . connect ( sockPath , ( socket ) => { } ) ;
116
+ } ) ;
64
117
} ) ;
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 ( ) ;
72
142
} ) ;
143
+ process . removeListener ( 'disconnect' , exit ) ;
144
+ }
145
+
146
+ if ( process . argv [ 2 ] === 'child' ) {
147
+ child ( ) ;
73
148
} else {
74
149
const helper = require ( '../common/report.js' ) ;
75
150
const fork = require ( 'child_process' ) . fork ;
76
151
const assert = require ( 'assert' ) ;
77
- const tmpdir = require ( '../common/tmpdir' ) ;
78
152
tmpdir . refresh ( ) ;
79
153
const options = { encoding : 'utf8' , silent : true , cwd : tmpdir . path } ;
80
154
const child = fork ( __filename , [ 'child' ] , options ) ;
@@ -86,11 +160,11 @@ if (process.argv[2] === 'child') {
86
160
const report_msg = 'Report files were written: unexpectedly' ;
87
161
child . stdout . on ( 'data' , ( chunk ) => { stdout += chunk ; } ) ;
88
162
child . on ( 'exit' , common . mustCall ( ( code , signal ) => {
163
+ assert . strictEqual ( stderr . trim ( ) , '' ) ;
89
164
assert . deepStrictEqual ( code , 0 , 'Process exited unexpectedly with code: ' +
90
165
`${ code } ` ) ;
91
166
assert . deepStrictEqual ( signal , null , 'Process should have exited cleanly,' +
92
167
` but did not: ${ signal } ` ) ;
93
- assert . strictEqual ( stderr . trim ( ) , '' ) ;
94
168
95
169
const reports = helper . findReports ( child . pid , tmpdir . path ) ;
96
170
assert . deepStrictEqual ( reports , [ ] , report_msg , reports ) ;
@@ -116,6 +190,7 @@ if (process.argv[2] === 'child') {
116
190
const expected_filename = `${ prefix } ${ __filename } ` ;
117
191
const found_tcp = [ ] ;
118
192
const found_udp = [ ] ;
193
+ const found_named_pipe = [ ] ;
119
194
// Functions are named to aid debugging when they are not called.
120
195
const validators = {
121
196
fs_event : common . mustCall ( function fs_event_validator ( handle ) {
@@ -133,6 +208,21 @@ if (process.argv[2] === 'child') {
133
208
} ) ,
134
209
pipe : common . mustCallAtLeast ( function pipe_validator ( handle ) {
135
210
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
+ }
136
226
} ) ,
137
227
process : common . mustCall ( function process_validator ( handle ) {
138
228
assert . strictEqual ( handle . pid , child_data . pid ) ;
@@ -172,7 +262,7 @@ if (process.argv[2] === 'child') {
172
262
assert ( handle . is_referenced ) ;
173
263
} , 2 ) ,
174
264
} ;
175
- console . log ( report . libuv ) ;
265
+
176
266
for ( const entry of report . libuv ) {
177
267
if ( validators [ entry . type ] ) validators [ entry . type ] ( entry ) ;
178
268
}
@@ -182,6 +272,9 @@ if (process.argv[2] === 'child') {
182
272
for ( const socket of [ 'connected' , 'unconnected' ] ) {
183
273
assert ( found_udp . includes ( socket ) , `${ socket } UDP socket was not found` ) ;
184
274
}
275
+ for ( const socket of [ 'listening' , 'inbound' ] ) {
276
+ assert ( found_named_pipe . includes ( socket ) , `${ socket } named pipe socket was not found` ) ;
277
+ }
185
278
186
279
// Common report tests.
187
280
helper . validateContent ( stdout ) ;
0 commit comments