| import 'dart:async'; |
| import 'dart:io' show ProcessResult, Process; |
| |
| import 'package:file/file.dart'; |
| import 'package:flutter_tools/src/device.dart'; |
| import 'package:flutter_tools/src/application_package.dart'; |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/ios/mac.dart'; |
| import 'package:flutter_tools/src/ios/simulators.dart'; |
| import 'package:mockito/mockito.dart'; |
| import 'package:platform/platform.dart'; |
| import 'package:process/process.dart'; |
| import 'package:test/test.dart'; |
| |
| import '../src/context.dart'; |
| |
| class MockFile extends Mock implements File {} |
| class MockIMobileDevice extends Mock implements IMobileDevice {} |
| class MockProcess extends Mock implements Process {} |
| class MockProcessManager extends Mock implements ProcessManager {} |
| class MockXcode extends Mock implements Xcode {} |
| |
| void main() { |
| FakePlatform osx; |
| |
| setUp(() { |
| osx = new FakePlatform.fromPlatform(const LocalPlatform()); |
| osx.operatingSystem = 'macos'; |
| }); |
| |
| group('logFilePath', () { |
| testUsingContext('defaults to rooted from HOME', () { |
| osx.environment['HOME'] = '/foo/bar'; |
| expect(new IOSSimulator('123').logFilePath, '/foo/bar/Library/Logs/CoreSimulator/123/system.log'); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }, 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'; |
| expect(new IOSSimulator('456').logFilePath, '/baz/qux/456/system.log'); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }); |
| }); |
| |
| group('compareIosVersions', () { |
| test('compares correctly', () { |
| // This list must be sorted in ascending preference order |
| final List<String> testList = <String>[ |
| '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', () { |
| test('compares correctly', () { |
| // This list must be sorted in ascending preference order |
| final List<String> testList = <String>[ |
| 'com.apple.CoreSimulator.SimDeviceType.iPhone-4s', |
| 'com.apple.CoreSimulator.SimDeviceType.iPhone-5', |
| 'com.apple.CoreSimulator.SimDeviceType.iPhone-5s', |
| 'com.apple.CoreSimulator.SimDeviceType.iPhone-6strange', |
| 'com.apple.CoreSimulator.SimDeviceType.iPhone-6-Plus', |
| 'com.apple.CoreSimulator.SimDeviceType.iPhone-6', |
| 'com.apple.CoreSimulator.SimDeviceType.iPhone-6s-Plus', |
| 'com.apple.CoreSimulator.SimDeviceType.iPhone-6s', |
| ]; |
| |
| for (int i = 0; i < testList.length; i++) { |
| expect(compareIphoneVersions(testList[i], testList[i]), 0); |
| } |
| |
| for (int i = 0; i < testList.length - 1; i++) { |
| for (int j = i + 1; j < testList.length; j++) { |
| expect(compareIphoneVersions(testList[i], testList[j]), lessThan(0)); |
| expect(compareIphoneVersions(testList[j], testList[i]), greaterThan(0)); |
| } |
| } |
| }); |
| }); |
| |
| group('IOSSimulator.isSupported', () { |
| testUsingContext('Apple TV is unsupported', () { |
| expect(new IOSSimulator('x', name: 'Apple TV').isSupported(), false); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }); |
| |
| testUsingContext('Apple Watch is unsupported', () { |
| expect(new IOSSimulator('x', name: 'Apple Watch').isSupported(), false); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }); |
| |
| testUsingContext('iPad 2 is unsupported', () { |
| expect(new IOSSimulator('x', name: 'iPad 2').isSupported(), false); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }); |
| |
| testUsingContext('iPad Retina is unsupported', () { |
| expect(new IOSSimulator('x', name: 'iPad Retina').isSupported(), false); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }); |
| |
| testUsingContext('iPhone 5 is unsupported', () { |
| expect(new IOSSimulator('x', name: 'iPhone 5').isSupported(), false); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }); |
| |
| testUsingContext('iPhone 5s is supported', () { |
| expect(new IOSSimulator('x', name: 'iPhone 5s').isSupported(), true); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }); |
| |
| testUsingContext('iPhone SE is supported', () { |
| expect(new IOSSimulator('x', name: 'iPhone SE').isSupported(), true); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }); |
| |
| testUsingContext('iPhone 7 Plus is supported', () { |
| expect(new IOSSimulator('x', name: 'iPhone 7 Plus').isSupported(), true); |
| }, overrides: <Type, Generator>{ |
| Platform: () => osx, |
| }); |
| }); |
| |
| group('Simulator screenshot', () { |
| MockXcode mockXcode; |
| MockProcessManager mockProcessManager; |
| IOSSimulator deviceUnderTest; |
| |
| setUp(() { |
| mockXcode = new MockXcode(); |
| mockProcessManager = new MockProcessManager(); |
| // Let everything else return exit code 0 so process.dart doesn't crash. |
| when( |
| mockProcessManager.run(any, environment: null, workingDirectory: null) |
| ).thenAnswer((Invocation invocation) => |
| new Future<ProcessResult>.value(new ProcessResult(2, 0, '', '')) |
| ); |
| // Doesn't matter what the device is. |
| deviceUnderTest = new IOSSimulator('x', name: 'iPhone SE'); |
| }); |
| |
| testUsingContext( |
| 'old Xcode doesn\'t support screenshot', |
| () { |
| when(mockXcode.xcodeMajorVersion).thenReturn(7); |
| when(mockXcode.xcodeMinorVersion).thenReturn(1); |
| expect(deviceUnderTest.supportsScreenshot, false); |
| }, |
| overrides: <Type, Generator>{Xcode: () => mockXcode} |
| ); |
| |
| testUsingContext( |
| 'Xcode 8.2+ supports screenshots', |
| () async { |
| when(mockXcode.xcodeMajorVersion).thenReturn(8); |
| when(mockXcode.xcodeMinorVersion).thenReturn(2); |
| expect(deviceUnderTest.supportsScreenshot, true); |
| final MockFile mockFile = new MockFile(); |
| when(mockFile.path).thenReturn(fs.path.join('some', 'path', 'to', 'screenshot.png')); |
| await deviceUnderTest.takeScreenshot(mockFile); |
| verify(mockProcessManager.run( |
| <String>[ |
| '/usr/bin/xcrun', |
| 'simctl', |
| 'io', |
| 'x', |
| 'screenshot', |
| fs.path.join('some', 'path', 'to', 'screenshot.png'), |
| ], |
| environment: null, |
| workingDirectory: null |
| )); |
| }, |
| overrides: <Type, Generator>{ |
| ProcessManager: () => mockProcessManager, |
| // Test a real one. Screenshot doesn't require instance states. |
| SimControl: () => new SimControl(), |
| Xcode: () => mockXcode, |
| } |
| ); |
| }); |
| |
| group('launchDeviceLogTool', () { |
| MockProcessManager mockProcessManager; |
| |
| setUp(() { |
| mockProcessManager = new MockProcessManager(); |
| when(mockProcessManager.start(any, environment: null, workingDirectory: null)) |
| .thenAnswer((Invocation invocation) => new Future<Process>.value(new MockProcess())); |
| }); |
| |
| testUsingContext('uses tail on iOS versions prior to iOS 11', () async { |
| final IOSSimulator device = new IOSSimulator('x', name: 'iPhone SE', category: 'iOS 9.3'); |
| await launchDeviceLogTool(device); |
| expect( |
| verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, |
| contains('tail'), |
| ); |
| }, |
| overrides: <Type, Generator>{ |
| ProcessManager: () => mockProcessManager, |
| }); |
| |
| testUsingContext('uses /usr/bin/log on iOS 11 and above', () async { |
| final IOSSimulator device = new IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.0'); |
| await launchDeviceLogTool(device); |
| expect( |
| verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, |
| contains('/usr/bin/log'), |
| ); |
| }, |
| overrides: <Type, Generator>{ |
| ProcessManager: () => mockProcessManager, |
| }); |
| }); |
| |
| group('launchSystemLogTool', () { |
| MockProcessManager mockProcessManager; |
| |
| setUp(() { |
| mockProcessManager = new MockProcessManager(); |
| when(mockProcessManager.start(any, environment: null, workingDirectory: null)) |
| .thenAnswer((Invocation invocation) => new Future<Process>.value(new MockProcess())); |
| }); |
| |
| testUsingContext('uses tail on iOS versions prior to iOS 11', () async { |
| final IOSSimulator device = new IOSSimulator('x', name: 'iPhone SE', category: 'iOS 9.3'); |
| await launchSystemLogTool(device); |
| expect( |
| verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, |
| contains('tail'), |
| ); |
| }, |
| overrides: <Type, Generator>{ |
| ProcessManager: () => mockProcessManager, |
| }); |
| |
| testUsingContext('uses /usr/bin/log on iOS 11 and above', () async { |
| final IOSSimulator device = new IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.0'); |
| await launchSystemLogTool(device); |
| verifyNever(mockProcessManager.start(any, environment: null, workingDirectory: null)); |
| }, |
| overrides: <Type, Generator>{ |
| ProcessManager: () => mockProcessManager, |
| }); |
| }); |
| |
| group('log reader', () { |
| MockProcessManager mockProcessManager; |
| |
| setUp(() { |
| mockProcessManager = new MockProcessManager(); |
| }); |
| |
| testUsingContext('simulator can output `)`', () async { |
| when(mockProcessManager.start(any, environment: null, workingDirectory: null)) |
| .thenAnswer((Invocation invocation) { |
| final Process mockProcess = new MockProcess(); |
| when(mockProcess.stdout).thenAnswer((Invocation invocation) => |
| new Stream<List<int>>.fromIterable(<List<int>>[''' |
| 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) Observatory listening on http://127.0.0.1:57701/ |
| 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) )))))))))) |
| 2017-09-13 15:26:57.228948-0700 localhost Runner[37195]: (Flutter) #0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)''' |
| .codeUnits])); |
| when(mockProcess.stderr) |
| .thenAnswer((Invocation invocation) => const Stream<List<int>>.empty()); |
| // Delay return of exitCode until after stdout stream data, since it terminates the logger. |
| when(mockProcess.exitCode) |
| .thenAnswer((Invocation invocation) => new Future<int>.delayed(Duration.ZERO, () => 0)); |
| return new Future<Process>.value(mockProcess); |
| }) |
| .thenThrow(new TestFailure('Should start one process only')); |
| |
| final IOSSimulator device = new IOSSimulator('123456', category: 'iOS 11.0'); |
| final DeviceLogReader logReader = device.getLogReader( |
| app: new BuildableIOSApp(projectBundleId: 'bundleId'), |
| ); |
| |
| 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)', |
| ]); |
| }, overrides: <Type, Generator>{ |
| ProcessManager: () => mockProcessManager, |
| }); |
| }); |
| } |