-
Notifications
You must be signed in to change notification settings - Fork 0
HW4 SimpleFTP #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
aea17a6
f92c800
cfde83c
d2be351
bfe6ba7
6294f41
aa7236e
7102428
6b17549
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyFTP", "MyFTP\MyFTP.csproj", "{15BAB866-F9EC-4581-A2DA-D9436094B887}" | ||
| EndProject | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyFTP.client", "MyFTP.client\MyFTP.client.csproj", "{9A29907E-A801-4F31-8176-2DD0F1A9F475}" | ||
| EndProject | ||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyFTP.Tests", "MyFTP.Tests\MyFTP.Tests.csproj", "{75720F9D-8B5C-4EA4-ABB9-FEFD37969DBF}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Any CPU = Debug|Any CPU | ||
| Release|Any CPU = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {15BAB866-F9EC-4581-A2DA-D9436094B887}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {15BAB866-F9EC-4581-A2DA-D9436094B887}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {15BAB866-F9EC-4581-A2DA-D9436094B887}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {15BAB866-F9EC-4581-A2DA-D9436094B887}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| {9A29907E-A801-4F31-8176-2DD0F1A9F475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {9A29907E-A801-4F31-8176-2DD0F1A9F475}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {9A29907E-A801-4F31-8176-2DD0F1A9F475}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {9A29907E-A801-4F31-8176-2DD0F1A9F475}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| {75720F9D-8B5C-4EA4-ABB9-FEFD37969DBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {75720F9D-8B5C-4EA4-ABB9-FEFD37969DBF}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {75720F9D-8B5C-4EA4-ABB9-FEFD37969DBF}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {75720F9D-8B5C-4EA4-ABB9-FEFD37969DBF}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| EndGlobalSection | ||
| EndGlobal |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| <GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
| <IsPackable>false</IsPackable> | ||
| <IsTestProject>true</IsTestProject> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="coverlet.collector" Version="6.0.0"/> | ||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/> | ||
| <PackageReference Include="NUnit" Version="3.14.0"/> | ||
| <PackageReference Include="NUnit.Analyzers" Version="3.9.0"/> | ||
| <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Using Include="NUnit.Framework"/> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\MyFTP.client\MyFTP.client.csproj" /> | ||
| <ProjectReference Include="..\MyFTP\MyFTP.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <AdditionalFiles Include="stylecop.json" /> | ||
| </ItemGroup> | ||
|
|
||
| <Target Name="CopyServerAndClient" AfterTargets="Build"> | ||
| <ItemGroup> | ||
| <ServerFiles Include="..\MyFTP\bin\$(Configuration)\net9.0\*.*" /> | ||
| <ClientFiles Include="..\MyFTP.client\bin\$(Configuration)\net9.0\*.*" /> | ||
| </ItemGroup> | ||
| <Copy SourceFiles="@(ServerFiles)" DestinationFolder="$(OutputPath)server\" SkipUnchangedFiles="true" /> | ||
| <Copy SourceFiles="@(ClientFiles)" DestinationFolder="$(OutputPath)client\" SkipUnchangedFiles="true" /> | ||
| </Target> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,227 @@ | ||||||
| // <copyright file="MyFtpIntegrationTests.cs" company="khusainovilas"> | ||||||
| // Copyright (c) khusainovilas. All rights reserved. | ||||||
| // </copyright> | ||||||
|
|
||||||
| namespace MyFTP.Tests; | ||||||
|
|
||||||
| using System.Diagnostics; | ||||||
| using System.Security.Cryptography; | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// End-to-end integration tests for the MyFTP. | ||||||
| /// </summary> | ||||||
| public class MyFtpIntegrationTests | ||||||
| { | ||||||
| private const int Port = 12345; | ||||||
| private const string ClientExe = @"..\..\..\..\MyFTP.client\bin\Debug\net9.0\MyFTP.client.exe"; | ||||||
| private const string ServerExe = @"..\..\..\..\MyFTP\bin\Debug\net9.0\MyFTP.exe"; | ||||||
| private const string ServerRoot = @"..\..\..\..\MyFTP"; | ||||||
|
|
||||||
| private Process? server; | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Starts the server. | ||||||
| /// </summary> | ||||||
| [OneTimeSetUp] | ||||||
| public void StartServer() | ||||||
| { | ||||||
| KillAllProcesses(); | ||||||
|
|
||||||
| var serverPath = Path.GetFullPath(ServerExe); | ||||||
| if (!File.Exists(serverPath)) | ||||||
| { | ||||||
| Assert.Inconclusive($"\nServer not found: {serverPath}"); | ||||||
| } | ||||||
|
|
||||||
| this.server = new Process | ||||||
| { | ||||||
| StartInfo = new ProcessStartInfo | ||||||
| { | ||||||
| FileName = serverPath, | ||||||
| Arguments = Port.ToString(), | ||||||
| UseShellExecute = false, | ||||||
| CreateNoWindow = true, | ||||||
| WorkingDirectory = Path.GetFullPath(ServerRoot), | ||||||
| }, | ||||||
| }; | ||||||
|
|
||||||
| this.server.Start(); | ||||||
| Thread.Sleep(3000); | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Stops the server. | ||||||
| /// </summary> | ||||||
| [OneTimeTearDown] | ||||||
| public void StopServer() | ||||||
| { | ||||||
| if (this.server is { HasExited: false }) | ||||||
| { | ||||||
| try | ||||||
| { | ||||||
| this.server.Kill(); | ||||||
| } | ||||||
| catch | ||||||
| { | ||||||
| // ignored | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| this.server?.Dispose(); | ||||||
| KillAllProcesses(); | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Clears the folder. | ||||||
| /// </summary> | ||||||
| [SetUp] | ||||||
| public void Cleanup() | ||||||
| { | ||||||
| var dir = TestContext.CurrentContext.TestDirectory; | ||||||
| foreach (var file in new[] { "file.txt", "pin.jpg", "123.docx", "111.txt" }) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| { | ||||||
| var path = Path.Combine(dir, file); | ||||||
| if (File.Exists(path)) | ||||||
| { | ||||||
| File.Delete(path); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Checking the List command for the directory ./Test. | ||||||
| /// </summary> | ||||||
| [Test] | ||||||
| public void IntegrationTest_ReturnsCorrectFileList() | ||||||
| { | ||||||
| var output = RunClient("1 ./Test"); | ||||||
|
|
||||||
| var line = output.Split('\n') | ||||||
| .Select(l => l.Trim()) | ||||||
| .FirstOrDefault(l => l.Contains("./Test/pin.jpg") && l.Contains("./Test/dir")); | ||||||
|
|
||||||
| Assert.That(line, Is.Not.Null, $"List didn't work! Full output:\n{output}"); | ||||||
| StringAssert.Contains("./Test/pin.jpg false", line); | ||||||
| StringAssert.Contains("./Test/dir true", line); | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Checking the download of a .txt file. | ||||||
| /// </summary> | ||||||
| [Test] | ||||||
| public void IntegrationTest_ExistingTextFile_DownloadsSuccessfully() | ||||||
| { | ||||||
| RunClient("2 ./Test/file.txt"); | ||||||
| var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "file.txt"); | ||||||
| Assert.That(File.Exists(path), Is.True, "file.txt did not download!"); | ||||||
| } | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Стоило проверить и его содержимое, вдруг там пустой файл просто создаётся |
||||||
|
|
||||||
| /// <summary> | ||||||
| /// Checking the download of a .jpg file. | ||||||
| /// </summary> | ||||||
| [Test] | ||||||
| public void IntegrationTest_ExistingJpgFile_DownloadsSuccessfully() | ||||||
| { | ||||||
| var original = Path.Combine(ServerRoot, "Test", "pin.jpg"); | ||||||
| var originalHash = GetSha256(original); | ||||||
|
|
||||||
| RunClient("2 ./Test/pin.jpg"); | ||||||
|
|
||||||
| var downloaded = Path.Combine(TestContext.CurrentContext.TestDirectory, "pin.jpg"); | ||||||
| Assert.Multiple(() => | ||||||
| { | ||||||
| Assert.That(File.Exists(downloaded), Is.True, "pin.jpg did not download!"); | ||||||
| Assert.That(GetSha256(downloaded), Is.EqualTo(originalHash)); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Checking the download of a .docx file. | ||||||
| /// </summary> | ||||||
| [Test] | ||||||
| public void IntegrationTest_ExistingDocxFile_DownloadsSuccessfully() | ||||||
| { | ||||||
| var original = Path.Combine(ServerRoot, "Test", "123.docx"); | ||||||
| var originalHash = GetSha256(original); | ||||||
|
|
||||||
| RunClient("2 ./Test/123.docx"); | ||||||
|
|
||||||
| var downloaded = Path.Combine(TestContext.CurrentContext.TestDirectory, "123.docx"); | ||||||
| Assert.Multiple(() => | ||||||
| { | ||||||
| Assert.That(File.Exists(downloaded), Is.True, "123.docx did not download!"); | ||||||
| Assert.That(GetSha256(downloaded), Is.EqualTo(originalHash)); | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Checking the behavior when requesting a non-existent file. | ||||||
| /// </summary> | ||||||
| [Test] | ||||||
| public void IntegrationTest_NonExistentFile_ReturnsMinusOne() | ||||||
| { | ||||||
| var output = RunClient("2 ./Test/nonexistent.txt"); | ||||||
| StringAssert.Contains("-1", output); | ||||||
| } | ||||||
|
|
||||||
| private static string RunClient(string command) | ||||||
| { | ||||||
| var clientPath = Path.GetFullPath(ClientExe); | ||||||
| var psi = new ProcessStartInfo | ||||||
| { | ||||||
| FileName = clientPath, | ||||||
| UseShellExecute = false, | ||||||
| RedirectStandardInput = true, | ||||||
| RedirectStandardOutput = true, | ||||||
| RedirectStandardError = true, | ||||||
| CreateNoWindow = true, | ||||||
| WorkingDirectory = TestContext.CurrentContext.TestDirectory, | ||||||
| }; | ||||||
|
|
||||||
| using var client = Process.Start(psi)!; | ||||||
| client.StandardInput.WriteLine(command); | ||||||
| client.StandardInput.WriteLine("exit"); | ||||||
| client.StandardInput.Flush(); | ||||||
|
|
||||||
| var output = client.StandardOutput.ReadToEnd(); | ||||||
| var error = client.StandardError.ReadToEnd(); | ||||||
|
|
||||||
| client.WaitForExit(20000); | ||||||
| if (!client.HasExited) | ||||||
| { | ||||||
| client.Kill(); | ||||||
| } | ||||||
|
|
||||||
| if (client.ExitCode != 0) | ||||||
| { | ||||||
| Assert.Fail($"The client has fallen!\nError: {error}\nOutput: {output}"); | ||||||
| } | ||||||
|
|
||||||
| return output; | ||||||
| } | ||||||
|
|
||||||
| private static string GetSha256(string path) | ||||||
| { | ||||||
| using var sha256 = SHA256.Create(); | ||||||
| using var stream = File.OpenRead(path); | ||||||
| return BitConverter.ToString(sha256.ComputeHash(stream)).Replace("-", string.Empty).ToLowerInvariant(); | ||||||
| } | ||||||
|
|
||||||
| private static void KillAllProcesses() | ||||||
| { | ||||||
| foreach (var p in Process.GetProcesses()) | ||||||
| { | ||||||
| if (p.ProcessName.Contains("MyFTP")) | ||||||
| { | ||||||
| try | ||||||
| { | ||||||
| p.Kill(); | ||||||
| } | ||||||
| catch | ||||||
| { | ||||||
| // ignored | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", | ||
| "settings": { | ||
| "documentationRules": { | ||
| "companyName": "khusainovilas", | ||
| "copyrightText": "Copyright (c) {companyName}. All rights reserved." | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| <GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <AdditionalFiles Include="stylecop.json" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Сурово. Можно было создать объект-сервер в этом же процессе, они бы нормально общались с клиентом по сети. Хотя так более показательно, так что можно не править