mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Remove race conditions involving finding available ports (#18698)
This is an attempt to reland #18488 with less breakage on macOS.
This commit is contained in:
parent
ed2376a7fc
commit
35ad2a786d
@ -35,12 +35,14 @@ void main() {
|
|||||||
.listen((String line) {
|
.listen((String line) {
|
||||||
print('run:stdout: $line');
|
print('run:stdout: $line');
|
||||||
stdout.add(line);
|
stdout.add(line);
|
||||||
if (lineContainsServicePort(line)) {
|
if (vmServicePort == null) {
|
||||||
vmServicePort = parseServicePort(line);
|
vmServicePort = parseServicePort(line);
|
||||||
print('service protocol connection available at port $vmServicePort');
|
if (vmServicePort != null) {
|
||||||
print('run: ready!');
|
print('service protocol connection available at port $vmServicePort');
|
||||||
ready.complete();
|
print('run: ready!');
|
||||||
ok ??= true;
|
ready.complete();
|
||||||
|
ok ??= true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
run.stderr
|
run.stderr
|
||||||
|
@ -41,12 +41,14 @@ void main() {
|
|||||||
.transform(const LineSplitter())
|
.transform(const LineSplitter())
|
||||||
.listen((String line) {
|
.listen((String line) {
|
||||||
print('run:stdout: $line');
|
print('run:stdout: $line');
|
||||||
if (lineContainsServicePort(line)) {
|
if (vmServicePort == null) {
|
||||||
vmServicePort = parseServicePort(line);
|
vmServicePort = parseServicePort(line);
|
||||||
print('service protocol connection available at port $vmServicePort');
|
if (vmServicePort != null) {
|
||||||
print('run: ready!');
|
print('service protocol connection available at port $vmServicePort');
|
||||||
ready.complete();
|
print('run: ready!');
|
||||||
ok ??= true;
|
ready.complete();
|
||||||
|
ok ??= true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
run.stderr
|
run.stderr
|
||||||
|
@ -33,12 +33,14 @@ void main() {
|
|||||||
.transform(const LineSplitter())
|
.transform(const LineSplitter())
|
||||||
.listen((String line) {
|
.listen((String line) {
|
||||||
print('run:stdout: $line');
|
print('run:stdout: $line');
|
||||||
if (lineContainsServicePort(line)) {
|
if (vmServicePort == null) {
|
||||||
vmServicePort = parseServicePort(line);
|
vmServicePort = parseServicePort(line);
|
||||||
print('service protocol connection available at port $vmServicePort');
|
if (vmServicePort != null) {
|
||||||
print('run: ready!');
|
print('service protocol connection available at port $vmServicePort');
|
||||||
ready.complete();
|
print('run: ready!');
|
||||||
ok ??= true;
|
ready.complete();
|
||||||
|
ok ??= true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
run.stderr
|
run.stderr
|
||||||
|
@ -28,9 +28,8 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a
|
|||||||
if (!file(taskExecutable).existsSync())
|
if (!file(taskExecutable).existsSync())
|
||||||
throw 'Executable Dart file not found: $taskExecutable';
|
throw 'Executable Dart file not found: $taskExecutable';
|
||||||
|
|
||||||
final int vmServicePort = await findAvailablePort();
|
|
||||||
final Process runner = await startProcess(dartBin, <String>[
|
final Process runner = await startProcess(dartBin, <String>[
|
||||||
'--enable-vm-service=$vmServicePort',
|
'--enable-vm-service=0', // zero causes the system to choose a free port
|
||||||
'--no-pause-isolates-on-exit',
|
'--no-pause-isolates-on-exit',
|
||||||
taskExecutable,
|
taskExecutable,
|
||||||
]);
|
]);
|
||||||
@ -41,10 +40,17 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a
|
|||||||
runnerFinished = true;
|
runnerFinished = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final Completer<int> port = new Completer<int>();
|
||||||
|
|
||||||
final StreamSubscription<String> stdoutSub = runner.stdout
|
final StreamSubscription<String> stdoutSub = runner.stdout
|
||||||
.transform(const Utf8Decoder())
|
.transform(const Utf8Decoder())
|
||||||
.transform(const LineSplitter())
|
.transform(const LineSplitter())
|
||||||
.listen((String line) {
|
.listen((String line) {
|
||||||
|
if (!port.isCompleted) {
|
||||||
|
final int portValue = parseServicePort(line, prefix: 'Observatory listening on ');
|
||||||
|
if (portValue != null)
|
||||||
|
port.complete(portValue);
|
||||||
|
}
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
stdout.writeln('[$taskName] [STDOUT] $line');
|
stdout.writeln('[$taskName] [STDOUT] $line');
|
||||||
}
|
}
|
||||||
@ -59,7 +65,7 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent = false }) a
|
|||||||
|
|
||||||
String waitingFor = 'connection';
|
String waitingFor = 'connection';
|
||||||
try {
|
try {
|
||||||
final VMIsolateRef isolate = await _connectToRunnerIsolate(vmServicePort);
|
final VMIsolateRef isolate = await _connectToRunnerIsolate(await port.future);
|
||||||
waitingFor = 'task completion';
|
waitingFor = 'task completion';
|
||||||
final Map<String, dynamic> taskResult =
|
final Map<String, dynamic> taskResult =
|
||||||
await isolate.invokeExtension('ext.cocoonRunTask').timeout(taskTimeoutWithGracePeriod);
|
await isolate.invokeExtension('ext.cocoonRunTask').timeout(taskTimeoutWithGracePeriod);
|
||||||
|
@ -494,21 +494,6 @@ Future<Null> runAndCaptureAsyncStacks(Future<Null> callback()) {
|
|||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return an unused TCP port number.
|
|
||||||
Future<int> findAvailablePort() async {
|
|
||||||
int port = 20000;
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
final ServerSocket socket =
|
|
||||||
await ServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, port); // ignore: deprecated_member_use
|
|
||||||
await socket.close();
|
|
||||||
return port;
|
|
||||||
} catch (_) {
|
|
||||||
port++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canRun(String path) => _processManager.canRun(path);
|
bool canRun(String path) => _processManager.canRun(path);
|
||||||
|
|
||||||
String extractCloudAuthTokenArg(List<String> rawArgs) {
|
String extractCloudAuthTokenArg(List<String> rawArgs) {
|
||||||
@ -531,13 +516,20 @@ String extractCloudAuthTokenArg(List<String> rawArgs) {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "An Observatory debugger and profiler on ... is available at: http://127.0.0.1:8100/"
|
/// Tries to extract a port from the string.
|
||||||
final RegExp _kObservatoryRegExp = new RegExp(r'An Observatory debugger .* is available at: (\S+:(\d+))');
|
///
|
||||||
|
/// The `prefix`, if specified, is a regular expression pattern and must not contain groups.
|
||||||
bool lineContainsServicePort(String line) => line.contains(_kObservatoryRegExp);
|
///
|
||||||
|
/// The `multiLine` flag should be set to true if `line` is actually a buffer of many lines.
|
||||||
int parseServicePort(String line) {
|
int parseServicePort(String line, {
|
||||||
final Match match = _kObservatoryRegExp.firstMatch(line);
|
String prefix = 'An Observatory debugger .* is available at: ',
|
||||||
|
bool multiLine = false,
|
||||||
|
}) {
|
||||||
|
// e.g. "An Observatory debugger and profiler on ... is available at: http://127.0.0.1:8100/"
|
||||||
|
final RegExp pattern = new RegExp('$prefix(\\S+:(\\d+)/\\S*)\$', multiLine: multiLine);
|
||||||
|
final Match match = pattern.firstMatch(line);
|
||||||
|
print(pattern);
|
||||||
|
print(match);
|
||||||
return match == null ? null : int.parse(match.group(2));
|
return match == null ? null : int.parse(match.group(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,8 +441,6 @@ class MemoryTest {
|
|||||||
if (deviceOperatingSystem == DeviceOperatingSystem.ios)
|
if (deviceOperatingSystem == DeviceOperatingSystem.ios)
|
||||||
await prepareProvisioningCertificates(testDirectory);
|
await prepareProvisioningCertificates(testDirectory);
|
||||||
|
|
||||||
final int observatoryPort = await findAvailablePort();
|
|
||||||
|
|
||||||
final List<String> runOptions = <String>[
|
final List<String> runOptions = <String>[
|
||||||
'-v',
|
'-v',
|
||||||
'--profile',
|
'--profile',
|
||||||
@ -450,11 +448,14 @@ class MemoryTest {
|
|||||||
'-d',
|
'-d',
|
||||||
deviceId,
|
deviceId,
|
||||||
'--observatory-port',
|
'--observatory-port',
|
||||||
observatoryPort.toString(),
|
'0',
|
||||||
];
|
];
|
||||||
if (testTarget != null)
|
if (testTarget != null)
|
||||||
runOptions.addAll(<String>['-t', testTarget]);
|
runOptions.addAll(<String>['-t', testTarget]);
|
||||||
await flutter('run', options: runOptions);
|
final String output = await evalFlutter('run', options: runOptions);
|
||||||
|
final int observatoryPort = parseServicePort(output, prefix: 'Successfully connected to service protocol: ', multiLine: true);
|
||||||
|
if (observatoryPort == null)
|
||||||
|
throw new Exception('Could not find observatory port in "flutter run" output.');
|
||||||
|
|
||||||
final Map<String, dynamic> startData = await device.getMemoryStats(packageName);
|
final Map<String, dynamic> startData = await device.getMemoryStats(packageName);
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import '../base/common.dart' show throwToolExit;
|
|||||||
import '../base/file_system.dart';
|
import '../base/file_system.dart';
|
||||||
import '../base/io.dart';
|
import '../base/io.dart';
|
||||||
import '../base/logger.dart';
|
import '../base/logger.dart';
|
||||||
import '../base/port_scanner.dart';
|
|
||||||
import '../base/process.dart';
|
import '../base/process.dart';
|
||||||
import '../base/process_manager.dart';
|
import '../base/process_manager.dart';
|
||||||
import '../base/utils.dart';
|
import '../base/utils.dart';
|
||||||
@ -844,7 +843,7 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
|
|||||||
final int devicePort = _extractPort(splitLine[2]);
|
final int devicePort = _extractPort(splitLine[2]);
|
||||||
|
|
||||||
// Failed, skip.
|
// Failed, skip.
|
||||||
if ((hostPort == null) || (devicePort == null))
|
if (hostPort == null || devicePort == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ports.add(new ForwardedPort(hostPort, devicePort));
|
ports.add(new ForwardedPort(hostPort, devicePort));
|
||||||
@ -855,16 +854,30 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> forward(int devicePort, { int hostPort }) async {
|
Future<int> forward(int devicePort, {int hostPort}) async {
|
||||||
if ((hostPort == null) || (hostPort == 0)) {
|
hostPort ??= 0;
|
||||||
// Auto select host port.
|
final RunResult process = await runCheckedAsync(device.adbCommandForDevice(
|
||||||
hostPort = await portScanner.findAvailablePort();
|
|
||||||
}
|
|
||||||
|
|
||||||
await runCheckedAsync(device.adbCommandForDevice(
|
|
||||||
<String>['forward', 'tcp:$hostPort', 'tcp:$devicePort']
|
<String>['forward', 'tcp:$hostPort', 'tcp:$devicePort']
|
||||||
));
|
));
|
||||||
|
|
||||||
|
if (process.stderr.isNotEmpty)
|
||||||
|
process.throwException('adb returned error:\n${process.stderr}');
|
||||||
|
|
||||||
|
if (process.exitCode != 0) {
|
||||||
|
if (process.stdout.isNotEmpty)
|
||||||
|
process.throwException('adb returned error:\n${process.stdout}');
|
||||||
|
process.throwException('adb failed without a message');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostPort == 0) {
|
||||||
|
if (process.stdout.isEmpty)
|
||||||
|
process.throwException('adb did not report forwarded port');
|
||||||
|
hostPort = int.tryParse(process.stdout) ?? (throw 'adb returned invalid port number:\n${process.stdout}');
|
||||||
|
} else {
|
||||||
|
if (process.stdout.isNotEmpty)
|
||||||
|
process.throwException('adb returned error:\n${process.stdout}');
|
||||||
|
}
|
||||||
|
|
||||||
return hostPort;
|
return hostPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,10 +169,10 @@ class AndroidWorkflow extends DoctorValidator implements Workflow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<LicensesAccepted> get licensesAccepted async {
|
Future<LicensesAccepted> get licensesAccepted async {
|
||||||
LicensesAccepted status = LicensesAccepted.unknown;
|
LicensesAccepted status;
|
||||||
|
|
||||||
void _onLine(String line) {
|
void _onLine(String line) {
|
||||||
if (licenseAccepted.hasMatch(line)) {
|
if (status == null && licenseAccepted.hasMatch(line)) {
|
||||||
status = LicensesAccepted.all;
|
status = LicensesAccepted.all;
|
||||||
} else if (licenseCounts.hasMatch(line)) {
|
} else if (licenseCounts.hasMatch(line)) {
|
||||||
final Match match = licenseCounts.firstMatch(line);
|
final Match match = licenseCounts.firstMatch(line);
|
||||||
@ -196,22 +196,22 @@ class AndroidWorkflow extends DoctorValidator implements Workflow {
|
|||||||
);
|
);
|
||||||
process.stdin.write('n\n');
|
process.stdin.write('n\n');
|
||||||
final Future<void> output = process.stdout
|
final Future<void> output = process.stdout
|
||||||
.transform(const Utf8Decoder(allowMalformed: true))
|
.transform(const Utf8Decoder(allowMalformed: true))
|
||||||
.transform(const LineSplitter())
|
.transform(const LineSplitter())
|
||||||
.listen(_onLine)
|
.listen(_onLine)
|
||||||
.asFuture<void>(null);
|
.asFuture<void>(null);
|
||||||
final Future<void> errors = process.stderr
|
final Future<void> errors = process.stderr
|
||||||
.transform(const Utf8Decoder(allowMalformed: true))
|
.transform(const Utf8Decoder(allowMalformed: true))
|
||||||
.transform(const LineSplitter())
|
.transform(const LineSplitter())
|
||||||
.listen(_onLine)
|
.listen(_onLine)
|
||||||
.asFuture<void>(null);
|
.asFuture<void>(null);
|
||||||
try {
|
try {
|
||||||
await Future.wait<void>(<Future<void>>[output, errors]).timeout(const Duration(seconds: 30));
|
await Future.wait<void>(<Future<void>>[output, errors]).timeout(const Duration(seconds: 30));
|
||||||
} catch (TimeoutException) {
|
} catch (TimeoutException) {
|
||||||
printTrace('Intentionally killing ${androidSdk.sdkManagerPath}');
|
printTrace('Intentionally killing ${androidSdk.sdkManagerPath}');
|
||||||
processManager.killPid(process.pid);
|
processManager.killPid(process.pid);
|
||||||
}
|
}
|
||||||
return status;
|
return status ?? LicensesAccepted.unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the Android SDK manager tool in order to accept SDK licenses.
|
/// Run the Android SDK manager tool in order to accept SDK licenses.
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
import 'file_system.dart';
|
import 'file_system.dart';
|
||||||
import 'platform.dart';
|
import 'platform.dart';
|
||||||
|
|
||||||
const int kDefaultObservatoryPort = 8100;
|
|
||||||
|
|
||||||
/// Return the absolute path of the user's home directory
|
/// Return the absolute path of the user's home directory
|
||||||
String get homeDirPath {
|
String get homeDirPath {
|
||||||
if (_homeDirPath == null) {
|
if (_homeDirPath == null) {
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'context.dart';
|
|
||||||
import 'io.dart';
|
|
||||||
|
|
||||||
const int _kMaxSearchIterations = 20;
|
|
||||||
|
|
||||||
PortScanner get portScanner => context[PortScanner];
|
|
||||||
|
|
||||||
abstract class PortScanner {
|
|
||||||
const PortScanner();
|
|
||||||
|
|
||||||
/// Returns true if the specified [port] is available to bind to.
|
|
||||||
Future<bool> isPortAvailable(int port);
|
|
||||||
|
|
||||||
/// Returns an available ephemeral port.
|
|
||||||
Future<int> findAvailablePort();
|
|
||||||
|
|
||||||
/// Returns an available port as close to [defaultPort] as possible.
|
|
||||||
///
|
|
||||||
/// If [defaultPort] is available, this will return it. Otherwise, it will
|
|
||||||
/// search for an available port close to [defaultPort]. If it cannot find one,
|
|
||||||
/// it will return any available port.
|
|
||||||
Future<int> findPreferredPort(int defaultPort) async {
|
|
||||||
int iterationCount = 0;
|
|
||||||
|
|
||||||
while (iterationCount < _kMaxSearchIterations) {
|
|
||||||
final int port = defaultPort + iterationCount;
|
|
||||||
if (await isPortAvailable(port))
|
|
||||||
return port;
|
|
||||||
iterationCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return findAvailablePort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HostPortScanner extends PortScanner {
|
|
||||||
const HostPortScanner();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> isPortAvailable(int port) async {
|
|
||||||
try {
|
|
||||||
// TODO(ianh): This is super racy.
|
|
||||||
final ServerSocket socket = await ServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, port); // ignore: deprecated_member_use
|
|
||||||
await socket.close();
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> findAvailablePort() async {
|
|
||||||
ServerSocket socket;
|
|
||||||
try {
|
|
||||||
socket = await ServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0); // ignore: deprecated_member_use
|
|
||||||
} on SocketException {
|
|
||||||
socket = await ServerSocket.bind(InternetAddress.LOOPBACK_IP_V6, 0, v6Only: true); // ignore: deprecated_member_use
|
|
||||||
}
|
|
||||||
final int port = socket.port;
|
|
||||||
await socket.close();
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
}
|
|
@ -229,7 +229,7 @@ Future<RunResult> runAsync(List<String> cmd, {
|
|||||||
workingDirectory: workingDirectory,
|
workingDirectory: workingDirectory,
|
||||||
environment: _environment(allowReentrantFlutter, environment),
|
environment: _environment(allowReentrantFlutter, environment),
|
||||||
);
|
);
|
||||||
final RunResult runResults = new RunResult(results);
|
final RunResult runResults = new RunResult(results, cmd);
|
||||||
printTrace(runResults.toString());
|
printTrace(runResults.toString());
|
||||||
return runResults;
|
return runResults;
|
||||||
}
|
}
|
||||||
@ -240,10 +240,10 @@ Future<RunResult> runCheckedAsync(List<String> cmd, {
|
|||||||
Map<String, String> environment
|
Map<String, String> environment
|
||||||
}) async {
|
}) async {
|
||||||
final RunResult result = await runAsync(
|
final RunResult result = await runAsync(
|
||||||
cmd,
|
cmd,
|
||||||
workingDirectory: workingDirectory,
|
workingDirectory: workingDirectory,
|
||||||
allowReentrantFlutter: allowReentrantFlutter,
|
allowReentrantFlutter: allowReentrantFlutter,
|
||||||
environment: environment
|
environment: environment,
|
||||||
);
|
);
|
||||||
if (result.exitCode != 0)
|
if (result.exitCode != 0)
|
||||||
throw 'Exit code ${result.exitCode} from: ${cmd.join(' ')}:\n$result';
|
throw 'Exit code ${result.exitCode} from: ${cmd.join(' ')}:\n$result';
|
||||||
@ -364,10 +364,12 @@ class ProcessExit implements Exception {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RunResult {
|
class RunResult {
|
||||||
RunResult(this.processResult);
|
RunResult(this.processResult, this._command) : assert(_command != null), assert(_command.isNotEmpty);
|
||||||
|
|
||||||
final ProcessResult processResult;
|
final ProcessResult processResult;
|
||||||
|
|
||||||
|
final List<String> _command;
|
||||||
|
|
||||||
int get exitCode => processResult.exitCode;
|
int get exitCode => processResult.exitCode;
|
||||||
String get stdout => processResult.stdout;
|
String get stdout => processResult.stdout;
|
||||||
String get stderr => processResult.stderr;
|
String get stderr => processResult.stderr;
|
||||||
@ -381,4 +383,14 @@ class RunResult {
|
|||||||
out.writeln(processResult.stderr);
|
out.writeln(processResult.stderr);
|
||||||
return out.toString().trimRight();
|
return out.toString().trimRight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Throws a [ProcessException] with the given `message`.
|
||||||
|
void throwException(String message) {
|
||||||
|
throw new ProcessException(
|
||||||
|
_command.first,
|
||||||
|
_command.skip(1).toList(),
|
||||||
|
message,
|
||||||
|
exitCode,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import '../base/common.dart';
|
|||||||
import '../base/io.dart';
|
import '../base/io.dart';
|
||||||
import '../cache.dart';
|
import '../cache.dart';
|
||||||
import '../device.dart';
|
import '../device.dart';
|
||||||
|
import '../globals.dart';
|
||||||
import '../protocol_discovery.dart';
|
import '../protocol_discovery.dart';
|
||||||
import '../resident_runner.dart';
|
import '../resident_runner.dart';
|
||||||
import '../run_hot.dart';
|
import '../run_hot.dart';
|
||||||
@ -77,7 +78,7 @@ class AttachCommand extends FlutterCommand {
|
|||||||
try {
|
try {
|
||||||
observatoryDiscovery = new ProtocolDiscovery.observatory(
|
observatoryDiscovery = new ProtocolDiscovery.observatory(
|
||||||
device.getLogReader(), portForwarder: device.portForwarder);
|
device.getLogReader(), portForwarder: device.portForwarder);
|
||||||
print('Listening.');
|
printStatus('Listening.');
|
||||||
observatoryUri = await observatoryDiscovery.uri;
|
observatoryUri = await observatoryDiscovery.uri;
|
||||||
} finally {
|
} finally {
|
||||||
await observatoryDiscovery?.cancel();
|
await observatoryDiscovery?.cancel();
|
||||||
|
@ -55,8 +55,7 @@ abstract class RunCommandBase extends FlutterCommand {
|
|||||||
void usesPortOptions() {
|
void usesPortOptions() {
|
||||||
argParser.addOption('observatory-port',
|
argParser.addOption('observatory-port',
|
||||||
help: 'Listen to the given port for an observatory debugger connection.\n'
|
help: 'Listen to the given port for an observatory debugger connection.\n'
|
||||||
'Specifying port 0 will find a random free port.\n'
|
'Specifying port 0 (the default) will find a random free port.'
|
||||||
'Defaults to the first available port after $kDefaultObservatoryPort.'
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,14 +16,18 @@ import '../tracing.dart';
|
|||||||
class TraceCommand extends FlutterCommand {
|
class TraceCommand extends FlutterCommand {
|
||||||
TraceCommand() {
|
TraceCommand() {
|
||||||
requiresPubspecYaml();
|
requiresPubspecYaml();
|
||||||
argParser.addFlag('start', negatable: false, help: 'Start tracing.');
|
|
||||||
argParser.addFlag('stop', negatable: false, help: 'Stop tracing.');
|
|
||||||
argParser.addOption('out', help: 'Specify the path of the saved trace file.');
|
|
||||||
argParser.addOption('duration',
|
|
||||||
defaultsTo: '10', abbr: 'd', help: 'Duration in seconds to trace.');
|
|
||||||
argParser.addOption('debug-port',
|
argParser.addOption('debug-port',
|
||||||
defaultsTo: kDefaultObservatoryPort.toString(),
|
help: 'Local port where the observatory is listening. Required.',
|
||||||
help: 'Local port where the observatory is listening.');
|
);
|
||||||
|
argParser.addFlag('start', negatable: false, help: 'Start tracing. Implied if --stop is also omitted.');
|
||||||
|
argParser.addFlag('stop', negatable: false, help: 'Stop tracing. Implied if --start is also omitted.');
|
||||||
|
argParser.addOption('duration',
|
||||||
|
abbr: 'd',
|
||||||
|
help: 'Time to wait after starting (if --start is specified or implied) and before\n'
|
||||||
|
'stopping (if --stop is specified or implied).\n'
|
||||||
|
'Defaults to ten seconds if --stop is specified or implied, zero otherwise.',
|
||||||
|
);
|
||||||
|
argParser.addOption('out', help: 'Specify the path of the saved trace file.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -34,13 +38,39 @@ class TraceCommand extends FlutterCommand {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
final String usageFooter =
|
final String usageFooter =
|
||||||
'\`trace\` called with no arguments will automatically start tracing, delay a set amount of\n'
|
'\`trace\` called without the --start or --stop flags will automatically start tracing,\n'
|
||||||
'time (controlled by --duration), and stop tracing. To explicitly control tracing, call trace\n'
|
'delay a set amount of time (controlled by --duration), and stop tracing. To explicitly\n'
|
||||||
'with --start and later with --stop.';
|
'control tracing, call trace with --start and later with --stop.\n'
|
||||||
|
'The --debug-port argument is required.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Null> runCommand() async {
|
Future<Null> runCommand() async {
|
||||||
final int observatoryPort = int.parse(argResults['debug-port']);
|
int observatoryPort;
|
||||||
|
if (argResults.wasParsed('debug-port')) {
|
||||||
|
observatoryPort = int.tryParse(argResults['debug-port']);
|
||||||
|
}
|
||||||
|
if (observatoryPort == null) {
|
||||||
|
throwToolExit('The --debug-port argument must be specified.');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool start = argResults['start'];
|
||||||
|
bool stop = argResults['stop'];
|
||||||
|
if (!start && !stop) {
|
||||||
|
start = true;
|
||||||
|
stop = true;
|
||||||
|
}
|
||||||
|
assert(start || stop);
|
||||||
|
|
||||||
|
Duration duration;
|
||||||
|
if (argResults.wasParsed('duration')) {
|
||||||
|
try {
|
||||||
|
duration = new Duration(seconds: int.parse(argResults['duration']));
|
||||||
|
} on FormatException {
|
||||||
|
throwToolExit('Invalid duration passed to --duration; it should be a positive number of seconds.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
duration = stop ? const Duration(seconds: 10) : Duration.zero;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(danrubel): this will break if we move to the new observatory URL
|
// TODO(danrubel): this will break if we move to the new observatory URL
|
||||||
// See https://github.com/flutter/flutter/issues/7038
|
// See https://github.com/flutter/flutter/issues/7038
|
||||||
@ -56,20 +86,11 @@ class TraceCommand extends FlutterCommand {
|
|||||||
|
|
||||||
Cache.releaseLockEarly();
|
Cache.releaseLockEarly();
|
||||||
|
|
||||||
if ((!argResults['start'] && !argResults['stop']) ||
|
if (start)
|
||||||
(argResults['start'] && argResults['stop'])) {
|
|
||||||
// Setting neither flags or both flags means do both commands and wait
|
|
||||||
// duration seconds in between.
|
|
||||||
await tracing.startTracing();
|
await tracing.startTracing();
|
||||||
await new Future<Null>.delayed(
|
await new Future<Null>.delayed(duration);
|
||||||
new Duration(seconds: int.parse(argResults['duration'])),
|
if (stop)
|
||||||
() => _stopTracing(tracing)
|
|
||||||
);
|
|
||||||
} else if (argResults['stop']) {
|
|
||||||
await _stopTracing(tracing);
|
await _stopTracing(tracing);
|
||||||
} else {
|
|
||||||
await tracing.startTracing();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Null> _stopTracing(Tracing tracing) async {
|
Future<Null> _stopTracing(Tracing tracing) async {
|
||||||
|
@ -19,7 +19,6 @@ import 'base/io.dart';
|
|||||||
import 'base/logger.dart';
|
import 'base/logger.dart';
|
||||||
import 'base/os.dart';
|
import 'base/os.dart';
|
||||||
import 'base/platform.dart';
|
import 'base/platform.dart';
|
||||||
import 'base/port_scanner.dart';
|
|
||||||
import 'base/utils.dart';
|
import 'base/utils.dart';
|
||||||
import 'cache.dart';
|
import 'cache.dart';
|
||||||
import 'compile.dart';
|
import 'compile.dart';
|
||||||
@ -70,7 +69,6 @@ Future<T> runInContext<T>(
|
|||||||
KernelCompiler: () => const KernelCompiler(),
|
KernelCompiler: () => const KernelCompiler(),
|
||||||
Logger: () => platform.isWindows ? new WindowsStdoutLogger() : new StdoutLogger(),
|
Logger: () => platform.isWindows ? new WindowsStdoutLogger() : new StdoutLogger(),
|
||||||
OperatingSystemUtils: () => new OperatingSystemUtils(),
|
OperatingSystemUtils: () => new OperatingSystemUtils(),
|
||||||
PortScanner: () => const HostPortScanner(),
|
|
||||||
SimControl: () => new SimControl(),
|
SimControl: () => new SimControl(),
|
||||||
Stdio: () => const Stdio(),
|
Stdio: () => const Stdio(),
|
||||||
Usage: () => new Usage(),
|
Usage: () => new Usage(),
|
||||||
|
@ -7,10 +7,8 @@ import 'dart:math' as math;
|
|||||||
|
|
||||||
import 'android/android_device.dart';
|
import 'android/android_device.dart';
|
||||||
import 'application_package.dart';
|
import 'application_package.dart';
|
||||||
import 'base/common.dart';
|
|
||||||
import 'base/context.dart';
|
import 'base/context.dart';
|
||||||
import 'base/file_system.dart';
|
import 'base/file_system.dart';
|
||||||
import 'base/port_scanner.dart';
|
|
||||||
import 'base/utils.dart';
|
import 'base/utils.dart';
|
||||||
import 'build_info.dart';
|
import 'build_info.dart';
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
@ -367,14 +365,6 @@ class DebuggingOptions {
|
|||||||
final int observatoryPort;
|
final int observatoryPort;
|
||||||
|
|
||||||
bool get hasObservatoryPort => observatoryPort != null;
|
bool get hasObservatoryPort => observatoryPort != null;
|
||||||
|
|
||||||
/// Return the user specified observatory port. If that isn't available,
|
|
||||||
/// return [kDefaultObservatoryPort], or a port close to that one.
|
|
||||||
Future<int> findBestObservatoryPort() {
|
|
||||||
if (hasObservatoryPort)
|
|
||||||
return new Future<int>.value(observatoryPort);
|
|
||||||
return portScanner.findPreferredPort(observatoryPort ?? kDefaultObservatoryPort);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LaunchResult {
|
class LaunchResult {
|
||||||
@ -414,9 +404,9 @@ abstract class DevicePortForwarder {
|
|||||||
List<ForwardedPort> get forwardedPorts;
|
List<ForwardedPort> get forwardedPorts;
|
||||||
|
|
||||||
/// Forward [hostPort] on the host to [devicePort] on the device.
|
/// Forward [hostPort] on the host to [devicePort] on the device.
|
||||||
/// If [hostPort] is null, will auto select a host port.
|
/// If [hostPort] is null or zero, will auto select a host port.
|
||||||
/// Returns a Future that completes with the host port.
|
/// Returns a Future that completes with the host port.
|
||||||
Future<int> forward(int devicePort, { int hostPort });
|
Future<int> forward(int devicePort, {int hostPort});
|
||||||
|
|
||||||
/// Stops forwarding [forwardedPort].
|
/// Stops forwarding [forwardedPort].
|
||||||
Future<Null> unforward(ForwardedPort forwardedPort);
|
Future<Null> unforward(ForwardedPort forwardedPort);
|
||||||
|
@ -10,7 +10,6 @@ import '../base/file_system.dart';
|
|||||||
import '../base/io.dart';
|
import '../base/io.dart';
|
||||||
import '../base/logger.dart';
|
import '../base/logger.dart';
|
||||||
import '../base/platform.dart';
|
import '../base/platform.dart';
|
||||||
import '../base/port_scanner.dart';
|
|
||||||
import '../base/process.dart';
|
import '../base/process.dart';
|
||||||
import '../base/process_manager.dart';
|
import '../base/process_manager.dart';
|
||||||
import '../build_info.dart';
|
import '../build_info.dart';
|
||||||
@ -199,14 +198,9 @@ class IOSDevice extends Device {
|
|||||||
if (debuggingOptions.useTestFonts)
|
if (debuggingOptions.useTestFonts)
|
||||||
launchArguments.add('--use-test-fonts');
|
launchArguments.add('--use-test-fonts');
|
||||||
|
|
||||||
if (debuggingOptions.debuggingEnabled) {
|
if (debuggingOptions.debuggingEnabled)
|
||||||
launchArguments.add('--enable-checked-mode');
|
launchArguments.add('--enable-checked-mode');
|
||||||
|
|
||||||
// Note: We do NOT need to set the observatory port since this is going to
|
|
||||||
// be setup on the device. Let it pick a port automatically. We will check
|
|
||||||
// the port picked and scrape that later.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debuggingOptions.enableSoftwareRendering)
|
if (debuggingOptions.enableSoftwareRendering)
|
||||||
launchArguments.add('--enable-software-rendering');
|
launchArguments.add('--enable-software-rendering');
|
||||||
|
|
||||||
@ -507,28 +501,46 @@ class _IOSDevicePortForwarder extends DevicePortForwarder {
|
|||||||
@override
|
@override
|
||||||
List<ForwardedPort> get forwardedPorts => _forwardedPorts;
|
List<ForwardedPort> get forwardedPorts => _forwardedPorts;
|
||||||
|
|
||||||
|
static const Duration _kiProxyPortForwardTimeout = const Duration(seconds: 1);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> forward(int devicePort, {int hostPort}) async {
|
Future<int> forward(int devicePort, {int hostPort}) async {
|
||||||
if ((hostPort == null) || (hostPort == 0)) {
|
final bool autoselect = hostPort == null || hostPort == 0;
|
||||||
// Auto select host port.
|
if (autoselect)
|
||||||
hostPort = await portScanner.findAvailablePort();
|
hostPort = 1024;
|
||||||
|
|
||||||
|
Process process;
|
||||||
|
|
||||||
|
bool connected = false;
|
||||||
|
while (!connected) {
|
||||||
|
printTrace('attempting to forward device port $devicePort to host port $hostPort');
|
||||||
|
// Usage: iproxy LOCAL_TCP_PORT DEVICE_TCP_PORT UDID
|
||||||
|
process = await runCommand(<String>[
|
||||||
|
device._iproxyPath,
|
||||||
|
hostPort.toString(),
|
||||||
|
devicePort.toString(),
|
||||||
|
device.id,
|
||||||
|
]);
|
||||||
|
// TODO(ianh): This is a flakey race condition, https://github.com/libimobiledevice/libimobiledevice/issues/674
|
||||||
|
connected = !await process.stdout.isEmpty.timeout(_kiProxyPortForwardTimeout, onTimeout: () => false);
|
||||||
|
if (!connected) {
|
||||||
|
if (autoselect) {
|
||||||
|
hostPort += 1;
|
||||||
|
if (hostPort > 65535)
|
||||||
|
throw new Exception('Could not find open port on host.');
|
||||||
|
} else {
|
||||||
|
throw new Exception('Port $hostPort is not available.');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
assert(connected);
|
||||||
|
assert(process != null);
|
||||||
|
|
||||||
// Usage: iproxy LOCAL_TCP_PORT DEVICE_TCP_PORT UDID
|
final ForwardedPort forwardedPort = new ForwardedPort.withContext(
|
||||||
final Process process = await runCommand(<String>[
|
hostPort, devicePort, process,
|
||||||
device._iproxyPath,
|
);
|
||||||
hostPort.toString(),
|
|
||||||
devicePort.toString(),
|
|
||||||
device.id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
final ForwardedPort forwardedPort = new ForwardedPort.withContext(hostPort,
|
|
||||||
devicePort, process);
|
|
||||||
|
|
||||||
printTrace('Forwarded port $forwardedPort');
|
printTrace('Forwarded port $forwardedPort');
|
||||||
|
|
||||||
_forwardedPorts.add(forwardedPort);
|
_forwardedPorts.add(forwardedPort);
|
||||||
|
|
||||||
return hostPort;
|
return hostPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,8 +305,7 @@ class IOSSimulator extends Device {
|
|||||||
args.add('--skia-deterministic-rendering');
|
args.add('--skia-deterministic-rendering');
|
||||||
if (debuggingOptions.useTestFonts)
|
if (debuggingOptions.useTestFonts)
|
||||||
args.add('--use-test-fonts');
|
args.add('--use-test-fonts');
|
||||||
|
final int observatoryPort = debuggingOptions.observatoryPort ?? 0;
|
||||||
final int observatoryPort = await debuggingOptions.findBestObservatoryPort();
|
|
||||||
args.add('--observatory-port=$observatoryPort');
|
args.add('--observatory-port=$observatoryPort');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,7 +692,7 @@ class _IOSSimulatorDevicePortForwarder extends DevicePortForwarder {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> forward(int devicePort, {int hostPort}) async {
|
Future<int> forward(int devicePort, {int hostPort}) async {
|
||||||
if ((hostPort == null) || (hostPort == 0)) {
|
if (hostPort == null || hostPort == 0) {
|
||||||
hostPort = devicePort;
|
hostPort = devicePort;
|
||||||
}
|
}
|
||||||
assert(devicePort == hostPort);
|
assert(devicePort == hostPort);
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'base/common.dart';
|
|
||||||
import 'base/io.dart';
|
import 'base/io.dart';
|
||||||
import 'base/port_scanner.dart';
|
|
||||||
import 'device.dart';
|
import 'device.dart';
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
|
|
||||||
@ -18,10 +16,8 @@ class ProtocolDiscovery {
|
|||||||
this.serviceName, {
|
this.serviceName, {
|
||||||
this.portForwarder,
|
this.portForwarder,
|
||||||
this.hostPort,
|
this.hostPort,
|
||||||
this.defaultHostPort,
|
|
||||||
this.ipv6,
|
this.ipv6,
|
||||||
}) : assert(logReader != null),
|
}) : assert(logReader != null),
|
||||||
assert(portForwarder == null || defaultHostPort != null),
|
|
||||||
_prefix = '$serviceName listening on ' {
|
_prefix = '$serviceName listening on ' {
|
||||||
_deviceLogSubscription = logReader.logLines.listen(_handleLine);
|
_deviceLogSubscription = logReader.logLines.listen(_handleLine);
|
||||||
}
|
}
|
||||||
@ -37,7 +33,6 @@ class ProtocolDiscovery {
|
|||||||
logReader, kObservatoryService,
|
logReader, kObservatoryService,
|
||||||
portForwarder: portForwarder,
|
portForwarder: portForwarder,
|
||||||
hostPort: hostPort,
|
hostPort: hostPort,
|
||||||
defaultHostPort: kDefaultObservatoryPort,
|
|
||||||
ipv6: ipv6,
|
ipv6: ipv6,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -46,7 +41,6 @@ class ProtocolDiscovery {
|
|||||||
final String serviceName;
|
final String serviceName;
|
||||||
final DevicePortForwarder portForwarder;
|
final DevicePortForwarder portForwarder;
|
||||||
final int hostPort;
|
final int hostPort;
|
||||||
final int defaultHostPort;
|
|
||||||
final bool ipv6;
|
final bool ipv6;
|
||||||
|
|
||||||
final String _prefix;
|
final String _prefix;
|
||||||
@ -88,16 +82,15 @@ class ProtocolDiscovery {
|
|||||||
Uri hostUri = deviceUri;
|
Uri hostUri = deviceUri;
|
||||||
|
|
||||||
if (portForwarder != null) {
|
if (portForwarder != null) {
|
||||||
final int devicePort = deviceUri.port;
|
final int actualDevicePort = deviceUri.port;
|
||||||
int hostPort = this.hostPort ?? await portScanner.findPreferredPort(defaultHostPort);
|
final int actualHostPort = await portForwarder.forward(actualDevicePort, hostPort: hostPort);
|
||||||
hostPort = await portForwarder.forward(devicePort, hostPort: hostPort);
|
printTrace('Forwarded host port $actualHostPort to device port $actualDevicePort for $serviceName');
|
||||||
printTrace('Forwarded host port $hostPort to device port $devicePort for $serviceName');
|
hostUri = deviceUri.replace(port: actualHostPort);
|
||||||
hostUri = deviceUri.replace(port: hostPort);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(new InternetAddress(hostUri.host).isLoopback);
|
assert(new InternetAddress(hostUri.host).isLoopback);
|
||||||
if (ipv6) {
|
if (ipv6) {
|
||||||
hostUri = hostUri.replace(host: InternetAddress.LOOPBACK_IP_V6.host); // ignore: deprecated_member_use
|
hostUri = hostUri.replace(host: InternetAddress.loopbackIPv6.host);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hostUri;
|
return hostUri;
|
||||||
|
@ -41,13 +41,10 @@ class FlutterTesterApp extends ApplicationPackage {
|
|||||||
|
|
||||||
// TODO(scheglov): This device does not currently work with full restarts.
|
// TODO(scheglov): This device does not currently work with full restarts.
|
||||||
class FlutterTesterDevice extends Device {
|
class FlutterTesterDevice extends Device {
|
||||||
final _FlutterTesterDeviceLogReader _logReader =
|
FlutterTesterDevice(String deviceId) : super(deviceId);
|
||||||
new _FlutterTesterDeviceLogReader();
|
|
||||||
|
|
||||||
Process _process;
|
Process _process;
|
||||||
|
|
||||||
FlutterTesterDevice(String deviceId) : super(deviceId);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> get isLocalEmulator async => false;
|
Future<bool> get isLocalEmulator async => false;
|
||||||
|
|
||||||
@ -69,6 +66,9 @@ class FlutterTesterDevice extends Device {
|
|||||||
@override
|
@override
|
||||||
void clearLogs() {}
|
void clearLogs() {}
|
||||||
|
|
||||||
|
final _FlutterTesterDeviceLogReader _logReader =
|
||||||
|
new _FlutterTesterDeviceLogReader();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DeviceLogReader getLogReader({ApplicationPackage app}) => _logReader;
|
DeviceLogReader getLogReader({ApplicationPackage app}) => _logReader;
|
||||||
|
|
||||||
@ -118,12 +118,10 @@ class FlutterTesterDevice extends Device {
|
|||||||
'--packages=${PackageMap.globalPackagesPath}',
|
'--packages=${PackageMap.globalPackagesPath}',
|
||||||
];
|
];
|
||||||
if (debuggingOptions.debuggingEnabled) {
|
if (debuggingOptions.debuggingEnabled) {
|
||||||
if (debuggingOptions.startPaused) {
|
if (debuggingOptions.startPaused)
|
||||||
command.add('--start-paused');
|
command.add('--start-paused');
|
||||||
}
|
|
||||||
if (debuggingOptions.hasObservatoryPort)
|
if (debuggingOptions.hasObservatoryPort)
|
||||||
command
|
command.add('--observatory-port=${debuggingOptions.observatoryPort}');
|
||||||
.add('--observatory-port=${debuggingOptions.hasObservatoryPort}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build assets and perform initial compilation.
|
// Build assets and perform initial compilation.
|
||||||
@ -170,9 +168,10 @@ class FlutterTesterDevice extends Device {
|
|||||||
if (!debuggingOptions.debuggingEnabled)
|
if (!debuggingOptions.debuggingEnabled)
|
||||||
return new LaunchResult.succeeded();
|
return new LaunchResult.succeeded();
|
||||||
|
|
||||||
final ProtocolDiscovery observatoryDiscovery =
|
final ProtocolDiscovery observatoryDiscovery = new ProtocolDiscovery.observatory(
|
||||||
new ProtocolDiscovery.observatory(getLogReader(),
|
getLogReader(),
|
||||||
hostPort: debuggingOptions.observatoryPort);
|
hostPort: debuggingOptions.observatoryPort,
|
||||||
|
);
|
||||||
|
|
||||||
final Uri observatoryUri = await observatoryDiscovery.uri;
|
final Uri observatoryUri = await observatoryDiscovery.uri;
|
||||||
return new LaunchResult.succeeded(observatoryUri: observatoryUri);
|
return new LaunchResult.succeeded(observatoryUri: observatoryUri);
|
||||||
@ -186,7 +185,6 @@ class FlutterTesterDevice extends Device {
|
|||||||
Future<bool> stopApp(ApplicationPackage app) async {
|
Future<bool> stopApp(ApplicationPackage app) async {
|
||||||
_process?.kill();
|
_process?.kill();
|
||||||
_process = null;
|
_process = null;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +193,8 @@ class FlutterTesterDevice extends Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlutterTesterDevices extends PollingDeviceDiscovery {
|
class FlutterTesterDevices extends PollingDeviceDiscovery {
|
||||||
|
FlutterTesterDevices() : super('Flutter tester');
|
||||||
|
|
||||||
static const String kTesterDeviceId = 'flutter-tester';
|
static const String kTesterDeviceId = 'flutter-tester';
|
||||||
|
|
||||||
static bool showFlutterTesterDevice = false;
|
static bool showFlutterTesterDevice = false;
|
||||||
@ -202,8 +202,6 @@ class FlutterTesterDevices extends PollingDeviceDiscovery {
|
|||||||
final FlutterTesterDevice _testerDevice =
|
final FlutterTesterDevice _testerDevice =
|
||||||
new FlutterTesterDevice(kTesterDeviceId);
|
new FlutterTesterDevice(kTesterDeviceId);
|
||||||
|
|
||||||
FlutterTesterDevices() : super('Flutter tester');
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get canListAnything => true;
|
bool get canListAnything => true;
|
||||||
|
|
||||||
|
@ -381,7 +381,7 @@ void main() {
|
|||||||
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'snapshot_assembly.S')} : ',
|
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'snapshot_assembly.S')} : ',
|
||||||
};
|
};
|
||||||
|
|
||||||
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''));
|
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
|
||||||
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
|
|
||||||
@ -427,7 +427,7 @@ void main() {
|
|||||||
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'snapshot_assembly.S')} : ',
|
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'snapshot_assembly.S')} : ',
|
||||||
};
|
};
|
||||||
|
|
||||||
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''));
|
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
|
||||||
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
|
|
||||||
@ -474,7 +474,7 @@ void main() {
|
|||||||
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'vm_snapshot_data')} : ',
|
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'vm_snapshot_data')} : ',
|
||||||
};
|
};
|
||||||
|
|
||||||
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''));
|
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
|
||||||
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
|
|
||||||
@ -525,7 +525,7 @@ void main() {
|
|||||||
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'vm_snapshot_data')} : ',
|
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'vm_snapshot_data')} : ',
|
||||||
};
|
};
|
||||||
|
|
||||||
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''));
|
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
|
||||||
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
|
|
||||||
@ -571,7 +571,7 @@ void main() {
|
|||||||
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'snapshot_assembly.S')} : ',
|
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'snapshot_assembly.S')} : ',
|
||||||
};
|
};
|
||||||
|
|
||||||
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''));
|
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
|
||||||
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
|
|
||||||
@ -617,7 +617,7 @@ void main() {
|
|||||||
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'snapshot_assembly.S')} : ',
|
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'snapshot_assembly.S')} : ',
|
||||||
};
|
};
|
||||||
|
|
||||||
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''));
|
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
|
||||||
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
|
|
||||||
@ -683,7 +683,7 @@ void main() {
|
|||||||
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'vm_snapshot_data')} : ',
|
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'vm_snapshot_data')} : ',
|
||||||
};
|
};
|
||||||
|
|
||||||
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''));
|
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
|
||||||
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
|
|
||||||
@ -734,7 +734,7 @@ void main() {
|
|||||||
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'vm_snapshot_data')} : ',
|
fs.path.join(outputPath, 'snapshot.d'): '${fs.path.join(outputPath, 'vm_snapshot_data')} : ',
|
||||||
};
|
};
|
||||||
|
|
||||||
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''));
|
final RunResult successResult = new RunResult(new ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
|
||||||
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.cc(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
when(xcode.clang(any)).thenAnswer((_) => new Future<RunResult>.value(successResult));
|
||||||
|
|
||||||
|
@ -42,10 +42,13 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Future<LaunchResult> start(String mainPath) async {
|
Future<LaunchResult> start(String mainPath) async {
|
||||||
return await device.startApp(null,
|
return await device.startApp(
|
||||||
mainPath: mainPath,
|
null,
|
||||||
debuggingOptions: new DebuggingOptions.enabled(
|
mainPath: mainPath,
|
||||||
const BuildInfo(BuildMode.debug, null)));
|
debuggingOptions: new DebuggingOptions.enabled(
|
||||||
|
const BuildInfo(BuildMode.debug, null),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
testUsingContext('start', () async {
|
testUsingContext('start', () async {
|
||||||
|
@ -115,16 +115,16 @@ void main() {
|
|||||||
testUsingContext('default port', () async {
|
testUsingContext('default port', () async {
|
||||||
final MockDeviceLogReader logReader = new MockDeviceLogReader();
|
final MockDeviceLogReader logReader = new MockDeviceLogReader();
|
||||||
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
|
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
|
||||||
logReader,
|
logReader,
|
||||||
portForwarder: new MockPortForwarder(99),
|
portForwarder: new MockPortForwarder(99),
|
||||||
hostPort: 54777);
|
);
|
||||||
|
|
||||||
// Get next port future.
|
// Get next port future.
|
||||||
final Future<Uri> nextUri = discoverer.uri;
|
final Future<Uri> nextUri = discoverer.uri;
|
||||||
logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
|
logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
|
||||||
final Uri uri = await nextUri;
|
final Uri uri = await nextUri;
|
||||||
expect(uri.port, 54777);
|
expect(uri.port, 99);
|
||||||
expect('$uri', 'http://127.0.0.1:54777/PTwjm8Ii8qg=/');
|
expect('$uri', 'http://127.0.0.1:99/PTwjm8Ii8qg=/');
|
||||||
|
|
||||||
discoverer.cancel();
|
discoverer.cancel();
|
||||||
logReader.dispose();
|
logReader.dispose();
|
||||||
@ -133,9 +133,10 @@ void main() {
|
|||||||
testUsingContext('specified port', () async {
|
testUsingContext('specified port', () async {
|
||||||
final MockDeviceLogReader logReader = new MockDeviceLogReader();
|
final MockDeviceLogReader logReader = new MockDeviceLogReader();
|
||||||
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
|
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
|
||||||
logReader,
|
logReader,
|
||||||
portForwarder: new MockPortForwarder(99),
|
portForwarder: new MockPortForwarder(99),
|
||||||
hostPort: 1243);
|
hostPort: 1243,
|
||||||
|
);
|
||||||
|
|
||||||
// Get next port future.
|
// Get next port future.
|
||||||
final Future<Uri> nextUri = discoverer.uri;
|
final Future<Uri> nextUri = discoverer.uri;
|
||||||
@ -148,13 +149,33 @@ void main() {
|
|||||||
logReader.dispose();
|
logReader.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUsingContext('specified port zero', () async {
|
||||||
|
final MockDeviceLogReader logReader = new MockDeviceLogReader();
|
||||||
|
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
|
||||||
|
logReader,
|
||||||
|
portForwarder: new MockPortForwarder(99),
|
||||||
|
hostPort: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get next port future.
|
||||||
|
final Future<Uri> nextUri = discoverer.uri;
|
||||||
|
logReader.addLine('I/flutter : Observatory listening on http://127.0.0.1:54804/PTwjm8Ii8qg=/');
|
||||||
|
final Uri uri = await nextUri;
|
||||||
|
expect(uri.port, 99);
|
||||||
|
expect('$uri', 'http://127.0.0.1:99/PTwjm8Ii8qg=/');
|
||||||
|
|
||||||
|
discoverer.cancel();
|
||||||
|
logReader.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
testUsingContext('ipv6', () async {
|
testUsingContext('ipv6', () async {
|
||||||
final MockDeviceLogReader logReader = new MockDeviceLogReader();
|
final MockDeviceLogReader logReader = new MockDeviceLogReader();
|
||||||
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
|
final ProtocolDiscovery discoverer = new ProtocolDiscovery.observatory(
|
||||||
logReader,
|
logReader,
|
||||||
portForwarder: new MockPortForwarder(99),
|
portForwarder: new MockPortForwarder(99),
|
||||||
hostPort: 54777,
|
hostPort: 54777,
|
||||||
ipv6: true);
|
ipv6: true,
|
||||||
|
);
|
||||||
|
|
||||||
// Get next port future.
|
// Get next port future.
|
||||||
final Future<Uri> nextUri = discoverer.uri;
|
final Future<Uri> nextUri = discoverer.uri;
|
||||||
@ -175,7 +196,12 @@ class MockPortForwarder extends DevicePortForwarder {
|
|||||||
MockPortForwarder([this.availablePort]);
|
MockPortForwarder([this.availablePort]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> forward(int devicePort, {int hostPort}) async => hostPort ?? availablePort;
|
Future<int> forward(int devicePort, {int hostPort}) async {
|
||||||
|
hostPort ??= 0;
|
||||||
|
if (hostPort == 0)
|
||||||
|
return availablePort;
|
||||||
|
return hostPort;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ForwardedPort> get forwardedPorts => throw 'not implemented';
|
List<ForwardedPort> get forwardedPorts => throw 'not implemented';
|
||||||
|
@ -12,7 +12,6 @@ import 'package:flutter_tools/src/base/file_system.dart';
|
|||||||
import 'package:flutter_tools/src/base/io.dart';
|
import 'package:flutter_tools/src/base/io.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/base/os.dart';
|
import 'package:flutter_tools/src/base/os.dart';
|
||||||
import 'package:flutter_tools/src/base/port_scanner.dart';
|
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/context_runner.dart';
|
import 'package:flutter_tools/src/context_runner.dart';
|
||||||
import 'package:flutter_tools/src/device.dart';
|
import 'package:flutter_tools/src/device.dart';
|
||||||
@ -77,7 +76,6 @@ void testUsingContext(String description, dynamic testMethod(), {
|
|||||||
},
|
},
|
||||||
Logger: () => new BufferLogger(),
|
Logger: () => new BufferLogger(),
|
||||||
OperatingSystemUtils: () => new MockOperatingSystemUtils(),
|
OperatingSystemUtils: () => new MockOperatingSystemUtils(),
|
||||||
PortScanner: () => new MockPortScanner(),
|
|
||||||
SimControl: () => new MockSimControl(),
|
SimControl: () => new MockSimControl(),
|
||||||
Usage: () => new MockUsage(),
|
Usage: () => new MockUsage(),
|
||||||
XcodeProjectInterpreter: () => new MockXcodeProjectInterpreter(),
|
XcodeProjectInterpreter: () => new MockXcodeProjectInterpreter(),
|
||||||
@ -127,16 +125,6 @@ void _printBufferedErrors(AppContext testContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockPortScanner extends PortScanner {
|
|
||||||
static int _nextAvailablePort = 12345;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> isPortAvailable(int port) async => true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> findAvailablePort() async => _nextAvailablePort++;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MockDeviceManager implements DeviceManager {
|
class MockDeviceManager implements DeviceManager {
|
||||||
List<Device> devices = <Device>[];
|
List<Device> devices = <Device>[];
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
import 'package:flutter_tools/src/base/port_scanner.dart';
|
|
||||||
import 'package:flutter_tools/src/vmservice.dart';
|
import 'package:flutter_tools/src/vmservice.dart';
|
||||||
|
|
||||||
import 'src/common.dart';
|
import 'src/common.dart';
|
||||||
@ -13,9 +12,8 @@ import 'src/context.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
group('VMService', () {
|
group('VMService', () {
|
||||||
testUsingContext('fails connection eagerly in the connect() method', () async {
|
testUsingContext('fails connection eagerly in the connect() method', () async {
|
||||||
final int port = await const HostPortScanner().findAvailablePort();
|
|
||||||
expect(
|
expect(
|
||||||
VMService.connect(Uri.parse('http://localhost:$port')),
|
VMService.connect(Uri.parse('http://host.invalid:9999/')),
|
||||||
throwsToolExit(),
|
throwsToolExit(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user