| /* |
| * 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.npw; |
| |
| import com.android.SdkConstants; |
| import com.android.tools.idea.gradle.IdeaGradleProject; |
| import com.android.tools.idea.gradle.eclipse.GradleImport; |
| import com.android.tools.idea.gradle.facet.AndroidGradleFacet; |
| import com.android.tools.idea.gradle.parser.GradleBuildFile; |
| import com.android.tools.idea.gradle.parser.GradleSettingsFile; |
| import com.android.tools.idea.gradle.project.GradleProjectImporter; |
| import com.android.tools.idea.gradle.project.GradleSyncListener; |
| import com.android.tools.idea.gradle.stubs.gradle.GradleProjectStub; |
| import com.android.tools.idea.gradle.util.GradleUtil; |
| import com.android.tools.idea.gradle.util.Projects; |
| import com.android.tools.idea.npw.CreateModuleFromArchiveAction; |
| import com.android.tools.idea.npw.NewModuleWizardState; |
| import com.android.tools.idea.npw.WrapArchiveWizardPath; |
| import com.android.tools.idea.templates.AndroidGradleTestCase; |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import com.google.common.io.Closer; |
| import com.google.common.io.Files; |
| import com.google.common.util.concurrent.Atomics; |
| import com.intellij.facet.FacetManager; |
| import com.intellij.facet.ModifiableFacetModel; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.Result; |
| import com.intellij.openapi.command.WriteCommandAction; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ex.ProjectManagerEx; |
| import com.intellij.openapi.project.impl.ProjectManagerImpl; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.ThrowableComputable; |
| import com.intellij.openapi.vfs.VfsUtil; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.testFramework.PlatformTestCase; |
| import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; |
| import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; |
| import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; |
| import com.intellij.testFramework.fixtures.TestFixtureBuilder; |
| import org.gradle.tooling.model.GradleProject; |
| import org.jetbrains.android.AndroidTestBase; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.util.jar.Attributes; |
| import java.util.jar.JarEntry; |
| import java.util.jar.JarOutputStream; |
| import java.util.jar.Manifest; |
| |
| public final class WrapArchiveWizardPathTest extends AndroidTestBase { |
| public static final String LIB_DIR_NAME = "lib"; |
| public static final String LIBS_DEPENDENCY = "compile fileTree(dir: '" + LIB_DIR_NAME + "', include: ['*.jar'])"; |
| public static final String LIBRARY_JAR_NAME = "library.jar"; |
| private static final String TOP_LEVEL_BUILD_GRADLE = "buildscript {\n" + |
| " repositories {\n" + |
| GradleImport.MAVEN_REPOSITORY + |
| " }\n" + |
| " dependencies {\n" + |
| " classpath 'com.android.tools.build:gradle:" + |
| SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION + "'\n" + |
| " }\n" + |
| "}\n" + |
| "\n" + |
| "allprojects {\n" + |
| " repositories {\n" + |
| GradleImport.MAVEN_REPOSITORY + |
| " }\n" + |
| "}\n"; |
| private static final String BUILD_GRADLE_TEMPLATE = "apply plugin: 'java'\n\n" + |
| "dependencies {\n" + |
| " compile 'com.android.support:support-v4:13.0.+'\n" + |
| " %s\n" + |
| "}\n\n"; |
| private File dir; |
| private File myJarFile; |
| |
| private static void assertJarImport(@NotNull Project project, |
| @NotNull String gradlePath, |
| @NotNull File jarFile, |
| boolean sourceFileShouldExist) throws IOException { |
| File defaultSubprojectLocation = GradleUtil.getModuleDefaultPath(project.getBaseDir(), gradlePath); |
| File copy = new File(defaultSubprojectLocation, jarFile.getName()); |
| assertTrue(String.format("File %s does not exist", copy), copy.exists()); |
| File buildGradle = new File(defaultSubprojectLocation, SdkConstants.FN_BUILD_GRADLE); |
| assertTrue(String.format("File %s does not exist", buildGradle), buildGradle.exists()); |
| VirtualFile vfile = VfsUtil.findFileByIoFile(buildGradle, true); |
| assert vfile != null; |
| assertEquals(CreateModuleFromArchiveAction.getBuildGradleText(jarFile), VfsUtilCore.loadText(vfile)); |
| GradleSettingsFile settingsFile = GradleSettingsFile.get(project); |
| assert settingsFile != null; |
| Iterable<String> modules = settingsFile.getModules(); |
| assertTrue("{ " + Joiner.on(", ").join(modules) + " }", Iterables.contains(modules, gradlePath)); |
| assertEquals("Source file existance", sourceFileShouldExist, jarFile.isFile()); |
| } |
| |
| /** |
| * We need real Jar contents as this test will actually run Gradle that will peek inside the archive. |
| */ |
| @NotNull |
| private static byte[] createRealJarArchive() throws IOException { |
| ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |
| Closer closer = Closer.create(); |
| Manifest manifest = new Manifest(); |
| manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); |
| JarOutputStream jar = closer.register(new JarOutputStream(buffer, manifest)); |
| try { |
| jar.putNextEntry(new JarEntry("/dummy.txt")); |
| jar.write(TOP_LEVEL_BUILD_GRADLE.getBytes()); |
| closer.close(); |
| return buffer.toByteArray(); |
| } |
| catch (IOException e) { |
| closer.close(); |
| throw closer.rethrow(e); |
| } |
| } |
| |
| private static VirtualFile createFile(VirtualFile directory, String fname, String contents) throws IOException { |
| VirtualFile archive = directory.createChildData(Integer.valueOf(0), fname); |
| VfsUtil.saveText(archive, contents); |
| return archive; |
| } |
| |
| private static Module[] getModules(final Project project, String... moduleNames) { |
| if (moduleNames.length == 0) { |
| return new Module[0]; |
| } |
| else { |
| List<Module> modules = Lists.newArrayList(); |
| for (String name : moduleNames) { |
| Module module = GradleUtil.findModuleByGradlePath(project, WrapArchiveWizardPath.makeAbsolute(name)); |
| assert module != null : "Module \"" + WrapArchiveWizardPath.makeAbsolute(name) + "\" was not found in the project "; |
| modules.add(module); |
| } |
| return Iterables.toArray(modules, Module.class); |
| } |
| } |
| |
| private static void createModule(@NotNull Project project, |
| @NotNull File archive, |
| @NotNull String gradlePath, |
| boolean moveFile, |
| @Nullable Module[] modulesToUpdateDependency) throws IOException { |
| NewModuleWizardState wizardState = new NewModuleWizardState(); |
| WrapArchiveWizardPath path = new WrapArchiveWizardPath(wizardState, project, null, null); |
| |
| wizardState.templateChanged(project, NewModuleWizardState.ARCHIVE_IMPORT_NAME); |
| wizardState.put(WrapArchiveWizardPath.KEY_ARCHIVE, archive.getAbsolutePath()); |
| wizardState.put(WrapArchiveWizardPath.KEY_GRADLE_PATH, gradlePath); |
| |
| wizardState.put(WrapArchiveWizardPath.KEY_MOVE_ARCHIVE, moveFile); |
| wizardState.put(WrapArchiveWizardPath.KEY_MODULES_FOR_DEPENDENCY_UPDATE, modulesToUpdateDependency); |
| |
| path.createModule(); |
| } |
| |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| |
| final TestFixtureBuilder<IdeaProjectTestFixture> projectBuilder = |
| IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getName()); |
| myFixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(projectBuilder.getFixture()); |
| myFixture.setUp(); |
| myFixture.setTestDataPath(getTestDataPath()); |
| |
| dir = Files.createTempDir(); |
| |
| myJarFile = new File(dir, LIBRARY_JAR_NAME); |
| Files.write(createRealJarArchive(), myJarFile); |
| |
| VirtualFile baseDir = getProject().getBaseDir(); |
| AndroidGradleTestCase.createGradleWrapper(VfsUtilCore.virtualToIoFile(baseDir)); |
| System.out.printf("Project location: %s\n", baseDir); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| if (myFixture != null) { |
| myFixture.tearDown(); |
| myFixture = null; |
| } |
| ProjectManagerEx projectManager = ProjectManagerEx.getInstanceEx(); |
| Project[] openProjects = projectManager.getOpenProjects(); |
| if (openProjects.length > 0) { |
| final Project project = openProjects[0]; |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| Disposer.dispose(project); |
| ProjectManagerEx projectManager = ProjectManagerEx.getInstanceEx(); |
| if (projectManager instanceof ProjectManagerImpl) { |
| Collection<Project> projectsStillOpen = projectManager.closeTestProject(project); |
| if (!projectsStillOpen.isEmpty()) { |
| Project project = projectsStillOpen.iterator().next(); |
| projectsStillOpen.clear(); |
| throw new AssertionError("Test project is not disposed: " + project + ";\n created in: " + |
| PlatformTestCase.getCreationPlace(project)); |
| } |
| } |
| } |
| }); |
| } |
| if (dir != null && dir.isDirectory()) { |
| ApplicationManager.getApplication().runWriteAction(new ThrowableComputable<Boolean, IOException>() { |
| @Override |
| public Boolean compute() throws IOException { |
| VirtualFile vfile = VfsUtil.findFileByIoFile(dir, true); |
| if (vfile != null) { |
| vfile.delete(this); |
| } |
| return true; |
| } |
| }); |
| } |
| super.tearDown(); |
| } |
| |
| public void testCreateModuleDefaultName() throws Exception { |
| Project project = myFixture.getProject(); |
| String gradlePath = WrapArchiveWizardPath.makeAbsolute(Files.getNameWithoutExtension(myJarFile.getAbsolutePath())); |
| createModule(project, myJarFile, gradlePath, false, null); |
| assertJarImport(project, gradlePath, myJarFile, true); |
| } |
| |
| public void testCreateModuleNonDefaultSimpleName() throws Exception { |
| Project project = myFixture.getProject(); |
| String gradlePath = WrapArchiveWizardPath.makeAbsolute("testmodule"); |
| createModule(project, myJarFile, gradlePath, false, null); |
| assertJarImport(project, gradlePath, myJarFile, true); |
| } |
| |
| public void testCreateModuleNonDefaultNestedName() throws Exception { |
| Project project = myFixture.getProject(); |
| String gradlePath = WrapArchiveWizardPath.makeAbsolute(":category:module"); |
| createModule(project, myJarFile, gradlePath, false, null); |
| assertJarImport(project, gradlePath, myJarFile, true); |
| } |
| |
| public void testMoveJarFromProject() throws IOException { |
| String buildGradleText = String.format(BUILD_GRADLE_TEMPLATE, LIBS_DEPENDENCY); |
| String newModuleName = WrapArchiveWizardPath.makeAbsolute(Files.getNameWithoutExtension(LIBRARY_JAR_NAME)); |
| String buildGradleWithReplacedDependency = String.format(BUILD_GRADLE_TEMPLATE, |
| LIBS_DEPENDENCY + "\n compile project('" + newModuleName + "')"); |
| asseryJarProperlyMoved(buildGradleText, buildGradleWithReplacedDependency); |
| } |
| |
| public void testMoveJarFromProjectReplaceFileDependency() throws IOException { |
| String buildGradleText = String.format(BUILD_GRADLE_TEMPLATE, |
| LIBS_DEPENDENCY + "\n compile files('lib/" + LIBRARY_JAR_NAME + "')"); |
| String newModuleName = WrapArchiveWizardPath.makeAbsolute(Files.getNameWithoutExtension(LIBRARY_JAR_NAME)); |
| String buildGradleWithReplacedDependency = String.format(BUILD_GRADLE_TEMPLATE, |
| LIBS_DEPENDENCY + "\n compile project('" + newModuleName + "')"); |
| asseryJarProperlyMoved(buildGradleText, buildGradleWithReplacedDependency); |
| } |
| |
| public void testMoveJarFromProjectReplaceFileDependencyFromList() throws IOException { |
| String buildGradleText = String.format(BUILD_GRADLE_TEMPLATE, |
| LIBS_DEPENDENCY + |
| "\n compile files('lib/" + LIBRARY_JAR_NAME + "', 'some/other/file.jar')"); |
| String newModuleName = WrapArchiveWizardPath.makeAbsolute(Files.getNameWithoutExtension(LIBRARY_JAR_NAME)); |
| String buildGradleWithReplacedDependency = String.format(BUILD_GRADLE_TEMPLATE, |
| LIBS_DEPENDENCY + |
| "\n compile files('some/other/file.jar')" + |
| "\n compile project('" + newModuleName + "')"); |
| asseryJarProperlyMoved(buildGradleText, buildGradleWithReplacedDependency); |
| } |
| |
| private void asseryJarProperlyMoved(String initialBuildGradle, String expectedBuildGradle) throws IOException { |
| String moduleName = "mymodule"; |
| Project project = getProject(); |
| |
| File fileToImport = new CreateAndroidStudioProjectAction(project, moduleName, initialBuildGradle).execute().getResultObject(); |
| String newModuleName = WrapArchiveWizardPath.makeAbsolute(Files.getNameWithoutExtension(LIBRARY_JAR_NAME)); |
| createModule(project, fileToImport, newModuleName, true, getModules(project, moduleName)); |
| assertJarImport(project, newModuleName, fileToImport, false); |
| VirtualFile buildGradle = project.getBaseDir().findFileByRelativePath(moduleName + "/" + SdkConstants.FN_BUILD_GRADLE); |
| assert buildGradle != null; |
| GradleBuildFile buildModel = new GradleBuildFile(buildGradle, project); |
| assertEquals(expectedBuildGradle, buildModel.getPsiFile().getText()); |
| } |
| |
| private static class CreateAndroidStudioProjectAction extends WriteCommandAction<File> { |
| private final Project myProject; |
| private final String myModuleName; |
| @NotNull private final String myModuleBuildGradleBody; |
| |
| public CreateAndroidStudioProjectAction(Project project, String moduleName, @NotNull String moduleBuildGradleBody) { |
| super(project); |
| myProject = project; |
| myModuleName = moduleName; |
| myModuleBuildGradleBody = moduleBuildGradleBody; |
| } |
| |
| @Override |
| protected void run(@NotNull final Result<File> result) throws Throwable { |
| File path = new File(VfsUtilCore.virtualToIoFile(myProject.getBaseDir()), myModuleName + File.separator + LIB_DIR_NAME); |
| VirtualFile directory = VfsUtil.createDirectories(path.getAbsolutePath()); |
| VirtualFile archive = directory.createChildData(this, LIBRARY_JAR_NAME); |
| archive.setBinaryContent(createRealJarArchive()); |
| final VirtualFile moduleBuildGradle = createFile(directory.getParent(), SdkConstants.FN_BUILD_GRADLE, myModuleBuildGradleBody); |
| final VirtualFile topBuildGradle = createFile(myProject.getBaseDir(), SdkConstants.FN_BUILD_GRADLE, TOP_LEVEL_BUILD_GRADLE); |
| PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); |
| GradleSettingsFile settingsGradle = GradleSettingsFile.getOrCreate(myProject); |
| settingsGradle.addModule(WrapArchiveWizardPath.makeAbsolute(myModuleName), VfsUtilCore.virtualToIoFile(directory.getParent())); |
| final AtomicReference<String> error = Atomics.newReference(); |
| final AtomicBoolean done = new AtomicBoolean(false); |
| GradleProjectImporter.getInstance().requestProjectSync(myProject, new GradleSyncListener.Adapter() { |
| @Override |
| public void syncSucceeded(@NotNull Project project) { |
| Module module = ModuleManager.getInstance(myProject).findModuleByName(myModuleName); |
| assert module != null; |
| FacetManager facetManager = FacetManager.getInstance(module); |
| ModifiableFacetModel modifiableModel = facetManager.createModifiableModel(); |
| AndroidGradleFacet facet = facetManager.createFacet(AndroidGradleFacet.getFacetType(), AndroidGradleFacet.NAME, null); |
| GradleProject gradleProject = new GradleProjectStub(myProject.getName(), |
| WrapArchiveWizardPath.makeAbsolute(myModuleName), |
| VfsUtilCore.virtualToIoFile(topBuildGradle), |
| "compile"); |
| IdeaGradleProject ideaGradleProject = IdeaGradleProject.newIdeaGradleProject(myModuleName, |
| gradleProject, |
| VfsUtilCore.virtualToIoFile(moduleBuildGradle), |
| "2.2.1"); |
| facet.setGradleProject(ideaGradleProject); |
| modifiableModel.addFacet(facet); |
| modifiableModel.commit(); |
| assert Projects.isBuildWithGradle(module); |
| done.set(true); |
| } |
| |
| @Override |
| public void syncFailed(@NotNull Project project, @NotNull String errorMessage) { |
| error.set(errorMessage); |
| } |
| }); |
| if (error.get() != null) { |
| throw new IllegalStateException(error.get()); |
| } |
| if (!done.get()) { |
| throw new IllegalStateException("Sync should've been complete by now"); |
| } |
| result.setResult(VfsUtilCore.virtualToIoFile(archive)); |
| } |
| |
| } |
| } |