Add support for IOS_SIMULATOR_HOME environment variable in IOSSimulat… (#13236)

Add support for IOS_SIMULATOR_HOME environment variable in IOSSimulator.logFilePath

flutter_tools can be run on environments where the user's HOME directory
is not the root of the iOS simulators' configs. This change adds support
for such environments by allowing the caller to set the simulator root
directory via an environment variable.
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index a80485c..5b6112f 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -415,7 +415,9 @@
   }
 
   String get logFilePath {
-    return fs.path.join(homeDirPath, 'Library', 'Logs', 'CoreSimulator', id, 'system.log');
+    return platform.environment.containsKey('IOS_SIMULATOR_LOG_FILE_PATH')
+        ? platform.environment['IOS_SIMULATOR_LOG_FILE_PATH'].replaceAll('%{id}', id)
+        : fs.path.join(homeDirPath, 'Library', 'Logs', 'CoreSimulator', id, 'system.log');
   }
 
   @override
@@ -424,6 +426,13 @@
   @override
   Future<String> get sdkNameAndVersion async => category;
 
+  final RegExp _iosSdkRegExp = new RegExp(r'iOS (\d+)');
+
+  Future<int> get sdkMajorVersion async {
+    final Match sdkMatch = _iosSdkRegExp.firstMatch(await sdkNameAndVersion);
+    return int.parse(sdkMatch.group(1) ?? 11);
+  }
+
   @override
   DeviceLogReader getLogReader({ApplicationPackage app}) {
     assert(app is IOSApp);
@@ -444,10 +453,12 @@
     }
   }
 
-  void ensureLogsExists() {
-    final File logFile = fs.file(logFilePath);
-    if (!logFile.existsSync())
-      logFile.writeAsBytesSync(<int>[]);
+  Future<Null> ensureLogsExists() async {
+    if (await sdkMajorVersion < 11) {
+      final File logFile = fs.file(logFilePath);
+      if (!logFile.existsSync())
+        logFile.writeAsBytesSync(<int>[]);
+    }
   }
 
   bool get _xcodeVersionSupportsScreenshot {
@@ -463,15 +474,10 @@
   }
 }
 
-final RegExp _iosSdkRegExp = new RegExp(r'iOS (\d+)');
-
 /// Launches the device log reader process on the host.
 Future<Process> launchDeviceLogTool(IOSSimulator device) async {
-  final Match sdkMatch = _iosSdkRegExp.firstMatch(await device.sdkNameAndVersion);
-  final int majorVersion = int.parse(sdkMatch.group(1) ?? 11);
-
   // Versions of iOS prior to iOS 11 log to the simulator syslog file.
-  if (majorVersion < 11)
+  if (await device.sdkMajorVersion < 11)
     return runCommand(<String>['tail', '-n', '0', '-F', device.logFilePath]);
 
   // For iOS 11 and above, use /usr/bin/log to tail process logs.
@@ -482,11 +488,8 @@
 }
 
 Future<Process> launchSystemLogTool(IOSSimulator device) async {
-  final Match sdkMatch = _iosSdkRegExp.firstMatch(await device.sdkNameAndVersion);
-  final int majorVersion = int.parse(sdkMatch.group(1) ?? 11);
-
   // Versions of iOS prior to 11 tail the simulator syslog file.
-  if (majorVersion < 11)
+  if (await device.sdkMajorVersion < 11)
     return runCommand(<String>['tail', '-n', '0', '-F', '/private/var/log/system.log']);
 
   // For iOS 11 and later, all relevant detail is in the device log.
@@ -520,7 +523,7 @@
 
   Future<Null> _start() async {
     // Device log.
-    device.ensureLogsExists();
+    await device.ensureLogsExists();
     _deviceProcess = await launchDeviceLogTool(device);
     _deviceProcess.stdout.transform(UTF8.decoder).transform(const LineSplitter()).listen(_onDeviceLine);
     _deviceProcess.stderr.transform(UTF8.decoder).transform(const LineSplitter()).listen(_onDeviceLine);
diff --git a/packages/flutter_tools/test/ios/simulators_test.dart b/packages/flutter_tools/test/ios/simulators_test.dart
index 182b4ae..8cca0db 100644
--- a/packages/flutter_tools/test/ios/simulators_test.dart
+++ b/packages/flutter_tools/test/ios/simulators_test.dart
@@ -18,8 +18,29 @@
 class MockProcess extends Mock implements Process {}
 
 void main() {
-  final FakePlatform osx = new FakePlatform.fromPlatform(const LocalPlatform());
-  osx.operatingSystem = 'macos';
+  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', () {
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index f75f101..db567d7 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -68,6 +68,7 @@
   Timeout timeout,
   Map<Type, Generator> overrides: const <Type, Generator>{},
   ContextInitializer initializeContext: _defaultInitializeContext,
+  String testOn,
   bool skip, // should default to `false`, but https://github.com/dart-lang/test/issues/545 doesn't allow this
 }) {
 
@@ -124,7 +125,7 @@
       rethrow;
     }
 
-  }, timeout: timeout, skip: skip);
+  }, timeout: timeout, testOn: testOn, skip: skip);
 }
 
 void _printBufferedErrors(AppContext testContext) {