Skip to content

Commit b99dfa7

Browse files
authored
Merge pull request #1774 from fsprojects/repo-assist/fix-windows-ci-port-binding-2026-05-08-f18a3441d94bbb4a
[Repo Assist] fix: use TcpListener(0) and StartAsync in XmlExtensions tests to avoid Windows CI failures
2 parents ffd32bc + 103989e commit b99dfa7

1 file changed

Lines changed: 16 additions & 27 deletions

File tree

tests/FSharp.Data.Core.Tests/XmlExtensions.fs

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ open FSharp.Data.HttpRequestHeaders
99
open Microsoft.AspNetCore.Builder
1010
open Microsoft.AspNetCore.Http
1111
open System.Threading.Tasks
12-
open System.Net.NetworkInformation
12+
open System.Net.Sockets
1313
open System.IO
1414
open System.Text
1515

@@ -50,29 +50,31 @@ let startXmlHttpLocalServer() =
5050
} |> Async.StartAsTask :> Task
5151
)) |> ignore
5252

53+
// Use TcpListener(0) to ask the OS for a free port, then release it.
54+
// This avoids Windows excluded port ranges (reserved by Hyper-V/Docker) that
55+
// caused intermittent SocketException (10013: WSAEACCES) on Windows CI.
5356
let freePort =
54-
let random = new System.Random()
55-
let mutable port = random.Next(10000, 65000)
56-
while
57-
IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()
58-
|> Array.map (fun x -> x.Port)
59-
|> Array.contains port do
60-
port <- random.Next(10000, 65000)
57+
let listener = new TcpListener(System.Net.IPAddress.Loopback, 0)
58+
listener.Start()
59+
let port = (listener.LocalEndpoint :?> System.Net.IPEndPoint).Port
60+
listener.Stop()
6161
port
6262

6363
let baseAddress = $"http://127.0.0.1:{freePort}"
64-
let workerTask = app.RunAsync(baseAddress)
64+
65+
// Use StartAsync so the server is guaranteed ready before we return
66+
app.Urls.Add(baseAddress)
67+
app.StartAsync() |> Async.AwaitTask |> Async.RunSynchronously
6568

6669
{ new ITestHttpServer with
67-
member this.Dispose() =
68-
app.StopAsync() |> Async.AwaitTask |> ignore
69-
member this.WorkerTask = workerTask
70-
member this.BaseAddress = baseAddress }
70+
member _.Dispose() =
71+
app.StopAsync() |> Async.AwaitTask |> Async.RunSynchronously
72+
member _.WorkerTask = Task.CompletedTask
73+
member _.BaseAddress = baseAddress }
7174

7275
[<Test>]
7376
let ``XElement.Request sends XML via POST by default`` () =
7477
use localServer = startXmlHttpLocalServer()
75-
System.Threading.Thread.Sleep(100) // Let server start
7678

7779
let xml = XElement(XName.Get("test"), "sample content")
7880
let response = xml.Request(localServer.BaseAddress + "/echo")
@@ -85,7 +87,6 @@ let ``XElement.Request sends XML via POST by default`` () =
8587
[<Test>]
8688
let ``XElement.Request with custom HTTP method`` () =
8789
use localServer = startXmlHttpLocalServer()
88-
System.Threading.Thread.Sleep(100)
8990

9091
let xml = XElement(XName.Get("test"), "content")
9192
let response = xml.Request(localServer.BaseAddress + "/test/PUT", httpMethod = HttpMethod.Put)
@@ -98,7 +99,6 @@ let ``XElement.Request with custom HTTP method`` () =
9899
[<Test>]
99100
let ``XElement.Request includes default User-Agent header`` () =
100101
use localServer = startXmlHttpLocalServer()
101-
System.Threading.Thread.Sleep(100)
102102

103103
let xml = XElement(XName.Get("test"))
104104
let response = xml.Request(localServer.BaseAddress + "/echo")
@@ -109,7 +109,6 @@ let ``XElement.Request includes default User-Agent header`` () =
109109
[<Test>]
110110
let ``XElement.Request with custom headers`` () =
111111
use localServer = startXmlHttpLocalServer()
112-
System.Threading.Thread.Sleep(100)
113112

114113
let xml = XElement(XName.Get("test"))
115114
let customHeaders = [("X-Custom-Header", "test-value")]
@@ -120,7 +119,6 @@ let ``XElement.Request with custom headers`` () =
120119
[<Test>]
121120
let ``XElement.Request preserves existing User-Agent when provided`` () =
122121
use localServer = startXmlHttpLocalServer()
123-
System.Threading.Thread.Sleep(100)
124122

125123
let xml = XElement(XName.Get("test"))
126124
let customHeaders = [UserAgent "CustomAgent/1.0"]
@@ -131,7 +129,6 @@ let ``XElement.Request preserves existing User-Agent when provided`` () =
131129
[<Test>]
132130
let ``XElement.Request includes XML content type header`` () =
133131
use localServer = startXmlHttpLocalServer()
134-
System.Threading.Thread.Sleep(100)
135132

136133
let xml = XElement(XName.Get("test"))
137134
let response = xml.Request(localServer.BaseAddress + "/echo")
@@ -142,7 +139,6 @@ let ``XElement.Request includes XML content type header`` () =
142139
[<Test>]
143140
let ``XElement.Request with complex XML structure`` () =
144141
use localServer = startXmlHttpLocalServer()
145-
System.Threading.Thread.Sleep(100)
146142

147143
let xml =
148144
XElement(XName.Get("root"),
@@ -163,7 +159,6 @@ let ``XElement.Request with complex XML structure`` () =
163159
[<Test>]
164160
let ``XElement.RequestAsync sends XML via POST by default`` () =
165161
use localServer = startXmlHttpLocalServer()
166-
System.Threading.Thread.Sleep(100)
167162

168163
let xml = XElement(XName.Get("test"), "async content")
169164
let response = xml.RequestAsync(localServer.BaseAddress + "/echo") |> Async.RunSynchronously
@@ -176,7 +171,6 @@ let ``XElement.RequestAsync sends XML via POST by default`` () =
176171
// [<Test>]
177172
// let ``XElement.RequestAsync with custom HTTP method`` () =
178173
// use localServer = startXmlHttpLocalServer()
179-
// System.Threading.Thread.Sleep(100)
180174

181175
// let xml = XElement(XName.Get("test"))
182176
// let response = xml.RequestAsync(localServer.BaseAddress + "/test/PUT", httpMethod = HttpMethod.Put) |> Async.RunSynchronously
@@ -189,7 +183,6 @@ let ``XElement.RequestAsync sends XML via POST by default`` () =
189183
[<Test>]
190184
let ``XElement.RequestAsync with custom headers`` () =
191185
use localServer = startXmlHttpLocalServer()
192-
System.Threading.Thread.Sleep(100)
193186

194187
let xml = XElement(XName.Get("test"))
195188
let customHeaders = [("X-Async-Header", "async-value")]
@@ -200,7 +193,6 @@ let ``XElement.RequestAsync with custom headers`` () =
200193
[<Test>]
201194
let ``XElement.RequestAsync includes default User-Agent header`` () =
202195
use localServer = startXmlHttpLocalServer()
203-
System.Threading.Thread.Sleep(100)
204196

205197
let xml = XElement(XName.Get("test"))
206198
let response = xml.RequestAsync(localServer.BaseAddress + "/echo") |> Async.RunSynchronously
@@ -210,7 +202,6 @@ let ``XElement.RequestAsync includes default User-Agent header`` () =
210202
[<Test>]
211203
let ``XElement.RequestAsync preserves existing User-Agent when provided`` () =
212204
use localServer = startXmlHttpLocalServer()
213-
System.Threading.Thread.Sleep(100)
214205

215206
let xml = XElement(XName.Get("test"))
216207
let customHeaders = [UserAgent "AsyncAgent/1.0"]
@@ -221,7 +212,6 @@ let ``XElement.RequestAsync preserves existing User-Agent when provided`` () =
221212
[<Test>]
222213
let ``XElement with namespaces serializes correctly`` () =
223214
use localServer = startXmlHttpLocalServer()
224-
System.Threading.Thread.Sleep(100)
225215

226216
let ns = XNamespace.Get("http://example.com/test")
227217
let xml = XElement(ns + "root", XAttribute(XNamespace.Xmlns + "test", ns.NamespaceName), "content")
@@ -235,7 +225,6 @@ let ``XElement with namespaces serializes correctly`` () =
235225
[<Test>]
236226
let ``XElement serialization disables formatting`` () =
237227
use localServer = startXmlHttpLocalServer()
238-
System.Threading.Thread.Sleep(100)
239228

240229
let xml =
241230
XElement(XName.Get("root"),

0 commit comments

Comments
 (0)