Skip to content

Commit cff138c

Browse files
committed
tests: start adding quic test server utilities
PR-URL: #59946 Reviewed-By: Yagiz Nizipli <[email protected]>
1 parent 65a32ba commit cff138c

File tree

5 files changed

+216
-38
lines changed

5 files changed

+216
-38
lines changed

deps/ngtcp2/ngtcp2.gyp

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,18 @@
266266
'HAVE_CONFIG_H',
267267
'WITH_EXAMPLE_OSSL',
268268
'EV_STANDALONE=1',
269+
'HAVE_UNISTD_H',
270+
'HAVE_ARPA_INET_H',
271+
'HAVE_NETINET_IN_H',
272+
'HAVE_NETINET_IP_H',
269273
],
270274
'conditions': [
275+
['OS=="aix" or OS=="win"', {
276+
# AIX does not support some of the networking features used in
277+
# the test server. Windows also lacks the Unix-specific headers
278+
# and system calls required by the ngtcp2 examples.
279+
'type': 'none', # Disable as executable on AIX and Windows
280+
}],
271281
['OS=="mac"', {
272282
'defines': [
273283
'__APPLE_USE_RFC_3542',
@@ -281,25 +291,6 @@
281291
'libraries': [ '-lsocket', '-lnsl' ],
282292
},
283293
}],
284-
['OS=="win"', {
285-
'defines': [
286-
'WIN32',
287-
'_WINDOWS',
288-
],
289-
'msvs_settings': {
290-
'VCCLCompilerTool': {
291-
'CompileAs': '1'
292-
},
293-
},
294-
}],
295-
['OS!="win"', {
296-
'defines': [
297-
'HAVE_UNISTD_H',
298-
'HAVE_ARPA_INET_H',
299-
'HAVE_NETINET_IN_H',
300-
'HAVE_NETINET_IP_H',
301-
],
302-
}],
303294
[ 'OS=="linux" or OS=="openharmony"', {
304295
'link_settings': {
305296
'libraries': [ '-ldl', '-lrt' ],
@@ -333,8 +324,18 @@
333324
'HAVE_CONFIG_H',
334325
'WITH_EXAMPLE_OSSL',
335326
'EV_STANDALONE=1',
327+
'HAVE_UNISTD_H',
328+
'HAVE_ARPA_INET_H',
329+
'HAVE_NETINET_IN_H',
330+
'HAVE_NETINET_IP_H',
336331
],
337332
'conditions': [
333+
['OS=="aix" or OS=="win"', {
334+
# AIX does not support some of the networking features used in
335+
# the test client. Windows also lacks the Unix-specific headers
336+
# and system calls required by the ngtcp2 examples.
337+
'type': 'none', # Disable as executable on AIX and Windows
338+
}],
338339
['OS=="mac"', {
339340
'defines': [
340341
'__APPLE_USE_RFC_3542',
@@ -348,25 +349,6 @@
348349
'libraries': [ '-lsocket', '-lnsl' ],
349350
},
350351
}],
351-
['OS=="win"', {
352-
'defines': [
353-
'WIN32',
354-
'_WINDOWS',
355-
],
356-
'msvs_settings': {
357-
'VCCLCompilerTool': {
358-
'CompileAs': '1'
359-
},
360-
},
361-
}],
362-
['OS!="win"', {
363-
'defines': [
364-
'HAVE_UNISTD_H',
365-
'HAVE_ARPA_INET_H',
366-
'HAVE_NETINET_IN_H',
367-
'HAVE_NETINET_IP_H',
368-
],
369-
}],
370352
[ 'OS=="linux" or OS=="openharmony"', {
371353
'link_settings': {
372354
'libraries': [ '-ldl', '-lrt' ],

test/common/quic/test-client.mjs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { resolve } from 'node:path';
2+
import { spawn } from 'node:child_process';
3+
4+
export default class QuicTestClient {
5+
#pathToClient;
6+
#runningProcess;
7+
8+
constructor() {
9+
this.#pathToClient = resolve(process.execPath, '../ngtcp2_test_client');
10+
console.log(this.#pathToClient);
11+
}
12+
13+
help(options = { stdio: 'inherit' }) {
14+
const { promise, resolve, reject } = Promise.withResolvers();
15+
const proc = spawn(this.#pathToClient, ['--help'], options);
16+
proc.on('error', reject);
17+
proc.on('exit', (code, signal) => {
18+
if (code === 0) {
19+
resolve();
20+
} else {
21+
reject(new Error(`Process exited with code ${code} and signal ${signal}`));
22+
}
23+
});
24+
return promise;
25+
}
26+
27+
run(address, port, uri, options = { stdio: 'inherit' }) {
28+
const { promise, resolve, reject } = Promise.withResolvers();
29+
if (this.#runningProcess) {
30+
reject(new Error('Server is already running'));
31+
return promise;
32+
}
33+
const args = [
34+
address,
35+
port,
36+
uri ?? '',
37+
];
38+
this.#runningProcess = spawn(this.#pathToClient, args, options);
39+
this.#runningProcess.on('error', (err) => {
40+
this.#runningProcess = undefined;
41+
reject(err);
42+
});
43+
this.#runningProcess.on('exit', (code, signal) => {
44+
if (code === 0) {
45+
resolve();
46+
} else {
47+
if (code === null && signal === 'SIGTERM') {
48+
// Normal termination due to stop() being called.
49+
resolve();
50+
return;
51+
}
52+
reject(new Error(`Process exited with code ${code} and signal ${signal}`));
53+
}
54+
});
55+
return promise;
56+
}
57+
58+
stop() {
59+
if (this.#runningProcess) {
60+
this.#runningProcess.kill();
61+
this.#runningProcess = undefined;
62+
}
63+
}
64+
};

test/common/quic/test-server.mjs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { resolve } from 'node:path';
2+
import { spawn } from 'node:child_process';
3+
4+
export default class QuicTestServer {
5+
#pathToServer;
6+
#runningProcess;
7+
8+
constructor() {
9+
this.#pathToServer = resolve(process.execPath, '../ngtcp2_test_server');
10+
console.log(this.#pathToServer);
11+
}
12+
13+
help(options = { stdio: 'inherit' }) {
14+
const { promise, resolve, reject } = Promise.withResolvers();
15+
const proc = spawn(this.#pathToServer, ['--help'], options);
16+
proc.on('error', reject);
17+
proc.on('exit', (code, signal) => {
18+
if (code === 0) {
19+
resolve();
20+
} else {
21+
reject(new Error(`Process exited with code ${code} and signal ${signal}`));
22+
}
23+
});
24+
return promise;
25+
}
26+
27+
run(address, port, keyFile, certFile, options = { stdio: 'inherit' }) {
28+
const { promise, resolve, reject } = Promise.withResolvers();
29+
if (this.#runningProcess) {
30+
reject(new Error('Server is already running'));
31+
return promise;
32+
}
33+
const args = [
34+
address,
35+
port,
36+
keyFile,
37+
certFile,
38+
];
39+
this.#runningProcess = spawn(this.#pathToServer, args, options);
40+
this.#runningProcess.on('error', (err) => {
41+
this.#runningProcess = undefined;
42+
reject(err);
43+
});
44+
this.#runningProcess.on('exit', (code, signal) => {
45+
this.#runningProcess = undefined;
46+
if (code === 0) {
47+
resolve();
48+
} else {
49+
if (code === null && signal === 'SIGTERM') {
50+
// Normal termination due to stop() being called.
51+
resolve();
52+
return;
53+
}
54+
reject(new Error(`Process exited with code ${code} and signal ${signal}`));
55+
}
56+
});
57+
return promise;
58+
}
59+
60+
stop() {
61+
if (this.#runningProcess) {
62+
this.#runningProcess.kill();
63+
this.#runningProcess = undefined;
64+
}
65+
}
66+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Flags: --experimental-quic
2+
import { hasQuic, isAIX, isWindows, skip } from '../common/index.mjs';
3+
import { rejects } from 'node:assert';
4+
5+
if (!hasQuic) {
6+
skip('QUIC support is not enabled');
7+
}
8+
if (isAIX) {
9+
// AIX does not support some of the networking features used in the ngtcp2
10+
// example server and client.
11+
skip('QUIC third-party tests are disabled on AIX');
12+
}
13+
if (isWindows) {
14+
// Windows does not support the [Li/U]nix specific headers and system calls
15+
// required by the ngtcp2 example server/client.
16+
skip('QUIC third-party tests are disabled on Windows');
17+
}
18+
19+
const { default: QuicTestClient } = await import('../common/quic/test-client.mjs');
20+
21+
const client = new QuicTestClient();
22+
23+
// If this completes without throwing, the test passes.
24+
await client.help({ stdio: 'ignore' });
25+
26+
setTimeout(() => {
27+
client.stop();
28+
}, 100);
29+
30+
// We expect this to fail since there's no server running.
31+
await rejects(client.run('localhost', '12345', undefined, { stdio: 'ignore' }),
32+
{ message: /Process exited with code 1 and signal null/ });
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Flags: --experimental-quic
2+
import { hasQuic, isAIX, isWindows, skip } from '../common/index.mjs';
3+
4+
if (!hasQuic) {
5+
skip('QUIC support is not enabled');
6+
}
7+
if (isAIX) {
8+
// AIX does not support some of the networking features used in the ngtcp2
9+
// example server and client.
10+
skip('QUIC third-party tests are disabled on AIX');
11+
}
12+
if (isWindows) {
13+
// Windows does not support the [Li/U]nix specific headers and system calls
14+
// required by the ngtcp2 example server/client.
15+
skip('QUIC third-party tests are disabled on Windows');
16+
}
17+
18+
const { default: QuicTestServer } = await import('../common/quic/test-server.mjs');
19+
const fixtures = await import('../common/fixtures.mjs');
20+
21+
const server = new QuicTestServer();
22+
const fixturesPath = fixtures.path();
23+
24+
// If this completes without throwing, the test passes.
25+
await server.help({ stdio: 'ignore' });
26+
27+
setTimeout(() => {
28+
server.stop();
29+
}, 100);
30+
31+
await server.run('localhost', '12345',
32+
`${fixturesPath}/keys/agent1-key.pem`,
33+
`${fixturesPath}/keys/agent1-cert.pem`,
34+
{ stdio: 'inherit' });

0 commit comments

Comments
 (0)