Support for running JUnit tests.

- Enable "Gradle-aware make" step in JUnit run configurations.
- Make it run the correct Gradle task.
- Set the correct compiler test output path in android modules.
- a JUnitPatcher that removes stub android.jar from the
  test classpath.
- Don't offer android instrumentation test configurations when
  the selected artifact is "unit tests".
- Fix BuildVariantView layout.

Change-Id: I02fc9f7c3a7512b4b7162bddb78bff9dc6bd589b
(cherry picked from commit be2339152df0f20ee336d37199a1ce7d896b4ba9)
diff --git a/android/src/META-INF/plugin.xml b/android/src/META-INF/plugin.xml
index 6c954ca..eaa39fd 100755
--- a/android/src/META-INF/plugin.xml
+++ b/android/src/META-INF/plugin.xml
@@ -664,6 +664,7 @@
     <spellchecker.support language="Groovy" implementationClass="org.jetbrains.android.spellchecker.AndroidGradleSpellcheckingStrategy" order="first"/>
     <deadCode implementation="org.jetbrains.android.inspections.AndroidComponentEntryPoint"/>
     <testSrcLocator implementation="org.jetbrains.android.run.testing.AndroidTestLocationProvider"/>
+    <junitPatcher implementation="org.jetbrains.android.run.testing.AndroidJunitPatcher" />
 
     <virtualFileSystem key="android-dummy" implementationClass="com.android.tools.idea.editors.AndroidFakeFileSystem"/>
   </extensions>
diff --git a/android/src/com/android/tools/idea/gradle/customizer/android/CompilerOutputModuleCustomizer.java b/android/src/com/android/tools/idea/gradle/customizer/android/CompilerOutputModuleCustomizer.java
index f182500..2111dc9 100644
--- a/android/src/com/android/tools/idea/gradle/customizer/android/CompilerOutputModuleCustomizer.java
+++ b/android/src/com/android/tools/idea/gradle/customizer/android/CompilerOutputModuleCustomizer.java
@@ -15,6 +15,7 @@
  */
 package com.android.tools.idea.gradle.customizer.android;
 
+import com.android.builder.model.BaseArtifact;
 import com.android.builder.model.Variant;
 import com.android.tools.idea.gradle.IdeaAndroidProject;
 import com.android.tools.idea.gradle.customizer.AbstractCompileOutputModuleCustomizer;
@@ -44,8 +45,11 @@
       return;
     }
     Variant selectedVariant = androidProject.getSelectedVariant();
-    File outputFile = selectedVariant.getMainArtifact().getClassesFolder();
-    setOutputPaths(module, outputFile, null);
+    File mainClassesFolder = selectedVariant.getMainArtifact().getClassesFolder();
+    BaseArtifact testArtifact = androidProject.findSelectedTestArtifact(selectedVariant);
+    File testClassesFolder = testArtifact == null ? null : testArtifact.getClassesFolder();
+
+    setOutputPaths(module, mainClassesFolder, testClassesFolder);
   }
 
   @Override
diff --git a/android/src/com/android/tools/idea/gradle/invoker/GradleInvoker.java b/android/src/com/android/tools/idea/gradle/invoker/GradleInvoker.java
index 55b3a13..d8df831 100644
--- a/android/src/com/android/tools/idea/gradle/invoker/GradleInvoker.java
+++ b/android/src/com/android/tools/idea/gradle/invoker/GradleInvoker.java
@@ -280,7 +280,7 @@
           tasks.add(createBuildTask(gradlePath, properties.COMPILE_JAVA_TASK_NAME));
       }
 
-      if (testCompileType == TestCompileType.ANDROID_TESTS) {
+      if (testCompileType != TestCompileType.NONE) {
         String gradleTaskName = properties.ASSEMBLE_TEST_TASK_NAME;
         if (StringUtil.isNotEmpty(gradleTaskName)) {
           tasks.add(createBuildTask(gradlePath, gradleTaskName));
diff --git a/android/src/com/android/tools/idea/gradle/run/MakeBeforeRunTaskProvider.java b/android/src/com/android/tools/idea/gradle/run/MakeBeforeRunTaskProvider.java
index 42f67a8..6c4d795 100644
--- a/android/src/com/android/tools/idea/gradle/run/MakeBeforeRunTaskProvider.java
+++ b/android/src/com/android/tools/idea/gradle/run/MakeBeforeRunTaskProvider.java
@@ -30,6 +30,7 @@
 import com.intellij.execution.BeforeRunTaskProvider;
 import com.intellij.execution.configurations.ModuleBasedConfiguration;
 import com.intellij.execution.configurations.RunConfiguration;
+import com.intellij.execution.junit.JUnitConfiguration;
 import com.intellij.execution.runners.ExecutionEnvironment;
 import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.diagnostic.Logger;
@@ -42,6 +43,7 @@
 import com.intellij.util.concurrency.Semaphore;
 import icons.AndroidIcons;
 import org.jetbrains.android.run.AndroidRunConfigurationBase;
+import org.jetbrains.android.util.AndroidUtils;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -107,7 +109,11 @@
   @Override
   public MakeBeforeRunTask createTask(RunConfiguration runConfiguration) {
     // "Gradle-aware Make" is only available in Android Studio.
-    if (AndroidStudioSpecificInitializer.isAndroidStudio() && runConfiguration instanceof AndroidRunConfigurationBase) {
+    if (AndroidStudioSpecificInitializer.isAndroidStudio()
+        // Enable "Gradle-aware Make" only for android configurations...
+        && (runConfiguration instanceof AndroidRunConfigurationBase  ||
+            // ...and JUnit configurations if unit-testing support is enabled.
+            (AndroidUtils.isUnitTestingSupportEnabled() && runConfiguration instanceof JUnitConfiguration))) {
       return new MakeBeforeRunTask();
     } else {
       return null;
diff --git a/android/src/org/jetbrains/android/run/testing/AndroidJunitPatcher.java b/android/src/org/jetbrains/android/run/testing/AndroidJunitPatcher.java
new file mode 100644
index 0000000..160a790
--- /dev/null
+++ b/android/src/org/jetbrains/android/run/testing/AndroidJunitPatcher.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.android.run.testing;
+
+import com.android.builder.model.AndroidProject;
+import com.android.sdklib.IAndroidTarget;
+import com.android.tools.idea.gradle.IdeaAndroidProject;
+import com.intellij.execution.JUnitPatcher;
+import com.intellij.execution.configurations.JavaParameters;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkAdditionalData;
+import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.util.PathsList;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.android.sdk.AndroidPlatform;
+import org.jetbrains.android.sdk.AndroidSdkAdditionalData;
+import org.jetbrains.android.sdk.AndroidSdkType;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Implementation of {@link com.intellij.execution.JUnitPatcher} that removes android.jar from the class path. It's only applicable to
+ * JUnit run configurations if the selected test artifact is "unit tests". In this case, the mockable android.jar is already in the
+ * dependencies (taken from the model).
+ */
+public class AndroidJunitPatcher extends JUnitPatcher {
+  @Override
+  public void patchJavaParameters(@Nullable Module module, JavaParameters javaParameters) {
+    if (module == null) {
+      return;
+    }
+
+    AndroidFacet androidFacet = AndroidFacet.getInstance(module);
+    if (androidFacet == null) {
+      return;
+    }
+
+    IdeaAndroidProject ideaAndroidProject = androidFacet.getIdeaAndroidProject();
+    if (ideaAndroidProject == null) {
+      return;
+    }
+
+    // Modify the class path only if we're dealing with the unit test artifact.
+    if (!ideaAndroidProject.getSelectedTestArtifactName().equals(AndroidProject.ARTIFACT_UNIT_TEST)) {
+      return;
+    }
+
+    final PathsList classPath = javaParameters.getClassPath();
+
+    final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
+    if (sdk == null || !(sdk.getSdkType() instanceof AndroidSdkType)) {
+      return;
+    }
+
+    final SdkAdditionalData data = sdk.getSdkAdditionalData();
+    if (!(data instanceof AndroidSdkAdditionalData)) {
+      return;
+    }
+
+    final AndroidPlatform platform = ((AndroidSdkAdditionalData)data).getAndroidPlatform();
+    if (platform == null) {
+      return;
+    }
+
+    classPath.remove(platform.getTarget().getPath(IAndroidTarget.ANDROID_JAR));
+  }
+}
diff --git a/android/src/org/jetbrains/android/run/testing/AndroidTestConfigurationProducer.java b/android/src/org/jetbrains/android/run/testing/AndroidTestConfigurationProducer.java
index 4f4d664..3b13e0c 100644
--- a/android/src/org/jetbrains/android/run/testing/AndroidTestConfigurationProducer.java
+++ b/android/src/org/jetbrains/android/run/testing/AndroidTestConfigurationProducer.java
@@ -16,6 +16,8 @@
 
 package org.jetbrains.android.run.testing;
 
+import com.android.builder.model.AndroidProject;
+import com.android.tools.idea.gradle.IdeaAndroidProject;
 import com.intellij.execution.JavaExecutionUtil;
 import com.intellij.execution.Location;
 import com.intellij.execution.actions.ConfigurationContext;
@@ -142,6 +144,7 @@
     if (module == null) {
       return false;
     }
+
     Location location = context.getLocation();
 
     if (location == null) {
@@ -159,6 +162,12 @@
       return false;
     }
 
+    IdeaAndroidProject ideaAndroidProject = facet.getIdeaAndroidProject();
+    if (ideaAndroidProject != null && !ideaAndroidProject.getSelectedTestArtifactName().equals(AndroidProject.ARTIFACT_ANDROID_TEST)) {
+      // Only suggest the android test run configuration if it makes sense for the selected test artifact.
+      return false;
+    }
+
     setupInstrumentationTestRunner(configuration, facet);
     if (setupAllInPackageConfiguration(configuration, element, context, sourceElement)) {
       return true;
diff --git a/android/src/org/jetbrains/android/util/AndroidUtils.java b/android/src/org/jetbrains/android/util/AndroidUtils.java
index 8ba5585..be048a4 100644
--- a/android/src/org/jetbrains/android/util/AndroidUtils.java
+++ b/android/src/org/jetbrains/android/util/AndroidUtils.java
@@ -272,7 +272,7 @@
           configuration.PREFERRED_AVD = preferredAvdName;
         }
         runManager.addConfiguration(settings, false);
-        runManager.setActiveConfiguration(settings);
+        runManager.setSelectedConfiguration(settings);
       }
     };
     if (!ask) {
diff --git a/android/testData/guiTests/SimpleApplicationWithUnitTests/app/build.gradle b/android/testData/guiTests/SimpleApplicationWithUnitTests/app/build.gradle
index 224b369..27ed973 100644
--- a/android/testData/guiTests/SimpleApplicationWithUnitTests/app/build.gradle
+++ b/android/testData/guiTests/SimpleApplicationWithUnitTests/app/build.gradle
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 apply plugin: 'com.android.application'
 
 android {
diff --git a/android/testData/guiTests/SimpleApplicationWithUnitTests/app/src/main/res/menu/my.xml b/android/testData/guiTests/SimpleApplicationWithUnitTests/app/src/main/res/menu/my.xml
index bea58cc..9a98b1d 100644
--- a/android/testData/guiTests/SimpleApplicationWithUnitTests/app/src/main/res/menu/my.xml
+++ b/android/testData/guiTests/SimpleApplicationWithUnitTests/app/src/main/res/menu/my.xml
@@ -1,3 +1,18 @@
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     tools:context=".MyActivity" >
diff --git a/android/testData/guiTests/SimpleApplicationWithUnitTests/settings.gradle b/android/testData/guiTests/SimpleApplicationWithUnitTests/settings.gradle
index e7b4def..7a4f23d 100644
--- a/android/testData/guiTests/SimpleApplicationWithUnitTests/settings.gradle
+++ b/android/testData/guiTests/SimpleApplicationWithUnitTests/settings.gradle
@@ -1 +1,16 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 include ':app'