mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
This reverts commit 3aa7a80053
.
This commit is contained in:
parent
eb4b6dc91b
commit
ce6fbf6668
|
@ -166,11 +166,6 @@ Future<T> runInContext<T>(
|
|||
fileSystem: globals.fs,
|
||||
xcodeProjectInterpreter: xcodeProjectInterpreter,
|
||||
),
|
||||
XCDevice: () => XCDevice(
|
||||
processManager: globals.processManager,
|
||||
logger: globals.logger,
|
||||
xcode: globals.xcode,
|
||||
),
|
||||
XcodeProjectInterpreter: () => XcodeProjectInterpreter(
|
||||
logger: globals.logger,
|
||||
processManager: globals.processManager,
|
||||
|
|
|
@ -57,8 +57,6 @@ Xcode get xcode => context.get<Xcode>();
|
|||
FlutterVersion get flutterVersion => context.get<FlutterVersion>();
|
||||
IMobileDevice get iMobileDevice => context.get<IMobileDevice>();
|
||||
|
||||
XCDevice get xcdevice => context.get<XCDevice>();
|
||||
|
||||
/// Display an error level message to the user. Commands should use this if they
|
||||
/// fail in some way.
|
||||
///
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
|
||||
import '../application_package.dart';
|
||||
import '../artifacts.dart';
|
||||
|
@ -19,7 +18,6 @@ import '../build_info.dart';
|
|||
import '../convert.dart';
|
||||
import '../device.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../macos/xcode.dart';
|
||||
import '../mdns_discovery.dart';
|
||||
import '../project.dart';
|
||||
import '../protocol_discovery.dart';
|
||||
|
@ -113,18 +111,11 @@ class IOSDevices extends PollingDeviceDiscovery {
|
|||
bool get canListAnything => iosWorkflow.canListDevices;
|
||||
|
||||
@override
|
||||
Future<List<Device>> pollingGetDevices() => IOSDevice.getAttachedDevices(globals.platform, globals.xcdevice);
|
||||
|
||||
@override
|
||||
Future<List<String>> getDiagnostics() => IOSDevice.getDiagnostics(globals.platform, globals.xcdevice);
|
||||
Future<List<Device>> pollingGetDevices() => IOSDevice.getAttachedDevices();
|
||||
}
|
||||
|
||||
class IOSDevice extends Device {
|
||||
IOSDevice(String id, {
|
||||
@required this.name,
|
||||
@required this.cpuArchitecture,
|
||||
@required String sdkVersion,
|
||||
})
|
||||
IOSDevice(String id, { this.name, String sdkVersion })
|
||||
: _sdkVersion = sdkVersion,
|
||||
super(
|
||||
id,
|
||||
|
@ -166,8 +157,6 @@ class IOSDevice extends Device {
|
|||
@override
|
||||
final String name;
|
||||
|
||||
final DarwinArch cpuArchitecture;
|
||||
|
||||
Map<IOSApp, DeviceLogReader> _logReaders;
|
||||
|
||||
DevicePortForwarder _portForwarder;
|
||||
|
@ -181,20 +170,34 @@ class IOSDevice extends Device {
|
|||
@override
|
||||
bool get supportsStartPaused => false;
|
||||
|
||||
static Future<List<IOSDevice>> getAttachedDevices(Platform platform, XCDevice xcdevice) async {
|
||||
if (!platform.isMacOS) {
|
||||
throw UnsupportedError('Control of iOS devices or simulators only supported on macOS.');
|
||||
static Future<List<IOSDevice>> getAttachedDevices() async {
|
||||
if (!globals.platform.isMacOS) {
|
||||
throw UnsupportedError('Control of iOS devices or simulators only supported on Mac OS.');
|
||||
}
|
||||
if (!globals.iMobileDevice.isInstalled) {
|
||||
return <IOSDevice>[];
|
||||
}
|
||||
|
||||
return await xcdevice.getAvailableTetheredIOSDevices();
|
||||
}
|
||||
final List<IOSDevice> devices = <IOSDevice>[];
|
||||
for (String id in (await globals.iMobileDevice.getAvailableDeviceIDs()).split('\n')) {
|
||||
id = id.trim();
|
||||
if (id.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
static Future<List<String>> getDiagnostics(Platform platform, XCDevice xcdevice) async {
|
||||
if (!platform.isMacOS) {
|
||||
return const <String>['Control of iOS devices or simulators only supported on macOS.'];
|
||||
try {
|
||||
final String deviceName = await globals.iMobileDevice.getInfoForDevice(id, 'DeviceName');
|
||||
final String sdkVersion = await globals.iMobileDevice.getInfoForDevice(id, 'ProductVersion');
|
||||
devices.add(IOSDevice(id, name: deviceName, sdkVersion: sdkVersion));
|
||||
} on IOSDeviceNotFoundError catch (error) {
|
||||
// Unable to find device with given udid. Possibly a network device.
|
||||
globals.printTrace('Error getting attached iOS device: $error');
|
||||
} on IOSDeviceNotTrustedError catch (error) {
|
||||
globals.printTrace('Error getting attached iOS device information: $error');
|
||||
UsageEvent('device', 'ios-trust-failure').send();
|
||||
}
|
||||
}
|
||||
|
||||
return await xcdevice.getDiagnostics();
|
||||
return devices;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -277,13 +280,24 @@ class IOSDevice extends Device {
|
|||
// TODO(chinmaygarde): Use mainPath, route.
|
||||
globals.printTrace('Building ${package.name} for $id');
|
||||
|
||||
String cpuArchitecture;
|
||||
|
||||
try {
|
||||
cpuArchitecture = await globals.iMobileDevice.getInfoForDevice(id, 'CPUArchitecture');
|
||||
} on IOSDeviceNotFoundError catch (e) {
|
||||
globals.printError(e.message);
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
|
||||
final DarwinArch iosArch = getIOSArchForName(cpuArchitecture);
|
||||
|
||||
// Step 1: Build the precompiled/DBC application if necessary.
|
||||
final XcodeBuildResult buildResult = await buildXcodeProject(
|
||||
app: package as BuildableIOSApp,
|
||||
buildInfo: debuggingOptions.buildInfo,
|
||||
targetOverride: mainPath,
|
||||
buildForDevice: true,
|
||||
activeArch: cpuArchitecture,
|
||||
activeArch: iosArch,
|
||||
);
|
||||
if (!buildResult.success) {
|
||||
globals.printError('Could not build the precompiled application for the device.');
|
||||
|
|
|
@ -13,11 +13,7 @@ import '../base/file_system.dart';
|
|||
import '../base/io.dart';
|
||||
import '../base/logger.dart';
|
||||
import '../base/process.dart';
|
||||
import '../build_info.dart';
|
||||
import '../convert.dart';
|
||||
import '../ios/devices.dart';
|
||||
import '../ios/xcodeproj.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
|
||||
const int kXcodeRequiredVersionMajor = 10;
|
||||
const int kXcodeRequiredVersionMinor = 2;
|
||||
|
@ -189,315 +185,3 @@ class Xcode {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A utility class for interacting with Xcode xcdevice command line tools.
|
||||
class XCDevice {
|
||||
XCDevice({
|
||||
@required ProcessManager processManager,
|
||||
@required Logger logger,
|
||||
@required Xcode xcode,
|
||||
}) : _processUtils = ProcessUtils(logger: logger, processManager: processManager),
|
||||
_logger = logger,
|
||||
_xcode = xcode;
|
||||
|
||||
final ProcessUtils _processUtils;
|
||||
final Logger _logger;
|
||||
final Xcode _xcode;
|
||||
|
||||
bool get isInstalled => _xcode.isInstalledAndMeetsVersionCheck && xcdevicePath != null;
|
||||
|
||||
String _xcdevicePath;
|
||||
String get xcdevicePath {
|
||||
if (_xcdevicePath == null) {
|
||||
try {
|
||||
_xcdevicePath = _processUtils.runSync(
|
||||
<String>[
|
||||
'xcrun',
|
||||
'--find',
|
||||
'xcdevice'
|
||||
],
|
||||
throwOnError: true,
|
||||
).stdout.trim();
|
||||
} on ProcessException catch (exception) {
|
||||
_logger.printTrace('Process exception finding xcdevice:\n$exception');
|
||||
} on ArgumentError catch (exception) {
|
||||
_logger.printTrace('Argument exception finding xcdevice:\n$exception');
|
||||
}
|
||||
}
|
||||
return _xcdevicePath;
|
||||
}
|
||||
|
||||
Future<List<dynamic>> _getAllDevices({bool useCache = false}) async {
|
||||
if (!isInstalled) {
|
||||
_logger.printTrace('Xcode not found. Run \'flutter doctor\' for more information.');
|
||||
return null;
|
||||
}
|
||||
if (useCache && _cachedListResults != null) {
|
||||
return _cachedListResults;
|
||||
}
|
||||
try {
|
||||
// USB-tethered devices should be found quickly. 1 second timeout is faster than the default.
|
||||
final RunResult result = await _processUtils.run(
|
||||
<String>[
|
||||
'xcrun',
|
||||
'xcdevice',
|
||||
'list',
|
||||
'--timeout',
|
||||
'1',
|
||||
],
|
||||
throwOnError: true,
|
||||
);
|
||||
if (result.exitCode == 0) {
|
||||
final List<dynamic> listResults = json.decode(result.stdout) as List<dynamic>;
|
||||
_cachedListResults = listResults;
|
||||
return listResults;
|
||||
}
|
||||
_logger.printTrace('xcdevice returned an error:\n${result.stderr}');
|
||||
} on ProcessException catch (exception) {
|
||||
_logger.printTrace('Process exception running xcdevice list:\n$exception');
|
||||
} on ArgumentError catch (exception) {
|
||||
_logger.printTrace('Argument exception running xcdevice list:\n$exception');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
List<dynamic> _cachedListResults;
|
||||
|
||||
/// List of devices available over USB.
|
||||
Future<List<IOSDevice>> getAvailableTetheredIOSDevices() async {
|
||||
final List<dynamic> allAvailableDevices = await _getAllDevices();
|
||||
|
||||
if (allAvailableDevices == null) {
|
||||
return const <IOSDevice>[];
|
||||
}
|
||||
|
||||
// [
|
||||
// {
|
||||
// "simulator" : true,
|
||||
// "operatingSystemVersion" : "13.3 (17K446)",
|
||||
// "available" : true,
|
||||
// "platform" : "com.apple.platform.appletvsimulator",
|
||||
// "modelCode" : "AppleTV5,3",
|
||||
// "identifier" : "CBB5E1ED-2172-446E-B4E7-F2B5823DBBA6",
|
||||
// "architecture" : "x86_64",
|
||||
// "modelName" : "Apple TV",
|
||||
// "name" : "Apple TV"
|
||||
// },
|
||||
// {
|
||||
// "simulator" : false,
|
||||
// "operatingSystemVersion" : "13.3 (17C54)",
|
||||
// "interface" : "usb",
|
||||
// "available" : true,
|
||||
// "platform" : "com.apple.platform.iphoneos",
|
||||
// "modelCode" : "iPhone8,1",
|
||||
// "identifier" : "d83d5bc53967baa0ee18626ba87b6254b2ab5418",
|
||||
// "architecture" : "arm64",
|
||||
// "modelName" : "iPhone 6s",
|
||||
// "name" : "iPhone"
|
||||
// },
|
||||
// {
|
||||
// "simulator" : true,
|
||||
// "operatingSystemVersion" : "6.1.1 (17S445)",
|
||||
// "available" : true,
|
||||
// "platform" : "com.apple.platform.watchsimulator",
|
||||
// "modelCode" : "Watch5,4",
|
||||
// "identifier" : "2D74FB11-88A0-44D0-B81E-C0C142B1C94A",
|
||||
// "architecture" : "i386",
|
||||
// "modelName" : "Apple Watch Series 5 - 44mm",
|
||||
// "name" : "Apple Watch Series 5 - 44mm"
|
||||
// },
|
||||
// ...
|
||||
|
||||
final List<IOSDevice> devices = <IOSDevice>[];
|
||||
for (final dynamic device in allAvailableDevices) {
|
||||
if (device is! Map) {
|
||||
continue;
|
||||
}
|
||||
final Map<String, dynamic> deviceProperties = device as Map<String, dynamic>;
|
||||
|
||||
// Only include iPhone, iPad, iPod, or other iOS devices.
|
||||
if (!_isIPhoneOSDevice(deviceProperties)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String errorMessage = _parseErrorMessage(deviceProperties);
|
||||
if (errorMessage != null) {
|
||||
if (errorMessage.contains('not paired')) {
|
||||
UsageEvent('device', 'ios-trust-failure').send();
|
||||
}
|
||||
_logger.printTrace(errorMessage);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// In case unavailable without an error (may not be possible...)
|
||||
if (!_isAvailable(deviceProperties)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only support USB devices, skip "network" interface (Xcode > Window > Devices and Simulators > Connect via network).
|
||||
if (!_isUSBTethered(deviceProperties)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
devices.add(IOSDevice(
|
||||
device['identifier'] as String,
|
||||
name: device['name'] as String,
|
||||
cpuArchitecture: _cpuArchitecture(deviceProperties),
|
||||
sdkVersion: _sdkVersion(deviceProperties),
|
||||
));
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
return platform == 'com.apple.platform.iphoneos';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _isAvailable(Map<String, dynamic> deviceProperties) {
|
||||
return deviceProperties.containsKey('available') && (deviceProperties['available'] as bool);
|
||||
}
|
||||
|
||||
static bool _isUSBTethered(Map<String, dynamic> deviceProperties) {
|
||||
// Interface can be "usb", "network", or not present for simulators.
|
||||
return deviceProperties.containsKey('interface') &&
|
||||
(deviceProperties['interface'] as String).toLowerCase() == 'usb';
|
||||
}
|
||||
|
||||
static String _sdkVersion(Map<String, dynamic> deviceProperties) {
|
||||
if (deviceProperties.containsKey('operatingSystemVersion')) {
|
||||
// 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;
|
||||
return operatingSystemRegex.firstMatch(operatingSystemVersion.trim())?.group(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
DarwinArch _cpuArchitecture(Map<String, dynamic> deviceProperties) {
|
||||
DarwinArch cpuArchitecture;
|
||||
if (deviceProperties.containsKey('architecture')) {
|
||||
final String architecture = deviceProperties['architecture'] as String;
|
||||
try {
|
||||
cpuArchitecture = getIOSArchForName(architecture);
|
||||
} catch (error) {
|
||||
// Fallback to default iOS architecture. Future-proof against a theoretical version
|
||||
// of Xcode that changes this string to something slightly different like "ARM64".
|
||||
cpuArchitecture ??= defaultIOSArchs.first;
|
||||
_logger.printError('Unknown architecture $architecture, defaulting to ${getNameForDarwinArch(cpuArchitecture)}');
|
||||
}
|
||||
}
|
||||
return cpuArchitecture;
|
||||
}
|
||||
|
||||
/// Error message parsed from xcdevice. null if no error.
|
||||
static String _parseErrorMessage(Map<String, dynamic> deviceProperties) {
|
||||
// {
|
||||
// "simulator" : false,
|
||||
// "operatingSystemVersion" : "13.3 (17C54)",
|
||||
// "interface" : "usb",
|
||||
// "available" : false,
|
||||
// "platform" : "com.apple.platform.iphoneos",
|
||||
// "modelCode" : "iPhone8,1",
|
||||
// "identifier" : "98206e7a4afd4aedaff06e687594e089dede3c44",
|
||||
// "architecture" : "arm64",
|
||||
// "modelName" : "iPhone 6s",
|
||||
// "name" : "iPhone",
|
||||
// "error" : {
|
||||
// "code" : -9,
|
||||
// "failureReason" : "",
|
||||
// "underlyingErrors" : [
|
||||
// {
|
||||
// "code" : 5,
|
||||
// "failureReason" : "allowsSecureServices: 1. isConnected: 0. Platform: <DVTPlatform:0x7f804ce32880:'com.apple.platform.iphoneos':<DVTFilePath:0x7f804ce32800:'\/Users\/magder\/Applications\/Xcode_11-3-1.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform'>>. DTDKDeviceIdentifierIsIDID: 0",
|
||||
// "description" : "📱<DVTiOSDevice (0x7f801f190450), iPhone, iPhone, 13.3 (17C54), d83d5bc53967baa0ee18626ba87b6254b2ab5418> -- Failed _shouldMakeReadyForDevelopment check even though device is not locked by passcode.",
|
||||
// "recoverySuggestion" : "",
|
||||
// "domain" : "com.apple.platform.iphoneos"
|
||||
// }
|
||||
// ],
|
||||
// "description" : "iPhone is not paired with your computer.",
|
||||
// "recoverySuggestion" : "To use iPhone with Xcode, unlock it and choose to trust this computer when prompted.",
|
||||
// "domain" : "com.apple.platform.iphoneos"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "simulator" : false,
|
||||
// "operatingSystemVersion" : "13.3 (17C54)",
|
||||
// "interface" : "usb",
|
||||
// "available" : false,
|
||||
// "platform" : "com.apple.platform.iphoneos",
|
||||
// "modelCode" : "iPhone8,1",
|
||||
// "identifier" : "d83d5bc53967baa0ee18626ba87b6254b2ab5418",
|
||||
// "architecture" : "arm64",
|
||||
// "modelName" : "iPhone 6s",
|
||||
// "name" : "iPhone",
|
||||
// "error" : {
|
||||
// "code" : -9,
|
||||
// "failureReason" : "",
|
||||
// "description" : "iPhone is not paired with your computer.",
|
||||
// "domain" : "com.apple.platform.iphoneos"
|
||||
// }
|
||||
// }
|
||||
// ...
|
||||
|
||||
if (!deviceProperties.containsKey('error')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> error = deviceProperties['error'] as Map<String, dynamic>;
|
||||
|
||||
final StringBuffer errorMessage = StringBuffer('Error: ');
|
||||
|
||||
if (error.containsKey('description')) {
|
||||
final String description = error['description'] as String;
|
||||
errorMessage.write(description);
|
||||
if (!description.endsWith('.')) {
|
||||
errorMessage.write('.');
|
||||
}
|
||||
} else {
|
||||
errorMessage.write('Xcode pairing error.');
|
||||
}
|
||||
|
||||
if (error.containsKey('recoverySuggestion')) {
|
||||
final String recoverySuggestion = error['recoverySuggestion'] as String;
|
||||
errorMessage.write(' $recoverySuggestion');
|
||||
}
|
||||
|
||||
if (error.containsKey('code') && error['code'] is int) {
|
||||
final int code = error['code'] as int;
|
||||
errorMessage.write(' (code $code)');
|
||||
}
|
||||
|
||||
return errorMessage.toString();
|
||||
}
|
||||
|
||||
/// List of all devices reporting errors.
|
||||
Future<List<String>> getDiagnostics() async {
|
||||
final List<dynamic> allAvailableDevices = await _getAllDevices(useCache: true);
|
||||
|
||||
if (allAvailableDevices == null) {
|
||||
return const <String>[];
|
||||
}
|
||||
|
||||
final List<String> diagnostics = <String>[];
|
||||
for (final dynamic device in allAvailableDevices) {
|
||||
if (device is! Map) {
|
||||
continue;
|
||||
}
|
||||
final Map<String, dynamic> deviceProperties = device as Map<String, dynamic>;
|
||||
final String errorMessage = _parseErrorMessage(deviceProperties);
|
||||
if (errorMessage != null) {
|
||||
diagnostics.add(errorMessage);
|
||||
}
|
||||
}
|
||||
return diagnostics;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,6 @@ class MockXcode extends Mock implements Xcode {}
|
|||
class MockFile extends Mock implements File {}
|
||||
class MockPortForwarder extends Mock implements DevicePortForwarder {}
|
||||
class MockUsage extends Mock implements Usage {}
|
||||
class MockXcdevice extends Mock implements XCDevice {}
|
||||
|
||||
void main() {
|
||||
final FakePlatform macPlatform = FakePlatform.fromPlatform(const LocalPlatform());
|
||||
|
@ -66,17 +65,17 @@ void main() {
|
|||
final List<Platform> unsupportedPlatforms = <Platform>[linuxPlatform, windowsPlatform];
|
||||
|
||||
testUsingContext('successfully instantiates on Mac OS', () {
|
||||
IOSDevice('device-123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
IOSDevice('device-123');
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
|
||||
testUsingContext('parses major version', () {
|
||||
expect(IOSDevice('device-123', name: 'iPhone 1', cpuArchitecture: DarwinArch.arm64, sdkVersion: '1.0.0').majorSdkVersion, 1);
|
||||
expect(IOSDevice('device-123', name: 'iPhone 1', cpuArchitecture: DarwinArch.arm64, sdkVersion: '13.1.1').majorSdkVersion, 13);
|
||||
expect(IOSDevice('device-123', name: 'iPhone 1', cpuArchitecture: DarwinArch.arm64, sdkVersion: '10').majorSdkVersion, 10);
|
||||
expect(IOSDevice('device-123', name: 'iPhone 1', cpuArchitecture: DarwinArch.arm64, sdkVersion: '0').majorSdkVersion, 0);
|
||||
expect(IOSDevice('device-123', name: 'iPhone 1', cpuArchitecture: DarwinArch.arm64, sdkVersion: 'bogus').majorSdkVersion, 0);
|
||||
expect(IOSDevice('device-123', sdkVersion: '1.0.0').majorSdkVersion, 1);
|
||||
expect(IOSDevice('device-123', sdkVersion: '13.1.1').majorSdkVersion, 13);
|
||||
expect(IOSDevice('device-123', sdkVersion: '10').majorSdkVersion, 10);
|
||||
expect(IOSDevice('device-123', sdkVersion: '0').majorSdkVersion, 0);
|
||||
expect(IOSDevice('device-123', sdkVersion: 'bogus').majorSdkVersion, 0);
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
|
@ -84,7 +83,7 @@ void main() {
|
|||
for (final Platform platform in unsupportedPlatforms) {
|
||||
testUsingContext('throws UnsupportedError exception if instantiated on ${platform.operatingSystem}', () {
|
||||
expect(
|
||||
() { IOSDevice('device-123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64); },
|
||||
() { IOSDevice('device-123'); },
|
||||
throwsAssertionError,
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
|
@ -133,7 +132,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext(' kills all log readers & port forwarders', () async {
|
||||
device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
device = IOSDevice('123');
|
||||
logReader1 = createLogReader(device, appPackage1, mockProcess1);
|
||||
logReader2 = createLogReader(device, appPackage2, mockProcess2);
|
||||
portForwarder = createPortForwarder(forwardedPort, device);
|
||||
|
@ -240,6 +239,9 @@ void main() {
|
|||
)).thenAnswer(
|
||||
(_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', ''))
|
||||
);
|
||||
|
||||
when(mockIMobileDevice.getInfoForDevice(any, 'CPUArchitecture'))
|
||||
.thenAnswer((_) => Future<String>.value('arm64'));
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
|
@ -250,7 +252,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('disposing device disposes the portForwarder', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
device.portForwarder = mockPortForwarder;
|
||||
device.setLogReader(mockApp, mockLogReader);
|
||||
await device.dispose();
|
||||
|
@ -259,8 +261,24 @@ void main() {
|
|||
Platform: () => macPlatform,
|
||||
});
|
||||
|
||||
testUsingContext('returns failed if the IOSDevice is not found', () async {
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
when(mockIMobileDevice.getInfoForDevice(any, 'CPUArchitecture')).thenThrow(
|
||||
const IOSDeviceNotFoundError(
|
||||
'ideviceinfo could not find device:\n'
|
||||
'No device found with udid 123, is it plugged in?\n'
|
||||
'Try unlocking attached devices.'
|
||||
)
|
||||
);
|
||||
final LaunchResult result = await device.startApp(mockApp);
|
||||
expect(result.started, false);
|
||||
}, overrides: <Type, Generator>{
|
||||
IMobileDevice: () => mockIMobileDevice,
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
|
||||
testUsingContext(' succeeds in debug mode via mDNS', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
device.portForwarder = mockPortForwarder;
|
||||
device.setLogReader(mockApp, mockLogReader);
|
||||
final Uri uri = Uri(
|
||||
|
@ -297,7 +315,7 @@ void main() {
|
|||
testUsingContext(' .forward() will kill iproxy processes before invoking a second', () async {
|
||||
const String deviceId = '123';
|
||||
const int devicePort = 456;
|
||||
final IOSDevice device = IOSDevice(deviceId, name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice(deviceId);
|
||||
final IOSDevicePortForwarder portForwarder = IOSDevicePortForwarder(device);
|
||||
bool firstRun = true;
|
||||
final MockProcess successProcess = MockProcess(
|
||||
|
@ -331,7 +349,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext(' succeeds in debug mode when mDNS fails by falling back to manual protocol discovery', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
device.portForwarder = mockPortForwarder;
|
||||
device.setLogReader(mockApp, mockLogReader);
|
||||
// Now that the reader is used, start writing messages to it.
|
||||
|
@ -363,7 +381,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext(' fails in debug mode when mDNS fails and when Observatory URI is malformed', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
device.portForwarder = mockPortForwarder;
|
||||
device.setLogReader(mockApp, mockLogReader);
|
||||
|
||||
|
@ -395,7 +413,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('succeeds in release mode', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
final LaunchResult launchResult = await device.startApp(mockApp,
|
||||
prebuiltApplication: true,
|
||||
debuggingOptions: DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null, treeShakeIcons: false)),
|
||||
|
@ -413,7 +431,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('succeeds with --cache-sksl', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
device.setLogReader(mockApp, mockLogReader);
|
||||
final Uri uri = Uri(
|
||||
scheme: 'http',
|
||||
|
@ -457,7 +475,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('succeeds with --device-vmservice-port', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
device.setLogReader(mockApp, mockLogReader);
|
||||
final Uri uri = Uri(
|
||||
scheme: 'http',
|
||||
|
@ -571,7 +589,7 @@ void main() {
|
|||
|
||||
final IOSApp app = await AbsoluteBuildableIOSApp.fromProject(
|
||||
FlutterProject.fromDirectory(projectDir).ios);
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
|
||||
// Pre-create the expected build products.
|
||||
targetBuildDir.createSync(recursive: true);
|
||||
|
@ -679,7 +697,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('installApp() invokes process with correct environment', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
const String bundlePath = '/path/to/bundle';
|
||||
final List<String> args = <String>[installerPath, '-i', bundlePath];
|
||||
when(mockApp.deviceBundlePath).thenReturn(bundlePath);
|
||||
|
@ -701,7 +719,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('isAppInstalled() invokes process with correct environment', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
final List<String> args = <String>[installerPath, '--list-apps'];
|
||||
when(mockProcessManager.run(args, environment: env))
|
||||
.thenAnswer(
|
||||
|
@ -718,7 +736,7 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('uninstallApp() invokes process with correct environment', () async {
|
||||
final IOSDevice device = IOSDevice('123', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123');
|
||||
final List<String> args = <String>[installerPath, '-U', appId];
|
||||
when(mockApp.id).thenReturn(appId);
|
||||
when(mockProcessManager.run(args, environment: env))
|
||||
|
@ -737,61 +755,90 @@ void main() {
|
|||
});
|
||||
|
||||
group('getAttachedDevices', () {
|
||||
MockXcdevice mockXcdevice;
|
||||
MockIMobileDevice mockIMobileDevice;
|
||||
|
||||
setUp(() {
|
||||
mockXcdevice = MockXcdevice();
|
||||
mockIMobileDevice = MockIMobileDevice();
|
||||
});
|
||||
|
||||
testUsingContext('return no devices if Xcode is not installed', () async {
|
||||
when(mockIMobileDevice.isInstalled).thenReturn(false);
|
||||
expect(await IOSDevice.getAttachedDevices(), isEmpty);
|
||||
}, overrides: <Type, Generator>{
|
||||
IMobileDevice: () => mockIMobileDevice,
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
|
||||
testUsingContext('returns no devices if none are attached', () async {
|
||||
when(globals.iMobileDevice.isInstalled).thenReturn(true);
|
||||
when(globals.iMobileDevice.getAvailableDeviceIDs())
|
||||
.thenAnswer((Invocation invocation) => Future<String>.value(''));
|
||||
final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
|
||||
expect(devices, isEmpty);
|
||||
}, overrides: <Type, Generator>{
|
||||
IMobileDevice: () => mockIMobileDevice,
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
|
||||
final List<Platform> unsupportedPlatforms = <Platform>[linuxPlatform, windowsPlatform];
|
||||
for (final Platform unsupportedPlatform in unsupportedPlatforms) {
|
||||
testWithoutContext('throws Unsupported Operation exception on ${unsupportedPlatform.operatingSystem}', () async {
|
||||
when(mockXcdevice.isInstalled).thenReturn(false);
|
||||
for (final Platform platform in unsupportedPlatforms) {
|
||||
testUsingContext('throws Unsupported Operation exception on ${platform.operatingSystem}', () async {
|
||||
when(globals.iMobileDevice.isInstalled).thenReturn(false);
|
||||
when(globals.iMobileDevice.getAvailableDeviceIDs())
|
||||
.thenAnswer((Invocation invocation) => Future<String>.value(''));
|
||||
expect(
|
||||
() async { await IOSDevice.getAttachedDevices(unsupportedPlatform, mockXcdevice); },
|
||||
() async { await IOSDevice.getAttachedDevices(); },
|
||||
throwsA(isA<UnsupportedError>()),
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
IMobileDevice: () => mockIMobileDevice,
|
||||
Platform: () => platform,
|
||||
});
|
||||
}
|
||||
|
||||
testUsingContext('returns attached devices', () async {
|
||||
when(mockXcdevice.isInstalled).thenReturn(true);
|
||||
final IOSDevice device = IOSDevice('d83d5bc53967baa0ee18626ba87b6254b2ab5418', name: 'Paired iPhone', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64);
|
||||
when(mockXcdevice.getAvailableTetheredIOSDevices())
|
||||
.thenAnswer((Invocation invocation) => Future<List<IOSDevice>>.value(<IOSDevice>[device]));
|
||||
|
||||
final List<IOSDevice> devices = await IOSDevice.getAttachedDevices(macPlatform, mockXcdevice);
|
||||
expect(devices, hasLength(1));
|
||||
expect(identical(devices.first, device), isTrue);
|
||||
when(globals.iMobileDevice.isInstalled).thenReturn(true);
|
||||
when(globals.iMobileDevice.getAvailableDeviceIDs())
|
||||
.thenAnswer((Invocation invocation) => Future<String>.value('''
|
||||
98206e7a4afd4aedaff06e687594e089dede3c44
|
||||
f577a7903cc54959be2e34bc4f7f80b7009efcf4
|
||||
'''));
|
||||
when(globals.iMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'DeviceName'))
|
||||
.thenAnswer((_) => Future<String>.value('La tele me regarde'));
|
||||
when(globals.iMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'ProductVersion'))
|
||||
.thenAnswer((_) => Future<String>.value('10.3.2'));
|
||||
when(globals.iMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'DeviceName'))
|
||||
.thenAnswer((_) => Future<String>.value('Puits sans fond'));
|
||||
when(globals.iMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'ProductVersion'))
|
||||
.thenAnswer((_) => Future<String>.value('11.0'));
|
||||
final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
|
||||
expect(devices, hasLength(2));
|
||||
expect(devices[0].id, '98206e7a4afd4aedaff06e687594e089dede3c44');
|
||||
expect(devices[0].name, 'La tele me regarde');
|
||||
expect(devices[1].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4');
|
||||
expect(devices[1].name, 'Puits sans fond');
|
||||
}, overrides: <Type, Generator>{
|
||||
IMobileDevice: () => mockIMobileDevice,
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
});
|
||||
|
||||
group('getDiagnostics', () {
|
||||
MockXcdevice mockXcdevice;
|
||||
|
||||
setUp(() {
|
||||
mockXcdevice = MockXcdevice();
|
||||
});
|
||||
|
||||
final List<Platform> unsupportedPlatforms = <Platform>[linuxPlatform, windowsPlatform];
|
||||
for (final Platform unsupportedPlatform in unsupportedPlatforms) {
|
||||
testWithoutContext('throws returns platform diagnostic exception on ${unsupportedPlatform.operatingSystem}', () async {
|
||||
when(mockXcdevice.isInstalled).thenReturn(false);
|
||||
expect((await IOSDevice.getDiagnostics(unsupportedPlatform, mockXcdevice)).first, 'Control of iOS devices or simulators only supported on macOS.');
|
||||
});
|
||||
}
|
||||
|
||||
testUsingContext('returns diagnostics', () async {
|
||||
when(mockXcdevice.isInstalled).thenReturn(true);
|
||||
when(mockXcdevice.getDiagnostics())
|
||||
.thenAnswer((Invocation invocation) => Future<List<String>>.value(<String>['Generic pairing error']));
|
||||
|
||||
final List<String> diagnostics = await IOSDevice.getDiagnostics(macPlatform, mockXcdevice);
|
||||
expect(diagnostics, hasLength(1));
|
||||
expect(diagnostics.first, 'Generic pairing error');
|
||||
testUsingContext('returns attached devices and ignores devices that cannot be found by ideviceinfo', () async {
|
||||
when(globals.iMobileDevice.isInstalled).thenReturn(true);
|
||||
when(globals.iMobileDevice.getAvailableDeviceIDs())
|
||||
.thenAnswer((Invocation invocation) => Future<String>.value('''
|
||||
98206e7a4afd4aedaff06e687594e089dede3c44
|
||||
f577a7903cc54959be2e34bc4f7f80b7009efcf4
|
||||
'''));
|
||||
when(globals.iMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'DeviceName'))
|
||||
.thenAnswer((_) => Future<String>.value('La tele me regarde'));
|
||||
when(globals.iMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'DeviceName'))
|
||||
.thenThrow(const IOSDeviceNotFoundError('Device not found'));
|
||||
final List<IOSDevice> devices = await IOSDevice.getAttachedDevices();
|
||||
expect(devices, hasLength(1));
|
||||
expect(devices[0].id, '98206e7a4afd4aedaff06e687594e089dede3c44');
|
||||
expect(devices[0].name, 'La tele me regarde');
|
||||
}, overrides: <Type, Generator>{
|
||||
IMobileDevice: () => mockIMobileDevice,
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
});
|
||||
|
@ -830,7 +877,7 @@ Runner(UIKit)[297] <Notice>: E is for enpitsu"
|
|||
return Future<Process>.value(mockProcess);
|
||||
});
|
||||
|
||||
final IOSDevice device = IOSDevice('123456', name: 'iPhone 1', sdkVersion: '10.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123456');
|
||||
final DeviceLogReader logReader = device.getLogReader(
|
||||
app: await BuildableIOSApp.fromProject(mockIosProject),
|
||||
);
|
||||
|
@ -841,7 +888,6 @@ Runner(UIKit)[297] <Notice>: E is for enpitsu"
|
|||
IMobileDevice: () => mockIMobileDevice,
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
|
||||
testUsingContext('includes multi-line Flutter logs in the output', () async {
|
||||
when(mockIMobileDevice.startLogger('123456')).thenAnswer((Invocation invocation) {
|
||||
final Process mockProcess = MockProcess(
|
||||
|
@ -856,7 +902,7 @@ Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
|
|||
return Future<Process>.value(mockProcess);
|
||||
});
|
||||
|
||||
final IOSDevice device = IOSDevice('123456', name: 'iPhone 1', sdkVersion: '10.3', cpuArchitecture: DarwinArch.arm64);
|
||||
final IOSDevice device = IOSDevice('123456');
|
||||
final DeviceLogReader logReader = device.getLogReader(
|
||||
app: await BuildableIOSApp.fromProject(mockIosProject),
|
||||
);
|
||||
|
@ -886,7 +932,7 @@ flutter:
|
|||
globals.fs.file('.packages').createSync();
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
|
||||
expect(IOSDevice('test', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64).isSupportedForProject(flutterProject), true);
|
||||
expect(IOSDevice('test').isSupportedForProject(flutterProject), true);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
|
@ -898,7 +944,7 @@ flutter:
|
|||
globals.fs.directory('ios').createSync();
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
|
||||
expect(IOSDevice('test', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64).isSupportedForProject(flutterProject), true);
|
||||
expect(IOSDevice('test').isSupportedForProject(flutterProject), true);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
|
@ -910,7 +956,7 @@ flutter:
|
|||
globals.fs.file('.packages').createSync();
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
|
||||
expect(IOSDevice('test', name: 'iPhone 1', sdkVersion: '13.3', cpuArchitecture: DarwinArch.arm64).isSupportedForProject(flutterProject), false);
|
||||
expect(IOSDevice('test').isSupportedForProject(flutterProject), false);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
|
|
|
@ -6,8 +6,6 @@ import 'package:file/memory.dart';
|
|||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult;
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/ios/devices.dart';
|
||||
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||
import 'package:flutter_tools/src/macos/xcode.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
@ -23,494 +21,160 @@ class MockPlatform extends Mock implements Platform {}
|
|||
|
||||
void main() {
|
||||
ProcessManager processManager;
|
||||
Xcode xcode;
|
||||
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
|
||||
MockPlatform platform;
|
||||
Logger logger;
|
||||
FileSystem fileSystem;
|
||||
|
||||
setUp(() {
|
||||
logger = MockLogger();
|
||||
fileSystem = MemoryFileSystem();
|
||||
processManager = MockProcessManager();
|
||||
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
|
||||
platform = MockPlatform();
|
||||
xcode = Xcode(
|
||||
logger: logger,
|
||||
platform: platform,
|
||||
fileSystem: fileSystem,
|
||||
processManager: processManager,
|
||||
xcodeProjectInterpreter: mockXcodeProjectInterpreter,
|
||||
);
|
||||
});
|
||||
|
||||
group('Xcode', () {
|
||||
Xcode xcode;
|
||||
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
|
||||
MockPlatform platform;
|
||||
FileSystem fileSystem;
|
||||
testWithoutContext('xcodeSelectPath returns null when xcode-select is not installed', () {
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenThrow(const ProcessException('/usr/bin/xcode-select', <String>['--print-path']));
|
||||
expect(xcode.xcodeSelectPath, isNull);
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenThrow(ArgumentError('Invalid argument(s): Cannot find executable for /usr/bin/xcode-select'));
|
||||
|
||||
setUp(() {
|
||||
fileSystem = MemoryFileSystem();
|
||||
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
|
||||
platform = MockPlatform();
|
||||
xcode = Xcode(
|
||||
logger: logger,
|
||||
platform: platform,
|
||||
fileSystem: fileSystem,
|
||||
processManager: processManager,
|
||||
xcodeProjectInterpreter: mockXcodeProjectInterpreter,
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('xcodeSelectPath returns null when xcode-select is not installed', () {
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenThrow(const ProcessException('/usr/bin/xcode-select', <String>['--print-path']));
|
||||
expect(xcode.xcodeSelectPath, isNull);
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenThrow(ArgumentError('Invalid argument(s): Cannot find executable for /usr/bin/xcode-select'));
|
||||
|
||||
expect(xcode.xcodeSelectPath, isNull);
|
||||
});
|
||||
|
||||
testWithoutContext('xcodeSelectPath returns path when xcode-select is installed', () {
|
||||
const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 0, xcodePath, ''));
|
||||
|
||||
expect(xcode.xcodeSelectPath, xcodePath);
|
||||
});
|
||||
|
||||
testWithoutContext('xcodeVersionSatisfactory is false when version is less than minimum', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
|
||||
|
||||
expect(xcode.isVersionSatisfactory, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('xcodeVersionSatisfactory is false when xcodebuild tools are not installed', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
|
||||
|
||||
expect(xcode.isVersionSatisfactory, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('xcodeVersionSatisfactory is true when version meets minimum', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
|
||||
|
||||
expect(xcode.isVersionSatisfactory, isTrue);
|
||||
});
|
||||
|
||||
testWithoutContext('xcodeVersionSatisfactory is true when major version exceeds minimum', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
|
||||
|
||||
expect(xcode.isVersionSatisfactory, isTrue);
|
||||
});
|
||||
|
||||
testWithoutContext('xcodeVersionSatisfactory is true when minor version exceeds minimum', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(3);
|
||||
|
||||
expect(xcode.isVersionSatisfactory, isTrue);
|
||||
});
|
||||
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is false when not macOS', () {
|
||||
when(platform.isMacOS).thenReturn(false);
|
||||
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is false when not installed', () {
|
||||
when(platform.isMacOS).thenReturn(true);
|
||||
const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 0, xcodePath, ''));
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
|
||||
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is false when no xcode-select', () {
|
||||
when(platform.isMacOS).thenReturn(true);
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 127, '', 'ERROR'));
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
|
||||
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is false when version not satisfied', () {
|
||||
when(platform.isMacOS).thenReturn(true);
|
||||
const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 0, xcodePath, ''));
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
|
||||
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is true when macOS and installed and version is satisfied', () {
|
||||
when(platform.isMacOS).thenReturn(true);
|
||||
const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 0, xcodePath, ''));
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
|
||||
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isTrue);
|
||||
});
|
||||
|
||||
testWithoutContext('eulaSigned is false when clang is not installed', () {
|
||||
when(processManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
|
||||
.thenThrow(const ProcessException('/usr/bin/xcrun', <String>['clang']));
|
||||
|
||||
expect(xcode.eulaSigned, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('eulaSigned is false when clang output indicates EULA not yet accepted', () {
|
||||
when(processManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
|
||||
.thenReturn(ProcessResult(1, 1, '', 'Xcode EULA has not been accepted.\nLaunch Xcode and accept the license.'));
|
||||
|
||||
expect(xcode.eulaSigned, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('eulaSigned is true when clang output indicates EULA has been accepted', () {
|
||||
when(processManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
|
||||
.thenReturn(ProcessResult(1, 1, '', 'clang: error: no input files'));
|
||||
|
||||
expect(xcode.eulaSigned, isTrue);
|
||||
});
|
||||
|
||||
testWithoutContext('SDK name', () {
|
||||
expect(getNameForSdk(SdkType.iPhone), 'iphoneos');
|
||||
expect(getNameForSdk(SdkType.iPhoneSimulator), 'iphonesimulator');
|
||||
expect(getNameForSdk(SdkType.macOS), 'macosx');
|
||||
});
|
||||
expect(xcode.xcodeSelectPath, isNull);
|
||||
});
|
||||
|
||||
group('xcdevice', () {
|
||||
XCDevice xcdevice;
|
||||
MockXcode mockXcode;
|
||||
testWithoutContext('xcodeSelectPath returns path when xcode-select is installed', () {
|
||||
const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 0, xcodePath, ''));
|
||||
|
||||
setUp(() {
|
||||
mockXcode = MockXcode();
|
||||
xcdevice = XCDevice(
|
||||
processManager: processManager,
|
||||
logger: logger,
|
||||
xcode: mockXcode,
|
||||
);
|
||||
});
|
||||
expect(xcode.xcodeSelectPath, xcodePath);
|
||||
});
|
||||
|
||||
group('installed', () {
|
||||
testWithoutContext('Xcode not installed', () {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false);
|
||||
expect(xcdevice.isInstalled, false);
|
||||
});
|
||||
testWithoutContext('xcodeVersionSatisfactory is false when version is less than minimum', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
|
||||
|
||||
testWithoutContext("xcrun can't find xcdevice", () {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
|
||||
expect(xcode.isVersionSatisfactory, isFalse);
|
||||
});
|
||||
|
||||
when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
|
||||
.thenThrow(const ProcessException('xcrun', <String>['--find', 'xcdevice']));
|
||||
expect(xcdevice.isInstalled, false);
|
||||
verify(processManager.runSync(any)).called(1);
|
||||
});
|
||||
testWithoutContext('xcodeVersionSatisfactory is false when xcodebuild tools are not installed', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
|
||||
|
||||
testWithoutContext('is installed', () {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
|
||||
expect(xcode.isVersionSatisfactory, isFalse);
|
||||
});
|
||||
|
||||
when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
|
||||
.thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', ''));
|
||||
expect(xcdevice.isInstalled, true);
|
||||
});
|
||||
});
|
||||
testWithoutContext('xcodeVersionSatisfactory is true when version meets minimum', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
|
||||
|
||||
group('available devices', () {
|
||||
final FakePlatform macPlatform = FakePlatform.fromPlatform(const LocalPlatform());
|
||||
macPlatform.operatingSystem = 'macos';
|
||||
expect(xcode.isVersionSatisfactory, isTrue);
|
||||
});
|
||||
|
||||
testWithoutContext('Xcode not installed', () async {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false);
|
||||
testWithoutContext('xcodeVersionSatisfactory is true when major version exceeds minimum', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
|
||||
|
||||
expect(await xcdevice.getAvailableTetheredIOSDevices(), isEmpty);
|
||||
verifyNever(processManager.run(any));
|
||||
});
|
||||
expect(xcode.isVersionSatisfactory, isTrue);
|
||||
});
|
||||
|
||||
testWithoutContext('xcdevice fails', () async {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
|
||||
testWithoutContext('xcodeVersionSatisfactory is true when minor version exceeds minimum', () {
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(3);
|
||||
|
||||
when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
|
||||
.thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', ''));
|
||||
expect(xcode.isVersionSatisfactory, isTrue);
|
||||
});
|
||||
|
||||
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '1']))
|
||||
.thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '1']));
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is false when not macOS', () {
|
||||
when(platform.isMacOS).thenReturn(false);
|
||||
|
||||
expect(await xcdevice.getAvailableTetheredIOSDevices(), isEmpty);
|
||||
});
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
|
||||
});
|
||||
|
||||
testUsingContext('returns devices', () async {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is false when not installed', () {
|
||||
when(platform.isMacOS).thenReturn(true);
|
||||
const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 0, xcodePath, ''));
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
|
||||
|
||||
when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
|
||||
.thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', ''));
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
|
||||
});
|
||||
|
||||
const String devicesOutput = '''
|
||||
[
|
||||
{
|
||||
"simulator" : true,
|
||||
"operatingSystemVersion" : "13.3 (17K446)",
|
||||
"available" : true,
|
||||
"platform" : "com.apple.platform.appletvsimulator",
|
||||
"modelCode" : "AppleTV5,3",
|
||||
"identifier" : "CBB5E1ED-2172-446E-B4E7-F2B5823DBBA6",
|
||||
"architecture" : "x86_64",
|
||||
"modelName" : "Apple TV",
|
||||
"name" : "Apple TV"
|
||||
},
|
||||
{
|
||||
"simulator" : false,
|
||||
"operatingSystemVersion" : "13.3 (17C54)",
|
||||
"interface" : "usb",
|
||||
"available" : true,
|
||||
"platform" : "com.apple.platform.iphoneos",
|
||||
"modelCode" : "iPhone8,1",
|
||||
"identifier" : "d83d5bc53967baa0ee18626ba87b6254b2ab5418",
|
||||
"architecture" : "arm64",
|
||||
"modelName" : "iPhone 6s",
|
||||
"name" : "An iPhone (Space Gray)"
|
||||
},
|
||||
{
|
||||
"simulator" : false,
|
||||
"operatingSystemVersion" : "10.1 (14C54)",
|
||||
"interface" : "usb",
|
||||
"available" : true,
|
||||
"platform" : "com.apple.platform.iphoneos",
|
||||
"modelCode" : "iPad11,4",
|
||||
"identifier" : "98206e7a4afd4aedaff06e687594e089dede3c44",
|
||||
"architecture" : "armv7",
|
||||
"modelName" : "iPad Air 3rd Gen",
|
||||
"name" : "iPad 1"
|
||||
},
|
||||
{
|
||||
"simulator" : false,
|
||||
"operatingSystemVersion" : "10.1 (14C54)",
|
||||
"interface" : "network",
|
||||
"available" : true,
|
||||
"platform" : "com.apple.platform.iphoneos",
|
||||
"modelCode" : "iPad11,4",
|
||||
"identifier" : "234234234234234234345445687594e089dede3c44",
|
||||
"architecture" : "arm64",
|
||||
"modelName" : "iPad Air 3rd Gen",
|
||||
"name" : "A networked iPad"
|
||||
},
|
||||
{
|
||||
"simulator" : false,
|
||||
"operatingSystemVersion" : "10.1 (14C54)",
|
||||
"interface" : "usb",
|
||||
"available" : true,
|
||||
"platform" : "com.apple.platform.iphoneos",
|
||||
"modelCode" : "iPad11,4",
|
||||
"identifier" : "f577a7903cc54959be2e34bc4f7f80b7009efcf4",
|
||||
"architecture" : "BOGUS",
|
||||
"modelName" : "iPad Air 3rd Gen",
|
||||
"name" : "iPad 2"
|
||||
},
|
||||
{
|
||||
"simulator" : true,
|
||||
"operatingSystemVersion" : "6.1.1 (17S445)",
|
||||
"available" : true,
|
||||
"platform" : "com.apple.platform.watchsimulator",
|
||||
"modelCode" : "Watch5,4",
|
||||
"identifier" : "2D74FB11-88A0-44D0-B81E-C0C142B1C94A",
|
||||
"architecture" : "i386",
|
||||
"modelName" : "Apple Watch Series 5 - 44mm",
|
||||
"name" : "Apple Watch Series 5 - 44mm"
|
||||
},
|
||||
{
|
||||
"simulator" : false,
|
||||
"operatingSystemVersion" : "13.3 (17C54)",
|
||||
"interface" : "usb",
|
||||
"available" : false,
|
||||
"platform" : "com.apple.platform.iphoneos",
|
||||
"modelCode" : "iPhone8,1",
|
||||
"identifier" : "c4ca6f7a53027d1b7e4972e28478e7a28e2faee2",
|
||||
"architecture" : "arm64",
|
||||
"modelName" : "iPhone 6s",
|
||||
"name" : "iPhone",
|
||||
"error" : {
|
||||
"code" : -9,
|
||||
"failureReason" : "",
|
||||
"description" : "iPhone is not paired with your computer.",
|
||||
"domain" : "com.apple.platform.iphoneos"
|
||||
}
|
||||
}
|
||||
]
|
||||
''';
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is false when no xcode-select', () {
|
||||
when(platform.isMacOS).thenReturn(true);
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 127, '', 'ERROR'));
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
|
||||
|
||||
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '1']))
|
||||
.thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(1, 0, devicesOutput, '')));
|
||||
final List<IOSDevice> devices = await xcdevice.getAvailableTetheredIOSDevices();
|
||||
expect(devices, hasLength(3));
|
||||
expect(devices[0].id, 'd83d5bc53967baa0ee18626ba87b6254b2ab5418');
|
||||
expect(devices[0].name, 'An iPhone (Space Gray)');
|
||||
expect(await devices[0].sdkNameAndVersion, 'iOS 13.3');
|
||||
expect(devices[0].cpuArchitecture, DarwinArch.arm64);
|
||||
expect(devices[1].id, '98206e7a4afd4aedaff06e687594e089dede3c44');
|
||||
expect(devices[1].name, 'iPad 1');
|
||||
expect(await devices[1].sdkNameAndVersion, 'iOS 10.1');
|
||||
expect(devices[1].cpuArchitecture, DarwinArch.armv7);
|
||||
expect(devices[2].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4');
|
||||
expect(devices[2].name, 'iPad 2');
|
||||
expect(await devices[2].sdkNameAndVersion, 'iOS 10.1');
|
||||
expect(devices[2].cpuArchitecture, DarwinArch.arm64); // Defaults to arm64 for unknown architecture.
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
});
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
|
||||
});
|
||||
|
||||
group('diagnostics', () {
|
||||
final FakePlatform macPlatform = FakePlatform.fromPlatform(const LocalPlatform());
|
||||
macPlatform.operatingSystem = 'macos';
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is false when version not satisfied', () {
|
||||
when(platform.isMacOS).thenReturn(true);
|
||||
const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 0, xcodePath, ''));
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
|
||||
|
||||
testWithoutContext('Xcode not installed', () async {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false);
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
|
||||
});
|
||||
|
||||
expect(await xcdevice.getDiagnostics(), isEmpty);
|
||||
verifyNever(processManager.run(any));
|
||||
});
|
||||
testWithoutContext('isInstalledAndMeetsVersionCheck is true when macOS and installed and version is satisfied', () {
|
||||
when(platform.isMacOS).thenReturn(true);
|
||||
const String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
|
||||
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||
.thenReturn(ProcessResult(1, 0, xcodePath, ''));
|
||||
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
|
||||
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
|
||||
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
|
||||
|
||||
testWithoutContext('xcdevice fails', () async {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
|
||||
expect(xcode.isInstalledAndMeetsVersionCheck, isTrue);
|
||||
});
|
||||
|
||||
when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
|
||||
.thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', ''));
|
||||
testWithoutContext('eulaSigned is false when clang is not installed', () {
|
||||
when(processManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
|
||||
.thenThrow(const ProcessException('/usr/bin/xcrun', <String>['clang']));
|
||||
|
||||
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '1']))
|
||||
.thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '1']));
|
||||
expect(xcode.eulaSigned, isFalse);
|
||||
});
|
||||
|
||||
expect(await xcdevice.getDiagnostics(), isEmpty);
|
||||
});
|
||||
testWithoutContext('eulaSigned is false when clang output indicates EULA not yet accepted', () {
|
||||
when(processManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
|
||||
.thenReturn(ProcessResult(1, 1, '', 'Xcode EULA has not been accepted.\nLaunch Xcode and accept the license.'));
|
||||
|
||||
testUsingContext('uses cache', () async {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
|
||||
expect(xcode.eulaSigned, isFalse);
|
||||
});
|
||||
|
||||
when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
|
||||
.thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', ''));
|
||||
testWithoutContext('eulaSigned is true when clang output indicates EULA has been accepted', () {
|
||||
when(processManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
|
||||
.thenReturn(ProcessResult(1, 1, '', 'clang: error: no input files'));
|
||||
|
||||
const String devicesOutput = '''
|
||||
[
|
||||
{
|
||||
"simulator" : false,
|
||||
"operatingSystemVersion" : "13.3 (17C54)",
|
||||
"interface" : "network",
|
||||
"available" : false,
|
||||
"platform" : "com.apple.platform.iphoneos",
|
||||
"modelCode" : "iPhone8,1",
|
||||
"identifier" : "d83d5bc53967baa0ee18626ba87b6254b2ab5418",
|
||||
"architecture" : "arm64",
|
||||
"modelName" : "iPhone 6s",
|
||||
"error" : {
|
||||
"code" : -13,
|
||||
"failureReason" : "",
|
||||
"domain" : "com.apple.platform.iphoneos"
|
||||
}
|
||||
}
|
||||
]
|
||||
''';
|
||||
expect(xcode.eulaSigned, isTrue);
|
||||
});
|
||||
|
||||
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '1']))
|
||||
.thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(1, 0, devicesOutput, '')));
|
||||
await xcdevice.getAvailableTetheredIOSDevices();
|
||||
final List<String> errors = await xcdevice.getDiagnostics();
|
||||
expect(errors, hasLength(1));
|
||||
|
||||
verify(processManager.run(any)).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
|
||||
testUsingContext('returns error message', () async {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
|
||||
|
||||
when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
|
||||
.thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', ''));
|
||||
|
||||
const String devicesOutput = '''
|
||||
[
|
||||
{
|
||||
"simulator" : false,
|
||||
"operatingSystemVersion" : "13.3 (17C54)",
|
||||
"interface" : "usb",
|
||||
"available" : false,
|
||||
"platform" : "com.apple.platform.iphoneos",
|
||||
"modelCode" : "iPhone8,1",
|
||||
"identifier" : "98206e7a4afd4aedaff06e687594e089dede3c44",
|
||||
"architecture" : "arm64",
|
||||
"modelName" : "iPhone 6s",
|
||||
"name" : "An iPhone (Space Gray)",
|
||||
"error" : {
|
||||
"code" : -9,
|
||||
"failureReason" : "",
|
||||
"underlyingErrors" : [
|
||||
{
|
||||
"code" : 5,
|
||||
"failureReason" : "allowsSecureServices: 1. isConnected: 0. Platform: <DVTPlatform:0x7f804ce32880:'com.apple.platform.iphoneos':<DVTFilePath:0x7f804ce32800:'\/Users\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform'>>. DTDKDeviceIdentifierIsIDID: 0",
|
||||
"description" : "📱<DVTiOSDevice (0x7f801f190450), iPhone, iPhone, 13.3 (17C54), d83d5bc53967baa0ee18626ba87b6254b2ab5418> -- Failed _shouldMakeReadyForDevelopment check even though device is not locked by passcode.",
|
||||
"recoverySuggestion" : "",
|
||||
"domain" : "com.apple.platform.iphoneos"
|
||||
}
|
||||
],
|
||||
"description" : "iPhone is not paired with your computer.",
|
||||
"recoverySuggestion" : "To use iPhone with Xcode, unlock it and choose to trust this computer when prompted.",
|
||||
"domain" : "com.apple.platform.iphoneos"
|
||||
}
|
||||
},
|
||||
{
|
||||
"simulator" : false,
|
||||
"operatingSystemVersion" : "13.3 (17C54)",
|
||||
"interface" : "usb",
|
||||
"available" : false,
|
||||
"platform" : "com.apple.platform.iphoneos",
|
||||
"modelCode" : "iPhone8,1",
|
||||
"identifier" : "d83d5bc53967baa0ee18626ba87b6254b2ab5418",
|
||||
"architecture" : "arm64",
|
||||
"modelName" : "iPhone 6s",
|
||||
"name" : "iPhone",
|
||||
"error" : {
|
||||
"code" : -9,
|
||||
"failureReason" : "",
|
||||
"description" : "iPhone is not paired with your computer",
|
||||
"domain" : "com.apple.platform.iphoneos"
|
||||
}
|
||||
},
|
||||
{
|
||||
"simulator" : false,
|
||||
"operatingSystemVersion" : "13.3 (17C54)",
|
||||
"interface" : "network",
|
||||
"available" : false,
|
||||
"platform" : "com.apple.platform.iphoneos",
|
||||
"modelCode" : "iPhone8,1",
|
||||
"identifier" : "d83d5bc53967baa0ee18626ba87b6254b2ab5418",
|
||||
"architecture" : "arm64",
|
||||
"modelName" : "iPhone 6s",
|
||||
"error" : {
|
||||
"code" : -13,
|
||||
"failureReason" : "",
|
||||
"domain" : "com.apple.platform.iphoneos"
|
||||
}
|
||||
}
|
||||
]
|
||||
''';
|
||||
|
||||
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '1']))
|
||||
.thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(1, 0, devicesOutput, '')));
|
||||
final List<String> errors = await xcdevice.getDiagnostics();
|
||||
expect(errors, hasLength(3));
|
||||
expect(errors[0], 'Error: iPhone is not paired with your computer. To use iPhone with Xcode, unlock it and choose to trust this computer when prompted. (code -9)');
|
||||
expect(errors[1], 'Error: iPhone is not paired with your computer. (code -9)');
|
||||
expect(errors[2], 'Error: Xcode pairing error. (code -13)');
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => macPlatform,
|
||||
});
|
||||
});
|
||||
testWithoutContext('SDK name', () {
|
||||
expect(getNameForSdk(SdkType.iPhone), 'iphoneos');
|
||||
expect(getNameForSdk(SdkType.iPhoneSimulator), 'iphonesimulator');
|
||||
expect(getNameForSdk(SdkType.macOS), 'macosx');
|
||||
});
|
||||
}
|
||||
|
||||
class MockLogger extends Mock implements Logger {}
|
||||
class MockXcode extends Mock implements Xcode {}
|
||||
|
|
Loading…
Reference in a new issue