mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 19:07:16 +01:00
Fix red CI: coverage 7.16.2, split workout_screen.dart and modes test
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (flutter, flutter-analyze, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint,flutter-analyze) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (0) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (1) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (2) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (3) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (4) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (5) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (6) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (7) (push) Waiting to run
Tests / flutter-coverage (push) Blocked by required conditions
Tests / web-coverage (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (flutter, flutter-analyze, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint,flutter-analyze) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (0) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (1) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (2) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (3) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (4) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (5) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (6) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (7) (push) Waiting to run
Tests / flutter-coverage (push) Blocked by required conditions
Tests / web-coverage (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
- coverage 7.16.1 -> 7.16.2 (dependency freshness). - workout_screen.dart (253) -> _allSetsCompleted/_isPaused move into workout_screen_breaks.dart next to _stateOf. - storage_service_modes_test.dart (289) -> setExerciseSettings and per-mode progression groups move to storage_service_settings_test.dart. Claude-Session: https://claude.ai/code/session_01KeVXsWTuwjmZK5uvbw6vqF Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d35edfc10c
commit
cf2d917b33
@ -2,7 +2,7 @@
|
||||
# Runtime: tkinter/subprocess/socket/sqlite3 (stdlib) plus gatelock (below).
|
||||
bandit==1.9.4
|
||||
codespell==2.4.3
|
||||
coverage==7.16.1
|
||||
coverage==7.16.2
|
||||
crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.1#subdirectory=crdt-sync
|
||||
freedays @ git+https://github.com/kuhyx/utils@freedays-v0.2.0#subdirectory=freedays
|
||||
gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.8.2#subdirectory=gatelock
|
||||
|
||||
@ -172,22 +172,6 @@ class _WorkoutScreenState extends State<WorkoutScreen> {
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// Every set recorded, not counting paused exercises: those have no sets
|
||||
/// to tap and are recorded as failed at Finish.
|
||||
bool get _allSetsCompleted {
|
||||
for (var i = 0; i < _tapped.length; i++) {
|
||||
if (!_isPaused(i) && !_tapped[i].every((t) => t)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Whether exercise [exIdx] is on an injury pause right now.
|
||||
bool _isPaused(int exIdx) =>
|
||||
_exerciseStates[widget.exercises[exIdx].name]?.isPausedAt(
|
||||
DateTime.now(),
|
||||
) ??
|
||||
false;
|
||||
|
||||
/// Runs [fn] inside `setState` on behalf of this library's extensions.
|
||||
///
|
||||
/// `setState` is `@protected`, so an extension cannot call it directly. This
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Rest-period timers and per-exercise settings edits.
|
||||
// Rest-period timers, per-exercise state lookups and settings edits.
|
||||
//
|
||||
// See workout_screen_session.dart for why this is a `part`. `setState` is
|
||||
// `@protected` and unreachable from an extension, so these mutate through the
|
||||
@ -12,6 +12,22 @@ const _expiredBreakGraceSecs = 120;
|
||||
|
||||
/// Starting, ticking, cancelling and finishing the rest period between sets.
|
||||
extension _WorkoutScreenBreaks on _WorkoutScreenState {
|
||||
/// Every set recorded, not counting paused exercises: those have no sets
|
||||
/// to tap and are recorded as failed at Finish.
|
||||
bool get _allSetsCompleted {
|
||||
for (var i = 0; i < _tapped.length; i++) {
|
||||
if (!_isPaused(i) && !_tapped[i].every((t) => t)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Whether exercise [exIdx] is on an injury pause right now.
|
||||
bool _isPaused(int exIdx) =>
|
||||
_exerciseStates[widget.exercises[exIdx].name]?.isPausedAt(
|
||||
DateTime.now(),
|
||||
) ??
|
||||
false;
|
||||
|
||||
/// Exercise [exIdx]'s stored state, or its defaults while the states are
|
||||
/// still loading: that is where its rest lengths live.
|
||||
ExerciseState _stateOf(int exIdx) {
|
||||
|
||||
@ -11,8 +11,6 @@ import 'package:workout_app/services/storage_service.dart';
|
||||
|
||||
StorageService get _svc => StorageService.instance;
|
||||
|
||||
final _yesterday = DateTime.now().subtract(const Duration(days: 1));
|
||||
|
||||
/// Builds a database exactly as a v3 install left it: the v3 schema, a
|
||||
/// finished workout from today, and a workout in progress.
|
||||
Future<void> _writeV3Database(String path) async {
|
||||
@ -122,12 +120,10 @@ void main() {
|
||||
test('keeps progression and gives every row weight mode', () async {
|
||||
for (final ex in [...workoutA, ...workoutB]) {
|
||||
final s = (await _svc.getExerciseState(ex.name))!;
|
||||
expect((s.weight, s.reps, s.successStreak, s.failStreak), (
|
||||
17.5,
|
||||
9,
|
||||
2,
|
||||
1,
|
||||
));
|
||||
expect(
|
||||
(s.weight, s.reps, s.successStreak, s.failStreak),
|
||||
(17.5, 9, 2, 1),
|
||||
);
|
||||
expect((s.successThreshold, s.failThreshold), (5, 4));
|
||||
expect(s.mode, ProgressionMode.weight);
|
||||
expect((s.repsHigh, s.repsLow), (kDefaultRepsHigh, kDefaultRepsLow));
|
||||
@ -180,110 +176,4 @@ void main() {
|
||||
expect(situp.pausedUntil, isNull);
|
||||
expect(await _svc.getLastWorkoutDate(), DateTime(2026, 9, 27));
|
||||
});
|
||||
|
||||
group('setExerciseSettings', () {
|
||||
test('persists mode, rep range, warmup and thresholds', () async {
|
||||
final name = workoutA.first.name;
|
||||
final s = (await _svc.getExerciseState(name))!;
|
||||
await _svc.setExerciseSettings(
|
||||
s.copyWith(
|
||||
mode: ProgressionMode.doubleProgression,
|
||||
repsHigh: 15,
|
||||
repsLow: 8,
|
||||
hasWarmup: false,
|
||||
successThreshold: 1,
|
||||
failThreshold: 4,
|
||||
),
|
||||
);
|
||||
final after = (await _svc.getExerciseState(name))!;
|
||||
expect(after.mode, ProgressionMode.doubleProgression);
|
||||
expect((after.repsHigh, after.repsLow), (15, 8));
|
||||
expect(after.hasWarmup, isFalse);
|
||||
expect((after.successThreshold, after.failThreshold), (1, 4));
|
||||
// The target itself is untouched until the next finished workout.
|
||||
expect((after.weight, after.reps), (s.weight, s.reps));
|
||||
});
|
||||
|
||||
test('persists a pause and its resume', () async {
|
||||
final s = (await _svc.getExerciseState('Situp'))!;
|
||||
await _svc.setExerciseSettings(
|
||||
s.copyWith(pausedUntil: DateTime(2026, 10, 11)),
|
||||
);
|
||||
expect(
|
||||
(await _svc.getExerciseState('Situp'))!.pausedUntil,
|
||||
DateTime(2026, 10, 11),
|
||||
);
|
||||
await _svc.setExerciseSettings(s.copyWith(resume: true));
|
||||
expect((await _svc.getExerciseState('Situp'))!.pausedUntil, isNull);
|
||||
});
|
||||
|
||||
test('reaches the workout through getCurrentExercises', () async {
|
||||
final name = workoutA.first.name;
|
||||
final s = (await _svc.getExerciseState(name))!;
|
||||
await _svc.setExerciseSettings(s.copyWith(hasWarmup: false));
|
||||
final exercises = await _svc.getCurrentExercises('A');
|
||||
expect(exercises.first.hasWarmup, isFalse);
|
||||
expect(exercises[1].hasWarmup, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('applyProgression per mode', () {
|
||||
Future<ExerciseState> run(
|
||||
ProgressionMode mode, {
|
||||
required bool success,
|
||||
required int reps,
|
||||
DateTime? last,
|
||||
}) async {
|
||||
final name = workoutA.first.name;
|
||||
final s = (await _svc.getExerciseState(name))!;
|
||||
await _svc.setExerciseSettings(
|
||||
s.copyWith(mode: mode, successThreshold: 1, failThreshold: 1),
|
||||
);
|
||||
await _svc.setExerciseReps(name, reps);
|
||||
await _svc.setExerciseWeight(name, 10);
|
||||
await _svc.applyProgression(
|
||||
succeededExercises: {name: success},
|
||||
lastWorkoutDate: last ?? _yesterday,
|
||||
);
|
||||
return (await _svc.getExerciseState(name))!;
|
||||
}
|
||||
|
||||
test('reps: success adds a rep', () async {
|
||||
final s = await run(ProgressionMode.reps, success: true, reps: 10);
|
||||
expect((s.weight, s.reps, s.successStreak), (10, 11, 0));
|
||||
});
|
||||
|
||||
test('reps: failure removes a rep', () async {
|
||||
final s = await run(ProgressionMode.reps, success: false, reps: 10);
|
||||
expect((s.weight, s.reps, s.failStreak), (10, 9, 0));
|
||||
});
|
||||
|
||||
test('double: success at n moves up in weight and back to m', () async {
|
||||
final s = await run(
|
||||
ProgressionMode.doubleProgression,
|
||||
success: true,
|
||||
reps: 12,
|
||||
);
|
||||
expect((s.weight, s.reps), (12.5, 6));
|
||||
});
|
||||
|
||||
test('double: failure at m moves down in weight and up to n', () async {
|
||||
final s = await run(
|
||||
ProgressionMode.doubleProgression,
|
||||
success: false,
|
||||
reps: 6,
|
||||
);
|
||||
expect((s.weight, s.reps), (7.5, 12));
|
||||
});
|
||||
|
||||
test('a week off is one regression step in the active mode', () async {
|
||||
final s = await run(
|
||||
ProgressionMode.reps,
|
||||
success: true,
|
||||
reps: 10,
|
||||
last: DateTime.now().subtract(const Duration(days: 9)),
|
||||
);
|
||||
expect((s.weight, s.reps, s.successStreak, s.failStreak), (10, 9, 0, 0));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,128 @@
|
||||
// Per-exercise settings writes and progression per mode, through storage.
|
||||
// Split from storage_service_modes_test.dart for the 250-line cap.
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:workout_app/models/workout_plan.dart';
|
||||
import 'package:workout_app/services/storage_service.dart';
|
||||
|
||||
StorageService get _svc => StorageService.instance;
|
||||
|
||||
final _yesterday = DateTime.now().subtract(const Duration(days: 1));
|
||||
|
||||
void main() {
|
||||
setUpAll(() {
|
||||
sqfliteFfiInit();
|
||||
databaseFactory = databaseFactoryFfi;
|
||||
});
|
||||
|
||||
setUp(() async {
|
||||
StorageService.resetForTesting();
|
||||
await StorageService.init();
|
||||
});
|
||||
|
||||
group('setExerciseSettings', () {
|
||||
test('persists mode, rep range, warmup and thresholds', () async {
|
||||
final name = workoutA.first.name;
|
||||
final s = (await _svc.getExerciseState(name))!;
|
||||
await _svc.setExerciseSettings(
|
||||
s.copyWith(
|
||||
mode: ProgressionMode.doubleProgression,
|
||||
repsHigh: 15,
|
||||
repsLow: 8,
|
||||
hasWarmup: false,
|
||||
successThreshold: 1,
|
||||
failThreshold: 4,
|
||||
),
|
||||
);
|
||||
final after = (await _svc.getExerciseState(name))!;
|
||||
expect(after.mode, ProgressionMode.doubleProgression);
|
||||
expect((after.repsHigh, after.repsLow), (15, 8));
|
||||
expect(after.hasWarmup, isFalse);
|
||||
expect((after.successThreshold, after.failThreshold), (1, 4));
|
||||
// The target itself is untouched until the next finished workout.
|
||||
expect((after.weight, after.reps), (s.weight, s.reps));
|
||||
});
|
||||
|
||||
test('persists a pause and its resume', () async {
|
||||
final s = (await _svc.getExerciseState('Situp'))!;
|
||||
await _svc.setExerciseSettings(
|
||||
s.copyWith(pausedUntil: DateTime(2026, 10, 11)),
|
||||
);
|
||||
expect(
|
||||
(await _svc.getExerciseState('Situp'))!.pausedUntil,
|
||||
DateTime(2026, 10, 11),
|
||||
);
|
||||
await _svc.setExerciseSettings(s.copyWith(resume: true));
|
||||
expect((await _svc.getExerciseState('Situp'))!.pausedUntil, isNull);
|
||||
});
|
||||
|
||||
test('reaches the workout through getCurrentExercises', () async {
|
||||
final name = workoutA.first.name;
|
||||
final s = (await _svc.getExerciseState(name))!;
|
||||
await _svc.setExerciseSettings(s.copyWith(hasWarmup: false));
|
||||
final exercises = await _svc.getCurrentExercises('A');
|
||||
expect(exercises.first.hasWarmup, isFalse);
|
||||
expect(exercises[1].hasWarmup, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('applyProgression per mode', () {
|
||||
Future<ExerciseState> run(
|
||||
ProgressionMode mode, {
|
||||
required bool success,
|
||||
required int reps,
|
||||
DateTime? last,
|
||||
}) async {
|
||||
final name = workoutA.first.name;
|
||||
final s = (await _svc.getExerciseState(name))!;
|
||||
await _svc.setExerciseSettings(
|
||||
s.copyWith(mode: mode, successThreshold: 1, failThreshold: 1),
|
||||
);
|
||||
await _svc.setExerciseReps(name, reps);
|
||||
await _svc.setExerciseWeight(name, 10);
|
||||
await _svc.applyProgression(
|
||||
succeededExercises: {name: success},
|
||||
lastWorkoutDate: last ?? _yesterday,
|
||||
);
|
||||
return (await _svc.getExerciseState(name))!;
|
||||
}
|
||||
|
||||
test('reps: success adds a rep', () async {
|
||||
final s = await run(ProgressionMode.reps, success: true, reps: 10);
|
||||
expect((s.weight, s.reps, s.successStreak), (10, 11, 0));
|
||||
});
|
||||
|
||||
test('reps: failure removes a rep', () async {
|
||||
final s = await run(ProgressionMode.reps, success: false, reps: 10);
|
||||
expect((s.weight, s.reps, s.failStreak), (10, 9, 0));
|
||||
});
|
||||
|
||||
test('double: success at n moves up in weight and back to m', () async {
|
||||
final s = await run(
|
||||
ProgressionMode.doubleProgression,
|
||||
success: true,
|
||||
reps: 12,
|
||||
);
|
||||
expect((s.weight, s.reps), (12.5, 6));
|
||||
});
|
||||
|
||||
test('double: failure at m moves down in weight and up to n', () async {
|
||||
final s = await run(
|
||||
ProgressionMode.doubleProgression,
|
||||
success: false,
|
||||
reps: 6,
|
||||
);
|
||||
expect((s.weight, s.reps), (7.5, 12));
|
||||
});
|
||||
|
||||
test('a week off is one regression step in the active mode', () async {
|
||||
final s = await run(
|
||||
ProgressionMode.reps,
|
||||
success: true,
|
||||
reps: 10,
|
||||
last: DateTime.now().subtract(const Duration(days: 9)),
|
||||
);
|
||||
expect((s.weight, s.reps, s.successStreak, s.failStreak), (10, 9, 0, 0));
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user