diff --git a/EchoTcpServer/Program.cs b/EchoTcpServer/Program.cs index 5966c57..7116e7e 100644 --- a/EchoTcpServer/Program.cs +++ b/EchoTcpServer/Program.cs @@ -1,66 +1,91 @@ using System; +using System.IO; +using System.Linq; using System.Net; using System.Net.Sockets; -using System.Text; using System.Threading; using System.Threading.Tasks; -/// -/// This program was designed for test purposes only -/// Not for a review -/// +public interface ITcpListenerWrapper +{ + void Start(); + void Stop(); + Task AcceptTcpClientAsync(); +} + +public interface ITcpClientWrapper : IDisposable +{ + Stream GetStream(); + void Close(); +} + +public class TcpListenerWrapper : ITcpListenerWrapper +{ + private readonly TcpListener _listener; + public TcpListenerWrapper(IPAddress localaddr, int port) { _listener = new TcpListener(localaddr, port); } + public void Start() => _listener.Start(); + public void Stop() => _listener.Stop(); + public async Task AcceptTcpClientAsync() => new TcpClientWrapper(await _listener.AcceptTcpClientAsync()); +} + +public class TcpClientWrapper : ITcpClientWrapper +{ + private readonly TcpClient _client; + public TcpClientWrapper(TcpClient client) { _client = client; } + public Stream GetStream() => _client.GetStream(); + public void Close() => _client.Close(); + public void Dispose() => _client.Dispose(); +} + public class EchoServer { - private readonly int _port; - private TcpListener _listener; + private readonly ITcpListenerWrapper _listener; private CancellationTokenSource _cancellationTokenSource; - - public EchoServer(int port) + public EchoServer(ITcpListenerWrapper listener) { - _port = port; + _listener = listener; _cancellationTokenSource = new CancellationTokenSource(); } public async Task StartAsync() { - _listener = new TcpListener(IPAddress.Any, _port); _listener.Start(); - Console.WriteLine($"Server started on port {_port}."); + Console.WriteLine("Server started."); while (!_cancellationTokenSource.Token.IsCancellationRequested) { try { - TcpClient client = await _listener.AcceptTcpClientAsync(); + ITcpClientWrapper client = await _listener.AcceptTcpClientAsync(); Console.WriteLine("Client connected."); _ = Task.Run(() => HandleClientAsync(client, _cancellationTokenSource.Token)); } - catch (ObjectDisposedException) + catch (Exception ex) when (ex is ObjectDisposedException || ex is OperationCanceledException) { - // Listener has been closed break; } } - Console.WriteLine("Server shutdown."); } - private async Task HandleClientAsync(TcpClient client, CancellationToken token) + public async Task HandleClientAsync(ITcpClientWrapper client, CancellationToken token) { - using (NetworkStream stream = client.GetStream()) + using (client) { try { - byte[] buffer = new byte[8192]; - int bytesRead; - - while (!token.IsCancellationRequested && (bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0) + using (Stream stream = client.GetStream()) { - // Echo back the received message - await stream.WriteAsync(buffer, 0, bytesRead, token); - Console.WriteLine($"Echoed {bytesRead} bytes to the client."); + byte[] buffer = new byte[8192]; + int bytesRead; + + while (!token.IsCancellationRequested && (bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0) + { + await stream.WriteAsync(buffer, 0, bytesRead, token); + Console.WriteLine($"Echoed {bytesRead} bytes to the client."); + } } } catch (Exception ex) when (!(ex is OperationCanceledException)) @@ -82,17 +107,20 @@ public void Stop() _cancellationTokenSource.Dispose(); Console.WriteLine("Server stopped."); } +} +public class Program +{ public static async Task Main(string[] args) { - EchoServer server = new EchoServer(5000); + var listener = new TcpListenerWrapper(IPAddress.Any, 5000); + EchoServer server = new EchoServer(listener); - // Start the server in a separate task _ = Task.Run(() => server.StartAsync()); - string host = "127.0.0.1"; // Target IP - int port = 60000; // Target Port - int intervalMilliseconds = 5000; // Send every 3 seconds + string host = "127.0.0.1"; + int port = 60000; + int intervalMilliseconds = 5000; using (var sender = new UdpTimedSender(host, port)) { @@ -102,7 +130,6 @@ public static async Task Main(string[] args) Console.WriteLine("Press 'q' to quit..."); while (Console.ReadKey(intercept: true).Key != ConsoleKey.Q) { - // Just wait until 'q' is pressed } sender.StopSending(); @@ -112,7 +139,6 @@ public static async Task Main(string[] args) } } - public class UdpTimedSender : IDisposable { private readonly string _host; @@ -141,7 +167,6 @@ private void SendMessageCallback(object state) { try { - //dummy data Random rnd = new Random(); byte[] samples = new byte[1024]; rnd.NextBytes(samples); diff --git a/EchoTcpServerTests/EchoServerTests.cs b/EchoTcpServerTests/EchoServerTests.cs new file mode 100644 index 0000000..ede370b --- /dev/null +++ b/EchoTcpServerTests/EchoServerTests.cs @@ -0,0 +1,83 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Moq; +using NUnit.Framework; + +namespace EchoTcpServerTests +{ + public class EchoServerTests + { + private Mock _listenerMock; + private Mock _clientMock; + private EchoServer _server; + + [SetUp] + public void Setup() + { + _listenerMock = new Mock(); + _clientMock = new Mock(); + _server = new EchoServer(_listenerMock.Object); + } + + [Test] + public async Task HandleClientAsync_EchoesData_Correctly() + { + // Arrange + byte[] inputData = new byte[] { 1, 2, 3, 4, 5 }; + var memoryStream = new MemoryStream(); + memoryStream.Write(inputData, 0, inputData.Length); + memoryStream.Position = 0; + + _clientMock.Setup(c => c.GetStream()).Returns(memoryStream); + var cts = new CancellationTokenSource(); + + // Act + await _server.HandleClientAsync(_clientMock.Object, cts.Token); + + // Assert + _clientMock.Verify(c => c.GetStream(), Times.Once); + _clientMock.Verify(c => c.Close(), Times.Once); + + Assert.That(memoryStream.Length, Is.EqualTo(inputData.Length * 2)); + + memoryStream.Position = inputData.Length; + byte[] echoedData = new byte[inputData.Length]; + memoryStream.Read(echoedData, 0, echoedData.Length); + + Assert.That(echoedData, Is.EqualTo(inputData)); + } + + [Test] + public void Stop_CancelsTokenAndStopsListener() + { + // Act + _server.Stop(); + + // Assert + _listenerMock.Verify(l => l.Stop(), Times.Once); + } + + [Test] + public async Task StartAsync_AcceptsClients_UntilCancelled() + { + // Arrange + var tcs = new TaskCompletionSource(); + _listenerMock.SetupSequence(l => l.AcceptTcpClientAsync()) + .ReturnsAsync(_clientMock.Object) + .Returns(tcs.Task); + + // Act + var startTask = _server.StartAsync(); + await Task.Delay(50); + + _server.Stop(); + tcs.SetCanceled(); + + // Assert + _listenerMock.Verify(l => l.Start(), Times.Once); + _listenerMock.Verify(l => l.AcceptTcpClientAsync(), Times.AtLeastOnce); + } + } +} \ No newline at end of file diff --git a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs index 0d69b4d..438dfaf 100644 --- a/NetSdrClientApp/Messages/NetSdrMessageHelper.cs +++ b/NetSdrClientApp/Messages/NetSdrMessageHelper.cs @@ -16,6 +16,8 @@ public static class NetSdrMessageHelper private const short _msgControlItemLength = 2; //2 byte, 16 bit private const short _msgSequenceNumberLength = 2; //2 byte, 16 bit + + public enum MsgTypes { SetControlItem, diff --git a/NetSdrClientAppTests/ArchitectureTests.cs b/NetSdrClientAppTests/ArchitectureTests.cs new file mode 100644 index 0000000..dec06fc --- /dev/null +++ b/NetSdrClientAppTests/ArchitectureTests.cs @@ -0,0 +1,24 @@ +using NetArchTest.Rules; +using NUnit.Framework; +using NetSdrClientApp.Networking; + +namespace NetSdrClientAppTests +{ + public class ArchitectureTests + { + [Test] + public void Messages_ShouldNotDependOn_Networking() + { + // Arrange + var result = Types.InAssembly(typeof(TcpClientWrapper).Assembly) + .That() + .ResideInNamespace("NetSdrClientApp.Messages") + .ShouldNot() + .HaveDependencyOn("NetSdrClientApp.Networking") + .GetResult(); + + // Assert + Assert.That(result.IsSuccessful, Is.True, "Messages namespace has a prohibited dependency on Networking."); + } + } +} \ No newline at end of file diff --git a/NetSdrClientAppTests/NetSdrClientAppTests.csproj b/NetSdrClientAppTests/NetSdrClientAppTests.csproj index eb99533..728eebf 100644 --- a/NetSdrClientAppTests/NetSdrClientAppTests.csproj +++ b/NetSdrClientAppTests/NetSdrClientAppTests.csproj @@ -17,6 +17,7 @@ + diff --git a/README.md b/README.md index 63ef847..3b83954 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,31 @@ https://github.com/friezze/ReengineeringCourse/commit/2534516c319a38d4aecd5d70a4 Може здатися дивним що їх стало більше але скоріше за все це тому що пофіксився білдер через зміни в які я лив в мейн і мерджив в гілку але сонар аналізував пуши в мейн а тік злиття. Але у будьякому випадку 8 фіксед. # 3 +До а якщо бути точним одразу після включення ковередж +![alt text](image-5.png) +Після(пушу тестів нових) +![alt text](image-6.png) + +# 4 До + ![alt text](image-4.png) -Після + +Після + +![alt text](image-7.png) + +Важливо зауважити що завдяки зменьшенню дублікатів піднявся і тест коверед бо коду стало банально меньше + +![alt text](image-8.png) + +# 5 + +Це так виглядає після додання порушення прав архітектури і самого порушення. +![alt text](image-9.png) + + +![alt text](image-10.png) diff --git a/image-10.png b/image-10.png new file mode 100644 index 0000000..dd73204 Binary files /dev/null and b/image-10.png differ diff --git a/image-11.png b/image-11.png new file mode 100644 index 0000000..d0bc53d Binary files /dev/null and b/image-11.png differ diff --git a/image-4.png b/image-4.png new file mode 100644 index 0000000..8bb1a03 Binary files /dev/null and b/image-4.png differ diff --git a/image-7.png b/image-7.png new file mode 100644 index 0000000..e21f162 Binary files /dev/null and b/image-7.png differ diff --git a/image-8.png b/image-8.png new file mode 100644 index 0000000..1b32640 Binary files /dev/null and b/image-8.png differ diff --git a/image-9.png b/image-9.png new file mode 100644 index 0000000..c41bc24 Binary files /dev/null and b/image-9.png differ