2019-11-27 23:04:02 +00:00
|
|
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
2019-02-27 01:38:21 +00:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
2017-04-26 00:23:00 +00:00
|
|
|
import 'dart:async';
|
2017-09-14 19:28:21 +00:00
|
|
|
import 'dart:io' show ProcessResult, Process;
|
2016-03-10 19:04:02 +00:00
|
|
|
|
2017-02-16 20:50:27 +00:00
|
|
|
import 'package:file/file.dart';
|
2019-04-25 19:25:12 +00:00
|
|
|
import 'package:file/memory.dart';
|
2020-05-06 15:15:39 +00:00
|
|
|
import 'package:flutter_tools/src/application_package.dart';
|
|
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
2020-03-06 21:16:01 +00:00
|
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
2020-05-06 15:15:39 +00:00
|
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
|
|
import 'package:flutter_tools/src/build_info.dart';
|
2019-10-31 02:40:19 +00:00
|
|
|
import 'package:flutter_tools/src/build_system/build_system.dart';
|
2018-01-05 19:34:05 +00:00
|
|
|
import 'package:flutter_tools/src/device.dart';
|
2020-05-06 15:15:39 +00:00
|
|
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
2017-02-16 20:50:27 +00:00
|
|
|
import 'package:flutter_tools/src/ios/mac.dart';
|
2019-08-17 00:10:07 +00:00
|
|
|
import 'package:flutter_tools/src/ios/plist_parser.dart';
|
2016-03-10 19:04:02 +00:00
|
|
|
import 'package:flutter_tools/src/ios/simulators.dart';
|
2019-05-25 02:51:02 +00:00
|
|
|
import 'package:flutter_tools/src/macos/xcode.dart';
|
2019-04-25 19:25:12 +00:00
|
|
|
import 'package:flutter_tools/src/project.dart';
|
2017-03-10 21:53:22 +00:00
|
|
|
import 'package:mockito/mockito.dart';
|
2017-02-16 20:50:27 +00:00
|
|
|
import 'package:process/process.dart';
|
|
|
|
|
2019-07-13 18:51:44 +00:00
|
|
|
import '../../src/common.dart';
|
|
|
|
import '../../src/context.dart';
|
|
|
|
import '../../src/mocks.dart';
|
2017-02-16 20:50:27 +00:00
|
|
|
|
|
|
|
class MockFile extends Mock implements File {}
|
2018-01-05 19:34:05 +00:00
|
|
|
class MockIMobileDevice extends Mock implements IMobileDevice {}
|
2020-03-06 21:16:01 +00:00
|
|
|
class MockLogger extends Mock implements Logger {}
|
2017-09-14 19:28:21 +00:00
|
|
|
class MockProcess extends Mock implements Process {}
|
2018-01-05 19:34:05 +00:00
|
|
|
class MockProcessManager extends Mock implements ProcessManager {}
|
|
|
|
class MockXcode extends Mock implements Xcode {}
|
2019-05-01 17:21:43 +00:00
|
|
|
class MockSimControl extends Mock implements SimControl {}
|
2019-08-17 00:10:07 +00:00
|
|
|
class MockPlistUtils extends Mock implements PlistParser {}
|
2016-03-10 19:04:02 +00:00
|
|
|
|
2020-06-24 17:23:59 +00:00
|
|
|
final Platform macosPlatform = FakePlatform(
|
|
|
|
operatingSystem: 'macos',
|
|
|
|
environment: <String, String>{
|
|
|
|
'HOME': '/'
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2016-03-10 21:47:20 +00:00
|
|
|
void main() {
|
2017-11-29 16:22:05 +00:00
|
|
|
FakePlatform osx;
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystemUtils fsUtils;
|
|
|
|
MemoryFileSystem fileSystem;
|
2017-11-29 16:22:05 +00:00
|
|
|
|
|
|
|
setUp(() {
|
2020-04-10 23:51:28 +00:00
|
|
|
osx = FakePlatform(
|
|
|
|
environment: <String, String>{},
|
|
|
|
operatingSystem: 'macos',
|
|
|
|
);
|
2020-06-23 00:04:17 +00:00
|
|
|
fileSystem = MemoryFileSystem.test();
|
2020-03-06 06:51:15 +00:00
|
|
|
fsUtils = FileSystemUtils(fileSystem: fileSystem, platform: osx);
|
2017-11-29 16:22:05 +00:00
|
|
|
});
|
|
|
|
|
2020-01-24 02:23:03 +00:00
|
|
|
group('_IOSSimulatorDevicePortForwarder', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
MockSimControl mockSimControl;
|
|
|
|
MockXcode mockXcode;
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
mockSimControl = MockSimControl();
|
|
|
|
mockXcode = MockXcode();
|
|
|
|
});
|
|
|
|
|
2020-01-24 02:23:03 +00:00
|
|
|
testUsingContext('dispose() does not throw an exception', () async {
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator simulator = IOSSimulator(
|
|
|
|
'123',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2020-01-24 02:23:03 +00:00
|
|
|
final DevicePortForwarder portForwarder = simulator.portForwarder;
|
|
|
|
await portForwarder.forward(123);
|
|
|
|
await portForwarder.forward(124);
|
|
|
|
expect(portForwarder.forwardedPorts.length, 2);
|
|
|
|
try {
|
|
|
|
await portForwarder.dispose();
|
2020-03-06 18:22:12 +00:00
|
|
|
} on Exception catch (e) {
|
2020-01-24 02:23:03 +00:00
|
|
|
fail('Encountered exception: $e');
|
|
|
|
}
|
|
|
|
expect(portForwarder.forwardedPorts.length, 0);
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2020-01-24 02:23:03 +00:00
|
|
|
}, testOn: 'posix');
|
|
|
|
});
|
|
|
|
|
2020-06-08 18:20:15 +00:00
|
|
|
testUsingContext('simulators only support debug mode', () async {
|
|
|
|
final IOSSimulator simulator = IOSSimulator(
|
|
|
|
'123',
|
|
|
|
simControl: MockSimControl(),
|
|
|
|
xcode: MockXcode(),
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(simulator.supportsRuntimeMode(BuildMode.debug), true);
|
|
|
|
expect(simulator.supportsRuntimeMode(BuildMode.profile), false);
|
|
|
|
expect(simulator.supportsRuntimeMode(BuildMode.release), false);
|
|
|
|
expect(simulator.supportsRuntimeMode(BuildMode.jitRelease), false);
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
|
|
});
|
|
|
|
|
2017-11-29 16:22:05 +00:00
|
|
|
group('logFilePath', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
MockSimControl mockSimControl;
|
|
|
|
MockXcode mockXcode;
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
mockSimControl = MockSimControl();
|
|
|
|
mockXcode = MockXcode();
|
|
|
|
});
|
|
|
|
|
2017-11-29 16:22:05 +00:00
|
|
|
testUsingContext('defaults to rooted from HOME', () {
|
|
|
|
osx.environment['HOME'] = '/foo/bar';
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator simulator = IOSSimulator(
|
|
|
|
'123',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
expect(simulator.logFilePath, '/foo/bar/Library/Logs/CoreSimulator/123/system.log');
|
2017-11-29 16:22:05 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystemUtils: () => fsUtils,
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-11-29 16:22:05 +00:00
|
|
|
}, testOn: 'posix');
|
|
|
|
|
|
|
|
testUsingContext('respects IOS_SIMULATOR_LOG_FILE_PATH', () {
|
|
|
|
osx.environment['HOME'] = '/foo/bar';
|
|
|
|
osx.environment['IOS_SIMULATOR_LOG_FILE_PATH'] = '/baz/qux/%{id}/system.log';
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator simulator = IOSSimulator(
|
|
|
|
'456',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
expect(simulator.logFilePath, '/baz/qux/456/system.log');
|
2017-11-29 16:22:05 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystemUtils: () => fsUtils,
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-11-29 16:22:05 +00:00
|
|
|
});
|
|
|
|
});
|
2017-03-10 17:39:01 +00:00
|
|
|
|
2016-03-10 19:04:02 +00:00
|
|
|
group('compareIosVersions', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext('compares correctly', () {
|
2016-03-10 19:04:02 +00:00
|
|
|
// This list must be sorted in ascending preference order
|
2017-03-04 01:50:46 +00:00
|
|
|
final List<String> testList = <String>[
|
2016-03-10 19:04:02 +00:00
|
|
|
'8', '8.0', '8.1', '8.2',
|
|
|
|
'9', '9.0', '9.1', '9.2',
|
|
|
|
'10', '10.0', '10.1',
|
|
|
|
];
|
|
|
|
|
|
|
|
for (int i = 0; i < testList.length; i++) {
|
|
|
|
expect(compareIosVersions(testList[i], testList[i]), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < testList.length - 1; i++) {
|
|
|
|
for (int j = i + 1; j < testList.length; j++) {
|
|
|
|
expect(compareIosVersions(testList[i], testList[j]), lessThan(0));
|
|
|
|
expect(compareIosVersions(testList[j], testList[i]), greaterThan(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
group('compareIphoneVersions', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext('compares correctly', () {
|
2016-03-10 19:04:02 +00:00
|
|
|
// This list must be sorted in ascending preference order
|
2017-03-04 01:50:46 +00:00
|
|
|
final List<String> testList = <String>[
|
2016-03-10 19:04:02 +00:00
|
|
|
'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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2017-02-03 20:25:05 +00:00
|
|
|
|
2018-04-02 00:35:05 +00:00
|
|
|
group('sdkMajorVersion', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
MockSimControl mockSimControl;
|
|
|
|
MockXcode mockXcode;
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
mockSimControl = MockSimControl();
|
|
|
|
mockXcode = MockXcode();
|
|
|
|
});
|
|
|
|
|
2018-04-02 00:35:05 +00:00
|
|
|
// This new version string appears in SimulatorApp-850 CoreSimulator-518.16 beta.
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext('can be parsed from iOS-11-3', () async {
|
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone SE',
|
|
|
|
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2018-04-02 00:35:05 +00:00
|
|
|
|
|
|
|
expect(await device.sdkMajorVersion, 11);
|
|
|
|
});
|
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext('can be parsed from iOS 11.2', () async {
|
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone SE',
|
|
|
|
simulatorCategory: 'iOS 11.2',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2018-04-02 00:35:05 +00:00
|
|
|
|
|
|
|
expect(await device.sdkMajorVersion, 11);
|
|
|
|
});
|
2019-06-18 22:23:14 +00:00
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext('Has a simulator category', () async {
|
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone SE',
|
|
|
|
simulatorCategory: 'iOS 11.2',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2019-06-18 22:23:14 +00:00
|
|
|
|
|
|
|
expect(device.category, Category.mobile);
|
|
|
|
});
|
2018-04-02 00:35:05 +00:00
|
|
|
});
|
|
|
|
|
2017-02-03 20:25:05 +00:00
|
|
|
group('IOSSimulator.isSupported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
MockSimControl mockSimControl;
|
|
|
|
MockXcode mockXcode;
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
mockSimControl = MockSimControl();
|
|
|
|
mockXcode = MockXcode();
|
|
|
|
});
|
|
|
|
|
2017-03-10 17:39:01 +00:00
|
|
|
testUsingContext('Apple TV is unsupported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator simulator = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'Apple TV',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
expect(simulator.isSupported(), false);
|
2017-03-10 17:39:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-02-03 20:25:05 +00:00
|
|
|
});
|
|
|
|
|
2017-03-10 17:39:01 +00:00
|
|
|
testUsingContext('Apple Watch is unsupported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
expect(IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'Apple Watch',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
).isSupported(), false);
|
2017-03-10 17:39:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-02-03 20:25:05 +00:00
|
|
|
});
|
|
|
|
|
2018-05-09 20:34:39 +00:00
|
|
|
testUsingContext('iPad 2 is supported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
expect(IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPad 2',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
).isSupported(), true);
|
2017-03-10 17:39:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-02-03 20:25:05 +00:00
|
|
|
});
|
|
|
|
|
2018-05-09 20:34:39 +00:00
|
|
|
testUsingContext('iPad Retina is supported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
expect(IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPad Retina',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
).isSupported(), true);
|
2017-03-10 17:39:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-02-03 20:25:05 +00:00
|
|
|
});
|
|
|
|
|
2018-05-09 20:34:39 +00:00
|
|
|
testUsingContext('iPhone 5 is supported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
expect(IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone 5',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
).isSupported(), true);
|
2017-03-10 17:39:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-02-03 20:25:05 +00:00
|
|
|
});
|
|
|
|
|
2017-03-10 17:39:01 +00:00
|
|
|
testUsingContext('iPhone 5s is supported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
expect(IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone 5s',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
).isSupported(), true);
|
2017-03-10 17:39:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-02-03 20:25:05 +00:00
|
|
|
});
|
|
|
|
|
2017-03-10 17:39:01 +00:00
|
|
|
testUsingContext('iPhone SE is supported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
expect(IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone SE',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
).isSupported(), true);
|
2017-03-10 17:39:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-02-03 20:25:05 +00:00
|
|
|
});
|
|
|
|
|
2017-03-10 17:39:01 +00:00
|
|
|
testUsingContext('iPhone 7 Plus is supported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
expect(IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone 7 Plus',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
).isSupported(), true);
|
2017-03-10 17:39:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2017-02-03 20:25:05 +00:00
|
|
|
});
|
2018-05-09 20:34:39 +00:00
|
|
|
|
|
|
|
testUsingContext('iPhone X is supported', () {
|
2020-03-06 21:16:01 +00:00
|
|
|
expect(IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone X',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
).isSupported(), true);
|
2018-05-09 20:34:39 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
Platform: () => osx,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2018-05-09 20:34:39 +00:00
|
|
|
});
|
2017-02-03 20:25:05 +00:00
|
|
|
});
|
2017-02-16 20:50:27 +00:00
|
|
|
|
|
|
|
group('Simulator screenshot', () {
|
|
|
|
MockXcode mockXcode;
|
2020-03-06 21:16:01 +00:00
|
|
|
MockLogger mockLogger;
|
2017-02-16 20:50:27 +00:00
|
|
|
MockProcessManager mockProcessManager;
|
|
|
|
IOSSimulator deviceUnderTest;
|
2020-03-06 21:16:01 +00:00
|
|
|
// only used for fs.path.join()
|
|
|
|
final FileSystem fs = globals.fs;
|
2017-02-16 20:50:27 +00:00
|
|
|
|
|
|
|
setUp(() {
|
2018-09-12 06:29:29 +00:00
|
|
|
mockXcode = MockXcode();
|
2020-03-06 21:16:01 +00:00
|
|
|
mockLogger = MockLogger();
|
2018-09-12 06:29:29 +00:00
|
|
|
mockProcessManager = MockProcessManager();
|
2017-02-16 20:50:27 +00:00
|
|
|
// Let everything else return exit code 0 so process.dart doesn't crash.
|
|
|
|
when(
|
2018-06-21 23:23:47 +00:00
|
|
|
mockProcessManager.run(any, environment: null, workingDirectory: null)
|
2017-12-19 21:13:57 +00:00
|
|
|
).thenAnswer((Invocation invocation) =>
|
2018-09-12 06:29:29 +00:00
|
|
|
Future<ProcessResult>.value(ProcessResult(2, 0, '', ''))
|
2017-02-16 20:50:27 +00:00
|
|
|
);
|
2020-03-06 21:16:01 +00:00
|
|
|
// Test a real one. Screenshot doesn't require instance states.
|
|
|
|
final SimControl simControl = SimControl(processManager: mockProcessManager, logger: mockLogger);
|
2017-02-16 20:50:27 +00:00
|
|
|
// Doesn't matter what the device is.
|
2020-03-06 21:16:01 +00:00
|
|
|
deviceUnderTest = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone SE',
|
|
|
|
simControl: simControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2017-02-16 20:50:27 +00:00
|
|
|
});
|
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext(
|
2020-02-11 19:58:27 +00:00
|
|
|
"old Xcode doesn't support screenshot",
|
2017-02-16 20:50:27 +00:00
|
|
|
() {
|
2018-03-07 07:41:23 +00:00
|
|
|
when(mockXcode.majorVersion).thenReturn(7);
|
|
|
|
when(mockXcode.minorVersion).thenReturn(1);
|
2017-02-16 20:50:27 +00:00
|
|
|
expect(deviceUnderTest.supportsScreenshot, false);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext(
|
2017-02-16 20:50:27 +00:00
|
|
|
'Xcode 8.2+ supports screenshots',
|
2017-04-26 00:23:00 +00:00
|
|
|
() async {
|
2018-03-07 07:41:23 +00:00
|
|
|
when(mockXcode.majorVersion).thenReturn(8);
|
|
|
|
when(mockXcode.minorVersion).thenReturn(2);
|
2017-02-16 20:50:27 +00:00
|
|
|
expect(deviceUnderTest.supportsScreenshot, true);
|
2018-09-12 06:29:29 +00:00
|
|
|
final MockFile mockFile = MockFile();
|
2020-03-06 21:16:01 +00:00
|
|
|
when(mockFile.path).thenReturn(fs.path.join('some', 'path', 'to', 'screenshot.png'));
|
2017-04-26 00:23:00 +00:00
|
|
|
await deviceUnderTest.takeScreenshot(mockFile);
|
|
|
|
verify(mockProcessManager.run(
|
2017-02-16 20:50:27 +00:00
|
|
|
<String>[
|
2019-09-27 08:46:45 +00:00
|
|
|
'/usr/bin/xcrun',
|
|
|
|
'simctl',
|
|
|
|
'io',
|
|
|
|
'x',
|
|
|
|
'screenshot',
|
2020-03-06 21:16:01 +00:00
|
|
|
fs.path.join('some', 'path', 'to', 'screenshot.png'),
|
2017-02-16 20:50:27 +00:00
|
|
|
],
|
|
|
|
environment: null,
|
2019-03-01 07:17:55 +00:00
|
|
|
workingDirectory: null,
|
2017-02-16 20:50:27 +00:00
|
|
|
));
|
|
|
|
},
|
|
|
|
);
|
|
|
|
});
|
2017-09-14 19:28:21 +00:00
|
|
|
|
2020-04-09 21:35:01 +00:00
|
|
|
group('device log tool', () {
|
2017-09-14 19:28:21 +00:00
|
|
|
MockProcessManager mockProcessManager;
|
2020-03-06 21:16:01 +00:00
|
|
|
MockXcode mockXcode;
|
|
|
|
MockSimControl mockSimControl;
|
2017-09-14 19:28:21 +00:00
|
|
|
|
|
|
|
setUp(() {
|
2018-09-12 06:29:29 +00:00
|
|
|
mockProcessManager = MockProcessManager();
|
2018-06-21 23:23:47 +00:00
|
|
|
when(mockProcessManager.start(any, environment: null, workingDirectory: null))
|
2018-09-12 06:29:29 +00:00
|
|
|
.thenAnswer((Invocation invocation) => Future<Process>.value(MockProcess()));
|
2020-03-06 21:16:01 +00:00
|
|
|
mockSimControl = MockSimControl();
|
|
|
|
mockXcode = MockXcode();
|
2017-09-14 19:28:21 +00:00
|
|
|
});
|
|
|
|
|
2020-04-09 21:35:01 +00:00
|
|
|
testUsingContext('syslog uses tail', () async {
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone SE',
|
|
|
|
simulatorCategory: 'iOS 9.3',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2020-04-09 21:35:01 +00:00
|
|
|
await launchDeviceSystemLogTool(device);
|
2017-09-14 19:28:21 +00:00
|
|
|
expect(
|
2018-06-21 23:23:47 +00:00
|
|
|
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
|
2017-09-14 19:28:21 +00:00
|
|
|
contains('tail'),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
overrides: <Type, Generator>{
|
|
|
|
ProcessManager: () => mockProcessManager,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
2020-06-24 17:23:59 +00:00
|
|
|
Platform: () => macosPlatform,
|
|
|
|
FileSystemUtils: () => FileSystemUtils(
|
|
|
|
fileSystem: fileSystem,
|
|
|
|
platform: macosPlatform,
|
|
|
|
)
|
2017-09-14 19:28:21 +00:00
|
|
|
});
|
|
|
|
|
2020-04-09 21:35:01 +00:00
|
|
|
testUsingContext('unified logging with app name', () async {
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone SE',
|
|
|
|
simulatorCategory: 'iOS 11.0',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2020-05-04 18:31:08 +00:00
|
|
|
await launchDeviceUnifiedLogging(device, 'My Super Awesome App');
|
2020-04-09 21:35:01 +00:00
|
|
|
|
|
|
|
const String expectedPredicate = 'eventType = logEvent AND '
|
2020-05-04 18:31:08 +00:00
|
|
|
'processImagePath ENDSWITH "My Super Awesome App" AND '
|
2020-04-09 21:35:01 +00:00
|
|
|
'(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND '
|
|
|
|
'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND '
|
|
|
|
'NOT(eventMessage BEGINSWITH "assertion failed: ") AND '
|
|
|
|
'NOT(eventMessage CONTAINS " libxpc.dylib ")';
|
|
|
|
|
|
|
|
final List<String> command = verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single as List<String>;
|
|
|
|
expect(command, <String>[
|
|
|
|
'/usr/bin/xcrun',
|
|
|
|
'simctl',
|
|
|
|
'spawn',
|
2020-03-06 21:16:01 +00:00
|
|
|
'x',
|
2020-04-09 21:35:01 +00:00
|
|
|
'log',
|
|
|
|
'stream',
|
|
|
|
'--style',
|
|
|
|
'json',
|
|
|
|
'--predicate',
|
|
|
|
expectedPredicate
|
|
|
|
]);
|
2017-09-14 19:28:21 +00:00
|
|
|
},
|
2020-04-09 21:35:01 +00:00
|
|
|
overrides: <Type, Generator>{
|
2017-09-14 19:28:21 +00:00
|
|
|
ProcessManager: () => mockProcessManager,
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
2017-09-14 19:28:21 +00:00
|
|
|
});
|
|
|
|
|
2020-04-09 21:35:01 +00:00
|
|
|
testUsingContext('unified logging without app name', () async {
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone SE',
|
|
|
|
simulatorCategory: 'iOS 11.0',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2020-04-09 21:35:01 +00:00
|
|
|
await launchDeviceUnifiedLogging(device, null);
|
|
|
|
|
|
|
|
const String expectedPredicate = 'eventType = logEvent AND '
|
|
|
|
'(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND '
|
|
|
|
'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND '
|
|
|
|
'NOT(eventMessage BEGINSWITH "assertion failed: ") AND '
|
|
|
|
'NOT(eventMessage CONTAINS " libxpc.dylib ")';
|
|
|
|
|
|
|
|
final List<String> command = verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single as List<String>;
|
|
|
|
expect(command, <String>[
|
|
|
|
'/usr/bin/xcrun',
|
|
|
|
'simctl',
|
|
|
|
'spawn',
|
|
|
|
'x',
|
|
|
|
'log',
|
|
|
|
'stream',
|
|
|
|
'--style',
|
|
|
|
'json',
|
|
|
|
'--predicate',
|
|
|
|
expectedPredicate
|
|
|
|
]);
|
2017-09-14 19:28:21 +00:00
|
|
|
},
|
2020-04-09 21:35:01 +00:00
|
|
|
overrides: <Type, Generator>{
|
|
|
|
ProcessManager: () => mockProcessManager,
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
});
|
2017-09-14 19:28:21 +00:00
|
|
|
});
|
2018-01-05 19:34:05 +00:00
|
|
|
|
|
|
|
group('log reader', () {
|
2020-04-29 20:02:39 +00:00
|
|
|
FakeProcessManager fakeProcessManager;
|
2018-08-30 14:18:44 +00:00
|
|
|
MockIosProject mockIosProject;
|
2020-03-06 21:16:01 +00:00
|
|
|
MockSimControl mockSimControl;
|
|
|
|
MockXcode mockXcode;
|
2018-01-05 19:34:05 +00:00
|
|
|
|
|
|
|
setUp(() {
|
2020-04-29 20:02:39 +00:00
|
|
|
fakeProcessManager = FakeProcessManager.list(<FakeCommand>[]);
|
2018-09-12 06:29:29 +00:00
|
|
|
mockIosProject = MockIosProject();
|
2020-03-06 21:16:01 +00:00
|
|
|
mockSimControl = MockSimControl();
|
|
|
|
mockXcode = MockXcode();
|
2018-01-05 19:34:05 +00:00
|
|
|
});
|
|
|
|
|
2020-04-09 21:35:01 +00:00
|
|
|
group('syslog', () {
|
|
|
|
setUp(() {
|
|
|
|
final File syslog = fileSystem.file('system.log')..createSync();
|
|
|
|
osx.environment['IOS_SIMULATOR_LOG_FILE_PATH'] = syslog.path;
|
|
|
|
});
|
|
|
|
|
2020-04-29 20:02:39 +00:00
|
|
|
testUsingContext('simulator can parse Xcode 8/iOS 10-style logs', () async {
|
|
|
|
fakeProcessManager
|
|
|
|
..addCommand(const FakeCommand(
|
|
|
|
command: <String>['tail', '-n', '0', '-F', 'system.log'],
|
2020-05-04 18:31:08 +00:00
|
|
|
stdout: '''
|
|
|
|
Dec 20 17:04:32 md32-11-vm1 My Super Awesome App[88374]: flutter: Observatory listening on http://127.0.0.1:64213/1Uoeu523990=/
|
|
|
|
Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
|
2020-04-29 20:02:39 +00:00
|
|
|
))
|
|
|
|
..addCommand(const FakeCommand(
|
|
|
|
command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log']
|
|
|
|
));
|
|
|
|
|
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'123456',
|
|
|
|
simulatorCategory: 'iOS 10.0',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
final DeviceLogReader logReader = device.getLogReader(
|
2020-05-11 18:56:44 +00:00
|
|
|
app: await BuildableIOSApp.fromProject(mockIosProject, null),
|
2020-04-29 20:02:39 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
final List<String> lines = await logReader.logLines.toList();
|
|
|
|
expect(lines, <String>[
|
|
|
|
'flutter: Observatory listening on http://127.0.0.1:64213/1Uoeu523990=/',
|
|
|
|
]);
|
|
|
|
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
ProcessManager: () => fakeProcessManager,
|
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => osx,
|
|
|
|
});
|
|
|
|
|
2020-04-09 21:35:01 +00:00
|
|
|
testUsingContext('simulator can output `)`', () async {
|
2020-04-29 20:02:39 +00:00
|
|
|
fakeProcessManager
|
|
|
|
..addCommand(const FakeCommand(
|
|
|
|
command: <String>['tail', '-n', '0', '-F', 'system.log'],
|
|
|
|
stdout: '''
|
2020-05-04 18:31:08 +00:00
|
|
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/
|
|
|
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) ))))))))))
|
|
|
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) #0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)'''
|
2020-04-29 20:02:39 +00:00
|
|
|
))
|
|
|
|
..addCommand(const FakeCommand(
|
|
|
|
command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log']
|
|
|
|
));
|
2018-01-05 19:34:05 +00:00
|
|
|
|
2020-04-09 21:35:01 +00:00
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'123456',
|
|
|
|
simulatorCategory: 'iOS 10.3',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
final DeviceLogReader logReader = device.getLogReader(
|
2020-05-11 18:56:44 +00:00
|
|
|
app: await BuildableIOSApp.fromProject(mockIosProject, null),
|
2020-04-09 21:35:01 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
final List<String> lines = await logReader.logLines.toList();
|
|
|
|
expect(lines, <String>[
|
|
|
|
'Observatory listening on http://127.0.0.1:57701/',
|
|
|
|
'))))))))))',
|
|
|
|
'#0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)',
|
|
|
|
]);
|
2020-04-29 20:02:39 +00:00
|
|
|
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
2020-04-09 21:35:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
2020-04-29 20:02:39 +00:00
|
|
|
ProcessManager: () => fakeProcessManager,
|
2020-04-09 21:35:01 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => osx,
|
|
|
|
});
|
|
|
|
|
2020-04-29 20:02:39 +00:00
|
|
|
testUsingContext('multiline messages', () async {
|
|
|
|
fakeProcessManager
|
|
|
|
..addCommand(const FakeCommand(
|
|
|
|
command: <String>['tail', '-n', '0', '-F', 'system.log'],
|
|
|
|
stdout: '''
|
2020-05-04 18:31:08 +00:00
|
|
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Single line message
|
|
|
|
2017-09-13 15:26:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Multi line message
|
2020-03-19 22:41:39 +00:00
|
|
|
continues...
|
|
|
|
continues...
|
2020-05-04 18:31:08 +00:00
|
|
|
2020-03-11 15:58:28.207175-0700 localhost My Super Awesome App[72166]: (libnetwork.dylib) [com.apple.network:] [28 www.googleapis.com:443 stream, pid: 72166, tls] cancelled
|
2020-03-19 22:41:39 +00:00
|
|
|
[28.1 64A98447-EABF-4983-A387-7DB9D0C1785F 10.0.1.200.57912<->172.217.6.74:443]
|
|
|
|
Connected Path: satisfied (Path is satisfied), interface: en18
|
|
|
|
Duration: 0.271s, DNS @0.000s took 0.001s, TCP @0.002s took 0.019s, TLS took 0.046s
|
|
|
|
bytes in/out: 4468/1933, packets in/out: 11/10, rtt: 0.016s, retransmitted packets: 0, out-of-order packets: 0
|
2020-05-04 18:31:08 +00:00
|
|
|
2017-09-13 15:36:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Multi line message again
|
2020-03-19 22:41:39 +00:00
|
|
|
and it goes...
|
|
|
|
and goes...
|
2020-05-04 18:31:08 +00:00
|
|
|
2017-09-13 15:36:57.228948-0700 localhost My Super Awesome App[37195]: (Flutter) Single line message, not the part of the above
|
2020-03-19 22:41:39 +00:00
|
|
|
'''
|
2020-04-29 20:02:39 +00:00
|
|
|
))
|
|
|
|
..addCommand(const FakeCommand(
|
|
|
|
command: <String>['tail', '-n', '0', '-F', '/private/var/log/system.log']
|
|
|
|
));
|
2020-03-19 22:41:39 +00:00
|
|
|
|
2020-04-09 21:35:01 +00:00
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'123456',
|
|
|
|
simulatorCategory: 'iOS 10.3',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
final DeviceLogReader logReader = device.getLogReader(
|
2020-05-11 18:56:44 +00:00
|
|
|
app: await BuildableIOSApp.fromProject(mockIosProject, null),
|
2020-04-09 21:35:01 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
final List<String> lines = await logReader.logLines.toList();
|
|
|
|
expect(lines, <String>[
|
|
|
|
'Single line message',
|
|
|
|
'Multi line message',
|
|
|
|
' continues...',
|
|
|
|
' continues...',
|
|
|
|
'Multi line message again',
|
|
|
|
' and it goes...',
|
|
|
|
' and goes...',
|
|
|
|
'Single line message, not the part of the above'
|
|
|
|
]);
|
2020-04-29 20:02:39 +00:00
|
|
|
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
2020-04-09 21:35:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
2020-04-29 20:02:39 +00:00
|
|
|
ProcessManager: () => fakeProcessManager,
|
2020-04-09 21:35:01 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
Platform: () => osx,
|
|
|
|
});
|
|
|
|
});
|
2020-03-19 22:41:39 +00:00
|
|
|
|
2020-04-09 21:35:01 +00:00
|
|
|
group('unified logging', () {
|
|
|
|
testUsingContext('log reader handles escaped multiline messages', () async {
|
2020-05-04 18:31:08 +00:00
|
|
|
const String logPredicate = 'eventType = logEvent AND processImagePath ENDSWITH "My Super Awesome App" '
|
2020-04-29 20:02:39 +00:00
|
|
|
'AND (senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" '
|
|
|
|
'OR processImageUUID == senderImageUUID) AND NOT(eventMessage CONTAINS ": could not find icon '
|
|
|
|
'for representation -> com.apple.") AND NOT(eventMessage BEGINSWITH "assertion failed: ") '
|
|
|
|
'AND NOT(eventMessage CONTAINS " libxpc.dylib ")';
|
|
|
|
fakeProcessManager.addCommand(const FakeCommand(
|
|
|
|
command: <String>[
|
|
|
|
'/usr/bin/xcrun',
|
|
|
|
'simctl',
|
|
|
|
'spawn',
|
|
|
|
'123456',
|
|
|
|
'log',
|
|
|
|
'stream',
|
|
|
|
'--style',
|
|
|
|
'json',
|
|
|
|
'--predicate',
|
|
|
|
logPredicate,
|
|
|
|
],
|
|
|
|
stdout: '''
|
2020-04-09 21:35:01 +00:00
|
|
|
},{
|
|
|
|
"traceID" : 37579774151491588,
|
|
|
|
"eventMessage" : "Single line message",
|
|
|
|
"eventType" : "logEvent"
|
|
|
|
},{
|
|
|
|
"traceID" : 37579774151491588,
|
|
|
|
"eventMessage" : "Multi line message\\n continues...\\n continues..."
|
|
|
|
},{
|
|
|
|
"traceID" : 37579774151491588,
|
|
|
|
"eventMessage" : "Single line message, not the part of the above",
|
|
|
|
"eventType" : "logEvent"
|
|
|
|
},{
|
|
|
|
'''
|
2020-04-29 20:02:39 +00:00
|
|
|
));
|
2020-04-09 21:35:01 +00:00
|
|
|
|
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'123456',
|
|
|
|
simulatorCategory: 'iOS 11.0',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
final DeviceLogReader logReader = device.getLogReader(
|
2020-05-11 18:56:44 +00:00
|
|
|
app: await BuildableIOSApp.fromProject(mockIosProject, null),
|
2020-04-09 21:35:01 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
final List<String> lines = await logReader.logLines.toList();
|
|
|
|
expect(lines, <String>[
|
|
|
|
'Single line message', 'Multi line message\n continues...\n continues...',
|
|
|
|
'Single line message, not the part of the above'
|
|
|
|
]);
|
2020-04-29 20:02:39 +00:00
|
|
|
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
2020-04-09 21:35:01 +00:00
|
|
|
}, overrides: <Type, Generator>{
|
2020-04-29 20:02:39 +00:00
|
|
|
ProcessManager: () => fakeProcessManager,
|
2020-04-09 21:35:01 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
});
|
2020-03-19 22:41:39 +00:00
|
|
|
});
|
2018-01-05 19:34:05 +00:00
|
|
|
});
|
2018-07-01 00:17:35 +00:00
|
|
|
|
|
|
|
group('SimControl', () {
|
|
|
|
const int mockPid = 123;
|
|
|
|
const String validSimControlOutput = '''
|
|
|
|
{
|
|
|
|
"devices" : {
|
|
|
|
"watchOS 4.3" : [
|
|
|
|
{
|
|
|
|
"state" : "Shutdown",
|
|
|
|
"availability" : "(available)",
|
|
|
|
"name" : "Apple Watch - 38mm",
|
|
|
|
"udid" : "TEST-WATCH-UDID"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"iOS 11.4" : [
|
|
|
|
{
|
|
|
|
"state" : "Booted",
|
|
|
|
"availability" : "(available)",
|
|
|
|
"name" : "iPhone 5s",
|
|
|
|
"udid" : "TEST-PHONE-UDID"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"tvOS 11.4" : [
|
|
|
|
{
|
|
|
|
"state" : "Shutdown",
|
|
|
|
"availability" : "(available)",
|
|
|
|
"name" : "Apple TV",
|
|
|
|
"udid" : "TEST-TV-UDID"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
''';
|
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
MockLogger mockLogger;
|
2018-07-01 00:17:35 +00:00
|
|
|
MockProcessManager mockProcessManager;
|
2020-03-06 21:16:01 +00:00
|
|
|
MockXcode mockXcode;
|
2018-07-01 00:17:35 +00:00
|
|
|
SimControl simControl;
|
2020-03-06 21:16:01 +00:00
|
|
|
const String deviceId = 'smart-phone';
|
|
|
|
const String appId = 'flutterApp';
|
2018-07-01 00:17:35 +00:00
|
|
|
|
|
|
|
setUp(() {
|
2020-03-06 21:16:01 +00:00
|
|
|
mockLogger = MockLogger();
|
2018-09-12 06:29:29 +00:00
|
|
|
mockProcessManager = MockProcessManager();
|
2019-08-10 07:57:23 +00:00
|
|
|
when(mockProcessManager.run(any)).thenAnswer((Invocation _) async {
|
|
|
|
return ProcessResult(mockPid, 0, validSimControlOutput, '');
|
|
|
|
});
|
2018-07-01 00:17:35 +00:00
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
simControl = SimControl(
|
|
|
|
logger: mockLogger,
|
|
|
|
processManager: mockProcessManager,
|
|
|
|
);
|
|
|
|
mockXcode = MockXcode();
|
2018-07-01 00:17:35 +00:00
|
|
|
});
|
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext('getDevices succeeds', () async {
|
2019-08-10 07:57:23 +00:00
|
|
|
final List<SimDevice> devices = await simControl.getDevices();
|
2018-07-01 00:17:35 +00:00
|
|
|
|
|
|
|
final SimDevice watch = devices[0];
|
|
|
|
expect(watch.category, 'watchOS 4.3');
|
|
|
|
expect(watch.state, 'Shutdown');
|
|
|
|
expect(watch.availability, '(available)');
|
|
|
|
expect(watch.name, 'Apple Watch - 38mm');
|
|
|
|
expect(watch.udid, 'TEST-WATCH-UDID');
|
|
|
|
expect(watch.isBooted, isFalse);
|
|
|
|
|
|
|
|
final SimDevice phone = devices[1];
|
|
|
|
expect(phone.category, 'iOS 11.4');
|
|
|
|
expect(phone.state, 'Booted');
|
|
|
|
expect(phone.availability, '(available)');
|
|
|
|
expect(phone.name, 'iPhone 5s');
|
|
|
|
expect(phone.udid, 'TEST-PHONE-UDID');
|
|
|
|
expect(phone.isBooted, isTrue);
|
|
|
|
|
|
|
|
final SimDevice tv = devices[2];
|
|
|
|
expect(tv.category, 'tvOS 11.4');
|
|
|
|
expect(tv.state, 'Shutdown');
|
|
|
|
expect(tv.availability, '(available)');
|
|
|
|
expect(tv.name, 'Apple TV');
|
|
|
|
expect(tv.udid, 'TEST-TV-UDID');
|
|
|
|
expect(tv.isBooted, isFalse);
|
|
|
|
});
|
2019-08-10 07:57:23 +00:00
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext('getDevices handles bad simctl output', () async {
|
2019-08-10 07:57:23 +00:00
|
|
|
when(mockProcessManager.run(any))
|
|
|
|
.thenAnswer((Invocation _) async => ProcessResult(mockPid, 0, 'Install Started', ''));
|
|
|
|
final List<SimDevice> devices = await simControl.getDevices();
|
|
|
|
|
|
|
|
expect(devices, isEmpty);
|
|
|
|
});
|
2019-08-12 22:21:28 +00:00
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
testWithoutContext('sdkMajorVersion defaults to 11 when sdkNameAndVersion is junk', () async {
|
|
|
|
final IOSSimulator iosSimulatorA = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'Testo',
|
|
|
|
simulatorCategory: 'NaN',
|
|
|
|
simControl: simControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2019-08-12 22:21:28 +00:00
|
|
|
|
|
|
|
expect(await iosSimulatorA.sdkMajorVersion, 11);
|
|
|
|
});
|
2020-03-06 21:16:01 +00:00
|
|
|
|
|
|
|
testWithoutContext('.install() handles exceptions', () async {
|
|
|
|
when(mockProcessManager.run(
|
|
|
|
<String>['/usr/bin/xcrun', 'simctl', 'install', deviceId, appId],
|
|
|
|
environment: anyNamed('environment'),
|
|
|
|
workingDirectory: anyNamed('workingDirectory'),
|
|
|
|
)).thenThrow(const ProcessException('xcrun', <String>[]));
|
|
|
|
expect(
|
|
|
|
() async => await simControl.install(deviceId, appId),
|
|
|
|
throwsToolExit(message: r'Unable to install'),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
testWithoutContext('.uninstall() handles exceptions', () async {
|
|
|
|
when(mockProcessManager.run(
|
|
|
|
<String>['/usr/bin/xcrun', 'simctl', 'uninstall', deviceId, appId],
|
|
|
|
environment: anyNamed('environment'),
|
|
|
|
workingDirectory: anyNamed('workingDirectory'),
|
|
|
|
)).thenThrow(const ProcessException('xcrun', <String>[]));
|
|
|
|
expect(
|
|
|
|
() async => await simControl.uninstall(deviceId, appId),
|
|
|
|
throwsToolExit(message: r'Unable to uninstall'),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
testWithoutContext('.launch() handles exceptions', () async {
|
|
|
|
when(mockProcessManager.run(
|
|
|
|
<String>['/usr/bin/xcrun', 'simctl', 'launch', deviceId, appId],
|
|
|
|
environment: anyNamed('environment'),
|
|
|
|
workingDirectory: anyNamed('workingDirectory'),
|
|
|
|
)).thenThrow(const ProcessException('xcrun', <String>[]));
|
|
|
|
expect(
|
|
|
|
() async => await simControl.launch(deviceId, appId),
|
|
|
|
throwsToolExit(message: r'Unable to launch'),
|
|
|
|
);
|
|
|
|
});
|
2018-07-01 00:17:35 +00:00
|
|
|
});
|
2019-04-25 19:25:12 +00:00
|
|
|
|
2019-05-01 17:21:43 +00:00
|
|
|
group('startApp', () {
|
|
|
|
SimControl simControl;
|
2020-03-06 21:16:01 +00:00
|
|
|
MockXcode mockXcode;
|
2019-05-01 17:21:43 +00:00
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
simControl = MockSimControl();
|
2020-03-06 21:16:01 +00:00
|
|
|
mockXcode = MockXcode();
|
2019-05-01 17:21:43 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext("startApp uses compiled app's Info.plist to find CFBundleIdentifier", () async {
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator device = IOSSimulator(
|
|
|
|
'x',
|
|
|
|
name: 'iPhone SE',
|
|
|
|
simulatorCategory: 'iOS 11.2',
|
|
|
|
simControl: simControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
2020-02-27 02:31:42 +00:00
|
|
|
when(globals.plistParser.getValueFromFile(any, any)).thenReturn('correct');
|
2019-05-01 17:21:43 +00:00
|
|
|
|
2020-01-06 19:04:20 +00:00
|
|
|
final Directory mockDir = globals.fs.currentDirectory;
|
2019-09-24 19:03:37 +00:00
|
|
|
final IOSApp package = PrebuiltIOSApp(projectBundleId: 'incorrect', bundleName: 'name', bundleDir: mockDir);
|
2019-05-01 17:21:43 +00:00
|
|
|
|
2020-02-05 04:34:24 +00:00
|
|
|
const BuildInfo mockInfo = BuildInfo(BuildMode.debug, 'flavor', treeShakeIcons: false);
|
2019-09-24 19:03:37 +00:00
|
|
|
final DebuggingOptions mockOptions = DebuggingOptions.disabled(mockInfo);
|
|
|
|
await device.startApp(package, prebuiltApplication: true, debuggingOptions: mockOptions);
|
2019-05-01 17:21:43 +00:00
|
|
|
|
2019-09-24 19:03:37 +00:00
|
|
|
verify(simControl.launch(any, 'correct', any));
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
PlistParser: () => MockPlistUtils(),
|
2020-03-06 06:51:15 +00:00
|
|
|
FileSystem: () => fileSystem,
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
2019-09-24 19:03:37 +00:00
|
|
|
});
|
2019-05-01 17:21:43 +00:00
|
|
|
});
|
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
group('IOSDevice.isSupportedForProject', () {
|
|
|
|
MockSimControl mockSimControl;
|
|
|
|
MockXcode mockXcode;
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
mockSimControl = MockSimControl();
|
|
|
|
mockXcode = MockXcode();
|
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('is true on module project', () async {
|
|
|
|
globals.fs.file('pubspec.yaml')
|
|
|
|
..createSync()
|
|
|
|
..writeAsStringSync(r'''
|
2019-04-25 19:25:12 +00:00
|
|
|
name: example
|
|
|
|
|
|
|
|
flutter:
|
|
|
|
module: {}
|
|
|
|
''');
|
2020-03-06 21:16:01 +00:00
|
|
|
globals.fs.file('.packages').createSync();
|
|
|
|
final FlutterProject flutterProject = FlutterProject.current();
|
2019-04-25 19:25:12 +00:00
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator simulator = IOSSimulator(
|
|
|
|
'test',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
expect(simulator.isSupportedForProject(flutterProject), true);
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
FileSystem: () => MemoryFileSystem(),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
|
|
});
|
2019-04-25 19:25:12 +00:00
|
|
|
|
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
testUsingContext('is true with editable host app', () async {
|
|
|
|
globals.fs.file('pubspec.yaml').createSync();
|
|
|
|
globals.fs.file('.packages').createSync();
|
|
|
|
globals.fs.directory('ios').createSync();
|
|
|
|
final FlutterProject flutterProject = FlutterProject.current();
|
2019-04-25 19:25:12 +00:00
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator simulator = IOSSimulator(
|
|
|
|
'test',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
expect(simulator.isSupportedForProject(flutterProject), true);
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
FileSystem: () => MemoryFileSystem(),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('is false with no host app and no module', () async {
|
|
|
|
globals.fs.file('pubspec.yaml').createSync();
|
|
|
|
globals.fs.file('.packages').createSync();
|
|
|
|
final FlutterProject flutterProject = FlutterProject.current();
|
2019-04-25 19:25:12 +00:00
|
|
|
|
2020-03-06 21:16:01 +00:00
|
|
|
final IOSSimulator simulator = IOSSimulator(
|
|
|
|
'test',
|
|
|
|
simControl: mockSimControl,
|
|
|
|
xcode: mockXcode,
|
|
|
|
);
|
|
|
|
expect(simulator.isSupportedForProject(flutterProject), false);
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
FileSystem: () => MemoryFileSystem(),
|
|
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
|
|
});
|
2019-04-25 19:25:12 +00:00
|
|
|
});
|
2016-03-10 19:04:02 +00:00
|
|
|
}
|
2019-10-31 02:40:19 +00:00
|
|
|
|
|
|
|
class MockBuildSystem extends Mock implements BuildSystem {}
|