From 1982bb3abd8b9cce7ae74b43f24755fc0de1ce9f Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 14 Sep 2026 08:36:46 +0800 Subject: [PATCH] test: reuse cursor fixtures for Web and alpha coverage --- flutter/test/cursor_native_alpha_test.dart | 47 ++----------- flutter/test/cursor_web_test.dart | 78 +++------------------- 2 files changed, 17 insertions(+), 108 deletions(-) diff --git a/flutter/test/cursor_native_alpha_test.dart b/flutter/test/cursor_native_alpha_test.dart index 35c2a1e56..79d1cd96c 100644 --- a/flutter/test/cursor_native_alpha_test.dart +++ b/flutter/test/cursor_native_alpha_test.dart @@ -9,6 +9,8 @@ import 'package:flutter_hbb/native/custom_cursor.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:image/image.dart' as img; +import 'cursor_test_utils.dart'; + const _side = 32; const _hotspot = Offset(7, 9); const _colors = [ @@ -29,41 +31,12 @@ class _Canvas extends Fake implements CanvasModel { displayHeight: 160); } -class _Peer extends Fake implements FfiModel { - _Peer(String platform) : pi = (PeerInfo()..platform = platform); - @override - final PeerInfo pi; -} - -class _FFI extends Fake implements FFI { - _FFI(String platform) : ffiModel = _Peer(platform); - @override - final canvasModel = _Canvas(); - @override - final _Peer ffiModel; -} - void main() { final binding = TestWidgetsFlutterBinding.ensureInitialized(); final view = binding.platformDispatcher.views.single; - final channel = Platform.isWindows - ? SystemChannels.mouseCursor - : const MethodChannel('flutter_custom_cursor'); final registrations = >[]; - setUp(() { - registrations.clear(); - binding.defaultBinaryMessenger.setMockMethodCallHandler(channel, - (call) async { - if (!call.method.startsWith('createCustomCursor')) return null; - final args = call.arguments as Map; - registrations.add(args); - return args['name']; - }); - }); - tearDown(() { - view.resetDevicePixelRatio(); - binding.defaultBinaryMessenger.setMockMethodCallHandler(channel, null); - }); + captureNativeCursors(registrations); + tearDown(view.resetDevicePixelRatio); for (final testCase in <(String, String?, List)>[ (kPeerPlatformMacOS, null, [64, 32, 16, 128]), (kPeerPlatformMacOS, '0', [64, 32, 16, 128]), @@ -87,7 +60,7 @@ void main() { Future _checkCursor((String, String?, List) testCase, double dpr, List> registrations) async { final (platform, density, color) = testCase; - final ffi = _FFI(platform); + final ffi = CursorTestFFI(_Canvas())..ffiModel.pi.platform = platform; final cursor = CursorModel(WeakReference(ffi))..id = '$testCase-$dpr'; addTearDown(() { cursor.disposeImages(); @@ -126,15 +99,7 @@ Future _checkCursor((String, String?, List) testCase, double dpr, } void _checkRegistration(Map args, double dpr) { - final bytes = args['buffer'] as Uint8List; - final decoded = Platform.isWindows - ? img.Image.fromBytes( - width: args['width'] as int, - height: args['height'] as int, - bytes: bytes.buffer, - bytesOffset: bytes.offsetInBytes, - order: img.ChannelOrder.bgra) - : img.decodePng(bytes)!; + final decoded = decodeNativeCursorRaster(args); expect((decoded.width, decoded.height), (_side * dpr, _side * dpr)); expect((args['hotX'], args['hotY']), (_hotspot.dx * dpr, _hotspot.dy * dpr)); _checkColors(decoded); diff --git a/flutter/test/cursor_web_test.dart b/flutter/test/cursor_web_test.dart index 84e3320e7..654ace6c2 100644 --- a/flutter/test/cursor_web_test.dart +++ b/flutter/test/cursor_web_test.dart @@ -11,7 +11,6 @@ import 'dart:ui' as ui; import 'package:flutter/widgets.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/pages/remote_page.dart'; -import 'package:flutter_hbb/models/input_model.dart'; import 'package:flutter_hbb/models/model.dart' as model; import 'package:flutter_hbb/web/custom_cursor.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -19,6 +18,8 @@ import 'package:get/get.dart'; import 'package:image/image.dart' as img; import 'package:provider/provider.dart'; +import 'cursor_test_utils.dart'; + void main() { TestWidgetsFlutterBinding.ensureInitialized(); _alphaTests(); @@ -38,9 +39,9 @@ void main() { } test('Web cursor aligns CSS hotspots with rounded PNG dimensions', () async { final registered = _captureCursor(); - final canvas = _Canvas(kRemoteViewStyleAdaptive); + final canvas = CursorTestCanvas(1, style: kRemoteViewStyleAdaptive, scale: 0.5); addTearDown(canvas.dispose); - final ffi = _FFI(canvas); + final ffi = CursorTestFFI(canvas); for (final (hotspot, scale, side, expected) in [ ((7.0, 7.0), 633 / 1600, 19, (3, 3)), ((21.0, 23.0), 633 / 1600, 19, (8, 9)), @@ -75,9 +76,9 @@ void _thinCursorTests() { } test('Web thin cursors keep a raster pixel and an in-bounds hotspot', () async { final registered = _captureCursor(); - final canvas = _Canvas(kRemoteViewStyleAdaptive); + final canvas = CursorTestCanvas(1, style: kRemoteViewStyleAdaptive, scale: 0.5); addTearDown(canvas.dispose); - final ffi = _FFI(canvas); + final ffi = CursorTestFFI(canvas); for (final (source, hotspot, scale, size, expected) in [ ((4, 64), (2.0, 32.0), 0.5, (2, 32), (1, 16)), ((2, 128), (1.0, 64.0), 0.01, (1, 12), (0, 6)), @@ -99,63 +100,6 @@ void _thinCursorTests() { }); } -class _Image extends ChangeNotifier implements model.ImageModel { - @override - bool get useTextureRender => false; - @override - ui.Image? get image => null; - @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - -class _Canvas extends ChangeNotifier implements model.CanvasModel { - _Canvas(String style) - : viewStyle = model.ViewStyle( - style: style, - width: 200, - height: 160, - displayWidth: 400, - displayHeight: 320); - @override - final model.ViewStyle viewStyle; - @override - final imageOverflow = false.obs; - @override - bool get cursorEmbedded => false; - @override - Size get size => const Size(200, 160); - @override - double get scale => 0.5; - @override - double get x => 0; - @override - double get y => 0; - @override - dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - -class _Input extends Fake implements InputModel { - @override - final relativeMouseMode = false.obs; -} - -class _Peer extends Fake implements model.FfiModel { - @override - final pi = model.PeerInfo(); - @override - bool get isPeerLinux => false; -} - -class _FFI extends Fake implements model.FFI { - _FFI(this.canvasModel); - @override - final model.CanvasModel canvasModel; - @override - final inputModel = _Input(); - @override - final ffiModel = _Peer(); -} - Future _loadCursor(model.FFI ffi, String id, {List pixel = const [255, 255, 255, 255], (int, int) source = (48, 48), @@ -212,9 +156,9 @@ Future _checkPolicy(WidgetTester tester, String style, double dpr, (int, int) expectedHotspot = (7, 9), double? pixelRatio = 1, String platform = kPeerPlatformMacOS}) async { final registered = _captureCursor(); - final canvas = _Canvas(style); + final canvas = CursorTestCanvas(1, style: style, scale: 0.5); addTearDown(canvas.dispose); - final ffi = _FFI(canvas); + final ffi = CursorTestFFI(canvas); ffi.ffiModel.pi.platform = platform; // Retina export changes the bitmap too; varying metadata alone misses this boundary. final density = platform == kPeerPlatformMacOS && pixelRatio == 2 ? 2 : 1; @@ -225,7 +169,7 @@ Future _checkPolicy(WidgetTester tester, String style, double dpr, data: MediaQueryData(devicePixelRatio: dpr), child: MultiProvider( providers: [ - ChangeNotifierProvider(create: (_) => _Image()), + ChangeNotifierProvider(create: (_) => CursorTestImage()), ChangeNotifierProvider.value(value: canvas), ChangeNotifierProvider.value(value: cursor), ], @@ -268,9 +212,9 @@ void _alphaTests() { Future _checkAlpha(double? density, List pixel) async { final registered = _captureCursor(); - final canvas = _Canvas(kRemoteViewStyleAdaptive); + final canvas = CursorTestCanvas(1, style: kRemoteViewStyleAdaptive, scale: 0.5); addTearDown(canvas.dispose); - final ffi = _FFI(canvas)..ffiModel.pi.platform = kPeerPlatformMacOS; + final ffi = CursorTestFFI(canvas)..ffiModel.pi.platform = kPeerPlatformMacOS; final cursor = await _loadCursor(ffi, 'alpha-$density-$pixel', pixel: pixel, pixelRatio: density); // Cover both the painted remote cursor and the initial CSS cursor PNG.