import 'dart:async'; import 'package:ducafe_ui_core/ducafe_ui_core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:forui/forui.dart'; import 'package:pj_supermarket_cloud/common/index.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); late int createTextureCount; late int releaseTextureCount; setUp(() { createTextureCount = 0; releaseTextureCount = 0; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(FaceDiscernPlugs.getChannel, (call) async { if (call.method == 'createPreviewTexture') { createTextureCount++; return { 'textureId': 42, 'width': 1280, 'height': 720, 'rotation': 0, 'mirror': true, }; } if (call.method == 'releasePreviewTexture') { releaseTextureCount++; } return null; }); }); tearDown(() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(FaceDiscernPlugs.getChannel, null); }); testWidgets('面板销毁时取消事件监听、当前会话并释放预览纹理', (tester) async { final eventController = StreamController.broadcast(); addTearDown(eventController.close); final resultCompleter = Completer(); final service = _FakeFaceRecognitionSessionService('session-1'); final panelController = FaceRecognitionPanelController(); final handle = FaceRecognitionSessionHandle( sessionId: 'session-1', scene: FaceRecognitionScene.cashier, events: eventController.stream, result: resultCompleter.future, ); await tester.pumpWidget( _facePanelTestApp( FaceRecognitionPanel( service: service, handle: handle, controller: panelController, onRecognized: (_) {}, timeout: const Duration(minutes: 1), ), ), ); await tester.pump(); expect(createTextureCount, 1); expect(eventController.hasListener, isTrue); await tester.pumpWidget(_facePanelTestApp(const SizedBox.shrink())); await tester.pump(); expect(eventController.hasListener, isFalse); expect(service.cancelCount, 1); expect(releaseTextureCount, 1); await panelController.cancel(); expect(service.cancelCount, 1, reason: '销毁后的控制句柄不得继续操作旧会话'); expect(tester.takeException(), isNull); }); } /// 构建具备项目缩放和 ForUI 主题的人脸面板测试环境。 Widget _facePanelTestApp(Widget child) { return ScreenUtilInit( designSize: const Size(1920, 1080), builder: (_, __) => MaterialApp( home: FTheme( data: FThemes.zinc.light, child: Scaffold(body: Center(child: child)), ), ), ); } /// 测试专用人脸会话服务,只记录面板是否按所有权释放当前会话。 class _FakeFaceRecognitionSessionService extends FaceRecognitionSessionService { /// 创建持有指定会话的测试服务。 _FakeFaceRecognitionSessionService(this.sessionId); /// 当前测试服务拥有的会话 ID。 final String sessionId; /// 面板销毁触发的取消次数。 int cancelCount = 0; /// 当前测试会话是否仍归此服务所有。 bool active = true; /// 仅声明与测试句柄一致的会话归属。 @override bool owns(String? sessionId) => active && sessionId == this.sessionId; /// 记录销毁时的取消动作并释放测试会话所有权。 @override Future cancel({String message = '已取消人脸识别'}) async { cancelCount++; active = false; } /// 结束路径与取消路径都释放测试会话所有权。 @override Future finish() async { active = false; } }