Skip to content

Commit 4de6d50

Browse files
committed
test(api): make getFreePort CI checks deterministic
1 parent b48cef0 commit 4de6d50

File tree

2 files changed

+102
-44
lines changed

2 files changed

+102
-44
lines changed

tests/e2e/api.test.js

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
const path = require('node:path');
2+
const net = require('node:net');
23
const { rspack } = require('@rspack/core');
34
const { RspackDevServer: Server } = require('@rspack/dev-server');
45
const config = require('../fixtures/client-config/rspack.config');
56
const runBrowser = require('../helpers/run-browser');
67
const sessionSubscribe = require('../helpers/session-subscribe');
7-
const { getRandomPorts } = require('../helpers/get-random-port');
8+
const {
9+
getRandomPorts,
10+
releaseRandomPorts,
11+
} = require('../helpers/get-random-port');
812
const port = require('../helpers/ports-map').api;
913

1014
describe('API', () => {
@@ -339,7 +343,7 @@ describe('API', () => {
339343

340344
describe('Server.getFreePort', () => {
341345
let basePort;
342-
let reservedPorts;
346+
let reservedPorts = [];
343347
let dummyServers = [];
344348
let devServerPort;
345349

@@ -367,6 +371,8 @@ describe('API', () => {
367371
)
368372
.then(() => {
369373
dummyServers = [];
374+
releaseRandomPorts(reservedPorts);
375+
reservedPorts = [];
370376
});
371377
});
372378

@@ -605,16 +611,50 @@ describe('API', () => {
605611
});
606612

607613
it("should throw the error when the port isn't found", async () => {
608-
rs.doMockRequire(
609-
'../../dist/getPort',
610-
() => () => Promise.reject(new Error('busy')),
611-
);
614+
const busyServers = [];
615+
const busyPorts = [65534, 65535];
616+
617+
try {
618+
await Promise.all(
619+
busyPorts.map(
620+
(busyPort) =>
621+
new Promise((resolve, reject) => {
622+
const server = net.createServer();
623+
624+
server.unref();
625+
server.on('error', reject);
626+
server.listen(busyPort, '0.0.0.0', () => {
627+
busyServers.push(server);
628+
resolve();
629+
});
630+
}),
631+
),
632+
);
612633

613-
process.env.RSPACK_DEV_SERVER_PORT_RETRY = 1;
634+
process.env.RSPACK_DEV_SERVER_BASE_PORT = '65534';
635+
process.env.RSPACK_DEV_SERVER_PORT_RETRY = 0;
614636

615-
const { RspackDevServer: Server } = require('@rspack/dev-server');
637+
await expect(
638+
Server.getFreePort(),
639+
).rejects.toThrowErrorMatchingSnapshot();
640+
} finally {
641+
await Promise.all(
642+
busyServers.map(
643+
(server) =>
644+
new Promise((resolve, reject) => {
645+
server.close((error) => {
646+
if (error) {
647+
reject(error);
648+
649+
return;
650+
}
616651

617-
await expect(Server.getFreePort()).rejects.toThrowErrorMatchingSnapshot();
652+
resolve();
653+
});
654+
}),
655+
),
656+
);
657+
}
618658
});
619659
});
620660

tests/helpers/get-random-port.js

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
const net = require('node:net');
22

3-
function isPortAvailable(port, host = '0.0.0.0') {
3+
const minRandomPort = 15000;
4+
const maxRandomPort = 45000;
5+
6+
async function isPortAvailable(port, host = '0.0.0.0') {
47
try {
58
const server = net.createServer();
69

710
server.unref();
811

9-
return new Promise((resolve) => {
12+
return await new Promise((resolve) => {
1013
server.on('listening', () => {
1114
server.close(() => {
1215
resolve(true);
@@ -24,33 +27,15 @@ function isPortAvailable(port, host = '0.0.0.0') {
2427

2528
const portMap = new Map();
2629

27-
/**
28-
* Get a random port.
29-
* Available port ranges: 1024 ~ 65535
30-
* `10080` is not available on macOS CI, `> 50000` gets "permission denied" on Windows,
31-
* so we use `15000` ~ `45000`.
32-
*/
33-
async function getRandomPort(
34-
defaultPort = Math.ceil(Math.random() * 30000) + 15000,
35-
host,
36-
) {
37-
let port = defaultPort;
38-
39-
while (true) {
40-
if (!portMap.get(port) && (await isPortAvailable(port, host))) {
41-
portMap.set(port, 1);
42-
43-
return port;
44-
}
45-
46-
port += 1;
47-
}
30+
function getDefaultPort() {
31+
return Math.ceil(Math.random() * 30000) + minRandomPort;
4832
}
4933

50-
async function getRandomPorts(count, host) {
51-
let port = await getRandomPort(undefined, host);
34+
async function findAvailablePort(startPort, count, host) {
35+
let port = Math.max(startPort, minRandomPort);
36+
const maxStartPort = maxRandomPort - count + 1;
5237

53-
while (true) {
38+
while (port <= maxStartPort) {
5439
let isAvailable = true;
5540

5641
for (let index = 0; index < count; index += 1) {
@@ -61,27 +46,60 @@ async function getRandomPorts(count, host) {
6146
!(await isPortAvailable(currentPort, host))
6247
) {
6348
isAvailable = false;
64-
port = currentPort + 1;
6549
break;
6650
}
6751
}
6852

6953
if (isAvailable) {
70-
const ports = [];
54+
return port;
55+
}
7156

72-
for (let index = 0; index < count; index += 1) {
73-
const currentPort = port + index;
57+
port += 1;
58+
}
7459

75-
portMap.set(currentPort, 1);
76-
ports.push(currentPort);
77-
}
60+
throw new Error('No available ports found');
61+
}
7862

79-
return ports;
80-
}
63+
/**
64+
* Get a random port.
65+
* Available port ranges: 1024 ~ 65535
66+
* `10080` is not available on macOS CI, `> 50000` gets "permission denied" on Windows,
67+
* so we use `15000` ~ `45000`.
68+
*/
69+
async function getRandomPort(defaultPort = getDefaultPort(), host) {
70+
const port = await findAvailablePort(defaultPort, 1, host);
71+
72+
portMap.set(port, 1);
73+
74+
return port;
75+
}
76+
77+
async function getRandomPorts(count, host) {
78+
if (count < 1) {
79+
throw new Error('Port count must be greater than 0');
80+
}
81+
82+
const port = await findAvailablePort(getDefaultPort(), count, host);
83+
const ports = [];
84+
85+
for (let index = 0; index < count; index += 1) {
86+
const currentPort = port + index;
87+
88+
portMap.set(currentPort, 1);
89+
ports.push(currentPort);
90+
}
91+
92+
return ports;
93+
}
94+
95+
function releaseRandomPorts(ports = []) {
96+
for (const port of ports) {
97+
portMap.delete(port);
8198
}
8299
}
83100

84101
module.exports = {
85102
getRandomPort,
86103
getRandomPorts,
104+
releaseRandomPorts,
87105
};

0 commit comments

Comments
 (0)