From c4cc39759ad18fd46151d4e5b70779472c9bc7ef Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sat, 22 Aug 2026 13:06:49 +0200 Subject: [PATCH] Cover the lock-mode flag, and mark the one branch Linux cannot reach The Flutter coverage gate blocked the push on 8 lines, all from the desktop target's commit. Seven of them are one branch: didChangeAppLifecycleState returns on its first line under Platform.isLinux, and the suite runs on Linux, so the switch below it is unreachable from any test this repo can run. Covering it would mean threading a platform seam through the nine Platform.isLinux checks in this app for test reachability alone, so it is marked instead -- the same treatment, and the same shape, as storage_service_schema's platform edge. The eighth was just untested: parseLockMode is a pure function, and it now has the cases that matter, including the flag arriving next to --demo-escape. main_lifecycle_test covers the observer's initState/dispose either way. 408 tests, 2759/2759 lines. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DfJC1Wu1SEgJ6X49k5Jqtg --- .../workout_app/lib/main.dart | 7 +++ .../workout_app/test/main_lifecycle_test.dart | 43 +++++++++++++++++++ .../test/services/lock_mode_test.dart | 30 +++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 stronglift_replacement/workout_app/test/main_lifecycle_test.dart create mode 100644 stronglift_replacement/workout_app/test/services/lock_mode_test.dart diff --git a/stronglift_replacement/workout_app/lib/main.dart b/stronglift_replacement/workout_app/lib/main.dart index afc909b..d958ab6 100644 --- a/stronglift_replacement/workout_app/lib/main.dart +++ b/stronglift_replacement/workout_app/lib/main.dart @@ -111,6 +111,12 @@ class _WorkoutAppState extends State with WidgetsBindingObserver { super.dispose(); } + // coverage:ignore-start + // Platform edge, same shape as storage_service_schema's: the suite runs on + // Linux, where this returns on the first line, so the switch below is + // unreachable from any test this repo can run. Covering it would mean + // threading a platform seam through the nine Platform.isLinux checks in + // this app purely for test reachability. @override void didChangeAppLifecycleState(AppLifecycleState state) { // The socket is never started on Linux (see main()), so there is nothing @@ -127,6 +133,7 @@ class _WorkoutAppState extends State with WidgetsBindingObserver { break; } } + // coverage:ignore-end @override Widget build(BuildContext context) { diff --git a/stronglift_replacement/workout_app/test/main_lifecycle_test.dart b/stronglift_replacement/workout_app/test/main_lifecycle_test.dart new file mode 100644 index 0000000..2ba191a --- /dev/null +++ b/stronglift_replacement/workout_app/test/main_lifecycle_test.dart @@ -0,0 +1,43 @@ +// The lifecycle observer that starts and stops the phone's HTTP socket. +// +// Linux returns early because main() never starts the socket there, so a +// resume must not start one behind the lock screen's back. +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import 'package:workout_app/main.dart'; +import 'package:workout_app/services/storage_service.dart'; + +import 'fake_secure_storage.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + setUpAll(() { + // The widget-test host has no Android sqflite plugin; the FFI factory + // drives the same schema, as everywhere else in this suite. + sqfliteFfiInit(); + databaseFactory = databaseFactoryFfi; + }); + + setUp(() async { + StorageService.resetForTesting(); + await StorageService.init(); + installFakeSecureStorage(); + }); + + testWidgets('every lifecycle transition is handled without throwing', ( + tester, + ) async { + await tester.pumpWidget(const WorkoutApp()); + await tester.pump(); + + // Drives didChangeAppLifecycleState for each state the switch names. + for (final state in AppLifecycleState.values) { + tester.binding.handleAppLifecycleStateChanged(state); + await tester.pump(); + } + + expect(tester.takeException(), isNull); + }); +} diff --git a/stronglift_replacement/workout_app/test/services/lock_mode_test.dart b/stronglift_replacement/workout_app/test/services/lock_mode_test.dart new file mode 100644 index 0000000..5ac80b9 --- /dev/null +++ b/stronglift_replacement/workout_app/test/services/lock_mode_test.dart @@ -0,0 +1,30 @@ +// The flag the Linux runner and the Dart side must agree on. +// +// They read the same argument on purpose: if the runner grabbed the screen +// while Dart still showed its exits, the lock would be dismissible. +import 'package:flutter_test/flutter_test.dart'; +import 'package:workout_app/services/lock_mode.dart'; + +void main() { + group('parseLockMode', () { + test('is false for an ordinary windowed launch', () { + expect(parseLockMode([]), isFalse); + }); + + test('is true when the runner was given the flag', () { + expect(parseLockMode([kLockModeFlag]), isTrue); + }); + + test('finds the flag alongside the other arguments', () { + expect(parseLockMode(['--demo-escape', kLockModeFlag]), isTrue); + }); + + test('does not match a different flag', () { + expect(parseLockMode(['--demo-escape']), isFalse); + }); + }); + + test('lockModeEnabled defaults to false, so tests behave as before', () { + expect(lockModeEnabled, isFalse); + }); +}