Unverified Commit 912c3ab1 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Revert "Revert "[flutter_tools] Serve DevTools at app start (#73366)" (#73896)" (#73903)

This reverts commit 388dcd24.
parent ffbbf93c
......@@ -156,6 +156,8 @@ Future<void> main(List<String> args) async {
processManager: globals.processManager,
pubExecutable: globals.artifacts.getArtifactPath(Artifact.pubExecutable),
logger: globals.logger,
platform: globals.platform,
persistentToolState: globals.persistentToolState,
),
Logger: () {
final LoggerFactory loggerFactory = LoggerFactory(
......
......@@ -101,6 +101,7 @@ class AttachCommand extends FlutterCommand {
);
usesTrackWidgetCreation(verboseHelp: verboseHelp);
addDdsOptions(verboseHelp: verboseHelp);
addDevToolsOptions();
usesDeviceTimeoutOption();
hotRunnerFactory ??= HotRunnerFactory();
}
......@@ -405,7 +406,11 @@ known, it can be explicitly provided to attach via the command-line, e.g.
);
flutterDevice.observatoryUris = observatoryUris;
final List<FlutterDevice> flutterDevices = <FlutterDevice>[flutterDevice];
final DebuggingOptions debuggingOptions = DebuggingOptions.enabled(buildInfo, disableDds: boolArg('disable-dds'));
final DebuggingOptions debuggingOptions = DebuggingOptions.enabled(
buildInfo,
disableDds: boolArg('disable-dds'),
devToolsServerAddress: devToolsServerAddress,
);
return buildInfo.isDebug
? hotRunnerFactory.build(
......
......@@ -871,8 +871,7 @@ class DevToolsDomain extends Domain {
Future<Map<String, dynamic>> serve([ Map<String, dynamic> args ]) async {
_devtoolsLauncher ??= DevtoolsLauncher.instance;
final bool openInBrowser = args != null && (args['openInBrowser'] == 'true');
final DevToolsServerAddress server = await _devtoolsLauncher.serve(openInBrowser: openInBrowser);
final DevToolsServerAddress server = await _devtoolsLauncher.serve();
return<String, dynamic>{
'host': server?.host,
'port': server?.port,
......
......@@ -134,6 +134,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
usesDeviceUserOption();
usesDeviceTimeoutOption();
addDdsOptions(verboseHelp: verboseHelp);
addDevToolsOptions();
addAndroidSpecificBuildOptions(hide: !verboseHelp);
}
......@@ -195,6 +196,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
hostVmServicePort: hostVmservicePort,
disablePortPublication: disablePortPublication,
ddsPort: ddsPort,
devToolsServerAddress: devToolsServerAddress,
verboseSystemLogs: boolArg('verbose-system-logs'),
hostname: featureFlags.isWebEnabled ? stringArg('web-hostname') : '',
port: featureFlags.isWebEnabled ? stringArg('web-port') : '',
......
......@@ -184,6 +184,8 @@ Future<T> runInContext<T>(
processManager: globals.processManager,
pubExecutable: globals.artifacts.getArtifactPath(Artifact.pubExecutable),
logger: globals.logger,
platform: globals.platform,
persistentToolState: globals.persistentToolState,
),
Doctor: () => Doctor(logger: globals.logger),
DoctorValidatorsProvider: () => DoctorValidatorsProvider.defaultInstance,
......
......@@ -852,6 +852,7 @@ class DebuggingOptions {
this.disablePortPublication = false,
this.deviceVmServicePort,
this.ddsPort,
this.devToolsServerAddress,
this.hostname,
this.port,
this.webEnableExposeUrl,
......@@ -895,6 +896,7 @@ class DebuggingOptions {
disablePortPublication = false,
deviceVmServicePort = null,
ddsPort = null,
devToolsServerAddress = null,
vmserviceOutFile = null,
fastStart = false,
webEnableExpressionEvaluation = false,
......@@ -924,6 +926,7 @@ class DebuggingOptions {
final int deviceVmServicePort;
final bool disablePortPublication;
final int ddsPort;
final Uri devToolsServerAddress;
final String port;
final String hostname;
final bool webEnableExposeUrl;
......
......@@ -4,13 +4,15 @@
import 'dart:async';
import 'package:browser_launcher/browser_launcher.dart';
import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
import 'package:process/process.dart';
import 'base/io.dart' as io;
import 'base/logger.dart';
import 'base/platform.dart';
import 'convert.dart';
import 'persistent_tool_state.dart';
import 'resident_runner.dart';
/// An implementation of the devtools launcher that uses the server package.
......@@ -19,59 +21,68 @@ import 'resident_runner.dart';
/// a devtools dep in google3.
class DevtoolsServerLauncher extends DevtoolsLauncher {
DevtoolsServerLauncher({
@required Platform platform,
@required ProcessManager processManager,
@required String pubExecutable,
@required Logger logger,
@required PersistentToolState persistentToolState,
}) : _processManager = processManager,
_pubExecutable = pubExecutable,
_logger = logger;
_logger = logger,
_platform = platform,
_persistentToolState = persistentToolState;
final ProcessManager _processManager;
final String _pubExecutable;
final Logger _logger;
final Platform _platform;
final PersistentToolState _persistentToolState;
io.Process _devToolsProcess;
Uri _devToolsUri;
static final RegExp _serveDevToolsPattern =
RegExp(r'Serving DevTools at ((http|//)[a-zA-Z0-9:/=_\-\.\[\]]+)');
@override
Future<void> launch(Uri vmServiceUri, {bool openInBrowser = false}) async {
if (_devToolsProcess != null && _devToolsUri != null) {
// DevTools is already running.
if (openInBrowser) {
await Chrome.start(<String>[_devToolsUri.toString()]);
Future<void> launch(Uri vmServiceUri) async {
// 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
// failure.
try {
bool offline = false;
try {
const String pubHostedUrlKey = 'PUB_HOSTED_URL';
if (_platform.environment.containsKey(pubHostedUrlKey)) {
await http.head(_platform.environment[pubHostedUrlKey]);
} else {
await http.head('https://pub.dev');
}
} on Exception {
offline = true;
}
return;
}
final Status status = _logger.startProgress(
'Activating Dart DevTools...',
);
try {
// TODO(kenz): https://github.com/dart-lang/pub/issues/2791 - calling `pub
// global activate` adds ~ 4.5 seconds of latency.
final io.ProcessResult _devToolsActivateProcess = await _processManager.run(<String>[
_pubExecutable,
'global',
'activate',
'devtools'
]);
if (_devToolsActivateProcess.exitCode != 0) {
status.cancel();
_logger.printError('Error running `pub global activate '
'devtools`:\n${_devToolsActivateProcess.stderr}');
if (offline) {
// TODO(kenz): we should launch an already activated version of DevTools
// here, if available, once DevTools has offline support. DevTools does
// not work without internet currently due to the failed request of a
// couple scripts. See https://github.com/flutter/devtools/issues/2420.
return;
} else {
final bool didActivateDevTools = await _activateDevTools();
final bool devToolsActive = await _checkForActiveDevTools();
if (!didActivateDevTools && !devToolsActive) {
// At this point, we failed to activate the DevTools package and the
// package is not already active.
return;
}
}
status.stop();
_devToolsProcess = await _processManager.start(<String>[
_pubExecutable,
'global',
'run',
'devtools',
if (!openInBrowser) '--no-launch-browser',
'--no-launch-browser',
if (vmServiceUri != null) '--vm-uri=$vmServiceUri',
]);
final Completer<Uri> completer = Completer<Uri>();
......@@ -91,30 +102,83 @@ class DevtoolsServerLauncher extends DevtoolsLauncher {
}
completer.complete(Uri.parse(uri));
}
_logger.printStatus(line);
});
_devToolsProcess.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen(_logger.printError);
_devToolsUri = await completer.future;
devToolsUri = await completer.future
.timeout(const Duration(seconds: 10));
} on Exception catch (e, st) {
status.cancel();
_logger.printError('Failed to launch DevTools: $e', stackTrace: st);
}
}
@override
Future<DevToolsServerAddress> serve({bool openInBrowser = false}) async {
await launch(null, openInBrowser: openInBrowser);
if (_devToolsUri == null) {
return null;
Future<bool> _checkForActiveDevTools() async {
// We are offline, and cannot activate DevTools, so check if the DevTools
// package is already active.
final io.ProcessResult _pubGlobalListProcess = await _processManager.run(<String>[
_pubExecutable,
'global',
'list',
]);
if (_pubGlobalListProcess.stdout.toString().contains('devtools ')) {
return true;
}
return false;
}
/// Helper method to activate the DevTools pub package.
///
/// Returns a bool indicating whether or not the package was successfully
/// activated from pub.
Future<bool> _activateDevTools() async {
final DateTime now = DateTime.now();
// Only attempt to activate DevTools twice a day.
final bool shouldActivate =
_persistentToolState.lastDevToolsActivationTime == null ||
now.difference(_persistentToolState.lastDevToolsActivationTime).inHours >= 12;
if (!shouldActivate) {
return false;
}
return DevToolsServerAddress(_devToolsUri.host, _devToolsUri.port);
final Status status = _logger.startProgress(
'Activating Dart DevTools...',
);
try {
final io.ProcessResult _devToolsActivateProcess = await _processManager
.run(<String>[
_pubExecutable,
'global',
'activate',
'devtools'
]);
if (_devToolsActivateProcess.exitCode != 0) {
status.cancel();
_logger.printError('Error running `pub global activate '
'devtools`:\n${_devToolsActivateProcess.stderr}');
return false;
}
status.stop();
_persistentToolState.lastDevToolsActivationTime = DateTime.now();
return true;
} on Exception catch (e, _) {
status.stop();
_logger.printError('Error running `pub global activate devtools`: $e');
return false;
}
}
@override
Future<DevToolsServerAddress> serve() async {
await launch(null);
return activeDevToolsServer;
}
@override
Future<void> close() async {
devToolsUri = null;
if (_devToolsProcess != null) {
_devToolsProcess.kill();
await _devToolsProcess.exitCode;
......
......@@ -52,6 +52,9 @@ abstract class PersistentToolState {
/// Whether this client was already determined to be or not be a bot.
bool isRunningOnBot;
/// The last time the the DevTools package was activated from pub.
DateTime lastDevToolsActivationTime;
}
class _DefaultPersistentToolState implements PersistentToolState {
......@@ -85,6 +88,7 @@ class _DefaultPersistentToolState implements PersistentToolState {
Channel.stable: 'last-active-stable-version'
};
static const String _kBotKey = 'is-bot';
static const String _kLastDevToolsActivationTimeKey = 'last-devtools-activation-time';
static const String _kLicenseHash = 'license-hash';
final Config _config;
......@@ -131,4 +135,14 @@ class _DefaultPersistentToolState implements PersistentToolState {
@override
set isRunningOnBot(bool value) => _config.setValue(_kBotKey, value);
@override
DateTime get lastDevToolsActivationTime {
final String value = _config.getValue(_kLastDevToolsActivationTimeKey) as String;
return value != null ? DateTime.parse(value) : null;
}
@override
set lastDevToolsActivationTime(DateTime time) =>
_config.setValue(_kLastDevToolsActivationTimeKey, time.toString());
}
......@@ -774,7 +774,7 @@ abstract class ResidentRunner {
final CommandHelp commandHelp;
final bool machine;
DevtoolsLauncher _devtoolsLauncher;
DevtoolsLauncher _devToolsLauncher;
bool _exited = false;
Completer<int> _finished = Completer<int>();
......@@ -926,7 +926,7 @@ abstract class ResidentRunner {
}
@protected
void writeVmserviceFile() {
void writeVmServiceFile() {
if (debuggingOptions.vmserviceOutFile != null) {
try {
final String address = flutterDevices.first.vmService.wsAddress.toString();
......@@ -941,7 +941,7 @@ abstract class ResidentRunner {
Future<void> exit() async {
_exited = true;
await shutdownDevtools();
await shutdownDevTools();
await stopEchoingDeviceLog();
await preExit();
await exitApp();
......@@ -949,7 +949,7 @@ abstract class ResidentRunner {
}
Future<void> detach() async {
await shutdownDevtools();
await shutdownDevTools();
await stopEchoingDeviceLog();
await preExit();
await shutdownDartDevelopmentService();
......@@ -1250,22 +1250,29 @@ abstract class ResidentRunner {
}
}
Future<bool> launchDevTools({bool openInBrowser = false}) async {
DevToolsServerAddress activeDevToolsServer() {
_devToolsLauncher ??= DevtoolsLauncher.instance;
return _devToolsLauncher.activeDevToolsServer;
}
Future<void> serveDevToolsGracefully({
Uri devToolsServerAddress
}) async {
if (!supportsServiceProtocol) {
return false;
return;
}
_devToolsLauncher ??= DevtoolsLauncher.instance;
if (devToolsServerAddress != null) {
_devToolsLauncher.devToolsUri = devToolsServerAddress;
} else {
await _devToolsLauncher.serve();
}
assert(supportsServiceProtocol);
_devtoolsLauncher ??= DevtoolsLauncher.instance;
unawaited(_devtoolsLauncher.launch(
flutterDevices.first.vmService.httpAddress,
openInBrowser: openInBrowser,
));
return true;
}
Future<void> shutdownDevtools() async {
await _devtoolsLauncher?.close();
_devtoolsLauncher = null;
Future<void> shutdownDevTools() async {
await _devToolsLauncher?.close();
_devToolsLauncher = null;
}
Future<void> _serviceProtocolDone(dynamic object) async {
......@@ -1551,8 +1558,6 @@ class TerminalHandler {
return residentRunner.debugDumpRenderTree();
case 'U':
return residentRunner.debugDumpSemanticsTreeInInverseHitTestOrder();
case 'v':
return residentRunner.launchDevTools(openInBrowser: true);
case 'w':
case 'W':
return residentRunner.debugDumpApp();
......@@ -1645,16 +1650,25 @@ String nextPlatform(String currentPlatform, FeatureFlags featureFlags) {
/// A launcher for the devtools debugger and analysis tool.
abstract class DevtoolsLauncher {
Uri devToolsUri;
/// Launch a Dart DevTools process, optionally targeting a specific VM Service
/// URI if [vmServiceUri] is non-null.
Future<void> launch(Uri vmServiceUri, {bool openInBrowser = false});
Future<void> launch(Uri vmServiceUri);
/// Serve Dart DevTools and return the host and port they are available on.
Future<DevToolsServerAddress> serve({bool openInBrowser = false});
Future<DevToolsServerAddress> serve();
Future<void> close();
static DevtoolsLauncher get instance => context.get<DevtoolsLauncher>();
DevToolsServerAddress get activeDevToolsServer {
if (devToolsUri == null) {
return null;
}
return DevToolsServerAddress(devToolsUri.host, devToolsUri.port);
}
}
class DevToolsServerAddress {
......@@ -1662,4 +1676,11 @@ class DevToolsServerAddress {
final String host;
final int port;
Uri get uri {
if (host == null || port == null) {
return null;
}
return Uri(scheme: 'http', host: host, port: port);
}
}
......@@ -72,7 +72,12 @@ class ColdRunner extends ResidentRunner {
// Connect to observatory.
if (debuggingOptions.debuggingEnabled) {
try {
await connectToServiceProtocol();
await Future.wait(<Future<void>>[
connectToServiceProtocol(),
serveDevToolsGracefully(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
),
]);
} on String catch (message) {
globals.printError(message);
appFailedToStart();
......@@ -115,7 +120,7 @@ class ColdRunner extends ResidentRunner {
appStartedCompleter?.complete();
writeVmserviceFile();
writeVmServiceFile();
if (stayResident && !traceStartup) {
return waitForAppToFinish();
......@@ -132,10 +137,15 @@ class ColdRunner extends ResidentRunner {
}) async {
_didAttach = true;
try {
await connectToServiceProtocol(
getSkSLMethod: writeSkSL,
allowExistingDdsInstance: allowExistingDdsInstance,
);
await Future.wait(<Future<void>>[
connectToServiceProtocol(
getSkSLMethod: writeSkSL,
allowExistingDdsInstance: allowExistingDdsInstance,
),
serveDevToolsGracefully(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
),
]);
} on Exception catch (error) {
globals.printError('Error connecting to the service protocol: $error');
return 2;
......@@ -195,6 +205,19 @@ class ColdRunner extends ResidentRunner {
'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',
);
}
}
}
}
}
......
......@@ -174,13 +174,18 @@ class HotRunner extends ResidentRunner {
}) async {
_didAttach = true;
try {
await connectToServiceProtocol(
reloadSources: _reloadSourcesService,
restart: _restartService,
compileExpression: _compileExpressionService,
getSkSLMethod: writeSkSL,
allowExistingDdsInstance: allowExistingDdsInstance,
);
await Future.wait(<Future<void>>[
connectToServiceProtocol(
reloadSources: _reloadSourcesService,
restart: _restartService,
compileExpression: _compileExpressionService,
getSkSLMethod: writeSkSL,
allowExistingDdsInstance: allowExistingDdsInstance,
),
serveDevToolsGracefully(
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
),
]);
// Catches all exceptions, non-Exception objects are rethrown.
} catch (error) { // ignore: avoid_catches_without_on_clauses
if (error is! Exception && error is! String) {
......@@ -280,7 +285,7 @@ class HotRunner extends ResidentRunner {
benchmarkOutput.writeAsStringSync(toPrettyJson(benchmarkData));
return 0;
}
writeVmserviceFile();
writeVmServiceFile();
int result = 0;
if (stayResident) {
......@@ -1087,6 +1092,19 @@ class HotRunner extends ResidentRunner {
'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('');
if (debuggingOptions.buildInfo.nullSafetyMode == NullSafetyMode.sound) {
......
......@@ -128,6 +128,9 @@ abstract class FlutterCommand extends Command<void> {
/// The option name for a custom observatory port.
static const String observatoryPortOption = 'observatory-port';
/// The option name for a custom DevTools server address.
static const String kDevToolsServerAddress = 'devtools-server-address';
/// The flag name for whether or not to use ipv6.
static const String ipv6Flag = 'ipv6';
......@@ -322,6 +325,13 @@ abstract class FlutterCommand extends Command<void> {
_usesPortOption = true;
}
void addDevToolsOptions() {
argParser.addOption(kDevToolsServerAddress,
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 '
'at this address.');
}
void addDdsOptions({@required bool verboseHelp}) {
argParser.addOption('dds-port',
help: 'When this value is provided, the Dart Development Service (DDS) will be '
......@@ -365,6 +375,16 @@ abstract class FlutterCommand extends Command<void> {
return 0;
}
Uri get devToolsServerAddress {
if (argResults.wasParsed(kDevToolsServerAddress)) {
final Uri uri = Uri.tryParse(stringArg(kDevToolsServerAddress));
if (uri != null && uri.host.isNotEmpty && uri.port != 0) {
return uri;
}
}
return null;
}
/// Gets the vmservice port provided to in the 'observatory-port' or
/// 'host-vmservice-port option.
///
......
......@@ -4,20 +4,41 @@
import 'dart:async';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/devtools_launcher.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/persistent_tool_state.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import '../src/common.dart';
import '../src/context.dart';
void main() {
BufferLogger logger;
FakePlatform platform;
PersistentToolState persistentToolState;
setUp(() {
logger = BufferLogger.test();
platform = FakePlatform(environment: <String, String>{});
final Directory tempDir = globals.fs.systemTempDirectory.createTempSync('devtools_launcher_test');
persistentToolState = PersistentToolState.test(
directory: tempDir,
logger: logger,
);
});
testWithoutContext('DevtoolsLauncher launches DevTools through pub and saves the URI', () async {
final Completer<void> completer = Completer<void>();
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
pubExecutable: 'pub',
logger: BufferLogger.test(),
logger: logger,
platform: platform,
persistentToolState: persistentToolState,
processManager: FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
......@@ -28,6 +49,14 @@ void main() {
],
stdout: 'Activated DevTools 0.9.5',
),
const FakeCommand(
command: <String>[
'pub',
'global',
'list',
],
stdout: 'devtools 0.9.6',
),
FakeCommand(
command: const <String>[
'pub',
......@@ -51,7 +80,9 @@ void main() {
final Completer<void> completer = Completer<void>();
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
pubExecutable: 'pub',
logger: BufferLogger.test(),
logger: logger,
platform: platform,
persistentToolState: persistentToolState,
processManager: FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
......@@ -62,12 +93,21 @@ void main() {
],
stdout: 'Activated DevTools 0.9.5',
),
const FakeCommand(
command: <String>[
'pub',
'global',
'list',
],
stdout: 'devtools 0.9.6',
),
FakeCommand(
command: const <String>[
'pub',
'global',
'run',
'devtools',
'--no-launch-browser',
],
stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
completer: completer,
......@@ -75,16 +115,49 @@ void main() {
]),
);
final DevToolsServerAddress address = await launcher.serve(openInBrowser: true);
final DevToolsServerAddress address = await launcher.serve();
expect(address.host, '127.0.0.1');
expect(address.port, 9100);
});
testWithoutContext('DevtoolsLauncher does not activate DevTools if it was recently activated', () async {
persistentToolState.lastDevToolsActivationTime = DateTime.now();
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
pubExecutable: 'pub',
logger: logger,
platform: platform,
persistentToolState: persistentToolState,
processManager: FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'pub',
'global',
'list',
],
stdout: 'devtools 0.9.6',
),
const FakeCommand(
command: <String>[
'pub',
'global',
'run',
'devtools',
'--no-launch-browser',
],
stdout: 'Serving DevTools at http://127.0.0.1:9100\n',
),
]),
);
await launcher.serve();
});
testWithoutContext('DevtoolsLauncher prints error if exception is thrown during activate', () async {
final BufferLogger logger = BufferLogger.test();
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
pubExecutable: 'pub',
logger: logger,
platform: platform,
persistentToolState: persistentToolState,
processManager: FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
......@@ -96,6 +169,14 @@ void main() {
stderr: 'Error - could not activate devtools',
exitCode: 1,
),
const FakeCommand(
command: <String>[
'pub',
'global',
'list',
],
stdout: 'devtools 0.9.6',
),
FakeCommand(
command: const <String>[
'pub',
......@@ -118,10 +199,11 @@ void main() {
});
testWithoutContext('DevtoolsLauncher prints error if exception is thrown during launch', () async {
final BufferLogger logger = BufferLogger.test();
final DevtoolsLauncher launcher = DevtoolsServerLauncher(
pubExecutable: 'pub',
logger: logger,
platform: platform,
persistentToolState: persistentToolState,
processManager: FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
......@@ -132,6 +214,14 @@ void main() {
],
stdout: 'Activated DevTools 0.9.5',
),
const FakeCommand(
command: <String>[
'pub',
'global',
'list',
],
stdout: 'devtools 0.9.6',
),
FakeCommand(
command: const <String>[
'pub',
......
......@@ -57,4 +57,23 @@ void main() {
expect(state2.lastActiveVersion(Channel.beta), 'ghi');
expect(state2.lastActiveVersion(Channel.stable), 'jkl');
});
testWithoutContext('lastDevToolsActivationTime can be cached and stored', () {
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final Directory directory = fileSystem.directory('state_dir')..createSync();
final PersistentToolState state1 = PersistentToolState.test(
directory: directory,
logger: BufferLogger.test(),
);
final DateTime time = DateTime.now();
state1.lastDevToolsActivationTime = time;
final PersistentToolState state2 = PersistentToolState.test(
directory: directory,
logger: BufferLogger.test(),
);
expect(state2.lastDevToolsActivationTime, equals(time));
});
}
......@@ -290,6 +290,48 @@ void main() {
expect(FlutterCommandResult.warning().exitStatus, ExitStatus.warning);
});
testUsingContext('devToolsServerAddress returns parsed uri', () async {
final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions();
await createTestCommandRunner(command).run(<String>[
'dummy',
'--${FlutterCommand.kDevToolsServerAddress}',
'http://127.0.0.1:9105',
]);
expect(command.devToolsServerAddress.toString(), equals('http://127.0.0.1:9105'));
});
testUsingContext('devToolsServerAddress returns null for bad input', () async {
final DummyFlutterCommand command = DummyFlutterCommand()..addDevToolsOptions();
final CommandRunner<void> runner = createTestCommandRunner(command);
await runner.run(<String>[
'dummy',
'--${FlutterCommand.kDevToolsServerAddress}',
'hello-world',
]);
expect(command.devToolsServerAddress, isNull);
await runner.run(<String>[
'dummy',
'--${FlutterCommand.kDevToolsServerAddress}',
'',
]);
expect(command.devToolsServerAddress, isNull);
await runner.run(<String>[
'dummy',
'--${FlutterCommand.kDevToolsServerAddress}',
'9101',
]);
expect(command.devToolsServerAddress, isNull);
await runner.run(<String>[
'dummy',
'--${FlutterCommand.kDevToolsServerAddress}',
'127.0.0.1:9101',
]);
expect(command.devToolsServerAddress, isNull);
});
group('signals tests', () {
MockIoProcessSignal mockSignal;
ProcessSignal signalUnderTest;
......
......@@ -285,13 +285,6 @@ void main() {
verify(mockResidentRunner.debugDumpSemanticsTreeInInverseHitTestOrder()).called(1);
});
testWithoutContext('v - launchDevTools', () async {
when(mockResidentRunner.supportsServiceProtocol).thenReturn(true);
await terminalHandler.processTerminalInput('v');
verify(mockResidentRunner.launchDevTools(openInBrowser: true)).called(1);
});
testWithoutContext('w,W - debugDumpApp with service protocol', () async {
await terminalHandler.processTerminalInput('w');
await terminalHandler.processTerminalInput('W');
......
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