Commit 647028b

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2023-12-06 16:34:10
test and fix ScriptedFormatter
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 89a3e3b
Changed files (5)
lib/model/export_import/column.dart
@@ -2,7 +2,7 @@ import 'dart:convert';
 
 import 'package:blood_pressure_app/model/blood_pressure.dart';
 import 'package:blood_pressure_app/model/export_import/legacy_column.dart';
-import 'package:blood_pressure_app/model/export_import/reocord_formatter.dart';
+import 'package:blood_pressure_app/model/export_import/record_formatter.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 
lib/model/export_import/legacy_column.dart
@@ -1,5 +1,5 @@
 import 'package:blood_pressure_app/model/blood_pressure.dart';
-import 'package:blood_pressure_app/model/export_import/reocord_formatter.dart';
+import 'package:blood_pressure_app/model/export_import/record_formatter.dart';
 import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 
 /// Convert [BloodPressureRecord]s from and to strings and provide metadata about the conversion.
@@ -80,12 +80,24 @@ class ExportColumn { // TODO: change this class so it implements the interface.
   }
 }
 
-/// Type a [ExportColumn] can be parsed as.
+/// Type a [Formatter] can uses to indicate the kind of data returned.
+///
+/// The data types returned from the deprecated [ExportColumn] may differ from the guarantees.
 enum RowDataFieldType {
-  timestamp, sys, dia, pul, notes,
-  @Deprecated('use needlePin instead')
+  /// Guarantees [DateTime] is returned.
+  timestamp,
+  /// Guarantees [int] is returned.
+  sys,
+  /// Guarantees [int] is returned.
+  dia,
+  /// Guarantees [int] is returned.
+  pul,
+  /// Guarantees [String] is returned.
+  notes,
+  @Deprecated('use needlePin instead') // TODO: implement conversion to needle pin?
   color,
-  needlePin;
+  /// Guarantees that the returned type is of type [MeasurementNeedlePin].
+  needlePin;  // TODO implement in ScriptedFormatter
 
   String localize(AppLocalizations localizations) {
     switch(this) {
lib/model/export_import/reocord_formatter.dart → lib/model/export_import/record_formatter.dart
@@ -38,40 +38,36 @@ class ScriptedFormatter implements Formatter {
 
   @override
   (RowDataFieldType, dynamic)? decode(String formattedRecord) {
-    // TODO: rewrite contents
+    print('$pattern.decode($formattedRecord)');
     if (restoreAbleType == null) return null;
 
-    if (pattern == r'$NOTE') return (RowDataFieldType.notes, formattedRecord);
-    if (pattern == r'$COLOR') {
-      final value = int.tryParse(formattedRecord);
-      return value == null ? null : (RowDataFieldType.color, Color(value));
-    }
-
-    // records are parse by replacing the values with capture groups
-    final types = RegExp(r'\$(TIMESTAMP|SYS|DIA|PUL)').allMatches(pattern).map((e) => e.group(0)).toList();
-    final numRegex = pattern.replaceAll(RegExp(r'\$(TIMESTAMP|SYS|DIA|PUL)'), '([0-9]+.?[0-9]*)'); // ints and doubles
-    final numMatches = RegExp(numRegex).allMatches(formattedRecord);
-    final numbers = [];
-    if (numMatches.isNotEmpty) {
-      for (var i = 1; i <= numMatches.first.groupCount; i++) {
-        numbers.add(numMatches.first[i]);
-      }
-    }
-
-    for (var i = 0; i < types.length; i++) {
-      switch (types[i]) {
-        case r'$TIMESTAMP':
-          return (RowDataFieldType.timestamp, int.tryParse(numbers[i] ?? ''));
-        case r'$SYS':
-          return (RowDataFieldType.sys, double.tryParse(numbers[i] ?? ''));
-        case r'$DIA':
-          return (RowDataFieldType.dia, double.tryParse(numbers[i] ?? ''));
-        case r'$PUL':
-          return (RowDataFieldType.pul, double.tryParse(numbers[i] ?? ''));
-      }
-    }
-
-    assert(false);
+    final valueRegex = RegExp(pattern.replaceAll(RegExp(r'\$(TIMESTAMP|COLOR|SYS|DIA|PUL|NOTE)'), '(?<value>.*)'),);
+    final match = valueRegex.firstMatch(formattedRecord);
+    if (match == null) return null;
+    var text = match.namedGroup('value');
+    if (text == null) return null;
+
+    final value = (){switch(restoreAbleType!) {
+      case RowDataFieldType.timestamp:
+        final num = int.tryParse(text);
+        if (num != null) return DateTime.fromMillisecondsSinceEpoch(num);
+        return null;
+      case RowDataFieldType.sys:
+      case RowDataFieldType.dia:
+      case RowDataFieldType.pul:
+        return int.tryParse(text);
+      case RowDataFieldType.notes:
+        return text;
+      case RowDataFieldType.color:
+        final num = int.tryParse(text);
+        if (num != null) return Color(num);
+        return null;
+      case RowDataFieldType.needlePin:
+        final num = int.tryParse(text);
+        if (num == null) return null;
+        return MeasurementNeedlePin(Color(num));
+    }}();
+    if (value != null) return (restoreAbleType!, value);
     return null;
   }
 
@@ -114,7 +110,6 @@ class ScriptedFormatter implements Formatter {
   RowDataFieldType? _restoreAbleType;
 
   @override
-  // TODO: rewrite (logic)
   RowDataFieldType? get restoreAbleType {
     if (_hasRestoreableType == false) {
       if (pattern.contains(RegExp(r'[{},]'))) {
@@ -123,14 +118,14 @@ class ScriptedFormatter implements Formatter {
         _restoreAbleType = RowDataFieldType.timestamp; 
       } else if (pattern == r'$COLOR') { 
         _restoreAbleType = RowDataFieldType.color; 
-      } else if (pattern.contains(RegExp(r'[^{},$]*\$(SYS)[^{},$]*'))) { 
+      } else if (pattern == r'$NOTE') {
+        _restoreAbleType = RowDataFieldType.notes;
+      } else if (pattern.contains(RegExp(r'[^{},$]*\$(SYS)[^{},$]*'))) {
         _restoreAbleType = RowDataFieldType.sys; 
       } else if (pattern.contains(RegExp(r'[^{},$]*\$(DIA)[^{},$]*'))) { 
         _restoreAbleType = RowDataFieldType.dia; 
       } else if (pattern.contains(RegExp(r'[^{},$]*\$(PUL)[^{},$]*'))) { 
         _restoreAbleType = RowDataFieldType.pul; 
-      } else if (pattern.contains(RegExp(r'[^{},$]*\$(NOTE)[^{},$]*'))) { 
-        _restoreAbleType = RowDataFieldType.notes; 
       } else { _restoreAbleType = null; }
       _hasRestoreableType = true;
     }
test/model/export_import/column_test.dart
@@ -1,79 +0,0 @@
-
-import 'package:blood_pressure_app/model/blood_pressure.dart';
-import 'package:blood_pressure_app/model/export_import/legacy_column.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-import 'package:intl/intl.dart';
-
-void main() {
-  group('ExportColumn', () {
-    test('should not throw errors', () async {
-      final c = ExportColumn(
-          internalName: 'testColumn',
-          columnTitle: 'Test',
-          formatPattern: r'$SYS'
-      );
-
-      expect(c.internalName, 'testColumn');
-      expect(c.columnTitle, 'Test');
-      expect(c.formatPattern, r'$SYS');
-      c.formatRecord(BloodPressureRecord(DateTime.now(), 123, 45, 67, 'notes'));
-      c.formatRecord(BloodPressureRecord(DateTime.now(), null, null, null, ''));
-      c.parseRecord('122');
-    });
-    test('should not change during json conversion', () {
-      final original = ExportColumn(
-          internalName: 'testColumn',
-          columnTitle: 'Test',
-          formatPattern: r'{{$SYS-$DIA}}',
-      );
-      final fromJson = ExportColumn.fromJson(original.toJson());
-      expect(original.internalName, fromJson.internalName);
-      expect(original.columnTitle, fromJson.columnTitle);
-      expect(original.formatPattern, fromJson.formatPattern);
-      expect(original.isReversible, fromJson.isReversible);
-      expect(original.formatPattern, fromJson.formatPattern);
-      expect(original.toJson(), fromJson.toJson());
-    });
-    test('should create correct strings', () {
-      final testRecord = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), 123, 45, 67, 'Test',
-          needlePin: const MeasurementNeedlePin(Colors.red));
-
-      expect(_testColumn(r'$SYS',).formatRecord(testRecord), testRecord.systolic.toString());
-      expect(_testColumn(r'$DIA',).formatRecord(testRecord), testRecord.diastolic.toString());
-      expect(_testColumn(r'$PUL',).formatRecord(testRecord), testRecord.pulse.toString());
-      expect(_testColumn(r'$COLOR',).formatRecord(testRecord), testRecord.needlePin!.color.value.toString());
-      expect(_testColumn(r'$NOTE',).formatRecord(testRecord), testRecord.notes);
-      expect(_testColumn(r'$TIMESTAMP',).formatRecord(testRecord), testRecord.creationTime.millisecondsSinceEpoch.toString());
-      expect(_testColumn(r'{{$SYS-$DIA}}',).formatRecord(testRecord),
-          (testRecord.systolic! - testRecord.diastolic!).toDouble().toString());
-      expect(_testColumn(r'{{$SYS*$DIA-$PUL}}',).formatRecord(testRecord),
-          (testRecord.systolic! * testRecord.diastolic! - testRecord.pulse!).toDouble().toString());
-      expect(_testColumn(r'$SYS-$DIA',).formatRecord(testRecord), ('${testRecord.systolic}-${testRecord.diastolic}'));
-
-      final formatter = DateFormat.yMMMMEEEEd();
-      expect(_testColumn('\$FORMAT{\$TIMESTAMP,${formatter.pattern}}',).formatRecord(testRecord),
-          formatter.format(testRecord.creationTime));
-    });
-    test('should report correct reversibility', () {
-      expect(_testColumn(r'$SYS',).isReversible, true);
-      expect(_testColumn(r'$DIA',).isReversible, true);
-      expect(_testColumn(r'$PUL',).isReversible, true);
-      expect(_testColumn(r'$TIMESTAMP',).isReversible, true);
-      expect(_testColumn(r'$NOTE',).isReversible, true);
-      expect(_testColumn(r'$COLOR',).isReversible, true);
-      expect(_testColumn(r'test$NOTE',).isReversible, true);
-      expect(_testColumn(r'test$NOTE123',).isReversible, true);
-      expect(_testColumn(r'test$SYS123',).isReversible, true);
-      expect(_testColumn(r'test$DIA123',).isReversible, true);
-      expect(_testColumn(r'test$PUL123',).isReversible, true);
-
-      //expect(_testColumn(r'$PUL$SYS',).isReversible, false);
-      expect(_testColumn(r'{{$PUL-$SYS}}',).isReversible, false);
-    });
-    // TODO: test parsing
-  });
-}
-
-ExportColumn _testColumn(String formatPattern) =>
-    ExportColumn(internalName: '', columnTitle: '', formatPattern: formatPattern,);
\ No newline at end of file
test/model/export_import/record_formatter_test.dart
@@ -0,0 +1,73 @@
+import 'package:blood_pressure_app/model/blood_pressure.dart';
+import 'package:blood_pressure_app/model/export_import/legacy_column.dart';
+import 'package:blood_pressure_app/model/export_import/record_formatter.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:intl/intl.dart';
+
+void main() {
+  group('ScriptedFormatter', () {
+    test('should not throw errors', () async {
+      final f = ScriptedFormatter(r'$SYS');
+      
+      expect(f.formatPattern, r'$SYS');
+      f.encode(BloodPressureRecord(DateTime.now(), 123, 456, 789, 'test text'));
+      f.encode(BloodPressureRecord(DateTime.now(), null, null, null, ''));
+      f.decode('123');
+    });
+    test('should create correct strings', () {
+      final testRecord = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), 123, 45, 67, 'Test',
+          needlePin: const MeasurementNeedlePin(Colors.red));
+
+      expect(ScriptedFormatter(r'constant text',).encode(testRecord), 'constant text');
+      expect(ScriptedFormatter(r'$SYS',).encode(testRecord), testRecord.systolic.toString());
+      expect(ScriptedFormatter(r'$DIA',).encode(testRecord), testRecord.diastolic.toString());
+      expect(ScriptedFormatter(r'$PUL',).encode(testRecord), testRecord.pulse.toString());
+      expect(ScriptedFormatter(r'$COLOR',).encode(testRecord), testRecord.needlePin!.color.value.toString());
+      expect(ScriptedFormatter(r'$NOTE',).encode(testRecord), testRecord.notes);
+      expect(ScriptedFormatter(r'$TIMESTAMP',).encode(testRecord), testRecord.creationTime.millisecondsSinceEpoch.toString());
+      expect(ScriptedFormatter(r'{{$SYS-$DIA}}',).encode(testRecord),
+          (testRecord.systolic! - testRecord.diastolic!).toDouble().toString());
+      expect(ScriptedFormatter(r'{{$SYS*$DIA-$PUL}}',).encode(testRecord),
+          (testRecord.systolic! * testRecord.diastolic! - testRecord.pulse!).toDouble().toString());
+      expect(ScriptedFormatter(r'$SYS-$DIA',).encode(testRecord), ('${testRecord.systolic}-${testRecord.diastolic}'));
+
+      final formatter = DateFormat.yMMMMEEEEd();
+      expect(ScriptedFormatter('\$FORMAT{\$TIMESTAMP,${formatter.pattern}}',).encode(testRecord),
+          formatter.format(testRecord.creationTime));
+    });
+    test('should report correct reversibility', () {
+      expect(ScriptedFormatter(r'$SYS',).restoreAbleType, RowDataFieldType.sys);
+      expect(ScriptedFormatter(r'$DIA',).restoreAbleType, RowDataFieldType.dia);
+      expect(ScriptedFormatter(r'$PUL',).restoreAbleType, RowDataFieldType.pul);
+      expect(ScriptedFormatter(r'$TIMESTAMP',).restoreAbleType, RowDataFieldType.timestamp);
+      expect(ScriptedFormatter(r'$NOTE',).restoreAbleType, RowDataFieldType.notes);
+      expect(ScriptedFormatter(r'$COLOR',).restoreAbleType, RowDataFieldType.color);
+      expect(ScriptedFormatter(r'test$SYS123',).restoreAbleType, RowDataFieldType.sys);
+      expect(ScriptedFormatter(r'test$DIA123',).restoreAbleType, RowDataFieldType.dia);
+      expect(ScriptedFormatter(r'test$PUL123',).restoreAbleType, RowDataFieldType.pul);
+
+      //expect(ScriptedFormatter(r'$PUL$SYS',).isReversible, false);
+      expect(ScriptedFormatter(r'test$NOTE',).restoreAbleType, null);
+      expect(ScriptedFormatter(r'test$NOTE123',).restoreAbleType, null);
+      expect(ScriptedFormatter(r'{{$PUL-$SYS}}',).restoreAbleType, null);
+    });
+    test('should correctly decode reversible patterns', () {
+      expect(ScriptedFormatter(r'$SYS',).decode('123'), (RowDataFieldType.sys, 123));
+      expect(ScriptedFormatter(r'$DIA',).decode('456'), (RowDataFieldType.dia, 456));
+      expect(ScriptedFormatter(r'$PUL',).decode('789'), (RowDataFieldType.pul, 789));
+      expect(ScriptedFormatter(r'$TIMESTAMP',).decode('12345678'), (RowDataFieldType.timestamp, DateTime.fromMillisecondsSinceEpoch(12345678)));
+      expect(ScriptedFormatter(r'$NOTE',).decode('test note'), (RowDataFieldType.notes, 'test note'));
+      final encodedPurple = ScriptedFormatter(r'$COLOR',)
+          .encode(BloodPressureRecord(DateTime.now(), null, null, null, '',
+          needlePin: const MeasurementNeedlePin(Colors.purple)));
+      expect(ScriptedFormatter(r'$COLOR',).decode(encodedPurple)?.$1, RowDataFieldType.color);
+      expect(ScriptedFormatter(r'$COLOR',).decode(encodedPurple)?.$2, isA<Color>()
+          .having((p0) => p0.value, 'color', Colors.purple.value));
+      expect(ScriptedFormatter(r'test$SYS',).decode('test567'), (RowDataFieldType.sys, 567));
+      expect(ScriptedFormatter(r'test$SYS123',).decode('test567123'), (RowDataFieldType.sys, 567));
+      expect(ScriptedFormatter(r'test$DIA123',).decode('test567123'), (RowDataFieldType.dia, 567));
+      expect(ScriptedFormatter(r'test$PUL123',).decode('test567123'), (RowDataFieldType.pul, 567));
+    });
+  });
+}
\ No newline at end of file