mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-24 00:10:47 +00:00
Compare commits
4 commits
f9b18330cd
...
2a7463184b
Author | SHA1 | Date | |
---|---|---|---|
|
2a7463184b | ||
|
dca34f6721 | ||
|
e230a05b5f | ||
|
35af009f99 |
7 changed files with 207 additions and 39 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -58,3 +58,5 @@ android/app/google-services.json
|
||||||
ios/firebase_app_id_file.json
|
ios/firebase_app_id_file.json
|
||||||
ios/Runner/GoogleService-Info.plist
|
ios/Runner/GoogleService-Info.plist
|
||||||
lib/firebase_options.dart
|
lib/firebase_options.dart
|
||||||
|
|
||||||
|
coverage/
|
|
@ -90,7 +90,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
iso = event.equipmentProfileData.isoValues.first;
|
iso = event.equipmentProfileData.isoValues.first;
|
||||||
_meteringInteractor.film = Film.values.first;
|
_meteringInteractor.film = Film.values.first;
|
||||||
film = Film.values.first;
|
film = Film.values.first;
|
||||||
willUpdateMeasurements &= true;
|
willUpdateMeasurements = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The same for ND filter
|
/// The same for ND filter
|
||||||
|
@ -98,7 +98,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
|
||||||
if (!event.equipmentProfileData.ndValues.any((v) => state.nd.value == v.value)) {
|
if (!event.equipmentProfileData.ndValues.any((v) => state.nd.value == v.value)) {
|
||||||
_meteringInteractor.ndFilter = event.equipmentProfileData.ndValues.first;
|
_meteringInteractor.ndFilter = event.equipmentProfileData.ndValues.first;
|
||||||
nd = event.equipmentProfileData.ndValues.first;
|
nd = event.equipmentProfileData.ndValues.first;
|
||||||
willUpdateMeasurements &= true;
|
willUpdateMeasurements = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (willUpdateMeasurements) {
|
if (willUpdateMeasurements) {
|
||||||
|
|
|
@ -50,7 +50,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`MeasureEvent` tests',
|
'`MeasureEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<MeteringBloc, MeteringState>(
|
blocTest<MeteringBloc, MeteringState>(
|
||||||
'`MeasureEvent` -> success',
|
'`MeasureEvent` -> success',
|
||||||
|
@ -144,7 +144,7 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`IsoChangedEvent` tests',
|
'`IsoChangedEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<MeteringBloc, MeteringState>(
|
blocTest<MeteringBloc, MeteringState>(
|
||||||
'Pick different ISO (ev100 != null)',
|
'Pick different ISO (ev100 != null)',
|
||||||
|
@ -263,7 +263,7 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`NdChangedEvent` tests',
|
'`NdChangedEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<MeteringBloc, MeteringState>(
|
blocTest<MeteringBloc, MeteringState>(
|
||||||
'Pick different ND (ev100 != null)',
|
'Pick different ND (ev100 != null)',
|
||||||
|
@ -378,7 +378,7 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`FilmChangedEvent` tests',
|
'`FilmChangedEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<MeteringBloc, MeteringState>(
|
blocTest<MeteringBloc, MeteringState>(
|
||||||
'Pick different film with different ISO',
|
'Pick different film with different ISO',
|
||||||
|
@ -485,11 +485,126 @@ void main() {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO(vodemn): when this feautre is enabled
|
group(
|
||||||
// group(
|
'`EquipmentProfileChangedEvent`',
|
||||||
// '`EquipmentProfileChangedEvent` tests',
|
() {
|
||||||
// () {
|
final reducedProfile = EquipmentProfileData(
|
||||||
//
|
id: '0',
|
||||||
// },
|
name: 'Reduced',
|
||||||
// );
|
apertureValues: ApertureValue.values,
|
||||||
|
ndValues: NdValue.values.getRange(0, 3).toList(),
|
||||||
|
shutterSpeedValues: ShutterSpeedValue.values,
|
||||||
|
isoValues: IsoValue.values.getRange(4, 23).toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<MeteringBloc, MeteringState>(
|
||||||
|
'New profile has current ISO & ND',
|
||||||
|
build: () => bloc,
|
||||||
|
seed: () => MeteringDataState(
|
||||||
|
ev100: 1.0,
|
||||||
|
film: Film.values[1],
|
||||||
|
iso: const IsoValue(100, StopType.full),
|
||||||
|
nd: NdValue.values.first,
|
||||||
|
isMetering: false,
|
||||||
|
),
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(EquipmentProfileChangedEvent(reducedProfile));
|
||||||
|
},
|
||||||
|
verify: (_) {
|
||||||
|
verifyNever(() => meteringInteractor.film = const Film.other());
|
||||||
|
verifyNever(() => meteringInteractor.iso = reducedProfile.isoValues.first);
|
||||||
|
verifyNever(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first);
|
||||||
|
verifyNever(() => meteringInteractor.responseVibration());
|
||||||
|
},
|
||||||
|
expect: () => [],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<MeteringBloc, MeteringState>(
|
||||||
|
'New profile has new ISO & current ND',
|
||||||
|
build: () => bloc,
|
||||||
|
seed: () => MeteringDataState(
|
||||||
|
ev100: 1.0,
|
||||||
|
film: Film.values[1],
|
||||||
|
iso: IsoValue.values[2],
|
||||||
|
nd: NdValue.values.first,
|
||||||
|
isMetering: false,
|
||||||
|
),
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(EquipmentProfileChangedEvent(reducedProfile));
|
||||||
|
},
|
||||||
|
verify: (_) {
|
||||||
|
verify(() => meteringInteractor.film = const Film.other()).called(1);
|
||||||
|
verify(() => meteringInteractor.iso = reducedProfile.isoValues.first).called(1);
|
||||||
|
verifyNever(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first);
|
||||||
|
verify(() => meteringInteractor.responseVibration()).called(1);
|
||||||
|
},
|
||||||
|
expect: () => [
|
||||||
|
isA<MeteringDataState>()
|
||||||
|
.having((state) => state.ev100, 'ev100', 1.0)
|
||||||
|
.having((state) => state.film, 'film', const Film.other())
|
||||||
|
.having((state) => state.iso, 'iso', reducedProfile.isoValues.first)
|
||||||
|
.having((state) => state.nd, 'nd', NdValue.values.first)
|
||||||
|
.having((state) => state.isMetering, 'isMetering', false),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<MeteringBloc, MeteringState>(
|
||||||
|
'New profile has current ISO & new ND',
|
||||||
|
build: () => bloc,
|
||||||
|
seed: () => MeteringDataState(
|
||||||
|
ev100: 1.0,
|
||||||
|
film: Film.values[1],
|
||||||
|
iso: const IsoValue(100, StopType.full),
|
||||||
|
nd: NdValue.values[4],
|
||||||
|
isMetering: false,
|
||||||
|
),
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(EquipmentProfileChangedEvent(reducedProfile));
|
||||||
|
},
|
||||||
|
verify: (_) {
|
||||||
|
verifyNever(() => meteringInteractor.film = const Film.other());
|
||||||
|
verifyNever(() => meteringInteractor.iso = reducedProfile.isoValues.first);
|
||||||
|
verify(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first).called(1);
|
||||||
|
verify(() => meteringInteractor.responseVibration()).called(1);
|
||||||
|
},
|
||||||
|
expect: () => [
|
||||||
|
isA<MeteringDataState>()
|
||||||
|
.having((state) => state.ev100, 'ev100', 1.0)
|
||||||
|
.having((state) => state.film, 'film', Film.values[1])
|
||||||
|
.having((state) => state.iso, 'iso', const IsoValue(100, StopType.full))
|
||||||
|
.having((state) => state.nd, 'nd', reducedProfile.ndValues.first)
|
||||||
|
.having((state) => state.isMetering, 'isMetering', false),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<MeteringBloc, MeteringState>(
|
||||||
|
'New profile has new ISO & new ND',
|
||||||
|
build: () => bloc,
|
||||||
|
seed: () => MeteringDataState(
|
||||||
|
ev100: 1.0,
|
||||||
|
film: Film.values[1],
|
||||||
|
iso: IsoValue.values[2],
|
||||||
|
nd: NdValue.values[4],
|
||||||
|
isMetering: false,
|
||||||
|
),
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(EquipmentProfileChangedEvent(reducedProfile));
|
||||||
|
},
|
||||||
|
verify: (_) {
|
||||||
|
verify(() => meteringInteractor.film = const Film.other()).called(1);
|
||||||
|
verify(() => meteringInteractor.iso = reducedProfile.isoValues.first).called(1);
|
||||||
|
verify(() => meteringInteractor.ndFilter = reducedProfile.ndValues.first).called(1);
|
||||||
|
verify(() => meteringInteractor.responseVibration()).called(1);
|
||||||
|
},
|
||||||
|
expect: () => [
|
||||||
|
isA<MeteringDataState>()
|
||||||
|
.having((state) => state.ev100, 'ev100', 1.0)
|
||||||
|
.having((state) => state.film, 'film', const Film.other())
|
||||||
|
.having((state) => state.iso, 'iso', reducedProfile.isoValues.first)
|
||||||
|
.having((state) => state.nd, 'nd', reducedProfile.ndValues.first)
|
||||||
|
.having((state) => state.isMetering, 'isMetering', false),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`MeasureEvent` tests',
|
'`MeasureEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<MeteringCommunicationBloc, MeteringCommunicationState>(
|
blocTest<MeteringCommunicationBloc, MeteringCommunicationState>(
|
||||||
'Multiple consequtive measure events',
|
'Multiple consequtive measure events',
|
||||||
|
@ -60,7 +60,7 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`MeteringInProgressEvent` tests',
|
'`MeteringInProgressEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<MeteringCommunicationBloc, MeteringCommunicationState>(
|
blocTest<MeteringCommunicationBloc, MeteringCommunicationState>(
|
||||||
'Multiple consequtive in progress events',
|
'Multiple consequtive in progress events',
|
||||||
|
@ -83,7 +83,7 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`MeteringEndedEvent` tests',
|
'`MeteringEndedEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<MeteringCommunicationBloc, MeteringCommunicationState>(
|
blocTest<MeteringCommunicationBloc, MeteringCommunicationState>(
|
||||||
'Multiple consequtive ended events',
|
'Multiple consequtive ended events',
|
||||||
|
|
|
@ -41,10 +41,25 @@ void main() {
|
||||||
"sensorOrientation": 0,
|
"sensorOrientation": 0,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
Future<Object?>? cameraMethodCallSuccessHandler(MethodCall methodCall) async {
|
const frontCameras = [
|
||||||
|
{
|
||||||
|
"name": "front",
|
||||||
|
"lensFacing": "front",
|
||||||
|
"sensorOrientation": 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "front2",
|
||||||
|
"lensFacing": "front",
|
||||||
|
"sensorOrientation": 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
Future<Object?>? cameraMethodCallSuccessHandler(
|
||||||
|
MethodCall methodCall, {
|
||||||
|
List<Map<String, Object>> cameras = availableCameras,
|
||||||
|
}) async {
|
||||||
switch (methodCall.method) {
|
switch (methodCall.method) {
|
||||||
case "availableCameras":
|
case "availableCameras":
|
||||||
return availableCameras;
|
return cameras;
|
||||||
case "create":
|
case "create":
|
||||||
return {"cameraId": 1};
|
return {"cameraId": 1};
|
||||||
case "initialize":
|
case "initialize":
|
||||||
|
@ -119,7 +134,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`RequestPermissionEvent` tests',
|
'`RequestPermissionEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<CameraContainerBloc, CameraContainerState>(
|
blocTest<CameraContainerBloc, CameraContainerState>(
|
||||||
'Request denied',
|
'Request denied',
|
||||||
|
@ -168,15 +183,33 @@ void main() {
|
||||||
verify(() => meteringInteractor.requestPermission()).called(1);
|
verify(() => meteringInteractor.requestPermission()).called(1);
|
||||||
verify(() => meteringInteractor.checkCameraPermission()).called(1);
|
verify(() => meteringInteractor.checkCameraPermission()).called(1);
|
||||||
},
|
},
|
||||||
expect: () => [
|
expect: () => initializedStateSequence,
|
||||||
...initializedStateSequence,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`InitializeEvent`/`DeinitializeEvent` tests',
|
'`OpenAppSettingsEvent`',
|
||||||
|
() {
|
||||||
|
blocTest<CameraContainerBloc, CameraContainerState>(
|
||||||
|
'App settings opened',
|
||||||
|
setUp: () {
|
||||||
|
when(() => meteringInteractor.openAppSettings()).thenAnswer((_) {});
|
||||||
|
},
|
||||||
|
build: () => bloc,
|
||||||
|
act: (bloc) async {
|
||||||
|
bloc.add(const OpenAppSettingsEvent());
|
||||||
|
},
|
||||||
|
verify: (_) {
|
||||||
|
verify(() => meteringInteractor.openAppSettings()).called(1);
|
||||||
|
},
|
||||||
|
expect: () => [],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
group(
|
||||||
|
'`InitializeEvent`/`DeinitializeEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<CameraContainerBloc, CameraContainerState>(
|
blocTest<CameraContainerBloc, CameraContainerState>(
|
||||||
'No cameras detected error',
|
'No cameras detected error',
|
||||||
|
@ -185,14 +218,7 @@ void main() {
|
||||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
.setMockMethodCallHandler(
|
.setMockMethodCallHandler(
|
||||||
cameraMethodChannel,
|
cameraMethodChannel,
|
||||||
(methodCall) async {
|
(methodCall) async => cameraMethodCallSuccessHandler(methodCall, cameras: const []),
|
||||||
switch (methodCall.method) {
|
|
||||||
case "availableCameras":
|
|
||||||
return const [];
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
tearDown: () {
|
tearDown: () {
|
||||||
|
@ -211,6 +237,28 @@ void main() {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
blocTest<CameraContainerBloc, CameraContainerState>(
|
||||||
|
'No back facing cameras available',
|
||||||
|
setUp: () {
|
||||||
|
when(() => meteringInteractor.checkCameraPermission()).thenAnswer((_) async => true);
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(
|
||||||
|
cameraMethodChannel,
|
||||||
|
(methodCall) async => cameraMethodCallSuccessHandler(methodCall, cameras: frontCameras),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
tearDown: () {
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||||
|
.setMockMethodCallHandler(cameraMethodChannel, null);
|
||||||
|
},
|
||||||
|
build: () => bloc,
|
||||||
|
act: (bloc) => bloc.add(const InitializeEvent()),
|
||||||
|
verify: (_) {
|
||||||
|
verify(() => meteringInteractor.checkCameraPermission()).called(1);
|
||||||
|
},
|
||||||
|
expect: () => initializedStateSequence,
|
||||||
|
);
|
||||||
|
|
||||||
blocTest<CameraContainerBloc, CameraContainerState>(
|
blocTest<CameraContainerBloc, CameraContainerState>(
|
||||||
'Catch other initialization errors',
|
'Catch other initialization errors',
|
||||||
setUp: () {
|
setUp: () {
|
||||||
|
@ -269,7 +317,7 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`_takePicture()` tests',
|
'`_takePicture()`',
|
||||||
() {
|
() {
|
||||||
blocTest<CameraContainerBloc, CameraContainerState>(
|
blocTest<CameraContainerBloc, CameraContainerState>(
|
||||||
'Returned ev100 == null',
|
'Returned ev100 == null',
|
||||||
|
@ -281,14 +329,15 @@ void main() {
|
||||||
bloc.add(const InitializeEvent());
|
bloc.add(const InitializeEvent());
|
||||||
await Future.delayed(Duration.zero);
|
await Future.delayed(Duration.zero);
|
||||||
bloc.onCommunicationState(const communication_states.MeasureState());
|
bloc.onCommunicationState(const communication_states.MeasureState());
|
||||||
|
bloc.onCommunicationState(const communication_states.MeasureState());
|
||||||
|
bloc.onCommunicationState(const communication_states.MeasureState());
|
||||||
|
bloc.onCommunicationState(const communication_states.MeasureState());
|
||||||
},
|
},
|
||||||
verify: (_) {
|
verify: (_) {
|
||||||
verify(() => meteringInteractor.checkCameraPermission()).called(1);
|
verify(() => meteringInteractor.checkCameraPermission()).called(1);
|
||||||
verifyNever(() => meteringInteractor.cameraEvCalibration);
|
verifyNever(() => meteringInteractor.cameraEvCalibration);
|
||||||
},
|
},
|
||||||
expect: () => [
|
expect: () => initializedStateSequence,
|
||||||
...initializedStateSequence,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO(vodemn): figure out how to mock `_file.readAsBytes()`
|
// TODO(vodemn): figure out how to mock `_file.readAsBytes()`
|
||||||
|
@ -321,11 +370,10 @@ void main() {
|
||||||
// ],
|
// ],
|
||||||
// );
|
// );
|
||||||
},
|
},
|
||||||
skip: true,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`ZoomChangedEvent` tests',
|
'`ZoomChangedEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<CameraContainerBloc, CameraContainerState>(
|
blocTest<CameraContainerBloc, CameraContainerState>(
|
||||||
'Set zoom multiple times',
|
'Set zoom multiple times',
|
||||||
|
@ -372,7 +420,7 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`ExposureOffsetChangedEvent`/`ExposureOffsetResetEvent` tests',
|
'`ExposureOffsetChangedEvent`/`ExposureOffsetResetEvent`',
|
||||||
() {
|
() {
|
||||||
blocTest<CameraContainerBloc, CameraContainerState>(
|
blocTest<CameraContainerBloc, CameraContainerState>(
|
||||||
'Set exposure offset multiple times and reset',
|
'Set exposure offset multiple times and reset',
|
||||||
|
|
|
@ -39,7 +39,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
group(
|
group(
|
||||||
'`LuxMeteringEvent` tests',
|
'`LuxMeteringEvent`',
|
||||||
() {
|
() {
|
||||||
const List<int> luxIterable = [1, 2, 2, 2, 3];
|
const List<int> luxIterable = [1, 2, 2, 2, 3];
|
||||||
final List<double> resultList = luxIterable.map((lux) => log2(lux / 2.5)).toList();
|
final List<double> resultList = luxIterable.map((lux) => log2(lux / 2.5)).toList();
|
||||||
|
|
3
test_coverage.sh
Normal file
3
test_coverage.sh
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
flutter test --coverage
|
||||||
|
genhtml coverage/lcov.info -o coverage/html
|
||||||
|
open coverage/html/index.html
|
Loading…
Reference in a new issue