diff --git a/src/libraries/Common/tests/System/Net/Sockets/SocketTestExtensions.cs b/src/libraries/Common/tests/System/Net/Sockets/SocketTestExtensions.cs index 76043a3d3653e9..f386473f06f81f 100644 --- a/src/libraries/Common/tests/System/Net/Sockets/SocketTestExtensions.cs +++ b/src/libraries/Common/tests/System/Net/Sockets/SocketTestExtensions.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Threading; + namespace System.Net.Sockets.Tests { internal static class SocketTestExtensions @@ -49,5 +51,32 @@ public static (Socket, Socket) CreateConnectedSocketPair() return (client, server); } + + // Tries to connect within the provided timeout interval + // Useful to speed up "can not connect" assertions on Windows + public static bool TryConnect(this Socket socket, EndPoint remoteEndpoint, int millisecondsTimeout) + { + var mre = new ManualResetEventSlim(false); + using var sea = new SocketAsyncEventArgs() + { + RemoteEndPoint = remoteEndpoint, + UserToken = mre + }; + + sea.Completed += (s, e) => ((ManualResetEventSlim)e.UserToken).Set(); + + bool pending = socket.ConnectAsync(sea); + if (!pending || mre.Wait(millisecondsTimeout)) + { + mre.Dispose(); + return sea.SocketError == SocketError.Success; + } + + Socket.CancelConnectAsync(sea); // this will close the socket! + + // In case of time-out, ManualResetEventSlim is left undisposed to avoid race conditions, + // letting SafeHandle's finalizer to do the cleanup. + return false; + } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs index bcb9be6e71574a..9fc617fd20bc9e 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs @@ -122,6 +122,9 @@ public void Ctor_Raw_NotSupported_ExpectedError(AddressFamily addressFamily, Pro [InlineData(false, 2)] public void CtorAndAccept_SocketNotKeptAliveViaInheritance(bool validateClientOuter, int acceptApiOuter) { + // 300 ms should be long enough to connect if the socket is actually present & listening. + const int ConnectionTimeoutMs = 300; + // Run the test in another process so as to not have trouble with other tests // launching child processes that might impact inheritance. RemoteExecutor.Invoke((validateClientString, acceptApiString) => @@ -186,11 +189,13 @@ public void CtorAndAccept_SocketNotKeptAliveViaInheritance(bool validateClientOu listener.Dispose(); using (var tmpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { - Assert.ThrowsAny(() => tmpClient.Connect(ep)); - } + bool connected = tmpClient.TryConnect(ep, ConnectionTimeoutMs); - // Let the child process terminate. - serverPipe.WriteByte(42); + // Let the child process terminate. + serverPipe.WriteByte(42); + + Assert.False(connected); + } } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs index 7aababd00fd90d..ae370c770188ea 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketDuplicationTests.cs @@ -167,11 +167,13 @@ public async Task DuplicateAndClose_TcpListener() Assert.Equal(TestMessage, receivedMessage); } - [OuterLoop] // long-running [PlatformSpecific(TestPlatforms.Windows)] [Fact] public void DuplicateSocket_IsNotInheritable() { + // 300 ms should be long enough to connect if the socket is actually present & listening. + const int ConnectionTimeoutMs = 300; + // The test is based on CreateSocket.CtorAndAccept_SocketNotKeptAliveViaInheritance, // but contains simpler validation logic, sufficient to test the behavior on Windows static void RunTest() @@ -202,10 +204,9 @@ static void ChildProcessBody(string clientPipeHandle) listenerDuplicate.Dispose(); // Validate that we after closing the listening socket, we're not able to connect: - SocketException ex = Assert.ThrowsAny(() => client.Connect(ep)); - Debug.Print(ex.Message); - + bool connected = client.TryConnect(ep, ConnectionTimeoutMs); serverPipe.WriteByte(42); + Assert.False(connected); } }