Skip to content
Merged
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
37 changes: 37 additions & 0 deletions Tasks/Test1/Test1.Tests/CheckSumUtilsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Alexander Bugaev 2024
//
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

namespace CheckSum.Tests;

public static class CheckSumUtilsTest
{
private const string TestFilesPath = "TestFiles/";

public static IEnumerable<TestCaseData> TestCases()
{
foreach (var testFile in Directory.GetFileSystemEntries(TestFilesPath))
{
yield return new TestCaseData(testFile);
}
}
Comment on lines +13 to +19

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public static IEnumerable<TestCaseData> TestCases()
{
foreach (var testFile in Directory.GetFileSystemEntries(TestFilesPath))
{
yield return new TestCaseData(testFile);
}
}
public static IEnumerable<TestCaseData> TestCases()
=> Directory.GetFileSystemEntries(TestFilesPath).Select(e => new TestCaseData(e));

Если проходите по коллекции, чтобы сделать yield, это сразу знак, что делаете что-то не так, коллекция ведь и так коллекция, а yield нужен, чтобы по набору значений коллекцию сделать


[TestCaseSource(nameof(TestCases))]
public static async Task TestCheckSumEvaluation_CorrectCases_CheckSumsAreEqual(string path)
{
var hash1 = CheckSumUtils.GetCheckSumSequentially(path);
var hash2 = await CheckSumUtils.GetCheckSumConcurrently(path);
Assert.That(hash1, Is.EqualTo(hash2));
}

[Test]
public static void TestCheckSumEvaluation_UnexistentFile_ThrowException()
{
Assert.Throws<FileNotFoundException>(
() => CheckSumUtils.GetCheckSumSequentially("agsgdffdghgf"));
Assert.ThrowsAsync<FileNotFoundException>(
async () => await CheckSumUtils.GetCheckSumConcurrently("agsgdffdghgf"));
}
}
7 changes: 7 additions & 0 deletions Tasks/Test1/Test1.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) Alexander Bugaev 2024
//
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

global using NUnit.Framework;
30 changes: 30 additions & 0 deletions Tasks/Test1/Test1.Tests/Test1.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

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

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

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Test1\Test1.csproj" />
</ItemGroup>

<ItemGroup>
<Content Include="TestFiles/**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions Tasks/Test1/Test1.Tests/TestFiles/Test1/testFile1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
yvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyyuvyu
vyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyy
uvyuvyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyv
3 changes: 3 additions & 0 deletions Tasks/Test1/Test1.Tests/TestFiles/Test2/testFile1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
qqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwq
qqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwq
qqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwq
4 changes: 4 additions & 0 deletions Tasks/Test1/Test1.Tests/TestFiles/Test2/testFile2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
qqqqqqqqwqwqwq2131313131
qqqqqqqqwqwqwq2131313131
qqqqqqqqwqwqwq2131313131
qqqqqqqqwqwqwq2131313131
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
90090090302131312900900903021313129009009030213131290090090302131312
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
11111111111111222222222222aaaaaaaaaaa
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
11111111111111222222222222aaaaaaaaaaa11111111111111222222222222aaaaaaaaaaa1211
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
00000000111111111111111111111111111112222222222222211111111111111222222222222aaaaaaaaaaa
3 changes: 3 additions & 0 deletions Tasks/Test1/Test1.Tests/TestFiles/Test5
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fasgagasasgassg049203209-05034958034
32313
321312
Empty file.
32 changes: 32 additions & 0 deletions Tasks/Test1/Test1.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test1", "Test1\Test1.csproj", "{E17B3681-8DCA-4F5D-BCF6-B941B2A3C896}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test1.Tests", "Test1.Tests\Test1.Tests.csproj", "{F9AFFFD2-F330-491C-8C88-A114CE9EBC22}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E17B3681-8DCA-4F5D-BCF6-B941B2A3C896}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E17B3681-8DCA-4F5D-BCF6-B941B2A3C896}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E17B3681-8DCA-4F5D-BCF6-B941B2A3C896}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E17B3681-8DCA-4F5D-BCF6-B941B2A3C896}.Release|Any CPU.Build.0 = Release|Any CPU
{7F25481E-3621-4545-95FA-ACF6A3C42F70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F25481E-3621-4545-95FA-ACF6A3C42F70}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F25481E-3621-4545-95FA-ACF6A3C42F70}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F25481E-3621-4545-95FA-ACF6A3C42F70}.Release|Any CPU.Build.0 = Release|Any CPU
{F9AFFFD2-F330-491C-8C88-A114CE9EBC22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F9AFFFD2-F330-491C-8C88-A114CE9EBC22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F9AFFFD2-F330-491C-8C88-A114CE9EBC22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9AFFFD2-F330-491C-8C88-A114CE9EBC22}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
121 changes: 121 additions & 0 deletions Tasks/Test1/Test1/CheckSumUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright (c) Alexander Bugaev 2024
//
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

using System.Security.Cryptography;
using System.Text;

namespace CheckSum;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namespace лучше перед using


/// <summary>
/// Class for evaluating file check sums.
/// </summary>
public static class CheckSumUtils
{
/// <summary>
/// Evaluate the check sum of given file sequentially.
/// </summary>
/// <param name="path">Path of the file to get check sum of.</param>
/// <returns>An array of bytes: evaluated check sum.</returns>
/// <exception cref="FileNotFoundException">File was not found.</exception>
public static byte[] GetCheckSumSequentially(string path)
{
if (File.Exists(path))
{
return GetFileCheckSumSequentially(path);
}

if (Directory.Exists(path))
{
return GetDirectoryCheckSumSequentially(path);
}

throw new FileNotFoundException();
}

/// <summary>
/// Evaluate the check sum of given file concurrently.
/// </summary>
/// <param name="path">Path of the file to get check sum of.</param>
/// <returns>A task with result as an array of bytes: evaluated check sum.</returns>
/// <exception cref="FileNotFoundException">File was not found.</exception>
public static async Task<byte[]> GetCheckSumConcurrently(string path)
{
if (File.Exists(path))
{
return await GetFileCheckSumConcurrently(path);
}

if (Directory.Exists(path))
{
return GetDirectoryCheckSumConcurrently(path);
}

throw new FileNotFoundException();
}

private static byte[] GetFileCheckSumSequentially(string path)
{
var content = File.ReadAllBytes(path);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если файл в память не лезет, быть беде

return MD5.HashData(content);
}

private static byte[] GetDirectoryCheckSumSequentially(string path)
{
var result = new List<byte>();
var name = Path.GetFileName(path);
var nameHash = MD5.HashData(Encoding.Unicode.GetBytes(name));
result.AddRange(nameHash);

var entries = Directory.GetFileSystemEntries(path);
Array.Sort(entries);
foreach (var entry in entries)
{
result.AddRange(GetCheckSumSequentially(entry));
}

return result.ToArray();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так это не чексумма, а конкатенированный набор чексумм. Надо было это ещё в MD5 запихать.

}

private static async Task<byte[]> GetFileCheckSumConcurrently(string path)
{
var content = await File.ReadAllBytesAsync(path);
return MD5.HashData(content);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вы не поверите, но и в однопоточном случае стоило так с файлом и поступать. Синхронный ввод-вывод в современном мире, я бы сказал, вообще не нужен. Тут оно асинхронное, но не параллельное, так что не противоречит условию.


private static byte[] GetDirectoryCheckSumConcurrently(string path)
{
var tasks = new List<Task<byte[]>>();
var name = Path.GetFileName(path);
var nameHash = MD5.HashData(Encoding.Unicode.GetBytes(name));
tasks.Add(Task.Run(() => nameHash));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Он же уже посчитан. Task.Result?


var entries = Directory.GetFileSystemEntries(path);
Array.Sort(entries);
foreach (var entry in entries)
{
tasks.Add(GetCheckSumConcurrently(entry));
}

var resultSize = 0;
foreach (var task in tasks)
{
resultSize += task.Result.Length;
}

var result = new byte[resultSize];
var currentIndex = 0;
foreach (var task in tasks)
{
for (int i = 0; i < task.Result.Length; ++i)
{
result[currentIndex] = task.Result[i];
++currentIndex;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Процесс можно было немного автоматизировать через Array.Copy

}

return result;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

И тоже, ожидался 128-битный хеш, вернули что попало

}
}
49 changes: 49 additions & 0 deletions Tasks/Test1/Test1/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Alexander Bugaev 2024
//
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

namespace CheckSum;

using System.Diagnostics;

/// <summary>
/// Program that compares the time of sequential and
/// concurrent check sum evaluation variations.
/// </summary>
public static class Program
{
/// <summary>
/// Main entry point of the program.
/// </summary>
/// <param name="args">First argument is the path of
/// the file to evaluate the check sum of.</param>
public static void Main(string[] args)
Comment on lines +15 to +22

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это не нужно, используйте top-level statements

{
if (args.Length != 1)
{
throw new ArgumentException("Invalid arguments.\nExpected: Path of the file");
}

var path = args[0];
var stopWatch = new Stopwatch();

try
{
stopWatch.Restart();
CheckSumUtils.GetCheckSumSequentially(path);
stopWatch.Stop();
Console.WriteLine($"Time of the sequential evaluation: {stopWatch.Elapsed}");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Указывайте единицы измерения всегда


stopWatch.Restart();
CheckSumUtils.GetCheckSumConcurrently(path).Wait();
stopWatch.Stop();
Console.WriteLine($"Time of the concurrent evaluation: {stopWatch.Elapsed}");
}
catch (FileNotFoundException e)
{
Console.WriteLine($"Error: {e.Message}");
}
}
}
11 changes: 11 additions & 0 deletions Tasks/Test1/Test1/Test1.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all"/>
</ItemGroup>
</Project>