From 28e3f4a2e6114d345e06994ec469e24b8a7eb050 Mon Sep 17 00:00:00 2001 From: Eugene Date: Tue, 10 Feb 2026 16:47:23 +0800 Subject: [PATCH 01/19] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20IDisposable=20=D0=B2=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80?= =?UTF-8?q?=D1=84=D0=B5=D0=B9=D1=81=20IReadOnlyStream.=20=D0=A0=D0=B5?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=B2=20?= =?UTF-8?q?=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B5=20ReadOnlyStream.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask/IReadOnlyStream.cs | 8 +++++--- TestTask/ReadOnlyStream.cs | 29 ++++++++++++++++++++++++++++- TestTask/TestTask.csproj | 6 +++++- TestTask/app.config | 3 +++ 4 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 TestTask/app.config diff --git a/TestTask/IReadOnlyStream.cs b/TestTask/IReadOnlyStream.cs index 6946745..7f40832 100644 --- a/TestTask/IReadOnlyStream.cs +++ b/TestTask/IReadOnlyStream.cs @@ -1,12 +1,14 @@ -namespace TestTask +using System; + +namespace TestTask { /// /// Интерфейс для работы с файлом в сильно урезаном виде. /// Умеет всего 2 вещи: прочитать символ, и перемотать стрим на начало. /// - internal interface IReadOnlyStream + internal interface IReadOnlyStream: IDisposable { - // TODO : Необходимо доработать данный интерфейс для обеспечения гарантированного закрытия файла, по окончанию работы с таковым! + // TODO : Необходимо доработать данный интерфейс (доработано, как IDisposable) char ReadNextChar(); void ResetPositionToStart(); diff --git a/TestTask/ReadOnlyStream.cs b/TestTask/ReadOnlyStream.cs index a51a61e..d038527 100644 --- a/TestTask/ReadOnlyStream.cs +++ b/TestTask/ReadOnlyStream.cs @@ -1,11 +1,13 @@ using System; using System.IO; +using System.Runtime.InteropServices.ComTypes; namespace TestTask { public class ReadOnlyStream : IReadOnlyStream { - private Stream _localStream; + private readonly Stream _localStream; + private bool _disposed = false; /// /// Конструктор класса. @@ -38,6 +40,7 @@ public bool IsEof /// Считанный символ. public char ReadNextChar() { + CheckDisposed(); // TODO : Необходимо считать очередной символ из _localStream throw new NotImplementedException(); } @@ -56,5 +59,29 @@ public void ResetPositionToStart() _localStream.Position = 0; IsEof = false; } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _localStream.Dispose(); + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void CheckDisposed() + { + if (_disposed) + throw new ObjectDisposedException(GetType().Name); + } } } diff --git a/TestTask/TestTask.csproj b/TestTask/TestTask.csproj index dc470b0..9903e02 100644 --- a/TestTask/TestTask.csproj +++ b/TestTask/TestTask.csproj @@ -8,9 +8,10 @@ Exe TestTask TestTask - v4.6.1 + v4.8 512 true + AnyCPU @@ -49,5 +50,8 @@ + + + \ No newline at end of file diff --git a/TestTask/app.config b/TestTask/app.config new file mode 100644 index 0000000..3e0e37c --- /dev/null +++ b/TestTask/app.config @@ -0,0 +1,3 @@ + + + From c0bfb3b600f0bc4d1ac18e335833a5ba3a328a68 Mon Sep 17 00:00:00 2001 From: Eugene Date: Tue, 10 Feb 2026 20:24:34 +0800 Subject: [PATCH 02/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20ReadOnlyStream.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask/Program.cs | 21 ++++++---- TestTask/ReadOnlyStream.cs | 84 +++++++++++++++++++++++--------------- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/TestTask/Program.cs b/TestTask/Program.cs index fdf048e..df2eb25 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -16,18 +16,23 @@ public class Program /// Второй параметр - путь до второго файла. static void Main(string[] args) { - IReadOnlyStream inputStream1 = GetInputStream(args[0]); - IReadOnlyStream inputStream2 = GetInputStream(args[1]); - - IList singleLetterStats = FillSingleLetterStats(inputStream1); - IList doubleLetterStats = FillDoubleLetterStats(inputStream2); - + IList singleLetterStats = new List(); + using (IReadOnlyStream inputStream1 = GetInputStream(args[0])) + { + singleLetterStats = FillSingleLetterStats(inputStream1); + } RemoveCharStatsByType(singleLetterStats, CharType.Vowel); - RemoveCharStatsByType(doubleLetterStats, CharType.Consonants); - PrintStatistic(singleLetterStats); + + IList doubleLetterStats = new List(); + using (IReadOnlyStream inputStream2 = GetInputStream(args[1])) + { + doubleLetterStats = FillDoubleLetterStats(inputStream2); + } + RemoveCharStatsByType(doubleLetterStats, CharType.Consonants); PrintStatistic(doubleLetterStats); + // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы. } diff --git a/TestTask/ReadOnlyStream.cs b/TestTask/ReadOnlyStream.cs index d038527..30f257b 100644 --- a/TestTask/ReadOnlyStream.cs +++ b/TestTask/ReadOnlyStream.cs @@ -1,36 +1,36 @@ using System; using System.IO; -using System.Runtime.InteropServices.ComTypes; +using System.Text; namespace TestTask { public class ReadOnlyStream : IReadOnlyStream { + private const string __errorNoMoreSymbols = "Нет символов для чтения"; + private const string __errorStreamSeekNotSupport = "Файловый поток не поддерживает перемещение"; + private const string __errorFileOpen = "Ошибка открытия файла '{0}'."; + private readonly Stream _localStream; + private readonly Encoding _encoding; private bool _disposed = false; /// /// Конструктор класса. - /// Т.к. происходит прямая работа с файлом, необходимо - /// обеспечить ГАРАНТИРОВАННОЕ закрытие файла после окончания работы с таковым! /// /// Полный путь до файла для чтения - public ReadOnlyStream(string fileFullPath) + public ReadOnlyStream(string fileFullPath, Encoding encoding = default) { - IsEof = true; - - // TODO : Заменить на создание реального стрима для чтения файла! - _localStream = null; + CheckPath(fileFullPath); + _encoding = DefineEncoding(encoding); + // TODO : Заменить на создание реального стрима для чтения файла! (заменено) + _localStream = TryOpenStream(fileFullPath); } - + /// /// Флаг окончания файла. /// - public bool IsEof - { - get; // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении - private set; - } + // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении (реализовано) + public bool IsEof => _localStream.Position >= _localStream.Length; /// /// Ф-ция чтения следующего символа из потока. @@ -41,8 +41,14 @@ public bool IsEof public char ReadNextChar() { CheckDisposed(); - // TODO : Необходимо считать очередной символ из _localStream - throw new NotImplementedException(); + // TODO : Необходимо считать очередной символ из _localStream (реализовано) + byte[] buffer = new byte[sizeof(char)]; + int bytesRead = _localStream.Read(buffer, 0, sizeof(char)); + + if (bytesRead != sizeof(char)) + throw new EndOfStreamException(__errorNoMoreSymbols); + + return _encoding.GetChars(buffer)[0]; } /// @@ -50,32 +56,19 @@ public char ReadNextChar() /// public void ResetPositionToStart() { - if (_localStream == null) - { - IsEof = true; - return; - } + if (!_localStream.CanSeek) + throw new InvalidOperationException(__errorStreamSeekNotSupport); _localStream.Position = 0; - IsEof = false; } - protected virtual void Dispose(bool disposing) + public void Dispose() { - if (_disposed) - return; - - if (disposing) + if (!_disposed) { _localStream.Dispose(); + _disposed = true; } - _disposed = true; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); } private void CheckDisposed() @@ -83,5 +76,28 @@ private void CheckDisposed() if (_disposed) throw new ObjectDisposedException(GetType().Name); } + + private Stream TryOpenStream(string fileFullPath) + { + try + { + return File.OpenRead(fileFullPath); + } + catch (IOException ex) + { + throw new IOException(string.Format(__errorFileOpen, fileFullPath), ex); + } + } + + private static void CheckPath(string fileFullPath) + { + if (string.IsNullOrWhiteSpace(fileFullPath)) + throw new ArgumentNullException(nameof(fileFullPath)); + } + + private static Encoding DefineEncoding(Encoding encoding) => + encoding == default + ? Encoding.UTF8 + : encoding; } } From 3b2db89fe8d455dc9de35ffba7cf5325e3673f88 Mon Sep 17 00:00:00 2001 From: Eugene Date: Tue, 10 Feb 2026 20:59:56 +0800 Subject: [PATCH 03/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20FillSingleLetterStats.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask/Program.cs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/TestTask/Program.cs b/TestTask/Program.cs index df2eb25..8b044f9 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -55,15 +55,28 @@ private static IReadOnlyStream GetInputStream(string fileFullPath) private static IList FillSingleLetterStats(IReadOnlyStream stream) { stream.ResetPositionToStart(); + Dictionary letterCountsMap = new Dictionary(); while (!stream.IsEof) { char c = stream.ReadNextChar(); // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый. + if (char.IsLetter(c)) + { + if (letterCountsMap.TryGetValue(c, out LetterStats existingStat)) + { + IncStatistic(existingStat); + } + else + { + letterCountsMap[c] = new LetterStats + { + Letter = c.ToString(), + Count = 1 + }; + } + } } - - //return ???; - - throw new NotImplementedException(); + return new List(letterCountsMap.Values); } /// From 03db2c8efe02e17d5febf9a3570460b7c054f17b Mon Sep 17 00:00:00 2001 From: Eugene Date: Tue, 10 Feb 2026 21:22:43 +0800 Subject: [PATCH 04/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20FillDoubleLetterStats.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask/Program.cs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/TestTask/Program.cs b/TestTask/Program.cs index 8b044f9..fe04b4b 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -89,15 +89,35 @@ private static IList FillSingleLetterStats(IReadOnlyStream stream) private static IList FillDoubleLetterStats(IReadOnlyStream stream) { stream.ResetPositionToStart(); + Dictionary pairCountsMap = new Dictionary(); + char prevC = '\0'; while (!stream.IsEof) { char c = stream.ReadNextChar(); // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый. - } - - //return ???; + if (char.IsLetter(prevC) && char.IsLetter(c)) + { + if (char.ToUpperInvariant(prevC) == char.ToUpperInvariant(c)) + { + string pair = $"{prevC}{c}".ToUpper(); - throw new NotImplementedException(); + if (pairCountsMap.TryGetValue(pair, out LetterStats existingStat)) + { + IncStatistic(existingStat); + } + else + { + pairCountsMap[pair] = new LetterStats + { + Letter = pair, + Count = 1 + }; + } + } + } + prevC = c; + } + return new List(pairCountsMap.Values); } /// From 6e5dec46b296fa550c76957c57f23cd16e113471 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 07:06:46 +0800 Subject: [PATCH 05/19] =?UTF-8?q?=D0=9F=D1=80=D0=B8=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=BB=D1=81=D1=8F=20=D0=B2=20FillSingleLetterStats=20=D0=B8=20?= =?UTF-8?q?FillDoubleLetterStats.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask/IReadOnlyStream.cs | 2 +- TestTask/Program.cs | 98 ++++++++++++++++++++++--------------- TestTask/ReadOnlyStream.cs | 6 +-- 3 files changed, 63 insertions(+), 43 deletions(-) diff --git a/TestTask/IReadOnlyStream.cs b/TestTask/IReadOnlyStream.cs index 7f40832..7d8de04 100644 --- a/TestTask/IReadOnlyStream.cs +++ b/TestTask/IReadOnlyStream.cs @@ -8,7 +8,7 @@ namespace TestTask /// internal interface IReadOnlyStream: IDisposable { - // TODO : Необходимо доработать данный интерфейс (доработано, как IDisposable) + // TODO : Необходимо доработать данный интерфейс (+) char ReadNextChar(); void ResetPositionToStart(); diff --git a/TestTask/Program.cs b/TestTask/Program.cs index fe04b4b..a3ab463 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Data; +using System.Globalization; namespace TestTask { @@ -33,7 +35,8 @@ static void Main(string[] args) PrintStatistic(doubleLetterStats); - // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы. + // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы. (+) + Console.ReadKey(); } /// @@ -55,26 +58,12 @@ private static IReadOnlyStream GetInputStream(string fileFullPath) private static IList FillSingleLetterStats(IReadOnlyStream stream) { stream.ResetPositionToStart(); - Dictionary letterCountsMap = new Dictionary(); + Dictionary letterCountsMap = new Dictionary(); while (!stream.IsEof) { char c = stream.ReadNextChar(); - // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый. - if (char.IsLetter(c)) - { - if (letterCountsMap.TryGetValue(c, out LetterStats existingStat)) - { - IncStatistic(existingStat); - } - else - { - letterCountsMap[c] = new LetterStats - { - Letter = c.ToString(), - Count = 1 - }; - } - } + // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый. (+) + FillSingleStatMap(c, letterCountsMap); } return new List(letterCountsMap.Values); } @@ -94,27 +83,8 @@ private static IList FillDoubleLetterStats(IReadOnlyStream stream) while (!stream.IsEof) { char c = stream.ReadNextChar(); - // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый. - if (char.IsLetter(prevC) && char.IsLetter(c)) - { - if (char.ToUpperInvariant(prevC) == char.ToUpperInvariant(c)) - { - string pair = $"{prevC}{c}".ToUpper(); - - if (pairCountsMap.TryGetValue(pair, out LetterStats existingStat)) - { - IncStatistic(existingStat); - } - else - { - pairCountsMap[pair] = new LetterStats - { - Letter = pair, - Count = 1 - }; - } - } - } + // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый. (+) + FillDoubleStatMap(prevC, c, pairCountsMap); prevC = c; } return new List(pairCountsMap.Values); @@ -163,5 +133,55 @@ private static void IncStatistic(LetterStats letterStats) } + + + + private static void FillSingleStatMap(char c, Dictionary map) + { + if (IsGoodSingleChar(c)) + { + FillStatMap(BuildSingleKey(c), map); + } + } + + private static void FillDoubleStatMap(char c1, char c2, Dictionary map) + { + if (IsGoodDoubleChar(c1, c2)) + { + FillStatMap(BuildDoubleKey(c1, c2), map); + } + } + + private static string BuildSingleKey(char c) => c.ToString(); + + private static string BuildDoubleKey(char c1, char c2) => $"{c1}{c2}".ToUpper(); + + private static bool IsGoodSingleChar(char c) => + char.IsLetter(c); + + private static bool IsGoodDoubleChar(char c1, char c2) => + IsLetterBoth(c1, c2) && IsEquallyBoth(c1, c2); + + private static bool IsLetterBoth(char c1, char c2) => + char.IsLetter(c1) && char.IsLetter(c2); + + private static bool IsEquallyBoth(char c1, char c2) => + char.ToUpperInvariant(c1) == char.ToUpperInvariant(c2); + + private static void FillStatMap(string key, Dictionary map) + { + if (map.TryGetValue(key, out LetterStats existingStat)) + { + IncStatistic(existingStat); + } + else + { + map[key] = new LetterStats + { + Letter = key, + Count = 1 + }; + } + } } } diff --git a/TestTask/ReadOnlyStream.cs b/TestTask/ReadOnlyStream.cs index 30f257b..97c5e75 100644 --- a/TestTask/ReadOnlyStream.cs +++ b/TestTask/ReadOnlyStream.cs @@ -22,14 +22,14 @@ public ReadOnlyStream(string fileFullPath, Encoding encoding = default) { CheckPath(fileFullPath); _encoding = DefineEncoding(encoding); - // TODO : Заменить на создание реального стрима для чтения файла! (заменено) + // TODO : Заменить на создание реального стрима для чтения файла! (+) _localStream = TryOpenStream(fileFullPath); } /// /// Флаг окончания файла. /// - // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении (реализовано) + // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении (+) public bool IsEof => _localStream.Position >= _localStream.Length; /// @@ -41,7 +41,7 @@ public ReadOnlyStream(string fileFullPath, Encoding encoding = default) public char ReadNextChar() { CheckDisposed(); - // TODO : Необходимо считать очередной символ из _localStream (реализовано) + // TODO : Необходимо считать очередной символ из _localStream (+) byte[] buffer = new byte[sizeof(char)]; int bytesRead = _localStream.Read(buffer, 0, sizeof(char)); From 1613c677f4a6393c15c663dd6cef050ca1d7d4aa Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 08:37:37 +0800 Subject: [PATCH 06/19] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D1=81=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D1=83=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D1=81=D1=82=D0=B8=D0=BA=D0=B8=20=D0=B2=20Hel?= =?UTF-8?q?perFillStat.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask/Helpers/HelperFillStat.cs | 64 ++++++++++++++++++++++++++++ TestTask/Program.cs | 68 ++---------------------------- TestTask/TestTask.csproj | 1 + 3 files changed, 68 insertions(+), 65 deletions(-) create mode 100644 TestTask/Helpers/HelperFillStat.cs diff --git a/TestTask/Helpers/HelperFillStat.cs b/TestTask/Helpers/HelperFillStat.cs new file mode 100644 index 0000000..51dcc2c --- /dev/null +++ b/TestTask/Helpers/HelperFillStat.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; + +namespace TestTask.Helpers +{ + public static class HelperFillStat + { + public static void FillSingleStatMap(char c, Dictionary map) + { + if (IsGoodSingleChar(c)) + { + FillStatMap(BuildSingleKey(c), map); + } + } + + public static void FillDoubleStatMap(char c1, char c2, Dictionary map) + { + if (IsGoodDoubleChar(c1, c2)) + { + FillStatMap(BuildDoubleKey(c1, c2), map); + } + } + + private static string BuildSingleKey(char c) => c.ToString(); + + private static string BuildDoubleKey(char c1, char c2) => $"{c1}{c2}".ToUpper(); + + private static bool IsGoodSingleChar(char c) => + char.IsLetter(c); + + private static bool IsGoodDoubleChar(char c1, char c2) => + IsLetterBoth(c1, c2) && IsEquallyBoth(c1, c2); + + private static bool IsLetterBoth(char c1, char c2) => + char.IsLetter(c1) && char.IsLetter(c2); + + private static bool IsEquallyBoth(char c1, char c2) => + char.ToUpperInvariant(c1) == char.ToUpperInvariant(c2); + + private static void FillStatMap(string key, Dictionary map) + { + if (map.TryGetValue(key, out LetterStats existingStat)) + { + IncStatistic(existingStat); + } + else + { + map[key] = new LetterStats + { + Letter = key, + Count = 1 + }; + } + } + + /// + /// Метод увеличивает счётчик вхождений по переданной структуре. + /// + /// + private static void IncStatistic(LetterStats letterStats) + { + letterStats.Count++; + } + } +} diff --git a/TestTask/Program.cs b/TestTask/Program.cs index a3ab463..5ea82d3 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; -using System.Data; -using System.Globalization; +using TestTask.Helpers; namespace TestTask { @@ -63,7 +62,7 @@ private static IList FillSingleLetterStats(IReadOnlyStream stream) { char c = stream.ReadNextChar(); // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый. (+) - FillSingleStatMap(c, letterCountsMap); + HelperFillStat.FillSingleStatMap(c, letterCountsMap); } return new List(letterCountsMap.Values); } @@ -84,7 +83,7 @@ private static IList FillDoubleLetterStats(IReadOnlyStream stream) { char c = stream.ReadNextChar(); // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый. (+) - FillDoubleStatMap(prevC, c, pairCountsMap); + HelperFillStat.FillDoubleStatMap(prevC, c, pairCountsMap); prevC = c; } return new List(pairCountsMap.Values); @@ -122,66 +121,5 @@ private static void PrintStatistic(IEnumerable letters) // TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту! throw new NotImplementedException(); } - - /// - /// Метод увеличивает счётчик вхождений по переданной структуре. - /// - /// - private static void IncStatistic(LetterStats letterStats) - { - letterStats.Count++; - } - - - - - - private static void FillSingleStatMap(char c, Dictionary map) - { - if (IsGoodSingleChar(c)) - { - FillStatMap(BuildSingleKey(c), map); - } - } - - private static void FillDoubleStatMap(char c1, char c2, Dictionary map) - { - if (IsGoodDoubleChar(c1, c2)) - { - FillStatMap(BuildDoubleKey(c1, c2), map); - } - } - - private static string BuildSingleKey(char c) => c.ToString(); - - private static string BuildDoubleKey(char c1, char c2) => $"{c1}{c2}".ToUpper(); - - private static bool IsGoodSingleChar(char c) => - char.IsLetter(c); - - private static bool IsGoodDoubleChar(char c1, char c2) => - IsLetterBoth(c1, c2) && IsEquallyBoth(c1, c2); - - private static bool IsLetterBoth(char c1, char c2) => - char.IsLetter(c1) && char.IsLetter(c2); - - private static bool IsEquallyBoth(char c1, char c2) => - char.ToUpperInvariant(c1) == char.ToUpperInvariant(c2); - - private static void FillStatMap(string key, Dictionary map) - { - if (map.TryGetValue(key, out LetterStats existingStat)) - { - IncStatistic(existingStat); - } - else - { - map[key] = new LetterStats - { - Letter = key, - Count = 1 - }; - } - } } } diff --git a/TestTask/TestTask.csproj b/TestTask/TestTask.csproj index 9903e02..e1eb7a9 100644 --- a/TestTask/TestTask.csproj +++ b/TestTask/TestTask.csproj @@ -44,6 +44,7 @@ + From 6921e2d52f9d3aecf631953c9cdd322f16fbe17e Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 08:51:25 +0800 Subject: [PATCH 07/19] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=8E=D0=BD=D0=B8=D1=82-=D1=82=D0=BD=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D0=B2.=20=D0=A3=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=B8=20=D0=B4=D0=BB=D1=8F=20=D1=82=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask.UnitTests/Class1.cs | 12 ++ TestTask.UnitTests/Properties/AssemblyInfo.cs | 33 ++++ TestTask.UnitTests/TestTask.UnitTests.csproj | 166 ++++++++++++++++++ TestTask.UnitTests/app.config | 23 +++ TestTask.UnitTests/packages.config | 32 ++++ TestTask/Helpers/HelperFillStat.cs | 13 +- TestTask/TestTask.sln | 13 +- 7 files changed, 285 insertions(+), 7 deletions(-) create mode 100644 TestTask.UnitTests/Class1.cs create mode 100644 TestTask.UnitTests/Properties/AssemblyInfo.cs create mode 100644 TestTask.UnitTests/TestTask.UnitTests.csproj create mode 100644 TestTask.UnitTests/app.config create mode 100644 TestTask.UnitTests/packages.config diff --git a/TestTask.UnitTests/Class1.cs b/TestTask.UnitTests/Class1.cs new file mode 100644 index 0000000..dc7d49c --- /dev/null +++ b/TestTask.UnitTests/Class1.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TestTask.UnitTests +{ + public class Class1 + { + } +} diff --git a/TestTask.UnitTests/Properties/AssemblyInfo.cs b/TestTask.UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7140d2e --- /dev/null +++ b/TestTask.UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Общие сведения об этой сборке предоставляются следующим набором +// набора атрибутов. Измените значения этих атрибутов для изменения сведений, +// связанные со сборкой. +[assembly: AssemblyTitle("TestTask.UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestTask.UnitTests")] +[assembly: AssemblyCopyright("Copyright © 2026")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми +// для компонентов COM. Если необходимо обратиться к типу в этой сборке через +// COM, задайте атрибуту ComVisible значение TRUE для этого типа. +[assembly: ComVisible(false)] + +// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM +[assembly: Guid("99a1b5e9-b57d-4591-b8df-0e6796c45d4c")] + +// Сведения о версии сборки состоят из указанных ниже четырех значений: +// +// Основной номер версии +// Дополнительный номер версии +// Номер сборки +// Редакция +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/TestTask.UnitTests/TestTask.UnitTests.csproj b/TestTask.UnitTests/TestTask.UnitTests.csproj new file mode 100644 index 0000000..a8be7c3 --- /dev/null +++ b/TestTask.UnitTests/TestTask.UnitTests.csproj @@ -0,0 +1,166 @@ + + + + + + + + + Debug + AnyCPU + {99A1B5E9-B57D-4591-B8DF-0E6796C45D4C} + Library + Properties + TestTask.UnitTests + TestTask.UnitTests + v4.8 + 512 + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\TestTask\packages\FluentAssertions.8.8.0\lib\net47\FluentAssertions.dll + + + ..\TestTask\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll + + + ..\TestTask\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\TestTask\packages\Microsoft.Testing.Platform.MSBuild.1.9.1\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll + + + ..\TestTask\packages\Microsoft.Testing.Extensions.Telemetry.1.9.1\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll + + + ..\TestTask\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.9.1\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll + + + ..\TestTask\packages\Microsoft.Testing.Platform.1.9.1\lib\netstandard2.0\Microsoft.Testing.Platform.dll + + + ..\TestTask\packages\Microsoft.TestPlatform.ObjectModel.17.13.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll + + + ..\TestTask\packages\Microsoft.TestPlatform.ObjectModel.17.13.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll + + + ..\TestTask\packages\Microsoft.TestPlatform.ObjectModel.17.13.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + + + ..\TestTask\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll + + + + ..\TestTask\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\TestTask\packages\System.Collections.Immutable.6.0.0\lib\net461\System.Collections.Immutable.dll + + + + + ..\TestTask\packages\System.Diagnostics.DiagnosticSource.6.0.0\lib\net461\System.Diagnostics.DiagnosticSource.dll + + + ..\TestTask\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll + + + + ..\TestTask\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\TestTask\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll + + + + ..\TestTask\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + + ..\TestTask\packages\System.Security.AccessControl.5.0.0\lib\net461\System.Security.AccessControl.dll + + + ..\TestTask\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll + + + ..\TestTask\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + + + + + + + ..\TestTask\packages\xunit.v3.assert.3.2.2\lib\netstandard2.0\xunit.v3.assert.dll + + + ..\TestTask\packages\xunit.v3.common.3.2.2\lib\netstandard2.0\xunit.v3.common.dll + + + ..\TestTask\packages\xunit.v3.extensibility.core.3.2.2\lib\netstandard2.0\xunit.v3.core.dll + + + ..\TestTask\packages\xunit.v3.core.mtp-v1.3.2.2\lib\net472\xunit.v3.mtp-v1.dll + + + ..\TestTask\packages\xunit.v3.runner.common.3.2.2\lib\netstandard2.0\xunit.v3.runner.common.dll + + + ..\TestTask\packages\xunit.v3.runner.inproc.console.3.2.2\lib\net472\xunit.v3.runner.inproc.console.dll + + + + + + + + + {6e9922f2-af75-4807-b6e4-f74499317c39} + TestTask + + + + + + + + + + + + + + Данный проект ссылается на пакеты NuGet, отсутствующие на этом компьютере. Используйте восстановление пакетов NuGet, чтобы скачать их. Дополнительную информацию см. по адресу: http://go.microsoft.com/fwlink/?LinkID=322105. Отсутствует следующий файл: {0}. + + + + + + + + + + + \ No newline at end of file diff --git a/TestTask.UnitTests/app.config b/TestTask.UnitTests/app.config new file mode 100644 index 0000000..cb74baa --- /dev/null +++ b/TestTask.UnitTests/app.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestTask.UnitTests/packages.config b/TestTask.UnitTests/packages.config new file mode 100644 index 0000000..5b81b3d --- /dev/null +++ b/TestTask.UnitTests/packages.config @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestTask/Helpers/HelperFillStat.cs b/TestTask/Helpers/HelperFillStat.cs index 51dcc2c..edb275a 100644 --- a/TestTask/Helpers/HelperFillStat.cs +++ b/TestTask/Helpers/HelperFillStat.cs @@ -44,14 +44,17 @@ private static void FillStatMap(string key, Dictionary map) } else { - map[key] = new LetterStats - { - Letter = key, - Count = 1 - }; + map[key] = CreateLetterStats(key); } } + private static LetterStats CreateLetterStats(string key) => + new LetterStats + { + Letter = key, + Count = 1 + }; + /// /// Метод увеличивает счётчик вхождений по переданной структуре. /// diff --git a/TestTask/TestTask.sln b/TestTask/TestTask.sln index 494bc7e..3540f1b 100644 --- a/TestTask/TestTask.sln +++ b/TestTask/TestTask.sln @@ -1,10 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27428.2011 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36429.23 d17.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestTask", "TestTask.csproj", "{6E9922F2-AF75-4807-B6E4-F74499317C39}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestTask.UnitTests", "..\TestTask.UnitTests\TestTask.UnitTests.csproj", "{99A1B5E9-B57D-4591-B8DF-0E6796C45D4C}" + ProjectSection(ProjectDependencies) = postProject + {6E9922F2-AF75-4807-B6E4-F74499317C39} = {6E9922F2-AF75-4807-B6E4-F74499317C39} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +20,10 @@ Global {6E9922F2-AF75-4807-B6E4-F74499317C39}.Debug|Any CPU.Build.0 = Debug|Any CPU {6E9922F2-AF75-4807-B6E4-F74499317C39}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E9922F2-AF75-4807-B6E4-F74499317C39}.Release|Any CPU.Build.0 = Release|Any CPU + {99A1B5E9-B57D-4591-B8DF-0E6796C45D4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99A1B5E9-B57D-4591-B8DF-0E6796C45D4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99A1B5E9-B57D-4591-B8DF-0E6796C45D4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99A1B5E9-B57D-4591-B8DF-0E6796C45D4C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From cb2a8cf0bd402b09f183d685fccec3a4a6e638b2 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 09:03:43 +0800 Subject: [PATCH 08/19] =?UTF-8?q?=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0=D0=BB=20Fa?= =?UTF-8?q?keReadOnlyStream.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Fakes/FakeReadOnlyStream.cs | 35 +++++++++++++++++++ TestTask.UnitTests/TestTask.UnitTests.csproj | 4 +++ TestTask/IReadOnlyStream.cs | 2 +- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 TestTask.UnitTests/Fakes/FakeReadOnlyStream.cs diff --git a/TestTask.UnitTests/Fakes/FakeReadOnlyStream.cs b/TestTask.UnitTests/Fakes/FakeReadOnlyStream.cs new file mode 100644 index 0000000..560e604 --- /dev/null +++ b/TestTask.UnitTests/Fakes/FakeReadOnlyStream.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TestTask.UnitTests.Fakes +{ + public class FakeReadOnlyStream : IReadOnlyStream + { + public bool IsEof => throw new NotImplementedException(); + + #region .ctors + public FakeReadOnlyStream(string inputData) + { + + } + #endregion + + public void Dispose() + { + throw new NotImplementedException(); + } + + public char ReadNextChar() + { + throw new NotImplementedException(); + } + + public void ResetPositionToStart() + { + throw new NotImplementedException(); + } + } +} diff --git a/TestTask.UnitTests/TestTask.UnitTests.csproj b/TestTask.UnitTests/TestTask.UnitTests.csproj index a8be7c3..d42945d 100644 --- a/TestTask.UnitTests/TestTask.UnitTests.csproj +++ b/TestTask.UnitTests/TestTask.UnitTests.csproj @@ -133,6 +133,7 @@ + @@ -149,6 +150,9 @@ + + + diff --git a/TestTask/IReadOnlyStream.cs b/TestTask/IReadOnlyStream.cs index 7d8de04..35dfc83 100644 --- a/TestTask/IReadOnlyStream.cs +++ b/TestTask/IReadOnlyStream.cs @@ -6,7 +6,7 @@ namespace TestTask /// Интерфейс для работы с файлом в сильно урезаном виде. /// Умеет всего 2 вещи: прочитать символ, и перемотать стрим на начало. /// - internal interface IReadOnlyStream: IDisposable + public interface IReadOnlyStream: IDisposable { // TODO : Необходимо доработать данный интерфейс (+) char ReadNextChar(); From 5fcac989dd9ec41c938362033daeb24542e8a7fa Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 12:25:30 +0800 Subject: [PATCH 09/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=BF=D0=B5=D1=80=D0=B2=D1=8B=D0=B9=20=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=20=D0=B4=D0=BB=D1=8F=20FillStats.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask.UnitTests/Class1.cs | 12 ----- .../Fakes/FakeReadOnlyStream.cs | 36 ++++++--------- TestTask.UnitTests/FillStatsTests.cs | 38 +++++++++++++++ TestTask.UnitTests/InputData/DataFillStat.cs | 8 ++++ TestTask.UnitTests/TestTask.UnitTests.csproj | 46 +++++++++---------- TestTask.UnitTests/packages.config | 16 +++---- TestTask/Program.cs | 28 +++++------ TestTask/ReadOnlyStream.cs | 38 +++++++++------ TestTask/TestTask.csproj | 3 +- 9 files changed, 128 insertions(+), 97 deletions(-) delete mode 100644 TestTask.UnitTests/Class1.cs create mode 100644 TestTask.UnitTests/FillStatsTests.cs create mode 100644 TestTask.UnitTests/InputData/DataFillStat.cs diff --git a/TestTask.UnitTests/Class1.cs b/TestTask.UnitTests/Class1.cs deleted file mode 100644 index dc7d49c..0000000 --- a/TestTask.UnitTests/Class1.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace TestTask.UnitTests -{ - public class Class1 - { - } -} diff --git a/TestTask.UnitTests/Fakes/FakeReadOnlyStream.cs b/TestTask.UnitTests/Fakes/FakeReadOnlyStream.cs index 560e604..966d8da 100644 --- a/TestTask.UnitTests/Fakes/FakeReadOnlyStream.cs +++ b/TestTask.UnitTests/Fakes/FakeReadOnlyStream.cs @@ -1,35 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.IO; using System.Text; -using System.Threading.Tasks; namespace TestTask.UnitTests.Fakes { - public class FakeReadOnlyStream : IReadOnlyStream + public class FakeReadOnlyStream : ReadOnlyStream { - public bool IsEof => throw new NotImplementedException(); - - #region .ctors - public FakeReadOnlyStream(string inputData) - { - - } - #endregion - - public void Dispose() - { - throw new NotImplementedException(); - } - - public char ReadNextChar() + public FakeReadOnlyStream(string inputData, Encoding encoding = default) : base(inputData, encoding) { - throw new NotImplementedException(); } - public void ResetPositionToStart() + public override Stream CreateStream(string param) { - throw new NotImplementedException(); + try + { + byte[] bytes = Encoding.GetBytes(param); + return new MemoryStream(bytes); + } + catch + { + return Stream.Null; + } } } } diff --git a/TestTask.UnitTests/FillStatsTests.cs b/TestTask.UnitTests/FillStatsTests.cs new file mode 100644 index 0000000..fbf9c85 --- /dev/null +++ b/TestTask.UnitTests/FillStatsTests.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using TestTask.UnitTests.Fakes; +using TestTask.UnitTests.InputData; +using Xunit; +using Xunit.Abstractions; + +namespace TestTask.UnitTests +{ + public class FillStatsTests + { + public static List Data = new List() + { + // new object[] { null, new Exception() }, + new object[] { "", }, + new object[] { "\0\0\0\0\0\0\0\0\0\0\t\t\t\r\n\r\n"}, + new object[] { "123454321708090706,-./" }, + }; + + public FillStatsTests() + { + //foreach(var inputData in DataFillStat.Data) + // _sut.Add(new FakeReadOnlyStream(inputData)); + } + + // [Theory] + // [MemberData(nameof(Data))] + [Fact] + public void create_reader_with_null_param_will_be_exception() + { + Assert.Throws(() => new FakeReadOnlyStream(null)); + } + } +} diff --git a/TestTask.UnitTests/InputData/DataFillStat.cs b/TestTask.UnitTests/InputData/DataFillStat.cs new file mode 100644 index 0000000..27898c9 --- /dev/null +++ b/TestTask.UnitTests/InputData/DataFillStat.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace TestTask.UnitTests.InputData +{ + public static class DataFillStat + { + } +} diff --git a/TestTask.UnitTests/TestTask.UnitTests.csproj b/TestTask.UnitTests/TestTask.UnitTests.csproj index d42945d..64fcdcb 100644 --- a/TestTask.UnitTests/TestTask.UnitTests.csproj +++ b/TestTask.UnitTests/TestTask.UnitTests.csproj @@ -1,5 +1,6 @@ + @@ -27,6 +28,7 @@ DEBUG;TRACE prompt 4 + x86 pdbonly @@ -112,46 +114,37 @@ - - ..\TestTask\packages\xunit.v3.assert.3.2.2\lib\netstandard2.0\xunit.v3.assert.dll + + False + ..\TestTask\bin\Debug\TestTask.exe - - ..\TestTask\packages\xunit.v3.common.3.2.2\lib\netstandard2.0\xunit.v3.common.dll + + ..\TestTask\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll + True - - ..\TestTask\packages\xunit.v3.extensibility.core.3.2.2\lib\netstandard2.0\xunit.v3.core.dll + + ..\TestTask\packages\xunit.assert.2.9.3\lib\netstandard1.1\xunit.assert.dll - - ..\TestTask\packages\xunit.v3.core.mtp-v1.3.2.2\lib\net472\xunit.v3.mtp-v1.dll + + ..\TestTask\packages\xunit.extensibility.core.2.9.3\lib\net452\xunit.core.dll - - ..\TestTask\packages\xunit.v3.runner.common.3.2.2\lib\netstandard2.0\xunit.v3.runner.common.dll - - - ..\TestTask\packages\xunit.v3.runner.inproc.console.3.2.2\lib\net472\xunit.v3.runner.inproc.console.dll + + ..\TestTask\packages\xunit.extensibility.execution.2.9.3\lib\net452\xunit.execution.desktop.dll - + + - - - {6e9922f2-af75-4807-b6e4-f74499317c39} - TestTask - - - - - - - + + @@ -164,7 +157,10 @@ + + + \ No newline at end of file diff --git a/TestTask.UnitTests/packages.config b/TestTask.UnitTests/packages.config index 5b81b3d..4b993ee 100644 --- a/TestTask.UnitTests/packages.config +++ b/TestTask.UnitTests/packages.config @@ -19,14 +19,12 @@ - + + + + + + + - - - - - - - - \ No newline at end of file diff --git a/TestTask/Program.cs b/TestTask/Program.cs index 5ea82d3..30f3538 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -17,21 +17,21 @@ public class Program /// Второй параметр - путь до второго файла. static void Main(string[] args) { - IList singleLetterStats = new List(); - using (IReadOnlyStream inputStream1 = GetInputStream(args[0])) - { - singleLetterStats = FillSingleLetterStats(inputStream1); - } - RemoveCharStatsByType(singleLetterStats, CharType.Vowel); - PrintStatistic(singleLetterStats); + //IList singleLetterStats = new List(); + //using (IReadOnlyStream inputStream1 = GetInputStream(args[0])) + //{ + // singleLetterStats = FillSingleLetterStats(inputStream1); + //} + //RemoveCharStatsByType(singleLetterStats, CharType.Vowel); + //PrintStatistic(singleLetterStats); - IList doubleLetterStats = new List(); - using (IReadOnlyStream inputStream2 = GetInputStream(args[1])) - { - doubleLetterStats = FillDoubleLetterStats(inputStream2); - } - RemoveCharStatsByType(doubleLetterStats, CharType.Consonants); - PrintStatistic(doubleLetterStats); + //IList doubleLetterStats = new List(); + //using (IReadOnlyStream inputStream2 = GetInputStream(args[1])) + //{ + // doubleLetterStats = FillDoubleLetterStats(inputStream2); + //} + //RemoveCharStatsByType(doubleLetterStats, CharType.Consonants); + //PrintStatistic(doubleLetterStats); // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы. (+) diff --git a/TestTask/ReadOnlyStream.cs b/TestTask/ReadOnlyStream.cs index 97c5e75..8b2bf43 100644 --- a/TestTask/ReadOnlyStream.cs +++ b/TestTask/ReadOnlyStream.cs @@ -9,10 +9,18 @@ public class ReadOnlyStream : IReadOnlyStream private const string __errorNoMoreSymbols = "Нет символов для чтения"; private const string __errorStreamSeekNotSupport = "Файловый поток не поддерживает перемещение"; private const string __errorFileOpen = "Ошибка открытия файла '{0}'."; + private const string __errorNullStream = "Ошибка создания потока."; private readonly Stream _localStream; - private readonly Encoding _encoding; private bool _disposed = false; + + public Encoding Encoding { get; } + + /// + /// Флаг окончания файла. + /// + // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении (+) + public bool IsEof => _localStream.Position >= _localStream.Length; /// /// Конструктор класса. @@ -20,17 +28,17 @@ public class ReadOnlyStream : IReadOnlyStream /// Полный путь до файла для чтения public ReadOnlyStream(string fileFullPath, Encoding encoding = default) { - CheckPath(fileFullPath); - _encoding = DefineEncoding(encoding); + Encoding = DefineEncoding(encoding); // TODO : Заменить на создание реального стрима для чтения файла! (+) - _localStream = TryOpenStream(fileFullPath); + _localStream = CreateStream(fileFullPath); + CheckStream(); } - /// - /// Флаг окончания файла. - /// - // TODO : Заполнять данный флаг при достижении конца файла/стрима при чтении (+) - public bool IsEof => _localStream.Position >= _localStream.Length; + public virtual Stream CreateStream(string param) + { + CheckPath(param); + return TryOpenStream(param); + } /// /// Ф-ция чтения следующего символа из потока. @@ -44,11 +52,9 @@ public char ReadNextChar() // TODO : Необходимо считать очередной символ из _localStream (+) byte[] buffer = new byte[sizeof(char)]; int bytesRead = _localStream.Read(buffer, 0, sizeof(char)); - if (bytesRead != sizeof(char)) throw new EndOfStreamException(__errorNoMoreSymbols); - - return _encoding.GetChars(buffer)[0]; + return Encoding.GetChars(buffer)[0]; } /// @@ -77,7 +83,13 @@ private void CheckDisposed() throw new ObjectDisposedException(GetType().Name); } - private Stream TryOpenStream(string fileFullPath) + private void CheckStream() + { + if (_localStream == null || _localStream == Stream.Null) + throw new ArgumentNullException(nameof(Stream), __errorNullStream); + } + + private static Stream TryOpenStream(string fileFullPath) { try { diff --git a/TestTask/TestTask.csproj b/TestTask/TestTask.csproj index e1eb7a9..2341fe1 100644 --- a/TestTask/TestTask.csproj +++ b/TestTask/TestTask.csproj @@ -14,7 +14,7 @@ - AnyCPU + x86 true full false @@ -22,6 +22,7 @@ DEBUG;TRACE prompt 4 + false AnyCPU From 3f82f501505ab108bdb5b4cd8d4aa06a89e439d7 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 12:32:40 +0800 Subject: [PATCH 10/19] =?UTF-8?q?=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0=D0=BB=20wo?= =?UTF-8?q?rkflow=20=D0=B4=D0=BB=D1=8F=20=D0=B7=D0=B0=D0=BF=D1=83=D1=81?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/TestTask-tests-workflow.yml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/TestTask-tests-workflow.yml diff --git a/.github/workflows/TestTask-tests-workflow.yml b/.github/workflows/TestTask-tests-workflow.yml new file mode 100644 index 0000000..ae31fa5 --- /dev/null +++ b/.github/workflows/TestTask-tests-workflow.yml @@ -0,0 +1,21 @@ +name: TestTask Build and Test # Имя workflow, отображается в интерфейсе GitHub Actions +on: + push: # Запускается при каждом push в репозиторий + branches: + - master # ветки, для которых нужно запускать тесты + +jobs: # Определяет набор задач (jobs), которые будут выполнены + service-build: # Имя задачи + runs-on: windows-latest # на каком окружении будет выполняться задача + steps: # Список шагов, которые будут выполнены в рамках задачи + - name: Checkout repository + uses: actions/checkout@v3 + - name: Restore NuGet packages + run: | + nuget restore TestTask\TestTask.sln # Ваш sln-файл решения + - name: Build solution + run: | + & "${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\amd64\msbuild.exe" /m TestTask\TestTask.sln # Сборка решения + - name: Run tests + working-directory: TestTask.UnitTests + run: dotnet test TestTask.UnitTests.csproj --verbosity normal # Выполняет команду dotnet test для запуска юнит-тестов \ No newline at end of file From 0ed8b53eac9bad68171d03b756a8b64416375256 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 14:04:26 +0800 Subject: [PATCH 11/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?FillStats.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask.UnitTests/FillStatsTests.cs | 86 ++++++++++++++++++++++------ TestTask/Helpers/HelperFillStat.cs | 51 +++++++++++++++-- TestTask/Program.cs | 68 +++++----------------- 3 files changed, 129 insertions(+), 76 deletions(-) diff --git a/TestTask.UnitTests/FillStatsTests.cs b/TestTask.UnitTests/FillStatsTests.cs index fbf9c85..fda25d6 100644 --- a/TestTask.UnitTests/FillStatsTests.cs +++ b/TestTask.UnitTests/FillStatsTests.cs @@ -1,38 +1,90 @@ -using System; +using FluentAssertions; +using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using FluentAssertions; +using System.IO; +using TestTask.Helpers; using TestTask.UnitTests.Fakes; -using TestTask.UnitTests.InputData; using Xunit; -using Xunit.Abstractions; namespace TestTask.UnitTests { public class FillStatsTests { - public static List Data = new List() - { - // new object[] { null, new Exception() }, - new object[] { "", }, - new object[] { "\0\0\0\0\0\0\0\0\0\0\t\t\t\r\n\r\n"}, + // format: inputData + public static List DataWithNoLetters = new List() + { + new object[] { "" }, new object[] { "123454321708090706,-./" }, + new object[] { "12345\0\0\0\0\0\0\0\0\0\0\t\t\t\r\n\r\n"}, + }; + + // format: inputData, singleStatCount, doubleStatCount + public static List DataWithLettersOnly = new List() + { + new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 9, 4 }, + new object[] { "аабббввввгдежзяяя", 9, 4 }, + new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 13, 4}, }; public FillStatsTests() { - //foreach(var inputData in DataFillStat.Data) - // _sut.Add(new FakeReadOnlyStream(inputData)); } - // [Theory] - // [MemberData(nameof(Data))] [Fact] public void create_reader_with_null_param_will_be_exception() { - Assert.Throws(() => new FakeReadOnlyStream(null)); + FakeReadOnlyStream sut; + + Assert.Throws(() => sut = new FakeReadOnlyStream(null)); + } + + [Fact] + public void fill_stats_with_control_symbols_only_will_be_exception() + { + string stream = "\0\0\0\0\0\0\0\0\0\0\t\t\t\r\n\r\n"; + + IList singleResult = new List(); + IList doubleResult = new List(); + + using (IReadOnlyStream sut = new FakeReadOnlyStream(stream)) + { + Assert.Throws(() => singleResult = HelperFillStat.FillSingleLetterStats(sut)); + Assert.Throws(() => doubleResult = HelperFillStat.FillDoubleLetterStats(sut)); + } + } + + [Theory] + [MemberData(nameof(DataWithNoLetters))] + public void fill_stats_with_no_letters_symbols_only_will_be_empty_stats(string inputData) + { + IList singleResult = new List(); + IList doubleResult = new List(); + + using (IReadOnlyStream sut = new FakeReadOnlyStream(inputData)) + { + singleResult = HelperFillStat.FillSingleLetterStats(sut); + doubleResult = HelperFillStat.FillDoubleLetterStats(sut); + } + + singleResult.Should().HaveCount(0); + doubleResult.Should().HaveCount(0); + } + + [Theory] + [MemberData(nameof(DataWithLettersOnly))] + public void fill_stats_with_letters_symbols_only_will_has_count(string inputData, int singleStatsCount, int doubleStatsCount) + { + IList singleResult = new List(); + IList doubleResult = new List(); + + using (IReadOnlyStream sut = new FakeReadOnlyStream(inputData)) + { + singleResult = HelperFillStat.FillSingleLetterStats(sut); + doubleResult = HelperFillStat.FillDoubleLetterStats(sut); + } + + singleResult.Should().HaveCount(singleStatsCount); + doubleResult.Should().HaveCount(doubleStatsCount); } } } diff --git a/TestTask/Helpers/HelperFillStat.cs b/TestTask/Helpers/HelperFillStat.cs index edb275a..554a56f 100644 --- a/TestTask/Helpers/HelperFillStat.cs +++ b/TestTask/Helpers/HelperFillStat.cs @@ -4,15 +4,55 @@ namespace TestTask.Helpers { public static class HelperFillStat { - public static void FillSingleStatMap(char c, Dictionary map) + /// + /// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения каждой буквы. + /// Статистика РЕГИСТРОЗАВИСИМАЯ! + /// + /// Стрим для считывания символов для последующего анализа + /// Коллекция статистик по каждой букве, что была прочитана из стрима. + public static IList FillSingleLetterStats(IReadOnlyStream stream) + { + stream.ResetPositionToStart(); + Dictionary letterCountsMap = new Dictionary(); + while (!stream.IsEof) + { + char c = stream.ReadNextChar(); + // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый. (+) + FillSingleStatMap(c, letterCountsMap); + } + return new List(letterCountsMap.Values); + } + + /// + /// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения парных букв. + /// В статистику должны попадать только пары из одинаковых букв, например АА, СС, УУ, ЕЕ и т.д. + /// Статистика - НЕ регистрозависимая! + /// + /// Стрим для считывания символов для последующего анализа + /// Коллекция статистик по каждой букве, что была прочитана из стрима. + public static IList FillDoubleLetterStats(IReadOnlyStream stream) + { + stream.ResetPositionToStart(); + Dictionary pairCountsMap = new Dictionary(); + char prevC = '\0'; + while (!stream.IsEof) + { + char c = stream.ReadNextChar(); + // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый. (+) + HelperFillStat.FillDoubleStatMap(prevC, c, pairCountsMap); + prevC = c; + } + return new List(pairCountsMap.Values); + } + + private static void FillSingleStatMap(char c, Dictionary map) { if (IsGoodSingleChar(c)) { FillStatMap(BuildSingleKey(c), map); } } - - public static void FillDoubleStatMap(char c1, char c2, Dictionary map) + private static void FillDoubleStatMap(char c1, char c2, Dictionary map) { if (IsGoodDoubleChar(c1, c2)) { @@ -40,7 +80,8 @@ private static void FillStatMap(string key, Dictionary map) { if (map.TryGetValue(key, out LetterStats existingStat)) { - IncStatistic(existingStat); + IncStatistic(ref existingStat); + map[key] = existingStat; } else { @@ -59,7 +100,7 @@ private static LetterStats CreateLetterStats(string key) => /// Метод увеличивает счётчик вхождений по переданной структуре. /// /// - private static void IncStatistic(LetterStats letterStats) + private static void IncStatistic(ref LetterStats letterStats) { letterStats.Count++; } diff --git a/TestTask/Program.cs b/TestTask/Program.cs index 30f3538..bf28070 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -17,21 +17,21 @@ public class Program /// Второй параметр - путь до второго файла. static void Main(string[] args) { - //IList singleLetterStats = new List(); - //using (IReadOnlyStream inputStream1 = GetInputStream(args[0])) - //{ - // singleLetterStats = FillSingleLetterStats(inputStream1); - //} - //RemoveCharStatsByType(singleLetterStats, CharType.Vowel); - //PrintStatistic(singleLetterStats); + IList singleLetterStats = new List(); + using (IReadOnlyStream inputStream1 = GetInputStream(args[0])) + { + singleLetterStats = HelperFillStat.FillSingleLetterStats(inputStream1); + } + RemoveCharStatsByType(singleLetterStats, CharType.Vowel); + PrintStatistic(singleLetterStats); - //IList doubleLetterStats = new List(); - //using (IReadOnlyStream inputStream2 = GetInputStream(args[1])) - //{ - // doubleLetterStats = FillDoubleLetterStats(inputStream2); - //} - //RemoveCharStatsByType(doubleLetterStats, CharType.Consonants); - //PrintStatistic(doubleLetterStats); + IList doubleLetterStats = new List(); + using (IReadOnlyStream inputStream2 = GetInputStream(args[1])) + { + doubleLetterStats = HelperFillStat.FillDoubleLetterStats(inputStream2); + } + RemoveCharStatsByType(doubleLetterStats, CharType.Consonants); + PrintStatistic(doubleLetterStats); // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы. (+) @@ -48,46 +48,6 @@ private static IReadOnlyStream GetInputStream(string fileFullPath) return new ReadOnlyStream(fileFullPath); } - /// - /// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения каждой буквы. - /// Статистика РЕГИСТРОЗАВИСИМАЯ! - /// - /// Стрим для считывания символов для последующего анализа - /// Коллекция статистик по каждой букве, что была прочитана из стрима. - private static IList FillSingleLetterStats(IReadOnlyStream stream) - { - stream.ResetPositionToStart(); - Dictionary letterCountsMap = new Dictionary(); - while (!stream.IsEof) - { - char c = stream.ReadNextChar(); - // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - регистрозависимый. (+) - HelperFillStat.FillSingleStatMap(c, letterCountsMap); - } - return new List(letterCountsMap.Values); - } - - /// - /// Ф-ция считывающая из входящего потока все буквы, и возвращающая коллекцию статистик вхождения парных букв. - /// В статистику должны попадать только пары из одинаковых букв, например АА, СС, УУ, ЕЕ и т.д. - /// Статистика - НЕ регистрозависимая! - /// - /// Стрим для считывания символов для последующего анализа - /// Коллекция статистик по каждой букве, что была прочитана из стрима. - private static IList FillDoubleLetterStats(IReadOnlyStream stream) - { - stream.ResetPositionToStart(); - Dictionary pairCountsMap = new Dictionary(); - char prevC = '\0'; - while (!stream.IsEof) - { - char c = stream.ReadNextChar(); - // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый. (+) - HelperFillStat.FillDoubleStatMap(prevC, c, pairCountsMap); - prevC = c; - } - return new List(pairCountsMap.Values); - } /// /// Ф-ция перебирает все найденные буквы/парные буквы, содержащие в себе только гласные или согласные буквы. From 313b4d5eff7575a55c55e400a6a3682e694cea69 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 14:39:50 +0800 Subject: [PATCH 12/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20HelperRemoveStat.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask/Helpers/HelperRemoveStat.cs | 51 ++++++++++++++++++++++++++++ TestTask/Program.cs | 24 ++----------- TestTask/TestTask.csproj | 1 + 3 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 TestTask/Helpers/HelperRemoveStat.cs diff --git a/TestTask/Helpers/HelperRemoveStat.cs b/TestTask/Helpers/HelperRemoveStat.cs new file mode 100644 index 0000000..faeeae9 --- /dev/null +++ b/TestTask/Helpers/HelperRemoveStat.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Linq; + +namespace TestTask.Helpers +{ + public static class HelperRemoveStat + { + private static HashSet __vowelsEnRu = new HashSet("aeiouAEIOUаеёиоуыэюяАЕЁИОУЫЭЮЯ".ToCharArray()); + + /// + /// Ф-ция перебирает все найденные буквы/парные буквы, содержащие в себе только гласные или согласные буквы. + /// (Тип букв для перебора определяется параметром charType) + /// Все найденные буквы/пары соответствующие параметру поиска - удаляются из переданной коллекции статистик. + /// + /// Коллекция со статистиками вхождения букв/пар + /// Тип букв для анализа + public static void RemoveCharStatsByType(IList letters, CharType charType) + { + // TODO : Удалить статистику по запрошенному типу букв. (+) + switch (charType) + { + case CharType.Consonants: + RemoveConsonants(letters); + break; + case CharType.Vowel: + RemoveVowels(letters); + break; + } + } + + private static void RemoveVowels(IList letters) + { + letters = GetConsonantsOnly(letters) + .ToList(); + } + + private static void RemoveConsonants(IList letters) + { + letters = GetVowelsOnly(letters) + .ToList(); + } + + private static IEnumerable GetVowelsOnly(IList letters) => + letters + .Where(x => __vowelsEnRu.Contains(x.Letter.First())); + + private static IEnumerable GetConsonantsOnly(IList letters) => + letters + .Where(x => !__vowelsEnRu.Contains(x.Letter.First())); + } +} diff --git a/TestTask/Program.cs b/TestTask/Program.cs index bf28070..137ddb0 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -22,7 +22,7 @@ static void Main(string[] args) { singleLetterStats = HelperFillStat.FillSingleLetterStats(inputStream1); } - RemoveCharStatsByType(singleLetterStats, CharType.Vowel); + HelperRemoveStat.RemoveCharStatsByType(singleLetterStats, CharType.Vowel); PrintStatistic(singleLetterStats); IList doubleLetterStats = new List(); @@ -30,7 +30,7 @@ static void Main(string[] args) { doubleLetterStats = HelperFillStat.FillDoubleLetterStats(inputStream2); } - RemoveCharStatsByType(doubleLetterStats, CharType.Consonants); + HelperRemoveStat.RemoveCharStatsByType(doubleLetterStats, CharType.Consonants); PrintStatistic(doubleLetterStats); @@ -49,26 +49,6 @@ private static IReadOnlyStream GetInputStream(string fileFullPath) } - /// - /// Ф-ция перебирает все найденные буквы/парные буквы, содержащие в себе только гласные или согласные буквы. - /// (Тип букв для перебора определяется параметром charType) - /// Все найденные буквы/пары соответствующие параметру поиска - удаляются из переданной коллекции статистик. - /// - /// Коллекция со статистиками вхождения букв/пар - /// Тип букв для анализа - private static void RemoveCharStatsByType(IList letters, CharType charType) - { - // TODO : Удалить статистику по запрошенному типу букв. - switch (charType) - { - case CharType.Consonants: - break; - case CharType.Vowel: - break; - } - - } - /// /// Ф-ция выводит на экран полученную статистику в формате "{Буква} : {Кол-во}" /// Каждая буква - с новой строки. diff --git a/TestTask/TestTask.csproj b/TestTask/TestTask.csproj index 2341fe1..403c9c1 100644 --- a/TestTask/TestTask.csproj +++ b/TestTask/TestTask.csproj @@ -46,6 +46,7 @@ + From 7c90b359ee9bc2e092e9359c796d54af4630e784 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 15:48:48 +0800 Subject: [PATCH 13/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?RemoveStats.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask.UnitTests/FillStatsTests.cs | 19 +----- TestTask.UnitTests/InputData/DataFillStat.cs | 8 --- .../InputData/InputObjectData.cs | 39 ++++++++++++ TestTask.UnitTests/RemoveStatsTests.cs | 63 +++++++++++++++++++ TestTask.UnitTests/TestTask.UnitTests.csproj | 3 +- TestTask/Helpers/HelperRemoveStat.cs | 10 +-- 6 files changed, 113 insertions(+), 29 deletions(-) delete mode 100644 TestTask.UnitTests/InputData/DataFillStat.cs create mode 100644 TestTask.UnitTests/InputData/InputObjectData.cs create mode 100644 TestTask.UnitTests/RemoveStatsTests.cs diff --git a/TestTask.UnitTests/FillStatsTests.cs b/TestTask.UnitTests/FillStatsTests.cs index fda25d6..a995f4e 100644 --- a/TestTask.UnitTests/FillStatsTests.cs +++ b/TestTask.UnitTests/FillStatsTests.cs @@ -4,6 +4,7 @@ using System.IO; using TestTask.Helpers; using TestTask.UnitTests.Fakes; +using TestTask.UnitTests.InputData; using Xunit; namespace TestTask.UnitTests @@ -11,24 +12,10 @@ namespace TestTask.UnitTests public class FillStatsTests { // format: inputData - public static List DataWithNoLetters = new List() - { - new object[] { "" }, - new object[] { "123454321708090706,-./" }, - new object[] { "12345\0\0\0\0\0\0\0\0\0\0\t\t\t\r\n\r\n"}, - }; + public static List DataWithNoLetters = InputObjectData.DataWithNoLetters; // format: inputData, singleStatCount, doubleStatCount - public static List DataWithLettersOnly = new List() - { - new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 9, 4 }, - new object[] { "аабббввввгдежзяяя", 9, 4 }, - new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 13, 4}, - }; - - public FillStatsTests() - { - } + public static List DataWithLettersOnly = InputObjectData.DataWithLettersOnlyFillStat; [Fact] public void create_reader_with_null_param_will_be_exception() diff --git a/TestTask.UnitTests/InputData/DataFillStat.cs b/TestTask.UnitTests/InputData/DataFillStat.cs deleted file mode 100644 index 27898c9..0000000 --- a/TestTask.UnitTests/InputData/DataFillStat.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; - -namespace TestTask.UnitTests.InputData -{ - public static class DataFillStat - { - } -} diff --git a/TestTask.UnitTests/InputData/InputObjectData.cs b/TestTask.UnitTests/InputData/InputObjectData.cs new file mode 100644 index 0000000..0b99413 --- /dev/null +++ b/TestTask.UnitTests/InputData/InputObjectData.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; + +namespace TestTask.UnitTests.InputData +{ + public static class InputObjectData + { + // format: inputData + public static List DataWithNoLetters = new List() + { + new object[] { "" }, + new object[] { "123454321708090706,-./" }, + new object[] { "12345\0\0\0\0\0\0\0\0\0\0\t\t\t\r\n\r\n"}, + }; + + // format: inputData, singleStatCount, doubleStatCount + public static List DataWithLettersOnlyFillStat = new List() + { + new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 9, 4 }, + new object[] { "аабббввввгдежзяяя", 9, 4 }, + new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 13, 4}, + }; + + // format: inputData, singleStatCount, doubleStatCount + public static List DataWithLettersOnlyRemoveVowelStat = new List() + { + new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 6, 2 }, + new object[] { "аабббввввгдежзяяя", 6, 2 }, + new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 8, 2 }, + }; + + // format: inputData, singleStatCount, doubleStatCount + public static List DataWithLettersOnlyRemoveConsonantStat = new List() + { + new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 3, 2 }, + new object[] { "аабббввввгдежзяяя", 3, 2 }, + new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 5, 2 }, + }; + } +} diff --git a/TestTask.UnitTests/RemoveStatsTests.cs b/TestTask.UnitTests/RemoveStatsTests.cs new file mode 100644 index 0000000..bfd4523 --- /dev/null +++ b/TestTask.UnitTests/RemoveStatsTests.cs @@ -0,0 +1,63 @@ +using FluentAssertions; +using System.Collections.Generic; +using TestTask.Helpers; +using TestTask.UnitTests.Fakes; +using TestTask.UnitTests.InputData; +using Xunit; + +namespace TestTask.UnitTests +{ + public class RemoveStatsTests + { + public static List DataWithLettersOnlyVowel = InputObjectData.DataWithLettersOnlyRemoveVowelStat; + public static List DataWithLettersOnlyConsonant = InputObjectData.DataWithLettersOnlyRemoveConsonantStat; + + [Theory] + [MemberData(nameof(DataWithLettersOnlyVowel))] + public void after_remove_vowels_single_stats_will_be_have_count(string inputData, int singleStatsCount, int _) + { + var stream = new FakeReadOnlyStream(inputData); + var singleStat = HelperFillStat.FillSingleLetterStats(stream); + + HelperRemoveStat.RemoveCharStatsByType(singleStat, CharType.Vowel); + + singleStat.Should().HaveCount(singleStatsCount); + } + + [Theory] + [MemberData(nameof(DataWithLettersOnlyVowel))] + public void after_remove_vowels_double_stats_will_be_have_count(string inputData, int _, int doubleStatsCount) + { + var stream = new FakeReadOnlyStream(inputData); + var doubleStat = HelperFillStat.FillDoubleLetterStats(stream); + + HelperRemoveStat.RemoveCharStatsByType(doubleStat, CharType.Vowel); + + doubleStat.Should().HaveCount(doubleStatsCount); + } + + [Theory] + [MemberData(nameof(DataWithLettersOnlyConsonant))] + public void after_remove_consonants_single_stats_will_be_have_count(string inputData, int singleStatsCount, int _) + { + var stream = new FakeReadOnlyStream(inputData); + var singleStat = HelperFillStat.FillSingleLetterStats(stream); + + HelperRemoveStat.RemoveCharStatsByType(singleStat, CharType.Consonants); + + singleStat.Should().HaveCount(singleStatsCount); + } + + [Theory] + [MemberData(nameof(DataWithLettersOnlyConsonant))] + public void after_remove_consonants_double_stats_will_be_have_count(string inputData, int _, int doubleStatsCount) + { + var stream = new FakeReadOnlyStream(inputData); + var doubleStat = HelperFillStat.FillDoubleLetterStats(stream); + + HelperRemoveStat.RemoveCharStatsByType(doubleStat, CharType.Consonants); + + doubleStat.Should().HaveCount(doubleStatsCount); + } + } +} diff --git a/TestTask.UnitTests/TestTask.UnitTests.csproj b/TestTask.UnitTests/TestTask.UnitTests.csproj index 64fcdcb..33443c3 100644 --- a/TestTask.UnitTests/TestTask.UnitTests.csproj +++ b/TestTask.UnitTests/TestTask.UnitTests.csproj @@ -135,8 +135,9 @@ - + + diff --git a/TestTask/Helpers/HelperRemoveStat.cs b/TestTask/Helpers/HelperRemoveStat.cs index faeeae9..d41a70b 100644 --- a/TestTask/Helpers/HelperRemoveStat.cs +++ b/TestTask/Helpers/HelperRemoveStat.cs @@ -30,14 +30,16 @@ public static void RemoveCharStatsByType(IList letters, CharType ch private static void RemoveVowels(IList letters) { - letters = GetConsonantsOnly(letters) - .ToList(); + for (int i = letters.Count - 1; i >= 0; i--) + if (__vowelsEnRu.Contains(letters[i].Letter.First())) + letters.RemoveAt(i); } private static void RemoveConsonants(IList letters) { - letters = GetVowelsOnly(letters) - .ToList(); + for (int i = letters.Count - 1; i >= 0; i--) + if (!__vowelsEnRu.Contains(letters[i].Letter.First())) + letters.RemoveAt(i); } private static IEnumerable GetVowelsOnly(IList letters) => From d90f68c1fa3d61b2d1cc20adad65ddcc66fefae2 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 16:50:04 +0800 Subject: [PATCH 14/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20HelperPrintStat.=20=D0=9F=D1=80=D0=B8=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=BB=D1=81=D1=8F=20=D0=B2=20Program.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestTask/Helpers/HelperPrintStat.cs | 39 +++++++++++++++++++++++++ TestTask/Helpers/HelperRemoveStat.cs | 8 ------ TestTask/Program.cs | 43 +++++++++++++++++----------- TestTask/TestTask.csproj | 1 + 4 files changed, 67 insertions(+), 24 deletions(-) create mode 100644 TestTask/Helpers/HelperPrintStat.cs diff --git a/TestTask/Helpers/HelperPrintStat.cs b/TestTask/Helpers/HelperPrintStat.cs new file mode 100644 index 0000000..818803d --- /dev/null +++ b/TestTask/Helpers/HelperPrintStat.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace TestTask.Helpers +{ + public static class HelperPrintStat + { + private const string __space = " "; + private const string __colon = ":"; + private const string __total = "ИТОГО"; + private const string __empty = "[ПУСТО]"; + private static readonly string __rn = Environment.NewLine; + + public static string BuildReport(IEnumerable letters) + { + if (!letters.Any()) + return __empty; + var sortedLetters = SortedLetters(letters); + var body = string.Join(__rn, BuildLines(sortedLetters)); + var total = BuildTotal(letters.Count()); + return $"{body}{__rn}{total}"; + } + + private static IEnumerable SortedLetters(IEnumerable letters) => + letters + .OrderBy(x => x.Letter); + + private static IEnumerable BuildLines(IEnumerable letters) => + letters + .Select(x => BuildLine(x)); + + private static string BuildLine(LetterStats stat) => $"{stat.Letter}{BuildBreak()}{stat.Count}"; + + private static string BuildTotal(int count) => $"{__total}{BuildBreak()}{count}"; + + private static string BuildBreak() => $"{__space}{__colon}{__space}"; + } +} diff --git a/TestTask/Helpers/HelperRemoveStat.cs b/TestTask/Helpers/HelperRemoveStat.cs index d41a70b..96c0b84 100644 --- a/TestTask/Helpers/HelperRemoveStat.cs +++ b/TestTask/Helpers/HelperRemoveStat.cs @@ -41,13 +41,5 @@ private static void RemoveConsonants(IList letters) if (!__vowelsEnRu.Contains(letters[i].Letter.First())) letters.RemoveAt(i); } - - private static IEnumerable GetVowelsOnly(IList letters) => - letters - .Where(x => __vowelsEnRu.Contains(x.Letter.First())); - - private static IEnumerable GetConsonantsOnly(IList letters) => - letters - .Where(x => !__vowelsEnRu.Contains(x.Letter.First())); } } diff --git a/TestTask/Program.cs b/TestTask/Program.cs index 137ddb0..48d03a9 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -16,26 +16,37 @@ public class Program /// Первый параметр - путь до первого файла. /// Второй параметр - путь до второго файла. static void Main(string[] args) + { + Processing(GetSingleLetterStats(args[0])); + Processing(GetDoubleLetterStats(args[1])); + // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы. (+) + Console.ReadKey(); + } + + private static void Processing(IList letterStats) + { + HelperRemoveStat.RemoveCharStatsByType(letterStats, CharType.Consonants); + PrintStatistic(letterStats); + } + + private static IList GetSingleLetterStats(string fileFullPath) { IList singleLetterStats = new List(); - using (IReadOnlyStream inputStream1 = GetInputStream(args[0])) + using (IReadOnlyStream inputStream = GetInputStream(fileFullPath)) { - singleLetterStats = HelperFillStat.FillSingleLetterStats(inputStream1); + singleLetterStats = HelperFillStat.FillSingleLetterStats(inputStream); } - HelperRemoveStat.RemoveCharStatsByType(singleLetterStats, CharType.Vowel); - PrintStatistic(singleLetterStats); + return singleLetterStats; + } + private static IList GetDoubleLetterStats(string fileFullPath) + { IList doubleLetterStats = new List(); - using (IReadOnlyStream inputStream2 = GetInputStream(args[1])) + using (IReadOnlyStream inputStream = GetInputStream(fileFullPath)) { - doubleLetterStats = HelperFillStat.FillDoubleLetterStats(inputStream2); + doubleLetterStats = HelperFillStat.FillDoubleLetterStats(inputStream); } - HelperRemoveStat.RemoveCharStatsByType(doubleLetterStats, CharType.Consonants); - PrintStatistic(doubleLetterStats); - - - // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы. (+) - Console.ReadKey(); + return doubleLetterStats; } /// @@ -48,7 +59,6 @@ private static IReadOnlyStream GetInputStream(string fileFullPath) return new ReadOnlyStream(fileFullPath); } - /// /// Ф-ция выводит на экран полученную статистику в формате "{Буква} : {Кол-во}" /// Каждая буква - с новой строки. @@ -56,10 +66,11 @@ private static IReadOnlyStream GetInputStream(string fileFullPath) /// В конце отдельная строчка с ИТОГО, содержащая в себе общее кол-во найденных букв/пар /// /// Коллекция со статистикой - private static void PrintStatistic(IEnumerable letters) + public static void PrintStatistic(IEnumerable letters) { - // TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту! - throw new NotImplementedException(); + // TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту! (+) + var report = HelperPrintStat.BuildReport(letters); + Console.WriteLine(report); } } } diff --git a/TestTask/TestTask.csproj b/TestTask/TestTask.csproj index 403c9c1..17dd93f 100644 --- a/TestTask/TestTask.csproj +++ b/TestTask/TestTask.csproj @@ -46,6 +46,7 @@ + From 3f04d1827ed680621a72f9a882dfca02c09b0f41 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 19:52:08 +0800 Subject: [PATCH 15/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20HelperMain.=20=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?PrintStats.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputData/InputObjectData.cs | 17 +++++++ TestTask.UnitTests/PrintStatsTests.cs | 47 +++++++++++++++++++ TestTask.UnitTests/TestTask.UnitTests.csproj | 1 + TestTask/Helpers/HelperMain.cs | 26 ++++++++++ TestTask/Program.cs | 32 +++++++++---- TestTask/TestTask.csproj | 1 + 6 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 TestTask.UnitTests/PrintStatsTests.cs create mode 100644 TestTask/Helpers/HelperMain.cs diff --git a/TestTask.UnitTests/InputData/InputObjectData.cs b/TestTask.UnitTests/InputData/InputObjectData.cs index 0b99413..5321085 100644 --- a/TestTask.UnitTests/InputData/InputObjectData.cs +++ b/TestTask.UnitTests/InputData/InputObjectData.cs @@ -35,5 +35,22 @@ public static class InputObjectData new object[] { "аабббввввгдежзяяя", 3, 2 }, new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 5, 2 }, }; + + + // format: inputData, singleStatLineCount + public static List DataWithLettersOnlySinglePrintStat = new List() + { + new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 7 }, + new object[] { "аабббввввгдежзяяя", 7 }, + new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 9 }, + }; + + // format: inputData, doubleStatCount + public static List DataWithLettersOnlyDoublePrintStat = new List() + { + new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 3 }, + new object[] { "аабббввввгдежзяяя", 3 }, + new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 3 }, + }; } } diff --git a/TestTask.UnitTests/PrintStatsTests.cs b/TestTask.UnitTests/PrintStatsTests.cs new file mode 100644 index 0000000..ca1b977 --- /dev/null +++ b/TestTask.UnitTests/PrintStatsTests.cs @@ -0,0 +1,47 @@ +using FluentAssertions; +using System; +using System.Collections.Generic; +using System.Linq; +using TestTask.Helpers; +using TestTask.UnitTests.Fakes; +using TestTask.UnitTests.InputData; +using Xunit; + +namespace TestTask.UnitTests +{ + public class PrintStatsTests + { + public static List DataWithLettersOnlySingle = InputObjectData.DataWithLettersOnlySinglePrintStat; + public static List DataWithLettersOnlyDouble = InputObjectData.DataWithLettersOnlyDoublePrintStat; + + [Theory] + [MemberData(nameof(DataWithLettersOnlySingle))] + public void report_single_stats_will_be_have_line_count(string inputData, int singleStatsLineCount) + { + var stream = new FakeReadOnlyStream(inputData); + var singleStat = HelperFillStat.FillSingleLetterStats(stream); + HelperRemoveStat.RemoveCharStatsByType(singleStat, CharType.Vowel); + + var linesCount = HelperPrintStat.BuildReport(singleStat) + .Split(new string[] { Environment.NewLine }, StringSplitOptions.None) + .Count(); + + linesCount.Should().Be(singleStatsLineCount); + } + + [Theory] + [MemberData(nameof(DataWithLettersOnlyDouble))] + public void report_double_stats_will_be_have_line_count(string inputData, int doubleStatsLineCount) + { + var stream = new FakeReadOnlyStream(inputData); + var doubleStat = HelperFillStat.FillDoubleLetterStats(stream); + HelperRemoveStat.RemoveCharStatsByType(doubleStat, CharType.Consonants); + + var linesCount = HelperPrintStat.BuildReport(doubleStat) + .Split(new string[] { Environment.NewLine }, StringSplitOptions.None) + .Count(); + + linesCount.Should().Be(doubleStatsLineCount); + } + } +} diff --git a/TestTask.UnitTests/TestTask.UnitTests.csproj b/TestTask.UnitTests/TestTask.UnitTests.csproj index 33443c3..17a3c7b 100644 --- a/TestTask.UnitTests/TestTask.UnitTests.csproj +++ b/TestTask.UnitTests/TestTask.UnitTests.csproj @@ -136,6 +136,7 @@ + diff --git a/TestTask/Helpers/HelperMain.cs b/TestTask/Helpers/HelperMain.cs new file mode 100644 index 0000000..683c253 --- /dev/null +++ b/TestTask/Helpers/HelperMain.cs @@ -0,0 +1,26 @@ +using System; + +namespace TestTask.Helpers +{ + public static class HelperMain + { + private const string __help = "Пожалуйста введите 2 аргумента командной строки! Пути до файлов для обработки."; + private const string __pressAnyKey = "Для закрытия окна, нажмите любую клавишу..."; + + public static bool IsBadArgs(string[] args) => + args == null || args.Length < 2 || string.IsNullOrWhiteSpace(args[0]) || string.IsNullOrWhiteSpace(args[1]); + + public static void PrintHelp() => PrintToConsole(__help); + + public static void WaitPressKey() + { + PrintToConsole(string.Empty); + PrintToConsole(__pressAnyKey); + Console.ReadKey(); + } + + public static void PrintToConsole(string report) => + Console.WriteLine(report); + + } +} diff --git a/TestTask/Program.cs b/TestTask/Program.cs index 48d03a9..879fcda 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using TestTask.Helpers; namespace TestTask @@ -17,16 +16,31 @@ public class Program /// Второй параметр - путь до второго файла. static void Main(string[] args) { - Processing(GetSingleLetterStats(args[0])); - Processing(GetDoubleLetterStats(args[1])); + if (HelperMain.IsBadArgs(args)) + { + HelperMain.PrintHelp(); + } + else + { + ProcessingSingleStats(args[0]); + ProcessingDoubleStats(args[1]); + } // TODO : Необжодимо дождаться нажатия клавиши, прежде чем завершать выполнение программы. (+) - Console.ReadKey(); + HelperMain.WaitPressKey(); + } + + private static void ProcessingSingleStats(string fileFullPath) + { + var singleStats = GetSingleLetterStats(fileFullPath); + HelperRemoveStat.RemoveCharStatsByType(singleStats, CharType.Vowel); + PrintStatistic(singleStats); } - private static void Processing(IList letterStats) + private static void ProcessingDoubleStats(string fileFullPath) { - HelperRemoveStat.RemoveCharStatsByType(letterStats, CharType.Consonants); - PrintStatistic(letterStats); + var doubleStats = GetDoubleLetterStats(fileFullPath); + HelperRemoveStat.RemoveCharStatsByType(doubleStats, CharType.Consonants); + PrintStatistic(doubleStats); } private static IList GetSingleLetterStats(string fileFullPath) @@ -70,7 +84,7 @@ public static void PrintStatistic(IEnumerable letters) { // TODO : Выводить на экран статистику. Выводить предварительно отсортировав по алфавиту! (+) var report = HelperPrintStat.BuildReport(letters); - Console.WriteLine(report); + HelperMain.PrintToConsole(report); } } } diff --git a/TestTask/TestTask.csproj b/TestTask/TestTask.csproj index 17dd93f..e964224 100644 --- a/TestTask/TestTask.csproj +++ b/TestTask/TestTask.csproj @@ -46,6 +46,7 @@ + From 2c41656eb0ac18ea56da6f1dc2063cc21a609316 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 22:22:14 +0800 Subject: [PATCH 16/19] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?HelperMain.=20=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BB=20xml-?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8?= =?UTF-8?q?=D0=B8.=20=D0=9F=D1=80=D0=B8=D0=B1=D1=80=D0=B0=D0=BB=D1=81?= =?UTF-8?q?=D1=8F=20=D0=B2=20=D0=BA=D0=BE=D0=B4=D0=B5.=20=D0=94=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=B8=D0=BD=D1=84=D0=BE=20?= =?UTF-8?q?=D0=B2=20Readme.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 42 +++++++++++++- TestTask.UnitTests/HelperMainTests.cs | 22 +++++++ .../InputData/InputObjectData.cs | 58 ++++++++++++++----- TestTask.UnitTests/TestTask.UnitTests.csproj | 1 + TestTask/Helpers/HelperFillStat.cs | 58 ++++++++++++++++++- TestTask/Helpers/HelperMain.cs | 16 ++++- TestTask/Helpers/HelperPrintStat.cs | 29 ++++++++++ TestTask/Helpers/HelperRemoveStat.cs | 17 +++++- TestTask/Program.cs | 19 +++++- TestTask/ReadOnlyStream.cs | 33 ++++++++++- 10 files changed, 273 insertions(+), 22 deletions(-) create mode 100644 TestTask.UnitTests/HelperMainTests.cs diff --git a/README.md b/README.md index 1036790..683f898 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Тестовое задание. +# ТЗ. Доброго дня. Данный репозиторий представляет из себя тестовое задание для выполнения перед собеседованием на вакансию разработчика C# в компанию Катрен. @@ -20,3 +20,43 @@ __Для разработки настоятельно рекомендуетс И по готовности результат отдать на ревью методом Pull Request'a.__ Если данный способ по каким-то причинам Вам не представляется реализуемым, можете выслать результат в виде архива на почту кадровому агенту. + +# РЕШЕНИЕ +## Что сделано? +Всё, согласно ТЗ, все `TODO` (9 шт). Код исправлен и доработан. + +### Дополнительно +- Написаны юнит-тесты, +- Настроен автозапуск тестов на `GitHub Actions` при каждом коммите в удаленный репозиторий. + +### Структура Решения +Решение состоит из 2-х проектов +- TestTask (исходный проект), +- TestTask.UnitTests (юнит-тесты). + +``` +. +├──📂TestTask +│ ├──📂Helpers +│ │ ├──📄HelperFillStat.cs +│ │ ├──📄HelperMain.cs +│ │ ├──📄HelperPrintStat.cs +│ │ └──📄HelperRemoveStat.cs +│ ├──📄CharType.cs +│ ├──📄IReadOnlyStream.cs +│ ├──📄LetterStats.cs +│ ├──📄Program.cs +│ └──📄ReadOnlyStream.cs +│ +└──📂TestTask.UnitTests + ├──📂Fakes + │ └──📄FakeReadOnlyStream.cs + ├──📂InputData + │ └──📄InputObjectData.cs + ├──📄FillStatsTests.cs + ├──📄HelperMainTests.cs + ├──📄PrintStatsTests.cs + └──📄RemoveStatsTests.cs +``` + +Исполняемые файлы с программой [здесь](https://github.com/Certalarm/Reformer/blob/dev/_assets/Reformer.zip). Не требует установки. \ No newline at end of file diff --git a/TestTask.UnitTests/HelperMainTests.cs b/TestTask.UnitTests/HelperMainTests.cs new file mode 100644 index 0000000..a035037 --- /dev/null +++ b/TestTask.UnitTests/HelperMainTests.cs @@ -0,0 +1,22 @@ +using FluentAssertions; +using System.Collections.Generic; +using TestTask.Helpers; +using TestTask.UnitTests.InputData; +using Xunit; + +namespace TestTask.UnitTests +{ + public class HelperMainTests + { + public static List DataWithBadPaths = InputObjectData.DataWithBadPaths; + + [Theory] + [MemberData(nameof(DataWithBadPaths))] + public void isBadArgs_with_bad_args_will_be_true(string[] args) + { + var result = HelperMain.IsBadArgs(args); + + result.Should().BeTrue(); + } + } +} diff --git a/TestTask.UnitTests/InputData/InputObjectData.cs b/TestTask.UnitTests/InputData/InputObjectData.cs index 5321085..5ec013d 100644 --- a/TestTask.UnitTests/InputData/InputObjectData.cs +++ b/TestTask.UnitTests/InputData/InputObjectData.cs @@ -4,6 +4,14 @@ namespace TestTask.UnitTests.InputData { public static class InputObjectData { + private static readonly List _inputData = new List() + { + "ААБББВВВВГДЕЖЗЯЯЯ", + "аабббввввгдежзяяя", + "аАБбБвВВвГдЕЖзяЯЯ", + }; + + // format: inputData public static List DataWithNoLetters = new List() { @@ -15,42 +23,60 @@ public static class InputObjectData // format: inputData, singleStatCount, doubleStatCount public static List DataWithLettersOnlyFillStat = new List() { - new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 9, 4 }, - new object[] { "аабббввввгдежзяяя", 9, 4 }, - new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 13, 4}, + new object[] { _inputData[0], 9, 4 }, + new object[] { _inputData[1], 9, 4 }, + new object[] { _inputData[2], 13, 4}, }; // format: inputData, singleStatCount, doubleStatCount public static List DataWithLettersOnlyRemoveVowelStat = new List() { - new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 6, 2 }, - new object[] { "аабббввввгдежзяяя", 6, 2 }, - new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 8, 2 }, + new object[] { _inputData[0], 6, 2 }, + new object[] { _inputData[1], 6, 2 }, + new object[] { _inputData[2], 8, 2 }, }; // format: inputData, singleStatCount, doubleStatCount public static List DataWithLettersOnlyRemoveConsonantStat = new List() { - new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 3, 2 }, - new object[] { "аабббввввгдежзяяя", 3, 2 }, - new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 5, 2 }, + new object[] { _inputData[0], 3, 2 }, + new object[] { _inputData[1], 3, 2 }, + new object[] { _inputData[2], 5, 2 }, }; // format: inputData, singleStatLineCount public static List DataWithLettersOnlySinglePrintStat = new List() { - new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 7 }, - new object[] { "аабббввввгдежзяяя", 7 }, - new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 9 }, + new object[] { _inputData[0], 7 }, + new object[] { _inputData[1], 7 }, + new object[] { _inputData[2], 9 }, }; - // format: inputData, doubleStatCount + // format: inputData, doubleStatLineCount public static List DataWithLettersOnlyDoublePrintStat = new List() { - new object[] { "ААБББВВВВГДЕЖЗЯЯЯ", 3 }, - new object[] { "аабббввввгдежзяяя", 3 }, - new object[] { "аАБбБвВВвГдЕЖзяЯЯ", 3 }, + new object[] { _inputData[0], 3 }, + new object[] { _inputData[1], 3 }, + new object[] { _inputData[2], 3 }, + }; + + // format: args[] + public static List DataWithBadPaths = new List() + { + new object[] { null }, + new object[] { new object[] { null, null } }, + new object[] { new object[] { "", "" } }, + new object[] { new object[] { null, "" } }, + new object[] { new object[] { "", null } }, + new object[] { new object[] { null, "1" } }, + new object[] { new object[] { "1", null } }, + new object[] { new object[] { "", "1" } }, + new object[] { new object[] { "1", "" } }, + new object[] { new object[] { "1", "" } }, + new object[] { new object[] { "1" } }, + new object[] { new object[] { "" } }, + new object[] { new object[] { null } }, }; } } diff --git a/TestTask.UnitTests/TestTask.UnitTests.csproj b/TestTask.UnitTests/TestTask.UnitTests.csproj index 17a3c7b..bacaf93 100644 --- a/TestTask.UnitTests/TestTask.UnitTests.csproj +++ b/TestTask.UnitTests/TestTask.UnitTests.csproj @@ -135,6 +135,7 @@ + diff --git a/TestTask/Helpers/HelperFillStat.cs b/TestTask/Helpers/HelperFillStat.cs index 554a56f..afa8cd2 100644 --- a/TestTask/Helpers/HelperFillStat.cs +++ b/TestTask/Helpers/HelperFillStat.cs @@ -39,12 +39,17 @@ public static IList FillDoubleLetterStats(IReadOnlyStream stream) { char c = stream.ReadNextChar(); // TODO : заполнять статистику с использованием метода IncStatistic. Учёт букв - НЕ регистрозависимый. (+) - HelperFillStat.FillDoubleStatMap(prevC, c, pairCountsMap); + FillDoubleStatMap(prevC, c, pairCountsMap); prevC = c; } return new List(pairCountsMap.Values); } + /// + /// Метод добавляет в статистику по одиночным буквам, прочитанный символ. + /// + /// Символ для добаления в статистику. + /// Словарь статистики. private static void FillSingleStatMap(char c, Dictionary map) { if (IsGoodSingleChar(c)) @@ -52,6 +57,13 @@ private static void FillSingleStatMap(char c, Dictionary ma FillStatMap(BuildSingleKey(c), map); } } + + /// + /// Метод добавляет в статистику по парным буквам, прочитанную пару символов. + /// + /// Первый символ для добаления в статистику. + /// Второй символ для добаления в статистику. + /// Словарь статистики. private static void FillDoubleStatMap(char c1, char c2, Dictionary map) { if (IsGoodDoubleChar(c1, c2)) @@ -60,22 +72,61 @@ private static void FillDoubleStatMap(char c1, char c2, Dictionary + /// Метод строит ключ для одиночной статистики. + /// + /// Символ для построения ключа. + /// Ключ для одиночной статистики. private static string BuildSingleKey(char c) => c.ToString(); + /// + /// Метод строит ключ для парной статистики. + /// + /// Первый символ для построения ключа. + /// Второй символ для построения ключа. + /// Ключ для парной статистики. private static string BuildDoubleKey(char c1, char c2) => $"{c1}{c2}".ToUpper(); + /// + /// Метод проверяет символ на принадлежность к букве. + /// + /// Символ для проверки. + /// Был ли символ буквой. private static bool IsGoodSingleChar(char c) => char.IsLetter(c); + /// + /// Метод проверяет пару символов на принадлежность к буквам и на равенство букв. + /// + /// Первый символ для проверки. + /// Второй символ для проверки. + /// Является ли пара символов парой букв и одинаковы ли они. private static bool IsGoodDoubleChar(char c1, char c2) => IsLetterBoth(c1, c2) && IsEquallyBoth(c1, c2); + /// + /// Метод проверяет пару символов на принадлежность к буквам. + /// + /// Первый символ для проверки. + /// Второй символ для проверки. + /// Является ли пара символов парой букв. private static bool IsLetterBoth(char c1, char c2) => char.IsLetter(c1) && char.IsLetter(c2); + /// + /// Метод проверяет пару символов на равенство без учета регистра. + /// + /// Первый символ для проверки. + /// Второй символ для проверки. + /// Одинаковы ли символы. private static bool IsEquallyBoth(char c1, char c2) => char.ToUpperInvariant(c1) == char.ToUpperInvariant(c2); + /// + /// Метод увеличивает статистику по существующему ключу или созает новую запись. + /// + /// Ключ для проверки. + /// Словарь со статистикой. private static void FillStatMap(string key, Dictionary map) { if (map.TryGetValue(key, out LetterStats existingStat)) @@ -89,6 +140,11 @@ private static void FillStatMap(string key, Dictionary map) } } + /// + /// Метод создает статистику для впервые встретившегося ключа. + /// + /// Ключ статистики. + /// Статистика. private static LetterStats CreateLetterStats(string key) => new LetterStats { diff --git a/TestTask/Helpers/HelperMain.cs b/TestTask/Helpers/HelperMain.cs index 683c253..316fdf3 100644 --- a/TestTask/Helpers/HelperMain.cs +++ b/TestTask/Helpers/HelperMain.cs @@ -7,11 +7,22 @@ public static class HelperMain private const string __help = "Пожалуйста введите 2 аргумента командной строки! Пути до файлов для обработки."; private const string __pressAnyKey = "Для закрытия окна, нажмите любую клавишу..."; + /// + /// Метод проверяет подхолит ли массив аргументов. + /// + /// Входные аргументы. + /// Является ли аргументы не подходящими. public static bool IsBadArgs(string[] args) => args == null || args.Length < 2 || string.IsNullOrWhiteSpace(args[0]) || string.IsNullOrWhiteSpace(args[1]); + /// + /// Метод печатает в консоли подсказку по использованию программы. + /// public static void PrintHelp() => PrintToConsole(__help); + /// + /// Метод печатает в консоли сообщение о нажатии клавиши и ждет от пользователя нажатия клавишы. + /// public static void WaitPressKey() { PrintToConsole(string.Empty); @@ -19,8 +30,11 @@ public static void WaitPressKey() Console.ReadKey(); } + /// + /// Метод печатает в консоль переданную строку. + /// + /// Строка, которую нужно напечатать. public static void PrintToConsole(string report) => Console.WriteLine(report); - } } diff --git a/TestTask/Helpers/HelperPrintStat.cs b/TestTask/Helpers/HelperPrintStat.cs index 818803d..412b9c9 100644 --- a/TestTask/Helpers/HelperPrintStat.cs +++ b/TestTask/Helpers/HelperPrintStat.cs @@ -12,6 +12,11 @@ public static class HelperPrintStat private const string __empty = "[ПУСТО]"; private static readonly string __rn = Environment.NewLine; + /// + /// Метод отчет по статистике. + /// + /// Коллекция статистик. + /// Строковый отчет. public static string BuildReport(IEnumerable letters) { if (!letters.Any()) @@ -22,18 +27,42 @@ public static string BuildReport(IEnumerable letters) return $"{body}{__rn}{total}"; } + /// + /// Метод сортирует коллекцию статистик по алфавиту. + /// + /// Коллекция статистик. + /// Отсортированная коллекция статистик. private static IEnumerable SortedLetters(IEnumerable letters) => letters .OrderBy(x => x.Letter); + /// + /// Метод строит коллекцию строк из коллекции статистик. + /// + /// Входная коллекция статистик. + /// Коллекция строковых статистик. private static IEnumerable BuildLines(IEnumerable letters) => letters .Select(x => BuildLine(x)); + /// + /// Метод строит строку из статистики. + /// + /// Статистика. + /// Строковая статистика. private static string BuildLine(LetterStats stat) => $"{stat.Letter}{BuildBreak()}{stat.Count}"; + /// + /// Метод строит строку ИТОГО. + /// + /// Количество для ИТОГО. + /// Строка ИТОГО со значением. private static string BuildTotal(int count) => $"{__total}{BuildBreak()}{count}"; + /// + /// Метод строит строку для отделения значений. + /// + /// строка с разделителями. private static string BuildBreak() => $"{__space}{__colon}{__space}"; } } diff --git a/TestTask/Helpers/HelperRemoveStat.cs b/TestTask/Helpers/HelperRemoveStat.cs index 96c0b84..f8e4daf 100644 --- a/TestTask/Helpers/HelperRemoveStat.cs +++ b/TestTask/Helpers/HelperRemoveStat.cs @@ -5,7 +5,8 @@ namespace TestTask.Helpers { public static class HelperRemoveStat { - private static HashSet __vowelsEnRu = new HashSet("aeiouAEIOUаеёиоуыэюяАЕЁИОУЫЭЮЯ".ToCharArray()); + private static readonly HashSet __vowelsEnRu = + new HashSet("aeiouAEIOUаеёиоуыэюяАЕЁИОУЫЭЮЯ".ToCharArray()); /// /// Ф-ция перебирает все найденные буквы/парные буквы, содержащие в себе только гласные или согласные буквы. @@ -25,21 +26,35 @@ public static void RemoveCharStatsByType(IList letters, CharType ch case CharType.Vowel: RemoveVowels(letters); break; + default: + break; } } + /// + /// Метод удаляет статистику по гласным буквам. + /// + /// Статистика. private static void RemoveVowels(IList letters) { for (int i = letters.Count - 1; i >= 0; i--) + { if (__vowelsEnRu.Contains(letters[i].Letter.First())) letters.RemoveAt(i); + } } + /// + /// Метод удаляет статистику по согласным буквам. + /// + /// Статистика. private static void RemoveConsonants(IList letters) { for (int i = letters.Count - 1; i >= 0; i--) + { if (!__vowelsEnRu.Contains(letters[i].Letter.First())) letters.RemoveAt(i); + } } } } diff --git a/TestTask/Program.cs b/TestTask/Program.cs index 879fcda..1a78a44 100644 --- a/TestTask/Program.cs +++ b/TestTask/Program.cs @@ -5,7 +5,6 @@ namespace TestTask { public class Program { - /// /// Программа принимает на входе 2 пути до файлов. /// Анализирует в первом файле кол-во вхождений каждой буквы (регистрозависимо). Например А, б, Б, Г и т.д. @@ -29,6 +28,10 @@ static void Main(string[] args) HelperMain.WaitPressKey(); } + /// + /// Метод запускает обработку файла для одиночных букв. + /// + /// Полный путь до файла для чтения private static void ProcessingSingleStats(string fileFullPath) { var singleStats = GetSingleLetterStats(fileFullPath); @@ -36,6 +39,10 @@ private static void ProcessingSingleStats(string fileFullPath) PrintStatistic(singleStats); } + /// + /// Метод запускает обработку файла для парных букв. + /// + /// Полный путь до файла для чтения private static void ProcessingDoubleStats(string fileFullPath) { var doubleStats = GetDoubleLetterStats(fileFullPath); @@ -43,6 +50,11 @@ private static void ProcessingDoubleStats(string fileFullPath) PrintStatistic(doubleStats); } + /// + /// Метод получает статистику по одиночным буквам. + /// + /// Полный путь до файла для чтения + /// Коллекция статистик. private static IList GetSingleLetterStats(string fileFullPath) { IList singleLetterStats = new List(); @@ -53,6 +65,11 @@ private static IList GetSingleLetterStats(string fileFullPath) return singleLetterStats; } + /// + /// Метод получает статистику по парным буквам. + /// + /// Полный путь до файла для чтения + /// Коллекция статистик. private static IList GetDoubleLetterStats(string fileFullPath) { IList doubleLetterStats = new List(); diff --git a/TestTask/ReadOnlyStream.cs b/TestTask/ReadOnlyStream.cs index 8b2bf43..41376b0 100644 --- a/TestTask/ReadOnlyStream.cs +++ b/TestTask/ReadOnlyStream.cs @@ -13,7 +13,10 @@ public class ReadOnlyStream : IReadOnlyStream private readonly Stream _localStream; private bool _disposed = false; - + + /// + /// Кодировка для чтения символа из файла. + /// public Encoding Encoding { get; } /// @@ -34,6 +37,11 @@ public ReadOnlyStream(string fileFullPath, Encoding encoding = default) CheckStream(); } + /// + /// Метод открывает Stream по имени файла (можно переопределять). + /// + /// Полный путь до файла для чтения + /// Открытый поток. public virtual Stream CreateStream(string param) { CheckPath(param); @@ -68,6 +76,9 @@ public void ResetPositionToStart() _localStream.Position = 0; } + /// + /// Метод для очистки неуправляемых ресурсов. + /// public void Dispose() { if (!_disposed) @@ -77,18 +88,29 @@ public void Dispose() } } + /// + /// Метод выбрасывает исключение, если ресурс уже освобожден. + /// private void CheckDisposed() { if (_disposed) throw new ObjectDisposedException(GetType().Name); } + /// + /// Метод проверяет внутренний стрим на null, выбрасывает исключение. + /// private void CheckStream() { if (_localStream == null || _localStream == Stream.Null) throw new ArgumentNullException(nameof(Stream), __errorNullStream); } + /// + /// Метод пытается открыть файловый поток, выбрасывает исключение при невозможности. + /// + /// Полный путь до файла для чтения. + /// Открытый файловый поток. private static Stream TryOpenStream(string fileFullPath) { try @@ -101,12 +123,21 @@ private static Stream TryOpenStream(string fileFullPath) } } + /// + /// Метод проверяет путь к файлу на null и пустоту. + /// + /// Полный путь до файла private static void CheckPath(string fileFullPath) { if (string.IsNullOrWhiteSpace(fileFullPath)) throw new ArgumentNullException(nameof(fileFullPath)); } + /// + /// Метод проверяет Encoding на null, возвращает UTF8 при положительной проверке. + /// + /// Задаваемая кодировка + /// Кодировка Encoding. private static Encoding DefineEncoding(Encoding encoding) => encoding == default ? Encoding.UTF8 From 77844cf9d268a019c046d9abde15a06a935bf801 Mon Sep 17 00:00:00 2001 From: Evgeniy Rusinov Date: Wed, 11 Feb 2026 22:25:04 +0800 Subject: [PATCH 17/19] =?UTF-8?q?=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=BF=D0=B0=D0=BF=D0=BA=D1=83=20=5Fassets.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _assets/readme.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 _assets/readme.md diff --git a/_assets/readme.md b/_assets/readme.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/_assets/readme.md @@ -0,0 +1 @@ + From f2384b3d6a07069adbabfbbb92ed24df86e69d41 Mon Sep 17 00:00:00 2001 From: Eugene Date: Wed, 11 Feb 2026 22:26:25 +0800 Subject: [PATCH 18/19] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B8=D1=81=D0=B7=D0=BE=D0=B4=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=D0=BF=D0=B0=D0=BF?= =?UTF-8?q?=D0=BA=D1=83=20=5Fassets.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _assets/TestTask.zip | Bin 0 -> 5844 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 _assets/TestTask.zip diff --git a/_assets/TestTask.zip b/_assets/TestTask.zip new file mode 100644 index 0000000000000000000000000000000000000000..1c090dee8f1bf0bbdfd6e3eee08070a9569d4df9 GIT binary patch literal 5844 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 W7wSrA=>G{w|Jw8)&Hv-B0sjS>c~DUR literal 0 HcmV?d00001 From cf8ef95a51553184f96a0ff876c961efb07f2b0d Mon Sep 17 00:00:00 2001 From: Evgeniy Rusinov Date: Wed, 11 Feb 2026 22:35:47 +0800 Subject: [PATCH 19/19] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 683f898..8d999a2 100644 --- a/README.md +++ b/README.md @@ -36,27 +36,27 @@ __Для разработки настоятельно рекомендуетс ``` . -├──📂TestTask -│ ├──📂Helpers -│ │ ├──📄HelperFillStat.cs -│ │ ├──📄HelperMain.cs -│ │ ├──📄HelperPrintStat.cs -│ │ └──📄HelperRemoveStat.cs +├──📂TestTask Исходный проект +│ ├──📂Helpers +│ │ ├──📄HelperFillStat.cs Логика для заполнения статистики +│ │ ├──📄HelperMain.cs Логика для основного метода Main +│ │ ├──📄HelperPrintStat.cs Логика для печати статистики +│ │ └──📄HelperRemoveStat.cs Логика для удаления статистики │ ├──📄CharType.cs │ ├──📄IReadOnlyStream.cs │ ├──📄LetterStats.cs -│ ├──📄Program.cs +│ ├──📄Program.cs Точка входа в программу │ └──📄ReadOnlyStream.cs │ -└──📂TestTask.UnitTests - ├──📂Fakes - │ └──📄FakeReadOnlyStream.cs - ├──📂InputData - │ └──📄InputObjectData.cs - ├──📄FillStatsTests.cs - ├──📄HelperMainTests.cs - ├──📄PrintStatsTests.cs - └──📄RemoveStatsTests.cs +└──📂TestTask.UnitTests Проект с юнит-тестами + ├──📂Fakes + │ └──📄FakeReadOnlyStream.cs Фейковая реализация ReadOnlyStream + ├──📂InputData + │ └──📄InputObjectData.cs Входные данные для тестов + ├──📄FillStatsTests.cs Юнит-тесты для HelperFillStat + ├──📄HelperMainTests.cs Юнит-тесты для HelperMain + ├──📄PrintStatsTests.cs Юнит-тесты для HelperPrintStat + └──📄RemoveStatsTests.cs Юнит-тесты для HelperRemoveStat ``` -Исполняемые файлы с программой [здесь](https://github.com/Certalarm/Reformer/blob/dev/_assets/Reformer.zip). Не требует установки. \ No newline at end of file +Исполняемые файлы с программой [здесь](https://github.com/Certalarm/TestTask/tree/master/_assets/TestTask.zip). Не требует установки.