mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 21:07:14 +01:00
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Tests / test (push) Has been cancelled
Tests / lock-popup-safety (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
The between-set break never finished once the phone's screen was off. Three independent causes (worked through on-device on 2026-09-12; left uncommitted awaiting the go-ahead, committed here as-is plus one pin bump): 1. The countdown was not wall-clock: `_breakRemaining--` on a Timer.periodic, which Android throttles and Doze suspends in a backgrounded isolate, so the counter stalled and _onBreakFinished() never fired. Now a BreakClock deadline on the wall clock. 2. An expired break was silently dropped on restore (`if (remaining > 0)` only) — now cued and logged. 3. No notification infrastructure at all: no package, no POST_NOTIFICATIONS / FOREGROUND_SERVICE / WAKE_LOCK. Now: flutter_foreground_task runs the countdown in a service isolate that survives BACK and the screen lock; flutter_local_notifications shows an ongoing "Workout A · break m:ss / Next: …" notification with −1 rep / Done actions and a "Break over" alert on its own channel (raw break_end sound, vibration). Button presses go through a persisted BreakIntent queue the screen drains on resume, so a press with the app killed is not lost. Finish and Reset stop the service; a startup reaper handles the crash case. Verified on the phone with the screen off and locked: service alive and ticking while Dozing, both notifications on the lock screen, channel importance 5 with the break_end sound. Not verifiable from adb (receiver is RECEIVER_NOT_EXPORTED): an actual action-button tap, and a true recents swipe — see stronglift_replacement/TODO-workout-app-v2.md. Also fixed en route: the break-end sound had never played in any widget test (AssetSource needs path_provider, which nothing faked, and the failure was swallowed by catchError); the queue's prune wrote back a stale list. This commit additionally: - takes the freshness gate's bump flutter_local_notifications 22.3.1; - restores web/package.json to HEAD (the tree held a stale pre-bump copy); - gives scripts/check_flutter_coverage.sh a FLUTTER_TEST_CONCURRENCY knob: the default one-isolate-per-core run peaks over 4 GiB and is OOM-killed under the resource cap. Default behaviour unchanged. Gate: flutter analyze clean, 511 tests, 3187/3187 lines (100%), pre-commit green. Not re-installed on the phone for the 22.3.1 pin: no device attached at commit time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C7vgacLhb4WJjxuVj2QLEP
100 lines
3.9 KiB
Bash
Executable File
100 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Enforces 100% line coverage for the Flutter workout app.
|
|
#
|
|
# Run from the repo root. Fails if:
|
|
# 1. Any lib/**/*.dart file is missing from coverage/lcov.info (unreachable
|
|
# file — the 100% would be fake without this check).
|
|
# 2. Line coverage across all lib/ files is below 100%.
|
|
#
|
|
# Usage: scripts/check_flutter_coverage.sh
|
|
set -euo pipefail
|
|
|
|
APP_DIR="stronglift_replacement/workout_app"
|
|
LCOV_INFO="$APP_DIR/coverage/lcov.info"
|
|
|
|
cd "$APP_DIR"
|
|
|
|
echo "Running flutter test --coverage ..."
|
|
# env -u GIT_DIR: git exports it to hooks, and the flutter tool shells out to
|
|
# git to identify its own SDK. With it set, flutter reads THIS repository as
|
|
# the SDK -- reporting our HEAD as the framework revision -- and pub then
|
|
# resolves every version constraint against "0.0.0-unknown" and fails.
|
|
# FLUTTER_TEST_CONCURRENCY: one test isolate per core is the default and peaks
|
|
# well over 4 GiB on this suite, which the 4 GiB resource cap (capped.sh)
|
|
# OOM-kills. Set it to 1-2 when running under the cap; unset keeps the default.
|
|
concurrency=()
|
|
if [[ -n "${FLUTTER_TEST_CONCURRENCY:-}" ]]; then
|
|
concurrency=(--concurrency "$FLUTTER_TEST_CONCURRENCY")
|
|
fi
|
|
env -u GIT_DIR -u GIT_WORK_TREE -u GIT_INDEX_FILE \
|
|
flutter test --coverage "${concurrency[@]}"
|
|
|
|
cd - > /dev/null
|
|
|
|
if [[ ! -f "$LCOV_INFO" ]]; then
|
|
echo "ERROR: $LCOV_INFO not found — did flutter test run?" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ── Guard: every lib/**/*.dart must appear in the coverage report ──────────────
|
|
# Flutter only reports files that are transitively imported by a test.
|
|
# Unreferenced files silently score 100% by absence — catch that here.
|
|
#
|
|
# Exception: a file whose only content is a conditional `export` (a
|
|
# platform-gate barrel like google_platform.dart, re-exporting google_platform_io.dart
|
|
# or google_platform_web.dart per dart.library.js_interop) has zero executable
|
|
# statements. Dart's coverage instrumentation never emits an SF: entry for such
|
|
# a file regardless of whether it was imported -- there is no "silently 100% by
|
|
# absence" for it to hide, since there is nothing to cover. Importing it (even
|
|
# transitively) is what makes its *resolved* sibling appear in lcov instead.
|
|
barrel_pattern="^(library;)?[[:space:]]*export[[:space:]]+'[^']+'[[:space:]]+if[[:space:]]*\([^)]+\)[[:space:]]+'[^']+';[[:space:]]*\$"
|
|
|
|
missing=0
|
|
while IFS= read -r dart_file; do
|
|
rel="${dart_file#"$APP_DIR"/}"
|
|
# Strip //-comments, ///-doc-comments and blank lines, then collapse
|
|
# whitespace, so a wrapped `export 'x' if (...) 'y';` statement (the
|
|
# platform-gate barrel shape) becomes matchable on one logical line
|
|
# regardless of how it's line-wrapped in source.
|
|
code_only=$(grep -vE '^[[:space:]]*(///|//|$)' "$dart_file" | tr '\n' ' ' | tr -s ' ')
|
|
if [[ $code_only =~ $barrel_pattern ]]; then
|
|
continue
|
|
fi
|
|
if ! grep -qF "SF:$rel" "$LCOV_INFO" && \
|
|
! grep -qF "SF:lib/${rel#lib/}" "$LCOV_INFO"; then
|
|
echo "MISSING FROM COVERAGE: $dart_file" >&2
|
|
missing=1
|
|
fi
|
|
done < <(find "$APP_DIR/lib" -name "*.dart" ! -path "*/generated/*")
|
|
|
|
if [[ $missing -eq 1 ]]; then
|
|
echo ""
|
|
echo "ERROR: Some lib/ files are not covered. Import them in a test." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ── Compute line coverage percentage from lcov.info ───────────────────────────
|
|
total_found=0
|
|
total_hit=0
|
|
while IFS= read -r line; do
|
|
if [[ $line == LF:* ]]; then
|
|
total_found=$(( total_found + ${line#LF:} ))
|
|
elif [[ $line == LH:* ]]; then
|
|
total_hit=$(( total_hit + ${line#LH:} ))
|
|
fi
|
|
done < "$LCOV_INFO"
|
|
|
|
if [[ $total_found -eq 0 ]]; then
|
|
echo "ERROR: No line coverage data found in $LCOV_INFO" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $total_hit -lt $total_found ]]; then
|
|
missed=$(( total_found - total_hit ))
|
|
echo "ERROR: Line coverage is not 100% — $missed/$total_found lines not covered." >&2
|
|
echo "Run: flutter test --coverage && genhtml coverage/lcov.info -o coverage/html" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Coverage OK: $total_hit/$total_found lines covered (100%)."
|