Skip to content
Merged

Lab6 #10

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
3 changes: 3 additions & 0 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ jobs:
/d:sonar.cpd.cs.minimumTokens=40 `
/d:sonar.cpd.cs.minimumLines=5 `
/d:sonar.exclusions=**/bin/**,**/obj/**,**/sonarcloud.yml `
/d:sonar.tests=NetSdrClientAppTests,EchoServerTests `
/d:sonar.test.inclusions=**/*Tests.cs,**/*Test.cs `
/d:sonar.coverage.exclusions=**/*Tests/**,**/*Test/** `
/d:sonar.qualitygate.wait=true
shell: pwsh
# 2) BUILD & TEST
Expand Down
33 changes: 33 additions & 0 deletions EchoServerTests/ClientWrapperFactoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using EchoServer;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace EchoServerTests
{
[TestFixture]
public class ClientWrapperFactoryTests
{
[Test]
public void CreateClientWrapper_ShouldReturnWrapper()
{
// Arrange
var factory = new ClientWrapperFactory();
var tcpClient = new TcpClient();

// Act
var wrapper = factory.CreateClientWrapper(tcpClient);

// Assert
Assert.IsNotNull(wrapper);
Assert.IsInstanceOf<TcpClientWrapper>(wrapper);

// Cleanup
wrapper.Dispose();
}
}
}
23 changes: 23 additions & 0 deletions EchoServerTests/ConsoleLoggerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EchoServer;

namespace EchoServerTests
{
[TestFixture]
public class ConsoleLoggerTests
{
[Test]
public void Log_ShouldNotThrow()
{
// Arrange
var logger = new ConsoleLogger();

// Act & Assert
Assert.DoesNotThrow(() => logger.Log("Test message"));
}
}
}
248 changes: 248 additions & 0 deletions EchoServerTests/EchoServerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
using NUnit.Framework;
using Moq;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using EchoServer;
using EchoServer.Interfaces;

namespace EchoServerTests
{
[TestFixture]
public class EchoServerTests
{
private Mock<ILogger> _mockLogger;
private Mock<IClientWrapperFactory> _mockClientFactory;
private Mock<ITcpListenerWrapper> _mockListener;
private CancellationTokenSource _cts;

[SetUp]
public void SetUp()
{
_mockLogger = new Mock<ILogger>();
_mockClientFactory = new Mock<IClientWrapperFactory>();
_mockListener = new Mock<ITcpListenerWrapper>();
_cts = new CancellationTokenSource();
}

[TearDown]
public void TearDown()
{
_cts?.Dispose();
}

[Test]
public void Constructor_ShouldInitializeWithDefaultDependencies()
{
// Arrange & Act
var server = new EchoServer.EchoServer(5000);

// Assert
Assert.IsFalse(server.IsRunning);
}

[Test]
public void Constructor_ShouldAcceptCustomLogger()
{
// Arrange & Act
var server = new EchoServer.EchoServer(5000, _mockLogger.Object);

// Assert
Assert.IsNotNull(server);
Assert.IsFalse(server.IsRunning);
}

[Test]
public async Task StartAsync_ShouldStartServerAndHandleClients()
{
// Arrange
var mockStream = new Mock<INetworkStreamWrapper>();
var mockClient = new Mock<IClientWrapper>();
var mockTcpClient = new Mock<TcpClient>();
var cts = new CancellationTokenSource();

byte[] testData = new byte[] { 1, 2, 3, 4, 5 };

mockStream.SetupSequence(s => s.ReadAsync(It.IsAny<byte[]>(), 0, It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(testData.Length))
.Returns(Task.FromResult(0));

mockStream.Setup(s => s.WriteAsync(It.IsAny<byte[]>(), 0, It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);

mockClient.Setup(c => c.GetStream()).Returns(mockStream.Object);
mockClient.Setup(c => c.Dispose());

_mockClientFactory.Setup(f => f.CreateClientWrapper(It.IsAny<TcpClient>()))
.Returns(mockClient.Object);

_mockListener.Setup(l => l.Start());
_mockListener.Setup(l => l.Stop());
_mockListener.SetupSequence(l => l.AcceptTcpClientAsync())
.ReturnsAsync(mockTcpClient.Object)
.Returns(Task.FromException<TcpClient>(new ObjectDisposedException("Listener")));

Func<IPAddress, int, ITcpListenerWrapper> listenerFactory = (addr, port) => _mockListener.Object;
var server = new EchoServer.EchoServer(5000, _mockLogger.Object, _mockClientFactory.Object, listenerFactory);

// Act
var serverTask = Task.Run(() => server.StartAsync());
await Task.Delay(100); // Give server time to start
server.Stop();
await serverTask;

// Assert
_mockLogger.Verify(l => l.Log(It.Is<string>(msg => msg.Contains("Server started"))), Times.Once);
_mockLogger.Verify(l => l.Log("Client connected."), Times.AtLeastOnce);
}

[Test]
public async Task StartAsync_ShouldHandleMultipleClients()
{
// Arrange
var mockStream = new Mock<INetworkStreamWrapper>();
var mockClient1 = new Mock<IClientWrapper>();
var mockClient2 = new Mock<IClientWrapper>();
var mockTcpClient1 = new Mock<TcpClient>();
var mockTcpClient2 = new Mock<TcpClient>();

mockStream.SetupSequence(s => s.ReadAsync(It.IsAny<byte[]>(), 0, It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(10))
.Returns(Task.FromResult(0));

mockStream.Setup(s => s.WriteAsync(It.IsAny<byte[]>(), 0, It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);

mockClient1.Setup(c => c.GetStream()).Returns(mockStream.Object);
mockClient2.Setup(c => c.GetStream()).Returns(mockStream.Object);

_mockClientFactory.SetupSequence(f => f.CreateClientWrapper(It.IsAny<TcpClient>()))
.Returns(mockClient1.Object)
.Returns(mockClient2.Object);

_mockListener.Setup(l => l.Start());
_mockListener.Setup(l => l.Stop());
_mockListener.SetupSequence(l => l.AcceptTcpClientAsync())
.ReturnsAsync(mockTcpClient1.Object)
.ReturnsAsync(mockTcpClient2.Object)
.Returns(Task.FromException<TcpClient>(new ObjectDisposedException("Listener")));

Func<IPAddress, int, ITcpListenerWrapper> listenerFactory = (addr, port) => _mockListener.Object;
var server = new EchoServer.EchoServer(5000, _mockLogger.Object, _mockClientFactory.Object, listenerFactory);

// Act
var serverTask = Task.Run(() => server.StartAsync());
await Task.Delay(150);
server.Stop();
await serverTask;

// Assert
_mockLogger.Verify(l => l.Log("Client connected."), Times.AtLeast(2));
}

[Test]
public async Task StartAsync_ShouldStopOnCancellation()
{
// Arrange
var cts = new CancellationTokenSource();

_mockListener.Setup(l => l.Start());
_mockListener.Setup(l => l.Stop());
_mockListener.Setup(l => l.AcceptTcpClientAsync())
.Returns(async () =>
{
await Task.Delay(5000);
return new TcpClient();
});

Func<IPAddress, int, ITcpListenerWrapper> listenerFactory = (addr, port) => _mockListener.Object;
var server = new EchoServer.EchoServer(5000, _mockLogger.Object, _mockClientFactory.Object, listenerFactory);

// Act
var serverTask = Task.Run(() => server.StartAsync());
await Task.Delay(50);
server.Stop();
await serverTask;

// Assert
_mockLogger.Verify(l => l.Log(It.Is<string>(msg => msg.Contains("Server started"))), Times.Once);
_mockLogger.Verify(l => l.Log("Server shutdown."), Times.Once);
}

[Test]
public async Task StartAsync_ShouldHandleClientExceptions()
{
// Arrange
var mockTcpClient = new Mock<TcpClient>();

_mockListener.Setup(l => l.Start());
_mockListener.Setup(l => l.Stop());
_mockListener.SetupSequence(l => l.AcceptTcpClientAsync())
.ReturnsAsync(mockTcpClient.Object)
.Returns(Task.FromException<TcpClient>(new ObjectDisposedException("Listener")));

var mockClient = new Mock<IClientWrapper>();
var mockStream = new Mock<INetworkStreamWrapper>();

mockStream.Setup(s => s.ReadAsync(It.IsAny<byte[]>(), 0, It.IsAny<int>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new IOException("Network error"));

mockClient.Setup(c => c.GetStream()).Returns(mockStream.Object);
_mockClientFactory.Setup(f => f.CreateClientWrapper(It.IsAny<TcpClient>()))
.Returns(mockClient.Object);

Func<IPAddress, int, ITcpListenerWrapper> listenerFactory = (addr, port) => _mockListener.Object;
var server = new EchoServer.EchoServer(5000, _mockLogger.Object, _mockClientFactory.Object, listenerFactory);

// Act
var serverTask = Task.Run(() => server.StartAsync());
await Task.Delay(150);
server.Stop();
await serverTask;

// Assert
_mockLogger.Verify(l => l.Log(It.Is<string>(msg => msg.Contains("Error:"))), Times.AtLeastOnce);
}

[Test]
public void Stop_ShouldStopServer()
{
// Arrange
Func<IPAddress, int, ITcpListenerWrapper> listenerFactory = (addr, port) => _mockListener.Object;
var server = new EchoServer.EchoServer(5000, _mockLogger.Object, _mockClientFactory.Object, listenerFactory);

// Act
server.Stop();

// Assert
_mockLogger.Verify(l => l.Log("Server stopped."), Times.Once);
Assert.IsFalse(server.IsRunning);
}

[Test]
public void Stop_ShouldNotThrowWhenCalledMultipleTimes()
{
// Arrange
var server = new EchoServer.EchoServer(5000, _mockLogger.Object);

// Act & Assert
Assert.DoesNotThrow(() => server.Stop());
Assert.DoesNotThrow(() => server.Stop());
}

[Test]
public void Dispose_ShouldStopServer()
{
// Arrange
var server = new EchoServer.EchoServer(5000, _mockLogger.Object);

// Act
server.Dispose();

// Assert
Assert.IsFalse(server.IsRunning);
}
}
}
33 changes: 33 additions & 0 deletions EchoServerTests/EchoServerTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="coverlet.msbuild" Version="6.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\EchoTspServer\EchoServer.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

</Project>
39 changes: 39 additions & 0 deletions EchoServerTests/TcpClientWrapperTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using EchoServer;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace EchoServerTests
{
[TestFixture]
public class TcpClientWrapperTests
{
[Test]
public void GetStream_ShouldReturnNetworkStreamWrapper()
{
// Arrange
var tcpClient = new TcpClient();
var wrapper = new TcpClientWrapper(tcpClient);

// Act & Assert
// Cannot test without actual connection, but verify no exception
Assert.DoesNotThrow(() => wrapper.Dispose());
}

[Test]
public void Dispose_ShouldCloseClient()
{
// Arrange
var tcpClient = new TcpClient();
var wrapper = new TcpClientWrapper(tcpClient);

// Act & Assert
Assert.DoesNotThrow(() => wrapper.Dispose());
}
}
}
Loading
Loading