blob: 745b3f893322c37ee5eda0dbc92f8369c0c84738 [file] [log] [blame]
/*
* Copyright (C) 2018 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 com.android.tools.idea.gradle.actions;
import com.android.ide.common.gradle.model.IdeAndroidArtifact;
import com.android.ide.common.gradle.model.IdeAndroidProject;
import com.android.ide.common.gradle.model.IdeVariant;
import com.android.tools.idea.gradle.plugin.AndroidPluginVersionUpdater;
import com.android.tools.idea.gradle.project.GradleProjectInfo;
import com.android.tools.idea.gradle.project.ProjectStructure;
import com.android.tools.idea.gradle.project.build.invoker.GradleBuildInvoker;
import com.android.tools.idea.gradle.project.facet.gradle.GradleFacet;
import com.android.tools.idea.gradle.project.model.AndroidModelFeatures;
import com.android.tools.idea.gradle.project.model.AndroidModuleModel;
import com.android.tools.idea.gradle.project.model.GradleModuleModel;
import com.android.tools.idea.gradle.run.OutputBuildAction;
import com.android.tools.idea.gradle.stubs.gradle.GradleProjectStub;
import com.android.tools.idea.testing.Facets;
import com.android.tools.idea.testing.IdeComponents;
import com.android.tools.idea.testing.TestMessagesDialog;
import com.google.common.collect.ImmutableList;
import com.intellij.ide.IdeEventQueue;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TestDialog;
import com.intellij.testFramework.IdeaTestCase;
import org.gradle.tooling.model.GradleProject;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.annotations.NotNull;
import org.mockito.Mock;
import static com.android.SdkConstants.GRADLE_PATH_SEPARATOR;
import static com.android.tools.idea.Projects.getBaseDirPath;
import static com.android.tools.idea.testing.Facets.createAndAddGradleFacet;
import static com.google.common.truth.Truth.assertThat;
import static java.util.Collections.emptyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
/**
* Tests for {@link BuildApkAction}.
*/
public class BuildBundleActionTest extends IdeaTestCase {
@Mock private GradleProjectInfo myGradleProjectInfo;
@Mock private GradleBuildInvoker myBuildInvoker;
@Mock private ProjectStructure myProjectStructure;
@Mock private AndroidModuleModel myAndroidModel;
@Mock private IdeAndroidProject myIdeAndroidProject;
@Mock private IdeVariant myIdeVariant;
@Mock private IdeAndroidArtifact myMainArtifact;
@Mock private AndroidPluginVersionUpdater myAndroidPluginVersionUpdater;
private BuildBundleAction myAction;
private TestDialog myDefaultTestDialog;
@Override
protected void setUp() throws Exception {
super.setUp();
initMocks(this);
new IdeComponents(myProject).replaceProjectService(GradleBuildInvoker.class, myBuildInvoker);
new IdeComponents(myProject).replaceProjectService(GradleProjectInfo.class, myGradleProjectInfo);
new IdeComponents(myProject).replaceProjectService(ProjectStructure.class, myProjectStructure);
new IdeComponents(myProject).replaceProjectService(AndroidPluginVersionUpdater.class, myAndroidPluginVersionUpdater);
myAction = new BuildBundleAction();
}
@Override
protected void tearDown() throws Exception {
try {
if (myDefaultTestDialog != null) {
Messages.setTestDialog(myDefaultTestDialog);
}
} finally {
super.tearDown();
}
}
public void testActionPerformed() {
Module appModule = createModule("app1");
setUpModuleAsAndroidModule(appModule, myAndroidModel, myIdeAndroidProject, myIdeVariant, myMainArtifact);
// Ignore return value, as we just want to make sure the "bundle" action does not apply to all modules
createModule("app2");
Module[] appModules = {appModule};
when(myProjectStructure.getAppModules()).thenReturn(ImmutableList.copyOf(appModules));
when(myGradleProjectInfo.isBuildWithGradle()).thenReturn(true);
AnActionEvent event = mock(AnActionEvent.class);
when(event.getProject()).thenReturn(getProject());
myAction.actionPerformed(event);
verify(myBuildInvoker).bundle(eq(appModules), eq(emptyList()), any(OutputBuildAction.class));
}
public void testUpdateGradlePluginNotification() {
Module appModule = createModule("app1");
setUpModuleAsAndroidModule(appModule, myAndroidModel, myIdeAndroidProject, myIdeVariant, myMainArtifact);
when(myMainArtifact.getBundleTaskName()).thenReturn(null);
// Ignore return value, as we just want to make sure the "bundle" action does not apply to all modules
createModule("app2");
Module[] appModules = {appModule};
when(myProjectStructure.getAppModules()).thenReturn(ImmutableList.copyOf(appModules));
when(myGradleProjectInfo.isBuildWithGradle()).thenReturn(true);
AnActionEvent event = mock(AnActionEvent.class);
when(event.getProject()).thenReturn(getProject());
@SuppressWarnings("MagicConstant") // Using custom button IDs
TestMessagesDialog testDialog = new TestMessagesDialog(1 /* Update*/);
myDefaultTestDialog = Messages.setTestDialog(testDialog);
myAction.actionPerformed(event);
assertThat(testDialog.getDisplayedMessage()).isEqualTo(getHtmlUpdateMessage());
// flush event queue to ensure the update call is processed.
IdeEventQueue.getInstance().flushQueue();
verify(myAndroidPluginVersionUpdater).updatePluginVersion(any(), any());
}
public void testUpdateGradlePluginCanceledNotification() throws InterruptedException {
Module appModule = createModule("app1");
setUpModuleAsAndroidModule(appModule, myAndroidModel, myIdeAndroidProject, myIdeVariant, myMainArtifact);
when(myMainArtifact.getBundleTaskName()).thenReturn(null);
// Ignore return value, as we just want to make sure the "bundle" action does not apply to all modules
createModule("app2");
Module[] appModules = {appModule};
when(myProjectStructure.getAppModules()).thenReturn(ImmutableList.copyOf(appModules));
when(myGradleProjectInfo.isBuildWithGradle()).thenReturn(true);
AnActionEvent event = mock(AnActionEvent.class);
when(event.getProject()).thenReturn(getProject());
@SuppressWarnings("MagicConstant") // Using custom button IDs
TestMessagesDialog testDialog = new TestMessagesDialog(0 /* Cancel*/);
myDefaultTestDialog = Messages.setTestDialog(testDialog);
myAction.actionPerformed(event);
assertThat(testDialog.getDisplayedMessage()).isEqualTo(getHtmlUpdateMessage());
verify(myAndroidPluginVersionUpdater, never()).updatePluginVersion(any(), any());
}
private static String getHtmlUpdateMessage() {
return "<html><body>Building Android App Bundles requires you to update to the latest version of the Android Gradle Plugin.<BR/>" +
"<A HREF=\"https://d.android.com/r/studio-ui/dynamic-delivery/overview.html\">Learn More</A><BR/><BR/>" +
"App bundles allow you to support multiple device configurations from a single build artifact.<BR/>" +
"App stores that support the bundle format use it to build and sign your APKs for you, and<BR/>" +
"serve those APKs to users as needed.<BR/><BR/></body></html>";
}
private static void setUpModuleAsAndroidModule(@NotNull Module module,
@NotNull AndroidModuleModel androidModel,
@NotNull IdeAndroidProject ideAndroidProject,
@NotNull IdeVariant ideVariant,
@NotNull IdeAndroidArtifact mainArtifact) {
setUpModuleAsGradleModule(module);
when(androidModel.getAndroidProject()).thenReturn(ideAndroidProject);
when(androidModel.getSelectedVariant()).thenReturn(ideVariant);
when(ideVariant.getMainArtifact()).thenReturn(mainArtifact);
when(mainArtifact.getBundleTaskName()).thenReturn("bundleDebug");
AndroidModelFeatures androidModelFeatures = mock(AndroidModelFeatures.class);
when(androidModel.getFeatures()).thenReturn(androidModelFeatures);
AndroidFacet androidFacet = Facets.createAndAddAndroidFacet(module);
androidFacet.getConfiguration().setModel(androidModel);
}
private static void setUpModuleAsGradleModule(@NotNull Module module) {
GradleFacet gradleFacet = createAndAddGradleFacet(module);
String gradlePath = GRADLE_PATH_SEPARATOR + module.getName();
gradleFacet.getConfiguration().GRADLE_PROJECT_PATH = gradlePath;
GradleProject gradleProjectStub = new GradleProjectStub(emptyList(), gradlePath, getBaseDirPath(module.getProject()));
GradleModuleModel model = new GradleModuleModel(module.getName(), gradleProjectStub, emptyList(), null, null, null);
gradleFacet.setGradleModuleModel(model);
}
}