From 10bf463021ace134a2665ca4405dbcbfc2a231e2 Mon Sep 17 00:00:00 2001 From: Vadim Date: Sun, 7 May 2023 21:28:19 +0200 Subject: [PATCH] wip --- lib/screens/metering/bloc_metering.dart | 25 +- pubspec.yaml | 6 + test/screens/metering/bloc_metering_test.dart | 216 ++++++++++++++++++ 3 files changed, 242 insertions(+), 5 deletions(-) create mode 100644 test/screens/metering/bloc_metering_test.dart diff --git a/lib/screens/metering/bloc_metering.dart b/lib/screens/metering/bloc_metering.dart index 4ebe422..1fa5bb8 100644 --- a/lib/screens/metering/bloc_metering.dart +++ b/lib/screens/metering/bloc_metering.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'dart:math'; +import 'package:bloc_concurrency/bloc_concurrency.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/film.dart'; @@ -58,7 +60,7 @@ class MeteringBloc extends Bloc { on(_onFilmChanged); on(_onIsoChanged); on(_onNdChanged); - on(_onMeasure); + on(_onMeasure, transformer: droppable()); on(_onMeasured); on(_onMeasureError); } @@ -89,14 +91,14 @@ class MeteringBloc extends Bloc { /// Update selected ISO value, if selected equipment profile /// doesn't contain currently selected value - if (!event.equipmentProfileData.isoValues.any((v) => _iso.value == v.value)) { + if (!event.equipmentProfileData.isoValues.any((v) => iso.value == v.value)) { _meteringInteractor.iso = event.equipmentProfileData.isoValues.first; _iso = event.equipmentProfileData.isoValues.first; willUpdateMeasurements &= true; } /// The same for ND filter - if (!event.equipmentProfileData.ndValues.any((v) => _nd.value == v.value)) { + if (!event.equipmentProfileData.ndValues.any((v) => nd.value == v.value)) { _meteringInteractor.ndFilter = event.equipmentProfileData.ndValues.first; _nd = event.equipmentProfileData.ndValues.first; willUpdateMeasurements &= true; @@ -203,7 +205,20 @@ class MeteringBloc extends Bloc { ); } - List _buildExposureValues(double ev) { + void _onMeasureError(MeasureErrorEvent _, Emitter emit) { + _meteringInteractor.errorVibration(); + emit(MeteringDataState( + ev: null, + film: film, + iso: iso, + nd: nd, + exposurePairs: const [], + continuousMetering: isMeteringInProgress, + )); + } + + @visibleForTesting + List buildExposureValues(double ev) { if (ev.isNaN || ev.isInfinite) { return List.empty(); } @@ -254,7 +269,7 @@ class MeteringBloc extends Bloc { itemsCount, (index) => ExposurePair( _apertureValues[index + apertureOffset], - _film.reciprocityFailure(_shutterSpeedValues[index + shutterSpeedOffset]), + film.reciprocityFailure(_shutterSpeedValues[index + shutterSpeedOffset]), ), growable: false, ); diff --git a/pubspec.yaml b/pubspec.yaml index 361e254..0a281ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,12 +35,18 @@ dependencies: vibration: 1.7.7 dev_dependencies: + bloc_test: 9.1.1 + build_runner: ^2.1.7 flutter_launcher_icons: 0.11.0 flutter_native_splash: 2.2.16 google_fonts: 3.0.1 lint: 2.1.2 + mocktail: 0.3.0 test: 1.24.1 +dependency_overrides: + test_api: 0.4.16 + flutter: uses-material-design: true diff --git a/test/screens/metering/bloc_metering_test.dart b/test/screens/metering/bloc_metering_test.dart new file mode 100644 index 0000000..e29d12c --- /dev/null +++ b/test/screens/metering/bloc_metering_test.dart @@ -0,0 +1,216 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:lightmeter/data/models/film.dart'; +import 'package:lightmeter/interactors/metering_interactor.dart'; +import 'package:lightmeter/res/dimens.dart'; +import 'package:lightmeter/screens/metering/bloc_metering.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/event_metering.dart'; +import 'package:lightmeter/screens/metering/state_metering.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 EquipmentProfileData equipmentProfileData; + late MeteringBloc bloc; + const iso100 = IsoValue(100, StopType.full); + double initEV = 0.0; + + setUpAll(() { + communicationBloc = _MockMeteringCommunicationBloc(); + meteringInteractor = _MockMeteringInteractor(); + equipmentProfileData = const EquipmentProfileData( + id: '0', + name: 'Test equipment', + apertureValues: ApertureValue.values, + ndValues: NdValue.values, + shutterSpeedValues: ShutterSpeedValue.values, + isoValues: IsoValue.values, + ); + + when(() => meteringInteractor.iso).thenReturn(iso100); + when(() => meteringInteractor.ndFilter).thenReturn(NdValue.values.first); + when(() => meteringInteractor.film).thenReturn(Film.values.first); + when(meteringInteractor.quickVibration).thenAnswer((_) async {}); + when(meteringInteractor.responseVibration).thenAnswer((_) async {}); + }); + + setUp(() { + bloc = MeteringBloc( + communicationBloc, + meteringInteractor, + equipmentProfileData, + StopType.third, + ); + }); + + tearDown(() { + bloc.close(); + }); + + group('MeteringBloc test:', () { + test('Initial state', () { + expect( + bloc.state, + isA() + .having((state) => state.ev, 'ev', initEV) + .having((state) => state.film, 'film', bloc.film) + .having((state) => state.iso, 'iso', bloc.iso) + .having((state) => state.nd, 'nd', bloc.nd) + .having((state) => state.exposurePairs, 'exposurePairs', const []), + ); + }); + + blocTest( + 'Measure', + build: () => bloc, + act: (bloc) { + bloc.add(const MeasureEvent()); + bloc.add(const MeasureEvent()); + bloc.add(const MeasureEvent()); + bloc.add(const MeasureEvent()); + }, + verify: (_) { + verify(() => meteringInteractor.quickVibration()).called(1); + verify(() => communicationBloc.add(const communication_events.MeasureEvent())).called(1); + }, + expect: () => [ + isA() + .having((state) => state.film, 'film', Film.values.first) + .having((state) => state.iso, 'iso', iso100) + .having((state) => state.nd, 'nd', NdValue.values.first), + ], + ); + + blocTest( + 'Measured', + build: () => bloc, + act: (bloc) => bloc.add(const MeasuredEvent(2)), + verify: (_) { + verify(() => meteringInteractor.responseVibration()).called(1); + }, + expect: () => [ + isA() + .having((_) => bloc.ev100, 'ev100', 2) + .having((state) => state.ev, 'ev', 2) + .having((state) => state.film, 'film', Film.values.first) + .having((state) => state.iso, 'iso', iso100) + .having((state) => state.nd, 'nd', NdValue.values.first), + ], + ); + + const isoValueToSet = IsoValue(200, StopType.full); + blocTest( + 'ISO change', + setUp: () { + when(() => meteringInteractor.iso = isoValueToSet); + bloc.ev100 = 0; + bloc.iso = iso100; + }, + seed: () => MeteringDataState( + ev: 0.0, + film: meteringInteractor.film, + iso: meteringInteractor.iso, + nd: meteringInteractor.ndFilter, + exposurePairs: const [], + continuousMetering: false, + ), + build: () => bloc, + act: (bloc) async { + bloc.add(const MeasuredEvent(1)); + bloc.add(const IsoChangedEvent(isoValueToSet)); + await Future.delayed(Dimens.durationS); + bloc.add(const MeasureEvent()); + await Future.delayed(Dimens.durationS); + bloc.add(const MeasuredEvent(3)); + await Future.delayed(Dimens.durationS); + }, + verify: (_) { + verify(() => meteringInteractor.iso = isoValueToSet).called(1); + }, + expect: () => [ + isA() + .having((_) => bloc.ev100, 'ev100', 1) + .having((state) => state.ev, 'ev', 1) + .having((state) => state.film, 'film', Film.values.first) + .having((state) => state.iso, 'iso', iso100) + .having((state) => state.nd, 'nd', NdValue.values.first), + isA() + .having((_) => bloc.ev100, 'ev100', 1) + .having((_) => bloc.iso, 'blocIso', isoValueToSet) + .having((state) => state.ev, 'ev', 2) + .having((state) => state.film, 'film', Film.values.first) + .having((state) => state.iso, 'iso', isoValueToSet) + .having((state) => state.nd, 'nd', NdValue.values.first), + isA() + .having((state) => state.film, 'film', Film.values.first) + .having((state) => state.iso, 'iso', isoValueToSet) + .having((state) => state.nd, 'nd', NdValue.values.first), + isA() + .having((_) => bloc.ev100, 'ev100', 3) + .having((_) => bloc.iso, 'blocIso', isoValueToSet) + //.having((state) => state.ev, 'ev', 4) + .having((state) => state.film, 'film', Film.values.first) + .having((state) => state.iso, 'iso', isoValueToSet) + .having((state) => state.nd, 'nd', NdValue.values.first), + ], + ); + + blocTest( + 'Measured', + build: () => bloc, + setUp: () { + when(() => meteringInteractor.iso = isoValueToSet); + bloc.ev100 = 2; + bloc.iso = isoValueToSet; + }, + act: (bloc) => bloc.add(const MeasuredEvent(3)), + verify: (_) { + verify(() => meteringInteractor.responseVibration()).called(1); + }, + expect: () => [ + isA() + .having((_) => bloc.ev100, 'ev100', 3) + .having((_) => bloc.iso, 'blocIso', isoValueToSet) + .having((state) => state.ev, 'ev', 4) + .having((state) => state.film, 'film', Film.values.first) + .having((state) => state.iso, 'iso', isoValueToSet) + .having((state) => state.nd, 'nd', NdValue.values.first), + ], + ); + + // blocTest( + // 'ND change', + // build: () => bloc, + // act: (bloc) => bloc.add(NdChangedEvent(NdValue.values[1])), + // expect: () => [ + // isA() + // .having((state) => state.ev, 'ev', -1) + // .having((state) => state.nd, 'nd', NdValue.values[1]), + // ], + // ); + + // blocTest( + // 'Measure', + // build: () => bloc, + // act: (bloc) => bloc.add(NdChangedEvent(NdValue.values[1])), + // expect: () => [ + // isA() + // .having((state) => state.ev, 'ev', -1) + // .having((state) => state.nd, 'nd', NdValue.values[1]), + // ], + // ); + }); +}