Unverified Commit 8acac060 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Remove the timeout when launching DevTools (#74859)

parent 43d604e1
...@@ -103,7 +103,7 @@ class AttachCommand extends FlutterCommand { ...@@ -103,7 +103,7 @@ class AttachCommand extends FlutterCommand {
); );
usesTrackWidgetCreation(verboseHelp: verboseHelp); usesTrackWidgetCreation(verboseHelp: verboseHelp);
addDdsOptions(verboseHelp: verboseHelp); addDdsOptions(verboseHelp: verboseHelp);
addDevToolsOptions(); addDevToolsOptions(verboseHelp: verboseHelp);
usesDeviceTimeoutOption(); usesDeviceTimeoutOption();
hotRunnerFactory ??= HotRunnerFactory(); hotRunnerFactory ??= HotRunnerFactory();
} }
...@@ -207,7 +207,8 @@ known, it can be explicitly provided to attach via the command-line, e.g. ...@@ -207,7 +207,8 @@ known, it can be explicitly provided to attach via the command-line, e.g.
body: () => _attachToDevice(device), body: () => _attachToDevice(device),
overrides: <Type, Generator>{ overrides: <Type, Generator>{
Artifacts: () => overrideArtifacts, Artifacts: () => overrideArtifacts,
}); },
);
return FlutterCommandResult.success(); return FlutterCommandResult.success();
} }
...@@ -327,6 +328,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. ...@@ -327,6 +328,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
allowExistingDdsInstance: true, allowExistingDdsInstance: true,
enableDevTools: boolArg(FlutterCommand.kEnableDevTools),
); );
}, },
device, device,
...@@ -365,6 +367,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. ...@@ -365,6 +367,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
result = await runner.attach( result = await runner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: onAppStart,
allowExistingDdsInstance: true, allowExistingDdsInstance: true,
enableDevTools: boolArg(FlutterCommand.kEnableDevTools),
); );
if (result != 0) { if (result != 0) {
throwToolExit(null, exitCode: result); throwToolExit(null, exitCode: result);
......
...@@ -515,6 +515,7 @@ class AppDomain extends Domain { ...@@ -515,6 +515,7 @@ class AppDomain extends Domain {
return runner.run( return runner.run(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
enableDevTools: true,
route: route, route: route,
); );
}, },
......
...@@ -136,7 +136,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment ...@@ -136,7 +136,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
usesDeviceUserOption(); usesDeviceUserOption();
usesDeviceTimeoutOption(); usesDeviceTimeoutOption();
addDdsOptions(verboseHelp: verboseHelp); addDdsOptions(verboseHelp: verboseHelp);
addDevToolsOptions(); addDevToolsOptions(verboseHelp: verboseHelp);
addAndroidSpecificBuildOptions(hide: !verboseHelp); addAndroidSpecificBuildOptions(hide: !verboseHelp);
} }
...@@ -621,6 +621,7 @@ class RunCommand extends RunCommandBase { ...@@ -621,6 +621,7 @@ class RunCommand extends RunCommandBase {
final int result = await runner.run( final int result = await runner.run(
appStartedCompleter: appStartedTimeRecorder, appStartedCompleter: appStartedTimeRecorder,
enableDevTools: stayResident && boolArg(FlutterCommand.kEnableDevTools),
route: route, route: route,
); );
if (result != 0) { if (result != 0) {
......
...@@ -48,8 +48,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -48,8 +48,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
@override @override
Future<void> launch(Uri vmServiceUri) async { Future<void> launch(Uri vmServiceUri) async {
// Place this entire method in a try/catch that swallows exceptions because // Place this entire method in a try/catch that swallows exceptions because
// we do not want to block Flutter run/attach operations on a DevTools // this method is guaranteed not to return a Future that throws.
// failure.
try { try {
bool offline = false; bool offline = false;
try { try {
...@@ -109,8 +108,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -109,8 +108,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
.transform(utf8.decoder) .transform(utf8.decoder)
.transform(const LineSplitter()) .transform(const LineSplitter())
.listen(_logger.printError); .listen(_logger.printError);
devToolsUri = await completer.future devToolsUrl = await completer.future;
.timeout(const Duration(seconds: 10));
} on Exception catch (e, st) { } on Exception catch (e, st) {
_logger.printError('Failed to launch DevTools: $e', stackTrace: st); _logger.printError('Failed to launch DevTools: $e', stackTrace: st);
} }
...@@ -124,7 +122,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -124,7 +122,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
'global', 'global',
'list', 'list',
]); ]);
if (_pubGlobalListProcess.stdout.toString().contains('devtools ')) { if (_pubGlobalListProcess.stdout.toString().contains('devtools ')) {
return true; return true;
} }
...@@ -144,7 +141,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -144,7 +141,6 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
if (!shouldActivate) { if (!shouldActivate) {
return false; return false;
} }
final Status status = _logger.startProgress( final Status status = _logger.startProgress(
'Activating Dart DevTools...', 'Activating Dart DevTools...',
); );
...@@ -182,7 +178,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher { ...@@ -182,7 +178,7 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
@override @override
Future<void> close() async { Future<void> close() async {
devToolsUri = null; devToolsUrl = null;
if (_devToolsProcess != null) { if (_devToolsProcess != null) {
_devToolsProcess.kill(); _devToolsProcess.kill();
await _devToolsProcess.exitCode; await _devToolsProcess.exitCode;
......
...@@ -76,6 +76,7 @@ class WebDriverService extends DriverService { ...@@ -76,6 +76,7 @@ class WebDriverService extends DriverService {
final Completer<void> appStartedCompleter = Completer<void>.sync(); final Completer<void> appStartedCompleter = Completer<void>.sync();
final int result = await _residentRunner.run( final int result = await _residentRunner.run(
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
enableDevTools: false,
route: route, route: route,
); );
_webUri = _residentRunner.uri; _webUri = _residentRunner.uri;
......
...@@ -454,6 +454,7 @@ class _ResidentWebRunner extends ResidentWebRunner { ...@@ -454,6 +454,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false, // ignored, we don't yet support devtools for web
String route, String route,
}) async { }) async {
firstBuildTime = DateTime.now(); firstBuildTime = DateTime.now();
...@@ -531,6 +532,7 @@ class _ResidentWebRunner extends ResidentWebRunner { ...@@ -531,6 +532,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
return attach( return attach(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
enableDevTools: enableDevTools,
); );
}); });
} on WebSocketException { } on WebSocketException {
...@@ -745,6 +747,7 @@ class _ResidentWebRunner extends ResidentWebRunner { ...@@ -745,6 +747,7 @@ class _ResidentWebRunner extends ResidentWebRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false, // ignored, we don't yet support devtools for web
}) async { }) async {
if (_chromiumLauncher != null) { if (_chromiumLauncher != null) {
final Chromium chrome = await _chromiumLauncher.connectedInstance; final Chromium chrome = await _chromiumLauncher.connectedInstance;
......
...@@ -795,6 +795,8 @@ abstract class ResidentRunner { ...@@ -795,6 +795,8 @@ abstract class ResidentRunner {
final CommandHelp commandHelp; final CommandHelp commandHelp;
final bool machine; final bool machine;
@visibleForTesting
DevtoolsLauncher get devToolsLauncher => _devToolsLauncher;
DevtoolsLauncher _devToolsLauncher; DevtoolsLauncher _devToolsLauncher;
bool _exited = false; bool _exited = false;
...@@ -878,6 +880,7 @@ abstract class ResidentRunner { ...@@ -878,6 +880,7 @@ abstract class ResidentRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false,
String route, String route,
}); });
...@@ -885,6 +888,7 @@ abstract class ResidentRunner { ...@@ -885,6 +888,7 @@ abstract class ResidentRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false,
}); });
bool get supportsRestart => false; bool get supportsRestart => false;
...@@ -1275,19 +1279,26 @@ abstract class ResidentRunner { ...@@ -1275,19 +1279,26 @@ abstract class ResidentRunner {
return _devToolsLauncher.activeDevToolsServer; return _devToolsLauncher.activeDevToolsServer;
} }
Future<void> serveDevToolsGracefully({ // This must be guaranteed not to return a Future that fails.
Uri devToolsServerAddress Future<void> serveAndAnnounceDevTools({
Uri devToolsServerAddress,
}) async { }) async {
if (!supportsServiceProtocol) { if (!supportsServiceProtocol) {
return; return;
} }
_devToolsLauncher ??= DevtoolsLauncher.instance; _devToolsLauncher ??= DevtoolsLauncher.instance;
if (devToolsServerAddress != null) { if (devToolsServerAddress != null) {
_devToolsLauncher.devToolsUri = devToolsServerAddress; _devToolsLauncher.devToolsUrl = devToolsServerAddress;
} else { } else {
await _devToolsLauncher.serve(); unawaited(_devToolsLauncher.serve());
}
await _devToolsLauncher.ready;
if (_reportedDebuggers) {
// Since the DevTools only just became available, we haven't had a chance to
// report their URLs yet. Do so now.
printDebuggerList(includeObservatory: false);
} }
await maybeCallDevToolsUriServiceExtension();
} }
Future<void> maybeCallDevToolsUriServiceExtension() async { Future<void> maybeCallDevToolsUriServiceExtension() async {
...@@ -1417,6 +1428,39 @@ abstract class ResidentRunner { ...@@ -1417,6 +1428,39 @@ abstract class ResidentRunner {
appFinished(); appFinished();
} }
bool _reportedDebuggers = false;
void printDebuggerList({ bool includeObservatory = true, bool includeDevtools = true }) {
final DevToolsServerAddress devToolsServerAddress = activeDevToolsServer();
if (devToolsServerAddress == null) {
includeDevtools = false;
}
for (final FlutterDevice device in flutterDevices) {
if (device.vmService == null) {
continue;
}
if (includeObservatory) {
// Caution: This log line is parsed by device lab tests.
globals.printStatus(
'An Observatory debugger and profiler on ${device.device.name} is available at: '
'${device.vmService.httpAddress}',
);
}
if (includeDevtools) {
final Uri uri = devToolsServerAddress.uri?.replace(
queryParameters: <String, dynamic>{'uri': '${device.vmService.httpAddress}'},
);
if (uri != null) {
globals.printStatus(
'The Flutter DevTools debugger and profiler '
'on ${device.device.name} is available at: $uri',
);
}
}
}
_reportedDebuggers = true;
}
/// Called to print help to the terminal. /// Called to print help to the terminal.
void printHelp({ @required bool details }); void printHelp({ @required bool details });
...@@ -1763,24 +1807,51 @@ String nextPlatform(String currentPlatform, FeatureFlags featureFlags) { ...@@ -1763,24 +1807,51 @@ String nextPlatform(String currentPlatform, FeatureFlags featureFlags) {
/// A launcher for the devtools debugger and analysis tool. /// A launcher for the devtools debugger and analysis tool.
abstract class DevtoolsLauncher { abstract class DevtoolsLauncher {
Uri devToolsUri; static DevtoolsLauncher get instance => context.get<DevtoolsLauncher>();
/// Serve Dart DevTools and return the host and port they are available on.
///
/// This method must return a future that is guaranteed not to fail, because it
/// will be used in unawaited contexts. It may, however, return null.
Future<DevToolsServerAddress> serve();
/// Launch a Dart DevTools process, optionally targeting a specific VM Service /// Launch a Dart DevTools process, optionally targeting a specific VM Service
/// URI if [vmServiceUri] is non-null. /// URI if [vmServiceUri] is non-null.
///
/// This method must return a future that is guaranteed not to fail, because it
/// will be used in unawaited contexts.
@visibleForTesting
Future<void> launch(Uri vmServiceUri); Future<void> launch(Uri vmServiceUri);
/// Serve Dart DevTools and return the host and port they are available on.
Future<DevToolsServerAddress> serve();
Future<void> close(); Future<void> close();
static DevtoolsLauncher get instance => context.get<DevtoolsLauncher>(); /// Returns a future that completes when the DevTools server is ready.
///
/// Completes when [devToolsUrl] is set. That can be set either directly, or
/// by calling [serve].
Future<void> get ready => _readyCompleter.future;
Completer<void> _readyCompleter = Completer<void>();
Uri get devToolsUrl => _devToolsUrl;
Uri _devToolsUrl;
set devToolsUrl(Uri value) {
assert((_devToolsUrl == null) != (value == null));
_devToolsUrl = value;
if (_devToolsUrl != null) {
_readyCompleter.complete();
} else {
_readyCompleter = Completer<void>();
}
}
/// The URL of the current DevTools server.
///
/// Returns null if [ready] is not complete.
DevToolsServerAddress get activeDevToolsServer { DevToolsServerAddress get activeDevToolsServer {
if (devToolsUri == null) { if (_devToolsUrl == null) {
return null; return null;
} }
return DevToolsServerAddress(devToolsUri.host, devToolsUri.port); return DevToolsServerAddress(devToolsUrl.host, devToolsUrl.port);
} }
} }
......
...@@ -53,6 +53,7 @@ class ColdRunner extends ResidentRunner { ...@@ -53,6 +53,7 @@ class ColdRunner extends ResidentRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false,
String route, String route,
}) async { }) async {
try { try {
...@@ -72,17 +73,17 @@ class ColdRunner extends ResidentRunner { ...@@ -72,17 +73,17 @@ class ColdRunner extends ResidentRunner {
return 1; return 1;
} }
if (enableDevTools) {
// The method below is guaranteed never to return a failing future.
unawaited(serveAndAnnounceDevTools(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
));
}
// Connect to observatory. // Connect to observatory.
if (debuggingOptions.debuggingEnabled) { if (debuggingEnabled) {
try { try {
await Future.wait(<Future<void>>[ await connectToServiceProtocol(allowExistingDdsInstance: false);
connectToServiceProtocol(
allowExistingDdsInstance: false,
),
serveDevToolsGracefully(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
),
]);
} on String catch (message) { } on String catch (message) {
globals.printError(message); globals.printError(message);
appFailedToStart(); appFailedToStart();
...@@ -124,7 +125,6 @@ class ColdRunner extends ResidentRunner { ...@@ -124,7 +125,6 @@ class ColdRunner extends ResidentRunner {
} }
if (debuggingEnabled) { if (debuggingEnabled) {
unawaited(maybeCallDevToolsUriServiceExtension());
unawaited(callConnectedVmServiceUriExtension()); unawaited(callConnectedVmServiceUriExtension());
} }
...@@ -144,22 +144,26 @@ class ColdRunner extends ResidentRunner { ...@@ -144,22 +144,26 @@ class ColdRunner extends ResidentRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false,
}) async { }) async {
_didAttach = true; _didAttach = true;
try { try {
await Future.wait(<Future<void>>[ await connectToServiceProtocol(
connectToServiceProtocol( getSkSLMethod: writeSkSL,
getSkSLMethod: writeSkSL, allowExistingDdsInstance: allowExistingDdsInstance,
allowExistingDdsInstance: allowExistingDdsInstance, );
),
serveDevToolsGracefully(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
),
], eagerError: true);
} on Exception catch (error) { } on Exception catch (error) {
globals.printError('Error connecting to the service protocol: $error'); globals.printError('Error connecting to the service protocol: $error');
return 2; return 2;
} }
if (enableDevTools) {
// The method below is guaranteed never to return a failing future.
unawaited(serveAndAnnounceDevTools(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
));
}
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
await device.initLogReader(); await device.initLogReader();
} }
...@@ -170,7 +174,6 @@ class ColdRunner extends ResidentRunner { ...@@ -170,7 +174,6 @@ class ColdRunner extends ResidentRunner {
} }
} }
unawaited(maybeCallDevToolsUriServiceExtension());
unawaited(callConnectedVmServiceUriExtension()); unawaited(callConnectedVmServiceUriExtension());
appStartedCompleter?.complete(); appStartedCompleter?.complete();
...@@ -205,35 +208,13 @@ class ColdRunner extends ResidentRunner { ...@@ -205,35 +208,13 @@ class ColdRunner extends ResidentRunner {
if (details) { if (details) {
printHelpDetails(); printHelpDetails();
} }
commandHelp.h.print(); commandHelp.h.print(); // TODO(ianh): print different message if details is false
if (_didAttach) { if (_didAttach) {
commandHelp.d.print(); commandHelp.d.print();
} }
commandHelp.c.print(); commandHelp.c.print();
commandHelp.q.print(); commandHelp.q.print();
for (final FlutterDevice device in flutterDevices) { printDebuggerList();
final String dname = device.device.name;
if (device.vmService != null) {
// Caution: This log line is parsed by device lab tests.
globals.printStatus(
'An Observatory debugger and profiler on $dname is available at: '
'${device.vmService.httpAddress}',
);
final DevToolsServerAddress devToolsServerAddress = activeDevToolsServer();
if (devToolsServerAddress != null) {
final Uri uri = devToolsServerAddress.uri?.replace(
queryParameters: <String, dynamic>{'uri': '${device.vmService.httpAddress}'},
);
if (uri != null) {
globals.printStatus(
'\nFlutter DevTools, a Flutter debugger and profiler, on '
'${device.device.name} is available at: $uri',
);
}
}
}
}
} }
@override @override
......
...@@ -174,21 +174,17 @@ class HotRunner extends ResidentRunner { ...@@ -174,21 +174,17 @@ class HotRunner extends ResidentRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false,
}) async { }) async {
_didAttach = true; _didAttach = true;
try { try {
await Future.wait(<Future<void>>[ await connectToServiceProtocol(
connectToServiceProtocol( reloadSources: _reloadSourcesService,
reloadSources: _reloadSourcesService, restart: _restartService,
restart: _restartService, compileExpression: _compileExpressionService,
compileExpression: _compileExpressionService, getSkSLMethod: writeSkSL,
getSkSLMethod: writeSkSL, allowExistingDdsInstance: allowExistingDdsInstance,
allowExistingDdsInstance: allowExistingDdsInstance, );
),
serveDevToolsGracefully(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
),
]);
// Catches all exceptions, non-Exception objects are rethrown. // Catches all exceptions, non-Exception objects are rethrown.
} catch (error) { // ignore: avoid_catches_without_on_clauses } catch (error) { // ignore: avoid_catches_without_on_clauses
if (error is! Exception && error is! String) { if (error is! Exception && error is! String) {
...@@ -198,6 +194,13 @@ class HotRunner extends ResidentRunner { ...@@ -198,6 +194,13 @@ class HotRunner extends ResidentRunner {
return 2; return 2;
} }
if (enableDevTools) {
// The method below is guaranteed never to return a failing future.
unawaited(serveAndAnnounceDevTools(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
));
}
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
await device.initLogReader(); await device.initLogReader();
} }
...@@ -218,7 +221,6 @@ class HotRunner extends ResidentRunner { ...@@ -218,7 +221,6 @@ class HotRunner extends ResidentRunner {
return 3; return 3;
} }
unawaited(maybeCallDevToolsUriServiceExtension());
unawaited(callConnectedVmServiceUriExtension()); unawaited(callConnectedVmServiceUriExtension());
final Stopwatch initialUpdateDevFSsTimer = Stopwatch()..start(); final Stopwatch initialUpdateDevFSsTimer = Stopwatch()..start();
...@@ -306,6 +308,7 @@ class HotRunner extends ResidentRunner { ...@@ -306,6 +308,7 @@ class HotRunner extends ResidentRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false,
String route, String route,
}) async { }) async {
firstBuildTime = DateTime.now(); firstBuildTime = DateTime.now();
...@@ -356,6 +359,7 @@ class HotRunner extends ResidentRunner { ...@@ -356,6 +359,7 @@ class HotRunner extends ResidentRunner {
return attach( return attach(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
appStartedCompleter: appStartedCompleter, appStartedCompleter: appStartedCompleter,
enableDevTools: enableDevTools,
); );
} }
...@@ -1086,7 +1090,7 @@ class HotRunner extends ResidentRunner { ...@@ -1086,7 +1090,7 @@ class HotRunner extends ResidentRunner {
if (canHotRestart) { if (canHotRestart) {
commandHelp.R.print(); commandHelp.R.print();
} }
commandHelp.h.print(); commandHelp.h.print(); // TODO(ianh): print different message if "details" is false
if (_didAttach) { if (_didAttach) {
commandHelp.d.print(); commandHelp.d.print();
} }
...@@ -1095,26 +1099,6 @@ class HotRunner extends ResidentRunner { ...@@ -1095,26 +1099,6 @@ class HotRunner extends ResidentRunner {
if (details) { if (details) {
printHelpDetails(); printHelpDetails();
} }
for (final FlutterDevice device in flutterDevices) {
// Caution: This log line is parsed by device lab tests.
globals.printStatus(
'An Observatory debugger and profiler on ${device.device.name} is available at: '
'${device.vmService.httpAddress}',
);
final DevToolsServerAddress devToolsServerAddress = activeDevToolsServer();
if (devToolsServerAddress != null) {
final Uri uri = devToolsServerAddress.uri?.replace(
queryParameters: <String, dynamic>{'uri': '${device.vmService.httpAddress}'},
);
if (uri != null) {
globals.printStatus(
'\nFlutter DevTools, a Flutter debugger and profiler, on '
'${device.device.name} is available at: $uri',
);
}
}
}
globals.printStatus(''); globals.printStatus('');
if (debuggingOptions.buildInfo.nullSafetyMode == NullSafetyMode.sound) { if (debuggingOptions.buildInfo.nullSafetyMode == NullSafetyMode.sound) {
globals.printStatus('💪 Running with sound null safety 💪', emphasis: true); globals.printStatus('💪 Running with sound null safety 💪', emphasis: true);
...@@ -1127,6 +1111,8 @@ class HotRunner extends ResidentRunner { ...@@ -1127,6 +1111,8 @@ class HotRunner extends ResidentRunner {
'For more information see https://dart.dev/null-safety/unsound-null-safety', 'For more information see https://dart.dev/null-safety/unsound-null-safety',
); );
} }
globals.printStatus('');
printDebuggerList();
} }
Future<void> _evictDirtyAssets() async { Future<void> _evictDirtyAssets() async {
......
...@@ -133,6 +133,9 @@ abstract class FlutterCommand extends Command<void> { ...@@ -133,6 +133,9 @@ abstract class FlutterCommand extends Command<void> {
/// The option name for a custom DevTools server address. /// The option name for a custom DevTools server address.
static const String kDevToolsServerAddress = 'devtools-server-address'; static const String kDevToolsServerAddress = 'devtools-server-address';
/// The flag name for whether to launch the DevTools or not.
static const String kEnableDevTools = 'devtools';
/// The flag name for whether or not to use ipv6. /// The flag name for whether or not to use ipv6.
static const String ipv6Flag = 'ipv6'; static const String ipv6Flag = 'ipv6';
...@@ -327,27 +330,39 @@ abstract class FlutterCommand extends Command<void> { ...@@ -327,27 +330,39 @@ abstract class FlutterCommand extends Command<void> {
_usesPortOption = true; _usesPortOption = true;
} }
void addDevToolsOptions() { void addDevToolsOptions({@required bool verboseHelp}) {
argParser.addOption(kDevToolsServerAddress, argParser.addFlag(
kEnableDevTools,
hide: !verboseHelp,
defaultsTo: true,
help: 'Enable (or disable, with --no-$kEnableDevTools) the launching of the '
'Flutter DevTools debugger and profiler. '
'If specified, --$kDevToolsServerAddress is ignored.'
);
argParser.addOption(
kDevToolsServerAddress,
hide: !verboseHelp,
help: 'When this value is provided, the Flutter tool will not spin up a ' help: 'When this value is provided, the Flutter tool will not spin up a '
'new DevTools server instance, but instead will use the one provided ' 'new DevTools server instance, and will instead use the one provided '
'at this address.'); 'at the given address. Ignored if --no-$kEnableDevTools is specified.'
);
} }
void addDdsOptions({@required bool verboseHelp}) { void addDdsOptions({@required bool verboseHelp}) {
argParser.addOption('dds-port', argParser.addOption('dds-port',
help: 'When this value is provided, the Dart Development Service (DDS) will be ' help: 'When this value is provided, the Dart Development Service (DDS) will be '
'bound to the provided port.\nSpecifying port 0 (the default) will find ' 'bound to the provided port.\n'
'a random free port.'); 'Specifying port 0 (the default) will find a random free port.'
);
argParser.addFlag( argParser.addFlag(
'disable-dds', 'disable-dds', // TODO(ianh): this should be called `dds` and default to true (see style guide about double negatives)
hide: !verboseHelp, hide: !verboseHelp,
help: 'Disable the Dart Developer Service (DDS). This flag should only be provided' help: 'Disable the Dart Developer Service (DDS). This flag should only be provided '
' when attaching to an application with an existing DDS instance (e.g.,' 'when attaching to an application with an existing DDS instance (e.g., '
' attaching to an application currently connected to by "flutter run") or' 'attaching to an application currently connected to by "flutter run") or '
' when running certain tests.\n' 'when running certain tests.\n'
'Note: passing this flag may degrade IDE functionality if a DDS instance is not' 'Passing this flag may degrade IDE functionality if a DDS instance is not '
' already connected to the target application.' 'already connected to the target application.'
); );
} }
......
...@@ -181,8 +181,11 @@ void main() { ...@@ -181,8 +181,11 @@ void main() {
const String outputDill = '/tmp/output.dill'; const String outputDill = '/tmp/output.dill';
final MockHotRunner mockHotRunner = MockHotRunner(); final MockHotRunner mockHotRunner = MockHotRunner();
when(mockHotRunner.attach(appStartedCompleter: anyNamed('appStartedCompleter'), allowExistingDdsInstance: true)) when(mockHotRunner.attach(
.thenAnswer((_) async => 0); appStartedCompleter: anyNamed('appStartedCompleter'),
allowExistingDdsInstance: true,
enableDevTools: anyNamed('enableDevTools'),
)).thenAnswer((_) async => 0);
when(mockHotRunner.exited).thenReturn(false); when(mockHotRunner.exited).thenReturn(false);
when(mockHotRunner.isWaitingForObservatory).thenReturn(false); when(mockHotRunner.isWaitingForObservatory).thenReturn(false);
...@@ -313,8 +316,11 @@ void main() { ...@@ -313,8 +316,11 @@ void main() {
.thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]); .thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]);
when(portForwarder.unforward(any)) when(portForwarder.unforward(any))
.thenAnswer((_) async {}); .thenAnswer((_) async {});
when(mockHotRunner.attach(appStartedCompleter: anyNamed('appStartedCompleter'), allowExistingDdsInstance: true)) when(mockHotRunner.attach(
.thenAnswer((_) async => 0); appStartedCompleter: anyNamed('appStartedCompleter'),
allowExistingDdsInstance: true,
enableDevTools: anyNamed('enableDevTools'),
)).thenAnswer((_) async => 0);
when(mockHotRunnerFactory.build( when(mockHotRunnerFactory.build(
any, any,
target: anyNamed('target'), target: anyNamed('target'),
...@@ -397,8 +403,11 @@ void main() { ...@@ -397,8 +403,11 @@ void main() {
.thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]); .thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]);
when(portForwarder.unforward(any)) when(portForwarder.unforward(any))
.thenAnswer((_) async {}); .thenAnswer((_) async {});
when(mockHotRunner.attach(appStartedCompleter: anyNamed('appStartedCompleter'), allowExistingDdsInstance: true)) when(mockHotRunner.attach(
.thenAnswer((_) async => 0); appStartedCompleter: anyNamed('appStartedCompleter'),
allowExistingDdsInstance: true,
enableDevTools: anyNamed('enableDevTools'),
)).thenAnswer((_) async => 0);
when(mockHotRunnerFactory.build( when(mockHotRunnerFactory.build(
any, any,
target: anyNamed('target'), target: anyNamed('target'),
......
...@@ -42,7 +42,9 @@ void main() { ...@@ -42,7 +42,9 @@ void main() {
final int exitCode = await ColdRunner(devices, final int exitCode = await ColdRunner(devices,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
target: 'main.dart', target: 'main.dart',
).attach(); ).attach(
enableDevTools: false,
);
expect(exitCode, 2); expect(exitCode, 2);
}); });
...@@ -90,7 +92,9 @@ void main() { ...@@ -90,7 +92,9 @@ void main() {
applicationBinary: applicationBinary, applicationBinary: applicationBinary,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
target: 'main.dart', target: 'main.dart',
).run(); ).run(
enableDevTools: false,
);
expect(result, 1); expect(result, 1);
verify(mockFlutterDevice.runCold( verify(mockFlutterDevice.runCold(
......
...@@ -524,7 +524,9 @@ void main() { ...@@ -524,7 +524,9 @@ void main() {
final int exitCode = await HotRunner(devices, final int exitCode = await HotRunner(devices,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
target: 'main.dart', target: 'main.dart',
).attach(); ).attach(
enableDevTools: false,
);
expect(exitCode, 2); expect(exitCode, 2);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
......
...@@ -224,21 +224,22 @@ void main() { ...@@ -224,21 +224,22 @@ void main() {
listViews, listViews,
setAssetBundlePath, setAssetBundlePath,
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
final Future<int> result = residentRunner.attach( final Future<int> result = residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
); );
final Future<DebugConnectionInfo> connectionInfo = onConnectionInfo.future; final Future<DebugConnectionInfo> connectionInfo = futureConnectionInfo.future;
expect(await result, 0); expect(await result, 0);
verify(mockFlutterDevice.initLogReader()).called(1); verify(mockFlutterDevice.initLogReader()).called(1);
expect(onConnectionInfo.isCompleted, true); expect(futureConnectionInfo.isCompleted, true);
expect((await connectionInfo).baseUri, 'foo://bar'); expect((await connectionInfo).baseUri, 'foo://bar');
expect(onAppStart.isCompleted, true); expect(futureAppStart.isCompleted, true);
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
}), overrides: <Type, Generator>{ }), overrides: <Type, Generator>{
DevtoolsLauncher: () => mockDevtoolsLauncher, DevtoolsLauncher: () => mockDevtoolsLauncher,
...@@ -278,7 +279,7 @@ void main() { ...@@ -278,7 +279,7 @@ void main() {
return 0; return 0;
}); });
expect(await residentRunner.run(), 0); expect(await residentRunner.run(enableDevTools: true), 0);
verify(residentCompiler.recompile( verify(residentCompiler.recompile(
any, any,
any, any,
...@@ -412,7 +413,7 @@ void main() { ...@@ -412,7 +413,7 @@ void main() {
return 0; return 0;
}); });
expect(await residentRunner.run(), 0); expect(await residentRunner.run(enableDevTools: true), 0);
verify(residentCompiler.recompile( verify(residentCompiler.recompile(
any, any,
any, any,
...@@ -477,21 +478,22 @@ void main() { ...@@ -477,21 +478,22 @@ void main() {
), ),
target: 'main.dart', target: 'main.dart',
); );
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
final Future<int> result = residentRunner.attach( final Future<int> result = residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
); );
final Future<DebugConnectionInfo> connectionInfo = onConnectionInfo.future; final Future<DebugConnectionInfo> connectionInfo = futureConnectionInfo.future;
expect(await result, 0); expect(await result, 0);
verify(mockFlutterDevice.initLogReader()).called(1); verify(mockFlutterDevice.initLogReader()).called(1);
expect(onConnectionInfo.isCompleted, true); expect(futureConnectionInfo.isCompleted, true);
expect((await connectionInfo).baseUri, 'foo://bar'); expect((await connectionInfo).baseUri, 'foo://bar');
expect(onAppStart.isCompleted, true); expect(futureAppStart.isCompleted, true);
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
}), overrides: <Type, Generator>{ }), overrides: <Type, Generator>{
DevtoolsLauncher: () => mockDevtoolsLauncher, DevtoolsLauncher: () => mockDevtoolsLauncher,
...@@ -504,13 +506,14 @@ void main() { ...@@ -504,13 +506,14 @@ void main() {
setAssetBundlePath, setAssetBundlePath,
listViews, listViews,
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await onAppStart.future; await futureAppStart.future;
when(mockFlutterDevice.updateDevFS( when(mockFlutterDevice.updateDevFS(
mainUri: anyNamed('mainUri'), mainUri: anyNamed('mainUri'),
target: anyNamed('target'), target: anyNamed('target'),
...@@ -551,13 +554,13 @@ void main() { ...@@ -551,13 +554,13 @@ void main() {
listViews, listViews,
setAssetBundlePath, setAssetBundlePath,
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
)); ));
await onAppStart.future; await futureAppStart.future;
when(mockFlutterDevice.devFS).thenReturn(null); when(mockFlutterDevice.devFS).thenReturn(null);
final OperationResult result = await residentRunner.restart(fullRestart: false); final OperationResult result = await residentRunner.restart(fullRestart: false);
...@@ -565,7 +568,7 @@ void main() { ...@@ -565,7 +568,7 @@ void main() {
expect(result.code, 1); expect(result.code, 1);
expect(result.message, contains('Device initialization has not completed.')); expect(result.message, contains('Device initialization has not completed.'));
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
}), timeout: const Timeout(Duration(seconds: 15))); // https://github.com/flutter/flutter/issues/74539 }));
testUsingContext('ResidentRunner can handle an reload-barred exception from hot reload', () => testbed.run(() async { testUsingContext('ResidentRunner can handle an reload-barred exception from hot reload', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...@@ -574,13 +577,14 @@ void main() { ...@@ -574,13 +577,14 @@ void main() {
setAssetBundlePath, setAssetBundlePath,
listViews, listViews,
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await onAppStart.future; await futureAppStart.future;
when(mockFlutterDevice.updateDevFS( when(mockFlutterDevice.updateDevFS(
mainUri: anyNamed('mainUri'), mainUri: anyNamed('mainUri'),
target: anyNamed('target'), target: anyNamed('target'),
...@@ -636,13 +640,14 @@ void main() { ...@@ -636,13 +640,14 @@ void main() {
], ],
)), )),
); );
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await onAppStart.future; await futureAppStart.future;
when(mockFlutterDevice.updateDevFS( when(mockFlutterDevice.updateDevFS(
mainUri: anyNamed('mainUri'), mainUri: anyNamed('mainUri'),
target: anyNamed('target'), target: anyNamed('target'),
...@@ -705,13 +710,14 @@ void main() { ...@@ -705,13 +710,14 @@ void main() {
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
target: 'main.dart', target: 'main.dart',
); );
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await onAppStart.future; await futureAppStart.future;
when(mockFlutterDevice.updateDevFS( when(mockFlutterDevice.updateDevFS(
mainUri: anyNamed('mainUri'), mainUri: anyNamed('mainUri'),
target: anyNamed('target'), target: anyNamed('target'),
...@@ -780,13 +786,14 @@ void main() { ...@@ -780,13 +786,14 @@ void main() {
}, },
), ),
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await onAppStart.future; await futureAppStart.future;
when(mockFlutterDevice.updateDevFS( when(mockFlutterDevice.updateDevFS(
mainUri: anyNamed('mainUri'), mainUri: anyNamed('mainUri'),
target: anyNamed('target'), target: anyNamed('target'),
...@@ -861,13 +868,14 @@ void main() { ...@@ -861,13 +868,14 @@ void main() {
}, },
), ),
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await onAppStart.future; await futureAppStart.future;
when(mockFlutterDevice.updateDevFS( when(mockFlutterDevice.updateDevFS(
mainUri: anyNamed('mainUri'), mainUri: anyNamed('mainUri'),
target: anyNamed('target'), target: anyNamed('target'),
...@@ -938,13 +946,14 @@ void main() { ...@@ -938,13 +946,14 @@ void main() {
}, },
), ),
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await onAppStart.future; await futureAppStart.future;
final OperationResult result = await residentRunner.restart(fullRestart: false); final OperationResult result = await residentRunner.restart(fullRestart: false);
expect(result.fatal, false); expect(result.fatal, false);
...@@ -1043,14 +1052,15 @@ void main() { ...@@ -1043,14 +1052,15 @@ void main() {
); );
}); });
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await onAppStart.future; await futureAppStart.future;
final OperationResult result = await residentRunner.restart(fullRestart: false); final OperationResult result = await residentRunner.restart(fullRestart: false);
expect(result.fatal, false); expect(result.fatal, false);
...@@ -1112,11 +1122,12 @@ void main() { ...@@ -1112,11 +1122,12 @@ void main() {
) )
) )
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
final OperationResult result = await residentRunner.restart(fullRestart: true); final OperationResult result = await residentRunner.restart(fullRestart: true);
...@@ -1189,11 +1200,12 @@ void main() { ...@@ -1189,11 +1200,12 @@ void main() {
) )
) )
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
final OperationResult result = await residentRunner.restart(fullRestart: true); final OperationResult result = await residentRunner.restart(fullRestart: true);
...@@ -1312,11 +1324,12 @@ void main() { ...@@ -1312,11 +1324,12 @@ void main() {
), ),
) )
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await residentRunner.restart(fullRestart: true); await residentRunner.restart(fullRestart: true);
...@@ -1334,13 +1347,14 @@ void main() { ...@@ -1334,13 +1347,14 @@ void main() {
listViews, listViews,
setAssetBundlePath, setAssetBundlePath,
]); ]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync(); final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync(); final Completer<void> futureAppStart = Completer<void>.sync();
unawaited(residentRunner.attach( unawaited(residentRunner.attach(
appStartedCompleter: onAppStart, appStartedCompleter: futureAppStart,
connectionInfoCompleter: onConnectionInfo, connectionInfoCompleter: futureConnectionInfo,
enableDevTools: true,
)); ));
await onAppStart.future; await futureAppStart.future;
when(mockFlutterDevice.updateDevFS( when(mockFlutterDevice.updateDevFS(
mainUri: anyNamed('mainUri'), mainUri: anyNamed('mainUri'),
target: anyNamed('target'), target: anyNamed('target'),
...@@ -1479,9 +1493,11 @@ void main() { ...@@ -1479,9 +1493,11 @@ void main() {
commandHelp.v, commandHelp.v,
commandHelp.P, commandHelp.P,
commandHelp.a, commandHelp.a,
'',
'💪 Running with sound null safety 💪',
'',
'An Observatory debugger and profiler on FakeDevice is available at: null', 'An Observatory debugger and profiler on FakeDevice is available at: null',
'\n💪 Running with sound null safety 💪', '',
''
].join('\n') ].join('\n')
)); ));
})); }));
...@@ -1577,7 +1593,7 @@ void main() { ...@@ -1577,7 +1593,7 @@ void main() {
listViews, listViews,
setAssetBundlePath, setAssetBundlePath,
]); ]);
final Future<int> result = residentRunner.attach(); final Future<int> result = residentRunner.attach(enableDevTools: true);
expect(await result, 0); expect(await result, 0);
// Verify DevTools was served. // Verify DevTools was served.
...@@ -1611,7 +1627,7 @@ void main() { ...@@ -1611,7 +1627,7 @@ void main() {
return 0; return 0;
}); });
final Future<int> result = residentRunner.attach(); final Future<int> result = residentRunner.attach(enableDevTools: true);
expect(await result, 0); expect(await result, 0);
// Verify DevTools was served. // Verify DevTools was served.
...@@ -1624,6 +1640,51 @@ void main() { ...@@ -1624,6 +1640,51 @@ void main() {
DevtoolsLauncher: () => mockDevtoolsLauncher, DevtoolsLauncher: () => mockDevtoolsLauncher,
}); });
testUsingContext('ResidentRunner ignores DevToolsLauncher when attaching with enableDevTools: false', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
listViews,
listViews,
setAssetBundlePath,
]);
final Future<int> result = residentRunner.attach(enableDevTools: false);
expect(await result, 0);
// Verify DevTools was served.
verifyNever(mockDevtoolsLauncher.serve());
}), overrides: <Type, Generator>{
DevtoolsLauncher: () => mockDevtoolsLauncher,
});
testUsingContext('ResidentRunner ignores DevtoolsLauncher when attaching with enableDevTools: false - cold mode', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
listViews,
listViews,
setAssetBundlePath,
]);
residentRunner = ColdRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile, vmserviceOutFile: 'foo'),
target: 'main.dart',
);
when(mockFlutterDevice.runCold(
coldRunner: anyNamed('coldRunner'),
route: anyNamed('route'),
)).thenAnswer((Invocation invocation) async {
return 0;
});
final Future<int> result = residentRunner.attach(enableDevTools: false);
expect(await result, 0);
// Verify DevTools was served.
verifyNever(mockDevtoolsLauncher.serve());
}), overrides: <Type, Generator>{
DevtoolsLauncher: () => mockDevtoolsLauncher,
});
testUsingContext('ResidentRunner invokes DevtoolsLauncher when running and shutting down - cold mode', () => testbed.run(() async { testUsingContext('ResidentRunner invokes DevtoolsLauncher when running and shutting down - cold mode', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
listViews, listViews,
...@@ -1645,7 +1706,7 @@ void main() { ...@@ -1645,7 +1706,7 @@ void main() {
return 0; return 0;
}); });
final Future<int> result = residentRunner.run(); final Future<int> result = residentRunner.run(enableDevTools: true);
expect(await result, 0); expect(await result, 0);
// Verify DevTools was served. // Verify DevTools was served.
...@@ -2277,7 +2338,7 @@ void main() { ...@@ -2277,7 +2338,7 @@ void main() {
)).thenAnswer((Invocation invocation) async { )).thenAnswer((Invocation invocation) async {
return 0; return 0;
}); });
await residentRunner.run(); await residentRunner.run(enableDevTools: true);
expect(await globals.fs.file('foo').readAsString(), testUri.toString()); expect(await globals.fs.file('foo').readAsString(), testUri.toString());
}), overrides: <Type, Generator>{ }), overrides: <Type, Generator>{
...@@ -2319,7 +2380,7 @@ void main() { ...@@ -2319,7 +2380,7 @@ void main() {
)).thenAnswer((Invocation invocation) async { )).thenAnswer((Invocation invocation) async {
return 0; return 0;
}); });
await residentRunner.run(); await residentRunner.run(enableDevTools: true);
expect(await globals.fs.file(globals.fs.path.join('build', 'cache.dill')).readAsString(), 'ABC'); expect(await globals.fs.file(globals.fs.path.join('build', 'cache.dill')).readAsString(), 'ABC');
}), overrides: <Type, Generator>{ }), overrides: <Type, Generator>{
...@@ -2368,7 +2429,7 @@ void main() { ...@@ -2368,7 +2429,7 @@ void main() {
)).thenAnswer((Invocation invocation) async { )).thenAnswer((Invocation invocation) async {
return 0; return 0;
}); });
await residentRunner.run(); await residentRunner.run(enableDevTools: true);
expect(await globals.fs.file(globals.fs.path.join( expect(await globals.fs.file(globals.fs.path.join(
'build', '187ef4436122d1cc2f40dc2b92f0eba0.cache.dill')).readAsString(), 'ABC'); 'build', '187ef4436122d1cc2f40dc2b92f0eba0.cache.dill')).readAsString(), 'ABC');
...@@ -2418,7 +2479,7 @@ void main() { ...@@ -2418,7 +2479,7 @@ void main() {
)).thenAnswer((Invocation invocation) async { )).thenAnswer((Invocation invocation) async {
return 0; return 0;
}); });
await residentRunner.run(); await residentRunner.run(enableDevTools: true);
expect(await globals.fs.file(globals.fs.path.join( expect(await globals.fs.file(globals.fs.path.join(
'build', '3416d3007730479552122f01c01e326d.cache.dill')).readAsString(), 'ABC'); 'build', '3416d3007730479552122f01c01e326d.cache.dill')).readAsString(), 'ABC');
...@@ -2462,7 +2523,7 @@ void main() { ...@@ -2462,7 +2523,7 @@ void main() {
)).thenAnswer((Invocation invocation) async { )).thenAnswer((Invocation invocation) async {
return 0; return 0;
}); });
await residentRunner.run(); await residentRunner.run(enableDevTools: true);
expect(globals.fs.file(globals.fs.path.join('build', 'cache.dill')), isNot(exists)); expect(globals.fs.file(globals.fs.path.join('build', 'cache.dill')), isNot(exists));
}), overrides: <Type, Generator>{ }), overrides: <Type, Generator>{
...@@ -2509,7 +2570,7 @@ void main() { ...@@ -2509,7 +2570,7 @@ void main() {
)).thenAnswer((Invocation invocation) async { )).thenAnswer((Invocation invocation) async {
return 0; return 0;
}); });
await residentRunner.run(); await residentRunner.run(enableDevTools: true);
expect(await globals.fs.file(globals.fs.path.join('build', 'cache.dill.track.dill')).readAsString(), 'ABC'); expect(await globals.fs.file(globals.fs.path.join('build', 'cache.dill.track.dill')).readAsString(), 'ABC');
}), overrides: <Type, Generator>{ }), overrides: <Type, Generator>{
...@@ -2565,7 +2626,7 @@ void main() { ...@@ -2565,7 +2626,7 @@ void main() {
)).thenAnswer((Invocation invocation) async { )).thenAnswer((Invocation invocation) async {
return 0; return 0;
}); });
await residentRunner.run(); await residentRunner.run(enableDevTools: true);
expect(testLogger.errorText, contains('Failed to write vmservice-out-file at foo')); expect(testLogger.errorText, contains('Failed to write vmservice-out-file at foo'));
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
...@@ -2608,9 +2669,10 @@ void main() { ...@@ -2608,9 +2669,10 @@ void main() {
)).thenAnswer((Invocation invocation) async { )).thenAnswer((Invocation invocation) async {
return 0; return 0;
}); });
await residentRunner.run(); await residentRunner.run(enableDevTools: true);
// Await a short delay so that we don't try to exit before all the expected // Await a short delay so that we don't try to exit before all the expected
// VM service requests have been fired. // VM service requests have been fired.
// TODO(ianh): remove this delay, it's almost certainly going to cause flakes
await Future<void>.delayed(const Duration(milliseconds: 200)); await Future<void>.delayed(const Duration(milliseconds: 200));
expect(await globals.fs.file('foo').readAsString(), testUri.toString()); expect(await globals.fs.file('foo').readAsString(), testUri.toString());
......
...@@ -289,7 +289,7 @@ void main() { ...@@ -289,7 +289,7 @@ void main() {
}); });
testUsingContext('devToolsServerAddress returns parsed uri', () async { testUsingContext('devToolsServerAddress returns parsed uri', () async {
final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions(); final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions(verboseHelp: false);
await createTestCommandRunner(command).run(<String>[ await createTestCommandRunner(command).run(<String>[
'dummy', 'dummy',
'--${FlutterCommand.kDevToolsServerAddress}', '--${FlutterCommand.kDevToolsServerAddress}',
...@@ -299,7 +299,7 @@ void main() { ...@@ -299,7 +299,7 @@ void main() {
}); });
testUsingContext('devToolsServerAddress returns null for bad input', () async { testUsingContext('devToolsServerAddress returns null for bad input', () async {
final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions(); final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions(verboseHelp: false);
final CommandRunner<void> runner = createTestCommandRunner(command); final CommandRunner<void> runner = createTestCommandRunner(command);
await runner.run(<String>[ await runner.run(<String>[
'dummy', 'dummy',
......
...@@ -341,6 +341,7 @@ class TestRunner extends Mock implements ResidentRunner { ...@@ -341,6 +341,7 @@ class TestRunner extends Mock implements ResidentRunner {
Future<int> run({ Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool enableDevTools = false,
String route, String route,
}) async => null; }) async => null;
...@@ -349,5 +350,6 @@ class TestRunner extends Mock implements ResidentRunner { ...@@ -349,5 +350,6 @@ class TestRunner extends Mock implements ResidentRunner {
Completer<DebugConnectionInfo> connectionInfoCompleter, Completer<DebugConnectionInfo> connectionInfoCompleter,
Completer<void> appStartedCompleter, Completer<void> appStartedCompleter,
bool allowExistingDdsInstance = false, bool allowExistingDdsInstance = false,
bool enableDevTools = false,
}) async => null; }) async => null;
} }
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