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
23 changes: 23 additions & 0 deletions Hw4.Tests/Hw4.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

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

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

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Hw4\Hw4.csproj" />
</ItemGroup>
</Project>
80 changes: 80 additions & 0 deletions Hw4.Tests/ServerAndClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
namespace Hw4.Tests;

using NUnit.Framework.Internal;


public class Tests
{
private Client client;
private Server server;

[OneTimeSetUp]
public void SetUp()
{
Directory.CreateDirectory("testDir");
File.WriteAllText("testDir/test.txt", "test message");

Directory.CreateDirectory("testDir/directory");
File.WriteAllText("testDir/directory/test.txt", "test message");
File.WriteAllText("testDir/test.txt", "test message");

Directory.CreateDirectory("downloadedFiles");

server = new Server(2303);
client = new Client(2303);
Task.Run(() => server.Start());
}

[OneTimeTearDown]
public void Delete()
{
server.Stop();
Directory.Delete("testDir", true);
Directory.Delete("downloadedFiles", true);
}

[Test]
public async Task ListTest()
{
var result = await client.List("testDir");
Assert.Multiple(() =>
{
Assert.That(result.size, Is.EqualTo(2));
Assert.That(result.Item2, Is.EqualTo(new List<(string, bool)> { ("testDir/directory", true), ("testDir/test.txt", false) }));
});
}

[Test]
public async Task GetTest()
{
using (FileStream SourceStream = File.Open("downloadedFiles/file1", FileMode.OpenOrCreate))
{
await client.Get("testDir/test.txt", SourceStream);
}

var expected = File.ReadAllBytes("testDir/test.txt");
var result = File.ReadAllBytes("downloadedFiles/file1");

Assert.That(result, Is.EqualTo(expected));
}

[Test]
public async Task GetForNotExistingFileTest()
{
var result = 0;
using (FileStream SourceStream = File.Open("downloadedFiles/file1", FileMode.OpenOrCreate))
{
result = await client.Get("notExistingFile.txt", SourceStream);
}

Assert.That(result, Is.EqualTo(-1));
}

[Test]
public async Task ListForNotExistingFileTest()
{
var result = await client.List("notExistingDirectory");

Assert.That(result.size, Is.EqualTo(-1));
}
}
1 change: 1 addition & 0 deletions Hw4.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using NUnit.Framework;
143 changes: 143 additions & 0 deletions Hw4/Client.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System.Net.Sockets;
using System.Text;

namespace Hw4;

public class Client
{
private string host;
private int port;

/// <summary>
/// Instantiates a new instance of Client class.
/// </summary>
public Client(int port, string host = "localhost")
{
this.port = port;
this.host = host;
}

/// <summary>
/// Requests a listing of files in a directory on the server and reads server's response
/// </summary>
/// <param name="filePath">The path on the server to list files from.</param>
/// <returns>the parsed response from the server - how many entries in directory (size), list of entries with bool value - "is this entry a directory" </returns>
public async Task<(int size, List<(string, bool)>?)> List(string filePath)
{
using (var client = new TcpClient(host, port))
{
var stream = client.GetStream();
var writer = new StreamWriter(stream) { AutoFlush = true };
var reader = new StreamReader(stream);
await writer.WriteAsync("1 " + filePath + "\n");
try
{
return await ListResponse(reader);
}
catch
{
throw;
}
}
}

/// <summary>
/// Requests the file size and contents in bytes and writes the contents to the passed stream.
/// </summary>
/// <param name="filePath">The path to get files from.</param>
/// <param name="outputStream">The stream to which Get sends content from downloaded file.</param>
/// <returns> amount of bytes in downloaded file </returns>
public async Task<int> Get(string filePath, Stream outputStream)
{
using (var client = new TcpClient(host, port))
{
var stream = client.GetStream();
var writer = new StreamWriter(stream) { AutoFlush = true };
var reader = new StreamReader(stream);
await writer.WriteAsync("2 " + filePath + "\n");
try
{
return await GetResponse(reader, outputStream);
}
catch (InvalidDataException e)
{
throw new InvalidDataException(e.Message + ":Wrong server response");
}
catch (InvalidOperationException)
{
throw new InvalidOperationException("Asynchronous read operation returned null");
}
}
}

private static async Task<(int size, List<(string, bool)>?)> ListResponse(StreamReader reader)
{
var listResponse = await reader.ReadLineAsync() ?? throw new InvalidOperationException();
var parsedListResponse = listResponse.Split();
var isFirstItemNumber = int.TryParse(parsedListResponse[0], out int size);
if (!isFirstItemNumber)
{
throw new InvalidDataException("Amount of content in directory expected");
}

if (size == -1)
{
return (-1, null);
}

List<(string, bool)> directoryContent = new();

if (size * 2 != parsedListResponse.Length - 1)
{
throw new InvalidDataException("Size isn't equal to amount of content in directory");
}
for (int i = 1; i <= size; i++)
{
string currentPath = parsedListResponse[2 * i - 1];
bool currentPathIsDirectory;
if (parsedListResponse[i * 2] == "True")
{
currentPathIsDirectory = true;
}
else if (parsedListResponse[i * 2] == "False")
{
currentPathIsDirectory = false;
}
else
{
throw new InvalidDataException("True or false expected");
}
directoryContent.Add((currentPath, currentPathIsDirectory));
}

return (size, directoryContent);
}

private static async Task<int> GetResponse(StreamReader reader, Stream output)
{
var response = await reader.ReadToEndAsync();

var responseArray = response.Split(" ", 2);
var stringSize = responseArray[0];

var isFirstItemNumber = int.TryParse(stringSize, out int size);

if (!isFirstItemNumber || size < -1)
{
throw new InvalidDataException("Amount of bytes in file expected");
}

if (size == -1)
{
return -1;
}

var content = responseArray[1];

await output.WriteAsync(Encoding.UTF8.GetBytes(content).AsMemory(0, size));
return size;
}
}



9 changes: 9 additions & 0 deletions Hw4/Hw4.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

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

</Project>
31 changes: 31 additions & 0 deletions Hw4/Hw4.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 25.0.1706.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hw4", "Hw4.csproj", "{9757B2DC-6B82-4675-B32F-615890E1AC73}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hw4.Tests", "..\Hw4.Tests\Hw4.Tests.csproj", "{03EAEE58-748F-446E-B3C3-0395DA404E39}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9757B2DC-6B82-4675-B32F-615890E1AC73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9757B2DC-6B82-4675-B32F-615890E1AC73}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9757B2DC-6B82-4675-B32F-615890E1AC73}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9757B2DC-6B82-4675-B32F-615890E1AC73}.Release|Any CPU.Build.0 = Release|Any CPU
{03EAEE58-748F-446E-B3C3-0395DA404E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{03EAEE58-748F-446E-B3C3-0395DA404E39}.Debug|Any CPU.Build.0 = Debug|Any CPU
{03EAEE58-748F-446E-B3C3-0395DA404E39}.Release|Any CPU.ActiveCfg = Release|Any CPU
{03EAEE58-748F-446E-B3C3-0395DA404E39}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3D80B059-63A4-4CF8-B8EA-D0774CEDF1B8}
EndGlobalSection
EndGlobal
Loading