|
...
|
...
|
@@ -240,6 +240,106 @@ void main() { |
|
|
|
expect(daily.single.uploaded, isFalse);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('daily stress marks uploaded after upload and resets today on calculate',
|
|
|
|
() async {
|
|
|
|
final now = DateTime.now();
|
|
|
|
final todayDate = now.year * 10000 + now.month * 100 + now.day;
|
|
|
|
final nowSeconds = now.millisecondsSinceEpoch ~/ 1000;
|
|
|
|
final store = _MemoryHealthRawStressLocalStore();
|
|
|
|
store.insertDailyStress(
|
|
|
|
HealthRawDailyStressPoint(
|
|
|
|
userId: 42,
|
|
|
|
date: todayDate,
|
|
|
|
stressValue: 40,
|
|
|
|
stressScore: 40,
|
|
|
|
state: HealthRawStressState.normal,
|
|
|
|
dataTime: nowSeconds,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
await store.markDailyStressUploadedUntil(userId: 42, date: todayDate);
|
|
|
|
var daily = await store.queryDailyStressPoints(
|
|
|
|
userId: 42,
|
|
|
|
startDate: todayDate,
|
|
|
|
endDate: todayDate,
|
|
|
|
);
|
|
|
|
expect(daily.single.uploaded, isTrue);
|
|
|
|
|
|
|
|
await store.upsertDailyStressPoints(
|
|
|
|
userId: 42,
|
|
|
|
points: [
|
|
|
|
HealthRawDailyStressPoint(
|
|
|
|
userId: 42,
|
|
|
|
date: todayDate,
|
|
|
|
stressValue: 40,
|
|
|
|
stressScore: 40,
|
|
|
|
state: HealthRawStressState.normal,
|
|
|
|
dataTime: nowSeconds + 1,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
daily = await store.queryDailyStressPoints(
|
|
|
|
userId: 42,
|
|
|
|
startDate: todayDate,
|
|
|
|
endDate: todayDate,
|
|
|
|
);
|
|
|
|
expect(daily.single.uploaded, isFalse);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('daily stress recalculates today before native upload', () async {
|
|
|
|
final operationLog = <String>[];
|
|
|
|
final now = DateTime.now();
|
|
|
|
final todayStart = DateTime(now.year, now.month, now.day);
|
|
|
|
final todayDate =
|
|
|
|
todayStart.year * 10000 + todayStart.month * 100 + todayStart.day;
|
|
|
|
final startSeconds = todayStart.millisecondsSinceEpoch ~/ 1000;
|
|
|
|
final api = _FakeHealthKitRawDataHostApi(operationLog: operationLog)
|
|
|
|
..dailyStressUploadUntil = todayDate;
|
|
|
|
final store = _MemoryHealthRawStressLocalStore(operationLog: operationLog);
|
|
|
|
await store.upsertResult(
|
|
|
|
HealthRawStressCalculationResult(
|
|
|
|
userId: 42,
|
|
|
|
hrvStressPoints: const <HealthRawHrvStressPoint>[],
|
|
|
|
realtimeStressPoints: [
|
|
|
|
_realtimeStressPoint(startSeconds + 60),
|
|
|
|
_realtimeStressPoint(startSeconds + 120),
|
|
|
|
_realtimeStressPoint(startSeconds + 180),
|
|
|
|
],
|
|
|
|
dailyStressPoints: const <HealthRawDailyStressPoint>[],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
store.insertDailyStress(
|
|
|
|
HealthRawDailyStressPoint(
|
|
|
|
userId: 42,
|
|
|
|
date: todayDate,
|
|
|
|
stressValue: 30,
|
|
|
|
stressScore: 30,
|
|
|
|
state: HealthRawStressState.normal,
|
|
|
|
dataTime: startSeconds,
|
|
|
|
uploaded: true,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
final service = HealthRawDataCoreService(
|
|
|
|
healthApi: _FakeHealthKitHostApi(),
|
|
|
|
rawDataApi: api,
|
|
|
|
localStore: store,
|
|
|
|
userIdProvider: () => 42,
|
|
|
|
);
|
|
|
|
|
|
|
|
await service.startCoreCaculate(
|
|
|
|
endTime: startSeconds + 240,
|
|
|
|
readChunkDays: 1,
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(
|
|
|
|
operationLog.indexOf('upsertDailyStressPoints'),
|
|
|
|
lessThan(operationLog.indexOf('performAvgRealtimeStressDataUpload')),
|
|
|
|
);
|
|
|
|
expect(api.dailyStressUploadCallCount, 1);
|
|
|
|
final rows = await store.debugQueryAllRows(42);
|
|
|
|
expect(rows.dailyStressRows.single['uploaded'], 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('same calculated rows keep uploaded state', () async {
|
|
|
|
final store = _MemoryHealthRawStressLocalStore();
|
|
|
|
const uploadedDaily = HealthRawDailyStressPoint(
|
|
...
|
...
|
@@ -414,6 +514,77 @@ void main() { |
|
|
|
expect(rows.sleepRows.single['uploaded'], 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
test(
|
|
|
|
'startCoreCaculate uploads pending rows even when no new data calculates',
|
|
|
|
() async {
|
|
|
|
const base = 1800000000;
|
|
|
|
final oldDate = DateTime.fromMillisecondsSinceEpoch(base * 1000);
|
|
|
|
final oldDateKey = oldDate.year * 10000 + oldDate.month * 100 + oldDate.day;
|
|
|
|
final api = _FakeHealthKitRawDataHostApi()
|
|
|
|
..hrvUploadUntil = base + 10
|
|
|
|
..hrUploadUntil = base + 20
|
|
|
|
..dailyStressUploadUntil = oldDateKey
|
|
|
|
..sleepUploadUntil = base + 30;
|
|
|
|
final store = _MemoryHealthRawStressLocalStore();
|
|
|
|
await store.upsertResult(
|
|
|
|
HealthRawStressCalculationResult(
|
|
|
|
userId: 42,
|
|
|
|
hrvStressPoints: [_hrvStressPoint(base + 10)],
|
|
|
|
realtimeStressPoints: [_realtimeStressPoint(base + 20)],
|
|
|
|
dailyStressPoints: const <HealthRawDailyStressPoint>[],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
store.insertDailyStress(
|
|
|
|
HealthRawDailyStressPoint(
|
|
|
|
userId: 42,
|
|
|
|
date: oldDateKey,
|
|
|
|
stressValue: 40,
|
|
|
|
stressScore: 40,
|
|
|
|
state: HealthRawStressState.normal,
|
|
|
|
dataTime: base + 20,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
await store.upsertSleepResults(
|
|
|
|
userId: 42,
|
|
|
|
results: const [
|
|
|
|
HealthRawSleepResult(
|
|
|
|
userId: 42,
|
|
|
|
date: base + 30,
|
|
|
|
startDate: base,
|
|
|
|
sleepScore: 80,
|
|
|
|
sleepState: 2,
|
|
|
|
inBedMinutes: 500,
|
|
|
|
awakMinutes: 20,
|
|
|
|
sleepMinutes: 480,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
final service = HealthRawDataCoreService(
|
|
|
|
healthApi: _FakeHealthKitHostApi(),
|
|
|
|
rawDataApi: api,
|
|
|
|
localStore: store,
|
|
|
|
userIdProvider: () => 42,
|
|
|
|
);
|
|
|
|
|
|
|
|
final result = await service.startCoreCaculate(
|
|
|
|
endTime: base + 40,
|
|
|
|
readChunkDays: 1,
|
|
|
|
);
|
|
|
|
final rows = await store.debugQueryAllRows(42);
|
|
|
|
|
|
|
|
expect(result.hrvStressPoints, isEmpty);
|
|
|
|
expect(result.realtimeStressPoints, isEmpty);
|
|
|
|
expect(result.sleepResults, isEmpty);
|
|
|
|
expect(api.hrvUploadCallCount, 1);
|
|
|
|
expect(api.hrUploadCallCount, 1);
|
|
|
|
expect(api.dailyStressUploadCallCount, 1);
|
|
|
|
expect(api.sleepUploadCallCount, 1);
|
|
|
|
expect(rows.hrvRows.single['uploaded'], 1);
|
|
|
|
expect(rows.realtimeRows.single['uploaded'], 1);
|
|
|
|
expect(rows.dailyStressRows.single['uploaded'], 1);
|
|
|
|
expect(rows.sleepRows.single['uploaded'], 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
test('local data source returns earliest local hr start time', () async {
|
|
|
|
const base = 1800000000;
|
|
|
|
final api = _FakeHealthKitRawDataHostApi();
|
|
...
|
...
|
@@ -472,14 +643,40 @@ HealthRawRealtimeStressPoint _realtimeStressPoint(int time) { |
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
HealthRawHrvStressPoint _hrvStressPoint(int time) {
|
|
|
|
return HealthRawHrvStressPoint(
|
|
|
|
userId: 42,
|
|
|
|
rawEndTime: time,
|
|
|
|
rawHrv: 30,
|
|
|
|
result: 30,
|
|
|
|
sourceStartTime: time,
|
|
|
|
sourceEndTime: time,
|
|
|
|
state: HealthRawStressState.normal,
|
|
|
|
baselineHrv: 30,
|
|
|
|
baselineAwakeHrv: 30,
|
|
|
|
baselineSleepHrv: null,
|
|
|
|
baselineRestingHr: 60,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
class _FakeHealthKitRawDataHostApi extends HealthKitRawDataHostApi {
|
|
|
|
_FakeHealthKitRawDataHostApi({List<String>? operationLog})
|
|
|
|
: operationLog = operationLog ?? <String>[];
|
|
|
|
|
|
|
|
final Map<int, List<HealthKitRawDataPoint>> _pointsByType = {};
|
|
|
|
final List<HealthKitRawWorkoutDataPoint> _workoutPoints = [];
|
|
|
|
final List<HealthKitRawDataPoint> _sleepPoints = [];
|
|
|
|
final List<_ReadCall> calls = [];
|
|
|
|
final List<String> operationLog;
|
|
|
|
var sleepCallCount = 0;
|
|
|
|
var workoutCallCount = 0;
|
|
|
|
var hrUploadCallCount = 0;
|
|
|
|
var hrvUploadCallCount = 0;
|
|
|
|
var dailyStressUploadCallCount = 0;
|
|
|
|
var sleepUploadCallCount = 0;
|
|
|
|
var hrUploadUntil = 0;
|
|
|
|
var hrvUploadUntil = 0;
|
|
|
|
var dailyStressUploadUntil = 0;
|
|
|
|
var sleepUploadUntil = 0;
|
|
|
|
var hasData = true;
|
|
|
|
|
|
...
|
...
|
@@ -519,16 +716,25 @@ class _FakeHealthKitRawDataHostApi extends HealthKitRawDataHostApi { |
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<int> performHRDataUpload({required String sqliteFilePath}) async => 0;
|
|
|
|
Future<int> performHRDataUpload({required String sqliteFilePath}) async {
|
|
|
|
hrUploadCallCount += 1;
|
|
|
|
return hrUploadUntil;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<int> performHRVDataUpload({required String sqliteFilePath}) async => 0;
|
|
|
|
Future<int> performHRVDataUpload({required String sqliteFilePath}) async {
|
|
|
|
hrvUploadCallCount += 1;
|
|
|
|
return hrvUploadUntil;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<int> performAvgRealtimeStressDataUpload({
|
|
|
|
required String sqliteFilePath,
|
|
|
|
}) async =>
|
|
|
|
0;
|
|
|
|
}) async {
|
|
|
|
operationLog.add('performAvgRealtimeStressDataUpload');
|
|
|
|
dailyStressUploadCallCount += 1;
|
|
|
|
return dailyStressUploadUntil;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<int> performSleepAnalysisDataUpload({
|
|
...
|
...
|
@@ -588,11 +794,14 @@ class _ReadCall { |
|
|
|
}
|
|
|
|
|
|
|
|
class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore {
|
|
|
|
_MemoryHealthRawStressLocalStore({List<String>? operationLog})
|
|
|
|
: operationLog = operationLog ?? <String>[];
|
|
|
|
|
|
|
|
final Map<int, List<HealthRawHrvStressPoint>> _hrv = {};
|
|
|
|
final Map<int, List<HealthRawRealtimeStressPoint>> _realtime = {};
|
|
|
|
final Map<int, List<HealthRawDailyStressPoint>> _daily = {};
|
|
|
|
final Map<int, List<HealthRawSleepResult>> _sleep = {};
|
|
|
|
final List<String> operationLog = [];
|
|
|
|
final List<String> operationLog;
|
|
|
|
|
|
|
|
void insertDailyStress(HealthRawDailyStressPoint point) {
|
|
|
|
final byDate = <int, HealthRawDailyStressPoint>{
|
|
...
|
...
|
@@ -683,6 +892,7 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { |
|
|
|
existing.stressValue == point.stressValue &&
|
|
|
|
existing.stressScore == point.stressScore &&
|
|
|
|
existing.state == point.state;
|
|
|
|
final isToday = point.date == _todayDateKey();
|
|
|
|
byDate[point.date] = HealthRawDailyStressPoint(
|
|
|
|
userId: point.userId,
|
|
|
|
date: point.date,
|
|
...
|
...
|
@@ -690,7 +900,7 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { |
|
|
|
stressScore: point.stressScore,
|
|
|
|
state: point.state,
|
|
|
|
dataTime: point.dataTime,
|
|
|
|
uploaded: same ? existing.uploaded : false,
|
|
|
|
uploaded: isToday ? false : (same ? existing.uploaded : false),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
_daily[userId] = byDate.values.toList()
|
|
...
|
...
|
@@ -737,6 +947,14 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { |
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> clearTables(int userId) async {
|
|
|
|
_hrv.remove(userId);
|
|
|
|
_realtime.remove(userId);
|
|
|
|
_daily.remove(userId);
|
|
|
|
_sleep.remove(userId);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<List<HealthRawHrvStressPoint>> queryHrvStressPoints({
|
|
|
|
required int userId,
|
|
|
|
required int startTime,
|
|
...
|
...
|
@@ -1068,4 +1286,9 @@ class _MemoryHealthRawStressLocalStore extends HealthRawStressLocalStore { |
|
|
|
'uploaded': result.uploaded ? 1 : 0,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
int _todayDateKey() {
|
|
|
|
final now = DateTime.now();
|
|
|
|
return now.year * 10000 + now.month * 100 + now.day;
|
|
|
|
}
|
|
|
|
} |
...
|
...
|
|