| /* |
| * Copyright (C) 2014 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.tests.gui.gradle; |
| |
| import com.android.tools.idea.gradle.facet.JavaGradleFacet; |
| import com.android.tools.idea.gradle.parser.BuildFileKey; |
| import com.android.tools.idea.gradle.parser.BuildFileStatement; |
| import com.android.tools.idea.gradle.parser.Dependency; |
| import com.android.tools.idea.gradle.parser.GradleBuildFile; |
| import com.android.tools.idea.gradle.project.GradleExperimentalSettings; |
| import com.android.tools.idea.gradle.projectView.AndroidTreeStructureProvider; |
| import com.android.tools.idea.gradle.util.GradleProperties; |
| import com.android.tools.idea.gradle.util.LocalProperties; |
| import com.android.tools.idea.sdk.IdeSdks; |
| import com.android.tools.idea.sdk.Jdks; |
| import com.android.tools.idea.tests.gui.framework.BelongsToTestGroups; |
| import com.android.tools.idea.tests.gui.framework.GuiTestCase; |
| import com.android.tools.idea.tests.gui.framework.IdeGuiTest; |
| import com.android.tools.idea.tests.gui.framework.IdeGuiTestSetup; |
| import com.android.tools.idea.tests.gui.framework.fixture.*; |
| import com.android.tools.idea.tests.gui.framework.fixture.MessagesToolWindowFixture.AbstractContentFixture; |
| import com.android.tools.idea.tests.gui.framework.fixture.MessagesToolWindowFixture.HyperlinkFixture; |
| import com.android.tools.idea.tests.gui.framework.fixture.MessagesToolWindowFixture.MessageFixture; |
| import com.google.common.base.Charsets; |
| import com.google.common.collect.Lists; |
| import com.google.common.io.Files; |
| import com.intellij.ide.projectView.TreeStructureProvider; |
| import com.intellij.ide.util.treeView.AbstractTreeNode; |
| import com.intellij.lang.annotation.HighlightSeverity; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.Result; |
| import com.intellij.openapi.command.WriteCommandAction; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.externalSystem.model.DataNode; |
| import com.intellij.openapi.externalSystem.model.project.ModuleData; |
| import com.intellij.openapi.externalSystem.model.project.ProjectData; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ex.ProjectManagerEx; |
| import com.intellij.openapi.projectRoots.Sdk; |
| import com.intellij.openapi.roots.*; |
| import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; |
| import com.intellij.openapi.roots.libraries.Library; |
| import com.intellij.openapi.roots.libraries.LibraryTable; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.vfs.encoding.EncodingProjectManager; |
| import com.intellij.pom.java.LanguageLevel; |
| import com.intellij.util.SystemProperties; |
| import com.intellij.util.net.HttpConfigurable; |
| import org.fest.reflect.reference.TypeRef; |
| import org.fest.swing.core.GenericTypeMatcher; |
| import org.fest.swing.edt.GuiQuery; |
| import org.fest.swing.edt.GuiTask; |
| import org.fest.swing.fixture.DialogFixture; |
| import org.fest.swing.fixture.JButtonFixture; |
| import org.fest.swing.timing.Condition; |
| import org.jetbrains.android.AndroidPlugin.GuiTestSuiteState; |
| import org.jetbrains.android.facet.AndroidFacet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.gradle.settings.GradleProjectSettings; |
| import org.jetbrains.plugins.gradle.settings.GradleSettings; |
| import org.junit.Before; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| |
| import java.awt.*; |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.UUID; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import static com.android.SdkConstants.*; |
| import static com.android.tools.idea.gradle.customizer.AbstractDependenciesModuleCustomizer.pathToUrl; |
| import static com.android.tools.idea.gradle.parser.BuildFileKey.PLUGIN_VERSION; |
| import static com.android.tools.idea.gradle.util.FilePaths.findParentContentEntry; |
| import static com.android.tools.idea.gradle.util.GradleUtil.*; |
| import static com.android.tools.idea.gradle.util.PropertiesUtil.getProperties; |
| import static com.android.tools.idea.gradle.util.PropertiesUtil.savePropertiesToFile; |
| import static com.android.tools.idea.tests.gui.framework.GuiTests.*; |
| import static com.android.tools.idea.tests.gui.framework.TestGroup.PROJECT_SUPPORT; |
| import static com.android.tools.idea.tests.gui.framework.fixture.FileChooserDialogFixture.findImportProjectDialog; |
| import static com.android.tools.idea.tests.gui.framework.fixture.FileFixture.getDocument; |
| import static com.android.tools.idea.tests.gui.framework.fixture.MessagesToolWindowFixture.MessageMatcher.firstLineStartingWith; |
| import static com.google.common.io.Files.write; |
| import static com.intellij.ide.errorTreeView.ErrorTreeElementKind.*; |
| import static com.intellij.openapi.util.io.FileUtil.*; |
| import static com.intellij.openapi.util.io.FileUtilRt.createIfNotExists; |
| import static com.intellij.openapi.util.text.StringUtil.isNotEmpty; |
| import static com.intellij.openapi.vfs.VfsUtil.findFileByIoFile; |
| import static com.intellij.openapi.vfs.VfsUtilCore.isAncestor; |
| import static com.intellij.openapi.vfs.VfsUtilCore.urlToPath; |
| import static com.intellij.pom.java.LanguageLevel.*; |
| import static com.intellij.util.SystemProperties.getLineSeparator; |
| import static junit.framework.Assert.*; |
| import static org.fest.assertions.Assertions.assertThat; |
| import static org.fest.reflect.core.Reflection.field; |
| import static org.fest.swing.core.matcher.JButtonMatcher.withText; |
| import static org.fest.swing.edt.GuiActionRunner.execute; |
| import static org.fest.swing.finder.WindowFinder.findDialog; |
| import static org.fest.swing.timing.Pause.pause; |
| import static org.jetbrains.android.AndroidPlugin.GRADLE_SYNC_COMMAND_LINE_OPTIONS_KEY; |
| import static org.jetbrains.android.AndroidPlugin.getGuiTestSuiteState; |
| import static org.jetbrains.plugins.gradle.settings.DistributionType.DEFAULT_WRAPPED; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| |
| @BelongsToTestGroups({PROJECT_SUPPORT}) |
| @IdeGuiTestSetup(skipSourceGenerationOnSync = true) |
| public class GradleSyncTest extends GuiTestCase { |
| private static final String ANDROID_SDK_MANAGER_DIALOG_TITLE = "Android SDK Manager"; |
| private static final String GRADLE_SETTINGS_DIALOG_TITLE = "Gradle Settings"; |
| private static final String GRADLE_SYNC_DIALOG_TITLE = "Gradle Sync"; |
| |
| private File myAndroidRepoPath; |
| private File myAndroidRepoTempPath; |
| |
| @Before |
| public void restoreAndroidRepository() throws IOException { |
| File androidExtrasPath = new File(IdeSdks.getAndroidSdkPath(), join("extras", "android")); |
| myAndroidRepoPath = new File(androidExtrasPath ,"m2repository"); |
| myAndroidRepoTempPath = new File(androidExtrasPath, "m2repository.temp"); |
| |
| if (!myAndroidRepoPath.isDirectory() && myAndroidRepoTempPath.isDirectory()) { |
| rename(myAndroidRepoTempPath, myAndroidRepoPath); |
| } |
| } |
| |
| @Test @IdeGuiTest |
| public void testMissingInterModuleDependencies() throws IOException { |
| GradleExperimentalSettings.getInstance().SELECT_MODULES_ON_PROJECT_IMPORT = true; |
| File projectPath = importProject("ModuleDependencies"); |
| |
| ConfigureProjectSubsetDialogFixture projectSubsetDialog = ConfigureProjectSubsetDialogFixture.find(myRobot); |
| projectSubsetDialog.selectModule("javalib1", false) |
| .clickOk(); |
| |
| IdeFrameFixture projectFrame = findIdeFrame(projectPath); |
| projectFrame.waitForGradleProjectSyncToFinish(); |
| |
| AbstractContentFixture messages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| String expectedError = "Unable to find module with Gradle path ':javalib1' (needed by modules: 'androidlib1', 'app'.)"; |
| messages.findMessageContainingText(ERROR, expectedError); |
| |
| // Click "quick fix" to find and include any missing modules. |
| MessageFixture quickFixMsg = messages.findMessageContainingText(INFO, "The missing modules may have been excluded"); |
| HyperlinkFixture quickFix = quickFixMsg.findHyperlink("Find and include missing modules"); |
| quickFix.click(true); |
| |
| projectFrame.waitForBackgroundTasksToFinish(); |
| projectFrame.getModule("javalib1"); // Fails if the module is not found. |
| } |
| |
| @Test @IdeGuiTest |
| public void testNonExistingInterModuleDependencies() throws IOException { |
| final IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("ModuleDependencies"); |
| |
| Module appModule = projectFrame.getModule("app"); |
| final GradleBuildFile buildFile = GradleBuildFile.get(appModule); |
| assertNotNull(buildFile); |
| |
| // Set a dependency on a module that does not exist. |
| execute(new GuiTask() { |
| @Override |
| protected void executeInEDT() throws Throwable { |
| new WriteCommandAction<Void>(projectFrame.getProject(), "Adding dependencies", buildFile.getPsiFile()) { |
| @Override |
| protected void run(@NotNull Result<Void> result) throws Throwable { |
| final Dependency nonExisting = new Dependency(Dependency.Scope.COMPILE, Dependency.Type.MODULE, ":fakeLibrary"); |
| List<BuildFileStatement> dependencies = Lists.newArrayList(); |
| dependencies.add(nonExisting); |
| buildFile.setValue(BuildFileKey.DEPENDENCIES, dependencies); |
| } |
| }.execute(); |
| } |
| }); |
| |
| projectFrame.requestProjectSyncAndExpectFailure(); |
| |
| AbstractContentFixture messages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| String expectedError = "Project with path ':fakeLibrary' could not be found"; |
| MessageFixture msg = messages.findMessageContainingText(ERROR, expectedError); |
| msg.findHyperlink("Open File"); // Now it is possible to open the build.gradle where the missing dependency is declared. |
| } |
| |
| @Test @IdeGuiTest |
| public void testUserDefinedLibrarySources() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| Project project = projectFrame.getProject(); |
| |
| String libraryName = "guava-18.0"; |
| |
| LibraryTable libraryTable = ProjectLibraryTable.getInstance(project); |
| Library library = libraryTable.getLibraryByName(libraryName); |
| assertNotNull(library); |
| |
| String url = "jar://$USER_HOME$/fake-dir/fake-sources.jar!/"; |
| |
| // add an extra source path. |
| final Library.ModifiableModel libraryModel = library.getModifiableModel(); |
| libraryModel.addRoot(url, OrderRootType.SOURCES); |
| |
| execute(new GuiTask() { |
| @Override |
| protected void executeInEDT() throws Throwable { |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| libraryModel.commit(); |
| } |
| }); |
| } |
| }); |
| |
| projectFrame.requestProjectSync().waitForBackgroundTasksToFinish(); |
| |
| libraryTable = ProjectLibraryTable.getInstance(project); |
| library = libraryTable.getLibraryByName(libraryName); |
| assertNotNull(library); |
| |
| String[] urls = library.getUrls(OrderRootType.SOURCES); |
| assertThat(urls).contains(url); |
| } |
| |
| @Test @IdeGuiTest |
| public void testSyncMissingAppCompat() throws IOException { |
| if (myAndroidRepoPath.isDirectory()) { |
| // Instead of deleting the Android repo folder, we rename it and later on restore it in a @SetUp method, so if this fails, the SDK |
| // will be in good state. |
| delete(myAndroidRepoTempPath); |
| rename(myAndroidRepoPath, myAndroidRepoTempPath); |
| } |
| assertThat(myAndroidRepoPath).doesNotExist(); |
| |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| projectFrame.requestProjectSync().waitForGradleProjectSyncToFinish(); |
| |
| MessageFixture message = |
| projectFrame.getMessagesToolWindow().getGradleSyncContent().findMessage(ERROR, firstLineStartingWith("Failed to resolve:")); |
| |
| HyperlinkFixture hyperlink = message.findHyperlink("Install Repository and sync project"); |
| hyperlink.click(false); |
| |
| // TODO implement a proper "SDK Quick Fix wizard" fixture that wraps a SdkQuickfixWizard |
| DialogFixture quickFixDialog = findDialog(new GenericTypeMatcher<Dialog>(Dialog.class) { |
| @Override |
| protected boolean isMatching(@NotNull Dialog dialog) { |
| return "Install Missing Components".equals(dialog.getTitle()); |
| } |
| }).withTimeout(SHORT_TIMEOUT.duration()).using(myRobot); |
| |
| final JButtonFixture finish = quickFixDialog.button(withText("Finish")); |
| |
| // Wait until installation is finished. By then the "Finish" button will be enabled. |
| pause(new Condition("Android Support Repository is installed") { |
| @Override |
| public boolean test() { |
| //noinspection ConstantConditions |
| return execute(new GuiQuery<Boolean>() { |
| @Override |
| protected Boolean executeInEDT() { |
| return finish.target().isEnabled(); |
| } |
| }); |
| } |
| }, LONG_TIMEOUT); |
| |
| // Installation finished. Click finish to resync project. |
| finish.click(); |
| |
| projectFrame.waitForGradleProjectSyncToFinish().waitForBackgroundTasksToFinish(); |
| |
| assertThat(myAndroidRepoPath).as("Android Support Repository must have been reinstalled").isDirectory(); |
| } |
| |
| @Test @IdeGuiTest |
| public void testSyncDoesNotChangeDependenciesInBuildFiles() throws IOException { |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("MultiModule"); |
| File appBuildFilePath = new File(projectFrame.getProjectPath(), join("app", FN_BUILD_GRADLE)); |
| assertThat(appBuildFilePath).isFile(); |
| long lastModified = appBuildFilePath.lastModified(); |
| |
| projectFrame.requestProjectSync().waitForGradleProjectSyncToFinish(); |
| // See https://code.google.com/p/android/issues/detail?id=78628 |
| assertEquals(lastModified, appBuildFilePath.lastModified()); |
| } |
| |
| @Test @IdeGuiTest |
| public void testJdkNodeModificationInProjectView() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| AndroidTreeStructureProvider treeStructureProvider = null; |
| TreeStructureProvider[] treeStructureProviders = Extensions.getExtensions(TreeStructureProvider.EP_NAME, projectFrame.getProject()); |
| for (TreeStructureProvider current : treeStructureProviders) { |
| if (current instanceof AndroidTreeStructureProvider) { |
| treeStructureProvider = (AndroidTreeStructureProvider)current; |
| } |
| } |
| |
| assertNotNull(treeStructureProvider); |
| final List<AbstractTreeNode> changedNodes = Lists.newArrayList(); |
| treeStructureProvider.addChangeListener(new AndroidTreeStructureProvider.ChangeListener() { |
| @Override |
| public void nodeChanged(@NotNull AbstractTreeNode parent, @NotNull Collection<AbstractTreeNode> newChildren) { |
| changedNodes.add(parent); |
| } |
| }); |
| |
| ProjectViewFixture projectView = projectFrame.getProjectView(); |
| ProjectViewFixture.PaneFixture projectPane = projectView.selectProjectPane(); |
| ProjectViewFixture.NodeFixture externalLibrariesNode = projectPane.findExternalLibrariesNode(); |
| projectPane.expand(); |
| |
| pause(new Condition("Wait for 'Project View' to be customized") { |
| @Override |
| public boolean test() { |
| // 2 nodes should be changed: JDK (remove all children except rt.jar) and rt.jar (remove all children except packages 'java' and |
| // 'javax'. |
| return changedNodes.size() == 2; |
| } |
| }, LONG_TIMEOUT); |
| |
| List<ProjectViewFixture.NodeFixture> libraryNodes = externalLibrariesNode.getChildren(); |
| |
| ProjectViewFixture.NodeFixture jdkNode = null; |
| // Find JDK node. |
| for (ProjectViewFixture.NodeFixture node : libraryNodes) { |
| if (node.isJdk()) { |
| jdkNode = node; |
| break; |
| } |
| } |
| assertNotNull(jdkNode); |
| |
| // Now we verify that the JDK node has only these children: |
| // - jdk |
| // - rt.jar |
| // - java |
| // - javax |
| List<ProjectViewFixture.NodeFixture> jdkChildren = jdkNode.getChildren(); |
| assertThat(jdkChildren).hasSize(1); |
| |
| ProjectViewFixture.NodeFixture rtJarNode = jdkChildren.get(0); |
| rtJarNode.requireDirectory("rt.jar"); |
| |
| List<ProjectViewFixture.NodeFixture> rtJarChildren = rtJarNode.getChildren(); |
| assertThat(rtJarChildren).hasSize(2); |
| |
| rtJarChildren.get(0).requireDirectory("java"); |
| rtJarChildren.get(1).requireDirectory("javax"); |
| } |
| |
| @Test @IdeGuiTest @Ignore // Removed minimum plugin version check. It is failing in some projects. |
| public void testUnsupportedPluginVersion() throws IOException { |
| // Open the project without updating the version of the plug-in |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| final Project project = projectFrame.getProject(); |
| |
| // Use old, unsupported plugin version. |
| File buildFilePath = new File(project.getBasePath(), FN_BUILD_GRADLE); |
| final VirtualFile buildFile = findFileByIoFile(buildFilePath, true); |
| assertNotNull(buildFile); |
| WriteCommandAction.runWriteCommandAction(project, new Runnable() { |
| @Override |
| public void run() { |
| new GradleBuildFile(buildFile, project).setValue(PLUGIN_VERSION, "0.12.+"); |
| } |
| }); |
| |
| // Use old, unsupported Gradle in the wrapper. |
| File wrapperPropertiesFile = findWrapperPropertiesFile(project); |
| assertNotNull(wrapperPropertiesFile); |
| updateGradleDistributionUrl("1.12", wrapperPropertiesFile); |
| |
| GradleProjectSettings settings = getGradleProjectSettings(project); |
| assertNotNull(settings); |
| settings.setDistributionType(DEFAULT_WRAPPED); |
| |
| projectFrame.requestProjectSyncAndExpectFailure(); |
| |
| AbstractContentFixture syncMessages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| String errorPrefix = "The minimum supported version of the Android Gradle plugin"; |
| MessageFixture message = syncMessages.findMessage(ERROR, firstLineStartingWith(errorPrefix)); |
| |
| MessagesToolWindowFixture.HyperlinkFixture hyperlink = message.findHyperlink("Fix plugin version"); |
| hyperlink.click(true); |
| |
| projectFrame.waitForGradleProjectSyncToFinish(); |
| } |
| |
| // See https://code.google.com/p/android/issues/detail?id=75060 |
| @Test @IdeGuiTest @Ignore // Works only when executed individually |
| public void testHandlingOfOutOfMemoryErrors() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| // Force a sync failure by allocating not enough memory for the Gradle daemon. |
| Properties gradleProperties = new Properties(); |
| gradleProperties.setProperty("org.gradle.jvmargs", "-XX:MaxHeapSize=8m"); |
| File gradlePropertiesFilePath = new File(projectFrame.getProjectPath(), FN_GRADLE_PROPERTIES); |
| savePropertiesToFile(gradleProperties, gradlePropertiesFilePath, null); |
| |
| projectFrame.requestProjectSyncAndExpectFailure(); |
| |
| MessagesToolWindowFixture messages = projectFrame.getMessagesToolWindow(); |
| MessageFixture message = messages.getGradleSyncContent().findMessage(ERROR, firstLineStartingWith("Out of memory")); |
| |
| // Verify that at least we offer some sort of hint. |
| MessagesToolWindowFixture.HyperlinkFixture hyperlink = message.findHyperlink("Read Gradle's configuration guide"); |
| hyperlink.requireUrl("http://www.gradle.org/docs/current/userguide/build_environment.html"); |
| } |
| |
| // See https://code.google.com/p/android/issues/detail?id=73872 |
| @Test @IdeGuiTest |
| public void testHandlingOfClassLoadingErrors() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| projectFrame.requestProjectSyncAndSimulateFailure("Unable to load class 'com.android.utils.ILogger'"); |
| |
| MessagesToolWindowFixture messages = projectFrame.getMessagesToolWindow(); |
| MessageFixture message = messages.getGradleSyncContent().findMessage(ERROR, firstLineStartingWith("Unable to load class")); |
| |
| message.findHyperlink("Re-download dependencies and sync project (requires network)"); |
| message.findHyperlink("Open Gradle Daemon documentation"); |
| } |
| |
| @Test @IdeGuiTest |
| // See https://code.google.com/p/android/issues/detail?id=72556 |
| public void testHandlingOfUnexpectedEndOfBlockData() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| projectFrame.requestProjectSyncAndSimulateFailure("unexpected end of block data"); |
| |
| MessagesToolWindowFixture messages = projectFrame.getMessagesToolWindow(); |
| MessageFixture message = messages.getGradleSyncContent().findMessage(ERROR, firstLineStartingWith("An unexpected I/O error occurred.")); |
| |
| message.findHyperlink("Build Project"); |
| message.findHyperlink("Open Android SDK Manager"); |
| } |
| |
| @Test @IdeGuiTest |
| // See https://code.google.com/p/android/issues/detail?id=66880 |
| public void testAutomaticCreationOfMissingWrapper() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| projectFrame.deleteGradleWrapper() |
| .requestProjectSync() |
| .waitForGradleProjectSyncToFinish() |
| .requireGradleWrapperSet(); |
| } |
| |
| @Test @IdeGuiTest |
| // See https://code.google.com/p/android/issues/detail?id=72294 |
| public void testSyncWithEmptyGradleSettingsFileInMultiModuleProject() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| createEmptyGradleSettingsFile(projectFrame.getProjectPath()); |
| |
| // Sync should be successful for multi-module projects with an empty settings.gradle file. |
| projectFrame.requestProjectSync() |
| .waitForGradleProjectSyncToFinish(); |
| } |
| |
| @Test @IdeGuiTest |
| // See https://code.google.com/p/android/issues/detail?id=76444 |
| public void testSyncWithEmptyGradleSettingsFileInSingleModuleProject() throws IOException { |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("Basic"); |
| |
| createEmptyGradleSettingsFile(projectFrame.getProjectPath()); |
| |
| // Sync should be successful for single-module projects with an empty settings.gradle file. |
| projectFrame.requestProjectSync() |
| .waitForGradleProjectSyncToFinish(); |
| } |
| |
| private static void createEmptyGradleSettingsFile(@NotNull File projectPath) throws IOException { |
| File settingsFilePath = new File(projectPath, FN_SETTINGS_GRADLE); |
| delete(settingsFilePath); |
| writeToFile(settingsFilePath, " "); |
| assertThat(settingsFilePath).isFile(); |
| } |
| |
| @Test @IdeGuiTest |
| public void testGradleDslMethodNotFoundInBuildFile() throws IOException { |
| final IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| File topLevelBuildFile = new File(projectFrame.getProjectPath(), FN_BUILD_GRADLE); |
| assertThat(topLevelBuildFile).isFile(); |
| String content = "asdf()" + getLineSeparator() + loadFile(topLevelBuildFile); |
| writeToFile(topLevelBuildFile, content); |
| |
| projectFrame.requestProjectSyncAndExpectFailure(); |
| |
| AbstractContentFixture gradleSyncMessages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| MessageFixture message = gradleSyncMessages.findMessage(ERROR, firstLineStartingWith("Gradle DSL method not found: 'asdf()'")); |
| |
| final EditorFixture editor = projectFrame.getEditor(); |
| editor.close(); |
| |
| // Verify that at least we offer some sort of hint. |
| message.findHyperlink("Open Gradle wrapper file"); |
| } |
| |
| @Test @IdeGuiTest |
| public void testGradleDslMethodNotFoundInSettingsFile() throws IOException { |
| final IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| File settingsFile = new File(projectFrame.getProjectPath(), FN_SETTINGS_GRADLE); |
| assertThat(settingsFile).isFile(); |
| writeToFile(settingsFile, "incude ':app'"); |
| |
| projectFrame.requestProjectSyncAndExpectFailure(); |
| |
| AbstractContentFixture gradleSyncMessages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| MessageFixture message = gradleSyncMessages.findMessage(ERROR, firstLineStartingWith("Gradle DSL method not found: 'incude()'")); |
| |
| // Ensure the error message contains the location of the error. |
| message.requireLocation(settingsFile, 1); |
| } |
| |
| @Test @IdeGuiTest |
| // See https://code.google.com/p/android/issues/detail?id=76797 |
| public void testHandlingOfZipFileOpeningError() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| projectFrame.requestProjectSyncAndSimulateFailure("error in opening zip file"); |
| |
| MessagesToolWindowFixture messages = projectFrame.getMessagesToolWindow(); |
| MessageFixture message = messages.getGradleSyncContent().findMessage(ERROR, firstLineStartingWith("Failed to open zip file.")); |
| |
| message.findHyperlink("Re-download dependencies and sync project (requires network)"); |
| } |
| |
| @Test @IdeGuiTest |
| // See https://code.google.com/p/android/issues/detail?id=75520 |
| public void testConnectionPermissionDeniedError() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| String failure = "Connection to the Internet denied."; |
| projectFrame.requestProjectSyncAndSimulateFailure(failure); |
| |
| MessagesToolWindowFixture messages = projectFrame.getMessagesToolWindow(); |
| MessageFixture message = messages.getGradleSyncContent().findMessage(ERROR, firstLineStartingWith(failure)); |
| |
| HyperlinkFixture hyperlink = message.findHyperlink("More details (and potential fix)"); |
| hyperlink.requireUrl("http://tools.android.com/tech-docs/project-sync-issues-android-studio"); |
| } |
| |
| @Test @IdeGuiTest |
| // See https://code.google.com/p/android/issues/detail?id=76984 |
| public void testDaemonContextMismatchError() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| String failure = "The newly created daemon process has a different context than expected.\n" + |
| "It won't be possible to reconnect to this daemon. Context mismatch: \n" + |
| "Java home is different.\n" + |
| "javaHome=c:\\Program Files\\Java\\jdk,daemonRegistryDir=C:\\Users\\user.name\\.gradle\\daemon,pid=7868,idleTimeout=null]\n" + |
| "javaHome=C:\\Program Files\\Java\\jdk\\jre,daemonRegistryDir=C:\\Users\\user.name\\.gradle\\daemon,pid=4792,idleTimeout=10800000]"; |
| projectFrame.requestProjectSyncAndSimulateFailure(failure); |
| |
| MessagesToolWindowFixture messages = projectFrame.getMessagesToolWindow(); |
| MessageFixture message = messages.getGradleSyncContent().findMessage(ERROR, firstLineStartingWith("The newly created daemon")); |
| |
| message.findHyperlink("Open JDK Settings"); |
| } |
| |
| @Test @IdeGuiTest |
| public void testUpdateGradleVersionWithLocalDistribution() throws IOException { |
| File unsupportedGradleHome = getUnsupportedGradleHome(); |
| File gradleHomePath = getGradleHomePath(); |
| if (unsupportedGradleHome == null || gradleHomePath == null) { |
| skip("testUpdateGradleVersionWithLocalDistribution"); |
| return; |
| } |
| |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| projectFrame.deleteGradleWrapper() |
| .useLocalGradleDistribution(unsupportedGradleHome) |
| .requestProjectSync(); |
| |
| // Expect message suggesting to use Gradle wrapper. Click "Cancel" to use local distribution. |
| projectFrame.findMessageDialog(GRADLE_SYNC_DIALOG_TITLE).clickCancel(); |
| |
| ChooseGradleHomeDialogFixture chooseGradleHomeDialog = ChooseGradleHomeDialogFixture.find(myRobot); |
| chooseGradleHomeDialog.chooseGradleHome(gradleHomePath).clickOk() |
| .requireNotShowing(); |
| |
| projectFrame.waitForGradleProjectSyncToFinish(); |
| } |
| |
| @Test @IdeGuiTest |
| public void testShowUserFriendlyErrorWhenUsingUnsupportedVersionOfGradle() throws IOException { |
| File unsupportedGradleHome = getUnsupportedGradleHome(); |
| if (unsupportedGradleHome == null) { |
| skip("testShowUserFriendlyErrorWhenUsingUnsupportedVersionOfGradle"); |
| return; |
| } |
| |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| projectFrame.deleteGradleWrapper().useLocalGradleDistribution(unsupportedGradleHome).requestProjectSync(); |
| |
| // Expect message suggesting to use Gradle wrapper. Click "OK" to use wrapper. |
| projectFrame.findMessageDialog(GRADLE_SYNC_DIALOG_TITLE).clickOk(); |
| |
| projectFrame.waitForGradleProjectSyncToStart() |
| .waitForGradleProjectSyncToFinish().requireGradleWrapperSet(); |
| } |
| |
| @Test @IdeGuiTest |
| public void testCreateWrapperWhenLocalDistributionPathIsNotSet() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| projectFrame.deleteGradleWrapper() |
| .useLocalGradleDistribution("") |
| .requestProjectSync(); |
| |
| // Expect message suggesting to use Gradle wrapper. Click "OK" to use wrapper. |
| projectFrame.findMessageDialog(GRADLE_SYNC_DIALOG_TITLE).clickOk(); |
| |
| projectFrame.waitForGradleProjectSyncToStart() |
| .waitForGradleProjectSyncToFinish().requireGradleWrapperSet(); |
| } |
| |
| @Test @IdeGuiTest |
| public void testCreateWrapperWhenLocalDistributionPathDoesNotExist() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| File nonExistingDirPath = new File(SystemProperties.getUserHome(), UUID.randomUUID().toString()); |
| projectFrame.deleteGradleWrapper() |
| .useLocalGradleDistribution(nonExistingDirPath).requestProjectSync(); |
| |
| // Expect message suggesting to use Gradle wrapper. Click "OK" to use wrapper. |
| projectFrame.findMessageDialog(GRADLE_SYNC_DIALOG_TITLE).clickOk(); |
| |
| projectFrame.waitForGradleProjectSyncToStart() |
| .waitForGradleProjectSyncToFinish().requireGradleWrapperSet(); |
| } |
| |
| // See https://code.google.com/p/android/issues/detail?id=74842 |
| @Test @IdeGuiTest |
| public void testPrematureEndOfContentLength() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| // Simulate this Gradle error. |
| final String failure = "Premature end of Content-Length delimited message body (expected: 171012; received: 50250."; |
| projectFrame.requestProjectSyncAndSimulateFailure(failure); |
| |
| final String prefix = "Gradle's dependency cache seems to be corrupt or out of sync"; |
| MessagesToolWindowFixture messages = projectFrame.getMessagesToolWindow(); |
| |
| MessageFixture message = messages.getGradleSyncContent().findMessage(ERROR, firstLineStartingWith(prefix)); |
| HyperlinkFixture hyperlink = message.findHyperlink("Re-download dependencies and sync project (requires network)"); |
| hyperlink.click(true); |
| |
| projectFrame.waitForGradleProjectSyncToFinish(); |
| |
| // This is the only way we can at least know that we pass the right command-line option. |
| String[] commandLineOptions = ApplicationManager.getApplication().getUserData(GRADLE_SYNC_COMMAND_LINE_OPTIONS_KEY); |
| assertThat(commandLineOptions).contains("--refresh-dependencies"); |
| } |
| |
| // See https://code.google.com/p/android/issues/detail?id=74259 |
| @Test @IdeGuiTest |
| public void testImportProjectWithCentralBuildDirectoryInRootModule() throws IOException { |
| // In issue 74259, project sync fails because the "app" build directory is set to "CentralBuildDirectory/central/build", which is |
| // outside the content root of the "app" module. |
| String projectDirName = "CentralBuildDirectory"; |
| File projectPath = new File(getProjectCreationDirPath(), projectDirName); |
| |
| // The bug appears only when the central build folder does not exist. |
| final File centralBuildDirPath = new File(projectPath, join("central", "build")); |
| File centralBuildParentDirPath = centralBuildDirPath.getParentFile(); |
| delete(centralBuildParentDirPath); |
| |
| IdeFrameFixture ideFrame = importProjectAndWaitForProjectSyncToFinish(projectDirName); |
| final Module app = ideFrame.getModule("app"); |
| |
| // Now we have to make sure that if project import was successful, the build folder (with custom path) is excluded in the IDE (to |
| // prevent unnecessary file indexing, which decreases performance.) |
| final File[] excludeFolderPaths = execute(new GuiQuery<File[]>() { |
| @Override |
| protected File[] executeInEDT() throws Throwable { |
| ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(app); |
| ModifiableRootModel rootModel = moduleRootManager.getModifiableModel(); |
| try { |
| ContentEntry[] contentEntries = rootModel.getContentEntries(); |
| ContentEntry parent = findParentContentEntry(centralBuildDirPath, contentEntries); |
| assertNotNull(parent); |
| |
| List<File> paths = Lists.newArrayList(); |
| |
| for (ExcludeFolder excluded : parent.getExcludeFolders()) { |
| String path = urlToPath(excluded.getUrl()); |
| if (isNotEmpty(path)) { |
| paths.add(new File(toSystemDependentName(path))); |
| } |
| } |
| return paths.toArray(new File[paths.size()]); |
| } |
| finally { |
| rootModel.dispose(); |
| } |
| } |
| }); |
| |
| assertThat(excludeFolderPaths).isNotEmpty(); |
| |
| boolean isExcluded = false; |
| for (File path : notNullize(excludeFolderPaths)) { |
| if (isAncestor(centralBuildParentDirPath, path, true)) { |
| isExcluded = true; |
| break; |
| } |
| } |
| |
| assertTrue(String.format("Folder '%1$s' should be excluded", centralBuildDirPath.getPath()), isExcluded); |
| } |
| |
| @Test @IdeGuiTest |
| public void testSyncWithUnresolvedDependencies() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| testSyncWithUnresolvedAppCompat(projectFrame); |
| } |
| |
| @Test @IdeGuiTest |
| public void testSyncWithUnresolvedDependenciesWithAndroidGradlePluginOneDotZero() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| VirtualFile projectBuildFile = projectFrame.findFileByRelativePath("build.gradle", true); |
| Document document = getDocument(projectBuildFile); |
| assertNotNull(document); |
| |
| updateGradleDependencyVersion(projectFrame.getProject(), document, GRADLE_PLUGIN_NAME, new Computable<String>() { |
| @Override |
| public String compute() { |
| return "1.0.0"; |
| } |
| }); |
| |
| testSyncWithUnresolvedAppCompat(projectFrame); |
| } |
| |
| private static void testSyncWithUnresolvedAppCompat(@NotNull IdeFrameFixture projectFrame) { |
| VirtualFile appBuildFile = projectFrame.findFileByRelativePath("app/build.gradle", true); |
| Document document = getDocument(appBuildFile); |
| assertNotNull(document); |
| |
| updateGradleDependencyVersion(projectFrame.getProject(), document, "com.android.support:appcompat-v7:", new Computable<String>() { |
| @Override |
| public String compute() { |
| return "100.0.0"; |
| } |
| }); |
| |
| projectFrame.requestProjectSync().waitForGradleProjectSyncToFinish(); |
| |
| AbstractContentFixture syncMessages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| syncMessages.findMessage(ERROR, firstLineStartingWith("Failed to resolve: com.android.support:appcompat-v7:")); |
| } |
| |
| @Test @IdeGuiTest |
| public void testImportProjectWithoutWrapper() throws IOException { |
| GradleExperimentalSettings settings = GradleExperimentalSettings.getInstance(); |
| settings.SKIP_SOURCE_GEN_ON_PROJECT_SYNC = false; |
| settings.MAX_MODULE_COUNT_FOR_SOURCE_GEN = 5; |
| |
| File projectDirPath = copyProjectBeforeOpening("AarDependency"); |
| |
| IdeFrameFixture.deleteWrapper(projectDirPath); |
| |
| cleanUpProjectForImport(projectDirPath); |
| |
| // Import project |
| WelcomeFrameFixture welcomeFrame = findWelcomeFrame(); |
| welcomeFrame.clickImportProjectButton(); |
| FileChooserDialogFixture importProjectDialog = findImportProjectDialog(myRobot); |
| |
| VirtualFile toSelect = findFileByIoFile(projectDirPath, true); |
| assertNotNull(toSelect); |
| |
| importProjectDialog.select(toSelect).clickOk(); |
| |
| // Expect message suggesting to use Gradle wrapper. Click "OK" to use wrapper. |
| welcomeFrame.findMessageDialog(GRADLE_SYNC_DIALOG_TITLE).clickOk(); |
| |
| IdeFrameFixture projectFrame = findIdeFrame(projectDirPath); |
| projectFrame.waitForGradleProjectSyncToFinish() |
| .requireGradleWrapperSet(); |
| } |
| |
| // See https://code.google.com/p/android/issues/detail?id=74341 |
| @Test @IdeGuiTest |
| public void testEditorFindsAppCompatStyle() throws IOException { |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("AarDependency"); |
| |
| String stringsXmlPath = "app/src/main/res/values/strings.xml"; |
| projectFrame.getEditor().open(stringsXmlPath, EditorFixture.Tab.EDITOR); |
| |
| FileFixture file = projectFrame.findExistingFileByRelativePath(stringsXmlPath); |
| file.requireCodeAnalysisHighlightCount(HighlightSeverity.ERROR, 0); |
| } |
| |
| @Test @IdeGuiTest |
| public void testModuleSelectionOnImport() throws IOException { |
| GradleExperimentalSettings.getInstance().SELECT_MODULES_ON_PROJECT_IMPORT = true; |
| File projectPath = importProject("Flavoredlib"); |
| |
| ConfigureProjectSubsetDialogFixture projectSubsetDialog = ConfigureProjectSubsetDialogFixture.find(myRobot); |
| projectSubsetDialog.selectModule("lib", false) |
| .clickOk(); |
| |
| IdeFrameFixture projectFrame = findIdeFrame(projectPath); |
| projectFrame.waitForGradleProjectSyncToFinish(); |
| |
| // Verify that "lib" (which was unchecked in the "Select Modules to Include" dialog) is not a module. |
| assertThat(projectFrame.getModuleNames()).containsOnly("Flavoredlib", "app"); |
| |
| // subsequent project syncs should respect module selection |
| projectFrame.requestProjectSync().waitForGradleProjectSyncToFinish(); |
| assertThat(projectFrame.getModuleNames()).containsOnly("Flavoredlib", "app"); |
| } |
| |
| @Test @IdeGuiTest |
| public void testLocalJarsAsModules() throws IOException { |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("LocalJarsAsModules"); |
| Module localJarModule = projectFrame.getModule("localJarAsModule"); |
| |
| // Module should be a Java module, not buildable (since it doesn't have source code). |
| JavaGradleFacet javaFacet = JavaGradleFacet.getInstance(localJarModule); |
| assertNotNull(javaFacet); |
| assertFalse(javaFacet.getConfiguration().BUILDABLE); |
| |
| ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(localJarModule); |
| OrderEntry[] orderEntries = moduleRootManager.getOrderEntries(); |
| |
| // Verify that the module depends on the jar that it contains. |
| LibraryOrderEntry libraryDependency = null; |
| for (OrderEntry orderEntry : orderEntries) { |
| if (orderEntry instanceof LibraryOrderEntry) { |
| libraryDependency = (LibraryOrderEntry)orderEntry; |
| break; |
| } |
| } |
| assertNotNull(libraryDependency); |
| assertThat(libraryDependency.getLibraryName()).isEqualTo("localJarAsModule.local"); |
| assertTrue(libraryDependency.isExported()); |
| } |
| |
| @Test @IdeGuiTest |
| public void testLocalAarsAsModules() throws IOException { |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("LocalAarsAsModules"); |
| Module localAarModule = projectFrame.getModule("library-debug"); |
| |
| // When AAR files are exposed as artifacts, they don't have an AndroidProject model. |
| AndroidFacet androidFacet = AndroidFacet.getInstance(localAarModule); |
| assertNull(androidFacet); |
| assertNull(getAndroidProject(localAarModule)); |
| |
| ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(localAarModule); |
| LibraryOrderEntry libraryDependency = null; |
| for (OrderEntry orderEntry : moduleRootManager.getOrderEntries()) { |
| if (orderEntry instanceof LibraryOrderEntry) { |
| libraryDependency = (LibraryOrderEntry)orderEntry; |
| break; |
| } |
| } |
| assertNull(libraryDependency); // Should not expose the AAR as library, instead it should use the "exploded AAR". |
| |
| Module appModule = projectFrame.getModule("app"); |
| moduleRootManager = ModuleRootManager.getInstance(appModule); |
| // Verify that the module depends on the AAR that it contains (in "exploded-aar".) |
| libraryDependency = null; |
| for (OrderEntry orderEntry : moduleRootManager.getOrderEntries()) { |
| if (orderEntry instanceof LibraryOrderEntry) { |
| libraryDependency = (LibraryOrderEntry)orderEntry; |
| break; |
| } |
| } |
| |
| assertNotNull(libraryDependency); |
| assertThat(libraryDependency.getLibraryName()).isEqualTo("library-debug-unspecified"); |
| assertTrue(libraryDependency.isExported()); |
| } |
| |
| @Test @IdeGuiTest |
| public void testInterModuleDependencies() throws IOException { |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("MultiModule"); |
| |
| Module appModule = projectFrame.getModule("app"); |
| ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(appModule); |
| |
| // Verify that the module "app" depends on module "library" |
| ModuleOrderEntry moduleDependency = null; |
| for (OrderEntry orderEntry : moduleRootManager.getOrderEntries()) { |
| if (orderEntry instanceof ModuleOrderEntry) { |
| moduleDependency = (ModuleOrderEntry)orderEntry; |
| break; |
| } |
| } |
| |
| assertNotNull(moduleDependency); |
| assertThat(moduleDependency.getModuleName()).isEqualTo("library"); |
| } |
| |
| @Test @IdeGuiTest |
| public void testAndroidPluginAndGradleVersionCompatibility() throws IOException { |
| File gradleTwoDotFourHome = getGradleHomeFromSystemProperty("gradle.2.4.home", "2.4"); |
| if (gradleTwoDotFourHome == null) { |
| skip("testAndroidPluginAndGradleVersionCompatibility"); |
| return; |
| } |
| |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| // Set the plugin version to 1.0.0. This version is incompatible with Gradle 2.4. |
| // We expect the IDE to warn the user about this incompatibility. |
| updateAndroidModelVersion(projectFrame.getProjectPath(), "1.0.0"); |
| |
| projectFrame.useLocalGradleDistribution(gradleTwoDotFourHome) |
| .requestProjectSync() |
| .waitForGradleProjectSyncToFinish(); |
| |
| AbstractContentFixture syncMessages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| syncMessages.findMessage(ERROR, firstLineStartingWith("Android plugin version 1.0.0 is not compatible with Gradle version 2.4")); |
| } |
| |
| // See https://code.google.com/p/android/issues/detail?id=165576 |
| @Test @IdeGuiTest |
| public void testJavaModelSerialization() throws IOException { |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("MultipleModuleTypes"); |
| final File projectPath = projectFrame.getProjectPath(); |
| |
| projectFrame.requestProjectSync() |
| .waitForGradleProjectSyncToFinish(); |
| projectFrame.closeProject(); |
| |
| execute(new GuiTask() { |
| @Override |
| protected void executeInEDT() throws Throwable { |
| ProjectManagerEx projectManager = ProjectManagerEx.getInstanceEx(); |
| projectManager.loadAndOpenProject(projectPath.getPath()); |
| } |
| }); |
| |
| projectFrame = findIdeFrame(projectPath); |
| LibraryTable libraryTable = ProjectLibraryTable.getInstance(projectFrame.getProject()); |
| // When serialization of Java model fails, libraries are not set up. |
| // Here we confirm that serialization works, because the Java module has the dependency declared in its build.gradle file. |
| assertThat(libraryTable.getLibraries()).hasSize(1); |
| } |
| |
| // See https://code.google.com/p/android/issues/detail?id=167378 |
| @Test @IdeGuiTest |
| public void testInterJavaModuleDependencies() throws IOException { |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("MultiModule"); |
| projectFrame.requestProjectSync() |
| .waitForGradleProjectSyncToFinish(); |
| |
| Module library = projectFrame.getModule("library"); |
| ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(library); |
| |
| // Verify that the module "library" depends on module "library2" |
| ModuleOrderEntry moduleDependency = null; |
| for (OrderEntry orderEntry : moduleRootManager.getOrderEntries()) { |
| if (orderEntry instanceof ModuleOrderEntry) { |
| moduleDependency = (ModuleOrderEntry)orderEntry; |
| break; |
| } |
| } |
| |
| assertNotNull(moduleDependency); |
| assertThat(moduleDependency.getModuleName()).isEqualTo("library2"); |
| } |
| |
| // See https://code.google.com/p/android/issues/detail?id=73087 |
| @Test @IdeGuiTest |
| public void testUserDefinedLibraryAttachments() throws IOException { |
| File javadocJarPath = getFilePathProperty("guava.javadoc.jar.path", "the path of the Javadoc jar file for Guava", false); |
| if (javadocJarPath == null) { |
| skip("testUserDefinedLibraryAttachments"); |
| return; |
| } |
| |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("MultipleModuleTypes"); |
| LibraryPropertiesDialogFixture propertiesDialog = projectFrame.showPropertiesForLibrary("guava"); |
| propertiesDialog.addAttachment(javadocJarPath) |
| .clickOk(); |
| |
| String javadocJarUrl = pathToUrl(javadocJarPath.getPath()); |
| |
| // Verify that the library has the Javadoc attachment we just added. |
| LibraryFixture library = propertiesDialog.getLibrary(); |
| library.requireJavadocUrls(javadocJarUrl); |
| |
| projectFrame.requestProjectSync() |
| .waitForGradleProjectSyncToFinish(); |
| |
| // Verify that the library still has the Javadoc attachment after sync. |
| library = propertiesDialog.getLibrary(); |
| library.requireJavadocUrls(javadocJarUrl); |
| } |
| |
| // See https://code.google.com/p/android/issues/detail?id=169743 |
| // JVM settings for Gradle should be cleared before any invocation to Gradle. |
| @Test @IdeGuiTest |
| public void testClearJvmArgsOnSyncAndBuild() throws IOException { |
| final IdeFrameFixture projectFrame = importSimpleApplication(); |
| Project project = projectFrame.getProject(); |
| |
| GradleProperties gradleProperties = new GradleProperties(project); |
| gradleProperties.setJvmArgs(""); |
| |
| String jvmArgs = "-Xmx2048m"; |
| projectFrame.setGradleJvmArgs(jvmArgs) |
| .requestProjectSync(); |
| |
| // Copy JVM args to gradle.properties file. |
| projectFrame.findMessageDialog(GRADLE_SETTINGS_DIALOG_TITLE).clickYes(); |
| |
| // Verify JVM args were removed from IDE's Gradle settings. |
| projectFrame.waitForGradleProjectSyncToFinish(); |
| assertEquals("", GradleSettings.getInstance(project).getGradleVmOptions()); |
| |
| // Verify JVM args were copied to gradle.properties file |
| gradleProperties = new GradleProperties(project); |
| assertEquals(jvmArgs, gradleProperties.getJvmArgs()); |
| |
| projectFrame.setGradleJvmArgs(jvmArgs).invokeProjectMake(new Runnable() { |
| @Override |
| public void run() { |
| // Copy JVM args to gradle.properties file. |
| projectFrame.findMessageDialog(GRADLE_SETTINGS_DIALOG_TITLE).clickYes(); |
| } |
| }); |
| assertEquals("", GradleSettings.getInstance(project).getGradleVmOptions()); |
| } |
| |
| // Verifies that the IDE, during sync, asks the user to copy IDE proxy settings to gradle.properties, if applicable. |
| // See https://code.google.com/p/android/issues/detail?id=65325 |
| @Test @IdeGuiTest |
| public void testWithIdeProxySettings() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| File gradlePropertiesPath = new File(projectFrame.getProjectPath(), "gradle.properties"); |
| createIfNotExists(gradlePropertiesPath); |
| |
| String host = "myproxy.test.com"; |
| int port = 443; |
| |
| HttpConfigurable ideSettings = HttpConfigurable.getInstance(); |
| ideSettings.USE_HTTP_PROXY = true; |
| ideSettings.PROXY_HOST = host; |
| ideSettings.PROXY_PORT = port; |
| |
| projectFrame.requestProjectSync(); |
| |
| // Expect IDE to ask user to copy proxy settings. |
| MessagesFixture message = projectFrame.findMessageDialog("Proxy Settings"); |
| message.clickYes(); |
| |
| projectFrame.waitForGradleProjectSyncToStart().waitForGradleProjectSyncToFinish(); |
| |
| // Verify gradle.properties has proxy settings. |
| assertThat(gradlePropertiesPath).isFile(); |
| |
| Properties gradleProperties = getProperties(gradlePropertiesPath); |
| assertEquals(host, gradleProperties.getProperty("systemProp.http.proxyHost")); |
| assertEquals(String.valueOf(port), gradleProperties.getProperty("systemProp.http.proxyPort")); |
| } |
| |
| @Test @IdeGuiTest |
| public void testMismatchingEncodings() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| final Project project = projectFrame.getProject(); |
| |
| execute(new GuiTask() { |
| @Override |
| protected void executeInEDT() throws Throwable { |
| EncodingProjectManager encodings = EncodingProjectManager.getInstance(project); |
| encodings.setDefaultCharsetName("ISO-8859-1"); |
| } |
| }); |
| |
| projectFrame.requestProjectSync().waitForGradleProjectSyncToFinish(); |
| |
| String expectedMessage = "The project encoding (ISO-8859-1) has been reset to the encoding specified in the Gradle build files (UTF-8)."; |
| AbstractContentFixture syncMessages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| syncMessages.findMessage(INFO, firstLineStartingWith(expectedMessage)); |
| |
| assertEquals("UTF-8", EncodingProjectManager.getInstance(project).getDefaultCharsetName()); |
| } |
| |
| // Verifies that the IDE switches SDKs if the IDE and project SDKs are not the same. |
| @Test @IdeGuiTest |
| public void testSdkSwitch() throws IOException { |
| File secondSdkPath = getFilePathProperty("second.android.sdk.path", "the path of a secondary Android SDK", true); |
| if (secondSdkPath == null) { |
| skip("testSdkSwitch"); |
| return; |
| } |
| |
| getGuiTestSuiteState().setSkipSdkMerge(true); |
| |
| File originalSdkPath = IdeSdks.getAndroidSdkPath(); |
| assertNotNull(originalSdkPath); |
| |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| // Change the SDK in the project. We expect the IDE to have the same SDK as the project. |
| LocalProperties localProperties = new LocalProperties(projectFrame.getProject()); |
| localProperties.setAndroidSdkPath(secondSdkPath); |
| localProperties.save(); |
| |
| projectFrame.requestProjectSync(); |
| |
| MessagesFixture messages = projectFrame.findMessageDialog(ANDROID_SDK_MANAGER_DIALOG_TITLE); |
| messages.click("Use Project's SDK"); |
| |
| projectFrame.waitForGradleProjectSyncToFinish(); |
| |
| assertThat(IdeSdks.getAndroidSdkPath()).isEqualTo(secondSdkPath); |
| |
| // Set the project's SDK to be the original one. Now we will choose the IDE's SDK. |
| localProperties = new LocalProperties(projectFrame.getProject()); |
| localProperties.setAndroidSdkPath(originalSdkPath); |
| localProperties.save(); |
| |
| projectFrame.requestProjectSync(); |
| |
| messages = projectFrame.findMessageDialog(ANDROID_SDK_MANAGER_DIALOG_TITLE); |
| messages.click("Use Android Studio's SDK"); |
| |
| projectFrame.waitForGradleProjectSyncToFinish(); |
| |
| localProperties = new LocalProperties(projectFrame.getProject()); |
| assertThat(localProperties.getAndroidSdkPath()).isEqualTo(secondSdkPath); |
| } |
| |
| // Verifies that if syncing using cached model, and if the cached model is missing data, we fall back to a full Gradle sync. |
| // See: https://code.google.com/p/android/issues/detail?id=160899 |
| @Test @IdeGuiTest |
| public void testWithCacheMissingModules() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| // Remove a module from the cache. |
| Project project = projectFrame.getProject(); |
| DataNode<ProjectData> cache = getCachedProjectData(project); |
| assertNotNull(cache); |
| |
| List<DataNode<?>> cachedChildren = field("myChildren").ofType(new TypeRef<List<DataNode<?>>>(){}).in(cache).get(); |
| assertNotNull(cachedChildren); |
| assertThat(cachedChildren.size()).isGreaterThan(1); |
| DataNode<?> toRemove = null; |
| for (DataNode<?> child : cachedChildren) { |
| if (child.getData() instanceof ModuleData) { |
| toRemove = child; |
| break; |
| } |
| } |
| assertNotNull(toRemove); |
| cachedChildren.remove(toRemove); |
| |
| // Force the IDE to use cache for sync. |
| GuiTestSuiteState state = getGuiTestSuiteState(); |
| assertNotNull(state); |
| state.setUseCachedGradleModelOnly(true); |
| |
| // Sync again, and a full sync should occur, since the cache is missing modules. |
| // 'waitForGradleProjectSyncToFinish' will never finish and test will time out and fail if the IDE never gets notified that the sync |
| // finished. |
| projectFrame.requestProjectSync().waitForGradleProjectSyncToFinish(); |
| } |
| |
| // Verify that the IDE warns users about rendering issue when using plugin 1.2.0 to 1.2.2. |
| // See https://code.google.com/p/android/issues/detail?id=170841 |
| @Test @IdeGuiTest |
| public void testModelWithLayoutRenderingIssue() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| updateAndroidModelVersion(projectFrame.getProjectPath(), "1.2.0"); |
| projectFrame.requestProjectSync().waitForGradleProjectSyncToFinish(); |
| |
| AbstractContentFixture syncMessages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| syncMessages.findMessage(WARNING, firstLineStartingWith("Using an obsolete version of the Gradle plugin (1.2.0)")); |
| } |
| |
| // Verifies that after making a change in a build.gradle file, the editor notification saying that sync is needed shows up. This wasn't |
| // the case after a project import. |
| // See https://code.google.com/p/android/issues/detail?id=171370 |
| @Test @IdeGuiTest |
| public void testEditorNotificationsWhenSyncNeededAfterProjectImport() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| |
| EditorFixture editor = projectFrame.getEditor(); |
| editor.open("app/build.gradle").waitUntilErrorAnalysisFinishes().enterText("Hello World"); |
| |
| projectFrame.requireEditorNotification( |
| "Gradle files have changed since last project sync. " + "A project sync may be necessary for the IDE to work properly."); |
| } |
| |
| // Verifies that sync does not fail and user is warned when a project contains an Android module without variants. |
| // See https://code.google.com/p/android/issues/detail?id=170722 |
| @Test @IdeGuiTest |
| public void testWithAndroidProjectWithoutVariants() throws IOException { |
| IdeFrameFixture projectFrame = importSimpleApplication(); |
| Module appModule = projectFrame.getModule("app"); |
| assertNotNull(AndroidFacet.getInstance(appModule)); |
| |
| File appBuildFile = new File(projectFrame.getProjectPath(), join("app", FN_BUILD_GRADLE)); |
| assertThat(appBuildFile).isFile(); |
| |
| // Remove all variants. |
| appendToFile(appBuildFile, "android.variantFilter { variant -> variant.ignore = true }"); |
| |
| projectFrame.requestProjectSync().waitForGradleProjectSyncToFinish(); |
| |
| // Verify user was warned. |
| AbstractContentFixture syncMessages = projectFrame.getMessagesToolWindow().getGradleSyncContent(); |
| syncMessages.findMessage(ERROR, firstLineStartingWith("The module 'app' is an Android project without build variants")); |
| |
| // Verify AndroidFacet was removed. |
| appModule = projectFrame.getModule("app"); |
| assertNull(AndroidFacet.getInstance(appModule)); |
| } |
| |
| @Test @IdeGuiTest |
| public void testModuleLanguageLevel() throws IOException { |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("MultiModule"); |
| |
| Module library = projectFrame.getModule("library"); |
| Module library2 = projectFrame.getModule("library2"); |
| Module app = projectFrame.getModule("app"); |
| |
| assertEquals(JDK_1_6, getJavaLanguageLevel(library)); |
| assertEquals(JDK_1_5, getJavaLanguageLevel(library2)); |
| assertEquals(JDK_1_7, getJavaLanguageLevel(app)); |
| } |
| |
| @Test @IdeGuiTest |
| public void testModuleLanguageLevelWithJdk8() throws IOException { |
| Sdk jdk = IdeSdks.getJdk(); |
| assert jdk != null; |
| if (!Jdks.isApplicableJdk(jdk, JDK_1_8)) { |
| String testName = "testModuleLanguageLevelWithJdk8"; |
| skip(testName); |
| System.out.println(String.format("'%1$s' needs JDK 1.8 or newer.", testName)); |
| return; |
| } |
| |
| IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("MultipleModuleTypes"); |
| Module javaLib = projectFrame.getModule("javaLib"); |
| assertEquals(JDK_1_7, getJavaLanguageLevel(javaLib)); |
| } |
| |
| @Nullable |
| private static LanguageLevel getJavaLanguageLevel(@NotNull Module module) { |
| return LanguageLevelModuleExtensionImpl.getInstance(module).getLanguageLevel(); |
| } |
| |
| private static void updateAndroidModelVersion(@NotNull File projectPath, @NotNull String modelVersion) throws IOException { |
| File buildFile = new File(projectPath, FN_BUILD_GRADLE); |
| assertThat(buildFile).isFile(); |
| |
| String contents = Files.toString(buildFile, Charsets.UTF_8); |
| Pattern pattern = Pattern.compile("classpath ['\"]com.android.tools.build:gradle:(.+)['\"]"); |
| Matcher matcher = pattern.matcher(contents); |
| if (matcher.find()) { |
| contents = contents.substring(0, matcher.start(1)) + modelVersion + contents.substring(matcher.end(1)); |
| write(contents, buildFile, Charsets.UTF_8); |
| } |
| else { |
| fail("Cannot find declaration of Android plugin"); |
| } |
| } |
| } |