package ovh.gasser.newshapes.shapes; import ovh.gasser.newshapes.App; import ovh.gasser.newshapes.attributes.SelectionAttributes; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import java.awt.*; import static org.junit.jupiter.api.Assertions.*; class EdgeCaseTest { // ------------------------------------------------------------------------- // NegativeCoordinates // ------------------------------------------------------------------------- @Nested class NegativeCoordinates { @Test void rectangleAtNegativeCoordsShouldPreserveBounds() { SRectangle rect = SRectangle.create(-10, -20, 50, 50); Rectangle bounds = rect.getBounds(); assertEquals(-10, bounds.x, "x should be -10"); assertEquals(-20, bounds.y, "y should be -20"); assertEquals(50, bounds.width, "width should be 50"); assertEquals(50, bounds.height, "height should be 50"); } @Test void circleAtNegativeCoordsShouldPreserveBounds() { SCircle circle = SCircle.create(-30, -40, 25); Rectangle bounds = circle.getBounds(); assertEquals(-30, bounds.x, "x should be -30"); assertEquals(-40, bounds.y, "y should be -40"); assertEquals(50, bounds.width, "width should be radius*2 = 50"); assertEquals(50, bounds.height, "height should be radius*2 = 50"); } @Test void triangleAtNegativeCoordsShouldPreserveBounds() { STriangle tri = STriangle.create(-5, -15, 60, Color.RED, Color.BLACK); Rectangle bounds = tri.getBounds(); assertEquals(-5, bounds.x, "x should be -5"); assertEquals(-15, bounds.y, "y should be -15"); assertEquals(60, bounds.width, "width should be 60"); assertEquals(60, bounds.height, "height should be 60"); } @Test void translateToNegativePositionShouldUpdateBoundsCorrectly() { SRectangle rect = SRectangle.create(10, 20, 80, 40); rect.translate(-50, -70); Rectangle bounds = rect.getBounds(); assertEquals(-40, bounds.x, "x after translate should be -40"); assertEquals(-50, bounds.y, "y after translate should be -50"); // dimensions must be unchanged assertEquals(80, bounds.width, "width must remain 80 after translate"); assertEquals(40, bounds.height, "height must remain 40 after translate"); } } // ------------------------------------------------------------------------- // ZeroDimensions // ------------------------------------------------------------------------- @Nested class ZeroDimensions { @Test void rectangleWithZeroWidthShouldReturnZeroWidthFromFactory() { SRectangle rect = SRectangle.create(0, 0, 0, 50); assertEquals(0, rect.getBounds().width, "factory should allow zero width without clamping"); } @Test void rectangleWithZeroHeightShouldReturnZeroHeightFromFactory() { SRectangle rect = SRectangle.create(0, 0, 50, 0); assertEquals(0, rect.getBounds().height, "factory should allow zero height without clamping"); } @Test void circleWithZeroRadiusShouldHaveZeroBoundsDimensions() { SCircle circle = SCircle.create(5, 10, 0); assertEquals(0, circle.getRadius(), "radius should be 0"); assertEquals(0, circle.getBounds().width, "bounds width should be 0 (radius*2)"); assertEquals(0, circle.getBounds().height, "bounds height should be 0 (radius*2)"); } } // ------------------------------------------------------------------------- // ResizeClamping // ------------------------------------------------------------------------- @Nested class ResizeClamping { @Test void rectangleResizedToNegativeShouldClampWidthToOne() { // Start with a 10x10 rect and drag the E handle far to the left SRectangle rect = SRectangle.create(0, 0, 10, 10); rect.resize(ResizeHandle.E, -200, 0); assertTrue(rect.getBounds().width >= 1, "width must be clamped to a minimum of 1 after negative resize"); assertEquals(1, rect.getBounds().width, "width should be exactly 1 after extreme negative resize on E handle"); } @Test void rectangleResizedToNegativeShouldClampHeightToOne() { SRectangle rect = SRectangle.create(0, 0, 10, 10); rect.resize(ResizeHandle.S, 0, -200); assertTrue(rect.getBounds().height >= 1, "height must be clamped to a minimum of 1 after negative resize"); assertEquals(1, rect.getBounds().height, "height should be exactly 1 after extreme negative resize on S handle"); } @Test void circleResizedToNegativeShouldClampToTwo() { SCircle circle = SCircle.create(0, 0, 50); // Drag E handle far to the left → shrink circle.resize(ResizeHandle.E, -500, 0); assertTrue(circle.getBounds().width >= 2, "circle width must be clamped to a minimum of 2"); assertTrue(circle.getBounds().height >= 2, "circle height must be clamped to a minimum of 2"); assertEquals(2, circle.getBounds().width, "circle width should be exactly 2 after extreme negative resize"); assertEquals(2, circle.getBounds().height, "circle height should be exactly 2 after extreme negative resize"); } } // ------------------------------------------------------------------------- // NullHandling // ------------------------------------------------------------------------- @Nested class NullHandling { @Test void textCreatedWithNullShouldReturnPlaceholder() { SText text = SText.create(0, 0, null); assertEquals(SText.PLACEHOLDER_TEXT, text.getText(), "null input should be normalized to the placeholder text"); } @Test void textCreatedWithBlankStringShouldReturnPlaceholder() { SText text = SText.create(0, 0, " "); assertEquals(SText.PLACEHOLDER_TEXT, text.getText(), "blank string input should be normalized to the placeholder text"); } @Test void getAttributesWithUnknownKeyShouldReturnNull() { SRectangle rect = SRectangle.create(0, 0, 50, 50); assertNull(rect.getAttributes("NONEXISTENT"), "getAttributes() with an unknown key must return null"); } @Test void emptyCollectionCreationShouldNotThrow() { assertDoesNotThrow(() -> SCollection.of(), "SCollection.of() with no arguments must not throw"); } @Test void emptyCollectionGetBoundsShouldReturnWindowSize() { SCollection collection = SCollection.of(); Rectangle bounds = collection.getBounds(); assertNotNull(bounds, "getBounds() on empty collection must not return null"); assertEquals(App.WIN_SIZE.width, bounds.width, "empty collection bounds width should equal WIN_SIZE.width"); assertEquals(App.WIN_SIZE.height, bounds.height, "empty collection bounds height should equal WIN_SIZE.height"); } } // ------------------------------------------------------------------------- // LargeValues // ------------------------------------------------------------------------- @Nested class LargeValues { @Test void rectangleAtMaxIntHalfCoordsShouldNotThrow() { int halfMax = Integer.MAX_VALUE / 2; assertDoesNotThrow(() -> SRectangle.create(halfMax, halfMax, 100, 100), "SRectangle.create() with very large coordinates must not throw"); } @Test void rectangleAtMaxIntHalfCoordsPreservesBounds() { int halfMax = Integer.MAX_VALUE / 2; SRectangle rect = SRectangle.create(halfMax, halfMax, 100, 100); assertEquals(halfMax, rect.getBounds().x, "x should be Integer.MAX_VALUE/2"); assertEquals(halfMax, rect.getBounds().y, "y should be Integer.MAX_VALUE/2"); assertEquals(100, rect.getBounds().width, "width should be 100"); assertEquals(100, rect.getBounds().height, "height should be 100"); } @Test void translateWithLargeValuesShouldNotThrow() { SRectangle rect = SRectangle.create(0, 0, 50, 50); // Integer overflow is a known platform behaviour; we only assert no exception assertDoesNotThrow(() -> rect.translate(Integer.MAX_VALUE, Integer.MAX_VALUE), "translate() with Integer.MAX_VALUE must not throw (overflow is acceptable)"); } @Test void translateWithLargeNegativeValuesShouldNotThrow() { SRectangle rect = SRectangle.create(0, 0, 50, 50); assertDoesNotThrow(() -> rect.translate(Integer.MIN_VALUE, Integer.MIN_VALUE), "translate() with Integer.MIN_VALUE must not throw (overflow is acceptable)"); } } }