From b3f19f5c4c74f98016b7ca207b22d50725c5b6bf Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 2 Oct 2024 05:50:56 +1000 Subject: [PATCH 1/3] add unit tests back --- .../cartesian/cartesian_renderer_test.dart | 326 ++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 charts_common/test/chart/cartesian/cartesian_renderer_test.dart diff --git a/charts_common/test/chart/cartesian/cartesian_renderer_test.dart b/charts_common/test/chart/cartesian/cartesian_renderer_test.dart new file mode 100644 index 000000000..af4e22b39 --- /dev/null +++ b/charts_common/test/chart/cartesian/cartesian_renderer_test.dart @@ -0,0 +1,326 @@ +// Copyright 2018 the Charts project authors. Please see the AUTHORS file +// for details. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:math'; + +import 'package:mockito/mockito.dart'; +import 'package:nimble_charts_common/src/chart/cartesian/cartesian_renderer.dart'; +import 'package:nimble_charts_common/src/chart/common/chart_canvas.dart'; +import 'package:nimble_charts_common/src/chart/common/datum_details.dart'; +import 'package:nimble_charts_common/src/chart/common/processed_series.dart'; +import 'package:nimble_charts_common/src/chart/common/series_datum.dart'; +import 'package:nimble_charts_common/src/common/symbol_renderer.dart'; +import 'package:test/test.dart'; + +import '../../mox.mocks.dart'; + +/// For testing viewport start / end. +class FakeCartesianRenderer extends BaseCartesianRenderer { + FakeCartesianRenderer() : super(rendererId: 'fake', layoutPaintOrder: 0); + + @override + List> getNearestDatumDetailPerSeries( + Point chartPoint, + bool byDomain, + Rectangle? boundsOverride, { + bool selectOverlappingPoints = false, + bool selectExactEventLocation = false, + }) => + throw UnimplementedError(); + + @override + void paint(ChartCanvas canvas, double animationPercent) {} + + @override + void update(List> seriesList, bool isAnimating) {} + + @override + SymbolRenderer get symbolRenderer => throw UnimplementedError(); + + @override + DatumDetails addPositionToDetailsForSeriesDatum( + DatumDetails details, + SeriesDatum seriesDatum, + ) => + details; +} + +void main() { + late BaseCartesianRenderer renderer; + + setUp(() { + renderer = FakeCartesianRenderer(); + }); + + group('find viewport start', () { + test('several domains are in the viewport', () { + final data = [0, 1, 2, 3, 4, 5, 6]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(-1); + when(axis.compareDomainValueToViewport(1)).thenReturn(-1); + when(axis.compareDomainValueToViewport(2)).thenReturn(0); + when(axis.compareDomainValueToViewport(3)).thenReturn(0); + when(axis.compareDomainValueToViewport(4)).thenReturn(0); + when(axis.compareDomainValueToViewport(5)).thenReturn(1); + when(axis.compareDomainValueToViewport(6)).thenReturn(1); + + final start = renderer.findNearestViewportStart( + axis, + (index) => data[index!], + data, + ); + + expect(start, equals(2)); + }); + + test('extents are all in the viewport, use the first domain', () { + // Start of viewport is the same as the start of the domain. + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(any)).thenReturn(0); + + final start = renderer.findNearestViewportStart( + axis, + (index) => data[index!], + data, + ); + + expect(start, equals(0)); + }); + + test('is the first domain', () { + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(0); + when(axis.compareDomainValueToViewport(1)).thenReturn(1); + when(axis.compareDomainValueToViewport(2)).thenReturn(1); + when(axis.compareDomainValueToViewport(3)).thenReturn(1); + + final start = renderer.findNearestViewportStart( + axis, + (index) => data[index!], + data, + ); + + expect(start, equals(0)); + }); + + test('is the last domain', () { + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(-1); + when(axis.compareDomainValueToViewport(1)).thenReturn(-1); + when(axis.compareDomainValueToViewport(2)).thenReturn(-1); + when(axis.compareDomainValueToViewport(3)).thenReturn(0); + + final start = renderer.findNearestViewportStart( + axis, + (index) => data[index!], + data, + ); + + expect(start, equals(3)); + }); + + test('is the middle', () { + final data = [0, 1, 2, 3, 4, 5, 6]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(-1); + when(axis.compareDomainValueToViewport(1)).thenReturn(-1); + when(axis.compareDomainValueToViewport(2)).thenReturn(-1); + when(axis.compareDomainValueToViewport(3)).thenReturn(0); + when(axis.compareDomainValueToViewport(4)).thenReturn(1); + when(axis.compareDomainValueToViewport(5)).thenReturn(1); + when(axis.compareDomainValueToViewport(6)).thenReturn(1); + + final start = renderer.findNearestViewportStart( + axis, + (index) => data[index!], + data, + ); + + expect(start, equals(3)); + }); + + test('viewport is between data', () { + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(-1); + when(axis.compareDomainValueToViewport(1)).thenReturn(-1); + when(axis.compareDomainValueToViewport(2)).thenReturn(1); + when(axis.compareDomainValueToViewport(3)).thenReturn(1); + + final start = renderer.findNearestViewportStart( + axis, + (index) => data[index!], + data, + ); + + expect(start, equals(1)); + }); + + // Error case, viewport shouldn't be set to the outside of the extents. + // We still want to provide a value. + test('all extents greater than viewport ', () { + // Return the right most value as start of viewport. + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(any)).thenReturn(1); + + final start = renderer.findNearestViewportStart( + axis, + (index) => data[index!], + data, + ); + + expect(start, equals(3)); + }); + + // Error case, viewport shouldn't be set to the outside of the extents. + // We still want to provide a value. + test('all extents less than viewport ', () { + // Return the left most value as the start of the viewport. + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(any)).thenReturn(-1); + + final start = renderer.findNearestViewportStart( + axis, + (index) => data[index!], + data, + ); + + expect(start, equals(0)); + }); + }); + + group('find viewport end', () { + test('several domains are in the viewport', () { + final data = [0, 1, 2, 3, 4, 5, 6]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(-1); + when(axis.compareDomainValueToViewport(1)).thenReturn(-1); + when(axis.compareDomainValueToViewport(2)).thenReturn(0); + when(axis.compareDomainValueToViewport(3)).thenReturn(0); + when(axis.compareDomainValueToViewport(4)).thenReturn(0); + when(axis.compareDomainValueToViewport(5)).thenReturn(1); + when(axis.compareDomainValueToViewport(6)).thenReturn(1); + + final start = + renderer.findNearestViewportEnd(axis, (index) => data[index!], data); + + expect(start, equals(4)); + }); + + test('extents are all in the viewport, use the last domain', () { + // Start of viewport is the same as the end of the domain. + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(any)).thenReturn(0); + + final start = + renderer.findNearestViewportEnd(axis, (index) => data[index!], data); + + expect(start, equals(3)); + }); + + test('is the first domain', () { + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(0); + when(axis.compareDomainValueToViewport(1)).thenReturn(1); + when(axis.compareDomainValueToViewport(2)).thenReturn(1); + when(axis.compareDomainValueToViewport(3)).thenReturn(1); + + final start = + renderer.findNearestViewportEnd(axis, (index) => data[index!], data); + + expect(start, equals(0)); + }); + + test('is the last domain', () { + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(-1); + when(axis.compareDomainValueToViewport(1)).thenReturn(-1); + when(axis.compareDomainValueToViewport(2)).thenReturn(-1); + when(axis.compareDomainValueToViewport(3)).thenReturn(0); + + final start = + renderer.findNearestViewportEnd(axis, (index) => data[index!], data); + + expect(start, equals(3)); + }); + + test('is the middle', () { + final data = [0, 1, 2, 3, 4, 5, 6]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(-1); + when(axis.compareDomainValueToViewport(1)).thenReturn(-1); + when(axis.compareDomainValueToViewport(2)).thenReturn(-1); + when(axis.compareDomainValueToViewport(3)).thenReturn(0); + when(axis.compareDomainValueToViewport(4)).thenReturn(1); + when(axis.compareDomainValueToViewport(5)).thenReturn(1); + when(axis.compareDomainValueToViewport(6)).thenReturn(1); + + final start = + renderer.findNearestViewportEnd(axis, (index) => data[index!], data); + + expect(start, equals(3)); + }); + + test('viewport is between data', () { + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(0)).thenReturn(-1); + when(axis.compareDomainValueToViewport(1)).thenReturn(-1); + when(axis.compareDomainValueToViewport(2)).thenReturn(1); + when(axis.compareDomainValueToViewport(3)).thenReturn(1); + + final start = + renderer.findNearestViewportEnd(axis, (index) => data[index!], data); + + expect(start, equals(2)); + }); + + // Error case, viewport shouldn't be set to the outside of the extents. + // We still want to provide a value. + test('all extents greater than viewport ', () { + // Return the right most value as start of viewport. + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(any)).thenReturn(1); + + final start = + renderer.findNearestViewportEnd(axis, (index) => data[index!], data); + + expect(start, equals(3)); + }); + + // Error case, viewport shouldn't be set to the outside of the extents. + // We still want to provide a value. + test('all extents less than viewport ', () { + // Return the left most value as the start of the viewport. + final data = [0, 1, 2, 3]; + final axis = MockAxis(); + when(axis.compareDomainValueToViewport(any)).thenReturn(-1); + + final start = + renderer.findNearestViewportEnd(axis, (index) => data[index!], data); + + expect(start, equals(0)); + }); + }); +} From d9af77c5261e0dbab444e76321930e71a172224b Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 2 Oct 2024 05:58:02 +1000 Subject: [PATCH 2/3] fix tests --- .../axis/numeric_tick_provider_test.dart | 513 ++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 charts_common/test/chart/cartesian/axis/numeric_tick_provider_test.dart diff --git a/charts_common/test/chart/cartesian/axis/numeric_tick_provider_test.dart b/charts_common/test/chart/cartesian/axis/numeric_tick_provider_test.dart new file mode 100644 index 000000000..603a78b75 --- /dev/null +++ b/charts_common/test/chart/cartesian/axis/numeric_tick_provider_test.dart @@ -0,0 +1,513 @@ +// Copyright 2018 the Charts project authors. Please see the AUTHORS file +// for details. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:math'; + +import 'package:mockito/mockito.dart'; +import 'package:nimble_charts_common/src/chart/cartesian/axis/axis.dart'; +import 'package:nimble_charts_common/src/chart/cartesian/axis/collision_report.dart'; +import 'package:nimble_charts_common/src/chart/cartesian/axis/draw_strategy/base_tick_draw_strategy.dart'; +import 'package:nimble_charts_common/src/chart/cartesian/axis/numeric_extents.dart'; +import 'package:nimble_charts_common/src/chart/cartesian/axis/numeric_tick_provider.dart'; +import 'package:nimble_charts_common/src/chart/cartesian/axis/tick.dart'; +import 'package:nimble_charts_common/src/chart/cartesian/axis/tick_formatter.dart'; +import 'package:nimble_charts_common/src/chart/cartesian/axis/tick_provider.dart'; +import 'package:nimble_charts_common/src/chart/common/chart_canvas.dart'; +import 'package:nimble_charts_common/src/chart/common/chart_context.dart'; +import 'package:nimble_charts_common/src/chart/common/unitconverter/unit_converter.dart'; +import 'package:nimble_charts_common/src/common/graphics_factory.dart'; +import 'package:nimble_charts_common/src/common/line_style.dart'; +import 'package:nimble_charts_common/src/common/text_element.dart'; +import 'package:nimble_charts_common/src/common/text_style.dart'; +import 'package:test/test.dart'; + +import '../../../mox.mocks.dart'; + + +/// A fake draw strategy that reports collision and alternate ticks +/// +/// Reports collision when the tick count is greater than or equal to +/// [collidesAfterTickCount]. +/// +/// Reports alternate rendering after tick count is greater than or equal to +/// [alternateRenderingAfterTickCount]. +class FakeDrawStrategy extends BaseTickDrawStrategy { + FakeDrawStrategy( + this.collidesAfterTickCount, + this.alternateRenderingAfterTickCount, + ) : super(MockChartContext(), FakeGraphicsFactory()); + final int collidesAfterTickCount; + final int alternateRenderingAfterTickCount; + + @override + CollisionReport collides( + List>? ticks, + AxisOrientation? orientation, + ) { + final ticksCollide = (ticks?.length ?? 0) >= collidesAfterTickCount; + final alternateTicksUsed = + (ticks?.length ?? 0) >= alternateRenderingAfterTickCount; + + return CollisionReport( + ticksCollide: ticksCollide, + ticks: ticks, + alternateTicksUsed: alternateTicksUsed, + ); + } + + @override + void draw( + ChartCanvas canvas, + Tick tick, { + required AxisOrientation orientation, + required Rectangle axisBounds, + required Rectangle drawAreaBounds, + required bool isFirst, + required bool isLast, + bool collision = false, + }) {} +} + +/// A fake [GraphicsFactory] that returns [MockTextStyle] and [MockTextElement]. +class FakeGraphicsFactory extends GraphicsFactory { + @override + TextStyle createTextPaint() => MockTextStyle(); + + @override + TextElement createTextElement(String text) => MockTextElement(); + + @override + LineStyle createLinePaint() => MockLinePaint(); +} + +/// A celsius to fahrenheit converter for testing axis with unit converter. +class CelsiusToFahrenheitConverter implements UnitConverter { + const CelsiusToFahrenheitConverter(); + + @override + num convert(num value) => (value * 1.8) + 32.0; + + @override + num invert(num value) => (value - 32.0) / 1.8; +} + +void main() { + late FakeGraphicsFactory graphicsFactory; + late MockNumericScale scale; + late NumericTickProvider tickProvider; + late TickFormatter formatter; + late ChartContext context; + + setUp(() { + graphicsFactory = FakeGraphicsFactory(); + scale = MockNumericScale(); + tickProvider = NumericTickProvider(); + formatter = NumericTickFormatter(); + context = MockChartContext(); + }); + + test('singleTickCount_choosesTicksWithSmallestStepCoveringDomain', () { + tickProvider + ..zeroBound = false + ..dataIsInWholeNumbers = false + ..setFixedTickCount(4) + ..allowedSteps = [1.0, 2.5, 5.0]; + final drawStrategy = FakeDrawStrategy(10, 10); + when(scale.viewportDomain).thenReturn(const NumericExtents(10.0, 70.0)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + expect(ticks, hasLength(4)); + expect(ticks[0].value, equals(0)); + expect(ticks[1].value, equals(25)); + expect(ticks[2].value, equals(50)); + expect(ticks[3].value, equals(75)); + }); + + test( + 'tickCountRangeChoosesTicksWithMost' + 'TicksAndSmallestIntervalCoveringDomain', () { + tickProvider + ..zeroBound = false + ..dataIsInWholeNumbers = false + ..setTickCount(5, 3) + ..allowedSteps = [1.0, 2.5, 5.0]; + final drawStrategy = FakeDrawStrategy(10, 10); + when(scale.viewportDomain).thenReturn(const NumericExtents(10.0, 80.0)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + expect(ticks, hasLength(5)); + expect(ticks[0].value, equals(0)); + expect(ticks[1].value, equals(25)); + expect(ticks[2].value, equals(50)); + expect(ticks[3].value, equals(75)); + expect(ticks[4].value, equals(100)); + }); + + test('choosesNonAlternateRenderingTicksEvenIfIntervalIsLarger', () { + tickProvider + ..zeroBound = false + ..dataIsInWholeNumbers = false + ..setTickCount(5, 3) + ..allowedSteps = [1.0, 2.5, 6.0]; + final drawStrategy = FakeDrawStrategy(10, 5); + when(scale.viewportDomain).thenReturn(const NumericExtents(10.0, 80.0)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + expect(ticks, hasLength(3)); + expect(ticks[0].value, equals(0)); + expect(ticks[1].value, equals(60)); + expect(ticks[2].value, equals(120)); + }); + + test('choosesNonCollidingTicksEvenIfIntervalIsLarger', () { + tickProvider + ..zeroBound = false + ..dataIsInWholeNumbers = false + ..setTickCount(5, 3) + ..allowedSteps = [1.0, 2.5, 6.0]; + final drawStrategy = FakeDrawStrategy(5, 5); + when(scale.viewportDomain).thenReturn(const NumericExtents(10.0, 80.0)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + expect(ticks, hasLength(3)); + expect(ticks[0].value, equals(0)); + expect(ticks[1].value, equals(60)); + expect(ticks[2].value, equals(120)); + }); + + test('zeroBound_alwaysReturnsZeroTick', () { + tickProvider + ..zeroBound = true + ..dataIsInWholeNumbers = false + ..setFixedTickCount(3) + ..allowedSteps = [1.0, 2.5, 5.0]; + final drawStrategy = FakeDrawStrategy(10, 10); + when(scale.viewportDomain).thenReturn(const NumericExtents(55.0, 135.0)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + final tickValues = ticks.map((tick) => tick.value).toList(); + + expect(tickValues, contains(0.0)); + }); + + test('boundsCrossOrigin_alwaysReturnsZeroTick', () { + tickProvider + ..zeroBound = false + ..dataIsInWholeNumbers = false + ..setFixedTickCount(3) + ..allowedSteps = [1.0, 2.5, 5.0]; + final drawStrategy = FakeDrawStrategy(10, 10); + when(scale.viewportDomain).thenReturn(const NumericExtents(-55.0, 135.0)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + final tickValues = ticks.map((tick) => tick.value).toList(); + + expect(tickValues, contains(0.0)); + }); + + test('boundsCrossOrigin_returnsValidTickRange', () { + final drawStrategy = FakeDrawStrategy(10, 10); + when(scale.viewportDomain).thenReturn(const NumericExtents(-55.0, 135.0)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + final tickValues = ticks.map((tick) => tick.value).toList(); + + // We expect to see a range of ticks that crosses zero. + expect( + tickValues, + equals([-60.0, -30.0, 0.0, 30.0, 60.0, 90.0, 120.0, 150.0]), + ); + }); + + test('dataIsWholeNumbers_returnsWholeNumberTicks', () { + tickProvider + ..zeroBound = false + ..dataIsInWholeNumbers = true + ..setFixedTickCount(3) + ..allowedSteps = [1.0, 2.5, 5.0]; + final drawStrategy = FakeDrawStrategy(10, 10); + + when(scale.viewportDomain).thenReturn(const NumericExtents(0.25, 0.75)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + expect(ticks[0].value, equals(0)); + expect(ticks[1].value, equals(1)); + expect(ticks[2].value, equals(2)); + }); + + test('choosesTicksBasedOnPreferredAxisUnits', () { + tickProvider + ..zeroBound = true + ..dataIsInWholeNumbers = false + ..setFixedTickCount(3) + ..allowedSteps = [5.0] + ..dataToAxisUnitConverter = const CelsiusToFahrenheitConverter(); + + final drawStrategy = FakeDrawStrategy(10, 10); + + when(scale.viewportDomain).thenReturn(const NumericExtents(0.0, 20.0)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + expect(ticks[0].value, closeTo(-17.8, 0.1)); // 0 in axis units + expect(ticks[1].value, closeTo(10, 0.1)); // 50 in axis units + expect(ticks[2].value, closeTo(37.8, 0.1)); // 100 in axis units + }); + + test('handlesVerySmallMeasures', () { + tickProvider + ..zeroBound = true + ..dataIsInWholeNumbers = false + ..setFixedTickCount(5); + + final drawStrategy = FakeDrawStrategy(10, 10); + + when(scale.viewportDomain) + .thenReturn(const NumericExtents(0.000001, 0.000002)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + expect(ticks.length, equals(5)); + expect(ticks[0].value, equals(0)); + expect(ticks[1].value, equals(0.0000005)); + expect(ticks[2].value, equals(0.0000010)); + expect(ticks[3].value, equals(0.0000015)); + expect(ticks[4].value, equals(0.000002)); + }); + + test('handlesVerySmallMeasuresForWholeNumbers', () { + tickProvider + ..zeroBound = true + ..dataIsInWholeNumbers = true + ..setFixedTickCount(5); + + final drawStrategy = FakeDrawStrategy(10, 10); + + when(scale.viewportDomain) + .thenReturn(const NumericExtents(0.000001, 0.000002)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + expect(ticks.length, equals(5)); + expect(ticks[0].value, equals(0)); + expect(ticks[1].value, equals(1)); + expect(ticks[2].value, equals(2)); + expect(ticks[3].value, equals(3)); + expect(ticks[4].value, equals(4)); + }); + + test('handlesVerySmallMeasuresForWholeNumbersWithoutZero', () { + tickProvider + ..zeroBound = false + ..dataIsInWholeNumbers = true + ..setFixedTickCount(5); + + final drawStrategy = FakeDrawStrategy(10, 10); + + when(scale.viewportDomain) + .thenReturn(const NumericExtents(101.000001, 101.000002)); + when(scale.rangeWidth).thenReturn(1000); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + ); + + expect(ticks.length, equals(5)); + expect(ticks[0].value, equals(101)); + expect(ticks[1].value, equals(102)); + expect(ticks[2].value, equals(103)); + expect(ticks[3].value, equals(104)); + expect(ticks[4].value, equals(105)); + }); + + test('handles tick hint for non zero ticks', () { + final drawStrategy = FakeDrawStrategy(10, 10); + when(scale.viewportDomain).thenReturn(const NumericExtents(20.0, 35.0)); + when(scale.rangeWidth).thenReturn(1000); + + // Step Size: 3, + // Previous start tick: 10 + // Previous window: 10 - 25 + // Previous ticks: 10, 13, 16, 19, 22, 25 + final tickHint = TickHint(10, 25, tickCount: 6); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + tickHint: tickHint, + ); + + // adjusted ticks for window 20 - 35 + // Should have ticks 22, 25, 28, 31, 34, 37 + expect(ticks, hasLength(6)); + expect(ticks[0].value, equals(22)); + expect(ticks[1].value, equals(25)); + expect(ticks[2].value, equals(28)); + expect(ticks[3].value, equals(31)); + expect(ticks[4].value, equals(34)); + expect(ticks[5].value, equals(37)); + }); + + test('handles tick hint for negative starting ticks', () { + final drawStrategy = FakeDrawStrategy(10, 10); + when(scale.viewportDomain).thenReturn(const NumericExtents(-35.0, -20.0)); + when(scale.rangeWidth).thenReturn(1000); + + // Step Size: 3, + // Previous start tick: -25 + // Previous window: -25 to -10 + // Previous ticks: -25, -22, -19, -16, -13, -10 + final tickHint = TickHint(-25, -10, tickCount: 6); + + final ticks = tickProvider.getTicks( + context: context, + graphicsFactory: graphicsFactory, + scale: scale, + formatter: formatter, + formatterValueCache: {}, + tickDrawStrategy: drawStrategy, + orientation: null, + tickHint: tickHint, + ); + + // adjusted ticks for window -35 to -20 + // Should have ticks -34, -31, -28, -25, -22, -19 + expect(ticks, hasLength(6)); + expect(ticks[0].value, equals(-34)); + expect(ticks[1].value, equals(-31)); + expect(ticks[2].value, equals(-28)); + expect(ticks[3].value, equals(-25)); + expect(ticks[4].value, equals(-22)); + expect(ticks[5].value, equals(-19)); + }); +} From 09927e8a4ec23e2a59ebb391a34dbd5f51beaf11 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 2 Oct 2024 06:01:53 +1000 Subject: [PATCH 3/3] format --- .../test/chart/cartesian/axis/numeric_tick_provider_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/charts_common/test/chart/cartesian/axis/numeric_tick_provider_test.dart b/charts_common/test/chart/cartesian/axis/numeric_tick_provider_test.dart index 603a78b75..8d817456a 100644 --- a/charts_common/test/chart/cartesian/axis/numeric_tick_provider_test.dart +++ b/charts_common/test/chart/cartesian/axis/numeric_tick_provider_test.dart @@ -35,7 +35,6 @@ import 'package:test/test.dart'; import '../../../mox.mocks.dart'; - /// A fake draw strategy that reports collision and alternate ticks /// /// Reports collision when the tick count is greater than or equal to