mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-01-01 16:59:35 +01:00
* Tons of API doc updates * Removed stale test * Removed stale tests * Fixed Skipped Shadow test 1 * Fixed Skipped Shadow test 2 * Fixed Skipped Shadow test 3 * Removed stale test * Removed stale test2 * Explicit unregister of event handler on Application.Driver!.ClearedContents * Added Toplevels to dict * code cleanup * spelling error * Removed stale test3 * Removed stale test4 * Removed stale test5 * added script * tweaked script * tweaked script * Created StressTests project; moved some tests * Created IntegrationTests project; moved some tests * New yml * made old yml just unit tests * Tweaked Button_IsDefault_Raises_Accepted_Correctly * tweaked script * cleaned up ymls * tweakled up ymls * stress tests... * stress tests on ubuntu only * Fixed WindowsDriver in InvokeLeakTest * Fixed WindowsDriver in InvokeLeakTest2 * Added Directory.Packages.props. Added Directory.Build.props * Shortened StressTest time * Removed dupe file. * DemoFiles * Moved all tests to ./Tests dir. * Fixed release build issue * Fixed .sln file * Fixed .sl* files * Fixing ymls * Fixing interation tests * Create link to the file TestHelpers. * Created Tests/UnitTestsParallelizable. Moved all obviously parallelizable tests. Updated yml. * fixing logs * fixing logs2 * fixing logs3 * don't require stress to pass for PRs * Fix a failure? * tweaked script * Coudl this be it? * Moved tons of tests to parallelizable * Fixed some stuff * Script to find duplicate tests * Testing workflows * Updated to v4 * Fix RelativeBasePath issue * Replace powershell to pwsh * Add ignore projects. * Removed dupe unit tests * Code cleanup of tests * Cleaned up test warnings * yml tweak * Moved setter * tweak ymls * just randomly throwing spaghetti at a wall * Enable runing 5 test runners in par * Turned off DEBUG_DISPOSABLE for par tests * RunningUnitTests=true * code cleanup (forcing more Action runs) * DISABLE_DEBUG_IDISPOSABLE * Added View.DebugIDisposable. False by default. * Remobed bogus tareet * Remobed bogus tareet2 * fixed warning * added api doc * fixed warning * fixed warning * fixed warning2 * fixed warning3 * fixed warning4 --------- Co-authored-by: BDisp <bd.bdisp@gmail.com>
This commit is contained in:
166
Tests/UnitTestsParallelizable/Drawing/Region/DifferenceTests.cs
Normal file
166
Tests/UnitTestsParallelizable/Drawing/Region/DifferenceTests.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Terminal.Gui.DrawingTests;
|
||||
|
||||
public class DifferenceTests
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public void Difference_Rectangle_ExcludesFromRegion ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
region.Combine (new Rectangle (20, 20, 20, 20), RegionOp.Difference);
|
||||
Assert.False (region.Contains (25, 25));
|
||||
Assert.True (region.Contains (15, 15));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Difference_Region_ExcludesRegions ()
|
||||
{
|
||||
var region1 = new Region (new (10, 10, 50, 50));
|
||||
var region2 = new Region (new (20, 20, 20, 20));
|
||||
region1.Combine (region2, RegionOp.Difference);
|
||||
Assert.False (region1.Contains (25, 25));
|
||||
Assert.True (region1.Contains (15, 15));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Difference_WithRectangle_ExcludesRectangle ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
var rect = new Rectangle (30, 30, 50, 50);
|
||||
|
||||
region.Combine (rect, RegionOp.Difference);
|
||||
|
||||
Assert.True (region.Contains (20, 20));
|
||||
Assert.False (region.Contains (35, 35));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Difference_WithRegion_ExcludesRegion ()
|
||||
{
|
||||
var region1 = new Region (new (10, 10, 50, 50));
|
||||
var region2 = new Region (new (30, 30, 50, 50));
|
||||
|
||||
region1.Combine (region2, RegionOp.Difference);
|
||||
|
||||
Assert.True (region1.Contains (20, 20));
|
||||
Assert.False (region1.Contains (35, 35));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Difference_ContainedRectangle_ExcludesRectangle ()
|
||||
{
|
||||
|
||||
/*
|
||||
INPUT: (top-left origin, x→, y↓):
|
||||
|
||||
x=0 1 2 3 4 5
|
||||
y=0 A A A A A A
|
||||
y=1 A A A A A A
|
||||
y=2 A A B B A A
|
||||
y=3 A A B B A A
|
||||
y=4 A A A A A A
|
||||
y=5 A A A A A A
|
||||
|
||||
*/
|
||||
|
||||
|
||||
var regionA = new Region (new (0, 0, 6, 6));
|
||||
var rectangleB = new Rectangle (2, 2, 2, 2);
|
||||
|
||||
regionA.Combine (rectangleB, RegionOp.Difference);
|
||||
|
||||
Assert.True (regionA.Contains (0, 0));
|
||||
Assert.True (regionA.Contains (1, 1));
|
||||
Assert.True (regionA.Contains (4, 4));
|
||||
Assert.True (regionA.Contains (5, 5));
|
||||
|
||||
Assert.False (regionA.Contains (2, 2));
|
||||
Assert.False (regionA.Contains (3, 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Difference_ContainedRegion_ExcludesRegion ()
|
||||
{
|
||||
|
||||
/*
|
||||
INPUT: (top-left origin, x→, y↓):
|
||||
|
||||
x=0 1 2 3 4 5
|
||||
y=0 A A A A A A
|
||||
y=1 A A A A A A
|
||||
y=2 A A B B A A
|
||||
y=3 A A B B A A
|
||||
y=4 A A A A A A
|
||||
y=5 A A A A A A
|
||||
|
||||
*/
|
||||
|
||||
|
||||
var regionA = new Region (new (0, 0, 6, 6));
|
||||
var regionB = new Region (new (2, 2, 2, 2));
|
||||
|
||||
regionA.Combine (regionB, RegionOp.Difference);
|
||||
|
||||
Assert.True (regionA.Contains (0, 0));
|
||||
Assert.True (regionA.Contains (1, 1));
|
||||
Assert.True (regionA.Contains (4, 4));
|
||||
Assert.True (regionA.Contains (5, 5));
|
||||
|
||||
Assert.False (regionA.Contains (2, 2));
|
||||
Assert.False (regionA.Contains (3, 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Difference_NonRectangularRegion_ExcludesRegion ()
|
||||
{
|
||||
|
||||
/*
|
||||
INPUT: (top-left origin, x→, y↓):
|
||||
|
||||
x=0 1 2 3 4 5
|
||||
y=0 A A A A A A
|
||||
y=1 A A A A A A
|
||||
y=2 A A B B A A
|
||||
y=3 A A B A A A
|
||||
y=4 A A A A A A
|
||||
y=5 A A A A A A
|
||||
|
||||
*/
|
||||
|
||||
var regionA = new Region (new (0, 0, 6, 6));
|
||||
|
||||
var regionB = new Region ();
|
||||
regionB.Combine (new Rectangle (2, 2, 2, 1), RegionOp.MinimalUnion);
|
||||
regionB.Combine (new Rectangle (2, 3, 1, 1), RegionOp.MinimalUnion);
|
||||
|
||||
// regionB is a non-rectangular region that looks like this:
|
||||
// x= 0 1 2 3 4 5
|
||||
// y=0 . . . . . .
|
||||
// y=1 . . . . . .
|
||||
// y=2 . . B B . .
|
||||
// y=3 . . B . . .
|
||||
// y=4 . . . . . .
|
||||
// y=5 . . . . . .
|
||||
|
||||
Assert.True (regionB.Contains (2, 2));
|
||||
Assert.True (regionB.Contains (3, 2));
|
||||
Assert.True (regionB.Contains (2, 3));
|
||||
Assert.False (regionB.Contains (3, 3));
|
||||
|
||||
regionA.Combine (regionB, RegionOp.Difference);
|
||||
|
||||
Assert.True (regionA.Contains (0, 0));
|
||||
Assert.True (regionA.Contains (1, 1));
|
||||
Assert.True (regionA.Contains (3, 3));
|
||||
Assert.True (regionA.Contains (4, 4));
|
||||
Assert.True (regionA.Contains (5, 5));
|
||||
|
||||
Assert.False (regionA.Contains (2, 2));
|
||||
Assert.False (regionA.Contains (3, 2));
|
||||
Assert.False (regionA.Contains (2, 3));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
namespace Terminal.Gui.DrawingTests;
|
||||
|
||||
|
||||
public class MergeRectanglesTests
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public void MergeRectangles_ComplexAdjacentRectangles_NoOverlap ()
|
||||
{
|
||||
/*
|
||||
INPUT: Complex arrangement of four adjacent rectangles forming a hollow square ring.
|
||||
Top-left origin (0,0), x→, y↓:
|
||||
|
||||
x=0 1 2 3 4
|
||||
y=0 A A A
|
||||
y=1 B C
|
||||
y=2 B C
|
||||
y=3 B C
|
||||
y=4 D D D
|
||||
|
||||
Rectangles (width × height):
|
||||
A: (1,0,3,1) // top edge
|
||||
B: (0,1,1,3) // left edge
|
||||
C: (4,1,1,3) // right edge
|
||||
D: (1,4,3,1) // bottom edge
|
||||
|
||||
They only touch corners or edges, with no overlapping areas.
|
||||
The expected result is exactly these four rectangles, unmerged.
|
||||
*/
|
||||
|
||||
List<Rectangle> rectangles = new ()
|
||||
{
|
||||
new (1, 0, 3, 1), // A
|
||||
new (0, 1, 1, 3), // B
|
||||
new (4, 1, 1, 3), // C
|
||||
new (1, 4, 3, 1) // D
|
||||
};
|
||||
|
||||
List<Rectangle> merged = Region.MergeRectangles (rectangles, false);
|
||||
|
||||
// Because there's no overlapping area, the method shouldn't merge any of them.
|
||||
Assert.Equal (4, merged.Count);
|
||||
Assert.Contains (new (1, 0, 3, 1), merged);
|
||||
Assert.Contains (new (0, 1, 1, 3), merged);
|
||||
Assert.Contains (new (4, 1, 1, 3), merged);
|
||||
Assert.Contains (new (1, 4, 3, 1), merged);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeRectangles_ComplexContainedRectangles_AllMergeIntoBoundingRect ()
|
||||
{
|
||||
/*
|
||||
INPUT: (top-left origin, x→, y↓):
|
||||
|
||||
x=0 1 2 3 4 5
|
||||
y=0 A A A A A A
|
||||
y=1 A . . . . A
|
||||
y=2 A . B B . A
|
||||
y=3 A . B B . A
|
||||
y=4 A . . . C C
|
||||
y=5 A A A A C C
|
||||
|
||||
Where:
|
||||
A = (0,0,6,6) // Large bounding rectangle
|
||||
B = (2,2,2,2) // Fully contained inside A
|
||||
C = (4,4,2,2) // Also fully contained inside A
|
||||
*/
|
||||
|
||||
List<Rectangle> rectangles = new ()
|
||||
{
|
||||
new (0, 0, 6, 6), // A
|
||||
new (2, 2, 2, 2), // B inside A
|
||||
new (4, 4, 2, 2) // C inside A
|
||||
};
|
||||
|
||||
List<Rectangle> merged = Region.MergeRectangles (rectangles, minimize: false);
|
||||
|
||||
/*
|
||||
OUTPUT: The expected result should be a minimal set of non-overlapping rectangles
|
||||
that cover the same area as the input rectangles.
|
||||
|
||||
x=0 1 2 3 4 5
|
||||
y=0 a a b b c c
|
||||
y=1 a a b b c c
|
||||
y=2 a a b b c c
|
||||
y=3 a a b b c c
|
||||
y=4 a a b b c c
|
||||
y=5 a a b b c c
|
||||
|
||||
*/
|
||||
|
||||
Assert.Equal (3, merged.Count);
|
||||
Assert.Contains (new (0, 0, 2, 6), merged); // a
|
||||
Assert.Contains (new (2, 0, 2, 6), merged); // b
|
||||
Assert.Contains (new (4, 0, 2, 6), merged); // c
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeRectangles_ComplexOverlap_ReturnsMergedRectangles ()
|
||||
{
|
||||
/*
|
||||
INPUT: Visual diagram treating (0,0) as top-left, x increasing to the right, y increasing downward:
|
||||
|
||||
x=0 1 2 3 4 5 6 ...
|
||||
y=0 A A
|
||||
y=1 A B B
|
||||
y=2 B B
|
||||
y=3 C C
|
||||
y=4 C D D
|
||||
y=5 D D
|
||||
|
||||
A overlaps B slightly; C overlaps D slightly. The union of A & B forms one rectangle,
|
||||
and the union of C & D forms another.
|
||||
*/
|
||||
|
||||
List<Rectangle> rectangles = new ()
|
||||
{
|
||||
// A
|
||||
new (0, 0, 2, 2),
|
||||
|
||||
// B
|
||||
new (1, 1, 2, 2),
|
||||
|
||||
// C
|
||||
new (3, 3, 2, 2),
|
||||
|
||||
// D
|
||||
new (4, 4, 2, 2)
|
||||
};
|
||||
|
||||
List<Rectangle> merged = Region.MergeRectangles (rectangles, false);
|
||||
|
||||
/*
|
||||
OUTPUT: Merged fragments (top-left origin, x→, y↓).
|
||||
Lowercase letters a..f show the six sub-rectangles:
|
||||
|
||||
x=0 1 2 3 4 5
|
||||
y=0 a b
|
||||
y=1 a b c
|
||||
y=2 b c
|
||||
y=3 d e
|
||||
y=4 d e f
|
||||
y=5 e f
|
||||
*/
|
||||
|
||||
Assert.Equal (6, merged.Count);
|
||||
|
||||
Assert.Contains (new (0, 0, 1, 2), merged); // a
|
||||
Assert.Contains (new (1, 0, 1, 3), merged); // b
|
||||
Assert.Contains (new (2, 1, 1, 2), merged); // c
|
||||
Assert.Contains (new (3, 3, 1, 2), merged); // d
|
||||
Assert.Contains (new (4, 3, 1, 3), merged); // e
|
||||
Assert.Contains (new (5, 4, 1, 2), merged); // f
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeRectangles_NoOverlap_ReturnsSameRectangles ()
|
||||
{
|
||||
List<Rectangle> rectangles = new ()
|
||||
{
|
||||
new (0, 0, 10, 10),
|
||||
new (20, 20, 10, 10),
|
||||
new (40, 40, 10, 10)
|
||||
};
|
||||
|
||||
List<Rectangle> result = Region.MergeRectangles (rectangles, false);
|
||||
|
||||
Assert.Equal (3, result.Count);
|
||||
Assert.Contains (new (0, 0, 10, 10), result);
|
||||
Assert.Contains (new (20, 20, 10, 10), result);
|
||||
Assert.Contains (new (40, 40, 10, 10), result);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void MergeRectangles_EmptyRectangles_ReturnsEmptyList ()
|
||||
{
|
||||
// Arrange: Create list of empty rectangles
|
||||
var emptyRectangles = new List<Rectangle> { new (0, 0, 0, 0), new (0, 0, 0, 0) };
|
||||
|
||||
// Act: Call MergeRectangles with granular output
|
||||
var result = Region.MergeRectangles (emptyRectangles, minimize: false);
|
||||
|
||||
// Assert: Result is empty
|
||||
Assert.Empty (result);
|
||||
}
|
||||
|
||||
}
|
||||
893
Tests/UnitTestsParallelizable/Drawing/Region/RegionTests.cs
Normal file
893
Tests/UnitTestsParallelizable/Drawing/Region/RegionTests.cs
Normal file
@@ -0,0 +1,893 @@
|
||||
namespace Terminal.Gui.DrawingTests;
|
||||
|
||||
public class RegionTests
|
||||
{
|
||||
[Fact]
|
||||
public void Clone_CreatesExactCopy ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
Region clone = region.Clone ();
|
||||
Assert.True (clone.Contains (20, 20));
|
||||
Assert.Equal (region.GetRectangles (), clone.GetRectangles ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Combine_EmptyRectangles_ProducesEmptyRegion ()
|
||||
{
|
||||
// Arrange: Create region and combine with empty rectangles
|
||||
var region = new Region ();
|
||||
region.Combine (new Rectangle (0, 0, 0, 0), RegionOp.Union); // Empty rectangle
|
||||
region.Combine (new Region (), RegionOp.Union); // Empty region
|
||||
|
||||
// Assert: Region is empty
|
||||
Assert.Empty (region.GetRectangles ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Complement_Rectangle_ComplementsRegion ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
region.Complement (new (0, 0, 100, 100));
|
||||
Assert.True (region.Contains (5, 5));
|
||||
Assert.False (region.Contains (20, 20));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData (nameof (Complement_TestData))]
|
||||
public void Complement_Region_Success (Region region, Rectangle [] rectangles, Rectangle [] expectedScans)
|
||||
{
|
||||
foreach (Rectangle rect in rectangles)
|
||||
{
|
||||
region.Complement (rect);
|
||||
}
|
||||
|
||||
Rectangle [] actualScans = region.GetRectangles ();
|
||||
Assert.Equal (expectedScans, actualScans);
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<object []> Complement_TestData ()
|
||||
{
|
||||
yield return new object []
|
||||
{
|
||||
new Region (new (10, 10, 100, 100)),
|
||||
new Rectangle [] { new (40, 60, 100, 20) },
|
||||
new Rectangle [] { new (110, 60, 30, 20) }
|
||||
};
|
||||
|
||||
yield return new object []
|
||||
{
|
||||
new Region (new (70, 10, 100, 100)),
|
||||
new Rectangle [] { new (40, 60, 100, 20) },
|
||||
new Rectangle [] { new (40, 60, 30, 20) }
|
||||
};
|
||||
|
||||
yield return new object []
|
||||
{
|
||||
new Region (new (40, 100, 100, 100)),
|
||||
new Rectangle [] { new (70, 80, 50, 40) },
|
||||
new Rectangle [] { new (70, 80, 50, 20) }
|
||||
};
|
||||
|
||||
yield return new object []
|
||||
{
|
||||
new Region (new (40, 10, 100, 100)),
|
||||
new Rectangle [] { new (70, 80, 50, 40) },
|
||||
new Rectangle [] { new (70, 110, 50, 10) }
|
||||
};
|
||||
|
||||
yield return new object []
|
||||
{
|
||||
new Region (new (30, 30, 80, 80)),
|
||||
new Rectangle []
|
||||
{
|
||||
new (45, 45, 200, 200),
|
||||
new (160, 260, 10, 10),
|
||||
new (170, 260, 10, 10)
|
||||
},
|
||||
new Rectangle [] { new (170, 260, 10, 10) }
|
||||
};
|
||||
|
||||
yield return new object []
|
||||
{
|
||||
new Region (),
|
||||
new [] { Rectangle.Empty },
|
||||
new Rectangle[0]
|
||||
};
|
||||
|
||||
yield return new object []
|
||||
{
|
||||
new Region (),
|
||||
new Rectangle [] { new (1, 2, 3, 4) },
|
||||
new Rectangle[0]
|
||||
};
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Complement_WithRectangle_ComplementsRegion ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
var rect = new Rectangle (0, 0, 100, 100);
|
||||
|
||||
region.Complement (rect);
|
||||
|
||||
// Points that were inside the original region should now be outside
|
||||
Assert.False (region.Contains (35, 35));
|
||||
|
||||
// Points that were outside the original region but inside bounds should now be inside
|
||||
Assert.True (region.Contains (5, 5));
|
||||
Assert.True (region.Contains (95, 95));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Complement_WithRegion_ComplementsRegion ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
var bounds = new Rectangle (0, 0, 100, 100);
|
||||
|
||||
region.Complement (bounds);
|
||||
|
||||
// Points that were inside the original region should now be outside
|
||||
Assert.False (region.Contains (35, 35));
|
||||
|
||||
// Points that were outside the original region but inside bounds should now be inside
|
||||
Assert.True (region.Contains (5, 5));
|
||||
Assert.True (region.Contains (95, 95));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_EmptyRegion_IsEmpty ()
|
||||
{
|
||||
var region = new Region ();
|
||||
Assert.True (region.IsEmpty ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WithRectangle_IsNotEmpty ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
Assert.False (region.IsEmpty ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Contains_Point_ReturnsCorrectResult ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
|
||||
Assert.True (region.Contains (20, 20));
|
||||
Assert.False (region.Contains (100, 100));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Contains_PointInsideRegion_ReturnsTrue ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
Assert.True (region.Contains (20, 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Contains_RectangleInsideRegion_ReturnsTrue ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
Assert.True (region.Contains (new (20, 20, 10, 10)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_NullRegion_ReturnsFalse ()
|
||||
{
|
||||
var region = new Region ();
|
||||
Assert.False (region.Equals (null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equals_SameRegion_ReturnsTrue ()
|
||||
{
|
||||
var region = new Region (new (1, 2, 3, 4));
|
||||
Assert.True (region.Equals (region));
|
||||
}
|
||||
|
||||
public static IEnumerable<object []> Equals_TestData ()
|
||||
{
|
||||
static Region Empty ()
|
||||
{
|
||||
Region emptyRegion = new ();
|
||||
emptyRegion.Intersect (Rectangle.Empty);
|
||||
|
||||
return emptyRegion;
|
||||
}
|
||||
|
||||
yield return new object [] { new Region (), new Region (), true };
|
||||
yield return new object [] { new Region (), Empty (), true };
|
||||
yield return new object [] { new Region (), new Region (new (1, 2, 3, 4)), false };
|
||||
|
||||
yield return new object [] { Empty (), Empty (), true };
|
||||
yield return new object [] { Empty (), new Region (new (0, 0, 0, 0)), true };
|
||||
yield return new object [] { Empty (), new Region (new (1, 2, 3, 3)), false };
|
||||
|
||||
yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (1, 2, 3, 4)), true };
|
||||
yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (2, 2, 3, 4)), false };
|
||||
yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (1, 3, 3, 4)), false };
|
||||
yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (1, 2, 4, 4)), false };
|
||||
yield return new object [] { new Region (new (1, 2, 3, 4)), new Region (new (1, 2, 3, 5)), false };
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData (nameof (Equals_TestData))]
|
||||
public void Equals_Valid_ReturnsExpected (Region region1, Region region2, bool expected) { Assert.Equal (expected, region1.Equals (region2)); }
|
||||
|
||||
[Fact]
|
||||
public void GetBounds_ReturnsBoundingRectangle ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
region.Union (new Rectangle (100, 100, 20, 20));
|
||||
Rectangle bounds = region.GetBounds ();
|
||||
Assert.Equal (new (10, 10, 110, 110), bounds);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetBounds_ReturnsCorrectBounds ()
|
||||
{
|
||||
var region = new Region ();
|
||||
region.Union (new Rectangle (10, 10, 50, 50));
|
||||
region.Union (new Rectangle (30, 30, 50, 50));
|
||||
|
||||
Rectangle bounds = region.GetBounds ();
|
||||
|
||||
Assert.Equal (new (10, 10, 70, 70), bounds);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetRegionScans_ReturnsAllRectangles ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
region.Union (new Rectangle (100, 100, 20, 20));
|
||||
Rectangle [] scans = region.GetRectangles ();
|
||||
Assert.Equal (2, scans.Length);
|
||||
Assert.Contains (new (10, 10, 50, 50), scans);
|
||||
Assert.Contains (new (100, 100, 20, 20), scans);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_Rectangle_IntersectsRegion ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
region.Intersect (new Rectangle (30, 30, 50, 50));
|
||||
Assert.False (region.Contains (20, 20));
|
||||
Assert.True (region.Contains (40, 40));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_Region_IntersectsRegions ()
|
||||
{
|
||||
var region1 = new Region (new (10, 10, 50, 50));
|
||||
var region2 = new Region (new (30, 30, 50, 50));
|
||||
region1.Intersect (region2);
|
||||
Assert.False (region1.Contains (20, 20));
|
||||
Assert.True (region1.Contains (40, 40));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_WithEmptyRectangle_ResultsInEmptyRegion ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region (new (0, 0, 10, 10));
|
||||
var rectangle = Rectangle.Empty; // Use Empty instead of 0-size
|
||||
|
||||
// Act
|
||||
region.Intersect (rectangle);
|
||||
|
||||
// Assert
|
||||
Assert.True (region.IsEmpty ());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 0)] // Empty by zero size
|
||||
[InlineData (0, 0, 0, 10)] // Empty by zero width
|
||||
[InlineData (0, 0, 10, 0)] // Empty by zero height
|
||||
[InlineData (-5, -5, 0, 0)] // Empty by zero size at negative coords
|
||||
[InlineData (10, 10, -5, -5)] // Empty by negative size
|
||||
public void Intersect_WithEmptyRegion_ResultsInEmptyRegion (int x, int y, int width, int height)
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region ();
|
||||
region.Union (new Rectangle (0, 0, 10, 10));
|
||||
region.Union (new Rectangle (20, 0, 10, 10));
|
||||
|
||||
// Create a region that should be considered empty
|
||||
var emptyRegion = new Region ();
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
// For negative or zero dimensions, use an empty region
|
||||
emptyRegion = new ();
|
||||
}
|
||||
else
|
||||
{
|
||||
emptyRegion = new (new (x, y, width, height));
|
||||
}
|
||||
|
||||
// Verify initial states
|
||||
Assert.Equal (2, region.GetRectangles ().Length);
|
||||
Assert.True (emptyRegion.IsEmpty ());
|
||||
|
||||
// Act
|
||||
region.Intersect (emptyRegion);
|
||||
|
||||
// Assert
|
||||
Assert.True (region.IsEmpty ());
|
||||
Assert.Empty (region.GetRectangles ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_WithFullyContainedRectangle_ResultsInSmallerRegion ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region (new (0, 0, 10, 10));
|
||||
var rectangle = new Rectangle (2, 2, 4, 4);
|
||||
|
||||
// Act
|
||||
region.Intersect (rectangle);
|
||||
|
||||
// Assert
|
||||
Assert.Single (region.GetRectangles ());
|
||||
Assert.Equal (new (2, 2, 4, 4), region.GetRectangles () [0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_WithMultipleRectanglesInRegion_IntersectsAll ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region ();
|
||||
region.Union (new Rectangle (0, 0, 5, 5));
|
||||
region.Union (new Rectangle (10, 0, 5, 5));
|
||||
var rectangle = new Rectangle (2, 2, 10, 2);
|
||||
|
||||
// Act
|
||||
region.Intersect (rectangle);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (2, region.GetRectangles ().Length);
|
||||
Assert.Contains (new (2, 2, 3, 2), region.GetRectangles ());
|
||||
Assert.Contains (new (10, 2, 2, 2), region.GetRectangles ());
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public void Intersect_WithEmptyRegion_ResultsInEmptyRegion ()
|
||||
//{
|
||||
// // Arrange
|
||||
// var region = new Region ();
|
||||
// var rectangle = new Rectangle (0, 0, 10, 10);
|
||||
|
||||
// // Act
|
||||
// region.Intersect (rectangle);
|
||||
|
||||
// // Assert
|
||||
// Assert.True (region.IsEmpty ());
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_WithNonOverlappingRectangle_ResultsInEmptyRegion ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region (new (0, 0, 5, 5));
|
||||
var rectangle = new Rectangle (10, 10, 5, 5);
|
||||
|
||||
// Act
|
||||
region.Intersect (rectangle);
|
||||
|
||||
// Assert
|
||||
Assert.True (region.IsEmpty ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_WithNullRegion_ResultsInEmptyRegion ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region ();
|
||||
region.Union (new Rectangle (0, 0, 10, 10));
|
||||
region.Union (new Rectangle (20, 0, 10, 10));
|
||||
|
||||
// Verify initial state
|
||||
Assert.Equal (2, region.GetRectangles ().Length);
|
||||
|
||||
// Act
|
||||
region.Intersect (null);
|
||||
|
||||
// Assert
|
||||
Assert.True (region.IsEmpty ());
|
||||
Assert.Empty (region.GetRectangles ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_WithPartiallyOverlappingRectangle_ResultsInIntersectedRegion ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region (new (0, 0, 5, 5));
|
||||
var rectangle = new Rectangle (2, 2, 5, 5);
|
||||
|
||||
// Act
|
||||
region.Intersect (rectangle);
|
||||
|
||||
// Assert
|
||||
Assert.Single (region.GetRectangles ());
|
||||
Assert.Equal (new (2, 2, 3, 3), region.GetRectangles () [0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_WithRectangle_IntersectsRectangles ()
|
||||
{
|
||||
var region = new Region (new (10, 10, 50, 50));
|
||||
var rect = new Rectangle (30, 30, 50, 50);
|
||||
|
||||
region.Intersect (rect);
|
||||
|
||||
Assert.True (region.Contains (35, 35));
|
||||
Assert.False (region.Contains (20, 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_WithRegion_IntersectsRegions ()
|
||||
{
|
||||
var region1 = new Region (new (10, 10, 50, 50));
|
||||
var region2 = new Region (new (30, 30, 50, 50));
|
||||
|
||||
region1.Intersect (region2.GetBounds ());
|
||||
|
||||
Assert.True (region1.Contains (35, 35));
|
||||
Assert.False (region1.Contains (20, 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_ImmediateNormalization_AffectsRectangleOrder ()
|
||||
{
|
||||
// Create a region with two overlapping rectangles
|
||||
var region1 = new Region (new (0, 0, 4, 4)); // 0,0 to 4,4
|
||||
|
||||
// Intersect with a region that partially overlaps
|
||||
var region2 = new Region (new (2, 2, 4, 4)); // 2,2 to 6,6
|
||||
region1.Intersect (region2);
|
||||
|
||||
// Get the resulting rectangles
|
||||
Rectangle [] result = region1.GetRectangles ();
|
||||
|
||||
// Expected behavior from original Region:
|
||||
// Intersect immediately produces a single rectangle (2,2,2,2)
|
||||
Assert.Single (result); // Original has 1 rectangle due to immediate processing
|
||||
Assert.Equal (new (2, 2, 2, 2), result [0]);
|
||||
|
||||
// My updated Region defers normalization after Intersect,
|
||||
// so GetRectangles() might merge differently or preserve order differently,
|
||||
// potentially failing the exact match or count due to _isDirty
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmpty_AfterClear_ReturnsTrue ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region (new (0, 0, 10, 10));
|
||||
|
||||
// Act
|
||||
region.Intersect (Rectangle.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.True (region.IsEmpty ());
|
||||
Assert.Empty (region.GetRectangles ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmpty_AfterComplement_ReturnsCorrectState ()
|
||||
{
|
||||
// Test 1: Complement a region with bounds that fully contain it
|
||||
var region = new Region (new (2, 2, 5, 5)); // Small inner rectangle
|
||||
region.Complement (new (0, 0, 10, 10)); // Larger outer bounds
|
||||
Assert.False (region.IsEmpty ()); // Should have area around the original rectangle
|
||||
|
||||
// Test 2: Complement with bounds equal to the region
|
||||
region = new (new (0, 0, 10, 10));
|
||||
region.Complement (new (0, 0, 10, 10));
|
||||
Assert.True (region.IsEmpty ()); // Should be empty as there's no area left
|
||||
|
||||
// Test 3: Complement with empty bounds
|
||||
region = new (new (0, 0, 10, 10));
|
||||
region.Complement (Rectangle.Empty);
|
||||
Assert.True (region.IsEmpty ()); // Should be empty as there's no bounds
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmpty_AfterExclude_ReturnsTrue ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region (new (0, 0, 10, 10));
|
||||
|
||||
// Act
|
||||
region.Exclude (new Rectangle (0, 0, 10, 10));
|
||||
|
||||
// Assert
|
||||
Assert.True (region.IsEmpty ());
|
||||
Assert.Empty (region.GetRectangles ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmpty_AfterUnion_ReturnsFalse ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region ();
|
||||
region.Union (new Rectangle (0, 0, 10, 10));
|
||||
|
||||
// Assert
|
||||
Assert.False (region.IsEmpty ());
|
||||
Assert.Single (region.GetRectangles ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmpty_EmptyRegion_ReturnsTrue ()
|
||||
{
|
||||
var region = new Region ();
|
||||
Assert.True (region.IsEmpty ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmpty_MultipleOperations_ReturnsExpectedResult ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region ();
|
||||
|
||||
// Act & Assert - Should be empty initially
|
||||
Assert.True (region.IsEmpty ());
|
||||
|
||||
// Add a rectangle - Should not be empty
|
||||
region.Union (new Rectangle (0, 0, 10, 10));
|
||||
Assert.False (region.IsEmpty ());
|
||||
|
||||
// Exclude the same rectangle - Should be empty again
|
||||
region.Exclude (new Rectangle (0, 0, 10, 10));
|
||||
Assert.True (region.IsEmpty ());
|
||||
|
||||
// Add two rectangles - Should not be empty
|
||||
region.Union (new Rectangle (0, 0, 5, 5));
|
||||
region.Union (new Rectangle (10, 10, 5, 5));
|
||||
Assert.False (region.IsEmpty ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmpty_NewRegion_ReturnsTrue ()
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region ();
|
||||
|
||||
// Act & Assert
|
||||
Assert.True (region.IsEmpty ());
|
||||
Assert.Empty (region.GetRectangles ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmpty_ReturnsCorrectResult ()
|
||||
{
|
||||
var region = new Region ();
|
||||
|
||||
Assert.True (region.IsEmpty ());
|
||||
|
||||
region.Union (new Rectangle (10, 10, 50, 50));
|
||||
|
||||
Assert.False (region.IsEmpty ());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 1, 1)] // 1x1 at origin
|
||||
[InlineData (10, 10, 5, 5)] // 5x5 at (10,10)
|
||||
[InlineData (-5, -5, 10, 10)] // Negative coordinates
|
||||
public void IsEmpty_ValidRectangle_ReturnsFalse (int x, int y, int width, int height)
|
||||
{
|
||||
// Arrange
|
||||
var region = new Region (new (x, y, width, height));
|
||||
|
||||
// Assert
|
||||
Assert.False (region.IsEmpty ());
|
||||
Assert.Single (region.GetRectangles ());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 0)] // Zero size
|
||||
[InlineData (0, 0, 0, 10)] // Zero width
|
||||
[InlineData (0, 0, 10, 0)] // Zero height
|
||||
[InlineData (-5, -5, 0, 0)] // Zero size at negative coords
|
||||
public void IsEmpty_ZeroSizeRectangle_ReturnsCorrectState (int x, int y, int width, int height)
|
||||
{
|
||||
var region = new Region (new (x, y, width, height));
|
||||
|
||||
// Only check IsEmpty() since Rectangle(0,0,0,0) is still stored
|
||||
Assert.True (region.IsEmpty ());
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
//public void MinimalUnion_SingleRectangle_DoesNotChange ()
|
||||
//{
|
||||
// var region = new Region (new Rectangle (0, 0, 10, 10));
|
||||
// region.MinimalUnion (new Rectangle (0, 0, 10, 10));
|
||||
|
||||
// Assert.Single (region.GetRectangles ());
|
||||
// Assert.Equal (new Rectangle (0, 0, 10, 10), region.GetRectangles ().First ());
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void MinimalUnion_OverlappingRectangles_MergesIntoOne ()
|
||||
//{
|
||||
// var region = new Region (new Rectangle (0, 0, 10, 10));
|
||||
// region.MinimalUnion (new Rectangle (5, 0, 10, 10));
|
||||
|
||||
// Assert.Single (region.GetRectangles ());
|
||||
// Assert.Equal (new Rectangle (0, 0, 15, 10), region.GetRectangles ().First ());
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void MinimalUnion_AdjacentRectangles_MergesIntoOne ()
|
||||
//{
|
||||
// var region = new Region (new Rectangle (0, 0, 10, 10));
|
||||
// region.MinimalUnion (new Rectangle (10, 0, 10, 10));
|
||||
|
||||
// Assert.Single (region.GetRectangles ());
|
||||
// Assert.Equal (new Rectangle (0, 0, 20, 10), region.GetRectangles ().First ());
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void MinimalUnion_SeparateRectangles_KeepsBoth ()
|
||||
//{
|
||||
// var region = new Region (new Rectangle (0, 0, 10, 10));
|
||||
// region.MinimalUnion (new Rectangle (20, 20, 10, 10));
|
||||
|
||||
// Assert.Equal (2, region.GetRectangles ().Length);
|
||||
// Assert.Contains (new Rectangle (0, 0, 10, 10), region.GetRectangles ());
|
||||
// Assert.Contains (new Rectangle (20, 20, 10, 10), region.GetRectangles ());
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void MinimalUnion_ComplexMerging_ProducesMinimalSet ()
|
||||
//{
|
||||
// var region = new Region (new Rectangle (0, 0, 10, 10));
|
||||
// region.MinimalUnion (new Rectangle (10, 0, 10, 10));
|
||||
// region.MinimalUnion (new Rectangle (0, 10, 10, 10));
|
||||
// region.MinimalUnion (new Rectangle (10, 10, 10, 10));
|
||||
|
||||
// Assert.Single (region.GetRectangles ());
|
||||
// Assert.Equal (new Rectangle (0, 0, 20, 20), region.GetRectangles ().First ());
|
||||
//}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Intersect_ViewportLimitsDrawnRegion ()
|
||||
{
|
||||
// Arrange: Create regions for viewport and drawn content
|
||||
var viewport = new Region (new Rectangle (0, 0, 100, 100)); // Viewport
|
||||
var drawnRegion = new Region (new Rectangle (50, 50, 200, 200)); // Larger drawn content
|
||||
|
||||
// Act: Intersect drawn region with viewport
|
||||
drawnRegion.Intersect (viewport);
|
||||
|
||||
// Assert: Drawn region should be limited to viewport
|
||||
var rectangles = drawnRegion.GetRectangles ();
|
||||
Assert.Single (rectangles);
|
||||
Assert.Equal (new Rectangle (50, 50, 50, 50), rectangles [0]); // Limited to viewport bounds
|
||||
}
|
||||
|
||||
//[Fact]
|
||||
|
||||
|
||||
//public void MinimalUnion_HorizontalMerge_MergesToSingleRectangle ()
|
||||
//{
|
||||
// // Arrange: Create a region with a rectangle at (0,0,5,5)
|
||||
// var region = new Region (new Rectangle (0, 0, 5, 5));
|
||||
|
||||
// // Act: Merge an adjacent rectangle on the right using MinimalUnion
|
||||
// region.MinimalUnion (new Rectangle (5, 0, 5, 5));
|
||||
// var result = region.GetRectangles ();
|
||||
|
||||
// // Assert: Expect a single merged rectangle covering (0,0,10,5)
|
||||
// Assert.Single (result);
|
||||
// Assert.Equal (new Rectangle (0, 0, 10, 5), result [0]);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void MinimalUnion_VerticalMerge_MergesToSingleRectangle ()
|
||||
//{
|
||||
// // Arrange: Create a region with a rectangle at (0,0,5,5)
|
||||
// var region = new Region (new Rectangle (0, 0, 5, 5));
|
||||
|
||||
// // Act: Merge an adjacent rectangle below using MinimalUnion
|
||||
// region.MinimalUnion (new Rectangle (0, 5, 5, 5));
|
||||
// var result = region.GetRectangles ();
|
||||
|
||||
// // Assert: Expect a single merged rectangle covering (0,0,5,10)
|
||||
// Assert.Single (result);
|
||||
// Assert.Equal (new Rectangle (0, 0, 5, 10), result [0]);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void MinimalUnion_OverlappingMerge_MergesToSingleRectangle ()
|
||||
//{
|
||||
// // Arrange: Create a region with a rectangle that overlaps with the next one horizontally
|
||||
// var region = new Region (new Rectangle (0, 0, 6, 5));
|
||||
|
||||
// // Act: Merge an overlapping rectangle using MinimalUnion
|
||||
// region.MinimalUnion (new Rectangle (4, 0, 6, 5));
|
||||
// var result = region.GetRectangles ();
|
||||
|
||||
// // Assert: Expect a single merged rectangle covering (0,0,10,5)
|
||||
// Assert.Single (result);
|
||||
// Assert.Equal (new Rectangle (0, 0, 10, 5), result [0]);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void MinimalUnion_NonAdjacentRectangles_NoMergeOccurs ()
|
||||
//{
|
||||
// // Arrange: Create a region with one rectangle
|
||||
// var region = new Region (new Rectangle (0, 0, 5, 5));
|
||||
|
||||
// // Act: Merge with a rectangle that does not touch the first
|
||||
// region.MinimalUnion (new Rectangle (6, 0, 5, 5));
|
||||
// var result = region.GetRectangles ();
|
||||
|
||||
// // Assert: Expect two separate rectangles since they are not adjacent
|
||||
// Assert.Equal (2, result.Length);
|
||||
// Assert.Contains (new Rectangle (0, 0, 5, 5), result);
|
||||
// Assert.Contains (new Rectangle (6, 0, 5, 5), result);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public void MinimalUnion_MultipleMerge_FormsSingleContiguousRectangle ()
|
||||
//{
|
||||
// // Arrange: Four small rectangles that form a contiguous 6x6 block
|
||||
// var region = new Region (new Rectangle (0, 0, 3, 3));
|
||||
|
||||
// // Act: Merge adjacent rectangles one by one using MinimalUnion
|
||||
// region.MinimalUnion (new Rectangle (3, 0, 3, 3)); // Now covers (0,0,6,3)
|
||||
// region.MinimalUnion (new Rectangle (0, 3, 3, 3)); // Add bottom-left
|
||||
// region.MinimalUnion (new Rectangle (3, 3, 3, 3)); // Add bottom-right to complete block
|
||||
// var result = region.GetRectangles ();
|
||||
|
||||
// // Assert: Expect a single merged rectangle covering (0,0,6,6)
|
||||
// Assert.Single (result);
|
||||
// Assert.Equal (new Rectangle (0, 0, 6, 6), result [0]);
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public void Translate_EmptyRegionAfterEmptyCombine_NoEffect ()
|
||||
{
|
||||
// Arrange: Create region and combine with empty rectangles
|
||||
var region = new Region ();
|
||||
region.Combine (new Rectangle (0, 0, 0, 0), RegionOp.Union); // Empty rectangle
|
||||
region.Combine (new Region (), RegionOp.Union); // Empty region
|
||||
|
||||
// Act: Translate by (10, 20)
|
||||
region.Translate (10, 20);
|
||||
|
||||
// Assert: Still empty
|
||||
Assert.Empty (region.GetRectangles ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_Rectangle_AddsToRegion ()
|
||||
{
|
||||
var region = new Region ();
|
||||
region.Union (new Rectangle (10, 10, 50, 50));
|
||||
Assert.False (region.IsEmpty ());
|
||||
Assert.True (region.Contains (20, 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_Region_MergesRegions ()
|
||||
{
|
||||
var region1 = new Region (new (10, 10, 50, 50));
|
||||
var region2 = new Region (new (30, 30, 50, 50));
|
||||
region1.Union (region2);
|
||||
Assert.True (region1.Contains (20, 20));
|
||||
Assert.True (region1.Contains (40, 40));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Proves MergeRegion does not overly combine regions.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Union_Region_MergesRegions_NonOverlapping ()
|
||||
{
|
||||
// 012345
|
||||
// 0+++
|
||||
// 1+ +
|
||||
// 2+++
|
||||
// 3 ***
|
||||
// 4 * *
|
||||
// 5 ***
|
||||
|
||||
var region1 = new Region (new (0, 0, 3, 3));
|
||||
var region2 = new Region (new (3, 3, 3, 3));
|
||||
region1.Union (region2);
|
||||
|
||||
// Positive
|
||||
Assert.True (region1.Contains (0, 0));
|
||||
Assert.True (region1.Contains (1, 1));
|
||||
Assert.True (region1.Contains (2, 2));
|
||||
Assert.True (region1.Contains (4, 4));
|
||||
Assert.True (region1.Contains (5, 5));
|
||||
|
||||
// Negative
|
||||
Assert.False (region1.Contains (0, 3));
|
||||
Assert.False (region1.Contains (3, 0));
|
||||
Assert.False (region1.Contains (6, 6));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Proves MergeRegion does not overly combine regions.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Union_Region_MergesRegions_Overlapping ()
|
||||
{
|
||||
// 01234567
|
||||
// 0+++++
|
||||
// 1+ +
|
||||
// 2+ +
|
||||
// 3+ *****
|
||||
// 4+++* *
|
||||
// 5 * *
|
||||
// 6 * *
|
||||
// 7 *****
|
||||
|
||||
var region1 = new Region (new (0, 0, 5, 5));
|
||||
var region2 = new Region (new (3, 3, 5, 5));
|
||||
region1.Union (region2);
|
||||
|
||||
// Positive
|
||||
Assert.True (region1.Contains (0, 0));
|
||||
Assert.True (region1.Contains (1, 1));
|
||||
Assert.True (region1.Contains (4, 4));
|
||||
Assert.True (region1.Contains (7, 7));
|
||||
|
||||
// Negative
|
||||
Assert.False (region1.Contains (0, 5));
|
||||
Assert.False (region1.Contains (5, 0));
|
||||
Assert.False (region1.Contains (8, 8));
|
||||
Assert.False (region1.Contains (8, 8));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_WithRectangle_AddsRectangle ()
|
||||
{
|
||||
var region = new Region ();
|
||||
var rect = new Rectangle (10, 10, 50, 50);
|
||||
|
||||
region.Union (rect);
|
||||
|
||||
Assert.True (region.Contains (20, 20));
|
||||
Assert.False (region.Contains (100, 100));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Union_WithRegion_AddsRegion ()
|
||||
{
|
||||
var region1 = new Region (new (10, 10, 50, 50));
|
||||
var region2 = new Region (new (30, 30, 50, 50));
|
||||
|
||||
region1.Union (region2.GetBounds ());
|
||||
|
||||
Assert.True (region1.Contains (20, 20));
|
||||
Assert.True (region1.Contains (40, 40));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Intersect_DeferredNormalization_PreservesSegments ()
|
||||
{
|
||||
var region = new Region (new (0, 0, 3, 1)); // Horizontal
|
||||
region.Union (new Rectangle (1, 0, 1, 2)); // Vertical
|
||||
region.Intersect (new Rectangle (0, 0, 2, 2)); // Clip
|
||||
|
||||
Rectangle [] result = region.GetRectangles ();
|
||||
|
||||
// Original & Updated (with normalization disabled) behavior:
|
||||
// Produces [(0,0,1,1), (1,0,1,2), (2,0,0,1)]
|
||||
Assert.Equal (3, result.Length);
|
||||
Assert.Contains (new (0, 0, 1, 1), result);
|
||||
Assert.Contains (new (1, 0, 1, 2), result);
|
||||
Assert.Contains (new (2, 0, 0, 1), result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
namespace Terminal.Gui.DrawingTests;
|
||||
|
||||
using Xunit;
|
||||
|
||||
public class SubtractRectangleTests
|
||||
{
|
||||
[Fact]
|
||||
public void SubtractRectangle_NoOverlap_ReturnsOriginalRectangle ()
|
||||
{
|
||||
// Arrange: Non-overlapping rectangles
|
||||
Rectangle original = new (0, 0, 10, 10);
|
||||
Rectangle subtract = new (15, 15, 5, 5);
|
||||
|
||||
// Act: Subtract non-overlapping rectangle
|
||||
var result = Region.SubtractRectangle (original, subtract).ToList ();
|
||||
|
||||
// Assert: Original rectangle unchanged
|
||||
Assert.Single (result);
|
||||
Assert.Equal (new Rectangle (0, 0, 10, 10), result [0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_CompleteOverlap_ReturnsEmpty ()
|
||||
{
|
||||
// Arrange: Subtract rectangle completely overlaps original
|
||||
Rectangle original = new (0, 0, 5, 5);
|
||||
Rectangle subtract = new (0, 0, 5, 5);
|
||||
|
||||
// Act: Subtract overlapping rectangle
|
||||
var result = Region.SubtractRectangle (original, subtract).ToList ();
|
||||
|
||||
// Assert: No rectangles returned (empty result)
|
||||
Assert.Empty (result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_PartialOverlap_TopAndBottom ()
|
||||
{
|
||||
// Arrange: Original rectangle with subtract overlapping top and bottom
|
||||
Rectangle original = new (0, 0, 10, 10);
|
||||
Rectangle subtract = new (0, 4, 10, 2); // Overlaps y=4 to y=5
|
||||
|
||||
// Act: Subtract overlapping rectangle
|
||||
var result = Region.SubtractRectangle (original, subtract).ToList ();
|
||||
|
||||
// Assert: Expect top (0,0,10,4) and bottom (0,6,10,4)
|
||||
Assert.Equal (2, result.Count);
|
||||
Assert.Contains (new Rectangle (0, 0, 10, 4), result); // Top part
|
||||
Assert.Contains (new Rectangle (0, 6, 10, 4), result); // Bottom part
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_PartialOverlap_LeftAndRight ()
|
||||
{
|
||||
// Arrange: Original rectangle with subtract overlapping left and right
|
||||
Rectangle original = new (0, 0, 10, 10);
|
||||
Rectangle subtract = new (4, 0, 2, 10); // Overlaps x=4 to x=5
|
||||
|
||||
// Act: Subtract overlapping rectangle
|
||||
var result = Region.SubtractRectangle (original, subtract).ToList ();
|
||||
|
||||
// Assert: Expect left (0,0,4,10) and right (6,0,4,10)
|
||||
Assert.Equal (2, result.Count);
|
||||
Assert.Contains (new Rectangle (0, 0, 4, 10), result); // Left part
|
||||
Assert.Contains (new Rectangle (6, 0, 4, 10), result); // Right part
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_EmptyOriginal_ReturnsEmpty ()
|
||||
{
|
||||
// Arrange: Empty original rectangle
|
||||
Rectangle original = Rectangle.Empty;
|
||||
Rectangle subtract = new (0, 0, 5, 5);
|
||||
|
||||
// Act: Subtract from empty rectangle
|
||||
var result = Region.SubtractRectangle (original, subtract).ToList ();
|
||||
|
||||
// Assert: No rectangles returned (empty result)
|
||||
Assert.Empty (result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_EmptySubtract_ReturnsOriginal ()
|
||||
{
|
||||
// Arrange: Empty subtract rectangle
|
||||
Rectangle original = new (0, 0, 5, 5);
|
||||
Rectangle subtract = Rectangle.Empty;
|
||||
|
||||
// Act: Subtract empty rectangle
|
||||
var result = Region.SubtractRectangle (original, subtract).ToList ();
|
||||
|
||||
// Assert: Original rectangle unchanged
|
||||
Assert.Single (result);
|
||||
Assert.Equal (new Rectangle (0, 0, 5, 5), result [0]);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_ZeroWidthOrHeight_HandlesCorrectly ()
|
||||
{
|
||||
// Arrange: Rectangle with zero width or height
|
||||
Rectangle original = new (0, 0, 5, 5);
|
||||
Rectangle subtract = new (0, 0, 0, 5); // Zero width
|
||||
|
||||
// Act: Subtract zero-width rectangle
|
||||
var result = Region.SubtractRectangle (original, subtract).ToList ();
|
||||
|
||||
// Assert: Original rectangle unchanged
|
||||
Assert.Single (result);
|
||||
Assert.Equal (new Rectangle (0, 0, 5, 5), result [0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_NegativeCoordinates_HandlesCorrectly ()
|
||||
{
|
||||
// Arrange:
|
||||
// Original rectangle: (-5,-5) with width 10 and height 10.
|
||||
// Subtract rectangle: (-3,-3) with width 2 and height 2.
|
||||
Rectangle original = new (-5, -5, 10, 10);
|
||||
Rectangle subtract = new (-3, -3, 2, 2);
|
||||
|
||||
// Act:
|
||||
var results = Region.SubtractRectangle (original, subtract).ToList ();
|
||||
|
||||
// Expected fragments based on our algorithm:
|
||||
// Top: (-5,-5,10,2)
|
||||
// Bottom: (-5,-1,10,6)
|
||||
// Left: (-5,-3,2,2)
|
||||
// Right: (-1,-3,6,2)
|
||||
var expectedTop = new Rectangle (-5, -5, 10, 2);
|
||||
var expectedBottom = new Rectangle (-5, -1, 10, 6);
|
||||
var expectedLeft = new Rectangle (-5, -3, 2, 2);
|
||||
var expectedRight = new Rectangle (-1, -3, 6, 2);
|
||||
|
||||
// Assert:
|
||||
Assert.Contains (expectedTop, results);
|
||||
Assert.Contains (expectedBottom, results);
|
||||
Assert.Contains (expectedLeft, results);
|
||||
Assert.Contains (expectedRight, results);
|
||||
Assert.Equal (4, results.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_EdgeOverlap_TopLeftCorner ()
|
||||
{
|
||||
// Arrange: Subtract overlaps only the top-left corner.
|
||||
// Original: (0,0,5,5); Subtract: (0,0,1,1)
|
||||
Rectangle original = new (0, 0, 5, 5);
|
||||
Rectangle subtract = new (0, 0, 1, 1);
|
||||
|
||||
// Act:
|
||||
var result = Region.SubtractRectangle (original, subtract).ToList ();
|
||||
|
||||
// The algorithm produces two fragments:
|
||||
// 1. Bottom: (0,1,5,4)
|
||||
// 2. Right: (1,0,4,1)
|
||||
Assert.Equal (2, result.Count);
|
||||
Assert.Contains (new Rectangle (0, 1, 5, 4), result);
|
||||
Assert.Contains (new Rectangle (1, 0, 4, 1), result);
|
||||
}
|
||||
|
||||
// Updated L-shaped test: The algorithm produces 6 fragments, not 5.
|
||||
[Fact]
|
||||
public void SubtractRectangle_LShapedSubtract_MultipleFragments ()
|
||||
{
|
||||
// Arrange:
|
||||
// Original: (0,0,6,6)
|
||||
// subtract1: (2,2,2,1) creates a horizontal gap.
|
||||
// subtract2: (2,3,1,1) removes an additional piece from the bottom fragment.
|
||||
Rectangle original = new (0, 0, 6, 6);
|
||||
Rectangle subtract1 = new (2, 2, 2, 1);
|
||||
Rectangle subtract2 = new (2, 3, 1, 1);
|
||||
|
||||
// Act:
|
||||
var intermediateResult = Region.SubtractRectangle (original, subtract1).ToList ();
|
||||
var finalResult = intermediateResult.SelectMany (r => Region.SubtractRectangle (r, subtract2)).ToList ();
|
||||
|
||||
// Explanation:
|
||||
// After subtracting subtract1, we expect these 4 fragments:
|
||||
// - Top: (0,0,6,2)
|
||||
// - Bottom: (0,3,6,3)
|
||||
// - Left: (0,2,2,1)
|
||||
// - Right: (4,2,2,1)
|
||||
// Then subtracting subtract2 from the bottom fragment (0,3,6,3) produces 3 fragments:
|
||||
// - Bottom part: (0,4,6,2)
|
||||
// - Left part: (0,3,2,1)
|
||||
// - Right part: (3,3,3,1)
|
||||
// Total fragments = 1 (top) + 3 (modified bottom) + 1 (left) + 1 (right) = 6.
|
||||
Assert.Equal (6, finalResult.Count);
|
||||
Assert.Contains (new Rectangle (0, 0, 6, 2), finalResult); // Top fragment remains unchanged.
|
||||
Assert.Contains (new Rectangle (0, 4, 6, 2), finalResult); // Bottom part from modified bottom.
|
||||
Assert.Contains (new Rectangle (0, 3, 2, 1), finalResult); // Left part from modified bottom.
|
||||
Assert.Contains (new Rectangle (3, 3, 3, 1), finalResult); // Right part from modified bottom.
|
||||
Assert.Contains (new Rectangle (0, 2, 2, 1), finalResult); // Left fragment from first subtraction.
|
||||
Assert.Contains (new Rectangle (4, 2, 2, 1), finalResult); // Right fragment from first subtraction.
|
||||
}
|
||||
|
||||
// Additional focused tests for L-shaped subtraction scenarios:
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_LShapedSubtract_HorizontalThenVertical ()
|
||||
{
|
||||
// Arrange:
|
||||
// First, subtract a horizontal band.
|
||||
Rectangle original = new (0, 0, 10, 10);
|
||||
Rectangle subtractHorizontal = new (0, 4, 10, 2);
|
||||
var horizontalResult = Region.SubtractRectangle (original, subtractHorizontal).ToList ();
|
||||
// Expect two fragments: top (0,0,10,4) and bottom (0,6,10,4).
|
||||
Assert.Equal (2, horizontalResult.Count);
|
||||
Assert.Contains (new Rectangle (0, 0, 10, 4), horizontalResult);
|
||||
Assert.Contains (new Rectangle (0, 6, 10, 4), horizontalResult);
|
||||
|
||||
// Act:
|
||||
// Now, subtract a vertical piece from the top fragment.
|
||||
Rectangle subtractVertical = new (3, 0, 2, 4);
|
||||
var finalResult = Region.SubtractRectangle (horizontalResult [0], subtractVertical).ToList ();
|
||||
|
||||
// Assert:
|
||||
// The subtraction yields two fragments:
|
||||
// Left fragment: (0,0,3,4)
|
||||
// Right fragment: (5,0,5,4)
|
||||
Assert.Equal (2, finalResult.Count);
|
||||
Assert.Contains (new Rectangle (0, 0, 3, 4), finalResult);
|
||||
Assert.Contains (new Rectangle (5, 0, 5, 4), finalResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubtractRectangle_LShapedSubtract_VerticalThenHorizontal ()
|
||||
{
|
||||
// Arrange:
|
||||
// First, subtract a vertical band.
|
||||
// Original: (0,0,10,10)
|
||||
// Vertical subtract: (4,0,2,10) produces two fragments:
|
||||
// Left: (0,0,4,10) and Right: (6,0,4,10)
|
||||
Rectangle original = new (0, 0, 10, 10);
|
||||
Rectangle subtractVertical = new (4, 0, 2, 10);
|
||||
var verticalResult = Region.SubtractRectangle (original, subtractVertical).ToList ();
|
||||
Assert.Equal (2, verticalResult.Count);
|
||||
Assert.Contains (new Rectangle (0, 0, 4, 10), verticalResult);
|
||||
Assert.Contains (new Rectangle (6, 0, 4, 10), verticalResult);
|
||||
|
||||
// Act:
|
||||
// Now, subtract a horizontal piece from the left fragment.
|
||||
// Horizontal subtract: (0,3,4,2)
|
||||
// from the left fragment (0,0,4,10)
|
||||
// Let's determine the expected fragments:
|
||||
// 1. Top band: since original.Top (0) < subtract.Top (3), we get:
|
||||
// (0,0,4, 3) -- height = subtract.Top - original.Top = 3.
|
||||
// 2. Bottom band: since original.Bottom (10) > subtract.Bottom (5), we get:
|
||||
// (0,5,4, 5) -- height = original.Bottom - subtract.Bottom = 5.
|
||||
// No left or right fragments are produced because subtractHorizontal spans the full width.
|
||||
// Total fragments: 2.
|
||||
Rectangle subtractHorizontal = new (0, 3, 4, 2);
|
||||
var finalResult = Region.SubtractRectangle (verticalResult [0], subtractHorizontal).ToList ();
|
||||
|
||||
// Assert:
|
||||
// Expecting two fragments: (0,0,4,3) and (0,5,4,5).
|
||||
Assert.Equal (2, finalResult.Count);
|
||||
Assert.Contains (new Rectangle (0, 0, 4, 3), finalResult);
|
||||
Assert.Contains (new Rectangle (0, 5, 4, 5), finalResult);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user