Skip to content

Commit 078b4ac

Browse files
Merge pull request #16 from 0neDistruktion/lab8Fix
Lab8 fix
2 parents a3ea5b9 + 51bde0c commit 078b4ac

20 files changed

Lines changed: 760 additions & 420 deletions

.github/workflows/sonarcloud.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ jobs:
4747
# 4) END: SonarScanner
4848
- name: SonarScanner End
4949
run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
50-
shell: pwsh
50+
shell: pwsh

EchoTcpServer/EchoServer.cs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
using System.Threading;
66
using System.Threading.Tasks;
77

8-
// Додаємо namespace, щоб тести могли його бачити
98
namespace EchoTcpServerApp.Server
109
{
1110
public class EchoServer
1211
{
1312
private readonly int _port;
14-
private TcpListener _listener;
15-
private CancellationTokenSource _cancellationTokenSource;
13+
private TcpListener? _listener; // Nullable
14+
private readonly CancellationTokenSource _cancellationTokenSource; // Readonly
1615

1716
public EchoServer(int port)
1817
{
@@ -30,65 +29,65 @@ public async Task StartAsync()
3029
{
3130
try
3231
{
33-
TcpClient client = await _listener.AcceptTcpClientAsync();
32+
if (_listener == null) break;
33+
TcpClient client = await _listener.AcceptTcpClientAsync(_cancellationTokenSource.Token);
3434
Console.WriteLine("Client connected.");
3535

36-
// Викликаємо приватний обробник
36+
// Викликаємо статичний обробник
3737
_ = Task.Run(() => HandleClientAsync(client, _cancellationTokenSource.Token));
3838
}
39-
catch (ObjectDisposedException)
39+
catch (OperationCanceledException)
4040
{
4141
break;
4242
}
43+
catch (Exception ex)
44+
{
45+
Console.WriteLine($"Accept error: {ex.Message}");
46+
}
4347
}
4448
Console.WriteLine("Server shutdown.");
4549
}
4650

47-
// Цей приватний метод керує з'єднанням
48-
private async Task HandleClientAsync(TcpClient client, CancellationToken token)
51+
// Smell fix: Made static
52+
private static async Task HandleClientAsync(TcpClient client, CancellationToken token)
4953
{
54+
using (client)
5055
using (NetworkStream stream = client.GetStream())
5156
{
5257
try
5358
{
54-
// РЕФАКТОРИНГ
55-
// Ми викликаємо нашу нову ТЕСТОВАНУ логіку
5659
await EchoStreamAsync(stream, token);
57-
5860
}
59-
catch (Exception ex) when (!(ex is OperationCanceledException))
61+
catch (Exception ex) when (ex is not OperationCanceledException)
6062
{
6163
Console.WriteLine($"Error: {ex.Message}");
6264
}
6365
finally
6466
{
65-
client.Close();
6667
Console.WriteLine("Client disconnected.");
6768
}
6869
}
6970
}
7071

71-
// НОВИЙ МЕТОД ДЛЯ ТЕСТІВ
72-
// Цей публічний, статичний метод містить тільки логіку "ехо".
73-
// Він нічого не знає про TcpClient, тому ми можемо його легко протестувати.
7472
public static async Task EchoStreamAsync(Stream stream, CancellationToken token)
7573
{
7674
byte[] buffer = new byte[8192];
7775
int bytesRead;
7876

79-
while (!token.IsCancellationRequested && (bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
77+
// Smell fix: Using ReadAsync(Memory<byte>, CancellationToken) implicitly
78+
while (!token.IsCancellationRequested &&
79+
(bytesRead = await stream.ReadAsync(buffer, token)) > 0)
8080
{
81-
// Echo back the received message
82-
await stream.WriteAsync(buffer, 0, bytesRead, token);
81+
// Smell fix: Using WriteAsync(ReadOnlyMemory<byte>, CancellationToken) implicitly
82+
await stream.WriteAsync(buffer.AsMemory(0, bytesRead), token);
8383
Console.WriteLine($"Echoed {bytesRead} bytes to the client.");
8484
}
8585
}
86-
8786

8887
public void Stop()
8988
{
9089
_cancellationTokenSource.Cancel();
91-
_listener.Stop();
90+
_listener?.Stop();
9291
_cancellationTokenSource.Dispose();
9392
Console.WriteLine("Server stopped.");
9493
}

EchoTcpServer/EchoServer.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
99

10-
</Project>
10+
</Project>

EchoTcpServer/UdpTimedSender.cs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
using System.Net.Sockets;
55
using System.Threading;
66

7-
// Додаємо namespace
87
namespace EchoTcpServerApp.Client
98
{
109
public class UdpTimedSender : IDisposable
1110
{
1211
private readonly string _host;
1312
private readonly int _port;
1413
private readonly UdpClient _udpClient;
15-
private Timer _timer;
14+
private Timer? _timer; // Nullable
1615

1716
public UdpTimedSender(string host, int port)
1817
{
@@ -29,19 +28,21 @@ public void StartSending(int intervalMilliseconds)
2928
_timer = new Timer(SendMessageCallback, null, 0, intervalMilliseconds);
3029
}
3130

32-
ushort i = 0;
31+
ushort _counter = 0;
3332

34-
private void SendMessageCallback(object state)
33+
// Smell fix: state is nullable
34+
private void SendMessageCallback(object? state)
3535
{
3636
try
3737
{
38-
//dummy data
39-
Random rnd = new Random();
4038
byte[] samples = new byte[1024];
41-
rnd.NextBytes(samples);
42-
i++;
39+
Random.Shared.NextBytes(samples);
40+
_counter++;
4341

44-
byte[] msg = (new byte[] { 0x04, 0x84 }).Concat(BitConverter.GetBytes(i)).Concat(samples).ToArray();
42+
byte[] msg = (new byte[] { 0x04, 0x84 })
43+
.Concat(BitConverter.GetBytes(_counter))
44+
.Concat(samples).ToArray();
45+
4546
var endpoint = new IPEndPoint(IPAddress.Parse(_host), _port);
4647

4748
_udpClient.Send(msg, msg.Length, endpoint);
@@ -61,8 +62,17 @@ public void StopSending()
6162

6263
public void Dispose()
6364
{
64-
StopSending();
65-
_udpClient.Dispose();
65+
Dispose(true);
66+
GC.SuppressFinalize(this);
67+
}
68+
69+
protected virtual void Dispose(bool disposing)
70+
{
71+
if (disposing)
72+
{
73+
StopSending();
74+
_udpClient.Dispose();
75+
}
6676
}
6777
}
6878
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using NUnit.Framework;
2+
using EchoTcpServerApp.Server;
3+
using System.Net.Sockets;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System;
7+
8+
namespace EchoTcpServerTests
9+
{
10+
[TestFixture]
11+
public class EchoServerIntegrationTests
12+
{
13+
[Test]
14+
public async Task Server_Should_AcceptClient_And_EchoMessage_RealConnection()
15+
{
16+
// 1. Arrange: Запускаємо сервер на тестовому порту
17+
int port = 55555;
18+
var server = new EchoServer(port);
19+
20+
// Запускаємо сервер у фоні (не чекаємо await, бо він заблокує тест)
21+
var serverTask = server.StartAsync();
22+
23+
// Даємо серверу трохи часу на старт
24+
await Task.Delay(100);
25+
26+
try
27+
{
28+
// 2. Act: Створюємо справжнього TCP клієнта і підключаємося
29+
using var client = new TcpClient();
30+
await client.ConnectAsync("127.0.0.1", port);
31+
32+
// Відправляємо повідомлення
33+
var stream = client.GetStream();
34+
string message = "Integration Test Data";
35+
byte[] data = Encoding.UTF8.GetBytes(message);
36+
await stream.WriteAsync(data, 0, data.Length);
37+
38+
// Читаємо відповідь (Ехо)
39+
byte[] buffer = new byte[1024];
40+
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
41+
string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
42+
43+
// 3. Assert: Сервер мав повернути те саме
44+
Assert.That(response, Is.EqualTo(message));
45+
}
46+
finally
47+
{
48+
// 4. Cleanup: Зупиняємо сервер
49+
server.Stop();
50+
51+
// Ігноруємо помилки скасування задачі, це нормально при зупинці
52+
try { await serverTask; } catch { }
53+
}
54+
}
55+
}
56+
}
Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using NUnit.Framework;
2+
using System;
23
using System.IO;
4+
using System.Net.Sockets;
35
using System.Text;
46
using System.Threading;
57
using System.Threading.Tasks;
@@ -9,57 +11,87 @@ namespace EchoTcpServerTests
911
{
1012
public class EchoServerTests
1113
{
12-
[SetUp]
13-
public void Setup()
14-
{
15-
}
16-
1714
[Test]
1815
public async Task EchoStreamAsync_ShouldEchoBackMessage()
1916
{
20-
// --- Arrange (Підготовка) ---
2117
var originalMessage = "Hello, World!";
2218
var messageBytes = Encoding.UTF8.GetBytes(originalMessage);
2319

24-
// Створюємо потік і записуємо туди "вхідні" дані як байти
25-
var stream = new MemoryStream();
26-
await stream.WriteAsync(messageBytes, 0, messageBytes.Length);
27-
28-
// "Перемотуємо" на початок, щоб сервер міг прочитати це як вхідні дані
20+
using var stream = new MemoryStream();
21+
await stream.WriteAsync(messageBytes);
2922
stream.Position = 0;
3023

31-
// --- Act (Дія) ---
32-
// Запускаємо ехо. Воно прочитає дані і допише їх в кінець потоку.
3324
await EchoServer.EchoStreamAsync(stream, CancellationToken.None);
3425

35-
// --- Assert (Перевірка) ---
36-
// Тепер у потоці має бути: [Вхідні дані] + [Ехо-відповідь]
37-
38-
// Перемотуємо на початок і читаємо все разом
3926
stream.Position = 0;
4027
using var reader = new StreamReader(stream, Encoding.UTF8);
4128
var fullContent = await reader.ReadToEndAsync();
4229

43-
// Перевіряємо, що повний текст це "Hello, World!" + "Hello, World!"
44-
Assert.AreEqual(originalMessage + originalMessage, fullContent);
30+
Assert.That(fullContent, Is.EqualTo(originalMessage + originalMessage));
4531
}
4632

4733
[Test]
4834
public async Task EchoStreamAsync_ShouldHandleEmptyMessage()
4935
{
50-
// Arrange
51-
var stream = new MemoryStream();
52-
stream.Position = 0;
53-
54-
// Act
36+
using var stream = new MemoryStream();
37+
5538
await EchoServer.EchoStreamAsync(stream, CancellationToken.None);
5639

57-
// Assert
5840
stream.Position = 0;
5941
using var reader = new StreamReader(stream, Encoding.UTF8);
6042
var actualMessage = await reader.ReadToEndAsync();
6143

62-
Assert.AreEqual(string.Empty, actualMessage);
44+
Assert.That(actualMessage, Is.EqualTo(string.Empty));
45+
}
46+
47+
[Test]
48+
public async Task Stop_ShouldCancelServerLoop_Gracefully()
49+
{
50+
var server = new EchoServer(0);
51+
var serverTask = server.StartAsync();
52+
53+
await Task.Delay(100);
54+
server.Stop();
55+
56+
Assert.DoesNotThrowAsync(async () => await serverTask);
57+
}
58+
59+
[Test]
60+
public async Task Integration_ServerShouldHandleRealTcpConnection()
61+
{
62+
int port = new Random().Next(50000, 60000);
63+
var server = new EchoServer(port);
64+
var serverTask = server.StartAsync();
65+
66+
await Task.Delay(100);
67+
68+
using (var client = new TcpClient())
69+
{
70+
await client.ConnectAsync("127.0.0.1", port);
71+
using var stream = client.GetStream();
72+
73+
byte[] dataToSend = { 10, 20, 30 };
74+
await stream.WriteAsync(dataToSend);
75+
76+
byte[] buffer = new byte[3];
77+
78+
// FIX: Зберігаємо Task в змінну, щоб порівнювати посилання
79+
var readValueTask = stream.ReadAsync(buffer);
80+
var actualReadTask = readValueTask.AsTask();
81+
82+
var completedTask = await Task.WhenAny(actualReadTask, Task.Delay(2000));
83+
84+
if (completedTask == actualReadTask)
85+
{
86+
Assert.That(buffer, Is.EqualTo(dataToSend));
87+
}
88+
else
89+
{
90+
Assert.Fail("Timeout waiting for echo response");
91+
}
92+
}
93+
server.Stop();
94+
Assert.DoesNotThrowAsync(async () => await serverTask);
6395
}
6496
}
6597
}

0 commit comments

Comments
 (0)