From d9b4d66c0f264a7351ac32bdcf720a41dcdbad6c Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Fri, 12 Sep 2025 15:03:33 +0200 Subject: [PATCH] Add tests for interpolation logic --- lib/helpers/consts.dart | 4 ++ lib/helpers/measurements.dart | 12 +++- lib/widgets/measurements/charts.dart | 2 +- test/helpers/measurements_test.dart | 90 ++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 test/helpers/measurements_test.dart diff --git a/lib/helpers/consts.dart b/lib/helpers/consts.dart index 7d538e02..e7136ca0 100644 --- a/lib/helpers/consts.dart +++ b/lib/helpers/consts.dart @@ -137,3 +137,7 @@ enum WeightUnitEnum { kg, lb } const textInputTypeDecimal = TextInputType.numberWithOptions(decimal: true); const String API_MAX_PAGE_SIZE = '999'; + +/// Marker used for identifying interpolated values in a list, e.g. for measurements +/// the milliseconds in the entry date are set to this value +const INTERPOLATION_MARKER = 123; diff --git a/lib/helpers/measurements.dart b/lib/helpers/measurements.dart index 9f5d0050..f3cd5462 100644 --- a/lib/helpers/measurements.dart +++ b/lib/helpers/measurements.dart @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/date.dart'; import 'package:wger/widgets/measurements/charts.dart'; @@ -95,8 +96,15 @@ MeasurementChartEntry interpolateBetween( // Create a special DateTime with milliseconds ending in 123 to mark it as interpolated // which we leverage in the UI - final markedDate = - DateTime(date.year, date.month, date.day, date.hour, date.minute, date.second, 123); + final markedDate = DateTime( + date.year, + date.month, + date.day, + date.hour, + date.minute, + date.second, + INTERPOLATION_MARKER, + ); return MeasurementChartEntry( before.value + (after.value - before.value) * (startDuration / totalDuration), diff --git a/lib/widgets/measurements/charts.dart b/lib/widgets/measurements/charts.dart index 4c09f139..076fa493 100644 --- a/lib/widgets/measurements/charts.dart +++ b/lib/widgets/measurements/charts.dart @@ -88,7 +88,7 @@ class _MeasurementChartWidgetFlState extends State { DateFormat.Md(Localizations.localeOf(context).languageCode).format(date); // Check if this is an interpolated point (milliseconds ending with 123) - final bool isInterpolated = msSinceEpoch % 1000 == 123; + final bool isInterpolated = msSinceEpoch % 1000 == INTERPOLATION_MARKER; final String interpolatedMarker = isInterpolated ? ' (interpolated)' : ''; return LineTooltipItem( diff --git a/test/helpers/measurements_test.dart b/test/helpers/measurements_test.dart new file mode 100644 index 00000000..ddf0886a --- /dev/null +++ b/test/helpers/measurements_test.dart @@ -0,0 +1,90 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:wger/helpers/consts.dart'; +import 'package:wger/helpers/measurements.dart'; +import 'package:wger/widgets/measurements/charts.dart'; + +void main() { + group('whereDateWithInterpolation', () { + // Helper to create entries + MeasurementChartEntry entry(num value, DateTime date) => MeasurementChartEntry(value, date); + + // Test: No interpolation needed, exact start and end dates exist + test('returns entries within range when start and end exist', () { + final entries = [ + entry(10, DateTime(2023, 1, 1)), + entry(20, DateTime(2023, 1, 2)), + entry(30, DateTime(2023, 1, 3)), + ]; + final result = entries.whereDateWithInterpolation(DateTime(2023, 1, 1), DateTime(2023, 1, 3)); + + // Entries on start and end date should be included if they exist + expect(result.first.value, 10); + expect(result.last.value, 20); + }); + + // Test: Interpolates start if missing + test('interpolates start if missing', () { + final entries = [ + entry(10, DateTime(2023, 1, 1)), + entry(30, DateTime(2023, 1, 3)), + ]; + final result = entries.whereDateWithInterpolation(DateTime(2023, 1, 2), DateTime(2023, 1, 3)); + + // Only the interpolated value for 2nd Jan is included + expect(result.length, 1); + expect(result.first.value, closeTo(20, 0.0001)); + expect(result.first.date.millisecond, INTERPOLATION_MARKER); + expect(result.first.date.day, 2); + }); + + // Test: Interpolates end if missing + test('interpolates end if missing', () { + final entries = [ + entry(10, DateTime(2023, 1, 1)), + entry(30, DateTime(2023, 1, 3)), + ]; + final result = entries.whereDateWithInterpolation(DateTime(2023, 1, 1), DateTime(2023, 1, 2)); + // Should include the entry for 1st Jan and an interpolated value for 2nd Jan + expect(result.length, 2); + expect(result.first.value, 10); + expect(result.first.date.day, 1); + expect(result.last.value, closeTo(20, 0.0001)); + expect(result.last.date.day, 2); + }); + + // Test: No interpolation if out of bounds + test('returns empty if no data in range', () { + final entries = [ + entry(10, DateTime(2023, 1, 1)), + entry(20, DateTime(2023, 1, 2)), + ]; + final result = entries.whereDateWithInterpolation(DateTime(2023, 2, 1), DateTime(2023, 2, 2)); + expect(result, isEmpty); + }); + + // Test: Only start interpolation if data exists before and after + test('does not interpolate if no data before start', () { + final entries = [ + entry(10, DateTime(2023, 1, 2)), + entry(20, DateTime(2023, 1, 3)), + ]; + final result = entries.whereDateWithInterpolation(DateTime(2023, 1, 1), DateTime(2023, 1, 3)); + // No interpolation possible for Jan 1, only entry for Jan 2 is included + expect(result.length, 1); + expect(result.first.date.day, 2); + }); + + // Test: Only end interpolation if data exists before and after + test('does not interpolate if no data after end', () { + final entries = [ + entry(10, DateTime(2023, 1, 1)), + entry(20, DateTime(2023, 1, 2)), + ]; + final result = entries.whereDateWithInterpolation(DateTime(2023, 1, 1), DateTime(2023, 1, 3)); + // No interpolation possible for Jan 3, only entries for Jan 1 and Jan 2 are included + expect(result.length, 2); + expect(result.first.date.day, 1); + expect(result.last.date.day, 2); + }); + }); +}