import 'package:cardash_launcher/data/models/bluetooth_device_info.dart'; import 'package:cardash_launcher/data/models/bluetooth_power_state.dart'; import 'package:cardash_launcher/data/models/gps_speed.dart'; import 'package:cardash_launcher/data/models/location_reading.dart'; import 'package:cardash_launcher/data/models/obd_state.dart'; import 'package:cardash_launcher/data/repositories/settings_repository.dart'; import 'package:cardash_launcher/data/services/telemetry_bridge.dart'; import 'package:cardash_launcher/data/services/weather_service.dart'; import 'package:cardash_launcher/features/home/gps_speed_controller.dart'; import 'package:cardash_launcher/features/home/telemetry_settings_controller.dart'; import 'package:cardash_launcher/features/home/weather_controller.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; class _TelemetryPlatform implements TelemetryPlatform { _TelemetryPlatform({this.locationGranted = true}); bool locationGranted; int locationRequests = 0; @override Future ensureLocationPermission() async { locationRequests++; return locationGranted; } @override Future getLocation() async => const LocationReading(latitude: 1, longitude: 2, place: 'Sofia'); @override Future> listBondedDevices() async => const []; @override Future bluetoothState() async => BluetoothPowerState.on; @override Future ensureBluetoothPermission() async => true; @override Future openBluetoothSettings() async => true; @override Future connectObd(String address) async {} @override Future disconnectObd() async {} @override Future getObdState() async => const ObdState(); @override Future startGpsSpeed() async => false; @override Future stopGpsSpeed() async {} @override Future getGpsSpeed() async => const GpsSpeed(); } class _WeatherService extends WeatherService { @override Future temperatureCelsius({ required double latitude, required double longitude, }) async => 12; } void main() { TestWidgetsFlutterBinding.ensureInitialized(); test('persists source choices and starts weather when needed', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final settings = SettingsRepository(prefs); await settings.setTemperatureEnabled(true); final weather = WeatherController( _TelemetryPlatform(), weatherService: _WeatherService(), ); final gps = GpsSpeedController(_TelemetryPlatform()); final controller = TelemetrySettingsController(settings, weather, gps) ..initialize(); addTearDown(controller.dispose); addTearDown(gps.dispose); addTearDown(weather.dispose); expect(controller.speedEnabled, isTrue); expect(controller.speedUseObd, isTrue); expect(controller.temperatureEnabled, isTrue); expect(controller.temperatureUseObd, isTrue); await controller.setSpeedEnabled(false); await controller.setSpeedUseObd(false); await controller.setTemperatureUseObd(false); expect(settings.speedEnabled, isFalse); expect(settings.speedUseObd, isFalse); expect(settings.temperatureUseObd, isFalse); expect(weather.enabled, isTrue); expect(weather.hasTemperature, isTrue); expect(settings.weatherLocationTemperature, isTrue); }); test('turning temperature off stops and clears the weather poller', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final settings = SettingsRepository(prefs); await settings.setTemperatureEnabled(true); final weather = WeatherController( _TelemetryPlatform(), weatherService: _WeatherService(), ); final gps = GpsSpeedController(_TelemetryPlatform()); final controller = TelemetrySettingsController(settings, weather, gps) ..initialize(); addTearDown(controller.dispose); addTearDown(gps.dispose); addTearDown(weather.dispose); await controller.setTemperatureUseObd(false); expect(weather.hasTemperature, isTrue); await controller.setTemperatureEnabled(false); expect(controller.temperatureEnabled, isFalse); expect(settings.temperatureEnabled, isFalse); expect(weather.enabled, isFalse); expect(weather.hasTemperature, isFalse); expect(settings.weatherLocationTemperature, isFalse); }); test( 'denied location leaves the switch on but reports weather failure', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final settings = SettingsRepository(prefs); await settings.setTemperatureEnabled(true); final weather = WeatherController( _TelemetryPlatform(locationGranted: false), weatherService: _WeatherService(), ); final gps = GpsSpeedController(_TelemetryPlatform()); final controller = TelemetrySettingsController(settings, weather, gps) ..initialize(); addTearDown(controller.dispose); addTearDown(gps.dispose); addTearDown(weather.dispose); final granted = await controller.setTemperatureUseObd(false); expect(granted, isFalse); expect(controller.temperatureUseObd, isFalse); expect(weather.enabled, isFalse); expect(controller.weatherError, isNotNull); }, ); test('enabling speed asks for location without an OBD adapter', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final settings = SettingsRepository(prefs); await settings.setSpeedEnabled(false); final platform = _TelemetryPlatform(); final weather = WeatherController(platform); final gps = GpsSpeedController(platform); final controller = TelemetrySettingsController(settings, weather, gps) ..initialize(); addTearDown(controller.dispose); addTearDown(gps.dispose); addTearDown(weather.dispose); final granted = await controller.setSpeedEnabled(true); expect(granted, isTrue); expect(platform.locationRequests, 1); expect(controller.speedEnabled, isTrue); expect(settings.speedEnabled, isTrue); }); test('enabling speed does not ask when OBD speed is selected', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final settings = SettingsRepository(prefs); await settings.setSpeedEnabled(false); await settings.setObdAdapterAddress('AA:BB:CC:DD:EE:FF'); final platform = _TelemetryPlatform(); final weather = WeatherController(platform); final gps = GpsSpeedController(platform); final controller = TelemetrySettingsController(settings, weather, gps) ..initialize(); addTearDown(controller.dispose); addTearDown(gps.dispose); addTearDown(weather.dispose); final granted = await controller.setSpeedEnabled(true); expect(granted, isTrue); expect(platform.locationRequests, 0); expect(controller.speedEnabled, isTrue); }); test('denied location keeps speed on and reports the failure', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final settings = SettingsRepository(prefs); await settings.setSpeedEnabled(false); final platform = _TelemetryPlatform(locationGranted: false); final weather = WeatherController(platform); final gps = GpsSpeedController(platform); final controller = TelemetrySettingsController(settings, weather, gps) ..initialize(); addTearDown(controller.dispose); addTearDown(gps.dispose); addTearDown(weather.dispose); final granted = await controller.setSpeedEnabled(true); expect(granted, isFalse); expect(platform.locationRequests, 1); expect(controller.speedEnabled, isTrue); expect(settings.speedEnabled, isTrue); }); test('choosing GPS speed asks for location while speed is on', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final settings = SettingsRepository(prefs); await settings.setObdAdapterAddress('AA:BB:CC:DD:EE:FF'); final platform = _TelemetryPlatform(); final weather = WeatherController(platform); final gps = GpsSpeedController(platform); final controller = TelemetrySettingsController(settings, weather, gps) ..initialize(); addTearDown(controller.dispose); addTearDown(gps.dispose); addTearDown(weather.dispose); final granted = await controller.setSpeedUseObd(false); expect(granted, isTrue); expect(platform.locationRequests, 1); expect(controller.speedUseObd, isFalse); expect(await controller.setSpeedUseObd(true), isTrue); expect(platform.locationRequests, 1); }); }