Commit 7fa7c10

derdilla <derdilla06@gmail.com>
2023-05-07 11:16:31
Add model tests
1 parent 4c41bb7
lib/model/blood_pressure.dart
@@ -123,7 +123,7 @@ class BloodPressureModel extends ChangeNotifier {
     if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
       callback(true, 'Exported to: $path');
     } else if (Platform.isAndroid || Platform.isIOS) {
-      // TODO: compatability option
+      // TODO: compatability option (MIME Types)
       Share.shareXFiles([XFile(path, mimeType: MimeType.csv.type,)]);
       callback(true, null);
     } else {
lib/model/settings.dart
@@ -1,4 +1,6 @@
 
+import 'dart:io';
+
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:path/path.dart';
@@ -35,6 +37,13 @@ class Settings extends ChangeNotifier {
   }
   // factory method, to allow for async contructor
   static Future<Settings> create() async {
+    if (Platform.isWindows || Platform.isLinux) {
+      // Initialize FFI
+      sqfliteFfiInit();
+      // Change the default factory
+      databaseFactory = databaseFactoryFfi;
+    }
+
     final component = Settings._create();
     await component._asyncInit();
     return component;
@@ -72,7 +81,7 @@ class Settings extends ChangeNotifier {
     _graphStepSize = (await pGraphStepSize as int?) ?? TimeStep.day;
     _graphStart = DateTime.fromMillisecondsSinceEpoch((await pGraphStart as int?) ?? -1);
     _graphEnd = DateTime.fromMillisecondsSinceEpoch((await pGraphEnd as int?) ?? -1);
-    _followSystemDarkMode = ((await pFollowSystemDarkMode as int?) ?? "1") == "1" ? true : false;
+    _followSystemDarkMode = ((await pFollowSystemDarkMode as int?) ?? 1) == 1 ? true : false;
     _darkMode = ((await pDarkMode as int?) ?? 1) == 1 ? true : false;
     _accentColor = createMaterialColor(await pAccentColor as int? ?? 0xFF009688);
     _sysColor = createMaterialColor(await pSysColor as int? ?? 0xFF009688);
@@ -206,6 +215,8 @@ class TimeStep {
   static const year = 2;
   static const lifetime = 3;
 
+  TimeStep._create();
+
   static String getName(int opt) {
     switch (opt) {
       case day:
lib/main.dart
@@ -9,14 +9,6 @@ import 'package:sqflite/sqflite.dart';
 
 void main() async {
   WidgetsFlutterBinding.ensureInitialized();
-
-  if (Platform.isWindows || Platform.isLinux) {
-    // Initialize FFI
-    sqfliteFfiInit();
-    // Change the default factory
-    databaseFactory = databaseFactoryFfi;
-  }
-
   // 2 different db files
   final dataModel = await BloodPressureModel.create();
   final settingsModel = await Settings.create();
test/model/bood_pressure_test.dart
@@ -0,0 +1,128 @@
+import 'package:blood_pressure_app/model/blood_pressure.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:sqflite_common_ffi/sqflite_ffi.dart';
+import 'dart:io';
+
+void main() {
+  group('BloodPressureRecord', () {
+    test('should initialize with all values supported by dart', () {
+      BloodPressureRecord record = BloodPressureRecord(
+          DateTime.fromMicrosecondsSinceEpoch(0),
+          0,
+          -50,
+          1000,
+          "((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+
+      expect(record.creationTime, DateTime.fromMicrosecondsSinceEpoch(0));
+      expect(record.systolic, 0);
+      expect(record.diastolic, -50);
+      expect(record.pulse, 1000);
+      expect(record.notes, "((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+    });
+    test('should compare depending on creation time', () {
+      var first = BloodPressureRecord(DateTime.fromMicrosecondsSinceEpoch(0), 0, 0, 0, '');
+      var middle = BloodPressureRecord(DateTime.fromMicrosecondsSinceEpoch(10000), 0, 0, 0, '');
+      var last = BloodPressureRecord(DateTime.now(), 0, 0, 0, '');
+
+      expect(first.compareTo(middle), -1);
+      expect(middle.compareTo(first), 1);
+      expect(last.compareTo(first), 1);
+      expect(last.compareTo(last), 0);
+    });
+  });
+
+  group('BloodPressureModel',() {
+    // setup db path
+    databaseFactory = databaseFactoryFfi;
+
+    test('should initialize', () async {
+      await clearDbDir();
+      expect(() async { await BloodPressureModel.create(); }, returnsNormally);
+    });
+    test('should start empty', () async {
+      await clearDbDir();
+      var m = await BloodPressureModel.create();
+
+      expect((await m.getLastX(100)).length, 0);
+      expect((await m.getInTimeRange(DateTime.fromMillisecondsSinceEpoch(0), DateTime.now())).length, 0);
+
+    });
+
+    test('should notify when adding entries', () async {
+      await clearDbDir();
+      var m = await BloodPressureModel.create();
+
+      int listenerCalls = 0;
+      int added = 0;
+      m.addListener(() {
+        listenerCalls++;
+        expect(listenerCalls, added);
+      });
+
+      m.add(BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(0), 0, 0, 0, ''));
+      added++;
+      m.add(BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(1), 0, 0, 0, ''));
+      added++;
+      m.add(BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(2), 0, 0, 0, ''));
+      added++;
+
+    });
+
+    test('should return entries as added', () async {
+      await clearDbDir();
+      var m = await BloodPressureModel.create();
+
+      var r = BloodPressureRecord(DateTime.now(), -172, 10000, 0, "((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+      m.addListener(() async {
+        var res = (await m.getLastX(1)).first;
+        expect(res, isNotNull);
+        expect(res.creationTime, r.creationTime);
+        expect(res.systolic, r.systolic);
+        expect(res.diastolic, r.diastolic);
+        expect(res.pulse, r.pulse);
+        expect(res.notes, r.notes);
+      });
+
+      m.add(r);
+    });
+
+    test('should save and load between objects/sessions', () async {
+      await clearDbDir();
+
+      var m = await BloodPressureModel.create();
+      var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0, "((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+      await m.add(r);
+
+      var m2 = await BloodPressureModel.create();
+      var res = (await m2.getLastX(1)).first;
+
+      expect(res.creationTime, r.creationTime);
+      expect(res.systolic, r.systolic);
+      expect(res.diastolic, r.diastolic);
+      expect(res.pulse, r.pulse);
+      expect(res.notes, r.notes);
+    });
+
+    test('should import exported values', () async {
+      await clearDbDir();
+      var m = await BloodPressureModel.create();
+      var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0, "((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+      await m.add(r);
+
+      m.save((success, msg) {
+        expect(success, true);
+        // TODO: rewrite blood_pressure to remove UI code entirely
+      });
+    });
+  });
+}
+
+Future<void> clearDbDir() async {
+  databaseFactory.setDatabasesPath((await getDatabasesPath()).replaceAll('databases', 'test_databases'));
+  try {
+    Directory(await getDatabasesPath()).deleteSync(recursive: true);
+  } catch (e) {
+    print('no directory to delete!');
+  }
+  Directory(await getDatabasesPath()).create(recursive: true);
+}
\ No newline at end of file
test/model/settings_test.dart
@@ -0,0 +1,117 @@
+import 'dart:io';
+
+import 'package:blood_pressure_app/model/settings.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:sqflite_common_ffi/sqflite_ffi.dart';
+
+void main() {
+  group('TimeStep', () {
+    test('names should match to fields', () {
+      expect(TimeStep.getName(TimeStep.day), 'day');
+      expect(TimeStep.getName(TimeStep.month), 'month');
+      expect(TimeStep.getName(TimeStep.year), 'year');
+      expect(TimeStep.getName(TimeStep.lifetime), 'lifetime');
+    });
+  });
+
+  group('Settings model',() {
+    // setup db path
+    databaseFactory = databaseFactoryFfi;
+
+    test('should initialize', () async {
+      await clearDbDir();
+      expect(() async { await Settings.create(); }, returnsNormally);
+    });
+    test('fields defaults should be set after initialization', () async {
+      await clearDbDir();
+
+      var s = await Settings.create();
+      expect(s.graphStepSize, TimeStep.day);
+      expect(s.graphStart, DateTime.fromMillisecondsSinceEpoch(-1));
+      expect(s.graphEnd, DateTime.fromMillisecondsSinceEpoch(-1));
+      expect(s.followSystemDarkMode, true);
+      expect(s.darkMode, true);
+      expect(s.accentColor.value, 0xFF009688);
+      expect(s.sysColor.value, 0xFF009688);
+      expect(s.diaColor.value, 0xFF4CAF50);
+      expect(s.pulColor.value, 0xFFF44336);
+      expect(s.allowManualTimeInput, true);
+      expect(s.dateFormatString, 'yy-MM-dd H:mm');
+    });
+
+    test('setting fields should notify listeners and change values', () async {
+      await clearDbDir();
+      var s = await Settings.create();
+
+      int i = 0;
+      s.addListener(() {
+        i++;
+        if (i >= 1) {
+          expect(s.graphStepSize, TimeStep.lifetime);
+        }
+        if (i >= 11) {
+          expect(s.dateFormatString, 'yy:dd @ H:mm.ss');
+        }
+      });
+
+      s.graphStepSize = TimeStep.lifetime;
+      s.graphStart = DateTime.fromMillisecondsSinceEpoch(10000);
+      s.graphEnd = DateTime.fromMillisecondsSinceEpoch(200000);
+      s.followSystemDarkMode = false;
+      s.darkMode = false;
+      s.accentColor = s.createMaterialColor(0xFF942DA4);
+      s.sysColor = s.createMaterialColor(0xFF942DA5);
+      s.diaColor = s.createMaterialColor(0xFF942DA6);
+      s.pulColor = s.createMaterialColor(0xFF942DA7);
+      s.allowManualTimeInput = false;
+      s.dateFormatString = 'yy:dd @ H:mm.ss';
+
+
+      expect(s.graphStart, DateTime.fromMillisecondsSinceEpoch(10000));
+      expect(s.graphEnd, DateTime.fromMillisecondsSinceEpoch(200000));
+      expect(s.followSystemDarkMode, false);
+      expect(s.darkMode, false);
+      expect(s.accentColor.value, 0xFF942DA4);
+      expect(s.sysColor.value, 0xFF942DA5);
+      expect(s.diaColor.value, 0xFF942DA6);
+      expect(s.pulColor.value, 0xFF942DA7);
+      expect(s.allowManualTimeInput, false);
+
+    });
+    test('setting fields should notify listeners and change values', () async {
+      await clearDbDir();
+      var s = await Settings.create();
+
+      int i = 0;
+      s.addListener(() {
+        i++;
+      });
+
+      s.graphStepSize = TimeStep.lifetime;
+      s.graphStart = DateTime.fromMillisecondsSinceEpoch(10000);
+      s.graphEnd = DateTime.fromMillisecondsSinceEpoch(200000);
+      s.followSystemDarkMode = false;
+      s.darkMode = false;
+      s.accentColor = s.createMaterialColor(0xFF942DA4);
+      s.sysColor = s.createMaterialColor(0xFF942DA5);
+      s.diaColor = s.createMaterialColor(0xFF942DA6);
+      s.pulColor = s.createMaterialColor(0xFF942DA7);
+      s.allowManualTimeInput = false;
+      s.dateFormatString = 'yy:dd @ H:mm.ss';
+
+
+      expect(i, 11);
+    });
+
+  });
+}
+
+Future<void> clearDbDir() async {
+  databaseFactory.setDatabasesPath((await getDatabasesPath()).replaceAll('databases', 'test_databases'));
+  try {
+    Directory(await getDatabasesPath()).deleteSync(recursive: true);
+  } catch (e) {
+    print('no directory to delete!');
+  }
+  Directory(await getDatabasesPath()).create(recursive: true);
+}
\ No newline at end of file
test/model_test.dart
@@ -1,5 +0,0 @@
-import 'package:flutter_test/flutter_test.dart';
-import 'package:blood_pressure_app/model/blood_pressure.dart';
-
-void main() {
-}
\ No newline at end of file
test/widget_test.dart
@@ -1,16 +0,0 @@
-// This is a basic Flutter widget test.
-//
-// To perform an interaction with a widget in your test, use the WidgetTester
-// utility in the flutter_test package. For example, you can send tap and scroll
-// gestures. You can also use WidgetTester to find child widgets in the widget
-// tree, read text, and verify that the values of widget properties are correct.
-
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:blood_pressure_app/main.dart';
-
-void main() {
-  testWidgets('TODO', (WidgetTester tester) async {
-  });
-}
pubspec.lock
@@ -90,7 +90,7 @@ packages:
     source: hosted
     version: "2.0.1"
   file:
-    dependency: transitive
+    dependency: "direct dev"
     description:
       name: file
       sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
pubspec.yaml
@@ -48,6 +48,7 @@ dev_dependencies:
   flutter_test:
     sdk: flutter
   sqflite_common_ffi:
+  file:
 
   # The "flutter_lints" package below contains a set of recommended lints to
   # encourage good coding practices. The lint set provided by the package is