using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Net;
using System.Threading;
using Hazel.Udp;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace Hazel.UnitTests
{
[TestClass]
public class UdpConnectionTests
{
[TestMethod]
public void ServerDisposeDisconnectsTest()
{
IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 4296);
bool serverConnected = false;
bool serverDisconnected = false;
bool clientDisconnected = false;
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), ep))
{
listener.NewConnection += (evt) =>
{
serverConnected = true;
evt.Connection.Disconnected += (o, et) => serverDisconnected = true;
};
connection.Disconnected += (o, evt) => clientDisconnected = true;
listener.Start();
connection.Connect();
Thread.Sleep(100); // Gotta wait for the server to set up the events.
listener.Dispose();
Thread.Sleep(100);
Assert.IsTrue(serverConnected);
Assert.IsTrue(clientDisconnected);
Assert.IsFalse(serverDisconnected);
}
}
[TestMethod]
public void ClientServerDisposeDisconnectsTest()
{
IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 4296);
bool serverConnected = false;
bool serverDisconnected = false;
bool clientDisconnected = false;
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), ep))
{
listener.NewConnection += (evt) =>
{
serverConnected = true;
evt.Connection.Disconnected += (o, et) => serverDisconnected = true;
};
connection.Disconnected += (o, et) => clientDisconnected = true;
listener.Start();
connection.Connect();
Thread.Sleep(100); // Gotta wait for the server to set up the events.
connection.Dispose();
Thread.Sleep(100);
Assert.IsTrue(serverConnected);
Assert.IsTrue(serverDisconnected);
Assert.IsFalse(clientDisconnected);
}
}
///
/// Tests the fields on UdpConnection.
///
[TestMethod]
public void UdpFieldTest()
{
IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 4296);
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), ep))
{
listener.Start();
connection.Connect();
//Connection fields
Assert.AreEqual(ep, connection.EndPoint);
//UdpConnection fields
Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 4296), connection.EndPoint);
Assert.AreEqual(1, connection.Statistics.DataBytesSent);
Assert.AreEqual(0, connection.Statistics.DataBytesReceived);
}
}
[TestMethod]
public void UdpHandshakeTest()
{
byte[] TestData = new byte[] { 1, 2, 3, 4, 5, 6 };
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
listener.Start();
MessageReader output = null;
listener.NewConnection += delegate (NewConnectionEventArgs e)
{
output = e.HandshakeData.Duplicate();
};
connection.Connect(TestData);
Thread.Sleep(10);
for (int i = 0; i < TestData.Length; ++i)
{
Assert.AreEqual(TestData[i], output.ReadByte());
}
}
}
[TestMethod]
public void UdpUnreliableMessageSendTest()
{
byte[] TestData = new byte[] { 1, 2, 3, 4, 5, 6 };
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
MessageReader output = null;
listener.NewConnection += delegate (NewConnectionEventArgs e)
{
e.Connection.DataReceived += delegate (DataReceivedEventArgs evt)
{
output = evt.Message;
};
};
listener.Start();
connection.Connect();
for (int i = 0; i < 4; ++i)
{
var msg = MessageWriter.Get(SendOption.None);
msg.Write(TestData);
connection.Send(msg);
msg.Recycle();
}
Thread.Sleep(10);
for (int i = 0; i < TestData.Length; ++i)
{
Assert.AreEqual(TestData[i], output.ReadByte());
}
}
}
///
/// Tests IPv4 connectivity.
///
[TestMethod]
public void UdpIPv4ConnectionTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
listener.Start();
connection.Connect();
}
}
///
/// Tests dual mode connectivity.
///
[TestMethod]
public void MixedConnectionTest()
{
using (UdpConnectionListener listener2 = new UdpConnectionListener(new IPEndPoint(IPAddress.IPv6Any, 4296), IPMode.IPv6))
{
listener2.Start();
listener2.NewConnection += (evt) =>
{
Console.WriteLine("v6 connection: " + ((NetworkConnection)evt.Connection).GetIP4Address());
};
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4296)))
{
connection.Connect();
Assert.AreEqual(ConnectionState.Connected, connection.State);
}
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.IPv6Loopback, 4296), IPMode.IPv6))
{
connection.Connect();
Assert.AreEqual(ConnectionState.Connected, connection.State);
}
}
}
///
/// Tests IPv4 resilience to non-hello connections.
///
[TestMethod]
public void FalseConnectionTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
int connects = 0;
listener.NewConnection += (obj) =>
{
Interlocked.Increment(ref connects);
};
listener.Start();
socket.Bind(new IPEndPoint(IPAddress.Any, 0));
var bytes = new byte[2];
bytes[0] = (byte)32;
for (int i = 0; i < 10; ++i)
{
socket.SendTo(bytes, new IPEndPoint(IPAddress.Loopback, 4296));
}
Thread.Sleep(500);
Assert.AreEqual(0, connects);
}
}
///
/// Tests IPv4 resilience to multiple hellos.
///
[TestMethod]
public void ConnectLikeAJerkTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
int connects = 0;
listener.NewConnection += (obj) =>
{
Interlocked.Increment(ref connects);
};
listener.Start();
socket.Bind(new IPEndPoint(IPAddress.Any, 0));
var bytes = new byte[2];
bytes[0] = (byte)UdpSendOption.Hello;
for (int i = 0; i < 10; ++i)
{
socket.SendTo(bytes, new IPEndPoint(IPAddress.Loopback, 4296));
}
Thread.Sleep(500);
Assert.AreEqual(1, connects);
}
}
///
/// Tests dual mode connectivity.
///
[TestMethod]
public void UdpIPv6ConnectionTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.IPv6Any, 4296), IPMode.IPv6))
{
listener.Start();
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4296), IPMode.IPv6))
{
connection.Connect();
}
}
}
///
/// Tests server to client unreliable communication on the UdpConnection.
///
[TestMethod]
public void UdpUnreliableServerToClientTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
TestHelper.RunServerToClientTest(listener, connection, 10, SendOption.None);
}
}
///
/// Tests server to client reliable communication on the UdpConnection.
///
[TestMethod]
public void UdpReliableServerToClientTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
TestHelper.RunServerToClientTest(listener, connection, 10, SendOption.Reliable);
}
}
///
/// Tests server to client unreliable communication on the UdpConnection.
///
[TestMethod]
public void UdpUnreliableClientToServerTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
TestHelper.RunClientToServerTest(listener, connection, 10, SendOption.None);
}
}
///
/// Tests server to client reliable communication on the UdpConnection.
///
[TestMethod]
public void UdpReliableClientToServerTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
TestHelper.RunClientToServerTest(listener, connection, 10, SendOption.Reliable);
}
}
///
/// Tests the keepalive functionality from the client,
///
[TestMethod]
public void PingDisconnectClientTest()
{
#if DEBUG
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
listener.Start();
connection.Connect();
// After connecting, quietly stop responding to all messages to fake connection loss.
Thread.Sleep(10);
listener.TestDropRate = 1;
connection.KeepAliveInterval = 100;
Thread.Sleep(1050); //Enough time for ~10 keep alive packets
Assert.AreEqual(ConnectionState.NotConnected, connection.State);
Assert.AreEqual(3 * connection.MissingPingsUntilDisconnect + 4, connection.Statistics.TotalBytesSent); // + 4 for connecting overhead
}
#else
Assert.Inconclusive("Only works in DEBUG");
#endif
}
///
/// Tests the keepalive functionality from the client,
///
[TestMethod]
public void KeepAliveClientTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
listener.Start();
connection.Connect();
connection.KeepAliveInterval = 100;
Thread.Sleep(1050); //Enough time for ~10 keep alive packets
Assert.AreEqual(ConnectionState.Connected, connection.State);
Assert.IsTrue(
connection.Statistics.TotalBytesSent >= 30 &&
connection.Statistics.TotalBytesSent <= 50,
"Sent: " + connection.Statistics.TotalBytesSent
);
}
}
///
/// Tests the keepalive functionality from the client,
///
[TestMethod]
public void KeepAliveServerTest()
{
ManualResetEvent mutex = new ManualResetEvent(false);
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
UdpConnection client = null;
listener.NewConnection += delegate (NewConnectionEventArgs args)
{
client = (UdpConnection)args.Connection;
client.KeepAliveInterval = 100;
Thread.Sleep(1050); //Enough time for ~10 keep alive packets
mutex.Set();
};
listener.Start();
connection.Connect();
mutex.WaitOne();
Assert.AreEqual(ConnectionState.Connected, client.State);
Assert.IsTrue(
client.Statistics.TotalBytesSent >= 27 &&
client.Statistics.TotalBytesSent <= 50,
"Sent: " + client.Statistics.TotalBytesSent
);
}
}
///
/// Tests disconnection from the client.
///
[TestMethod]
public void ClientDisconnectTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
TestHelper.RunClientDisconnectTest(listener, connection);
}
}
///
/// Test that a disconnect is sent when the client is disposed.
///
public void ClientDisconnectOnDisposeTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
TestHelper.RunClientDisconnectOnDisposeTest(listener, connection);
}
}
///
/// Tests disconnection from the server.
///
[TestMethod]
public void ServerDisconnectTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
TestHelper.RunServerDisconnectTest(listener, connection);
}
}
///
/// Tests disconnection from the server.
///
[TestMethod]
public void ServerExtraDataDisconnectTest()
{
using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
using (UdpConnection connection = new UdpClientConnection(new TestLogger("Client"), new IPEndPoint(IPAddress.Loopback, 4296)))
{
string received = null;
ManualResetEvent mutex = new ManualResetEvent(false);
connection.Disconnected += delegate (object sender, DisconnectedEventArgs args)
{
// We don't own the message, we have to read the string now
received = args.Message.ReadString();
mutex.Set();
};
listener.NewConnection += delegate (NewConnectionEventArgs args)
{
// As it turns out, the UdpConnectionListener can have an issue on loopback where the disconnect can happen before the hello confirm
// Tossing it on a different thread makes this test more reliable. Perhaps something to think about elsewhere though.
Task.Run(async () =>
{
await Task.Delay(1);
MessageWriter writer = MessageWriter.Get(SendOption.None);
writer.Write("Goodbye");
args.Connection.Disconnect("Testing", writer);
});
};
listener.Start();
connection.Connect();
mutex.WaitOne();
Assert.IsNotNull(received);
Assert.AreEqual("Goodbye", received);
}
}
}
}