Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Шевырин Никита #245

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NUnit.Framework.Interfaces;

namespace TagsCloudVisualization.Tests;

[TestFixture]
[TestOf(typeof(CircularCloudLayouterImpl))]
public class CircularCloudLayouterTests
{

private static readonly string FailReportFolderPath = "./failed";
private static readonly int MaxDistanceFromBarycenter = 20;

private ICircularCloudLayouter _circularCloudLayouter;

[SetUp]
public void SetUp()
{
_circularCloudLayouter = new CircularCloudLayouterImpl(Point.Empty);
}

[OneTimeSetUp]
public void EmptyFailReportFolder()
{
if (Directory.Exists(FailReportFolderPath))
Directory.Delete(FailReportFolderPath, true);
}

[TearDown]
public void ReportFailures()
{
var context = TestContext.CurrentContext;
if (context.Result.Outcome.Status == TestStatus.Failed)
{
if (context.Test.MethodName == null)
{
Console.WriteLine("Teardown error: test method name is null");
return;
}

var testType = DetermineTestType(context.Test.MethodName);
if (testType == TestType.NoTearDown)
return;

var rectangles = _circularCloudLayouter.Layout.ToArray();

var savingPath = $"{FailReportFolderPath}/{context.Test.Name}.png";
Directory.CreateDirectory(FailReportFolderPath);

var args = context.Test.Arguments;
var center = new Point((int) args[0]!, (int) args[1]!);

#pragma warning disable CA1416

new Bitmap(1000, 1000)
.DrawFailedTestImage(
rectangles.ToArray(),
center,
ComputeBaryCenter(rectangles),
MaxDistanceFromBarycenter,
testType)
.Save(savingPath, ImageFormat.Png);

#pragma warning restore CA1416

Console.WriteLine($"Failure was reported to {Path.GetFullPath(savingPath)}");
}
}

private static TestType DetermineTestType(string methodName)
{
if (methodName == nameof(PutNextRectangle_ThrowsOnHeightOrWidth_BeingLessOrEqualToZero))
return TestType.NoTearDown;

if (methodName == nameof(RectanglesCommonBarycenterIsCloseToTheProvidedCenter))
return TestType.BarycenterTest;

return TestType.OtherTest;
}

[Test]
[Description("Проверяем, что поле CloudCenter бросает исключение, " +
"если процесс генерации облака уже начался")]
public void CloudCenter_ThrowsIfLayoutContainsGeneratedRectangles()
{
_circularCloudLayouter.PutNextRectangle(new Size(1, 1));

Action act = () => _circularCloudLayouter.CloudCenter = new Point(1, 1);

act.Should().Throw<InvalidOperationException>()
.WithMessage("Can not change cloud center after generation start");
}

[Test]
[Description("Проверяем, что метод PutNextRectangle бросает ArgumentException, " +
"если ему передан размер прямоугольника с высотой или шириной " +
"меньше либо равной нулю")]
[TestCase(0, -4, 0, 4)]
[TestCase(0, 1, -2, 4)]
[TestCase(0, 0, 4, -2)]
[TestCase(2, 3, 2, 0)]
[TestCase(1, 2, -2, -1)]
[TestCase(-1, 0, -1, 0)]
public void PutNextRectangle_ThrowsOnHeightOrWidth_BeingLessOrEqualToZero(
int centerX,
int centerY,
int width,
int height)
{
Arrange(centerX, centerY);

Action act = () => _circularCloudLayouter.PutNextRectangle(new Size(width, height));

act.Should()
.Throw<ArgumentOutOfRangeException>();
}

[Test]
[Description("Проверяем, что прямоугольники не пересекаются друг с другом")]
[TestCaseSource(typeof(TestCaseData), nameof(TestCaseData.IntersectionTestSource))]
public void RectanglesShouldNotIntersectEachOther(
int centerX,
int centerY,
(int, int)[] sizes)
{
Arrange(centerX, centerY);

var rectangles = GenerateTestLayout(sizes);

Assert_RectanglesDoNotIntersect(rectangles);
}

[Test]
[Description("Проверяем, что центр прямоугольника, размер которого был передан первым " +
"совпадает с центром, переданным в аргумент конструктора CircularCloudLayouter")]
[TestCaseSource(typeof(TestCaseData), nameof(TestCaseData.FirstRectanglePositionTestSource))]
public void FirstRectangleShouldBePositionedAtProvidedCenter(
int centerX,
int centerY,
(int, int)[] sizes)
{
Arrange(centerX, centerY);

var rectangles = GenerateTestLayout(sizes);

Assert_FirstRectangleIsPositionedAtProvidedCenter(rectangles, new Point(centerX, centerY));
}

[Test]
[Description("Проверяем, что прямоугольники расположены наиболее плотно, " +
"то есть максимум из попарных расстояний между центрами " +
"прямоугольников не превышает maxDistance")]
[TestCaseSource(typeof(TestCaseData), nameof(TestCaseData.DensityTestSource))]
public void RectanglesShouldBeCloseToEachOther(
int centerX,
int centerY,
int maxDistance,
(int, int)[] sizes)
{
Arrange(centerX, centerY);

var rectangles = GenerateTestLayout(sizes);

Assert_RectanglesArePositionedCloseToEachOther(rectangles, maxDistance);
}

[Test]
[Description("Проверяем, что общий центр масс всех прямоугольников находится " +
"рядом с центром, переданным в конструктор CircularCloudLayouter")]
[TestCaseSource(typeof(TestCaseData), nameof(TestCaseData.CenterTestSource))]
public void RectanglesCommonBarycenterIsCloseToTheProvidedCenter(
int centerX,
int centerY,
(int, int)[] sizes)
{
Arrange(centerX, centerY);

var rectangles = GenerateTestLayout(sizes);

Assert_RectanglesBarycenterIsCloseToCenter(rectangles, new Point(centerX, centerY));
}

private void Arrange(int centerX, int centerY)
{
_circularCloudLayouter.CloudCenter = new Point(centerX, centerY);
}

private IEnumerable<Rectangle> GenerateTestLayout((int, int)[] sizes)
{
return _circularCloudLayouter.GenerateLayout(
sizes.Select(size => new Size(size.Item1, size.Item2)).ToArray());
}

private void Assert_RectanglesDoNotIntersect(IEnumerable<Rectangle> rectangles)
{
rectangles.CheckForAllPairs(pair => !pair.Item1.IntersectsWith(pair.Item2))
.Should()
.BeTrue();
}

private void Assert_FirstRectangleIsPositionedAtProvidedCenter(IEnumerable<Rectangle> rectangles, Point center)
{
rectangles
.First()
.RectangleCenter()
.Should()
.BeEquivalentTo(center);
}

private void Assert_RectanglesArePositionedCloseToEachOther(
IEnumerable<Rectangle> rectangles,
int maxDistance)
{
rectangles.CheckForAllPairs(pair => pair.Item1.DistanceToOtherIsNotGreaterThan(pair.Item2, maxDistance))
.Should()
.BeTrue();
}

private Point ComputeBaryCenter(IEnumerable<Rectangle> rectangles)
{
var (totalX, totalY, count) = rectangles
.Aggregate((0, 0, 0), ((int totalX, int totalY, int count) res, Rectangle rect) =>
{
var rectCenter = rect.RectangleCenter();
return (res.totalX + rectCenter.X, res.totalY + rectCenter.Y, ++res.count);
});
return new Point(totalX / count, totalY / count);
}

private void Assert_RectanglesBarycenterIsCloseToCenter(
IEnumerable<Rectangle> rectangles,
Point center)
{
var barycenter = ComputeBaryCenter(rectangles);
var deviationFromCenter = barycenter.SquaredDistanceTo(center);
deviationFromCenter.Should().BeLessOrEqualTo(MaxDistanceFromBarycenter * MaxDistanceFromBarycenter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
</ItemGroup>

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

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Drawing;

namespace TagsCloudVisualization.Tests;

internal static class TestBitmapExtensions
{
public static Bitmap DrawFailedTestImage(
this Bitmap bitmap,
Rectangle[] rectangles,
Point centerPoint,
Point barycenterPoint,
int maxDistanceFromBarycenter,
TestType testType)
{
#pragma warning disable CA1416
bitmap.DrawRectangles(rectangles, new Pen(Color.Blue));

if (testType != TestType.BarycenterTest)
return bitmap;

bitmap.DrawEllipse(
new Rectangle(centerPoint.X, centerPoint.Y, maxDistanceFromBarycenter, maxDistanceFromBarycenter),
new Pen(Color.Lime));
bitmap.DrawEllipse(
new Rectangle(barycenterPoint.X, barycenterPoint.Y, 1, 1),
new Pen(Color.Red));
#pragma warning restore CA1416
return bitmap;
}
}
Loading