diff --git a/lib/screens/metering/bloc_metering.dart b/lib/screens/metering/bloc_metering.dart index 48086af..1f041a5 100644 --- a/lib/screens/metering/bloc_metering.dart +++ b/lib/screens/metering/bloc_metering.dart @@ -15,13 +15,13 @@ import 'package:lightmeter/screens/metering/state_metering.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class MeteringBloc extends Bloc { - final MeteringCommunicationBloc _communicationBloc; final MeteringInteractor _meteringInteractor; + final MeteringCommunicationBloc _communicationBloc; late final StreamSubscription _communicationSubscription; MeteringBloc( - this._communicationBloc, this._meteringInteractor, + this._communicationBloc, ) : super( MeteringDataState( ev100: null, diff --git a/lib/screens/metering/communication/event_communication_metering.dart b/lib/screens/metering/communication/event_communication_metering.dart index 7c00173..ac63b57 100644 --- a/lib/screens/metering/communication/event_communication_metering.dart +++ b/lib/screens/metering/communication/event_communication_metering.dart @@ -22,8 +22,28 @@ abstract class MeasuredEvent extends SourceEvent { class MeteringInProgressEvent extends MeasuredEvent { const MeteringInProgressEvent(super.ev100); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other.runtimeType != runtimeType) return false; + return other is MeteringInProgressEvent && other.ev100 == ev100; + } + + @override + int get hashCode => Object.hash(ev100, runtimeType); } class MeteringEndedEvent extends MeasuredEvent { const MeteringEndedEvent(super.ev100); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other.runtimeType != runtimeType) return false; + return other is MeteringEndedEvent && other.ev100 == ev100; + } + + @override + int get hashCode => Object.hash(ev100, runtimeType); } diff --git a/lib/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart b/lib/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart index 45719ec..d0e4031 100644 --- a/lib/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart +++ b/lib/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' @@ -16,35 +17,46 @@ class LightSensorContainerBloc final MeteringInteractor _meteringInteractor; StreamSubscription? _luxSubscriptions; - double _ev100 = 0.0; LightSensorContainerBloc( this._meteringInteractor, MeteringCommunicationBloc communicationBloc, ) : super( communicationBloc, - const LightSensorInitState(), - ); + const LightSensorContainerState(null), + ) { + on(_onLuxMeteringEvent); + } @override void onCommunicationState(communication_states.SourceState communicationState) { if (communicationState is communication_states.MeasureState) { if (_luxSubscriptions == null) { - _luxSubscriptions = _meteringInteractor.luxStream().listen((event) { - _ev100 = log2(event.toDouble() / 2.5) + _meteringInteractor.lightSensorEvCalibration; - communicationBloc.add(communication_event.MeteringInProgressEvent(_ev100)); - }); + _startMetering(); } else { - communicationBloc.add(communication_event.MeteringEndedEvent(_ev100)); - _luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null); + _cancelMetering(); } } } @override Future close() async { - communicationBloc.add(communication_event.MeteringEndedEvent(_ev100)); - _luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null); + _cancelMetering(); return super.close(); } + + void _onLuxMeteringEvent(LuxMeteringEvent event, Emitter emit) { + final ev100 = log2(event.lux.toDouble() / 2.5) + _meteringInteractor.lightSensorEvCalibration; + emit(LightSensorContainerState(ev100)); + communicationBloc.add(communication_event.MeteringInProgressEvent(ev100)); + } + + void _startMetering() { + _luxSubscriptions = _meteringInteractor.luxStream().listen((lux) => add(LuxMeteringEvent(lux))); + } + + void _cancelMetering() { + communicationBloc.add(communication_event.MeteringEndedEvent(state.ev100)); + _luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null); + } } diff --git a/lib/screens/metering/components/light_sensor_container/event_container_light_sensor.dart b/lib/screens/metering/components/light_sensor_container/event_container_light_sensor.dart index 6c6cbf6..8db83b3 100644 --- a/lib/screens/metering/components/light_sensor_container/event_container_light_sensor.dart +++ b/lib/screens/metering/components/light_sensor_container/event_container_light_sensor.dart @@ -1,3 +1,9 @@ abstract class LightSensorContainerEvent { const LightSensorContainerEvent(); } + +class LuxMeteringEvent extends LightSensorContainerEvent { + final int lux; + + const LuxMeteringEvent(this.lux); +} diff --git a/lib/screens/metering/components/light_sensor_container/state_container_light_sensor.dart b/lib/screens/metering/components/light_sensor_container/state_container_light_sensor.dart index 810be0c..346c8fb 100644 --- a/lib/screens/metering/components/light_sensor_container/state_container_light_sensor.dart +++ b/lib/screens/metering/components/light_sensor_container/state_container_light_sensor.dart @@ -1,7 +1,5 @@ -abstract class LightSensorContainerState { - const LightSensorContainerState(); -} +class LightSensorContainerState { + final double? ev100; -class LightSensorInitState extends LightSensorContainerState { - const LightSensorInitState(); + const LightSensorContainerState(this.ev100); } diff --git a/lib/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart b/lib/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart index 5d8f9c2..05c3fb1 100644 --- a/lib/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart +++ b/lib/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' @@ -21,5 +22,6 @@ abstract class EvSourceBlocBase extends Bloc { return super.close(); } + @visibleForTesting void onCommunicationState(communication_states.SourceState communicationState); } diff --git a/lib/screens/metering/flow_metering.dart b/lib/screens/metering/flow_metering.dart index ebbef96..ac6cb0d 100644 --- a/lib/screens/metering/flow_metering.dart +++ b/lib/screens/metering/flow_metering.dart @@ -34,8 +34,8 @@ class _MeteringFlowState extends State { BlocProvider(create: (_) => MeteringCommunicationBloc()), BlocProvider( create: (context) => MeteringBloc( - context.read(), context.get(), + context.read(), ), ), ], diff --git a/test/screens/metering/bloc_metering_test.dart b/test/screens/metering/bloc_metering_test.dart index bc4a718..0daf62c 100644 --- a/test/screens/metering/bloc_metering_test.dart +++ b/test/screens/metering/bloc_metering_test.dart @@ -20,14 +20,14 @@ class _MockMeteringCommunicationBloc extends MockBloc< class _MockMeteringInteractor extends Mock implements MeteringInteractor {} void main() { - late _MockMeteringCommunicationBloc communicationBloc; late _MockMeteringInteractor meteringInteractor; + late _MockMeteringCommunicationBloc communicationBloc; late MeteringBloc bloc; const iso100 = IsoValue(100, StopType.full); setUpAll(() { - communicationBloc = _MockMeteringCommunicationBloc(); meteringInteractor = _MockMeteringInteractor(); + communicationBloc = _MockMeteringCommunicationBloc(); when(() => meteringInteractor.iso).thenReturn(iso100); when(() => meteringInteractor.ndFilter).thenReturn(NdValue.values.first); @@ -40,8 +40,8 @@ void main() { setUp(() { bloc = MeteringBloc( - communicationBloc, meteringInteractor, + communicationBloc, ); }); diff --git a/test/screens/metering/components/light_sensor/bloc_light_sensor_test.dart b/test/screens/metering/components/light_sensor/bloc_light_sensor_test.dart new file mode 100644 index 0000000..c2c4ed9 --- /dev/null +++ b/test/screens/metering/components/light_sensor/bloc_light_sensor_test.dart @@ -0,0 +1,83 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:lightmeter/interactors/metering_interactor.dart'; +import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; +import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' + as communication_events; +import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' + as communication_states; +import 'package:lightmeter/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart'; +import 'package:lightmeter/screens/metering/components/light_sensor_container/state_container_light_sensor.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; + +class _MockMeteringCommunicationBloc extends MockBloc< + communication_events.MeteringCommunicationEvent, + communication_states.MeteringCommunicationState> implements MeteringCommunicationBloc {} + +class _MockMeteringInteractor extends Mock implements MeteringInteractor {} + +void main() { + late _MockMeteringCommunicationBloc communicationBloc; + late _MockMeteringInteractor meteringInteractor; + late LightSensorContainerBloc bloc; + + setUpAll(() { + communicationBloc = _MockMeteringCommunicationBloc(); + meteringInteractor = _MockMeteringInteractor(); + }); + + setUp(() { + bloc = LightSensorContainerBloc( + meteringInteractor, + communicationBloc, + ); + }); + + tearDown(() { + bloc.close(); + }); + + group( + '`LuxMeteringEvent` tests', + () { + const List luxIterable = [1, 2, 3]; + final List resultList = luxIterable.map((lux) => log2(lux / 2.5)).toList(); + blocTest( + 'Turn measuring on/off', + build: () => bloc, + setUp: () { + when(() => meteringInteractor.luxStream()) + .thenAnswer((_) => Stream.fromIterable(luxIterable)); + when(() => meteringInteractor.lightSensorEvCalibration).thenReturn(0.0); + }, + act: (bloc) async { + bloc.onCommunicationState(const communication_states.MeasureState()); + await Future.delayed(Duration.zero); + bloc.onCommunicationState(const communication_states.MeasureState()); + }, + verify: (_) { + verify(() => meteringInteractor.luxStream().listen((_) {})).called(1); + verify(() => meteringInteractor.lightSensorEvCalibration).called(3); + verify(() { + communicationBloc.add(communication_events.MeteringInProgressEvent(resultList[0])); + }).called(1); + verify(() { + communicationBloc.add(communication_events.MeteringInProgressEvent(resultList[1])); + }).called(1); + verify(() { + communicationBloc.add(communication_events.MeteringInProgressEvent(resultList[2])); + }).called(1); + verify(() { + communicationBloc.add(communication_events.MeteringEndedEvent(resultList[2])); + }).called(2); // +1 from dispose + }, + expect: () => [ + isA().having((state) => state.ev100, 'ev100', resultList[0]), + isA().having((state) => state.ev100, 'ev100', resultList[1]), + isA().having((state) => state.ev100, 'ev100', resultList[2]), + ], + ); + }, + ); +}