diff --git a/Tasks/Test1/Test1.Tests/CheckSumUtilsTest.cs b/Tasks/Test1/Test1.Tests/CheckSumUtilsTest.cs new file mode 100644 index 0000000..094dce5 --- /dev/null +++ b/Tasks/Test1/Test1.Tests/CheckSumUtilsTest.cs @@ -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 TestCases() + { + foreach (var testFile in Directory.GetFileSystemEntries(TestFilesPath)) + { + yield return new TestCaseData(testFile); + } + } + + [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( + () => CheckSumUtils.GetCheckSumSequentially("agsgdffdghgf")); + Assert.ThrowsAsync( + async () => await CheckSumUtils.GetCheckSumConcurrently("agsgdffdghgf")); + } +} diff --git a/Tasks/Test1/Test1.Tests/GlobalUsings.cs b/Tasks/Test1/Test1.Tests/GlobalUsings.cs new file mode 100644 index 0000000..977475e --- /dev/null +++ b/Tasks/Test1/Test1.Tests/GlobalUsings.cs @@ -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; diff --git a/Tasks/Test1/Test1.Tests/Test1.Tests.csproj b/Tasks/Test1/Test1.Tests/Test1.Tests.csproj new file mode 100644 index 0000000..cf018c1 --- /dev/null +++ b/Tasks/Test1/Test1.Tests/Test1.Tests.csproj @@ -0,0 +1,30 @@ + + + + net9.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + Always + + + + diff --git a/Tasks/Test1/Test1.Tests/TestFiles/Test1/testFile1 b/Tasks/Test1/Test1.Tests/TestFiles/Test1/testFile1 new file mode 100644 index 0000000..12d2fca --- /dev/null +++ b/Tasks/Test1/Test1.Tests/TestFiles/Test1/testFile1 @@ -0,0 +1,3 @@ +yvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyyuvyu +vyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyy +uvyuvyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyvyvuyvuyvyuyyyuvyuvyuuyvyv \ No newline at end of file diff --git a/Tasks/Test1/Test1.Tests/TestFiles/Test2/testFile1 b/Tasks/Test1/Test1.Tests/TestFiles/Test2/testFile1 new file mode 100644 index 0000000..844ac40 --- /dev/null +++ b/Tasks/Test1/Test1.Tests/TestFiles/Test2/testFile1 @@ -0,0 +1,3 @@ +qqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwq +qqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwq +qqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwqqqqqqqqqwqwqwq \ No newline at end of file diff --git a/Tasks/Test1/Test1.Tests/TestFiles/Test2/testFile2 b/Tasks/Test1/Test1.Tests/TestFiles/Test2/testFile2 new file mode 100644 index 0000000..cddf275 --- /dev/null +++ b/Tasks/Test1/Test1.Tests/TestFiles/Test2/testFile2 @@ -0,0 +1,4 @@ +qqqqqqqqwqwqwq2131313131 +qqqqqqqqwqwqwq2131313131 +qqqqqqqqwqwqwq2131313131 +qqqqqqqqwqwqwq2131313131 \ No newline at end of file diff --git a/Tasks/Test1/Test1.Tests/TestFiles/Test3/testFirectory1/testFile1 b/Tasks/Test1/Test1.Tests/TestFiles/Test3/testFirectory1/testFile1 new file mode 100644 index 0000000..aa77590 --- /dev/null +++ b/Tasks/Test1/Test1.Tests/TestFiles/Test3/testFirectory1/testFile1 @@ -0,0 +1 @@ +90090090302131312900900903021313129009009030213131290090090302131312 \ No newline at end of file diff --git a/Tasks/Test1/Test1.Tests/TestFiles/Test4/testDirectory1/testFile1 b/Tasks/Test1/Test1.Tests/TestFiles/Test4/testDirectory1/testFile1 new file mode 100644 index 0000000..e23cce4 --- /dev/null +++ b/Tasks/Test1/Test1.Tests/TestFiles/Test4/testDirectory1/testFile1 @@ -0,0 +1 @@ +11111111111111222222222222aaaaaaaaaaa \ No newline at end of file diff --git a/Tasks/Test1/Test1.Tests/TestFiles/Test4/testDirectory2/testFile2 b/Tasks/Test1/Test1.Tests/TestFiles/Test4/testDirectory2/testFile2 new file mode 100644 index 0000000..ad779cf --- /dev/null +++ b/Tasks/Test1/Test1.Tests/TestFiles/Test4/testDirectory2/testFile2 @@ -0,0 +1 @@ +11111111111111222222222222aaaaaaaaaaa11111111111111222222222222aaaaaaaaaaa1211 \ No newline at end of file diff --git a/Tasks/Test1/Test1.Tests/TestFiles/Test4/testDirectory2/testFile3 b/Tasks/Test1/Test1.Tests/TestFiles/Test4/testDirectory2/testFile3 new file mode 100644 index 0000000..caac8a4 --- /dev/null +++ b/Tasks/Test1/Test1.Tests/TestFiles/Test4/testDirectory2/testFile3 @@ -0,0 +1 @@ +00000000111111111111111111111111111112222222222222211111111111111222222222222aaaaaaaaaaa \ No newline at end of file diff --git a/Tasks/Test1/Test1.Tests/TestFiles/Test5 b/Tasks/Test1/Test1.Tests/TestFiles/Test5 new file mode 100644 index 0000000..69395d1 --- /dev/null +++ b/Tasks/Test1/Test1.Tests/TestFiles/Test5 @@ -0,0 +1,3 @@ +fasgagasasgassg049203209-05034958034 +32313 +321312 \ No newline at end of file diff --git a/Tasks/Test1/Test1.Tests/TestFiles/Test6 b/Tasks/Test1/Test1.Tests/TestFiles/Test6 new file mode 100644 index 0000000..e69de29 diff --git a/Tasks/Test1/Test1.sln b/Tasks/Test1/Test1.sln new file mode 100644 index 0000000..a4c8db6 --- /dev/null +++ b/Tasks/Test1/Test1.sln @@ -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 diff --git a/Tasks/Test1/Test1/CheckSumUtils.cs b/Tasks/Test1/Test1/CheckSumUtils.cs new file mode 100644 index 0000000..e71c7e9 --- /dev/null +++ b/Tasks/Test1/Test1/CheckSumUtils.cs @@ -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; + +/// +/// Class for evaluating file check sums. +/// +public static class CheckSumUtils +{ + /// + /// Evaluate the check sum of given file sequentially. + /// + /// Path of the file to get check sum of. + /// An array of bytes: evaluated check sum. + /// File was not found. + public static byte[] GetCheckSumSequentially(string path) + { + if (File.Exists(path)) + { + return GetFileCheckSumSequentially(path); + } + + if (Directory.Exists(path)) + { + return GetDirectoryCheckSumSequentially(path); + } + + throw new FileNotFoundException(); + } + + /// + /// Evaluate the check sum of given file concurrently. + /// + /// Path of the file to get check sum of. + /// A task with result as an array of bytes: evaluated check sum. + /// File was not found. + public static async Task 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); + return MD5.HashData(content); + } + + private static byte[] GetDirectoryCheckSumSequentially(string path) + { + var result = new List(); + 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(); + } + + private static async Task GetFileCheckSumConcurrently(string path) + { + var content = await File.ReadAllBytesAsync(path); + return MD5.HashData(content); + } + + private static byte[] GetDirectoryCheckSumConcurrently(string path) + { + var tasks = new List>(); + var name = Path.GetFileName(path); + var nameHash = MD5.HashData(Encoding.Unicode.GetBytes(name)); + tasks.Add(Task.Run(() => nameHash)); + + 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; + } + } + + return result; + } +} diff --git a/Tasks/Test1/Test1/Program.cs b/Tasks/Test1/Test1/Program.cs new file mode 100644 index 0000000..e4eb63a --- /dev/null +++ b/Tasks/Test1/Test1/Program.cs @@ -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; + +/// +/// Program that compares the time of sequential and +/// concurrent check sum evaluation variations. +/// +public static class Program +{ + /// + /// Main entry point of the program. + /// + /// First argument is the path of + /// the file to evaluate the check sum of. + public static void Main(string[] args) + { + 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}"); + + 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}"); + } + } +} diff --git a/Tasks/Test1/Test1/Test1.csproj b/Tasks/Test1/Test1/Test1.csproj new file mode 100644 index 0000000..a719fbf --- /dev/null +++ b/Tasks/Test1/Test1/Test1.csproj @@ -0,0 +1,11 @@ + + + Exe + net9.0 + enable + enable + + + + + \ No newline at end of file