Cover the lock-mode flag, and mark the one branch Linux cannot reach
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
release-apk / release (APK) (push) Waiting to run

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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfJC1Wu1SEgJ6X49k5Jqtg
This commit is contained in:
Krzysztof Rudnicki 2026-08-22 13:06:49 +02:00
parent 2f95c6cb69
commit c4cc39759a
3 changed files with 80 additions and 0 deletions

View File

@ -111,6 +111,12 @@ class _WorkoutAppState extends State<WorkoutApp> 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<WorkoutApp> with WidgetsBindingObserver {
break;
}
}
// coverage:ignore-end
@override
Widget build(BuildContext context) {

View File

@ -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);
});
}

View File

@ -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);
});
}