hot_test.dart 11.5 KB
Newer Older
1 2 3 4
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:async';

7
import 'package:flutter_tools/src/artifacts.dart';
8
import 'package:flutter_tools/src/build_info.dart';
9
import 'package:flutter_tools/src/devfs.dart';
10 11
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/resident_runner.dart';
12
import 'package:flutter_tools/src/run_hot.dart';
13 14
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
15

16
import 'src/common.dart';
17
import 'src/context.dart';
18
import 'src/mocks.dart';
19

20
void main() {
21 22 23
  group('validateReloadReport', () {
    testUsingContext('invalid', () async {
      expect(HotRunner.validateReloadReport(<String, dynamic>{}), false);
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
      expect(HotRunner.validateReloadReport(<String, dynamic>{
        'type': 'ReloadReport',
        'success': false,
        'details': <String, dynamic>{},
      }), false);
      expect(HotRunner.validateReloadReport(<String, dynamic>{
        'type': 'ReloadReport',
        'success': false,
        'details': <String, dynamic>{
          'notices': <Map<String, dynamic>>[
          ],
        },
      }), false);
      expect(HotRunner.validateReloadReport(<String, dynamic>{
        'type': 'ReloadReport',
        'success': false,
        'details': <String, dynamic>{
          'notices': <String, dynamic>{
            'message': 'error',
          },
        },
      }), false);
      expect(HotRunner.validateReloadReport(<String, dynamic>{
        'type': 'ReloadReport',
        'success': false,
        'details': <String, dynamic>{
          'notices': <Map<String, dynamic>>[],
        },
      }), false);
      expect(HotRunner.validateReloadReport(<String, dynamic>{
        'type': 'ReloadReport',
        'success': false,
        'details': <String, dynamic>{
          'notices': <Map<String, dynamic>>[
58
            <String, dynamic>{'message': false},
59 60 61 62 63 64 65 66
          ],
        },
      }), false);
      expect(HotRunner.validateReloadReport(<String, dynamic>{
        'type': 'ReloadReport',
        'success': false,
        'details': <String, dynamic>{
          'notices': <Map<String, dynamic>>[
67
            <String, dynamic>{'message': <String>['error']},
68 69 70 71 72 73 74 75
          ],
        },
      }), false);
      expect(HotRunner.validateReloadReport(<String, dynamic>{
        'type': 'ReloadReport',
        'success': false,
        'details': <String, dynamic>{
          'notices': <Map<String, dynamic>>[
76 77
            <String, dynamic>{'message': 'error'},
            <String, dynamic>{'message': <String>['error']},
78 79 80 81 82 83 84 85
          ],
        },
      }), false);
      expect(HotRunner.validateReloadReport(<String, dynamic>{
        'type': 'ReloadReport',
        'success': false,
        'details': <String, dynamic>{
          'notices': <Map<String, dynamic>>[
86
            <String, dynamic>{'message': 'error'},
87 88 89 90 91 92 93
          ],
        },
      }), false);
      expect(HotRunner.validateReloadReport(<String, dynamic>{
        'type': 'ReloadReport',
        'success': true,
      }), true);
94 95
    });
  });
96 97

  group('hotRestart', () {
98
    final MockResidentCompiler residentCompiler = MockResidentCompiler();
99
    final MockDevFs mockDevFs = MockDevFs();
100
    MockLocalEngineArtifacts mockArtifacts;
101

102 103 104 105 106 107 108 109 110 111 112 113
    when(mockDevFs.update(
      mainPath: anyNamed('mainPath'),
      target: anyNamed('target'),
      bundle: anyNamed('bundle'),
      firstBuildTime: anyNamed('firstBuildTime'),
      bundleFirstUpload: anyNamed('bundleFirstUpload'),
      generator: anyNamed('generator'),
      fullRestart: anyNamed('fullRestart'),
      dillOutputPath: anyNamed('dillOutputPath'),
      trackWidgetCreation: anyNamed('trackWidgetCreation'),
      projectRootPath: anyNamed('projectRootPath'),
      pathToReload: anyNamed('pathToReload'),
114
      invalidatedFiles: anyNamed('invalidatedFiles'),
115 116
    )).thenAnswer((Invocation _) => Future<UpdateFSReport>.value(
        UpdateFSReport(success: true, syncedBytes: 1000, invalidatedSourcesCount: 1)));
117
    when(mockDevFs.assetPathsToEvict).thenReturn(<String>{});
118
    when(mockDevFs.baseUri).thenReturn(Uri.file('test'));
119 120
    when(mockDevFs.sources).thenReturn(<Uri>[Uri.file('test')]);
    when(mockDevFs.lastCompiled).thenReturn(DateTime.now());
121

122
    setUp(() {
123
      mockArtifacts = MockLocalEngineArtifacts();
124
      when(mockArtifacts.getArtifactPath(Artifact.flutterPatchedSdkPath)).thenReturn('some/path');
125 126
    });

127 128 129 130 131 132 133
    testUsingContext('Does not hot restart when device does not support it', () async {
      // Setup mocks
      final MockDevice mockDevice = MockDevice();
      when(mockDevice.supportsHotReload).thenReturn(true);
      when(mockDevice.supportsHotRestart).thenReturn(false);
      // Trigger hot restart.
      final List<FlutterDevice> devices = <FlutterDevice>[
134
        FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug)..devFS = mockDevFs,
135
      ];
136
      final OperationResult result = await HotRunner(devices).restart(fullRestart: true);
137
      // Expect hot restart failed.
138
      expect(result.isOk, false);
139
      expect(result.message, 'hotRestart not supported');
140
    }, overrides: <Type, Generator>{
141
      Artifacts: () => mockArtifacts,
142
      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
143 144 145 146 147 148 149 150 151 152 153 154
    });

    testUsingContext('Does not hot restart when one of many devices does not support it', () async {
      // Setup mocks
      final MockDevice mockDevice = MockDevice();
      final MockDevice mockHotDevice = MockDevice();
      when(mockDevice.supportsHotReload).thenReturn(true);
      when(mockDevice.supportsHotRestart).thenReturn(false);
      when(mockHotDevice.supportsHotReload).thenReturn(true);
      when(mockHotDevice.supportsHotRestart).thenReturn(true);
      // Trigger hot restart.
      final List<FlutterDevice> devices = <FlutterDevice>[
155 156
        FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug)..devFS = mockDevFs,
        FlutterDevice(mockHotDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug)..devFS = mockDevFs,
157 158 159 160 161 162 163
      ];
      final OperationResult result = await HotRunner(devices).restart(fullRestart: true);
      // Expect hot restart failed.
      expect(result.isOk, false);
      expect(result.message, 'hotRestart not supported');
    }, overrides: <Type, Generator>{
      Artifacts: () => mockArtifacts,
164
      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
165 166 167 168 169 170 171 172 173 174 175 176
    });

    testUsingContext('Does hot restarts when all devices support it', () async {
      // Setup mocks
      final MockDevice mockDevice = MockDevice();
      final MockDevice mockHotDevice = MockDevice();
      when(mockDevice.supportsHotReload).thenReturn(true);
      when(mockDevice.supportsHotRestart).thenReturn(true);
      when(mockHotDevice.supportsHotReload).thenReturn(true);
      when(mockHotDevice.supportsHotRestart).thenReturn(true);
      // Trigger a restart.
      final List<FlutterDevice> devices = <FlutterDevice>[
177 178
        FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug)..devFS = mockDevFs,
        FlutterDevice(mockHotDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug)..devFS = mockDevFs,
179 180 181 182 183 184 185
      ];
      final OperationResult result = await HotRunner(devices).restart(fullRestart: true);
      // Expect hot restart was successful.
      expect(result.isOk, true);
      expect(result.message, isNot('hotRestart not supported'));
    }, overrides: <Type, Generator>{
      Artifacts: () => mockArtifacts,
186
      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
187 188 189
    });

    testUsingContext('setup function fails', () async {
190 191 192 193
      final MockDevice mockDevice = MockDevice();
      when(mockDevice.supportsHotReload).thenReturn(true);
      when(mockDevice.supportsHotRestart).thenReturn(true);
      final List<FlutterDevice> devices = <FlutterDevice>[
194
        FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug),
195
      ];
196 197 198
      final OperationResult result = await HotRunner(devices).restart(fullRestart: true);
      expect(result.isOk, false);
      expect(result.message, 'setupHotRestart failed');
199
    }, overrides: <Type, Generator>{
200
      Artifacts: () => mockArtifacts,
201
      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: false),
202
    });
203 204 205 206 207 208 209 210

    testUsingContext('hot restart supported', () async {
      // Setup mocks
      final MockDevice mockDevice = MockDevice();
      when(mockDevice.supportsHotReload).thenReturn(true);
      when(mockDevice.supportsHotRestart).thenReturn(true);
      // Trigger hot restart.
      final List<FlutterDevice> devices = <FlutterDevice>[
211
        FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug)..devFS = mockDevFs,
212 213 214 215 216 217 218
      ];
      final OperationResult result = await HotRunner(devices).restart(fullRestart: true);
      // Expect hot restart successful.
      expect(result.isOk, true);
      expect(result.message, isNot('setupHotRestart failed'));
    }, overrides: <Type, Generator>{
      Artifacts: () => mockArtifacts,
219
      HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
220
    });
221 222 223 224 225 226 227 228 229 230 231 232 233 234

    group('shutdown hook tests', () {
      TestHotRunnerConfig shutdownTestingConfig;

      setUp(() {
        shutdownTestingConfig = TestHotRunnerConfig(
          successfulSetup: true,
        );
      });

      testUsingContext('shutdown hook called after signal', () async {
        final MockDevice mockDevice = MockDevice();
        when(mockDevice.supportsHotReload).thenReturn(true);
        when(mockDevice.supportsHotRestart).thenReturn(true);
235
        when(mockDevice.supportsFlutterExit).thenReturn(false);
236
        final List<FlutterDevice> devices = <FlutterDevice>[
237
          FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug),
238 239 240
        ];
        await HotRunner(devices).cleanupAfterSignal();
        expect(shutdownTestingConfig.shutdownHookCalled, true);
241
      }, overrides: <Type, Generator>{
242 243 244 245 246 247 248 249
        Artifacts: () => mockArtifacts,
        HotRunnerConfig: () => shutdownTestingConfig,
      });

      testUsingContext('shutdown hook called after app stop', () async {
        final MockDevice mockDevice = MockDevice();
        when(mockDevice.supportsHotReload).thenReturn(true);
        when(mockDevice.supportsHotRestart).thenReturn(true);
250
        when(mockDevice.supportsFlutterExit).thenReturn(false);
251
        final List<FlutterDevice> devices = <FlutterDevice>[
252
          FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug),
253
        ];
254
        await HotRunner(devices).preExit();
255
        expect(shutdownTestingConfig.shutdownHookCalled, true);
256
      }, overrides: <Type, Generator>{
257 258 259 260
        Artifacts: () => mockArtifacts,
        HotRunnerConfig: () => shutdownTestingConfig,
      });
    });
261 262 263
  });
}

264 265
class MockDevFs extends Mock implements DevFS {}

266 267
class MockLocalEngineArtifacts extends Mock implements LocalEngineArtifacts {}

268 269 270 271 272 273 274
class MockDevice extends Mock implements Device {
  MockDevice() {
    when(isSupported()).thenReturn(true);
  }
}

class TestHotRunnerConfig extends HotRunnerConfig {
275
  TestHotRunnerConfig({@required this.successfulSetup});
276
  bool successfulSetup;
277
  bool shutdownHookCalled = false;
278

279 280 281 282
  @override
  Future<bool> setupHotRestart() async {
    return successfulSetup;
  }
283 284 285 286 287

  @override
  Future<void> runPreShutdownOperations() async {
    shutdownHookCalled = true;
  }
288
}