From 195211f3cbc7030cb2eb2f4bde83535245d54cf2 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Fri, 10 Jan 2025 19:42:07 +0500 Subject: [PATCH 01/32] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D1=8B?= =?UTF-8?q?=20=D0=B8=D0=B7=20TagCloudll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../CloudLayouterPainterTest.cs | 30 + ...rcularCloudLayouterMainRequirementsTest.cs | 185 ++ .../CircularCloudLayouterTest.cs | 22 + ...edFrequencyBasedCloudLayouterWorkerTest.cs | 60 + .../RandomCloudLayouterWorkerTest.cs | 32 + .../Extensions/RectangleExtensions.cs | 20 + TagCloud.Tests/GlobalSuppressions.cs | 10 + .../ImageSaversTests/ImageSaverTest.cs | 62 + TagCloud.Tests/MainTest.cs | 64 + .../NormalizersTest/NormalizerTest.cs | 49 + TagCloud.Tests/TagCloud.Tests.csproj | 34 + .../WordCountersTests/WordCounterTest.cs | 46 + .../WordFiltersTests/BannedWordLists.cs | 60 + .../WordFilterChangeBannedWordsTest.cs | 41 + .../WordFilterDefaultBannedWordsTest.cs | 59 + .../WordReadersTests/WordReaderTest.cs | 80 + .../CloudLayouterPainter.cs | 87 + .../ICloudLayouterPainter.cs | 10 + .../ICloudLayouterWorker.cs | 12 + ...alizedFrequencyBasedCloudLayouterWorker.cs | 49 + .../RandomCloudLayouterWorker.cs | 52 + .../CircularCloudLayouter/Circle.cs | 25 + .../CircularCloudLayouter.cs | 75 + TagCloud/CloudLayouters/ICloudLayouter.cs | 10 + TagCloud/CommandLineOptions.cs | 72 + TagCloud/DIContainer.cs | 105 + TagCloud/GlobalSuppressions.cs | 15 + TagCloud/ImageSavers/IImageSaver.cs | 10 + TagCloud/ImageSavers/ImageSaver.cs | 25 + TagCloud/Normalizers/INormalizer.cs | 11 + TagCloud/Normalizers/Normalizer.cs | 58 + TagCloud/Parsers/BoolParser.cs | 14 + TagCloud/Parsers/ColorParser.cs | 17 + TagCloud/Parsers/FontParser.cs | 18 + TagCloud/Parsers/SizeParser.cs | 21 + TagCloud/Program.cs | 37 + TagCloud/ProgramExecutor.cs | 35 + TagCloud/SnowWhiteContent.txt | 2941 +++++++++++++++++ TagCloud/Tag.cs | 6 + TagCloud/TagCloud.csproj | 17 + TagCloud/WordCounters/IWordCounter.cs | 9 + TagCloud/WordCounters/WordCounter.cs | 18 + TagCloud/WordFilters/IWordFilter.cs | 8 + TagCloud/WordFilters/WordFilter.cs | 75 + TagCloud/WordReaders/IWordReader.cs | 8 + TagCloud/WordReaders/WordReader.cs | 22 + fp.sln | 29 +- 48 files changed, 4742 insertions(+), 4 deletions(-) create mode 100644 TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs create mode 100644 TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs create mode 100644 TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs create mode 100644 TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs create mode 100644 TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs create mode 100644 TagCloud.Tests/Extensions/RectangleExtensions.cs create mode 100644 TagCloud.Tests/GlobalSuppressions.cs create mode 100644 TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs create mode 100644 TagCloud.Tests/MainTest.cs create mode 100644 TagCloud.Tests/NormalizersTest/NormalizerTest.cs create mode 100644 TagCloud.Tests/TagCloud.Tests.csproj create mode 100644 TagCloud.Tests/WordCountersTests/WordCounterTest.cs create mode 100644 TagCloud.Tests/WordFiltersTests/BannedWordLists.cs create mode 100644 TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs create mode 100644 TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs create mode 100644 TagCloud.Tests/WordReadersTests/WordReaderTest.cs create mode 100644 TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs create mode 100644 TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs create mode 100644 TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs create mode 100644 TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs create mode 100644 TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs create mode 100644 TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs create mode 100644 TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs create mode 100644 TagCloud/CloudLayouters/ICloudLayouter.cs create mode 100644 TagCloud/CommandLineOptions.cs create mode 100644 TagCloud/DIContainer.cs create mode 100644 TagCloud/GlobalSuppressions.cs create mode 100644 TagCloud/ImageSavers/IImageSaver.cs create mode 100644 TagCloud/ImageSavers/ImageSaver.cs create mode 100644 TagCloud/Normalizers/INormalizer.cs create mode 100644 TagCloud/Normalizers/Normalizer.cs create mode 100644 TagCloud/Parsers/BoolParser.cs create mode 100644 TagCloud/Parsers/ColorParser.cs create mode 100644 TagCloud/Parsers/FontParser.cs create mode 100644 TagCloud/Parsers/SizeParser.cs create mode 100644 TagCloud/Program.cs create mode 100644 TagCloud/ProgramExecutor.cs create mode 100644 TagCloud/SnowWhiteContent.txt create mode 100644 TagCloud/Tag.cs create mode 100644 TagCloud/TagCloud.csproj create mode 100644 TagCloud/WordCounters/IWordCounter.cs create mode 100644 TagCloud/WordCounters/WordCounter.cs create mode 100644 TagCloud/WordFilters/IWordFilter.cs create mode 100644 TagCloud/WordFilters/WordFilter.cs create mode 100644 TagCloud/WordReaders/IWordReader.cs create mode 100644 TagCloud/WordReaders/WordReader.cs diff --git a/.gitignore b/.gitignore index 445484b62..155692fb6 100644 --- a/.gitignore +++ b/.gitignore @@ -197,3 +197,4 @@ FakesAssemblies/ *.opt *Solved.cs +/TagCloud/Properties diff --git a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs new file mode 100644 index 000000000..549ca4687 --- /dev/null +++ b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs @@ -0,0 +1,30 @@ +using System.Drawing; +using TagCloud.CloudLayouterPainters; + +namespace TagCloud.Tests.CloudLayouterPaintersTest +{ + internal class CloudLayouterPainterTest + { + private CloudLayouterPainter painter; + + [SetUp] + public void SetUp() + { + painter = new CloudLayouterPainter(new Size(1, 1)); + } + + [Test] + public void Draw_ThrowsArgumentException_WithEmptyTags() + { + var painter = new CloudLayouterPainter(new Size(1, 1)); + Assert.Throws(() => painter.Draw(new List())); + } + + [Test] + public void Draw_ThrowsArgumentNullException_WithTagsAsNull() + { + var painter = new CloudLayouterPainter(new Size(1, 1)); + Assert.Throws(() => painter.Draw(null!)); + } + } +} diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs new file mode 100644 index 000000000..cc2f2dfc9 --- /dev/null +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs @@ -0,0 +1,185 @@ +using FluentAssertions; +using System.Drawing; +using TagCloud.CloudLayouterPainters; +using TagCloud.CloudLayouters.CircularCloudLayouter; +using TagCloud.CloudLayouterWorkers; +using TagCloud.ImageSavers; +using TagCloud.Tests.Extensions; + +namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests +{ + [TestFixture] + internal class CircularCloudLayouterMainRequirementsTest + { + private Point center = new Point(); + private Rectangle[] rectangles; + private List tags; + private readonly string failedTestsDirectory = "FailedTest"; + + private readonly ImageSaver imageSaver = new ImageSaver(); + private readonly CloudLayouterPainter cloudLayouterPainter + = new CloudLayouterPainter(new Size(5000, 5000)); + + [OneTimeSetUp] + public void Init() + { + Directory.CreateDirectory(failedTestsDirectory); + } + + [SetUp] + public void SetUp() + { + var minRectangleWidth = 30; + var maxRectangleWidth = 70; + var minRectangleHeight = 20; + var maxRectangleHeight = 50; + var rectanglesCount = 1000; + + tags = new List(); + var circularCloudLayouter = new CircularCloudLayouter(); + + var randomWorker = new RandomCloudLayouterWorker( + minRectangleWidth, + maxRectangleWidth, + minRectangleHeight, + maxRectangleHeight); + foreach (var rectangleProperty in randomWorker + .GetNextRectangleProperties().Take(rectanglesCount)) + { + tags.Add( + new Tag( + rectangleProperty.word, + circularCloudLayouter.PutNextRectangle(rectangleProperty.size))); + } + rectangles = tags.Select(x => x.Rectangle).ToArray(); + } + + [TestCase(0.7, 1000)] + [Repeat(10)] + public void ShouldPlaceRectanglesInCircle(double expectedCoverageRatio, int gridSize) + { + var maxRadius = rectangles.Max( + x => x.GetMaxDistanceFromPointToRectangleAngles(center)); + var step = 2 * maxRadius / gridSize; + + var occupancyGrid = GetOccupancyGrid(gridSize, maxRadius, step); + + var actualCoverageRatio = GetOccupancyGridRatio(occupancyGrid, maxRadius, step); + actualCoverageRatio.Should().BeGreaterThanOrEqualTo(expectedCoverageRatio); + } + + [TestCase(15)] + [Repeat(10)] + public void ShouldPlaceCenterOfMassOfRectanglesNearCenter(int tolerance) + { + var centerX = rectangles.Average(r => r.Left + r.Width / 2.0); + var centerY = rectangles.Average(r => r.Top + r.Height / 2.0); + var actualCenter = new Point((int)centerX, (int)centerY); + + var distance = Math.Sqrt(Math.Pow(actualCenter.X - center.X, 2) + + Math.Pow(actualCenter.Y - center.Y, 2)); + + distance.Should().BeLessThanOrEqualTo(tolerance); + } + + [Test] + [Repeat(10)] + public void ShouldPlaceRectanglesWithoutOverlap() + { + for (var i = 0; i < rectangles.Length; i++) + { + for (var j = i + 1; j < rectangles.Length; j++) + { + Assert.That( + rectangles[i].IntersectsWith(rectangles[j]) == false, + $"Прямоугольники пересекаются:\n" + + $"{rectangles[i].ToString()}\n" + + $"{rectangles[j].ToString()}"); + } + } + } + + [TearDown] + public void Cleanup() + { + if (TestContext.CurrentContext.Result.FailCount == 0) + { + return; + } + + var name = $"{TestContext.CurrentContext.Test.Name}.png"; + var path = Path.Combine(failedTestsDirectory, name); + imageSaver.SaveFile(cloudLayouterPainter.Draw(tags), path); + Console.WriteLine($"Tag cloud visualization saved to file {path}"); + } + + [OneTimeTearDown] + public void OneTimeCleanup() + { + if (Directory.Exists(failedTestsDirectory) + && Directory.GetFiles(failedTestsDirectory).Length == 0) + { + Directory.Delete(failedTestsDirectory); + } + } + + private (int start, int end) GetGridIndexesInterval( + int rectangleStartValue, + int rectangleCorrespondingSize, + double maxRadius, + double step) + { + var start = (int)((rectangleStartValue - center.X + maxRadius) / step); + var end = (int)((rectangleStartValue + + rectangleCorrespondingSize - center.X + maxRadius) / step); + return (start, end); + } + + private bool[,] GetOccupancyGrid(int gridSize, double maxRadius, double step) + { + var result = new bool[gridSize, gridSize]; + foreach (var rect in rectangles) + { + var xInterval = GetGridIndexesInterval(rect.X, rect.Width, maxRadius, step); + var yInterval = GetGridIndexesInterval(rect.Y, rect.Height, maxRadius, step); + for (var x = xInterval.start; x <= xInterval.end; x++) + { + for (var y = yInterval.start; y <= yInterval.end; y++) + { + result[x, y] = true; + } + } + } + return result; + } + + private double GetOccupancyGridRatio(bool[,] occupancyGrid, double maxRadius, double step) + { + var totalCellsInsideCircle = 0; + var coveredCellsInsideCircle = 0; + for (var x = 0; x < occupancyGrid.GetLength(0); x++) + { + for (var y = 0; y < occupancyGrid.GetLength(0); y++) + { + var cellCenterX = x * step - maxRadius + center.X; + var cellCenterY = y * step - maxRadius + center.Y; + + var distance = Math.Sqrt( + Math.Pow(cellCenterX - center.X, 2) + Math.Pow(cellCenterY - center.Y, 2)); + + if (distance > maxRadius) + { + continue; + } + + totalCellsInsideCircle += 1; + if (occupancyGrid[x, y]) + { + coveredCellsInsideCircle += 1; + } + } + } + return (double)coveredCellsInsideCircle / totalCellsInsideCircle; + } + } +} diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs new file mode 100644 index 000000000..8a5ed0e77 --- /dev/null +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs @@ -0,0 +1,22 @@ +using System.Drawing; +using TagCloud.CloudLayouters.CircularCloudLayouter; + +namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests +{ + [TestFixture] + internal class CircularCloudLayouterTest + { + [TestCase(0, 100)] + [TestCase(-1, 100)] + [TestCase(100, 0)] + [TestCase(100, -1)] + public void PutNextRectangle_ThrowsArgumentException_OnAnyNegativeOrZeroSize( + int width, + int height) + { + var size = new Size(width, height); + Assert.Throws( + () => new CircularCloudLayouter().PutNextRectangle(size)); + } + } +} diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs new file mode 100644 index 000000000..a6d75e2f5 --- /dev/null +++ b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs @@ -0,0 +1,60 @@ +using FluentAssertions; +using System.Drawing; +using TagCloud.CloudLayouterWorkers; + +namespace TagCloud.Tests.CloudLayouterWorkersTests +{ + internal class NormalizedFrequencyBasedCloudLayouterWorkerTest + { + private readonly Dictionary normalizedValues + = new Dictionary + { + { "three", 0.625 }, + { "one", 0.25 }, + { "two", 0.2917 }, + { "four", 1.0 }, + }; + + [TestCase(0, 100)] + [TestCase(-1, 100)] + [TestCase(100, 0)] + [TestCase(100, -1)] + public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize( + int width, + int height) + { + Assert.Throws( + () => new NormalizedFrequencyBasedCloudLayouterWorker(width, height, normalizedValues)); + } + + [TestCase(100, 25, false)] + [TestCase(100, 25, true)] + public void GetNextRectangleSize_WorksCorrectly(int width, int height, bool isSortedOrder) + { + var index = 0; + string[]? keys = null; + if (isSortedOrder) + { + keys = normalizedValues.OrderByDescending(x => x.Value).Select(x => x.Key).ToArray(); + } + else + { + keys = normalizedValues.Keys.ToArray(); + } + + var worker = new NormalizedFrequencyBasedCloudLayouterWorker( + width, + height, + normalizedValues, + isSortedOrder); + foreach (var rectangleSize in worker + .GetNextRectangleProperties()) + { + var currentValue = normalizedValues[keys[index]]; + var expected = new Size((int)(currentValue * width), (int)(currentValue * height)); + index += 1; + rectangleSize.size.Should().BeEquivalentTo(expected); + } + } + } +} diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs new file mode 100644 index 000000000..81f71e264 --- /dev/null +++ b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs @@ -0,0 +1,32 @@ +using TagCloud.CloudLayouterWorkers; + +namespace TagCloud.Tests.CloudLayouterWorkersTests +{ + [TestFixture] + internal class CircularCloudLayouterWorkerTests + { + [TestCase(0, 100)] + [TestCase(-1, 100)] + [TestCase(100, 0)] + [TestCase(100, -1)] + public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize( + int width, + int height) + { + Assert.Throws( + () => new RandomCloudLayouterWorker(width, width, height, height)); + } + + [TestCase(50, 25, 25, 50)] + [TestCase(25, 50, 50, 25)] + public void GetNextRectangleSize_ThrowsArgumentException_OnNonConsecutiveSizeValues( + int minWidth, + int maxWidth, + int minHeight, + int maxHeight) + { + Assert.Throws( + () => new RandomCloudLayouterWorker(minWidth, maxWidth, minHeight, maxHeight)); + } + } +} diff --git a/TagCloud.Tests/Extensions/RectangleExtensions.cs b/TagCloud.Tests/Extensions/RectangleExtensions.cs new file mode 100644 index 000000000..d4c8aa55f --- /dev/null +++ b/TagCloud.Tests/Extensions/RectangleExtensions.cs @@ -0,0 +1,20 @@ +using System.Drawing; + +namespace TagCloud.Tests.Extensions +{ + internal static class RectangleExtensions + { + public static double GetMaxDistanceFromPointToRectangleAngles( + this Rectangle rectangle, + Point point) + { + var dx = Math.Max( + Math.Abs(rectangle.X - point.X), + Math.Abs(rectangle.X + rectangle.Width - point.X)); + var dy = Math.Max( + Math.Abs(rectangle.Y - point.Y), + Math.Abs(rectangle.Y + rectangle.Height - point.Y)); + return Math.Sqrt(dx * dx + dy * dy); + } + } +} diff --git a/TagCloud.Tests/GlobalSuppressions.cs b/TagCloud.Tests/GlobalSuppressions.cs new file mode 100644 index 000000000..cdd580ffc --- /dev/null +++ b/TagCloud.Tests/GlobalSuppressions.cs @@ -0,0 +1,10 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +// Так делать явно плохо +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.ImageSaversTests.ImageSaverTest.SaveFile_SavesFile(System.String,System.String)~System.Boolean")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.ImageSaversTests.ImageSaverTest.SaveFile_ThrowsArgumentException_WithInvalidFilename(System.String)")] diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs new file mode 100644 index 000000000..5a4b1bb55 --- /dev/null +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs @@ -0,0 +1,62 @@ +using System.Drawing; +using TagCloud.ImageSavers; + +namespace TagCloud.Tests.ImageSaversTests +{ + [TestFixture] + internal class ImageSaverTest + { + private string directoryPath = "TempFilesForImageSaverTests"; + private ImageSaver imageSaver; + + [OneTimeSetUp] + public void Init() + { + Directory.CreateDirectory(directoryPath); + } + + [SetUp] + public void SetUp() + { + imageSaver = new ImageSaver(); + } + + [TestCase("Test")] + public void SaveFile_ArgumentNullException_WithNullBitmap(string filename) + { + var path = Path.Combine(directoryPath, filename); + Assert.Throws(() => imageSaver.SaveFile(null!, path)); + } + + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void SaveFile_ThrowsArgumentException_WithInvalidFilename(string? filename) + { + var dummyImage = new Bitmap(1, 1); + Assert.Throws(() => imageSaver.SaveFile(dummyImage, filename!)); + } + + [TestCase("Test", "png", ExpectedResult = true)] + [TestCase("Test", "bmp", ExpectedResult = true)] + public bool SaveFile_SavesFile(string filename, string format) + { + var dummyImage = new Bitmap(1, 1); + var path = Path.Combine(directoryPath, filename); + + File.Delete(path); + imageSaver.SaveFile(dummyImage, path, format); + return File.Exists($"{path}.{format}"); + } + + + [OneTimeTearDown] + public void OneTimeCleanup() + { + if (Directory.Exists(directoryPath)) + { + Directory.Delete(directoryPath, true); + } + } + } +} diff --git a/TagCloud.Tests/MainTest.cs b/TagCloud.Tests/MainTest.cs new file mode 100644 index 000000000..76bf6026a --- /dev/null +++ b/TagCloud.Tests/MainTest.cs @@ -0,0 +1,64 @@ +using Autofac; +using FluentAssertions; +namespace TagCloud.Tests +{ + [TestFixture] + internal class MainTest + { + private static string directoryPath = "TempFilesForMainTest"; + private static readonly string dataFile = Path.Combine(directoryPath, "TestData.txt"); + private readonly string imageFile = Path.Combine(directoryPath, "Test"); + + [OneTimeSetUp] + public void Init() + { + Directory.CreateDirectory(directoryPath); + File.WriteAllLines(dataFile, new string[] + { + "One", + "One", + "Two", + "Three", + "Four", + "Four", + "Four", + "Four" + }); + } + + [TestCase("bmp")] + public void Program_ExecutesSuccessfully_WithValidArguments(string format) + { + var options = new CommandLineOptions + { + BackgroundColor = "Red", + TextColor = "Blue", + Font = "Calibri", + IsSorted = true.ToString(), + ImageSize = "1000:1000", + MaxRectangleHeight = 100, + MaxRectangleWidth = 200, + ImageFileName = imageFile, + DataFileName = dataFile, + ResultFormat = format + }; + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + + Assert.DoesNotThrow(() => executor.Execute(format)); + + File.Exists($"{imageFile}.{format}").Should().BeTrue(); + } + + [OneTimeTearDown] + public void OneTimeCleanup() + { + if (Directory.Exists(directoryPath)) + { + Directory.Delete(directoryPath, true); + } + } + } +} diff --git a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs new file mode 100644 index 000000000..92121b779 --- /dev/null +++ b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs @@ -0,0 +1,49 @@ +using FluentAssertions; +using TagCloud.Normalizers; + +namespace TagCloud.Tests.WordCountersTests +{ + [TestFixture] + internal class NormalizerTest + { + private readonly Normalizer normalizer = new Normalizer(); + private readonly Dictionary values = new Dictionary + { + { "one", 14 }, + { "two", 15 }, + { "three", 23 }, + { "four", 32 }, + }; + private readonly Dictionary expectedResult = new Dictionary + { + { "one", 0.25 }, + { "two",0.29166666666666669 }, + { "three", 0.625 }, + { "four", 1.0 }, + + }; + private readonly int defaultDecimalPlaces = 4; + private readonly double defaultMinCoefficient = 0.25; + + [TestCase(-0.1)] + public void Normalize_ThrowsArgumentException_WithMinCoefficientLessThanZero( + double minCoefficient) + { + Assert.Throws(() + => normalizer.Normalize(values, minCoefficient, defaultDecimalPlaces)); + } + + [TestCase(0.25, 4)] + [TestCase(0.25, 2)] + public void Normalize_CalculatesСorrectly(double minCoefficient, int decimalPlaces) + { + foreach (var pair in expectedResult) + { + expectedResult[pair.Key] = Math.Round(pair.Value, decimalPlaces); + } + var actual = normalizer.Normalize(values, minCoefficient, decimalPlaces); + + actual.Should().BeEquivalentTo(expectedResult); + } + } +} diff --git a/TagCloud.Tests/TagCloud.Tests.csproj b/TagCloud.Tests/TagCloud.Tests.csproj new file mode 100644 index 000000000..391e86bc2 --- /dev/null +++ b/TagCloud.Tests/TagCloud.Tests.csproj @@ -0,0 +1,34 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + diff --git a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs new file mode 100644 index 000000000..5a2a698f8 --- /dev/null +++ b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs @@ -0,0 +1,46 @@ +using FluentAssertions; +using TagCloud.WordCounters; + +namespace TagCloud.Tests.WordCountersTests +{ + [TestFixture] + internal class WordCounterTest + { + private WordCounter wordCounter; + + [SetUp] + public void SetUp() + { + wordCounter = new WordCounter(); + } + + [Test] + public void WordCounter_CountsCorrect() + { + var expected = new Dictionary() + { + { "One", 2 }, + { "Two", 1 }, + { "Three", 1 }, + { "Four", 4 }, + }; + var values = new string[] + { + "One", + "One", + "Two", + "Three", + "Four", + "Four", + "Four", + "Four" + }; + + foreach (var value in values) + { + wordCounter.AddWord(value); + } + wordCounter.Values.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/WordFiltersTests/BannedWordLists.cs b/TagCloud.Tests/WordFiltersTests/BannedWordLists.cs new file mode 100644 index 000000000..5dea13028 --- /dev/null +++ b/TagCloud.Tests/WordFiltersTests/BannedWordLists.cs @@ -0,0 +1,60 @@ +namespace TagCloud.Tests.WordFiltersTests +{ + internal static class BannedWordLists + { + public static string[] CustomBans = new string[] + { + "not", "also", "how", "let" + }; + + public static string[] ToHaveForms = new string[] + { + "have", "has", "had", "having", + }; + + public static string[] ToBeForms = new string[] + { + "am", "is", "are", "was", "were", "be", "been", "being", + }; + + public static string[] Articles = new string[] + { + "a", "an", "the" + }; + + public static string[] Pronouns => new string[] + { + "i", "you", "he", "she", "it", "we", "they", "me", "him", + "her", "us", "them", "my", "your", "his", "its", "our", "their", + "mine", "yours", "hers", "theirs", "myself", "yourself", "himself", + "herself", "itself", "ourselves", "yourselves", "themselves", "this", + "that", "these", "those", "who", "whom", "whose", "what", "which", + "some", "any", "none", "all", "many", "few", "several", + "everyone", "somebody", "anybody", "nobody", "everything", "anything", + "nothing", "each", "every", "either", "neither" + }; + + public static string[] Prepositions => new string[] + { + "about", "above", "across", "after", "against", "along", "amid", "among", + "around", "as", "at", "before", "behind", "below", "beneath", "beside", + "besides", "between", "beyond", "but", "by", "despite", "down", "during", + "except", "for", "from", "in", "inside", "into", "like", "near", "of", "off", + "on", "onto", "out", "outside", "over", "past", "since", "through", "throughout", + "till", "to", "toward", "under", "underneath", "until", "up", "upon", "with", + "within", "without" + }; + + public static string[] Conjunctions => new string[] + { + "and", "but", "or", "nor", "for", "yet", "so", "if", "because", "although", "though", + "since", "until", "unless", "while", "whereas", "when", "where", "before", "after" + }; + + public static string[] Interjections => new string[] + { + "o", "ah", "aha", "alas", "aw", "aye", "eh", "hmm", "huh", "hurrah", "no", "oh", "oops", + "ouch", "ow", "phew", "shh", "tsk", "ugh", "um", "wow", "yay", "yes", "yikes" + }; + } +} diff --git a/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs new file mode 100644 index 000000000..01dbdbfd8 --- /dev/null +++ b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs @@ -0,0 +1,41 @@ +using FluentAssertions; +using TagCloud.WordFilters; + +namespace TagCloud.Tests.WordFiltersTests +{ + [TestFixture] + internal class WordFilterChangeBannedWordsTest + { + private WordFilter wordFilter; + + [SetUp] + public void SetUp() + { + wordFilter = new WordFilter(); + } + + [Test] + public void Clear_ShouldClearBannedWordList() + { + wordFilter.Clear(); + wordFilter.BannedWords.Should().BeEmpty(); + } + + [TestCase("WordToAdd")] + public void Add_ShouldAddWord_ToBannedWords(string word) + { + wordFilter.Clear(); + wordFilter.Add(word); + wordFilter.BannedWords.Should().Contain(word).And.HaveCount(1); + } + + [TestCase("WordToRemove")] + public void Remove_ShouldRemoveWord_InBannedWords(string word) + { + wordFilter.Clear(); + wordFilter.Add(word); + wordFilter.Remove(word); + wordFilter.BannedWords.Should().NotContain(word); + } + } +} diff --git a/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs b/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs new file mode 100644 index 000000000..6d6f9a437 --- /dev/null +++ b/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs @@ -0,0 +1,59 @@ +using FluentAssertions; +using TagCloud.WordFilters; + +namespace TagCloud.Tests.WordFiltersTests +{ + [TestFixture] + internal class WordFilterDefaultBannedWordsTest + { + private readonly WordFilter wordFilter = new WordFilter(); + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.CustomBans))] + public void IsCorrectWord_ShouldBeFalse_WithCustomBans(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.ToHaveForms))] + public void IsCorrectWord_ShouldBeFalse_WithToHaveForms(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.ToBeForms))] + public void IsCorrectWord_ShouldBeFalse_WithToBeForms(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Articles))] + public void IsCorrectWord_ShouldBeFalse_WithArticles(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Pronouns))] + public void IsCorrectWord_ShouldBeFalse_WithPronouns(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Prepositions))] + public void IsCorrectWord_ShouldBeFalse_WithPrepositions(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Conjunctions))] + public void IsCorrectWord_ShouldBeFalse_WithConjunctions(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + + [TestCaseSource(typeof(BannedWordLists), nameof(BannedWordLists.Interjections))] + public void IsCorrectWord_ShouldBeFalse_WithInterjections(string word) + { + wordFilter.IsCorrectWord(word).Should().BeFalse(); + } + } +} diff --git a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs new file mode 100644 index 000000000..1f0aea180 --- /dev/null +++ b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs @@ -0,0 +1,80 @@ +using TagCloud.WordReaders; + +namespace TagCloud.Tests.WordReadersTests +{ + [TestFixture] + internal class WordReaderTest + { + private readonly string directoryPath = "TempFilesForWordReaderTests"; + + private readonly string fileWithCorrectValuesPath = "correctFile.txt"; + private readonly string[] correctValues = new string[] + { + "One", + "One", + "Two", + "Three", + "Four", + "Four", + "Four", + "Four" + }; + + private readonly string fileWithIncorrectValuesPath = "incorrectFile.txt"; + private readonly string[] incorrectValues = new string[] + { + "One", + "Two", + "Three Three", + "Four" + }; + + private WordReader wordReader; + + [OneTimeSetUp] + public void Init() + { + Directory.CreateDirectory(directoryPath); + File.WriteAllLines( + Path.Combine( + directoryPath, + fileWithCorrectValuesPath), + correctValues); + File.WriteAllLines + (Path.Combine( + directoryPath, + fileWithIncorrectValuesPath), + incorrectValues); + } + + [SetUp] + public void SetUp() + { + wordReader = new WordReader(); + } + + [TestCase(" ")] + [TestCase("ThisFileDoesNotExist.txt")] + public void WordReader_ThrowsFileNotFoundException_WithInvalidFilename(string filename) + { + var path = Path.Combine(directoryPath, filename); + Assert.Throws(() => wordReader.ReadByLines(path).ToArray()); + } + + [Test] + public void WordReader_ThrowsException_WithTwoWordsInOneLine() + { + var path = Path.Combine(directoryPath, fileWithIncorrectValuesPath); + Assert.Throws(() => wordReader.ReadByLines(path).ToArray()); + } + + [OneTimeTearDown] + public void OneTimeCleanup() + { + if (Directory.Exists(directoryPath)) + { + Directory.Delete(directoryPath, true); + } + } + } +} diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs new file mode 100644 index 000000000..6c46b3818 --- /dev/null +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -0,0 +1,87 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterPainters +{ + internal class CloudLayouterPainter( + Size imageSize, + Color? backgroundColor = null, + Color? textColor = null, + string? fontName = null) : ICloudLayouterPainter + { + private readonly Color backgroundColor = backgroundColor ?? Color.White; + private readonly Color textColor = textColor ?? Color.Black; + private readonly string fontName = fontName ?? "Arial"; + + public Bitmap Draw(IList tags) + { + ArgumentNullException.ThrowIfNull(tags); + + if (tags.Count == 0) + { + throw new ArgumentException("Список тегов пуст"); + } + + var result = new Bitmap(imageSize.Width, imageSize.Height); + + using var graphics = Graphics.FromImage(result); + graphics.Clear(backgroundColor); + + foreach (var tag in tags) + { + var positionOnCanvas = GetPositionOnCanvas(tag.Rectangle); + var rectOnCanvas = new Rectangle( + positionOnCanvas.X, + positionOnCanvas.Y, + tag.Rectangle.Width, + tag.Rectangle.Height); + DrawText(graphics, rectOnCanvas, tag.Text); + } + + return result; + } + + private Point GetPositionOnCanvas(Rectangle rectangle) + => new Point(rectangle.X + imageSize.Width / 2, rectangle.Y + imageSize.Height / 2); + + private void DrawText(Graphics graphics, Rectangle rectangle, string text) + { + var fontSize = FindFittingFontSize(graphics, text, rectangle); + var fittingFont = new Font(fontName, fontSize, FontStyle.Regular, GraphicsUnit.Pixel); + + using var stringFormat = new StringFormat + { + Alignment = StringAlignment.Center, + LineAlignment = StringAlignment.Center + }; + + using var brush = new SolidBrush(textColor); + graphics.DrawString(text, fittingFont, brush, rectangle, stringFormat); + } + + private int FindFittingFontSize(Graphics graphics, string text, Rectangle rectangle) + { + var minSize = 1; + var maxSize = Math.Min(rectangle.Width, rectangle.Height); + var result = minSize; + + while (minSize <= maxSize) + { + var midSize = (minSize + maxSize) / 2; + using var font = new Font(fontName, midSize, FontStyle.Regular, GraphicsUnit.Pixel); + + var textSize = graphics.MeasureString(text, font); + if (textSize.Width <= rectangle.Width && textSize.Height <= rectangle.Height) + { + result = midSize; + minSize = midSize + 1; + } + else + { + maxSize = midSize - 1; + } + } + + return result; + } + } +} diff --git a/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs new file mode 100644 index 000000000..38fe25ab8 --- /dev/null +++ b/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterPainters +{ + // Интерфейс отрисовки прямоугольников + internal interface ICloudLayouterPainter + { + public Bitmap Draw(IList tags); + } +} diff --git a/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs new file mode 100644 index 000000000..befcb3a45 --- /dev/null +++ b/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs @@ -0,0 +1,12 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterWorkers +{ + // Интерфейс получения свойств следующего прямоугольника + // По хорошему, нужно возвращать IEnumerable, + // для повышения возможности переиспользования + internal interface ICloudLayouterWorker + { + public IEnumerable<(string word, Size size)> GetNextRectangleProperties(); + } +} diff --git a/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs new file mode 100644 index 000000000..8bda8ce46 --- /dev/null +++ b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs @@ -0,0 +1,49 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterWorkers +{ + internal class NormalizedFrequencyBasedCloudLayouterWorker : ICloudLayouterWorker + { + public readonly int MaxRectangleWidth; + public readonly int MaxRectangleHeight; + private readonly Dictionary values; + private readonly string[] keysOrder; + public string[] KeysOrder => keysOrder; + + public NormalizedFrequencyBasedCloudLayouterWorker( + int maxRectangleWidth, + int maxRectangleHeight, + Dictionary normalizedValues, + bool isSorted = true) + { + if (maxRectangleWidth <= 0 || maxRectangleHeight <= 0) + { + throw new ArgumentException( + "Ширина или высота прямоугольника должна быть положительной"); + } + + MaxRectangleWidth = maxRectangleWidth; + MaxRectangleHeight = maxRectangleHeight; + values = normalizedValues; + if (isSorted) + { + keysOrder = values.OrderByDescending(x => x.Value).Select(x => x.Key).ToArray(); + } + else + { + keysOrder = values.Keys.ToArray(); + } + } + + public IEnumerable<(string word, Size size)> GetNextRectangleProperties() + { + foreach (var key in keysOrder) + { + var value = values[key]; + var width = (int)(MaxRectangleWidth * value); + var height = (int)(MaxRectangleHeight * value); + yield return (key, new Size(width, height)); + } + } + } +} diff --git a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs new file mode 100644 index 000000000..b05b6a470 --- /dev/null +++ b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs @@ -0,0 +1,52 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouterWorkers +{ + // Класс, со старого задания TagCloud, + // выдающий случайный размер прямоугольника + // Оставил его для пары тестов. + internal class RandomCloudLayouterWorker : ICloudLayouterWorker + { + private Random random = new Random(); + public readonly int MinRectangleWidth; + public readonly int MaxRectangleWidth; + public readonly int MinRectangleHeight; + public readonly int MaxRectangleHeight; + + public RandomCloudLayouterWorker( + int minRectangleWidth, + int maxRectangleWidth, + int minRectangleHeight, + int maxRectangleHeight) + { + if (minRectangleWidth <= 0 || maxRectangleWidth <= 0 + || minRectangleHeight <= 0 || maxRectangleHeight <= 0) + { + throw new ArgumentException( + "Ширина или высота прямоугольника должна быть положительной"); + } + + if (minRectangleWidth > maxRectangleWidth + || minRectangleHeight > maxRectangleHeight) + { + throw new ArgumentException( + "Минимальное значение ширины или высоты не может быть больше максимального"); + } + + MinRectangleWidth = minRectangleWidth; + MaxRectangleWidth = maxRectangleWidth; + MinRectangleHeight = minRectangleHeight; + MaxRectangleHeight = maxRectangleHeight; + } + + public IEnumerable<(string word, Size size)> GetNextRectangleProperties() + { + while (true) + { + var width = random.Next(MinRectangleWidth, MaxRectangleWidth); + var height = random.Next(MinRectangleHeight, MaxRectangleHeight); + yield return (string.Empty, new Size(width, height)); + } + } + } +} diff --git a/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs b/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs new file mode 100644 index 000000000..55198b985 --- /dev/null +++ b/TagCloud/CloudLayouters/CircularCloudLayouter/Circle.cs @@ -0,0 +1,25 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouters.CircularCloudLayouter +{ + internal class Circle(float startRadius = 2.0f) + { + private readonly Point center = new Point(0, 0); + public float Radius { get; set; } = startRadius; + + public IEnumerable GetCoordinatesOnCircle( + int startAngle, + int step = 1) + { + for (var dAngle = 0; dAngle < 360; dAngle += step) + { + var angle = (startAngle + dAngle) % 360; + + double angleInRadians = angle * Math.PI / 180; + var x = (int)(center.X + Radius * Math.Cos(angleInRadians)); + var y = (int)(center.Y + Radius * Math.Sin(angleInRadians)); + yield return new Point(x, y); + } + } + } +} diff --git a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs new file mode 100644 index 000000000..7809e0453 --- /dev/null +++ b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs @@ -0,0 +1,75 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouters.CircularCloudLayouter +{ + // Класс, со старого задания TagCloud, + // который расставляет прямоугольники по окружности + // с постепенно увеличивающимся радиусом. + // Прямоугольники расставляются вокруг точки с координатой (0, 0), + // Затем, в CloudLayouterPainter координат пересыитываются таким образом, + // что бы расположить первый прямоугольник в центре холста. + // Можно создать интерфейс IShape, который через GetCoordinates + // будет возвращать координаты линии формы. + // Тогда Circle можно заменить на IShape и ввести новые формы расстановки. + + internal class CircularCloudLayouter : ICloudLayouter + { + private readonly Circle arrangementСircle = new Circle(); + private readonly Random random = new Random(); + private readonly List rectangles = new List(); + + public Rectangle PutNextRectangle(Size rectangleSize) + { + if (rectangleSize.Width <= 0 || rectangleSize.Height <= 0) + { + throw new ArgumentException( + "Размеры прямоугольника не могут быть меньше либо равны нуля."); + } + + var result = new Rectangle(); + arrangementСircle.Radius -= 1.0f; + + var isPlaced = false; + while (!isPlaced) + { + var startAngle = random.Next(360); + foreach (var coordinate in arrangementСircle.GetCoordinatesOnCircle(startAngle)) + { + var location = GetRectangleLocation(coordinate, rectangleSize); + var nextRectangle = new Rectangle(location, rectangleSize); + if (!IsIntersectionWithAlreadyPlaced(nextRectangle)) + { + rectangles.Add(nextRectangle); + isPlaced = true; + result = nextRectangle; + break; + } + } + + arrangementСircle.Radius += 1.0f; + } + + return result; + } + + private bool IsIntersectionWithAlreadyPlaced(Rectangle rectangle) + { + foreach (var rect in rectangles) + { + if (rect.IntersectsWith(rectangle)) + { + return true; + } + } + + return false; + } + + private Point GetRectangleLocation(Point pointOnCircle, Size rectangleSize) + { + var x = pointOnCircle.X - rectangleSize.Width / 2; + var y = pointOnCircle.Y - rectangleSize.Height / 2; + return new Point(x, y); + } + } +} diff --git a/TagCloud/CloudLayouters/ICloudLayouter.cs b/TagCloud/CloudLayouters/ICloudLayouter.cs new file mode 100644 index 000000000..49abc7956 --- /dev/null +++ b/TagCloud/CloudLayouters/ICloudLayouter.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagCloud.CloudLayouters +{ + // Интерфейс расстановки прямоугольников + internal interface ICloudLayouter + { + public Rectangle PutNextRectangle(Size rectangleSize); + } +} diff --git a/TagCloud/CommandLineOptions.cs b/TagCloud/CommandLineOptions.cs new file mode 100644 index 000000000..447062718 --- /dev/null +++ b/TagCloud/CommandLineOptions.cs @@ -0,0 +1,72 @@ +using CommandLine; + +namespace TagCloud +{ + // 1. Нужно добавить опцию для указания имени файла, + // который содержит слова, для иссключения из фильтра скучных слов + // 2. Нужно добавить опцию для указания имени файла, + // который содержит слова, для добавления в фильтр скучных слов + public class CommandLineOptions + { + [Option( + "backgroundColor", + Required = false, + HelpText = "Цвет заднего фона изображения, например White.")] + public string BackgroundColor { get; set; } = "White"; + + [Option( + "textColor", + Required = false, + HelpText = "Цвет текста на изображении, например Black.")] + public string TextColor { get; set; } = "Black"; + + [Option( + "font", + Required = false, + HelpText = "Шрифт текста на изображении, например Arial.")] + public string Font { get; set; } = "Arial"; + + [Option( + "nonSorted", + Required = false, + HelpText = "Отключение сортировки слов, например False")] + public string IsSorted { get; set; } = true.ToString(); + + [Option( + "size", + Required = false, + HelpText = "Размер изображения в формате ШИРИНА:ВЫСОТА, например 5000:5000.")] + public string ImageSize { get; set; } = "5000:5000"; + + [Option( + "maxRectangleWidth", + Required = false, + HelpText = "Максимальная ширина прямоугольника.")] + public int MaxRectangleWidth { get; set; } = 500; + + [Option( + "maxRectangleHeight", + Required = false, + HelpText = "Максимальная высота прямоугольника.")] + public int MaxRectangleHeight { get; set; } = 200; + + [Option( + "imageFile", + Required = false, + HelpText = "Имя выходного файла изображения.")] + public string ImageFileName { get; set; } = "Result"; + + [Option( + "dataFile", + Required = true, + HelpText = "Имя файла с исходными данными.")] + public required string DataFileName { get; set; } + + [Option( + "resultFormat", + Required = false, + HelpText = "Формат создаваемого изображение, например png.")] + public string ResultFormat { get; set; } = "png"; + } + +} \ No newline at end of file diff --git a/TagCloud/DIContainer.cs b/TagCloud/DIContainer.cs new file mode 100644 index 000000000..13dbbb6e0 --- /dev/null +++ b/TagCloud/DIContainer.cs @@ -0,0 +1,105 @@ +using Autofac; +using TagCloud.CloudLayouterPainters; +using TagCloud.CloudLayouters.CircularCloudLayouter; +using TagCloud.CloudLayouters; +using TagCloud.CloudLayouterWorkers; +using TagCloud.ImageSavers; +using TagCloud.Normalizers; +using TagCloud.WordCounters; +using TagCloud.WordFilters; +using TagCloud.WordReaders; +using TagCloud.Parsers; + +namespace TagCloud +{ + public static class DIContainer + { + // 1. Разбить содержимое этого метода на отдельны части + // 2. Добавить проверку корректностей значений: + // - options.ImageSize; + // - options.MaxRectangleWidth; + // - options.MaxRectangleHeight; + public static IContainer ConfigureContainer(CommandLineOptions options) + { + var builder = new ContainerBuilder(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + builder + .RegisterType() + .As() + .SingleInstance(); + + var imageSize = SizeParser.ParseImageSize(options.ImageSize); + var backgroundColor = ColorParser.ParseColor(options.BackgroundColor); + var textColor = ColorParser.ParseColor(options.TextColor); + var font = FontParser.ParseFont(options.Font); + builder.RegisterType() + .As() + .WithParameter("imageSize", imageSize) + .WithParameter("backgroundColor", backgroundColor) + .WithParameter("textColor", textColor) + .WithParameter("fontName", font) + .SingleInstance(); + + var isSorted = BoolParser.ParseIsSorted(options.IsSorted); + builder.Register((c, p) => + { + var wordReader = c.Resolve(); + var wordCounter = c.Resolve(); + var normalizer = c.Resolve(); + var wordFilter = c.Resolve(); + + foreach (var word in wordReader.ReadByLines(options.DataFileName)) + { + var wordInLowerCase = word.ToLower(); + if (!wordFilter.IsCorrectWord(wordInLowerCase)) + { + continue; + } + wordCounter.AddWord(wordInLowerCase); + } + + var normalizedValues = normalizer.Normalize(wordCounter.Values); + return new NormalizedFrequencyBasedCloudLayouterWorker( + options.MaxRectangleWidth, + options.MaxRectangleHeight, + normalizedValues, + isSorted); + }).As().SingleInstance(); + + builder.RegisterType() + .WithParameter("size", imageSize) + .WithParameter("maxRectangleWidth", options.MaxRectangleWidth) + .WithParameter("maxRectangleHeight", options.MaxRectangleHeight) + .WithParameter("imageFileName", options.ImageFileName) + .WithParameter("dataFileName", options.DataFileName) + .SingleInstance(); + + return builder.Build(); + } + } +} diff --git a/TagCloud/GlobalSuppressions.cs b/TagCloud/GlobalSuppressions.cs new file mode 100644 index 000000000..75089be86 --- /dev/null +++ b/TagCloud/GlobalSuppressions.cs @@ -0,0 +1,15 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + + +// Так делать явно плохо +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.DrawText(System.Drawing.Graphics,System.Drawing.Rectangle,System.String)")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.Draw(System.Collections.Generic.IList{TagCloud.Tag})~System.Drawing.Bitmap")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.FindFittingFontSize(System.Drawing.Graphics,System.String,System.Drawing.Rectangle)~System.Int32")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:Program.ParseFont(System.String)~System.String")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.ImageSavers.ImageSaver.SaveFile(System.Drawing.Bitmap,System.String,System.String)")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~System.String")] diff --git a/TagCloud/ImageSavers/IImageSaver.cs b/TagCloud/ImageSavers/IImageSaver.cs new file mode 100644 index 000000000..794e0bc04 --- /dev/null +++ b/TagCloud/ImageSavers/IImageSaver.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagCloud.ImageSavers +{ + // Интерфейс сохранения изображения в файл + internal interface IImageSaver + { + public void SaveFile(Bitmap image, string fileName, string format = "png"); + } +} diff --git a/TagCloud/ImageSavers/ImageSaver.cs b/TagCloud/ImageSavers/ImageSaver.cs new file mode 100644 index 000000000..f5d2a0820 --- /dev/null +++ b/TagCloud/ImageSavers/ImageSaver.cs @@ -0,0 +1,25 @@ +using System.Drawing; + +namespace TagCloud.ImageSavers +{ + // Реализован пункт на перспективу: + // Формат результата. + // Поддерживать разные форматы изображений. + internal class ImageSaver : IImageSaver + { + public void SaveFile(Bitmap image, string fileName, string format = "png") + { + if (image is null) + { + throw new ArgumentNullException("Передаваемое изображение не должно быть null"); + } + + if (string.IsNullOrWhiteSpace(fileName)) + { + throw new ArgumentException("Некорректное имя файла для создания"); + } + + image.Save($"{fileName}.{format}"); + } + } +} diff --git a/TagCloud/Normalizers/INormalizer.cs b/TagCloud/Normalizers/INormalizer.cs new file mode 100644 index 000000000..050a7450c --- /dev/null +++ b/TagCloud/Normalizers/INormalizer.cs @@ -0,0 +1,11 @@ +namespace TagCloud.Normalizers +{ + // Интерфейс нормализации количества каждого слова + internal interface INormalizer + { + public Dictionary Normalize( + Dictionary values, + double minCoefficient = 0.25, + int decimalPlaces = 4); + } +} diff --git a/TagCloud/Normalizers/Normalizer.cs b/TagCloud/Normalizers/Normalizer.cs new file mode 100644 index 000000000..6f7cd329f --- /dev/null +++ b/TagCloud/Normalizers/Normalizer.cs @@ -0,0 +1,58 @@ +namespace TagCloud.Normalizers +{ + // Слово, которое встречается чаще всего, будет иметь вес 1.0. + // Это означает, что оно в дальнейшем будет иметь прямоугольник + // с максимальным размером. + // Слово с минимальной частотой будет иметь + // minCoefficient * максимальный размеро прямоугольника. + internal class Normalizer : INormalizer + { + public Dictionary Normalize( + Dictionary values, + double minCoefficient = 0.25, + int decimalPlaces = 4) + { + if (minCoefficient < 0) + { + throw new ArgumentException("Минимальный коэффициент не может быть меньше 0"); + } + + var result = new Dictionary(); + + var maxValue = values.Values.Max(); + var minValue = values.Values.Min(); + + var scale = 1.0 - minCoefficient; + + foreach (var pair in values) + { + result[pair.Key] = CalculateNormalizedValue( + minCoefficient, + scale, + pair.Value, + minValue, + maxValue, + decimalPlaces); + } + + return result; + } + + private double CalculateNormalizedValue( + double minCoefficient, + double scale, + double value, + uint minValue, + uint maxValue, + int decimalPlaces) + { + if (minValue == maxValue) + { + return 1.0; + } + return Math.Round( + minCoefficient + scale * ((double)(value - minValue) / (maxValue - minValue)), + decimalPlaces); + } + } +} diff --git a/TagCloud/Parsers/BoolParser.cs b/TagCloud/Parsers/BoolParser.cs new file mode 100644 index 000000000..82b78e875 --- /dev/null +++ b/TagCloud/Parsers/BoolParser.cs @@ -0,0 +1,14 @@ +namespace TagCloud.Parsers +{ + internal static class BoolParser + { + public static bool ParseIsSorted(string value) + { + if (value == false.ToString() || value == true.ToString()) + { + return value == true.ToString(); + } + throw new ArgumentException($"Неизвестный параметр сортировки {value}"); + } + } +} diff --git a/TagCloud/Parsers/ColorParser.cs b/TagCloud/Parsers/ColorParser.cs new file mode 100644 index 000000000..0d7ed14e2 --- /dev/null +++ b/TagCloud/Parsers/ColorParser.cs @@ -0,0 +1,17 @@ +using System.Drawing; + +namespace TagCloud.Parsers +{ + internal static class ColorParser + { + public static Color ParseColor(string color) + { + var result = Color.FromName(color); + if (!result.IsKnownColor) + { + throw new ArgumentException($"Неизвестный цвет {color}"); + } + return result; + } + } +} diff --git a/TagCloud/Parsers/FontParser.cs b/TagCloud/Parsers/FontParser.cs new file mode 100644 index 000000000..46c97adaf --- /dev/null +++ b/TagCloud/Parsers/FontParser.cs @@ -0,0 +1,18 @@ +using System.Drawing; + +namespace TagCloud.Parsers +{ + internal static class FontParser + { + public static string ParseFont(string font) + { + if (!FontFamily.Families.Any( + x => x.Name.Equals(font, StringComparison.OrdinalIgnoreCase))) + { + throw new ArgumentException($"Неизвестный шрифт {font}"); + } + return font; + } + + } +} diff --git a/TagCloud/Parsers/SizeParser.cs b/TagCloud/Parsers/SizeParser.cs new file mode 100644 index 000000000..1ad249b35 --- /dev/null +++ b/TagCloud/Parsers/SizeParser.cs @@ -0,0 +1,21 @@ +using System.Drawing; + +namespace TagCloud.Parsers +{ + internal static class SizeParser + { + public static Size ParseImageSize(string size) + { + var dimensions = size.Split(':'); + if (dimensions.Length != 2 + || !int.TryParse(dimensions[0], out var width) + || !int.TryParse(dimensions[1], out var height)) + { + throw new ArgumentException( + $"Некорректный формат размера изображения: {size}." + + $" Используйте формат Ширина:Высота, например 5000:5000."); + } + return new Size(width, height); + } + } +} diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs new file mode 100644 index 000000000..47680d128 --- /dev/null +++ b/TagCloud/Program.cs @@ -0,0 +1,37 @@ +using Autofac; +using CommandLine; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("TagCloud.Tests")] +namespace TagCloud +{ + internal class Program + { + private static IContainer container; + + static void Main(string[] args) + { + var parserResult = Parser.Default.ParseArguments(args); + + parserResult.WithParsed(options => + { + container = DIContainer.ConfigureContainer(options); + Run(options.ResultFormat); + }); + + parserResult.WithNotParsed(errors => + { + Console.WriteLine("Ошибка парсинга аргументов:"); + foreach (var error in errors) + Console.WriteLine(error.ToString()); + }); + } + + private static void Run(string resultFormat) + { + using var scope = container.BeginLifetimeScope(); + var program = scope.Resolve(); + program.Execute(resultFormat); + } + } +} diff --git a/TagCloud/ProgramExecutor.cs b/TagCloud/ProgramExecutor.cs new file mode 100644 index 000000000..c50e0d2eb --- /dev/null +++ b/TagCloud/ProgramExecutor.cs @@ -0,0 +1,35 @@ +using System.Drawing; +using TagCloud.CloudLayouters; +using TagCloud.CloudLayouterPainters; +using TagCloud.CloudLayouterWorkers; +using TagCloud.ImageSavers; +using TagCloud.Normalizers; +using TagCloud.WordCounters; +using TagCloud.WordFilters; +using TagCloud.WordReaders; + +namespace TagCloud +{ + internal class ProgramExecutor( + string imageFileName, + IWordCounter wordCounter, + INormalizer normalizer, + ICloudLayouter layouter, + ICloudLayouterPainter painter, + ICloudLayouterWorker worker, + IImageSaver imageSaver) + { + public void Execute(string resultFormat) + { + var normalizedWordWeights = normalizer.Normalize(wordCounter.Values); + + var tags = new List(); + foreach (var rectangleProperty in worker.GetNextRectangleProperties()) + { + tags.Add(new Tag(rectangleProperty.word, layouter.PutNextRectangle(rectangleProperty.size))); + } + + imageSaver.SaveFile(painter.Draw(tags), imageFileName, resultFormat); + } + } +} diff --git a/TagCloud/SnowWhiteContent.txt b/TagCloud/SnowWhiteContent.txt new file mode 100644 index 000000000..239707285 --- /dev/null +++ b/TagCloud/SnowWhiteContent.txt @@ -0,0 +1,2941 @@ +Long +long +ago +in +the +winter +time +when +the +snowflakes +were +falling +like +little +white +feathers +from +the +sky +a +beautiful +Queen +sat +beside +her +window +which +was +framed +in +black +ebony +and +stitched +As +she +worked +she +looked +sometimes +at +the +falling +snow +and +so +it +happened +that +she +pricked +her +finger +with +her +needle +so +that +three +drops +of +blood +fell +upon +the +snow +How +pretty +the +red +blood +looked +upon +the +dazzling +white +The +Queen +said +to +herself +as +she +saw +it +Ah +me +If +only +I +had +a +dear +little +child +as +white +as +the +snow +as +rosy +as +the +blood +and +with +hair +as +black +as +the +ebony +window +frame +Soon +afterwards +a +little +daughter +came +to +her +who +was +white +as +snow +rosy +as +the +blood +and +whose +hair +was +as +black +as +ebony +so +she +was +called +Little +Snow +White +But +alas +When +the +little +one +came +the +good +Queen +died +A +year +passed +away +and +the +King +took +another +wife +She +was +very +beautiful +but +so +proud +and +haughty +that +she +could +not +bear +to +be +surpassed +in +beauty +by +anyone +She +possessed +a +wonderful +mirror +which +could +answer +her +when +she +stood +before +it +and +said +Mirror +mirror +upon +the +wall +Who +is +the +fairest +of +all +The +mirror +answered +Thou +O +Queen +art +the +fairest +of +all +and +the +Queen +was +contented +because +she +knew +the +mirror +could +speak +nothing +but +the +truth +But +as +time +passed +on +Little +Snow +White +grew +more +and +more +beautiful +until +when +she +was +seven +years +old +she +was +as +lovely +as +the +bright +day +and +still +more +lovely +than +the +Queen +herself +so +that +when +the +lady +one +day +asked +her +mirror +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +It +answered +O +Lady +Queen +though +fair +ye +be +Snow +White +is +fairer +far +to +see +The +Queen +was +horrified +and +from +that +moment +envy +and +pride +grew +in +her +heart +like +rank +weeds +until +one +day +she +called +a +huntsman +and +said +Take +the +child +away +into +the +woods +and +kill +her +for +I +can +no +longer +bear +the +sight +of +her +And +when +you +return +bring +with +you +her +heart +that +I +may +know +you +have +obeyed +my +will +The +huntsman +dared +not +disobey +so +he +led +Snow +White +out +into +the +woods +and +placed +an +arrow +in +his +bow +to +pierce +her +innocent +heart +but +the +little +maid +begged +him +to +spare +her +life +and +the +child’s +beauty +touched +his +heart +with +pity +so +that +he +bade +her +run +away +Then +as +a +young +wild +boar +came +rushing +by +he +killed +it +took +out +its +heart +and +carried +it +home +to +the +Queen +Poor +little +Snow +White +was +now +all +alone +in +the +wild +wood +and +so +frightened +was +she +that +she +trembled +at +every +leaf +that +rustled +So +she +began +to +run +and +ran +on +and +on +until +she +came +to +a +little +house +where +she +went +in +to +rest +In +the +little +house +everything +she +saw +was +tiny +but +more +dainty +and +clean +than +words +can +tell +Upon +a +white +covered +table +stood +seven +little +plates +and +upon +each +plate +lay +a +little +spoon +besides +which +there +were +seven +knives +and +forks +and +seven +little +goblets +Against +the +wall +and +side +by +side +stood +seven +little +beds +covered +with +snow +white +sheets +Snow +White +was +so +hungry +and +thirsty +that +she +took +a +little +food +from +each +of +the +seven +plates +and +drank +a +few +drops +of +wine +from +each +goblet +for +she +did +not +wish +to +take +everything +away +from +one +Then +because +she +was +so +tired +she +crept +into +one +bed +after +the +other +seeking +for +rest +but +one +was +too +long +another +too +short +and +so +on +until +she +came +to +the +seventh +which +suited +her +exactly +so +she +said +her +prayers +and +soon +fell +fast +asleep +When +night +fell +the +masters +of +the +little +house +came +home +They +were +seven +dwarfs +who +worked +with +a +pick +axe +and +spade +searching +for +cooper +and +gold +in +the +heart +of +the +mountains +They +lit +their +seven +candles +and +then +saw +that +someone +had +been +to +visit +them +The +first +said +Who +has +been +sitting +on +my +chair +The +second +said +Who +has +been +eating +from +my +plate +The +third +said +Who +has +taken +a +piece +of +my +bread +The +fourth +said +Who +has +taken +some +of +my +vegetables +The +fifth +Who +has +been +using +my +fork +The +sixth +Who +has +been +cutting +with +my +knife +The +seventh +Who +has +been +drinking +out +of +my +goblet +The +first +looked +round +and +saw +that +his +bed +was +rumpled +so +he +said +Who +has +been +getting +into +my +bed +Then +the +others +looked +round +and +each +one +cried +Someone +has +been +on +my +bed +too +But +the +seventh +saw +little +Snow +White +lying +asleep +in +his +bed +and +called +the +others +to +come +and +look +at +her +and +they +cried +aloud +with +surprise +and +fetched +their +seven +little +candles +so +that +they +might +see +her +the +better +and +they +were +so +pleased +with +her +beauty +that +they +let +her +sleep +on +all +night +When +the +sun +rose +Snow +White +awoke +and +oh +How +frightened +she +was +when +she +saw +the +seven +little +dwarfs +But +they +were +very +friendly +and +asked +what +her +name +was +My +name +is +Snow +White +she +answered +And +how +did +you +come +to +get +into +our +house +questioned +the +dwarfs +Then +she +told +them +how +her +cruel +step +mother +had +intended +her +to +be +killed +but +how +the +huntsman +had +spared +her +life +and +she +had +run +on +until +she +reached +the +little +house +And +the +dwarfs +said +If +you +will +take +care +of +our +house +cook +for +us +and +make +the +beds +wash +mend +and +knit +and +keep +everything +neat +and +clean +then +you +may +stay +with +us +altogether +and +you +shall +want +for +nothing +With +all +my +heart +answered +Snow +White +and +so +she +stayed +She +kept +the +house +neat +and +clean +for +the +dwarfs +who +went +off +early +in +the +morning +to +search +for +copper +and +gold +in +the +mountains +and +who +expected +their +meal +to +be +standing +ready +for +them +when +they +returned +at +night +All +day +long +Snow +White +was +alone +and +the +good +little +dwarfs +warned +her +to +be +careful +to +let +no +one +into +the +house +For +said +they +your +step +mother +will +soon +discover +that +you +are +living +here +The +Queen +believing +of +course +that +Snow +White +was +dead +and +that +therefore +she +was +again +the +most +beautiful +lady +in +the +land +went +to +her +mirror +and +said +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +Then +the +mirror +answered +O +Lady +Queen +though +fair +ye +be +Snow +White +is +fairer +far +to +see +Over +the +hills +and +far +away +She +dwells +with +seven +dwarfs +to +day +How +angry +she +was +for +she +knew +that +the +mirror +spoke +the +truth +and +that +the +huntsman +must +have +deceived +her +She +thought +and +thought +how +she +might +kill +Snow +White +for +she +knew +she +would +have +neither +rest +nor +peace +until +she +really +was +the +most +beautiful +lady +in +the +land +At +length +she +decided +what +to +do +She +painted +her +face +and +dressed +herself +like +an +old +peddler +woman +so +that +no +one +could +recognize +her +and +in +this +disguise +she +climbed +the +seven +mountains +that +lay +between +her +and +the +dwarfs’ +house +and +knocked +at +their +door +and +cried +Good +wares +to +sell +very +cheap +today +Snow +White +peeped +from +the +window +and +said +Good +day +good +wife +and +what +are +your +wares +All +sorts +of +pretty +things +my +dear +answered +the +woman +Silken +laces +of +every +colour +and +she +held +up +a +bright +coloured +one +made +of +plaited +silks +Surely +I +might +let +this +honest +old +woman +come +in +thought +Snow +White +and +unbolted +the +door +and +bought +the +pretty +lace +Dear +dear +what +a +figure +you +are +child +said +the +old +woman +come +let +me +lace +you +properly +for +once +Snow +White +had +no +suspicious +thoughts +so +she +placed +herself +in +front +of +the +old +woman +that +she +might +fasten +her +dress +with +the +new +silk +lace +But +in +less +than +no +time +the +wicked +creature +had +laced +her +so +tightly +that +she +could +not +breathe +but +fell +down +upon +the +ground +as +though +she +were +dead +Now +said +the +Queen +I +am +once +more +the +most +beautiful +lady +in +the +land +and +she +went +away +When +the +dwarfs +came +home +they +were +very +grieved +to +find +their +dear +little +Snow +White +lying +upon +the +ground +as +though +she +were +dead +They +lifted +her +gently +and +seeing +that +she +was +too +tightly +laced +they +cut +the +silken +cord +when +she +drew +a +long +breath +and +then +gradually +came +back +to +life +When +the +dwarfs +heard +all +that +had +happened +they +said +The +peddler +woman +was +certainly +the +wicked +Queen +Now +take +care +in +future +that +you +open +the +door +to +none +when +we +are +not +with +you +The +wicked +Queen +had +no +sooner +reached +home +than +she +went +to +her +mirror +and +said +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +And +the +mirror +answered +as +before +O +Lady +Queen +though +fair +ye +be +Snow +White +is +fairer +far +to +see +Over +the +hills +and +far +away +She +dwells +with +seven +dwarfs +to +day +The +blood +rushed +to +her +face +as +she +heard +these +words +for +she +knew +that +Snow +White +must +have +come +to +life +again +But +I +will +manage +to +put +an +end +to +her +yet +she +said +and +then +by +means +of +her +magic +she +made +a +poisonous +comb +Again +she +disguised +herself +climbed +the +seven +mountains +and +knocked +at +the +door +of +the +seven +dwarfs’ +cottage +crying +Good +wares +to +sell +very +cheap +today +Snow +White +looked +out +of +the +window +and +said +Go +away +good +woman +for +I +dare +not +let +you +in +Surely +you +can +look +at +my +goods +answered +the +woman +and +held +up +the +poisonous +comb +which +pleased +Snow +White +so +well +that +she +opened +the +door +and +bought +it +Come +let +me +comb +your +hair +in +the +newest +way +said +the +woman +and +the +poor +unsuspicious +child +let +her +have +her +way +but +no +sooner +did +the +comb +touch +her +hair +than +the +poison +began +to +work +and +she +fell +fainting +to +the +ground +There +you +model +of +beauty +said +the +wicked +woman +as +she +went +away +you +are +done +for +at +last +But +fortunately +it +was +almost +time +for +the +dwarfs +to +come +home +and +as +soon +as +they +came +in +and +found +Snow +White +lying +upon +the +ground +they +guessed +that +her +wicked +step +mother +had +been +there +again +and +set +to +work +to +find +out +what +was +wrong +They +soon +saw +the +poisonous +comb +and +drew +it +out +and +almost +immediately +Snow +White +began +to +recover +and +told +them +what +had +happened +Once +more +they +warned +her +to +be +on +her +guard +and +to +open +the +door +to +no +one +When +the +Queen +reached +home +she +went +straight +to +the +mirror +and +said +Mirror +mirror +on +the +wall +Who +is +the +fairest +fair +of +all +And +the +mirror +answered +O +Lady +Queen +though +fair +ye +be +Snow +White +is +fairer +far +to +see +Over +the +hills +and +far +away +She +dwells +with +seven +dwarfs +to +day +When +the +Queen +heard +these +words +she +shook +with +rage +Snow +White +shall +die +she +cried +even +if +it +costs +me +my +own +life +to +manage +it +She +went +into +a +secret +chamber +where +no +one +else +ever +entered +and +there +she +made +a +poisonous +apple +and +then +she +painted +her +face +and +disguised +herself +as +a +peasant +woman +and +climbed +the +seven +mountains +and +went +to +the +dwarfs’ +house +She +knocked +at +the +door +Snow +White +put +her +head +out +of +the +window +and +said +I +must +not +let +anyone +in +the +seven +dwarfs +have +forbidden +me +to +do +so +It’s +all +the +same +to +me +answered +the +peasant +woman +I +shall +soon +get +rid +of +these +fine +apples +But +before +I +go +I’ll +make +you +a +present +of +one +Oh +No +said +Snow +White +for +I +must +not +take +it +Surely +you +are +not +afraid +of +poison +said +the +woman +See +I +will +cut +one +in +two +the +rosy +cheek +you +shall +take +and +the +white +cheek +I +will +eat +myself +Now +the +apple +had +been +so +cleverly +made +that +only +the +rose +cheeked +side +contained +the +poison +Snow +White +longed +for +the +delicious +looking +fruit +and +when +she +saw +that +the +woman +ate +half +of +it +she +thought +there +could +be +no +danger +and +stretched +out +her +hand +and +took +the +other +part +But +no +sooner +had +she +tasted +it +than +she +fell +down +dead +The +wicked +Queen +laughed +aloud +with +joy +as +she +gazed +at +her +White +as +snow +red +as +blood +black +as +ebony +she +said +this +time +the +dwarfs +cannot +awaken +you +And +she +went +straight +home +and +asked +her +mirror +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +And +at +length +it +answered +Thou +O +Queen +art +fairest +of +all +So +her +envious +heart +had +peace +at +least +so +much +peace +as +an +envious +heart +can +have +When +the +little +dwarfs +came +home +at +night +they +found +Snow +White +lying +upon +the +ground +No +breath +came +from +her +parted +lips +for +she +was +dead +They +lifted +her +tenderly +and +sought +for +some +poisonous +object +which +might +have +caused +the +mischief +unlaced +her +frock +combed +her +hair +and +washed +her +with +wine +and +water +but +all +in +vain +dead +she +was +and +dead +she +remained +They +laid +her +upon +a +bier +and +all +seven +of +them +sat +round +about +it +and +wept +as +though +their +hearts +would +break +for +three +whole +days +When +the +time +came +that +she +should +be +laid +in +the +ground +they +could +not +bear +to +part +from +her +Her +pretty +cheeks +were +still +rosy +red +and +she +looked +just +as +though +she +were +still +living +We +cannot +hide +her +away +in +the +dark +earth +said +the +dwarfs +and +so +they +made +a +transparent +coffin +of +shining +glass +and +laid +her +in +it +and +wrote +her +name +upon +it +in +letters +of +gold +also +they +wrote +that +she +was +a +King’s +daughter +Then +they +placed +the +coffin +upon +the +mountain +top +and +took +it +in +turns +to +watch +beside +it +And +all +the +animals +came +and +wept +for +Snow +White +first +an +owl +then +a +raven +and +then +a +little +dove +For +a +long +long +time +little +Snow +White +lay +in +the +coffin +but +her +form +did +not +wither +she +only +looked +as +though +she +slept +for +she +was +still +as +white +as +snow +as +red +as +blood +and +as +black +as +ebony +It +chanced +that +a +King’s +son +came +into +the +wood +and +went +to +the +dwarfs’ +house +meaning +to +spend +the +night +there +He +saw +the +coffin +upon +the +mountain +top +with +little +Snow +White +lying +within +it +and +he +read +the +words +that +were +written +upon +it +in +letters +of +gold +And +he +said +to +the +dwarfs +If +you +will +but +let +me +have +the +coffin +you +may +ask +of +me +what +you +will +and +I +will +give +it +to +you +But +the +dwarfs +answered +We +would +not +sell +it +for +all +the +gold +in +the +world +Then +said +the +Prince +Let +me +have +it +as +a +gift +I +pray +you +for +I +cannot +live +without +seeing +little +Snow +White +and +I +will +prize +your +gift +as +the +dearest +of +my +possessions +The +good +little +dwarfs +pitied +him +when +they +heard +these +words +and +so +gave +him +the +coffin +The +King’s +son +then +bade +his +servants +place +it +upon +their +shoulders +and +carry +it +away +but +as +they +went +they +stumbled +over +the +stump +of +a +tree +and +the +violent +shaking +shook +the +piece +of +poisonous +apple +which +had +lodged +in +Snow +White’s +throat +out +again +so +that +she +opened +her +eyes +raised +the +lid +of +the +coffin +and +sat +up +alive +once +more +Where +am +I +she +cried +and +the +happy +Prince +answered +Thou +art +with +me +dearest +Then +he +told +her +all +that +had +happened +and +how +he +loved +her +better +than +the +whole +world +and +begged +her +to +go +with +him +to +his +father’s +palace +and +be +his +wife +Snow +White +consented +and +went +with +him +and +the +wedding +was +celebrated +with +great +splendour +and +magnificence +Little +Snow +White’s +wicked +step +mother +was +bidden +to +the +feast +and +when +she +had +arrayed +herself +in +her +most +beautiful +garments +she +stood +before +her +mirror +and +said +Mirror +mirror +upon +the +wall +Who +is +the +fairest +fair +of +all +And +the +mirror +answered +O +Lady +Queen +though +fair +ye +be +The +young +Queen +is +fairer +to +see +Oh +How +angry +the +wicked +woman +was +then +and +so +terrified +too +that +she +scarcely +knew +what +to +do +At +first +she +thought +she +would +not +go +to +the +wedding +at +all +but +then +she +felt +that +she +could +not +rest +until +she +had +seen +the +young +Queen +No +sooner +did +she +enter +the +palace +than +she +recognized +little +Snow +White +and +could +not +move +for +terror +Then +a +pair +of +iron +shoes +was +brought +into +the +room +and +set +before +her +and +these +she +was +forced +to +put +on +and +to +dance +in +them +until +she +could +dance +no +longer +but +fell +down +dead +and +that +was +the +end +of +her \ No newline at end of file diff --git a/TagCloud/Tag.cs b/TagCloud/Tag.cs new file mode 100644 index 000000000..ee37ae006 --- /dev/null +++ b/TagCloud/Tag.cs @@ -0,0 +1,6 @@ +using System.Drawing; + +namespace TagCloud +{ + internal record Tag(string Text, Rectangle Rectangle); +} diff --git a/TagCloud/TagCloud.csproj b/TagCloud/TagCloud.csproj new file mode 100644 index 000000000..113b51b7e --- /dev/null +++ b/TagCloud/TagCloud.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + diff --git a/TagCloud/WordCounters/IWordCounter.cs b/TagCloud/WordCounters/IWordCounter.cs new file mode 100644 index 000000000..f41d1763b --- /dev/null +++ b/TagCloud/WordCounters/IWordCounter.cs @@ -0,0 +1,9 @@ +namespace TagCloud.WordCounters +{ + // Интерфейс подсчёта количества каждого уникального слова + internal interface IWordCounter + { + public void AddWord(string word); + public Dictionary Values { get; } + } +} diff --git a/TagCloud/WordCounters/WordCounter.cs b/TagCloud/WordCounters/WordCounter.cs new file mode 100644 index 000000000..c5953a1f1 --- /dev/null +++ b/TagCloud/WordCounters/WordCounter.cs @@ -0,0 +1,18 @@ +namespace TagCloud.WordCounters +{ + internal class WordCounter : IWordCounter + { + private readonly Dictionary counts = new Dictionary(); + public Dictionary Values => counts; + + public void AddWord(string word) + { + if (!counts.ContainsKey(word)) + { + counts[word] = 1; + return; + } + counts[word] += 1; + } + } +} diff --git a/TagCloud/WordFilters/IWordFilter.cs b/TagCloud/WordFilters/IWordFilter.cs new file mode 100644 index 000000000..4e175d569 --- /dev/null +++ b/TagCloud/WordFilters/IWordFilter.cs @@ -0,0 +1,8 @@ +namespace TagCloud.WordFilters +{ + // Интерфейс фильтрации "скучных" слов + internal interface IWordFilter + { + public bool IsCorrectWord(string word); + } +} diff --git a/TagCloud/WordFilters/WordFilter.cs b/TagCloud/WordFilters/WordFilter.cs new file mode 100644 index 000000000..add9c3eb1 --- /dev/null +++ b/TagCloud/WordFilters/WordFilter.cs @@ -0,0 +1,75 @@ +namespace TagCloud.WordFilters +{ + // Реализован пункт на перспективу: + // Предобработка слов. + // Дать возможность влиять на список скучных слов, которые не попадут в облако. + internal class WordFilter : IWordFilter + { + private readonly HashSet bannedWords = new HashSet() + { + // Просто скучные по моему мнению + "not", "also", "how", "let", + // To have + "have", "has", "had", "having", + // To be + "am", "is", "are", "was", "were", "be", "been", "being", + // Артикли + "a", "an", "the", + // Местоимения + "i", "you", "he", "she", "it", "we", "they", "me", "him", + "her", "us", "them", "my", "your", "his", "its", "our", "their", + "mine", "yours", "hers", "theirs", "myself", "yourself", "himself", + "herself", "itself", "ourselves", "yourselves", "themselves", "this", + "that", "these", "those", "who", "whom", "whose", "what", "which", + "some", "any", "none", "all", "many", "few", "several", + "everyone", "somebody", "anybody", "nobody", "everything", "anything", + "nothing", "each", "every", "either", "neither", + // Предлоги + "about", "above", "across", "after", "against", "along", "amid", "among", + "around", "as", "at", "before", "behind", "below", "beneath", "beside", + "besides", "between", "beyond", "but", "by", "despite", "down", "during", + "except", "for", "from", "in", "inside", "into", "like", "near", "of", "off", + "on", "onto", "out", "outside", "over", "past", "since", "through", "throughout", + "till", "to", "toward", "under", "underneath", "until", "up", "upon", "with", + "within", "without", + // Союзы + "and", "but", "or", "nor", "for", "yet", "so", "if", "because", "although", "though", + "since", "until", "unless", "while", "whereas", "when", "where", "before", "after", + // Междометия + "o","ah", "aha", "alas", "aw", "aye", "eh", "hmm", "huh", "hurrah", "no", "oh", "oops", + "ouch", "ow", "phew", "shh", "tsk", "ugh", "um", "wow", "yay", "yes", "yikes" + }; + + public WordFilter(IList? toAdd = null, IList? toExclude = null) + { + if (toAdd is not null) + { + foreach (var word in toAdd) + { + Add(word); + } + } + + if (toExclude is not null) + { + foreach (var word in toExclude) + { + Remove(word); + } + } + } + + public bool Add(string word) => bannedWords.Add(word); + + public bool Remove(string word) => bannedWords.Remove(word); + + public void Clear() => bannedWords.Clear(); + + public HashSet BannedWords => bannedWords; + + public bool IsCorrectWord(string word) + { + return !bannedWords.Contains(word); + } + } +} diff --git a/TagCloud/WordReaders/IWordReader.cs b/TagCloud/WordReaders/IWordReader.cs new file mode 100644 index 000000000..5384df2ab --- /dev/null +++ b/TagCloud/WordReaders/IWordReader.cs @@ -0,0 +1,8 @@ +namespace TagCloud.WordReaders +{ + // Интерфейс для построчного чтения содержимого файла + internal interface IWordReader + { + public IEnumerable ReadByLines(string path); + } +} diff --git a/TagCloud/WordReaders/WordReader.cs b/TagCloud/WordReaders/WordReader.cs new file mode 100644 index 000000000..4443209a2 --- /dev/null +++ b/TagCloud/WordReaders/WordReader.cs @@ -0,0 +1,22 @@ +namespace TagCloud.WordReaders +{ + internal class WordReader : IWordReader + { + public IEnumerable ReadByLines(string path) + { + if (!File.Exists(path)) + { + throw new FileNotFoundException($"Файл {path} не существует"); + } + + foreach (var line in File.ReadAllLines(path)) + { + if (line.Contains(' ')) + { + throw new Exception($"Файл {path} содержит строку с двумя и более словами"); + } + yield return line; + } + } + } +} diff --git a/fp.sln b/fp.sln index d104ab530..6a1522eb2 100644 --- a/fp.sln +++ b/fp.sln @@ -1,14 +1,21 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileSenderRailway", "FileSenderRailway\FileSenderRailway.csproj", "{D979A1EA-516A-46BC-BE6C-8845CA10853D}" +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileSenderRailway", "FileSenderRailway\FileSenderRailway.csproj", "{D979A1EA-516A-46BC-BE6C-8845CA10853D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ErrorHandling", "ErrorHandling\ErrorHandling.csproj", "{66FAF276-533D-4733-AB2E-A9905D678CF6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ErrorHandling", "ErrorHandling\ErrorHandling.csproj", "{66FAF276-533D-4733-AB2E-A9905D678CF6}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{754C1CC8-A8B6-46C6-B35C-8A43B80111A0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Summator", "Samples\Summator\Summator.csproj", "{C33F3A5E-A1ED-4657-9B35-968A4CB23AA1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Summator", "Samples\Summator\Summator.csproj", "{C33F3A5E-A1ED-4657-9B35-968A4CB23AA1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConwaysGameOfLife", "Samples\ConwaysGameOfLife\ConwaysGameOfLife.csproj", "{4B77EC28-5FB5-4095-B3D7-127F5C488D6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConwaysGameOfLife", "Samples\ConwaysGameOfLife\ConwaysGameOfLife.csproj", "{4B77EC28-5FB5-4095-B3D7-127F5C488D6E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TagCloud", "TagCloud\TagCloud.csproj", "{4B940669-E063-446F-AE0C-6DDF3725D758}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TagCloud.Tests", "TagCloud.Tests\TagCloud.Tests.csproj", "{F738E726-1084-490D-A000-BD7B3F50D554}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -32,9 +39,23 @@ Global {4B77EC28-5FB5-4095-B3D7-127F5C488D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU {4B77EC28-5FB5-4095-B3D7-127F5C488D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU {4B77EC28-5FB5-4095-B3D7-127F5C488D6E}.Release|Any CPU.Build.0 = Release|Any CPU + {4B940669-E063-446F-AE0C-6DDF3725D758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B940669-E063-446F-AE0C-6DDF3725D758}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B940669-E063-446F-AE0C-6DDF3725D758}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B940669-E063-446F-AE0C-6DDF3725D758}.Release|Any CPU.Build.0 = Release|Any CPU + {F738E726-1084-490D-A000-BD7B3F50D554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F738E726-1084-490D-A000-BD7B3F50D554}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F738E726-1084-490D-A000-BD7B3F50D554}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F738E726-1084-490D-A000-BD7B3F50D554}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {C33F3A5E-A1ED-4657-9B35-968A4CB23AA1} = {754C1CC8-A8B6-46C6-B35C-8A43B80111A0} {4B77EC28-5FB5-4095-B3D7-127F5C488D6E} = {754C1CC8-A8B6-46C6-B35C-8A43B80111A0} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {32B979F8-E2E6-47D9-B6B1-02CA478687F8} + EndGlobalSection EndGlobal From 741d7f648b00d383408d024729b244031c1ea9b7 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 13 Jan 2025 20:10:53 +0500 Subject: [PATCH 02/32] =?UTF-8?q?=D0=A1=D1=82=D0=B0=D1=80=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D1=8B,=20=D0=B2=D1=8B=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D1=81=D1=8B=D0=B2=D0=B0=D1=8E=D1=89=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D1=8F,?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D1=8B?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=BF=D0=B0=D1=82=D1=82=D0=B5=D1=80=D0=BD=20?= =?UTF-8?q?Result?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FileSenderRailway/Result.cs | 3 ++ .../CloudLayouterPainterTest.cs | 16 +++++--- ...rcularCloudLayouterMainRequirementsTest.cs | 4 +- .../CircularCloudLayouterTest.cs | 9 +++-- ...edFrequencyBasedCloudLayouterWorkerTest.cs | 4 +- .../RandomCloudLayouterWorkerTest.cs | 11 ++++-- .../ImageSaversTests/ImageSaverTest.cs | 15 ++++++-- .../NormalizersTest/NormalizerTest.cs | 19 ++++++---- .../WordReadersTests/WordReaderTest.cs | 12 ++++-- .../CloudLayouterPainter.cs | 14 ++++--- .../ICloudLayouterPainter.cs | 5 ++- ...alizedFrequencyBasedCloudLayouterWorker.cs | 14 +++---- .../RandomCloudLayouterWorker.cs | 29 ++++++++------- .../CircularCloudLayouter.cs | 10 ++--- TagCloud/CloudLayouters/ICloudLayouter.cs | 5 ++- TagCloud/ImageSavers/IImageSaver.cs | 5 ++- TagCloud/ImageSavers/ImageSaver.cs | 11 ++++-- TagCloud/Normalizers/INormalizer.cs | 6 ++- TagCloud/Normalizers/Normalizer.cs | 10 +++-- TagCloud/Parsers/BoolParser.cs | 10 +++-- TagCloud/Parsers/ColorParser.cs | 9 +++-- TagCloud/Parsers/FontParser.cs | 9 +++-- TagCloud/Parsers/SizeParser.cs | 37 ++++++++++++++----- TagCloud/WordReaders/IWordReader.cs | 6 ++- TagCloud/WordReaders/WordReader.cs | 14 ++++--- 25 files changed, 182 insertions(+), 105 deletions(-) diff --git a/FileSenderRailway/Result.cs b/FileSenderRailway/Result.cs index da50ce0ad..94978f036 100644 --- a/FileSenderRailway/Result.cs +++ b/FileSenderRailway/Result.cs @@ -1,5 +1,8 @@ using System; +using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("TagCloud")] +[assembly: InternalsVisibleTo("TagCloud.Tests")] namespace FileSenderRailway; public class None diff --git a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs index 549ca4687..bf5b5e748 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs @@ -1,5 +1,8 @@ -using System.Drawing; +using FileSenderRailway; +using FluentAssertions; +using System.Drawing; using TagCloud.CloudLayouterPainters; +using TagCloud.WordReaders; namespace TagCloud.Tests.CloudLayouterPaintersTest { @@ -16,15 +19,18 @@ public void SetUp() [Test] public void Draw_ThrowsArgumentException_WithEmptyTags() { - var painter = new CloudLayouterPainter(new Size(1, 1)); - Assert.Throws(() => painter.Draw(new List())); + var expected = Result.Fail("Список тегов пуст"); + var actual = painter.Draw(new List()); + actual.Should().BeEquivalentTo(expected); } [Test] public void Draw_ThrowsArgumentNullException_WithTagsAsNull() { - var painter = new CloudLayouterPainter(new Size(1, 1)); - Assert.Throws(() => painter.Draw(null!)); + + var expected = Result.Fail("Tags передан как null"); + var actual = painter.Draw(null!); + actual.Should().BeEquivalentTo(expected); } } } diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs index cc2f2dfc9..632a18592 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs @@ -49,7 +49,7 @@ public void SetUp() tags.Add( new Tag( rectangleProperty.word, - circularCloudLayouter.PutNextRectangle(rectangleProperty.size))); + circularCloudLayouter.PutNextRectangle(rectangleProperty.size).GetValueOrThrow())); } rectangles = tags.Select(x => x.Rectangle).ToArray(); } @@ -109,7 +109,7 @@ public void Cleanup() var name = $"{TestContext.CurrentContext.Test.Name}.png"; var path = Path.Combine(failedTestsDirectory, name); - imageSaver.SaveFile(cloudLayouterPainter.Draw(tags), path); + imageSaver.SaveFile(cloudLayouterPainter.Draw(tags).GetValueOrThrow(), path); Console.WriteLine($"Tag cloud visualization saved to file {path}"); } diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs index 8a5ed0e77..2a6db6970 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs @@ -1,4 +1,6 @@ -using System.Drawing; +using FileSenderRailway; +using FluentAssertions; +using System.Drawing; using TagCloud.CloudLayouters.CircularCloudLayouter; namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests @@ -15,8 +17,9 @@ public void PutNextRectangle_ThrowsArgumentException_OnAnyNegativeOrZeroSize( int height) { var size = new Size(width, height); - Assert.Throws( - () => new CircularCloudLayouter().PutNextRectangle(size)); + var expected = Result.Fail("Размеры прямоугольника не могут быть меньше либо равны нуля"); + var actual = new CircularCloudLayouter().PutNextRectangle(size); + actual.Should().BeEquivalentTo(expected); } } } diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs index a6d75e2f5..5034a6575 100644 --- a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs +++ b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs @@ -23,8 +23,10 @@ public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize int width, int height) { - Assert.Throws( + var message = $"Переданное числовое значение должно быть больше 0: {(width <= 0 ? width : height)}"; + var exception = Assert.Throws( () => new NormalizedFrequencyBasedCloudLayouterWorker(width, height, normalizedValues)); + exception.Message.Should().Contain(message); } [TestCase(100, 25, false)] diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs index 81f71e264..92929f460 100644 --- a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs +++ b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs @@ -1,4 +1,5 @@ -using TagCloud.CloudLayouterWorkers; +using FluentAssertions; +using TagCloud.CloudLayouterWorkers; namespace TagCloud.Tests.CloudLayouterWorkersTests { @@ -13,8 +14,10 @@ public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize int width, int height) { - Assert.Throws( + var message = $"Переданное числовое значение должно быть больше 0: {(width <= 0 ? width : height)}"; + var exception = Assert.Throws( () => new RandomCloudLayouterWorker(width, width, height, height)); + exception.Message.Should().Contain(message); } [TestCase(50, 25, 25, 50)] @@ -25,8 +28,10 @@ public void GetNextRectangleSize_ThrowsArgumentException_OnNonConsecutiveSizeVal int minHeight, int maxHeight) { - Assert.Throws( + var message = "Минимальное значение не может быть больше максимального"; + var exception = Assert.Throws( () => new RandomCloudLayouterWorker(minWidth, maxWidth, minHeight, maxHeight)); + exception.Message.Should().Contain(message); } } } diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs index 5a4b1bb55..415ee955c 100644 --- a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs @@ -1,5 +1,8 @@ -using System.Drawing; +using FileSenderRailway; +using FluentAssertions; +using System.Drawing; using TagCloud.ImageSavers; +using TagCloud.WordReaders; namespace TagCloud.Tests.ImageSaversTests { @@ -25,7 +28,9 @@ public void SetUp() public void SaveFile_ArgumentNullException_WithNullBitmap(string filename) { var path = Path.Combine(directoryPath, filename); - Assert.Throws(() => imageSaver.SaveFile(null!, path)); + var expected = Result.Fail("Передаваемое изображение не должно быть null"); + var actual = imageSaver.SaveFile(null!, path); + actual.Should().BeEquivalentTo(expected); } [TestCase(null)] @@ -34,7 +39,9 @@ public void SaveFile_ArgumentNullException_WithNullBitmap(string filename) public void SaveFile_ThrowsArgumentException_WithInvalidFilename(string? filename) { var dummyImage = new Bitmap(1, 1); - Assert.Throws(() => imageSaver.SaveFile(dummyImage, filename!)); + var expected = Result.Fail("Некорректное имя файла для создания"); + var actual = imageSaver.SaveFile(dummyImage, filename!); + actual.Should().BeEquivalentTo(expected); } [TestCase("Test", "png", ExpectedResult = true)] @@ -44,7 +51,7 @@ public bool SaveFile_SavesFile(string filename, string format) var dummyImage = new Bitmap(1, 1); var path = Path.Combine(directoryPath, filename); - File.Delete(path); + File.Delete($"{path}.{format}"); imageSaver.SaveFile(dummyImage, path, format); return File.Exists($"{path}.{format}"); } diff --git a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs index 92121b779..43fca9fd2 100644 --- a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs +++ b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs @@ -1,7 +1,8 @@ -using FluentAssertions; +using FileSenderRailway; +using FluentAssertions; using TagCloud.Normalizers; -namespace TagCloud.Tests.WordCountersTests +namespace TagCloud.Tests.NormalaizersTest { [TestFixture] internal class NormalizerTest @@ -29,21 +30,25 @@ internal class NormalizerTest public void Normalize_ThrowsArgumentException_WithMinCoefficientLessThanZero( double minCoefficient) { - Assert.Throws(() - => normalizer.Normalize(values, minCoefficient, defaultDecimalPlaces)); + var expected = Result.Fail>("Минимальный коэффициент нормализации не может быть меньше 0"); + var actual = normalizer.Normalize(values, minCoefficient, defaultDecimalPlaces); + actual.Should().BeEquivalentTo(expected); } [TestCase(0.25, 4)] [TestCase(0.25, 2)] public void Normalize_CalculatesСorrectly(double minCoefficient, int decimalPlaces) { + var dict = new Dictionary(); foreach (var pair in expectedResult) { - expectedResult[pair.Key] = Math.Round(pair.Value, decimalPlaces); + dict[pair.Key] = Math.Round(pair.Value, decimalPlaces); } + var expected = dict.AsResult(); var actual = normalizer.Normalize(values, minCoefficient, decimalPlaces); - - actual.Should().BeEquivalentTo(expectedResult); + actual.IsSuccess.Should().Be(expected.IsSuccess); + actual.Error.Should().Be(expected.Error); + actual.Value.Should().BeEquivalentTo(expected.Value); } } } diff --git a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs index 1f0aea180..86a834ee1 100644 --- a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs +++ b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs @@ -1,4 +1,6 @@ -using TagCloud.WordReaders; +using FileSenderRailway; +using FluentAssertions; +using TagCloud.WordReaders; namespace TagCloud.Tests.WordReadersTests { @@ -58,14 +60,18 @@ public void SetUp() public void WordReader_ThrowsFileNotFoundException_WithInvalidFilename(string filename) { var path = Path.Combine(directoryPath, filename); - Assert.Throws(() => wordReader.ReadByLines(path).ToArray()); + var expected = Result.Fail($"Файл {path} не существует"); + var actual = wordReader.ReadByLines(path).ToArray(); + actual.Should().Contain(expected).And.HaveCount(1); } [Test] public void WordReader_ThrowsException_WithTwoWordsInOneLine() { var path = Path.Combine(directoryPath, fileWithIncorrectValuesPath); - Assert.Throws(() => wordReader.ReadByLines(path).ToArray()); + var expected = Result.Fail($"Файл {path} содержит строку с двумя и более словами"); + var actual = wordReader.ReadByLines(path).ToArray(); + actual.Should().Contain(expected); } [OneTimeTearDown] diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs index 6c46b3818..416bc876c 100644 --- a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; namespace TagCloud.CloudLayouterPainters { @@ -12,13 +13,16 @@ internal class CloudLayouterPainter( private readonly Color textColor = textColor ?? Color.Black; private readonly string fontName = fontName ?? "Arial"; - public Bitmap Draw(IList tags) + public Result Draw(IList tags) { - ArgumentNullException.ThrowIfNull(tags); + if (tags is null) + { + return Result.Fail("Tags передан как null"); + } if (tags.Count == 0) { - throw new ArgumentException("Список тегов пуст"); + return Result.Fail("Список тегов пуст"); } var result = new Bitmap(imageSize.Width, imageSize.Height); @@ -37,7 +41,7 @@ public Bitmap Draw(IList tags) DrawText(graphics, rectOnCanvas, tag.Text); } - return result; + return result.AsResult(); } private Point GetPositionOnCanvas(Rectangle rectangle) diff --git a/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs index 38fe25ab8..71e1a0ce6 100644 --- a/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/ICloudLayouterPainter.cs @@ -1,10 +1,11 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; namespace TagCloud.CloudLayouterPainters { // Интерфейс отрисовки прямоугольников internal interface ICloudLayouterPainter { - public Bitmap Draw(IList tags); + public Result Draw(IList tags); } } diff --git a/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs index 8bda8ce46..a0f9906eb 100644 --- a/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs +++ b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs @@ -1,4 +1,6 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; +using TagCloud.Parsers; namespace TagCloud.CloudLayouterWorkers { @@ -16,14 +18,8 @@ public NormalizedFrequencyBasedCloudLayouterWorker( Dictionary normalizedValues, bool isSorted = true) { - if (maxRectangleWidth <= 0 || maxRectangleHeight <= 0) - { - throw new ArgumentException( - "Ширина или высота прямоугольника должна быть положительной"); - } - - MaxRectangleWidth = maxRectangleWidth; - MaxRectangleHeight = maxRectangleHeight; + MaxRectangleWidth = SizeParser.ParseSizeDimension(maxRectangleWidth).GetValueOrThrow(); + MaxRectangleHeight = SizeParser.ParseSizeDimension(maxRectangleHeight).GetValueOrThrow(); values = normalizedValues; if (isSorted) { diff --git a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs index b05b6a470..ac8e5a954 100644 --- a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs +++ b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs @@ -1,4 +1,6 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; +using TagCloud.Parsers; namespace TagCloud.CloudLayouterWorkers { @@ -19,24 +21,23 @@ public RandomCloudLayouterWorker( int minRectangleHeight, int maxRectangleHeight) { - if (minRectangleWidth <= 0 || maxRectangleWidth <= 0 - || minRectangleHeight <= 0 || maxRectangleHeight <= 0) + if (AreMinAndMaxSizesAppropriate(minRectangleWidth, maxRectangleWidth).GetValueOrThrow() + && AreMinAndMaxSizesAppropriate(minRectangleHeight, maxRectangleHeight).GetValueOrThrow()) { - throw new ArgumentException( - "Ширина или высота прямоугольника должна быть положительной"); + MinRectangleWidth = SizeParser.ParseSizeDimension(minRectangleWidth).GetValueOrThrow(); + MaxRectangleWidth = SizeParser.ParseSizeDimension(maxRectangleWidth).GetValueOrThrow(); + MinRectangleHeight = SizeParser.ParseSizeDimension(minRectangleHeight).GetValueOrThrow(); + MaxRectangleHeight = SizeParser.ParseSizeDimension(maxRectangleHeight).GetValueOrThrow(); } + } - if (minRectangleWidth > maxRectangleWidth - || minRectangleHeight > maxRectangleHeight) + private Result AreMinAndMaxSizesAppropriate(int min, int max) + { + if (min > max) { - throw new ArgumentException( - "Минимальное значение ширины или высоты не может быть больше максимального"); + return Result.Fail("Минимальное значение не может быть больше максимального"); } - - MinRectangleWidth = minRectangleWidth; - MaxRectangleWidth = maxRectangleWidth; - MinRectangleHeight = minRectangleHeight; - MaxRectangleHeight = maxRectangleHeight; + return true.AsResult(); } public IEnumerable<(string word, Size size)> GetNextRectangleProperties() diff --git a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs index 7809e0453..75f0685ee 100644 --- a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs +++ b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; namespace TagCloud.CloudLayouters.CircularCloudLayouter { @@ -18,12 +19,11 @@ internal class CircularCloudLayouter : ICloudLayouter private readonly Random random = new Random(); private readonly List rectangles = new List(); - public Rectangle PutNextRectangle(Size rectangleSize) + public Result PutNextRectangle(Size rectangleSize) { if (rectangleSize.Width <= 0 || rectangleSize.Height <= 0) { - throw new ArgumentException( - "Размеры прямоугольника не могут быть меньше либо равны нуля."); + return Result.Fail("Размеры прямоугольника не могут быть меньше либо равны нуля"); } var result = new Rectangle(); @@ -49,7 +49,7 @@ public Rectangle PutNextRectangle(Size rectangleSize) arrangementСircle.Radius += 1.0f; } - return result; + return result.AsResult(); } private bool IsIntersectionWithAlreadyPlaced(Rectangle rectangle) diff --git a/TagCloud/CloudLayouters/ICloudLayouter.cs b/TagCloud/CloudLayouters/ICloudLayouter.cs index 49abc7956..ad6a7eb25 100644 --- a/TagCloud/CloudLayouters/ICloudLayouter.cs +++ b/TagCloud/CloudLayouters/ICloudLayouter.cs @@ -1,10 +1,11 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; namespace TagCloud.CloudLayouters { // Интерфейс расстановки прямоугольников internal interface ICloudLayouter { - public Rectangle PutNextRectangle(Size rectangleSize); + public Result PutNextRectangle(Size rectangleSize); } } diff --git a/TagCloud/ImageSavers/IImageSaver.cs b/TagCloud/ImageSavers/IImageSaver.cs index 794e0bc04..aee878dc6 100644 --- a/TagCloud/ImageSavers/IImageSaver.cs +++ b/TagCloud/ImageSavers/IImageSaver.cs @@ -1,10 +1,11 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; namespace TagCloud.ImageSavers { // Интерфейс сохранения изображения в файл internal interface IImageSaver { - public void SaveFile(Bitmap image, string fileName, string format = "png"); + public Result SaveFile(Bitmap image, string fileName, string format = "png"); } } diff --git a/TagCloud/ImageSavers/ImageSaver.cs b/TagCloud/ImageSavers/ImageSaver.cs index f5d2a0820..6f0d19f2f 100644 --- a/TagCloud/ImageSavers/ImageSaver.cs +++ b/TagCloud/ImageSavers/ImageSaver.cs @@ -1,4 +1,6 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; +using System.IO; namespace TagCloud.ImageSavers { @@ -7,19 +9,20 @@ namespace TagCloud.ImageSavers // Поддерживать разные форматы изображений. internal class ImageSaver : IImageSaver { - public void SaveFile(Bitmap image, string fileName, string format = "png") + public Result SaveFile(Bitmap image, string fileName, string format = "png") { if (image is null) { - throw new ArgumentNullException("Передаваемое изображение не должно быть null"); + return Result.Fail("Передаваемое изображение не должно быть null"); } if (string.IsNullOrWhiteSpace(fileName)) { - throw new ArgumentException("Некорректное имя файла для создания"); + return Result.Fail("Некорректное имя файла для создания"); } image.Save($"{fileName}.{format}"); + return Result.Ok(); } } } diff --git a/TagCloud/Normalizers/INormalizer.cs b/TagCloud/Normalizers/INormalizer.cs index 050a7450c..cf67bd480 100644 --- a/TagCloud/Normalizers/INormalizer.cs +++ b/TagCloud/Normalizers/INormalizer.cs @@ -1,9 +1,11 @@ -namespace TagCloud.Normalizers +using FileSenderRailway; + +namespace TagCloud.Normalizers { // Интерфейс нормализации количества каждого слова internal interface INormalizer { - public Dictionary Normalize( + public Result> Normalize( Dictionary values, double minCoefficient = 0.25, int decimalPlaces = 4); diff --git a/TagCloud/Normalizers/Normalizer.cs b/TagCloud/Normalizers/Normalizer.cs index 6f7cd329f..5c58a3b18 100644 --- a/TagCloud/Normalizers/Normalizer.cs +++ b/TagCloud/Normalizers/Normalizer.cs @@ -1,4 +1,6 @@ -namespace TagCloud.Normalizers +using FileSenderRailway; + +namespace TagCloud.Normalizers { // Слово, которое встречается чаще всего, будет иметь вес 1.0. // Это означает, что оно в дальнейшем будет иметь прямоугольник @@ -7,14 +9,14 @@ // minCoefficient * максимальный размеро прямоугольника. internal class Normalizer : INormalizer { - public Dictionary Normalize( + public Result> Normalize( Dictionary values, double minCoefficient = 0.25, int decimalPlaces = 4) { if (minCoefficient < 0) { - throw new ArgumentException("Минимальный коэффициент не может быть меньше 0"); + return Result.Fail>("Минимальный коэффициент нормализации не может быть меньше 0"); } var result = new Dictionary(); @@ -35,7 +37,7 @@ public Dictionary Normalize( decimalPlaces); } - return result; + return result.AsResult(); } private double CalculateNormalizedValue( diff --git a/TagCloud/Parsers/BoolParser.cs b/TagCloud/Parsers/BoolParser.cs index 82b78e875..4718c0fe7 100644 --- a/TagCloud/Parsers/BoolParser.cs +++ b/TagCloud/Parsers/BoolParser.cs @@ -1,14 +1,16 @@ -namespace TagCloud.Parsers +using FileSenderRailway; + +namespace TagCloud.Parsers { internal static class BoolParser { - public static bool ParseIsSorted(string value) + public static Result ParseIsSorted(string value) { if (value == false.ToString() || value == true.ToString()) { - return value == true.ToString(); + return (value == true.ToString()).AsResult(); } - throw new ArgumentException($"Неизвестный параметр сортировки {value}"); + return Result.Fail($"Неизвестный параметр сортировки {value}"); } } } diff --git a/TagCloud/Parsers/ColorParser.cs b/TagCloud/Parsers/ColorParser.cs index 0d7ed14e2..3e811ef66 100644 --- a/TagCloud/Parsers/ColorParser.cs +++ b/TagCloud/Parsers/ColorParser.cs @@ -1,17 +1,18 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; namespace TagCloud.Parsers { internal static class ColorParser { - public static Color ParseColor(string color) + public static Result ParseColor(string color) { var result = Color.FromName(color); if (!result.IsKnownColor) { - throw new ArgumentException($"Неизвестный цвет {color}"); + return Result.Fail($"Неизвестный цвет {color}"); } - return result; + return result.AsResult(); } } } diff --git a/TagCloud/Parsers/FontParser.cs b/TagCloud/Parsers/FontParser.cs index 46c97adaf..bf6d3eeb4 100644 --- a/TagCloud/Parsers/FontParser.cs +++ b/TagCloud/Parsers/FontParser.cs @@ -1,17 +1,18 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; namespace TagCloud.Parsers { internal static class FontParser { - public static string ParseFont(string font) + public static Result ParseFont(string font) { if (!FontFamily.Families.Any( x => x.Name.Equals(font, StringComparison.OrdinalIgnoreCase))) { - throw new ArgumentException($"Неизвестный шрифт {font}"); + return Result.Fail($"Неизвестный шрифт {font}"); } - return font; + return font.AsResult(); } } diff --git a/TagCloud/Parsers/SizeParser.cs b/TagCloud/Parsers/SizeParser.cs index 1ad249b35..fd3101072 100644 --- a/TagCloud/Parsers/SizeParser.cs +++ b/TagCloud/Parsers/SizeParser.cs @@ -1,21 +1,40 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; namespace TagCloud.Parsers { internal static class SizeParser { - public static Size ParseImageSize(string size) + public static Result ParseImageSize(string size) { var dimensions = size.Split(':'); - if (dimensions.Length != 2 - || !int.TryParse(dimensions[0], out var width) - || !int.TryParse(dimensions[1], out var height)) + if (dimensions.Length != 2) { - throw new ArgumentException( - $"Некорректный формат размера изображения: {size}." + - $" Используйте формат Ширина:Высота, например 5000:5000."); + return Result.Fail($"Некорректный формат размера изображения: {size}, используйте формат Ширина:Высота, например 5000:5000"); } - return new Size(width, height); + + var width = ParseSizeDimension(dimensions[0]).GetValueOrThrow(); + var height = ParseSizeDimension(dimensions[1]).GetValueOrThrow(); + + return new Size(width, height).AsResult(); + } + + public static Result ParseSizeDimension(string dimension) + { + if (!int.TryParse(dimension, out var result)) + { + return Result.Fail($"Передано не число: {dimension}"); + } + return ParseSizeDimension(result); + } + + public static Result ParseSizeDimension(int dimension) + { + if (dimension <= 0) + { + return Result.Fail($"Переданное числовое значение должно быть больше 0: {dimension}"); + } + return dimension.AsResult(); } } } diff --git a/TagCloud/WordReaders/IWordReader.cs b/TagCloud/WordReaders/IWordReader.cs index 5384df2ab..c83304e41 100644 --- a/TagCloud/WordReaders/IWordReader.cs +++ b/TagCloud/WordReaders/IWordReader.cs @@ -1,8 +1,10 @@ -namespace TagCloud.WordReaders +using FileSenderRailway; + +namespace TagCloud.WordReaders { // Интерфейс для построчного чтения содержимого файла internal interface IWordReader { - public IEnumerable ReadByLines(string path); + public IEnumerable> ReadByLines(string path); } } diff --git a/TagCloud/WordReaders/WordReader.cs b/TagCloud/WordReaders/WordReader.cs index 4443209a2..019dca669 100644 --- a/TagCloud/WordReaders/WordReader.cs +++ b/TagCloud/WordReaders/WordReader.cs @@ -1,21 +1,25 @@ -namespace TagCloud.WordReaders +using FileSenderRailway; + +namespace TagCloud.WordReaders { internal class WordReader : IWordReader { - public IEnumerable ReadByLines(string path) + public IEnumerable> ReadByLines(string path) { if (!File.Exists(path)) { - throw new FileNotFoundException($"Файл {path} не существует"); + yield return Result.Fail($"Файл {path} не существует"); + yield break; } foreach (var line in File.ReadAllLines(path)) { if (line.Contains(' ')) { - throw new Exception($"Файл {path} содержит строку с двумя и более словами"); + yield return Result.Fail($"Файл {path} содержит строку с двумя и более словами"); + yield break; } - yield return line; + yield return line.AsResult(); } } } From ee91491032dda0c08b8fae8603c7c8f8eda1798b Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 18:47:47 +0500 Subject: [PATCH 03/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BA=D0=BE=D0=B4=D0=B0=20Fon?= =?UTF-8?q?tParser.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FontParser теперь возвращает FontFamily --- TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs | 4 ++-- TagCloud/Parsers/FontParser.cs | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs index 416bc876c..a962de58e 100644 --- a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -7,11 +7,11 @@ internal class CloudLayouterPainter( Size imageSize, Color? backgroundColor = null, Color? textColor = null, - string? fontName = null) : ICloudLayouterPainter + FontFamily? fontName = null) : ICloudLayouterPainter { private readonly Color backgroundColor = backgroundColor ?? Color.White; private readonly Color textColor = textColor ?? Color.Black; - private readonly string fontName = fontName ?? "Arial"; + private readonly FontFamily fontName = fontName ?? new FontFamily("Arial"); public Result Draw(IList tags) { diff --git a/TagCloud/Parsers/FontParser.cs b/TagCloud/Parsers/FontParser.cs index bf6d3eeb4..7038483da 100644 --- a/TagCloud/Parsers/FontParser.cs +++ b/TagCloud/Parsers/FontParser.cs @@ -5,15 +5,14 @@ namespace TagCloud.Parsers { internal static class FontParser { - public static Result ParseFont(string font) + public static Result ParseFont(string font) { if (!FontFamily.Families.Any( x => x.Name.Equals(font, StringComparison.OrdinalIgnoreCase))) { - return Result.Fail($"Неизвестный шрифт {font}"); + return Result.Fail($"Неизвестный шрифт {font}"); } - return font.AsResult(); + return new FontFamily(font).AsResult(); } - } } From e920ea9b64a2cd83a31da1273707682e2f269703 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 18:49:17 +0500 Subject: [PATCH 04/32] =?UTF-8?q?=D0=91=D0=B8=D0=B7=D0=BD=D0=B5=D1=81=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=B8=D0=B7=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80=D0=B0=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B2=20=D0=BE?= =?UTF-8?q?=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D1=81=D1=81=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Добавлена фабрика по созданию WordFilter. 2. Добавлена фабрика по созданиюCloudLayouterWorker. 3. Рефакторинг DIContainer. --- TagCloud/DIContainer.cs | 118 ++++++++++-------- .../Factories/CloudLayouterWorkerFactory.cs | 39 ++++++ TagCloud/Factories/WordFilterFactory.cs | 53 ++++++++ 3 files changed, 161 insertions(+), 49 deletions(-) create mode 100644 TagCloud/Factories/CloudLayouterWorkerFactory.cs create mode 100644 TagCloud/Factories/WordFilterFactory.cs diff --git a/TagCloud/DIContainer.cs b/TagCloud/DIContainer.cs index 13dbbb6e0..224325b90 100644 --- a/TagCloud/DIContainer.cs +++ b/TagCloud/DIContainer.cs @@ -9,54 +9,66 @@ using TagCloud.WordFilters; using TagCloud.WordReaders; using TagCloud.Parsers; +using System.Drawing; +using TagCloud.Factories; namespace TagCloud { public static class DIContainer { - // 1. Разбить содержимое этого метода на отдельны части // 2. Добавить проверку корректностей значений: - // - options.ImageSize; // - options.MaxRectangleWidth; // - options.MaxRectangleHeight; public static IContainer ConfigureContainer(CommandLineOptions options) { var builder = new ContainerBuilder(); - builder - .RegisterType() - .As() - .SingleInstance(); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); + RegisterIWordFillterSevice(builder, options); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); - builder - .RegisterType() - .As() - .SingleInstance(); + var imageSize = SizeParser.ParseImageSize(options.ImageSize).GetValueOrThrow(); + RegisterICloudLayouterPainterSevice(builder, options, imageSize); - builder - .RegisterType() - .As() - .SingleInstance(); + RegisterICloudLayouterWorkerSevice(builder, options); - builder - .RegisterType() - .As() - .SingleInstance(); + RegisterProgramExecutorService(builder, options, imageSize); + + return builder.Build(); + } + private static void RegisterSimpleSevice(ContainerBuilder builder) + where TImplementation : TService + where TService : notnull + { builder - .RegisterType() - .As() + .RegisterType() + .As() .SingleInstance(); + } + private static void RegisterSimpleSevice(ContainerBuilder builder) + where TImplementation : notnull + { builder - .RegisterType() - .As() + .RegisterType() + .AsSelf() .SingleInstance(); + } - var imageSize = SizeParser.ParseImageSize(options.ImageSize); - var backgroundColor = ColorParser.ParseColor(options.BackgroundColor); - var textColor = ColorParser.ParseColor(options.TextColor); - var font = FontParser.ParseFont(options.Font); + private static void RegisterICloudLayouterPainterSevice( + ContainerBuilder builder, + CommandLineOptions options, + Size imageSize) + { + var backgroundColor = ColorParser.ParseColor(options.BackgroundColor).GetValueOrThrow(); + var textColor = ColorParser.ParseColor(options.TextColor).GetValueOrThrow(); + var font = FontParser.ParseFont(options.Font).GetValueOrThrow(); builder.RegisterType() .As() .WithParameter("imageSize", imageSize) @@ -64,42 +76,50 @@ public static IContainer ConfigureContainer(CommandLineOptions options) .WithParameter("textColor", textColor) .WithParameter("fontName", font) .SingleInstance(); + } - var isSorted = BoolParser.ParseIsSorted(options.IsSorted); - builder.Register((c, p) => + private static void RegisterICloudLayouterWorkerSevice( + ContainerBuilder builder, + CommandLineOptions options) + { + builder.Register(c => { - var wordReader = c.Resolve(); - var wordCounter = c.Resolve(); - var normalizer = c.Resolve(); - var wordFilter = c.Resolve(); - - foreach (var word in wordReader.ReadByLines(options.DataFileName)) - { - var wordInLowerCase = word.ToLower(); - if (!wordFilter.IsCorrectWord(wordInLowerCase)) - { - continue; - } - wordCounter.AddWord(wordInLowerCase); - } - - var normalizedValues = normalizer.Normalize(wordCounter.Values); - return new NormalizedFrequencyBasedCloudLayouterWorker( + var factory = c.Resolve(); + return factory.Create( + options.DataFileName, options.MaxRectangleWidth, options.MaxRectangleHeight, - normalizedValues, - isSorted); + BoolParser.ParseIsSorted(options.IsSorted).GetValueOrThrow()); }).As().SingleInstance(); + } + private static void RegisterIWordFillterSevice( + ContainerBuilder builder, + CommandLineOptions options) + { + builder.Register(c => + { + var factory = c.Resolve(); + return factory.Create( + options.WordsToIncludeFileName, + options.WordsToExcludeFileName, + c.Resolve()); + }).As().SingleInstance(); + } + + private static void RegisterProgramExecutorService( + ContainerBuilder builder, + CommandLineOptions options, + Size imageSize) + { builder.RegisterType() .WithParameter("size", imageSize) + .WithParameter("resultFormat", options.ResultFormat) .WithParameter("maxRectangleWidth", options.MaxRectangleWidth) .WithParameter("maxRectangleHeight", options.MaxRectangleHeight) .WithParameter("imageFileName", options.ImageFileName) .WithParameter("dataFileName", options.DataFileName) .SingleInstance(); - - return builder.Build(); } } } diff --git a/TagCloud/Factories/CloudLayouterWorkerFactory.cs b/TagCloud/Factories/CloudLayouterWorkerFactory.cs new file mode 100644 index 000000000..52f5fd3fa --- /dev/null +++ b/TagCloud/Factories/CloudLayouterWorkerFactory.cs @@ -0,0 +1,39 @@ +using TagCloud.CloudLayouterWorkers; +using TagCloud.Normalizers; +using TagCloud.WordCounters; +using TagCloud.WordFilters; +using TagCloud.WordReaders; + +namespace TagCloud.Factories +{ + internal class CloudLayouterWorkerFactory( + IWordReader wordReader, + IWordCounter wordCounter, + INormalizer normalizer, + IWordFilter wordFilter) + { + public ICloudLayouterWorker Create( + string dataFileName, + int maxRectangleWidth, + int maxRectangleHeight, + bool isSorted) + { + foreach (var word in wordReader.ReadByLines(dataFileName)) + { + var wordInLowerCase = word.GetValueOrThrow().ToLower(); + if (!wordFilter.IsCorrectWord(wordInLowerCase)) + { + continue; + } + wordCounter.AddWord(wordInLowerCase); + } + + var normalizedValues = normalizer.Normalize(wordCounter.Values); + return new NormalizedFrequencyBasedCloudLayouterWorker( + maxRectangleWidth, + maxRectangleHeight, + normalizedValues.GetValueOrThrow(), + isSorted); + } + } +} diff --git a/TagCloud/Factories/WordFilterFactory.cs b/TagCloud/Factories/WordFilterFactory.cs new file mode 100644 index 000000000..3ed7a52e5 --- /dev/null +++ b/TagCloud/Factories/WordFilterFactory.cs @@ -0,0 +1,53 @@ +using TagCloud.WordFilters; +using TagCloud.WordReaders; + +namespace TagCloud.Factories +{ + internal class WordFilterFactory + { + public IWordFilter Create( + string? wordsToIncludeFileName, + string? wordsToExcludeFileName, + IWordReader wordReader) + { + var result = new WordFilter(); + + if (IsFileNameCorrect(wordsToIncludeFileName)) + { + AddWords(wordReader, wordsToIncludeFileName!, result); + } + + if (IsFileNameCorrect(wordsToExcludeFileName)) + { + RemoveWords(wordReader, wordsToExcludeFileName!, result); + } + + return result; + } + + private bool IsFileNameCorrect(string? fileName) + => !string.IsNullOrEmpty(fileName) && !string.IsNullOrWhiteSpace(fileName); + + private void AddWords( + IWordReader wordReader, + string wordsToIncludeFileName, + WordFilter wordFilter) + { + foreach (var word in wordReader.ReadByLines(wordsToIncludeFileName)) + { + wordFilter.Add(word.GetValueOrThrow().ToLower()); + } + } + + private void RemoveWords( + IWordReader wordReader, + string wordsToExcludeFileName, + WordFilter wordFilter) + { + foreach (var word in wordReader.ReadByLines(wordsToExcludeFileName)) + { + wordFilter.Remove(word.GetValueOrThrow().ToLower()); + } + } + } +} From 1ad02b7bf1ccefd48c54d85cfb30db0ec01cdbfd Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 18:51:22 +0500 Subject: [PATCH 05/32] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5=20=D0=B0?= =?UTF-8?q?=D1=80=D0=B3=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D0=BD=D0=BE=D0=B9?= =?UTF-8?q?=20=D1=81=D1=82=D1=80=D0=BE=D0=BA=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. wordsToIncludeFile - добавление слов в фильтр "скучных слов" из файла. 2. wordsToExcludeFile- исключение слов из фильтра "скучных слов" из файла. 3. normalizeMinCoefficient - минимальный коэффициент нормализации. 3. Рефакторинг WordFilter. --- TagCloud/CommandLineOptions.cs | 19 ++++++++++++++++++- TagCloud/WordFilters/WordFilter.cs | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/TagCloud/CommandLineOptions.cs b/TagCloud/CommandLineOptions.cs index 447062718..93497c6d7 100644 --- a/TagCloud/CommandLineOptions.cs +++ b/TagCloud/CommandLineOptions.cs @@ -67,6 +67,23 @@ public class CommandLineOptions Required = false, HelpText = "Формат создаваемого изображение, например png.")] public string ResultFormat { get; set; } = "png"; - } + [Option( + "normalizeMinCoefficient", + Required = false, + HelpText = "Минимальный коэффициент нормализации, например 0.25")] + public double NormalizeMinCoefficient { get; set; } = 0.25; + + [Option( + "wordsToIncludeFile", + Required = false, + HelpText = "Имя файла со словами для добавления в фильтр \"скучных слов\".")] + public string? WordsToIncludeFileName { get; set; } = null; + + [Option( + "wordsToExcludeFile", + Required = false, + HelpText = "Имя файла со словами для исключения из фильтра \"скучных слов\".")] + public string? WordsToExcludeFileName { get; set; } = null; + } } \ No newline at end of file diff --git a/TagCloud/WordFilters/WordFilter.cs b/TagCloud/WordFilters/WordFilter.cs index add9c3eb1..81900028a 100644 --- a/TagCloud/WordFilters/WordFilter.cs +++ b/TagCloud/WordFilters/WordFilter.cs @@ -65,7 +65,7 @@ public WordFilter(IList? toAdd = null, IList? toExclude = null) public void Clear() => bannedWords.Clear(); - public HashSet BannedWords => bannedWords; + public HashSet BannedWords => bannedWords.ToHashSet(); public bool IsCorrectWord(string word) { From aaa88e21b94cb0993f3d101229a0a69b642be1d7 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 18:53:29 +0500 Subject: [PATCH 06/32] =?UTF-8?q?=D0=9E=D0=B1=D1=89=D0=B8=D0=B9=20=D1=80?= =?UTF-8?q?=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=B4=D0=B0=20=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B7?= =?UTF-8?q?=D1=83=D0=BB=D1=8C=D1=82=D0=B0=D1=82=D0=B0=D0=BC=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=20=D0=B2=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B9=20=D1=87=D0=B0=D1=81=D1=82=D0=B8=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20TagCloud?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud.Tests/GlobalSuppressions.cs | 3 +++ TagCloud.Tests/MainTest.cs | 2 +- TagCloud/GlobalSuppressions.cs | 7 +++++++ TagCloud/Parsers/BoolParser.cs | 4 ++-- TagCloud/Program.cs | 9 +++------ TagCloud/ProgramExecutor.cs | 28 ++++++++++++++-------------- TagCloud/TagCloud.csproj | 4 ++++ TagCloud/WordCounters/WordCounter.cs | 8 ++------ 8 files changed, 36 insertions(+), 29 deletions(-) diff --git a/TagCloud.Tests/GlobalSuppressions.cs b/TagCloud.Tests/GlobalSuppressions.cs index cdd580ffc..b14f1e7d3 100644 --- a/TagCloud.Tests/GlobalSuppressions.cs +++ b/TagCloud.Tests/GlobalSuppressions.cs @@ -8,3 +8,6 @@ // Так делать явно плохо [assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.ImageSaversTests.ImageSaverTest.SaveFile_SavesFile(System.String,System.String)~System.Boolean")] [assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.ImageSaversTests.ImageSaverTest.SaveFile_ThrowsArgumentException_WithInvalidFilename(System.String)")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests.CircularCloudLayouterMainRequirementsTest.Cleanup")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.CloudLayouterPaintersTest.CloudLayouterPainterTest.Draw_ThrowsArgumentException_WithEmptyTags")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.CloudLayouterPaintersTest.CloudLayouterPainterTest.Draw_ThrowsArgumentNullException_WithTagsAsNull")] diff --git a/TagCloud.Tests/MainTest.cs b/TagCloud.Tests/MainTest.cs index 76bf6026a..8673add37 100644 --- a/TagCloud.Tests/MainTest.cs +++ b/TagCloud.Tests/MainTest.cs @@ -47,7 +47,7 @@ public void Program_ExecutesSuccessfully_WithValidArguments(string format) using var scope = container.BeginLifetimeScope(); var executor = scope.Resolve(); - Assert.DoesNotThrow(() => executor.Execute(format)); + Assert.DoesNotThrow(() => executor.Execute()); File.Exists($"{imageFile}.{format}").Should().BeTrue(); } diff --git a/TagCloud/GlobalSuppressions.cs b/TagCloud/GlobalSuppressions.cs index 75089be86..e3b20b229 100644 --- a/TagCloud/GlobalSuppressions.cs +++ b/TagCloud/GlobalSuppressions.cs @@ -13,3 +13,10 @@ [assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:Program.ParseFont(System.String)~System.String")] [assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.ImageSavers.ImageSaver.SaveFile(System.Drawing.Bitmap,System.String,System.String)")] [assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~System.String")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.Draw(System.Collections.Generic.IList{TagCloud.Tag})~FileSenderRailway.Result{System.Drawing.Bitmap}")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.ProgramExecutor.Execute(System.String)")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~FileSenderRailway.Result{System.String}")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.ProgramExecutor.Execute")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~FileSenderRailway.Result{System.Drawing.FontFamily}")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~F:TagCloud.CloudLayouterPainters.CloudLayouterPainter.fontName")] +[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.DIContainer.RegisterICloudLayouterPainterSevice(Autofac.ContainerBuilder,TagCloud.CommandLineOptions,System.Drawing.Size)")] diff --git a/TagCloud/Parsers/BoolParser.cs b/TagCloud/Parsers/BoolParser.cs index 4718c0fe7..e209d3100 100644 --- a/TagCloud/Parsers/BoolParser.cs +++ b/TagCloud/Parsers/BoolParser.cs @@ -6,9 +6,9 @@ internal static class BoolParser { public static Result ParseIsSorted(string value) { - if (value == false.ToString() || value == true.ToString()) + if (value == bool.FalseString || value == bool.TrueString) { - return (value == true.ToString()).AsResult(); + return Convert.ToBoolean(value).AsResult(); } return Result.Fail($"Неизвестный параметр сортировки {value}"); } diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs index 47680d128..d67ab880a 100644 --- a/TagCloud/Program.cs +++ b/TagCloud/Program.cs @@ -7,16 +7,13 @@ namespace TagCloud { internal class Program { - private static IContainer container; - static void Main(string[] args) { var parserResult = Parser.Default.ParseArguments(args); parserResult.WithParsed(options => { - container = DIContainer.ConfigureContainer(options); - Run(options.ResultFormat); + Run(DIContainer.ConfigureContainer(options)); }); parserResult.WithNotParsed(errors => @@ -27,11 +24,11 @@ static void Main(string[] args) }); } - private static void Run(string resultFormat) + private static void Run(IContainer container) { using var scope = container.BeginLifetimeScope(); var program = scope.Resolve(); - program.Execute(resultFormat); + program.Execute(); } } } diff --git a/TagCloud/ProgramExecutor.cs b/TagCloud/ProgramExecutor.cs index c50e0d2eb..a7068564c 100644 --- a/TagCloud/ProgramExecutor.cs +++ b/TagCloud/ProgramExecutor.cs @@ -1,35 +1,35 @@ -using System.Drawing; -using TagCloud.CloudLayouters; +using TagCloud.CloudLayouters; using TagCloud.CloudLayouterPainters; using TagCloud.CloudLayouterWorkers; using TagCloud.ImageSavers; -using TagCloud.Normalizers; -using TagCloud.WordCounters; -using TagCloud.WordFilters; -using TagCloud.WordReaders; namespace TagCloud { internal class ProgramExecutor( string imageFileName, - IWordCounter wordCounter, - INormalizer normalizer, + string resultFormat, ICloudLayouter layouter, ICloudLayouterPainter painter, ICloudLayouterWorker worker, IImageSaver imageSaver) { - public void Execute(string resultFormat) + public void Execute() { - var normalizedWordWeights = normalizer.Normalize(wordCounter.Values); - var tags = new List(); foreach (var rectangleProperty in worker.GetNextRectangleProperties()) { - tags.Add(new Tag(rectangleProperty.word, layouter.PutNextRectangle(rectangleProperty.size))); + var tagSize = layouter + .PutNextRectangle(rectangleProperty.size) + .GetValueOrThrow(); + var newTag = new Tag(rectangleProperty.word, tagSize); + tags.Add(newTag); } - - imageSaver.SaveFile(painter.Draw(tags), imageFileName, resultFormat); + imageSaver + .SaveFile( + painter.Draw(tags).GetValueOrThrow(), + imageFileName, + resultFormat) + .GetValueOrThrow(); } } } diff --git a/TagCloud/TagCloud.csproj b/TagCloud/TagCloud.csproj index 113b51b7e..f89c5ddcb 100644 --- a/TagCloud/TagCloud.csproj +++ b/TagCloud/TagCloud.csproj @@ -14,4 +14,8 @@ + + + + diff --git a/TagCloud/WordCounters/WordCounter.cs b/TagCloud/WordCounters/WordCounter.cs index c5953a1f1..283d919bb 100644 --- a/TagCloud/WordCounters/WordCounter.cs +++ b/TagCloud/WordCounters/WordCounter.cs @@ -7,12 +7,8 @@ internal class WordCounter : IWordCounter public void AddWord(string word) { - if (!counts.ContainsKey(word)) - { - counts[word] = 1; - return; - } - counts[word] += 1; + counts.TryGetValue(word, out uint value); + counts[word] = value + 1; } } } From 0d4319bc1a9b085de215f97332b8f6f84a61a441 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 20:42:51 +0500 Subject: [PATCH 07/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20CloudLayouterPainter.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Рефакторинг CloudLayouterPainter. 2. Добавлен новый тест. --- .../CloudLayouterPainterTest.cs | 21 +++++- .../CloudLayouterPainter.cs | 68 +++++++++++++------ 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs index bf5b5e748..b9eb7dd2a 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs @@ -2,7 +2,6 @@ using FluentAssertions; using System.Drawing; using TagCloud.CloudLayouterPainters; -using TagCloud.WordReaders; namespace TagCloud.Tests.CloudLayouterPaintersTest { @@ -17,7 +16,7 @@ public void SetUp() } [Test] - public void Draw_ThrowsArgumentException_WithEmptyTags() + public void Draw_ThrowsException_WithEmptyTags() { var expected = Result.Fail("Список тегов пуст"); var actual = painter.Draw(new List()); @@ -25,12 +24,28 @@ public void Draw_ThrowsArgumentException_WithEmptyTags() } [Test] - public void Draw_ThrowsArgumentNullException_WithTagsAsNull() + public void Draw_ThrowsException_WithTagsAsNull() { var expected = Result.Fail("Tags передан как null"); var actual = painter.Draw(null!); actual.Should().BeEquivalentTo(expected); } + + [Test] + public void Draw_ThrowsException_WithTooSmallTiFitImage() + { + var expected = Result + .Fail("Все прямоугольники не помещаются на изображение. Измените его размеры"); + var actual = painter + .Draw( + new Tag[] + { + new Tag( + "Test", + new Rectangle(new Point(0, 0), new Size(100, 100))) + }); + actual.Should().BeEquivalentTo(expected); + } } } diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs index a962de58e..ae2e0cb47 100644 --- a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -14,34 +14,65 @@ internal class CloudLayouterPainter( private readonly FontFamily fontName = fontName ?? new FontFamily("Arial"); public Result Draw(IList tags) + { + return ValidateTags(tags) + .Then(validTags => CreateBitmap(validTags)) + .OnFail(error => Result.Fail(error)); + } + + private Result> ValidateTags(IList tags) { if (tags is null) { - return Result.Fail("Tags передан как null"); + return Result.Fail>("Tags передан как null"); } if (tags.Count == 0) { - return Result.Fail("Список тегов пуст"); + return Result.Fail>("Список тегов пуст"); } - var result = new Bitmap(imageSize.Width, imageSize.Height); + if (!DoRectanglesFit(tags)) + { + return Result + .Fail>("Все прямоугольники не помещаются на изображение. Измените его размеры"); + } - using var graphics = Graphics.FromImage(result); - graphics.Clear(backgroundColor); + return tags.AsResult(); + } - foreach (var tag in tags) + private Result CreateBitmap(IList tags) + { + return Result.Of(() => { - var positionOnCanvas = GetPositionOnCanvas(tag.Rectangle); - var rectOnCanvas = new Rectangle( - positionOnCanvas.X, - positionOnCanvas.Y, - tag.Rectangle.Width, - tag.Rectangle.Height); - DrawText(graphics, rectOnCanvas, tag.Text); - } + var bitmap = new Bitmap(imageSize.Width, imageSize.Height); - return result.AsResult(); + using var graphics = Graphics.FromImage(bitmap); + graphics.Clear(backgroundColor); + + foreach (var tag in tags) + { + var positionOnCanvas = GetPositionOnCanvas(tag.Rectangle); + var rectOnCanvas = new Rectangle( + positionOnCanvas.X, + positionOnCanvas.Y, + tag.Rectangle.Width, + tag.Rectangle.Height); + + DrawText(graphics, rectOnCanvas, tag.Text); + } + + return bitmap; + }); + } + + private bool DoRectanglesFit(IList tags) + { + var minimums = new Point(tags.Min(t => t.Rectangle.Left), tags.Min(t => t.Rectangle.Top)); + var maximums = new Point(tags.Max(t => t.Rectangle.Right), tags.Max(t => t.Rectangle.Bottom)); + var actualWidth = maximums.X - minimums.X; + var actualHeight = maximums.Y - minimums.Y; + return actualWidth < imageSize.Width && actualHeight < imageSize.Height; } private Point GetPositionOnCanvas(Rectangle rectangle) @@ -50,15 +81,14 @@ private Point GetPositionOnCanvas(Rectangle rectangle) private void DrawText(Graphics graphics, Rectangle rectangle, string text) { var fontSize = FindFittingFontSize(graphics, text, rectangle); - var fittingFont = new Font(fontName, fontSize, FontStyle.Regular, GraphicsUnit.Pixel); - + using var fittingFont = new Font(fontName, fontSize, FontStyle.Regular, GraphicsUnit.Pixel); using var stringFormat = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }; - using var brush = new SolidBrush(textColor); + graphics.DrawString(text, fittingFont, brush, rectangle, stringFormat); } @@ -88,4 +118,4 @@ private int FindFittingFontSize(Graphics graphics, string text, Rectangle rectan return result; } } -} +} \ No newline at end of file From 5a8ab71a9a0926d7e9bb98100bfc5ea3bf8e37ca Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 21:01:18 +0500 Subject: [PATCH 08/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20CircularCloudLayouter.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CircularCloudLayouterTest.cs | 2 +- .../CircularCloudLayouter.cs | 67 ++++++++++--------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs index 2a6db6970..a8e35630e 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs @@ -12,7 +12,7 @@ internal class CircularCloudLayouterTest [TestCase(-1, 100)] [TestCase(100, 0)] [TestCase(100, -1)] - public void PutNextRectangle_ThrowsArgumentException_OnAnyNegativeOrZeroSize( + public void PutNextRectangle_ThrowsException_OnAnyNegativeOrZeroSize( int width, int height) { diff --git a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs index 75f0685ee..99a640d49 100644 --- a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs +++ b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs @@ -1,4 +1,5 @@ using FileSenderRailway; +using System; using System.Drawing; namespace TagCloud.CloudLayouters.CircularCloudLayouter @@ -7,11 +8,8 @@ namespace TagCloud.CloudLayouters.CircularCloudLayouter // который расставляет прямоугольники по окружности // с постепенно увеличивающимся радиусом. // Прямоугольники расставляются вокруг точки с координатой (0, 0), - // Затем, в CloudLayouterPainter координат пересыитываются таким образом, + // Затем, в CloudLayouterPainter координат пересчитываются таким образом, // что бы расположить первый прямоугольник в центре холста. - // Можно создать интерфейс IShape, который через GetCoordinates - // будет возвращать координаты линии формы. - // Тогда Circle можно заменить на IShape и ввести новые формы расстановки. internal class CircularCloudLayouter : ICloudLayouter { @@ -20,50 +18,53 @@ internal class CircularCloudLayouter : ICloudLayouter private readonly List rectangles = new List(); public Result PutNextRectangle(Size rectangleSize) + => ValidateRectangleSize(rectangleSize) + .Then(size => PlaceRectangle(size)) + .OnFail(error => Result.Fail(error)); + + private Result ValidateRectangleSize(Size rectangleSize) { if (rectangleSize.Width <= 0 || rectangleSize.Height <= 0) { - return Result.Fail("Размеры прямоугольника не могут быть меньше либо равны нуля"); + return Result.Fail("Размеры прямоугольника не могут быть меньше либо равны нуля"); } - var result = new Rectangle(); - arrangementСircle.Radius -= 1.0f; + return rectangleSize.AsResult(); + } - var isPlaced = false; - while (!isPlaced) + private Result PlaceRectangle(Size rectangleSize) + { + return Result.Of(() => { - var startAngle = random.Next(360); - foreach (var coordinate in arrangementСircle.GetCoordinatesOnCircle(startAngle)) + var result = new Rectangle(); + arrangementСircle.Radius -= 1.0f; + + var isPlaced = false; + while (!isPlaced) { - var location = GetRectangleLocation(coordinate, rectangleSize); - var nextRectangle = new Rectangle(location, rectangleSize); - if (!IsIntersectionWithAlreadyPlaced(nextRectangle)) + var startAngle = random.Next(360); + foreach (var coordinate in arrangementСircle.GetCoordinatesOnCircle(startAngle)) { - rectangles.Add(nextRectangle); - isPlaced = true; - result = nextRectangle; - break; + var location = GetRectangleLocation(coordinate, rectangleSize); + var nextRectangle = new Rectangle(location, rectangleSize); + if (!IsIntersectionWithAlreadyPlaced(nextRectangle)) + { + rectangles.Add(nextRectangle); + isPlaced = true; + result = nextRectangle; + break; + } } - } - arrangementСircle.Radius += 1.0f; - } + arrangementСircle.Radius += 1.0f; + } - return result.AsResult(); + return result; + }); } private bool IsIntersectionWithAlreadyPlaced(Rectangle rectangle) - { - foreach (var rect in rectangles) - { - if (rect.IntersectsWith(rectangle)) - { - return true; - } - } - - return false; - } + => rectangles.Any(rect => rect.IntersectsWith(rectangle)); private Point GetRectangleLocation(Point pointOnCircle, Size rectangleSize) { From a3f9304f67f88baf391e2a1d8d930fd3979ff038 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 22:03:00 +0500 Subject: [PATCH 09/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20NormalizedFrequencyBasedCloudL?= =?UTF-8?q?ayouterWorker.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...edFrequencyBasedCloudLayouterWorkerTest.cs | 21 +++++++++----- .../ICloudLayouterWorker.cs | 5 ++-- ...alizedFrequencyBasedCloudLayouterWorker.cs | 29 +++++++++++++------ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs index 5034a6575..d9e0abd7c 100644 --- a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs +++ b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs @@ -1,4 +1,5 @@ -using FluentAssertions; +using FileSenderRailway; +using FluentAssertions; using System.Drawing; using TagCloud.CloudLayouterWorkers; @@ -19,14 +20,20 @@ private readonly Dictionary normalizedValues [TestCase(-1, 100)] [TestCase(100, 0)] [TestCase(100, -1)] - public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize( + public void GetNextRectangleSize_ThrowsException_OnAnyNegativeOrZeroSize( int width, int height) { - var message = $"Переданное числовое значение должно быть больше 0: {(width <= 0 ? width : height)}"; - var exception = Assert.Throws( - () => new NormalizedFrequencyBasedCloudLayouterWorker(width, height, normalizedValues)); - exception.Message.Should().Contain(message); + var expected = Result + .Fail> + ($"Переданное числовое значение должно быть больше 0: {(width <= 0 ? width : height)}"); + + var worker = new NormalizedFrequencyBasedCloudLayouterWorker( + width, + height, + normalizedValues); + var actual = worker.GetNextRectangleProperties(); + actual.Should().BeEquivalentTo(expected); } [TestCase(100, 25, false)] @@ -50,7 +57,7 @@ public void GetNextRectangleSize_WorksCorrectly(int width, int height, bool isSo normalizedValues, isSortedOrder); foreach (var rectangleSize in worker - .GetNextRectangleProperties()) + .GetNextRectangleProperties().GetValueOrThrow()) { var currentValue = normalizedValues[keys[index]]; var expected = new Size((int)(currentValue * width), (int)(currentValue * height)); diff --git a/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs index befcb3a45..f24dd8b3f 100644 --- a/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs +++ b/TagCloud/CloudLayouterWorkers/ICloudLayouterWorker.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using FileSenderRailway; +using System.Drawing; namespace TagCloud.CloudLayouterWorkers { @@ -7,6 +8,6 @@ namespace TagCloud.CloudLayouterWorkers // для повышения возможности переиспользования internal interface ICloudLayouterWorker { - public IEnumerable<(string word, Size size)> GetNextRectangleProperties(); + public Result> GetNextRectangleProperties(); } } diff --git a/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs index a0f9906eb..e1b377f11 100644 --- a/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs +++ b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs @@ -6,11 +6,11 @@ namespace TagCloud.CloudLayouterWorkers { internal class NormalizedFrequencyBasedCloudLayouterWorker : ICloudLayouterWorker { - public readonly int MaxRectangleWidth; - public readonly int MaxRectangleHeight; + private readonly int maxRectangleWidth; + private readonly int maxRectangleHeight; private readonly Dictionary values; private readonly string[] keysOrder; - public string[] KeysOrder => keysOrder; + public string[] KeysOrder => keysOrder.ToArray(); public NormalizedFrequencyBasedCloudLayouterWorker( int maxRectangleWidth, @@ -18,8 +18,8 @@ public NormalizedFrequencyBasedCloudLayouterWorker( Dictionary normalizedValues, bool isSorted = true) { - MaxRectangleWidth = SizeParser.ParseSizeDimension(maxRectangleWidth).GetValueOrThrow(); - MaxRectangleHeight = SizeParser.ParseSizeDimension(maxRectangleHeight).GetValueOrThrow(); + this.maxRectangleWidth = maxRectangleWidth; + this.maxRectangleHeight = maxRectangleHeight; values = normalizedValues; if (isSorted) { @@ -31,15 +31,26 @@ public NormalizedFrequencyBasedCloudLayouterWorker( } } - public IEnumerable<(string word, Size size)> GetNextRectangleProperties() + public Result> GetNextRectangleProperties() + => ValidateDimensions() + .Then(_ => GenerateRectangles()) + .OnFail(error => Result.Fail>(error)); + + private Result ValidateDimensions() + => SizeParser.ParseSizeDimension(maxRectangleWidth) + .Then(_ => SizeParser.ParseSizeDimension(maxRectangleHeight)) + .Then(_ => Result.Ok()) + .OnFail(error => Result.Fail(error)); + + private IEnumerable<(string word, Size size)> GenerateRectangles() { foreach (var key in keysOrder) { var value = values[key]; - var width = (int)(MaxRectangleWidth * value); - var height = (int)(MaxRectangleHeight * value); + var width = (int)(maxRectangleWidth * value); + var height = (int)(maxRectangleHeight * value); yield return (key, new Size(width, height)); } } } -} +} \ No newline at end of file From 76f46ac0ca7b18064fd010191b9bbca8a4416431 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 14 Jan 2025 22:20:54 +0500 Subject: [PATCH 10/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20RandomCloudLayouterWorker.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...rcularCloudLayouterMainRequirementsTest.cs | 2 +- .../RandomCloudLayouterWorkerTest.cs | 28 ++++++---- .../RandomCloudLayouterWorker.cs | 55 +++++++++---------- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs index 632a18592..f6f860065 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs @@ -44,7 +44,7 @@ public void SetUp() minRectangleHeight, maxRectangleHeight); foreach (var rectangleProperty in randomWorker - .GetNextRectangleProperties().Take(rectanglesCount)) + .GetNextRectangleProperties().GetValueOrThrow().Take(rectanglesCount)) { tags.Add( new Tag( diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs index 92929f460..bb7b6f787 100644 --- a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs +++ b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs @@ -1,4 +1,6 @@ -using FluentAssertions; +using FileSenderRailway; +using FluentAssertions; +using System.Drawing; using TagCloud.CloudLayouterWorkers; namespace TagCloud.Tests.CloudLayouterWorkersTests @@ -10,14 +12,17 @@ internal class CircularCloudLayouterWorkerTests [TestCase(-1, 100)] [TestCase(100, 0)] [TestCase(100, -1)] - public void GetNextRectangleSize_ThrowsArgumentException_OnAnyNegativeOrZeroSize( + public void GetNextRectangleSize_ThrowsException_OnAnyNegativeOrZeroSize( int width, int height) { - var message = $"Переданное числовое значение должно быть больше 0: {(width <= 0 ? width : height)}"; - var exception = Assert.Throws( - () => new RandomCloudLayouterWorker(width, width, height, height)); - exception.Message.Should().Contain(message); + var expected = Result + .Fail> + ($"Переданное числовое значение должно быть больше 0: {(width <= 0 ? width : height)}"); + + var worker = new RandomCloudLayouterWorker(width, width, height, height); + var actual = worker.GetNextRectangleProperties(); + actual.Should().BeEquivalentTo(expected); } [TestCase(50, 25, 25, 50)] @@ -28,10 +33,13 @@ public void GetNextRectangleSize_ThrowsArgumentException_OnNonConsecutiveSizeVal int minHeight, int maxHeight) { - var message = "Минимальное значение не может быть больше максимального"; - var exception = Assert.Throws( - () => new RandomCloudLayouterWorker(minWidth, maxWidth, minHeight, maxHeight)); - exception.Message.Should().Contain(message); + var expected = Result + .Fail> + ("Минимальное значение не может быть больше максимального"); + + var worker = new RandomCloudLayouterWorker(minWidth, maxWidth, minHeight, maxHeight); + var actual = worker.GetNextRectangleProperties(); + actual.Should().BeEquivalentTo(expected); } } } diff --git a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs index ac8e5a954..4066dc8e7 100644 --- a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs +++ b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs @@ -4,32 +4,31 @@ namespace TagCloud.CloudLayouterWorkers { - // Класс, со старого задания TagCloud, - // выдающий случайный размер прямоугольника - // Оставил его для пары тестов. - internal class RandomCloudLayouterWorker : ICloudLayouterWorker + internal class RandomCloudLayouterWorker( + int minRectangleWidth, + int maxRectangleWidth, + int minRectangleHeight, + int maxRectangleHeight) : ICloudLayouterWorker { - private Random random = new Random(); - public readonly int MinRectangleWidth; - public readonly int MaxRectangleWidth; - public readonly int MinRectangleHeight; - public readonly int MaxRectangleHeight; + private readonly Random random = new Random(); + public Result> GetNextRectangleProperties() + => ValidateDimensions() + .Then(_ => GenerateRectangles()) + .OnFail(error => Result.Fail>(error)); - public RandomCloudLayouterWorker( - int minRectangleWidth, - int maxRectangleWidth, - int minRectangleHeight, - int maxRectangleHeight) - { - if (AreMinAndMaxSizesAppropriate(minRectangleWidth, maxRectangleWidth).GetValueOrThrow() - && AreMinAndMaxSizesAppropriate(minRectangleHeight, maxRectangleHeight).GetValueOrThrow()) - { - MinRectangleWidth = SizeParser.ParseSizeDimension(minRectangleWidth).GetValueOrThrow(); - MaxRectangleWidth = SizeParser.ParseSizeDimension(maxRectangleWidth).GetValueOrThrow(); - MinRectangleHeight = SizeParser.ParseSizeDimension(minRectangleHeight).GetValueOrThrow(); - MaxRectangleHeight = SizeParser.ParseSizeDimension(maxRectangleHeight).GetValueOrThrow(); - } - } + private Result ValidateDimensions() + => AreMinAndMaxSizesAppropriate(minRectangleWidth, maxRectangleWidth) + .Then(_ => AreMinAndMaxSizesAppropriate(minRectangleHeight, maxRectangleHeight)) + .Then(_ => ParseSizes()) + .OnFail(error => Result.Fail(error)); + + private Result ParseSizes() + => SizeParser.ParseSizeDimension(minRectangleWidth) + .Then(_ => SizeParser.ParseSizeDimension(maxRectangleWidth) + .Then(_ => SizeParser.ParseSizeDimension(minRectangleHeight) + .Then(_ => SizeParser.ParseSizeDimension(maxRectangleHeight) + .Then(_ => Result.Ok())))) + .OnFail(error => Result.Fail(error)); private Result AreMinAndMaxSizesAppropriate(int min, int max) { @@ -40,14 +39,14 @@ private Result AreMinAndMaxSizesAppropriate(int min, int max) return true.AsResult(); } - public IEnumerable<(string word, Size size)> GetNextRectangleProperties() + private IEnumerable<(string word, Size size)> GenerateRectangles() { while (true) { - var width = random.Next(MinRectangleWidth, MaxRectangleWidth); - var height = random.Next(MinRectangleHeight, MaxRectangleHeight); + var width = random.Next(minRectangleWidth, maxRectangleWidth); + var height = random.Next(minRectangleHeight, maxRectangleHeight); yield return (string.Empty, new Size(width, height)); } } } -} +} \ No newline at end of file From 14d48e846a2b3f3ae4679f0417c98efb93a5ad56 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Wed, 15 Jan 2025 16:17:29 +0500 Subject: [PATCH 11/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20ImageSaver.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Рефакторинг ImageSaver. 2. Обновление тестов. --- .../ImageSaversTests/ImageSaverTest.cs | 25 ++++++++++--- TagCloud/ImageSavers/ImageSaver.cs | 36 +++++++++++++++++-- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs index 415ee955c..6a5e4275a 100644 --- a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs @@ -2,14 +2,13 @@ using FluentAssertions; using System.Drawing; using TagCloud.ImageSavers; -using TagCloud.WordReaders; namespace TagCloud.Tests.ImageSaversTests { [TestFixture] internal class ImageSaverTest { - private string directoryPath = "TempFilesForImageSaverTests"; + private readonly string directoryPath = "TempFilesForImageSaverTests"; private ImageSaver imageSaver; [OneTimeSetUp] @@ -25,7 +24,7 @@ public void SetUp() } [TestCase("Test")] - public void SaveFile_ArgumentNullException_WithNullBitmap(string filename) + public void SaveFile_ThrowsException_WithNullBitmap(string filename) { var path = Path.Combine(directoryPath, filename); var expected = Result.Fail("Передаваемое изображение не должно быть null"); @@ -36,7 +35,7 @@ public void SaveFile_ArgumentNullException_WithNullBitmap(string filename) [TestCase(null)] [TestCase("")] [TestCase(" ")] - public void SaveFile_ThrowsArgumentException_WithInvalidFilename(string? filename) + public void SaveFile_ThrowsException_WithInvalidFilename(string? filename) { var dummyImage = new Bitmap(1, 1); var expected = Result.Fail("Некорректное имя файла для создания"); @@ -44,8 +43,25 @@ public void SaveFile_ThrowsArgumentException_WithInvalidFilename(string? filenam actual.Should().BeEquivalentTo(expected); } + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + [TestCase("abc")] + public void SaveFile_ThrowsException_WithInvalidFormat(string? format) + { + var dummyImage = new Bitmap(1, 1); + var filename = "Test"; + var expected = Result.Fail($"Формат \"{format}\" не поддерживается"); + var actual = imageSaver.SaveFile(dummyImage, filename, format!); + actual.Should().BeEquivalentTo(expected); + } + [TestCase("Test", "png", ExpectedResult = true)] + [TestCase("Test", "jpg", ExpectedResult = true)] + [TestCase("Test", "jpeg", ExpectedResult = true)] [TestCase("Test", "bmp", ExpectedResult = true)] + [TestCase("Test", "gif", ExpectedResult = true)] + [TestCase("Test", "tiff", ExpectedResult = true)] public bool SaveFile_SavesFile(string filename, string format) { var dummyImage = new Bitmap(1, 1); @@ -56,7 +72,6 @@ public bool SaveFile_SavesFile(string filename, string format) return File.Exists($"{path}.{format}"); } - [OneTimeTearDown] public void OneTimeCleanup() { diff --git a/TagCloud/ImageSavers/ImageSaver.cs b/TagCloud/ImageSavers/ImageSaver.cs index 6f0d19f2f..ec91e4fc4 100644 --- a/TagCloud/ImageSavers/ImageSaver.cs +++ b/TagCloud/ImageSavers/ImageSaver.cs @@ -1,6 +1,6 @@ using FileSenderRailway; using System.Drawing; -using System.IO; +using System.Drawing.Imaging; namespace TagCloud.ImageSavers { @@ -9,7 +9,23 @@ namespace TagCloud.ImageSavers // Поддерживать разные форматы изображений. internal class ImageSaver : IImageSaver { + private readonly Dictionary supportedFormats = + new Dictionary() + { + {"png" , ImageFormat.Png }, + {"jpg" , ImageFormat.Jpeg }, + {"jpeg" , ImageFormat.Jpeg }, + {"bmp" , ImageFormat.Bmp }, + {"gif" , ImageFormat.Gif }, + {"tiff" , ImageFormat.Tiff } + }; + public Result SaveFile(Bitmap image, string fileName, string format = "png") + => ValidateInput(image, fileName, format) + .Then(_ => SaveImage(image, fileName, format)) + .OnFail(error => Result.Fail(error)); + + private Result ValidateInput(Bitmap image, string fileName, string format) { if (image is null) { @@ -21,8 +37,22 @@ public Result SaveFile(Bitmap image, string fileName, string format = "png return Result.Fail("Некорректное имя файла для создания"); } - image.Save($"{fileName}.{format}"); + if (string.IsNullOrWhiteSpace(format) || !IsSupportedFormat(format)) + { + return Result.Fail($"Формат \"{format}\" не поддерживается"); + } + return Result.Ok(); } + + private Result SaveImage(Bitmap image, string fileName, string format) + { + var imageFormat = supportedFormats[format]; + image.Save($"{fileName}.{format}", imageFormat); + return Result.Ok(); + } + + private bool IsSupportedFormat(string format) + => supportedFormats.ContainsKey(format); } -} +} \ No newline at end of file From a518eecc09bcf142db5dc4408f2c85f31707cc03 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Wed, 15 Jan 2025 16:36:55 +0500 Subject: [PATCH 12/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20Normalizer.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Рефакторинг Normalizer. 2. Обновление тестов. --- .../NormalizersTest/NormalizerTest.cs | 30 ++++++++- TagCloud/Normalizers/Normalizer.cs | 67 +++++++++++++------ 2 files changed, 76 insertions(+), 21 deletions(-) diff --git a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs index 43fca9fd2..2db54f7b8 100644 --- a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs +++ b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs @@ -27,14 +27,40 @@ internal class NormalizerTest private readonly double defaultMinCoefficient = 0.25; [TestCase(-0.1)] - public void Normalize_ThrowsArgumentException_WithMinCoefficientLessThanZero( + [TestCase(1.1)] + public void Normalize_ThrowsException_WithInvalidMinCoefficient( double minCoefficient) { - var expected = Result.Fail>("Минимальный коэффициент нормализации не может быть меньше 0"); + var expected = Result.Fail>("Минимальный коэффициент нормализации должен быть в диапазоне от 0 до 1"); var actual = normalizer.Normalize(values, minCoefficient, defaultDecimalPlaces); actual.Should().BeEquivalentTo(expected); } + [Test] + public void Normalize_ThrowsException_WithEmptyValues() + { + var expected = Result.Fail>("Словарь значений не может быть пустым"); + var actual = normalizer.Normalize(new Dictionary(), defaultMinCoefficient, defaultDecimalPlaces); + actual.Should().BeEquivalentTo(expected); + } + + [Test] + public void Normalize_ThrowsException_WithValuesAsNull() + { + var expected = Result.Fail>("Словарь значений не может быть пустым"); + var actual = normalizer.Normalize(null!, defaultMinCoefficient, defaultDecimalPlaces); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase(-1)] + public void Normalize_ThrowsException_WithInvalidDecimalPlaces( + int decimalPlaces) + { + var expected = Result.Fail>("Количество знаков после запятой не может быть отрицательным"); + var actual = normalizer.Normalize(values, defaultMinCoefficient, decimalPlaces); + actual.Should().BeEquivalentTo(expected); + } + [TestCase(0.25, 4)] [TestCase(0.25, 2)] public void Normalize_CalculatesСorrectly(double minCoefficient, int decimalPlaces) diff --git a/TagCloud/Normalizers/Normalizer.cs b/TagCloud/Normalizers/Normalizer.cs index 5c58a3b18..90974d339 100644 --- a/TagCloud/Normalizers/Normalizer.cs +++ b/TagCloud/Normalizers/Normalizer.cs @@ -13,37 +13,65 @@ public Result> Normalize( Dictionary values, double minCoefficient = 0.25, int decimalPlaces = 4) + => ValidateInput(values, minCoefficient, decimalPlaces) + .Then(_ => CalculateNormalizedValues(values, minCoefficient, decimalPlaces)) + .OnFail(error => Result.Fail>(error)); + + private Result ValidateInput( + Dictionary values, + double minCoefficient, + int decimalPlaces) { - if (minCoefficient < 0) + if (values is null || values.Count == 0) { - return Result.Fail>("Минимальный коэффициент нормализации не может быть меньше 0"); + return Result.Fail("Словарь значений не может быть пустым"); } - var result = new Dictionary(); - - var maxValue = values.Values.Max(); - var minValue = values.Values.Min(); - - var scale = 1.0 - minCoefficient; + if (minCoefficient < 0.0 || minCoefficient > 1.0) + { + return Result.Fail("Минимальный коэффициент нормализации должен быть в диапазоне от 0 до 1"); + } - foreach (var pair in values) + if (decimalPlaces < 0) { - result[pair.Key] = CalculateNormalizedValue( - minCoefficient, - scale, - pair.Value, - minValue, - maxValue, - decimalPlaces); + return Result.Fail("Количество знаков после запятой не может быть отрицательным"); } - return result.AsResult(); + return Result.Ok(); } + private Result> CalculateNormalizedValues( + Dictionary values, + double minCoefficient, + int decimalPlaces) + => Result.Of(() => + { + var result = new Dictionary(); + + var maxValue = values.Values.Max(); + var minValue = values.Values.Min(); + + var scale = 1.0 - minCoefficient; + + foreach (var pair in values) + { + result[pair.Key] = CalculateNormalizedValue( + minCoefficient, + scale, + pair.Value, + minValue, + maxValue, + decimalPlaces); + } + + return result; + }); + + private double CalculateNormalizedValue( double minCoefficient, double scale, - double value, + uint value, uint minValue, uint maxValue, int decimalPlaces) @@ -52,9 +80,10 @@ private double CalculateNormalizedValue( { return 1.0; } + return Math.Round( minCoefficient + scale * ((double)(value - minValue) / (maxValue - minValue)), decimalPlaces); } } -} +} \ No newline at end of file From 5381142b192a2ab156b78fa3c7b3aa561f438946 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Wed, 15 Jan 2025 16:50:16 +0500 Subject: [PATCH 13/32] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20BoolParser.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud.Tests/ParsersTests/BoolParserTest.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 TagCloud.Tests/ParsersTests/BoolParserTest.cs diff --git a/TagCloud.Tests/ParsersTests/BoolParserTest.cs b/TagCloud.Tests/ParsersTests/BoolParserTest.cs new file mode 100644 index 000000000..eb6a41bc0 --- /dev/null +++ b/TagCloud.Tests/ParsersTests/BoolParserTest.cs @@ -0,0 +1,30 @@ +using FileSenderRailway; +using FluentAssertions; +using TagCloud.Parsers; + +namespace TagCloud.Tests.ParsersTests +{ + [TestFixture] + internal class BoolParserTest + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("abc")] + public void BoolParser_ThrowsException_WithInvalidInput(string value) + { + var expected = Result.Fail($"Неизвестный параметр сортировки {value}"); + var actual = BoolParser.ParseIsSorted(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("True")] + [TestCase("False")] + public void BoolParser_WorksCorrectly(string value) + { + var expected = Convert.ToBoolean(value); + var actual = BoolParser.ParseIsSorted(value).GetValueOrThrow(); + actual.Should().Be(expected); + } + } +} From 80b703ae183dc69df683585bce8d6d6c4921a1b5 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Wed, 15 Jan 2025 17:02:17 +0500 Subject: [PATCH 14/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20ColorParser.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Рефакторинг ColorParser. 2. Добавлены тесты для ColorParser. --- .../ParsersTests/ColorParserTest.cs | 42 +++++++++++++++++++ TagCloud/Parsers/ColorParser.cs | 4 ++ 2 files changed, 46 insertions(+) create mode 100644 TagCloud.Tests/ParsersTests/ColorParserTest.cs diff --git a/TagCloud.Tests/ParsersTests/ColorParserTest.cs b/TagCloud.Tests/ParsersTests/ColorParserTest.cs new file mode 100644 index 000000000..f814f554a --- /dev/null +++ b/TagCloud.Tests/ParsersTests/ColorParserTest.cs @@ -0,0 +1,42 @@ +using FileSenderRailway; +using FluentAssertions; +using System.Drawing; +using TagCloud.Parsers; + +namespace TagCloud.Tests.ParsersTests +{ + [TestFixture] + internal class ColorParserTest + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + public void ColorParser_ThrowsException_WithInvalidInput(string value) + { + var expected = Result.Fail($"Некорректная строка {value}"); + var actual = ColorParser.ParseColor(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("abc")] + public void ColorParser_ThrowsException_WithUnknownColor(string value) + { + var expected = Result.Fail($"Неизвестный цвет {value}"); + var actual = ColorParser.ParseColor(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("red")] + [TestCase("blue")] + [TestCase("white")] + [TestCase("black")] + [TestCase("green")] + [TestCase("yellow")] + public void ColorParser_WorksCorrectly(string value) + { + var expected = Color.FromName(value); + var actual = ColorParser.ParseColor(value).GetValueOrThrow(); + actual.Should().Be(expected); + } + } +} diff --git a/TagCloud/Parsers/ColorParser.cs b/TagCloud/Parsers/ColorParser.cs index 3e811ef66..3e541a561 100644 --- a/TagCloud/Parsers/ColorParser.cs +++ b/TagCloud/Parsers/ColorParser.cs @@ -7,6 +7,10 @@ internal static class ColorParser { public static Result ParseColor(string color) { + if (string.IsNullOrWhiteSpace(color)) + { + return Result.Fail($"Некорректная строка {color}"); + } var result = Color.FromName(color); if (!result.IsKnownColor) { From 437d5c41b61be56e9a339b7f359e0e94e0f8cdd3 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Wed, 15 Jan 2025 17:13:24 +0500 Subject: [PATCH 15/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20FontParser.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Рефакторинг FontParser. 2. Добавлены тесты для FontParser. --- TagCloud.Tests/ParsersTests/FontParserTest.cs | 40 +++++++++++++++++++ TagCloud/Parsers/FontParser.cs | 4 ++ 2 files changed, 44 insertions(+) create mode 100644 TagCloud.Tests/ParsersTests/FontParserTest.cs diff --git a/TagCloud.Tests/ParsersTests/FontParserTest.cs b/TagCloud.Tests/ParsersTests/FontParserTest.cs new file mode 100644 index 000000000..33755e4ca --- /dev/null +++ b/TagCloud.Tests/ParsersTests/FontParserTest.cs @@ -0,0 +1,40 @@ +using FileSenderRailway; +using FluentAssertions; +using System.Drawing; +using TagCloud.Parsers; + +namespace TagCloud.Tests.ParsersTests +{ + [TestFixture] + internal class FontParserTest + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + public void FontParser_ThrowsException_WithInvalidInput(string value) + { + var expected = Result.Fail($"Некорректная строка {value}"); + var actual = FontParser.ParseFont(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("abc")] + public void FontParser_ThrowsException_WithUnknownColor(string value) + { + var expected = Result.Fail($"Неизвестный шрифт {value}"); + var actual = FontParser.ParseFont(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("Arial")] + [TestCase("Times New Roman")] + [TestCase("Georgia")] + [TestCase("Verdana")] + public void FontParser_WorksCorrectly(string value) + { + var expected = new FontFamily(value); + var actual = FontParser.ParseFont(value).GetValueOrThrow(); + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud/Parsers/FontParser.cs b/TagCloud/Parsers/FontParser.cs index 7038483da..3bc319c14 100644 --- a/TagCloud/Parsers/FontParser.cs +++ b/TagCloud/Parsers/FontParser.cs @@ -7,6 +7,10 @@ internal static class FontParser { public static Result ParseFont(string font) { + if (string.IsNullOrWhiteSpace(font)) + { + return Result.Fail($"Некорректная строка {font}"); + } if (!FontFamily.Families.Any( x => x.Name.Equals(font, StringComparison.OrdinalIgnoreCase))) { From 741ea6541b9eb8cf3d646f5b37387c0a52cbd06a Mon Sep 17 00:00:00 2001 From: Zakhar Date: Wed, 15 Jan 2025 17:46:24 +0500 Subject: [PATCH 16/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20SizeParser.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Рефакторинг SizeParser. 2. Добавлены тесты для SizeParser. --- TagCloud.Tests/ParsersTests/SizeParserTest.cs | 88 +++++++++++++++++++ TagCloud/Parsers/SizeParser.cs | 28 ++++-- 2 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 TagCloud.Tests/ParsersTests/SizeParserTest.cs diff --git a/TagCloud.Tests/ParsersTests/SizeParserTest.cs b/TagCloud.Tests/ParsersTests/SizeParserTest.cs new file mode 100644 index 000000000..615ba8c19 --- /dev/null +++ b/TagCloud.Tests/ParsersTests/SizeParserTest.cs @@ -0,0 +1,88 @@ +using FileSenderRailway; +using FluentAssertions; +using System.Drawing; +using TagCloud.Parsers; + +namespace TagCloud.Tests.ParsersTests +{ + [TestFixture] + internal class SizeParserTest + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + public void ParseImageSize_ThrowsException_WithInvalidInput(string value) + { + var expected = Result.Fail($"Некорректная строка {value}"); + var actual = SizeParser.ParseImageSize(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("100:")] + [TestCase(":100")] + [TestCase("100:100:100")] + [TestCase("abc")] + public void ParseImageSize_ThrowsException_WithIncorrectFormat(string value) + { + var expected = Result.Fail($"Некорректный формат размера изображения: \"{value}\", используйте формат \"Ширина:Высота\", например 5000:5000"); + var actual = SizeParser.ParseImageSize(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("abc:100")] + [TestCase("100:abc")] + [TestCase("abc:abc")] + public void ParseImageSize_ThrowsException_WithIncorrectInput(string value) + { + var expected = Result.Fail($"Передано не число: abc"); + var actual = SizeParser.ParseImageSize(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("0:100")] + [TestCase("100:0")] + [TestCase("0:0")] + public void ParseImageSize_ThrowsException_WithInputLessThanZero(string value) + { + var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: 0"); + var actual = SizeParser.ParseImageSize(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + public void ParseSizeDimension_ThrowsException_WithInvalidInputAsString(string value) + { + var expected = Result.Fail($"Некорректная строка {value}"); + var actual = SizeParser.ParseSizeDimension(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("abc")] + public void ParseSizeDimension_ThrowsException_WithIncorrectInputAsString(string value) + { + var expected = Result.Fail($"Передано не число: {value}"); + var actual = SizeParser.ParseSizeDimension(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("0")] + [TestCase("-1")] + public void ParseSizeDimension_ThrowsException_WithInputLessThanZeroAsString(string value) + { + var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: {value}"); + var actual = SizeParser.ParseSizeDimension(value); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("0")] + [TestCase("-1")] + public void ParseSizeDimension_ThrowsException_WithInputLessThanZeroAsInt(int value) + { + var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: {value}"); + var actual = SizeParser.ParseSizeDimension(value); + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud/Parsers/SizeParser.cs b/TagCloud/Parsers/SizeParser.cs index fd3101072..b08f1c553 100644 --- a/TagCloud/Parsers/SizeParser.cs +++ b/TagCloud/Parsers/SizeParser.cs @@ -7,20 +7,38 @@ internal static class SizeParser { public static Result ParseImageSize(string size) { - var dimensions = size.Split(':'); + if (string.IsNullOrWhiteSpace(size)) + { + return Result.Fail($"Некорректная строка {size}"); + } + + var dimensions = size.Split(':', StringSplitOptions.RemoveEmptyEntries); if (dimensions.Length != 2) { - return Result.Fail($"Некорректный формат размера изображения: {size}, используйте формат Ширина:Высота, например 5000:5000"); + return Result.Fail($"Некорректный формат размера изображения: \"{size}\", используйте формат \"Ширина:Высота\", например 5000:5000"); + } + + var width = ParseSizeDimension(dimensions[0]); + if (!width.IsSuccess) + { + return Result.Fail(width.Error); } - var width = ParseSizeDimension(dimensions[0]).GetValueOrThrow(); - var height = ParseSizeDimension(dimensions[1]).GetValueOrThrow(); + var height = ParseSizeDimension(dimensions[1]); + if (!height.IsSuccess) + { + return Result.Fail(height.Error); + } - return new Size(width, height).AsResult(); + return new Size(width.Value, height.Value).AsResult(); } public static Result ParseSizeDimension(string dimension) { + if (string.IsNullOrWhiteSpace(dimension)) + { + return Result.Fail($"Некорректная строка {dimension}"); + } if (!int.TryParse(dimension, out var result)) { return Result.Fail($"Передано не число: {dimension}"); From 98b047f3871cd068c806a9f9d8ebd25d4a04bf6d Mon Sep 17 00:00:00 2001 From: Zakhar Date: Thu, 16 Jan 2025 19:29:27 +0500 Subject: [PATCH 17/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20TagCloud.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPainter.cs | 21 ++-- ...alizedFrequencyBasedCloudLayouterWorker.cs | 8 +- .../RandomCloudLayouterWorker.cs | 2 +- .../CircularCloudLayouter.cs | 9 +- TagCloud/CommandLineOptions.cs | 44 +++---- TagCloud/DIContainer.cs | 100 +++------------- .../Factories/CloudLayouterPainterFactory.cs | 19 +++ .../Factories/CloudLayouterWorkerFactory.cs | 54 ++++++--- .../Factories/ICloudLayouterPainterFactory.cs | 15 +++ .../Factories/ICloudLayouterWorkerFactory.cs | 16 +++ TagCloud/Factories/IWordFilterFactory.cs | 14 +++ TagCloud/Factories/WordFilterFactory.cs | 71 ++++++----- TagCloud/GlobalSuppressions.cs | 22 ---- TagCloud/ImageSavers/ImageSaver.cs | 3 +- TagCloud/Normalizers/Normalizer.cs | 10 +- TagCloud/Parsers/BoolParser.cs | 2 +- TagCloud/Parsers/ColorParser.cs | 7 +- TagCloud/Parsers/FontParser.cs | 8 +- TagCloud/Parsers/SizeParser.cs | 17 ++- TagCloud/Program.cs | 8 +- TagCloud/ProgramExecutor.cs | 112 ++++++++++++++---- TagCloud/WordCounters/WordCounter.cs | 2 +- TagCloud/WordFilters/IWordFilter.cs | 2 + TagCloud/WordFilters/WordFilter.cs | 19 --- TagCloud/WordReaders/IWordReader.cs | 2 +- TagCloud/WordReaders/WordReader.cs | 28 +++-- 26 files changed, 344 insertions(+), 271 deletions(-) create mode 100644 TagCloud/Factories/CloudLayouterPainterFactory.cs create mode 100644 TagCloud/Factories/ICloudLayouterPainterFactory.cs create mode 100644 TagCloud/Factories/ICloudLayouterWorkerFactory.cs create mode 100644 TagCloud/Factories/IWordFilterFactory.cs delete mode 100644 TagCloud/GlobalSuppressions.cs diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs index ae2e0cb47..11c8d56ae 100644 --- a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -3,6 +3,7 @@ namespace TagCloud.CloudLayouterPainters { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class CloudLayouterPainter( Size imageSize, Color? backgroundColor = null, @@ -14,11 +15,9 @@ internal class CloudLayouterPainter( private readonly FontFamily fontName = fontName ?? new FontFamily("Arial"); public Result Draw(IList tags) - { - return ValidateTags(tags) + => ValidateTags(tags) .Then(validTags => CreateBitmap(validTags)) .OnFail(error => Result.Fail(error)); - } private Result> ValidateTags(IList tags) { @@ -35,15 +34,14 @@ private Result> ValidateTags(IList tags) if (!DoRectanglesFit(tags)) { return Result - .Fail>("Все прямоугольники не помещаются на изображение. Измените его размеры"); + .Fail>("Все прямоугольники не помещаются на изображение"); } return tags.AsResult(); } private Result CreateBitmap(IList tags) - { - return Result.Of(() => + => Result.Of(() => { var bitmap = new Bitmap(imageSize.Width, imageSize.Height); @@ -64,12 +62,17 @@ private Result CreateBitmap(IList tags) return bitmap; }); - } private bool DoRectanglesFit(IList tags) { - var minimums = new Point(tags.Min(t => t.Rectangle.Left), tags.Min(t => t.Rectangle.Top)); - var maximums = new Point(tags.Max(t => t.Rectangle.Right), tags.Max(t => t.Rectangle.Bottom)); + var minimums = new Point( + tags.Min(t => t.Rectangle.Left), + tags.Min(t => t.Rectangle.Top)); + + var maximums = new Point( + tags.Max(t => t.Rectangle.Right), + tags.Max(t => t.Rectangle.Bottom)); + var actualWidth = maximums.X - minimums.X; var actualHeight = maximums.Y - minimums.Y; return actualWidth < imageSize.Width && actualHeight < imageSize.Height; diff --git a/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs index e1b377f11..4f290a5c5 100644 --- a/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs +++ b/TagCloud/CloudLayouterWorkers/NormalizedFrequencyBasedCloudLayouterWorker.cs @@ -13,13 +13,13 @@ internal class NormalizedFrequencyBasedCloudLayouterWorker : ICloudLayouterWorke public string[] KeysOrder => keysOrder.ToArray(); public NormalizedFrequencyBasedCloudLayouterWorker( - int maxRectangleWidth, - int maxRectangleHeight, + int maxWidth, + int maxHeight, Dictionary normalizedValues, bool isSorted = true) { - this.maxRectangleWidth = maxRectangleWidth; - this.maxRectangleHeight = maxRectangleHeight; + maxRectangleWidth = maxWidth; + maxRectangleHeight = maxHeight; values = normalizedValues; if (isSorted) { diff --git a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs index 4066dc8e7..43aef0143 100644 --- a/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs +++ b/TagCloud/CloudLayouterWorkers/RandomCloudLayouterWorker.cs @@ -30,7 +30,7 @@ private Result ParseSizes() .Then(_ => Result.Ok())))) .OnFail(error => Result.Fail(error)); - private Result AreMinAndMaxSizesAppropriate(int min, int max) + private static Result AreMinAndMaxSizesAppropriate(int min, int max) { if (min > max) { diff --git a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs index 99a640d49..ded9e7573 100644 --- a/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs +++ b/TagCloud/CloudLayouters/CircularCloudLayouter/CircularCloudLayouter.cs @@ -1,5 +1,4 @@ using FileSenderRailway; -using System; using System.Drawing; namespace TagCloud.CloudLayouters.CircularCloudLayouter @@ -22,7 +21,7 @@ public Result PutNextRectangle(Size rectangleSize) .Then(size => PlaceRectangle(size)) .OnFail(error => Result.Fail(error)); - private Result ValidateRectangleSize(Size rectangleSize) + private static Result ValidateRectangleSize(Size rectangleSize) { if (rectangleSize.Width <= 0 || rectangleSize.Height <= 0) { @@ -33,8 +32,7 @@ private Result ValidateRectangleSize(Size rectangleSize) } private Result PlaceRectangle(Size rectangleSize) - { - return Result.Of(() => + => Result.Of(() => { var result = new Rectangle(); arrangementСircle.Radius -= 1.0f; @@ -61,12 +59,11 @@ private Result PlaceRectangle(Size rectangleSize) return result; }); - } private bool IsIntersectionWithAlreadyPlaced(Rectangle rectangle) => rectangles.Any(rect => rect.IntersectsWith(rectangle)); - private Point GetRectangleLocation(Point pointOnCircle, Size rectangleSize) + private static Point GetRectangleLocation(Point pointOnCircle, Size rectangleSize) { var x = pointOnCircle.X - rectangleSize.Width / 2; var y = pointOnCircle.Y - rectangleSize.Height / 2; diff --git a/TagCloud/CommandLineOptions.cs b/TagCloud/CommandLineOptions.cs index 93497c6d7..902249b9e 100644 --- a/TagCloud/CommandLineOptions.cs +++ b/TagCloud/CommandLineOptions.cs @@ -2,88 +2,78 @@ namespace TagCloud { - // 1. Нужно добавить опцию для указания имени файла, - // который содержит слова, для иссключения из фильтра скучных слов - // 2. Нужно добавить опцию для указания имени файла, - // который содержит слова, для добавления в фильтр скучных слов public class CommandLineOptions { [Option( "backgroundColor", Required = false, - HelpText = "Цвет заднего фона изображения, например White.")] + HelpText = "Цвет заднего фона изображения, например \"White\".")] public string BackgroundColor { get; set; } = "White"; [Option( "textColor", Required = false, - HelpText = "Цвет текста на изображении, например Black.")] + HelpText = "Цвет текста на изображении, например \"Black\".")] public string TextColor { get; set; } = "Black"; [Option( "font", Required = false, - HelpText = "Шрифт текста на изображении, например Arial.")] + HelpText = "Шрифт текста на изображении, например \"Arial\".")] public string Font { get; set; } = "Arial"; [Option( "nonSorted", Required = false, - HelpText = "Отключение сортировки слов, например False")] - public string IsSorted { get; set; } = true.ToString(); + HelpText = "Отключение сортировки слов, например \"False\".")] + public string IsSorted { get; set; } = Boolean.TrueString; [Option( "size", Required = false, - HelpText = "Размер изображения в формате ШИРИНА:ВЫСОТА, например 5000:5000.")] + HelpText = "Размер изображения в формате ШИРИНА:ВЫСОТА, например \"5000:5000\".")] public string ImageSize { get; set; } = "5000:5000"; [Option( "maxRectangleWidth", Required = false, - HelpText = "Максимальная ширина прямоугольника.")] - public int MaxRectangleWidth { get; set; } = 500; + HelpText = "Максимальная ширина прямоугольника, например \"500\".")] + public string MaxRectangleWidth { get; set; } = "500"; [Option( "maxRectangleHeight", Required = false, - HelpText = "Максимальная высота прямоугольника.")] - public int MaxRectangleHeight { get; set; } = 200; + HelpText = "Максимальная высота прямоугольника, например \"200\".")] + public string MaxRectangleHeight { get; set; } = "200"; [Option( "imageFile", Required = false, - HelpText = "Имя выходного файла изображения.")] + HelpText = "Имя выходного файла изображения, например \"Result\".")] public string ImageFileName { get; set; } = "Result"; [Option( "dataFile", Required = true, - HelpText = "Имя файла с исходными данными.")] + HelpText = "Полный путь к файлу с исходными данными, например \"C:\\MyWorkSpace\\Coding\\MyCodes\\CSharp\\PostUniversityEra\\ShporaHomeworks\\Homework.6.TagCloudII\\SnowWhite.txt\".")] public required string DataFileName { get; set; } [Option( "resultFormat", Required = false, - HelpText = "Формат создаваемого изображение, например png.")] + HelpText = "Формат создаваемого изображение, например \"png\".")] public string ResultFormat { get; set; } = "png"; - [Option( - "normalizeMinCoefficient", - Required = false, - HelpText = "Минимальный коэффициент нормализации, например 0.25")] - public double NormalizeMinCoefficient { get; set; } = 0.25; - [Option( "wordsToIncludeFile", Required = false, - HelpText = "Имя файла со словами для добавления в фильтр \"скучных слов\".")] - public string? WordsToIncludeFileName { get; set; } = null; + HelpText = "Полный путь к файлу со словами для добавления в фильтр \"скучных слов\", например \"C:\\MyWorkSpace\\Coding\\MyCodes\\CSharp\\PostUniversityEra\\ShporaHomeworks\\Homework.6.TagCloudII\\WordsToInclude.txt\".")] + public string? WordsToIncludeFileName { get; set; } = null; //+ [Option( "wordsToExcludeFile", Required = false, - HelpText = "Имя файла со словами для исключения из фильтра \"скучных слов\".")] - public string? WordsToExcludeFileName { get; set; } = null; + HelpText = "Полный путь к файлу со словами для исключения из фильтра \"скучных слов\", например \"C:\\MyWorkSpace\\Coding\\MyCodes\\CSharp\\PostUniversityEra\\ShporaHomeworks\\Homework.6.TagCloudII\\WordsToExclude.txt\".")] + public string? WordsToExcludeFileName { get; set; } = null; //+ } } \ No newline at end of file diff --git a/TagCloud/DIContainer.cs b/TagCloud/DIContainer.cs index 224325b90..464a4bc68 100644 --- a/TagCloud/DIContainer.cs +++ b/TagCloud/DIContainer.cs @@ -1,125 +1,59 @@ using Autofac; -using TagCloud.CloudLayouterPainters; using TagCloud.CloudLayouters.CircularCloudLayouter; using TagCloud.CloudLayouters; -using TagCloud.CloudLayouterWorkers; using TagCloud.ImageSavers; using TagCloud.Normalizers; using TagCloud.WordCounters; -using TagCloud.WordFilters; using TagCloud.WordReaders; -using TagCloud.Parsers; -using System.Drawing; using TagCloud.Factories; namespace TagCloud { public static class DIContainer { - // 2. Добавить проверку корректностей значений: - // - options.MaxRectangleWidth; - // - options.MaxRectangleHeight; public static IContainer ConfigureContainer(CommandLineOptions options) { var builder = new ContainerBuilder(); RegisterSimpleSevice(builder); - RegisterSimpleSevice(builder); - RegisterIWordFillterSevice(builder, options); + RegisterSimpleSevice(builder); RegisterSimpleSevice(builder); RegisterSimpleSevice(builder); RegisterSimpleSevice(builder); RegisterSimpleSevice(builder); - RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); + RegisterSimpleSevice(builder); - var imageSize = SizeParser.ParseImageSize(options.ImageSize).GetValueOrThrow(); - RegisterICloudLayouterPainterSevice(builder, options, imageSize); - - RegisterICloudLayouterWorkerSevice(builder, options); - - RegisterProgramExecutorService(builder, options, imageSize); + RegisterProgramExecutorService(builder, options); return builder.Build(); } - private static void RegisterSimpleSevice(ContainerBuilder builder) + private static void RegisterSimpleSevice( + ContainerBuilder builder) where TImplementation : TService where TService : notnull - { - builder + => builder .RegisterType() .As() .SingleInstance(); - } - - private static void RegisterSimpleSevice(ContainerBuilder builder) - where TImplementation : notnull - { - builder - .RegisterType() - .AsSelf() - .SingleInstance(); - } - - private static void RegisterICloudLayouterPainterSevice( - ContainerBuilder builder, - CommandLineOptions options, - Size imageSize) - { - var backgroundColor = ColorParser.ParseColor(options.BackgroundColor).GetValueOrThrow(); - var textColor = ColorParser.ParseColor(options.TextColor).GetValueOrThrow(); - var font = FontParser.ParseFont(options.Font).GetValueOrThrow(); - builder.RegisterType() - .As() - .WithParameter("imageSize", imageSize) - .WithParameter("backgroundColor", backgroundColor) - .WithParameter("textColor", textColor) - .WithParameter("fontName", font) - .SingleInstance(); - } - - private static void RegisterICloudLayouterWorkerSevice( - ContainerBuilder builder, - CommandLineOptions options) - { - builder.Register(c => - { - var factory = c.Resolve(); - return factory.Create( - options.DataFileName, - options.MaxRectangleWidth, - options.MaxRectangleHeight, - BoolParser.ParseIsSorted(options.IsSorted).GetValueOrThrow()); - }).As().SingleInstance(); - } - - private static void RegisterIWordFillterSevice( - ContainerBuilder builder, - CommandLineOptions options) - { - builder.Register(c => - { - var factory = c.Resolve(); - return factory.Create( - options.WordsToIncludeFileName, - options.WordsToExcludeFileName, - c.Resolve()); - }).As().SingleInstance(); - } private static void RegisterProgramExecutorService( ContainerBuilder builder, - CommandLineOptions options, - Size imageSize) - { - builder.RegisterType() - .WithParameter("size", imageSize) - .WithParameter("resultFormat", options.ResultFormat) + CommandLineOptions options) + => builder.RegisterType() + .WithParameter("backgroundColor", options.BackgroundColor) + .WithParameter("textColor", options.TextColor) + .WithParameter("font", options.Font) + .WithParameter("isSorted", options.IsSorted) + .WithParameter("imageSize", options.ImageSize) .WithParameter("maxRectangleWidth", options.MaxRectangleWidth) .WithParameter("maxRectangleHeight", options.MaxRectangleHeight) .WithParameter("imageFileName", options.ImageFileName) .WithParameter("dataFileName", options.DataFileName) + .WithParameter("resultFormat", options.ResultFormat) + .WithParameter("wordsToIncludeFileName", options.WordsToIncludeFileName!) + .WithParameter("wordsToExcludeFileName", options.WordsToExcludeFileName!) .SingleInstance(); - } } } diff --git a/TagCloud/Factories/CloudLayouterPainterFactory.cs b/TagCloud/Factories/CloudLayouterPainterFactory.cs new file mode 100644 index 000000000..5d733e5d8 --- /dev/null +++ b/TagCloud/Factories/CloudLayouterPainterFactory.cs @@ -0,0 +1,19 @@ +using FileSenderRailway; +using System.Drawing; +using TagCloud.CloudLayouterPainters; + + +namespace TagCloud.Factories +{ + internal class CloudLayouterPainterFactory : ICloudLayouterPainterFactory + { + public Result Create( + Size imageSize, + Color? backgroundColor = null, + Color? textColor = null, + FontFamily? fontName = null) + => Result.Ok( + new CloudLayouterPainter(imageSize, backgroundColor, textColor, fontName)); + + } +} diff --git a/TagCloud/Factories/CloudLayouterWorkerFactory.cs b/TagCloud/Factories/CloudLayouterWorkerFactory.cs index 52f5fd3fa..c3be69c3f 100644 --- a/TagCloud/Factories/CloudLayouterWorkerFactory.cs +++ b/TagCloud/Factories/CloudLayouterWorkerFactory.cs @@ -1,4 +1,5 @@ -using TagCloud.CloudLayouterWorkers; +using FileSenderRailway; +using TagCloud.CloudLayouterWorkers; using TagCloud.Normalizers; using TagCloud.WordCounters; using TagCloud.WordFilters; @@ -8,32 +9,55 @@ namespace TagCloud.Factories { internal class CloudLayouterWorkerFactory( IWordReader wordReader, - IWordCounter wordCounter, - INormalizer normalizer, - IWordFilter wordFilter) + IWordCounter wordCounter, + INormalizer normalizer, + IWordFilterFactory wordFilterFactory) : ICloudLayouterWorkerFactory { - public ICloudLayouterWorker Create( + public Result Create( string dataFileName, + string? wordsToIncludeFileName, + string? wordsToExcludeFileName, int maxRectangleWidth, int maxRectangleHeight, bool isSorted) + => ReadWords(dataFileName) + .Then(initialWords => CreateFilter( + wordsToIncludeFileName, + wordsToExcludeFileName, + wordReader) + .Then(filter => AddWords(filter, initialWords))) + .Then(_ => normalizer.Normalize(wordCounter.Values)) + .Then(normalizer + => Result.Ok( + new NormalizedFrequencyBasedCloudLayouterWorker( + maxRectangleWidth, + maxRectangleHeight, + normalizer, + isSorted))) + .OnFail(error => Result.Fail(error)); + + private Result> ReadWords(string dataFileName) + => wordReader.ReadByLines(dataFileName); + + private Result CreateFilter( + string? wordsToIncludeFileName, + string? wordsToExcludeFileName, + IWordReader wordReader) + => wordFilterFactory.Create(wordsToIncludeFileName, wordsToExcludeFileName, wordReader); + + private Result AddWords(IWordFilter wordFilter, IEnumerable words) { - foreach (var word in wordReader.ReadByLines(dataFileName)) + foreach (var word in words) { - var wordInLowerCase = word.GetValueOrThrow().ToLower(); + var wordInLowerCase = word.ToLower(); if (!wordFilter.IsCorrectWord(wordInLowerCase)) { continue; } + wordCounter.AddWord(wordInLowerCase); } - - var normalizedValues = normalizer.Normalize(wordCounter.Values); - return new NormalizedFrequencyBasedCloudLayouterWorker( - maxRectangleWidth, - maxRectangleHeight, - normalizedValues.GetValueOrThrow(), - isSorted); + return Result.Ok(); } } -} +} \ No newline at end of file diff --git a/TagCloud/Factories/ICloudLayouterPainterFactory.cs b/TagCloud/Factories/ICloudLayouterPainterFactory.cs new file mode 100644 index 000000000..91724bf90 --- /dev/null +++ b/TagCloud/Factories/ICloudLayouterPainterFactory.cs @@ -0,0 +1,15 @@ +using FileSenderRailway; +using System.Drawing; +using TagCloud.CloudLayouterPainters; + +namespace TagCloud.Factories +{ + internal interface ICloudLayouterPainterFactory + { + public Result Create( + Size imageSize, + Color? backgroundColor = null, + Color? textColor = null, + FontFamily? fontName = null); + } +} diff --git a/TagCloud/Factories/ICloudLayouterWorkerFactory.cs b/TagCloud/Factories/ICloudLayouterWorkerFactory.cs new file mode 100644 index 000000000..eff29b50d --- /dev/null +++ b/TagCloud/Factories/ICloudLayouterWorkerFactory.cs @@ -0,0 +1,16 @@ +using FileSenderRailway; +using TagCloud.CloudLayouterWorkers; + +namespace TagCloud.Factories +{ + internal interface ICloudLayouterWorkerFactory + { + public Result Create( + string dataFileName, + string? wordsToIncludeFileName, + string? wordsToExcludeFileName, + int maxRectangleWidth, + int maxRectangleHeight, + bool isSorted); + } +} diff --git a/TagCloud/Factories/IWordFilterFactory.cs b/TagCloud/Factories/IWordFilterFactory.cs new file mode 100644 index 000000000..fcaf1c41b --- /dev/null +++ b/TagCloud/Factories/IWordFilterFactory.cs @@ -0,0 +1,14 @@ +using FileSenderRailway; +using TagCloud.WordFilters; +using TagCloud.WordReaders; + +namespace TagCloud.Factories +{ + internal interface IWordFilterFactory + { + public Result Create( + string? wordsToIncludeFileName, + string? wordsToExcludeFileName, + IWordReader wordReader); + } +} diff --git a/TagCloud/Factories/WordFilterFactory.cs b/TagCloud/Factories/WordFilterFactory.cs index 3ed7a52e5..f2e50a531 100644 --- a/TagCloud/Factories/WordFilterFactory.cs +++ b/TagCloud/Factories/WordFilterFactory.cs @@ -1,53 +1,66 @@ -using TagCloud.WordFilters; +using FileSenderRailway; +using TagCloud.CloudLayouterWorkers; +using TagCloud.WordFilters; using TagCloud.WordReaders; namespace TagCloud.Factories { - internal class WordFilterFactory + internal class WordFilterFactory : IWordFilterFactory { - public IWordFilter Create( + public Result Create( string? wordsToIncludeFileName, string? wordsToExcludeFileName, IWordReader wordReader) - { - var result = new WordFilter(); + => Result.Ok(new WordFilter()) + .Then(wordFilter => AddWords(wordReader, wordsToIncludeFileName!, wordFilter)) + .Then(wordFilter => RemoveWords(wordReader, wordsToExcludeFileName!, wordFilter)) + .OnFail(error => Result.Fail(error)); - if (IsFileNameCorrect(wordsToIncludeFileName)) - { - AddWords(wordReader, wordsToIncludeFileName!, result); - } + private static bool IsFileNameSet(string? fileName) + => fileName is not null; - if (IsFileNameCorrect(wordsToExcludeFileName)) - { - RemoveWords(wordReader, wordsToExcludeFileName!, result); - } - - return result; - } - - private bool IsFileNameCorrect(string? fileName) - => !string.IsNullOrEmpty(fileName) && !string.IsNullOrWhiteSpace(fileName); - - private void AddWords( + private static Result AddWords( IWordReader wordReader, string wordsToIncludeFileName, - WordFilter wordFilter) + IWordFilter wordFilter) { - foreach (var word in wordReader.ReadByLines(wordsToIncludeFileName)) + if (IsFileNameSet(wordsToIncludeFileName)) { - wordFilter.Add(word.GetValueOrThrow().ToLower()); + var wordsResult = wordReader.ReadByLines(wordsToIncludeFileName); + if (!wordsResult.IsSuccess) + { + return Result.Fail(wordsResult.Error); + } + + foreach (var word in wordsResult.Value) + { + wordFilter.Add(word.ToLower()); + } } + + return Result.Ok(wordFilter); } - private void RemoveWords( + private static Result RemoveWords( IWordReader wordReader, string wordsToExcludeFileName, - WordFilter wordFilter) + IWordFilter wordFilter) { - foreach (var word in wordReader.ReadByLines(wordsToExcludeFileName)) + if (IsFileNameSet(wordsToExcludeFileName)) { - wordFilter.Remove(word.GetValueOrThrow().ToLower()); + var wordsResult = wordReader.ReadByLines(wordsToExcludeFileName); + if (!wordsResult.IsSuccess) + { + return Result.Fail(wordsResult.Error); + } + + foreach (var word in wordsResult.Value) + { + wordFilter.Remove(word.ToLower()); + } } + + return Result.Ok(wordFilter); } } -} +} \ No newline at end of file diff --git a/TagCloud/GlobalSuppressions.cs b/TagCloud/GlobalSuppressions.cs deleted file mode 100644 index e3b20b229..000000000 --- a/TagCloud/GlobalSuppressions.cs +++ /dev/null @@ -1,22 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - - -// Так делать явно плохо -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.DrawText(System.Drawing.Graphics,System.Drawing.Rectangle,System.String)")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.Draw(System.Collections.Generic.IList{TagCloud.Tag})~System.Drawing.Bitmap")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.FindFittingFontSize(System.Drawing.Graphics,System.String,System.Drawing.Rectangle)~System.Int32")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:Program.ParseFont(System.String)~System.String")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.ImageSavers.ImageSaver.SaveFile(System.Drawing.Bitmap,System.String,System.String)")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~System.String")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.CloudLayouterPainters.CloudLayouterPainter.Draw(System.Collections.Generic.IList{TagCloud.Tag})~FileSenderRailway.Result{System.Drawing.Bitmap}")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.ProgramExecutor.Execute(System.String)")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~FileSenderRailway.Result{System.String}")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.ProgramExecutor.Execute")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Parsers.FontParser.ParseFont(System.String)~FileSenderRailway.Result{System.Drawing.FontFamily}")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~F:TagCloud.CloudLayouterPainters.CloudLayouterPainter.fontName")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.DIContainer.RegisterICloudLayouterPainterSevice(Autofac.ContainerBuilder,TagCloud.CommandLineOptions,System.Drawing.Size)")] diff --git a/TagCloud/ImageSavers/ImageSaver.cs b/TagCloud/ImageSavers/ImageSaver.cs index ec91e4fc4..e9aeb6581 100644 --- a/TagCloud/ImageSavers/ImageSaver.cs +++ b/TagCloud/ImageSavers/ImageSaver.cs @@ -7,6 +7,7 @@ namespace TagCloud.ImageSavers // Реализован пункт на перспективу: // Формат результата. // Поддерживать разные форматы изображений. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class ImageSaver : IImageSaver { private readonly Dictionary supportedFormats = @@ -34,7 +35,7 @@ private Result ValidateInput(Bitmap image, string fileName, string format) if (string.IsNullOrWhiteSpace(fileName)) { - return Result.Fail("Некорректное имя файла для создания"); + return Result.Fail($"Некорректное имя файла для создания \"{fileName}\""); } if (string.IsNullOrWhiteSpace(format) || !IsSupportedFormat(format)) diff --git a/TagCloud/Normalizers/Normalizer.cs b/TagCloud/Normalizers/Normalizer.cs index 90974d339..2f7fb6644 100644 --- a/TagCloud/Normalizers/Normalizer.cs +++ b/TagCloud/Normalizers/Normalizer.cs @@ -17,7 +17,7 @@ public Result> Normalize( .Then(_ => CalculateNormalizedValues(values, minCoefficient, decimalPlaces)) .OnFail(error => Result.Fail>(error)); - private Result ValidateInput( + private static Result ValidateInput( Dictionary values, double minCoefficient, int decimalPlaces) @@ -29,12 +29,14 @@ private Result ValidateInput( if (minCoefficient < 0.0 || minCoefficient > 1.0) { - return Result.Fail("Минимальный коэффициент нормализации должен быть в диапазоне от 0 до 1"); + return Result.Fail( + "Минимальный коэффициент нормализации должен быть в диапазоне от 0 до 1"); } if (decimalPlaces < 0) { - return Result.Fail("Количество знаков после запятой не может быть отрицательным"); + return Result.Fail( + "Количество знаков после запятой не может быть отрицательным"); } return Result.Ok(); @@ -68,7 +70,7 @@ private Result> CalculateNormalizedValues( }); - private double CalculateNormalizedValue( + private static double CalculateNormalizedValue( double minCoefficient, double scale, uint value, diff --git a/TagCloud/Parsers/BoolParser.cs b/TagCloud/Parsers/BoolParser.cs index e209d3100..086076d6a 100644 --- a/TagCloud/Parsers/BoolParser.cs +++ b/TagCloud/Parsers/BoolParser.cs @@ -10,7 +10,7 @@ public static Result ParseIsSorted(string value) { return Convert.ToBoolean(value).AsResult(); } - return Result.Fail($"Неизвестный параметр сортировки {value}"); + return Result.Fail($"Неизвестный параметр сортировки \"{value}\""); } } } diff --git a/TagCloud/Parsers/ColorParser.cs b/TagCloud/Parsers/ColorParser.cs index 3e541a561..f12fd05a2 100644 --- a/TagCloud/Parsers/ColorParser.cs +++ b/TagCloud/Parsers/ColorParser.cs @@ -9,14 +9,17 @@ public static Result ParseColor(string color) { if (string.IsNullOrWhiteSpace(color)) { - return Result.Fail($"Некорректная строка {color}"); + return GetError(color); } var result = Color.FromName(color); if (!result.IsKnownColor) { - return Result.Fail($"Неизвестный цвет {color}"); + return GetError(color); } return result.AsResult(); } + + private static Result GetError(string color) + => Result.Fail($"Неизвестный цвет \"{color}\""); } } diff --git a/TagCloud/Parsers/FontParser.cs b/TagCloud/Parsers/FontParser.cs index 3bc319c14..8d2bdf9cd 100644 --- a/TagCloud/Parsers/FontParser.cs +++ b/TagCloud/Parsers/FontParser.cs @@ -3,20 +3,24 @@ namespace TagCloud.Parsers { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal static class FontParser { public static Result ParseFont(string font) { if (string.IsNullOrWhiteSpace(font)) { - return Result.Fail($"Некорректная строка {font}"); + return GetError(font); } if (!FontFamily.Families.Any( x => x.Name.Equals(font, StringComparison.OrdinalIgnoreCase))) { - return Result.Fail($"Неизвестный шрифт {font}"); + return GetError(font); } return new FontFamily(font).AsResult(); } + + private static Result GetError(string font) + => Result.Fail($"Неизвестный шрифт \"{font}\""); } } diff --git a/TagCloud/Parsers/SizeParser.cs b/TagCloud/Parsers/SizeParser.cs index b08f1c553..d620d50f1 100644 --- a/TagCloud/Parsers/SizeParser.cs +++ b/TagCloud/Parsers/SizeParser.cs @@ -5,17 +5,20 @@ namespace TagCloud.Parsers { internal static class SizeParser { + private static Result GetErrorInvalidSize(string size) + => Result.Fail($"Некорректный формат размера изображения \"{size}\", используйте формат \"Ширина:Высота\""); + public static Result ParseImageSize(string size) { if (string.IsNullOrWhiteSpace(size)) { - return Result.Fail($"Некорректная строка {size}"); + return GetErrorInvalidSize(size); } var dimensions = size.Split(':', StringSplitOptions.RemoveEmptyEntries); if (dimensions.Length != 2) { - return Result.Fail($"Некорректный формат размера изображения: \"{size}\", используйте формат \"Ширина:Высота\", например 5000:5000"); + return GetErrorInvalidSize(size); } var width = ParseSizeDimension(dimensions[0]); @@ -35,13 +38,9 @@ public static Result ParseImageSize(string size) public static Result ParseSizeDimension(string dimension) { - if (string.IsNullOrWhiteSpace(dimension)) - { - return Result.Fail($"Некорректная строка {dimension}"); - } - if (!int.TryParse(dimension, out var result)) + if (string.IsNullOrWhiteSpace(dimension) || !int.TryParse(dimension, out var result)) { - return Result.Fail($"Передано не число: {dimension}"); + return Result.Fail($"Передано не число \"{dimension}\""); } return ParseSizeDimension(result); } @@ -50,7 +49,7 @@ public static Result ParseSizeDimension(int dimension) { if (dimension <= 0) { - return Result.Fail($"Переданное числовое значение должно быть больше 0: {dimension}"); + return Result.Fail($"Переданное числовое значение должно быть больше 0: \"{dimension}\""); } return dimension.AsResult(); } diff --git a/TagCloud/Program.cs b/TagCloud/Program.cs index d67ab880a..bb6718c9d 100644 --- a/TagCloud/Program.cs +++ b/TagCloud/Program.cs @@ -1,5 +1,6 @@ using Autofac; using CommandLine; +using FileSenderRailway; using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("TagCloud.Tests")] @@ -20,7 +21,9 @@ static void Main(string[] args) { Console.WriteLine("Ошибка парсинга аргументов:"); foreach (var error in errors) + { Console.WriteLine(error.ToString()); + } }); } @@ -28,7 +31,10 @@ private static void Run(IContainer container) { using var scope = container.BeginLifetimeScope(); var program = scope.Resolve(); - program.Execute(); + program + .Execute() + .Then(_ => Console.WriteLine("Изображение успешно сгенерировано.")) + .OnFail(error => Console.WriteLine(error)); } } } diff --git a/TagCloud/ProgramExecutor.cs b/TagCloud/ProgramExecutor.cs index a7068564c..8ac507ba3 100644 --- a/TagCloud/ProgramExecutor.cs +++ b/TagCloud/ProgramExecutor.cs @@ -1,35 +1,101 @@ -using TagCloud.CloudLayouters; -using TagCloud.CloudLayouterPainters; +using FileSenderRailway; +using TagCloud.CloudLayouters; using TagCloud.CloudLayouterWorkers; using TagCloud.ImageSavers; +using TagCloud.Factories; +using TagCloud.Parsers; +using System.Drawing; namespace TagCloud { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class ProgramExecutor( + string backgroundColor, + string textColor, + string font, + string isSorted, + string imageSize, + string maxRectangleWidth, + string maxRectangleHeight, string imageFileName, + string dataFileName, string resultFormat, + string? wordsToIncludeFileName, + string? wordsToExcludeFileName, + ICloudLayouterWorkerFactory cloudLayouterWorkerFactory, + ICloudLayouterPainterFactory cloudLayouterPainterFactory, ICloudLayouter layouter, - ICloudLayouterPainter painter, - ICloudLayouterWorker worker, IImageSaver imageSaver) { - public void Execute() - { - var tags = new List(); - foreach (var rectangleProperty in worker.GetNextRectangleProperties()) - { - var tagSize = layouter - .PutNextRectangle(rectangleProperty.size) - .GetValueOrThrow(); - var newTag = new Tag(rectangleProperty.word, tagSize); - tags.Add(newTag); - } - imageSaver - .SaveFile( - painter.Draw(tags).GetValueOrThrow(), - imageFileName, - resultFormat) - .GetValueOrThrow(); - } + public Result Execute() + => ProcessTags() + .Then(tags => Draw(tags)) + .Then(image => Save(image)) + .OnFail(error => Result.Fail(error)); + + private Result CreateWorker() + => SizeParser.ParseSizeDimension(maxRectangleWidth) + .Then(width => SizeParser.ParseSizeDimension(maxRectangleHeight) + .Then(height => BoolParser.ParseIsSorted(isSorted) + .Then(isSorted => cloudLayouterWorkerFactory.Create( + dataFileName, + wordsToIncludeFileName, + wordsToExcludeFileName, + width, + height, + isSorted)) + ) + ) + .OnFail(error => Result.Fail(error)); + + private Result> ProcessTagsWithWorker(ICloudLayouterWorker worker) + => worker + .GetNextRectangleProperties() + .Then(rectangleProperties => + { + var tags = new List(); + foreach (var rectangleProperty in rectangleProperties) + { + var tagResult = layouter + .PutNextRectangle(rectangleProperty.size) + .Then(tagSize => new Tag(rectangleProperty.word, tagSize)); + + if (!tagResult.IsSuccess) + { + return Result.Fail>(tagResult.Error); + } + + tags.Add(tagResult.Value); + } + return Result.Ok(tags); + }) + .OnFail(error => Result.Fail>(error)); + + private Result> ProcessTags() + => CreateWorker() + .Then(worker => ProcessTagsWithWorker(worker)) + .OnFail(error => Result.Fail>(error)); + + private Result Draw(List tags) + => SizeParser.ParseImageSize(imageSize) + .Then(size => ColorParser.ParseColor(backgroundColor) + .Then(bgColor => ColorParser.ParseColor(textColor) + .Then(textColor => FontParser.ParseFont(font) + .Then(font => cloudLayouterPainterFactory.Create(size, bgColor, textColor, font)) + .Then(painter => painter.Draw(tags)) + ) + ) + ) + .OnFail(error => Result.Fail(error)); + + private Result Save(Bitmap image) + => imageSaver.SaveFile(image, imageFileName, resultFormat) + .Then(_ => Result.Ok()) + .OnFail(error => Result.Fail(error)); + + private Result DrawAndSaveImage(List tags) + => Draw(tags) + .Then(image => Save(image)) + .OnFail(error => Result.Fail(error)); } -} +} \ No newline at end of file diff --git a/TagCloud/WordCounters/WordCounter.cs b/TagCloud/WordCounters/WordCounter.cs index 283d919bb..39e279357 100644 --- a/TagCloud/WordCounters/WordCounter.cs +++ b/TagCloud/WordCounters/WordCounter.cs @@ -3,7 +3,7 @@ internal class WordCounter : IWordCounter { private readonly Dictionary counts = new Dictionary(); - public Dictionary Values => counts; + public Dictionary Values => counts.ToDictionary(); public void AddWord(string word) { diff --git a/TagCloud/WordFilters/IWordFilter.cs b/TagCloud/WordFilters/IWordFilter.cs index 4e175d569..6a546151a 100644 --- a/TagCloud/WordFilters/IWordFilter.cs +++ b/TagCloud/WordFilters/IWordFilter.cs @@ -3,6 +3,8 @@ // Интерфейс фильтрации "скучных" слов internal interface IWordFilter { + public bool Add(string word); + public bool Remove(string word); public bool IsCorrectWord(string word); } } diff --git a/TagCloud/WordFilters/WordFilter.cs b/TagCloud/WordFilters/WordFilter.cs index 81900028a..aea059b33 100644 --- a/TagCloud/WordFilters/WordFilter.cs +++ b/TagCloud/WordFilters/WordFilter.cs @@ -40,25 +40,6 @@ internal class WordFilter : IWordFilter "ouch", "ow", "phew", "shh", "tsk", "ugh", "um", "wow", "yay", "yes", "yikes" }; - public WordFilter(IList? toAdd = null, IList? toExclude = null) - { - if (toAdd is not null) - { - foreach (var word in toAdd) - { - Add(word); - } - } - - if (toExclude is not null) - { - foreach (var word in toExclude) - { - Remove(word); - } - } - } - public bool Add(string word) => bannedWords.Add(word); public bool Remove(string word) => bannedWords.Remove(word); diff --git a/TagCloud/WordReaders/IWordReader.cs b/TagCloud/WordReaders/IWordReader.cs index c83304e41..37acea4ea 100644 --- a/TagCloud/WordReaders/IWordReader.cs +++ b/TagCloud/WordReaders/IWordReader.cs @@ -5,6 +5,6 @@ namespace TagCloud.WordReaders // Интерфейс для построчного чтения содержимого файла internal interface IWordReader { - public IEnumerable> ReadByLines(string path); + public Result> ReadByLines(string path); } } diff --git a/TagCloud/WordReaders/WordReader.cs b/TagCloud/WordReaders/WordReader.cs index 019dca669..69aa5a77e 100644 --- a/TagCloud/WordReaders/WordReader.cs +++ b/TagCloud/WordReaders/WordReader.cs @@ -4,23 +4,29 @@ namespace TagCloud.WordReaders { internal class WordReader : IWordReader { - public IEnumerable> ReadByLines(string path) + public Result> ReadByLines(string path) { if (!File.Exists(path)) { - yield return Result.Fail($"Файл {path} не существует"); - yield break; + return Result.Fail>($"Файл \"{path}\" не существует"); } - foreach (var line in File.ReadAllLines(path)) + if (path.Split('.')[^1] != "txt") { - if (line.Contains(' ')) - { - yield return Result.Fail($"Файл {path} содержит строку с двумя и более словами"); - yield break; - } - yield return line.AsResult(); + return Result.Fail>($"Файл \"{path}\" должен иметь расширение \"txt\""); } + + var lines = File.ReadAllLines(path); + if (lines.Length == 0) + { + return Result.Fail>($"Файл \"{path}\" пустой"); + } + if (lines.Any(line => line.Contains(' '))) + { + return Result.Fail>($"Файл \"{path}\" содержит строку с двумя и более словами"); + } + + return lines.AsEnumerable().AsResult(); } } -} +} \ No newline at end of file From 0235996011f2b4128dc0b6ce201d53b15f92f7c9 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Thu, 16 Jan 2025 20:04:15 +0500 Subject: [PATCH 18/32] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0?= =?UTF-8?q?=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D0=B0=D1=80=D0=B3=D1=83=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OptionsTests/BackgroundColorTests.cs | 27 ++++++ TagCloud.Tests/OptionsTests/BaseOptionTest.cs | 36 ++++++++ .../OptionsTests/DataFileNameTest.cs | 87 +++++++++++++++++++ TagCloud.Tests/OptionsTests/FontTests.cs | 26 ++++++ .../OptionsTests/ImageFileNameTests.cs | 26 ++++++ TagCloud.Tests/OptionsTests/ImageSizeTests.cs | 66 ++++++++++++++ TagCloud.Tests/OptionsTests/IsSortedTests.cs | 26 ++++++ .../OptionsTests/MaxRectangleHeightTest.cs | 42 +++++++++ .../OptionsTests/MaxRectangleWidthTest.cs | 42 +++++++++ .../OptionsTests/ResultFormatTests.cs | 26 ++++++ TagCloud.Tests/OptionsTests/TextColorTests.cs | 29 +++++++ TagCloud.Tests/OptionsTests/ValidValues.cs | 17 ++++ .../WordsToExcludeFileNameTests.cs | 85 ++++++++++++++++++ .../WordsToIncludeFileNameTests.cs | 84 ++++++++++++++++++ TagCloud.Tests/Utilities/FileUtilities.cs | 19 ++++ 15 files changed, 638 insertions(+) create mode 100644 TagCloud.Tests/OptionsTests/BackgroundColorTests.cs create mode 100644 TagCloud.Tests/OptionsTests/BaseOptionTest.cs create mode 100644 TagCloud.Tests/OptionsTests/DataFileNameTest.cs create mode 100644 TagCloud.Tests/OptionsTests/FontTests.cs create mode 100644 TagCloud.Tests/OptionsTests/ImageFileNameTests.cs create mode 100644 TagCloud.Tests/OptionsTests/ImageSizeTests.cs create mode 100644 TagCloud.Tests/OptionsTests/IsSortedTests.cs create mode 100644 TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs create mode 100644 TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs create mode 100644 TagCloud.Tests/OptionsTests/ResultFormatTests.cs create mode 100644 TagCloud.Tests/OptionsTests/TextColorTests.cs create mode 100644 TagCloud.Tests/OptionsTests/ValidValues.cs create mode 100644 TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs create mode 100644 TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs create mode 100644 TagCloud.Tests/Utilities/FileUtilities.cs diff --git a/TagCloud.Tests/OptionsTests/BackgroundColorTests.cs b/TagCloud.Tests/OptionsTests/BackgroundColorTests.cs new file mode 100644 index 000000000..86324b81f --- /dev/null +++ b/TagCloud.Tests/OptionsTests/BackgroundColorTests.cs @@ -0,0 +1,27 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; + +namespace TagCloud.Tests.OptionsTests +{ + [TestFixture] + internal class BackgroundColorTests() : BaseOptionTest("BackgroundColor") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("abc")] + public void Program_WorksCorrectly_WithInvalidBackgroundColor(string backgroundColor) + { + options.BackgroundColor = backgroundColor; + var expected = Result.Fail($"Неизвестный цвет \"{backgroundColor}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/BaseOptionTest.cs b/TagCloud.Tests/OptionsTests/BaseOptionTest.cs new file mode 100644 index 000000000..8f73e82ea --- /dev/null +++ b/TagCloud.Tests/OptionsTests/BaseOptionTest.cs @@ -0,0 +1,36 @@ +using TagCloud.Tests.Utilities; + +namespace TagCloud.Tests.OptionsTests +{ + internal abstract class BaseOptionTest + { + protected readonly string directoryPath; + protected readonly string dataFile; + protected readonly string imageFile; + protected readonly CommandLineOptions options; + + protected BaseOptionTest(string testName) + { + directoryPath = $"TempFilesFor{testName}Tests"; + dataFile = Path.Combine(directoryPath, "TestData.txt"); + imageFile = Path.Combine(directoryPath, "Test"); + + options = new CommandLineOptions + { + DataFileName = dataFile, + ImageFileName = imageFile, + }; + } + + [OneTimeSetUp] + protected void Init() + => FileUtilities.CreateDataFile( + directoryPath, + dataFile, + ValidValues.ValidDataFileContent); + + [OneTimeTearDown] + protected void OneTimeCleanup() + => FileUtilities.DeleteDirectory(directoryPath); + } +} diff --git a/TagCloud.Tests/OptionsTests/DataFileNameTest.cs b/TagCloud.Tests/OptionsTests/DataFileNameTest.cs new file mode 100644 index 000000000..6658a08e6 --- /dev/null +++ b/TagCloud.Tests/OptionsTests/DataFileNameTest.cs @@ -0,0 +1,87 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; +using TagCloud.Tests.Utilities; + +namespace TagCloud.Tests.OptionsTests +{ + internal class DataFileNameTest() : BaseOptionTest("DataFileName") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("NonExistingFile.txt")] + public void Program_WorksCorrectly_WithNonExistingFilename(string dataFileName) + { + options.DataFileName = dataFileName; + var expected = Result.Fail($"Файл \"{dataFileName}\" не существует"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] + public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine(string dataFileName) + { + var path = Path.Combine(directoryPath, dataFileName); + var invalidContent = new string[] + { + "one", + "two", + "three three three", + "four" + }; + FileUtilities.CreateDataFile(directoryPath, path, invalidContent); + + options.DataFileName = path; + var expected = Result.Fail( + $"Файл \"{path}\" содержит строку с двумя и более словами"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] + public void Program_WorksCorrectly_WithEmptyFile(string dataFileName) + { + var path = Path.Combine(directoryPath, dataFileName); + FileUtilities.CreateDataFile(directoryPath, path, Array.Empty()); + + options.DataFileName = path; + var expected = Result.Fail($"Файл \"{path}\" пустой"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("FileDoc.doc")] + [TestCase("FileImg.png")] + public void Program_WorksCorrectly_WithNonTxtFile(string dataFileName) + { + var path = Path.Combine(directoryPath, dataFileName); + FileUtilities.CreateDataFile(directoryPath, path, Array.Empty()); + + options.DataFileName = path; + var expected = Result.Fail($"Файл \"{path}\" должен иметь расширение \"txt\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/FontTests.cs b/TagCloud.Tests/OptionsTests/FontTests.cs new file mode 100644 index 000000000..3aa38b338 --- /dev/null +++ b/TagCloud.Tests/OptionsTests/FontTests.cs @@ -0,0 +1,26 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; + +namespace TagCloud.Tests.OptionsTests +{ + internal class FontTests() : BaseOptionTest("Font") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("abc")] + public void Program_WorksCorrectly_WithInvalidFont(string font) + { + options.Font = font; + var expected = Result.Fail($"Неизвестный шрифт \"{font}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/ImageFileNameTests.cs b/TagCloud.Tests/OptionsTests/ImageFileNameTests.cs new file mode 100644 index 000000000..98337e29b --- /dev/null +++ b/TagCloud.Tests/OptionsTests/ImageFileNameTests.cs @@ -0,0 +1,26 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; + +namespace TagCloud.Tests.OptionsTests +{ + internal class ImageFileNameTests() : BaseOptionTest("ImageFileName") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + public void Program_WorksCorrectly_WithInvalidImageFileName(string imageFileName) + { + options.ImageFileName = imageFileName; + var expected = Result.Fail( + $"Некорректное имя файла для создания \"{imageFileName}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/ImageSizeTests.cs b/TagCloud.Tests/OptionsTests/ImageSizeTests.cs new file mode 100644 index 000000000..8ddf21677 --- /dev/null +++ b/TagCloud.Tests/OptionsTests/ImageSizeTests.cs @@ -0,0 +1,66 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; + +namespace TagCloud.Tests.OptionsTests +{ + internal class ImageSizeTests() : BaseOptionTest("ImageSize") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("100:")] + [TestCase(":100")] + [TestCase("100:100:100")] + [TestCase("abc")] + public void Program_WorksCorrectly_WithIncorrectFormat(string size) + { + options.ImageSize = size; + var expected = Result.Fail( + $"Некорректный формат размера изображения \"{size}\", используйте формат \"Ширина:Высота\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("abc:100")] + [TestCase("100:abc")] + [TestCase("abc:abc")] + public void Program_WorksCorrectly_WithIncorrectInput(string size) + { + options.ImageSize = size; + var expected = Result.Fail($"Передано не число \"abc\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("0:100")] + [TestCase("-1:100")] + [TestCase("100:0")] + [TestCase("100:-1")] + [TestCase("0:0")] + [TestCase("-1:-1")] + public void Program_WorksCorrectly_WithInputLessThanZero(string size) + { + options.ImageSize = size; + var wrongValue = size.Contains("-1") ? "-1" : "0"; + var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: \"{wrongValue}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/IsSortedTests.cs b/TagCloud.Tests/OptionsTests/IsSortedTests.cs new file mode 100644 index 000000000..6b05a1c4c --- /dev/null +++ b/TagCloud.Tests/OptionsTests/IsSortedTests.cs @@ -0,0 +1,26 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; + +namespace TagCloud.Tests.OptionsTests +{ + internal class IsSortedTests() : BaseOptionTest("IsSorted") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("abc")] + public void Program_WorksCorrectly_WithInvalidIsSorted(string sorted) + { + options.IsSorted = sorted; + var expected = Result.Fail($"Неизвестный параметр сортировки \"{sorted}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs b/TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs new file mode 100644 index 000000000..f8735ccc5 --- /dev/null +++ b/TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs @@ -0,0 +1,42 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; + +namespace TagCloud.Tests.OptionsTests +{ + internal class MaxRectangleHeightTest() : BaseOptionTest("MaxRectangleHeight") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("abc")] + public void Program_WorksCorrectly_WithNotANumber(string maxRectangleHeight) + { + options.MaxRectangleWidth = maxRectangleHeight; + var expected = Result.Fail($"Передано не число \"{maxRectangleHeight}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("0")] + [TestCase("-1")] + public void Program_WorksCorrectly_WithNumberZeroOrLess(string maxRectangleHeight) + { + options.MaxRectangleWidth = maxRectangleHeight; + var expected = Result.Fail( + $"Переданное числовое значение должно быть больше 0: \"{maxRectangleHeight}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs b/TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs new file mode 100644 index 000000000..f62988839 --- /dev/null +++ b/TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs @@ -0,0 +1,42 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; + +namespace TagCloud.Tests.OptionsTests +{ + internal class MaxRectangleWidthTest() : BaseOptionTest("MaxRectangleWidth") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("abc")] + public void Program_WorksCorrectly_WithNotANumber(string maxRectangleWidth) + { + options.MaxRectangleWidth = maxRectangleWidth; + var expected = Result.Fail($"Передано не число \"{maxRectangleWidth}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("0")] + [TestCase("-1")] + public void Program_WorksCorrectly_WithNumberZeroOrLess(string maxRectangleWidth) + { + options.MaxRectangleWidth = maxRectangleWidth; + var expected = Result.Fail( + $"Переданное числовое значение должно быть больше 0: \"{maxRectangleWidth}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/ResultFormatTests.cs b/TagCloud.Tests/OptionsTests/ResultFormatTests.cs new file mode 100644 index 000000000..8cb08c844 --- /dev/null +++ b/TagCloud.Tests/OptionsTests/ResultFormatTests.cs @@ -0,0 +1,26 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; + +namespace TagCloud.Tests.OptionsTests +{ + internal class ResultFormatTests() : BaseOptionTest("ResultFormat") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("abc")] + public void Program_WorksCorrectly_WithInvalidResultFormat(string resultFormat) + { + options.ResultFormat = resultFormat; + var expected = Result.Fail($"Формат \"{resultFormat}\" не поддерживается"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/TextColorTests.cs b/TagCloud.Tests/OptionsTests/TextColorTests.cs new file mode 100644 index 000000000..d1af34b2d --- /dev/null +++ b/TagCloud.Tests/OptionsTests/TextColorTests.cs @@ -0,0 +1,29 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; + +namespace TagCloud.Tests.OptionsTests +{ + [TestFixture] + internal class TextColorTests() : BaseOptionTest("TextColor") + { + [TestCase("")] + [TestCase(" ")] + [TestCase(null!)] + [TestCase("abc")] + public void Program_WorksCorrectly_WithInvalidTextColor(string textColor) + { + options.TextColor = textColor; + var expected = Result.Fail($"Неизвестный цвет \"{textColor}\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + + var result = executor.Execute(); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/ValidValues.cs b/TagCloud.Tests/OptionsTests/ValidValues.cs new file mode 100644 index 000000000..87562b11c --- /dev/null +++ b/TagCloud.Tests/OptionsTests/ValidValues.cs @@ -0,0 +1,17 @@ +namespace TagCloud.Tests.OptionsTests +{ + internal static class ValidValues + { + public readonly static string[] ValidDataFileContent = new string[] + { + "One", + "One", + "Two", + "Three", + "Four", + "Four", + "Four", + "Four" + }; + } +} diff --git a/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs b/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs new file mode 100644 index 000000000..8069717c6 --- /dev/null +++ b/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs @@ -0,0 +1,85 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; +using TagCloud.Tests.Utilities; + +namespace TagCloud.Tests.OptionsTests +{ + internal class WordsToExcludeFileNameTests() : BaseOptionTest("WordsToExcludeFileName") + { + [TestCase("NonExistingFile.txt")] + public void Program_WorksCorrectly_WithNonExistingFilename(string wordsToExcludeFileName) + { + options.WordsToExcludeFileName = wordsToExcludeFileName; + var expected = Result.Fail($"Файл \"{wordsToExcludeFileName}\" не существует"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] + public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine( + string wordsToExcludeFileName) + { + var path = Path.Combine(directoryPath, wordsToExcludeFileName); + var invalidContent = new string[] + { + "one", + "two", + "three three three", + "four" + }; + FileUtilities.CreateDataFile(directoryPath, path, invalidContent); + + options.WordsToExcludeFileName = path; + var expected = Result.Fail( + $"Файл \"{path}\" содержит строку с двумя и более словами"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] + public void Program_WorksCorrectly_WithEmptyFile(string wordsToExcludeFileName) + { + var path = Path.Combine(directoryPath, wordsToExcludeFileName); + FileUtilities.CreateDataFile(directoryPath, path, Array.Empty()); + + options.WordsToExcludeFileName = path; + var expected = Result.Fail($"Файл \"{path}\" пустой"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("FileDoc.doc")] + [TestCase("FileImg.png")] + public void Program_WorksCorrectly_WithNonTxtFile(string wordsToExcludeFileName) + { + var path = Path.Combine(directoryPath, wordsToExcludeFileName); + FileUtilities.CreateDataFile(directoryPath, path, Array.Empty()); + + options.WordsToExcludeFileName = path; + var expected = Result.Fail($"Файл \"{path}\" должен иметь расширение \"txt\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs b/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs new file mode 100644 index 000000000..8b1c83e74 --- /dev/null +++ b/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs @@ -0,0 +1,84 @@ +using Autofac; +using FileSenderRailway; +using FluentAssertions; +using TagCloud.Tests.Utilities; + +namespace TagCloud.Tests.OptionsTests +{ + internal class WordsToIncludeFileNameTests() : BaseOptionTest("WordsToIncludeFileName") + { + [TestCase("NonExistingFile.txt")] + public void Program_WorksCorrectly_WithNonExistingFilename(string wordsToIncludeFileName) + { + options.WordsToIncludeFileName = wordsToIncludeFileName; + var expected = Result.Fail($"Файл \"{wordsToIncludeFileName}\" не существует"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] + public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine( + string wordsToIncludeFileName) + { + var path = Path.Combine(directoryPath, wordsToIncludeFileName); + var invalidContent = new string[] + { + "one", + "two", + "three three three", + "four" + }; + FileUtilities.CreateDataFile(directoryPath, path, invalidContent); + + options.WordsToIncludeFileName = path; + var expected = Result.Fail($"Файл \"{path}\" содержит строку с двумя и более словами"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] + public void Program_WorksCorrectly_WithEmptyFile(string wordsToIncludeFileName) + { + var path = Path.Combine(directoryPath, wordsToIncludeFileName); + FileUtilities.CreateDataFile(directoryPath, path, Array.Empty()); + + options.WordsToIncludeFileName = path; + var expected = Result.Fail($"Файл \"{path}\" пустой"); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("FileDoc.doc")] + [TestCase("FileImg.png")] + public void Program_WorksCorrectly_WithNonTxtFile(string wordsToIncludeFileName) + { + var path = Path.Combine(directoryPath, wordsToIncludeFileName); + FileUtilities.CreateDataFile(directoryPath, path, Array.Empty()); + + options.WordsToIncludeFileName = path; + var expected = Result.Fail($"Файл \"{path}\" должен иметь расширение \"txt\""); + + var container = DIContainer.ConfigureContainer(options); + using var scope = container.BeginLifetimeScope(); + var executor = scope.Resolve(); + var actual = executor.Execute(); + + actual.Should().BeEquivalentTo(expected); + } + } +} diff --git a/TagCloud.Tests/Utilities/FileUtilities.cs b/TagCloud.Tests/Utilities/FileUtilities.cs new file mode 100644 index 000000000..04c9f195c --- /dev/null +++ b/TagCloud.Tests/Utilities/FileUtilities.cs @@ -0,0 +1,19 @@ +namespace TagCloud.Tests.Utilities +{ + internal static class FileUtilities + { + public static void CreateDataFile(string directoryPath, string dataFile, string[] content) + { + Directory.CreateDirectory(directoryPath); + File.WriteAllLines(dataFile, content); + } + + public static void DeleteDirectory(string directoryPath) + { + if (Directory.Exists(directoryPath)) + { + Directory.Delete(directoryPath, true); + } + } + } +} From 3d8eeec4dbf85e8907d14ed93dfe91f578bd3927 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Thu, 16 Jan 2025 20:05:27 +0500 Subject: [PATCH 19/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D1=82=D0=B5=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPainterTest.cs | 5 +- ...rcularCloudLayouterMainRequirementsTest.cs | 7 +- .../CircularCloudLayouterTest.cs | 3 +- ...edFrequencyBasedCloudLayouterWorkerTest.cs | 5 +- .../RandomCloudLayouterWorkerTest.cs | 4 +- TagCloud.Tests/GlobalSuppressions.cs | 13 --- .../ImageSaversTests/ImageSaverTest.cs | 11 ++- TagCloud.Tests/MainTest.cs | 78 ++++++++-------- .../NormalizersTest/NormalizerTest.cs | 19 ++-- TagCloud.Tests/ParsersTests/BoolParserTest.cs | 12 +-- .../ParsersTests/ColorParserTest.cs | 19 ++-- TagCloud.Tests/ParsersTests/FontParserTest.cs | 19 ++-- TagCloud.Tests/ParsersTests/SizeParserTest.cs | 55 +++++------- .../WordReadersTests/WordReaderTest.cs | 89 ++++++++++++------- 14 files changed, 167 insertions(+), 172 deletions(-) delete mode 100644 TagCloud.Tests/GlobalSuppressions.cs diff --git a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs index b9eb7dd2a..ce9b7c777 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs @@ -5,6 +5,7 @@ namespace TagCloud.Tests.CloudLayouterPaintersTest { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class CloudLayouterPainterTest { private CloudLayouterPainter painter; @@ -33,10 +34,10 @@ public void Draw_ThrowsException_WithTagsAsNull() } [Test] - public void Draw_ThrowsException_WithTooSmallTiFitImage() + public void Draw_ThrowsException_WithTooSmallToFitImage() { var expected = Result - .Fail("Все прямоугольники не помещаются на изображение. Измените его размеры"); + .Fail("Все прямоугольники не помещаются на изображение"); var actual = painter .Draw( new Tag[] diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs index f6f860065..9f71c84ce 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs @@ -9,6 +9,7 @@ namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests { [TestFixture] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class CircularCloudLayouterMainRequirementsTest { private Point center = new Point(); @@ -49,7 +50,8 @@ public void SetUp() tags.Add( new Tag( rectangleProperty.word, - circularCloudLayouter.PutNextRectangle(rectangleProperty.size).GetValueOrThrow())); + circularCloudLayouter.PutNextRectangle(rectangleProperty.size) + .GetValueOrThrow())); } rectangles = tags.Select(x => x.Rectangle).ToArray(); } @@ -91,7 +93,8 @@ public void ShouldPlaceRectanglesWithoutOverlap() for (var j = i + 1; j < rectangles.Length; j++) { Assert.That( - rectangles[i].IntersectsWith(rectangles[j]) == false, + rectangles[i].IntersectsWith(rectangles[j]), + Is.EqualTo(false), $"Прямоугольники пересекаются:\n" + $"{rectangles[i].ToString()}\n" + $"{rectangles[j].ToString()}"); diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs index a8e35630e..cfe5b111f 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs @@ -17,7 +17,8 @@ public void PutNextRectangle_ThrowsException_OnAnyNegativeOrZeroSize( int height) { var size = new Size(width, height); - var expected = Result.Fail("Размеры прямоугольника не могут быть меньше либо равны нуля"); + var expected = Result.Fail( + "Размеры прямоугольника не могут быть меньше либо равны нуля"); var actual = new CircularCloudLayouter().PutNextRectangle(size); actual.Should().BeEquivalentTo(expected); } diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs index d9e0abd7c..41941bb16 100644 --- a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs +++ b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs @@ -26,7 +26,7 @@ public void GetNextRectangleSize_ThrowsException_OnAnyNegativeOrZeroSize( { var expected = Result .Fail> - ($"Переданное числовое значение должно быть больше 0: {(width <= 0 ? width : height)}"); + ($"Переданное числовое значение должно быть больше 0: \"{(width <= 0 ? width : height)}\""); var worker = new NormalizedFrequencyBasedCloudLayouterWorker( width, @@ -44,7 +44,8 @@ public void GetNextRectangleSize_WorksCorrectly(int width, int height, bool isSo string[]? keys = null; if (isSortedOrder) { - keys = normalizedValues.OrderByDescending(x => x.Value).Select(x => x.Key).ToArray(); + keys = normalizedValues + .OrderByDescending(x => x.Value).Select(x => x.Key).ToArray(); } else { diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs index bb7b6f787..37e2b3f3e 100644 --- a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs +++ b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs @@ -18,7 +18,7 @@ public void GetNextRectangleSize_ThrowsException_OnAnyNegativeOrZeroSize( { var expected = Result .Fail> - ($"Переданное числовое значение должно быть больше 0: {(width <= 0 ? width : height)}"); + ($"Переданное числовое значение должно быть больше 0: \"{(width <= 0 ? width : height)}\""); var worker = new RandomCloudLayouterWorker(width, width, height, height); var actual = worker.GetNextRectangleProperties(); @@ -27,7 +27,7 @@ public void GetNextRectangleSize_ThrowsException_OnAnyNegativeOrZeroSize( [TestCase(50, 25, 25, 50)] [TestCase(25, 50, 50, 25)] - public void GetNextRectangleSize_ThrowsArgumentException_OnNonConsecutiveSizeValues( + public void GetNextRectangleSize_ThrowsException_OnNonConsecutiveSizeValues( int minWidth, int maxWidth, int minHeight, diff --git a/TagCloud.Tests/GlobalSuppressions.cs b/TagCloud.Tests/GlobalSuppressions.cs deleted file mode 100644 index b14f1e7d3..000000000 --- a/TagCloud.Tests/GlobalSuppressions.cs +++ /dev/null @@ -1,13 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -// Так делать явно плохо -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.ImageSaversTests.ImageSaverTest.SaveFile_SavesFile(System.String,System.String)~System.Boolean")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.ImageSaversTests.ImageSaverTest.SaveFile_ThrowsArgumentException_WithInvalidFilename(System.String)")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests.CircularCloudLayouterMainRequirementsTest.Cleanup")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.CloudLayouterPaintersTest.CloudLayouterPainterTest.Draw_ThrowsArgumentException_WithEmptyTags")] -[assembly: SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>", Scope = "member", Target = "~M:TagCloud.Tests.CloudLayouterPaintersTest.CloudLayouterPainterTest.Draw_ThrowsArgumentNullException_WithTagsAsNull")] diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs index 6a5e4275a..d21c2b141 100644 --- a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs @@ -2,10 +2,12 @@ using FluentAssertions; using System.Drawing; using TagCloud.ImageSavers; +using TagCloud.Tests.Utilities; namespace TagCloud.Tests.ImageSaversTests { [TestFixture] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class ImageSaverTest { private readonly string directoryPath = "TempFilesForImageSaverTests"; @@ -32,13 +34,13 @@ public void SaveFile_ThrowsException_WithNullBitmap(string filename) actual.Should().BeEquivalentTo(expected); } - [TestCase(null)] [TestCase("")] [TestCase(" ")] + [TestCase(null)] public void SaveFile_ThrowsException_WithInvalidFilename(string? filename) { var dummyImage = new Bitmap(1, 1); - var expected = Result.Fail("Некорректное имя файла для создания"); + var expected = Result.Fail($"Некорректное имя файла для создания \"{filename}\""); var actual = imageSaver.SaveFile(dummyImage, filename!); actual.Should().BeEquivalentTo(expected); } @@ -75,10 +77,7 @@ public bool SaveFile_SavesFile(string filename, string format) [OneTimeTearDown] public void OneTimeCleanup() { - if (Directory.Exists(directoryPath)) - { - Directory.Delete(directoryPath, true); - } + FileUtilities.DeleteDirectory(directoryPath); } } } diff --git a/TagCloud.Tests/MainTest.cs b/TagCloud.Tests/MainTest.cs index 8673add37..76acb3f68 100644 --- a/TagCloud.Tests/MainTest.cs +++ b/TagCloud.Tests/MainTest.cs @@ -1,64 +1,60 @@ using Autofac; +using FileSenderRailway; using FluentAssertions; +using TagCloud.Tests.OptionsTests; +using TagCloud.Tests.Utilities; namespace TagCloud.Tests { [TestFixture] - internal class MainTest + internal class MainTest() : BaseOptionTest("MainTest") { - private static string directoryPath = "TempFilesForMainTest"; - private static readonly string dataFile = Path.Combine(directoryPath, "TestData.txt"); - private readonly string imageFile = Path.Combine(directoryPath, "Test"); - - [OneTimeSetUp] - public void Init() + [Test] + public void Program_ExecutesSuccessfully_WithValidArguments() { - Directory.CreateDirectory(directoryPath); - File.WriteAllLines(dataFile, new string[] - { - "One", - "One", - "Two", - "Three", - "Four", - "Four", - "Four", - "Four" - }); - } + var expected = Result.Ok(); + + var wordsToIncludePath = Path.Combine(directoryPath, "ToInclude.txt"); + FileUtilities.CreateDataFile( + directoryPath, + wordsToIncludePath, + new string[] + { + "snow", + "white" + }); + + var wordsToExcludePath = Path.Combine(directoryPath, "ToExclude.txt"); + FileUtilities.CreateDataFile( + directoryPath, + wordsToExcludePath, + new string[] + { + "the" + }); - [TestCase("bmp")] - public void Program_ExecutesSuccessfully_WithValidArguments(string format) - { var options = new CommandLineOptions { - BackgroundColor = "Red", - TextColor = "Blue", + BackgroundColor = "Black", + TextColor = "Yellow", Font = "Calibri", - IsSorted = true.ToString(), + IsSorted = Boolean.FalseString, ImageSize = "1000:1000", - MaxRectangleHeight = 100, - MaxRectangleWidth = 200, + MaxRectangleHeight = "100", + MaxRectangleWidth = "200", ImageFileName = imageFile, DataFileName = dataFile, - ResultFormat = format + ResultFormat = "bmp", + WordsToIncludeFileName = wordsToIncludePath, + WordsToExcludeFileName = wordsToExcludePath, }; var container = DIContainer.ConfigureContainer(options); using var scope = container.BeginLifetimeScope(); var executor = scope.Resolve(); + var actual = executor.Execute(); - Assert.DoesNotThrow(() => executor.Execute()); - - File.Exists($"{imageFile}.{format}").Should().BeTrue(); - } - - [OneTimeTearDown] - public void OneTimeCleanup() - { - if (Directory.Exists(directoryPath)) - { - Directory.Delete(directoryPath, true); - } + actual.Should().BeEquivalentTo(expected); + File.Exists($"{imageFile}.{options.ResultFormat}").Should().BeTrue(); } } } diff --git a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs index 2db54f7b8..2fecc4fae 100644 --- a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs +++ b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs @@ -2,7 +2,7 @@ using FluentAssertions; using TagCloud.Normalizers; -namespace TagCloud.Tests.NormalaizersTest +namespace TagCloud.Tests.NormalizersTest { [TestFixture] internal class NormalizerTest @@ -31,7 +31,8 @@ internal class NormalizerTest public void Normalize_ThrowsException_WithInvalidMinCoefficient( double minCoefficient) { - var expected = Result.Fail>("Минимальный коэффициент нормализации должен быть в диапазоне от 0 до 1"); + var expected = Result.Fail>( + "Минимальный коэффициент нормализации должен быть в диапазоне от 0 до 1"); var actual = normalizer.Normalize(values, minCoefficient, defaultDecimalPlaces); actual.Should().BeEquivalentTo(expected); } @@ -39,15 +40,18 @@ public void Normalize_ThrowsException_WithInvalidMinCoefficient( [Test] public void Normalize_ThrowsException_WithEmptyValues() { - var expected = Result.Fail>("Словарь значений не может быть пустым"); - var actual = normalizer.Normalize(new Dictionary(), defaultMinCoefficient, defaultDecimalPlaces); + var expected = Result.Fail>( + "Словарь значений не может быть пустым"); + var actual = normalizer.Normalize( + new Dictionary(), defaultMinCoefficient, defaultDecimalPlaces); actual.Should().BeEquivalentTo(expected); } [Test] public void Normalize_ThrowsException_WithValuesAsNull() { - var expected = Result.Fail>("Словарь значений не может быть пустым"); + var expected = Result.Fail>( + "Словарь значений не может быть пустым"); var actual = normalizer.Normalize(null!, defaultMinCoefficient, defaultDecimalPlaces); actual.Should().BeEquivalentTo(expected); } @@ -56,7 +60,8 @@ public void Normalize_ThrowsException_WithValuesAsNull() public void Normalize_ThrowsException_WithInvalidDecimalPlaces( int decimalPlaces) { - var expected = Result.Fail>("Количество знаков после запятой не может быть отрицательным"); + var expected = Result.Fail>( + "Количество знаков после запятой не может быть отрицательным"); var actual = normalizer.Normalize(values, defaultMinCoefficient, decimalPlaces); actual.Should().BeEquivalentTo(expected); } @@ -72,8 +77,6 @@ public void Normalize_CalculatesСorrectly(double minCoefficient, int decimalPla } var expected = dict.AsResult(); var actual = normalizer.Normalize(values, minCoefficient, decimalPlaces); - actual.IsSuccess.Should().Be(expected.IsSuccess); - actual.Error.Should().Be(expected.Error); actual.Value.Should().BeEquivalentTo(expected.Value); } } diff --git a/TagCloud.Tests/ParsersTests/BoolParserTest.cs b/TagCloud.Tests/ParsersTests/BoolParserTest.cs index eb6a41bc0..a91af3710 100644 --- a/TagCloud.Tests/ParsersTests/BoolParserTest.cs +++ b/TagCloud.Tests/ParsersTests/BoolParserTest.cs @@ -11,19 +11,19 @@ internal class BoolParserTest [TestCase(" ")] [TestCase(null!)] [TestCase("abc")] - public void BoolParser_ThrowsException_WithInvalidInput(string value) + public void BoolParser_ThrowsException_WithInvalidInput(string sorted) { - var expected = Result.Fail($"Неизвестный параметр сортировки {value}"); - var actual = BoolParser.ParseIsSorted(value); + var expected = Result.Fail($"Неизвестный параметр сортировки \"{sorted}\""); + var actual = BoolParser.ParseIsSorted(sorted); actual.Should().BeEquivalentTo(expected); } [TestCase("True")] [TestCase("False")] - public void BoolParser_WorksCorrectly(string value) + public void BoolParser_WorksCorrectly(string sorted) { - var expected = Convert.ToBoolean(value); - var actual = BoolParser.ParseIsSorted(value).GetValueOrThrow(); + var expected = Convert.ToBoolean(sorted); + var actual = BoolParser.ParseIsSorted(sorted).GetValueOrThrow(); actual.Should().Be(expected); } } diff --git a/TagCloud.Tests/ParsersTests/ColorParserTest.cs b/TagCloud.Tests/ParsersTests/ColorParserTest.cs index f814f554a..956d90e0e 100644 --- a/TagCloud.Tests/ParsersTests/ColorParserTest.cs +++ b/TagCloud.Tests/ParsersTests/ColorParserTest.cs @@ -11,18 +11,11 @@ internal class ColorParserTest [TestCase("")] [TestCase(" ")] [TestCase(null!)] - public void ColorParser_ThrowsException_WithInvalidInput(string value) - { - var expected = Result.Fail($"Некорректная строка {value}"); - var actual = ColorParser.ParseColor(value); - actual.Should().BeEquivalentTo(expected); - } - [TestCase("abc")] - public void ColorParser_ThrowsException_WithUnknownColor(string value) + public void ColorParser_ThrowsException_WithInvalidColor(string color) { - var expected = Result.Fail($"Неизвестный цвет {value}"); - var actual = ColorParser.ParseColor(value); + var expected = Result.Fail($"Неизвестный цвет \"{color}\""); + var actual = ColorParser.ParseColor(color); actual.Should().BeEquivalentTo(expected); } @@ -32,10 +25,10 @@ public void ColorParser_ThrowsException_WithUnknownColor(string value) [TestCase("black")] [TestCase("green")] [TestCase("yellow")] - public void ColorParser_WorksCorrectly(string value) + public void ColorParser_WorksCorrectly(string color) { - var expected = Color.FromName(value); - var actual = ColorParser.ParseColor(value).GetValueOrThrow(); + var expected = Color.FromName(color); + var actual = ColorParser.ParseColor(color).GetValueOrThrow(); actual.Should().Be(expected); } } diff --git a/TagCloud.Tests/ParsersTests/FontParserTest.cs b/TagCloud.Tests/ParsersTests/FontParserTest.cs index 33755e4ca..7b28f44f3 100644 --- a/TagCloud.Tests/ParsersTests/FontParserTest.cs +++ b/TagCloud.Tests/ParsersTests/FontParserTest.cs @@ -11,18 +11,11 @@ internal class FontParserTest [TestCase("")] [TestCase(" ")] [TestCase(null!)] - public void FontParser_ThrowsException_WithInvalidInput(string value) - { - var expected = Result.Fail($"Некорректная строка {value}"); - var actual = FontParser.ParseFont(value); - actual.Should().BeEquivalentTo(expected); - } - [TestCase("abc")] - public void FontParser_ThrowsException_WithUnknownColor(string value) + public void FontParser_ThrowsException_WithInvalidFont(string font) { - var expected = Result.Fail($"Неизвестный шрифт {value}"); - var actual = FontParser.ParseFont(value); + var expected = Result.Fail($"Неизвестный шрифт \"{font}\""); + var actual = FontParser.ParseFont(font); actual.Should().BeEquivalentTo(expected); } @@ -30,10 +23,10 @@ public void FontParser_ThrowsException_WithUnknownColor(string value) [TestCase("Times New Roman")] [TestCase("Georgia")] [TestCase("Verdana")] - public void FontParser_WorksCorrectly(string value) + public void FontParser_WorksCorrectly(string font) { - var expected = new FontFamily(value); - var actual = FontParser.ParseFont(value).GetValueOrThrow(); + var expected = new FontFamily(font); + var actual = FontParser.ParseFont(font).GetValueOrThrow(); actual.Should().BeEquivalentTo(expected); } } diff --git a/TagCloud.Tests/ParsersTests/SizeParserTest.cs b/TagCloud.Tests/ParsersTests/SizeParserTest.cs index 615ba8c19..9acac8c41 100644 --- a/TagCloud.Tests/ParsersTests/SizeParserTest.cs +++ b/TagCloud.Tests/ParsersTests/SizeParserTest.cs @@ -11,77 +11,68 @@ internal class SizeParserTest [TestCase("")] [TestCase(" ")] [TestCase(null!)] - public void ParseImageSize_ThrowsException_WithInvalidInput(string value) - { - var expected = Result.Fail($"Некорректная строка {value}"); - var actual = SizeParser.ParseImageSize(value); - actual.Should().BeEquivalentTo(expected); - } - [TestCase("100:")] [TestCase(":100")] [TestCase("100:100:100")] [TestCase("abc")] - public void ParseImageSize_ThrowsException_WithIncorrectFormat(string value) + public void ParseImageSize_ThrowsException_WithIncorrectFormat(string size) { - var expected = Result.Fail($"Некорректный формат размера изображения: \"{value}\", используйте формат \"Ширина:Высота\", например 5000:5000"); - var actual = SizeParser.ParseImageSize(value); + var expected = Result.Fail( + $"Некорректный формат размера изображения \"{size}\", используйте формат \"Ширина:Высота\""); + var actual = SizeParser.ParseImageSize(size); actual.Should().BeEquivalentTo(expected); } [TestCase("abc:100")] [TestCase("100:abc")] [TestCase("abc:abc")] - public void ParseImageSize_ThrowsException_WithIncorrectInput(string value) + public void ParseImageSize_ThrowsException_WithIncorrectInput(string size) { - var expected = Result.Fail($"Передано не число: abc"); - var actual = SizeParser.ParseImageSize(value); + var expected = Result.Fail($"Передано не число \"abc\""); + var actual = SizeParser.ParseImageSize(size); actual.Should().BeEquivalentTo(expected); } [TestCase("0:100")] + [TestCase("-1:100")] [TestCase("100:0")] + [TestCase("100:-1")] [TestCase("0:0")] - public void ParseImageSize_ThrowsException_WithInputLessThanZero(string value) + [TestCase("-1:-1")] + public void ParseImageSize_ThrowsException_WithInputLessThanZero(string size) { - var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: 0"); - var actual = SizeParser.ParseImageSize(value); + var wrongValue = size.Contains("-1") ? "-1" : "0"; + var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: \"{wrongValue}\""); + var actual = SizeParser.ParseImageSize(size); actual.Should().BeEquivalentTo(expected); } [TestCase("")] [TestCase(" ")] [TestCase(null!)] - public void ParseSizeDimension_ThrowsException_WithInvalidInputAsString(string value) - { - var expected = Result.Fail($"Некорректная строка {value}"); - var actual = SizeParser.ParseSizeDimension(value); - actual.Should().BeEquivalentTo(expected); - } - [TestCase("abc")] - public void ParseSizeDimension_ThrowsException_WithIncorrectInputAsString(string value) + public void ParseSizeDimension_ThrowsException_WithIncorrectInputAsString(string size) { - var expected = Result.Fail($"Передано не число: {value}"); - var actual = SizeParser.ParseSizeDimension(value); + var expected = Result.Fail($"Передано не число \"{size}\""); + var actual = SizeParser.ParseSizeDimension(size); actual.Should().BeEquivalentTo(expected); } [TestCase("0")] [TestCase("-1")] - public void ParseSizeDimension_ThrowsException_WithInputLessThanZeroAsString(string value) + public void ParseSizeDimension_ThrowsException_WithInputLessThanZeroAsString(string size) { - var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: {value}"); - var actual = SizeParser.ParseSizeDimension(value); + var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: \"{size}\""); + var actual = SizeParser.ParseSizeDimension(size); actual.Should().BeEquivalentTo(expected); } [TestCase("0")] [TestCase("-1")] - public void ParseSizeDimension_ThrowsException_WithInputLessThanZeroAsInt(int value) + public void ParseSizeDimension_ThrowsException_WithInputLessThanZeroAsInt(int size) { - var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: {value}"); - var actual = SizeParser.ParseSizeDimension(value); + var expected = Result.Fail($"Переданное числовое значение должно быть больше 0: \"{size}\""); + var actual = SizeParser.ParseSizeDimension(size); actual.Should().BeEquivalentTo(expected); } } diff --git a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs index 86a834ee1..0fc78a900 100644 --- a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs +++ b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs @@ -1,5 +1,7 @@ using FileSenderRailway; using FluentAssertions; +using TagCloud.Tests.OptionsTests; +using TagCloud.Tests.Utilities; using TagCloud.WordReaders; namespace TagCloud.Tests.WordReadersTests @@ -9,21 +11,11 @@ internal class WordReaderTest { private readonly string directoryPath = "TempFilesForWordReaderTests"; - private readonly string fileWithCorrectValuesPath = "correctFile.txt"; - private readonly string[] correctValues = new string[] - { - "One", - "One", - "Two", - "Three", - "Four", - "Four", - "Four", - "Four" - }; + private readonly string fileWithCorrectValuesPath = "CorrectFile.txt"; - private readonly string fileWithIncorrectValuesPath = "incorrectFile.txt"; - private readonly string[] incorrectValues = new string[] + private readonly string fileWithMoreThanOneWordInLinePath + = "InvalidFile_MoreThanOneWordInLine.txt"; + private readonly string[] moreThanOneWordInLineValues = new string[] { "One", "Two", @@ -31,22 +23,30 @@ internal class WordReaderTest "Four" }; + private readonly string fileEmptyPath = "InvalidFile_Empty.txt"; + private WordReader wordReader; [OneTimeSetUp] public void Init() { - Directory.CreateDirectory(directoryPath); - File.WriteAllLines( - Path.Combine( + FileUtilities + .CreateDataFile( directoryPath, - fileWithCorrectValuesPath), - correctValues); - File.WriteAllLines - (Path.Combine( + Path.Combine(directoryPath, fileWithCorrectValuesPath), + ValidValues.ValidDataFileContent); + + FileUtilities + .CreateDataFile( + directoryPath, + Path.Combine(directoryPath, fileWithMoreThanOneWordInLinePath), + moreThanOneWordInLineValues); + + FileUtilities + .CreateDataFile( directoryPath, - fileWithIncorrectValuesPath), - incorrectValues); + Path.Combine(directoryPath, fileEmptyPath), + Array.Empty()); } [SetUp] @@ -55,23 +55,50 @@ public void SetUp() wordReader = new WordReader(); } + [TestCase("")] [TestCase(" ")] - [TestCase("ThisFileDoesNotExist.txt")] + [TestCase("NonExistingFile.txt")] public void WordReader_ThrowsFileNotFoundException_WithInvalidFilename(string filename) { var path = Path.Combine(directoryPath, filename); - var expected = Result.Fail($"Файл {path} не существует"); - var actual = wordReader.ReadByLines(path).ToArray(); - actual.Should().Contain(expected).And.HaveCount(1); + var expected = Result.Fail>($"Файл \"{path}\" не существует"); + var actual = wordReader.ReadByLines(path); + actual.Should().BeEquivalentTo(expected); } [Test] public void WordReader_ThrowsException_WithTwoWordsInOneLine() { - var path = Path.Combine(directoryPath, fileWithIncorrectValuesPath); - var expected = Result.Fail($"Файл {path} содержит строку с двумя и более словами"); - var actual = wordReader.ReadByLines(path).ToArray(); - actual.Should().Contain(expected); + var path = Path.Combine(directoryPath, fileWithMoreThanOneWordInLinePath); + var expected = Result.Fail>($"Файл \"{path}\" содержит строку с двумя и более словами"); + var actual = wordReader.ReadByLines(path); + actual.Should().BeEquivalentTo(expected); + } + + [Test] + public void WordReader_ThrowsException_WithEmpty() + { + var path = Path.Combine(directoryPath, fileEmptyPath); + var expected = Result.Fail>($"Файл \"{path}\" пустой"); + var actual = wordReader.ReadByLines(path); + actual.Should().BeEquivalentTo(expected); + } + + [TestCase("FileDoc.doc")] + [TestCase("FileImg.png")] + public void WordReader_ThrowsException_WithNonTxt(string filename) + { + FileUtilities + .CreateDataFile( + directoryPath, + Path.Combine(directoryPath, filename), + Array.Empty()); + + var path = Path.Combine(directoryPath, filename); + var expected = Result.Fail>( + $"Файл \"{path}\" должен иметь расширение \"txt\""); + var actual = wordReader.ReadByLines(path); + actual.Should().BeEquivalentTo(expected); } [OneTimeTearDown] From dd9a5cdfb29ee3159e79a0e8a0bb29892d171caf Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 22:09:40 +0500 Subject: [PATCH 20/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BE=D0=B1=20=D0=BE=D0=B3=D1=80?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=87=D0=B5=D0=BD=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B8?= =?UTF-8?q?=20Bitmap=20=D0=BD=D0=B0=20=D1=80=D0=B0=D0=B7=D0=BD=D1=8B=D1=85?= =?UTF-8?q?=20=D0=BF=D0=BB=D0=B0=D1=82=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=85?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPaintersTest/CloudLayouterPainterTest.cs | 3 ++- .../CircularCloudLayouterMainRequirementsTest.cs | 3 ++- TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs | 3 ++- TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs | 3 ++- TagCloud/ImageSavers/ImageSaver.cs | 3 ++- TagCloud/Parsers/FontParser.cs | 3 ++- TagCloud/ProgramExecutor.cs | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs index ce9b7c777..d3684c867 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs @@ -1,11 +1,12 @@ using FileSenderRailway; using FluentAssertions; +using System.Diagnostics.CodeAnalysis; using System.Drawing; using TagCloud.CloudLayouterPainters; namespace TagCloud.Tests.CloudLayouterPaintersTest { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class CloudLayouterPainterTest { private CloudLayouterPainter painter; diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs index 9f71c84ce..cd742e204 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using System.Diagnostics.CodeAnalysis; using System.Drawing; using TagCloud.CloudLayouterPainters; using TagCloud.CloudLayouters.CircularCloudLayouter; @@ -9,7 +10,7 @@ namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests { [TestFixture] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class CircularCloudLayouterMainRequirementsTest { private Point center = new Point(); diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs index d21c2b141..ddef822ae 100644 --- a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs @@ -1,5 +1,6 @@ using FileSenderRailway; using FluentAssertions; +using System.Diagnostics.CodeAnalysis; using System.Drawing; using TagCloud.ImageSavers; using TagCloud.Tests.Utilities; @@ -7,7 +8,7 @@ namespace TagCloud.Tests.ImageSaversTests { [TestFixture] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class ImageSaverTest { private readonly string directoryPath = "TempFilesForImageSaverTests"; diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs index 11c8d56ae..32dac3a19 100644 --- a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -1,9 +1,10 @@ using FileSenderRailway; +using System.Diagnostics.CodeAnalysis; using System.Drawing; namespace TagCloud.CloudLayouterPainters { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class CloudLayouterPainter( Size imageSize, Color? backgroundColor = null, diff --git a/TagCloud/ImageSavers/ImageSaver.cs b/TagCloud/ImageSavers/ImageSaver.cs index e9aeb6581..40e47a8fc 100644 --- a/TagCloud/ImageSavers/ImageSaver.cs +++ b/TagCloud/ImageSavers/ImageSaver.cs @@ -1,4 +1,5 @@ using FileSenderRailway; +using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Drawing.Imaging; @@ -7,7 +8,7 @@ namespace TagCloud.ImageSavers // Реализован пункт на перспективу: // Формат результата. // Поддерживать разные форматы изображений. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class ImageSaver : IImageSaver { private readonly Dictionary supportedFormats = diff --git a/TagCloud/Parsers/FontParser.cs b/TagCloud/Parsers/FontParser.cs index 8d2bdf9cd..1c3af504d 100644 --- a/TagCloud/Parsers/FontParser.cs +++ b/TagCloud/Parsers/FontParser.cs @@ -1,9 +1,10 @@ using FileSenderRailway; +using System.Diagnostics.CodeAnalysis; using System.Drawing; namespace TagCloud.Parsers { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal static class FontParser { public static Result ParseFont(string font) diff --git a/TagCloud/ProgramExecutor.cs b/TagCloud/ProgramExecutor.cs index 8ac507ba3..a29d62ff1 100644 --- a/TagCloud/ProgramExecutor.cs +++ b/TagCloud/ProgramExecutor.cs @@ -5,10 +5,11 @@ using TagCloud.Factories; using TagCloud.Parsers; using System.Drawing; +using System.Diagnostics.CodeAnalysis; namespace TagCloud { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class ProgramExecutor( string backgroundColor, string textColor, From c35158ae9c0d16e29e53c40c74d2c94a84bdd403 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 22:11:25 +0500 Subject: [PATCH 21/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D1=82=D0=B5=D1=81=D1=82=D0=B0?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20CloudLayouterPainter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPainterTest.cs | 61 ++++++++----------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs index d3684c867..dbc3a2b04 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs @@ -9,45 +9,34 @@ namespace TagCloud.Tests.CloudLayouterPaintersTest [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class CloudLayouterPainterTest { - private CloudLayouterPainter painter; - - [SetUp] - public void SetUp() - { - painter = new CloudLayouterPainter(new Size(1, 1)); - } - - [Test] - public void Draw_ThrowsException_WithEmptyTags() + private static TestCaseData[] invalidTestCases = new TestCaseData[] { - var expected = Result.Fail("Список тегов пуст"); - var actual = painter.Draw(new List()); - actual.Should().BeEquivalentTo(expected); - } - - [Test] - public void Draw_ThrowsException_WithTagsAsNull() - { - - var expected = Result.Fail("Tags передан как null"); - var actual = painter.Draw(null!); - actual.Should().BeEquivalentTo(expected); - } + new TestCaseData( + "Список тегов пуст", + new List()) + .SetArgDisplayNames("EmptyTags"), + new TestCaseData( + "Tags передан как null", + null) + .SetArgDisplayNames("TagsAsNull"), + new TestCaseData( + "Все прямоугольники не помещаются на изображение", + new Tag[] + { + new Tag( + "Test", + new Rectangle(new Point(0, 0), new Size(100, 100))) + }) + .SetArgDisplayNames("TooSmallToFitImage"), + }; - [Test] - public void Draw_ThrowsException_WithTooSmallToFitImage() + [TestCaseSource(nameof(invalidTestCases))] + public void Draw_ThrowsException_WithInvalidCases(string errorMessage, IList tags) { - var expected = Result - .Fail("Все прямоугольники не помещаются на изображение"); - var actual = painter - .Draw( - new Tag[] - { - new Tag( - "Test", - new Rectangle(new Point(0, 0), new Size(100, 100))) - }); + var painter = new CloudLayouterPainter(new Size(1, 1)); + var expected = Result.Fail(errorMessage); + var actual = painter.Draw(tags); actual.Should().BeEquivalentTo(expected); } } -} +} \ No newline at end of file From 000025c501d5dd4e1de4cf34a040369a65a41bef Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 22:19:07 +0500 Subject: [PATCH 22/32] =?UTF-8?q?=D0=92=D0=BE=20=D0=B2=D1=81=D0=B5=D1=85?= =?UTF-8?q?=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2=D1=8B=D1=85=20=D0=BA?= =?UTF-8?q?=D0=BB=D0=B0=D1=81=D1=81=D0=B0=D1=85=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BD=D0=B5=D0=B4=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D1=8E=D1=89=D0=B8=D0=B9=20=D0=B0=D1=82=D1=80=D0=B8=D0=B1?= =?UTF-8?q?=D1=83=D1=82=20[TestFixture]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPaintersTest/CloudLayouterPainterTest.cs | 1 + .../NormalizedFrequencyBasedCloudLayouterWorkerTest.cs | 1 + TagCloud.Tests/OptionsTests/DataFileNameTest.cs | 1 + TagCloud.Tests/OptionsTests/FontTests.cs | 1 + TagCloud.Tests/OptionsTests/ImageFileNameTests.cs | 1 + TagCloud.Tests/OptionsTests/ImageSizeTests.cs | 1 + TagCloud.Tests/OptionsTests/IsSortedTests.cs | 1 + TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs | 1 + TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs | 1 + TagCloud.Tests/OptionsTests/ResultFormatTests.cs | 1 + TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs | 1 + TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs | 1 + TagCloud.Tests/ParsersTests/FontParserTest.cs | 3 +++ 13 files changed, 15 insertions(+) diff --git a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs index dbc3a2b04..6bbee5f73 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs @@ -6,6 +6,7 @@ namespace TagCloud.Tests.CloudLayouterPaintersTest { + [TestFixture] [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class CloudLayouterPainterTest { diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs index 41941bb16..3cb404aff 100644 --- a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs +++ b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs @@ -5,6 +5,7 @@ namespace TagCloud.Tests.CloudLayouterWorkersTests { + [TestFixture] internal class NormalizedFrequencyBasedCloudLayouterWorkerTest { private readonly Dictionary normalizedValues diff --git a/TagCloud.Tests/OptionsTests/DataFileNameTest.cs b/TagCloud.Tests/OptionsTests/DataFileNameTest.cs index 6658a08e6..ca7b7afba 100644 --- a/TagCloud.Tests/OptionsTests/DataFileNameTest.cs +++ b/TagCloud.Tests/OptionsTests/DataFileNameTest.cs @@ -5,6 +5,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class DataFileNameTest() : BaseOptionTest("DataFileName") { [TestCase("")] diff --git a/TagCloud.Tests/OptionsTests/FontTests.cs b/TagCloud.Tests/OptionsTests/FontTests.cs index 3aa38b338..64101ee16 100644 --- a/TagCloud.Tests/OptionsTests/FontTests.cs +++ b/TagCloud.Tests/OptionsTests/FontTests.cs @@ -4,6 +4,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class FontTests() : BaseOptionTest("Font") { [TestCase("")] diff --git a/TagCloud.Tests/OptionsTests/ImageFileNameTests.cs b/TagCloud.Tests/OptionsTests/ImageFileNameTests.cs index 98337e29b..353e842a4 100644 --- a/TagCloud.Tests/OptionsTests/ImageFileNameTests.cs +++ b/TagCloud.Tests/OptionsTests/ImageFileNameTests.cs @@ -4,6 +4,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class ImageFileNameTests() : BaseOptionTest("ImageFileName") { [TestCase("")] diff --git a/TagCloud.Tests/OptionsTests/ImageSizeTests.cs b/TagCloud.Tests/OptionsTests/ImageSizeTests.cs index 8ddf21677..deb1f853f 100644 --- a/TagCloud.Tests/OptionsTests/ImageSizeTests.cs +++ b/TagCloud.Tests/OptionsTests/ImageSizeTests.cs @@ -4,6 +4,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class ImageSizeTests() : BaseOptionTest("ImageSize") { [TestCase("")] diff --git a/TagCloud.Tests/OptionsTests/IsSortedTests.cs b/TagCloud.Tests/OptionsTests/IsSortedTests.cs index 6b05a1c4c..abc16c114 100644 --- a/TagCloud.Tests/OptionsTests/IsSortedTests.cs +++ b/TagCloud.Tests/OptionsTests/IsSortedTests.cs @@ -4,6 +4,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class IsSortedTests() : BaseOptionTest("IsSorted") { [TestCase("")] diff --git a/TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs b/TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs index f8735ccc5..c3fdd1124 100644 --- a/TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs +++ b/TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs @@ -4,6 +4,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class MaxRectangleHeightTest() : BaseOptionTest("MaxRectangleHeight") { [TestCase("")] diff --git a/TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs b/TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs index f62988839..611275ef1 100644 --- a/TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs +++ b/TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs @@ -4,6 +4,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class MaxRectangleWidthTest() : BaseOptionTest("MaxRectangleWidth") { [TestCase("")] diff --git a/TagCloud.Tests/OptionsTests/ResultFormatTests.cs b/TagCloud.Tests/OptionsTests/ResultFormatTests.cs index 8cb08c844..b5a2682bc 100644 --- a/TagCloud.Tests/OptionsTests/ResultFormatTests.cs +++ b/TagCloud.Tests/OptionsTests/ResultFormatTests.cs @@ -4,6 +4,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class ResultFormatTests() : BaseOptionTest("ResultFormat") { [TestCase("")] diff --git a/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs b/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs index 8069717c6..c8f5abce1 100644 --- a/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs +++ b/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs @@ -5,6 +5,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class WordsToExcludeFileNameTests() : BaseOptionTest("WordsToExcludeFileName") { [TestCase("NonExistingFile.txt")] diff --git a/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs b/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs index 8b1c83e74..2e8da380c 100644 --- a/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs +++ b/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs @@ -5,6 +5,7 @@ namespace TagCloud.Tests.OptionsTests { + [TestFixture] internal class WordsToIncludeFileNameTests() : BaseOptionTest("WordsToIncludeFileName") { [TestCase("NonExistingFile.txt")] diff --git a/TagCloud.Tests/ParsersTests/FontParserTest.cs b/TagCloud.Tests/ParsersTests/FontParserTest.cs index 7b28f44f3..d360c9bd1 100644 --- a/TagCloud.Tests/ParsersTests/FontParserTest.cs +++ b/TagCloud.Tests/ParsersTests/FontParserTest.cs @@ -1,17 +1,20 @@ using FileSenderRailway; using FluentAssertions; +using System.Diagnostics.CodeAnalysis; using System.Drawing; using TagCloud.Parsers; namespace TagCloud.Tests.ParsersTests { [TestFixture] + [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] internal class FontParserTest { [TestCase("")] [TestCase(" ")] [TestCase(null!)] [TestCase("abc")] + public void FontParser_ThrowsException_WithInvalidFont(string font) { var expected = Result.Fail($"Неизвестный шрифт \"{font}\""); From 866b9c97198c3389c84ba53c2726758951aece29 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 22:40:10 +0500 Subject: [PATCH 23/32] =?UTF-8?q?=D0=90=D1=82=D1=80=D0=B8=D0=B1=D1=83?= =?UTF-8?q?=D1=82=20[TestCase]=20=D1=83=20=D1=82=D0=B5=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=20=D1=81=20=D0=BE=D0=B4=D0=BD=D0=B8=D0=BC=20=D1=81=D0=BB?= =?UTF-8?q?=D1=83=D1=87=D0=B0=D0=B5=D0=BC=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=BE=20[Test]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ircularCloudLayouterMainRequirementsTest.cs | 13 +++++++++---- .../ImageSaversTests/ImageSaverTest.cs | 6 +++--- .../NormalizersTest/NormalizerTest.cs | 15 +++++++-------- .../OptionsTests/DataFileNameTest.cs | 12 ++++++------ .../WordsToExcludeFileNameTests.cs | 18 +++++++++--------- .../WordsToIncludeFileNameTests.cs | 17 ++++++++--------- .../WordFilterChangeBannedWordsTest.cs | 18 ++++++++++-------- 7 files changed, 52 insertions(+), 47 deletions(-) diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs index cd742e204..3e96535d5 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs +++ b/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs @@ -57,10 +57,13 @@ public void SetUp() rectangles = tags.Select(x => x.Rectangle).ToArray(); } - [TestCase(0.7, 1000)] + [Test] [Repeat(10)] - public void ShouldPlaceRectanglesInCircle(double expectedCoverageRatio, int gridSize) + public void ShouldPlaceRectanglesInCircle() { + var expectedCoverageRatio = 0.7; + var gridSize = 1000; + var maxRadius = rectangles.Max( x => x.GetMaxDistanceFromPointToRectangleAngles(center)); var step = 2 * maxRadius / gridSize; @@ -71,10 +74,12 @@ public void ShouldPlaceRectanglesInCircle(double expectedCoverageRatio, int grid actualCoverageRatio.Should().BeGreaterThanOrEqualTo(expectedCoverageRatio); } - [TestCase(15)] + [Test] [Repeat(10)] - public void ShouldPlaceCenterOfMassOfRectanglesNearCenter(int tolerance) + public void ShouldPlaceCenterOfMassOfRectanglesNearCenter() { + var tolerance = 15; + var centerX = rectangles.Average(r => r.Left + r.Width / 2.0); var centerY = rectangles.Average(r => r.Top + r.Height / 2.0); var actualCenter = new Point((int)centerX, (int)centerY); diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs index ddef822ae..fe0727037 100644 --- a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs @@ -26,10 +26,10 @@ public void SetUp() imageSaver = new ImageSaver(); } - [TestCase("Test")] - public void SaveFile_ThrowsException_WithNullBitmap(string filename) + [Test] + public void SaveFile_ThrowsException_WithNullBitmap() { - var path = Path.Combine(directoryPath, filename); + var path = Path.Combine(directoryPath, "Test"); var expected = Result.Fail("Передаваемое изображение не должно быть null"); var actual = imageSaver.SaveFile(null!, path); actual.Should().BeEquivalentTo(expected); diff --git a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs index 2fecc4fae..98ede3df0 100644 --- a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs +++ b/TagCloud.Tests/NormalizersTest/NormalizerTest.cs @@ -56,19 +56,18 @@ public void Normalize_ThrowsException_WithValuesAsNull() actual.Should().BeEquivalentTo(expected); } - [TestCase(-1)] - public void Normalize_ThrowsException_WithInvalidDecimalPlaces( - int decimalPlaces) + [Test] + public void Normalize_ThrowsException_WithInvalidDecimalPlaces() { var expected = Result.Fail>( "Количество знаков после запятой не может быть отрицательным"); - var actual = normalizer.Normalize(values, defaultMinCoefficient, decimalPlaces); + var actual = normalizer.Normalize(values, defaultMinCoefficient, -1); actual.Should().BeEquivalentTo(expected); } - [TestCase(0.25, 4)] - [TestCase(0.25, 2)] - public void Normalize_CalculatesСorrectly(double minCoefficient, int decimalPlaces) + [TestCase(4)] + [TestCase(2)] + public void Normalize_CalculatesСorrectly(int decimalPlaces) { var dict = new Dictionary(); foreach (var pair in expectedResult) @@ -76,7 +75,7 @@ public void Normalize_CalculatesСorrectly(double minCoefficient, int decimalPla dict[pair.Key] = Math.Round(pair.Value, decimalPlaces); } var expected = dict.AsResult(); - var actual = normalizer.Normalize(values, minCoefficient, decimalPlaces); + var actual = normalizer.Normalize(values, defaultMinCoefficient, decimalPlaces); actual.Value.Should().BeEquivalentTo(expected.Value); } } diff --git a/TagCloud.Tests/OptionsTests/DataFileNameTest.cs b/TagCloud.Tests/OptionsTests/DataFileNameTest.cs index ca7b7afba..02a70dfb5 100644 --- a/TagCloud.Tests/OptionsTests/DataFileNameTest.cs +++ b/TagCloud.Tests/OptionsTests/DataFileNameTest.cs @@ -25,10 +25,10 @@ public void Program_WorksCorrectly_WithNonExistingFilename(string dataFileName) actual.Should().BeEquivalentTo(expected); } - [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] - public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine(string dataFileName) + [Test] + public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine() { - var path = Path.Combine(directoryPath, dataFileName); + var path = Path.Combine(directoryPath, "InvalidFile_MoreThanOneWordInLine.txt"); var invalidContent = new string[] { "one", @@ -50,10 +50,10 @@ public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine(string data actual.Should().BeEquivalentTo(expected); } - [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] - public void Program_WorksCorrectly_WithEmptyFile(string dataFileName) + [Test] + public void Program_WorksCorrectly_WithEmptyFile() { - var path = Path.Combine(directoryPath, dataFileName); + var path = Path.Combine(directoryPath, "InvalidFile_MoreThanOneWordInLine.txt"); FileUtilities.CreateDataFile(directoryPath, path, Array.Empty()); options.DataFileName = path; diff --git a/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs b/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs index c8f5abce1..96b45deef 100644 --- a/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs +++ b/TagCloud.Tests/OptionsTests/WordsToExcludeFileNameTests.cs @@ -8,9 +8,10 @@ namespace TagCloud.Tests.OptionsTests [TestFixture] internal class WordsToExcludeFileNameTests() : BaseOptionTest("WordsToExcludeFileName") { - [TestCase("NonExistingFile.txt")] - public void Program_WorksCorrectly_WithNonExistingFilename(string wordsToExcludeFileName) + [Test] + public void Program_WorksCorrectly_WithNonExistingFilename() { + var wordsToExcludeFileName = "NonExistingFile.txt"; options.WordsToExcludeFileName = wordsToExcludeFileName; var expected = Result.Fail($"Файл \"{wordsToExcludeFileName}\" не существует"); @@ -22,11 +23,10 @@ public void Program_WorksCorrectly_WithNonExistingFilename(string wordsToExclude actual.Should().BeEquivalentTo(expected); } - [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] - public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine( - string wordsToExcludeFileName) + [Test] + public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine() { - var path = Path.Combine(directoryPath, wordsToExcludeFileName); + var path = Path.Combine(directoryPath, "InvalidFile_MoreThanOneWordInLine.txt"); var invalidContent = new string[] { "one", @@ -48,10 +48,10 @@ public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine( actual.Should().BeEquivalentTo(expected); } - [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] - public void Program_WorksCorrectly_WithEmptyFile(string wordsToExcludeFileName) + [Test] + public void Program_WorksCorrectly_WithEmptyFile() { - var path = Path.Combine(directoryPath, wordsToExcludeFileName); + var path = Path.Combine(directoryPath, "InvalidFile_MoreThanOneWordInLine.txt"); FileUtilities.CreateDataFile(directoryPath, path, Array.Empty()); options.WordsToExcludeFileName = path; diff --git a/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs b/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs index 2e8da380c..f78cd16d4 100644 --- a/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs +++ b/TagCloud.Tests/OptionsTests/WordsToIncludeFileNameTests.cs @@ -8,9 +8,10 @@ namespace TagCloud.Tests.OptionsTests [TestFixture] internal class WordsToIncludeFileNameTests() : BaseOptionTest("WordsToIncludeFileName") { - [TestCase("NonExistingFile.txt")] - public void Program_WorksCorrectly_WithNonExistingFilename(string wordsToIncludeFileName) + [Test] + public void Program_WorksCorrectly_WithNonExistingFilename() { + var wordsToIncludeFileName = "NonExistingFile.txt"; options.WordsToIncludeFileName = wordsToIncludeFileName; var expected = Result.Fail($"Файл \"{wordsToIncludeFileName}\" не существует"); @@ -22,11 +23,9 @@ public void Program_WorksCorrectly_WithNonExistingFilename(string wordsToInclude actual.Should().BeEquivalentTo(expected); } - [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] - public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine( - string wordsToIncludeFileName) + public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine() { - var path = Path.Combine(directoryPath, wordsToIncludeFileName); + var path = Path.Combine(directoryPath, "InvalidFile_MoreThanOneWordInLine.txt"); var invalidContent = new string[] { "one", @@ -47,10 +46,10 @@ public void Program_WorksCorrectly_WithFileWithMoreThanOneWordInLine( actual.Should().BeEquivalentTo(expected); } - [TestCase("InvalidFile_MoreThanOneWordInLine.txt")] - public void Program_WorksCorrectly_WithEmptyFile(string wordsToIncludeFileName) + [Test] + public void Program_WorksCorrectly_WithEmptyFile() { - var path = Path.Combine(directoryPath, wordsToIncludeFileName); + var path = Path.Combine(directoryPath, "InvalidFile_MoreThanOneWordInLine.txt"); FileUtilities.CreateDataFile(directoryPath, path, Array.Empty()); options.WordsToIncludeFileName = path; diff --git a/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs index 01dbdbfd8..7df947e52 100644 --- a/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs +++ b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs @@ -21,21 +21,23 @@ public void Clear_ShouldClearBannedWordList() wordFilter.BannedWords.Should().BeEmpty(); } - [TestCase("WordToAdd")] - public void Add_ShouldAddWord_ToBannedWords(string word) + [Test] + public void Add_ShouldAddWord_ToBannedWords() { + var wordToAdd = "WordToAdd"; wordFilter.Clear(); - wordFilter.Add(word); - wordFilter.BannedWords.Should().Contain(word).And.HaveCount(1); + wordFilter.Add(wordToAdd); + wordFilter.BannedWords.Should().Contain(wordToAdd).And.HaveCount(1); } - [TestCase("WordToRemove")] + [Test] public void Remove_ShouldRemoveWord_InBannedWords(string word) { + var wordToRemove = "WordToRemove"; wordFilter.Clear(); - wordFilter.Add(word); - wordFilter.Remove(word); - wordFilter.BannedWords.Should().NotContain(word); + wordFilter.Add(wordToRemove); + wordFilter.Remove(wordToRemove); + wordFilter.BannedWords.Should().NotContain(wordToRemove); } } } From eaa377bfab4986867abc5143e8450f42aa13ffce Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 22:44:39 +0500 Subject: [PATCH 24/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20=D1=82=D0=B5=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=20=D1=81=20=D0=B8=D0=B7=D0=B1=D1=8B=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BD=D1=8B=D0=BC=20=D0=B0=D1=82=D1=80=D0=B8=D0=B1=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D0=BC=20[SetUp]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs | 11 ++++------- TagCloud.Tests/WordCountersTests/WordCounterTest.cs | 9 +-------- .../WordFilterChangeBannedWordsTest.cs | 13 ++++--------- TagCloud.Tests/WordReadersTests/WordReaderTest.cs | 12 ++++-------- 4 files changed, 13 insertions(+), 32 deletions(-) diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs index fe0727037..7860d0c7a 100644 --- a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs @@ -12,7 +12,6 @@ namespace TagCloud.Tests.ImageSaversTests internal class ImageSaverTest { private readonly string directoryPath = "TempFilesForImageSaverTests"; - private ImageSaver imageSaver; [OneTimeSetUp] public void Init() @@ -20,15 +19,10 @@ public void Init() Directory.CreateDirectory(directoryPath); } - [SetUp] - public void SetUp() - { - imageSaver = new ImageSaver(); - } - [Test] public void SaveFile_ThrowsException_WithNullBitmap() { + var imageSaver = new ImageSaver(); var path = Path.Combine(directoryPath, "Test"); var expected = Result.Fail("Передаваемое изображение не должно быть null"); var actual = imageSaver.SaveFile(null!, path); @@ -40,6 +34,7 @@ public void SaveFile_ThrowsException_WithNullBitmap() [TestCase(null)] public void SaveFile_ThrowsException_WithInvalidFilename(string? filename) { + var imageSaver = new ImageSaver(); var dummyImage = new Bitmap(1, 1); var expected = Result.Fail($"Некорректное имя файла для создания \"{filename}\""); var actual = imageSaver.SaveFile(dummyImage, filename!); @@ -52,6 +47,7 @@ public void SaveFile_ThrowsException_WithInvalidFilename(string? filename) [TestCase("abc")] public void SaveFile_ThrowsException_WithInvalidFormat(string? format) { + var imageSaver = new ImageSaver(); var dummyImage = new Bitmap(1, 1); var filename = "Test"; var expected = Result.Fail($"Формат \"{format}\" не поддерживается"); @@ -67,6 +63,7 @@ public void SaveFile_ThrowsException_WithInvalidFormat(string? format) [TestCase("Test", "tiff", ExpectedResult = true)] public bool SaveFile_SavesFile(string filename, string format) { + var imageSaver = new ImageSaver(); var dummyImage = new Bitmap(1, 1); var path = Path.Combine(directoryPath, filename); diff --git a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs index 5a2a698f8..e6d5ef0a2 100644 --- a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs +++ b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs @@ -6,17 +6,10 @@ namespace TagCloud.Tests.WordCountersTests [TestFixture] internal class WordCounterTest { - private WordCounter wordCounter; - - [SetUp] - public void SetUp() - { - wordCounter = new WordCounter(); - } - [Test] public void WordCounter_CountsCorrect() { + var wordCounter = new WordCounter(); var expected = new Dictionary() { { "One", 2 }, diff --git a/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs index 7df947e52..fe54c8994 100644 --- a/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs +++ b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs @@ -6,17 +6,10 @@ namespace TagCloud.Tests.WordFiltersTests [TestFixture] internal class WordFilterChangeBannedWordsTest { - private WordFilter wordFilter; - - [SetUp] - public void SetUp() - { - wordFilter = new WordFilter(); - } - [Test] public void Clear_ShouldClearBannedWordList() { + var wordFilter = new WordFilter(); wordFilter.Clear(); wordFilter.BannedWords.Should().BeEmpty(); } @@ -24,6 +17,7 @@ public void Clear_ShouldClearBannedWordList() [Test] public void Add_ShouldAddWord_ToBannedWords() { + var wordFilter = new WordFilter(); var wordToAdd = "WordToAdd"; wordFilter.Clear(); wordFilter.Add(wordToAdd); @@ -31,8 +25,9 @@ public void Add_ShouldAddWord_ToBannedWords() } [Test] - public void Remove_ShouldRemoveWord_InBannedWords(string word) + public void Remove_ShouldRemoveWord_InBannedWords() { + var wordFilter = new WordFilter(); var wordToRemove = "WordToRemove"; wordFilter.Clear(); wordFilter.Add(wordToRemove); diff --git a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs index 0fc78a900..3167d30f3 100644 --- a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs +++ b/TagCloud.Tests/WordReadersTests/WordReaderTest.cs @@ -25,8 +25,6 @@ private readonly string fileWithMoreThanOneWordInLinePath private readonly string fileEmptyPath = "InvalidFile_Empty.txt"; - private WordReader wordReader; - [OneTimeSetUp] public void Init() { @@ -49,17 +47,12 @@ public void Init() Array.Empty()); } - [SetUp] - public void SetUp() - { - wordReader = new WordReader(); - } - [TestCase("")] [TestCase(" ")] [TestCase("NonExistingFile.txt")] public void WordReader_ThrowsFileNotFoundException_WithInvalidFilename(string filename) { + var wordReader = new WordReader(); var path = Path.Combine(directoryPath, filename); var expected = Result.Fail>($"Файл \"{path}\" не существует"); var actual = wordReader.ReadByLines(path); @@ -69,6 +62,7 @@ public void WordReader_ThrowsFileNotFoundException_WithInvalidFilename(string fi [Test] public void WordReader_ThrowsException_WithTwoWordsInOneLine() { + var wordReader = new WordReader(); var path = Path.Combine(directoryPath, fileWithMoreThanOneWordInLinePath); var expected = Result.Fail>($"Файл \"{path}\" содержит строку с двумя и более словами"); var actual = wordReader.ReadByLines(path); @@ -78,6 +72,7 @@ public void WordReader_ThrowsException_WithTwoWordsInOneLine() [Test] public void WordReader_ThrowsException_WithEmpty() { + var wordReader = new WordReader(); var path = Path.Combine(directoryPath, fileEmptyPath); var expected = Result.Fail>($"Файл \"{path}\" пустой"); var actual = wordReader.ReadByLines(path); @@ -88,6 +83,7 @@ public void WordReader_ThrowsException_WithEmpty() [TestCase("FileImg.png")] public void WordReader_ThrowsException_WithNonTxt(string filename) { + var wordReader = new WordReader(); FileUtilities .CreateDataFile( directoryPath, From 2f02a578ecf8949af7bf0553ea46340d95e70d7d Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 22:49:29 +0500 Subject: [PATCH 25/32] =?UTF-8?q?MainTest=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B8?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B2=20FullProgr?= =?UTF-8?q?amExecution,=20=D0=B4=D0=BB=D1=8F=20=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B5=D0=B9=20=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0?= =?UTF-8?q?=D1=82=D0=B8=D0=B2=D0=BD=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud.Tests/{MainTest.cs => FullProgramExecutionTest.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename TagCloud.Tests/{MainTest.cs => FullProgramExecutionTest.cs} (95%) diff --git a/TagCloud.Tests/MainTest.cs b/TagCloud.Tests/FullProgramExecutionTest.cs similarity index 95% rename from TagCloud.Tests/MainTest.cs rename to TagCloud.Tests/FullProgramExecutionTest.cs index 76acb3f68..7916d3a7b 100644 --- a/TagCloud.Tests/MainTest.cs +++ b/TagCloud.Tests/FullProgramExecutionTest.cs @@ -6,7 +6,7 @@ namespace TagCloud.Tests { [TestFixture] - internal class MainTest() : BaseOptionTest("MainTest") + internal class FullProgramExecutionTest() : BaseOptionTest("FullProgramExecutionTest") { [Test] public void Program_ExecutesSuccessfully_WithValidArguments() From 51d00af2e1443aabb961572b1e82fa73b82f7ab3 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 22:55:49 +0500 Subject: [PATCH 26/32] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=BE=D1=80=D1=8F=D0=B4=D0=BE=D0=BA?= =?UTF-8?q?=20=D0=BC=D0=BE=D0=B4=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloud.Tests/OptionsTests/ValidValues.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TagCloud.Tests/OptionsTests/ValidValues.cs b/TagCloud.Tests/OptionsTests/ValidValues.cs index 87562b11c..2dc086d39 100644 --- a/TagCloud.Tests/OptionsTests/ValidValues.cs +++ b/TagCloud.Tests/OptionsTests/ValidValues.cs @@ -2,7 +2,7 @@ { internal static class ValidValues { - public readonly static string[] ValidDataFileContent = new string[] + public static readonly string[] ValidDataFileContent = new string[] { "One", "One", From 0a9a7c6331cac4958fbe959faf8c9b4bc5a0dc75 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 22:59:31 +0500 Subject: [PATCH 27/32] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B5=D0=BB=D0=BD=D0=BE=20=D0=B4=D1=83=D0=B1=D0=BB=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=20=D0=B2=20WordCounterTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WordCountersTests/WordCounterTest.cs | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs index e6d5ef0a2..d4c1dc36d 100644 --- a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs +++ b/TagCloud.Tests/WordCountersTests/WordCounterTest.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using TagCloud.Tests.OptionsTests; using TagCloud.WordCounters; namespace TagCloud.Tests.WordCountersTests @@ -10,26 +11,14 @@ internal class WordCounterTest public void WordCounter_CountsCorrect() { var wordCounter = new WordCounter(); - var expected = new Dictionary() + var expected = new Dictionary(); + foreach (var word in ValidValues.ValidDataFileContent) { - { "One", 2 }, - { "Two", 1 }, - { "Three", 1 }, - { "Four", 4 }, - }; - var values = new string[] - { - "One", - "One", - "Two", - "Three", - "Four", - "Four", - "Four", - "Four" - }; + expected.TryGetValue(word, out var count); + expected[word] = count + 1; + } - foreach (var value in values) + foreach (var value in ValidValues.ValidDataFileContent) { wordCounter.AddWord(value); } From 01cc083e22635879aba9c85e203b25cc6435fc4c Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 23:15:06 +0500 Subject: [PATCH 28/32] =?UTF-8?q?=D0=9D=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D1=8B=20=D0=BA=20?= =?UTF-8?q?=D0=B5=D0=B4=D0=B8=D0=BD=D0=BE=D0=BE=D0=B1=D1=80=D0=B0=D0=B7?= =?UTF-8?q?=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPainterTests.cs} | 6 +++--- ...> NormalizedFrequencyBasedCloudLayouterWorkerTests.cs} | 2 +- ...terWorkerTest.cs => RandomCloudLayouterWorkerTests.cs} | 0 .../CircularCloudLayouterMainRequirementsTests.cs} | 8 ++++---- .../CircularCloudLayouterTests.cs} | 4 ++-- ...ogramExecutionTest.cs => FullProgramExecutionTests.cs} | 2 +- .../{ImageSaverTest.cs => ImageSaverTests.cs} | 2 +- .../NormalizerTests.cs} | 4 ++-- .../{DataFileNameTest.cs => DataFileNameTests.cs} | 2 +- ...xRectangleHeightTest.cs => MaxRectangleHeightTests.cs} | 2 +- ...MaxRectangleWidthTest.cs => MaxRectangleWidthTests.cs} | 2 +- .../{BoolParserTest.cs => BoolParserTests.cs} | 2 +- .../{ColorParserTest.cs => ColorParserTests.cs} | 2 +- .../{FontParserTest.cs => FontParserTests.cs} | 2 +- .../{SizeParserTest.cs => SizeParserTests.cs} | 2 +- .../{WordCounterTest.cs => WordCounterTests.cs} | 2 +- ...edWordsTest.cs => WordFilterChangeBannedWordsTests.cs} | 2 +- ...dWordsTest.cs => WordFilterDefaultBannedWordsTests.cs} | 2 +- .../{WordReaderTest.cs => WordReaderTests.cs} | 2 +- 19 files changed, 25 insertions(+), 25 deletions(-) rename TagCloud.Tests/{CloudLayouterPaintersTest/CloudLayouterPainterTest.cs => CloudLayouterPaintersTests/CloudLayouterPainterTests.cs} (88%) rename TagCloud.Tests/CloudLayouterWorkersTests/{NormalizedFrequencyBasedCloudLayouterWorkerTest.cs => NormalizedFrequencyBasedCloudLayouterWorkerTests.cs} (99%) rename TagCloud.Tests/CloudLayouterWorkersTests/{RandomCloudLayouterWorkerTest.cs => RandomCloudLayouterWorkerTests.cs} (100%) rename TagCloud.Tests/{CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs => CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs} (96%) rename TagCloud.Tests/{CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs => CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterTests.cs} (86%) rename TagCloud.Tests/{FullProgramExecutionTest.cs => FullProgramExecutionTests.cs} (95%) rename TagCloud.Tests/ImageSaversTests/{ImageSaverTest.cs => ImageSaverTests.cs} (98%) rename TagCloud.Tests/{NormalizersTest/NormalizerTest.cs => NormalizersTests/NormalizerTests.cs} (97%) rename TagCloud.Tests/OptionsTests/{DataFileNameTest.cs => DataFileNameTests.cs} (97%) rename TagCloud.Tests/OptionsTests/{MaxRectangleHeightTest.cs => MaxRectangleHeightTests.cs} (94%) rename TagCloud.Tests/OptionsTests/{MaxRectangleWidthTest.cs => MaxRectangleWidthTests.cs} (94%) rename TagCloud.Tests/ParsersTests/{BoolParserTest.cs => BoolParserTests.cs} (96%) rename TagCloud.Tests/ParsersTests/{ColorParserTest.cs => ColorParserTests.cs} (96%) rename TagCloud.Tests/ParsersTests/{FontParserTest.cs => FontParserTests.cs} (97%) rename TagCloud.Tests/ParsersTests/{SizeParserTest.cs => SizeParserTests.cs} (98%) rename TagCloud.Tests/WordCountersTests/{WordCounterTest.cs => WordCounterTests.cs} (95%) rename TagCloud.Tests/WordFiltersTests/{WordFilterChangeBannedWordsTest.cs => WordFilterChangeBannedWordsTests.cs} (95%) rename TagCloud.Tests/WordFiltersTests/{WordFilterDefaultBannedWordsTest.cs => WordFilterDefaultBannedWordsTests.cs} (97%) rename TagCloud.Tests/WordReadersTests/{WordReaderTest.cs => WordReaderTests.cs} (99%) diff --git a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs b/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs similarity index 88% rename from TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs rename to TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs index 6bbee5f73..c0853933f 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTest/CloudLayouterPainterTest.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs @@ -4,13 +4,13 @@ using System.Drawing; using TagCloud.CloudLayouterPainters; -namespace TagCloud.Tests.CloudLayouterPaintersTest +namespace TagCloud.Tests.CloudLayouterPaintersTests { [TestFixture] [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] - internal class CloudLayouterPainterTest + internal class CloudLayouterPainterTests { - private static TestCaseData[] invalidTestCases = new TestCaseData[] + private static readonly TestCaseData[] invalidTestCases = new TestCaseData[] { new TestCaseData( "Список тегов пуст", diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTests.cs similarity index 99% rename from TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs rename to TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTests.cs index 3cb404aff..1ea6b3cf7 100644 --- a/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTest.cs +++ b/TagCloud.Tests/CloudLayouterWorkersTests/NormalizedFrequencyBasedCloudLayouterWorkerTests.cs @@ -6,7 +6,7 @@ namespace TagCloud.Tests.CloudLayouterWorkersTests { [TestFixture] - internal class NormalizedFrequencyBasedCloudLayouterWorkerTest + internal class NormalizedFrequencyBasedCloudLayouterWorkerTests { private readonly Dictionary normalizedValues = new Dictionary diff --git a/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs b/TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTests.cs similarity index 100% rename from TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTest.cs rename to TagCloud.Tests/CloudLayouterWorkersTests/RandomCloudLayouterWorkerTests.cs diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs b/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs similarity index 96% rename from TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs rename to TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs index 3e96535d5..4552db6f9 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTest.cs +++ b/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs @@ -7,11 +7,11 @@ using TagCloud.ImageSavers; using TagCloud.Tests.Extensions; -namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests +namespace TagCloud.Tests.CloudLayoutersTests.CircularCloudLayouterTests { [TestFixture] [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] - internal class CircularCloudLayouterMainRequirementsTest + internal class CircularCloudLayouterMainRequirementsTests { private Point center = new Point(); private Rectangle[] rectangles; @@ -102,8 +102,8 @@ public void ShouldPlaceRectanglesWithoutOverlap() rectangles[i].IntersectsWith(rectangles[j]), Is.EqualTo(false), $"Прямоугольники пересекаются:\n" + - $"{rectangles[i].ToString()}\n" + - $"{rectangles[j].ToString()}"); + $"{rectangles[i]}\n" + + $"{rectangles[j]}"); } } } diff --git a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs b/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterTests.cs similarity index 86% rename from TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs rename to TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterTests.cs index cfe5b111f..e3883775a 100644 --- a/TagCloud.Tests/CloudLayouterTests/CircularCloudLayouterTests/CircularCloudLayouterTest.cs +++ b/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterTests.cs @@ -3,10 +3,10 @@ using System.Drawing; using TagCloud.CloudLayouters.CircularCloudLayouter; -namespace TagCloud.Tests.CloudLayouterTests.CircularCloudLayouterTests +namespace TagCloud.Tests.CloudLayoutersTests.CircularCloudLayouterTests { [TestFixture] - internal class CircularCloudLayouterTest + internal class CircularCloudLayouterTests { [TestCase(0, 100)] [TestCase(-1, 100)] diff --git a/TagCloud.Tests/FullProgramExecutionTest.cs b/TagCloud.Tests/FullProgramExecutionTests.cs similarity index 95% rename from TagCloud.Tests/FullProgramExecutionTest.cs rename to TagCloud.Tests/FullProgramExecutionTests.cs index 7916d3a7b..5667fbabd 100644 --- a/TagCloud.Tests/FullProgramExecutionTest.cs +++ b/TagCloud.Tests/FullProgramExecutionTests.cs @@ -6,7 +6,7 @@ namespace TagCloud.Tests { [TestFixture] - internal class FullProgramExecutionTest() : BaseOptionTest("FullProgramExecutionTest") + internal class FullProgramExecutionTests() : BaseOptionTest("FullProgramExecutionTest") { [Test] public void Program_ExecutesSuccessfully_WithValidArguments() diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTests.cs similarity index 98% rename from TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs rename to TagCloud.Tests/ImageSaversTests/ImageSaverTests.cs index 7860d0c7a..0d6c09f2a 100644 --- a/TagCloud.Tests/ImageSaversTests/ImageSaverTest.cs +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTests.cs @@ -9,7 +9,7 @@ namespace TagCloud.Tests.ImageSaversTests { [TestFixture] [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] - internal class ImageSaverTest + internal class ImageSaverTests { private readonly string directoryPath = "TempFilesForImageSaverTests"; diff --git a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs b/TagCloud.Tests/NormalizersTests/NormalizerTests.cs similarity index 97% rename from TagCloud.Tests/NormalizersTest/NormalizerTest.cs rename to TagCloud.Tests/NormalizersTests/NormalizerTests.cs index 98ede3df0..d6ef23783 100644 --- a/TagCloud.Tests/NormalizersTest/NormalizerTest.cs +++ b/TagCloud.Tests/NormalizersTests/NormalizerTests.cs @@ -2,10 +2,10 @@ using FluentAssertions; using TagCloud.Normalizers; -namespace TagCloud.Tests.NormalizersTest +namespace TagCloud.Tests.NormalizersTests { [TestFixture] - internal class NormalizerTest + internal class NormalizerTests { private readonly Normalizer normalizer = new Normalizer(); private readonly Dictionary values = new Dictionary diff --git a/TagCloud.Tests/OptionsTests/DataFileNameTest.cs b/TagCloud.Tests/OptionsTests/DataFileNameTests.cs similarity index 97% rename from TagCloud.Tests/OptionsTests/DataFileNameTest.cs rename to TagCloud.Tests/OptionsTests/DataFileNameTests.cs index 02a70dfb5..5cf971a84 100644 --- a/TagCloud.Tests/OptionsTests/DataFileNameTest.cs +++ b/TagCloud.Tests/OptionsTests/DataFileNameTests.cs @@ -6,7 +6,7 @@ namespace TagCloud.Tests.OptionsTests { [TestFixture] - internal class DataFileNameTest() : BaseOptionTest("DataFileName") + internal class DataFileNameTests() : BaseOptionTest("DataFileName") { [TestCase("")] [TestCase(" ")] diff --git a/TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs b/TagCloud.Tests/OptionsTests/MaxRectangleHeightTests.cs similarity index 94% rename from TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs rename to TagCloud.Tests/OptionsTests/MaxRectangleHeightTests.cs index c3fdd1124..062b109ed 100644 --- a/TagCloud.Tests/OptionsTests/MaxRectangleHeightTest.cs +++ b/TagCloud.Tests/OptionsTests/MaxRectangleHeightTests.cs @@ -5,7 +5,7 @@ namespace TagCloud.Tests.OptionsTests { [TestFixture] - internal class MaxRectangleHeightTest() : BaseOptionTest("MaxRectangleHeight") + internal class MaxRectangleHeightTests() : BaseOptionTest("MaxRectangleHeight") { [TestCase("")] [TestCase(" ")] diff --git a/TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs b/TagCloud.Tests/OptionsTests/MaxRectangleWidthTests.cs similarity index 94% rename from TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs rename to TagCloud.Tests/OptionsTests/MaxRectangleWidthTests.cs index 611275ef1..380eb857d 100644 --- a/TagCloud.Tests/OptionsTests/MaxRectangleWidthTest.cs +++ b/TagCloud.Tests/OptionsTests/MaxRectangleWidthTests.cs @@ -5,7 +5,7 @@ namespace TagCloud.Tests.OptionsTests { [TestFixture] - internal class MaxRectangleWidthTest() : BaseOptionTest("MaxRectangleWidth") + internal class MaxRectangleWidthTests() : BaseOptionTest("MaxRectangleWidth") { [TestCase("")] [TestCase(" ")] diff --git a/TagCloud.Tests/ParsersTests/BoolParserTest.cs b/TagCloud.Tests/ParsersTests/BoolParserTests.cs similarity index 96% rename from TagCloud.Tests/ParsersTests/BoolParserTest.cs rename to TagCloud.Tests/ParsersTests/BoolParserTests.cs index a91af3710..598a22eb5 100644 --- a/TagCloud.Tests/ParsersTests/BoolParserTest.cs +++ b/TagCloud.Tests/ParsersTests/BoolParserTests.cs @@ -5,7 +5,7 @@ namespace TagCloud.Tests.ParsersTests { [TestFixture] - internal class BoolParserTest + internal class BoolParserTests { [TestCase("")] [TestCase(" ")] diff --git a/TagCloud.Tests/ParsersTests/ColorParserTest.cs b/TagCloud.Tests/ParsersTests/ColorParserTests.cs similarity index 96% rename from TagCloud.Tests/ParsersTests/ColorParserTest.cs rename to TagCloud.Tests/ParsersTests/ColorParserTests.cs index 956d90e0e..99dac59d5 100644 --- a/TagCloud.Tests/ParsersTests/ColorParserTest.cs +++ b/TagCloud.Tests/ParsersTests/ColorParserTests.cs @@ -6,7 +6,7 @@ namespace TagCloud.Tests.ParsersTests { [TestFixture] - internal class ColorParserTest + internal class ColorParserTests { [TestCase("")] [TestCase(" ")] diff --git a/TagCloud.Tests/ParsersTests/FontParserTest.cs b/TagCloud.Tests/ParsersTests/FontParserTests.cs similarity index 97% rename from TagCloud.Tests/ParsersTests/FontParserTest.cs rename to TagCloud.Tests/ParsersTests/FontParserTests.cs index d360c9bd1..c352a8cbc 100644 --- a/TagCloud.Tests/ParsersTests/FontParserTest.cs +++ b/TagCloud.Tests/ParsersTests/FontParserTests.cs @@ -8,7 +8,7 @@ namespace TagCloud.Tests.ParsersTests { [TestFixture] [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] - internal class FontParserTest + internal class FontParserTests { [TestCase("")] [TestCase(" ")] diff --git a/TagCloud.Tests/ParsersTests/SizeParserTest.cs b/TagCloud.Tests/ParsersTests/SizeParserTests.cs similarity index 98% rename from TagCloud.Tests/ParsersTests/SizeParserTest.cs rename to TagCloud.Tests/ParsersTests/SizeParserTests.cs index 9acac8c41..5ca1142a6 100644 --- a/TagCloud.Tests/ParsersTests/SizeParserTest.cs +++ b/TagCloud.Tests/ParsersTests/SizeParserTests.cs @@ -6,7 +6,7 @@ namespace TagCloud.Tests.ParsersTests { [TestFixture] - internal class SizeParserTest + internal class SizeParserTests { [TestCase("")] [TestCase(" ")] diff --git a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs b/TagCloud.Tests/WordCountersTests/WordCounterTests.cs similarity index 95% rename from TagCloud.Tests/WordCountersTests/WordCounterTest.cs rename to TagCloud.Tests/WordCountersTests/WordCounterTests.cs index d4c1dc36d..eb12fba1d 100644 --- a/TagCloud.Tests/WordCountersTests/WordCounterTest.cs +++ b/TagCloud.Tests/WordCountersTests/WordCounterTests.cs @@ -5,7 +5,7 @@ namespace TagCloud.Tests.WordCountersTests { [TestFixture] - internal class WordCounterTest + internal class WordCounterTests { [Test] public void WordCounter_CountsCorrect() diff --git a/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTests.cs similarity index 95% rename from TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs rename to TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTests.cs index fe54c8994..04e9f06ae 100644 --- a/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTest.cs +++ b/TagCloud.Tests/WordFiltersTests/WordFilterChangeBannedWordsTests.cs @@ -4,7 +4,7 @@ namespace TagCloud.Tests.WordFiltersTests { [TestFixture] - internal class WordFilterChangeBannedWordsTest + internal class WordFilterChangeBannedWordsTests { [Test] public void Clear_ShouldClearBannedWordList() diff --git a/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs b/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTests.cs similarity index 97% rename from TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs rename to TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTests.cs index 6d6f9a437..8f06b4ecd 100644 --- a/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTest.cs +++ b/TagCloud.Tests/WordFiltersTests/WordFilterDefaultBannedWordsTests.cs @@ -4,7 +4,7 @@ namespace TagCloud.Tests.WordFiltersTests { [TestFixture] - internal class WordFilterDefaultBannedWordsTest + internal class WordFilterDefaultBannedWordsTests { private readonly WordFilter wordFilter = new WordFilter(); diff --git a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs b/TagCloud.Tests/WordReadersTests/WordReaderTests.cs similarity index 99% rename from TagCloud.Tests/WordReadersTests/WordReaderTest.cs rename to TagCloud.Tests/WordReadersTests/WordReaderTests.cs index 3167d30f3..62b369eb4 100644 --- a/TagCloud.Tests/WordReadersTests/WordReaderTest.cs +++ b/TagCloud.Tests/WordReadersTests/WordReaderTests.cs @@ -7,7 +7,7 @@ namespace TagCloud.Tests.WordReadersTests { [TestFixture] - internal class WordReaderTest + internal class WordReaderTests { private readonly string directoryPath = "TempFilesForWordReaderTests"; From 1139482a07ff60c77ddf393f7aba0804a7344e42 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Mon, 20 Jan 2025 23:34:30 +0500 Subject: [PATCH 29/32] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=202=20=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPainterTests.cs | 20 +++++++++++++++++++ .../ParsersTests/SizeParserTests.cs | 13 ++++++++++++ 2 files changed, 33 insertions(+) diff --git a/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs b/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs index c0853933f..375029a52 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs @@ -39,5 +39,25 @@ public void Draw_ThrowsException_WithInvalidCases(string errorMessage, IList + { + new Tag( + "Test", + new Rectangle( + new Point(size.Width / 2, size.Height / 2), + new Size(size.Width / 10, size.Height / 10))) + }; + + var painter = new CloudLayouterPainter(size); + var result = painter.Draw(tags); + + result.IsSuccess.Should().BeTrue(); + result.Value.Size.Should().Be(size); + } } } \ No newline at end of file diff --git a/TagCloud.Tests/ParsersTests/SizeParserTests.cs b/TagCloud.Tests/ParsersTests/SizeParserTests.cs index 5ca1142a6..f5271f1d3 100644 --- a/TagCloud.Tests/ParsersTests/SizeParserTests.cs +++ b/TagCloud.Tests/ParsersTests/SizeParserTests.cs @@ -75,5 +75,18 @@ public void ParseSizeDimension_ThrowsException_WithInputLessThanZeroAsInt(int si var actual = SizeParser.ParseSizeDimension(size); actual.Should().BeEquivalentTo(expected); } + + [Test] + public void ParseImageSize_ReturnsCorrectSize() + { + var width = 125; + var height = 55; + var size = $"{width}:{height}"; + var expectedSize = new Size(width, height); + + var result = SizeParser.ParseImageSize(size); + result.IsSuccess.Should().BeTrue(); + result.Value.Should().Be(expectedSize); + } } } From 9557b19a91d6a12eb71181b95aa33babdf40e898 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 21 Jan 2025 12:25:32 +0500 Subject: [PATCH 30/32] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20internal=20=D1=81=D0=B2=D0=BE=D0=B9=D1=81?= =?UTF-8?q?=D1=82=D0=B2=D0=B0=20Value=20=D1=83=20Result=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D0=BF=D1=83=D0=B1=D0=BB=D0=B8=D1=87=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20GetValueOrThrow()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FileSenderRailway/Result.cs | 3 --- .../CloudLayouterPaintersTests/CloudLayouterPainterTests.cs | 2 +- TagCloud.Tests/NormalizersTests/NormalizerTests.cs | 2 +- TagCloud.Tests/ParsersTests/SizeParserTests.cs | 2 +- TagCloud/Factories/WordFilterFactory.cs | 4 ++-- TagCloud/Parsers/SizeParser.cs | 2 +- TagCloud/ProgramExecutor.cs | 2 +- 7 files changed, 7 insertions(+), 10 deletions(-) diff --git a/FileSenderRailway/Result.cs b/FileSenderRailway/Result.cs index 94978f036..da50ce0ad 100644 --- a/FileSenderRailway/Result.cs +++ b/FileSenderRailway/Result.cs @@ -1,8 +1,5 @@ using System; -using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("TagCloud")] -[assembly: InternalsVisibleTo("TagCloud.Tests")] namespace FileSenderRailway; public class None diff --git a/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs b/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs index 375029a52..44136902c 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs @@ -57,7 +57,7 @@ public void Draw_ReturnsBitmapWithCorrectSize() var result = painter.Draw(tags); result.IsSuccess.Should().BeTrue(); - result.Value.Size.Should().Be(size); + result.GetValueOrThrow().Size.Should().Be(size); } } } \ No newline at end of file diff --git a/TagCloud.Tests/NormalizersTests/NormalizerTests.cs b/TagCloud.Tests/NormalizersTests/NormalizerTests.cs index d6ef23783..d925f5f57 100644 --- a/TagCloud.Tests/NormalizersTests/NormalizerTests.cs +++ b/TagCloud.Tests/NormalizersTests/NormalizerTests.cs @@ -76,7 +76,7 @@ public void Normalize_CalculatesСorrectly(int decimalPlaces) } var expected = dict.AsResult(); var actual = normalizer.Normalize(values, defaultMinCoefficient, decimalPlaces); - actual.Value.Should().BeEquivalentTo(expected.Value); + actual.GetValueOrThrow().Should().BeEquivalentTo(expected.GetValueOrThrow()); } } } diff --git a/TagCloud.Tests/ParsersTests/SizeParserTests.cs b/TagCloud.Tests/ParsersTests/SizeParserTests.cs index f5271f1d3..56720349e 100644 --- a/TagCloud.Tests/ParsersTests/SizeParserTests.cs +++ b/TagCloud.Tests/ParsersTests/SizeParserTests.cs @@ -86,7 +86,7 @@ public void ParseImageSize_ReturnsCorrectSize() var result = SizeParser.ParseImageSize(size); result.IsSuccess.Should().BeTrue(); - result.Value.Should().Be(expectedSize); + result.GetValueOrThrow().Should().Be(expectedSize); } } } diff --git a/TagCloud/Factories/WordFilterFactory.cs b/TagCloud/Factories/WordFilterFactory.cs index f2e50a531..97a39734a 100644 --- a/TagCloud/Factories/WordFilterFactory.cs +++ b/TagCloud/Factories/WordFilterFactory.cs @@ -32,7 +32,7 @@ private static Result AddWords( return Result.Fail(wordsResult.Error); } - foreach (var word in wordsResult.Value) + foreach (var word in wordsResult.GetValueOrThrow()) { wordFilter.Add(word.ToLower()); } @@ -54,7 +54,7 @@ private static Result RemoveWords( return Result.Fail(wordsResult.Error); } - foreach (var word in wordsResult.Value) + foreach (var word in wordsResult.GetValueOrThrow()) { wordFilter.Remove(word.ToLower()); } diff --git a/TagCloud/Parsers/SizeParser.cs b/TagCloud/Parsers/SizeParser.cs index d620d50f1..bb8b10650 100644 --- a/TagCloud/Parsers/SizeParser.cs +++ b/TagCloud/Parsers/SizeParser.cs @@ -33,7 +33,7 @@ public static Result ParseImageSize(string size) return Result.Fail(height.Error); } - return new Size(width.Value, height.Value).AsResult(); + return new Size(width.GetValueOrThrow(), height.GetValueOrThrow()).AsResult(); } public static Result ParseSizeDimension(string dimension) diff --git a/TagCloud/ProgramExecutor.cs b/TagCloud/ProgramExecutor.cs index a29d62ff1..c7fbb37a2 100644 --- a/TagCloud/ProgramExecutor.cs +++ b/TagCloud/ProgramExecutor.cs @@ -66,7 +66,7 @@ private Result> ProcessTagsWithWorker(ICloudLayouterWorker worker) return Result.Fail>(tagResult.Error); } - tags.Add(tagResult.Value); + tags.Add(tagResult.GetValueOrThrow()); } return Result.Ok(tags); }) From 42110bb93cd7709e044751b304606c66329b91d3 Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 21 Jan 2025 13:26:25 +0500 Subject: [PATCH 31/32] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BE=20=D0=BE=D0=B3=D1=80=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CloudLayouterPaintersTests/CloudLayouterPainterTests.cs | 5 ++++- .../CircularCloudLayouterMainRequirementsTests.cs | 5 ++++- TagCloud.Tests/ImageSaversTests/ImageSaverTests.cs | 5 ++++- TagCloud.Tests/ParsersTests/FontParserTests.cs | 5 ++++- TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs | 5 ++++- TagCloud/ImageSavers/ImageSaver.cs | 5 ++++- TagCloud/Parsers/FontParser.cs | 5 ++++- TagCloud/ProgramExecutor.cs | 5 ++++- 8 files changed, 32 insertions(+), 8 deletions(-) diff --git a/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs b/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs index 44136902c..acbfeb89f 100644 --- a/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs +++ b/TagCloud.Tests/CloudLayouterPaintersTests/CloudLayouterPainterTests.cs @@ -7,7 +7,10 @@ namespace TagCloud.Tests.CloudLayouterPaintersTests { [TestFixture] - [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage( + "Interoperability", + "CA1416:Проверка совместимости платформы", + Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] internal class CloudLayouterPainterTests { private static readonly TestCaseData[] invalidTestCases = new TestCaseData[] diff --git a/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs b/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs index 4552db6f9..aa26eebd2 100644 --- a/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs +++ b/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs @@ -10,7 +10,10 @@ namespace TagCloud.Tests.CloudLayoutersTests.CircularCloudLayouterTests { [TestFixture] - [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage( + "Interoperability", + "CA1416:Проверка совместимости платформы", + Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] internal class CircularCloudLayouterMainRequirementsTests { private Point center = new Point(); diff --git a/TagCloud.Tests/ImageSaversTests/ImageSaverTests.cs b/TagCloud.Tests/ImageSaversTests/ImageSaverTests.cs index 0d6c09f2a..cc0c4dff4 100644 --- a/TagCloud.Tests/ImageSaversTests/ImageSaverTests.cs +++ b/TagCloud.Tests/ImageSaversTests/ImageSaverTests.cs @@ -8,7 +8,10 @@ namespace TagCloud.Tests.ImageSaversTests { [TestFixture] - [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage( + "Interoperability", + "CA1416:Проверка совместимости платформы", + Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] internal class ImageSaverTests { private readonly string directoryPath = "TempFilesForImageSaverTests"; diff --git a/TagCloud.Tests/ParsersTests/FontParserTests.cs b/TagCloud.Tests/ParsersTests/FontParserTests.cs index c352a8cbc..70b96de16 100644 --- a/TagCloud.Tests/ParsersTests/FontParserTests.cs +++ b/TagCloud.Tests/ParsersTests/FontParserTests.cs @@ -7,7 +7,10 @@ namespace TagCloud.Tests.ParsersTests { [TestFixture] - [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage( + "Interoperability", + "CA1416:Проверка совместимости платформы", + Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] internal class FontParserTests { [TestCase("")] diff --git a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs index 32dac3a19..51b9fe2db 100644 --- a/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs +++ b/TagCloud/CloudLayouterPainters/CloudLayouterPainter.cs @@ -4,7 +4,10 @@ namespace TagCloud.CloudLayouterPainters { - [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage( + "Interoperability", + "CA1416:Проверка совместимости платформы", + Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] internal class CloudLayouterPainter( Size imageSize, Color? backgroundColor = null, diff --git a/TagCloud/ImageSavers/ImageSaver.cs b/TagCloud/ImageSavers/ImageSaver.cs index 40e47a8fc..ed2b9205b 100644 --- a/TagCloud/ImageSavers/ImageSaver.cs +++ b/TagCloud/ImageSavers/ImageSaver.cs @@ -8,7 +8,10 @@ namespace TagCloud.ImageSavers // Реализован пункт на перспективу: // Формат результата. // Поддерживать разные форматы изображений. - [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage( + "Interoperability", + "CA1416:Проверка совместимости платформы", + Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] internal class ImageSaver : IImageSaver { private readonly Dictionary supportedFormats = diff --git a/TagCloud/Parsers/FontParser.cs b/TagCloud/Parsers/FontParser.cs index 1c3af504d..a7f12a7d0 100644 --- a/TagCloud/Parsers/FontParser.cs +++ b/TagCloud/Parsers/FontParser.cs @@ -4,7 +4,10 @@ namespace TagCloud.Parsers { - [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage( + "Interoperability", + "CA1416:Проверка совместимости платформы", + Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] internal static class FontParser { public static Result ParseFont(string font) diff --git a/TagCloud/ProgramExecutor.cs b/TagCloud/ProgramExecutor.cs index c7fbb37a2..410457747 100644 --- a/TagCloud/ProgramExecutor.cs +++ b/TagCloud/ProgramExecutor.cs @@ -9,7 +9,10 @@ namespace TagCloud { - [SuppressMessage("Interoperability", "CA1416:Проверка совместимости платформы", Justification = "<Ожидание>")] + [SuppressMessage( + "Interoperability", + "CA1416:Проверка совместимости платформы", + Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] internal class ProgramExecutor( string backgroundColor, string textColor, From b2fbe3b8b6d1e8ad75202ba09e071682ec3e384d Mon Sep 17 00:00:00 2001 From: Zakhar Date: Tue, 21 Jan 2025 14:34:47 +0500 Subject: [PATCH 32/32] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20CircularCloudLayouterTest.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Тесты с CircularCloudLayouterMainRequirementsTest добавлены в CircularCloudLayouterTest. 2. Рефакторинг CircularCloudLayouterTest. --- ...cularCloudLayouterMainRequirementsTests.cs | 197 ------------------ .../CircularCloudLayouterTests.cs | 168 +++++++++++++++ TagCloud.Tests/Utilities/RectangleSetupper.cs | 37 ++++ 3 files changed, 205 insertions(+), 197 deletions(-) delete mode 100644 TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs create mode 100644 TagCloud.Tests/Utilities/RectangleSetupper.cs diff --git a/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs b/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs deleted file mode 100644 index aa26eebd2..000000000 --- a/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterMainRequirementsTests.cs +++ /dev/null @@ -1,197 +0,0 @@ -using FluentAssertions; -using System.Diagnostics.CodeAnalysis; -using System.Drawing; -using TagCloud.CloudLayouterPainters; -using TagCloud.CloudLayouters.CircularCloudLayouter; -using TagCloud.CloudLayouterWorkers; -using TagCloud.ImageSavers; -using TagCloud.Tests.Extensions; - -namespace TagCloud.Tests.CloudLayoutersTests.CircularCloudLayouterTests -{ - [TestFixture] - [SuppressMessage( - "Interoperability", - "CA1416:Проверка совместимости платформы", - Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] - internal class CircularCloudLayouterMainRequirementsTests - { - private Point center = new Point(); - private Rectangle[] rectangles; - private List tags; - private readonly string failedTestsDirectory = "FailedTest"; - - private readonly ImageSaver imageSaver = new ImageSaver(); - private readonly CloudLayouterPainter cloudLayouterPainter - = new CloudLayouterPainter(new Size(5000, 5000)); - - [OneTimeSetUp] - public void Init() - { - Directory.CreateDirectory(failedTestsDirectory); - } - - [SetUp] - public void SetUp() - { - var minRectangleWidth = 30; - var maxRectangleWidth = 70; - var minRectangleHeight = 20; - var maxRectangleHeight = 50; - var rectanglesCount = 1000; - - tags = new List(); - var circularCloudLayouter = new CircularCloudLayouter(); - - var randomWorker = new RandomCloudLayouterWorker( - minRectangleWidth, - maxRectangleWidth, - minRectangleHeight, - maxRectangleHeight); - foreach (var rectangleProperty in randomWorker - .GetNextRectangleProperties().GetValueOrThrow().Take(rectanglesCount)) - { - tags.Add( - new Tag( - rectangleProperty.word, - circularCloudLayouter.PutNextRectangle(rectangleProperty.size) - .GetValueOrThrow())); - } - rectangles = tags.Select(x => x.Rectangle).ToArray(); - } - - [Test] - [Repeat(10)] - public void ShouldPlaceRectanglesInCircle() - { - var expectedCoverageRatio = 0.7; - var gridSize = 1000; - - var maxRadius = rectangles.Max( - x => x.GetMaxDistanceFromPointToRectangleAngles(center)); - var step = 2 * maxRadius / gridSize; - - var occupancyGrid = GetOccupancyGrid(gridSize, maxRadius, step); - - var actualCoverageRatio = GetOccupancyGridRatio(occupancyGrid, maxRadius, step); - actualCoverageRatio.Should().BeGreaterThanOrEqualTo(expectedCoverageRatio); - } - - [Test] - [Repeat(10)] - public void ShouldPlaceCenterOfMassOfRectanglesNearCenter() - { - var tolerance = 15; - - var centerX = rectangles.Average(r => r.Left + r.Width / 2.0); - var centerY = rectangles.Average(r => r.Top + r.Height / 2.0); - var actualCenter = new Point((int)centerX, (int)centerY); - - var distance = Math.Sqrt(Math.Pow(actualCenter.X - center.X, 2) - + Math.Pow(actualCenter.Y - center.Y, 2)); - - distance.Should().BeLessThanOrEqualTo(tolerance); - } - - [Test] - [Repeat(10)] - public void ShouldPlaceRectanglesWithoutOverlap() - { - for (var i = 0; i < rectangles.Length; i++) - { - for (var j = i + 1; j < rectangles.Length; j++) - { - Assert.That( - rectangles[i].IntersectsWith(rectangles[j]), - Is.EqualTo(false), - $"Прямоугольники пересекаются:\n" + - $"{rectangles[i]}\n" + - $"{rectangles[j]}"); - } - } - } - - [TearDown] - public void Cleanup() - { - if (TestContext.CurrentContext.Result.FailCount == 0) - { - return; - } - - var name = $"{TestContext.CurrentContext.Test.Name}.png"; - var path = Path.Combine(failedTestsDirectory, name); - imageSaver.SaveFile(cloudLayouterPainter.Draw(tags).GetValueOrThrow(), path); - Console.WriteLine($"Tag cloud visualization saved to file {path}"); - } - - [OneTimeTearDown] - public void OneTimeCleanup() - { - if (Directory.Exists(failedTestsDirectory) - && Directory.GetFiles(failedTestsDirectory).Length == 0) - { - Directory.Delete(failedTestsDirectory); - } - } - - private (int start, int end) GetGridIndexesInterval( - int rectangleStartValue, - int rectangleCorrespondingSize, - double maxRadius, - double step) - { - var start = (int)((rectangleStartValue - center.X + maxRadius) / step); - var end = (int)((rectangleStartValue - + rectangleCorrespondingSize - center.X + maxRadius) / step); - return (start, end); - } - - private bool[,] GetOccupancyGrid(int gridSize, double maxRadius, double step) - { - var result = new bool[gridSize, gridSize]; - foreach (var rect in rectangles) - { - var xInterval = GetGridIndexesInterval(rect.X, rect.Width, maxRadius, step); - var yInterval = GetGridIndexesInterval(rect.Y, rect.Height, maxRadius, step); - for (var x = xInterval.start; x <= xInterval.end; x++) - { - for (var y = yInterval.start; y <= yInterval.end; y++) - { - result[x, y] = true; - } - } - } - return result; - } - - private double GetOccupancyGridRatio(bool[,] occupancyGrid, double maxRadius, double step) - { - var totalCellsInsideCircle = 0; - var coveredCellsInsideCircle = 0; - for (var x = 0; x < occupancyGrid.GetLength(0); x++) - { - for (var y = 0; y < occupancyGrid.GetLength(0); y++) - { - var cellCenterX = x * step - maxRadius + center.X; - var cellCenterY = y * step - maxRadius + center.Y; - - var distance = Math.Sqrt( - Math.Pow(cellCenterX - center.X, 2) + Math.Pow(cellCenterY - center.Y, 2)); - - if (distance > maxRadius) - { - continue; - } - - totalCellsInsideCircle += 1; - if (occupancyGrid[x, y]) - { - coveredCellsInsideCircle += 1; - } - } - } - return (double)coveredCellsInsideCircle / totalCellsInsideCircle; - } - } -} diff --git a/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterTests.cs b/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterTests.cs index e3883775a..1c3822495 100644 --- a/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterTests.cs +++ b/TagCloud.Tests/CloudLayoutersTests/CircularCloudLayouterTests/CircularCloudLayouterTests.cs @@ -1,13 +1,91 @@ using FileSenderRailway; using FluentAssertions; +using System.Diagnostics.CodeAnalysis; using System.Drawing; +using TagCloud.CloudLayouterPainters; using TagCloud.CloudLayouters.CircularCloudLayouter; +using TagCloud.ImageSavers; +using TagCloud.Tests.Extensions; +using TagCloud.Tests.Utilities; namespace TagCloud.Tests.CloudLayoutersTests.CircularCloudLayouterTests { [TestFixture] + [SuppressMessage( + "Interoperability", + "CA1416:Проверка совместимости платформы", + Justification = "Код предназначен для выполнения только на Windows 6.1 и новее")] internal class CircularCloudLayouterTests { + private RectangleSetupper? rectangleSetupper; + private Point center = new Point(); + private readonly string failedTestsDirectory = "FailedTest"; + + private readonly ImageSaver imageSaver = new ImageSaver(); + private readonly CloudLayouterPainter cloudLayouterPainter + = new CloudLayouterPainter(new Size(3000, 3000)); + + [OneTimeSetUp] + public void Init() + => Directory.CreateDirectory(failedTestsDirectory); + + [Test] + [Repeat(10)] + public void ShouldPlaceRectanglesInCircle() + { + rectangleSetupper = new RectangleSetupper(); + var rectangles = rectangleSetupper.Rectangles(); + var expectedCoverageRatio = 0.7; + var gridSize = 1000; + + var maxRadius = rectangles.Max( + x => x.GetMaxDistanceFromPointToRectangleAngles(center)); + var step = 2 * maxRadius / gridSize; + + var occupancyGrid = GetOccupancyGrid(gridSize, maxRadius, step, rectangles); + + var actualCoverageRatio = GetOccupancyGridRatio(occupancyGrid, maxRadius, step); + actualCoverageRatio.Should().BeGreaterThanOrEqualTo(expectedCoverageRatio); + } + + [Test] + [Repeat(10)] + public void ShouldPlaceCenterOfMassOfRectanglesNearCenter() + { + rectangleSetupper = new RectangleSetupper(); + var rectangles = rectangleSetupper.Rectangles(); + var tolerance = 15; + + var centerX = rectangles.Average(r => r.Left + r.Width / 2.0); + var centerY = rectangles.Average(r => r.Top + r.Height / 2.0); + var actualCenter = new Point((int)centerX, (int)centerY); + + var distance = Math.Sqrt(Math.Pow(actualCenter.X - center.X, 2) + + Math.Pow(actualCenter.Y - center.Y, 2)); + + distance.Should().BeLessThanOrEqualTo(tolerance); + } + + [Test] + [Repeat(10)] + public void ShouldPlaceRectanglesWithoutOverlap() + { + rectangleSetupper = new RectangleSetupper(); + var rectangles = rectangleSetupper.Rectangles(); + for (var i = 0; i < rectangles.Length; i++) + { + for (var j = i + 1; j < rectangles.Length; j++) + { + Assert.That( + rectangles[i].IntersectsWith(rectangles[j]), + Is.EqualTo(false), + $"Прямоугольники пересекаются:\n" + + $"{rectangles[i]}\n" + + $"{rectangles[j]}"); + } + } + } + [TestCase(0, 100)] [TestCase(-1, 100)] [TestCase(100, 0)] @@ -16,11 +94,101 @@ public void PutNextRectangle_ThrowsException_OnAnyNegativeOrZeroSize( int width, int height) { + rectangleSetupper = null; var size = new Size(width, height); var expected = Result.Fail( "Размеры прямоугольника не могут быть меньше либо равны нуля"); var actual = new CircularCloudLayouter().PutNextRectangle(size); actual.Should().BeEquivalentTo(expected); } + + [TearDown] + public void Cleanup() + { + if (TestContext.CurrentContext.Result.FailCount == 0 + || rectangleSetupper is null) + { + return; + } + + var name = $"{TestContext.CurrentContext.Test.Name}.png"; + var path = Path.Combine(failedTestsDirectory, name); + imageSaver.SaveFile( + cloudLayouterPainter.Draw(rectangleSetupper.Tags).GetValueOrThrow(), path); + Console.WriteLine($"Tag cloud visualization saved to file {path}"); + } + + [OneTimeTearDown] + public void OneTimeCleanup() + { + if (Directory.Exists(failedTestsDirectory) + && Directory.GetFiles(failedTestsDirectory).Length == 0) + { + Directory.Delete(failedTestsDirectory); + } + } + + private (int start, int end) GetGridIndexesInterval( + int rectangleStartValue, + int rectangleCorrespondingSize, + double maxRadius, + double step) + { + var start = (int)((rectangleStartValue - center.X + maxRadius) / step); + var end = (int)((rectangleStartValue + + rectangleCorrespondingSize - center.X + maxRadius) / step); + return (start, end); + } + + private bool[,] GetOccupancyGrid( + int gridSize, + double maxRadius, + double step, + Rectangle[] rectangles) + { + var result = new bool[gridSize, gridSize]; + foreach (var rect in rectangles) + { + var xInterval = GetGridIndexesInterval(rect.X, rect.Width, maxRadius, step); + var yInterval = GetGridIndexesInterval(rect.Y, rect.Height, maxRadius, step); + for (var x = xInterval.start; x <= xInterval.end; x++) + { + for (var y = yInterval.start; y <= yInterval.end; y++) + { + result[x, y] = true; + } + } + } + return result; + } + + private double GetOccupancyGridRatio(bool[,] occupancyGrid, double maxRadius, double step) + { + var totalCellsInsideCircle = 0; + var coveredCellsInsideCircle = 0; + for (var x = 0; x < occupancyGrid.GetLength(0); x++) + { + for (var y = 0; y < occupancyGrid.GetLength(0); y++) + { + var cellCenterX = x * step - maxRadius + center.X; + var cellCenterY = y * step - maxRadius + center.Y; + + var distance = Math.Sqrt( + Math.Pow(cellCenterX - center.X, 2) + Math.Pow(cellCenterY - center.Y, 2)); + + if (distance > maxRadius) + { + continue; + } + + totalCellsInsideCircle += 1; + if (occupancyGrid[x, y]) + { + coveredCellsInsideCircle += 1; + } + } + } + return (double)coveredCellsInsideCircle / totalCellsInsideCircle; + } } } diff --git a/TagCloud.Tests/Utilities/RectangleSetupper.cs b/TagCloud.Tests/Utilities/RectangleSetupper.cs new file mode 100644 index 000000000..2e2038103 --- /dev/null +++ b/TagCloud.Tests/Utilities/RectangleSetupper.cs @@ -0,0 +1,37 @@ +using System.Drawing; +using TagCloud.CloudLayouters.CircularCloudLayouter; +using TagCloud.CloudLayouterWorkers; + +namespace TagCloud.Tests.Utilities +{ + internal class RectangleSetupper + { + private readonly List tags = new List(); + public List Tags => tags.ToList(); + public Rectangle[] Rectangles() => tags.Select(x => x.Rectangle).ToArray(); + + public RectangleSetupper( + int minRectangleWidth = 30, + int maxRectangleWidth = 70, + int minRectangleHeight = 20, + int maxRectangleHeight = 50, + int rectanglesCount = 1000) + { + var circularCloudLayouter = new CircularCloudLayouter(); + var randomWorker = new RandomCloudLayouterWorker( + minRectangleWidth, + maxRectangleWidth, + minRectangleHeight, + maxRectangleHeight); + foreach (var rectangleProperty in randomWorker + .GetNextRectangleProperties().GetValueOrThrow().Take(rectanglesCount)) + { + tags.Add( + new Tag( + "Test", + circularCloudLayouter.PutNextRectangle(rectangleProperty.size) + .GetValueOrThrow())); + } + } + } +}