screen-locker/stronglift_replacement/workout_app/lib/main.dart
Krzysztof kuhy Rudnicki 0d0e6e4481 Cover services + app bootstrap toward 100% flutter coverage
Pre-existing coverage gaps, unrelated to the manual-workout feature, blocking the
100% pre-push gate:

- widget_test: exercise WorkoutApp's app-lifecycle handler (paused/detached/
  resumed/inactive/hidden) via handleAppLifecycleStateChanged.
- http_server_service: add a resetForTesting seam and test the bind-in-use
  SocketException swallow plus the /workout HTTP endpoint (no-data 404, data 200,
  unknown-path 404). coverage:ignore the /sdcard-file-read branch (only reachable
  on a real device).
- backup_service: coverage:ignore the Android permission wrappers and the
  defensive readSyncToken catch.
- main(): coverage:ignore the platform-channel app bootstrap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PM3auNTifaVgcXWDizZrjH
2026-07-13 13:27:18 +02:00

79 lines
2.4 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:workout_app/screens/home_screen.dart';
import 'package:workout_app/services/backup_service.dart';
import 'package:workout_app/services/http_server_service.dart';
import 'package:workout_app/services/storage_service.dart';
// coverage:ignore-start
// App bootstrap: permission request, DB init, and a real listening socket —
// platform-channel work that can't run on the CI test host. The pieces it wires
// up are unit-tested individually; the entry point itself is not.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await BackupService.instance.requestStoragePermission();
await StorageService.init();
await StorageService.instance.restoreFromBackupIfNeeded();
await HttpServerService.instance.start();
runApp(const WorkoutApp());
}
// coverage:ignore-end
/// Root widget that bootstraps the app with Material 3 dark theming.
///
/// Also owns the app-lifecycle observer that stops [HttpServerService]'s
/// listening socket while the app is backgrounded, so the process (and its
/// open socket) doesn't sit alive indefinitely and block Doze/App-Standby.
class WorkoutApp extends StatefulWidget {
/// Creates the root app widget.
const WorkoutApp({super.key});
@override
State<WorkoutApp> createState() => _WorkoutAppState();
}
class _WorkoutAppState extends State<WorkoutApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.paused:
case AppLifecycleState.detached:
unawaited(HttpServerService.instance.stop());
case AppLifecycleState.resumed:
unawaited(HttpServerService.instance.start());
case AppLifecycleState.inactive:
case AppLifecycleState.hidden:
break;
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Workout Tracker',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.indigo,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const HomeScreen(),
);
}
}