mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-24 00:10:47 +00:00
Compare commits
3 commits
7fdacfac37
...
2eb94f8a09
Author | SHA1 | Date | |
---|---|---|---|
|
2eb94f8a09 | ||
|
a3d875f066 | ||
|
8e1b64da64 |
9 changed files with 141 additions and 22 deletions
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
abstract class LightSensorContainerState {
|
class LightSensorContainerState {
|
||||||
const LightSensorContainerState();
|
final double? ev100;
|
||||||
}
|
|
||||||
|
|
||||||
class LightSensorInitState extends LightSensorContainerState {
|
const LightSensorContainerState(this.ev100);
|
||||||
const LightSensorInitState();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue