Unverified Commit 9904a7f4 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Refactor core resident runner logic (#80462)

parent aa9f6a2a
...@@ -41,18 +41,7 @@ class FileSystemUtils { ...@@ -41,18 +41,7 @@ class FileSystemUtils {
/// Appends a number to a filename in order to make it unique under a /// Appends a number to a filename in order to make it unique under a
/// directory. /// directory.
File getUniqueFile(Directory dir, String baseName, String ext) { File getUniqueFile(Directory dir, String baseName, String ext) {
final FileSystem fs = dir.fileSystem; return _getUniqueFile(dir, baseName, ext);
int i = 1;
while (true) {
final String name = '${baseName}_${i.toString().padLeft(2, '0')}.$ext';
final File file = fs.file(_fileSystem.path.join(dir.path, name));
if (!file.existsSync()) {
file.createSync(recursive: true);
return file;
}
i += 1;
}
} }
/// Appends a number to a directory name in order to make it unique under a /// Appends a number to a directory name in order to make it unique under a
...@@ -157,6 +146,27 @@ void copyDirectory( ...@@ -157,6 +146,27 @@ void copyDirectory(
} }
} }
File _getUniqueFile(Directory dir, String baseName, String ext) {
final FileSystem fs = dir.fileSystem;
int i = 1;
while (true) {
final String name = '${baseName}_${i.toString().padLeft(2, '0')}.$ext';
final File file = fs.file(dir.fileSystem.path.join(dir.path, name));
if (!file.existsSync()) {
file.createSync(recursive: true);
return file;
}
i += 1;
}
}
/// Appends a number to a filename in order to make it unique under a
/// directory.
File getUniqueFile(Directory dir, String baseName, String ext) {
return _getUniqueFile(dir, baseName, ext);
}
/// This class extends [local_fs.LocalFileSystem] in order to clean up /// This class extends [local_fs.LocalFileSystem] in order to clean up
/// directories and files that the tool creates under the system temporary /// directories and files that the tool creates under the system temporary
/// directory when the tool exits either normally or when killed by a signal. /// directory when the tool exits either normally or when killed by a signal.
......
...@@ -29,6 +29,7 @@ import '../resident_runner.dart'; ...@@ -29,6 +29,7 @@ import '../resident_runner.dart';
import '../run_cold.dart'; import '../run_cold.dart';
import '../run_hot.dart'; import '../run_hot.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../vmservice.dart';
import '../web/web_runner.dart'; import '../web/web_runner.dart';
const String protocolVersion = '0.6.0'; const String protocolVersion = '0.6.0';
...@@ -687,9 +688,16 @@ class AppDomain extends Domain { ...@@ -687,9 +688,16 @@ class AppDomain extends Domain {
if (app == null) { if (app == null) {
throw "app '$appId' not found"; throw "app '$appId' not found";
} }
final FlutterDevice device = app.runner.flutterDevices.first;
final Map<String, dynamic> result = await app.runner final List<FlutterView> views = await device.vmService.getFlutterViews();
.invokeFlutterExtensionRpcRawOnFirstIsolate(methodName, params: params); final Map<String, dynamic> result = await device
.vmService
.invokeFlutterExtensionRpcRaw(
methodName,
args: params,
isolateId: views
.first.uiIsolate.id
);
if (result == null) { if (result == null) {
throw 'method not available: $methodName'; throw 'method not available: $methodName';
} }
......
...@@ -76,7 +76,6 @@ class DwdsWebRunnerFactory extends WebRunnerFactory { ...@@ -76,7 +76,6 @@ class DwdsWebRunnerFactory extends WebRunnerFactory {
systemClock: systemClock, systemClock: systemClock,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: logger, logger: logger,
featureFlags: featureFlags,
); );
} }
} }
...@@ -99,14 +98,12 @@ class ResidentWebRunner extends ResidentRunner { ...@@ -99,14 +98,12 @@ class ResidentWebRunner extends ResidentRunner {
@required SystemClock systemClock, @required SystemClock systemClock,
@required Usage usage, @required Usage usage,
@required UrlTunneller urlTunneller, @required UrlTunneller urlTunneller,
@required FeatureFlags featureFlags,
ResidentDevtoolsHandlerFactory devtoolsHandler = createDefaultHandler, ResidentDevtoolsHandlerFactory devtoolsHandler = createDefaultHandler,
}) : _fileSystem = fileSystem, }) : _fileSystem = fileSystem,
_logger = logger, _logger = logger,
_systemClock = systemClock, _systemClock = systemClock,
_usage = usage, _usage = usage,
_urlTunneller = urlTunneller, _urlTunneller = urlTunneller,
_featureFlags = featureFlags,
super( super(
<FlutterDevice>[device], <FlutterDevice>[device],
target: target ?? fileSystem.path.join('lib', 'main.dart'), target: target ?? fileSystem.path.join('lib', 'main.dart'),
...@@ -122,7 +119,6 @@ class ResidentWebRunner extends ResidentRunner { ...@@ -122,7 +119,6 @@ class ResidentWebRunner extends ResidentRunner {
final SystemClock _systemClock; final SystemClock _systemClock;
final Usage _usage; final Usage _usage;
final UrlTunneller _urlTunneller; final UrlTunneller _urlTunneller;
final FeatureFlags _featureFlags;
FlutterDevice get device => flutterDevices.first; FlutterDevice get device => flutterDevices.first;
final FlutterProject flutterProject; final FlutterProject flutterProject;
...@@ -167,20 +163,7 @@ class ResidentWebRunner extends ResidentRunner { ...@@ -167,20 +163,7 @@ class ResidentWebRunner extends ResidentRunner {
FlutterVmService _instance; FlutterVmService _instance;
@override @override
bool get canHotRestart { bool get supportsRestart => true;
return true;
}
@override
Future<Map<String, dynamic>> invokeFlutterExtensionRpcRawOnFirstIsolate(
String method, {
FlutterDevice device,
Map<String, dynamic> params,
}) async {
final vmservice.Response response =
await _vmService.service.callServiceExtension(method, args: params);
return response.toJson();
}
@override @override
Future<void> cleanupAfterSignal() async { Future<void> cleanupAfterSignal() async {
...@@ -313,7 +296,7 @@ class ResidentWebRunner extends ResidentRunner { ...@@ -313,7 +296,7 @@ class ResidentWebRunner extends ResidentRunner {
?.flutterPlatformOverride( ?.flutterPlatformOverride(
isolateId: null, isolateId: null,
); );
final String platform = nextPlatform(currentPlatform, _featureFlags); final String platform = nextPlatform(currentPlatform);
await _vmService await _vmService
?.flutterPlatformOverride( ?.flutterPlatformOverride(
platform: platform, platform: platform,
......
...@@ -192,11 +192,11 @@ class FlutterDevice { ...@@ -192,11 +192,11 @@ class FlutterDevice {
); );
} }
final TargetPlatform targetPlatform;
final Device device; final Device device;
final ResidentCompiler generator; final ResidentCompiler generator;
final BuildInfo buildInfo; final BuildInfo buildInfo;
final String userIdentifier; final String userIdentifier;
final TargetPlatform targetPlatform;
DevFSWriter devFSWriter; DevFSWriter devFSWriter;
Stream<Uri> observatoryUris; Stream<Uri> observatoryUris;
...@@ -401,138 +401,16 @@ class FlutterDevice { ...@@ -401,138 +401,16 @@ class FlutterDevice {
return devFS.create(); return devFS.create();
} }
Future<void> debugDumpApp() async { Future<List<vm_service.IsolateRef>> _getCurrentIsolates() async {
final List<FlutterView> views = await vmService.getFlutterViews(); if (targetPlatform == TargetPlatform.web_javascript) {
for (final FlutterView view in views) { final vm_service.VM vm = await vmService.service.getVM();
final String data = await vmService.flutterDebugDumpApp( return vm.isolates;
isolateId: view.uiIsolate.id,
);
globals.printStatus(data);
}
}
Future<void> debugDumpRenderTree() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
final String data = await vmService.flutterDebugDumpRenderTree(
isolateId: view.uiIsolate.id,
);
globals.printStatus(data);
}
}
Future<void> debugDumpLayerTree() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
final String data = await vmService.flutterDebugDumpLayerTree(
isolateId: view.uiIsolate.id,
);
globals.printStatus(data);
}
}
Future<void> debugDumpSemanticsTreeInTraversalOrder() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
final String data = await vmService.flutterDebugDumpSemanticsTreeInTraversalOrder(
isolateId: view.uiIsolate.id,
);
globals.printStatus(data);
}
}
Future<void> debugDumpSemanticsTreeInInverseHitTestOrder() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
final String data = await vmService.flutterDebugDumpSemanticsTreeInInverseHitTestOrder(
isolateId: view.uiIsolate.id,
);
globals.printStatus(data);
}
}
Future<void> toggleDebugPaintSizeEnabled() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
await vmService.flutterToggleDebugPaintSizeEnabled(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> toggleDebugCheckElevationsEnabled() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
await vmService.flutterToggleDebugCheckElevationsEnabled(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> debugTogglePerformanceOverlayOverride() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
await vmService.flutterTogglePerformanceOverlayOverride(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> toggleWidgetInspector() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
await vmService.flutterToggleWidgetInspector(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> toggleInvertOversizedImages() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
await vmService.flutterToggleInvertOversizedImages(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> toggleProfileWidgetBuilds() async {
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
await vmService.flutterToggleProfileWidgetBuilds(
isolateId: view.uiIsolate.id,
);
}
}
Future<Brightness> toggleBrightness({ Brightness current }) async {
final List<FlutterView> views = await vmService.getFlutterViews();
Brightness next;
if (current == Brightness.light) {
next = Brightness.dark;
} else if (current == Brightness.dark) {
next = Brightness.light;
}
for (final FlutterView view in views) {
next = await vmService.flutterBrightnessOverride(
isolateId: view.uiIsolate.id,
brightness: next,
);
} }
return next;
}
Future<String> togglePlatform({ String from }) async {
final List<FlutterView> views = await vmService.getFlutterViews(); final List<FlutterView> views = await vmService.getFlutterViews();
final String to = nextPlatform(from, featureFlags); return <vm_service.IsolateRef>[
for (final FlutterView view in views) { for (FlutterView view in views)
await vmService.flutterPlatformOverride( view.uiIsolate,
platform: to, ];
isolateId: view.uiIsolate.id,
);
}
return to;
} }
Future<void> startEchoingDeviceLog() async { Future<void> startEchoingDeviceLog() async {
...@@ -629,7 +507,6 @@ class FlutterDevice { ...@@ -629,7 +507,6 @@ class FlutterDevice {
return 0; return 0;
} }
Future<int> runCold({ Future<int> runCold({
ColdRunner coldRunner, ColdRunner coldRunner,
String route, String route,
...@@ -741,22 +618,403 @@ class FlutterDevice { ...@@ -741,22 +618,403 @@ class FlutterDevice {
devFSStatus.cancel(); devFSStatus.cancel();
return UpdateFSReport(success: false); return UpdateFSReport(success: false);
} }
devFSStatus.stop(); devFSStatus.stop();
globals.printTrace('Synced ${getSizeAsMB(report.syncedBytes)}.'); globals.printTrace('Synced ${getSizeAsMB(report.syncedBytes)}.');
return report; return report;
}
Future<void> updateReloadStatus(bool wasReloadSuccessful) async {
if (wasReloadSuccessful) {
generator?.accept();
} else {
await generator?.reject();
}
}
}
/// A subset of the [ResidentRunner] for delegating to attached flutter devices.
abstract class ResidentHandlers {
List<FlutterDevice> get flutterDevices;
/// Whether the resident runner has hot reload and restart enabled.
bool get hotMode;
/// Whether the resident runner is connect to the device's VM Service.
bool get supportsServiceProtocol;
/// The application is running in debug mode.
bool get isRunningDebug;
/// The application is running in profile mode.
bool get isRunningProfile;
/// The application is running in release mode.
bool get isRunningRelease;
/// The resident runner should stay resident after establishing a connection with the
/// application.
bool get stayResident;
/// Whether all of the connected devices support hot restart.
///
/// To prevent scenarios where only a subset of devices are hot restarted,
/// the runner requires that all attached devices can support hot restart
/// before enabling it.
bool get supportsRestart;
/// Whether all of the connected devices support gathering SkSL.
bool get supportsWriteSkSL;
/// Whether all of the connected devices support hot reload.
bool get canHotReload;
@protected
Logger get logger;
@protected
FileSystem get fileSystem;
/// Called to print help to the terminal.
void printHelp({ @required bool details });
/// Perfor a hot reload or hot restart of all attached applications.
///
/// If [fullRestart] is true, a hot restart is performed. Otherwise a hot reload
/// is run instead. On web devices, this only performs a hot restart regardless of
/// the value of [fullRestart].
Future<OperationResult> restart({ bool fullRestart = false, bool pause = false, String reason }) {
final String mode = isRunningProfile ? 'profile' :isRunningRelease ? 'release' : 'this';
throw '${fullRestart ? 'Restart' : 'Reload'} is not supported in $mode mode';
}
/// Dump the application's current widget tree to the terminal.
Future<bool> debugDumpApp() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
final String data = await device.vmService.flutterDebugDumpApp(
isolateId: view.id,
);
logger.printStatus(data);
}
}
return true;
}
/// Dump the application's current render tree to the terminal.
Future<bool> debugDumpRenderTree() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
final String data = await device.vmService.flutterDebugDumpRenderTree(
isolateId: view.id,
);
logger.printStatus(data);
}
}
return true;
}
/// Dump the application's current layer tree to the terminal.
Future<bool> debugDumpLayerTree() async {
if (!supportsServiceProtocol || !isRunningDebug) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
final String data = await device.vmService.flutterDebugDumpLayerTree(
isolateId: view.id,
);
logger.printStatus(data);
}
}
return true;
}
/// Dump the application's current semantics tree to the terminal.
///
/// If semantics are not enabled, nothing is returned.
Future<bool> debugDumpSemanticsTreeInTraversalOrder() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
final String data = await device.vmService.flutterDebugDumpSemanticsTreeInTraversalOrder(
isolateId: view.id,
);
logger.printStatus(data);
}
}
return true;
}
/// Dump the application's current semantics tree to the terminal.
///
/// If semantics are not enabled, nothing is returned.
Future<bool> debugDumpSemanticsTreeInInverseHitTestOrder() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
final String data = await device.vmService.flutterDebugDumpSemanticsTreeInInverseHitTestOrder(
isolateId: view.id,
);
logger.printStatus(data);
}
}
return true;
}
/// Toggle the "paint size" debugging feature.
Future<bool> debugToggleDebugPaintSizeEnabled() async {
if (!supportsServiceProtocol || !isRunningDebug) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
await device.vmService.flutterToggleDebugPaintSizeEnabled(
isolateId: view.id,
);
}
}
return true;
}
/// Toggle the "elevation check" debugging feature.
Future<bool> debugToggleDebugCheckElevationsEnabled() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
await device.vmService.flutterToggleDebugCheckElevationsEnabled(
isolateId: view.id,
);
}
}
return true;
}
/// Toggle the performance overlay.
///
/// This is not supported in web mode.
Future<bool> debugTogglePerformanceOverlayOverride() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
if (device.targetPlatform == TargetPlatform.web_javascript) {
continue;
}
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
await device.vmService.flutterTogglePerformanceOverlayOverride(
isolateId: view.id,
);
}
}
return true;
}
/// Toggle the widget inspector.
Future<bool> debugToggleWidgetInspector() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
await device.vmService.flutterToggleWidgetInspector(
isolateId: view.id,
);
}
}
return true;
}
/// Toggle the "invert images" debugging feature.
Future<bool> debugToggleInvertOversizedImages() async {
if (!supportsServiceProtocol || !isRunningDebug) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
await device.vmService.flutterToggleInvertOversizedImages(
isolateId: view.id,
);
}
}
return true;
}
/// Toggle the "profile widget builds" debugging feature.
Future<bool> debugToggleProfileWidgetBuilds() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
await device.vmService.flutterToggleProfileWidgetBuilds(
isolateId: view.id,
);
}
}
return true;
}
/// Toggle the operating system brightness (light or dart).
Future<bool> debugToggleBrightness() async {
if (!supportsServiceProtocol) {
return false;
}
final List<vm_service.IsolateRef> views = await flutterDevices.first._getCurrentIsolates();
final Brightness current = await flutterDevices.first.vmService.flutterBrightnessOverride(
isolateId: views.first.id,
);
Brightness next;
if (current == Brightness.light) {
next = Brightness.dark;
} else {
next = Brightness.light;
}
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
await device.vmService.flutterBrightnessOverride(
isolateId: view.id,
brightness: next,
);
}
logger.printStatus('Changed brightness to $next.');
}
return true;
}
/// Rotate the application through different `defaultTargetPlatform` values.
Future<bool> debugTogglePlatform() async {
if (!supportsServiceProtocol || !isRunningDebug) {
return false;
}
final List<vm_service.IsolateRef> views = await flutterDevices.first._getCurrentIsolates();
final String from = await flutterDevices
.first.vmService.flutterPlatformOverride(
isolateId: views.first.id,
);
final String to = nextPlatform(from);
for (final FlutterDevice device in flutterDevices) {
for (final vm_service.IsolateRef view in await device._getCurrentIsolates()) {
await device.vmService.flutterPlatformOverride(
platform: to,
isolateId: view.id,
);
}
}
logger.printStatus('Switched operating system to $to');
return true;
}
/// Write the SkSL shaders to a zip file in build directory.
///
/// Returns the name of the file, or `null` on failures.
Future<String> writeSkSL() async {
if (!supportsWriteSkSL) {
throw Exception('writeSkSL is not supported by this runner.');
}
final List<FlutterView> views = await flutterDevices
.first
.vmService.getFlutterViews();
final Map<String, Object> data = await flutterDevices.first.vmService.getSkSLs(
viewId: views.first.id,
);
final Device device = flutterDevices.first.device;
return sharedSkSlWriter(device, data);
}
/// Take a screenshot on the provided [device].
///
/// If the device has a connected vmservice, this method will attempt to hide
/// and restore the debug banner before taking the screenshot.
///
/// Throws an [AssertionError] if [Device.supportsScreenshot] is not true.
Future<void> screenshot(FlutterDevice device) async {
assert(device.device.supportsScreenshot);
final Status status = logger.startProgress(
'Taking screenshot for ${device.device.name}...',
);
final File outputFile = getUniqueFile(
fileSystem.currentDirectory,
'flutter',
'png',
);
List<FlutterView> views = <FlutterView>[];
Future<bool> setDebugBanner(bool value) async {
try {
for (final FlutterView view in views) {
await device.vmService.flutterDebugAllowBanner(
value,
isolateId: view.uiIsolate.id,
);
}
return true;
} on Exception catch (error) {
status.cancel();
logger.printError('Error communicating with Flutter on the device: $error');
return false;
}
}
try {
if (supportsServiceProtocol && isRunningDebug) {
// Ensure that the vmService access is guarded by supportsServiceProtocol, it
// will be null in release mode.
views = await device.vmService.getFlutterViews();
if (!await setDebugBanner(false)) {
return;
}
}
try {
await device.device.takeScreenshot(outputFile);
} finally {
if (supportsServiceProtocol && isRunningDebug) {
await setDebugBanner(true);
}
}
final int sizeKB = outputFile.lengthSync() ~/ 1024;
status.stop();
logger.printStatus(
'Screenshot written to ${fileSystem.path.relative(outputFile.path)} (${sizeKB}kB).',
);
} on Exception catch (error) {
status.cancel();
logger.printError('Error taking screenshot: $error');
}
} }
Future<void> updateReloadStatus(bool wasReloadSuccessful) async { /// Remove sigusr signal handlers.
if (wasReloadSuccessful) { Future<void> cleanupAfterSignal();
generator?.accept();
} else { /// Tear down the runner and leave the application running.
await generator?.reject(); ///
} /// This is not supported on web devices where the runner is running
} /// the application server as well.
Future<void> detach();
/// Tear down the runner and exit the application.
Future<void> exit();
/// Run any source generators, such as localizations.
///
/// These are automatically run during hot restart, but can be
/// triggered manually to see the updated generated code.
Future<void> runSourceGenerators();
} }
// Shared code between different resident application runners. // Shared code between different resident application runners.
abstract class ResidentRunner { abstract class ResidentRunner extends ResidentHandlers {
ResidentRunner( ResidentRunner(
this.flutterDevices, { this.flutterDevices, {
@required this.target, @required this.target,
...@@ -788,12 +1046,19 @@ abstract class ResidentRunner { ...@@ -788,12 +1046,19 @@ abstract class ResidentRunner {
_residentDevtoolsHandler = devtoolsHandler(DevtoolsLauncher.instance, this, globals.logger); _residentDevtoolsHandler = devtoolsHandler(DevtoolsLauncher.instance, this, globals.logger);
} }
@protected @override
@visibleForTesting Logger get logger => globals.logger;
@override
FileSystem get fileSystem => globals.fs;
@override
final List<FlutterDevice> flutterDevices; final List<FlutterDevice> flutterDevices;
final String target; final String target;
final DebuggingOptions debuggingOptions; final DebuggingOptions debuggingOptions;
@override
final bool stayResident; final bool stayResident;
final bool ipv6; final bool ipv6;
final String _dillOutputPath; final String _dillOutputPath;
...@@ -812,6 +1077,10 @@ abstract class ResidentRunner { ...@@ -812,6 +1077,10 @@ abstract class ResidentRunner {
bool _exited = false; bool _exited = false;
Completer<int> _finished = Completer<int>(); Completer<int> _finished = Completer<int>();
BuildResult _lastBuild;
Environment _environment;
@override
bool hotMode; bool hotMode;
/// Returns true if every device is streaming observatory URIs. /// Returns true if every device is streaming observatory URIs.
...@@ -833,11 +1102,22 @@ abstract class ResidentRunner { ...@@ -833,11 +1102,22 @@ abstract class ResidentRunner {
} }
bool get debuggingEnabled => debuggingOptions.debuggingEnabled; bool get debuggingEnabled => debuggingOptions.debuggingEnabled;
@override
bool get isRunningDebug => debuggingOptions.buildInfo.isDebug; bool get isRunningDebug => debuggingOptions.buildInfo.isDebug;
@override
bool get isRunningProfile => debuggingOptions.buildInfo.isProfile; bool get isRunningProfile => debuggingOptions.buildInfo.isProfile;
@override
bool get isRunningRelease => debuggingOptions.buildInfo.isRelease; bool get isRunningRelease => debuggingOptions.buildInfo.isRelease;
@override
bool get supportsServiceProtocol => isRunningDebug || isRunningProfile; bool get supportsServiceProtocol => isRunningDebug || isRunningProfile;
@override
bool get supportsWriteSkSL => supportsServiceProtocol; bool get supportsWriteSkSL => supportsServiceProtocol;
bool get trackWidgetCreation => debuggingOptions.buildInfo.trackWidgetCreation; bool get trackWidgetCreation => debuggingOptions.buildInfo.trackWidgetCreation;
// Returns the Uri of the first connected device for mobile, // Returns the Uri of the first connected device for mobile,
...@@ -850,38 +1130,14 @@ abstract class ResidentRunner { ...@@ -850,38 +1130,14 @@ abstract class ResidentRunner {
/// Returns [true] if the resident runner exited after invoking [exit()]. /// Returns [true] if the resident runner exited after invoking [exit()].
bool get exited => _exited; bool get exited => _exited;
/// Whether this runner can hot restart. @override
/// bool get supportsRestart {
/// To prevent scenarios where only a subset of devices are hot restarted, return isRunningDebug && flutterDevices.every((FlutterDevice device) {
/// the runner requires that all attached devices can support hot restart
/// before enabling it.
bool get canHotRestart {
return flutterDevices.every((FlutterDevice device) {
return device.device.supportsHotRestart; return device.device.supportsHotRestart;
}); });
} }
/// Invoke an RPC extension method on the first attached ui isolate of the first device. @override
// TODO(jonahwilliams): Update/Remove this method when refactoring the resident
// runner to support a single flutter device.
Future<Map<String, dynamic>> invokeFlutterExtensionRpcRawOnFirstIsolate(
String method, {
FlutterDevice device,
Map<String, dynamic> params,
}) async {
device ??= flutterDevices.first;
final List<FlutterView> views = await device.vmService.getFlutterViews();
return device
.vmService
.invokeFlutterExtensionRpcRaw(
method,
args: params,
isolateId: views
.first.uiIsolate.id
);
}
/// Whether this runner can hot reload.
bool get canHotReload => hotMode; bool get canHotReload => hotMode;
/// Start the app and keep the process running during its lifetime. /// Start the app and keep the process running during its lifetime.
...@@ -902,17 +1158,7 @@ abstract class ResidentRunner { ...@@ -902,17 +1158,7 @@ abstract class ResidentRunner {
bool enableDevTools = false, bool enableDevTools = false,
}); });
bool get supportsRestart => false; @override
Future<OperationResult> restart({ bool fullRestart = false, bool pause = false, String reason }) {
final String mode = isRunningProfile ? 'profile' :
isRunningRelease ? 'release' : 'this';
throw '${fullRestart ? 'Restart' : 'Reload'} is not supported in $mode mode';
}
BuildResult _lastBuild;
Environment _environment;
Future<void> runSourceGenerators() async { Future<void> runSourceGenerators() async {
_environment ??= Environment( _environment ??= Environment(
artifacts: globals.artifacts, artifacts: globals.artifacts,
...@@ -944,23 +1190,6 @@ abstract class ResidentRunner { ...@@ -944,23 +1190,6 @@ abstract class ResidentRunner {
globals.logger.printTrace('complete'); globals.logger.printTrace('complete');
} }
/// Write the SkSL shaders to a zip file in build directory.
///
/// Returns the name of the file, or `null` on failures.
Future<String> writeSkSL() async {
if (!supportsWriteSkSL) {
throw Exception('writeSkSL is not supported by this runner.');
}
final List<FlutterView> views = await flutterDevices
.first
.vmService.getFlutterViews();
final Map<String, Object> data = await flutterDevices.first.vmService.getSkSLs(
viewId: views.first.id,
);
final Device device = flutterDevices.first.device;
return sharedSkSlWriter(device, data);
}
@protected @protected
void writeVmServiceFile() { void writeVmServiceFile() {
if (debuggingOptions.vmserviceOutFile != null) { if (debuggingOptions.vmserviceOutFile != null) {
...@@ -975,6 +1204,7 @@ abstract class ResidentRunner { ...@@ -975,6 +1204,7 @@ abstract class ResidentRunner {
} }
} }
@override
Future<void> exit() async { Future<void> exit() async {
_exited = true; _exited = true;
await residentDevtoolsHandler.shutdown(); await residentDevtoolsHandler.shutdown();
...@@ -984,6 +1214,7 @@ abstract class ResidentRunner { ...@@ -984,6 +1214,7 @@ abstract class ResidentRunner {
await shutdownDartDevelopmentService(); await shutdownDartDevelopmentService();
} }
@override
Future<void> detach() async { Future<void> detach() async {
await residentDevtoolsHandler.shutdown(); await residentDevtoolsHandler.shutdown();
await stopEchoingDeviceLog(); await stopEchoingDeviceLog();
...@@ -992,212 +1223,6 @@ abstract class ResidentRunner { ...@@ -992,212 +1223,6 @@ abstract class ResidentRunner {
appFinished(); appFinished();
} }
Future<bool> debugDumpApp() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.debugDumpApp();
}
return true;
}
Future<bool> debugDumpRenderTree() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.debugDumpRenderTree();
}
return true;
}
Future<bool> debugDumpLayerTree() async {
if (!supportsServiceProtocol || !isRunningDebug) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.debugDumpLayerTree();
}
return true;
}
Future<bool> debugDumpSemanticsTreeInTraversalOrder() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.debugDumpSemanticsTreeInTraversalOrder();
}
return true;
}
Future<bool> debugDumpSemanticsTreeInInverseHitTestOrder() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.debugDumpSemanticsTreeInInverseHitTestOrder();
}
return true;
}
Future<bool> debugToggleDebugPaintSizeEnabled() async {
if (!supportsServiceProtocol || !isRunningDebug) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.toggleDebugPaintSizeEnabled();
}
return true;
}
Future<bool> debugToggleDebugCheckElevationsEnabled() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.toggleDebugCheckElevationsEnabled();
}
return true;
}
Future<bool> debugTogglePerformanceOverlayOverride() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.debugTogglePerformanceOverlayOverride();
}
return true;
}
Future<bool> debugToggleWidgetInspector() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.toggleWidgetInspector();
}
return true;
}
Future<bool> debugToggleInvertOversizedImages() async {
if (!supportsServiceProtocol || !isRunningDebug) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.toggleInvertOversizedImages();
}
return true;
}
Future<bool> debugToggleProfileWidgetBuilds() async {
if (!supportsServiceProtocol) {
return false;
}
for (final FlutterDevice device in flutterDevices) {
await device.toggleProfileWidgetBuilds();
}
return true;
}
Future<bool> debugToggleBrightness() async {
if (!supportsServiceProtocol) {
return false;
}
final Brightness brightness = await flutterDevices.first.toggleBrightness();
Brightness next;
for (final FlutterDevice device in flutterDevices) {
next = await device.toggleBrightness(
current: brightness,
);
globals.logger.printStatus('Changed brightness to $next.');
}
return true;
}
/// Take a screenshot on the provided [device].
///
/// If the device has a connected vmservice, this method will attempt to hide
/// and restore the debug banner before taking the screenshot.
///
/// Throws an [AssertionError] if [Device.supportsScreenshot] is not true.
Future<void> screenshot(FlutterDevice device) async {
assert(device.device.supportsScreenshot);
final Status status = globals.logger.startProgress(
'Taking screenshot for ${device.device.name}...',
);
final File outputFile = globals.fsUtils.getUniqueFile(
globals.fs.currentDirectory,
'flutter',
'png',
);
List<FlutterView> views = <FlutterView>[];
Future<bool> setDebugBanner(bool value) async {
try {
for (final FlutterView view in views) {
await device.vmService.flutterDebugAllowBanner(
value,
isolateId: view.uiIsolate.id,
);
}
return true;
} on Exception catch (error) {
status.cancel();
globals.printError('Error communicating with Flutter on the device: $error');
return false;
}
}
try {
if (supportsServiceProtocol && isRunningDebug) {
// Ensure that the vmService access is guarded by supportsServiceProtocol, it
// will be null in release mode.
views = await device.vmService.getFlutterViews();
if (!await setDebugBanner(false)) {
return;
}
}
try {
await device.device.takeScreenshot(outputFile);
} finally {
if (supportsServiceProtocol && isRunningDebug) {
await setDebugBanner(true);
}
}
final int sizeKB = outputFile.lengthSync() ~/ 1024;
status.stop();
globals.printStatus(
'Screenshot written to ${globals.fs.path.relative(outputFile.path)} (${sizeKB}kB).',
);
} on Exception catch (error) {
status.cancel();
globals.printError('Error taking screenshot: $error');
}
}
Future<bool> debugTogglePlatform() async {
if (!supportsServiceProtocol || !isRunningDebug) {
return false;
}
final List<FlutterView> views = await flutterDevices
.first
.vmService.getFlutterViews();
final String isolateId = views.first.uiIsolate.id;
final String from = await flutterDevices
.first.vmService.flutterPlatformOverride(
isolateId: isolateId,
);
String to;
for (final FlutterDevice device in flutterDevices) {
to = await device.togglePlatform(from: from);
}
globals.printStatus('Switched operating system to $to');
return true;
}
Future<void> stopEchoingDeviceLog() async { Future<void> stopEchoingDeviceLog() async {
await Future.wait<void>( await Future.wait<void>(
flutterDevices.map<Future<void>>((FlutterDevice device) => device.stopEchoingDeviceLog()) flutterDevices.map<Future<void>>((FlutterDevice device) => device.stopEchoingDeviceLog())
...@@ -1379,9 +1404,6 @@ abstract class ResidentRunner { ...@@ -1379,9 +1404,6 @@ abstract class ResidentRunner {
_reportedDebuggers = true; _reportedDebuggers = true;
} }
/// Called to print help to the terminal.
void printHelp({ @required bool details });
void printHelpDetails() { void printHelpDetails() {
if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot)) { if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot)) {
commandHelp.s.print(); commandHelp.s.print();
...@@ -1413,14 +1435,11 @@ abstract class ResidentRunner { ...@@ -1413,14 +1435,11 @@ abstract class ResidentRunner {
} }
} }
/// Called when a signal has requested we exit. @override
Future<void> cleanupAfterSignal(); Future<void> cleanupAfterSignal();
/// Called right before we exit. /// Called right before we exit.
Future<void> cleanupAtFinish(); Future<void> cleanupAtFinish();
// Clears the screen.
void clearScreen() => globals.logger.clear();
} }
class OperationResult { class OperationResult {
...@@ -1479,7 +1498,7 @@ class TerminalHandler { ...@@ -1479,7 +1498,7 @@ class TerminalHandler {
final bool _reportReady; final bool _reportReady;
final String _pidFile; final String _pidFile;
final ResidentRunner residentRunner; final ResidentHandlers residentRunner;
bool _processingUserRequest = false; bool _processingUserRequest = false;
StreamSubscription<void> subscription; StreamSubscription<void> subscription;
File _actualPidFile; File _actualPidFile;
...@@ -1487,6 +1506,10 @@ class TerminalHandler { ...@@ -1487,6 +1506,10 @@ class TerminalHandler {
@visibleForTesting @visibleForTesting
String lastReceivedCommand; String lastReceivedCommand;
/// This is only a buffer logger in unit tests
@visibleForTesting
BufferLogger get logger => _logger as BufferLogger;
void setupTerminal() { void setupTerminal() {
if (!_logger.quiet) { if (!_logger.quiet) {
_logger.printStatus(''); _logger.printStatus('');
...@@ -1544,7 +1567,7 @@ class TerminalHandler { ...@@ -1544,7 +1567,7 @@ class TerminalHandler {
case 'b': case 'b':
return residentRunner.debugToggleBrightness(); return residentRunner.debugToggleBrightness();
case 'c': case 'c':
residentRunner.clearScreen(); _logger.clear();
return true; return true;
case 'd': case 'd':
case 'D': case 'D':
...@@ -1597,7 +1620,7 @@ class TerminalHandler { ...@@ -1597,7 +1620,7 @@ class TerminalHandler {
return true; return true;
case 'R': case 'R':
// If hot restart is not supported for all devices, ignore the command. // If hot restart is not supported for all devices, ignore the command.
if (!residentRunner.canHotRestart || !residentRunner.hotMode) { if (!residentRunner.supportsRestart || !residentRunner.hotMode) {
return false; return false;
} }
final OperationResult result = await residentRunner.restart(fullRestart: true); final OperationResult result = await residentRunner.restart(fullRestart: true);
...@@ -1694,17 +1717,14 @@ class DebugConnectionInfo { ...@@ -1694,17 +1717,14 @@ class DebugConnectionInfo {
/// ///
/// These values must match what is available in /// These values must match what is available in
/// `packages/flutter/lib/src/foundation/binding.dart`. /// `packages/flutter/lib/src/foundation/binding.dart`.
String nextPlatform(String currentPlatform, FeatureFlags featureFlags) { String nextPlatform(String currentPlatform) {
switch (currentPlatform) { switch (currentPlatform) {
case 'android': case 'android':
return 'iOS'; return 'iOS';
case 'iOS': case 'iOS':
return 'fuchsia'; return 'fuchsia';
case 'fuchsia': case 'fuchsia':
if (featureFlags.isMacOSEnabled) { return 'macOS';
return 'macOS';
}
return 'android';
case 'macOS': case 'macOS':
return 'android'; return 'android';
default: default:
......
...@@ -10,6 +10,7 @@ import 'package:meta/meta.dart'; ...@@ -10,6 +10,7 @@ import 'package:meta/meta.dart';
import 'base/common.dart'; import 'base/common.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/logger.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'device.dart'; import 'device.dart';
import 'globals_null_migrated.dart' as globals; import 'globals_null_migrated.dart' as globals;
...@@ -51,7 +52,10 @@ class ColdRunner extends ResidentRunner { ...@@ -51,7 +52,10 @@ class ColdRunner extends ResidentRunner {
bool get canHotReload => false; bool get canHotReload => false;
@override @override
bool get canHotRestart => false; Logger get logger => globals.logger;
@override
FileSystem get fileSystem => globals.fs;
@override @override
Future<int> run({ Future<int> run({
......
...@@ -598,9 +598,6 @@ class HotRunner extends ResidentRunner { ...@@ -598,9 +598,6 @@ class HotRunner extends ResidentRunner {
return true; return true;
} }
@override
bool get supportsRestart => true;
@override @override
Future<OperationResult> restart({ Future<OperationResult> restart({
bool fullRestart = false, bool fullRestart = false,
...@@ -670,7 +667,7 @@ class HotRunner extends ResidentRunner { ...@@ -670,7 +667,7 @@ class HotRunner extends ResidentRunner {
String reason, String reason,
bool silent, bool silent,
}) async { }) async {
if (!canHotRestart) { if (!supportsRestart) {
return OperationResult(1, 'hotRestart not supported'); return OperationResult(1, 'hotRestart not supported');
} }
Status status; Status status;
...@@ -1089,7 +1086,7 @@ class HotRunner extends ResidentRunner { ...@@ -1089,7 +1086,7 @@ class HotRunner extends ResidentRunner {
void printHelp({ @required bool details }) { void printHelp({ @required bool details }) {
globals.printStatus('Flutter run key commands.'); globals.printStatus('Flutter run key commands.');
commandHelp.r.print(); commandHelp.r.print();
if (canHotRestart) { if (supportsRestart) {
commandHelp.R.print(); commandHelp.R.print();
} }
commandHelp.h.print(); // TODO(ianh): print different message if "details" is false commandHelp.h.print(); // TODO(ianh): print different message if "details" is false
......
...@@ -1684,16 +1684,6 @@ void main() { ...@@ -1684,16 +1684,6 @@ void main() {
expect(testLogger.statusText, contains('1kB')); expect(testLogger.statusText, contains('1kB'));
})); }));
testUsingContext('ResidentRunner clears the screen when it should', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
const String message = 'This should be cleared';
expect(testLogger.statusText, equals(''));
testLogger.printStatus(message);
expect(testLogger.statusText, equals(message + '\n')); // printStatus makes a newline
residentRunner.clearScreen();
expect(testLogger.statusText, equals(''));
}));
testUsingContext('ResidentRunner bails taking screenshot on debug device if debugAllowBanner throws RpcError', () => testbed.run(() async { testUsingContext('ResidentRunner bails taking screenshot on debug device if debugAllowBanner throws RpcError', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
listViews, listViews,
...@@ -1906,361 +1896,6 @@ void main() { ...@@ -1906,361 +1896,6 @@ void main() {
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
})); }));
testUsingContext('ResidentRunner debugDumpApp calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugDumpApp(), true);
verify(mockFlutterDevice.debugDumpApp()).called(1);
}));
testUsingContext('ResidentRunner debugDumpApp does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugDumpApp(), false);
verifyNever(mockFlutterDevice.debugDumpApp());
}));
testUsingContext('ResidentRunner debugDumpRenderTree calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugDumpRenderTree(), true);
verify(mockFlutterDevice.debugDumpRenderTree()).called(1);
}));
testUsingContext('ResidentRunner debugDumpRenderTree does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugDumpRenderTree(), false);
verifyNever(mockFlutterDevice.debugDumpRenderTree());
}));
testUsingContext('ResidentRunner debugDumpLayerTree calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugDumpLayerTree(), true);
verify(mockFlutterDevice.debugDumpLayerTree()).called(1);
}));
testUsingContext('ResidentRunner debugDumpLayerTree does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugDumpLayerTree(), false);
verifyNever(mockFlutterDevice.debugDumpLayerTree());
}));
testUsingContext('ResidentRunner debugDumpLayerTree does not call flutter device if not running in debug mode', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugDumpLayerTree(), false);
verifyNever(mockFlutterDevice.debugDumpLayerTree());
}));
testUsingContext('ResidentRunner debugDumpSemanticsTreeInTraversalOrder calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugDumpSemanticsTreeInTraversalOrder(), true);
verify(mockFlutterDevice.debugDumpSemanticsTreeInTraversalOrder()).called(1);
}));
testUsingContext('ResidentRunner debugDumpSemanticsTreeInTraversalOrder does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugDumpSemanticsTreeInTraversalOrder(), false);
verifyNever(mockFlutterDevice.debugDumpSemanticsTreeInTraversalOrder());
}));
testUsingContext('ResidentRunner debugDumpSemanticsTreeInInverseHitTestOrder calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugDumpSemanticsTreeInInverseHitTestOrder(), true);
verify(mockFlutterDevice.debugDumpSemanticsTreeInInverseHitTestOrder()).called(1);
}));
testUsingContext('ResidentRunner debugDumpSemanticsTreeInInverseHitTestOrder does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugDumpSemanticsTreeInInverseHitTestOrder(), false);
verifyNever(mockFlutterDevice.debugDumpSemanticsTreeInInverseHitTestOrder());
}));
testUsingContext('ResidentRunner debugToggleDebugPaintSizeEnabled calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugToggleDebugPaintSizeEnabled(), true);
verify(mockFlutterDevice.toggleDebugPaintSizeEnabled()).called(1);
}));
testUsingContext('ResidentRunner debugToggleDebugPaintSizeEnabled does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugToggleDebugPaintSizeEnabled(), false);
verifyNever(mockFlutterDevice.toggleDebugPaintSizeEnabled());
}));
testUsingContext('ResidentRunner debugToggleBrightness calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugToggleBrightness(), true);
verify(mockFlutterDevice.toggleBrightness()).called(2);
}));
testUsingContext('ResidentRunner debugToggleBrightness does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugToggleBrightness(), false);
verifyNever(mockFlutterDevice.toggleBrightness());
}));
testUsingContext('FlutterDevice.toggleBrightness invokes correct VM service request', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
listViews,
const FakeVmServiceRequest(
method: 'ext.flutter.brightnessOverride',
args: <String, Object>{
'isolateId': '1',
},
jsonResponse: <String, Object>{
'value': 'Brightness.dark'
},
),
]);
final FlutterDevice flutterDevice = FlutterDevice(
mockDevice,
buildInfo: BuildInfo.debug,
);
flutterDevice.vmService = fakeVmServiceHost.vmService;
expect(await flutterDevice.toggleBrightness(), Brightness.dark);
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
testUsingContext('ResidentRunner debugToggleInvertOversizedImages calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugToggleInvertOversizedImages(), true);
verify(mockFlutterDevice.toggleInvertOversizedImages()).called(1);
}));
testUsingContext('ResidentRunner debugToggleInvertOversizedImages does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugToggleInvertOversizedImages(), false);
verifyNever(mockFlutterDevice.toggleInvertOversizedImages());
}));
testUsingContext('ResidentRunner debugToggleInvertOversizedImages does not call flutter device if in profile mode', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugToggleInvertOversizedImages(), false);
verifyNever(mockFlutterDevice.toggleInvertOversizedImages());
}));
testUsingContext('FlutterDevice.toggleInvertOversizedImages invokes correct VM service request', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
listViews,
const FakeVmServiceRequest(
method: 'ext.flutter.invertOversizedImages',
args: <String, Object>{
'isolateId': '1',
},
jsonResponse: <String, Object>{
'value': 'false'
},
),
]);
final FlutterDevice flutterDevice = FlutterDevice(
mockDevice,
buildInfo: BuildInfo.debug,
);
flutterDevice.vmService = fakeVmServiceHost.vmService;
await flutterDevice.toggleInvertOversizedImages();
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
testUsingContext('ResidentRunner debugToggleDebugCheckElevationsEnabled calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugToggleDebugCheckElevationsEnabled(), true);
verify(mockFlutterDevice.toggleDebugCheckElevationsEnabled()).called(1);
}));
testUsingContext('ResidentRunner debugToggleDebugCheckElevationsEnabled does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugToggleDebugCheckElevationsEnabled(), false);
verifyNever(mockFlutterDevice.toggleDebugCheckElevationsEnabled());
}));
testUsingContext('ResidentRunner debugTogglePerformanceOverlayOverride calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugTogglePerformanceOverlayOverride(), true);
verify(mockFlutterDevice.debugTogglePerformanceOverlayOverride()).called(1);
}));
testUsingContext('ResidentRunner debugTogglePerformanceOverlayOverride does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugTogglePerformanceOverlayOverride(), false);
verifyNever(mockFlutterDevice.debugTogglePerformanceOverlayOverride());
}));
testUsingContext('ResidentRunner debugToggleWidgetInspector calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(await residentRunner.debugToggleWidgetInspector(), true);
verify(mockFlutterDevice.toggleWidgetInspector()).called(1);
}));
testUsingContext('ResidentRunner debugToggleWidgetInspector does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugToggleWidgetInspector(), false);
verifyNever(mockFlutterDevice.toggleWidgetInspector());
}));
testUsingContext('ResidentRunner debugToggleProfileWidgetBuilds calls flutter device', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugToggleProfileWidgetBuilds();
verify(mockFlutterDevice.toggleProfileWidgetBuilds()).called(1);
}));
testUsingContext('ResidentRunner debugToggleProfileWidgetBuilds does not call flutter device if service protocol is unsupported', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
target: 'main.dart',
devtoolsHandler: createNoOpHandler,
);
expect(await residentRunner.debugToggleProfileWidgetBuilds(), false);
verifyNever(mockFlutterDevice.toggleProfileWidgetBuilds());
}));
testUsingContext('HotRunner writes vm service file when providing debugging option', () => testbed.run(() async { testUsingContext('HotRunner writes vm service file when providing debugging option', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
listViews, listViews,
...@@ -2811,11 +2446,11 @@ void main() { ...@@ -2811,11 +2446,11 @@ void main() {
})); }));
testUsingContext('nextPlatform moves through expected platforms', () { testUsingContext('nextPlatform moves through expected platforms', () {
expect(nextPlatform('android', TestFeatureFlags()), 'iOS'); expect(nextPlatform('android'), 'iOS');
expect(nextPlatform('iOS', TestFeatureFlags()), 'fuchsia'); expect(nextPlatform('iOS'), 'fuchsia');
expect(nextPlatform('fuchsia', TestFeatureFlags()), 'android'); expect(nextPlatform('fuchsia'), 'macOS');
expect(nextPlatform('fuchsia', TestFeatureFlags(isMacOSEnabled: true)), 'macOS'); expect(nextPlatform('macOS'), 'android');
expect(() => nextPlatform('unknown', TestFeatureFlags()), throwsAssertionError); expect(() => nextPlatform('unknown'), throwsAssertionError);
}); });
} }
......
...@@ -55,7 +55,6 @@ void main() { ...@@ -55,7 +55,6 @@ void main() {
ipv6: true, ipv6: true,
stayResident: true, stayResident: true,
urlTunneller: null, urlTunneller: null,
featureFlags: TestFeatureFlags(),
fileSystem: globals.fs, fileSystem: globals.fs,
logger: globals.logger, logger: globals.logger,
systemClock: globals.systemClock, systemClock: globals.systemClock,
......
...@@ -195,7 +195,6 @@ void main() { ...@@ -195,7 +195,6 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
usage: globals.flutterUsage, usage: globals.flutterUsage,
featureFlags: TestFeatureFlags(),
systemClock: globals.systemClock, systemClock: globals.systemClock,
); );
...@@ -229,7 +228,7 @@ void main() { ...@@ -229,7 +228,7 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
usage: globals.flutterUsage, usage: globals.flutterUsage,
featureFlags: TestFeatureFlags(),
systemClock: globals.systemClock, systemClock: globals.systemClock,
); );
...@@ -253,7 +252,6 @@ void main() { ...@@ -253,7 +252,6 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
usage: globals.flutterUsage, usage: globals.flutterUsage,
featureFlags: TestFeatureFlags(),
systemClock: globals.systemClock, systemClock: globals.systemClock,
); );
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]); fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
...@@ -268,7 +266,6 @@ void main() { ...@@ -268,7 +266,6 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
usage: globals.flutterUsage, usage: globals.flutterUsage,
featureFlags: TestFeatureFlags(),
systemClock: globals.systemClock, systemClock: globals.systemClock,
); );
...@@ -373,7 +370,6 @@ void main() { ...@@ -373,7 +370,6 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: logger, logger: logger,
usage: globals.flutterUsage, usage: globals.flutterUsage,
featureFlags: TestFeatureFlags(),
systemClock: globals.systemClock, systemClock: globals.systemClock,
); );
...@@ -398,7 +394,6 @@ void main() { ...@@ -398,7 +394,6 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
usage: globals.flutterUsage, usage: globals.flutterUsage,
featureFlags: TestFeatureFlags(),
systemClock: globals.systemClock, systemClock: globals.systemClock,
); );
...@@ -517,7 +512,6 @@ void main() { ...@@ -517,7 +512,6 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
usage: globals.flutterUsage, usage: globals.flutterUsage,
featureFlags: TestFeatureFlags(),
systemClock: globals.systemClock, systemClock: globals.systemClock,
); );
fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList()); fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
...@@ -1439,7 +1433,6 @@ void main() { ...@@ -1439,7 +1433,6 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: logger, logger: logger,
usage: globals.flutterUsage, usage: globals.flutterUsage,
featureFlags: TestFeatureFlags(),
systemClock: globals.systemClock, systemClock: globals.systemClock,
); );
...@@ -1487,7 +1480,6 @@ void main() { ...@@ -1487,7 +1480,6 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: logger, logger: logger,
usage: globals.flutterUsage, usage: globals.flutterUsage,
featureFlags: TestFeatureFlags(),
systemClock: globals.systemClock, systemClock: globals.systemClock,
); );
...@@ -1606,7 +1598,6 @@ ResidentRunner setUpResidentRunner(FlutterDevice flutterDevice, { ...@@ -1606,7 +1598,6 @@ ResidentRunner setUpResidentRunner(FlutterDevice flutterDevice, {
systemClock: systemClock ?? SystemClock.fixed(DateTime.now()), systemClock: systemClock ?? SystemClock.fixed(DateTime.now()),
fileSystem: globals.fs, fileSystem: globals.fs,
logger: logger ?? BufferLogger.test(), logger: logger ?? BufferLogger.test(),
featureFlags: TestFeatureFlags(),
); );
} }
......
...@@ -240,7 +240,7 @@ void main() { ...@@ -240,7 +240,7 @@ void main() {
}); });
testWithoutContext('R - hotRestart supported and succeeds', () async { testWithoutContext('R - hotRestart supported and succeeds', () async {
when(mockResidentRunner.canHotRestart).thenReturn(true); when(mockResidentRunner.supportsRestart).thenReturn(true);
when(mockResidentRunner.hotMode).thenReturn(true); when(mockResidentRunner.hotMode).thenReturn(true);
when(mockResidentRunner.restart(fullRestart: true)) when(mockResidentRunner.restart(fullRestart: true))
.thenAnswer((Invocation invocation) async { .thenAnswer((Invocation invocation) async {
...@@ -252,7 +252,7 @@ void main() { ...@@ -252,7 +252,7 @@ void main() {
}); });
testWithoutContext('R - hotRestart supported and fails', () async { testWithoutContext('R - hotRestart supported and fails', () async {
when(mockResidentRunner.canHotRestart).thenReturn(true); when(mockResidentRunner.supportsRestart).thenReturn(true);
when(mockResidentRunner.hotMode).thenReturn(true); when(mockResidentRunner.hotMode).thenReturn(true);
when(mockResidentRunner.restart(fullRestart: true)) when(mockResidentRunner.restart(fullRestart: true))
.thenAnswer((Invocation invocation) async { .thenAnswer((Invocation invocation) async {
...@@ -266,7 +266,7 @@ void main() { ...@@ -266,7 +266,7 @@ void main() {
}); });
testWithoutContext('R - hotRestart supported and fails fatally', () async { testWithoutContext('R - hotRestart supported and fails fatally', () async {
when(mockResidentRunner.canHotRestart).thenReturn(true); when(mockResidentRunner.supportsRestart).thenReturn(true);
when(mockResidentRunner.hotMode).thenReturn(true); when(mockResidentRunner.hotMode).thenReturn(true);
when(mockResidentRunner.restart(fullRestart: true)) when(mockResidentRunner.restart(fullRestart: true))
.thenAnswer((Invocation invocation) async { .thenAnswer((Invocation invocation) async {
...@@ -276,12 +276,23 @@ void main() { ...@@ -276,12 +276,23 @@ void main() {
}); });
testWithoutContext('R - hot restart unsupported', () async { testWithoutContext('R - hot restart unsupported', () async {
when(mockResidentRunner.canHotRestart).thenReturn(false); when(mockResidentRunner.supportsRestart).thenReturn(false);
await terminalHandler.processTerminalInput('R'); await terminalHandler.processTerminalInput('R');
verifyNever(mockResidentRunner.restart(fullRestart: true)); verifyNever(mockResidentRunner.restart(fullRestart: true));
}); });
testWithoutContext('ResidentRunner clears the screen when it should', () async {
const String message = 'This should be cleared';
expect(testLogger.statusText, equals(''));
testLogger.printStatus(message);
expect(testLogger.statusText, equals(message + '\n')); // printStatus makes a newline
await terminalHandler.processTerminalInput('c');
expect(testLogger.statusText, equals(''));
});
testWithoutContext('S - debugDumpSemanticsTreeInTraversalOrder with service protocol', () async { testWithoutContext('S - debugDumpSemanticsTreeInTraversalOrder with service protocol', () async {
await terminalHandler.processTerminalInput('S'); await terminalHandler.processTerminalInput('S');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment