mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
This reverts commit 2be4570d3a
.
This commit is contained in:
parent
2be4570d3a
commit
4881b4b07c
@ -212,9 +212,6 @@ class IOSDevice extends Device {
|
|||||||
|
|
||||||
DevicePortForwarder _portForwarder;
|
DevicePortForwarder _portForwarder;
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
IOSDeployDebugger iosDeployDebugger;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> get isLocalEmulator async => false;
|
Future<bool> get isLocalEmulator async => false;
|
||||||
|
|
||||||
@ -398,38 +395,23 @@ class IOSDevice extends Device {
|
|||||||
timeout: timeoutConfiguration.slowOperation);
|
timeout: timeoutConfiguration.slowOperation);
|
||||||
try {
|
try {
|
||||||
ProtocolDiscovery observatoryDiscovery;
|
ProtocolDiscovery observatoryDiscovery;
|
||||||
int installationResult = 1;
|
|
||||||
if (debuggingOptions.debuggingEnabled) {
|
if (debuggingOptions.debuggingEnabled) {
|
||||||
_logger.printTrace('Debugging is enabled, connecting to observatory');
|
_logger.printTrace('Debugging is enabled, connecting to observatory');
|
||||||
iosDeployDebugger = _iosDeploy.prepareDebuggerForLaunch(
|
|
||||||
deviceId: id,
|
|
||||||
bundlePath: bundle.path,
|
|
||||||
launchArguments: launchArguments,
|
|
||||||
interfaceType: interfaceType,
|
|
||||||
);
|
|
||||||
|
|
||||||
final DeviceLogReader deviceLogReader = getLogReader(app: package);
|
|
||||||
if (deviceLogReader is IOSDeviceLogReader) {
|
|
||||||
deviceLogReader.debuggerStream = iosDeployDebugger;
|
|
||||||
}
|
|
||||||
observatoryDiscovery = ProtocolDiscovery.observatory(
|
observatoryDiscovery = ProtocolDiscovery.observatory(
|
||||||
deviceLogReader,
|
getLogReader(app: package),
|
||||||
portForwarder: portForwarder,
|
portForwarder: portForwarder,
|
||||||
throttleDuration: fallbackPollingDelay,
|
|
||||||
throttleTimeout: fallbackThrottleTimeout ?? const Duration(seconds: 5),
|
|
||||||
hostPort: debuggingOptions.hostVmServicePort,
|
hostPort: debuggingOptions.hostVmServicePort,
|
||||||
devicePort: debuggingOptions.deviceVmServicePort,
|
devicePort: debuggingOptions.deviceVmServicePort,
|
||||||
ipv6: ipv6,
|
ipv6: ipv6,
|
||||||
);
|
throttleTimeout: fallbackThrottleTimeout ?? const Duration(seconds: 1),
|
||||||
installationResult = await iosDeployDebugger.launchAndAttach() ? 0 : 1;
|
|
||||||
} else {
|
|
||||||
installationResult = await _iosDeploy.launchApp(
|
|
||||||
deviceId: id,
|
|
||||||
bundlePath: bundle.path,
|
|
||||||
launchArguments: launchArguments,
|
|
||||||
interfaceType: interfaceType,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
final int installationResult = await _iosDeploy.runApp(
|
||||||
|
deviceId: id,
|
||||||
|
bundlePath: bundle.path,
|
||||||
|
launchArguments: launchArguments,
|
||||||
|
interfaceType: interfaceType,
|
||||||
|
);
|
||||||
if (installationResult != 0) {
|
if (installationResult != 0) {
|
||||||
_logger.printError('Could not run ${bundle.path} on $id.');
|
_logger.printError('Could not run ${bundle.path} on $id.');
|
||||||
_logger.printError('Try launching Xcode and selecting "Product > Run" to fix the problem:');
|
_logger.printError('Try launching Xcode and selecting "Product > Run" to fix the problem:');
|
||||||
@ -483,11 +465,7 @@ class IOSDevice extends Device {
|
|||||||
IOSApp app, {
|
IOSApp app, {
|
||||||
String userIdentifier,
|
String userIdentifier,
|
||||||
}) async {
|
}) async {
|
||||||
// If the debugger is not attached, killing the ios-deploy process won't stop the app.
|
// Currently we don't have a way to stop an app running on iOS.
|
||||||
if (iosDeployDebugger!= null && iosDeployDebugger.debuggerAttached) {
|
|
||||||
// Avoid null.
|
|
||||||
return iosDeployDebugger?.exit() == true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -677,13 +655,6 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
|||||||
// Matches a syslog line from any app.
|
// Matches a syslog line from any app.
|
||||||
RegExp _anyLineRegex;
|
RegExp _anyLineRegex;
|
||||||
|
|
||||||
// Logging from native code/Flutter engine is prefixed by timestamp and process metadata:
|
|
||||||
// 2020-09-15 19:15:10.931434-0700 Runner[541:226276] Did finish launching.
|
|
||||||
// 2020-09-15 19:15:10.931434-0700 Runner[541:226276] [Category] Did finish launching.
|
|
||||||
//
|
|
||||||
// Logging from the dart code has no prefixing metadata.
|
|
||||||
final RegExp _debuggerLoggingRegex = RegExp(r'^\S* \S* \S*\[[0-9:]*] (.*)');
|
|
||||||
|
|
||||||
StreamController<String> _linesController;
|
StreamController<String> _linesController;
|
||||||
List<StreamSubscription<void>> _loggingSubscriptions;
|
List<StreamSubscription<void>> _loggingSubscriptions;
|
||||||
|
|
||||||
@ -716,10 +687,6 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void logMessage(vm_service.Event event) {
|
void logMessage(vm_service.Event event) {
|
||||||
if (_iosDeployDebugger != null && _iosDeployDebugger.debuggerAttached) {
|
|
||||||
// Prefer the more complete logs from the attached debugger.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String message = processVmServiceMessage(event);
|
final String message = processVmServiceMessage(event);
|
||||||
if (message.isNotEmpty) {
|
if (message.isNotEmpty) {
|
||||||
_linesController.add(message);
|
_linesController.add(message);
|
||||||
@ -732,26 +699,6 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Log reader will listen to [debugger.logLines] and will detach debugger on dispose.
|
|
||||||
set debuggerStream(IOSDeployDebugger debugger) {
|
|
||||||
// Logging is gathered from syslog on iOS 13 and earlier.
|
|
||||||
if (_majorSdkVersion < _minimumUniversalLoggingSdkVersion) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_iosDeployDebugger = debugger;
|
|
||||||
// Add the debugger logs to the controller created on initialization.
|
|
||||||
_loggingSubscriptions.add(debugger.logLines.listen(
|
|
||||||
(String line) => _linesController.add(_debuggerLineHandler(line)),
|
|
||||||
onError: _linesController.addError,
|
|
||||||
onDone: _linesController.close,
|
|
||||||
cancelOnError: true,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
IOSDeployDebugger _iosDeployDebugger;
|
|
||||||
|
|
||||||
// Strip off the logging metadata (leave the category), or just echo the line.
|
|
||||||
String _debuggerLineHandler(String line) => _debuggerLoggingRegex?.firstMatch(line)?.group(1) ?? line;
|
|
||||||
|
|
||||||
void _listenToSysLog() {
|
void _listenToSysLog() {
|
||||||
// syslog is not written on iOS 13+.
|
// syslog is not written on iOS 13+.
|
||||||
if (_majorSdkVersion >= _minimumUniversalLoggingSdkVersion) {
|
if (_majorSdkVersion >= _minimumUniversalLoggingSdkVersion) {
|
||||||
@ -811,7 +758,6 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
|||||||
loggingSubscription.cancel();
|
loggingSubscription.cancel();
|
||||||
}
|
}
|
||||||
_idevicesyslogProcess?.kill();
|
_idevicesyslogProcess?.kill();
|
||||||
_iosDeployDebugger?.detach();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,29 +81,6 @@ class FallbackDiscovery {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
final Uri result = await _protocolDiscovery.uri;
|
|
||||||
if (result != null) {
|
|
||||||
UsageEvent(
|
|
||||||
_kEventName,
|
|
||||||
'log-success',
|
|
||||||
flutterUsage: _flutterUsage,
|
|
||||||
).send();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} on ArgumentError {
|
|
||||||
// In the event of an invalid InternetAddress, this code attempts to catch
|
|
||||||
// an ArgumentError from protocol_discovery.dart
|
|
||||||
} on Exception catch (err) {
|
|
||||||
_logger.printTrace(err.toString());
|
|
||||||
}
|
|
||||||
_logger.printTrace('Failed to connect with log scanning, falling back to mDNS');
|
|
||||||
UsageEvent(
|
|
||||||
_kEventName,
|
|
||||||
'log-failure',
|
|
||||||
flutterUsage: _flutterUsage,
|
|
||||||
).send();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Uri result = await _mDnsObservatoryDiscovery.getObservatoryUri(
|
final Uri result = await _mDnsObservatoryDiscovery.getObservatoryUri(
|
||||||
packageId,
|
packageId,
|
||||||
@ -122,12 +99,35 @@ class FallbackDiscovery {
|
|||||||
} on Exception catch (err) {
|
} on Exception catch (err) {
|
||||||
_logger.printTrace(err.toString());
|
_logger.printTrace(err.toString());
|
||||||
}
|
}
|
||||||
_logger.printTrace('Failed to connect with mDNS');
|
_logger.printTrace('Failed to connect with mDNS, falling back to log scanning');
|
||||||
UsageEvent(
|
UsageEvent(
|
||||||
_kEventName,
|
_kEventName,
|
||||||
'mdns-failure',
|
'mdns-failure',
|
||||||
flutterUsage: _flutterUsage,
|
flutterUsage: _flutterUsage,
|
||||||
).send();
|
).send();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Uri result = await _protocolDiscovery.uri;
|
||||||
|
if (result != null) {
|
||||||
|
UsageEvent(
|
||||||
|
_kEventName,
|
||||||
|
'fallback-success',
|
||||||
|
flutterUsage: _flutterUsage,
|
||||||
|
).send();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} on ArgumentError {
|
||||||
|
// In the event of an invalid InternetAddress, this code attempts to catch
|
||||||
|
// an ArgumentError from protocol_discovery.dart
|
||||||
|
} on Exception catch (err) {
|
||||||
|
_logger.printTrace(err.toString());
|
||||||
|
}
|
||||||
|
_logger.printTrace('Failed to connect with log scanning');
|
||||||
|
UsageEvent(
|
||||||
|
_kEventName,
|
||||||
|
'fallback-failure',
|
||||||
|
flutterUsage: _flutterUsage,
|
||||||
|
).send();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ class FallbackDiscovery {
|
|||||||
assumedWsUri = Uri.parse('ws://localhost:$hostPort/ws');
|
assumedWsUri = Uri.parse('ws://localhost:$hostPort/ws');
|
||||||
} on Exception catch (err) {
|
} on Exception catch (err) {
|
||||||
_logger.printTrace(err.toString());
|
_logger.printTrace(err.toString());
|
||||||
_logger.printTrace('Failed to connect directly, falling back to log scanning');
|
_logger.printTrace('Failed to connect directly, falling back to mDNS');
|
||||||
_sendFailureEvent(err, assumedDevicePort);
|
_sendFailureEvent(err, assumedDevicePort);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,15 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:process/process.dart';
|
import 'package:process/process.dart';
|
||||||
|
|
||||||
import '../artifacts.dart';
|
import '../artifacts.dart';
|
||||||
import '../base/common.dart';
|
|
||||||
import '../base/io.dart';
|
|
||||||
import '../base/logger.dart';
|
import '../base/logger.dart';
|
||||||
import '../base/platform.dart';
|
import '../base/platform.dart';
|
||||||
import '../base/process.dart';
|
import '../base/process.dart';
|
||||||
import '../build_info.dart';
|
import '../build_info.dart';
|
||||||
import '../cache.dart';
|
import '../cache.dart';
|
||||||
import '../convert.dart';
|
|
||||||
import 'code_signing.dart';
|
import 'code_signing.dart';
|
||||||
import 'devices.dart';
|
import 'devices.dart';
|
||||||
|
|
||||||
@ -112,47 +107,10 @@ class IOSDeploy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns [IOSDeployDebugger] wrapping attached debugger logic.
|
|
||||||
///
|
|
||||||
/// This method does not install the app. Call [IOSDeployDebugger.launchAndAttach()]
|
|
||||||
/// to install and attach the debugger to the specified app bundle.
|
|
||||||
IOSDeployDebugger prepareDebuggerForLaunch({
|
|
||||||
@required String deviceId,
|
|
||||||
@required String bundlePath,
|
|
||||||
@required List<String> launchArguments,
|
|
||||||
@required IOSDeviceInterface interfaceType,
|
|
||||||
}) {
|
|
||||||
// Interactive debug session to support sending the lldb detach command.
|
|
||||||
final List<String> launchCommand = <String>[
|
|
||||||
'script',
|
|
||||||
'-t',
|
|
||||||
'0',
|
|
||||||
'/dev/null',
|
|
||||||
_binaryPath,
|
|
||||||
'--id',
|
|
||||||
deviceId,
|
|
||||||
'--bundle',
|
|
||||||
bundlePath,
|
|
||||||
'--debug',
|
|
||||||
if (interfaceType != IOSDeviceInterface.network)
|
|
||||||
'--no-wifi',
|
|
||||||
if (launchArguments.isNotEmpty) ...<String>[
|
|
||||||
'--args',
|
|
||||||
launchArguments.join(' '),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
return IOSDeployDebugger(
|
|
||||||
launchCommand: launchCommand,
|
|
||||||
logger: _logger,
|
|
||||||
processUtils: _processUtils,
|
|
||||||
iosDeployEnv: iosDeployEnv,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Installs and then runs the specified app bundle.
|
/// Installs and then runs the specified app bundle.
|
||||||
///
|
///
|
||||||
/// Uses ios-deploy and returns the exit code.
|
/// Uses ios-deploy and returns the exit code.
|
||||||
Future<int> launchApp({
|
Future<int> runApp({
|
||||||
@required String deviceId,
|
@required String deviceId,
|
||||||
@required String bundlePath,
|
@required String bundlePath,
|
||||||
@required List<String> launchArguments,
|
@required List<String> launchArguments,
|
||||||
@ -211,202 +169,30 @@ class IOSDeploy {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _monitorFailure(String stdout) => _monitorIOSDeployFailure(stdout, _logger);
|
// Maps stdout line stream. Must return original line.
|
||||||
}
|
String _monitorFailure(String stdout) {
|
||||||
|
// Installation issues.
|
||||||
/// lldb attach state flow.
|
if (stdout.contains(noProvisioningProfileErrorOne) || stdout.contains(noProvisioningProfileErrorTwo)) {
|
||||||
enum _IOSDeployDebuggerState {
|
_logger.printError(noProvisioningProfileInstruction, emphasis: true);
|
||||||
detached,
|
|
||||||
launching,
|
|
||||||
attached,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper to launch app and attach the debugger with ios-deploy.
|
|
||||||
class IOSDeployDebugger {
|
|
||||||
IOSDeployDebugger({
|
|
||||||
@required Logger logger,
|
|
||||||
@required ProcessUtils processUtils,
|
|
||||||
@required List<String> launchCommand,
|
|
||||||
@required Map<String, String> iosDeployEnv,
|
|
||||||
}) : _processUtils = processUtils,
|
|
||||||
_logger = logger,
|
|
||||||
_launchCommand = launchCommand,
|
|
||||||
_iosDeployEnv = iosDeployEnv,
|
|
||||||
_debuggerState = _IOSDeployDebuggerState.detached;
|
|
||||||
|
|
||||||
/// Create a [IOSDeployDebugger] for testing.
|
|
||||||
///
|
|
||||||
/// Sets the command to "ios-deploy" and environment to an empty map.
|
|
||||||
@visibleForTesting
|
|
||||||
factory IOSDeployDebugger.test({
|
|
||||||
@required ProcessManager processManager,
|
|
||||||
Logger logger,
|
|
||||||
}) {
|
|
||||||
final Logger debugLogger = logger ?? BufferLogger.test();
|
|
||||||
return IOSDeployDebugger(
|
|
||||||
logger: debugLogger,
|
|
||||||
processUtils: ProcessUtils(logger: debugLogger, processManager: processManager),
|
|
||||||
launchCommand: <String>['ios-deploy'],
|
|
||||||
iosDeployEnv: <String, String>{},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Logger _logger;
|
|
||||||
final ProcessUtils _processUtils;
|
|
||||||
final List<String> _launchCommand;
|
|
||||||
final Map<String, String> _iosDeployEnv;
|
|
||||||
|
|
||||||
Process _iosDeployProcess;
|
|
||||||
|
|
||||||
Stream<String> get logLines => _debuggerOutput.stream;
|
|
||||||
final StreamController<String> _debuggerOutput = StreamController<String>.broadcast();
|
|
||||||
|
|
||||||
bool get debuggerAttached => _debuggerState == _IOSDeployDebuggerState.attached;
|
|
||||||
_IOSDeployDebuggerState _debuggerState;
|
|
||||||
|
|
||||||
// (lldb) run
|
|
||||||
// https://github.com/ios-control/ios-deploy/blob/1.11.2-beta.1/src/ios-deploy/ios-deploy.m#L51
|
|
||||||
static final RegExp _lldbRun = RegExp(r'\(lldb\)\s*run');
|
|
||||||
|
|
||||||
/// Launch the app on the device, and attach the debugger.
|
|
||||||
///
|
|
||||||
/// Returns whether or not the debugger successfully attached.
|
|
||||||
Future<bool> launchAndAttach() async {
|
|
||||||
// Return when the debugger attaches, or the ios-deploy process exits.
|
|
||||||
final Completer<bool> debuggerCompleter = Completer<bool>();
|
|
||||||
try {
|
|
||||||
_iosDeployProcess = await _processUtils.start(
|
|
||||||
_launchCommand,
|
|
||||||
environment: _iosDeployEnv,
|
|
||||||
);
|
|
||||||
String lastLineFromDebugger;
|
|
||||||
final StreamSubscription<String> stdoutSubscription = _iosDeployProcess.stdout
|
|
||||||
.transform<String>(utf8.decoder)
|
|
||||||
.transform<String>(const LineSplitter())
|
|
||||||
.listen((String line) {
|
|
||||||
_monitorIOSDeployFailure(line, _logger);
|
|
||||||
|
|
||||||
// (lldb) run
|
|
||||||
// success
|
|
||||||
// 2020-09-15 13:42:25.185474-0700 Runner[477:181141] flutter: Observatory listening on http://127.0.0.1:57782/
|
|
||||||
if (_lldbRun.hasMatch(line)) {
|
|
||||||
_logger.printTrace(line);
|
|
||||||
_debuggerState = _IOSDeployDebuggerState.launching;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Next line after "run" must be "success", or the attach failed.
|
|
||||||
// Example: "error: process launch failed"
|
|
||||||
if (_debuggerState == _IOSDeployDebuggerState.launching) {
|
|
||||||
_logger.printTrace(line);
|
|
||||||
final bool attachSuccess = line == 'success';
|
|
||||||
_debuggerState = attachSuccess ? _IOSDeployDebuggerState.attached : _IOSDeployDebuggerState.detached;
|
|
||||||
if (!debuggerCompleter.isCompleted) {
|
|
||||||
debuggerCompleter.complete(attachSuccess);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (line.contains('PROCESS_STOPPED') ||
|
|
||||||
line.contains('PROCESS_EXITED')) {
|
|
||||||
// The app exited or crashed, so stop echoing the output.
|
|
||||||
// Don't pass any further ios-deploy debugging messages to the log reader after it exits.
|
|
||||||
_debuggerState = _IOSDeployDebuggerState.detached;
|
|
||||||
_logger.printTrace(line);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_debuggerState != _IOSDeployDebuggerState.attached) {
|
|
||||||
_logger.printTrace(line);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (lastLineFromDebugger != null && lastLineFromDebugger.isNotEmpty && line.isEmpty) {
|
|
||||||
// The lldb console stream from ios-deploy is separated lines by an extra \r\n.
|
|
||||||
// To avoid all lines being double spaced, if the last line from the
|
|
||||||
// debugger was not an empty line, skip this empty line.
|
|
||||||
// This will still cause "legit" logged newlines to be doubled...
|
|
||||||
} else {
|
|
||||||
_debuggerOutput.add(line);
|
|
||||||
}
|
|
||||||
lastLineFromDebugger = line;
|
|
||||||
});
|
|
||||||
final StreamSubscription<String> stderrSubscription = _iosDeployProcess.stderr
|
|
||||||
.transform<String>(utf8.decoder)
|
|
||||||
.transform<String>(const LineSplitter())
|
|
||||||
.listen((String line) {
|
|
||||||
_monitorIOSDeployFailure(line, _logger);
|
|
||||||
_logger.printTrace(line);
|
|
||||||
});
|
|
||||||
unawaited(_iosDeployProcess.exitCode.then((int status) {
|
|
||||||
_logger.printTrace('ios-deploy exited with code $exitCode');
|
|
||||||
_debuggerState = _IOSDeployDebuggerState.detached;
|
|
||||||
unawaited(stdoutSubscription.cancel());
|
|
||||||
unawaited(stderrSubscription.cancel());
|
|
||||||
}).whenComplete(() async {
|
|
||||||
if (_debuggerOutput.hasListener) {
|
|
||||||
// Tell listeners the process died.
|
|
||||||
await _debuggerOutput.close();
|
|
||||||
}
|
|
||||||
if (!debuggerCompleter.isCompleted) {
|
|
||||||
debuggerCompleter.complete(false);
|
|
||||||
}
|
|
||||||
_iosDeployProcess = null;
|
|
||||||
}));
|
|
||||||
} on ProcessException catch (exception, stackTrace) {
|
|
||||||
_logger.printTrace('ios-deploy failed: $exception');
|
|
||||||
_debuggerState = _IOSDeployDebuggerState.detached;
|
|
||||||
_debuggerOutput.addError(exception, stackTrace);
|
|
||||||
} on ArgumentError catch (exception, stackTrace) {
|
|
||||||
_logger.printTrace('ios-deploy failed: $exception');
|
|
||||||
_debuggerState = _IOSDeployDebuggerState.detached;
|
|
||||||
_debuggerOutput.addError(exception, stackTrace);
|
|
||||||
}
|
|
||||||
// Wait until the debugger attaches, or the attempt fails.
|
|
||||||
return debuggerCompleter.future;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool exit() {
|
|
||||||
final bool success = (_iosDeployProcess == null) || _iosDeployProcess.kill();
|
|
||||||
_iosDeployProcess = null;
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
void detach() {
|
|
||||||
if (!debuggerAttached) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Detach lldb from the app process.
|
|
||||||
_iosDeployProcess?.stdin?.writeln('process detach');
|
|
||||||
_debuggerState = _IOSDeployDebuggerState.detached;
|
|
||||||
} on SocketException catch (error) {
|
|
||||||
// Best effort, try to detach, but maybe the app already exited or already detached.
|
|
||||||
_logger.printTrace('Could not detach from debugger: $error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maps stdout line stream. Must return original line.
|
|
||||||
String _monitorIOSDeployFailure(String stdout, Logger logger) {
|
|
||||||
// Installation issues.
|
|
||||||
if (stdout.contains(noProvisioningProfileErrorOne) || stdout.contains(noProvisioningProfileErrorTwo)) {
|
|
||||||
logger.printError(noProvisioningProfileInstruction, emphasis: true);
|
|
||||||
|
|
||||||
// Launch issues.
|
// Launch issues.
|
||||||
} else if (stdout.contains(deviceLockedError)) {
|
} else if (stdout.contains(deviceLockedError)) {
|
||||||
logger.printError('''
|
_logger.printError('''
|
||||||
═══════════════════════════════════════════════════════════════════════════════════
|
═══════════════════════════════════════════════════════════════════════════════════
|
||||||
Your device is locked. Unlock your device first before running.
|
Your device is locked. Unlock your device first before running.
|
||||||
═══════════════════════════════════════════════════════════════════════════════════''',
|
═══════════════════════════════════════════════════════════════════════════════════''',
|
||||||
emphasis: true);
|
emphasis: true);
|
||||||
} else if (stdout.contains(unknownAppLaunchError)) {
|
} else if (stdout.contains(unknownAppLaunchError)) {
|
||||||
logger.printError('''
|
_logger.printError('''
|
||||||
═══════════════════════════════════════════════════════════════════════════════════
|
═══════════════════════════════════════════════════════════════════════════════════
|
||||||
Error launching app. Try launching from within Xcode via:
|
Error launching app. Try launching from within Xcode via:
|
||||||
open ios/Runner.xcworkspace
|
open ios/Runner.xcworkspace
|
||||||
|
|
||||||
Your Xcode version may be too old for your iOS version.
|
Your Xcode version may be too old for your iOS version.
|
||||||
═══════════════════════════════════════════════════════════════════════════════════''',
|
═══════════════════════════════════════════════════════════════════════════════════''',
|
||||||
emphasis: true);
|
emphasis: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return stdout;
|
return stdout;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class ProtocolDiscovery {
|
|||||||
factory ProtocolDiscovery.observatory(
|
factory ProtocolDiscovery.observatory(
|
||||||
DeviceLogReader logReader, {
|
DeviceLogReader logReader, {
|
||||||
DevicePortForwarder portForwarder,
|
DevicePortForwarder portForwarder,
|
||||||
Duration throttleDuration,
|
Duration throttleDuration = const Duration(milliseconds: 200),
|
||||||
Duration throttleTimeout,
|
Duration throttleTimeout,
|
||||||
@required int hostPort,
|
@required int hostPort,
|
||||||
@required int devicePort,
|
@required int devicePort,
|
||||||
@ -45,7 +45,7 @@ class ProtocolDiscovery {
|
|||||||
logReader,
|
logReader,
|
||||||
kObservatoryService,
|
kObservatoryService,
|
||||||
portForwarder: portForwarder,
|
portForwarder: portForwarder,
|
||||||
throttleDuration: throttleDuration ?? const Duration(milliseconds: 200),
|
throttleDuration: throttleDuration,
|
||||||
throttleTimeout: throttleTimeout,
|
throttleTimeout: throttleTimeout,
|
||||||
hostPort: hostPort,
|
hostPort: hostPort,
|
||||||
devicePort: devicePort,
|
devicePort: devicePort,
|
||||||
@ -225,7 +225,7 @@ class _BufferedStreamController<T> {
|
|||||||
///
|
///
|
||||||
/// For example, consider a `waitDuration` of `10ms`, and list of event names
|
/// For example, consider a `waitDuration` of `10ms`, and list of event names
|
||||||
/// and arrival times: `a (0ms), b (5ms), c (11ms), d (21ms)`.
|
/// and arrival times: `a (0ms), b (5ms), c (11ms), d (21ms)`.
|
||||||
/// The events `a`, `c`, and `d` will be produced as a result.
|
/// The events `c` and `d` will be produced as a result.
|
||||||
StreamTransformer<S, S> _throttle<S>({
|
StreamTransformer<S, S> _throttle<S>({
|
||||||
@required Duration waitDuration,
|
@required Duration waitDuration,
|
||||||
}) {
|
}) {
|
||||||
@ -240,13 +240,10 @@ StreamTransformer<S, S> _throttle<S>({
|
|||||||
handleData: (S value, EventSink<S> sink) {
|
handleData: (S value, EventSink<S> sink) {
|
||||||
latestLine = value;
|
latestLine = value;
|
||||||
|
|
||||||
final bool isFirstMessage = lastExecution == null;
|
|
||||||
final int currentTime = DateTime.now().millisecondsSinceEpoch;
|
final int currentTime = DateTime.now().millisecondsSinceEpoch;
|
||||||
lastExecution ??= currentTime;
|
lastExecution ??= currentTime;
|
||||||
final int remainingTime = currentTime - lastExecution;
|
final int remainingTime = currentTime - lastExecution;
|
||||||
|
final int nextExecutionTime = remainingTime > waitDuration.inMilliseconds
|
||||||
// Always send the first event immediately.
|
|
||||||
final int nextExecutionTime = isFirstMessage || remainingTime > waitDuration.inMilliseconds
|
|
||||||
? 0
|
? 0
|
||||||
: waitDuration.inMilliseconds - remainingTime;
|
: waitDuration.inMilliseconds - remainingTime;
|
||||||
|
|
||||||
|
@ -2,15 +2,10 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter_tools/src/artifacts.dart';
|
import 'package:flutter_tools/src/artifacts.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/ios/devices.dart';
|
|
||||||
import 'package:flutter_tools/src/ios/ios_deploy.dart';
|
import 'package:flutter_tools/src/ios/ios_deploy.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:process/process.dart';
|
import 'package:process/process.dart';
|
||||||
@ -26,230 +21,50 @@ void main () {
|
|||||||
expect(environment['PATH'], startsWith('/usr/bin'));
|
expect(environment['PATH'], startsWith('/usr/bin'));
|
||||||
});
|
});
|
||||||
|
|
||||||
group('IOSDeploy.prepareDebuggerForLaunch', () {
|
testWithoutContext('IOSDeploy.uninstallApp calls ios-deploy with correct arguments and returns 0 on success', () async {
|
||||||
testWithoutContext('calls ios-deploy with correct arguments and returns when debugger attaches', () async {
|
const String deviceId = '123';
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
const String bundleId = 'com.example.app';
|
||||||
FakeCommand(
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
command: <String>[
|
const FakeCommand(command: <String>[
|
||||||
'script',
|
'ios-deploy',
|
||||||
'-t',
|
'--id',
|
||||||
'0',
|
deviceId,
|
||||||
'/dev/null',
|
'--uninstall_only',
|
||||||
'ios-deploy',
|
'--bundle_id',
|
||||||
'--id',
|
bundleId,
|
||||||
'123',
|
])
|
||||||
'--bundle',
|
]);
|
||||||
'/',
|
final IOSDeploy iosDeploy = setUpIOSDeploy(processManager);
|
||||||
'--debug',
|
final int exitCode = await iosDeploy.uninstallApp(
|
||||||
'--args',
|
deviceId: deviceId,
|
||||||
<String>[
|
bundleId: bundleId,
|
||||||
'--enable-dart-profiling',
|
);
|
||||||
].join(' '),
|
|
||||||
], environment: const <String, String>{
|
|
||||||
'PATH': '/usr/bin:/usr/local/bin:/usr/bin',
|
|
||||||
'DYLD_LIBRARY_PATH': '/path/to/libs',
|
|
||||||
},
|
|
||||||
stdout: '(lldb) run\nsuccess\nDid finish launching.',
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
final IOSDeploy iosDeploy = setUpIOSDeploy(processManager);
|
|
||||||
final IOSDeployDebugger iosDeployDebugger = iosDeploy.prepareDebuggerForLaunch(
|
|
||||||
deviceId: '123',
|
|
||||||
bundlePath: '/',
|
|
||||||
launchArguments: <String>['--enable-dart-profiling'],
|
|
||||||
interfaceType: IOSDeviceInterface.network,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(await iosDeployDebugger.launchAndAttach(), isTrue);
|
expect(exitCode, 0);
|
||||||
expect(await iosDeployDebugger.logLines.toList(), <String>['Did finish launching.']);
|
expect(processManager.hasRemainingExpectations, false);
|
||||||
expect(processManager.hasRemainingExpectations, false);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
group('IOSDeployDebugger', () {
|
testWithoutContext('IOSDeploy.uninstallApp returns non-zero exit code when ios-deploy does the same', () async {
|
||||||
group('launch', () {
|
const String deviceId = '123';
|
||||||
BufferLogger logger;
|
const String bundleId = 'com.example.app';
|
||||||
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
|
const FakeCommand(command: <String>[
|
||||||
|
'ios-deploy',
|
||||||
|
'--id',
|
||||||
|
deviceId,
|
||||||
|
'--uninstall_only',
|
||||||
|
'--bundle_id',
|
||||||
|
bundleId,
|
||||||
|
], exitCode: 1)
|
||||||
|
]);
|
||||||
|
final IOSDeploy iosDeploy = setUpIOSDeploy(processManager);
|
||||||
|
final int exitCode = await iosDeploy.uninstallApp(
|
||||||
|
deviceId: deviceId,
|
||||||
|
bundleId: bundleId,
|
||||||
|
);
|
||||||
|
|
||||||
setUp(() {
|
expect(exitCode, 1);
|
||||||
logger = BufferLogger.test();
|
expect(processManager.hasRemainingExpectations, false);
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('debugger attached', () async {
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
const FakeCommand(
|
|
||||||
command: <String>['ios-deploy'],
|
|
||||||
stdout: '(lldb) run\r\nsuccess\r\nsuccess\r\nLog on attach1\r\n\r\nLog on attach2\r\n\r\n\r\n\r\nPROCESS_STOPPED\r\nLog after process exit',
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
|
||||||
processManager: processManager,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
final List<String> receivedLogLines = <String>[];
|
|
||||||
final Stream<String> logLines = iosDeployDebugger.logLines
|
|
||||||
..listen(receivedLogLines.add);
|
|
||||||
|
|
||||||
expect(await iosDeployDebugger.launchAndAttach(), isTrue);
|
|
||||||
await logLines.toList();
|
|
||||||
expect(receivedLogLines, <String>[
|
|
||||||
'success', // ignore first "success" from lldb, but log subsequent ones from real logging.
|
|
||||||
'Log on attach1',
|
|
||||||
'Log on attach2',
|
|
||||||
'', '']);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('attach failed', () async {
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
const FakeCommand(
|
|
||||||
command: <String>['ios-deploy'],
|
|
||||||
// A success after an error should never happen, but test that we're handling random "successes" anyway.
|
|
||||||
stdout: '(lldb) run\r\nerror: process launch failed\r\nsuccess\r\nLog on attach1',
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
|
||||||
processManager: processManager,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
final List<String> receivedLogLines = <String>[];
|
|
||||||
final Stream<String> logLines = iosDeployDebugger.logLines
|
|
||||||
..listen(receivedLogLines.add);
|
|
||||||
|
|
||||||
expect(await iosDeployDebugger.launchAndAttach(), isFalse);
|
|
||||||
await logLines.toList();
|
|
||||||
// Debugger lines are double spaced, separated by an extra \r\n. Skip the extra lines.
|
|
||||||
// Still include empty lines other than the extra added newlines.
|
|
||||||
expect(receivedLogLines, isEmpty);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('no provisioning profile 1, stdout', () async {
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
const FakeCommand(
|
|
||||||
command: <String>['ios-deploy'],
|
|
||||||
stdout: 'Error 0xe8008015',
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
|
||||||
processManager: processManager,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
|
|
||||||
await iosDeployDebugger.launchAndAttach();
|
|
||||||
expect(logger.errorText, contains('No Provisioning Profile was found'));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('no provisioning profile 2, stderr', () async {
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
const FakeCommand(
|
|
||||||
command: <String>['ios-deploy'],
|
|
||||||
stderr: 'Error 0xe8000067',
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
|
||||||
processManager: processManager,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
await iosDeployDebugger.launchAndAttach();
|
|
||||||
expect(logger.errorText, contains('No Provisioning Profile was found'));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('device locked', () async {
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
const FakeCommand(
|
|
||||||
command: <String>['ios-deploy'],
|
|
||||||
stdout: 'e80000e2',
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
|
||||||
processManager: processManager,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
await iosDeployDebugger.launchAndAttach();
|
|
||||||
expect(logger.errorText, contains('Your device is locked.'));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('device locked', () async {
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
const FakeCommand(
|
|
||||||
command: <String>['ios-deploy'],
|
|
||||||
stdout: 'Error 0xe8000022',
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
|
||||||
processManager: processManager,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
await iosDeployDebugger.launchAndAttach();
|
|
||||||
expect(logger.errorText, contains('Try launching from within Xcode'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('detach', () async {
|
|
||||||
final StreamController<List<int>> stdin = StreamController<List<int>>();
|
|
||||||
final Stream<String> stdinStream = stdin.stream.transform<String>(const Utf8Decoder());
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
FakeCommand(
|
|
||||||
command: const <String>[
|
|
||||||
'ios-deploy',
|
|
||||||
],
|
|
||||||
stdout: '(lldb) run\nsuccess',
|
|
||||||
stdin: IOSink(stdin.sink),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
|
||||||
processManager: processManager,
|
|
||||||
);
|
|
||||||
await iosDeployDebugger.launchAndAttach();
|
|
||||||
iosDeployDebugger.detach();
|
|
||||||
expect(await stdinStream.first, 'process detach');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('IOSDeploy.uninstallApp', () {
|
|
||||||
testWithoutContext('calls ios-deploy with correct arguments and returns 0 on success', () async {
|
|
||||||
const String deviceId = '123';
|
|
||||||
const String bundleId = 'com.example.app';
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
const FakeCommand(command: <String>[
|
|
||||||
'ios-deploy',
|
|
||||||
'--id',
|
|
||||||
deviceId,
|
|
||||||
'--uninstall_only',
|
|
||||||
'--bundle_id',
|
|
||||||
bundleId,
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
final IOSDeploy iosDeploy = setUpIOSDeploy(processManager);
|
|
||||||
final int exitCode = await iosDeploy.uninstallApp(
|
|
||||||
deviceId: deviceId,
|
|
||||||
bundleId: bundleId,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(exitCode, 0);
|
|
||||||
expect(processManager.hasRemainingExpectations, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('returns non-zero exit code when ios-deploy does the same', () async {
|
|
||||||
const String deviceId = '123';
|
|
||||||
const String bundleId = 'com.example.app';
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
const FakeCommand(command: <String>[
|
|
||||||
'ios-deploy',
|
|
||||||
'--id',
|
|
||||||
deviceId,
|
|
||||||
'--uninstall_only',
|
|
||||||
'--bundle_id',
|
|
||||||
bundleId,
|
|
||||||
], exitCode: 1)
|
|
||||||
]);
|
|
||||||
final IOSDeploy iosDeploy = setUpIOSDeploy(processManager);
|
|
||||||
final int exitCode = await iosDeploy.uninstallApp(
|
|
||||||
deviceId: deviceId,
|
|
||||||
bundleId: bundleId,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(exitCode, 1);
|
|
||||||
expect(processManager.hasRemainingExpectations, false);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import 'package:flutter_tools/src/build_info.dart';
|
|||||||
import 'package:flutter_tools/src/convert.dart';
|
import 'package:flutter_tools/src/convert.dart';
|
||||||
import 'package:flutter_tools/src/device.dart';
|
import 'package:flutter_tools/src/device.dart';
|
||||||
import 'package:flutter_tools/src/ios/devices.dart';
|
import 'package:flutter_tools/src/ios/devices.dart';
|
||||||
import 'package:flutter_tools/src/ios/ios_deploy.dart';
|
|
||||||
import 'package:flutter_tools/src/ios/mac.dart';
|
import 'package:flutter_tools/src/ios/mac.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:vm_service/vm_service.dart';
|
import 'package:vm_service/vm_service.dart';
|
||||||
@ -31,299 +30,167 @@ void main() {
|
|||||||
artifacts = MockArtifacts();
|
artifacts = MockArtifacts();
|
||||||
logger = BufferLogger.test();
|
logger = BufferLogger.test();
|
||||||
when(artifacts.getArtifactPath(Artifact.idevicesyslog, platform: TargetPlatform.ios))
|
when(artifacts.getArtifactPath(Artifact.idevicesyslog, platform: TargetPlatform.ios))
|
||||||
.thenReturn('idevice-syslog');
|
.thenReturn('idevice-syslog');
|
||||||
});
|
});
|
||||||
|
|
||||||
group('syslog stream', () {
|
testWithoutContext('decodeSyslog decodes a syslog-encoded line', () {
|
||||||
testWithoutContext('decodeSyslog decodes a syslog-encoded line', () {
|
final String decoded = decodeSyslog(
|
||||||
final String decoded = decodeSyslog(
|
r'I \M-b\M^]\M-$\M-o\M-8\M^O syslog \M-B\M-/\'
|
||||||
r'I \M-b\M^]\M-$\M-o\M-8\M^O syslog \M-B\M-/\'
|
r'134_(\M-c\M^C\M^D)_/\M-B\M-/ \M-l\M^F\240!');
|
||||||
r'134_(\M-c\M^C\M^D)_/\M-B\M-/ \M-l\M^F\240!');
|
|
||||||
|
|
||||||
expect(decoded, r'I ❤️ syslog ¯\_(ツ)_/¯ 솠!');
|
expect(decoded, r'I ❤️ syslog ¯\_(ツ)_/¯ 솠!');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('decodeSyslog passes through un-decodeable lines as-is', () {
|
testWithoutContext('decodeSyslog passes through un-decodeable lines as-is', () {
|
||||||
final String decoded = decodeSyslog(r'I \M-b\M^O syslog!');
|
final String decoded = decodeSyslog(r'I \M-b\M^O syslog!');
|
||||||
|
|
||||||
expect(decoded, r'I \M-b\M^O syslog!');
|
expect(decoded, r'I \M-b\M^O syslog!');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('IOSDeviceLogReader suppresses non-Flutter lines from output with syslog', () async {
|
testWithoutContext('IOSDeviceLogReader suppresses non-Flutter lines from output with syslog', () async {
|
||||||
processManager.addCommand(
|
processManager.addCommand(
|
||||||
const FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>[
|
command: <String>[
|
||||||
'idevice-syslog', '-u', '1234',
|
'idevice-syslog', '-u', '1234',
|
||||||
],
|
],
|
||||||
stdout: '''
|
stdout: '''
|
||||||
Runner(Flutter)[297] <Notice>: A is for ari
|
Runner(Flutter)[297] <Notice>: A is for ari
|
||||||
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestaltSupport.m:153: pid 123 (Runner) does not have sandbox access for frZQaeyWLUvLjeuEK43hmg and IS NOT appropriately entitled
|
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestaltSupport.m:153: pid 123 (Runner) does not have sandbox access for frZQaeyWLUvLjeuEK43hmg and IS NOT appropriately entitled
|
||||||
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestalt.c:550: no access to InverseDeviceID (see <rdar://problem/11744455>)
|
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt MobileGestalt.c:550: no access to InverseDeviceID (see <rdar://problem/11744455>)
|
||||||
Runner(Flutter)[297] <Notice>: I is for ichigo
|
Runner(Flutter)[297] <Notice>: I is for ichigo
|
||||||
Runner(UIKit)[297] <Notice>: E is for enpitsu"
|
Runner(UIKit)[297] <Notice>: E is for enpitsu"
|
||||||
'''
|
'''
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final DeviceLogReader logReader = IOSDeviceLogReader.test(
|
final DeviceLogReader logReader = IOSDeviceLogReader.test(
|
||||||
iMobileDevice: IMobileDevice(
|
iMobileDevice: IMobileDevice(
|
||||||
artifacts: artifacts,
|
artifacts: artifacts,
|
||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
cache: fakeCache,
|
cache: fakeCache,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final List<String> lines = await logReader.logLines.toList();
|
final List<String> lines = await logReader.logLines.toList();
|
||||||
|
|
||||||
expect(lines, <String>['A is for ari', 'I is for ichigo']);
|
expect(lines, <String>['A is for ari', 'I is for ichigo']);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('IOSDeviceLogReader includes multi-line Flutter logs in the output with syslog', () async {
|
testWithoutContext('IOSDeviceLogReader includes multi-line Flutter logs in the output with syslog', () async {
|
||||||
processManager.addCommand(
|
processManager.addCommand(
|
||||||
const FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>[
|
command: <String>[
|
||||||
'idevice-syslog', '-u', '1234',
|
'idevice-syslog', '-u', '1234',
|
||||||
],
|
],
|
||||||
stdout: '''
|
stdout: '''
|
||||||
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
||||||
with another Flutter message following it.
|
with another Flutter message following it.
|
||||||
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
||||||
with a non-Flutter log message following it.
|
with a non-Flutter log message following it.
|
||||||
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
|
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
|
||||||
'''
|
'''
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final DeviceLogReader logReader = IOSDeviceLogReader.test(
|
final DeviceLogReader logReader = IOSDeviceLogReader.test(
|
||||||
iMobileDevice: IMobileDevice(
|
iMobileDevice: IMobileDevice(
|
||||||
artifacts: artifacts,
|
artifacts: artifacts,
|
||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
cache: fakeCache,
|
cache: fakeCache,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final List<String> lines = await logReader.logLines.toList();
|
final List<String> lines = await logReader.logLines.toList();
|
||||||
|
|
||||||
expect(lines, <String>[
|
expect(lines, <String>[
|
||||||
'This is a multi-line message,',
|
'This is a multi-line message,',
|
||||||
' with another Flutter message following it.',
|
' with another Flutter message following it.',
|
||||||
'This is a multi-line message,',
|
'This is a multi-line message,',
|
||||||
' with a non-Flutter log message following it.',
|
' with a non-Flutter log message following it.',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('includes multi-line Flutter logs in the output', () async {
|
testWithoutContext('includes multi-line Flutter logs in the output', () async {
|
||||||
processManager.addCommand(
|
processManager.addCommand(
|
||||||
const FakeCommand(
|
const FakeCommand(
|
||||||
command: <String>[
|
command: <String>[
|
||||||
'idevice-syslog', '-u', '1234',
|
'idevice-syslog', '-u', '1234',
|
||||||
],
|
],
|
||||||
stdout: '''
|
stdout: '''
|
||||||
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
||||||
with another Flutter message following it.
|
with another Flutter message following it.
|
||||||
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
Runner(Flutter)[297] <Notice>: This is a multi-line message,
|
||||||
with a non-Flutter log message following it.
|
with a non-Flutter log message following it.
|
||||||
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
|
Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
|
||||||
''',
|
''',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final DeviceLogReader logReader = IOSDeviceLogReader.test(
|
final DeviceLogReader logReader = IOSDeviceLogReader.test(
|
||||||
iMobileDevice: IMobileDevice(
|
iMobileDevice: IMobileDevice(
|
||||||
artifacts: artifacts,
|
artifacts: artifacts,
|
||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
cache: fakeCache,
|
cache: fakeCache,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final List<String> lines = await logReader.logLines.toList();
|
final List<String> lines = await logReader.logLines.toList();
|
||||||
|
|
||||||
expect(lines, <String>[
|
expect(lines, <String>[
|
||||||
'This is a multi-line message,',
|
'This is a multi-line message,',
|
||||||
' with another Flutter message following it.',
|
' with another Flutter message following it.',
|
||||||
'This is a multi-line message,',
|
'This is a multi-line message,',
|
||||||
' with a non-Flutter log message following it.',
|
' with a non-Flutter log message following it.',
|
||||||
]);
|
]);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
group('VM service', () {
|
testWithoutContext('IOSDeviceLogReader can listen to VM Service logs', () async {
|
||||||
testWithoutContext('IOSDeviceLogReader can listen to VM Service logs', () async {
|
final MockVmService vmService = MockVmService();
|
||||||
final MockVmService vmService = MockVmService();
|
final DeviceLogReader logReader = IOSDeviceLogReader.test(
|
||||||
final DeviceLogReader logReader = IOSDeviceLogReader.test(
|
useSyslog: false,
|
||||||
useSyslog: false,
|
iMobileDevice: IMobileDevice(
|
||||||
iMobileDevice: IMobileDevice(
|
artifacts: artifacts,
|
||||||
artifacts: artifacts,
|
processManager: processManager,
|
||||||
processManager: processManager,
|
cache: fakeCache,
|
||||||
cache: fakeCache,
|
logger: logger,
|
||||||
logger: logger,
|
),
|
||||||
),
|
);
|
||||||
);
|
final StreamController<Event> stdoutController = StreamController<Event>();
|
||||||
final StreamController<Event> stdoutController = StreamController<Event>();
|
final StreamController<Event> stderController = StreamController<Event>();
|
||||||
final StreamController<Event> stderController = StreamController<Event>();
|
final Completer<Success> stdoutCompleter = Completer<Success>();
|
||||||
final Completer<Success> stdoutCompleter = Completer<Success>();
|
final Completer<Success> stderrCompleter = Completer<Success>();
|
||||||
final Completer<Success> stderrCompleter = Completer<Success>();
|
when(vmService.streamListen('Stdout')).thenAnswer((Invocation invocation) {
|
||||||
when(vmService.streamListen('Stdout')).thenAnswer((Invocation invocation) {
|
return stdoutCompleter.future;
|
||||||
return stdoutCompleter.future;
|
|
||||||
});
|
|
||||||
when(vmService.streamListen('Stderr')).thenAnswer((Invocation invocation) {
|
|
||||||
return stderrCompleter.future;
|
|
||||||
});
|
|
||||||
when(vmService.onStdoutEvent).thenAnswer((Invocation invocation) {
|
|
||||||
return stdoutController.stream;
|
|
||||||
});
|
|
||||||
when(vmService.onStderrEvent).thenAnswer((Invocation invocation) {
|
|
||||||
return stderController.stream;
|
|
||||||
});
|
|
||||||
logReader.connectedVMService = vmService;
|
|
||||||
|
|
||||||
stdoutCompleter.complete(Success());
|
|
||||||
stderrCompleter.complete(Success());
|
|
||||||
stdoutController.add(Event(
|
|
||||||
kind: 'Stdout',
|
|
||||||
timestamp: 0,
|
|
||||||
bytes: base64.encode(utf8.encode(' This is a message ')),
|
|
||||||
));
|
|
||||||
stderController.add(Event(
|
|
||||||
kind: 'Stderr',
|
|
||||||
timestamp: 0,
|
|
||||||
bytes: base64.encode(utf8.encode(' And this is an error ')),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Wait for stream listeners to fire.
|
|
||||||
await expectLater(logReader.logLines, emitsInAnyOrder(<Matcher>[
|
|
||||||
equals(' This is a message '),
|
|
||||||
equals(' And this is an error '),
|
|
||||||
]));
|
|
||||||
});
|
});
|
||||||
|
when(vmService.streamListen('Stderr')).thenAnswer((Invocation invocation) {
|
||||||
testWithoutContext('IOSDeviceLogReader ignores VM Service logs when attached to debugger', () async {
|
return stderrCompleter.future;
|
||||||
final MockVmService vmService = MockVmService();
|
|
||||||
final IOSDeviceLogReader logReader = IOSDeviceLogReader.test(
|
|
||||||
useSyslog: false,
|
|
||||||
iMobileDevice: IMobileDevice(
|
|
||||||
artifacts: artifacts,
|
|
||||||
processManager: processManager,
|
|
||||||
cache: fakeCache,
|
|
||||||
logger: logger,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
final StreamController<Event> stdoutController = StreamController<Event>();
|
|
||||||
final StreamController<Event> stderController = StreamController<Event>();
|
|
||||||
final Completer<Success> stdoutCompleter = Completer<Success>();
|
|
||||||
final Completer<Success> stderrCompleter = Completer<Success>();
|
|
||||||
when(vmService.streamListen('Stdout')).thenAnswer((Invocation invocation) {
|
|
||||||
return stdoutCompleter.future;
|
|
||||||
});
|
|
||||||
when(vmService.streamListen('Stderr')).thenAnswer((Invocation invocation) {
|
|
||||||
return stderrCompleter.future;
|
|
||||||
});
|
|
||||||
when(vmService.onStdoutEvent).thenAnswer((Invocation invocation) {
|
|
||||||
return stdoutController.stream;
|
|
||||||
});
|
|
||||||
when(vmService.onStderrEvent).thenAnswer((Invocation invocation) {
|
|
||||||
return stderController.stream;
|
|
||||||
});
|
|
||||||
logReader.connectedVMService = vmService;
|
|
||||||
|
|
||||||
stdoutCompleter.complete(Success());
|
|
||||||
stderrCompleter.complete(Success());
|
|
||||||
stdoutController.add(Event(
|
|
||||||
kind: 'Stdout',
|
|
||||||
timestamp: 0,
|
|
||||||
bytes: base64.encode(utf8.encode(' This is a message ')),
|
|
||||||
));
|
|
||||||
stderController.add(Event(
|
|
||||||
kind: 'Stderr',
|
|
||||||
timestamp: 0,
|
|
||||||
bytes: base64.encode(utf8.encode(' And this is an error ')),
|
|
||||||
));
|
|
||||||
|
|
||||||
final MockIOSDeployDebugger iosDeployDebugger = MockIOSDeployDebugger();
|
|
||||||
when(iosDeployDebugger.debuggerAttached).thenReturn(true);
|
|
||||||
|
|
||||||
final Stream<String> debuggingLogs = Stream<String>.fromIterable(<String>[
|
|
||||||
'Message from debugger'
|
|
||||||
]);
|
|
||||||
when(iosDeployDebugger.logLines).thenAnswer((Invocation invocation) => debuggingLogs);
|
|
||||||
logReader.debuggerStream = iosDeployDebugger;
|
|
||||||
|
|
||||||
// Wait for stream listeners to fire.
|
|
||||||
await expectLater(logReader.logLines, emitsInAnyOrder(<Matcher>[
|
|
||||||
equals('Message from debugger'),
|
|
||||||
]));
|
|
||||||
});
|
});
|
||||||
});
|
when(vmService.onStdoutEvent).thenAnswer((Invocation invocation) {
|
||||||
|
return stdoutController.stream;
|
||||||
group('debugger stream', () {
|
|
||||||
testWithoutContext('IOSDeviceLogReader removes metadata prefix from lldb output', () async {
|
|
||||||
final Stream<String> debuggingLogs = Stream<String>.fromIterable(<String>[
|
|
||||||
'2020-09-15 19:15:10.931434-0700 Runner[541:226276] Did finish launching.',
|
|
||||||
'2020-09-15 19:15:10.931434-0700 Runner[541:226276] [Category] Did finish launching from logging category.',
|
|
||||||
'stderr from dart',
|
|
||||||
'',
|
|
||||||
]);
|
|
||||||
|
|
||||||
final IOSDeviceLogReader logReader = IOSDeviceLogReader.test(
|
|
||||||
iMobileDevice: IMobileDevice(
|
|
||||||
artifacts: artifacts,
|
|
||||||
processManager: processManager,
|
|
||||||
cache: fakeCache,
|
|
||||||
logger: logger,
|
|
||||||
),
|
|
||||||
useSyslog: false,
|
|
||||||
);
|
|
||||||
final MockIOSDeployDebugger iosDeployDebugger = MockIOSDeployDebugger();
|
|
||||||
when(iosDeployDebugger.logLines).thenAnswer((Invocation invocation) => debuggingLogs);
|
|
||||||
logReader.debuggerStream = iosDeployDebugger;
|
|
||||||
final Future<List<String>> logLines = logReader.logLines.toList();
|
|
||||||
|
|
||||||
expect(await logLines, <String>[
|
|
||||||
'Did finish launching.',
|
|
||||||
'[Category] Did finish launching from logging category.',
|
|
||||||
'stderr from dart',
|
|
||||||
'',
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
when(vmService.onStderrEvent).thenAnswer((Invocation invocation) {
|
||||||
testWithoutContext('errors on debugger stream closes log stream', () async {
|
return stderController.stream;
|
||||||
final Stream<String> debuggingLogs = Stream<String>.error('ios-deploy error');
|
|
||||||
final IOSDeviceLogReader logReader = IOSDeviceLogReader.test(
|
|
||||||
iMobileDevice: IMobileDevice(
|
|
||||||
artifacts: artifacts,
|
|
||||||
processManager: processManager,
|
|
||||||
cache: fakeCache,
|
|
||||||
logger: logger,
|
|
||||||
),
|
|
||||||
useSyslog: false,
|
|
||||||
);
|
|
||||||
final Completer<void> streamComplete = Completer<void>();
|
|
||||||
final MockIOSDeployDebugger iosDeployDebugger = MockIOSDeployDebugger();
|
|
||||||
when(iosDeployDebugger.logLines).thenAnswer((Invocation invocation) => debuggingLogs);
|
|
||||||
logReader.logLines.listen(null, onError: (Object error) => streamComplete.complete());
|
|
||||||
logReader.debuggerStream = iosDeployDebugger;
|
|
||||||
|
|
||||||
await streamComplete.future;
|
|
||||||
});
|
});
|
||||||
|
logReader.connectedVMService = vmService;
|
||||||
|
|
||||||
testWithoutContext('detaches debugger', () async {
|
stdoutCompleter.complete(Success());
|
||||||
final IOSDeviceLogReader logReader = IOSDeviceLogReader.test(
|
stderrCompleter.complete(Success());
|
||||||
iMobileDevice: IMobileDevice(
|
stdoutController.add(Event(
|
||||||
artifacts: artifacts,
|
kind: 'Stdout',
|
||||||
processManager: processManager,
|
timestamp: 0,
|
||||||
cache: fakeCache,
|
bytes: base64.encode(utf8.encode(' This is a message ')),
|
||||||
logger: logger,
|
));
|
||||||
),
|
stderController.add(Event(
|
||||||
useSyslog: false,
|
kind: 'Stderr',
|
||||||
);
|
timestamp: 0,
|
||||||
final MockIOSDeployDebugger iosDeployDebugger = MockIOSDeployDebugger();
|
bytes: base64.encode(utf8.encode(' And this is an error ')),
|
||||||
when(iosDeployDebugger.logLines).thenAnswer((Invocation invocation) => const Stream<String>.empty());
|
));
|
||||||
logReader.debuggerStream = iosDeployDebugger;
|
|
||||||
|
|
||||||
logReader.dispose();
|
// Wait for stream listeners to fire.
|
||||||
verify(iosDeployDebugger.detach());
|
await expectLater(logReader.logLines, emitsInAnyOrder(<Matcher>[
|
||||||
});
|
equals(' This is a message '),
|
||||||
|
equals(' And this is an error '),
|
||||||
|
]));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockArtifacts extends Mock implements Artifacts {}
|
class MockArtifacts extends Mock implements Artifacts {}
|
||||||
class MockVmService extends Mock implements VmService {}
|
class MockVmService extends Mock implements VmService {}
|
||||||
class MockIOSDeployDebugger extends Mock implements IOSDeployDebugger {}
|
|
||||||
|
@ -67,25 +67,19 @@ const FakeCommand kLaunchReleaseCommand = FakeCommand(
|
|||||||
|
|
||||||
// The command used to actually launch the app with args in debug.
|
// The command used to actually launch the app with args in debug.
|
||||||
const FakeCommand kLaunchDebugCommand = FakeCommand(command: <String>[
|
const FakeCommand kLaunchDebugCommand = FakeCommand(command: <String>[
|
||||||
'script',
|
|
||||||
'-t',
|
|
||||||
'0',
|
|
||||||
'/dev/null',
|
|
||||||
'ios-deploy',
|
'ios-deploy',
|
||||||
'--id',
|
'--id',
|
||||||
'123',
|
'123',
|
||||||
'--bundle',
|
'--bundle',
|
||||||
'/',
|
'/',
|
||||||
'--debug',
|
|
||||||
'--no-wifi',
|
'--no-wifi',
|
||||||
|
'--justlaunch',
|
||||||
'--args',
|
'--args',
|
||||||
'--enable-dart-profiling --enable-service-port-fallback --disable-service-auth-codes --observatory-port=60700 --enable-checked-mode --verify-entry-points'
|
'--enable-dart-profiling --enable-service-port-fallback --disable-service-auth-codes --observatory-port=60700 --enable-checked-mode --verify-entry-points'
|
||||||
], environment: <String, String>{
|
], environment: <String, String>{
|
||||||
'PATH': '/usr/bin:null',
|
'PATH': '/usr/bin:null',
|
||||||
'DYLD_LIBRARY_PATH': '/path/to/libraries',
|
'DYLD_LIBRARY_PATH': '/path/to/libraries',
|
||||||
},
|
});
|
||||||
stdout: '(lldb) run\nsuccess',
|
|
||||||
);
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// TODO(jonahwilliams): This test doesn't really belong here but
|
// TODO(jonahwilliams): This test doesn't really belong here but
|
||||||
@ -108,7 +102,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Still uses context for analytics and mDNS.
|
// Still uses context for analytics and mDNS.
|
||||||
testUsingContext('IOSDevice.startApp succeeds in debug mode via mDNS discovery when log reading fails', () async {
|
testUsingContext('IOSDevice.startApp succeeds in debug mode via mDNS discovery', () async {
|
||||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
kDeployCommand,
|
kDeployCommand,
|
||||||
@ -151,7 +145,6 @@ void main() {
|
|||||||
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
||||||
platformArgs: <String, dynamic>{},
|
platformArgs: <String, dynamic>{},
|
||||||
fallbackPollingDelay: Duration.zero,
|
fallbackPollingDelay: Duration.zero,
|
||||||
fallbackThrottleTimeout: const Duration(milliseconds: 10),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
verify(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-success')).called(1);
|
verify(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-success')).called(1);
|
||||||
@ -164,58 +157,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Still uses context for analytics and mDNS.
|
// Still uses context for analytics and mDNS.
|
||||||
testUsingContext('IOSDevice.startApp succeeds in debug mode via log reading', () async {
|
testUsingContext('IOSDevice.startApp succeeds in debug mode when mDNS fails', () async {
|
||||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
|
||||||
kDeployCommand,
|
|
||||||
kLaunchDebugCommand,
|
|
||||||
]);
|
|
||||||
final IOSDevice device = setUpIOSDevice(
|
|
||||||
processManager: processManager,
|
|
||||||
fileSystem: fileSystem,
|
|
||||||
vmServiceConnector: (String string, {Log log}) async {
|
|
||||||
throw const io.SocketException(
|
|
||||||
'OS Error: Connection refused, errno = 61, address = localhost, port '
|
|
||||||
'= 58943',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
final IOSApp iosApp = PrebuiltIOSApp(
|
|
||||||
projectBundleId: 'app',
|
|
||||||
bundleName: 'Runner',
|
|
||||||
bundleDir: fileSystem.currentDirectory,
|
|
||||||
);
|
|
||||||
final FakeDeviceLogReader deviceLogReader = FakeDeviceLogReader();
|
|
||||||
|
|
||||||
device.portForwarder = const NoOpDevicePortForwarder();
|
|
||||||
device.setLogReader(iosApp, deviceLogReader);
|
|
||||||
|
|
||||||
// Start writing messages to the log reader.
|
|
||||||
Timer.run(() {
|
|
||||||
deviceLogReader.addLine('Foo');
|
|
||||||
deviceLogReader.addLine('Observatory listening on http://127.0.0.1:456');
|
|
||||||
});
|
|
||||||
|
|
||||||
final LaunchResult launchResult = await device.startApp(iosApp,
|
|
||||||
prebuiltApplication: true,
|
|
||||||
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
||||||
platformArgs: <String, dynamic>{},
|
|
||||||
fallbackPollingDelay: Duration.zero,
|
|
||||||
fallbackThrottleTimeout: const Duration(milliseconds: 10),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(launchResult.started, true);
|
|
||||||
expect(launchResult.hasObservatory, true);
|
|
||||||
verify(globals.flutterUsage.sendEvent('ios-handshake', 'log-success')).called(1);
|
|
||||||
verifyNever(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-failure'));
|
|
||||||
expect(await device.stopApp(iosApp), false);
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
MDnsObservatoryDiscovery: () => MockMDnsObservatoryDiscovery(),
|
|
||||||
Usage: () => MockUsage(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Still uses context for analytics and mDNS.
|
|
||||||
testUsingContext('IOSDevice.startApp fails in debug mode when Observatory URI is malformed', () async {
|
|
||||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
kDeployCommand,
|
kDeployCommand,
|
||||||
@ -244,15 +186,69 @@ void main() {
|
|||||||
// Now that the reader is used, start writing messages to it.
|
// Now that the reader is used, start writing messages to it.
|
||||||
Timer.run(() {
|
Timer.run(() {
|
||||||
deviceLogReader.addLine('Foo');
|
deviceLogReader.addLine('Foo');
|
||||||
deviceLogReader.addLine('Observatory listening on http://127.0.0.1:456abc');
|
deviceLogReader.addLine('Observatory listening on http://127.0.0.1:456');
|
||||||
});
|
});
|
||||||
|
when(MDnsObservatoryDiscovery.instance.getObservatoryUri(any, any, usesIpv6: anyNamed('usesIpv6')))
|
||||||
|
.thenAnswer((Invocation invocation) async => null);
|
||||||
|
|
||||||
|
final LaunchResult launchResult = await device.startApp(iosApp,
|
||||||
|
prebuiltApplication: true,
|
||||||
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
||||||
|
platformArgs: <String, dynamic>{},
|
||||||
|
fallbackPollingDelay: Duration.zero,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(launchResult.started, true);
|
||||||
|
expect(launchResult.hasObservatory, true);
|
||||||
|
verify(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-failure')).called(1);
|
||||||
|
verify(globals.flutterUsage.sendEvent('ios-handshake', 'fallback-success')).called(1);
|
||||||
|
expect(await device.stopApp(iosApp), false);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
Usage: () => MockUsage(),
|
||||||
|
MDnsObservatoryDiscovery: () => MockMDnsObservatoryDiscovery(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Still uses context for analytics and mDNS.
|
||||||
|
testUsingContext('IOSDevice.startApp fails in debug mode when mDNS fails and '
|
||||||
|
'when Observatory URI is malformed', () async {
|
||||||
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||||
|
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||||
|
kDeployCommand,
|
||||||
|
kLaunchDebugCommand,
|
||||||
|
]);
|
||||||
|
final IOSDevice device = setUpIOSDevice(
|
||||||
|
processManager: processManager,
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
vmServiceConnector: (String string, {Log log}) async {
|
||||||
|
throw const io.SocketException(
|
||||||
|
'OS Error: Connection refused, errno = 61, address = localhost, port '
|
||||||
|
'= 58943',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final IOSApp iosApp = PrebuiltIOSApp(
|
||||||
|
projectBundleId: 'app',
|
||||||
|
bundleName: 'Runner',
|
||||||
|
bundleDir: fileSystem.currentDirectory,
|
||||||
|
);
|
||||||
|
final FakeDeviceLogReader deviceLogReader = FakeDeviceLogReader();
|
||||||
|
|
||||||
|
device.portForwarder = const NoOpDevicePortForwarder();
|
||||||
|
device.setLogReader(iosApp, deviceLogReader);
|
||||||
|
|
||||||
|
// Now that the reader is used, start writing messages to it.
|
||||||
|
Timer.run(() {
|
||||||
|
deviceLogReader.addLine('Foo');
|
||||||
|
deviceLogReader.addLine('Observatory listening on http:/:/127.0.0.1:456');
|
||||||
|
});
|
||||||
|
when(MDnsObservatoryDiscovery.instance.getObservatoryUri(any, any, usesIpv6: anyNamed('usesIpv6')))
|
||||||
|
.thenAnswer((Invocation invocation) async => null);
|
||||||
|
|
||||||
final LaunchResult launchResult = await device.startApp(iosApp,
|
final LaunchResult launchResult = await device.startApp(iosApp,
|
||||||
prebuiltApplication: true,
|
prebuiltApplication: true,
|
||||||
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
||||||
platformArgs: <String, dynamic>{},
|
platformArgs: <String, dynamic>{},
|
||||||
fallbackPollingDelay: Duration.zero,
|
fallbackPollingDelay: Duration.zero,
|
||||||
// fallbackThrottleTimeout: const Duration(milliseconds: 10),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(launchResult.started, false);
|
expect(launchResult.started, false);
|
||||||
@ -263,8 +259,8 @@ void main() {
|
|||||||
label: anyNamed('label'),
|
label: anyNamed('label'),
|
||||||
value: anyNamed('value'),
|
value: anyNamed('value'),
|
||||||
)).called(1);
|
)).called(1);
|
||||||
verify(globals.flutterUsage.sendEvent('ios-handshake', 'log-failure')).called(1);
|
|
||||||
verify(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-failure')).called(1);
|
verify(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-failure')).called(1);
|
||||||
|
verify(globals.flutterUsage.sendEvent('ios-handshake', 'fallback-failure')).called(1);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
MDnsObservatoryDiscovery: () => MockMDnsObservatoryDiscovery(),
|
MDnsObservatoryDiscovery: () => MockMDnsObservatoryDiscovery(),
|
||||||
Usage: () => MockUsage(),
|
Usage: () => MockUsage(),
|
||||||
@ -392,7 +388,6 @@ void main() {
|
|||||||
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
||||||
platformArgs: <String, dynamic>{},
|
platformArgs: <String, dynamic>{},
|
||||||
fallbackPollingDelay: Duration.zero,
|
fallbackPollingDelay: Duration.zero,
|
||||||
fallbackThrottleTimeout: const Duration(milliseconds: 10),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(launchResult.started, true);
|
expect(launchResult.started, true);
|
||||||
@ -410,17 +405,13 @@ void main() {
|
|||||||
kDeployCommand,
|
kDeployCommand,
|
||||||
FakeCommand(
|
FakeCommand(
|
||||||
command: <String>[
|
command: <String>[
|
||||||
'script',
|
|
||||||
'-t',
|
|
||||||
'0',
|
|
||||||
'/dev/null',
|
|
||||||
'ios-deploy',
|
'ios-deploy',
|
||||||
'--id',
|
'--id',
|
||||||
'123',
|
'123',
|
||||||
'--bundle',
|
'--bundle',
|
||||||
'/',
|
'/',
|
||||||
'--debug',
|
|
||||||
'--no-wifi',
|
'--no-wifi',
|
||||||
|
'--justlaunch',
|
||||||
// The arguments below are determined by what is passed into
|
// The arguments below are determined by what is passed into
|
||||||
// the debugging options argument to startApp.
|
// the debugging options argument to startApp.
|
||||||
'--args',
|
'--args',
|
||||||
@ -445,8 +436,7 @@ void main() {
|
|||||||
], environment: const <String, String>{
|
], environment: const <String, String>{
|
||||||
'PATH': '/usr/bin:null',
|
'PATH': '/usr/bin:null',
|
||||||
'DYLD_LIBRARY_PATH': '/path/to/libraries',
|
'DYLD_LIBRARY_PATH': '/path/to/libraries',
|
||||||
},
|
}
|
||||||
stdout: '(lldb) run\nsuccess',
|
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
final IOSDevice device = setUpIOSDevice(
|
final IOSDevice device = setUpIOSDevice(
|
||||||
@ -465,15 +455,22 @@ void main() {
|
|||||||
bundleName: 'Runner',
|
bundleName: 'Runner',
|
||||||
bundleDir: fileSystem.currentDirectory,
|
bundleDir: fileSystem.currentDirectory,
|
||||||
);
|
);
|
||||||
final FakeDeviceLogReader deviceLogReader = FakeDeviceLogReader();
|
final Uri uri = Uri(
|
||||||
|
scheme: 'http',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 1234,
|
||||||
|
path: 'observatory',
|
||||||
|
);
|
||||||
|
|
||||||
|
device.setLogReader(iosApp, FakeDeviceLogReader());
|
||||||
device.portForwarder = const NoOpDevicePortForwarder();
|
device.portForwarder = const NoOpDevicePortForwarder();
|
||||||
device.setLogReader(iosApp, deviceLogReader);
|
|
||||||
|
|
||||||
// Start writing messages to the log reader.
|
when(MDnsObservatoryDiscovery.instance.getObservatoryUri(
|
||||||
Timer.run(() {
|
any,
|
||||||
deviceLogReader.addLine('Observatory listening on http://127.0.0.1:1234');
|
any,
|
||||||
});
|
usesIpv6: anyNamed('usesIpv6'),
|
||||||
|
hostVmservicePort: anyNamed('hostVmservicePort')
|
||||||
|
)).thenAnswer((Invocation invocation) async => uri);
|
||||||
|
|
||||||
final LaunchResult launchResult = await device.startApp(iosApp,
|
final LaunchResult launchResult = await device.startApp(iosApp,
|
||||||
prebuiltApplication: true,
|
prebuiltApplication: true,
|
||||||
@ -495,7 +492,6 @@ void main() {
|
|||||||
),
|
),
|
||||||
platformArgs: <String, dynamic>{},
|
platformArgs: <String, dynamic>{},
|
||||||
fallbackPollingDelay: Duration.zero,
|
fallbackPollingDelay: Duration.zero,
|
||||||
fallbackThrottleTimeout: const Duration(milliseconds: 10),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(launchResult.started, true);
|
expect(launchResult.started, true);
|
||||||
|
Loading…
Reference in New Issue
Block a user