diff --git a/CodenameOne/src/com/codename1/charts/models/XYSeries.java b/CodenameOne/src/com/codename1/charts/models/XYSeries.java index 1d7bf17fc3..0b95a42cd4 100644 --- a/CodenameOne/src/com/codename1/charts/models/XYSeries.java +++ b/CodenameOne/src/com/codename1/charts/models/XYSeries.java @@ -212,9 +212,10 @@ public synchronized void clearSeriesValues() { /** * Removes all the existing annotations from the series. */ - public synchronized void clearAnnotations() { - mStringXY.clear(); - } + public synchronized void clearAnnotations() { + mStringXY.clear(); + mAnnotations.clear(); + } /** * Returns the current values that are used for drawing the series. diff --git a/maven/core-unittests/src/test/java/com/codename1/capture/CaptureTest.java b/maven/core-unittests/src/test/java/com/codename1/capture/CaptureTest.java new file mode 100644 index 0000000000..0952b308bc --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/capture/CaptureTest.java @@ -0,0 +1,137 @@ +package com.codename1.capture; + +import com.codename1.io.Util; +import com.codename1.test.UITestBase; +import com.codename1.ui.Image; +import com.codename1.ui.events.ActionEvent; +import com.codename1.ui.util.ImageIO; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class CaptureTest extends UITestBase { + private static final String ORIGINAL_PATH = "/tmp/photo.jpg"; + + @BeforeEach + void configureUtil() { + Util.setImplementation(implementation); + } + + @AfterEach + void resetUtil() { + Util.setImplementation(null); + reset(implementation); + } + + @Test + void callBackStoresUrlFromActionEvent() { + Capture.CallBack callBack = new Capture.CallBack(); + ActionEvent event = new ActionEvent(ORIGINAL_PATH); + + callBack.actionPerformed(event); + + assertEquals(ORIGINAL_PATH, callBack.url); + } + + @Test + void callBackSetsUrlToNullWhenEventIsNull() { + Capture.CallBack callBack = new Capture.CallBack(); + + callBack.actionPerformed(null); + + assertNull(callBack.url); + } + + @Test + void runSkipsProcessingWhenUrlIsNull() { + Capture.CallBack callBack = new Capture.CallBack(); + callBack.actionPerformed(null); + + callBack.run(); + + verify(implementation, never()).getImageIO(); + } + + @Test + void runRescalesImageWhenDimensionsProvided() throws Exception { + Capture.CallBack callBack = new Capture.CallBack(); + callBack.actionPerformed(new ActionEvent(ORIGINAL_PATH)); + setPrivateInt(callBack, "targetWidth", 120); + setPrivateInt(callBack, "targetHeight", 80); + + StubImageIO imageIO = new StubImageIO(); + when(implementation.getImageIO()).thenReturn(imageIO); + + AtomicReference savedPath = new AtomicReference<>(); + AtomicReference deletedPath = new AtomicReference<>(); + when(implementation.openFileOutputStream(anyString())).thenAnswer(invocation -> { + savedPath.set(invocation.getArgument(0)); + return new ByteArrayOutputStream(); + }); + doAnswer(invocation -> { + deletedPath.set(invocation.getArgument(0)); + return null; + }).when(implementation).deleteFile(anyString()); + when(implementation.openFileInputStream(anyString())).thenThrow(new IOException("Not expected")); + + callBack.run(); + + String expectedScaledPath = "/tmp/photos.jpg"; + assertEquals(expectedScaledPath, callBack.url); + assertEquals(expectedScaledPath, savedPath.get()); + assertEquals(ORIGINAL_PATH, deletedPath.get()); + assertEquals(Collections.singletonList(ORIGINAL_PATH), imageIO.savedFiles); + } + + private void setPrivateInt(Object target, String fieldName, int value) throws Exception { + Field field = target.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.setInt(target, value); + } + + private static class StubImageIO extends ImageIO { + private final List savedFiles = new ArrayList<>(); + + @Override + public void save(String imageFilePath, OutputStream response, String format, int width, int height, float quality) { + savedFiles.add(imageFilePath); + try { + response.write(1); + } catch (IOException ignored) { + } + } + + @Override + public void save(java.io.InputStream image, OutputStream response, String format, int width, int height, float quality) throws IOException { + // Not used in this test + response.write(1); + } + + @Override + protected void saveImage(Image img, OutputStream response, String format, float quality) throws IOException { + response.write(1); + } + + @Override + public boolean isFormatSupported(String format) { + return true; + } + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/charts/ChartComponentTest.java b/maven/core-unittests/src/test/java/com/codename1/charts/ChartComponentTest.java new file mode 100644 index 0000000000..84c2e952d3 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/charts/ChartComponentTest.java @@ -0,0 +1,258 @@ +package com.codename1.charts; + +import com.codename1.charts.compat.Canvas; +import com.codename1.charts.compat.Paint; +import com.codename1.charts.models.Point; +import com.codename1.charts.models.SeriesSelection; +import com.codename1.charts.models.XYMultipleSeriesDataset; +import com.codename1.charts.renderers.SimpleSeriesRenderer; +import com.codename1.charts.renderers.XYMultipleSeriesRenderer; +import com.codename1.charts.renderers.XYSeriesRenderer; +import com.codename1.charts.views.AbstractChart; +import com.codename1.charts.views.ClickableArea; +import com.codename1.charts.views.XYChart; +import com.codename1.test.UITestBase; +import com.codename1.ui.Transform; +import com.codename1.ui.geom.Rectangle; +import com.codename1.ui.geom.Shape; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ChartComponentTest extends UITestBase { + @Test + void constructorCopiesPanAndZoomSettingsFromXYChart() { + XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); + renderer.setPanEnabled(true); + renderer.setZoomEnabled(true, true); + StubXYChart chart = new StubXYChart(renderer); + + ChartComponent component = new ChartComponent(chart); + + assertTrue(component.isPanEnabled()); + assertTrue(component.isZoomEnabled()); + } + + @Test + void coordinateConversionsHonorCurrentTransform() throws Exception { + RecordingChart chart = new RecordingChart(); + ChartComponent component = new PositionedChartComponent(chart, 5, 10); + + Transform translation = Transform.makeTranslation(3, 4); + setCurrentTransform(component, translation); + + Point chartPoint = component.screenToChartCoord(20, 30); + assertEquals(12f, chartPoint.getX()); + assertEquals(16f, chartPoint.getY()); + + Point screenPoint = component.chartToScreenCoord(12, 16); + assertEquals(20f, screenPoint.getX()); + assertEquals(30f, screenPoint.getY()); + } + + @Test + void shapeConversionsTranslateBetweenSpaces() throws Exception { + RecordingChart chart = new RecordingChart(); + ChartComponent component = new PositionedChartComponent(chart, 10, 15); + + Rectangle screenRect = new Rectangle(component.getAbsoluteX(), component.getAbsoluteY(), 40, 50); + Rectangle chartBounds = component.screenToChartShape(screenRect).getBounds(); + assertEquals(0, chartBounds.getX()); + assertEquals(0, chartBounds.getY()); + assertEquals(0, chartBounds.getSize().getWidth()); + assertEquals(0, chartBounds.getSize().getHeight()); + + Rectangle chartRect = new Rectangle(0, 0, 40, 50); + Rectangle screenBounds = component.chartToScreenShape(chartRect).getBounds(); + assertEquals(0, screenBounds.getX()); + assertEquals(0, screenBounds.getY()); + assertEquals(0, screenBounds.getSize().getWidth()); + assertEquals(0, screenBounds.getSize().getHeight()); + } + + @Test + void pointerEventsInvokeSeriesCallbacksWhenSelectionExists() { + RecordingChart chart = new RecordingChart(); + chart.selectionToReturn = new SeriesSelection(1, 2, 3, 4); + TestChartComponent component = new TestChartComponent(chart); + + component.pointerPressed(5, 7); + assertEquals(chart.selectionToReturn, component.lastPressedSelection); + assertEquals(5f, chart.lastPoint.getX()); + assertEquals(7f, chart.lastPoint.getY()); + + component.pointerReleased(5, 7); + assertEquals(chart.selectionToReturn, component.lastReleasedSelection); + } + + @Test + void panConfigurationUpdatesRendererState() { + XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); + StubXYChart chart = new StubXYChart(renderer); + ChartComponent component = new ChartComponent(chart); + + component.setPanEnabled(false); + assertFalse(component.isPanEnabled()); + assertFalse(renderer.isPanEnabled()); + + component.setPanEnabled(true, false); + assertTrue(component.isPanEnabled()); + assertTrue(renderer.isPanXEnabled()); + assertFalse(renderer.isPanYEnabled()); + } + + @Test + void panLimitsAreAppliedToRenderer() { + XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); + StubXYChart chart = new StubXYChart(renderer); + ChartComponent component = new ChartComponent(chart); + + component.setPanLimits(1, 2, 3, 4); + assertArrayEquals(new double[]{1, 2, 3, 4}, renderer.getPanLimits()); + + component.clearPanLimits(); + assertNull(renderer.getPanLimits()); + } + + @Test + void settingPanLimitsOnNonXYChartThrows() { + ChartComponent component = new ChartComponent(new RecordingChart()); + assertThrows(RuntimeException.class, () -> component.setPanLimits(0, 1, 0, 1)); + } + + @Test + void zoomConfigurationKeepsComponentFocusable() { + XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); + StubXYChart chart = new StubXYChart(renderer); + ChartComponent component = new ChartComponent(chart); + + component.setZoomEnabled(true); + assertTrue(component.isZoomEnabled()); + assertTrue(component.isFocusable()); + assertTrue(renderer.isZoomEnabled()); + + component.setZoomEnabled(false, true); + assertTrue(component.isZoomEnabled()); + assertTrue(renderer.isZoomYEnabled()); + assertFalse(renderer.isZoomXEnabled()); + } + + @Test + void zoomLimitsRequireXYChart() { + ChartComponent component = new ChartComponent(new RecordingChart()); + assertThrows(RuntimeException.class, () -> component.setZoomLimits(1, 2, 3, 4)); + } + + private void setCurrentTransform(ChartComponent component, Transform transform) throws Exception { + Field field = ChartComponent.class.getDeclaredField("currentTransform"); + field.setAccessible(true); + field.set(component, transform); + } + + private static class RecordingChart extends AbstractChart { + private SeriesSelection selectionToReturn; + private Point lastPoint; + + @Override + public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint) { + } + + @Override + public SeriesSelection getSeriesAndPointForScreenCoordinate(Point screenPoint) { + lastPoint = screenPoint; + return selectionToReturn; + } + + @Override + public int getLegendShapeWidth(int seriesIndex) { + return 0; + } + + @Override + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, int seriesIndex, Paint paint) { + } + } + + private static class StubXYChart extends XYChart { + StubXYChart(XYMultipleSeriesRenderer renderer) { + super(new XYMultipleSeriesDataset(), renderer); + } + + @Override + public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint) { + } + + @Override + public void drawSeries(Canvas canvas, Paint paint, List points, XYSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex) { + } + + @Override + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, int seriesIndex, Paint paint) { + } + + @Override + protected ClickableArea[] clickableAreasForPoints(List points, List values, float yAxisValue, int seriesIndex, int startIndex) { + return new ClickableArea[0]; + } + + @Override + public int getLegendShapeWidth(int seriesIndex) { + return 0; + } + + @Override + public String getChartType() { + return "Stub"; + } + + @Override + public SeriesSelection getSeriesAndPointForScreenCoordinate(Point screenPoint) { + return null; + } + } + + private static class PositionedChartComponent extends ChartComponent { + private final int absoluteX; + private final int absoluteY; + + PositionedChartComponent(AbstractChart chart, int absoluteX, int absoluteY) { + super(chart); + this.absoluteX = absoluteX; + this.absoluteY = absoluteY; + setX(absoluteX); + setY(absoluteY); + } + + @Override + public int getAbsoluteX() { + return absoluteX; + } + + @Override + public int getAbsoluteY() { + return absoluteY; + } + } + + private static class TestChartComponent extends ChartComponent { + private SeriesSelection lastPressedSelection; + private SeriesSelection lastReleasedSelection; + + TestChartComponent(AbstractChart chart) { + super(chart); + } + + @Override + protected void seriesPressed(SeriesSelection sel) { + lastPressedSelection = sel; + } + + @Override + protected void seriesReleased(SeriesSelection sel) { + lastReleasedSelection = sel; + } + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/charts/models/XYSeriesTest.java b/maven/core-unittests/src/test/java/com/codename1/charts/models/XYSeriesTest.java new file mode 100644 index 0000000000..26d5db14bf --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/charts/models/XYSeriesTest.java @@ -0,0 +1,112 @@ +package com.codename1.charts.models; + +import com.codename1.charts.util.MathHelper; +import org.junit.jupiter.api.Test; + +import java.util.SortedMap; + +import static org.junit.jupiter.api.Assertions.*; + +class XYSeriesTest { + @Test + void addUpdatesRangeAndValues() { + XYSeries series = new XYSeries("s"); + series.add(1.0, 2.0); + series.add(3.0, 4.0); + + assertEquals(2, series.getItemCount()); + assertEquals(1.0, series.getMinX()); + assertEquals(3.0, series.getMaxX()); + assertEquals(2.0, series.getY(0)); + assertEquals(4.0, series.getY(1)); + } + + @Test + void duplicateXValuesAreOffsetToPreserveOrder() { + XYSeries series = new XYSeries("s"); + series.add(1.0, 2.0); + series.add(1.0, 3.0); + + double first = series.getX(0); + double second = series.getX(1); + assertTrue(second > first); + } + + @Test + void removeRecalculatesRangeWhenRemovingExtremes() { + XYSeries series = new XYSeries("s"); + series.add(1.0, 5.0); + series.add(5.0, -1.0); + series.add(3.0, 4.0); + + series.remove(0); + + assertEquals(3.0, series.getMinX()); + assertEquals(5.0, series.getMaxX()); + assertEquals(-1.0, series.getMinY()); + assertEquals(4.0, series.getMaxY()); + } + + @Test + void clearMethodsResetState() { + XYSeries series = new XYSeries("s"); + series.add(1.0, 2.0); + series.add(2.0, 3.0); + series.addAnnotation("a", 1.0, 2.0); + + series.clearSeriesValues(); + assertEquals(1, series.getAnnotationCount()); + + series.clear(); + assertEquals(0, series.getAnnotationCount()); + assertEquals(MathHelper.NULL_VALUE, series.getMinX()); + } + + @Test + void annotationLifecycleIsTracked() { + XYSeries series = new XYSeries("s"); + series.addAnnotation("first", 0, 1.0, 2.0); + series.addAnnotation("second", 1, 2.0, 3.0); + + assertEquals(2, series.getAnnotationCount()); + assertEquals("first", series.getAnnotationAt(0)); + assertEquals(2.0, series.getAnnotationX(1)); + assertEquals(3.0, series.getAnnotationY(1)); + + series.removeAnnotation(0); + assertEquals(1, series.getAnnotationCount()); + assertEquals("second", series.getAnnotationAt(0)); + } + + @Test + void getRangeSupportsBeforeAfterPoints() { + XYSeries series = new XYSeries("s"); + series.add(1.0, 1.0); + series.add(2.0, 2.0); + series.add(3.0, 3.0); + series.add(4.0, 4.0); + + SortedMap regularRange = series.getRange(1.5, 3.5, false); + assertEquals(2, regularRange.size()); + assertTrue(regularRange.containsKey(2.0)); + assertTrue(regularRange.containsKey(3.0)); + + SortedMap expandedRange = series.getRange(2.0, 3.0, true); + assertEquals(3, expandedRange.size()); + assertTrue(expandedRange.containsKey(1.0)); + assertTrue(expandedRange.containsKey(2.0)); + assertTrue(expandedRange.containsKey(3.0)); + } + + @Test + void getIndexForKeyUsesSortedOrder() { + XYSeries series = new XYSeries("s"); + series.add(5.0, 1.0); + series.add(1.0, 2.0); + series.add(3.0, 3.0); + + assertEquals(0, series.getIndexForKey(1.0)); + assertEquals(1, series.getIndexForKey(3.0)); + assertEquals(2, series.getIndexForKey(5.0)); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/charts/models/XYValueSeriesTest.java b/maven/core-unittests/src/test/java/com/codename1/charts/models/XYValueSeriesTest.java new file mode 100644 index 0000000000..a7f6d0b342 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/charts/models/XYValueSeriesTest.java @@ -0,0 +1,58 @@ +package com.codename1.charts.models; + +import com.codename1.charts.util.MathHelper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class XYValueSeriesTest { + @Test + void addTracksMinAndMaxValues() { + XYValueSeries series = new XYValueSeries("values"); + series.add(1.0, 2.0, 5.0); + series.add(2.0, 3.0, 1.0); + series.add(3.0, 4.0, 7.0); + + assertEquals(3, series.getItemCount()); + assertEquals(5.0, series.getValue(0)); + assertEquals(1.0, series.getMinValue()); + assertEquals(7.0, series.getMaxValue()); + } + + @Test + void addWithoutExplicitValueDefaultsToZero() { + XYValueSeries series = new XYValueSeries("values"); + series.add(1.0, 2.0); + + assertEquals(0.0, series.getValue(0)); + assertEquals(0.0, series.getMinValue()); + assertEquals(0.0, series.getMaxValue()); + } + + @Test + void removeReinitializesRangeWhenRemovingExtremes() { + XYValueSeries series = new XYValueSeries("values"); + series.add(1.0, 1.0, 5.0); + series.add(2.0, 2.0, -3.0); + series.add(3.0, 3.0, 2.0); + + series.remove(1); + + assertEquals(2, series.getItemCount()); + assertEquals(2.0, series.getMinValue()); + assertEquals(5.0, series.getMaxValue()); + } + + @Test + void clearResetsRangeAndValues() { + XYValueSeries series = new XYValueSeries("values"); + series.add(1.0, 1.0, 5.0); + series.add(2.0, 2.0, -3.0); + + series.clear(); + + assertEquals(0, series.getItemCount()); + assertEquals(MathHelper.NULL_VALUE, series.getMinValue()); + assertEquals(MathHelper.NULL_VALUE, series.getMaxValue()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/charts/transitions/SeriesTransitionTest.java b/maven/core-unittests/src/test/java/com/codename1/charts/transitions/SeriesTransitionTest.java new file mode 100644 index 0000000000..0888719e08 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/charts/transitions/SeriesTransitionTest.java @@ -0,0 +1,175 @@ +package com.codename1.charts.transitions; + +import com.codename1.charts.ChartComponent; +import com.codename1.charts.compat.Canvas; +import com.codename1.charts.compat.Paint; +import com.codename1.charts.renderers.SimpleSeriesRenderer; +import com.codename1.charts.views.AbstractChart; +import com.codename1.test.UITestBase; +import com.codename1.ui.Form; +import com.codename1.ui.animations.Animation; +import com.codename1.ui.animations.Motion; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +class SeriesTransitionTest extends UITestBase { + private RecordingForm form; + private TestChartComponent chartComponent; + + @BeforeEach + void setupChartComponent() { + form = new RecordingForm(); + chartComponent = new TestChartComponent(new RecordingChart()); + chartComponent.setWidth(100); + chartComponent.setHeight(100); + form.add(chartComponent); + } + + @Test + void animateChartRegistersAnimationAndRunsToCompletion() throws Exception { + TestSeriesTransition transition = new TestSeriesTransition(chartComponent, SeriesTransition.EASING_LINEAR, 10); + + transition.animateChart(); + assertTrue(form.registeredAnimations.contains(transition)); + + Motion motion = getMotion(transition); + motion.finish(); + + assertTrue(transition.animate()); + // Legacy motions only flip the finished flag once the progress value has been + // fetched, so one more animate() pass is required before cleanup kicks in. + assertTrue(transition.animate()); + assertFalse(transition.animate()); + assertTrue(transition.cleanupCalled); + assertTrue(form.deregisteredAnimations.contains(transition)); + assertEquals(100, transition.progressUpdates.get(transition.progressUpdates.size() - 1)); + } + + @Test + void updateChartForcesRepaint() { + TestSeriesTransition transition = new TestSeriesTransition(chartComponent, SeriesTransition.EASING_LINEAR, 10); + + transition.updateChart(); + + assertTrue(chartComponent.repaintCalled); + } + + @Test + void settersUpdateConfiguration() { + TestSeriesTransition transition = new TestSeriesTransition(chartComponent, SeriesTransition.EASING_LINEAR, 10); + ChartComponent otherComponent = new TestChartComponent(new RecordingChart()); + + transition.setDuration(250); + assertEquals(250, transition.getDuration()); + + transition.setEasing(SeriesTransition.EASING_IN_OUT); + assertEquals(SeriesTransition.EASING_IN_OUT, transition.getEasing()); + + transition.setChart(otherComponent); + assertSame(otherComponent, transition.getChart()); + } + + @Test + void initTransitionUsesExpectedMotionForEachEasing() throws Exception { + Map expectedMotionTypes = new HashMap<>(); + expectedMotionTypes.put(SeriesTransition.EASING_LINEAR, getMotionType(Motion.createLinearMotion(0, 100, 1))); + expectedMotionTypes.put(SeriesTransition.EASING_IN, getMotionType(Motion.createEaseInMotion(0, 100, 1))); + expectedMotionTypes.put(SeriesTransition.EASING_OUT, getMotionType(Motion.createEaseOutMotion(0, 100, 1))); + expectedMotionTypes.put(SeriesTransition.EASING_IN_OUT, getMotionType(Motion.createEaseInOutMotion(0, 100, 1))); + + for (Map.Entry entry : expectedMotionTypes.entrySet()) { + TestSeriesTransition transition = new TestSeriesTransition(chartComponent, entry.getKey(), 10); + transition.animateChart(); + Motion motion = getMotion(transition); + assertEquals(entry.getValue(), getMotionType(motion)); + motion.setCurrentMotionTime(transition.getDuration() + 1); + transition.animate(); + transition.animate(); + } + } + + private Motion getMotion(SeriesTransition transition) throws Exception { + Field motionField = SeriesTransition.class.getDeclaredField("motion"); + motionField.setAccessible(true); + return (Motion) motionField.get(transition); + } + + private int getMotionType(Motion motion) throws Exception { + Field typeField = Motion.class.getDeclaredField("motionType"); + typeField.setAccessible(true); + return typeField.getInt(motion); + } + + private static class RecordingForm extends Form { + private final List registeredAnimations = new ArrayList<>(); + private final List deregisteredAnimations = new ArrayList<>(); + + @Override + public void registerAnimated(Animation cmp) { + registeredAnimations.add(cmp); + super.registerAnimated(cmp); + } + + @Override + public void deregisterAnimated(Animation cmp) { + deregisteredAnimations.add(cmp); + super.deregisterAnimated(cmp); + } + } + + private static class RecordingChart extends AbstractChart { + @Override + public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint) { + } + + @Override + public int getLegendShapeWidth(int seriesIndex) { + return 0; + } + + @Override + public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y, int seriesIndex, Paint paint) { + } + } + + private static class TestChartComponent extends ChartComponent { + private boolean repaintCalled; + + TestChartComponent(AbstractChart chart) { + super(chart); + } + + @Override + public void repaint() { + repaintCalled = true; + super.repaint(); + } + } + + private static class TestSeriesTransition extends SeriesTransition { + private final List progressUpdates = new ArrayList<>(); + private boolean cleanupCalled; + + TestSeriesTransition(ChartComponent chart, int easing, int duration) { + super(chart, easing, duration); + } + + @Override + protected void update(int progress) { + progressUpdates.add(progress); + } + + @Override + protected void cleanup() { + cleanupCalled = true; + } + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/charts/util/ColorUtilTest.java b/maven/core-unittests/src/test/java/com/codename1/charts/util/ColorUtilTest.java new file mode 100644 index 0000000000..999c0d656e --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/charts/util/ColorUtilTest.java @@ -0,0 +1,32 @@ +package com.codename1.charts.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ColorUtilTest { + @Test + void argbAndRgbProduceExpectedValues() { + int color = ColorUtil.argb(0x12, 0x34, 0x56, 0x78); + assertEquals(0x12345678, color); + + int rgb = ColorUtil.rgb(0xAA, 0xBB, 0xCC); + assertEquals(0xFFAABBCC, rgb); + } + + @Test + void colorComponentExtractionReturnsOriginalValues() { + int color = 0x7FAB5623; + assertEquals(0x7F, ColorUtil.alpha(color)); + assertEquals(0xAB, ColorUtil.red(color)); + assertEquals(0x56, ColorUtil.green(color)); + assertEquals(0x23, ColorUtil.blue(color)); + } + + @Test + void predefinedColorsMatchRgbValues() { + assertEquals(ColorUtil.rgb(0, 0, 255), ColorUtil.BLUE); + assertEquals(ColorUtil.rgb(0, 255, 0), ColorUtil.GREEN); + assertEquals(ColorUtil.rgb(255, 255, 0), ColorUtil.YELLOW); + } +}