Compare commits

..

3 commits

Author SHA1 Message Date
Vadim
2eb94f8a09 rename 2023-06-12 16:13:55 +02:00
Vadim
a3d875f066 refined LuxMeteringEvent tests 2023-06-12 16:13:03 +02:00
Vadim
8e1b64da64 LuxMeteringEvent tests 2023-06-12 16:10:40 +02:00
9 changed files with 141 additions and 22 deletions

View file

@ -15,13 +15,13 @@ import 'package:lightmeter/screens/metering/state_metering.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class MeteringBloc extends Bloc<MeteringEvent, MeteringState> { class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
final MeteringCommunicationBloc _communicationBloc;
final MeteringInteractor _meteringInteractor; final MeteringInteractor _meteringInteractor;
final MeteringCommunicationBloc _communicationBloc;
late final StreamSubscription<communication_states.ScreenState> _communicationSubscription; late final StreamSubscription<communication_states.ScreenState> _communicationSubscription;
MeteringBloc( MeteringBloc(
this._communicationBloc,
this._meteringInteractor, this._meteringInteractor,
this._communicationBloc,
) : super( ) : super(
MeteringDataState( MeteringDataState(
ev100: null, ev100: null,

View file

@ -22,8 +22,28 @@ abstract class MeasuredEvent extends SourceEvent {
class MeteringInProgressEvent extends MeasuredEvent { class MeteringInProgressEvent extends MeasuredEvent {
const MeteringInProgressEvent(super.ev100); 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 { class MeteringEndedEvent extends MeasuredEvent {
const MeteringEndedEvent(super.ev100); 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);
} }

View file

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/interactors/metering_interactor.dart';
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
@ -16,35 +17,46 @@ class LightSensorContainerBloc
final MeteringInteractor _meteringInteractor; final MeteringInteractor _meteringInteractor;
StreamSubscription<int>? _luxSubscriptions; StreamSubscription<int>? _luxSubscriptions;
double _ev100 = 0.0;
LightSensorContainerBloc( LightSensorContainerBloc(
this._meteringInteractor, this._meteringInteractor,
MeteringCommunicationBloc communicationBloc, MeteringCommunicationBloc communicationBloc,
) : super( ) : super(
communicationBloc, communicationBloc,
const LightSensorInitState(), const LightSensorContainerState(null),
); ) {
on<LuxMeteringEvent>(_onLuxMeteringEvent);
}
@override @override
void onCommunicationState(communication_states.SourceState communicationState) { void onCommunicationState(communication_states.SourceState communicationState) {
if (communicationState is communication_states.MeasureState) { if (communicationState is communication_states.MeasureState) {
if (_luxSubscriptions == null) { if (_luxSubscriptions == null) {
_luxSubscriptions = _meteringInteractor.luxStream().listen((event) { _startMetering();
_ev100 = log2(event.toDouble() / 2.5) + _meteringInteractor.lightSensorEvCalibration;
communicationBloc.add(communication_event.MeteringInProgressEvent(_ev100));
});
} else { } else {
communicationBloc.add(communication_event.MeteringEndedEvent(_ev100)); _cancelMetering();
_luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null);
} }
} }
} }
@override @override
Future<void> close() async { Future<void> close() async {
communicationBloc.add(communication_event.MeteringEndedEvent(_ev100)); _cancelMetering();
_luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null);
return super.close(); return super.close();
} }
void _onLuxMeteringEvent(LuxMeteringEvent event, Emitter<LightSensorContainerState> 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);
}
} }

View file

@ -1,3 +1,9 @@
abstract class LightSensorContainerEvent { abstract class LightSensorContainerEvent {
const LightSensorContainerEvent(); const LightSensorContainerEvent();
} }
class LuxMeteringEvent extends LightSensorContainerEvent {
final int lux;
const LuxMeteringEvent(this.lux);
}

View file

@ -1,7 +1,5 @@
abstract class LightSensorContainerState { class LightSensorContainerState {
const LightSensorContainerState(); final double? ev100;
}
class LightSensorInitState extends LightSensorContainerState { const LightSensorContainerState(this.ev100);
const LightSensorInitState();
} }

View file

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart'
@ -21,5 +22,6 @@ abstract class EvSourceBlocBase<E, S> extends Bloc<E, S> {
return super.close(); return super.close();
} }
@visibleForTesting
void onCommunicationState(communication_states.SourceState communicationState); void onCommunicationState(communication_states.SourceState communicationState);
} }

View file

@ -34,8 +34,8 @@ class _MeteringFlowState extends State<MeteringFlow> {
BlocProvider(create: (_) => MeteringCommunicationBloc()), BlocProvider(create: (_) => MeteringCommunicationBloc()),
BlocProvider( BlocProvider(
create: (context) => MeteringBloc( create: (context) => MeteringBloc(
context.read<MeteringCommunicationBloc>(),
context.get<MeteringInteractor>(), context.get<MeteringInteractor>(),
context.read<MeteringCommunicationBloc>(),
), ),
), ),
], ],

View file

@ -20,14 +20,14 @@ class _MockMeteringCommunicationBloc extends MockBloc<
class _MockMeteringInteractor extends Mock implements MeteringInteractor {} class _MockMeteringInteractor extends Mock implements MeteringInteractor {}
void main() { void main() {
late _MockMeteringCommunicationBloc communicationBloc;
late _MockMeteringInteractor meteringInteractor; late _MockMeteringInteractor meteringInteractor;
late _MockMeteringCommunicationBloc communicationBloc;
late MeteringBloc bloc; late MeteringBloc bloc;
const iso100 = IsoValue(100, StopType.full); const iso100 = IsoValue(100, StopType.full);
setUpAll(() { setUpAll(() {
communicationBloc = _MockMeteringCommunicationBloc();
meteringInteractor = _MockMeteringInteractor(); meteringInteractor = _MockMeteringInteractor();
communicationBloc = _MockMeteringCommunicationBloc();
when<IsoValue>(() => meteringInteractor.iso).thenReturn(iso100); when<IsoValue>(() => meteringInteractor.iso).thenReturn(iso100);
when<NdValue>(() => meteringInteractor.ndFilter).thenReturn(NdValue.values.first); when<NdValue>(() => meteringInteractor.ndFilter).thenReturn(NdValue.values.first);
@ -40,8 +40,8 @@ void main() {
setUp(() { setUp(() {
bloc = MeteringBloc( bloc = MeteringBloc(
communicationBloc,
meteringInteractor, meteringInteractor,
communicationBloc,
); );
}); });

View file

@ -0,0 +1,81 @@
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<int> luxIterable = [1, 2, 2, 2, 3];
final List<double> resultList = luxIterable.map((lux) => log2(lux / 2.5)).toList();
blocTest<LightSensorContainerBloc, LightSensorContainerState>(
'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(5);
verify(() {
communicationBloc.add(communication_events.MeteringInProgressEvent(resultList.first));
}).called(1);
verify(() {
communicationBloc.add(communication_events.MeteringInProgressEvent(resultList[1]));
}).called(3);
verify(() {
communicationBloc.add(communication_events.MeteringInProgressEvent(resultList.last));
}).called(1);
verify(() {
communicationBloc.add(communication_events.MeteringEndedEvent(resultList.last));
}).called(2); // +1 from dispose
},
expect: () => resultList.map(
(e) => isA<LightSensorContainerState>().having((state) => state.ev100, 'ev100', e),
),
);
},
);
}