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

import 'dart:typed_data';

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

11
class MemoryPressureObserver with WidgetsBindingObserver {
12 13 14 15 16 17 18 19
  bool sawMemoryPressure = false;

  @override
  void didHaveMemoryPressure() {
    sawMemoryPressure = true;
  }
}

20
class AppLifecycleStateObserver with WidgetsBindingObserver {
21 22 23 24 25 26 27 28
  AppLifecycleState lifecycleState;

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    lifecycleState = state;
  }
}

29
class PushRouteObserver with WidgetsBindingObserver {
30 31 32 33 34 35 36 37 38
  String pushedRoute;

  @override
  Future<bool> didPushRoute(String route) async {
    pushedRoute = route;
    return true;
  }
}

39 40 41 42 43 44
void main() {
  setUp(() {
    WidgetsFlutterBinding.ensureInitialized();
  });

  testWidgets('didHaveMemoryPressure callback', (WidgetTester tester) async {
45
    final MemoryPressureObserver observer = MemoryPressureObserver();
46 47 48
    WidgetsBinding.instance.addObserver(observer);
    final ByteData message = const JSONMessageCodec().encodeMessage(
      <String, dynamic>{'type': 'memoryPressure'});
49
    await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/system', message, (_) { });
50 51 52
    expect(observer.sawMemoryPressure, true);
    WidgetsBinding.instance.removeObserver(observer);
  });
53 54

  testWidgets('handleLifecycleStateChanged callback', (WidgetTester tester) async {
55
    final BinaryMessenger defaultBinaryMessenger = ServicesBinding.instance.defaultBinaryMessenger;
56
    final AppLifecycleStateObserver observer = AppLifecycleStateObserver();
57 58 59
    WidgetsBinding.instance.addObserver(observer);

    ByteData message = const StringCodec().encodeMessage('AppLifecycleState.paused');
60
    await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
61 62 63
    expect(observer.lifecycleState, AppLifecycleState.paused);

    message = const StringCodec().encodeMessage('AppLifecycleState.resumed');
64
    await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
65 66 67
    expect(observer.lifecycleState, AppLifecycleState.resumed);

    message = const StringCodec().encodeMessage('AppLifecycleState.inactive');
68
    await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
69
    expect(observer.lifecycleState, AppLifecycleState.inactive);
70 71 72 73 74 75 76 77


    message = const StringCodec().encodeMessage('AppLifecycleState.detached');
    await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
    // TODO(chunhtai): this should be detached once the issue is fixed
    // https://github.com/flutter/flutter/issues/39832
    // The binding drops detached message for now.
    expect(observer.lifecycleState, AppLifecycleState.inactive);
78
  });
79 80

  testWidgets('didPushRoute callback', (WidgetTester tester) async {
81
    final PushRouteObserver observer = PushRouteObserver();
82 83
    WidgetsBinding.instance.addObserver(observer);

84
    const String testRouteName = 'testRouteName';
85
    final ByteData message = const JSONMethodCodec().encodeMethodCall(
86
      const MethodCall('pushRoute', testRouteName));
87
    await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { });
88 89 90 91
    expect(observer.pushedRoute, testRouteName);

    WidgetsBinding.instance.removeObserver(observer);
  });
92 93

  testWidgets('Application lifecycle affects frame scheduling', (WidgetTester tester) async {
94
    final BinaryMessenger defaultBinaryMessenger = ServicesBinding.instance.defaultBinaryMessenger;
95 96 97 98
    ByteData message;
    expect(tester.binding.hasScheduledFrame, isFalse);

    message = const StringCodec().encodeMessage('AppLifecycleState.paused');
99
    await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
100 101 102
    expect(tester.binding.hasScheduledFrame, isFalse);

    message = const StringCodec().encodeMessage('AppLifecycleState.resumed');
103
    await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
104 105 106 107 108
    expect(tester.binding.hasScheduledFrame, isTrue);
    await tester.pump();
    expect(tester.binding.hasScheduledFrame, isFalse);

    message = const StringCodec().encodeMessage('AppLifecycleState.inactive');
109
    await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
110 111 112
    expect(tester.binding.hasScheduledFrame, isFalse);

    message = const StringCodec().encodeMessage('AppLifecycleState.paused');
113
    await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
114 115 116 117 118 119
    expect(tester.binding.hasScheduledFrame, isFalse);

    tester.binding.scheduleFrame();
    expect(tester.binding.hasScheduledFrame, isFalse);

    tester.binding.scheduleForcedFrame();
120 121
    expect(tester.binding.hasScheduledFrame, isTrue);
    await tester.pump();
122 123 124 125 126 127 128 129 130 131 132 133 134
    expect(tester.binding.hasScheduledFrame, isFalse);

    int frameCount = 0;
    tester.binding.addPostFrameCallback((Duration duration) { frameCount += 1; });
    expect(tester.binding.hasScheduledFrame, isFalse);
    await tester.pump(const Duration(milliseconds: 1));
    expect(tester.binding.hasScheduledFrame, isFalse);
    expect(frameCount, 0);

    tester.binding.scheduleWarmUpFrame(); // this actually tests flutter_test's implementation
    expect(tester.binding.hasScheduledFrame, isFalse);
    expect(frameCount, 1);
  });
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

  testWidgets('scheduleFrameCallback error control test', (WidgetTester tester) async {
    FlutterError error;
    try {
      tester.binding.scheduleFrameCallback(null, rescheduling: true);
    } on FlutterError catch (e) {
      error = e;
    }
    expect(error, isNotNull);
    expect(error.diagnostics.length, 3);
    expect(error.diagnostics.last.level, DiagnosticLevel.hint);
    expect(
      error.diagnostics.last.toStringDeep(),
      equalsIgnoringHashCodes(
        'If this is the initial registration of the callback, or if the\n'
        'callback is asynchronous, then do not use the "rescheduling"\n'
        'argument.\n'
      ),
    );
    expect(
      error.toStringDeep(),
      'FlutterError\n'
      '   scheduleFrameCallback called with rescheduling true, but no\n'
      '   callback is in scope.\n'
      '   The "rescheduling" argument should only be set to true if the\n'
      '   callback is being reregistered from within the callback itself,\n'
      '   and only then if the callback itself is entirely synchronous.\n'
      '   If this is the initial registration of the callback, or if the\n'
      '   callback is asynchronous, then do not use the "rescheduling"\n'
      '   argument.\n'
    );
  });
167
}