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

Adam Barth's avatar
Adam Barth committed
5
import 'package:flutter_test/flutter_test.dart';
6
import 'package:flutter/foundation.dart';
7
import 'package:flutter/gestures.dart';
8
import 'package:vector_math/vector_math_64.dart';
9 10 11 12

void main() {
  test('Should route pointers', () {
    bool callbackRan = false;
Ian Hickson's avatar
Ian Hickson committed
13
    void callback(PointerEvent event) {
14 15 16
      callbackRan = true;
    }

17 18
    final TestPointer pointer2 = TestPointer(2);
    final TestPointer pointer3 = TestPointer(3);
19

20
    final PointerRouter router = PointerRouter();
21
    router.addRoute(3, callback);
22
    router.route(pointer2.down(Offset.zero));
23
    expect(callbackRan, isFalse);
24
    router.route(pointer3.down(Offset.zero));
25 26 27
    expect(callbackRan, isTrue);
    callbackRan = false;
    router.removeRoute(3, callback);
28
    router.route(pointer3.up());
29 30
    expect(callbackRan, isFalse);
  });
31 32 33 34 35 36

  test('Supports re-entrant cancellation', () {
    bool callbackRan = false;
    void callback(PointerEvent event) {
      callbackRan = true;
    }
37
    final PointerRouter router = PointerRouter();
38 39 40 41
    router.addRoute(2, (PointerEvent event) {
      router.removeRoute(2, callback);
    });
    router.addRoute(2, callback);
42
    final TestPointer pointer2 = TestPointer(2);
43
    router.route(pointer2.down(Offset.zero));
44 45
    expect(callbackRan, isFalse);
  });
46 47 48 49 50 51 52 53

  test('Supports global callbacks', () {
    bool secondCallbackRan = false;
    void secondCallback(PointerEvent event) {
      secondCallbackRan = true;
    }

    bool firstCallbackRan = false;
54
    final PointerRouter router = PointerRouter();
55 56 57 58 59
    router.addGlobalRoute((PointerEvent event) {
      firstCallbackRan = true;
      router.addGlobalRoute(secondCallback);
    });

60
    final TestPointer pointer2 = TestPointer(2);
61
    router.route(pointer2.down(Offset.zero));
62 63 64 65 66 67 68 69 70
    expect(firstCallbackRan, isTrue);
    expect(secondCallbackRan, isFalse);
  });

  test('Supports re-entrant global cancellation', () {
    bool callbackRan = false;
    void callback(PointerEvent event) {
      callbackRan = true;
    }
71
    final PointerRouter router = PointerRouter();
72 73 74 75
    router.addGlobalRoute((PointerEvent event) {
      router.removeGlobalRoute(callback);
    });
    router.addGlobalRoute(callback);
76
    final TestPointer pointer2 = TestPointer(2);
77
    router.route(pointer2.down(Offset.zero));
78 79 80 81 82 83 84 85
    expect(callbackRan, isFalse);
  });

  test('Per-pointer callbacks cannot re-entrantly add global routes', () {
    bool callbackRan = false;
    void callback(PointerEvent event) {
      callbackRan = true;
    }
86
    final PointerRouter router = PointerRouter();
87 88 89 90 91
    bool perPointerCallbackRan = false;
    router.addRoute(2, (PointerEvent event) {
      perPointerCallbackRan = true;
      router.addGlobalRoute(callback);
    });
92
    final TestPointer pointer2 = TestPointer(2);
93
    router.route(pointer2.down(Offset.zero));
94 95 96 97 98
    expect(perPointerCallbackRan, isTrue);
    expect(callbackRan, isFalse);
  });

  test('Per-pointer callbacks happen before global callbacks', () {
99
    final List<String> log = <String>[];
100
    final PointerRouter router = PointerRouter();
101 102 103 104 105 106 107 108 109 110 111 112
    router.addGlobalRoute((PointerEvent event) {
      log.add('global 1');
    });
    router.addRoute(2, (PointerEvent event) {
      log.add('per-pointer 1');
    });
    router.addGlobalRoute((PointerEvent event) {
      log.add('global 2');
    });
    router.addRoute(2, (PointerEvent event) {
      log.add('per-pointer 2');
    });
113
    final TestPointer pointer2 = TestPointer(2);
114
    router.route(pointer2.down(Offset.zero));
115 116 117 118 119 120 121
    expect(log, equals(<String>[
      'per-pointer 1',
      'per-pointer 2',
      'global 1',
      'global 2',
    ]));
  });
122 123

  test('Exceptions do not stop pointer routing', () {
124
    final List<String> log = <String>[];
125
    final PointerRouter router = PointerRouter();
126 127 128 129 130
    router.addRoute(2, (PointerEvent event) {
      log.add('per-pointer 1');
    });
    router.addRoute(2, (PointerEvent event) {
      log.add('per-pointer 2');
Ian Hickson's avatar
Ian Hickson committed
131
      throw 'Having a bad day!';
132 133 134 135 136
    });
    router.addRoute(2, (PointerEvent event) {
      log.add('per-pointer 3');
    });

137
    final FlutterExceptionHandler? previousErrorHandler = FlutterError.onError;
138 139 140 141
    FlutterError.onError = (FlutterErrorDetails details) {
      log.add('error report');
    };

142
    final TestPointer pointer2 = TestPointer(2);
143
    router.route(pointer2.down(Offset.zero));
144 145 146 147 148 149 150 151 152
    expect(log, equals(<String>[
      'per-pointer 1',
      'per-pointer 2',
      'error report',
      'per-pointer 3',
    ]));

    FlutterError.onError = previousErrorHandler;
  });
153

154 155 156 157 158 159 160 161 162 163 164
  test('Exceptions include router, route & event', () {
    try {
      final PointerRouter router = PointerRouter();
      router.addRoute(2, (PointerEvent event) => throw 'Pointer exception');
    } catch (e) {
      expect(e, contains('router: Instance of \'PointerRouter\''));
      expect(e, contains('route: Closure: (PointerEvent) => Null'));
      expect(e, contains('event: PointerDownEvent#[a-zA-Z0-9]{5}(position: Offset(0.0, 0.0))'));
    }
  });

165 166 167 168
  test('Should transform events', () {
    final List<PointerEvent> events = <PointerEvent>[];
    final List<PointerEvent> globalEvents = <PointerEvent>[];
    final PointerRouter router = PointerRouter();
169
    final Matrix4 transform = (Matrix4.identity()..scale(1 / 2.0, 1 / 2.0, 1.0)).multiplied(Matrix4.translationValues(-10, -30, 0));
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212

    router.addRoute(1, (PointerEvent event) {
      events.add(event);
    }, transform);

    router.addGlobalRoute((PointerEvent event) {
      globalEvents.add(event);
    }, transform);

    final TestPointer pointer1 = TestPointer(1);
    const Offset firstPosition = Offset(16, 36);
    router.route(pointer1.down(firstPosition));

    expect(events.single.transform, transform);
    expect(events.single.position, firstPosition);
    expect(events.single.delta, Offset.zero);
    expect(events.single.localPosition, const Offset(3, 3));
    expect(events.single.localDelta, Offset.zero);

    expect(globalEvents.single.transform, transform);
    expect(globalEvents.single.position, firstPosition);
    expect(globalEvents.single.delta, Offset.zero);
    expect(globalEvents.single.localPosition, const Offset(3, 3));
    expect(globalEvents.single.localDelta, Offset.zero);

    events.clear();
    globalEvents.clear();

    const Offset newPosition = Offset(20, 40);
    router.route(pointer1.move(newPosition));

    expect(events.single.transform, transform);
    expect(events.single.position, newPosition);
    expect(events.single.delta, newPosition - firstPosition);
    expect(events.single.localPosition, const Offset(5, 5));
    expect(events.single.localDelta, const Offset(2, 2));

    expect(globalEvents.single.transform, transform);
    expect(globalEvents.single.position, newPosition);
    expect(globalEvents.single.delta, newPosition - firstPosition);
    expect(globalEvents.single.localPosition, const Offset(5, 5));
    expect(globalEvents.single.localDelta, const Offset(2, 2));
  });
213
}