flutter/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
Jonah Williams 07caa0fbfe
[flutter_tools] Add plumbing for widget cache (#61766)
To support #61407 , the tool needs to check if a single widget reload is feasible, and then conditionally perform a fast reassemble.

To accomplish this, the FlutterDevice class will have a WidgetCache injected. This will eventually contain the logic for parsing the invalidated dart script. Concurrent with the devFS update, the widget cache will be updated/checked if a single widget reload is feasible. If so, an expression evaluation with the target type is performed and the success is communicated through the devFS result. An integration test which demonstrates that this works is already present in https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/integration.shard/hot_reload_test.dart#L86

Finally, when actually performing the reassemble the tool simply checks if this flag has been set and calls the alternative reassemble method.

Cleanups:

Remove modules, as this is unused now.
2020-07-20 14:03:44 -07:00

1717 lines
68 KiB
Dart

// 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 'dart:io';
import 'package:dwds/dwds.dart';
import 'package:file/memory.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/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_runner/devfs_web.dart';
import 'package:flutter_tools/src/build_runner/resident_web_runner.dart';
import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
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/vmservice.dart';
import 'package:flutter_tools/src/web/chrome.dart';
import 'package:flutter_tools/src/web/web_device.dart';
import 'package:mockito/mockito.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
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';
const List<VmServiceExpectation> kAttachLogExpectations = <VmServiceExpectation>[
FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Stdout',
},
),
FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Stderr',
},
)
];
const List<VmServiceExpectation> kAttachIsolateExpectations = <VmServiceExpectation>[
FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Isolate'
}
),
FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Extension',
},
),
FakeVmServiceRequest(
method: 'registerService',
args: <String, Object>{
'service': 'reloadSources',
'alias': 'FlutterTools',
}
)
];
const List<VmServiceExpectation> kAttachExpectations = <VmServiceExpectation>[
...kAttachLogExpectations,
...kAttachIsolateExpectations,
];
void main() {
MockDebugConnection mockDebugConnection;
MockChromeDevice mockChromeDevice;
MockAppConnection mockAppConnection;
MockFlutterDevice mockFlutterDevice;
MockWebDevFS mockWebDevFS;
MockResidentCompiler mockResidentCompiler;
MockChrome mockChrome;
MockChromeConnection mockChromeConnection;
MockChromeTab mockChromeTab;
MockWipConnection mockWipConnection;
MockWipDebugger mockWipDebugger;
MockWebServerDevice mockWebServerDevice;
MockDevice mockDevice;
FakeVmServiceHost fakeVmServiceHost;
FileSystem fileSystem;
ProcessManager processManager;
setUp(() {
fileSystem = MemoryFileSystem.test();
processManager = FakeProcessManager.any();
mockDebugConnection = MockDebugConnection();
mockDevice = MockDevice();
mockAppConnection = MockAppConnection();
mockFlutterDevice = MockFlutterDevice();
mockWebDevFS = MockWebDevFS();
mockResidentCompiler = MockResidentCompiler();
mockChrome = MockChrome();
mockChromeConnection = MockChromeConnection();
mockChromeTab = MockChromeTab();
mockWipConnection = MockWipConnection();
mockWipDebugger = MockWipDebugger();
mockWebServerDevice = MockWebServerDevice();
when(mockFlutterDevice.devFS).thenReturn(mockWebDevFS);
when(mockFlutterDevice.device).thenReturn(mockDevice);
when(mockWebDevFS.connect(any)).thenAnswer((Invocation invocation) async {
return ConnectionResult(mockAppConnection, mockDebugConnection);
});
fileSystem.file('.packages').writeAsStringSync('\n');
});
void _setupMocks() {
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file('lib/main.dart').createSync(recursive: true);
fileSystem.file('web/index.html').createSync(recursive: true);
when(mockWebDevFS.update(
mainUri: anyNamed('mainUri'),
target: anyNamed('target'),
bundle: anyNamed('bundle'),
firstBuildTime: anyNamed('firstBuildTime'),
bundleFirstUpload: anyNamed('bundleFirstUpload'),
generator: anyNamed('generator'),
fullRestart: anyNamed('fullRestart'),
dillOutputPath: anyNamed('dillOutputPath'),
projectRootPath: anyNamed('projectRootPath'),
pathToReload: anyNamed('pathToReload'),
invalidatedFiles: anyNamed('invalidatedFiles'),
trackWidgetCreation: anyNamed('trackWidgetCreation'),
packageConfig: anyNamed('packageConfig'),
)).thenAnswer((Invocation _) async {
return UpdateFSReport(success: true, syncedBytes: 0);
});
when(mockDebugConnection.vmService).thenAnswer((Invocation invocation) {
return fakeVmServiceHost.vmService;
});
when(mockDebugConnection.onDone).thenAnswer((Invocation invocation) {
return Completer<void>().future;
});
when(mockDebugConnection.uri).thenReturn('ws://127.0.0.1/abcd/');
when(mockFlutterDevice.devFS).thenReturn(mockWebDevFS);
when(mockWebDevFS.sources).thenReturn(<Uri>[]);
when(mockWebDevFS.baseUri).thenReturn(Uri.parse('http://localhost:12345'));
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);
}
testUsingContext('runner with web server device does not support debugging without --start-paused', () {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
when(mockFlutterDevice.device).thenReturn(WebServerDevice(
logger: BufferLogger.test(),
));
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
ipv6: true,
stayResident: true,
urlTunneller: null,
) as ResidentWebRunner;
expect(profileResidentWebRunner.debuggingEnabled, false);
when(mockFlutterDevice.device).thenReturn(MockChromeDevice());
expect(residentWebRunner.debuggingEnabled, true);
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('runner with web server device supports debugging with --start-paused', () {
fileSystem.file('.packages')
..createSync(recursive: true)
..writeAsStringSync('\n');
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockFlutterDevice.device).thenReturn(WebServerDevice(
logger: BufferLogger.test(),
));
final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, startPaused: true),
ipv6: true,
stayResident: true,
urlTunneller: null,
);
expect(profileResidentWebRunner.uri, mockWebDevFS.baseUri);
expect(profileResidentWebRunner.debuggingEnabled, true);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('profile does not supportsServiceProtocol', () {
fileSystem.file('.packages')
..createSync(recursive: true)
..writeAsStringSync('\n');
final ResidentRunner residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
ipv6: true,
stayResident: true,
urlTunneller: null,
) as ResidentWebRunner;
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
when(mockFlutterDevice.device).thenReturn(mockChromeDevice);
final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile),
ipv6: true,
stayResident: true,
urlTunneller: null,
);
expect(profileResidentWebRunner.supportsServiceProtocol, false);
expect(residentWebRunner.supportsServiceProtocol, true);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Exits on run if target file does not exist', () async {
fileSystem.file('.packages')
..createSync(recursive: true)
..writeAsStringSync('\n');
final ResidentRunner residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
ipv6: true,
stayResident: true,
urlTunneller: null,
) as ResidentWebRunner;
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file(fileSystem.path.join('web', 'index.html'))
.createSync(recursive: true);
expect(await residentWebRunner.run(), 1);
final String absoluteMain = fileSystem.path.absolute(fileSystem.path.join('lib', 'main.dart'));
expect(testLogger.errorText, contains('Tried to run $absoluteMain, but that file does not exist.'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Can successfully run and connect to vmservice', () async {
fileSystem.file('.packages')
..createSync(recursive: true)
..writeAsStringSync('\n');
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
_setupMocks();
final DelegateLogger delegateLogger = globals.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(status.stop()).called(1);
verify(pub.get(
context: PubContext.pubGet,
directory: anyNamed('directory'),
generateSyntheticPackage: false,
)).called(1);
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()),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('WebRunner copies compiled app.dill to cache during startup', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
_setupMocks();
residentWebRunner.artifactDirectory.childFile('app.dill').writeAsStringSync('ABC');
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
expect(await fileSystem.file(fileSystem.path.join('build', 'cache.dill')).readAsString(), 'ABC');
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
// Regression test for https://github.com/flutter/flutter/issues/60613
testUsingContext('ResidentWebRunner calls appFailedToStart if initial compilation fails', () async {
_setupMocks();
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fileSystem.file(globals.fs.path.join('lib', 'main.dart'))
.createSync(recursive: true);
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
when(mockWebDevFS.update(
mainUri: anyNamed('mainUri'),
target: anyNamed('target'),
bundle: anyNamed('bundle'),
firstBuildTime: anyNamed('firstBuildTime'),
bundleFirstUpload: anyNamed('bundleFirstUpload'),
generator: anyNamed('generator'),
fullRestart: anyNamed('fullRestart'),
dillOutputPath: anyNamed('dillOutputPath'),
projectRootPath: anyNamed('projectRootPath'),
pathToReload: anyNamed('pathToReload'),
invalidatedFiles: anyNamed('invalidatedFiles'),
trackWidgetCreation: anyNamed('trackWidgetCreation'),
packageConfig: anyNamed('packageConfig'),
)).thenAnswer((Invocation _) async {
return UpdateFSReport(success: false, syncedBytes: 0);
});
expect(await residentWebRunner.run(), 1);
// Completing this future ensures that the daemon can exit correctly.
expect(await residentWebRunner.waitForAppToFinish(), 1);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Can successfully run without an index.html including status warning', () async {
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
_setupMocks();
fileSystem.file(fileSystem.path.join('web', 'index.html'))
.deleteSync();
final ResidentWebRunner residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
ipv6: true,
stayResident: false,
urlTunneller: null,
) as ResidentWebRunner;
expect(await residentWebRunner.run(), 0);
expect(testLogger.statusText,
contains('This application is not configured to build on the web'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Can successfully run and disconnect with --no-resident', () async {
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
_setupMocks();
final ResidentRunner residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
ipv6: true,
stayResident: false,
urlTunneller: null,
) as ResidentWebRunner;
expect(await residentWebRunner.run(), 0);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Listens to stdout and stderr streams before running main', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachLogExpectations,
FakeVmServiceStreamResponse(
streamId: 'Stdout',
event: vm_service.Event(
timestamp: 0,
kind: vm_service.EventStreams.kStdout,
bytes: base64.encode(utf8.encode('THIS MESSAGE IS IMPORTANT'))
),
),
FakeVmServiceStreamResponse(
streamId: 'Stderr',
event: vm_service.Event(
timestamp: 0,
kind: vm_service.EventStreams.kStderr,
bytes: base64.encode(utf8.encode('SO IS THIS'))
),
),
...kAttachIsolateExpectations,
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
expect(testLogger.statusText, contains('THIS MESSAGE IS IMPORTANT'));
expect(testLogger.statusText, contains('SO IS THIS'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Listens to extension events with structured errors', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
final Map<String, String> extensionData = <String, String>{
'test': 'data',
'renderedErrorText': 'error text',
};
final Map<String, String> emptyExtensionData = <String, String>{
'test': 'data',
'renderedErrorText': '',
};
final Map<String, String> nonStructuredErrorData = <String, String>{
'other': 'other stuff',
};
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
FakeVmServiceStreamResponse(
streamId: 'Extension',
event: vm_service.Event(
timestamp: 0,
extensionKind: 'Flutter.Error',
extensionData: vm_service.ExtensionData.parse(extensionData),
kind: vm_service.EventStreams.kExtension,
),
),
// Empty error text should not break anything.
FakeVmServiceStreamResponse(
streamId: 'Extension',
event: vm_service.Event(
timestamp: 0,
extensionKind: 'Flutter.Error',
extensionData: vm_service.ExtensionData.parse(emptyExtensionData),
kind: vm_service.EventStreams.kExtension,
),
),
// This is not Flutter.Error kind data, so it should not be logged.
FakeVmServiceStreamResponse(
streamId: 'Extension',
event: vm_service.Event(
timestamp: 0,
extensionKind: 'Other',
extensionData: vm_service.ExtensionData.parse(nonStructuredErrorData),
kind: vm_service.EventStreams.kExtension,
),
),
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
// Need these to run events, otherwise expect statements below run before
// structured errors are processed.
await null;
await null;
await null;
expect(testLogger.statusText, contains('\nerror text'));
expect(testLogger.statusText, isNot(contains('other stuff')));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Does not run main with --start-paused', () async {
final ResidentRunner residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, startPaused: true),
ipv6: true,
stayResident: true,
urlTunneller: null,
) as ResidentWebRunner;
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
verifyNever(mockAppConnection.runMain());
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Can hot reload after attaching', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'hotRestart',
jsonResponse: <String, Object>{
'type': 'Success',
}
),
]);
_setupMocks();
final ChromiumLauncher chromiumLauncher = MockChromeLauncher();
when(chromiumLauncher.launch(any, cacheDir: anyNamed('cacheDir')))
.thenAnswer((Invocation invocation) async {
return mockChrome;
});
when(chromiumLauncher.connectedInstance).thenAnswer((Invocation invocation) async {
return mockChrome;
});
when(mockFlutterDevice.device).thenReturn(GoogleChromeDevice(
fileSystem: fileSystem,
chromiumLauncher: chromiumLauncher,
logger: globals.logger,
platform: FakePlatform(operatingSystem: 'linux'),
processManager: FakeProcessManager.any(),
));
when(chromiumLauncher.canFindExecutable()).thenReturn(true);
chromiumLauncher.testLaunchChromium(mockChrome);
when(mockWebDevFS.update(
mainUri: anyNamed('mainUri'),
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'),
packageConfig: anyNamed('packageConfig'),
)).thenAnswer((Invocation invocation) async {
// Generated entrypoint file in temp dir.
expect(invocation.namedArguments[#mainUri].toString(), contains('entrypoint.dart'));
return UpdateFSReport(success: true);
});
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future;
expect(debugConnectionInfo, isNotNull);
final OperationResult result = await residentWebRunner.restart(fullRestart: false);
expect(testLogger.statusText, contains('Restarted application in'));
expect(result.code, 0);
verify(mockResidentCompiler.accept()).called(2);
// ensure that analytics are sent.
final Map<String, String> config = verify(globals.flutterUsage.sendEvent('hot', 'restart',
parameters: captureAnyNamed('parameters'))).captured.first as Map<String, String>;
expect(config, allOf(<Matcher>[
containsPair('cd27', 'web-javascript'),
containsPair('cd28', ''),
containsPair('cd29', 'false'),
containsPair('cd30', 'true'),
]));
verify(globals.flutterUsage.sendTiming('hot', 'web-incremental-restart', any)).called(1);
}, overrides: <Type, Generator>{
Usage: () => MockFlutterUsage(),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Can hot restart after attaching', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'hotRestart',
jsonResponse: <String, Object>{
'type': 'Success',
}
),
]);
_setupMocks();
final ChromiumLauncher chromiumLauncher = MockChromeLauncher();
when(chromiumLauncher.launch(any, cacheDir: anyNamed('cacheDir')))
.thenAnswer((Invocation invocation) async {
return mockChrome;
});
when(chromiumLauncher.connectedInstance).thenAnswer((Invocation invocation) async {
return mockChrome;
});
when(chromiumLauncher.canFindExecutable()).thenReturn(true);
when(mockFlutterDevice.device).thenReturn(GoogleChromeDevice(
fileSystem: fileSystem,
chromiumLauncher: chromiumLauncher,
logger: globals.logger,
platform: FakePlatform(operatingSystem: 'linux'),
processManager: FakeProcessManager.any(),
));
chromiumLauncher.testLaunchChromium(mockChrome);
Uri entrypointFileUri;
when(mockWebDevFS.update(
mainUri: anyNamed('mainUri'),
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'),
packageConfig: anyNamed('packageConfig'),
)).thenAnswer((Invocation invocation) async {
entrypointFileUri = invocation.namedArguments[#mainUri] as Uri;
return UpdateFSReport(success: true);
});
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
// Ensure that generated entrypoint is generated correctly.
expect(entrypointFileUri, isNotNull);
final String entrypointContents = fileSystem.file(entrypointFileUri).readAsStringSync();
expect(entrypointContents, contains('// Flutter web bootstrap script'));
expect(entrypointContents, contains("import 'dart:ui' as ui;"));
expect(entrypointContents, contains('await ui.webOnlyInitializePlatform();'));
expect(testLogger.statusText, contains('Restarted application in'));
expect(result.code, 0);
verify(mockResidentCompiler.accept()).called(2);
// ensure that analytics are sent.
final Map<String, String> config = verify(globals.flutterUsage.sendEvent('hot', 'restart',
parameters: captureAnyNamed('parameters'))).captured.first as Map<String, String>;
expect(config, allOf(<Matcher>[
containsPair('cd27', 'web-javascript'),
containsPair('cd28', ''),
containsPair('cd29', 'false'),
containsPair('cd30', 'true'),
]));
verify(globals.flutterUsage.sendTiming('hot', 'web-incremental-restart', any)).called(1);
}, overrides: <Type, Generator>{
Usage: () => MockFlutterUsage(),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Can hot restart after attaching with web-server device', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests :kAttachExpectations);
_setupMocks();
when(mockFlutterDevice.device).thenReturn(mockWebServerDevice);
when(mockWebDevFS.update(
mainUri: anyNamed('mainUri'),
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'),
packageConfig: anyNamed('packageConfig'),
)).thenAnswer((Invocation invocation) async {
return UpdateFSReport(success: true);
});
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.
verifyNever(globals.flutterUsage.sendTiming('hot', 'web-incremental-restart', any));
}, overrides: <Type, Generator>{
Usage: () => MockFlutterUsage(),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('web resident runner is debuggable', () {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
expect(residentWebRunner.debuggingEnabled, true);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('web resident runner can toggle CanvasKit', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final WebAssetServer webAssetServer = WebAssetServer(null, null, null, null, null, null);
when(mockWebDevFS.webAssetServer).thenReturn(webAssetServer);
expect(residentWebRunner.supportsCanvasKit, true);
expect(webAssetServer.canvasKitRendering, false);
final bool toggleResult = await residentWebRunner.toggleCanvaskit();
expect(webAssetServer.canvasKitRendering, true);
expect(toggleResult, true);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Exits when initial compile fails', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockWebDevFS.update(
mainUri: anyNamed('mainUri'),
target: anyNamed('target'),
bundle: anyNamed('bundle'),
firstBuildTime: anyNamed('firstBuildTime'),
bundleFirstUpload: anyNamed('bundleFirstUpload'),
generator: anyNamed('generator'),
fullRestart: anyNamed('fullRestart'),
dillOutputPath: anyNamed('dillOutputPath'),
projectRootPath: anyNamed('projectRootPath'),
pathToReload: anyNamed('pathToReload'),
invalidatedFiles: anyNamed('invalidatedFiles'),
packageConfig: anyNamed('packageConfig'),
trackWidgetCreation: anyNamed('trackWidgetCreation'),
)).thenAnswer((Invocation _) async {
return UpdateFSReport(success: false, syncedBytes: 0);
});
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
expect(await residentWebRunner.run(), 1);
verifyNever(globals.flutterUsage.sendTiming('hot', 'web-restart', any));
}, overrides: <Type, Generator>{
Usage: () => MockFlutterUsage(),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Faithfully displays stdout messages with leading/trailing spaces', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachLogExpectations,
FakeVmServiceStreamResponse(
streamId: 'Stdout',
event: vm_service.Event(
timestamp: 0,
kind: vm_service.EventStreams.kStdout,
bytes: base64.encode(
utf8.encode(' This is a message with 4 leading and trailing spaces '),
),
),
),
...kAttachIsolateExpectations,
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
expect(testLogger.statusText,
contains(' This is a message with 4 leading and trailing spaces '));
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Fails on compilation errors in hot restart', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
when(mockWebDevFS.update(
mainUri: anyNamed('mainUri'),
target: anyNamed('target'),
bundle: anyNamed('bundle'),
firstBuildTime: anyNamed('firstBuildTime'),
bundleFirstUpload: anyNamed('bundleFirstUpload'),
generator: anyNamed('generator'),
fullRestart: anyNamed('fullRestart'),
dillOutputPath: anyNamed('dillOutputPath'),
projectRootPath: anyNamed('projectRootPath'),
pathToReload: anyNamed('pathToReload'),
invalidatedFiles: anyNamed('invalidatedFiles'),
packageConfig: anyNamed('packageConfig'),
trackWidgetCreation: anyNamed('trackWidgetCreation'),
)).thenAnswer((Invocation _) async {
return UpdateFSReport(success: false, syncedBytes: 0);
});
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
expect(result.code, 1);
expect(result.message, contains('Failed to recompile application.'));
verifyNever(globals.flutterUsage.sendTiming('hot', 'web-restart', any));
}, overrides: <Type, Generator>{
Usage: () => MockFlutterUsage(),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Fails non-fatally on vmservice response error for hot restart', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'hotRestart',
jsonResponse: <String, Object>{
'type': 'Failed',
}
)
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
final OperationResult result = await residentWebRunner.restart(fullRestart: false);
expect(result.code, 0);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Fails fatally on Vm Service error response', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'hotRestart',
// Failed response,
errorCode: RPCErrorCodes.kInternalError,
),
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
final OperationResult result = await residentWebRunner.restart(fullRestart: false);
expect(result.code, 1);
expect(result.message,
contains(RPCErrorCodes.kInternalError.toString()));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('printHelp without details has web warning', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
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.')));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugDumpApp', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.debugDumpApp',
args: <String, Object>{
'isolateId': null,
},
),
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpApp();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugDumpLayerTree', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.debugDumpLayerTree',
args: <String, Object>{
'isolateId': null,
},
),
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpLayerTree();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugDumpRenderTree', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.debugDumpRenderTree',
args: <String, Object>{
'isolateId': null,
},
),
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpRenderTree();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugDumpSemanticsTreeInTraversalOrder', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder',
args: <String, Object>{
'isolateId': null,
},
),
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpSemanticsTreeInTraversalOrder();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugDumpSemanticsTreeInInverseHitTestOrder', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder',
args: <String, Object>{
'isolateId': null,
},
),
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpSemanticsTreeInInverseHitTestOrder();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugToggleDebugPaintSizeEnabled', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.debugPaint',
args: <String, Object>{
'isolateId': null,
},
jsonResponse: <String, Object>{
'enabled': 'false'
},
),
const FakeVmServiceRequest(
method: 'ext.flutter.debugPaint',
args: <String, Object>{
'isolateId': null,
'enabled': 'true',
},
jsonResponse: <String, Object>{
'value': 'true'
},
)
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugToggleDebugPaintSizeEnabled();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugTogglePerformanceOverlayOverride', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.showPerformanceOverlay',
args: <String, Object>{
'isolateId': null,
},
jsonResponse: <String, Object>{
'enabled': 'false'
},
),
const FakeVmServiceRequest(
method: 'ext.flutter.showPerformanceOverlay',
args: <String, Object>{
'isolateId': null,
'enabled': 'true',
},
jsonResponse: <String, Object>{
'enabled': 'true'
},
)
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugTogglePerformanceOverlayOverride();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugToggleInvertOversizedImagesOverride', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.invertOversizedImages',
args: <String, Object>{
'isolateId': null,
},
jsonResponse: <String, Object>{
'enabled': 'false'
},
),
const FakeVmServiceRequest(
method: 'ext.flutter.invertOversizedImages',
args: <String, Object>{
'isolateId': null,
'enabled': 'true',
},
jsonResponse: <String, Object>{
'enabled': 'true'
},
)
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugToggleInvertOversizedImages();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugToggleWidgetInspector', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.inspector.show',
args: <String, Object>{
'isolateId': null,
},
jsonResponse: <String, Object>{
'enabled': 'false'
},
),
const FakeVmServiceRequest(
method: 'ext.flutter.inspector.show',
args: <String, Object>{
'isolateId': null,
'enabled': 'true',
},
jsonResponse: <String, Object>{
'enabled': 'true'
},
)
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugToggleWidgetInspector();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugToggleProfileWidgetBuilds', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.profileWidgetBuilds',
args: <String, Object>{
'isolateId': null,
},
jsonResponse: <String, Object>{
'enabled': 'false'
},
),
const FakeVmServiceRequest(
method: 'ext.flutter.profileWidgetBuilds',
args: <String, Object>{
'isolateId': null,
'enabled': 'true',
},
jsonResponse: <String, Object>{
'enabled': 'true'
},
)
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugToggleProfileWidgetBuilds();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugTogglePlatform', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.platformOverride',
args: <String, Object>{
'isolateId': null,
},
jsonResponse: <String, Object>{
'value': 'iOS'
},
),
const FakeVmServiceRequest(
method: 'ext.flutter.platformOverride',
args: <String, Object>{
'isolateId': null,
'value': 'fuchsia',
},
jsonResponse: <String, Object>{
'value': 'fuchsia'
},
),
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugTogglePlatform();
expect(testLogger.statusText,
contains('Switched operating system to fuchsia'));
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('debugToggleBrightness', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'ext.flutter.brightnessOverride',
args: <String, Object>{
'isolateId': null,
},
jsonResponse: <String, Object>{
'value': 'Brightness.light'
},
),
const FakeVmServiceRequest(
method: 'ext.flutter.brightnessOverride',
args: <String, Object>{
'isolateId': null,
'value': 'Brightness.dark',
},
jsonResponse: <String, Object>{
'value': 'Brightness.dark'
},
),
]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
await residentWebRunner.debugToggleBrightness();
expect(testLogger.statusText,
contains('Changed brightness to Brightness.dark.'));
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('cleanup of resources is safe to call multiple times', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
]);
_setupMocks();
bool debugClosed = false;
when(mockDevice.stopApp(any, userIdentifier: anyNamed('userIdentifier'))).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());
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('cleans up Chrome if tab is closed', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
]);
_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;
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Prints target and device name on run', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
]);
_setupMocks();
when(mockDevice.name).thenReturn('Chromez');
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
expect(testLogger.statusText, contains(
'Launching ${fileSystem.path.join('lib', 'main.dart')} on '
'Chromez in debug mode',
));
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Sends launched app.webLaunchUrl event for Chrome device', () async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachLogExpectations,
...kAttachIsolateExpectations,
]);
_setupMocks();
final ChromiumLauncher chromiumLauncher = MockChromeLauncher();
when(chromiumLauncher.launch(any, cacheDir: anyNamed('cacheDir')))
.thenAnswer((Invocation invocation) async {
return mockChrome;
});
when(chromiumLauncher.connectedInstance).thenAnswer((Invocation invocation) async {
return mockChrome;
});
when(mockFlutterDevice.device).thenReturn(GoogleChromeDevice(
fileSystem: fileSystem,
chromiumLauncher: chromiumLauncher,
logger: globals.logger,
platform: FakePlatform(operatingSystem: 'linux'),
processManager: FakeProcessManager.any(),
));
when(chromiumLauncher.canFindExecutable()).thenReturn(true);
when(mockWebDevFS.create()).thenAnswer((Invocation invocation) async {
return Uri.parse('http://localhost:8765/app/');
});
final MockChrome chrome = MockChrome();
final MockChromeConnection mockChromeConnection = MockChromeConnection();
final MockChromeTab mockChromeTab = MockChromeTab();
final MockWipConnection mockWipConnection = MockWipConnection();
when(mockChromeConnection.getTab(any)).thenAnswer((Invocation invocation) async {
return mockChromeTab;
});
when(mockChromeTab.connect()).thenAnswer((Invocation invocation) async {
return mockWipConnection;
});
when(chrome.chromeConnection).thenReturn(mockChromeConnection);
chromiumLauncher.testLaunchChromium(chrome);
final DelegateLogger delegateLogger = globals.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,
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.
expect((delegateLogger.delegate as BufferLogger).eventText,
contains(json.encode(<String, Object>{
'name': 'app.webLaunchUrl',
'args': <String, Object>{
'url': 'http://localhost:8765/app/',
'launched': true,
},
},
)));
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
Logger: () => DelegateLogger(BufferLogger.test()),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Sends unlaunched app.webLaunchUrl event for Web Server device', () async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockFlutterDevice.device).thenReturn(WebServerDevice(
logger: globals.logger,
));
when(mockWebDevFS.create()).thenAnswer((Invocation invocation) async {
return Uri.parse('http://localhost:8765/app/');
});
final DelegateLogger delegateLogger = globals.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,
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.
expect((delegateLogger.delegate as BufferLogger).eventText,
contains(json.encode(<String, Object>{
'name': 'app.webLaunchUrl',
'args': <String, Object>{
'url': 'http://localhost:8765/app/',
'launched': false,
},
},
)));
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
Logger: () => DelegateLogger(BufferLogger.test()),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Successfully turns WebSocketException into ToolExit', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockWebDevFS.connect(any))
.thenThrow(const WebSocketException());
await expectLater(residentWebRunner.run, throwsToolExit());
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Successfully turns AppConnectionException into ToolExit', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockWebDevFS.connect(any))
.thenThrow(AppConnectionException(''));
await expectLater(residentWebRunner.run, throwsToolExit());
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Successfully turns ChromeDebugError into ToolExit', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockWebDevFS.connect(any))
.thenThrow(ChromeDebugException(<String, dynamic>{}));
await expectLater(residentWebRunner.run, throwsToolExit());
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Rethrows unknown Exception type from dwds', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockWebDevFS.connect(any)).thenThrow(Exception());
await expectLater(residentWebRunner.run, throwsException);
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
testUsingContext('Rethrows unknown Error type from dwds tooling', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
final DelegateLogger delegateLogger = globals.logger as DelegateLogger;
final MockStatus mockStatus = MockStatus();
delegateLogger.status = mockStatus;
when(mockWebDevFS.connect(any)).thenThrow(StateError(''));
await expectLater(residentWebRunner.run, throwsStateError);
verify(mockStatus.stop()).called(1);
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
Logger: () => DelegateLogger(BufferLogger(
terminal: AnsiTerminal(
stdio: null,
platform: const LocalPlatform(),
),
outputPreferences: OutputPreferences.test(),
)),
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Pub: () => MockPub(),
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
});
}
ResidentRunner setUpResidentRunner(FlutterDevice flutterDevice) {
return DwdsWebRunnerFactory().createWebRunner(
flutterDevice,
flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
ipv6: true,
stayResident: true,
urlTunneller: null,
) as ResidentWebRunner;
}
class MockChromeLauncher extends Mock implements ChromiumLauncher {}
class MockFlutterUsage extends Mock implements Usage {}
class MockChromeDevice extends Mock implements ChromiumDevice {}
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 WebDevFS {}
class MockResidentCompiler extends Mock implements ResidentCompiler {}
class MockChrome extends Mock implements Chromium {}
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 MockWebServerDevice extends Mock implements WebServerDevice {}
class MockDevice extends Mock implements Device {}
class MockPub extends Mock implements Pub {}