Unverified Commit 53e04de6 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Migrate mdns_discovery and ios simulator to null safety (#92124)

parent b0810bc9
......@@ -44,7 +44,7 @@ import 'flutter_features.dart';
import 'fuchsia/fuchsia_device.dart' show FuchsiaDeviceTools;
import 'fuchsia/fuchsia_sdk.dart' show FuchsiaSdk, FuchsiaArtifacts;
import 'fuchsia/fuchsia_workflow.dart' show FuchsiaWorkflow, fuchsiaWorkflow;
import 'globals.dart' as globals;
import 'globals_null_migrated.dart' as globals;
import 'ios/ios_workflow.dart';
import 'ios/iproxy.dart';
import 'ios/simulators.dart';
......
......@@ -6,9 +6,7 @@
import 'base/context.dart';
import 'doctor.dart';
import 'ios/simulators.dart';
export 'globals_null_migrated.dart';
Doctor get doctor => context.get<Doctor>();
IOSSimulatorUtils get iosSimulatorUtils => context.get<IOSSimulatorUtils>();
......@@ -31,6 +31,7 @@ import 'device.dart';
import 'fuchsia/fuchsia_sdk.dart';
import 'ios/ios_workflow.dart';
import 'ios/plist_parser.dart';
import 'ios/simulators.dart';
import 'ios/xcodeproj.dart';
import 'macos/cocoapods.dart';
import 'macos/cocoapods_validator.dart';
......@@ -54,6 +55,7 @@ Config get config => context.get<Config>()!;
CrashReporter? get crashReporter => context.get<CrashReporter>();
DeviceManager? get deviceManager => context.get<DeviceManager>();
HttpClientFactory? get httpClientFactory => context.get<HttpClientFactory>();
IOSSimulatorUtils? get iosSimulatorUtils => context.get<IOSSimulatorUtils>();
Logger get logger => context.get<Logger>()!;
OperatingSystemUtils get os => context.get<OperatingSystemUtils>()!;
Signals get signals => context.get<Signals>() ?? LocalSignals.instance;
......
......@@ -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:math' as math;
......@@ -34,7 +32,7 @@ const String iosSimulatorId = 'apple_ios_simulator';
class IOSSimulators extends PollingDeviceDiscovery {
IOSSimulators({
@required IOSSimulatorUtils iosSimulatorUtils,
required IOSSimulatorUtils iosSimulatorUtils,
}) : _iosSimulatorUtils = iosSimulatorUtils,
super('iOS simulators');
......@@ -44,10 +42,10 @@ class IOSSimulators extends PollingDeviceDiscovery {
bool get supportsPlatform => globals.platform.isMacOS;
@override
bool get canListAnything => globals.iosWorkflow.canListDevices;
bool get canListAnything => globals.iosWorkflow?.canListDevices ?? false;
@override
Future<List<Device>> pollingGetDevices({ Duration timeout }) async => _iosSimulatorUtils.getAttachedDevices();
Future<List<Device>> pollingGetDevices({ Duration? timeout }) async => _iosSimulatorUtils.getAttachedDevices();
@override
List<String> get wellKnownIds => const <String>[];
......@@ -55,9 +53,9 @@ class IOSSimulators extends PollingDeviceDiscovery {
class IOSSimulatorUtils {
IOSSimulatorUtils({
@required Xcode xcode,
@required Logger logger,
@required ProcessManager processManager,
required Xcode xcode,
required Logger logger,
required ProcessManager processManager,
}) : _simControl = SimControl(
logger: logger,
processManager: processManager,
......@@ -74,23 +72,33 @@ class IOSSimulatorUtils {
}
final List<SimDevice> connected = await _simControl.getConnectedDevices();
return connected.map<IOSSimulator>((SimDevice device) {
return connected.map<IOSSimulator?>((SimDevice device) {
final String? udid = device.udid;
final String? name = device.name;
if (udid == null) {
globals.printTrace('Could not parse simulator udid');
return null;
}
if (name == null) {
globals.printTrace('Could not parse simulator name');
return null;
}
return IOSSimulator(
device.udid,
name: device.name,
udid,
name: name,
simControl: _simControl,
simulatorCategory: device.category,
);
}).toList();
}).whereType<IOSSimulator>().toList();
}
}
/// A wrapper around the `simctl` command line tool.
class SimControl {
SimControl({
@required Logger logger,
@required ProcessManager processManager,
@required Xcode xcode,
required Logger logger,
required ProcessManager processManager,
required Xcode xcode,
}) : _logger = logger,
_xcode = xcode,
_processUtils = ProcessUtils(processManager: processManager, logger: logger);
......@@ -101,7 +109,7 @@ class SimControl {
/// Runs `simctl list --json` and returns the JSON of the corresponding
/// [section].
Future<Map<String, dynamic>> _list(SimControlListSection section) async {
Future<Map<String, Object?>> _list(SimControlListSection section) async {
// Sample output from `simctl list --json`:
//
// {
......@@ -130,21 +138,21 @@ class SimControl {
final RunResult results = await _processUtils.run(command);
if (results.exitCode != 0) {
_logger.printError('Error executing simctl: ${results.exitCode}\n${results.stderr}');
return <String, Map<String, dynamic>>{};
return <String, Map<String, Object?>>{};
}
try {
final Object decodeResult = (json.decode(results.stdout) as Map<String, dynamic>)[section.name];
if (decodeResult is Map<String, dynamic>) {
final Object? decodeResult = (json.decode(results.stdout) as Map<String, Object?>)[section.name];
if (decodeResult is Map<String, Object?>) {
return decodeResult;
}
_logger.printError('simctl returned unexpected JSON response: ${results.stdout}');
return <String, dynamic>{};
return <String, Object>{};
} on FormatException {
// We failed to parse the simctl output, or it returned junk.
// One known message is "Install Started" isn't valid JSON but is
// returned sometimes.
_logger.printError('simctl returned non-JSON response: ${results.stdout}');
return <String, dynamic>{};
return <String, Object>{};
}
}
......@@ -152,12 +160,12 @@ class SimControl {
Future<List<SimDevice>> getDevices() async {
final List<SimDevice> devices = <SimDevice>[];
final Map<String, dynamic> devicesSection = await _list(SimControlListSection.devices);
final Map<String, Object?> devicesSection = await _list(SimControlListSection.devices);
for (final String deviceCategory in devicesSection.keys) {
final Object devicesData = devicesSection[deviceCategory];
if (devicesData != null && devicesData is List<dynamic>) {
for (final Map<String, dynamic> data in devicesData.map<Map<String, dynamic>>(castStringKeyedMap)) {
final Object? devicesData = devicesSection[deviceCategory];
if (devicesData != null && devicesData is List<Object?>) {
for (final Map<String, Object?> data in devicesData.map<Map<String, Object?>?>(castStringKeyedMap).whereType<Map<String, Object?>>()) {
devices.add(SimDevice(deviceCategory, data));
}
}
......@@ -220,7 +228,7 @@ class SimControl {
return result;
}
Future<RunResult> launch(String deviceId, String appIdentifier, [ List<String> launchArgs ]) async {
Future<RunResult> launch(String deviceId, String appIdentifier, [ List<String>? launchArgs ]) async {
RunResult result;
try {
result = await _processUtils.run(
......@@ -299,12 +307,12 @@ class SimDevice {
SimDevice(this.category, this.data);
final String category;
final Map<String, dynamic> data;
final Map<String, Object?> data;
String get state => data['state']?.toString();
String get availability => data['availability']?.toString();
String get name => data['name']?.toString();
String get udid => data['udid']?.toString();
String? get state => data['state']?.toString();
String? get availability => data['availability']?.toString();
String? get name => data['name']?.toString();
String? get udid => data['udid']?.toString();
bool get isBooted => state == 'Booted';
}
......@@ -312,9 +320,9 @@ class SimDevice {
class IOSSimulator extends Device {
IOSSimulator(
String id, {
this.name,
this.simulatorCategory,
@required SimControl simControl,
required this.name,
required this.simulatorCategory,
required SimControl simControl,
}) : _simControl = simControl,
super(
id,
......@@ -353,13 +361,13 @@ class IOSSimulator extends Device {
@override
bool supportsRuntimeMode(BuildMode buildMode) => buildMode == BuildMode.debug;
Map<ApplicationPackage, _IOSSimulatorLogReader> _logReaders;
_IOSSimulatorDevicePortForwarder _portForwarder;
final Map<IOSApp?, DeviceLogReader> _logReaders = <IOSApp?, DeviceLogReader>{};
_IOSSimulatorDevicePortForwarder? _portForwarder;
@override
Future<bool> isAppInstalled(
ApplicationPackage app, {
String userIdentifier,
String? userIdentifier,
}) {
return _simControl.isInstalled(id, app.id);
}
......@@ -370,7 +378,7 @@ class IOSSimulator extends Device {
@override
Future<bool> installApp(
covariant IOSApp app, {
String userIdentifier,
String? userIdentifier,
}) async {
try {
final IOSApp iosApp = app;
......@@ -384,7 +392,7 @@ class IOSSimulator extends Device {
@override
Future<bool> uninstallApp(
ApplicationPackage app, {
String userIdentifier,
String? userIdentifier,
}) async {
try {
await _simControl.uninstall(id, app.id);
......@@ -411,7 +419,7 @@ class IOSSimulator extends Device {
return true;
}
String _supportMessage;
String? _supportMessage;
@override
String supportMessage() {
......@@ -425,13 +433,13 @@ class IOSSimulator extends Device {
@override
Future<LaunchResult> startApp(
covariant 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,
String? userIdentifier,
}) async {
if (!prebuiltApplication && package is BuildableIOSApp) {
globals.printTrace('Building ${package.name} for $id.');
......@@ -439,7 +447,7 @@ class IOSSimulator extends Device {
try {
await _setupUpdatedApplicationBundle(package, debuggingOptions.buildInfo, mainPath);
} on ToolExit catch (e) {
globals.printError(e.message);
globals.printError('${e.message}');
return LaunchResult.failed();
}
} else {
......@@ -469,7 +477,7 @@ class IOSSimulator extends Device {
],
];
ProtocolDiscovery observatoryDiscovery;
ProtocolDiscovery? observatoryDiscovery;
if (debuggingOptions.debuggingEnabled) {
observatoryDiscovery = ProtocolDiscovery.observatory(
getLogReader(app: package),
......@@ -487,7 +495,11 @@ class IOSSimulator extends Device {
// parsing the xcodeproj or configuration files.
// See https://github.com/flutter/flutter/issues/31037 for more information.
final String plistPath = globals.fs.path.join(package.simulatorBundlePath, 'Info.plist');
final String bundleIdentifier = globals.plistParser.getValueFromFile(plistPath, PlistParser.kCFBundleIdentifierKey);
final String? bundleIdentifier = globals.plistParser.getValueFromFile(plistPath, PlistParser.kCFBundleIdentifierKey);
if (bundleIdentifier == null) {
globals.printError('Invalid prebuilt iOS app. Info.plist does not contain bundle identifier');
return LaunchResult.failed();
}
await _simControl.launch(id, bundleIdentifier, args);
} on Exception catch (error) {
......@@ -504,7 +516,7 @@ class IOSSimulator extends Device {
globals.printTrace('Waiting for observatory port to be available...');
try {
final Uri deviceUri = await observatoryDiscovery.uri;
final Uri? deviceUri = await observatoryDiscovery?.uri;
if (deviceUri != null) {
return LaunchResult.succeeded(observatoryUri: deviceUri);
}
......@@ -520,7 +532,7 @@ class IOSSimulator extends Device {
return LaunchResult.failed();
}
Future<void> _setupUpdatedApplicationBundle(covariant BuildableIOSApp app, BuildInfo buildInfo, String mainPath) async {
Future<void> _setupUpdatedApplicationBundle(covariant BuildableIOSApp app, BuildInfo buildInfo, String? mainPath) async {
// Step 1: Build the Xcode project.
// The build mode for the simulator is always debug.
assert(buildInfo.isDebug);
......@@ -550,17 +562,18 @@ class IOSSimulator extends Device {
@override
Future<bool> stopApp(
ApplicationPackage app, {
String userIdentifier,
String? userIdentifier,
}) async {
// Currently we don't have a way to stop an app running on iOS.
return false;
}
String get logFilePath {
return globals.platform.environment.containsKey('IOS_SIMULATOR_LOG_FILE_PATH')
? globals.platform.environment['IOS_SIMULATOR_LOG_FILE_PATH'].replaceAll('%{id}', id)
final String? logPath = globals.platform.environment['IOS_SIMULATOR_LOG_FILE_PATH'];
return logPath != null
? logPath.replaceAll('%{id}', id)
: globals.fs.path.join(
globals.fsUtils.homeDirPath,
globals.fsUtils.homeDirPath!,
'Library',
'Logs',
'CoreSimulator',
......@@ -578,18 +591,16 @@ class IOSSimulator extends Device {
final RegExp _iosSdkRegExp = RegExp(r'iOS( |-)(\d+)');
Future<int> get sdkMajorVersion async {
final Match sdkMatch = _iosSdkRegExp.firstMatch(await sdkNameAndVersion);
final Match? sdkMatch = _iosSdkRegExp.firstMatch(await sdkNameAndVersion);
return int.parse(sdkMatch?.group(2) ?? '11');
}
@override
DeviceLogReader getLogReader({
covariant IOSApp app,
IOSApp? app,
bool includePastLogs = false,
}) {
assert(app == null || app is IOSApp);
assert(!includePastLogs, 'Past log reading not supported on iOS simulators.');
_logReaders ??= <ApplicationPackage, _IOSSimulatorLogReader>{};
return _logReaders.putIfAbsent(app, () => _IOSSimulatorLogReader(this, app));
}
......@@ -630,11 +641,9 @@ class IOSSimulator extends Device {
@override
Future<void> dispose() async {
_logReaders?.forEach(
(ApplicationPackage application, _IOSSimulatorLogReader logReader) {
logReader.dispose();
},
);
for (final DeviceLogReader logReader in _logReaders.values) {
logReader.dispose();
}
await _portForwarder?.dispose();
}
}
......@@ -647,7 +656,7 @@ Future<Process> launchDeviceSystemLogTool(IOSSimulator device) async {
/// Launches the device log reader process on the host and parses unified logging.
@visibleForTesting
Future<Process> launchDeviceUnifiedLogging (IOSSimulator device, String appName) async {
Future<Process> launchDeviceUnifiedLogging (IOSSimulator device, String? appName) async {
// Make NSPredicate concatenation easier to read.
String orP(List<String> clauses) => '(${clauses.join(" OR ")})';
String andP(List<String> clauses) => clauses.join(' AND ');
......@@ -669,7 +678,7 @@ Future<Process> launchDeviceUnifiedLogging (IOSSimulator device, String appName)
]);
return globals.processUtils.start(<String>[
...globals.xcode.xcrunCommand(),
...globals.xcode!.xcrunCommand(),
'simctl',
'spawn',
device.id,
......@@ -683,7 +692,7 @@ Future<Process> launchDeviceUnifiedLogging (IOSSimulator device, String appName)
}
@visibleForTesting
Future<Process> launchSystemLogTool(IOSSimulator device) async {
Future<Process?> launchSystemLogTool(IOSSimulator device) async {
// Versions of iOS prior to 11 tail the simulator syslog file.
if (await device.sdkMajorVersion < 11) {
return globals.processUtils.start(<String>['tail', '-n', '0', '-F', '/private/var/log/system.log']);
......@@ -694,23 +703,20 @@ Future<Process> launchSystemLogTool(IOSSimulator device) async {
}
class _IOSSimulatorLogReader extends DeviceLogReader {
_IOSSimulatorLogReader(this.device, IOSApp app) {
_linesController = StreamController<String>.broadcast(
onListen: _start,
onCancel: _stop,
);
_appName = app?.name?.replaceAll('.app', '');
}
_IOSSimulatorLogReader(this.device, IOSApp? app) : _appName = app?.name?.replaceAll('.app', '');
final IOSSimulator device;
String _appName;
final String? _appName;
StreamController<String> _linesController;
late final StreamController<String> _linesController = StreamController<String>.broadcast(
onListen: _start,
onCancel: _stop,
);
// We log from two files: the device and the system log.
Process _deviceProcess;
Process _systemProcess;
Process? _deviceProcess;
Process? _systemProcess;
@override
Stream<String> get logLines => _linesController.stream;
......@@ -722,27 +728,27 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
// Unified logging iOS 11 and greater (introduced in iOS 10).
if (await device.sdkMajorVersion >= 11) {
_deviceProcess = await launchDeviceUnifiedLogging(device, _appName);
_deviceProcess.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onUnifiedLoggingLine);
_deviceProcess.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onUnifiedLoggingLine);
_deviceProcess?.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onUnifiedLoggingLine);
_deviceProcess?.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onUnifiedLoggingLine);
} else {
// Fall back to syslog parsing.
await device.ensureLogsExists();
_deviceProcess = await launchDeviceSystemLogTool(device);
_deviceProcess.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onSysLogDeviceLine);
_deviceProcess.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onSysLogDeviceLine);
_deviceProcess?.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onSysLogDeviceLine);
_deviceProcess?.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onSysLogDeviceLine);
}
// Track system.log crashes.
// ReportCrash[37965]: Saved crash report for FlutterRunner[37941]...
_systemProcess = await launchSystemLogTool(device);
if (_systemProcess != null) {
_systemProcess.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onSystemLine);
_systemProcess.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onSystemLine);
_systemProcess?.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onSystemLine);
_systemProcess?.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter()).listen(_onSystemLine);
}
// We don't want to wait for the process or its callback. Best effort
// cleanup in the callback.
unawaited(_deviceProcess.exitCode.whenComplete(() {
unawaited(_deviceProcess?.exitCode.whenComplete(() {
if (_linesController.hasListener) {
_linesController.close();
}
......@@ -764,19 +770,20 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
// a multiline record
bool _lastLineMatched = false;
String _filterDeviceLine(String string) {
final Match match = _mapRegex.matchAsPrefix(string);
String? _filterDeviceLine(String string) {
final Match? match = _mapRegex.matchAsPrefix(string);
if (match != null) {
// The category contains the text between the date and the PID. Depending on which version of iOS being run,
// it can contain "hostname App Name" or just "App Name".
final String category = match.group(1);
final String tag = match.group(2);
final String content = match.group(3);
final String? category = match.group(1);
final String? tag = match.group(2);
final String? content = match.group(3);
// Filter out log lines from an app other than this one (category doesn't match the app name).
// If the hostname is included in the category, check that it doesn't end with the app name.
if (_appName != null && !category.endsWith(_appName)) {
final String? appName = _appName;
if (appName != null && category != null && !category.endsWith(appName)) {
return null;
}
......@@ -790,13 +797,13 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
}
// assertion failed: 15G1212 13E230: libxpc.dylib + 57882 [66C28065-C9DB-3C8E-926F-5A40210A6D1B]: 0x7d
if (content.startsWith('assertion failed: ') && content.contains(' libxpc.dylib ')) {
if (content != null && content.startsWith('assertion failed: ') && content.contains(' libxpc.dylib ')) {
return null;
}
if (_appName == null) {
if (appName == null) {
return '$category: $content';
} else if (category == _appName || category.endsWith(' $_appName')) {
} else if (category != null && (category == appName || category.endsWith(' $appName'))) {
return content;
}
......@@ -827,24 +834,24 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
return string;
}
String _lastLine;
String? _lastLine;
void _onSysLogDeviceLine(String line) {
globals.printTrace('[DEVICE LOG] $line');
final Match multi = _lastMessageMultipleRegex.matchAsPrefix(line);
final Match? multi = _lastMessageMultipleRegex.matchAsPrefix(line);
if (multi != null) {
if (_lastLine != null) {
int repeat = int.parse(multi.group(1));
int repeat = int.parse(multi.group(1)!);
repeat = math.max(0, math.min(100, repeat));
for (int i = 1; i < repeat; i++) {
_linesController.add(_lastLine);
_linesController.add(_lastLine!);
}
}
} else {
_lastLine = _filterDeviceLine(line);
if (_lastLine != null) {
_linesController.add(_lastLine);
_linesController.add(_lastLine!);
_lastLineMatched = true;
} else {
_lastLineMatched = false;
......@@ -856,11 +863,11 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
static final RegExp _unifiedLoggingEventMessageRegex = RegExp(r'.*"eventMessage" : (".*")');
void _onUnifiedLoggingLine(String line) {
// The log command predicate handles filtering, so every log eventMessage should be decoded and added.
final Match eventMessageMatch = _unifiedLoggingEventMessageRegex.firstMatch(line);
final Match? eventMessageMatch = _unifiedLoggingEventMessageRegex.firstMatch(line);
if (eventMessageMatch != null) {
final String message = eventMessageMatch.group(1);
final String message = eventMessageMatch.group(1)!;
try {
final dynamic decodedJson = jsonDecode(message);
final Object? decodedJson = jsonDecode(message);
if (decodedJson is String) {
_linesController.add(decodedJson);
}
......@@ -871,7 +878,7 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
}
String _filterSystemLog(String string) {
final Match match = _mapRegex.matchAsPrefix(string);
final Match? match = _mapRegex.matchAsPrefix(string);
return match == null ? string : '${match.group(1)}: ${match.group(2)}';
}
......@@ -916,37 +923,6 @@ int compareIosVersions(String v1, String v2) {
return v1Fragments.length.compareTo(v2Fragments.length);
}
/// Matches on device type given an identifier.
///
/// Example device type identifiers:
///
/// - ✓ com.apple.CoreSimulator.SimDeviceType.iPhone-5
/// - ✓ com.apple.CoreSimulator.SimDeviceType.iPhone-6
/// - ✓ com.apple.CoreSimulator.SimDeviceType.iPhone-6s-Plus
/// - ✗ com.apple.CoreSimulator.SimDeviceType.iPad-2
/// - ✗ com.apple.CoreSimulator.SimDeviceType.Apple-Watch-38mm
final RegExp _iosDeviceTypePattern =
RegExp(r'com.apple.CoreSimulator.SimDeviceType.iPhone-(\d+)(.*)');
int compareIphoneVersions(String id1, String id2) {
final Match m1 = _iosDeviceTypePattern.firstMatch(id1);
final Match m2 = _iosDeviceTypePattern.firstMatch(id2);
final int v1 = int.parse(m1[1]);
final int v2 = int.parse(m2[1]);
if (v1 != v2) {
return v1.compareTo(v2);
}
// Sorted in the least preferred first order.
const List<String> qualifiers = <String>['-Plus', '', 's-Plus', 's'];
final int q1 = qualifiers.indexOf(m1[2]);
final int q2 = qualifiers.indexOf(m2[2]);
return q1.compareTo(q2);
}
class _IOSSimulatorDevicePortForwarder extends DevicePortForwarder {
_IOSSimulatorDevicePortForwarder(this.device);
......@@ -958,7 +934,7 @@ class _IOSSimulatorDevicePortForwarder extends DevicePortForwarder {
List<ForwardedPort> get forwardedPorts => _ports;
@override
Future<int> forward(int devicePort, { int hostPort }) async {
Future<int> forward(int devicePort, { int? hostPort }) async {
if (hostPort == null || hostPort == 0) {
hostPort = devicePort;
}
......
......@@ -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 'package:meta/meta.dart';
import 'package:multicast_dns/multicast_dns.dart';
......@@ -24,9 +22,9 @@ class MDnsObservatoryDiscovery {
/// automatically select which application to use if multiple are advertising
/// Dart observatory ports.
MDnsObservatoryDiscovery({
MDnsClient mdnsClient,
@required Logger logger,
@required Usage flutterUsage,
MDnsClient? mdnsClient,
required Logger logger,
required Usage flutterUsage,
}): _client = mdnsClient ?? MDnsClient(),
_logger = logger,
_flutterUsage = flutterUsage;
......@@ -38,7 +36,7 @@ class MDnsObservatoryDiscovery {
@visibleForTesting
static const String dartObservatoryName = '_dartobservatory._tcp.local';
static MDnsObservatoryDiscovery get instance => context.get<MDnsObservatoryDiscovery>();
static MDnsObservatoryDiscovery? get instance => context.get<MDnsObservatoryDiscovery>();
/// Executes an mDNS query for a Dart Observatory.
///
......@@ -59,7 +57,7 @@ class MDnsObservatoryDiscovery {
/// it will return that instance's information regardless of what application
/// the Observatory instance is for.
@visibleForTesting
Future<MDnsObservatoryDiscoveryResult> query({String applicationId, int deviceVmservicePort}) async {
Future<MDnsObservatoryDiscoveryResult?> query({String? applicationId, int? deviceVmservicePort}) async {
_logger.printTrace('Checking for advertised Dart observatories...');
try {
await _client.start();
......@@ -78,7 +76,7 @@ class MDnsObservatoryDiscovery {
.map<String>((PtrResourceRecord record) => record.domainName)
.toSet();
String domainName;
String? domainName;
if (applicationId != null) {
for (final String name in uniqueDomainNames) {
if (name.toLowerCase().startsWith(applicationId.toLowerCase())) {
......@@ -93,7 +91,7 @@ class MDnsObservatoryDiscovery {
final StringBuffer buffer = StringBuffer();
buffer.writeln('There are multiple observatory ports available.');
buffer.writeln('Rerun this command with one of the following passed in as the appId:');
buffer.writeln('');
buffer.writeln();
for (final String uniqueDomainName in uniqueDomainNames) {
buffer.writeln(' flutter attach --app-id ${uniqueDomainName.replaceAll('.$dartObservatoryName', '')}');
}
......@@ -120,15 +118,18 @@ class MDnsObservatoryDiscovery {
.lookup<TxtResourceRecord>(
ResourceRecordQuery.text(domainName),
)
?.toList();
.toList();
if (txt == null || txt.isEmpty) {
return MDnsObservatoryDiscoveryResult(srv.first.port, '');
}
const String authCodePrefix = 'authCode=';
final String raw = txt.first.text.split('\n').firstWhere(
(String s) => s.startsWith(authCodePrefix),
orElse: () => null,
);
String? raw;
for (final String record in txt.first.text.split('\n')) {
if (record.startsWith(authCodePrefix)) {
raw = record;
break;
}
}
if (raw == null) {
return MDnsObservatoryDiscoveryResult(srv.first.port, '');
}
......@@ -144,12 +145,12 @@ class MDnsObservatoryDiscovery {
}
}
Future<Uri> getObservatoryUri(String applicationId, Device device, {
Future<Uri?> getObservatoryUri(String applicationId, Device device, {
bool usesIpv6 = false,
int hostVmservicePort,
int deviceVmservicePort,
int? hostVmservicePort,
int? deviceVmservicePort,
}) async {
final MDnsObservatoryDiscoveryResult result = await query(
final MDnsObservatoryDiscoveryResult? result = await query(
applicationId: applicationId,
deviceVmservicePort: deviceVmservicePort,
);
......@@ -246,8 +247,8 @@ Future<Uri> buildObservatoryUri(
Device device,
String host,
int devicePort, [
int hostVmservicePort,
String authCode,
int? hostVmservicePort,
String? authCode,
]) async {
String path = '/';
if (authCode != null) {
......@@ -259,8 +260,8 @@ Future<Uri> buildObservatoryUri(
path += '/';
}
hostVmservicePort ??= 0;
final int actualHostPort = hostVmservicePort == 0 ?
await device.portForwarder.forward(devicePort) :
final int? actualHostPort = hostVmservicePort == 0 ?
await device.portForwarder?.forward(devicePort) :
hostVmservicePort;
return Uri(scheme: 'http', host: host, port: actualHostPort, path: path);
}
......@@ -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:file/file.dart';
......@@ -15,9 +13,9 @@ import 'convert.dart';
import 'device.dart';
import 'globals_null_migrated.dart' as globals;
Future<String> sharedSkSlWriter(Device device, Map<String, Object> data, {
File outputFile,
Logger logger,
Future<String?> sharedSkSlWriter(Device device, Map<String, Object> data, {
File? outputFile,
Logger? logger,
}) async {
logger ??= globals.logger;
if (data.isEmpty) {
......
......@@ -59,7 +59,9 @@ void main() {
testUsingContext('dispose() does not throw an exception', () async {
final IOSSimulator simulator = IOSSimulator(
'123',
name: 'iPhone 11',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-14-4',
);
final DevicePortForwarder portForwarder = simulator.portForwarder;
await portForwarder.forward(123);
......@@ -82,7 +84,9 @@ void main() {
testUsingContext('simulators only support debug mode', () async {
final IOSSimulator simulator = IOSSimulator(
'123',
name: 'iPhone 11',
simControl: FakeSimControl(),
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-14-4',
);
expect(simulator.supportsRuntimeMode(BuildMode.debug), true);
......@@ -106,7 +110,9 @@ void main() {
osx.environment['HOME'] = '/foo/bar';
final IOSSimulator simulator = IOSSimulator(
'123',
name: 'iPhone 11',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-14-4',
);
expect(simulator.logFilePath, '/foo/bar/Library/Logs/CoreSimulator/123/system.log');
}, overrides: <Type, Generator>{
......@@ -121,7 +127,9 @@ void main() {
osx.environment['IOS_SIMULATOR_LOG_FILE_PATH'] = '/baz/qux/%{id}/system.log';
final IOSSimulator simulator = IOSSimulator(
'456',
name: 'iPhone 11',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-14-4',
);
expect(simulator.logFilePath, '/baz/qux/456/system.log');
}, overrides: <Type, Generator>{
......@@ -154,33 +162,6 @@ void main() {
});
});
group('compareIphoneVersions', () {
testWithoutContext('compares correctly', () {
// This list must be sorted in ascending preference order
final List<String> testList = <String>[
'com.apple.CoreSimulator.SimDeviceType.iPhone-4s',
'com.apple.CoreSimulator.SimDeviceType.iPhone-5',
'com.apple.CoreSimulator.SimDeviceType.iPhone-5s',
'com.apple.CoreSimulator.SimDeviceType.iPhone-6strange',
'com.apple.CoreSimulator.SimDeviceType.iPhone-6-Plus',
'com.apple.CoreSimulator.SimDeviceType.iPhone-6',
'com.apple.CoreSimulator.SimDeviceType.iPhone-6s-Plus',
'com.apple.CoreSimulator.SimDeviceType.iPhone-6s',
];
for (int i = 0; i < testList.length; i++) {
expect(compareIphoneVersions(testList[i], testList[i]), 0);
}
for (int i = 0; i < testList.length - 1; i++) {
for (int j = i + 1; j < testList.length; j++) {
expect(compareIphoneVersions(testList[i], testList[j]), lessThan(0));
expect(compareIphoneVersions(testList[j], testList[i]), greaterThan(0));
}
}
});
});
group('sdkMajorVersion', () {
FakeSimControl simControl;
......@@ -235,6 +216,7 @@ void main() {
'x',
name: 'Apple TV',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.tvOS-14-5',
);
expect(simulator.isSupported(), false);
}, overrides: <Type, Generator>{
......@@ -248,6 +230,7 @@ void main() {
'x',
name: 'Apple Watch',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.watchOS-8-0',
).isSupported(), false);
}, overrides: <Type, Generator>{
Platform: () => osx,
......@@ -260,6 +243,7 @@ void main() {
'x',
name: 'iPad 2',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
).isSupported(), true);
}, overrides: <Type, Generator>{
Platform: () => osx,
......@@ -272,6 +256,7 @@ void main() {
'x',
name: 'iPad Retina',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
).isSupported(), true);
}, overrides: <Type, Generator>{
Platform: () => osx,
......@@ -284,6 +269,7 @@ void main() {
'x',
name: 'iPhone 5',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
).isSupported(), true);
}, overrides: <Type, Generator>{
Platform: () => osx,
......@@ -296,6 +282,7 @@ void main() {
'x',
name: 'iPhone 5s',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
).isSupported(), true);
}, overrides: <Type, Generator>{
Platform: () => osx,
......@@ -308,6 +295,7 @@ void main() {
'x',
name: 'iPhone SE',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
).isSupported(), true);
}, overrides: <Type, Generator>{
Platform: () => osx,
......@@ -320,6 +308,7 @@ void main() {
'x',
name: 'iPhone 7 Plus',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
).isSupported(), true);
}, overrides: <Type, Generator>{
Platform: () => osx,
......@@ -332,6 +321,7 @@ void main() {
'x',
name: 'iPhone X',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
).isSupported(), true);
}, overrides: <Type, Generator>{
Platform: () => osx,
......@@ -368,6 +358,7 @@ void main() {
'x',
name: 'iPhone SE',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
);
final File screenshot = MemoryFileSystem.test().file('screenshot.png');
......@@ -513,6 +504,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
final IOSSimulator device = IOSSimulator(
'123456',
name: 'iPhone 11',
simulatorCategory: 'iOS 10.0',
simControl: simControl,
);
......@@ -547,6 +539,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
final IOSSimulator device = IOSSimulator(
'123456',
name: 'iPhone 11',
simulatorCategory: 'iOS 10.3',
simControl: simControl,
);
......@@ -594,6 +587,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
final IOSSimulator device = IOSSimulator(
'123456',
name: 'iPhone 11',
simulatorCategory: 'iOS 10.3',
simControl: simControl,
);
......@@ -665,6 +659,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
final IOSSimulator device = IOSSimulator(
'123456',
name: 'iPhone 11',
simulatorCategory: 'iOS 11.0',
simControl: simControl,
);
......@@ -707,6 +702,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
final IOSSimulator device = IOSSimulator(
'123456',
name: 'iPhone 11',
simulatorCategory: 'iOS 11.0',
simControl: simControl,
);
......@@ -902,11 +898,13 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
FakePlistParser testPlistParser;
FakeSimControl simControl;
Xcode xcode;
BufferLogger logger;
setUp(() {
simControl = FakeSimControl();
xcode = Xcode.test(processManager: FakeProcessManager.any());
testPlistParser = FakePlistParser();
logger = BufferLogger.test();
});
testUsingContext("startApp uses compiled app's Info.plist to find CFBundleIdentifier", () async {
......@@ -933,7 +931,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
Xcode: () => xcode,
});
testUsingContext('startApp respects the enable software rendering flag', () async {
testUsingContext('startApp fails when cannot find CFBundleIdentifier', () async {
final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
......@@ -944,6 +942,33 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
final Directory mockDir = globals.fs.currentDirectory;
final IOSApp package = PrebuiltIOSApp(projectBundleId: 'incorrect', bundleName: 'name', bundleDir: mockDir);
const BuildInfo mockInfo = BuildInfo(BuildMode.debug, 'flavor', treeShakeIcons: false);
final DebuggingOptions mockOptions = DebuggingOptions.disabled(mockInfo);
final LaunchResult result = await device.startApp(package, prebuiltApplication: true, debuggingOptions: mockOptions);
expect(result.started, isFalse);
expect(simControl.requests, isEmpty);
expect(logger.errorText, contains('Invalid prebuilt iOS app. Info.plist does not contain bundle identifier'));
}, overrides: <Type, Generator>{
PlistParser: () => testPlistParser,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
Xcode: () => xcode,
});
testUsingContext('startApp respects the enable software rendering flag', () async {
final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 11.2',
simControl: simControl,
);
testPlistParser.setProperty('CFBundleIdentifier', 'correct');
final Directory mockDir = globals.fs.currentDirectory;
final IOSApp package = PrebuiltIOSApp(projectBundleId: 'correct', bundleName: 'name', bundleDir: mockDir);
const BuildInfo mockInfo = BuildInfo(BuildMode.debug, 'flavor', treeShakeIcons: false);
final DebuggingOptions mockOptions = DebuggingOptions.enabled(mockInfo, enableSoftwareRendering: true);
await device.startApp(package, prebuiltApplication: true, debuggingOptions: mockOptions);
......@@ -980,7 +1005,9 @@ flutter:
final IOSSimulator simulator = IOSSimulator(
'test',
name: 'iPhone 11',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
);
expect(simulator.isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
......@@ -998,7 +1025,9 @@ flutter:
final IOSSimulator simulator = IOSSimulator(
'test',
name: 'iPhone 11',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
);
expect(simulator.isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{
......@@ -1014,7 +1043,9 @@ flutter:
final IOSSimulator simulator = IOSSimulator(
'test',
name: 'iPhone 11',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
);
expect(simulator.isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{
......@@ -1026,7 +1057,9 @@ flutter:
testUsingContext('createDevFSWriter returns a LocalDevFSWriter', () {
final IOSSimulator simulator = IOSSimulator(
'test',
name: 'iPhone 11',
simControl: simControl,
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
);
expect(simulator.createDevFSWriter(null, ''), isA<LocalDevFSWriter>());
......
......@@ -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 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
......@@ -24,9 +22,9 @@ void main() {
setUp(() {
setNetworkInterfaceLister(
({
bool includeLoopback,
bool includeLinkLocal,
InternetAddressType type,
bool? includeLoopback,
bool? includeLinkLocal,
InternetAddressType? type,
}) async => <NetworkInterface>[],
);
});
......@@ -44,7 +42,7 @@ void main() {
logger: BufferLogger.test(),
flutterUsage: TestUsage(),
);
final int port = (await portDiscovery.query())?.port;
final int? port = (await portDiscovery.query())?.port;
expect(port, isNull);
});
......@@ -56,7 +54,7 @@ void main() {
logger: logger,
flutterUsage: TestUsage(),
);
final Uri uri = await portDiscovery.getObservatoryUri(
final Uri? uri = await portDiscovery.getObservatoryUri(
'',
FakeIOSDevice(),
);
......@@ -81,7 +79,7 @@ void main() {
logger: BufferLogger.test(),
flutterUsage: TestUsage(),
);
final int port = (await portDiscovery.query())?.port;
final int? port = (await portDiscovery.query())?.port;
expect(port, 123);
});
......@@ -107,7 +105,7 @@ void main() {
logger: BufferLogger.test(),
flutterUsage: TestUsage(),
);
final MDnsObservatoryDiscoveryResult result = await portDiscovery.query();
final MDnsObservatoryDiscoveryResult? result = await portDiscovery.query();
expect(result?.port, 123);
expect(result?.authCode, 'xyz/');
});
......@@ -157,7 +155,7 @@ void main() {
logger: BufferLogger.test(),
flutterUsage: TestUsage(),
);
final int port = (await portDiscovery.query(applicationId: 'fiz'))?.port;
final int? port = (await portDiscovery.query(applicationId: 'fiz'))?.port;
expect(port, 321);
});
......@@ -184,7 +182,7 @@ void main() {
logger: BufferLogger.test(),
flutterUsage: TestUsage(),
);
final int port = (await portDiscovery.query(applicationId: 'bar'))?.port;
final int? port = (await portDiscovery.query(applicationId: 'bar'))?.port;
expect(port, 1234);
});
......@@ -199,7 +197,7 @@ void main() {
logger: BufferLogger.test(),
flutterUsage: TestUsage(),
);
final int port = (await portDiscovery.query(applicationId: 'bar'))?.port;
final int? port = (await portDiscovery.query(applicationId: 'bar'))?.port;
expect(port, isNull);
});
......@@ -236,7 +234,7 @@ void main() {
logger: BufferLogger.test(),
flutterUsage: TestUsage(),
);
final Uri uri = await portDiscovery.getObservatoryUri('bar', device, hostVmservicePort: 0);
final Uri? uri = await portDiscovery.getObservatoryUri('bar', device, hostVmservicePort: 0);
expect(uri.toString(), 'http://127.0.0.1:123/');
});
});
......@@ -255,10 +253,10 @@ class FakeMDnsClient extends Fake implements MDnsClient {
@override
Future<void> start({
InternetAddress listenAddress,
NetworkInterfacesFactory interfacesFactory,
InternetAddress? listenAddress,
NetworkInterfacesFactory? interfacesFactory,
int mDnsPort = 5353,
InternetAddress mDnsAddress,
InternetAddress? mDnsAddress,
}) async {
if (osErrorOnStart) {
throw const OSError('Operation not supported on socket', 102);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment