mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Migrate xcdevice and ios devices to null safety (#91704)
This commit is contained in:
parent
299d484903
commit
0065873670
@ -3,7 +3,6 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import 'base/common.dart';
|
||||
@ -282,7 +281,6 @@ abstract class Artifacts {
|
||||
/// If a [fileSystem] is not provided, creates a new [MemoryFileSystem] instance.
|
||||
///
|
||||
/// Creates a [LocalEngineArtifacts] if `localEngine` is non-null
|
||||
@visibleForTesting
|
||||
factory Artifacts.test({String? localEngine, FileSystem? fileSystem}) {
|
||||
fileSystem ??= MemoryFileSystem.test();
|
||||
if (localEngine != null) {
|
||||
|
@ -127,7 +127,6 @@ class Cache {
|
||||
/// Defaults to a memory file system, fake platform,
|
||||
/// buffer logger, and no accessible artifacts.
|
||||
/// By default, the root cache directory path is "cache".
|
||||
@visibleForTesting
|
||||
factory Cache.test({
|
||||
Directory? rootOverride,
|
||||
List<ArtifactSet>? artifacts,
|
||||
|
@ -468,7 +468,7 @@ abstract class Device {
|
||||
/// The ID returned matches that in the output of `flutter emulators`. Fetching
|
||||
/// this name may require connecting to the device and if an error occurs null
|
||||
/// will be returned.
|
||||
Future<String> get emulatorId;
|
||||
Future<String?> get emulatorId;
|
||||
|
||||
/// Whether this device can run the provided [buildMode].
|
||||
///
|
||||
@ -550,7 +550,7 @@ abstract class Device {
|
||||
/// reader will also include log messages from before the invocation time.
|
||||
/// Defaults to false.
|
||||
FutureOr<DeviceLogReader> getLogReader({
|
||||
covariant ApplicationPackage app,
|
||||
covariant ApplicationPackage? app,
|
||||
bool includePastLogs = false,
|
||||
});
|
||||
|
||||
@ -572,13 +572,13 @@ abstract class Device {
|
||||
/// start call. The build mode is not used by all platforms.
|
||||
Future<LaunchResult> startApp(
|
||||
covariant ApplicationPackage package, {
|
||||
String mainPath,
|
||||
String route,
|
||||
DebuggingOptions debuggingOptions,
|
||||
String? mainPath,
|
||||
String? route,
|
||||
required DebuggingOptions debuggingOptions,
|
||||
Map<String, Object?> platformArgs,
|
||||
bool prebuiltApplication = false,
|
||||
bool ipv6 = false,
|
||||
String userIdentifier,
|
||||
String? userIdentifier,
|
||||
});
|
||||
|
||||
/// Whether this device implements support for hot reload.
|
||||
|
@ -7,11 +7,8 @@
|
||||
import 'base/context.dart';
|
||||
import 'doctor.dart';
|
||||
import 'ios/simulators.dart';
|
||||
import 'macos/xcdevice.dart';
|
||||
|
||||
export 'globals_null_migrated.dart';
|
||||
|
||||
Doctor get doctor => context.get<Doctor>();
|
||||
IOSSimulatorUtils get iosSimulatorUtils => context.get<IOSSimulatorUtils>();
|
||||
|
||||
XCDevice get xcdevice => context.get<XCDevice>();
|
||||
|
@ -34,6 +34,7 @@ import 'ios/plist_parser.dart';
|
||||
import 'ios/xcodeproj.dart';
|
||||
import 'macos/cocoapods.dart';
|
||||
import 'macos/cocoapods_validator.dart';
|
||||
import 'macos/xcdevice.dart';
|
||||
import 'macos/xcode.dart';
|
||||
import 'persistent_tool_state.dart';
|
||||
import 'project.dart';
|
||||
@ -62,6 +63,7 @@ FlutterVersion get flutterVersion => context.get<FlutterVersion>()!;
|
||||
FuchsiaArtifacts? get fuchsiaArtifacts => context.get<FuchsiaArtifacts>();
|
||||
Usage get flutterUsage => context.get<Usage>()!;
|
||||
XcodeProjectInterpreter? get xcodeProjectInterpreter => context.get<XcodeProjectInterpreter>();
|
||||
XCDevice? get xcdevice => context.get<XCDevice>();
|
||||
Xcode? get xcode => context.get<Xcode>();
|
||||
IOSWorkflow? get iosWorkflow => context.get<IOSWorkflow>();
|
||||
LocalEngineLocator? get localEngineLocator => context.get<LocalEngineLocator>();
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
@ -34,10 +32,10 @@ import 'mac.dart';
|
||||
|
||||
class IOSDevices extends PollingDeviceDiscovery {
|
||||
IOSDevices({
|
||||
@required Platform platform,
|
||||
@required XCDevice xcdevice,
|
||||
@required IOSWorkflow iosWorkflow,
|
||||
@required Logger logger,
|
||||
required Platform platform,
|
||||
required XCDevice xcdevice,
|
||||
required IOSWorkflow iosWorkflow,
|
||||
required Logger logger,
|
||||
}) : _platform = platform,
|
||||
_xcdevice = xcdevice,
|
||||
_iosWorkflow = iosWorkflow,
|
||||
@ -55,7 +53,7 @@ class IOSDevices extends PollingDeviceDiscovery {
|
||||
@override
|
||||
bool get canListAnything => _iosWorkflow.canListDevices;
|
||||
|
||||
StreamSubscription<Map<XCDeviceEvent, String>> _observedDeviceEventsSubscription;
|
||||
StreamSubscription<Map<XCDeviceEvent, String>>? _observedDeviceEventsSubscription;
|
||||
|
||||
@override
|
||||
Future<void> startPolling() async {
|
||||
@ -71,13 +69,13 @@ class IOSDevices extends PollingDeviceDiscovery {
|
||||
deviceNotifier ??= ItemListNotifier<Device>();
|
||||
|
||||
// Start by populating all currently attached devices.
|
||||
deviceNotifier.updateWithNewList(await pollingGetDevices());
|
||||
deviceNotifier!.updateWithNewList(await pollingGetDevices());
|
||||
|
||||
// cancel any outstanding subscriptions.
|
||||
await _observedDeviceEventsSubscription?.cancel();
|
||||
_observedDeviceEventsSubscription = _xcdevice.observedDeviceEvents()?.listen(
|
||||
_onDeviceEvent,
|
||||
onError: (dynamic error, StackTrace stack) {
|
||||
onError: (Object error, StackTrace stack) {
|
||||
_logger.printTrace('Process exception running xcdevice observe:\n$error\n$stack');
|
||||
}, onDone: () {
|
||||
// If xcdevice is killed or otherwise dies, polling will be stopped.
|
||||
@ -92,18 +90,26 @@ class IOSDevices extends PollingDeviceDiscovery {
|
||||
|
||||
Future<void> _onDeviceEvent(Map<XCDeviceEvent, String> event) async {
|
||||
final XCDeviceEvent eventType = event.containsKey(XCDeviceEvent.attach) ? XCDeviceEvent.attach : XCDeviceEvent.detach;
|
||||
final String deviceIdentifier = event[eventType];
|
||||
final Device knownDevice = deviceNotifier.items
|
||||
.firstWhere((Device device) => device.id == deviceIdentifier, orElse: () => null);
|
||||
final String? deviceIdentifier = event[eventType];
|
||||
final ItemListNotifier<Device>? notifier = deviceNotifier;
|
||||
if (notifier == null) {
|
||||
return;
|
||||
}
|
||||
Device? knownDevice;
|
||||
for (final Device device in notifier.items) {
|
||||
if (device.id == deviceIdentifier) {
|
||||
knownDevice = device;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore already discovered devices (maybe populated at the beginning).
|
||||
if (eventType == XCDeviceEvent.attach && knownDevice == null) {
|
||||
// There's no way to get details for an individual attached device,
|
||||
// so repopulate them all.
|
||||
final List<Device> devices = await pollingGetDevices();
|
||||
deviceNotifier.updateWithNewList(devices);
|
||||
notifier.updateWithNewList(devices);
|
||||
} else if (eventType == XCDeviceEvent.detach && knownDevice != null) {
|
||||
deviceNotifier.removeItem(knownDevice);
|
||||
notifier.removeItem(knownDevice);
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +119,7 @@ class IOSDevices extends PollingDeviceDiscovery {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
|
||||
Future<List<Device>> pollingGetDevices({ Duration? timeout }) async {
|
||||
if (!_platform.isMacOS) {
|
||||
throw UnsupportedError(
|
||||
'Control of iOS devices or simulators only supported on macOS.'
|
||||
@ -140,16 +146,16 @@ class IOSDevices extends PollingDeviceDiscovery {
|
||||
|
||||
class IOSDevice extends Device {
|
||||
IOSDevice(String id, {
|
||||
@required FileSystem fileSystem,
|
||||
@required this.name,
|
||||
@required this.cpuArchitecture,
|
||||
@required this.interfaceType,
|
||||
@required String sdkVersion,
|
||||
@required Platform platform,
|
||||
@required IOSDeploy iosDeploy,
|
||||
@required IMobileDevice iMobileDevice,
|
||||
@required IProxy iProxy,
|
||||
@required Logger logger,
|
||||
required FileSystem fileSystem,
|
||||
required this.name,
|
||||
required this.cpuArchitecture,
|
||||
required this.interfaceType,
|
||||
String? sdkVersion,
|
||||
required Platform platform,
|
||||
required IOSDeploy iosDeploy,
|
||||
required IMobileDevice iMobileDevice,
|
||||
required IProxy iProxy,
|
||||
required Logger logger,
|
||||
})
|
||||
: _sdkVersion = sdkVersion,
|
||||
_iosDeploy = iosDeploy,
|
||||
@ -170,7 +176,7 @@ class IOSDevice extends Device {
|
||||
}
|
||||
}
|
||||
|
||||
final String _sdkVersion;
|
||||
final String? _sdkVersion;
|
||||
final IOSDeploy _iosDeploy;
|
||||
final FileSystem _fileSystem;
|
||||
final Logger _logger;
|
||||
@ -180,7 +186,7 @@ class IOSDevice extends Device {
|
||||
|
||||
/// May be 0 if version cannot be parsed.
|
||||
int get majorSdkVersion {
|
||||
final String majorVersionString = _sdkVersion?.split('.')?.first?.trim();
|
||||
final String? majorVersionString = _sdkVersion?.split('.').first.trim();
|
||||
return majorVersionString != null ? int.tryParse(majorVersionString) ?? 0 : 0;
|
||||
}
|
||||
|
||||
@ -203,18 +209,18 @@ class IOSDevice extends Device {
|
||||
|
||||
final IOSDeviceConnectionInterface interfaceType;
|
||||
|
||||
Map<IOSApp, DeviceLogReader> _logReaders;
|
||||
final Map<IOSApp?, DeviceLogReader> _logReaders = <IOSApp?, DeviceLogReader>{};
|
||||
|
||||
DevicePortForwarder _portForwarder;
|
||||
DevicePortForwarder? _portForwarder;
|
||||
|
||||
@visibleForTesting
|
||||
IOSDeployDebugger iosDeployDebugger;
|
||||
IOSDeployDebugger? iosDeployDebugger;
|
||||
|
||||
@override
|
||||
Future<bool> get isLocalEmulator async => false;
|
||||
|
||||
@override
|
||||
Future<String> get emulatorId async => null;
|
||||
Future<String?> get emulatorId async => null;
|
||||
|
||||
@override
|
||||
bool get supportsStartPaused => false;
|
||||
@ -222,7 +228,7 @@ class IOSDevice extends Device {
|
||||
@override
|
||||
Future<bool> isAppInstalled(
|
||||
IOSApp app, {
|
||||
String userIdentifier,
|
||||
String? userIdentifier,
|
||||
}) async {
|
||||
bool result;
|
||||
try {
|
||||
@ -243,7 +249,7 @@ class IOSDevice extends Device {
|
||||
@override
|
||||
Future<bool> installApp(
|
||||
IOSApp app, {
|
||||
String userIdentifier,
|
||||
String? userIdentifier,
|
||||
}) async {
|
||||
final Directory bundle = _fileSystem.directory(app.deviceBundlePath);
|
||||
if (!bundle.existsSync()) {
|
||||
@ -277,7 +283,7 @@ class IOSDevice extends Device {
|
||||
@override
|
||||
Future<bool> uninstallApp(
|
||||
IOSApp app, {
|
||||
String userIdentifier,
|
||||
String? userIdentifier,
|
||||
}) async {
|
||||
int uninstallationResult;
|
||||
try {
|
||||
@ -302,16 +308,16 @@ class IOSDevice extends Device {
|
||||
@override
|
||||
Future<LaunchResult> startApp(
|
||||
IOSApp package, {
|
||||
String mainPath,
|
||||
String route,
|
||||
DebuggingOptions debuggingOptions,
|
||||
Map<String, dynamic> platformArgs,
|
||||
String? mainPath,
|
||||
String? route,
|
||||
required DebuggingOptions debuggingOptions,
|
||||
Map<String, Object?> platformArgs = const <String, Object?>{},
|
||||
bool prebuiltApplication = false,
|
||||
bool ipv6 = false,
|
||||
String userIdentifier,
|
||||
@visibleForTesting Duration discoveryTimeout,
|
||||
String? userIdentifier,
|
||||
@visibleForTesting Duration? discoveryTimeout,
|
||||
}) async {
|
||||
String packageId;
|
||||
String? packageId;
|
||||
|
||||
if (!prebuiltApplication) {
|
||||
_logger.printTrace('Building ${package.name} for $id');
|
||||
@ -321,7 +327,6 @@ class IOSDevice extends Device {
|
||||
app: package as BuildableIOSApp,
|
||||
buildInfo: debuggingOptions.buildInfo,
|
||||
targetOverride: mainPath,
|
||||
environmentType: EnvironmentType.physical,
|
||||
activeArch: cpuArchitecture,
|
||||
deviceID: id,
|
||||
);
|
||||
@ -366,14 +371,14 @@ class IOSDevice extends Device {
|
||||
if (debuggingOptions.verboseSystemLogs) '--verbose-logging',
|
||||
if (debuggingOptions.cacheSkSL) '--cache-sksl',
|
||||
if (debuggingOptions.purgePersistentCache) '--purge-persistent-cache',
|
||||
if (platformArgs['trace-startup'] as bool ?? false) '--trace-startup',
|
||||
if (platformArgs['trace-startup'] as bool? ?? false) '--trace-startup',
|
||||
];
|
||||
|
||||
final Status installStatus = _logger.startProgress(
|
||||
'Installing and launching...',
|
||||
);
|
||||
try {
|
||||
ProtocolDiscovery observatoryDiscovery;
|
||||
ProtocolDiscovery? observatoryDiscovery;
|
||||
int installationResult = 1;
|
||||
if (debuggingOptions.debuggingEnabled) {
|
||||
_logger.printTrace('Debugging is enabled, connecting to observatory');
|
||||
@ -411,7 +416,7 @@ class IOSDevice extends Device {
|
||||
interfaceType: interfaceType,
|
||||
);
|
||||
} else {
|
||||
installationResult = await iosDeployDebugger.launchAndAttach() ? 0 : 1;
|
||||
installationResult = await iosDeployDebugger!.launchAndAttach() ? 0 : 1;
|
||||
}
|
||||
if (installationResult != 0) {
|
||||
_logger.printError('Could not run ${bundle.path} on $id.');
|
||||
@ -429,7 +434,7 @@ class IOSDevice extends Device {
|
||||
final Timer timer = Timer(discoveryTimeout ?? const Duration(seconds: 30), () {
|
||||
_logger.printError('iOS Observatory not discovered after 30 seconds. This is taking much longer than expected...');
|
||||
});
|
||||
final Uri localUri = await observatoryDiscovery.uri;
|
||||
final Uri? localUri = await observatoryDiscovery?.uri;
|
||||
timer.cancel();
|
||||
if (localUri == null) {
|
||||
iosDeployDebugger?.detach();
|
||||
@ -448,12 +453,12 @@ class IOSDevice extends Device {
|
||||
@override
|
||||
Future<bool> stopApp(
|
||||
IOSApp app, {
|
||||
String userIdentifier,
|
||||
String? userIdentifier,
|
||||
}) async {
|
||||
// If the debugger is not attached, killing the ios-deploy process won't stop the app.
|
||||
if (iosDeployDebugger!= null && iosDeployDebugger.debuggerAttached) {
|
||||
// Avoid null.
|
||||
return iosDeployDebugger?.exit() == true;
|
||||
final IOSDeployDebugger? deployDebugger = iosDeployDebugger;
|
||||
if (deployDebugger != null && deployDebugger.debuggerAttached) {
|
||||
return deployDebugger.exit() == true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -466,11 +471,10 @@ class IOSDevice extends Device {
|
||||
|
||||
@override
|
||||
DeviceLogReader getLogReader({
|
||||
IOSApp app,
|
||||
IOSApp? app,
|
||||
bool includePastLogs = false,
|
||||
}) {
|
||||
assert(!includePastLogs, 'Past log reading not supported on iOS devices.');
|
||||
_logReaders ??= <IOSApp, DeviceLogReader>{};
|
||||
return _logReaders.putIfAbsent(app, () => IOSDeviceLogReader.create(
|
||||
device: this,
|
||||
app: app,
|
||||
@ -480,7 +484,6 @@ class IOSDevice extends Device {
|
||||
|
||||
@visibleForTesting
|
||||
void setLogReader(IOSApp app, DeviceLogReader logReader) {
|
||||
_logReaders ??= <IOSApp, DeviceLogReader>{};
|
||||
_logReaders[app] = logReader;
|
||||
}
|
||||
|
||||
@ -515,9 +518,10 @@ class IOSDevice extends Device {
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
_logReaders?.forEach((IOSApp application, DeviceLogReader logReader) {
|
||||
for (final DeviceLogReader logReader in _logReaders.values) {
|
||||
logReader.dispose();
|
||||
});
|
||||
}
|
||||
_logReaders.clear();
|
||||
await _portForwarder?.dispose();
|
||||
}
|
||||
}
|
||||
@ -590,31 +594,19 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
||||
this._deviceId,
|
||||
this.name,
|
||||
String appName,
|
||||
) {
|
||||
_linesController = StreamController<String>.broadcast(
|
||||
onListen: _listenToSysLog,
|
||||
onCancel: dispose,
|
||||
);
|
||||
|
||||
// Match for lines for the runner in syslog.
|
||||
//
|
||||
// iOS 9 format: Runner[297] <Notice>:
|
||||
// iOS 10 format: Runner(Flutter)[297] <Notice>:
|
||||
_runnerLineRegex = RegExp(appName + r'(\(Flutter\))?\[[\d]+\] <[A-Za-z]+>: ');
|
||||
// Similar to above, but allows ~arbitrary components instead of "Runner"
|
||||
// and "Flutter". The regex tries to strike a balance between not producing
|
||||
// false positives and not producing false negatives.
|
||||
_anyLineRegex = RegExp(r'\w+(\([^)]*\))?\[\d+\] <[A-Za-z]+>: ');
|
||||
_loggingSubscriptions = <StreamSubscription<void>>[];
|
||||
}
|
||||
) : // Match for lines for the runner in syslog.
|
||||
//
|
||||
// iOS 9 format: Runner[297] <Notice>:
|
||||
// iOS 10 format: Runner(Flutter)[297] <Notice>:
|
||||
_runnerLineRegex = RegExp(appName + r'(\(Flutter\))?\[[\d]+\] <[A-Za-z]+>: ');
|
||||
|
||||
/// Create a new [IOSDeviceLogReader].
|
||||
factory IOSDeviceLogReader.create({
|
||||
@required IOSDevice device,
|
||||
@required IOSApp app,
|
||||
@required IMobileDevice iMobileDevice,
|
||||
required IOSDevice device,
|
||||
IOSApp? app,
|
||||
required IMobileDevice iMobileDevice,
|
||||
}) {
|
||||
final String appName = app == null ? '' : app.name.replaceAll('.app', '');
|
||||
final String appName = app?.name?.replaceAll('.app', '') ?? '';
|
||||
return IOSDeviceLogReader._(
|
||||
iMobileDevice,
|
||||
device.majorSdkVersion,
|
||||
@ -626,7 +618,7 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
||||
|
||||
/// Create an [IOSDeviceLogReader] for testing.
|
||||
factory IOSDeviceLogReader.test({
|
||||
@required IMobileDevice iMobileDevice,
|
||||
required IMobileDevice iMobileDevice,
|
||||
bool useSyslog = true,
|
||||
}) {
|
||||
return IOSDeviceLogReader._(
|
||||
@ -641,8 +633,11 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
||||
|
||||
// Matches a syslog line from the runner.
|
||||
RegExp _runnerLineRegex;
|
||||
// Matches a syslog line from any app.
|
||||
RegExp _anyLineRegex;
|
||||
|
||||
// Similar to above, but allows ~arbitrary components instead of "Runner"
|
||||
// and "Flutter". The regex tries to strike a balance between not producing
|
||||
// false positives and not producing false negatives.
|
||||
final RegExp _anyLineRegex = RegExp(r'\w+(\([^)]*\))?\[\d+\] <[A-Za-z]+>: ');
|
||||
|
||||
// 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.
|
||||
@ -651,19 +646,24 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
||||
// Logging from the dart code has no prefixing metadata.
|
||||
final RegExp _debuggerLoggingRegex = RegExp(r'^\S* \S* \S*\[[0-9:]*] (.*)');
|
||||
|
||||
StreamController<String> _linesController;
|
||||
List<StreamSubscription<void>> _loggingSubscriptions;
|
||||
late final StreamController<String> _linesController = StreamController<String>.broadcast(
|
||||
onListen: _listenToSysLog,
|
||||
onCancel: dispose,
|
||||
);
|
||||
final List<StreamSubscription<void>> _loggingSubscriptions = <StreamSubscription<void>>[];
|
||||
|
||||
@override
|
||||
Stream<String> get logLines => _linesController.stream;
|
||||
|
||||
@override
|
||||
FlutterVmService get connectedVMService => _connectedVMService;
|
||||
FlutterVmService _connectedVMService;
|
||||
FlutterVmService? get connectedVMService => _connectedVMService;
|
||||
FlutterVmService? _connectedVMService;
|
||||
|
||||
@override
|
||||
set connectedVMService(FlutterVmService connectedVmService) {
|
||||
_listenToUnifiedLoggingEvents(connectedVmService);
|
||||
set connectedVMService(FlutterVmService? connectedVmService) {
|
||||
if (connectedVmService != null) {
|
||||
_listenToUnifiedLoggingEvents(connectedVmService);
|
||||
}
|
||||
_connectedVMService = connectedVmService;
|
||||
}
|
||||
|
||||
@ -687,7 +687,7 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
||||
}
|
||||
|
||||
void logMessage(vm_service.Event event) {
|
||||
if (_iosDeployDebugger != null && _iosDeployDebugger.debuggerAttached) {
|
||||
if (_iosDeployDebugger != null && _iosDeployDebugger!.debuggerAttached) {
|
||||
// Prefer the more complete logs from the attached debugger.
|
||||
return;
|
||||
}
|
||||
@ -704,13 +704,16 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
||||
}
|
||||
|
||||
/// Log reader will listen to [debugger.logLines] and will detach debugger on dispose.
|
||||
IOSDeployDebugger get debuggerStream => _iosDeployDebugger;
|
||||
set debuggerStream(IOSDeployDebugger debugger) {
|
||||
IOSDeployDebugger? get debuggerStream => _iosDeployDebugger;
|
||||
set debuggerStream(IOSDeployDebugger? debugger) {
|
||||
// Logging is gathered from syslog on iOS 13 and earlier.
|
||||
if (_majorSdkVersion < minimumUniversalLoggingSdkVersion) {
|
||||
return;
|
||||
}
|
||||
_iosDeployDebugger = debugger;
|
||||
if (debugger == null) {
|
||||
return;
|
||||
}
|
||||
// Add the debugger logs to the controller created on initialization.
|
||||
_loggingSubscriptions.add(debugger.logLines.listen(
|
||||
(String line) => _linesController.add(_debuggerLineHandler(line)),
|
||||
@ -719,10 +722,10 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
||||
cancelOnError: true,
|
||||
));
|
||||
}
|
||||
IOSDeployDebugger _iosDeployDebugger;
|
||||
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;
|
||||
String _debuggerLineHandler(String line) => _debuggerLoggingRegex.firstMatch(line)?.group(1) ?? line;
|
||||
|
||||
void _listenToSysLog() {
|
||||
// syslog is not written on iOS 13+.
|
||||
@ -743,7 +746,7 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
Process idevicesyslogProcess;
|
||||
Process? idevicesyslogProcess;
|
||||
|
||||
// Returns a stateful line handler to properly capture multiline output.
|
||||
//
|
||||
@ -764,7 +767,7 @@ class IOSDeviceLogReader extends DeviceLogReader {
|
||||
printing = false;
|
||||
}
|
||||
|
||||
final Match match = _runnerLineRegex.firstMatch(line);
|
||||
final Match? match = _runnerLineRegex.firstMatch(line);
|
||||
|
||||
if (match != null) {
|
||||
final String logLine = line.substring(match.end);
|
||||
@ -791,10 +794,10 @@ class IOSDevicePortForwarder extends DevicePortForwarder {
|
||||
|
||||
/// Create a new [IOSDevicePortForwarder].
|
||||
IOSDevicePortForwarder({
|
||||
@required Logger logger,
|
||||
@required String id,
|
||||
@required IProxy iproxy,
|
||||
@required OperatingSystemUtils operatingSystemUtils,
|
||||
required Logger logger,
|
||||
required String id,
|
||||
required IProxy iproxy,
|
||||
required OperatingSystemUtils operatingSystemUtils,
|
||||
}) : _logger = logger,
|
||||
_id = id,
|
||||
_iproxy = iproxy,
|
||||
@ -807,10 +810,10 @@ class IOSDevicePortForwarder extends DevicePortForwarder {
|
||||
///
|
||||
/// The device id may be provided, but otherwise defaults to '1234'.
|
||||
factory IOSDevicePortForwarder.test({
|
||||
@required ProcessManager processManager,
|
||||
@required Logger logger,
|
||||
String id,
|
||||
OperatingSystemUtils operatingSystemUtils,
|
||||
required ProcessManager processManager,
|
||||
required Logger logger,
|
||||
String? id,
|
||||
required OperatingSystemUtils operatingSystemUtils,
|
||||
}) {
|
||||
return IOSDevicePortForwarder(
|
||||
logger: logger,
|
||||
@ -839,20 +842,20 @@ class IOSDevicePortForwarder extends DevicePortForwarder {
|
||||
static const Duration _kiProxyPortForwardTimeout = Duration(seconds: 1);
|
||||
|
||||
@override
|
||||
Future<int> forward(int devicePort, { int hostPort }) async {
|
||||
Future<int> forward(int devicePort, { int? hostPort }) async {
|
||||
final bool autoselect = hostPort == null || hostPort == 0;
|
||||
if (autoselect) {
|
||||
final int freePort = await _operatingSystemUtils?.findFreePort();
|
||||
final int freePort = await _operatingSystemUtils.findFreePort();
|
||||
// Dynamic port range 49152 - 65535.
|
||||
hostPort = freePort == null || freePort == 0 ? 49152 : freePort;
|
||||
hostPort = freePort == 0 ? 49152 : freePort;
|
||||
}
|
||||
|
||||
Process process;
|
||||
Process? process;
|
||||
|
||||
bool connected = false;
|
||||
while (!connected) {
|
||||
_logger.printTrace('Attempting to forward device port $devicePort to host port $hostPort');
|
||||
process = await _iproxy.forward(devicePort, hostPort, _id);
|
||||
process = await _iproxy.forward(devicePort, hostPort!, _id);
|
||||
// TODO(ianh): This is a flaky race condition, https://github.com/libimobiledevice/libimobiledevice/issues/674
|
||||
connected = !await process.stdout.isEmpty.timeout(_kiProxyPortForwardTimeout, onTimeout: () => false);
|
||||
if (!connected) {
|
||||
@ -871,7 +874,7 @@ class IOSDevicePortForwarder extends DevicePortForwarder {
|
||||
assert(process != null);
|
||||
|
||||
final ForwardedPort forwardedPort = ForwardedPort.withContext(
|
||||
hostPort, devicePort, process,
|
||||
hostPort!, devicePort, process,
|
||||
);
|
||||
_logger.printTrace('Forwarded port $forwardedPort');
|
||||
forwardedPorts.add(forwardedPort);
|
||||
|
@ -45,6 +45,16 @@ class IMobileDevice {
|
||||
_processUtils = ProcessUtils(logger: logger, processManager: processManager),
|
||||
_processManager = processManager;
|
||||
|
||||
/// Create an [IMobileDevice] for testing.
|
||||
factory IMobileDevice.test({ required ProcessManager processManager }) {
|
||||
return IMobileDevice(
|
||||
artifacts: Artifacts.test(),
|
||||
cache: Cache.test(processManager: processManager),
|
||||
processManager: processManager,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
}
|
||||
|
||||
final String _idevicesyslogPath;
|
||||
final String _idevicescreenshotPath;
|
||||
final MapEntry<String, String> _dyLdLibEntry;
|
||||
@ -93,7 +103,7 @@ class IMobileDevice {
|
||||
Future<XcodeBuildResult> buildXcodeProject({
|
||||
required BuildableIOSApp app,
|
||||
required BuildInfo buildInfo,
|
||||
required String targetOverride,
|
||||
String? targetOverride,
|
||||
EnvironmentType environmentType = EnvironmentType.physical,
|
||||
DarwinArch? activeArch,
|
||||
bool codesign = true,
|
||||
|
@ -2,11 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import '../artifacts.dart';
|
||||
@ -34,13 +31,13 @@ enum XCDeviceEvent {
|
||||
/// A utility class for interacting with Xcode xcdevice command line tools.
|
||||
class XCDevice {
|
||||
XCDevice({
|
||||
@required Artifacts artifacts,
|
||||
@required Cache cache,
|
||||
@required ProcessManager processManager,
|
||||
@required Logger logger,
|
||||
@required Xcode xcode,
|
||||
@required Platform platform,
|
||||
@required IProxy iproxy,
|
||||
required Artifacts artifacts,
|
||||
required Cache cache,
|
||||
required ProcessManager processManager,
|
||||
required Logger logger,
|
||||
required Xcode xcode,
|
||||
required Platform platform,
|
||||
required IProxy iproxy,
|
||||
}) : _processUtils = ProcessUtils(logger: logger, processManager: processManager),
|
||||
_logger = logger,
|
||||
_iMobileDevice = IMobileDevice(
|
||||
@ -73,9 +70,9 @@ class XCDevice {
|
||||
final Xcode _xcode;
|
||||
final IProxy _iProxy;
|
||||
|
||||
List<dynamic> _cachedListResults;
|
||||
Process _deviceObservationProcess;
|
||||
StreamController<Map<XCDeviceEvent, String>> _deviceIdentifierByEvent;
|
||||
List<Object>? _cachedListResults;
|
||||
Process? _deviceObservationProcess;
|
||||
StreamController<Map<XCDeviceEvent, String>>? _deviceIdentifierByEvent;
|
||||
|
||||
void _setupDeviceIdentifierByEventStream() {
|
||||
// _deviceIdentifierByEvent Should always be available for listeners
|
||||
@ -88,9 +85,9 @@ class XCDevice {
|
||||
|
||||
bool get isInstalled => _xcode.isInstalledAndMeetsVersionCheck;
|
||||
|
||||
Future<List<dynamic>> _getAllDevices({
|
||||
Future<List<Object>?> _getAllDevices({
|
||||
bool useCache = false,
|
||||
@required Duration timeout
|
||||
required Duration timeout
|
||||
}) async {
|
||||
if (!isInstalled) {
|
||||
_logger.printTrace("Xcode not found. Run 'flutter doctor' for more information.");
|
||||
@ -114,7 +111,7 @@ class XCDevice {
|
||||
if (result.exitCode == 0) {
|
||||
final String listOutput = result.stdout;
|
||||
try {
|
||||
final List<dynamic> listResults = json.decode(listOutput) as List<dynamic>;
|
||||
final List<Object> listResults = (json.decode(result.stdout) as List<Object?>).whereType<Object>().toList();
|
||||
_cachedListResults = listResults;
|
||||
return listResults;
|
||||
} on FormatException {
|
||||
@ -137,12 +134,12 @@ class XCDevice {
|
||||
///
|
||||
/// Each attach and detach event is a tuple of one event type
|
||||
/// and identifier.
|
||||
Stream<Map<XCDeviceEvent, String>> observedDeviceEvents() {
|
||||
Stream<Map<XCDeviceEvent, String>>? observedDeviceEvents() {
|
||||
if (!isInstalled) {
|
||||
_logger.printTrace("Xcode not found. Run 'flutter doctor' for more information.");
|
||||
return null;
|
||||
}
|
||||
return _deviceIdentifierByEvent.stream;
|
||||
return _deviceIdentifierByEvent?.stream;
|
||||
}
|
||||
|
||||
// Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
|
||||
@ -171,7 +168,7 @@ class XCDevice {
|
||||
],
|
||||
);
|
||||
|
||||
final StreamSubscription<String> stdoutSubscription = _deviceObservationProcess.stdout
|
||||
final StreamSubscription<String> stdoutSubscription = _deviceObservationProcess!.stdout
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
@ -183,35 +180,35 @@ class XCDevice {
|
||||
// Attach: 00008027-00192736010F802E
|
||||
// Detach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
|
||||
// Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
|
||||
final RegExpMatch match = _observationIdentifierPattern.firstMatch(line);
|
||||
final RegExpMatch? match = _observationIdentifierPattern.firstMatch(line);
|
||||
if (match != null && match.groupCount == 2) {
|
||||
final String verb = match.group(1).toLowerCase();
|
||||
final String identifier = match.group(2);
|
||||
final String verb = match.group(1)!.toLowerCase();
|
||||
final String identifier = match.group(2)!;
|
||||
if (verb.startsWith('attach')) {
|
||||
_deviceIdentifierByEvent.add(<XCDeviceEvent, String>{
|
||||
_deviceIdentifierByEvent?.add(<XCDeviceEvent, String>{
|
||||
XCDeviceEvent.attach: identifier
|
||||
});
|
||||
} else if (verb.startsWith('detach')) {
|
||||
_deviceIdentifierByEvent.add(<XCDeviceEvent, String>{
|
||||
_deviceIdentifierByEvent?.add(<XCDeviceEvent, String>{
|
||||
XCDeviceEvent.detach: identifier
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
final StreamSubscription<String> stderrSubscription = _deviceObservationProcess.stderr
|
||||
final StreamSubscription<String> stderrSubscription = _deviceObservationProcess!.stderr
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
_logger.printTrace('xcdevice observe error: $line');
|
||||
});
|
||||
unawaited(_deviceObservationProcess.exitCode.then((int status) {
|
||||
unawaited(_deviceObservationProcess?.exitCode.then((int status) {
|
||||
_logger.printTrace('xcdevice exited with code $exitCode');
|
||||
unawaited(stdoutSubscription.cancel());
|
||||
unawaited(stderrSubscription.cancel());
|
||||
}).whenComplete(() async {
|
||||
if (_deviceIdentifierByEvent.hasListener) {
|
||||
if (_deviceIdentifierByEvent?.hasListener == true) {
|
||||
// Tell listeners the process died.
|
||||
await _deviceIdentifierByEvent.close();
|
||||
await _deviceIdentifierByEvent?.close();
|
||||
}
|
||||
_deviceObservationProcess = null;
|
||||
|
||||
@ -219,9 +216,9 @@ class XCDevice {
|
||||
_setupDeviceIdentifierByEventStream();
|
||||
}));
|
||||
} on ProcessException catch (exception, stackTrace) {
|
||||
_deviceIdentifierByEvent.addError(exception, stackTrace);
|
||||
_deviceIdentifierByEvent?.addError(exception, stackTrace);
|
||||
} on ArgumentError catch (exception, stackTrace) {
|
||||
_deviceIdentifierByEvent.addError(exception, stackTrace);
|
||||
_deviceIdentifierByEvent?.addError(exception, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,8 +227,8 @@ class XCDevice {
|
||||
}
|
||||
|
||||
/// [timeout] defaults to 2 seconds.
|
||||
Future<List<IOSDevice>> getAvailableIOSDevices({ Duration timeout }) async {
|
||||
final List<dynamic> allAvailableDevices = await _getAllDevices(timeout: timeout ?? const Duration(seconds: 2));
|
||||
Future<List<IOSDevice>> getAvailableIOSDevices({ Duration? timeout }) async {
|
||||
final List<Object>? allAvailableDevices = await _getAllDevices(timeout: timeout ?? const Duration(seconds: 2));
|
||||
|
||||
if (allAvailableDevices == null) {
|
||||
return const <IOSDevice>[];
|
||||
@ -275,22 +272,29 @@ class XCDevice {
|
||||
// ...
|
||||
|
||||
final List<IOSDevice> devices = <IOSDevice>[];
|
||||
for (final dynamic device in allAvailableDevices) {
|
||||
if (device is Map<String, dynamic>) {
|
||||
for (final Object device in allAvailableDevices) {
|
||||
if (device is Map<String, Object?>) {
|
||||
// Only include iPhone, iPad, iPod, or other iOS devices.
|
||||
if (!_isIPhoneOSDevice(device)) {
|
||||
continue;
|
||||
}
|
||||
final String? identifier = device['identifier'] as String?;
|
||||
final String? name = device['name'] as String?;
|
||||
if (identifier == null || name == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> errorProperties = _errorProperties(device);
|
||||
final Map<String, Object?>? errorProperties = _errorProperties(device);
|
||||
if (errorProperties != null) {
|
||||
final String errorMessage = _parseErrorMessage(errorProperties);
|
||||
if (errorMessage.contains('not paired')) {
|
||||
UsageEvent('device', 'ios-trust-failure', flutterUsage: globals.flutterUsage).send();
|
||||
final String? errorMessage = _parseErrorMessage(errorProperties);
|
||||
if (errorMessage != null) {
|
||||
if (errorMessage.contains('not paired')) {
|
||||
UsageEvent('device', 'ios-trust-failure', flutterUsage: globals.flutterUsage).send();
|
||||
}
|
||||
_logger.printTrace(errorMessage);
|
||||
}
|
||||
_logger.printTrace(errorMessage);
|
||||
|
||||
final int code = _errorCode(errorProperties);
|
||||
final int? code = _errorCode(errorProperties);
|
||||
|
||||
// Temporary error -10: iPhone is busy: Preparing debugger support for iPhone.
|
||||
// Sometimes the app launch will fail on these devices until Xcode is done setting up the device.
|
||||
@ -308,18 +312,18 @@ class XCDevice {
|
||||
continue;
|
||||
}
|
||||
|
||||
String sdkVersion = _sdkVersion(device);
|
||||
String? sdkVersion = _sdkVersion(device);
|
||||
|
||||
if (sdkVersion != null) {
|
||||
final String buildVersion = _buildVersion(device);
|
||||
final String? buildVersion = _buildVersion(device);
|
||||
if (buildVersion != null) {
|
||||
sdkVersion = '$sdkVersion $buildVersion';
|
||||
}
|
||||
}
|
||||
|
||||
devices.add(IOSDevice(
|
||||
device['identifier'] as String,
|
||||
name: device['name'] as String,
|
||||
identifier,
|
||||
name: name,
|
||||
cpuArchitecture: _cpuArchitecture(device),
|
||||
interfaceType: interface,
|
||||
sdkVersion: sdkVersion,
|
||||
@ -338,33 +342,30 @@ class XCDevice {
|
||||
|
||||
/// Despite the name, com.apple.platform.iphoneos includes iPhone, iPads, and all iOS devices.
|
||||
/// Excludes simulators.
|
||||
static bool _isIPhoneOSDevice(Map<String, dynamic> deviceProperties) {
|
||||
if (deviceProperties.containsKey('platform')) {
|
||||
final String platform = deviceProperties['platform'] as String;
|
||||
static bool _isIPhoneOSDevice(Map<String, Object?> deviceProperties) {
|
||||
final Object? platform = deviceProperties['platform'];
|
||||
if (platform is String) {
|
||||
return platform == 'com.apple.platform.iphoneos';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Map<String, dynamic> _errorProperties(Map<String, dynamic> deviceProperties) {
|
||||
if (deviceProperties.containsKey('error')) {
|
||||
return deviceProperties['error'] as Map<String, dynamic>;
|
||||
}
|
||||
return null;
|
||||
static Map<String, Object?>? _errorProperties(Map<String, Object?> deviceProperties) {
|
||||
final Object? error = deviceProperties['error'];
|
||||
return error is Map<String, Object?> ? error : null;
|
||||
}
|
||||
|
||||
static int _errorCode(Map<String, dynamic> errorProperties) {
|
||||
if (errorProperties.containsKey('code') && errorProperties['code'] is int) {
|
||||
return errorProperties['code'] as int;
|
||||
}
|
||||
return null;
|
||||
static int? _errorCode(Map<String, Object?> errorProperties) {
|
||||
final Object? code = errorProperties['code'];
|
||||
return code is int ? code : null;
|
||||
}
|
||||
|
||||
static IOSDeviceConnectionInterface _interfaceType(Map<String, dynamic> deviceProperties) {
|
||||
static IOSDeviceConnectionInterface _interfaceType(Map<String, Object?> deviceProperties) {
|
||||
// Interface can be "usb", "network", or "none" for simulators
|
||||
// and unknown future interfaces.
|
||||
if (deviceProperties.containsKey('interface')) {
|
||||
if ((deviceProperties['interface'] as String).toLowerCase() == 'network') {
|
||||
final Object? interface = deviceProperties['interface'];
|
||||
if (interface is String) {
|
||||
if (interface.toLowerCase() == 'network') {
|
||||
return IOSDeviceConnectionInterface.network;
|
||||
} else {
|
||||
return IOSDeviceConnectionInterface.usb;
|
||||
@ -374,13 +375,13 @@ class XCDevice {
|
||||
return IOSDeviceConnectionInterface.none;
|
||||
}
|
||||
|
||||
static String _sdkVersion(Map<String, dynamic> deviceProperties) {
|
||||
if (deviceProperties.containsKey('operatingSystemVersion')) {
|
||||
static String? _sdkVersion(Map<String, Object?> deviceProperties) {
|
||||
final Object? operatingSystemVersion = deviceProperties['operatingSystemVersion'];
|
||||
if (operatingSystemVersion is String) {
|
||||
// Parse out the OS version, ignore the build number in parentheses.
|
||||
// "13.3 (17C54)"
|
||||
final RegExp operatingSystemRegex = RegExp(r'(.*) \(.*\)$');
|
||||
final String operatingSystemVersion = deviceProperties['operatingSystemVersion'] as String;
|
||||
if(operatingSystemRegex.hasMatch(operatingSystemVersion.trim())) {
|
||||
if (operatingSystemRegex.hasMatch(operatingSystemVersion.trim())) {
|
||||
return operatingSystemRegex.firstMatch(operatingSystemVersion.trim())?.group(1);
|
||||
}
|
||||
return operatingSystemVersion;
|
||||
@ -388,20 +389,20 @@ class XCDevice {
|
||||
return null;
|
||||
}
|
||||
|
||||
static String _buildVersion(Map<String, dynamic> deviceProperties) {
|
||||
if (deviceProperties.containsKey('operatingSystemVersion')) {
|
||||
static String? _buildVersion(Map<String, Object?> deviceProperties) {
|
||||
final Object? operatingSystemVersion = deviceProperties['operatingSystemVersion'];
|
||||
if (operatingSystemVersion is String) {
|
||||
// Parse out the build version, for example 17C54 from "13.3 (17C54)".
|
||||
final RegExp buildVersionRegex = RegExp(r'\(.*\)$');
|
||||
final String operatingSystemVersion = deviceProperties['operatingSystemVersion'] as String;
|
||||
return buildVersionRegex.firstMatch(operatingSystemVersion)?.group(0)?.replaceAll(RegExp('[()]'), '');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
DarwinArch _cpuArchitecture(Map<String, dynamic> deviceProperties) {
|
||||
DarwinArch cpuArchitecture;
|
||||
if (deviceProperties.containsKey('architecture')) {
|
||||
final String architecture = deviceProperties['architecture'] as String;
|
||||
DarwinArch _cpuArchitecture(Map<String, Object?> deviceProperties) {
|
||||
DarwinArch? cpuArchitecture;
|
||||
final Object? architecture = deviceProperties['architecture'];
|
||||
if (architecture is String) {
|
||||
try {
|
||||
cpuArchitecture = getIOSArchForName(architecture);
|
||||
} on Exception {
|
||||
@ -420,11 +421,11 @@ class XCDevice {
|
||||
);
|
||||
}
|
||||
}
|
||||
return cpuArchitecture;
|
||||
return cpuArchitecture ?? DarwinArch.arm64;
|
||||
}
|
||||
|
||||
/// Error message parsed from xcdevice. null if no error.
|
||||
static String _parseErrorMessage(Map<String, dynamic> errorProperties) {
|
||||
static String? _parseErrorMessage(Map<String, Object?>? errorProperties) {
|
||||
// {
|
||||
// "simulator" : false,
|
||||
// "operatingSystemVersion" : "13.3 (17C54)",
|
||||
@ -479,8 +480,8 @@ class XCDevice {
|
||||
|
||||
final StringBuffer errorMessage = StringBuffer('Error: ');
|
||||
|
||||
if (errorProperties.containsKey('description')) {
|
||||
final String description = errorProperties['description'] as String;
|
||||
final Object? description = errorProperties['description'];
|
||||
if (description is String) {
|
||||
errorMessage.write(description);
|
||||
if (!description.endsWith('.')) {
|
||||
errorMessage.write('.');
|
||||
@ -489,12 +490,12 @@ class XCDevice {
|
||||
errorMessage.write('Xcode pairing error.');
|
||||
}
|
||||
|
||||
if (errorProperties.containsKey('recoverySuggestion')) {
|
||||
final String recoverySuggestion = errorProperties['recoverySuggestion'] as String;
|
||||
final Object? recoverySuggestion = errorProperties['recoverySuggestion'];
|
||||
if (recoverySuggestion is String) {
|
||||
errorMessage.write(' $recoverySuggestion');
|
||||
}
|
||||
|
||||
final int code = _errorCode(errorProperties);
|
||||
final int? code = _errorCode(errorProperties);
|
||||
if (code != null) {
|
||||
errorMessage.write(' (code $code)');
|
||||
}
|
||||
@ -504,7 +505,7 @@ class XCDevice {
|
||||
|
||||
/// List of all devices reporting errors.
|
||||
Future<List<String>> getDiagnostics() async {
|
||||
final List<dynamic> allAvailableDevices = await _getAllDevices(
|
||||
final List<Object>? allAvailableDevices = await _getAllDevices(
|
||||
useCache: true,
|
||||
timeout: const Duration(seconds: 2)
|
||||
);
|
||||
@ -514,13 +515,12 @@ class XCDevice {
|
||||
}
|
||||
|
||||
final List<String> diagnostics = <String>[];
|
||||
for (final dynamic device in allAvailableDevices) {
|
||||
if (device is! Map) {
|
||||
for (final Object deviceProperties in allAvailableDevices) {
|
||||
if (deviceProperties is! Map<String, Object?>) {
|
||||
continue;
|
||||
}
|
||||
final Map<String, dynamic> deviceProperties = device as Map<String, dynamic>;
|
||||
final Map<String, dynamic> errorProperties = _errorProperties(deviceProperties);
|
||||
final String errorMessage = _parseErrorMessage(errorProperties);
|
||||
final Map<String, Object?>? errorProperties = _errorProperties(deviceProperties);
|
||||
final String? errorMessage = _parseErrorMessage(errorProperties);
|
||||
if (errorMessage != null) {
|
||||
diagnostics.add(errorMessage);
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io' as io;
|
||||
|
||||
@ -39,16 +37,17 @@ void main() {
|
||||
|
||||
group('IOSDevice', () {
|
||||
final List<Platform> unsupportedPlatforms = <Platform>[linuxPlatform, windowsPlatform];
|
||||
Cache cache;
|
||||
Logger logger;
|
||||
IOSDeploy iosDeploy;
|
||||
IMobileDevice iMobileDevice;
|
||||
FileSystem nullFileSystem;
|
||||
late Cache cache;
|
||||
late Logger logger;
|
||||
late IOSDeploy iosDeploy;
|
||||
late IMobileDevice iMobileDevice;
|
||||
late FileSystem fileSystem;
|
||||
|
||||
setUp(() {
|
||||
final Artifacts artifacts = Artifacts.test();
|
||||
cache = Cache.test(processManager: FakeProcessManager.any());
|
||||
logger = BufferLogger.test();
|
||||
fileSystem = MemoryFileSystem.test();
|
||||
iosDeploy = IOSDeploy(
|
||||
artifacts: artifacts,
|
||||
cache: cache,
|
||||
@ -68,7 +67,7 @@ void main() {
|
||||
IOSDevice(
|
||||
'device-123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -84,7 +83,7 @@ void main() {
|
||||
expect(IOSDevice(
|
||||
'device-123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -97,7 +96,7 @@ void main() {
|
||||
expect(IOSDevice(
|
||||
'device-123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -110,7 +109,7 @@ void main() {
|
||||
expect(IOSDevice(
|
||||
'device-123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -123,7 +122,7 @@ void main() {
|
||||
expect(IOSDevice(
|
||||
'device-123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -136,7 +135,7 @@ void main() {
|
||||
expect(IOSDevice(
|
||||
'device-123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -152,7 +151,7 @@ void main() {
|
||||
final IOSDevice device = IOSDevice(
|
||||
'device-123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -170,7 +169,7 @@ void main() {
|
||||
final IOSDevice device = IOSDevice(
|
||||
'device-123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -194,7 +193,7 @@ void main() {
|
||||
IOSDevice(
|
||||
'device-123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: platform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -211,21 +210,21 @@ void main() {
|
||||
}
|
||||
|
||||
group('.dispose()', () {
|
||||
IOSDevice device;
|
||||
FakeIOSApp appPackage1;
|
||||
FakeIOSApp appPackage2;
|
||||
IOSDeviceLogReader logReader1;
|
||||
IOSDeviceLogReader logReader2;
|
||||
FakeProcess process1;
|
||||
FakeProcess process2;
|
||||
FakeProcess process3;
|
||||
IOSDevicePortForwarder portForwarder;
|
||||
ForwardedPort forwardedPort;
|
||||
Cache cache;
|
||||
Logger logger;
|
||||
IOSDeploy iosDeploy;
|
||||
FileSystem nullFileSystem;
|
||||
IProxy iproxy;
|
||||
late IOSDevice device;
|
||||
late FakeIOSApp appPackage1;
|
||||
late FakeIOSApp appPackage2;
|
||||
late IOSDeviceLogReader logReader1;
|
||||
late IOSDeviceLogReader logReader2;
|
||||
late FakeProcess process1;
|
||||
late FakeProcess process2;
|
||||
late FakeProcess process3;
|
||||
late IOSDevicePortForwarder portForwarder;
|
||||
late ForwardedPort forwardedPort;
|
||||
late Cache cache;
|
||||
late Logger logger;
|
||||
late IOSDeploy iosDeploy;
|
||||
late FileSystem fileSystem;
|
||||
late IProxy iproxy;
|
||||
|
||||
IOSDevicePortForwarder createPortForwarder(
|
||||
ForwardedPort forwardedPort,
|
||||
@ -235,7 +234,7 @@ void main() {
|
||||
id: device.id,
|
||||
logger: logger,
|
||||
operatingSystemUtils: OperatingSystemUtils(
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: FakePlatform(operatingSystem: 'macos'),
|
||||
processManager: FakeProcessManager.any(),
|
||||
@ -253,7 +252,7 @@ void main() {
|
||||
final IOSDeviceLogReader logReader = IOSDeviceLogReader.create(
|
||||
device: device,
|
||||
app: appPackage,
|
||||
iMobileDevice: null, // not used by this test.
|
||||
iMobileDevice: IMobileDevice.test(processManager: FakeProcessManager.any()),
|
||||
);
|
||||
logReader.idevicesyslogProcess = process;
|
||||
return logReader;
|
||||
@ -269,6 +268,8 @@ void main() {
|
||||
cache = Cache.test(
|
||||
processManager: FakeProcessManager.any(),
|
||||
);
|
||||
fileSystem = MemoryFileSystem.test();
|
||||
logger = BufferLogger.test();
|
||||
iosDeploy = IOSDeploy(
|
||||
artifacts: Artifacts.test(),
|
||||
cache: cache,
|
||||
@ -282,7 +283,7 @@ void main() {
|
||||
device = IOSDevice(
|
||||
'123',
|
||||
iProxy: IProxy.test(logger: logger, processManager: FakeProcessManager.any()),
|
||||
fileSystem: nullFileSystem,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
iosDeploy: iosDeploy,
|
||||
@ -309,15 +310,15 @@ void main() {
|
||||
});
|
||||
|
||||
group('polling', () {
|
||||
FakeXcdevice xcdevice;
|
||||
Cache cache;
|
||||
FakeProcessManager fakeProcessManager;
|
||||
BufferLogger logger;
|
||||
IOSDeploy iosDeploy;
|
||||
IMobileDevice iMobileDevice;
|
||||
IOSWorkflow iosWorkflow;
|
||||
IOSDevice device1;
|
||||
IOSDevice device2;
|
||||
late FakeXcdevice xcdevice;
|
||||
late Cache cache;
|
||||
late FakeProcessManager fakeProcessManager;
|
||||
late BufferLogger logger;
|
||||
late IOSDeploy iosDeploy;
|
||||
late IMobileDevice iMobileDevice;
|
||||
late IOSWorkflow iosWorkflow;
|
||||
late IOSDevice device1;
|
||||
late IOSDevice device2;
|
||||
|
||||
setUp(() {
|
||||
xcdevice = FakeXcdevice();
|
||||
@ -414,22 +415,22 @@ void main() {
|
||||
await iosDevices.startPolling();
|
||||
expect(xcdevice.getAvailableIOSDevicesCount, 1);
|
||||
|
||||
expect(iosDevices.deviceNotifier.items, isEmpty);
|
||||
expect(iosDevices.deviceNotifier!.items, isEmpty);
|
||||
expect(xcdevice.deviceEventController.hasListener, isTrue);
|
||||
|
||||
xcdevice.deviceEventController.add(<XCDeviceEvent, String>{
|
||||
XCDeviceEvent.attach: 'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
|
||||
});
|
||||
await added.future;
|
||||
expect(iosDevices.deviceNotifier.items.length, 2);
|
||||
expect(iosDevices.deviceNotifier.items, contains(device1));
|
||||
expect(iosDevices.deviceNotifier.items, contains(device2));
|
||||
expect(iosDevices.deviceNotifier!.items.length, 2);
|
||||
expect(iosDevices.deviceNotifier!.items, contains(device1));
|
||||
expect(iosDevices.deviceNotifier!.items, contains(device2));
|
||||
|
||||
xcdevice.deviceEventController.add(<XCDeviceEvent, String>{
|
||||
XCDeviceEvent.detach: 'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
|
||||
});
|
||||
await removed.future;
|
||||
expect(iosDevices.deviceNotifier.items, <Device>[device2]);
|
||||
expect(iosDevices.deviceNotifier!.items, <Device>[device2]);
|
||||
|
||||
// Remove stream will throw over-completion if called more than once
|
||||
// which proves this is ignored.
|
||||
@ -489,7 +490,7 @@ void main() {
|
||||
xcdevice.devices.add(<IOSDevice>[]);
|
||||
|
||||
await iosDevices.startPolling();
|
||||
expect(iosDevices.deviceNotifier.items, isEmpty);
|
||||
expect(iosDevices.deviceNotifier!.items, isEmpty);
|
||||
expect(xcdevice.deviceEventController.hasListener, isTrue);
|
||||
|
||||
iosDevices.dispose();
|
||||
@ -531,9 +532,9 @@ void main() {
|
||||
});
|
||||
|
||||
group('getDiagnostics', () {
|
||||
FakeXcdevice xcdevice;
|
||||
IOSWorkflow iosWorkflow;
|
||||
Logger logger;
|
||||
late FakeXcdevice xcdevice;
|
||||
late IOSWorkflow iosWorkflow;
|
||||
late Logger logger;
|
||||
|
||||
setUp(() {
|
||||
xcdevice = FakeXcdevice();
|
||||
@ -601,7 +602,7 @@ class FakeXcdevice extends Fake implements XCDevice {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<IOSDevice>> getAvailableIOSDevices({Duration timeout}) async {
|
||||
Future<List<IOSDevice>> getAvailableIOSDevices({Duration? timeout}) async {
|
||||
return devices[getAvailableIOSDevicesCount++];
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,16 @@
|
||||
// @dart = 2.8
|
||||
|
||||
import 'package:file/memory.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/platform.dart';
|
||||
import 'package:flutter_tools/src/build_info.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/iproxy.dart';
|
||||
import 'package:flutter_tools/src/ios/mac.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
@ -76,17 +80,26 @@ flutter:
|
||||
}
|
||||
|
||||
IOSDevice setUpIOSDevice(FileSystem fileSystem) {
|
||||
final Platform platform = FakePlatform(operatingSystem: 'macos');
|
||||
final Logger logger = BufferLogger.test();
|
||||
final ProcessManager processManager = FakeProcessManager.any();
|
||||
return IOSDevice(
|
||||
'test',
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
iosDeploy: null, // not used in this test
|
||||
iMobileDevice: null, // not used in this test
|
||||
platform: FakePlatform(operatingSystem: 'macos'),
|
||||
logger: logger,
|
||||
iosDeploy: IOSDeploy(
|
||||
platform: platform,
|
||||
logger: logger,
|
||||
processManager: processManager,
|
||||
artifacts: Artifacts.test(),
|
||||
cache: Cache.test(processManager: processManager),
|
||||
),
|
||||
iMobileDevice: IMobileDevice.test(processManager: processManager),
|
||||
platform: platform,
|
||||
name: 'iPhone 1',
|
||||
sdkVersion: '13.3',
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
iProxy: IProxy.test(logger: BufferLogger.test(), processManager: FakeProcessManager.any()),
|
||||
iProxy: IProxy.test(logger: logger, processManager: processManager),
|
||||
interfaceType: IOSDeviceConnectionInterface.usb,
|
||||
);
|
||||
}
|
||||
|
@ -326,7 +326,7 @@ void main() {
|
||||
processManager: fakeProcessManager,
|
||||
logger: logger,
|
||||
xcode: xcode,
|
||||
platform: null,
|
||||
platform: FakePlatform(operatingSystem: 'macos'),
|
||||
artifacts: Artifacts.test(),
|
||||
cache: Cache.test(processManager: FakeProcessManager.any()),
|
||||
iproxy: IProxy.test(logger: logger, processManager: fakeProcessManager),
|
||||
@ -354,7 +354,7 @@ void main() {
|
||||
processManager: fakeProcessManager,
|
||||
logger: logger,
|
||||
xcode: xcode,
|
||||
platform: null,
|
||||
platform: FakePlatform(operatingSystem: 'macos'),
|
||||
artifacts: Artifacts.test(),
|
||||
cache: Cache.test(processManager: FakeProcessManager.any()),
|
||||
iproxy: IProxy.test(logger: logger, processManager: fakeProcessManager),
|
||||
|
Loading…
Reference in New Issue
Block a user