259 lines
7.8 KiB
Dart
259 lines
7.8 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:forui/forui.dart';
|
|
|
|
import 'package:pj_supermarket_cloud/common/models/plugs/face_discern_models.dart';
|
|
import 'package:pj_supermarket_cloud/common/models/plugs/face_recognition_session_models.dart';
|
|
import 'package:pj_supermarket_cloud/common/services/face_recognition_session_service.dart';
|
|
import 'package:pj_supermarket_cloud/common/widgets/face/face_recognition_panel.dart';
|
|
|
|
const _sessionId = 'panel-test-session';
|
|
const _faceChannel = MethodChannel('local/face_discern');
|
|
|
|
class FakeFaceRecognitionSessionService extends FaceRecognitionSessionService {
|
|
FakeFaceRecognitionSessionService({this.finishCompletion});
|
|
|
|
final Completer<void>? finishCompletion;
|
|
final result = Completer<FaceStream>();
|
|
bool _ownsSession = true;
|
|
int finishCalls = 0;
|
|
int cancelCalls = 0;
|
|
|
|
FaceRecognitionSessionHandle get handle => FaceRecognitionSessionHandle(
|
|
sessionId: _sessionId,
|
|
scene: FaceRecognitionScene.cashier,
|
|
events: const Stream<FaceRecognitionSessionEvent>.empty(),
|
|
result: result.future,
|
|
);
|
|
|
|
@override
|
|
bool owns(String? sessionId) => _ownsSession && sessionId == _sessionId;
|
|
|
|
@override
|
|
Future<void> finish() async {
|
|
finishCalls++;
|
|
await finishCompletion?.future;
|
|
_ownsSession = false;
|
|
}
|
|
|
|
@override
|
|
Future<void> cancel({String message = '已取消人脸识别'}) async {
|
|
cancelCalls++;
|
|
_ownsSession = false;
|
|
}
|
|
}
|
|
|
|
Widget _harness({
|
|
required FakeFaceRecognitionSessionService service,
|
|
required ValueChanged<FaceStream> onRecognized,
|
|
FaceRecognitionPanelController? controller,
|
|
VoidCallback? onCancelled,
|
|
}) {
|
|
final base = FThemes.zinc.light;
|
|
return ScreenUtilInit(
|
|
designSize: const Size(1920, 1080),
|
|
minTextAdapt: true,
|
|
builder: (_, __) => FTheme(
|
|
data: FThemeData.inherit(
|
|
colorScheme: base.colorScheme,
|
|
typography: base.typography,
|
|
style: base.style,
|
|
),
|
|
child: MaterialApp(
|
|
home: Scaffold(
|
|
body: Center(
|
|
child: SizedBox(
|
|
width: 600,
|
|
child: FaceRecognitionPanel(
|
|
service: service,
|
|
handle: service.handle,
|
|
controller: controller,
|
|
onRecognized: onRecognized,
|
|
onCancelled: onCancelled,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
FaceStream _recognitionResult() => FaceStream(
|
|
similar: 0.96,
|
|
cost: 42,
|
|
sampleId: 123,
|
|
sessionId: _sessionId,
|
|
);
|
|
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
setUp(() {
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(SystemChannels.platform, (_) async => null);
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(_faceChannel, (call) async {
|
|
switch (call.method) {
|
|
case 'createPreviewTexture':
|
|
return {
|
|
'textureId': 7,
|
|
'width': 1280,
|
|
'height': 720,
|
|
'rotation': 0,
|
|
'mirror': false,
|
|
};
|
|
case 'releasePreviewTexture':
|
|
return null;
|
|
default:
|
|
throw PlatformException(code: 'UNEXPECTED_METHOD');
|
|
}
|
|
});
|
|
});
|
|
|
|
tearDown(() {
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(SystemChannels.platform, null);
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(_faceChannel, null);
|
|
});
|
|
|
|
testWidgets('成功结果无需推进一秒即可结束会话并通知业务', (tester) async {
|
|
final service = FakeFaceRecognitionSessionService();
|
|
final recognized = <FaceStream>[];
|
|
await tester.pumpWidget(
|
|
_harness(service: service, onRecognized: recognized.add),
|
|
);
|
|
await tester.pump();
|
|
|
|
final beforeResult = tester.binding.clock.now();
|
|
final result = _recognitionResult();
|
|
service.result.complete(result);
|
|
await tester.pump();
|
|
|
|
expect(tester.binding.clock.now(), beforeResult);
|
|
expect(service.finishCalls, 1);
|
|
expect(recognized, [same(result)]);
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
|
|
testWidgets('会话结束尚未完成时不提前通知业务', (tester) async {
|
|
final finishCompletion = Completer<void>();
|
|
final service = FakeFaceRecognitionSessionService(
|
|
finishCompletion: finishCompletion,
|
|
);
|
|
final recognized = <FaceStream>[];
|
|
await tester.pumpWidget(
|
|
_harness(service: service, onRecognized: recognized.add),
|
|
);
|
|
await tester.pump();
|
|
|
|
final result = _recognitionResult();
|
|
service.result.complete(result);
|
|
await tester.pump();
|
|
expect(service.finishCalls, 1);
|
|
expect(recognized, isEmpty);
|
|
|
|
finishCompletion.complete();
|
|
await tester.pump();
|
|
expect(recognized, [same(result)]);
|
|
await tester.pump();
|
|
expect(recognized, hasLength(1));
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
|
|
testWidgets('结束会话期间销毁面板会忽略迟到成功结果', (tester) async {
|
|
final finishCompletion = Completer<void>();
|
|
final service = FakeFaceRecognitionSessionService(
|
|
finishCompletion: finishCompletion,
|
|
);
|
|
final recognized = <FaceStream>[];
|
|
await tester.pumpWidget(
|
|
_harness(service: service, onRecognized: recognized.add),
|
|
);
|
|
await tester.pump();
|
|
|
|
service.result.complete(_recognitionResult());
|
|
await tester.pump();
|
|
expect(service.finishCalls, 1);
|
|
await tester.pumpWidget(const SizedBox.shrink());
|
|
finishCompletion.complete();
|
|
await tester.pump();
|
|
|
|
expect(recognized, isEmpty);
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
|
|
testWidgets('成功结果到达前取消面板会忽略迟到结果', (tester) async {
|
|
final service = FakeFaceRecognitionSessionService();
|
|
final controller = FaceRecognitionPanelController();
|
|
final recognized = <FaceStream>[];
|
|
var cancelled = 0;
|
|
await tester.pumpWidget(
|
|
_harness(
|
|
service: service,
|
|
onRecognized: recognized.add,
|
|
controller: controller,
|
|
onCancelled: () => cancelled++,
|
|
),
|
|
);
|
|
await tester.pump();
|
|
|
|
final cancel = controller.cancel();
|
|
await tester.pump();
|
|
await cancel;
|
|
expect(service.cancelCalls, 1);
|
|
expect(cancelled, 1);
|
|
|
|
service.result.complete(_recognitionResult());
|
|
await tester.pump();
|
|
expect(service.finishCalls, 0);
|
|
expect(recognized, isEmpty);
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
|
|
testWidgets('成功结果锁定后重复取消不会打断结束会话或重复通知业务', (tester) async {
|
|
final finishCompletion = Completer<void>();
|
|
final service = FakeFaceRecognitionSessionService(
|
|
finishCompletion: finishCompletion,
|
|
);
|
|
final controller = FaceRecognitionPanelController();
|
|
final recognized = <FaceStream>[];
|
|
var cancelled = 0;
|
|
await tester.pumpWidget(
|
|
_harness(
|
|
service: service,
|
|
onRecognized: recognized.add,
|
|
controller: controller,
|
|
onCancelled: () => cancelled++,
|
|
),
|
|
);
|
|
await tester.pump();
|
|
|
|
service.result.complete(_recognitionResult());
|
|
await tester.pump();
|
|
expect(service.finishCalls, 1);
|
|
await controller.cancel();
|
|
await controller.cancel();
|
|
await tester.pump();
|
|
expect(service.cancelCalls, 0);
|
|
expect(cancelled, 0);
|
|
expect(recognized, isEmpty);
|
|
|
|
finishCompletion.complete();
|
|
await tester.pump();
|
|
expect(recognized, hasLength(1));
|
|
await controller.cancel();
|
|
await tester.pump();
|
|
expect(recognized, hasLength(1));
|
|
expect(service.finishCalls, 1);
|
|
expect(service.cancelCalls, 0);
|
|
expect(cancelled, 0);
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
}
|