diff --git a/jhotdraw-core/jgiven-reports/org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest.json b/jhotdraw-core/jgiven-reports/org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest.json new file mode 100644 index 000000000..ed2e1a4a5 --- /dev/null +++ b/jhotdraw-core/jgiven-reports/org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest.json @@ -0,0 +1,202 @@ +{ + "className": "org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest", + "name": "Ellipse Figure BDD", + "scenarios": [ + { + "className": "org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest", + "testMethodName": "ellipse_resizes_when_bounds_are_set", + "description": "ellipse resizes when bounds are set", + "tagIds": [], + "explicitParameters": [], + "derivedParameters": [], + "scenarioCases": [ + { + "caseNr": 1, + "steps": [ + { + "name": "an ellipse", + "words": [ + { + "value": "Given", + "isIntroWord": true + }, + { + "value": "an ellipse" + } + ], + "status": "PASSED", + "durationInNanos": 21426208, + "depth": 0, + "parentFailed": false + }, + { + "name": "bounds are set", + "words": [ + { + "value": "When", + "isIntroWord": true + }, + { + "value": "bounds are set" + }, + { + "value": "0.0", + "argumentInfo": { + "argumentName": "x1", + "formattedValue": "0.0" + } + }, + { + "value": "0.0", + "argumentInfo": { + "argumentName": "y1", + "formattedValue": "0.0" + } + }, + { + "value": "100.0", + "argumentInfo": { + "argumentName": "x2", + "formattedValue": "100.0" + } + }, + { + "value": "50.0", + "argumentInfo": { + "argumentName": "y2", + "formattedValue": "50.0" + } + } + ], + "status": "FAILED", + "durationInNanos": 18851584, + "depth": 0, + "parentFailed": false + }, + { + "name": "ellipse should have size", + "words": [ + { + "value": "Then", + "isIntroWord": true + }, + { + "value": "ellipse should have size" + }, + { + "value": "100.0", + "argumentInfo": { + "argumentName": "width", + "formattedValue": "100.0" + } + }, + { + "value": "50.0", + "argumentInfo": { + "argumentName": "height", + "formattedValue": "50.0" + } + } + ], + "status": "SKIPPED", + "durationInNanos": 0, + "depth": 0, + "parentFailed": false + } + ], + "explicitArguments": [], + "derivedArguments": [], + "status": "FAILED", + "errorMessage": "java.lang.NullPointerException: Cannot invoke \"org.jhotdraw.draw.figure.EllipseFigure.setBounds(java.awt.geom.Point2D$Double, java.awt.geom.Point2D$Double)\" because \"this.ellipse\" is null", + "stackTrace": [ + "org.jhotdraw.draw.figure.BDDEllipse.WhenSettingBounds.bounds_are_set(WhenSettingBounds.java:16)", + "org.jhotdraw.draw.figure.BDDEllipse.WhenSettingBounds$ByteBuddy$rriOpszZ.bounds_are_set$accessor$FNcLk1s9(Unknown Source)", + "org.jhotdraw.draw.figure.BDDEllipse.WhenSettingBounds$ByteBuddy$rriOpszZ$auxiliary$zGYJXUzv.call(Unknown Source)", + "org.jhotdraw.draw.figure.BDDEllipse.WhenSettingBounds$ByteBuddy$rriOpszZ.bounds_are_set(Unknown Source)", + "org.jhotdraw.draw.figure.BDDEllipse.EllipseFigureBDDTest.ellipse_resizes_when_bounds_are_set(EllipseFigureBDDTest.java:13)", + "java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)", + "org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:701)", + "org.junit.platform.commons.support.ReflectionSupport.invokeMethod(ReflectionSupport.java:502)", + "org.junit.jupiter.engine.support.MethodReflectionUtils.invoke(MethodReflectionUtils.java:45)", + "org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:61)", + "org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:124)", + "org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:163)", + "org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:148)", + "org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)", + "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:123)", + "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:105)", + "org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:99)", + "org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:66)", + "org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:47)", + "org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:39)", + "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:104)", + "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:98)", + "org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invokeVoid(InterceptingExecutableInvoker.java:71)", + "org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$0(TestMethodTestDescriptor.java:219)", + "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)", + "org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:215)", + "org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:157)", + "org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:70)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:176)", + "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166)", + "org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164)", + "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116)", + "java.base/java.util.ArrayList.forEach(ArrayList.java:1597)", + "org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:42)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:180)", + "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166)", + "org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164)", + "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116)", + "java.base/java.util.ArrayList.forEach(ArrayList.java:1597)", + "org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:42)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$2(NodeTestTask.java:180)", + "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$1(NodeTestTask.java:166)", + "org.junit.platform.engine.support.hierarchical.Node.around(Node.java:138)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$0(NodeTestTask.java:164)", + "org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:74)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:163)", + "org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:116)", + "org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:36)", + "org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:52)", + "org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:58)", + "org.junit.platform.launcher.core.EngineExecutionOrchestrator.executeEngine(EngineExecutionOrchestrator.java:246)", + "org.junit.platform.launcher.core.EngineExecutionOrchestrator.failOrExecuteEngine(EngineExecutionOrchestrator.java:218)", + "org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:179)", + "org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)", + "org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:66)", + "org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:157)", + "org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:65)", + "org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:125)", + "org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)", + "org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:93)", + "org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:48)", + "org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$0(InterceptingLauncher.java:41)", + "org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25)", + "org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:40)", + "org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:48)", + "org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:67)", + "com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)", + "com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)", + "com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)", + "com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)", + "com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:231)", + "com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)" + ], + "durationInNanos": 99998209 + } + ], + "casesAsTable": false, + "durationInNanos": 99998209 + } + ], + "tagMap": {} +} \ No newline at end of file diff --git a/jhotdraw-core/pom.xml b/jhotdraw-core/pom.xml index 7c276da85..6ec23a754 100644 --- a/jhotdraw-core/pom.xml +++ b/jhotdraw-core/pom.xml @@ -1,6 +1,21 @@ 4.0.0 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + --add-opens java.base/java.lang=ALL-UNNAMED + + + + + + org.jhotdraw jhotdraw @@ -9,6 +24,22 @@ jhotdraw-core jar + + + com.tngtech.jgiven + jgiven-junit5 + 1.3.0 + test + + + + + org.assertj + assertj-core + 3.24.2 + test + + ${project.groupId} jhotdraw-api @@ -40,5 +71,29 @@ jhotdraw-actions ${project.version} + + junit + junit + 4.13.2 + compile + + + org.junit.jupiter + junit-jupiter + RELEASE + compile + + + com.tngtech.jgiven + jgiven-junit5 + 1.3.0 + compile + + + org.assertj + assertj-core + 3.25.3 + compile + \ No newline at end of file diff --git a/jhotdraw-core/src/main/java/org/jhotdraw/draw/figure/EllipseFigure.java b/jhotdraw-core/src/main/java/org/jhotdraw/draw/figure/EllipseFigure.java index 624562ac9..26262d6fc 100644 --- a/jhotdraw-core/src/main/java/org/jhotdraw/draw/figure/EllipseFigure.java +++ b/jhotdraw-core/src/main/java/org/jhotdraw/draw/figure/EllipseFigure.java @@ -9,6 +9,7 @@ import java.awt.*; import java.awt.geom.*; +import org.jhotdraw.draw.AttributeKey; import org.jhotdraw.draw.AttributeKeys; import org.jhotdraw.draw.connector.ChopEllipseConnector; import org.jhotdraw.draw.connector.Connector; @@ -25,6 +26,29 @@ public class EllipseFigure extends AbstractAttributedFigure { private static final long serialVersionUID = 1L; protected Ellipse2D.Double ellipse; + /** + * Copy constructor for EllipseFigure. + */ + public EllipseFigure(EllipseFigure source) { + this.ellipse = copyEllipse(source.ellipse); + setAttributes(source.getAttributes()); + for (AttributeKey key : source.getAttributes().keySet()) { + if (!source.isAttributeEnabled(key)) { + setAttributeEnabled(key, false); + } + } + } + + + private Ellipse2D.Double copyEllipse(Ellipse2D.Double sourceEllipse) { + return new Ellipse2D.Double( + sourceEllipse.x, + sourceEllipse.y, + sourceEllipse.width, + sourceEllipse.height + ); + } + /** * Constructs a new {@code EllipseFigure}, initialized to * location (0, 0) and size (0, 0). @@ -77,14 +101,21 @@ public Rectangle2D.Double getDrawingArea() { return r; } + private Ellipse2D.Double prepareEllipse(double grow) { + Ellipse2D.Double copy = copyEllipse(this.ellipse); + copy.x -= grow; + copy.y -= grow; + copy.width += grow * 2; + copy.height += grow * 2; + return copy; + } + @Override protected void drawFill(Graphics2D g) { - Ellipse2D.Double r = (Ellipse2D.Double) ellipse.clone(); - double grow = AttributeKeys.getPerpendicularFillGrowth(this, AttributeKeys.getScaleFactorFromGraphics(g)); - r.x -= grow; - r.y -= grow; - r.width += grow * 2; - r.height += grow * 2; + Ellipse2D.Double r = prepareEllipse( + AttributeKeys.getPerpendicularFillGrowth(this, + AttributeKeys.getScaleFactorFromGraphics(g)) + ); if (r.width > 0 && r.height > 0) { g.fill(r); } @@ -92,28 +123,20 @@ protected void drawFill(Graphics2D g) { @Override protected void drawStroke(Graphics2D g) { - Ellipse2D.Double r = (Ellipse2D.Double) ellipse.clone(); - double grow = AttributeKeys.getPerpendicularDrawGrowth(this, AttributeKeys.getScaleFactorFromGraphics(g)); - r.x -= grow; - r.y -= grow; - r.width += grow * 2; - r.height += grow * 2; + Ellipse2D.Double r = prepareEllipse( + AttributeKeys.getPerpendicularDrawGrowth(this, + AttributeKeys.getScaleFactorFromGraphics(g)) + ); if (r.width > 0 && r.height > 0) { g.draw(r); } } - /** - * Checks if a Point2D.Double is inside the figure. - */ @Override public boolean contains(Point2D.Double p) { - Ellipse2D.Double r = (Ellipse2D.Double) ellipse.clone(); - double grow = AttributeKeys.getPerpendicularHitGrowth(this, 1.0); - r.x -= grow; - r.y -= grow; - r.width += grow * 2; - r.height += grow * 2; + Ellipse2D.Double r = prepareEllipse( + AttributeKeys.getPerpendicularHitGrowth(this, 1.0) + ); return r.contains(p); } @@ -139,13 +162,6 @@ public void transform(AffineTransform tx) { (Point2D.Double) tx.transform(lead, lead)); } - @Override - public EllipseFigure clone() { - EllipseFigure that = (EllipseFigure) super.clone(); - that.ellipse = (Ellipse2D.Double) this.ellipse.clone(); - return that; - } - @Override public void restoreTransformTo(Object geometry) { Ellipse2D.Double e = (Ellipse2D.Double) geometry; diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/EllipseFigureBDDTest.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/EllipseFigureBDDTest.java new file mode 100644 index 000000000..1beec7270 --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/EllipseFigureBDDTest.java @@ -0,0 +1,16 @@ +package org.jhotdraw.draw.figure.BDDEllipse; + +import com.tngtech.jgiven.junit5.ScenarioTest; +import org.junit.jupiter.api.Test; + + +public class EllipseFigureBDDTest extends + ScenarioTest { + + @Test + public void ellipse_resizes_when_bounds_are_set() { + given().an_ellipse(); + when().bounds_are_set(0, 0, 100, 50); + then().ellipse_should_have_size(100, 50); + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/GivenEllipse.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/GivenEllipse.java new file mode 100644 index 000000000..33e6e8909 --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/GivenEllipse.java @@ -0,0 +1,16 @@ +package org.jhotdraw.draw.figure.BDDEllipse; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import org.jhotdraw.draw.figure.EllipseFigure; + +public class GivenEllipse extends Stage { + + @ProvidedScenarioState + EllipseFigure ellipse; + + public GivenEllipse an_ellipse() { + ellipse = new EllipseFigure(); + return self(); + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/ThenEllipse.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/ThenEllipse.java new file mode 100644 index 000000000..019d1083c --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/ThenEllipse.java @@ -0,0 +1,20 @@ +package org.jhotdraw.draw.figure.BDDEllipse; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import org.jhotdraw.draw.figure.EllipseFigure; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ThenEllipse extends Stage { + + @ExpectedScenarioState + EllipseFigure ellipse; + + public ThenEllipse ellipse_should_have_size(double width, double height) { + assertThat(ellipse.getBounds().width).isEqualTo(width); + assertThat(ellipse.getBounds().height).isEqualTo(height); + return self(); + } +} + diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/WhenSettingBounds.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/WhenSettingBounds.java new file mode 100644 index 000000000..ff28eeeef --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/figure/BDDEllipse/WhenSettingBounds.java @@ -0,0 +1,20 @@ +package org.jhotdraw.draw.figure.BDDEllipse; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import org.jhotdraw.draw.figure.EllipseFigure; + +import java.awt.geom.Point2D; + + +public class WhenSettingBounds extends Stage { + + @ExpectedScenarioState + EllipseFigure ellipse; + + public WhenSettingBounds bounds_are_set(double x1, double y1, double x2, double y2) { + ellipse.setBounds(new Point2D.Double(x1, y1), new Point2D.Double(x2, y2)); + return self(); + } +} +