mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
* Refactored test namespaces. Moved some tests that were in wrong project. Code cleanup * Parrallel -> Parallel
1107 lines
38 KiB
C#
1107 lines
38 KiB
C#
namespace UnitTests_Parallelizable.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));
|
|
}
|
|
|
|
[Fact]
|
|
public void Union_Third_Rect_Covering_Two_Disjoint_Merges ()
|
|
{
|
|
var origRegion = new Region ();
|
|
|
|
var region1 = new Region (new (0, 0, 1, 1));
|
|
var region2 = new Region (new (1, 0, 1, 1));
|
|
|
|
origRegion.Union (region1);
|
|
origRegion.Union (region2);
|
|
|
|
Assert.Equal (new Rectangle (0, 0, 2, 1), origRegion.GetBounds ());
|
|
Assert.Equal (2, origRegion.GetRectangles ().Length);
|
|
|
|
origRegion.Union (new Region (new (0, 0, 4, 1)));
|
|
|
|
Assert.Equal (new Rectangle (0, 0, 4, 1), origRegion.GetBounds ());
|
|
Assert.Equal (3, origRegion.GetRectangles ().Length);
|
|
}
|
|
|
|
[Fact]
|
|
public void MinimalUnion_Third_Rect_Covering_Two_Disjoint_Merges ()
|
|
{
|
|
var origRegion = new Region ();
|
|
|
|
var region1 = new Region (new (0, 0, 1, 1));
|
|
var region2 = new Region (new (1, 0, 1, 1));
|
|
|
|
origRegion.Union (region1);
|
|
origRegion.Union (region2);
|
|
|
|
Assert.Equal (new Rectangle (0, 0, 2, 1), origRegion.GetBounds ());
|
|
Assert.Equal (2, origRegion.GetRectangles ().Length);
|
|
|
|
origRegion.MinimalUnion (new Region (new (0, 0, 4, 1)));
|
|
|
|
Assert.Equal (new Rectangle (0, 0, 4, 1), origRegion.GetBounds ());
|
|
Assert.Single (origRegion.GetRectangles ());
|
|
}
|
|
|
|
/// <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);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeRectangles_Sort_Handles_Coincident_Events_Without_Crashing ()
|
|
{
|
|
// Arrange: Create rectangles designed to produce coincident start/end events
|
|
// Rect1 ends at x=10. Rect2 and Rect3 start at x=10.
|
|
// Rect4 ends at x=15. Rect5 starts at x=15.
|
|
var rect1 = new Rectangle (0, 0, 10, 10); // Ends at x=10
|
|
var rect2 = new Rectangle (10, 0, 10, 5); // Starts at x=10
|
|
var rect3 = new Rectangle (10, 5, 10, 5); // Starts at x=10, adjacent to rect2 vertically
|
|
var rect4 = new Rectangle (5, 10, 10, 5); // Ends at x=15
|
|
var rect5 = new Rectangle (15, 10, 5, 5); // Starts at x=15
|
|
|
|
var combinedList = new List<Rectangle> { rect1, rect2, rect3, rect4, rect5 };
|
|
|
|
// Act & Assert:
|
|
// The core assertion is that calling MergeRectangles with this list
|
|
// does *not* throw the ArgumentException related to sorting.
|
|
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
|
|
|
|
// Assert
|
|
Assert.Null (exception);
|
|
|
|
// Optional secondary assertion: Check if the merge produced a reasonable number of rectangles
|
|
// This isn't strictly necessary for proving the sort fix, but can be useful.
|
|
// var merged = Region.MergeRectangles(combinedList, false);
|
|
// Assert.True(merged.Count > 0 && merged.Count <= combinedList.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeRectangles_Sort_Handles_Multiple_Coincident_Starts ()
|
|
{
|
|
// Arrange: Multiple rectangles starting at the same X
|
|
var rect1 = new Rectangle (5, 0, 10, 5);
|
|
var rect2 = new Rectangle (5, 5, 10, 5);
|
|
var rect3 = new Rectangle (5, 10, 10, 5);
|
|
var combinedList = new List<Rectangle> { rect1, rect2, rect3 };
|
|
|
|
// Act & Assert: Ensure no sorting exception
|
|
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
|
|
Assert.Null (exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeRectangles_Sort_Handles_Multiple_Coincident_Ends ()
|
|
{
|
|
// Arrange: Multiple rectangles ending at the same X
|
|
var rect1 = new Rectangle (0, 0, 10, 5);
|
|
var rect2 = new Rectangle (0, 5, 10, 5);
|
|
var rect3 = new Rectangle (0, 10, 10, 5);
|
|
var combinedList = new List<Rectangle> { rect1, rect2, rect3 };
|
|
|
|
// Act & Assert: Ensure no sorting exception
|
|
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
|
|
Assert.Null (exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeRectangles_Sort_Handles_Coincident_Mixed_Events_Without_Crashing ()
|
|
{
|
|
// Arrange: Create rectangles specifically designed to produce multiple
|
|
// Start AND End events at the same x-coordinate (e.g., x=10),
|
|
// mimicking the pattern observed in the crash log.
|
|
var rectA = new Rectangle (0, 0, 10, 5); // Ends at x=10, y=[0, 5)
|
|
var rectB = new Rectangle (0, 10, 10, 5); // Ends at x=10, y=[10, 15)
|
|
var rectC = new Rectangle (10, 0, 10, 5); // Starts at x=10, y=[0, 5)
|
|
var rectD = new Rectangle (10, 10, 10, 5); // Starts at x=10, y=[10, 15)
|
|
|
|
// Add another set at a different X to increase complexity
|
|
var rectE = new Rectangle (5, 20, 10, 5); // Ends at x=15, y=[20, 25)
|
|
var rectF = new Rectangle (5, 30, 10, 5); // Ends at x=15, y=[30, 35)
|
|
var rectG = new Rectangle (15, 20, 10, 5); // Starts at x=15, y=[20, 25)
|
|
var rectH = new Rectangle (15, 30, 10, 5); // Starts at x=15, y=[30, 35)
|
|
|
|
// Add some unrelated rectangles
|
|
var rectI = new Rectangle (0, 40, 5, 5);
|
|
var rectJ = new Rectangle (100, 100, 5, 5);
|
|
|
|
|
|
var combinedList = new List<Rectangle> {
|
|
rectA, rectB, rectC, rectD,
|
|
rectE, rectF, rectG, rectH,
|
|
rectI, rectJ
|
|
};
|
|
|
|
// Act & Assert:
|
|
// Call MergeRectangles with the current code.
|
|
// This test *should* fail by throwing ArgumentException due to unstable sort.
|
|
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
|
|
|
|
// Assert that no exception was thrown (this assertion will fail with the current code)
|
|
Assert.Null (exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeRectangles_Sort_Reproduces_UICatalog_Crash_Pattern_Directly ()
|
|
{
|
|
// Arrange: Rectangles derived *directly* from the events list that caused the crash at x=67
|
|
// This aims to replicate the exact problematic pattern.
|
|
var rect_End_67_7_30 = new Rectangle (60, 7, 7, 23); // Ends at x=67, y=[7, 30) -> Event [33]
|
|
var rect_Start_67_2_30 = new Rectangle (67, 2, 10, 28); // Starts at x=67, y=[2, 30) -> Event [34]
|
|
var rect_Start_67_1_1 = new Rectangle (67, 1, 10, 0); // Starts at x=67, y=[1, 1) -> Event [49] (Height 0)
|
|
var rect_End_67_1_1 = new Rectangle (60, 1, 7, 0); // Ends at x=67, y=[1, 1) -> Event [64] (Height 0)
|
|
|
|
// Add rectangles for x=94/95 pattern
|
|
var rect_End_94_1_30 = new Rectangle (90, 1, 4, 29); // Ends at x=94, y=[1, 30) -> Event [55]
|
|
var rect_Start_94_1_1 = new Rectangle (94, 1, 10, 0); // Starts at x=94, y=[1, 1) -> Event [56]
|
|
var rect_Start_94_7_30 = new Rectangle (94, 7, 10, 23); // Starts at x=94, y=[7, 30) -> Event [58]
|
|
|
|
var rect_End_95_1_1 = new Rectangle (90, 1, 5, 0); // Ends at x=95, y=[1, 1) -> Event [57]
|
|
var rect_End_95_7_30 = new Rectangle (90, 7, 5, 23); // Ends at x=95, y=[7, 30) -> Event [59]
|
|
var rect_Start_95_0_30 = new Rectangle (95, 0, 10, 30); // Starts at x=95, y=[0, 30) -> Event [60]
|
|
|
|
|
|
var combinedList = new List<Rectangle> {
|
|
rect_End_67_7_30, rect_Start_67_2_30, rect_Start_67_1_1, rect_End_67_1_1,
|
|
rect_End_94_1_30, rect_Start_94_1_1, rect_Start_94_7_30,
|
|
rect_End_95_1_1, rect_End_95_7_30, rect_Start_95_0_30
|
|
};
|
|
|
|
// Act & Assert:
|
|
// Call MergeRectangles. This test is specifically designed to fail with the current code.
|
|
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
|
|
|
|
// Assert that no exception was thrown (this assertion *should* fail with the current code)
|
|
Assert.Null (exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeRectangles_Sort_Reproduces_UICatalog_Crash_From_Captured_Data ()
|
|
{
|
|
// Arrange: The exact list of rectangles captured during the UICatalog crash
|
|
var rectanglesFromCrash = new List<Rectangle> {
|
|
new Rectangle(38, 7, 1, 11),
|
|
new Rectangle(39, 7, 5, 23),
|
|
new Rectangle(44, 7, 1, 23),
|
|
new Rectangle(45, 7, 6, 23),
|
|
new Rectangle(51, 7, 1, 23),
|
|
new Rectangle(52, 7, 1, 23),
|
|
new Rectangle(53, 7, 1, 23),
|
|
new Rectangle(54, 7, 1, 23),
|
|
new Rectangle(55, 7, 1, 23),
|
|
new Rectangle(56, 7, 1, 23),
|
|
new Rectangle(57, 7, 1, 23),
|
|
new Rectangle(58, 7, 1, 23),
|
|
new Rectangle(59, 7, 1, 23),
|
|
new Rectangle(60, 7, 1, 23),
|
|
new Rectangle(61, 7, 3, 23),
|
|
new Rectangle(64, 7, 1, 23),
|
|
new Rectangle(65, 7, 2, 23),
|
|
new Rectangle(67, 2, 2, 28),
|
|
new Rectangle(69, 2, 3, 28),
|
|
new Rectangle(72, 2, 3, 28),
|
|
new Rectangle(75, 2, 1, 28),
|
|
new Rectangle(76, 2, 2, 28),
|
|
new Rectangle(78, 2, 2, 28),
|
|
new Rectangle(80, 7, 1, 23),
|
|
new Rectangle(81, 1, 7, 29),
|
|
new Rectangle(88, 1, 1, 29),
|
|
new Rectangle(89, 1, 2, 29),
|
|
new Rectangle(91, 1, 3, 29),
|
|
new Rectangle(94, 1, 1, 0), // Note: Zero height
|
|
new Rectangle(94, 7, 1, 23),
|
|
new Rectangle(95, 0, 1, 30),
|
|
new Rectangle(96, 0, 23, 30),
|
|
new Rectangle(67, 1, 0, 0) // Note: Zero width and height
|
|
};
|
|
|
|
// Act & Assert:
|
|
// Call MergeRectangles with the current code.
|
|
// This test *should* fail by throwing ArgumentException due to unstable sort.
|
|
var exception = Record.Exception (() => Region.MergeRectangles (rectanglesFromCrash, false));
|
|
|
|
// Assert that no exception was thrown (this assertion will fail with the current code)
|
|
Assert.Null (exception);
|
|
}
|
|
|
|
} |