Skip to content

Commit b48cef0

Browse files
committed
test(api): use random ports for getFreePort e2e tests
1 parent 3c68518 commit b48cef0

File tree

2 files changed

+105
-10
lines changed

2 files changed

+105
-10
lines changed

tests/e2e/api.test.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const { RspackDevServer: Server } = require('@rspack/dev-server');
44
const config = require('../fixtures/client-config/rspack.config');
55
const runBrowser = require('../helpers/run-browser');
66
const sessionSubscribe = require('../helpers/session-subscribe');
7+
const { getRandomPorts } = require('../helpers/get-random-port');
78
const port = require('../helpers/ports-map').api;
89

910
describe('API', () => {
@@ -337,12 +338,19 @@ describe('API', () => {
337338
});
338339

339340
describe('Server.getFreePort', () => {
341+
let basePort;
342+
let reservedPorts;
340343
let dummyServers = [];
341344
let devServerPort;
342345

346+
beforeEach(async () => {
347+
reservedPorts = await getRandomPorts(8, '0.0.0.0');
348+
[basePort] = reservedPorts;
349+
});
350+
343351
afterEach(() => {
344-
process.env.RSPACK_DEV_SERVER_BASE_PORT = undefined;
345-
process.env.RSPACK_DEV_SERVER_PORT_RETRY = undefined;
352+
delete process.env.RSPACK_DEV_SERVER_BASE_PORT;
353+
delete process.env.RSPACK_DEV_SERVER_PORT_RETRY;
346354

347355
return dummyServers
348356
.reduce(
@@ -363,14 +371,14 @@ describe('API', () => {
363371
});
364372

365373
function createDummyServers(n) {
366-
process.env.RSPACK_DEV_SERVER_BASE_PORT = 60000;
374+
process.env.RSPACK_DEV_SERVER_BASE_PORT = `${basePort}`;
367375

368376
return (Array.isArray(n) ? n : [...new Array(n)]).reduce(
369377
(p, _, i) =>
370378
p.then(
371379
() =>
372380
new Promise((resolve) => {
373-
devServerPort = 60000 + i;
381+
devServerPort = basePort + i;
374382
const compiler = rspack(config);
375383
const server = new Server(
376384
{ port: devServerPort, host: '0.0.0.0' },
@@ -407,7 +415,7 @@ describe('API', () => {
407415

408416
const freePort = await Server.getFreePort(null);
409417

410-
expect(freePort).toEqual(60000 + retryCount);
418+
expect(freePort).toEqual(basePort + retryCount);
411419

412420
const { page, browser } = await runBrowser();
413421

@@ -447,7 +455,7 @@ describe('API', () => {
447455
// eslint-disable-next-line no-undefined
448456
const freePort = await Server.getFreePort(undefined);
449457

450-
expect(freePort).toEqual(60000 + retryCount);
458+
expect(freePort).toEqual(basePort + retryCount);
451459

452460
const { page, browser } = await runBrowser();
453461

@@ -486,7 +494,7 @@ describe('API', () => {
486494

487495
const freePort = await Server.getFreePort();
488496

489-
expect(freePort).toEqual(60000 + retryCount);
497+
expect(freePort).toEqual(basePort + retryCount);
490498

491499
const { page, browser } = await runBrowser();
492500

@@ -525,7 +533,7 @@ describe('API', () => {
525533

526534
const freePort = await Server.getFreePort();
527535

528-
expect(freePort).toEqual(60000 + retryCount);
536+
expect(freePort).toEqual(basePort + retryCount);
529537

530538
const { page, browser } = await runBrowser();
531539

@@ -556,15 +564,15 @@ describe('API', () => {
556564
});
557565

558566
it('should retry finding the port when serial ports are busy', async () => {
559-
const busyPorts = [60000, 60001, 60002, 60003, 60004, 60005];
567+
const busyPorts = reservedPorts.slice(0, 6);
560568

561569
process.env.RSPACK_DEV_SERVER_PORT_RETRY = 1000;
562570

563571
await createDummyServers(busyPorts);
564572

565573
const freePort = await Server.getFreePort();
566574

567-
expect(freePort).toBeGreaterThan(60005);
575+
expect(freePort).toBeGreaterThan(busyPorts[busyPorts.length - 1]);
568576

569577
const { page, browser } = await runBrowser();
570578

tests/helpers/get-random-port.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
const net = require('node:net');
2+
3+
function isPortAvailable(port, host = '0.0.0.0') {
4+
try {
5+
const server = net.createServer();
6+
7+
server.unref();
8+
9+
return new Promise((resolve) => {
10+
server.on('listening', () => {
11+
server.close(() => {
12+
resolve(true);
13+
});
14+
});
15+
server.on('error', () => {
16+
resolve(false);
17+
});
18+
server.listen(port, host);
19+
});
20+
} catch {
21+
return false;
22+
}
23+
}
24+
25+
const portMap = new Map();
26+
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+
}
48+
}
49+
50+
async function getRandomPorts(count, host) {
51+
let port = await getRandomPort(undefined, host);
52+
53+
while (true) {
54+
let isAvailable = true;
55+
56+
for (let index = 0; index < count; index += 1) {
57+
const currentPort = port + index;
58+
59+
if (
60+
portMap.get(currentPort) ||
61+
!(await isPortAvailable(currentPort, host))
62+
) {
63+
isAvailable = false;
64+
port = currentPort + 1;
65+
break;
66+
}
67+
}
68+
69+
if (isAvailable) {
70+
const ports = [];
71+
72+
for (let index = 0; index < count; index += 1) {
73+
const currentPort = port + index;
74+
75+
portMap.set(currentPort, 1);
76+
ports.push(currentPort);
77+
}
78+
79+
return ports;
80+
}
81+
}
82+
}
83+
84+
module.exports = {
85+
getRandomPort,
86+
getRandomPorts,
87+
};

0 commit comments

Comments
 (0)