platform_view_test.dart 6.75 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:ui' as ui;

7
import 'package:fake_async/fake_async.dart';
8 9
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
10
import 'package:flutter/rendering.dart';
11
import 'package:flutter/services.dart';
12 13 14 15 16 17 18
import 'package:flutter_test/flutter_test.dart';
import '../services/fake_platform_views.dart';
import 'rendering_tester.dart';

void main() {

  group('PlatformViewRenderBox', () {
19 20
    late FakePlatformViewController fakePlatformViewController;
    late PlatformViewRenderBox platformViewRenderBox;
21
    setUp(() {
22
      renderer; // Initialize bindings
23
      fakePlatformViewController = FakePlatformViewController(0);
24 25 26 27
      platformViewRenderBox = PlatformViewRenderBox(
        controller: fakePlatformViewController,
        hitTestBehavior: PlatformViewHitTestBehavior.opaque,
        gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
28 29 30 31 32 33 34
          Factory<VerticalDragGestureRecognizer>(
            () {
              return VerticalDragGestureRecognizer();
            },
          ),
        },
      );
35 36 37 38 39 40 41 42
    });

    test('layout should size to max constraint', () {
      layout(platformViewRenderBox);
      platformViewRenderBox.layout(const BoxConstraints(minWidth: 50, minHeight: 50, maxWidth: 100, maxHeight: 100));
      expect(platformViewRenderBox.size, const Size(100, 100));
    });

43
    test('send semantics update if id is changed', () {
44
      final RenderConstrainedBox tree = RenderConstrainedBox(
45 46 47 48 49 50 51
        additionalConstraints: const BoxConstraints.tightFor(height: 20.0, width: 20.0),
        child: platformViewRenderBox,
      );
      int semanticsUpdateCount = 0;
      final SemanticsHandle semanticsHandle = renderer.pipelineOwner.ensureSemantics(
          listener: () {
            ++semanticsUpdateCount;
52
          },
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
      );
      layout(tree, phase: EnginePhase.flushSemantics);
      // Initial semantics update
      expect(semanticsUpdateCount, 1);

      semanticsUpdateCount = 0;

      // Request semantics update even though nothing changed.
      platformViewRenderBox.markNeedsSemanticsUpdate();
      pumpFrame(phase: EnginePhase.flushSemantics);
      expect(semanticsUpdateCount, 0);

      semanticsUpdateCount = 0;

      final FakePlatformViewController updatedFakePlatformViewController = FakePlatformViewController(10);
      platformViewRenderBox.controller = updatedFakePlatformViewController;
      pumpFrame(phase: EnginePhase.flushSemantics);
      // Update id should update the semantics.
      expect(semanticsUpdateCount, 1);

      semanticsHandle.dispose();
    });
75

76
    test('mouse hover events are dispatched via PlatformViewController.dispatchPointerEvent', () {
77 78 79
      layout(platformViewRenderBox);
      pumpFrame(phase: EnginePhase.flushSemantics);

80
      ui.window.onPointerDataPacket!(ui.PointerDataPacket(data: <ui.PointerData>[
81
        _pointerData(ui.PointerChange.add, Offset.zero),
82 83 84 85 86 87 88 89
        _pointerData(ui.PointerChange.hover, const Offset(10, 10)),
        _pointerData(ui.PointerChange.remove, const Offset(10, 10)),
      ]));

      expect(fakePlatformViewController.dispatchedPointerEvents, isNotEmpty);
    });

    test('touch hover events are dispatched via PlatformViewController.dispatchPointerEvent', () {
90 91 92
      layout(platformViewRenderBox);
      pumpFrame(phase: EnginePhase.flushSemantics);

93
      ui.window.onPointerDataPacket!(ui.PointerDataPacket(data: <ui.PointerData>[
94
        _pointerData(ui.PointerChange.add, Offset.zero),
95 96 97
        _pointerData(ui.PointerChange.hover, const Offset(10, 10)),
        _pointerData(ui.PointerChange.remove, const Offset(10, 10)),
      ]));
98 99 100 101

      expect(fakePlatformViewController.dispatchedPointerEvents, isNotEmpty);
    });

102
  });
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

  // Regression test for https://github.com/flutter/flutter/issues/69431
  test('multi-finger touch test', () {
    renderer; // Initialize bindings.
    final FakeAndroidPlatformViewsController viewsController = FakeAndroidPlatformViewsController();
    viewsController.registerViewType('webview');
    final AndroidViewController viewController =
      PlatformViewsService.initAndroidView(id: 0, viewType: 'webview', layoutDirection: TextDirection.rtl);
    final PlatformViewRenderBox platformViewRenderBox = PlatformViewRenderBox(
      controller: viewController,
      hitTestBehavior: PlatformViewHitTestBehavior.opaque,
      gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
        Factory<VerticalDragGestureRecognizer>(
          () => VerticalDragGestureRecognizer(),
        ),
      },
    );
    layout(platformViewRenderBox);
    pumpFrame(phase: EnginePhase.flushSemantics);

    viewController.pointTransformer = (Offset offset) => platformViewRenderBox.globalToLocal(offset);

    FakeAsync().run((FakeAsync async) {
      // Put one pointer down.
      ui.window.onPointerDataPacket!(ui.PointerDataPacket(data: <ui.PointerData>[
        _pointerData(ui.PointerChange.add, Offset.zero, pointer: 1, kind: PointerDeviceKind.touch),
        _pointerData(ui.PointerChange.down, const Offset(10, 10), pointer: 1, kind: PointerDeviceKind.touch),
        _pointerData(ui.PointerChange.remove, const Offset(10, 10), pointer: 1, kind: PointerDeviceKind.touch),
      ]));
      async.flushMicrotasks();

      // Put another pointer down and then cancel it.
      ui.window.onPointerDataPacket!(ui.PointerDataPacket(data: <ui.PointerData>[
        _pointerData(ui.PointerChange.add, Offset.zero, pointer: 2, kind: PointerDeviceKind.touch),
        _pointerData(ui.PointerChange.down, const Offset(20, 10), pointer: 2, kind: PointerDeviceKind.touch),
        _pointerData(ui.PointerChange.cancel, const Offset(20, 10), pointer: 2, kind: PointerDeviceKind.touch),
      ]));
      async.flushMicrotasks();

      // The first pointer can still moving without crashing.
      ui.window.onPointerDataPacket!(ui.PointerDataPacket(data: <ui.PointerData>[
        _pointerData(ui.PointerChange.add, Offset.zero, pointer: 1, kind: PointerDeviceKind.touch),
        _pointerData(ui.PointerChange.move, const Offset(10, 10), pointer: 1, kind: PointerDeviceKind.touch),
        _pointerData(ui.PointerChange.remove, const Offset(10, 10), pointer: 1, kind: PointerDeviceKind.touch),
      ]));
      async.flushMicrotasks();
    });

    // Passes if no crashes.
  });
153
}
154 155 156 157 158 159

ui.PointerData _pointerData(
  ui.PointerChange change,
  Offset logicalPosition, {
  int device = 0,
  PointerDeviceKind kind = PointerDeviceKind.mouse,
160
  int pointer = 0,
161 162
}) {
  return ui.PointerData(
163 164
    pointerIdentifier: pointer,
    embedderId: pointer,
165 166 167 168 169 170 171
    change: change,
    physicalX: logicalPosition.dx * ui.window.devicePixelRatio,
    physicalY: logicalPosition.dy * ui.window.devicePixelRatio,
    kind: kind,
    device: device,
  );
}