| Add a Mandelbrot example app, and a V kernel worth demonstrating 81540bb nandithebull 21h ago | 1 | // Widget-level checks on the explorer. These need the native library on the |
| 2 | // loader path: |
| 3 | // |
| 4 | // cd example && LD_LIBRARY_PATH=../build flutter test |
| 5 | import 'package:flutter/material.dart'; |
| 6 | import 'package:flutter_test/flutter_test.dart'; |
| 7 | import 'package:vflutter_ffi_example/main.dart'; |
| 8 | |
| 9 | /// pumpAndSettle cannot be used here: the in-flight-render spinner animates |
| 10 | /// indefinitely, and rendering runs on real isolates outside the fake clock. |
| 11 | Future<void> settle(WidgetTester tester) async { |
| 12 | for (var i = 0; i < 20; i++) { |
| 13 | await tester.pump(const Duration(milliseconds: 50)); |
| 14 | } |
| 15 | } |
| 16 | |
| 17 | void main() { |
| 18 | /// The side panel only exists in the wide layout, so size the surface like a |
| 19 | /// desktop window rather than the 800x600 test default. |
| 20 | Future<void> pumpWide(WidgetTester tester) async { |
| 21 | tester.view.physicalSize = const Size(1400, 900); |
| 22 | tester.view.devicePixelRatio = 1.0; |
| 23 | addTearDown(tester.view.reset); |
| 24 | await tester.pumpWidget(const ExampleApp()); |
| 25 | await tester.pump(const Duration(milliseconds: 100)); |
| 26 | } |
| 27 | |
| 28 | testWidgets('starts at the documented defaults, not the slider maxima', |
| 29 | (tester) async { |
| 30 | await pumpWide(tester); |
| 31 | |
| 32 | final sliders = tester.widgetList<Slider>(find.byType(Slider)).toList(); |
| 33 | expect(sliders, hasLength(2)); |
| 34 | expect(sliders[0].value, 400, reason: 'iterations should default to 400'); |
| Raise the isolate ceiling to 32, and add the sweep that justifies it 59a731a nandithebull 21h ago | 35 | expect(sliders[1].value, 16, reason: 'isolates should default to 16'); |
| Add a Mandelbrot example app, and a V kernel worth demonstrating 81540bb nandithebull 21h ago | 36 | }); |
| 37 | |
| 38 | testWidgets('reset restores the framing without touching iterations', |
| 39 | (tester) async { |
| 40 | await pumpWide(tester); |
| 41 | |
| 42 | // Move the iteration slider off its default, then reset the view. |
| 43 | await tester.drag(find.byType(Slider).first, const Offset(200, 0)); |
| 44 | await settle(tester); |
| 45 | final changed = |
| 46 | tester.widgetList<Slider>(find.byType(Slider)).first.value; |
| 47 | expect(changed, greaterThan(400), |
| 48 | reason: 'the drag should have raised the iteration count'); |
| 49 | |
| 50 | await tester.tap(find.text('Reset view')); |
| 51 | await settle(tester); |
| 52 | |
| 53 | // Reset owns pan/zoom only: the iteration setting must survive it. |
| 54 | expect(tester.widgetList<Slider>(find.byType(Slider)).first.value, changed); |
| 55 | expect(find.textContaining('zoom 1.0x'), findsOneWidget); |
| 56 | }); |
| 57 | |
| Add drag-to-pan 0ed90f3 nandithebull 21h ago | 58 | testWidgets('dragging pans without also zooming', (tester) async { |
| 59 | await pumpWide(tester); |
| 60 | String zoomLabel() => (tester |
| 61 | .widget<Text>(find.textContaining('zoom ').first) |
| 62 | .data)!; |
| 63 | final before = zoomLabel(); |
| 64 | |
| 65 | // Guards the gesture arena's behaviour: a pan must claim the pointer so |
| 66 | // the tap recognizer never fires. (This passes with the zoom on either |
| 67 | // onTapDown or onTapUp — the arena, not the callback choice, is what |
| 68 | // separates them.) |
| 69 | await tester.drag(find.byKey(canvasKey), const Offset(-120, -60)); |
| 70 | await settle(tester); |
| 71 | |
| 72 | expect(zoomLabel(), before, reason: 'a drag must not change the zoom'); |
| 73 | }); |
| 74 | |
| 75 | testWidgets('a tap still zooms', (tester) async { |
| 76 | await pumpWide(tester); |
| 77 | String zoomLabel() => (tester |
| 78 | .widget<Text>(find.textContaining('zoom ').first) |
| 79 | .data)!; |
| 80 | final before = zoomLabel(); |
| 81 | |
| 82 | await tester.tap(find.byKey(canvasKey)); |
| 83 | await settle(tester); |
| 84 | |
| 85 | expect(zoomLabel(), isNot(before)); |
| 86 | }); |
| 87 | |
| Add a Mandelbrot example app, and a V kernel worth demonstrating 81540bb nandithebull 21h ago | 88 | testWidgets('narrow layout lays out without overflowing', (tester) async { |
| 89 | // Regression guard: the controls used to be a ListView nested inside the |
| 90 | // page's ListView, which threw "Vertical viewport was given unbounded |
| 91 | // height" and broke the whole mobile layout. |
| 92 | tester.view.physicalSize = const Size(420, 900); |
| 93 | tester.view.devicePixelRatio = 1.0; |
| 94 | addTearDown(tester.view.reset); |
| 95 | |
| 96 | await tester.pumpWidget(const ExampleApp()); |
| 97 | await tester.pump(const Duration(milliseconds: 100)); |
| 98 | |
| 99 | expect(tester.takeException(), isNull); |
| 100 | }); |
| 101 | } |