Commit 38db6e1
Changed files (4)
lib
model
blood_pressure
medicine
test
model
lib/model/blood_pressure/medicine/intake_history.dart
@@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
///
/// Internally maintains a sorted list of intakes to allow for binary search.
class IntakeHistory {
-
+ /// Create a intake history from an unsorted list of intakes.
IntakeHistory(List<MedicineIntake> medicineIntakes):
_medicineIntakes = medicineIntakes.sorted((p0, p1) => p0.compareTo(p1));
lib/model/blood_pressure/medicine/medicine.dart
@@ -1,3 +1,4 @@
+import 'dart:convert';
import 'dart:ui';
import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine_intake.dart';
@@ -6,12 +7,33 @@ import 'package:flutter/material.dart';
/// Description of a specific medicine.
class Medicine {
/// Create a new medicine.
- const Medicine({
+ const Medicine(this.id, {
required this.designation,
required this.color,
required this.defaultDosis
});
+ Map<String, dynamic> toMap() => {
+ 'id': id,
+ 'designation': designation,
+ 'color': color.value,
+ 'defaultDosis': defaultDosis
+ };
+
+ String toJson() => jsonEncode(toMap());
+
+ factory Medicine.fromMap(Map<String, dynamic> map) => Medicine(
+ map['id'],
+ designation: map['designation'],
+ color: Color(map['color']),
+ defaultDosis: map['defaultDosis']
+ );
+
+ factory Medicine.fromJson(String json) => Medicine.fromMap(jsonDecode(json));
+
+ /// Unique id used to store the medicine in serialized objects.
+ final int id;
+
/// Name of the medicine.
final String designation;
@@ -21,15 +43,22 @@ class Medicine {
/// Default dosis used to autofill [MedicineIntake].
final double? defaultDosis;
+
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Medicine &&
runtimeType == other.runtimeType &&
+ id == other.id &&
designation == other.designation &&
- color == other.color &&
+ color.value == other.color.value &&
defaultDosis == other.defaultDosis;
@override
- int get hashCode => designation.hashCode ^ color.hashCode ^ defaultDosis.hashCode;
+ int get hashCode => id.hashCode ^ designation.hashCode ^ color.hashCode ^ defaultDosis.hashCode;
+
+ @override
+ String toString() {
+ return 'Medicine{id: $id, designation: $designation, color: $color, defaultDosis: $defaultDosis}';
+ }
}
test/model/medicine/intake_history_test.dart
@@ -4,6 +4,8 @@ import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine_intake
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
+import 'medicine_test.dart';
+
void main() {
group('IntakeHistory', () {
test('should return all matching intakes in range', () {
@@ -157,17 +159,11 @@ void main() {
/// [timeMs] creates the intake timestamp through [DateTime.fromMillisecondsSinceEpoch].
/// When is null [DateTime.now] is used.
MedicineIntake mockIntake({
- Color medicineColor = Colors.black,
- String medicineDesignation = '',
- double? medicineDefaultDosis,
double dosis = 0,
- int? timeMs
+ int? timeMs,
+ Medicine? medicine
}) => MedicineIntake(
- medicine: Medicine(
- color: medicineColor,
- designation: medicineDesignation,
- defaultDosis: medicineDefaultDosis
- ),
+ medicine: medicine ?? mockMedicine(),
dosis: dosis,
timestamp: timeMs == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(timeMs)
);
\ No newline at end of file
test/model/medicine/medicine_test.dart
@@ -0,0 +1,64 @@
+import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ group('Medicine', () {
+ test('should determine equality', () {
+ const med1 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 10);
+ const med2 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 10);
+ expect(med1, med2);
+ });
+ test('should determine inequality', () {
+ const med1 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 10);
+ const med2 = Medicine(1, designation: 'designatio', color: Colors.red, defaultDosis: 10);
+ expect(med1, isNot(med2));
+ const med3 = Medicine(1, designation: 'designation', color: Colors.blue, defaultDosis: 10);
+ expect(med1, isNot(med3));
+ const med4 = Medicine(1, designation: 'designation', color: Colors.red, defaultDosis: 11);
+ expect(med1, isNot(med4));
+
+ });
+ test('should restore after encoded to map', () {
+ final med1 = mockMedicine();
+ final med1Restored = Medicine.fromMap(med1.toMap());
+ expect(med1Restored, med1);
+
+ final med2 = mockMedicine(color: Colors.red, designation: 'designation', defaultDosis: 15);
+ final med2Restored = Medicine.fromMap(med2.toMap());
+ expect(med2Restored, med2);
+ });
+ test('should restore after encoded to json', () {
+ final med1 = mockMedicine();
+ final med1Restored = Medicine.fromJson(med1.toJson());
+ expect(med1Restored, med1);
+
+ final med2 = mockMedicine(color: Colors.red, designation: 'designation', defaultDosis: 15);
+ final med2Restored = Medicine.fromJson(med2.toJson());
+ expect(med2Restored, med2);
+ });
+ test('should generate the same json after restoration', () {
+ final med1 = mockMedicine();
+ final med1Restored = Medicine.fromJson(med1.toJson());
+ expect(med1Restored.toJson(), med1.toJson());
+
+ final med2 = mockMedicine(color: Colors.red, designation: 'designation', defaultDosis: 15);
+ final med2Restored = Medicine.fromJson(med2.toJson());
+ expect(med2Restored.toJson(), med2.toJson());
+ }); // not in a json serialization test as this is not a setting like file.
+ });
+}
+
+
+final List<Medicine> _meds = [];
+
+Medicine mockMedicine({
+ Color color = Colors.black,
+ String designation = '',
+ double? defaultDosis,
+}) {
+ final matchingMeds = _meds.where((med) => med.defaultDosis == defaultDosis && med.color == color && med.designation == designation);
+ if (matchingMeds.isNotEmpty) return matchingMeds.first;
+ final med = Medicine(_meds.length, designation: designation, color: color, defaultDosis: defaultDosis);
+ return med;
+}
\ No newline at end of file