@@ -15,6 +15,53 @@ import { FakeModuleLoader } from './fake-module-loader';
1515import { IExternalModuleLoader } from './interfaces/IExternalModule' ;
1616
1717const APPIUM_VENDOR_PREFIX = 'appium:' ;
18+
19+ // Port manager to track allocated ports
20+ class PortManager {
21+ private allocatedPorts : Set < number > = new Set ( ) ;
22+
23+ /**
24+ * Check if a port is already allocated
25+ */
26+ isPortAllocated ( port : number ) : boolean {
27+ return this . allocatedPorts . has ( port ) ;
28+ }
29+
30+ /**
31+ * Allocate a port by adding it to the allocated set
32+ */
33+ allocatePort ( port : number ) : void {
34+ if ( port && port > 0 ) {
35+ this . allocatedPorts . add ( port ) ;
36+ log . debug ( `Port ${ port } has been allocated` ) ;
37+ }
38+ }
39+
40+ /**
41+ * Release a port by removing it from the allocated set
42+ */
43+ releasePort ( port : number ) : void {
44+ if ( port && port > 0 && this . allocatedPorts . has ( port ) ) {
45+ this . allocatedPorts . delete ( port ) ;
46+ log . debug ( `Port ${ port } has been released` ) ;
47+ }
48+ }
49+
50+ /**
51+ * Release multiple ports
52+ */
53+ releasePorts ( ports : ( number | undefined | null ) [ ] ) : void {
54+ ports . forEach ( ( port ) => {
55+ if ( port && port > 0 ) {
56+ this . releasePort ( port ) ;
57+ }
58+ } ) ;
59+ }
60+ }
61+
62+ // Singleton instance of port manager
63+ const portManager = new PortManager ( ) ;
64+
1865export async function asyncForEach (
1966 array : string | any [ ] ,
2067 callback : {
@@ -69,16 +116,65 @@ export function checkIfPathIsAbsolute(configPath: string) {
69116 return path . isAbsolute ( configPath ) ;
70117}
71118
72- export async function getFreePort ( portRange ?: string ) {
119+ export async function getFreePort ( portRange ?: string ) : Promise < number > {
120+ let port : number ;
121+
73122 if ( portRange ) {
74123 const range = portRange . split ( '-' ) . map ( Number ) ;
75124 if ( range . length !== 2 || isNaN ( range [ 0 ] ) || isNaN ( range [ 1 ] ) || range [ 0 ] > range [ 1 ] ) {
76125 log . warn ( `Invalid port range format: "${ portRange } ". Falling back to any free port.` ) ;
126+ port = await getPort ( ) ;
127+ // Check if allocated and try again if needed
128+ while ( portManager . isPortAllocated ( port ) ) {
129+ port = await getPort ( ) ;
130+ }
77131 } else {
78- return await getPort ( { port : getPort . makeRange ( range [ 0 ] , range [ 1 ] ) } ) ;
132+ // Convert iterable to array and filter out allocated ports upfront
133+ const portOptions = Array . from ( getPort . makeRange ( range [ 0 ] , range [ 1 ] ) ) ;
134+ const availablePorts = portOptions . filter ( ( p : number ) => ! portManager . isPortAllocated ( p ) ) ;
135+
136+ if ( availablePorts . length === 0 ) {
137+ log . warn ( `All ports in range ${ portRange } are allocated. Falling back to any free port.` ) ;
138+ port = await getPort ( ) ;
139+ // Check if allocated and try again if needed
140+ while ( portManager . isPortAllocated ( port ) ) {
141+ port = await getPort ( ) ;
142+ }
143+ } else {
144+ // Get a free port from the available (non-allocated) ports
145+ // getPort will check system usage among these available ports
146+ port = await getPort ( { port : availablePorts } ) ;
147+ }
148+ }
149+ } else {
150+ port = await getPort ( ) ;
151+ // Check if the port is already allocated, keep trying until we find one that's not
152+ while ( portManager . isPortAllocated ( port ) ) {
153+ port = await getPort ( ) ;
79154 }
80155 }
81- return await getPort ( ) ;
156+
157+ // Allocate the port before returning it
158+ portManager . allocatePort ( port ) ;
159+ return port ;
160+ }
161+
162+ /**
163+ * Release a port that was previously allocated
164+ * @param port - The port number to release
165+ */
166+ export function releasePort ( port : number | undefined | null ) : void {
167+ if ( port && port > 0 ) {
168+ portManager . releasePort ( port ) ;
169+ }
170+ }
171+
172+ /**
173+ * Release multiple ports that were previously allocated
174+ * @param ports - Array of port numbers to release
175+ */
176+ export function releasePorts ( ports : ( number | undefined | null ) [ ] ) : void {
177+ portManager . releasePorts ( ports ) ;
82178}
83179
84180export function nodeUrl ( device : IDevice , basePath = '' ) : string {
0 commit comments