Harden and re-enable DualMode socket tests (#80715)
authorAnton Firszov <antonfir@gmail.com>
Tue, 7 Feb 2023 17:28:13 +0000 (18:28 +0100)
committerGitHub <noreply@github.com>
Tue, 7 Feb 2023 17:28:13 +0000 (18:28 +0100)
- UDP tests are moved to a non-parallel collection, since there is no way to prevent parallel UDP tests from intercepting each other's packets. This only increases the execution time by ~2 seconds.
- TCP interferences are prevented by a utility PortBlocker, which creates and binds a "shadow" socket of the opposite address family to prevent sockets in parallel tests to bind to the same port.
- Accept and SendTo tests are refactored to use SocketTestHelperBase<T>, reducing duplicate code.

src/libraries/Common/tests/System/Net/Sockets/SocketTestExtensions.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceive.cs

index b316d8e..1e252f0 100644 (file)
@@ -42,8 +42,16 @@ namespace System.Net.Sockets.Tests
         {
             IPAddress serverAddress = ipv6 ? IPAddress.IPv6Loopback : IPAddress.Loopback;
 
-            using Socket listener = new Socket(serverAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-            listener.Bind(new IPEndPoint(serverAddress, 0));
+            // PortBlocker creates a temporary socket of the opposite AddressFamily in the background, so parallel tests won't attempt
+            // to create their listener sockets on the same port, regardless of address family.
+            // This should prevent 'listener' from accepting DualMode connections of unrelated tests.
+            using PortBlocker portBlocker = new PortBlocker(() =>
+            {
+                Socket l = new Socket(serverAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+                l.BindToAnonymousPort(serverAddress);
+                return l;
+            });
+            Socket listener = portBlocker.MainSocket; // PortBlocker shall dispose this
             listener.Listen(1);
 
             IPEndPoint connectTo = (IPEndPoint)listener.LocalEndPoint;
@@ -84,4 +92,121 @@ namespace System.Net.Sockets.Tests
             return false;
         }
     }
+
+    /// <summary>
+    /// A utility to create and bind a socket while blocking it's port for both IPv4 and IPv6
+    /// by also creating and binding a "shadow" socket of the opposite address family.
+    /// </summary>
+    internal class PortBlocker : IDisposable
+    {
+        private const int MaxAttempts = 16;
+        private Socket _shadowSocket;
+        public Socket MainSocket { get; }
+
+        public PortBlocker(Func<Socket> socketFactory)
+        {
+            bool success = false;
+            for (int i = 0; i < MaxAttempts; i++)
+            {
+                MainSocket = socketFactory();
+                if (MainSocket.LocalEndPoint is not IPEndPoint)
+                {
+                    MainSocket.Dispose();
+                    throw new Exception($"{nameof(socketFactory)} is expected create and bind the socket.");
+                }
+
+                IPAddress shadowAddress = MainSocket.AddressFamily == AddressFamily.InterNetwork ?
+                        IPAddress.IPv6Loopback :
+                        IPAddress.Loopback;
+                int port = ((IPEndPoint)MainSocket.LocalEndPoint).Port;
+                IPEndPoint shadowEndPoint = new IPEndPoint(shadowAddress, port);
+
+                try
+                {
+                    _shadowSocket = new Socket(shadowAddress.AddressFamily, MainSocket.SocketType, MainSocket.ProtocolType);
+                    success = TryBindWithoutReuseAddress(_shadowSocket, shadowEndPoint, out _);
+
+                    if (success) break;
+                }
+                catch (SocketException)
+                {
+                    MainSocket.Dispose();
+                    _shadowSocket?.Dispose();
+                }
+            }
+
+            if (!success)
+            {
+                throw new Exception($"Failed to create the 'shadow' (port blocker) socket in {MaxAttempts} attempts.");
+            }
+        }
+
+        public void Dispose()
+        {
+            MainSocket.Dispose();
+            _shadowSocket.Dispose();
+        }
+
+        // Socket.Bind() auto-enables SO_REUSEADDR on Unix to allow Bind() during TIME_WAIT to emulate Windows behavior, see SystemNative_Bind() in 'pal_networking.c'.
+        // To prevent other sockets from succesfully binding to the same port port, we need to avoid this logic when binding the shadow socket.
+        // This method is doing a custom P/Invoke to bind() on Unix to achieve that.
+        private static unsafe bool TryBindWithoutReuseAddress(Socket socket, IPEndPoint endPoint, out int port)
+        {
+            if (PlatformDetection.IsWindows)
+            {
+                try
+                {
+                    socket.Bind(endPoint);
+                }
+                catch (SocketException)
+                {
+                    port = default;
+                    return false;
+                }
+
+                port = ((IPEndPoint)socket.LocalEndPoint).Port;
+                return true;
+            }
+
+            SocketAddress addr = endPoint.Serialize();
+            byte[] data = new byte[addr.Size];
+            for (int i = 0; i < data.Length; i++)
+            {
+                data[i] = addr[i];
+            }
+
+            fixed (byte* dataPtr = data)
+            {
+                int result = bind(socket.SafeHandle, (nint)dataPtr, (uint)data.Length);
+                if (result != 0)
+                {
+                    port = default;
+                    return false;
+                }
+                uint sockLen = (uint)data.Length;
+                result = getsockname(socket.SafeHandle, (nint)dataPtr, (IntPtr)(&sockLen));
+                if (result != 0)
+                {
+                    port = default;
+                    return false;
+                }
+
+                addr = new SocketAddress(endPoint.AddressFamily, (int)sockLen);
+            }
+
+            for (int i = 0; i < data.Length; i++)
+            {
+                addr[i] = data[i];
+            }
+
+            port = ((IPEndPoint)endPoint.Create(addr)).Port;
+            return true;
+
+            [Runtime.InteropServices.DllImport("libc", SetLastError = true)]
+            static extern int bind(SafeSocketHandle socket, IntPtr socketAddress, uint addrLen);
+
+            [Runtime.InteropServices.DllImport("libc", SetLastError = true)]
+            static extern int getsockname(SafeSocketHandle socket, IntPtr socketAddress, IntPtr addrLenPtr);
+        }
+    }
 }
index 8141ae2..a85abab 100644 (file)
@@ -195,6 +195,28 @@ namespace System.Net.Sockets.Tests
                 }
             }, maxAttempts: 10, retryWhen: e => e is XunitException);
         }
+
+        [OuterLoop("Connection failure takes long on Windows.")]
+        [Fact]
+        public async Task Connect_WithoutListener_ThrowSocketExceptionWithAppropriateInfo()
+        {
+            using PortBlocker portBlocker = new PortBlocker(() =>
+            {
+                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+                socket.BindToAnonymousPort(IPAddress.Loopback);
+                return socket;
+            });
+            Socket a = portBlocker.MainSocket;
+            using Socket b = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+
+            SocketException ex = await Assert.ThrowsAsync<SocketException>(() => ConnectAsync(b, a.LocalEndPoint));
+            Assert.Contains(Marshal.GetPInvokeErrorMessage(ex.NativeErrorCode), ex.Message);
+
+            if (UsesSync)
+            {
+                Assert.Contains(a.LocalEndPoint.ToString(), ex.Message);
+            }
+        }
     }
 
     public sealed class ConnectSync : Connect<SocketHelperArraySync>
@@ -215,29 +237,6 @@ namespace System.Net.Sockets.Tests
     public sealed class ConnectTask : Connect<SocketHelperTask>
     {
         public ConnectTask(ITestOutputHelper output) : base(output) {}
-
-        [OuterLoop]
-        [Fact]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/79820", TestPlatforms.Linux | TestPlatforms.Android)]
-        public static void Connect_ThrowSocketException_Success()
-        {
-            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
-            {
-                int anonymousPort = socket.BindToAnonymousPort(IPAddress.Loopback);
-                IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, anonymousPort);
-                Assert.ThrowsAsync<SocketException>(() => socket.ConnectAsync(ep));
-                try
-                {
-                    socket.Connect(ep);
-                    Assert.Fail("Socket Connect should throw SocketException in this case.");
-                }
-                catch (SocketException ex)
-                {
-                    Assert.Contains(Marshal.GetPInvokeErrorMessage(ex.NativeErrorCode), ex.Message);
-                    Assert.Contains(ep.ToString(), ex.Message);
-                }
-            }
-        }
     }
 
     public sealed class ConnectEap : Connect<SocketHelperEap>
index 91ff1ce..0a5f3ad 100644 (file)
@@ -3,6 +3,7 @@
 
 using System.Linq;
 using System.Net.Test.Common;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
@@ -144,6 +145,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 socket.Connect(connectTo, port);
                 Assert.True(socket.Connected);
             }
@@ -235,6 +237,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 socket.Connect(new IPEndPoint(connectTo, port));
                 Assert.True(socket.Connected);
             }
@@ -269,6 +272,7 @@ namespace System.Net.Sockets.Tests
 
                 using (SocketServer server = new SocketServer(_log, IPAddress.Loopback, false, out int port))
                 {
+                    server.Start();
                     AssertExtensions.Throws<ArgumentException>("addresses", () =>
                     {
                         socket.Connect(new IPAddress[] { IPAddress.Loopback }, port);
@@ -278,11 +282,31 @@ namespace System.Net.Sockets.Tests
         }
 
         [Theory]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
         [MemberData(nameof(DualMode_IPAddresses_ListenOn_DualMode_Throws_Data))]
         public void DualModeConnect_IPAddressListToHost_Throws(IPAddress[] connectTo, IPAddress listenOn, bool dualModeServer)
         {
-            Assert.ThrowsAny<SocketException>(() => DualModeConnect_IPAddressListToHost_Success(connectTo, listenOn, dualModeServer));
+            using Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
+            SocketServer server = null;
+            int port = 0;
+
+            // PortBlocker creates a temporary socket of the opposite AddressFamily in the background, so parallel tests won't attempt
+            // to create their listener sockets on the same port.
+            // This should prevent 'server' from accepting DualMode connections of unrelated tests.
+            using PortBlocker blocker = new PortBlocker(() =>
+            {
+                server = new SocketServer(_log, listenOn, dualModeServer, out port);
+                return server.Socket;
+            });
+
+            using (server)
+            {
+                server.Start();
+                Assert.ThrowsAny<SocketException>(() =>
+                {
+                    socket.Connect(connectTo, port);
+                });
+                Assert.False(socket.Connected);
+            }
         }
 
         [Theory]
@@ -292,6 +316,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 socket.Connect(connectTo, port);
                 Assert.True(socket.Connected);
             }
@@ -309,6 +334,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 socket.Connect("localhost", port);
                 Assert.True(socket.Connected);
             }
@@ -326,6 +352,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 socket.Connect(new DnsEndPoint("localhost", port, AddressFamily.Unspecified));
                 Assert.True(socket.Connected);
             }
@@ -374,6 +401,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 await Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, connectTo, port, null);
                 Assert.True(socket.Connected);
             }
@@ -426,6 +454,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 await Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, new IPEndPoint(connectTo, port), null);
                 Assert.True(socket.Connected);
             }
@@ -444,6 +473,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 await Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, connectTo, port, null);
                 Assert.True(socket.Connected);
             }
@@ -457,6 +487,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 await Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, "localhost", port, null);
                 Assert.True(socket.Connected);
             }
@@ -470,6 +501,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 await Task.Factory.FromAsync(socket.BeginConnect, socket.EndConnect, new DnsEndPoint("localhost", port), null);
                 Assert.True(socket.Connected);
             }
@@ -535,6 +567,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 ManualResetEvent waitHandle = new ManualResetEvent(false);
                 SocketAsyncEventArgs args = new SocketAsyncEventArgs();
                 args.Completed += new EventHandler<SocketAsyncEventArgs>(AsyncCompleted);
@@ -575,6 +608,7 @@ namespace System.Net.Sockets.Tests
             using (Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp))
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 ManualResetEvent waitHandle = new ManualResetEvent(false);
                 SocketAsyncEventArgs args = new SocketAsyncEventArgs();
                 args.Completed += new EventHandler<SocketAsyncEventArgs>(AsyncCompleted);
@@ -601,6 +635,7 @@ namespace System.Net.Sockets.Tests
         {
             using (SocketServer server = new SocketServer(_log, listenOn, dualModeServer, out int port))
             {
+                server.Start();
                 ManualResetEvent waitHandle = new ManualResetEvent(false);
                 SocketAsyncEventArgs args = new SocketAsyncEventArgs();
                 args.Completed += new EventHandler<SocketAsyncEventArgs>(AsyncCompleted);
@@ -692,1051 +727,363 @@ namespace System.Net.Sockets.Tests
         }
     }
 
-    [Trait("IPv4", "true")]
-    [Trait("IPv6", "true")]
-    public class DualModeAccept : DualModeBase
+    public abstract class DualModeAcceptBase<T> : SocketTestHelperBase<T> where T : SocketHelperBase, new()
     {
-        [Fact]
-        public void AcceptV4BoundToSpecificV4_Success()
+        public DualModeAcceptBase(ITestOutputHelper output) : base(output)
         {
-            Accept_Helper(IPAddress.Loopback, IPAddress.Loopback);
         }
 
         [Fact]
-        public void AcceptV4BoundToAnyV4_Success()
-        {
-            Accept_Helper(IPAddress.Any, IPAddress.Loopback);
-        }
+        public Task AcceptV4BoundToSpecificV4_Success() => Accept_Helper(IPAddress.Loopback, IPAddress.Loopback);
 
         [Fact]
-        public void AcceptV6BoundToSpecificV6_Success()
-        {
-            Accept_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback);
-        }
+        public Task AcceptV4BoundToAnyV4_Success() => Accept_Helper(IPAddress.Any, IPAddress.Loopback);
 
         [Fact]
-        public void AcceptV6BoundToAnyV6_Success()
-        {
-            Accept_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback);
-        }
+        public Task AcceptV6BoundToSpecificV6_Success() => Accept_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback);
 
         [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // https://github.com/dotnet/runtime/issues/16265
-        public void AcceptV6BoundToSpecificV4_CantConnect()
-        {
-            Assert.Throws<SocketException>(() =>
-            {
-                Accept_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback);
-            });
-        }
+        public Task AcceptV6BoundToAnyV6_Success() => Accept_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback);
 
         [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // https://github.com/dotnet/runtime/issues/16265
-        public void AcceptV4BoundToSpecificV6_CantConnect()
-        {
-            Assert.Throws<SocketException>(() =>
-            {
-                Accept_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback);
-            });
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // https://github.com/dotnet/runtime/issues/16265
-        public void AcceptV6BoundToAnyV4_CantConnect()
-        {
-            Assert.Throws<SocketException>(() =>
-            {
-                Accept_Helper(IPAddress.Any, IPAddress.IPv6Loopback);
-            });
-        }
-
-        [Fact]
-        public void AcceptV4BoundToAnyV6_Success()
-        {
-            Accept_Helper(IPAddress.IPv6Any, IPAddress.Loopback);
-        }
-
-        private void Accept_Helper(IPAddress listenOn, IPAddress connectTo)
-        {
-            using (Socket serverSocket = new Socket(SocketType.Stream, ProtocolType.Tcp))
-            {
-                int port = serverSocket.BindToAnonymousPort(listenOn);
-                serverSocket.Listen(1);
-                SocketClient client = new SocketClient(_log, serverSocket, connectTo, port);
-                Socket clientSocket = serverSocket.Accept();
-                Assert.True(clientSocket.Connected);
-                AssertDualModeEnabled(clientSocket, listenOn);
-                Assert.Equal(AddressFamily.InterNetworkV6, clientSocket.AddressFamily);
-            }
-        }
-    }
-
-    [Trait("IPv4", "true")]
-    [Trait("IPv6", "true")]
-    public class DualModeBeginAccept : DualModeBase
-    {
-        [Fact]
-        public void BeginAcceptV4BoundToSpecificV4_Success()
-        {
-            DualModeConnect_BeginAccept_Helper(IPAddress.Loopback, IPAddress.Loopback);
-        }
-
-        [Fact]
-        public void BeginAcceptV4BoundToAnyV4_Success()
-        {
-            DualModeConnect_BeginAccept_Helper(IPAddress.Any, IPAddress.Loopback);
-        }
-
-        [Fact]
-        public void BeginAcceptV6BoundToSpecificV6_Success()
-        {
-            DualModeConnect_BeginAccept_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback);
-        }
+        public Task AcceptV4BoundToAnyV6_Success() => Accept_Helper(IPAddress.IPv6Any, IPAddress.Loopback);
 
         [Fact]
-        public void BeginAcceptV6BoundToAnyV6_Success()
-        {
-            DualModeConnect_BeginAccept_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback);
-        }
+        public Task AcceptV6BoundToSpecificV4_CantConnect() => Accept_Helper_Failing(IPAddress.Loopback, IPAddress.IPv6Loopback);
 
         [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void BeginAcceptV6BoundToSpecificV4_CantConnect()
-        {
-            Assert.Throws<SocketException>(() =>
-            {
-                DualModeConnect_BeginAccept_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback);
-            });
-        }
+        public Task AcceptV4BoundToSpecificV6_CantConnect() => Accept_Helper_Failing(IPAddress.IPv6Loopback, IPAddress.Loopback);
 
         [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void BeginAcceptV4BoundToSpecificV6_CantConnect()
-        {
-            Assert.Throws<SocketException>(() =>
-            {
-                DualModeConnect_BeginAccept_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback);
-            });
-        }
+        public Task AcceptV6BoundToAnyV4_CantConnect() => Accept_Helper_Failing(IPAddress.Any, IPAddress.IPv6Loopback);
 
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void BeginAcceptV6BoundToAnyV4_CantConnect()
+        private async Task Accept_Helper(IPAddress listenOn, IPAddress connectTo)
         {
-            Assert.Throws<SocketException>(() =>
-            {
-                DualModeConnect_BeginAccept_Helper(IPAddress.Any, IPAddress.IPv6Loopback);
-            });
-        }
+            using Socket serverSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
 
-        [Fact]
-        public void BeginAcceptV4BoundToAnyV6_Success()
-        {
-            DualModeConnect_BeginAccept_Helper(IPAddress.IPv6Any, IPAddress.Loopback);
-        }
+            int port = serverSocket.BindToAnonymousPort(listenOn);
+            serverSocket.Listen(1);
 
-        private void DualModeConnect_BeginAccept_Helper(IPAddress listenOn, IPAddress connectTo)
-        {
-            using (Socket serverSocket = new Socket(SocketType.Stream, ProtocolType.Tcp))
+            using Socket client = new Socket(connectTo.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+            Task connectTask = client.ConnectAsync(connectTo, port);
+            Socket clientSocket = await AcceptAsync(serverSocket);
+            await connectTask;
+            Assert.True(clientSocket.Connected);
+            AssertDualModeEnabled(clientSocket, listenOn);
+            Assert.Equal(AddressFamily.InterNetworkV6, clientSocket.AddressFamily);
+            if (connectTo == IPAddress.Loopback)
             {
-                int port = serverSocket.BindToAnonymousPort(listenOn);
-                serverSocket.Listen(1);
-                IAsyncResult async = serverSocket.BeginAccept(null, null);
-                SocketClient client = new SocketClient(_log, serverSocket, connectTo, port);
-                
-                Assert.True(
-                    client.WaitHandle.WaitOne(TestSettings.PassingTestTimeout),
-                    "Timed out while waiting for connection");
-                Assert.True(
-                    async.AsyncWaitHandle.WaitOne(TestSettings.PassingTestTimeout),
-                    "Timed out while waiting to accept the client");
-
-                // Due to the nondeterministic nature of calling dispose on a Socket that is doing
-                // an EndAccept operation, we expect two types of exceptions to happen.
-                Socket clientSocket;
-                try
-                {
-                    clientSocket = serverSocket.EndAccept(async);
-                    Assert.True(clientSocket.Connected);
-                    AssertDualModeEnabled(clientSocket, listenOn);
-                    Assert.Equal(AddressFamily.InterNetworkV6, clientSocket.AddressFamily);
-                    if (connectTo == IPAddress.Loopback)
-                    {
-                        Assert.Contains(((IPEndPoint)clientSocket.LocalEndPoint).Address, ValidIPv6Loopbacks);
-                    }
-                    else
-                    {
-                        Assert.Equal(connectTo.MapToIPv6(), ((IPEndPoint)clientSocket.LocalEndPoint).Address);
-                    }
-                }
-                catch (ObjectDisposedException) { }
-                catch (SocketException) { }
-
-                if (client.Error != SocketError.Success)
-                {
-                    throw new SocketException((int)client.Error);
-                }
+                Assert.Contains(((IPEndPoint)clientSocket.LocalEndPoint).Address, DualModeBase.ValidIPv6Loopbacks);
             }
-        }
-    }
-
-    [Trait("IPv4", "true")]
-    [Trait("IPv6", "true")]
-    public class DualModeAcceptAsync : DualModeBase
-    {
-        [Fact]
-        public void AcceptAsyncV4BoundToSpecificV4_Success()
-        {
-            DualModeConnect_AcceptAsync_Helper(IPAddress.Loopback, IPAddress.Loopback);
-        }
-
-        [Fact]
-        public void AcceptAsyncV4BoundToAnyV4_Success()
-        {
-            DualModeConnect_AcceptAsync_Helper(IPAddress.Any, IPAddress.Loopback);
-        }
-
-        [Fact]
-        public void AcceptAsyncV6BoundToSpecificV6_Success()
-        {
-            DualModeConnect_AcceptAsync_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback);
-        }
-
-        [Fact]
-        public void AcceptAsyncV6BoundToAnyV6_Success()
-        {
-            DualModeConnect_AcceptAsync_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback);
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void AcceptAsyncV6BoundToSpecificV4_CantConnect()
-        {
-            Assert.Throws<SocketException>(() =>
-            {
-                DualModeConnect_AcceptAsync_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback);
-            });
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void AcceptAsyncV4BoundToSpecificV6_CantConnect()
-        {
-            Assert.Throws<SocketException>(() =>
+            else
             {
-                DualModeConnect_AcceptAsync_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback);
-            });
+                Assert.Equal(connectTo.MapToIPv6(), ((IPEndPoint)clientSocket.LocalEndPoint).Address);
+            }
         }
 
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void AcceptAsyncV6BoundToAnyV4_CantConnect()
+        private async Task Accept_Helper_Failing(IPAddress listenOn, IPAddress connectTo)
         {
-            Assert.Throws<SocketException>(() =>
-            {
-                DualModeConnect_AcceptAsync_Helper(IPAddress.Any, IPAddress.IPv6Loopback);
-            });
-        }
+            using Socket serverSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
+            int port = serverSocket.BindToAnonymousPort(listenOn);
+            serverSocket.Listen(1);
+            _ = AcceptAsync(serverSocket);
 
-        [Fact]
-        public void AcceptAsyncV4BoundToAnyV6_Success()
-        {
-            DualModeConnect_AcceptAsync_Helper(IPAddress.IPv6Any, IPAddress.Loopback);
+            using Socket client = new Socket(connectTo.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+            await Assert.ThrowsAsync<SocketException>(() => client.ConnectAsync(connectTo, port));
         }
 
-        private void DualModeConnect_AcceptAsync_Helper(IPAddress listenOn, IPAddress connectTo)
-        {
-            using (Socket serverSocket = new Socket(SocketType.Stream, ProtocolType.Tcp))
-            {
-                int port = serverSocket.BindToAnonymousPort(listenOn);
-                serverSocket.Listen(1);
-
-                SocketAsyncEventArgs args = new SocketAsyncEventArgs();
-                args.Completed += AsyncCompleted;
-                ManualResetEvent waitHandle = new ManualResetEvent(false);
-                args.UserToken = waitHandle;
-                args.SocketError = SocketError.SocketError;
-
-                _log.WriteLine(args.GetHashCode() + " SocketAsyncEventArgs with manual event " + waitHandle.GetHashCode());
-                if (!serverSocket.AcceptAsync(args))
-                {
-                    throw new SocketException((int)args.SocketError);
-                }
-
-                SocketClient client = new SocketClient(_log, serverSocket, connectTo, port);
-
-                var waitHandles = new WaitHandle[2];
-                waitHandles[0] = waitHandle;
-                waitHandles[1] = client.WaitHandle;
-
-                int completedHandle = WaitHandle.WaitAny(waitHandles, TestSettings.PassingTestTimeout);
-
-                if (completedHandle == WaitHandle.WaitTimeout)
-                {
-                    throw new TimeoutException("Timed out while waiting for either of client and server connections...");
-                }
-
-                if (completedHandle == 1)   // Client finished
-                {
-                    if (client.Error != SocketError.Success)
-                    {
-                        // Client SocketException
-                        throw new SocketException((int)client.Error);
-                    }
-
-                    if (!waitHandle.WaitOne(5000))  // Now wait for the server.
-                    {
-                        throw new TimeoutException("Timed out while waiting for the server accept...");
-                    }
-                }
-
-                _log.WriteLine(args.SocketError.ToString());
-
-
-                if (args.SocketError != SocketError.Success)
-                {
-                    throw new SocketException((int)args.SocketError);
-                }
-
-                Socket clientSocket = args.AcceptSocket;
-                Assert.NotNull(clientSocket);
-                Assert.True(clientSocket.Connected);
-                AssertDualModeEnabled(clientSocket, listenOn);
-                Assert.Equal(AddressFamily.InterNetworkV6, clientSocket.AddressFamily);
-                if (connectTo == IPAddress.Loopback)
-                {
-                    Assert.Contains(((IPEndPoint)clientSocket.LocalEndPoint).Address, ValidIPv6Loopbacks);
-                }
-                else
-                {
-                    Assert.Equal(connectTo.MapToIPv6(), ((IPEndPoint)clientSocket.LocalEndPoint).Address);
-                }
-                clientSocket.Dispose();
-            }
-        }
-    }
-
-    [OuterLoop]
-    [Trait("IPv4", "true")]
-    [Trait("IPv6", "true")]
-    public class DualModeConnectionlessSendTo : DualModeBase
-    {
-        #region SendTo Sync IPEndPoint
-
-        [Fact]
-        public void Socket_SendToV4IPEndPointToV4Host_Throws()
+        protected static void AssertDualModeEnabled(Socket socket, IPAddress listenOn)
         {
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
+            if (OperatingSystem.IsWindows())
             {
-                socket.DualMode = false;
-                Assert.Throws<SocketException>(() =>
-                {
-                    socket.SendTo(new byte[1], new IPEndPoint(IPAddress.Loopback, UnusedPort));
-                });
+                Assert.True(socket.DualMode);
             }
-        }
-
-        [Fact] // Base case
-        // "The parameter remoteEP must not be of type DnsEndPoint."
-        public void Socket_SendToDnsEndPoint_Throws()
-        {
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
+            else if (OperatingSystem.IsFreeBSD())
             {
-                AssertExtensions.Throws<ArgumentException>("remoteEP", () =>
-                {
-                    socket.SendTo(new byte[1], new DnsEndPoint("localhost", UnusedPort));
-                });
+                // This is not valid check on FreeBSD.
+                // Accepted socket is never DualMode and cannot be changed.
             }
-        }
-
-        [Fact]
-        public void SendToV4IPEndPointToV4Host_Success()
-        {
-            DualModeSendTo_IPEndPointToHost_Helper(IPAddress.Loopback, IPAddress.Loopback, false);
-        }
-
-        [Fact]
-        public void SendToV6IPEndPointToV6Host_Success()
-        {
-            DualModeSendTo_IPEndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback, false);
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void SendToV4IPEndPointToV6Host_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                DualModeSendTo_IPEndPointToHost_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback, false, expectedToTimeout: true);
-            });
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void SendToV6IPEndPointToV4Host_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                DualModeSendTo_IPEndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback, false, expectedToTimeout: true);
-            });
-        }
-
-        [Fact]
-        public void SendToV4IPEndPointToDualHost_Success()
-        {
-            DualModeSendTo_IPEndPointToHost_Helper(IPAddress.Loopback, IPAddress.IPv6Any, true);
-        }
-
-        [Fact]
-        public void SendToV6IPEndPointToDualHost_Success()
-        {
-            DualModeSendTo_IPEndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Any, true);
-        }
-
-        private void DualModeSendTo_IPEndPointToHost_Helper(IPAddress connectTo, IPAddress listenOn, bool dualModeServer, bool expectedToTimeout = false)
-        {
-            using (Socket client = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            using (SocketUdpServer server = new SocketUdpServer(_log, listenOn, dualModeServer, out int port))
+            else
             {
-                int sent = client.SendTo(new byte[1], new IPEndPoint(connectTo, port));
-                Assert.Equal(1, sent);
-
-                bool success = server.WaitHandle.WaitOne(expectedToTimeout ? TestSettings.FailingTestTimeout : TestSettings.PassingTestTimeout); // Make sure the bytes were received
-                if (!success)
-                {
-                    throw new TimeoutException();
-                }
+                Assert.True((listenOn != IPAddress.IPv6Any && !listenOn.IsIPv4MappedToIPv6) || socket.DualMode);
             }
         }
-
-        #endregion SendTo Sync
     }
 
-    [OuterLoop]
     [Trait("IPv4", "true")]
     [Trait("IPv6", "true")]
-    public class DualModeConnectionlessBeginSendTo : DualModeBase
+    public class DualModeAcceptSync : DualModeAcceptBase<SocketHelperArraySync>
     {
-        #region SendTo Begin/End
-
-        [Fact]
-        public void Socket_BeginSendToV4IPEndPointToV4Host_Throws()
-        {
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                socket.DualMode = false;
-
-                Assert.Throws<SocketException>(() =>
-                {
-                    // [ActiveIssue("https://github.com/dotnet/runtime/issues/47905")]
-                    // TODO: When fixing the issue above, revert this test to check that the exception is being thrown in BeginSendTo
-                    // without the need to call EndSendTo.
-                    IAsyncResult result = socket.BeginSendTo(new byte[1], 0, 1, SocketFlags.None, new IPEndPoint(IPAddress.Loopback, UnusedPort), null, null);
-                    socket.EndSendTo(result);
-                });
-            }
-        }
-
-        [Fact] // Base case
-        // "The parameter remoteEP must not be of type DnsEndPoint."
-        public void Socket_BeginSendToDnsEndPoint_Throws()
-        {
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                AssertExtensions.Throws<ArgumentException>("remoteEP", () =>
-                {
-                    socket.BeginSendTo(new byte[1], 0, 1, SocketFlags.None, new DnsEndPoint("localhost", UnusedPort), null, null);
-                });
-            }
-        }
-
-        [Fact]
-        public void BeginSendToV4IPEndPointToV4Host_Success()
-        {
-            DualModeBeginSendTo_EndPointToHost_Helper(IPAddress.Loopback, IPAddress.Loopback, false);
-        }
-
-        [Fact]
-        public void BeginSendToV6IPEndPointToV6Host_Success()
-        {
-            DualModeBeginSendTo_EndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback, false);
-        }
-
-        [Fact]
-        public void BeginSendToV4IPEndPointToV6Host_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                DualModeBeginSendTo_EndPointToHost_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback, false, expectedToTimeout: true);
-            });
-        }
-
-        [Fact]
-        public void BeginSendToV6IPEndPointToV4Host_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                DualModeBeginSendTo_EndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback, false, expectedToTimeout: true);
-            });
-        }
-
-        [Fact]
-        public void BeginSendToV4IPEndPointToDualHost_Success()
-        {
-            DualModeBeginSendTo_EndPointToHost_Helper(IPAddress.Loopback, IPAddress.IPv6Any, true);
-        }
-
-        [Fact]
-        public void BeginSendToV6IPEndPointToDualHost_Success()
-        {
-            DualModeBeginSendTo_EndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Any, true);
-        }
-
-        private void DualModeBeginSendTo_EndPointToHost_Helper(IPAddress connectTo, IPAddress listenOn, bool dualModeServer, bool expectedToTimeout = false)
-        {
-            using (Socket client = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            using (SocketUdpServer server = new SocketUdpServer(_log, listenOn, dualModeServer, out int port))
-            {
-                IAsyncResult async = client.BeginSendTo(new byte[1], 0, 1, SocketFlags.None, new IPEndPoint(connectTo, port), null, null);
-
-                int sent = client.EndSendTo(async);
-                Assert.Equal(1, sent);
-
-                bool success = server.WaitHandle.WaitOne(expectedToTimeout ? TestSettings.FailingTestTimeout : TestSettings.PassingTestTimeout); // Make sure the bytes were received
-                if (!success)
-                {
-                    throw new TimeoutException();
-                }
-            }
-        }
-
-        #endregion SendTo Begin/End
+        public DualModeAcceptSync(ITestOutputHelper output) : base(output) { }
     }
 
-    [OuterLoop]
     [Trait("IPv4", "true")]
     [Trait("IPv6", "true")]
-    public class DualModeConnectionlessSendToAsync : DualModeBase
+    public class DualModeAcceptApm : DualModeAcceptBase<SocketHelperApm>
     {
-        #region SendTo Async/Event
-
-        [Fact]
-        public void Socket_SendToAsyncV4IPEndPointToV4Host_Throws()
-        {
-            using (Socket socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp))
-            {
-                SocketAsyncEventArgs args = new SocketAsyncEventArgs();
-                args.RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, UnusedPort);
-                args.SetBuffer(new byte[1], 0, 1);
-                bool async = socket.SendToAsync(args);
-                Assert.False(async);
-
-                if (OperatingSystem.IsWindows())
-                {
-                    Assert.Equal(SocketError.Fault, args.SocketError);
-                }
-                else if (OperatingSystem.IsLinux())
-                {
-                    // NOTE: on Linux, this API returns ENETUNREACH instead of EFAULT: this platform
-                    //       checks the family of the provided socket address before checking its size
-                    //       (as long as the socket address is large enough to store an address family).
-                    Assert.Equal(SocketError.NetworkUnreachable, args.SocketError);
-                }
-                else
-                {
-                    // NOTE: on other Unix platforms, this API returns EINVAL instead of EFAULT.
-                    Assert.Equal(SocketError.InvalidArgument, args.SocketError);
-                }
-            }
-        }
-
-        [Fact] // Base case
-        // "The parameter remoteEP must not be of type DnsEndPoint."
-        public void Socket_SendToAsyncDnsEndPoint_Throws()
-        {
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                SocketAsyncEventArgs args = new SocketAsyncEventArgs();
-                args.RemoteEndPoint = new DnsEndPoint("localhost", UnusedPort);
-                args.SetBuffer(new byte[1], 0, 1);
-                AssertExtensions.Throws<ArgumentException>("remoteEP", () =>
-                {
-                    socket.SendToAsync(args);
-                });
-            }
-        }
-
-        [Fact]
-        public void SendToAsyncV4IPEndPointToV4Host_Success()
-        {
-            DualModeSendToAsync_IPEndPointToHost_Helper(IPAddress.Loopback, IPAddress.Loopback, false);
-        }
-
-        [Fact]
-        public void SendToAsyncV6IPEndPointToV6Host_Success()
-        {
-            DualModeSendToAsync_IPEndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback, false);
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void SendToAsyncV4IPEndPointToV6Host_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                DualModeSendToAsync_IPEndPointToHost_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback, false, expectedToTimeout: true);
-            });
-        }
-
-        [Fact]
-        [PlatformSpecific(TestPlatforms.Windows)] // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        public void SendToAsyncV6IPEndPointToV4Host_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                DualModeSendToAsync_IPEndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback, false, expectedToTimeout: true);
-            });
-        }
-
-        [Fact]
-        public void SendToAsyncV4IPEndPointToDualHost_Success()
-        {
-            DualModeSendToAsync_IPEndPointToHost_Helper(IPAddress.Loopback, IPAddress.IPv6Any, true);
-        }
-
-        [Fact]
-        public void SendToAsyncV6IPEndPointToDualHost_Success()
-        {
-            DualModeSendToAsync_IPEndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Any, true);
-        }
-
-        private void DualModeSendToAsync_IPEndPointToHost_Helper(IPAddress connectTo, IPAddress listenOn, bool dualModeServer, bool expectedToTimeout = false)
-        {
-            using (Socket client = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            using (SocketUdpServer server = new SocketUdpServer(_log, listenOn, dualModeServer, out int port))
-            {
-                using (ManualResetEvent waitHandle = new ManualResetEvent(false))
-                {
-                    SocketAsyncEventArgs args = new SocketAsyncEventArgs();
-                    args.RemoteEndPoint = new IPEndPoint(connectTo, port);
-                    args.SetBuffer(new byte[1], 0, 1);
-                    args.UserToken = waitHandle;
-                    args.Completed += AsyncCompleted;
-
-                    bool async = client.SendToAsync(args);
-                    if (async)
-                    {
-                        Assert.True(waitHandle.WaitOne(TestSettings.PassingTestTimeout), "Timeout while waiting for connection");
-                    }
-
-                    Assert.Equal(1, args.BytesTransferred);
-                    if (args.SocketError != SocketError.Success)
-                    {
-                        throw new SocketException((int)args.SocketError);
-                    }
-                }
-
-                bool success = server.WaitHandle.WaitOne(expectedToTimeout ? TestSettings.FailingTestTimeout : TestSettings.PassingTestTimeout); // Make sure the bytes were received
-                if (!success)
-                {
-                    throw new TimeoutException();
-                }
-            }
-        }
-
-        #endregion SendTo Async/Event
+        public DualModeAcceptApm(ITestOutputHelper output) : base(output) { }
     }
 
-    [OuterLoop]
     [Trait("IPv4", "true")]
     [Trait("IPv6", "true")]
-    public class DualModeConnectionlessReceiveFrom : DualModeBase
+    public class DualModeAcceptEap : DualModeAcceptBase<SocketHelperEap>
     {
-        #region ReceiveFrom Sync
-
-        [Fact] // Base case
-        public void Socket_ReceiveFromV4IPEndPointFromV4Client_Throws()
-        {
-            // "The supplied EndPoint of AddressFamily InterNetwork is not valid for this Socket, use InterNetworkV6 instead."
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                socket.DualMode = false;
-
-                EndPoint receivedFrom = new IPEndPoint(IPAddress.Loopback, UnusedPort);
-                AssertExtensions.Throws<ArgumentException>("remoteEP", () =>
-                {
-                    int received = socket.ReceiveFrom(new byte[1], ref receivedFrom);
-                });
-            }
-        }
-
-        [Fact] // Base case
-        public void Socket_ReceiveFromDnsEndPoint_Throws()
-        {
-            // "The parameter remoteEP must not be of type DnsEndPoint."
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                int port = socket.BindToAnonymousPort(IPAddress.IPv6Loopback);
-                EndPoint receivedFrom = new DnsEndPoint("localhost", port, AddressFamily.InterNetworkV6);
-                AssertExtensions.Throws<ArgumentException>("remoteEP", () =>
-                {
-                    int received = socket.ReceiveFrom(new byte[1], ref receivedFrom);
-                });
-            }
-        }
-
-        [Fact]
-        public void ReceiveFromV4BoundToSpecificV4_Success()
-        {
-            ReceiveFrom_Helper(IPAddress.Loopback, IPAddress.Loopback);
-        }
-
-        [Fact]
-        public void ReceiveFromV4BoundToAnyV4_Success()
-        {
-            ReceiveFrom_Helper(IPAddress.Any, IPAddress.Loopback);
-        }
-
-        [Fact]
-        public void ReceiveFromV6BoundToSpecificV6_Success()
-        {
-            ReceiveFrom_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback);
-        }
-
-        [Fact]
-        public void ReceiveFromV6BoundToAnyV6_Success()
-        {
-            ReceiveFrom_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback);
-        }
-
-        [Fact]
-        // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        // Also ReceiveFrom not supported on OSX
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void ReceiveFromV6BoundToSpecificV4_NotReceived()
-        {
-            Assert.Throws<SocketException>(() =>
-            {
-                ReceiveFrom_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback);
-            });
-        }
-
-        [Fact]
-        // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        // Also expected behavior is different on OSX and Linux (ArgumentException instead of SocketException)
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void ReceiveFromV4BoundToSpecificV6_NotReceived()
-        {
-            Assert.Throws<SocketException>(() =>
-            {
-                ReceiveFrom_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback);
-            });
-        }
-
-        [Fact]
-        // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        // Also ReceiveFrom not supported on OSX
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void ReceiveFromV6BoundToAnyV4_NotReceived()
-        {
-            Assert.Throws<SocketException>(() =>
-            {
-                ReceiveFrom_Helper(IPAddress.Any, IPAddress.IPv6Loopback);
-            });
-        }
-
-        [Fact]
-        public void ReceiveFromV4BoundToAnyV6_Success()
-        {
-            ReceiveFrom_Helper(IPAddress.IPv6Any, IPAddress.Loopback);
-        }
-
-        #endregion ReceiveFrom Sync
+        public DualModeAcceptEap(ITestOutputHelper output) : base(output) { }
     }
 
-    [OuterLoop]
     [Trait("IPv4", "true")]
-    [Trait("IPv6", "true")]
-    public class DualModeConnectionlessBeginReceiveFrom : DualModeBase
-    {
-        #region ReceiveFrom Begin/End
-
-        [Fact] // Base case
-        // "The supplied EndPoint of AddressFamily InterNetwork is not valid for this Socket, use InterNetworkV6 instead."
-        public void Socket_BeginReceiveFromV4IPEndPointFromV4Client_Throws()
-        {
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                socket.DualMode = false;
-
-                EndPoint receivedFrom = new IPEndPoint(IPAddress.Loopback, UnusedPort);
-                AssertExtensions.Throws<ArgumentException>("remoteEP", () =>
-                {
-                    socket.BeginReceiveFrom(new byte[1], 0, 1, SocketFlags.None, ref receivedFrom, null, null);
-                });
-            }
-        }
+    [Trait("IPv6", "true")]
+    public class DualModeAcceptTask : DualModeAcceptBase<SocketHelperTask>
+    {
+        public DualModeAcceptTask(ITestOutputHelper output) : base(output) { }
+    }
 
-        [Fact] // Base case
-        // "The parameter remoteEP must not be of type DnsEndPoint."
-        public void Socket_BeginReceiveFromDnsEndPoint_Throws()
+    public abstract class DualModeConnectionlessSendToBase<T> : SocketTestHelperBase<T> where T : SocketHelperBase, new()
+    {
+        protected DualModeConnectionlessSendToBase(ITestOutputHelper output) : base(output)
         {
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                int port = socket.BindToAnonymousPort(IPAddress.IPv6Loopback);
-                EndPoint receivedFrom = new DnsEndPoint("localhost", port, AddressFamily.InterNetworkV6);
-
-                AssertExtensions.Throws<ArgumentException>("remoteEP", () =>
-                {
-                    socket.BeginReceiveFrom(new byte[1], 0, 1, SocketFlags.None, ref receivedFrom, null, null);
-                });
-            }
         }
 
         [Fact]
-        public void BeginReceiveFromV4BoundToSpecificV4_Success()
+        public async Task Socket_SendToV4IPEndPointToV4Host_Throws()
         {
-            BeginReceiveFrom_Helper(IPAddress.Loopback, IPAddress.Loopback);
+            using Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp);
+            socket.DualMode = false;
+            await Assert.ThrowsAsync<SocketException>(
+                () => SendToAsync(socket, new byte[1], new IPEndPoint(IPAddress.Loopback, DualModeBase.UnusedPort)));
         }
 
-        [Fact]
-        [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.MacCatalyst | TestPlatforms.iOS | TestPlatforms.tvOS, "BeginReceiveFrom not supported on Apple platforms")]
-        public void BeginReceiveFromV4BoundToAnyV4_Success()
+        [Fact] // Base case
+        // "The parameter remoteEP must not be of type DnsEndPoint."
+        public async Task Socket_SendToDnsEndPoint_Throws()
         {
-            BeginReceiveFrom_Helper(IPAddress.Any, IPAddress.Loopback);
+            using Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp);
+
+            await AssertExtensions.ThrowsAsync<ArgumentException>("remoteEP",
+                () => SendToAsync(socket, new byte[1], new DnsEndPoint("localhost", DualModeBase.UnusedPort)));
         }
 
         [Fact]
-        public void BeginReceiveFromV6BoundToSpecificV6_Success()
-        {
-            BeginReceiveFrom_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback);
-        }
+        public Task SendToV4IPEndPointToV4Host_Success() => DualModeSendTo_IPEndPointToHost_Success_Helper(IPAddress.Loopback, IPAddress.Loopback, false);
 
         [Fact]
-        public void BeginReceiveFromV6BoundToAnyV6_Success()
-        {
-            BeginReceiveFrom_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback);
-        }
+        public Task SendToV6IPEndPointToV6Host_Success() => DualModeSendTo_IPEndPointToHost_Success_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback, false);
 
         [Fact]
-        // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        // Also BeginReceiveFrom not supported on OSX
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void BeginReceiveFromV6BoundToSpecificV4_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                BeginReceiveFrom_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback, expectedToTimeout: true);
-            });
-        }
+        public Task SendToV4IPEndPointToDualHost_Success() => DualModeSendTo_IPEndPointToHost_Success_Helper(IPAddress.Loopback, IPAddress.IPv6Any, true);
 
         [Fact]
-        // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        // Also expected behavior is different on OSX and Linux (ArgumentException instead of TimeoutException)
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void BeginReceiveFromV4BoundToSpecificV6_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                BeginReceiveFrom_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback, expectedToTimeout: true);
-            });
-        }
+        public Task SendToV6IPEndPointToDualHost_Success() => DualModeSendTo_IPEndPointToHost_Success_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Any, true);
 
         [Fact]
-        // Binds to a specific port on 'connectTo' which on Unix may already be in use
-        // Also BeginReceiveFrom not supported on OSX
-        [PlatformSpecific(TestPlatforms.Windows)]
-        public void BeginReceiveFromV6BoundToAnyV4_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                BeginReceiveFrom_Helper(IPAddress.Any, IPAddress.IPv6Loopback, expectedToTimeout: true);
-            });
-        }
+        public Task SendToV4IPEndPointToV6Host_NotReceived() => DualModeSendTo_IPEndPointToHost_Failing_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback, false);
 
         [Fact]
-        public void BeginReceiveFromV4BoundToAnyV6_Success()
-        {
-            BeginReceiveFrom_Helper(IPAddress.IPv6Any, IPAddress.Loopback);
-        }
+        public Task SendToV6IPEndPointToV4Host_NotReceived() => DualModeSendTo_IPEndPointToHost_Failing_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback, false);
 
-        private void BeginReceiveFrom_Helper(IPAddress listenOn, IPAddress connectTo, bool expectedToTimeout = false)
+        private async Task DualModeSendTo_IPEndPointToHost_Success_Helper(IPAddress connectTo, IPAddress listenOn, bool dualModeServer)
         {
-            using (Socket serverSocket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                serverSocket.ReceiveTimeout = 500;
-                int port = serverSocket.BindToAnonymousPort(listenOn);
-
-                EndPoint receivedFrom = new IPEndPoint(connectTo, port);
-                IAsyncResult async = serverSocket.BeginReceiveFrom(new byte[1], 0, 1, SocketFlags.None, ref receivedFrom, null, null);
-
-                // Behavior difference from Desktop: receivedFrom will _not_ change during the synchronous phase.
+            using Socket client = new Socket(SocketType.Dgram, ProtocolType.Udp);
+            using Socket server = dualModeServer ?
+                new Socket(SocketType.Dgram, ProtocolType.Udp) :
+                new Socket(listenOn.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
+            int port = server.BindToAnonymousPort(listenOn);
 
-                // IPEndPoint remoteEndPoint = receivedFrom as IPEndPoint;
-                // Assert.Equal(AddressFamily.InterNetworkV6, remoteEndPoint.AddressFamily);
-                // Assert.Equal(connectTo.MapToIPv6(), remoteEndPoint.Address);
+            Task<int> receiveTask = server.ReceiveAsync(new byte[1]);
+            int sent = await SendToAsync(client, new byte[1], new IPEndPoint(connectTo, port)).WaitAsync(TestSettings.PassingTestTimeout);
+            Assert.Equal(1, sent);
 
-                SocketUdpClient client = new SocketUdpClient(_log, serverSocket, connectTo, port);
-                bool success = async.AsyncWaitHandle.WaitOne(expectedToTimeout ? TestSettings.FailingTestTimeout : TestSettings.PassingTestTimeout);
-                if (!success)
-                {
-                    throw new TimeoutException();
-                }
+            int received = await receiveTask.WaitAsync(TestSettings.PassingTestTimeout);
+            Assert.Equal(1, received);
+        }
 
-                receivedFrom = new IPEndPoint(connectTo, port);
-                int received = serverSocket.EndReceiveFrom(async, ref receivedFrom);
+        private async Task DualModeSendTo_IPEndPointToHost_Failing_Helper(IPAddress connectTo, IPAddress listenOn, bool dualModeServer)
+        {
+            using Socket client = new Socket(SocketType.Dgram, ProtocolType.Udp);
+            using Socket server = dualModeServer ?
+                    new Socket(SocketType.Dgram, ProtocolType.Udp) :
+                    new Socket(listenOn.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
+            int port = server.BindToAnonymousPort(listenOn);
 
-                Assert.Equal(1, received);
-                Assert.Equal<Type>(typeof(IPEndPoint), receivedFrom.GetType());
+            _ = SendToAsync(client, new byte[1], new IPEndPoint(connectTo, port)).WaitAsync(TestSettings.PassingTestTimeout);
+            await Assert.ThrowsAsync<TimeoutException>(() => server.ReceiveAsync(new byte[1]).WaitAsync(TestSettings.FailingTestTimeout));
+        }
+    }
 
-                IPEndPoint remoteEndPoint = receivedFrom as IPEndPoint;
-                Assert.Equal(AddressFamily.InterNetworkV6, remoteEndPoint.AddressFamily);
-                Assert.Equal(connectTo.MapToIPv6(), remoteEndPoint.Address);
-            }
+    [Trait("IPv4", "true")]
+    [Trait("IPv6", "true")]
+    [Collection(nameof(DisableParallelization))]
+    public class DualModeConnectionlessSendToSync : DualModeConnectionlessSendToBase<SocketHelperArraySync>
+    {
+        public DualModeConnectionlessSendToSync(ITestOutputHelper output) : base(output)
+        {
         }
+    }
 
-        #endregion ReceiveFrom Begin/End
+    [Trait("IPv4", "true")]
+    [Trait("IPv6", "true")]
+    [Collection(nameof(DisableParallelization))]
+    public class DualModeConnectionlessSendToApm : DualModeConnectionlessSendToBase<SocketHelperApm>
+    {
+        public DualModeConnectionlessSendToApm(ITestOutputHelper output) : base(output)
+        {
+        }
     }
 
-    [OuterLoop]
     [Trait("IPv4", "true")]
     [Trait("IPv6", "true")]
-    public class DualModeConnectionlessReceiveFromAsync : DualModeBase
+    [Collection(nameof(DisableParallelization))]
+    public class DualModeConnectionlessSendToEap : DualModeConnectionlessSendToBase<SocketHelperEap>
     {
-        #region ReceiveFrom Async/Event
+        public DualModeConnectionlessSendToEap(ITestOutputHelper output) : base(output)
+        {
+        }
+    }
 
-        [Fact] // Base case
-        // "The supplied EndPoint of AddressFamily InterNetwork is not valid for this Socket, use InterNetworkV6 instead."
-        public void Socket_ReceiveFromAsyncV4IPEndPointFromV4Client_Throws()
+    [Trait("IPv4", "true")]
+    [Trait("IPv6", "true")]
+    [Collection(nameof(DisableParallelization))]
+    public class DualModeConnectionlessSendToTask : DualModeConnectionlessSendToBase<SocketHelperTask>
+    {
+        public DualModeConnectionlessSendToTask(ITestOutputHelper output) : base(output)
         {
-            using (Socket socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp))
-            {
-                SocketAsyncEventArgs args = new SocketAsyncEventArgs();
-                args.RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, UnusedPort);
-                args.SetBuffer(new byte[1], 0, 1);
+        }
+    }
 
-                AssertExtensions.Throws<ArgumentException>("e", () =>
-                {
-                    socket.ReceiveFromAsync(args);
-                });
-            }
+    public abstract class DualModeConnectionlessReceiveFromBase<T> : SocketTestHelperBase<T> where T : SocketHelperBase, new()
+    {
+        protected DualModeConnectionlessReceiveFromBase(ITestOutputHelper output) : base(output)
+        {
         }
 
         [Fact] // Base case
-        // "The parameter remoteEP must not be of type DnsEndPoint."
-        public void Socket_ReceiveFromAsyncDnsEndPoint_Throws()
+        public async Task Socket_ReceiveFromV4IPEndPointFromV4Client_Throws()
         {
-            using (Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                int port = socket.BindToAnonymousPort(IPAddress.IPv6Loopback);
-                SocketAsyncEventArgs args = new SocketAsyncEventArgs();
-                args.RemoteEndPoint = new DnsEndPoint("localhost", port, AddressFamily.InterNetworkV6);
-                args.SetBuffer(new byte[1], 0, 1);
+            // "The supplied EndPoint of AddressFamily InterNetwork is not valid for this Socket, use InterNetworkV6 instead."
+            using Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp);
+            socket.DualMode = false;
 
-                AssertExtensions.Throws<ArgumentException>("remoteEP", () =>
-                {
-                    socket.ReceiveFromAsync(args);
-                });
-            }
+            EndPoint receivedFrom = new IPEndPoint(IPAddress.Loopback, DualModeBase.UnusedPort);
+            await Assert.ThrowsAsync<ArgumentException>(() => ReceiveFromAsync(socket, new byte[1], receivedFrom));
         }
 
-        [Fact]
-        public void ReceiveFromAsyncV4BoundToSpecificV4_Success()
+        [Fact] // Base case
+        public async Task Socket_ReceiveFromDnsEndPoint_Throws()
         {
-            ReceiveFromAsync_Helper(IPAddress.Loopback, IPAddress.Loopback);
+            // "The parameter remoteEP must not be of type DnsEndPoint."
+            using Socket socket = new Socket(SocketType.Dgram, ProtocolType.Udp);
+            
+            int port = socket.BindToAnonymousPort(IPAddress.IPv6Loopback);
+            EndPoint receivedFrom = new DnsEndPoint("localhost", port, AddressFamily.InterNetworkV6);
+            await AssertExtensions.ThrowsAsync<ArgumentException>("remoteEP", () => ReceiveFromAsync(socket, new byte[1], receivedFrom));
         }
 
         [Fact]
-        public void ReceiveFromAsyncV4BoundToAnyV4_Success()
-        {
-            ReceiveFromAsync_Helper(IPAddress.Any, IPAddress.Loopback);
-        }
+        public Task ReceiveFromV4BoundToSpecificV4_Success() => ReceiveFrom_Success_Helper(IPAddress.Loopback, IPAddress.Loopback);
 
         [Fact]
-        public void ReceiveFromAsyncV6BoundToSpecificV6_Success()
-        {
-            ReceiveFromAsync_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback);
-        }
+        public Task ReceiveFromV4BoundToAnyV4_Success() => ReceiveFrom_Success_Helper(IPAddress.Any, IPAddress.Loopback);
 
         [Fact]
-        public void ReceiveFromAsyncV6BoundToAnyV6_Success()
-        {
-            ReceiveFromAsync_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback);
-        }
+        public Task ReceiveFromV6BoundToSpecificV6_Success() => ReceiveFrom_Success_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback);
 
         [Fact]
-        public void ReceiveFromAsyncV6BoundToSpecificV4_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                ReceiveFromAsync_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback, expectedToTimeout: true);
-            });
-        }
+        public Task ReceiveFromV6BoundToAnyV6_Success() => ReceiveFrom_Success_Helper(IPAddress.IPv6Any, IPAddress.IPv6Loopback);
 
         [Fact]
-        public void ReceiveFromAsyncV4BoundToSpecificV6_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                ReceiveFromAsync_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback, expectedToTimeout: true);
-            });
-        }
+        public Task ReceiveFromV4BoundToAnyV6_Success() => ReceiveFrom_Success_Helper(IPAddress.IPv6Any, IPAddress.Loopback);
 
         [Fact]
-        public void ReceiveFromAsyncV6BoundToAnyV4_NotReceived()
-        {
-            Assert.Throws<TimeoutException>(() =>
-            {
-                ReceiveFromAsync_Helper(IPAddress.Any, IPAddress.IPv6Loopback, expectedToTimeout: true);
-            });
-        }
+        // Binds to a specific port on 'connectTo' which on Unix may already be in use
+        // Also ReceiveFrom not supported on OSX
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public Task ReceiveFromV6BoundToSpecificV4_NotReceived() => ReceiveFrom_Failure_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback);
+
+        [Fact]
+        // Binds to a specific port on 'connectTo' which on Unix may already be in use
+        // Also expected behavior is different on OSX and Linux (ArgumentException instead of SocketException)
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public Task ReceiveFromV4BoundToSpecificV6_NotReceived() => ReceiveFrom_Failure_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback);
 
         [Fact]
-        public void ReceiveFromAsyncV4BoundToAnyV6_Success()
+        // Binds to a specific port on 'connectTo' which on Unix may already be in use
+        // Also ReceiveFrom not supported on OSX
+        [PlatformSpecific(TestPlatforms.Windows)]
+        public Task ReceiveFromV6BoundToAnyV4_NotReceived() => ReceiveFrom_Failure_Helper(IPAddress.Any, IPAddress.IPv6Loopback);
+
+        protected async Task ReceiveFrom_Success_Helper(IPAddress listenOn, IPAddress connectTo)
         {
-            ReceiveFromAsync_Helper(IPAddress.IPv6Any, IPAddress.Loopback);
+            using Socket serverSocket = new Socket(SocketType.Dgram, ProtocolType.Udp);
+            int port = serverSocket.BindToAnonymousPort(listenOn);
+
+            Socket client = new Socket(connectTo.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
+            Task<int> sendTask = client.SendToAsync(new byte[1], new IPEndPoint(connectTo, port))
+                .WaitAsync(TestSettings.PassingTestTimeout);
+
+            var result = await ReceiveFromAsync(serverSocket, new byte[1], new IPEndPoint(connectTo, port))
+                .WaitAsync(TestSettings.PassingTestTimeout);
+
+            Assert.Equal(1, result.ReceivedBytes);
+            IPEndPoint remoteEndPoint = Assert.IsType<IPEndPoint>(result.RemoteEndPoint);
+            Assert.Equal(AddressFamily.InterNetworkV6, remoteEndPoint.AddressFamily);
+            Assert.Equal(connectTo.MapToIPv6(), remoteEndPoint.Address);
         }
 
-        private void ReceiveFromAsync_Helper(IPAddress listenOn, IPAddress connectTo, bool expectedToTimeout = false)
+        protected async Task ReceiveFrom_Failure_Helper(IPAddress listenOn, IPAddress connectTo)
         {
-            using (Socket serverSocket = new Socket(SocketType.Dgram, ProtocolType.Udp))
-            {
-                int port = serverSocket.BindToAnonymousPort(listenOn);
-
-                ManualResetEvent waitHandle = new ManualResetEvent(false);
+            using Socket serverSocket = new Socket(SocketType.Dgram, ProtocolType.Udp);
+            int port = serverSocket.BindToAnonymousPort(listenOn);
 
-                SocketAsyncEventArgs args = new SocketAsyncEventArgs();
-                args.RemoteEndPoint = new IPEndPoint(listenOn, port);
-                args.SetBuffer(new byte[1], 0, 1);
-                args.UserToken = waitHandle;
-                args.Completed += AsyncCompleted;
+            Socket client = new Socket(connectTo.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
+            _ = client.SendToAsync(new byte[1], new IPEndPoint(connectTo, port)).WaitAsync(TestSettings.PassingTestTimeout);
+            await Assert.ThrowsAsync<TimeoutException>(() => ReceiveFromAsync(serverSocket, new byte[1], new IPEndPoint(connectTo, port))
+                .WaitAsync(TestSettings.FailingTestTimeout));
+        }
+    }
 
-                bool async = serverSocket.ReceiveFromAsync(args);
-                SocketUdpClient client = new SocketUdpClient(_log, serverSocket, connectTo, port);
-                if (async && !waitHandle.WaitOne(expectedToTimeout ? TestSettings.FailingTestTimeout : TestSettings.PassingTestTimeout))
-                {
-                    throw new TimeoutException();
-                }
+    [Trait("IPv4", "true")]
+    [Trait("IPv6", "true")]
+    [Collection(nameof(DisableParallelization))]
+    public class DualModeConnectionlessReceiveFromSync : DualModeConnectionlessReceiveFromBase<SocketHelperArraySync>
+    {
+        public DualModeConnectionlessReceiveFromSync(ITestOutputHelper output) : base(output)
+        {
+        }
+    }
 
-                if (args.SocketError != SocketError.Success)
-                {
-                    throw new SocketException((int)args.SocketError);
-                }
+    [Trait("IPv4", "true")]
+    [Trait("IPv6", "true")]
+    [Collection(nameof(DisableParallelization))]
+    public class DualModeConnectionlessReceiveFromApm : DualModeConnectionlessReceiveFromBase<SocketHelperApm>
+    {
+        public DualModeConnectionlessReceiveFromApm(ITestOutputHelper output) : base(output)
+        {
+        }
+    }
 
-                Assert.Equal(1, args.BytesTransferred);
-                Assert.Equal<Type>(typeof(IPEndPoint), args.RemoteEndPoint.GetType());
-                IPEndPoint remoteEndPoint = args.RemoteEndPoint as IPEndPoint;
-                Assert.Equal(AddressFamily.InterNetworkV6, remoteEndPoint.AddressFamily);
-                Assert.Equal(connectTo.MapToIPv6(), remoteEndPoint.Address);
-            }
+    [Trait("IPv4", "true")]
+    [Trait("IPv6", "true")]
+    [Collection(nameof(DisableParallelization))]
+    public class DualModeConnectionlessReceiveFromEap : DualModeConnectionlessReceiveFromBase<SocketHelperEap>
+    {
+        public DualModeConnectionlessReceiveFromEap(ITestOutputHelper output) : base(output)
+        {
         }
+    }
 
-        #endregion ReceiveFrom Async/Event
+    [Trait("IPv4", "true")]
+    [Trait("IPv6", "true")]
+    [Collection(nameof(DisableParallelization))]
+    public class DualModeConnectionlessReceiveFromTask : DualModeConnectionlessReceiveFromBase<SocketHelperTask>
+    {
+        public DualModeConnectionlessReceiveFromTask(ITestOutputHelper output) : base(output)
+        {
+        }
     }
 
-    [OuterLoop]
     [Trait("IPv4", "true")]
     [Trait("IPv6", "true")]
+    [Collection(nameof(DisableParallelization))]
     public class DualModeConnectionlessReceiveMessageFrom : DualModeBase
     {
         [Fact]
@@ -2356,12 +1703,12 @@ namespace System.Net.Sockets.Tests
     public class DualModeBase
     {
         // Ports 8 and 8887 are unassigned as per https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt
-        protected const int UnusedPort = 8;
+        internal const int UnusedPort = 8;
         protected const int UnusedBindablePort = 8887;
 
         protected readonly ITestOutputHelper _log;
 
-        protected static IPAddress[] ValidIPv6Loopbacks = new IPAddress[] {
+        internal static IPAddress[] ValidIPv6Loopbacks = new IPAddress[] {
             new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1 }, 0),  // ::127.0.0.1
             IPAddress.Loopback.MapToIPv6(),                                                     // ::ffff:127.0.0.1
             IPAddress.IPv6Loopback                                                              // ::1
@@ -2442,10 +1789,11 @@ namespace System.Net.Sockets.Tests
         protected class SocketServer : IDisposable
         {
             private readonly ITestOutputHelper _output;
-            private Socket _server;
             private Socket _acceptedSocket;
             private EventWaitHandle _waitHandle = new AutoResetEvent(false);
 
+            public Socket Socket { get; }
+
             public EventWaitHandle WaitHandle
             {
                 get { return _waitHandle; }
@@ -2457,24 +1805,27 @@ namespace System.Net.Sockets.Tests
 
                 if (dualMode)
                 {
-                    _server = new Socket(SocketType.Stream, ProtocolType.Tcp);
+                    Socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                 }
                 else
                 {
-                    _server = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+                    Socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                 }
 
-                port = _server.BindToAnonymousPort(address);
-                _server.Listen(1);
+                port = Socket.BindToAnonymousPort(address);
+                Socket.Listen(1);   
+            }
 
-                IPAddress remoteAddress = address.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any;
+            public void Start()
+            {
+                IPAddress remoteAddress = Socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any;
                 EndPoint remote = new IPEndPoint(remoteAddress, 0);
                 SocketAsyncEventArgs e = new SocketAsyncEventArgs();
                 e.RemoteEndPoint = remote;
                 e.Completed += new EventHandler<SocketAsyncEventArgs>(Accepted);
                 e.UserToken = _waitHandle;
 
-                _server.AcceptAsync(e);
+                Socket.AcceptAsync(e);
             }
 
             private void Accepted(object sender, SocketAsyncEventArgs e)
@@ -2493,7 +1844,7 @@ namespace System.Net.Sockets.Tests
             {
                 try
                 {
-                    _server.Dispose();
+                    Socket.Dispose();
                     if (_acceptedSocket != null)
                         _acceptedSocket.Dispose();
                 }
@@ -2501,77 +1852,6 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        protected class SocketClient
-        {
-            private IPAddress _connectTo;
-            private Socket _serverSocket;
-            private int _port;
-            private readonly ITestOutputHelper _output;
-
-            private EventWaitHandle _waitHandle = new AutoResetEvent(false);
-            public EventWaitHandle WaitHandle
-            {
-                get { return _waitHandle; }
-            }
-
-            public SocketError Error
-            {
-                get;
-                private set;
-            }
-
-            public SocketClient(ITestOutputHelper output, Socket serverSocket, IPAddress connectTo, int port)
-            {
-                _output = output;
-                _connectTo = connectTo;
-                _serverSocket = serverSocket;
-                _port = port;
-                Error = SocketError.Success;
-
-                Task.Run(() => ConnectClient(null));
-            }
-
-            private void ConnectClient(object state)
-            {
-                try
-                {
-                    Socket socket = new Socket(_connectTo.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-
-                    SocketAsyncEventArgs e = new SocketAsyncEventArgs();
-                    e.Completed += new EventHandler<SocketAsyncEventArgs>(Connected);
-                    e.RemoteEndPoint = new IPEndPoint(_connectTo, _port);
-                    e.UserToken = _waitHandle;
-
-                    if (!socket.ConnectAsync(e))
-                    {
-                        Connected(socket, e);
-                    }
-                }
-                catch (SocketException ex)
-                {
-                    Error = ex.SocketErrorCode;
-                    Thread.Sleep(TestSettings.FailingTestTimeout); // Give the other end a chance to call Accept().
-                    _serverSocket.Dispose(); // Cancels the test
-                    _waitHandle.Set();
-                }
-            }
-            private void Connected(object sender, SocketAsyncEventArgs e)
-            {
-                EventWaitHandle handle = (EventWaitHandle)e.UserToken;
-                _output.WriteLine(
-                    "Connected: " + e.GetHashCode() + " SocketAsyncEventArgs with manual event " +
-                    handle.GetHashCode() + " error: " + e.SocketError);
-
-                Error = e.SocketError;
-                if (Error != SocketError.Success)
-                {
-                    Thread.Sleep(TestSettings.FailingTestTimeout); // Give the other end a chance to call Accept().
-                    _serverSocket.Dispose(); // Cancels the test
-                }
-                handle.Set();
-            }
-        }
-
         protected class SocketUdpServer : IDisposable
         {
             private readonly ITestOutputHelper _output;
index b94fe8c..e096d53 100644 (file)
@@ -892,14 +892,14 @@ namespace System.Net.Sockets.Tests
                     {
                         b.SignalAndWait();
                         client.Dispose();
-                    }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+                    }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default).WaitAsync(TestSettings.PassingTestTimeout);
 
                     Task send = Task.Factory.StartNew(() =>
                     {
                         SendAsync(server, new ArraySegment<byte>(new byte[1])).GetAwaiter().GetResult();
                         b.SignalAndWait();
                         ReceiveAsync(client, new ArraySegment<byte>(new byte[1])).GetAwaiter().GetResult();
-                    }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+                    }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default).WaitAsync(TestSettings.PassingTestTimeout);
 
                     await dispose;
                     Exception error = await Record.ExceptionAsync(() => send);