Move simulator screenshot logic to use simctl (#8216) * Move simulator screenshot logic to use simctl * Add simulator screenshot tests
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index fa229e0..20cc97a 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -76,6 +76,12 @@ String _xcodeVersionText; String get xcodeVersionText => _xcodeVersionText; + int _xcodeMajorVersion; + int get xcodeMajorVersion => _xcodeMajorVersion; + + int _xcodeMinorVersion; + int get xcodeMinorVersion => _xcodeMinorVersion; + final RegExp xcodeVersionRegex = new RegExp(r'Xcode ([0-9.]+)'); bool get xcodeVersionSatisfactory { @@ -85,10 +91,10 @@ String version = xcodeVersionRegex.firstMatch(xcodeVersionText).group(1); List<String> components = version.split('.'); - int major = int.parse(components[0]); - int minor = components.length == 1 ? 0 : int.parse(components[1]); + _xcodeMajorVersion = int.parse(components[0]); + _xcodeMinorVersion = components.length == 1 ? 0 : int.parse(components[1]); - return _xcodeVersionCheckValid(major, minor); + return _xcodeVersionCheckValid(_xcodeMajorVersion, _xcodeMinorVersion); } }
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index 69143fd..ebf23f3 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -249,6 +249,10 @@ args.addAll(launchArgs); runCheckedSync(args); } + + void takeScreenshot(String outputPath) { + runCheckedSync(<String>[_xcrunPath, 'simctl', 'io', 'booted', 'screenshot', outputPath]); + } } /// Enumerates all data sections of `xcrun simctl list --json` command. @@ -582,44 +586,18 @@ logFile.writeAsBytesSync(<int>[]); } - @override - bool get supportsScreenshot => true; + bool get _xcodeVersionSupportsScreenshot { + return Xcode.instance.xcodeMajorVersion > 8 || + (Xcode.instance.xcodeMajorVersion == 8 && Xcode.instance.xcodeMinorVersion >= 2); + } @override - Future<Null> takeScreenshot(File outputFile) async { - Directory desktopDir = fs.directory(fs.path.join(homeDirPath, 'Desktop')); + bool get supportsScreenshot => _xcodeVersionSupportsScreenshot; - // 'Simulator Screen Shot Mar 25, 2016, 2.59.43 PM.png' - - Set<File> getScreenshots() { - return new Set<File>.from(desktopDir.listSync().where((FileSystemEntity entity) { - String name = fs.path.basename(entity.path); - return entity is File && name.startsWith('Simulator') && name.endsWith('.png'); - })); - } - - Set<File> existingScreenshots = getScreenshots(); - - runSync(<String>[ - 'osascript', - '-e', - 'activate application "Simulator"\n' - 'tell application "System Events" to keystroke "s" using command down' - ]); - - // There is some latency here from the applescript call. - await new Future<Null>.delayed(new Duration(seconds: 1)); - - Set<File> shots = getScreenshots().difference(existingScreenshots); - - if (shots.isEmpty) { - printError('Unable to locate the screenshot file.'); - return false; - } - - File shot = shots.first; - outputFile.writeAsBytesSync(shot.readAsBytesSync()); - shot.delete(); + @override + Future<Null> takeScreenshot(File outputFile) { + SimControl.instance.takeScreenshot(outputFile.path); + return new Future<Null>.value(); } }
diff --git a/packages/flutter_tools/test/src/ios/simulators_test.dart b/packages/flutter_tools/test/src/ios/simulators_test.dart index cb73c6a..37202e0 100644 --- a/packages/flutter_tools/test/src/ios/simulators_test.dart +++ b/packages/flutter_tools/test/src/ios/simulators_test.dart
@@ -1,6 +1,17 @@ +import 'dart:io' show ProcessResult; + +import 'package:file/file.dart'; +import 'package:flutter_tools/src/ios/mac.dart'; +import 'package:flutter_tools/src/ios/simulators.dart'; +import 'package:mockito/mockito.dart'; +import 'package:process/process.dart'; import 'package:test/test.dart'; -import 'package:flutter_tools/src/ios/simulators.dart'; +import '../context.dart'; + +class MockXcode extends Mock implements Xcode {} +class MockFile extends Mock implements File {} +class MockProcessManager extends Mock implements ProcessManager {} void main() { group('compareIosVersions', () { @@ -85,4 +96,63 @@ expect(new IOSSimulator('x', name: 'iPhone 7 Plus').isSupported(), true); }); }); + + 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.runSync(any, environment: null, workingDirectory: null) + ).thenReturn( + new ProcessResult(2, 0, '', null) + ); + // 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', + () { + when(mockXcode.xcodeMajorVersion).thenReturn(8); + when(mockXcode.xcodeMinorVersion).thenReturn(2); + expect(deviceUnderTest.supportsScreenshot, true); + MockFile mockFile = new MockFile(); + when(mockFile.path).thenReturn('/some/path/to/screenshot.png'); + deviceUnderTest.takeScreenshot(mockFile); + verify(mockProcessManager.runSync( + <String>[ + '/usr/bin/xcrun', + 'simctl', + 'io', + 'booted', + 'screenshot', + '/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, + } + ); + }); }