From 737daad6d021cae3ec980fe8ee7da024ad41b857 Mon Sep 17 00:00:00 2001 From: Vadim Date: Mon, 12 Jun 2023 17:02:04 +0200 Subject: [PATCH] wip --- .../bloc_container_camera.dart | 2 - .../provider_container_camera.dart | 3 +- pubspec.yaml | 2 + .../camera/bloc_container_camera_test.dart | 231 ++++++++++++++++++ .../bloc_container_light_sensor_test.dart | 4 +- 5 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 test/screens/metering/components/camera/bloc_container_camera_test.dart diff --git a/lib/screens/metering/components/camera_container/bloc_container_camera.dart b/lib/screens/metering/components/camera_container/bloc_container_camera.dart index 877c0ef..7b5222c 100644 --- a/lib/screens/metering/components/camera_container/bloc_container_camera.dart +++ b/lib/screens/metering/components/camera_container/bloc_container_camera.dart @@ -53,8 +53,6 @@ class CameraContainerBloc extends EvSourceBlocBase(_onZoomChanged); on(_onExposureOffsetChanged); on(_onExposureOffsetResetEvent); - - add(const RequestPermissionEvent()); } @override diff --git a/lib/screens/metering/components/camera_container/provider_container_camera.dart b/lib/screens/metering/components/camera_container/provider_container_camera.dart index 81357f7..344f573 100644 --- a/lib/screens/metering/components/camera_container/provider_container_camera.dart +++ b/lib/screens/metering/components/camera_container/provider_container_camera.dart @@ -5,6 +5,7 @@ import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/components/camera_container/bloc_container_camera.dart'; +import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/widget_container_camera.dart'; import 'package:lightmeter/utils/inherited_generics.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @@ -40,7 +41,7 @@ class CameraContainerProvider extends StatelessWidget { create: (context) => CameraContainerBloc( context.get(), context.read(), - ), + )..add(const RequestPermissionEvent()), child: CameraContainer( fastest: fastest, slowest: slowest, diff --git a/pubspec.yaml b/pubspec.yaml index 3beed6c..3c2451d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,8 @@ dev_dependencies: build_runner: ^2.1.7 flutter_launcher_icons: 0.11.0 flutter_native_splash: 2.2.16 + flutter_test: + sdk: flutter google_fonts: 3.0.1 lint: 2.1.2 mocktail: 0.3.0 diff --git a/test/screens/metering/components/camera/bloc_container_camera_test.dart b/test/screens/metering/components/camera/bloc_container_camera_test.dart new file mode 100644 index 0000000..82946ac --- /dev/null +++ b/test/screens/metering/components/camera/bloc_container_camera_test.dart @@ -0,0 +1,231 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_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/camera_container/bloc_container_camera.dart'; +import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart'; +import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart'; +import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart'; +import 'package:mocktail/mocktail.dart'; + +class _MockMeteringCommunicationBloc extends MockBloc< + communication_events.MeteringCommunicationEvent, + communication_states.MeteringCommunicationState> implements MeteringCommunicationBloc {} + +class _MockMeteringInteractor extends Mock implements MeteringInteractor {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late _MockMeteringInteractor meteringInteractor; + late _MockMeteringCommunicationBloc communicationBloc; + late CameraContainerBloc bloc; + + setUpAll(() { + meteringInteractor = _MockMeteringInteractor(); + communicationBloc = _MockMeteringCommunicationBloc(); + }); + + setUp(() { + bloc = CameraContainerBloc( + meteringInteractor, + communicationBloc, + ); + }); + + tearDown(() { + bloc.close(); + }); + + group( + '`RequestPermissionEvent` tests', + () { + blocTest( + 'Request denied', + build: () => bloc, + setUp: () { + when(() => meteringInteractor.requestPermission()).thenAnswer((_) async => false); + }, + act: (bloc) => bloc.add(const RequestPermissionEvent()), + verify: (_) { + verify(() => meteringInteractor.requestPermission()).called(1); + }, + expect: () => [ + isA() + .having((state) => state.error, "error", CameraErrorType.permissionNotGranted), + ], + ); + + blocTest( + 'Request granted -> check denied', + build: () => bloc, + setUp: () { + when(() => meteringInteractor.requestPermission()).thenAnswer((_) async => true); + when(() => meteringInteractor.checkCameraPermission()).thenAnswer((_) async => false); + }, + act: (bloc) => bloc.add(const RequestPermissionEvent()), + verify: (_) { + verify(() => meteringInteractor.requestPermission()).called(1); + verify(() => meteringInteractor.checkCameraPermission()).called(1); + }, + expect: () => [ + isA(), + isA() + .having((state) => state.error, "error", CameraErrorType.permissionNotGranted), + ], + ); + }, + ); + + group( + '`InitializeEvent` tests', + () { + const cameraMethodChannel = MethodChannel('plugins.flutter.io/camera'); + const cameraIdMethodChannel = MethodChannel('flutter.io/cameraPlugin/camera1'); + const availableCameras = [ + { + "name": "front", + "lensFacing": "front", + "sensorOrientation": 0, + }, + { + "name": "back", + "lensFacing": "back", + "sensorOrientation": 0, + }, + ]; + + blocTest( + 'No cameras detected', + setUp: () { + when(() => meteringInteractor.checkCameraPermission()).thenAnswer((_) async => true); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( + cameraMethodChannel, + (methodCall) async { + switch (methodCall.method) { + case "availableCameras": + return const []; + default: + return null; + } + }, + ); + }, + tearDown: () { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(cameraMethodChannel, null); + }, + build: () => bloc, + act: (bloc) => bloc.add(const InitializeEvent()), + verify: (_) { + verify(() => meteringInteractor.checkCameraPermission()).called(1); + }, + expect: () => [ + isA(), + isA() + .having((state) => state.error, "error", CameraErrorType.noCamerasDetected), + ], + ); + + blocTest( + 'Catch other initialization errors', + setUp: () { + when(() => meteringInteractor.checkCameraPermission()).thenAnswer((_) async => true); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( + cameraMethodChannel, + (methodCall) async { + switch (methodCall.method) { + case "availableCameras": + return availableCameras; + default: + return null; + } + }, + ); + }, + tearDown: () { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(cameraMethodChannel, null); + }, + build: () => bloc, + act: (bloc) => bloc.add(const InitializeEvent()), + verify: (_) { + verify(() => meteringInteractor.checkCameraPermission()).called(1); + }, + expect: () => [ + isA(), + isA().having((state) => state.error, "error", CameraErrorType.other), + ], + ); + + blocTest( + 'Successful initialization', + setUp: () { + when(() => meteringInteractor.checkCameraPermission()).thenAnswer((_) async => true); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( + cameraMethodChannel, + (methodCall) async { + switch (methodCall.method) { + case "availableCameras": + return availableCameras; + case "create": + return {"cameraId": 1}; + case "initialize": + await cameraIdMethodChannel.invokeMockMethod("initialized", { + 'cameraId': 1, + 'previewWidth': 2160.0, + 'previewHeight': 3840.0, + 'exposureMode': 'auto', + 'exposurePointSupported': true, + 'focusMode': 'auto', + 'focusPointSupported': true, + }); + return {}; + case "setFlashMode": + return ''; + // TODO: implement responses for other methods used initialization + default: + return null; + } + }, + ); + }, + tearDown: () { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(cameraMethodChannel, null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(cameraIdMethodChannel, null); + }, + build: () => bloc, + act: (bloc) => bloc.add(const InitializeEvent()), + verify: (_) { + verify(() => meteringInteractor.checkCameraPermission()).called(1); + }, + expect: () => [ + isA(), + isA(), + isA(), + ], + ); + }, + ); +} + +extension _MethodChannelMock on MethodChannel { + Future invokeMockMethod(String method, dynamic arguments) async { + final data = const StandardMethodCodec().encodeMethodCall(MethodCall(method, arguments)); + await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.handlePlatformMessage( + name, + data, + (ByteData? data) {}, + ); + } +} diff --git a/test/screens/metering/components/light_sensor/bloc_container_light_sensor_test.dart b/test/screens/metering/components/light_sensor/bloc_container_light_sensor_test.dart index 8e0a953..1cf1cc8 100644 --- a/test/screens/metering/components/light_sensor/bloc_container_light_sensor_test.dart +++ b/test/screens/metering/components/light_sensor/bloc_container_light_sensor_test.dart @@ -18,13 +18,13 @@ class _MockMeteringCommunicationBloc extends MockBloc< class _MockMeteringInteractor extends Mock implements MeteringInteractor {} void main() { - late _MockMeteringCommunicationBloc communicationBloc; late _MockMeteringInteractor meteringInteractor; + late _MockMeteringCommunicationBloc communicationBloc; late LightSensorContainerBloc bloc; setUpAll(() { - communicationBloc = _MockMeteringCommunicationBloc(); meteringInteractor = _MockMeteringInteractor(); + communicationBloc = _MockMeteringCommunicationBloc(); }); setUp(() {