| // 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 'dart:convert'; |
| |
| import 'package:build_daemon/client.dart'; |
| import 'package:dwds/dwds.dart'; |
| import 'package:flutter_tools/src/base/common.dart'; |
| 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/net.dart'; |
| import 'package:flutter_tools/src/build_info.dart'; |
| import 'package:flutter_tools/src/build_runner/resident_web_runner.dart'; |
| import 'package:flutter_tools/src/build_runner/web_fs.dart'; |
| import 'package:flutter_tools/src/compile.dart'; |
| import 'package:flutter_tools/src/devfs.dart'; |
| import 'package:flutter_tools/src/device.dart'; |
| import 'package:flutter_tools/src/features.dart'; |
| import 'package:flutter_tools/src/globals.dart'; |
| import 'package:flutter_tools/src/project.dart'; |
| import 'package:flutter_tools/src/reporting/reporting.dart'; |
| import 'package:flutter_tools/src/resident_runner.dart'; |
| import 'package:flutter_tools/src/web/chrome.dart'; |
| import 'package:flutter_tools/src/web/web_device.dart'; |
| import 'package:meta/meta.dart'; |
| import 'package:mockito/mockito.dart'; |
| import 'package:vm_service/vm_service.dart'; |
| import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; |
| |
| import '../src/common.dart'; |
| import '../src/context.dart'; |
| import '../src/testbed.dart'; |
| |
| void main() { |
| Testbed testbed; |
| MockFlutterWebFs mockWebFs; |
| ResidentWebRunner residentWebRunner; |
| MockDebugConnection mockDebugConnection; |
| MockVmService mockVmService; |
| MockChromeDevice mockChromeDevice; |
| MockAppConnection mockAppConnection; |
| MockFlutterDevice mockFlutterDevice; |
| MockWebDevFS mockWebDevFS; |
| MockResidentCompiler mockResidentCompiler; |
| MockChrome mockChrome; |
| MockChromeConnection mockChromeConnection; |
| MockChromeTab mockChromeTab; |
| MockWipConnection mockWipConnection; |
| MockWipDebugger mockWipDebugger; |
| MockWebServerDevice mockWebServerDevice; |
| bool didSkipDwds; |
| |
| setUp(() { |
| resetChromeForTesting(); |
| mockWebFs = MockFlutterWebFs(); |
| mockDebugConnection = MockDebugConnection(); |
| mockVmService = MockVmService(); |
| mockChromeDevice = MockChromeDevice(); |
| mockAppConnection = MockAppConnection(); |
| mockFlutterDevice = MockFlutterDevice(); |
| mockWebDevFS = MockWebDevFS(); |
| mockResidentCompiler = MockResidentCompiler(); |
| mockChrome = MockChrome(); |
| mockChromeConnection = MockChromeConnection(); |
| mockChromeTab = MockChromeTab(); |
| mockWipConnection = MockWipConnection(); |
| mockWipDebugger = MockWipDebugger(); |
| mockWebServerDevice = MockWebServerDevice(); |
| when(mockFlutterDevice.device).thenReturn(mockChromeDevice); |
| testbed = Testbed( |
| setup: () { |
| residentWebRunner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| ipv6: true, |
| stayResident: true, |
| dartDefines: const <String>[], |
| urlTunneller: null, |
| ) as ResidentWebRunner; |
| }, |
| overrides: <Type, Generator>{ |
| WebFsFactory: () => ({ |
| @required String target, |
| @required FlutterProject flutterProject, |
| @required BuildInfo buildInfo, |
| @required bool skipDwds, |
| @required bool initializePlatform, |
| @required String hostname, |
| @required String port, |
| @required List<String> dartDefines, |
| @required UrlTunneller urlTunneller, |
| }) async { |
| didSkipDwds = skipDwds; |
| return mockWebFs; |
| }, |
| }, |
| ); |
| }); |
| |
| void _setupMocks() { |
| fs.file('pubspec.yaml').createSync(); |
| fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true); |
| fs.file(fs.path.join('web', 'index.html')).createSync(recursive: true); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| when(mockWebFs.recompile()).thenAnswer((Invocation _) { |
| return Future<bool>.value(false); |
| }); |
| when(mockWebFs.uri).thenReturn('http://localhost:8765/app/'); |
| when(mockDebugConnection.vmService).thenReturn(mockVmService); |
| when(mockDebugConnection.onDone).thenAnswer((Invocation invocation) { |
| return Completer<void>().future; |
| }); |
| when(mockVmService.onStdoutEvent).thenAnswer((Invocation _) { |
| return const Stream<Event>.empty(); |
| }); |
| when(mockVmService.onDebugEvent).thenAnswer((Invocation _) { |
| return const Stream<Event>.empty(); |
| }); |
| when(mockDebugConnection.uri).thenReturn('ws://127.0.0.1/abcd/'); |
| when(mockFlutterDevice.devFS).thenReturn(mockWebDevFS); |
| when(mockWebDevFS.sources).thenReturn(<Uri>[]); |
| when(mockFlutterDevice.generator).thenReturn(mockResidentCompiler); |
| when(mockChrome.chromeConnection).thenReturn(mockChromeConnection); |
| when(mockChromeConnection.getTab(any)).thenAnswer((Invocation invocation) async { |
| return mockChromeTab; |
| }); |
| when(mockChromeTab.connect()).thenAnswer((Invocation invocation) async { |
| return mockWipConnection; |
| }); |
| when(mockWipConnection.debugger).thenReturn(mockWipDebugger); |
| } |
| |
| test('runner with web server device does not support debugging without --start-paused', () => testbed.run(() { |
| when(mockFlutterDevice.device).thenReturn(WebServerDevice()); |
| final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| ipv6: true, |
| stayResident: true, |
| dartDefines: const <String>[], |
| urlTunneller: null, |
| ) as ResidentWebRunner; |
| |
| expect(profileResidentWebRunner.debuggingEnabled, false); |
| |
| when(mockFlutterDevice.device).thenReturn(MockChromeDevice()); |
| expect(residentWebRunner.debuggingEnabled, true); |
| })); |
| |
| test('runner with web server device does not initialize dwds', () => testbed.run(() async { |
| _setupMocks(); |
| when(mockFlutterDevice.device).thenReturn(WebServerDevice()); |
| |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| |
| expect(didSkipDwds, true); |
| })); |
| |
| test('runner with web server device supports debugging with --start-paused', () => testbed.run(() { |
| when(mockFlutterDevice.device).thenReturn(WebServerDevice()); |
| final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, startPaused: true), |
| ipv6: true, |
| stayResident: true, |
| dartDefines: <String>[], |
| urlTunneller: null, |
| ); |
| |
| expect(profileResidentWebRunner.debuggingEnabled, true); |
| })); |
| |
| test('runner with web server device uses debug extension with --start-paused', () => testbed.run(() async { |
| _setupMocks(); |
| when(mockFlutterDevice.device).thenReturn(WebServerDevice()); |
| final ResidentWebRunner runner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, startPaused: true), |
| ipv6: true, |
| stayResident: true, |
| dartDefines: <String>[], |
| urlTunneller: null, |
| ) as ResidentWebRunner; |
| |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(runner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| |
| // Check connect() was told to use the debug extension. |
| verify(mockWebFs.connect(true)).called(1); |
| // And ensure the debug services was started. |
| expect(testLogger.statusText, contains('Debug service listening on')); |
| })); |
| |
| test('profile does not supportsServiceProtocol', () => testbed.run(() { |
| when(mockFlutterDevice.device).thenReturn(mockChromeDevice); |
| final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile), |
| ipv6: true, |
| stayResident: true, |
| dartDefines: const <String>[], |
| urlTunneller: null, |
| ); |
| |
| expect(profileResidentWebRunner.supportsServiceProtocol, false); |
| expect(residentWebRunner.supportsServiceProtocol, true); |
| })); |
| |
| test('Exits on run if application does not support the web', () => testbed.run(() async { |
| fs.file('pubspec.yaml').createSync(); |
| |
| expect(await residentWebRunner.run(), 1); |
| expect(testLogger.errorText, contains('This application is not configured to build on the web')); |
| })); |
| |
| test('Exits on run if target file does not exist', () => testbed.run(() async { |
| fs.file('pubspec.yaml').createSync(); |
| fs.file(fs.path.join('web', 'index.html')).createSync(recursive: true); |
| |
| expect(await residentWebRunner.run(), 1); |
| final String absoluteMain = fs.path.absolute(fs.path.join('lib', 'main.dart')); |
| expect(testLogger.errorText, contains('Tried to run $absoluteMain, but that file does not exist.')); |
| })); |
| |
| test('Can successfully run and connect to vmservice', () => testbed.run(() async { |
| _setupMocks(); |
| final DelegateLogger delegateLogger = logger as DelegateLogger; |
| final BufferLogger bufferLogger = delegateLogger.delegate as BufferLogger; |
| final MockStatus status = MockStatus(); |
| delegateLogger.status = status; |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future; |
| |
| verify(mockAppConnection.runMain()).called(1); |
| verify(mockVmService.registerService('reloadSources', 'FlutterTools')).called(1); |
| verify(status.stop()).called(2); |
| |
| expect(bufferLogger.statusText, contains('Debug service listening on ws://127.0.0.1/abcd/')); |
| expect(debugConnectionInfo.wsUri.toString(), 'ws://127.0.0.1/abcd/'); |
| }, overrides: <Type, Generator>{ |
| Logger: () => DelegateLogger(BufferLogger()), |
| })); |
| |
| test('Can successfully run and disconnect with --no-resident', () => testbed.run(() async { |
| _setupMocks(); |
| residentWebRunner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| ipv6: true, |
| stayResident: false, |
| dartDefines: const <String>[], |
| urlTunneller: null, |
| ) as ResidentWebRunner; |
| |
| expect(await residentWebRunner.run(), 0); |
| })); |
| |
| test('Listens to stdout streams before running main', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final StreamController<Event> controller = StreamController<Event>.broadcast(); |
| when(mockVmService.onStdoutEvent).thenAnswer((Invocation _) { |
| return controller.stream; |
| }); |
| when(mockAppConnection.runMain()).thenAnswer((Invocation invocation) { |
| controller.add(Event.parse(<String, Object>{ |
| 'type': 'Event', |
| 'kind': 'WriteEvent', |
| 'timestamp': 1569473488296, |
| 'bytes': base64.encode('THIS MESSAGE IS IMPORTANT'.codeUnits), |
| })); |
| }); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| |
| expect(testLogger.statusText, contains('THIS MESSAGE IS IMPORTANT')); |
| })); |
| |
| test('Does not run main with --start-paused', () => testbed.run(() async { |
| residentWebRunner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, startPaused: true), |
| ipv6: true, |
| stayResident: true, |
| dartDefines: const <String>[], |
| urlTunneller: null, |
| ) as ResidentWebRunner; |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final StreamController<Event> controller = StreamController<Event>.broadcast(); |
| when(mockVmService.onStdoutEvent).thenAnswer((Invocation _) { |
| return controller.stream; |
| }); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| |
| verifyNever(mockAppConnection.runMain()); |
| })); |
| |
| test('Can hot reload after attaching', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockWebFs.recompile()).thenAnswer((Invocation invocation) async { |
| return true; |
| }); |
| when(mockVmService.callServiceExtension('hotRestart')).thenAnswer((Invocation _) async { |
| return Response.parse(<String, Object>{'type': 'Success'}); |
| }); |
| final OperationResult result = await residentWebRunner.restart(fullRestart: false); |
| |
| expect(testLogger.statusText, contains('Reloaded application in')); |
| expect(result.code, 0); |
| // ensure that analytics are sent. |
| verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{ |
| 'cd27': 'web-javascript', |
| 'cd28': null, |
| 'cd29': 'false', |
| 'cd30': 'true', |
| })).called(1); |
| verify(Usage.instance.sendTiming('hot', 'web-restart', any)).called(1); |
| verify(Usage.instance.sendTiming('hot', 'web-refresh', any)).called(1); |
| verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1); |
| }, overrides: <Type, Generator>{ |
| Usage: () => MockFlutterUsage(), |
| })); |
| |
| test('Can hot reload after attaching - experimental', () => testbed.run(() async { |
| _setupMocks(); |
| launchChromeInstance(mockChrome); |
| when(mockWebDevFS.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'), |
| invalidatedFiles: anyNamed('invalidatedFiles'), |
| )).thenAnswer((Invocation invocation) async { |
| return UpdateFSReport(success: true) |
| ..invalidatedModules = <String>['example']; |
| }); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| final OperationResult result = await residentWebRunner.restart(fullRestart: false); |
| |
| expect(testLogger.statusText, contains('Reloaded application in')); |
| expect(result.code, 0); |
| verify(mockResidentCompiler.accept()).called(2); |
| // ensure that analytics are sent. |
| verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{ |
| 'cd27': 'web-javascript', |
| 'cd28': null, |
| 'cd29': 'false', |
| 'cd30': 'true', |
| })).called(1); |
| verify(Usage.instance.sendTiming('hot', 'web-incremental-restart', any)).called(1); |
| }, overrides: <Type, Generator>{ |
| Usage: () => MockFlutterUsage(), |
| FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true), |
| })); |
| |
| test('Can hot restart after attaching - experimental', () => testbed.run(() async { |
| _setupMocks(); |
| launchChromeInstance(mockChrome); |
| when(mockWebDevFS.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'), |
| invalidatedFiles: anyNamed('invalidatedFiles'), |
| )).thenAnswer((Invocation invocation) async { |
| return UpdateFSReport(success: true) |
| ..invalidatedModules = <String>['example']; |
| }); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| final OperationResult result = await residentWebRunner.restart(fullRestart: true); |
| |
| expect(testLogger.statusText, contains('Restarted application in')); |
| expect(result.code, 0); |
| verify(mockResidentCompiler.accept()).called(2); |
| // ensure that analytics are sent. |
| verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{ |
| 'cd27': 'web-javascript', |
| 'cd28': null, |
| 'cd29': 'false', |
| 'cd30': 'true', |
| })).called(1); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-incremental-restart', any)); |
| }, overrides: <Type, Generator>{ |
| Usage: () => MockFlutterUsage(), |
| FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true), |
| })); |
| |
| test('Can hot restart after attaching - experimental with web-server device', () => testbed.run(() async { |
| _setupMocks(); |
| when(mockFlutterDevice.device).thenReturn(mockWebServerDevice); |
| when(mockWebDevFS.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'), |
| invalidatedFiles: anyNamed('invalidatedFiles'), |
| )).thenAnswer((Invocation invocation) async { |
| return UpdateFSReport(success: true) |
| ..invalidatedModules = <String>['example']; |
| }); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| final OperationResult result = await residentWebRunner.restart(fullRestart: true); |
| |
| expect(testLogger.statusText, contains('Restarted application in')); |
| expect(result.code, 0); |
| verify(mockResidentCompiler.accept()).called(2); |
| // ensure that analytics are sent. |
| verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{ |
| 'cd27': 'web-javascript', |
| 'cd28': null, |
| 'cd29': 'false', |
| 'cd30': 'true', |
| })).called(1); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-incremental-restart', any)); |
| }, overrides: <Type, Generator>{ |
| Usage: () => MockFlutterUsage(), |
| FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true), |
| })); |
| |
| test('Can hot restart after attaching', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockWebFs.recompile()).thenAnswer((Invocation invocation) async { |
| return true; |
| }); |
| when(mockVmService.callServiceExtension('fullReload')).thenAnswer((Invocation _) async { |
| return Response.parse(<String, Object>{'type': 'Success'}); |
| }); |
| final OperationResult result = await residentWebRunner.restart(fullRestart: true); |
| |
| expect(testLogger.statusText, contains('Restarted application in')); |
| expect(result.code, 0); |
| // ensure that analytics are sent. |
| verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{ |
| 'cd27': 'web-javascript', |
| 'cd28': null, |
| 'cd29': 'false', |
| 'cd30': 'true', |
| })).called(1); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any)); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any)); |
| verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1); |
| }, overrides: <Type, Generator>{ |
| Usage: () => MockFlutterUsage(), |
| })); |
| |
| test('Selects Dwds runner in profile mode with incremental compiler enabled', () => testbed.run(() async { |
| final ResidentWebRunner residentWebRunner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile), |
| ipv6: true, |
| stayResident: true, |
| dartDefines: const <String>[], |
| urlTunneller: null, |
| ) as ResidentWebRunner; |
| |
| expect(residentWebRunner.runtimeType.toString(), '_DwdsResidentWebRunner'); |
| }, overrides: <Type, Generator>{ |
| FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true), |
| })); |
| |
| test('Fails on compilation errors in hot restart', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockWebFs.recompile()).thenAnswer((Invocation _) async { |
| return false; |
| }); |
| final OperationResult result = await residentWebRunner.restart(fullRestart: true); |
| |
| expect(result.code, 1); |
| expect(result.message, contains('Failed to recompile application.')); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any)); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any)); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-recompile', any)); |
| }, overrides: <Type, Generator>{ |
| Usage: () => MockFlutterUsage(), |
| })); |
| |
| test('Fails on vmservice response error for hot restart', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockWebFs.recompile()).thenAnswer((Invocation _) async { |
| return true; |
| }); |
| when(mockVmService.callServiceExtension('fullReload')).thenAnswer((Invocation _) async { |
| return Response.parse(<String, Object>{'type': 'Failed'}); |
| }); |
| final OperationResult result = await residentWebRunner.restart(fullRestart: true); |
| |
| expect(result.code, 1); |
| expect(result.message, contains('Failed')); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any)); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any)); |
| verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1); |
| }, overrides: <Type, Generator>{ |
| Usage: () => MockFlutterUsage(), |
| })); |
| |
| test('Fails on vmservice response error for hot reload', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockWebFs.recompile()).thenAnswer((Invocation _) async { |
| return true; |
| }); |
| when(mockVmService.callServiceExtension('hotRestart')).thenAnswer((Invocation _) async { |
| return Response.parse(<String, Object>{'type': 'Failed'}); |
| }); |
| final OperationResult result = await residentWebRunner.restart(fullRestart: false); |
| |
| expect(result.code, 1); |
| expect(result.message, contains('Failed')); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any)); |
| verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any)); |
| verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1); |
| }, overrides: <Type, Generator>{ |
| Usage: () => MockFlutterUsage(), |
| })); |
| |
| test('Fails on vmservice RpcError', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockWebFs.recompile()).thenAnswer((Invocation _) async { |
| return true; |
| }); |
| when(mockVmService.callServiceExtension('hotRestart')).thenThrow(RPCError('', 2, '123')); |
| final OperationResult result = await residentWebRunner.restart(fullRestart: false); |
| |
| expect(result.code, 1); |
| expect(result.message, contains('Page requires refresh')); |
| })); |
| |
| test('printHelp without details has web warning', () => testbed.run(() async { |
| residentWebRunner.printHelp(details: false); |
| |
| expect(testLogger.statusText, contains('Warning')); |
| expect(testLogger.statusText, contains('https://flutter.dev/web')); |
| expect(testLogger.statusText, isNot(contains('https://flutter.dev/web.'))); |
| })); |
| |
| test('debugDumpApp', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| await residentWebRunner.debugDumpApp(); |
| |
| verify(mockVmService.callServiceExtension('ext.flutter.debugDumpApp')).called(1); |
| })); |
| |
| test('debugDumpLayerTree', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| await residentWebRunner.debugDumpLayerTree(); |
| |
| verify(mockVmService.callServiceExtension('ext.flutter.debugDumpLayerTree')).called(1); |
| })); |
| |
| test('debugDumpRenderTree', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| await residentWebRunner.debugDumpRenderTree(); |
| |
| verify(mockVmService.callServiceExtension('ext.flutter.debugDumpRenderTree')).called(1); |
| })); |
| |
| test('debugDumpSemanticsTreeInTraversalOrder', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| await residentWebRunner.debugDumpSemanticsTreeInTraversalOrder(); |
| |
| verify(mockVmService.callServiceExtension('ext.flutter.debugDumpSemanticsTreeInTraversalOrder')).called(1); |
| })); |
| |
| test('debugDumpSemanticsTreeInInverseHitTestOrder', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| await residentWebRunner.debugDumpSemanticsTreeInInverseHitTestOrder(); |
| |
| verify(mockVmService.callServiceExtension('ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder')).called(1); |
| })); |
| |
| test('debugToggleDebugPaintSizeEnabled', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockVmService.callServiceExtension('ext.flutter.debugPaint')) |
| .thenAnswer((Invocation _) async { |
| return Response.parse(<String, Object>{'enabled': false}); |
| }); |
| await residentWebRunner.debugToggleDebugPaintSizeEnabled(); |
| |
| verify(mockVmService.callServiceExtension('ext.flutter.debugPaint', |
| args: <String, Object>{'enabled': true})).called(1); |
| })); |
| |
| |
| test('debugTogglePerformanceOverlayOverride', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockVmService.callServiceExtension('ext.flutter.showPerformanceOverlay')) |
| .thenAnswer((Invocation _) async { |
| return Response.parse(<String, Object>{'enabled': false}); |
| }); |
| |
| await residentWebRunner.debugTogglePerformanceOverlayOverride(); |
| |
| verify(mockVmService.callServiceExtension('ext.flutter.showPerformanceOverlay', |
| args: <String, Object>{'enabled': true})).called(1); |
| })); |
| |
| test('debugToggleWidgetInspector', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockVmService.callServiceExtension('ext.flutter.debugToggleWidgetInspector')) |
| .thenAnswer((Invocation _) async { |
| return Response.parse(<String, Object>{'enabled': false}); |
| }); |
| |
| await residentWebRunner.debugToggleWidgetInspector(); |
| |
| verify(mockVmService.callServiceExtension('ext.flutter.debugToggleWidgetInspector', |
| args: <String, Object>{'enabled': true})).called(1); |
| })); |
| |
| test('debugToggleProfileWidgetBuilds', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockVmService.callServiceExtension('ext.flutter.profileWidgetBuilds')) |
| .thenAnswer((Invocation _) async { |
| return Response.parse(<String, Object>{'enabled': false}); |
| }); |
| |
| await residentWebRunner.debugToggleProfileWidgetBuilds(); |
| |
| verify(mockVmService.callServiceExtension('ext.flutter.profileWidgetBuilds', |
| args: <String, Object>{'enabled': true})).called(1); |
| })); |
| |
| test('debugTogglePlatform', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| when(mockVmService.callServiceExtension('ext.flutter.platformOverride')) |
| .thenAnswer((Invocation _) async { |
| return Response.parse(<String, Object>{'value': 'iOS'}); |
| }); |
| |
| await residentWebRunner.debugTogglePlatform(); |
| |
| expect(testLogger.statusText, contains('Switched operating system to android')); |
| verify(mockVmService.callServiceExtension('ext.flutter.platformOverride', |
| args: <String, Object>{'value': 'android'})).called(1); |
| })); |
| |
| test('cleanup of resources is safe to call multiple times', () => testbed.run(() async { |
| _setupMocks(); |
| bool debugClosed = false; |
| when(mockChromeDevice.stopApp(any)).thenAnswer((Invocation invocation) async { |
| if (debugClosed) { |
| throw StateError('debug connection closed twice'); |
| } |
| debugClosed = true; |
| return true; |
| }); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| |
| await residentWebRunner.exit(); |
| await residentWebRunner.exit(); |
| |
| verifyNever(mockDebugConnection.close()); |
| })); |
| |
| test('cleans up Chrome if tab is closed', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<void> onDone = Completer<void>(); |
| when(mockDebugConnection.onDone).thenAnswer((Invocation invocation) { |
| return onDone.future; |
| }); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Future<int> result = residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ); |
| await connectionInfoCompleter.future; |
| onDone.complete(); |
| |
| await result; |
| verify(mockWebFs.stop()).called(1); |
| })); |
| |
| test('Prints target and device name on run', () => testbed.run(() async { |
| _setupMocks(); |
| when(mockChromeDevice.name).thenReturn('Chromez'); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| |
| expect(testLogger.statusText, contains('Launching ${fs.path.join('lib', 'main.dart')} on Chromez in debug mode')); |
| })); |
| |
| test('Sends launched app.webLaunchUrl event for Chrome device', () => testbed.run(() async { |
| _setupMocks(); |
| when(mockFlutterDevice.device).thenReturn(ChromeDevice()); |
| |
| final DelegateLogger delegateLogger = logger as DelegateLogger; |
| final MockStatus mockStatus = MockStatus(); |
| delegateLogger.status = mockStatus; |
| final ResidentWebRunner runner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| ipv6: true, |
| stayResident: true, |
| dartDefines: const <String>[], |
| urlTunneller: null, |
| ) as ResidentWebRunner; |
| |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(runner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| |
| // Ensure we got the URL and that it was already launched. |
| verify(logger.sendEvent( |
| 'app.webLaunchUrl', |
| argThat(allOf( |
| containsPair('url', 'http://localhost:8765/app/'), |
| containsPair('launched', true), |
| )) |
| )); |
| }, overrides: <Type, Generator>{ |
| Logger: () => DelegateLogger(MockLogger()), |
| ChromeLauncher: () => MockChromeLauncher(), |
| })); |
| |
| test('Sends unlaunched app.webLaunchUrl event for Web Server device', () => testbed.run(() async { |
| _setupMocks(); |
| when(mockFlutterDevice.device).thenReturn(WebServerDevice()); |
| |
| final DelegateLogger delegateLogger = logger as DelegateLogger; |
| final MockStatus mockStatus = MockStatus(); |
| delegateLogger.status = mockStatus; |
| final ResidentWebRunner runner = DwdsWebRunnerFactory().createWebRunner( |
| mockFlutterDevice, |
| flutterProject: FlutterProject.current(), |
| debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), |
| ipv6: true, |
| stayResident: true, |
| dartDefines: const <String>[], |
| urlTunneller: null, |
| ) as ResidentWebRunner; |
| |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| unawaited(runner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| )); |
| await connectionInfoCompleter.future; |
| |
| // Ensure we got the URL and that it was not already launched. |
| verify(logger.sendEvent( |
| 'app.webLaunchUrl', |
| argThat(allOf( |
| containsPair('url', 'http://localhost:8765/app/'), |
| containsPair('launched', false), |
| )) |
| )); |
| }, overrides: <Type, Generator>{ |
| Logger: () => DelegateLogger(MockLogger()) |
| })); |
| |
| test('Successfully turns WebSocketException into ToolExit', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Completer<void> unhandledErrorCompleter = Completer<void>(); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| unawaited(unhandledErrorCompleter.future.then((void value) { |
| throw const WebSocketException(); |
| })); |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| |
| final Future<void> expectation = expectLater(() => residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ), throwsA(isInstanceOf<ToolExit>())); |
| |
| unhandledErrorCompleter.complete(); |
| await expectation; |
| })); |
| |
| test('Successfully turns AppConnectionException into ToolExit', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Completer<void> unhandledErrorCompleter = Completer<void>(); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| unawaited(unhandledErrorCompleter.future.then((void value) { |
| throw AppConnectionException('Could not connect to application with appInstanceId: c0ae0750-ee91-11e9-cea6-35d95a968356'); |
| })); |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| |
| final Future<void> expectation = expectLater(() => residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ), throwsA(isInstanceOf<ToolExit>())); |
| |
| unhandledErrorCompleter.complete(); |
| await expectation; |
| })); |
| |
| test('Successfully turns ChromeDebugError into ToolExit', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Completer<void> unhandledErrorCompleter = Completer<void>(); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| unawaited(unhandledErrorCompleter.future.then((void value) { |
| throw ChromeDebugException(<String, dynamic>{}); |
| })); |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| |
| final Future<void> expectation = expectLater(() => residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ), throwsA(isInstanceOf<ToolExit>())); |
| |
| unhandledErrorCompleter.complete(); |
| await expectation; |
| })); |
| |
| test('Successfully turns OptionsSkew error into ToolExit', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Completer<void> unhandledErrorCompleter = Completer<void>(); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| unawaited(unhandledErrorCompleter.future.then((void value) { |
| throw OptionsSkew(); |
| })); |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| |
| final Future<void> expectation = expectLater(() => residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ), throwsA(isInstanceOf<ToolExit>())); |
| |
| unhandledErrorCompleter.complete(); |
| await expectation; |
| })); |
| |
| test('Successfully turns VersionSkew error into ToolExit', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Completer<void> unhandledErrorCompleter = Completer<void>(); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| unawaited(unhandledErrorCompleter.future.then((void value) { |
| throw VersionSkew(); |
| })); |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| |
| final Future<void> expectation = expectLater(() => residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ), throwsA(isInstanceOf<ToolExit>())); |
| |
| unhandledErrorCompleter.complete(); |
| await expectation; |
| })); |
| |
| test('Successfully turns failed startup StateError error into ToolExit', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Completer<void> unhandledErrorCompleter = Completer<void>(); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| unawaited(unhandledErrorCompleter.future.then((void value) { |
| throw StateError('Unable to start build daemon'); |
| })); |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| |
| final Future<void> expectation = expectLater(() => residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ), throwsA(isInstanceOf<ToolExit>())); |
| |
| unhandledErrorCompleter.complete(); |
| await expectation; |
| })); |
| |
| |
| test('Rethrows Exception type', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Completer<void> unhandledErrorCompleter = Completer<void>(); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| unawaited(unhandledErrorCompleter.future.then((void value) { |
| throw Exception('Something went wrong'); |
| })); |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| |
| final Future<void> expectation = expectLater(() => residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ), throwsA(isInstanceOf<Exception>())); |
| |
| unhandledErrorCompleter.complete(); |
| await expectation; |
| })); |
| |
| test('Successfully turns MissingPortFile error into ToolExit', () => testbed.run(() async { |
| _setupMocks(); |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Completer<void> unhandledErrorCompleter = Completer<void>(); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| unawaited(unhandledErrorCompleter.future.then((void value) { |
| throw MissingPortFile(); |
| })); |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| |
| final Future<void> expectation = expectLater(() => residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ), throwsA(isInstanceOf<ToolExit>())); |
| |
| unhandledErrorCompleter.complete(); |
| await expectation; |
| })); |
| |
| test('Rethrows unknown exception type from web tooling', () => testbed.run(() async { |
| _setupMocks(); |
| final DelegateLogger delegateLogger = logger as DelegateLogger; |
| final MockStatus mockStatus = MockStatus(); |
| delegateLogger.status = mockStatus; |
| final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); |
| final Completer<void> unhandledErrorCompleter = Completer<void>(); |
| when(mockWebFs.connect(any)).thenAnswer((Invocation _) async { |
| unawaited(unhandledErrorCompleter.future.then((void value) { |
| throw StateError('Something went wrong'); |
| })); |
| return ConnectionResult(mockAppConnection, mockDebugConnection); |
| }); |
| |
| final Future<void> expectation = expectLater(() => residentWebRunner.run( |
| connectionInfoCompleter: connectionInfoCompleter, |
| ), throwsA(isInstanceOf<StateError>())); |
| |
| unhandledErrorCompleter.complete(); |
| await expectation; |
| verify(mockStatus.stop()).called(2); |
| }, overrides: <Type, Generator>{ |
| Logger: () => DelegateLogger(BufferLogger()) |
| })); |
| } |
| |
| class MockChromeLauncher extends Mock implements ChromeLauncher {} |
| class MockFlutterUsage extends Mock implements Usage {} |
| class MockChromeDevice extends Mock implements ChromeDevice {} |
| class MockBuildDaemonCreator extends Mock implements BuildDaemonCreator {} |
| class MockFlutterWebFs extends Mock implements WebFs {} |
| class MockDebugConnection extends Mock implements DebugConnection {} |
| class MockAppConnection extends Mock implements AppConnection {} |
| class MockVmService extends Mock implements VmService {} |
| class MockStatus extends Mock implements Status {} |
| class MockFlutterDevice extends Mock implements FlutterDevice {} |
| class MockWebDevFS extends Mock implements DevFS {} |
| class MockResidentCompiler extends Mock implements ResidentCompiler {} |
| class MockChrome extends Mock implements Chrome {} |
| class MockChromeConnection extends Mock implements ChromeConnection {} |
| class MockChromeTab extends Mock implements ChromeTab {} |
| class MockWipConnection extends Mock implements WipConnection {} |
| class MockWipDebugger extends Mock implements WipDebugger {} |
| class MockLogger extends Mock implements Logger {} |
| class MockWebServerDevice extends Mock implements WebServerDevice {} |