Extract checking IntelliJ packages into a helper and use for Android Studio. (#16198)

diff --git a/packages/flutter_tools/lib/src/android/android_studio.dart b/packages/flutter_tools/lib/src/android/android_studio.dart
index 45897b1..1c2d2ed 100644
--- a/packages/flutter_tools/lib/src/android/android_studio.dart
+++ b/packages/flutter_tools/lib/src/android/android_studio.dart
@@ -38,6 +38,7 @@
   final Version version;
   final String configured;
 
+  String _pluginsPath;
   String _javaPath;
   bool _isValid = false;
   final List<String> _validationMessages = <String>[];
@@ -81,6 +82,26 @@
 
   bool get isValid => _isValid;
 
+  String get pluginsPath {
+    if (_pluginsPath == null) {
+      final int major = version.major;
+      final int minor = version.minor;
+      if (platform.isMacOS) {
+        _pluginsPath = fs.path.join(
+            homeDirPath,
+            'Library',
+            'Application Support',
+            'AndroidStudio$major.$minor');
+      } else {
+        _pluginsPath = fs.path.join(homeDirPath,
+            '.AndroidStudio$major.$minor',
+            'config',
+            'plugins');
+      }
+    }
+    return _pluginsPath;
+  }
+
   List<String> get validationMessages => _validationMessages;
 
   @override
diff --git a/packages/flutter_tools/lib/src/android/android_studio_validator.dart b/packages/flutter_tools/lib/src/android/android_studio_validator.dart
index b472c70..aeb24b4 100644
--- a/packages/flutter_tools/lib/src/android/android_studio_validator.dart
+++ b/packages/flutter_tools/lib/src/android/android_studio_validator.dart
@@ -7,6 +7,7 @@
 import '../base/version.dart';
 import '../doctor.dart';
 import '../globals.dart';
+import '../intellij/intellij.dart';
 import 'android_studio.dart';
 
 class AndroidStudioValidator extends DoctorValidator {
@@ -30,11 +31,18 @@
   Future<ValidationResult> validate() async {
     final List<ValidationMessage> messages = <ValidationMessage>[];
     ValidationType type = ValidationType.missing;
+
     final String studioVersionText = _studio.version == Version.unknown
         ? null
         : 'version ${_studio.version}';
     messages
         .add(new ValidationMessage('Android Studio at ${_studio.directory}'));
+
+    final IntelliJPlugins plugins = new IntelliJPlugins(_studio.pluginsPath);
+    plugins.validatePackage(messages, <String>['flutter-intellij', 'flutter-intellij.jar'],
+        'Flutter', minVersion: IntelliJPlugins.kMinFlutterPluginVersion);
+    plugins.validatePackage(messages, <String>['Dart'], 'Dart');
+
     if (_studio.isValid) {
       type = ValidationType.installed;
       messages.addAll(_studio.validationMessages
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index 1a70543..8b31a4d 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -3,9 +3,6 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert' show utf8;
-
-import 'package:archive/archive.dart';
 
 import 'android/android_studio_validator.dart';
 import 'android/android_workflow.dart';
@@ -21,6 +18,7 @@
 import 'cache.dart';
 import 'device.dart';
 import 'globals.dart';
+import 'intellij/intellij.dart';
 import 'ios/ios_workflow.dart';
 import 'ios/plist_utils.dart';
 import 'version.dart';
@@ -316,7 +314,6 @@
   };
 
   static final Version kMinIdeaVersion = new Version(2017, 1, 0);
-  static final Version kMinFlutterPluginVersion = new Version(16, 0, 0);
 
   static Iterable<DoctorValidator> get installedValidators {
     if (platform.isLinux || platform.isWindows)
@@ -332,9 +329,10 @@
 
     messages.add(new ValidationMessage('IntelliJ at $installPath'));
 
-    _validatePackage(messages, <String>['flutter-intellij', 'flutter-intellij.jar'],
-        'Flutter', minVersion: kMinFlutterPluginVersion);
-    _validatePackage(messages, <String>['Dart'], 'Dart');
+    final IntelliJPlugins plugins = new IntelliJPlugins(pluginsPath);
+    plugins.validatePackage(messages, <String>['flutter-intellij', 'flutter-intellij.jar'],
+        'Flutter', minVersion: IntelliJPlugins.kMinFlutterPluginVersion);
+    plugins.validatePackage(messages, <String>['Dart'], 'Dart');
 
     if (_hasIssues(messages)) {
       messages.add(new ValidationMessage(
@@ -371,70 +369,16 @@
       ));
     }
   }
-
-  void _validatePackage(List<ValidationMessage> messages, List<String> packageNames, String title, {
-    Version minVersion
-  }) {
-    for (String packageName in packageNames) {
-      if (!hasPackage(packageName)) {
-        continue;
-      }
-
-      final String versionText = _readPackageVersion(packageName);
-      final Version version = new Version.parse(versionText);
-      if (version != null && minVersion != null && version < minVersion) {
-        messages.add(new ValidationMessage.error(
-          '$title plugin version $versionText - the recommended minimum version is $minVersion'
-        ));
-      } else {
-        messages.add(new ValidationMessage(
-          '$title plugin ${version != null ? "version $version" : "installed"}'
-        ));
-      }
-
-      return;
-    }
-
-    messages.add(new ValidationMessage.error(
-      '$title plugin not installed; this adds $title specific functionality.'
-    ));
-  }
-
-  String _readPackageVersion(String packageName) {
-    final String jarPath = packageName.endsWith('.jar')
-        ? fs.path.join(pluginsPath, packageName)
-        : fs.path.join(pluginsPath, packageName, 'lib', '$packageName.jar');
-    // TODO(danrubel) look for a better way to extract a single 2K file from the zip
-    // rather than reading the entire file into memory.
-    try {
-      final Archive archive = new ZipDecoder().decodeBytes(fs.file(jarPath).readAsBytesSync());
-      final ArchiveFile file = archive.findFile('META-INF/plugin.xml');
-      final String content = utf8.decode(file.content);
-      const String versionStartTag = '<version>';
-      final int start = content.indexOf(versionStartTag);
-      final int end = content.indexOf('</version>', start);
-      return content.substring(start + versionStartTag.length, end);
-    } catch (_) {
-      return null;
-    }
-  }
-
-  bool hasPackage(String packageName) {
-    final String packagePath = fs.path.join(pluginsPath, packageName);
-    if (packageName.endsWith('.jar'))
-      return fs.isFileSync(packagePath);
-    return fs.isDirectorySync(packagePath);
-  }
 }
 
 class IntelliJValidatorOnLinuxAndWindows extends IntelliJValidator {
   IntelliJValidatorOnLinuxAndWindows(String title, this.version, String installPath, this.pluginsPath) : super(title, installPath);
 
   @override
-  String version;
+  final String version;
 
   @override
-  String pluginsPath;
+  final String pluginsPath;
 
   static Iterable<DoctorValidator> get installed {
     final List<DoctorValidator> validators = <DoctorValidator>[];
diff --git a/packages/flutter_tools/lib/src/intellij/intellij.dart b/packages/flutter_tools/lib/src/intellij/intellij.dart
new file mode 100644
index 0000000..bf34145
--- /dev/null
+++ b/packages/flutter_tools/lib/src/intellij/intellij.dart
@@ -0,0 +1,71 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:archive/archive.dart';
+
+import '../base/file_system.dart';
+import '../base/version.dart';
+import '../doctor.dart';
+
+class IntelliJPlugins {
+  static final Version kMinFlutterPluginVersion = new Version(16, 0, 0);
+
+  final String pluginsPath;
+
+  IntelliJPlugins(this.pluginsPath);
+
+  void validatePackage(
+      List<ValidationMessage> messages, List<String> packageNames, String title,
+      {Version minVersion}) {
+    for (String packageName in packageNames) {
+      if (!_hasPackage(packageName)) {
+        continue;
+      }
+
+      final String versionText = _readPackageVersion(packageName);
+      final Version version = new Version.parse(versionText);
+      if (version != null && minVersion != null && version < minVersion) {
+        messages.add(new ValidationMessage.error(
+            '$title plugin version $versionText - the recommended minimum version is $minVersion'));
+      } else {
+        messages.add(new ValidationMessage(
+            '$title plugin ${version != null ? "version $version" : "installed"}'));
+      }
+
+      return;
+    }
+
+    messages.add(new ValidationMessage.error(
+        '$title plugin not installed; this adds $title specific functionality.'));
+  }
+
+  bool _hasPackage(String packageName) {
+    final String packagePath = fs.path.join(pluginsPath, packageName);
+    if (packageName.endsWith('.jar'))
+      return fs.isFileSync(packagePath);
+    return fs.isDirectorySync(packagePath);
+  }
+
+  String _readPackageVersion(String packageName) {
+    final String jarPath = packageName.endsWith('.jar')
+        ? fs.path.join(pluginsPath, packageName)
+        : fs.path.join(pluginsPath, packageName, 'lib', '$packageName.jar');
+    // TODO(danrubel) look for a better way to extract a single 2K file from the zip
+    // rather than reading the entire file into memory.
+    try {
+      final Archive archive =
+          new ZipDecoder().decodeBytes(fs.file(jarPath).readAsBytesSync());
+      final ArchiveFile file = archive.findFile('META-INF/plugin.xml');
+      final String content = utf8.decode(file.content);
+      const String versionStartTag = '<version>';
+      final int start = content.indexOf(versionStartTag);
+      final int end = content.indexOf('</version>', start);
+      return content.substring(start + versionStartTag.length, end);
+    } catch (_) {
+      return null;
+    }
+  }
+}
diff --git a/packages/flutter_tools/test/intellij/intellij_test.dart b/packages/flutter_tools/test/intellij/intellij_test.dart
new file mode 100644
index 0000000..b1bb2bf
--- /dev/null
+++ b/packages/flutter_tools/test/intellij/intellij_test.dart
@@ -0,0 +1,57 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/doctor.dart';
+import 'package:flutter_tools/src/intellij/intellij.dart';
+import 'package:test/test.dart';
+
+import '../src/context.dart';
+
+void main() {
+  group('IntelliJ', () {
+    group('plugins', () {
+      testUsingContext('found', () async {
+        final String pluginsPath =
+            fs.path.join('test', 'data', 'intellij', 'plugins');
+        final IntelliJPlugins plugins = new IntelliJPlugins(pluginsPath);
+
+        final List<ValidationMessage> messages = <ValidationMessage>[];
+        plugins.validatePackage(messages,
+            <String>['flutter-intellij', 'flutter-intellij.jar'], 'Flutter',
+            minVersion: IntelliJPlugins.kMinFlutterPluginVersion);
+        plugins.validatePackage(messages, <String>['Dart'], 'Dart');
+
+        ValidationMessage message = messages
+            .firstWhere((ValidationMessage m) => m.message.startsWith('Dart '));
+        expect(message.message, 'Dart plugin version 162.2485');
+
+        message = messages.firstWhere(
+            (ValidationMessage m) => m.message.startsWith('Flutter '));
+        expect(message.message, contains('Flutter plugin version 0.1.3'));
+        expect(message.message, contains('recommended minimum version'));
+      });
+
+      testUsingContext('not found', () async {
+        final String pluginsPath =
+            fs.path.join('test', 'data', 'intellij', 'no_plugins');
+        final IntelliJPlugins plugins = new IntelliJPlugins(pluginsPath);
+
+        final List<ValidationMessage> messages = <ValidationMessage>[];
+        plugins.validatePackage(messages,
+            <String>['flutter-intellij', 'flutter-intellij.jar'], 'Flutter',
+            minVersion: IntelliJPlugins.kMinFlutterPluginVersion);
+        plugins.validatePackage(messages, <String>['Dart'], 'Dart');
+
+        ValidationMessage message = messages
+            .firstWhere((ValidationMessage m) => m.message.startsWith('Dart '));
+        expect(message.message, contains('Dart plugin not installed'));
+
+        message = messages.firstWhere(
+            (ValidationMessage m) => m.message.startsWith('Flutter '));
+        expect(message.message, contains('Flutter plugin not installed'));
+      });
+    });
+  });
+}