| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/base/io.dart'; |
| import 'package:flutter_tools/src/base/logger.dart'; |
| import 'package:flutter_tools/src/base/terminal.dart'; |
| import 'package:flutter_tools/src/build_info.dart'; |
| import 'package:flutter_tools/src/compile.dart'; |
| import 'package:flutter_tools/src/device.dart'; |
| import 'package:flutter_tools/src/resident_runner.dart'; |
| import 'package:flutter_tools/src/run_cold.dart'; |
| import 'package:flutter_tools/src/vmservice.dart'; |
| import 'package:meta/meta.dart'; |
| import 'package:mockito/mockito.dart'; |
| import 'package:platform/platform.dart'; |
| |
| import '../src/common.dart'; |
| import '../src/context.dart'; |
| import '../src/mocks.dart'; |
| |
| void main() { |
| group('cold attach', () { |
| MockResidentCompiler residentCompiler; |
| BufferLogger mockLogger; |
| |
| setUp(() { |
| mockLogger = BufferLogger( |
| terminal: AnsiTerminal( |
| stdio: null, |
| platform: const LocalPlatform(), |
| ), |
| outputPreferences: OutputPreferences.test(), |
| ); |
| residentCompiler = MockResidentCompiler(); |
| }); |
| |
| testUsingContext('Prints message when HttpException is thrown - 1', () async { |
| final MockDevice mockDevice = MockDevice(); |
| when(mockDevice.supportsHotReload).thenReturn(true); |
| when(mockDevice.supportsHotRestart).thenReturn(false); |
| when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester); |
| when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation _) async => 'Android 10'); |
| |
| final List<FlutterDevice> devices = <FlutterDevice>[ |
| TestFlutterDevice( |
| device: mockDevice, |
| generator: residentCompiler, |
| exception: const HttpException('Connection closed before full header was received, ' |
| 'uri = http://127.0.0.1:63394/5ZmLv8A59xY=/ws'), |
| ), |
| ]; |
| |
| final int exitCode = await ColdRunner(devices, |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| ).attach(); |
| expect(exitCode, 2); |
| expect(mockLogger.statusText, contains('If you are using an emulator running Android Q Beta, ' |
| 'consider using an emulator running API level 29 or lower.')); |
| expect(mockLogger.statusText, contains('Learn more about the status of this issue on ' |
| 'https://issuetracker.google.com/issues/132325318')); |
| }, overrides: <Type, Generator>{ |
| Logger: () => mockLogger, |
| }); |
| |
| testUsingContext('Prints message when HttpException is thrown - 2', () async { |
| final MockDevice mockDevice = MockDevice(); |
| when(mockDevice.supportsHotReload).thenReturn(true); |
| when(mockDevice.supportsHotRestart).thenReturn(false); |
| when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester); |
| when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation _) async => 'Android 10'); |
| |
| final List<FlutterDevice> devices = <FlutterDevice>[ |
| TestFlutterDevice( |
| device: mockDevice, |
| generator: residentCompiler, |
| exception: const HttpException(', uri = http://127.0.0.1:63394/5ZmLv8A59xY=/ws'), |
| ), |
| ]; |
| |
| final int exitCode = await ColdRunner(devices, |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| ).attach(); |
| expect(exitCode, 2); |
| expect(mockLogger.statusText, contains('If you are using an emulator running Android Q Beta, ' |
| 'consider using an emulator running API level 29 or lower.')); |
| expect(mockLogger.statusText, contains('Learn more about the status of this issue on ' |
| 'https://issuetracker.google.com/issues/132325318')); |
| }, overrides: <Type, Generator>{ |
| Logger: () => mockLogger, |
| }); |
| }); |
| |
| group('cleanupAtFinish()', () { |
| MockFlutterDevice mockFlutterDeviceFactory(Device device) { |
| final MockFlutterDevice mockFlutterDevice = MockFlutterDevice(); |
| when(mockFlutterDevice.stopEchoingDeviceLog()).thenAnswer((Invocation invocation) => Future<void>.value(null)); |
| when(mockFlutterDevice.device).thenReturn(device); |
| return mockFlutterDevice; |
| } |
| |
| testUsingContext('disposes each device', () async { |
| final MockDevice mockDevice1 = MockDevice(); |
| final MockDevice mockDevice2 = MockDevice(); |
| final MockFlutterDevice mockFlutterDevice1 = mockFlutterDeviceFactory(mockDevice1); |
| final MockFlutterDevice mockFlutterDevice2 = mockFlutterDeviceFactory(mockDevice2); |
| |
| final List<FlutterDevice> devices = <FlutterDevice>[mockFlutterDevice1, mockFlutterDevice2]; |
| |
| await ColdRunner(devices, |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| ).cleanupAtFinish(); |
| |
| verify(mockDevice1.dispose()); |
| verify(mockFlutterDevice1.stopEchoingDeviceLog()); |
| verify(mockDevice2.dispose()); |
| verify(mockFlutterDevice2.stopEchoingDeviceLog()); |
| }); |
| }); |
| |
| group('cold run', () { |
| testUsingContext('returns 1 if not prebuilt mode & mainPath does not exist', () async { |
| final MockDevice mockDevice = MockDevice(); |
| final MockFlutterDevice mockFlutterDevice = MockFlutterDevice(); |
| when(mockFlutterDevice.device).thenReturn(mockDevice); |
| final List<FlutterDevice> devices = <FlutterDevice>[mockFlutterDevice]; |
| final int result = await ColdRunner(devices).run(); |
| |
| expect(result, 1); |
| expect(testLogger.errorText, matches(r'Tried to run .*, but that file does not exist\.')); |
| expect(testLogger.errorText, matches(r'Consider using the -t option to specify the Dart file to start\.')); |
| }); |
| |
| testUsingContext('calls runCold on attached device', () async { |
| final MockDevice mockDevice = MockDevice(); |
| final MockFlutterDevice mockFlutterDevice = MockFlutterDevice(); |
| when(mockFlutterDevice.device).thenReturn(mockDevice); |
| when(mockFlutterDevice.runCold( |
| coldRunner: anyNamed('coldRunner'), |
| route: anyNamed('route') |
| )).thenAnswer((Invocation invocation) => Future<int>.value(1)); |
| final List<FlutterDevice> devices = <FlutterDevice>[mockFlutterDevice]; |
| final MockFile applicationBinary = MockFile(); |
| final int result = await ColdRunner( |
| devices, |
| applicationBinary: applicationBinary, |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| ).run(); |
| |
| expect(result, 1); |
| verify(mockFlutterDevice.runCold( |
| coldRunner: anyNamed('coldRunner'), |
| route: anyNamed('route'), |
| )); |
| }); |
| }); |
| } |
| |
| class MockFile extends Mock implements File {} |
| class MockFlutterDevice extends Mock implements FlutterDevice {} |
| class MockDevice extends Mock implements Device { |
| MockDevice() { |
| when(isSupported()).thenReturn(true); |
| } |
| } |
| |
| class TestFlutterDevice extends FlutterDevice { |
| TestFlutterDevice({ |
| @required Device device, |
| @required this.exception, |
| @required ResidentCompiler generator, |
| }) : assert(exception != null), |
| super(device, buildInfo: BuildInfo.debug, generator: generator); |
| |
| /// The exception to throw when the connect method is called. |
| final Exception exception; |
| |
| @override |
| Future<void> connect({ |
| ReloadSources reloadSources, |
| Restart restart, |
| CompileExpression compileExpression, |
| ReloadMethod reloadMethod, |
| }) async { |
| throw exception; |
| } |
| } |