Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 58 additions & 33 deletions EchoTcpServer/Program.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// This program was designed for test purposes only
/// Not for a review
/// </summary>
public interface ITcpListenerWrapper

Check warning on line 9 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Declare types in namespaces

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlr&open=AZ6FfknB0yBOVYkmgGlr&pullRequest=7

Check warning on line 9 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move 'ITcpListenerWrapper' into a named namespace.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGli&open=AZ6FfknB0yBOVYkmgGli&pullRequest=7
{
void Start();
void Stop();
Task<ITcpClientWrapper> AcceptTcpClientAsync();
}

public interface ITcpClientWrapper : IDisposable

Check warning on line 16 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move 'ITcpClientWrapper' into a named namespace.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlj&open=AZ6FfknB0yBOVYkmgGlj&pullRequest=7

Check warning on line 16 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Declare types in namespaces

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGls&open=AZ6FfknB0yBOVYkmgGls&pullRequest=7
{
Stream GetStream();
void Close();
}

public class TcpListenerWrapper : ITcpListenerWrapper

Check warning on line 22 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move 'TcpListenerWrapper' into a named namespace.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlm&open=AZ6FfknB0yBOVYkmgGlm&pullRequest=7

Check warning on line 22 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Declare types in namespaces

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlu&open=AZ6FfknB0yBOVYkmgGlu&pullRequest=7
{
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<ITcpClientWrapper> AcceptTcpClientAsync() => new TcpClientWrapper(await _listener.AcceptTcpClientAsync());
}

public class TcpClientWrapper : ITcpClientWrapper

Check warning on line 31 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move 'TcpClientWrapper' into a named namespace.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGll&open=AZ6FfknB0yBOVYkmgGll&pullRequest=7

Check warning on line 31 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Declare types in namespaces

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlt&open=AZ6FfknB0yBOVYkmgGlt&pullRequest=7

Check warning on line 31 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Fix this implementation of 'IDisposable' to conform to the dispose pattern.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGln&open=AZ6FfknB0yBOVYkmgGln&pullRequest=7
{
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();

Check warning on line 37 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Change TcpClientWrapper.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlw&open=AZ6FfknB0yBOVYkmgGlw&pullRequest=7
}

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)

Check warning on line 73 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Member 'HandleClientAsync' does not access instance data and can be marked as static

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlx&open=AZ6FfknB0yBOVYkmgGlx&pullRequest=7

Check warning on line 73 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make 'HandleClientAsync' a static method.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlq&open=AZ6FfknB0yBOVYkmgGlq&pullRequest=7
{
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))
Expand All @@ -82,17 +107,20 @@
_cancellationTokenSource.Dispose();
Console.WriteLine("Server stopped.");
}
}

public class Program

Check warning on line 112 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a 'protected' constructor or the 'static' keyword to the class declaration.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlk&open=AZ6FfknB0yBOVYkmgGlk&pullRequest=7

Check warning on line 112 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Move 'Program' into a named namespace.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlo&open=AZ6FfknB0yBOVYkmgGlo&pullRequest=7

Check warning on line 112 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Declare types in namespaces

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlv&open=AZ6FfknB0yBOVYkmgGlv&pullRequest=7
{
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))
{
Expand All @@ -101,9 +129,8 @@

Console.WriteLine("Press 'q' to quit...");
while (Console.ReadKey(intercept: true).Key != ConsoleKey.Q)
{
// Just wait until 'q' is pressed
}

Check warning on line 133 in EchoTcpServer/Program.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Either remove or fill this block of code.

See more on https://sonarcloud.io/project/issues?id=friezze_ReengineeringCourse&issues=AZ6FfknB0yBOVYkmgGlp&open=AZ6FfknB0yBOVYkmgGlp&pullRequest=7

sender.StopSending();
server.Stop();
Expand All @@ -112,7 +139,6 @@
}
}


public class UdpTimedSender : IDisposable
{
private readonly string _host;
Expand Down Expand Up @@ -141,7 +167,6 @@
{
try
{
//dummy data
Random rnd = new Random();
byte[] samples = new byte[1024];
rnd.NextBytes(samples);
Expand Down
83 changes: 83 additions & 0 deletions EchoTcpServerTests/EchoServerTests.cs
Original file line number Diff line number Diff line change
@@ -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<ITcpListenerWrapper> _listenerMock;
private Mock<ITcpClientWrapper> _clientMock;
private EchoServer _server;

[SetUp]
public void Setup()
{
_listenerMock = new Mock<ITcpListenerWrapper>();
_clientMock = new Mock<ITcpClientWrapper>();
_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<ITcpClientWrapper>();
_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);
}
}
}
2 changes: 2 additions & 0 deletions NetSdrClientApp/Messages/NetSdrMessageHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace NetSdrClientApp.Messages
{
//TODO: analyze possible use of [StructLayout] for better performance and readability

Check warning on line 10 in NetSdrClientApp/Messages/NetSdrMessageHelper.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Complete the task associated to this 'TODO' comment.
public static class NetSdrMessageHelper
{
private const short _maxMessageLength = 8191;
Expand All @@ -16,6 +16,8 @@
private const short _msgControlItemLength = 2; //2 byte, 16 bit
private const short _msgSequenceNumberLength = 2; //2 byte, 16 bit



public enum MsgTypes
{
SetControlItem,
Expand Down Expand Up @@ -106,12 +108,12 @@
return success;
}

public static IEnumerable<int> GetSamples(ushort sampleSize, byte[] body)

Check warning on line 111 in NetSdrClientApp/Messages/NetSdrMessageHelper.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Split this method into two, one handling parameters check and the other handling the iterator.
{
sampleSize /= 8; //to bytes
if (sampleSize > 4)
{
throw new ArgumentOutOfRangeException();

Check warning on line 116 in NetSdrClientApp/Messages/NetSdrMessageHelper.cs

View workflow job for this annotation

GitHub Actions / Sonar Check

Use a constructor overloads that allows a more meaningful exception message to be provided.
}

var bodyEnumerable = body as IEnumerable<byte>;
Expand Down
24 changes: 24 additions & 0 deletions NetSdrClientAppTests/ArchitectureTests.cs
Original file line number Diff line number Diff line change
@@ -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.");
}
}
}
1 change: 1 addition & 0 deletions NetSdrClientAppTests/NetSdrClientAppTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NetArchTest.Rules" Version="1.3.2" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
Expand Down
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)



Expand Down
Binary file added image-10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image-11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image-7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image-8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image-9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading