| /* |
| * 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.navigator; |
| |
| import com.android.tools.idea.gradle.project.GradleSyncListener; |
| import com.android.tools.idea.gradle.project.NewProjectImportGradleSyncListener; |
| import com.android.tools.idea.gradle.util.GradleUtil; |
| import com.android.tools.idea.navigator.nodes.AndroidViewProjectNode; |
| import com.android.tools.idea.templates.AndroidGradleTestCase; |
| import com.google.common.base.Joiner; |
| import com.google.common.base.Splitter; |
| import com.google.common.collect.Lists; |
| import com.intellij.ide.projectView.ViewSettings; |
| import com.intellij.ide.projectView.impl.GroupByTypeComparator; |
| import com.intellij.ide.util.treeView.AbstractTreeNode; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.Queryable; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.projectView.TestProjectTreeStructure; |
| import com.intellij.psi.PsiDirectory; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.xml.XmlFile; |
| import com.intellij.testFramework.PlatformTestUtil; |
| import com.intellij.testFramework.ProjectViewTestUtil; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.Comparator; |
| import java.util.List; |
| |
| // TODO: Test available actions for each node! |
| public class AndroidProjectViewTest extends AndroidGradleTestCase { |
| private AndroidProjectViewPane myPane; |
| |
| public void testProjectView() throws Exception { |
| loadProject("projects/navigator/packageview/simple"); |
| |
| myPane = createPane(); |
| TestAndroidTreeStructure structure = new TestAndroidTreeStructure(getProject(), myTestRootDisposable); |
| |
| Queryable.PrintInfo printInfo = new Queryable.PrintInfo(); |
| PsiDirectory dir = getBaseFolder(); |
| assertNotNull(dir); |
| |
| String rootModuleName = null; |
| for (Module module : ModuleManager.getInstance(getProject()).getModules()) { |
| if (GradleUtil.getGradlePath(module) == null) { |
| rootModuleName = module.getName(); |
| } |
| } |
| assertNotNull(rootModuleName); |
| |
| String projectName = getProject().getName(); |
| String expected = |
| projectName + "\n" + |
| " app (Android)\n" + |
| " manifests\n" + |
| " AndroidManifest.xml (main)\n" + |
| " AndroidManifest.xml (debug)\n" + |
| " java\n" + |
| " app (main)\n" + |
| " MainActivity\n" + |
| " app (androidTest)\n" + |
| " MainActivityTest.java\n" + |
| " Debug.java\n" + |
| " res\n" + |
| " drawable\n" + |
| " ic_launcher.png (2)\n" + |
| " ic_launcher.png (hdpi, debug)\n" + |
| " ic_launcher.png (mdpi)\n" + |
| " j.png (mdpi)\n" + |
| " layout\n" + |
| " activity_main.xml\n" + |
| " menu\n" + |
| " main.xml\n" + |
| " values\n" + |
| " dimens.xml (3)\n" + |
| " dimens.xml\n" + |
| " dimens.xml (debug)\n" + |
| " dimens.xml (w820dp)\n" + |
| " strings.xml (2)\n" + |
| " strings.xml\n" + |
| " strings.xml (debug)\n" + |
| " styles.xml\n" + |
| " assets\n" + |
| " raw.asset.txt (main)\n" + |
| " rs\n" + |
| " test.rs (main)\n" + |
| " empty (non-Android)\n" + |
| " javamodule (non-Android)\n" + |
| " java\n" + |
| " foo\n" + |
| " Foo.java\n" + |
| " tests\n" + |
| " foo\n" + |
| " FooTest.java\n" + |
| " resources\n" + |
| " res2.txt\n" + |
| " test-resources\n" + |
| " test-res.txt\n" + |
| " lib (Android)\n" + |
| " manifests\n" + |
| " AndroidManifest.xml (main)\n" + |
| " jni\n" + |
| " hello.c (main)\n" + |
| " jniLibs\n" + |
| " libc.so (main)\n" + |
| " libm.so (debug)\n" + |
| " res\n" + |
| " drawable\n" + |
| " ic_launcher.png (mdpi)\n" + |
| " values\n" + |
| " strings.xml\n" + |
| " Gradle Scripts\n" + |
| " build.gradle (Project: " + rootModuleName + ")\n" + |
| " build.gradle (Module: app)\n" + |
| " sonar.gradle (Module: app)\n" + |
| " build.gradle (Module: empty)\n" + |
| " build.gradle (Module: javamodule)\n" + |
| " build.gradle (Module: lib)\n" + |
| " gradle-wrapper.properties (Gradle Version)\n" + |
| " proguard-rules.pro (ProGuard Rules for app)\n" + |
| " proguard.cfg (ProGuard Rules for lib)\n" + |
| " settings.gradle (Project Settings)\n" + |
| " local.properties (SDK Location)\n"; |
| int numLines = expected.split("\n").length; |
| ProjectViewTestUtil |
| .assertStructureEqual(structure, expected, numLines, new GroupByTypeComparator(null, "android"), structure.getRootElement(), |
| printInfo); |
| } |
| |
| // Test that selecting a res group node causes the correct PSI Elements to be selected |
| public void testSelection() throws Exception { |
| loadProject("projects/navigator/packageview/simple"); |
| |
| myPane = createPane(); |
| TestAndroidTreeStructure structure = new TestAndroidTreeStructure(getProject(), myTestRootDisposable); |
| |
| // Select the node app/res/values/dimens.xml, which groups together 3 dimens.xml files |
| Object element = findElementForPath(structure, "app (Android)", "res", "values", "dimens.xml (3)"); |
| assertNotNull(element); |
| |
| myPane.getTreeBuilder().select(element); |
| |
| // Now make sure that selecting that group node caused the actual files to be selected |
| PsiElement[] psiElements = myPane.getSelectedPSIElements(); |
| assertEquals(3, psiElements.length); |
| |
| for (PsiElement e : psiElements) { |
| assertEquals("dimens.xml", ((XmlFile)psiElements[0]).getName()); |
| } |
| } |
| |
| @Nullable |
| private Object findElementForPath(TestAndroidTreeStructure structure, String... path) { |
| Object current = structure.getRootElement(); |
| |
| outer: for (String segment : path) { |
| for (Object child : structure.getChildElements(current)) { |
| AbstractTreeNode node = (AbstractTreeNode)child; |
| if (segment.equals(node.toTestString(null))) { |
| current = node; |
| continue outer; |
| } |
| } |
| |
| // none of the children match the expected segment |
| return null; |
| } |
| |
| return current; |
| } |
| |
| public void testCommonRoots() throws Exception { |
| loadProject("projects/navigator/packageview/commonroots"); |
| |
| myPane = createPane(); |
| TestAndroidTreeStructure structure = new TestAndroidTreeStructure(getProject(), myTestRootDisposable); |
| |
| Queryable.PrintInfo printInfo = new Queryable.PrintInfo(); |
| PsiDirectory dir = getBaseFolder(); |
| assertNotNull(dir); |
| |
| Module[] modules = ModuleManager.getInstance(getProject()).getModules(); |
| assertEquals(1, modules.length); |
| |
| String projectName = getProject().getName(); |
| String expected = |
| projectName + "\n" + |
| " Gradle Scripts\n" + |
| " build.gradle (Module: " + modules[0].getName() + ")\n" + |
| " gradle-wrapper.properties (Gradle Version)\n" + |
| " " + modules[0].getName() + " (Android)\n" + |
| " java\n" + |
| " foo (main)\n" + |
| " Foo.java\n" + |
| " manifests\n" + |
| " AndroidManifest.xml (main)\n" + |
| " res\n" + |
| " values\n" + |
| " dimens.xml (w820dp)\n" + |
| " resources\n" + |
| " sample_resource.txt\n"; |
| int numLines = expected.split("\n").length; |
| ProjectViewTestUtil |
| .assertStructureEqual(structure, expected, numLines, PlatformTestUtil.createComparator(printInfo), structure.getRootElement(), |
| printInfo); |
| } |
| |
| public void testFailedImport() throws Exception { |
| loadProject("projects/navigator/invalid", false, new GradleSyncListener.Adapter() { |
| @Override |
| public void syncFailed(@NotNull final Project project, @NotNull String errorMessage) { |
| // If the sync fails, then IDE creates an empty top level module. Mimic the same behavior for this test. |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| NewProjectImportGradleSyncListener.createTopLevelModule(project); |
| } |
| }); |
| } |
| }); |
| } |
| }); |
| |
| myPane = createPane(); |
| TestAndroidTreeStructure structure = new TestAndroidTreeStructure(getProject(), myTestRootDisposable); |
| |
| Queryable.PrintInfo printInfo = new Queryable.PrintInfo(); |
| PsiDirectory dir = getBaseFolder(); |
| assertNotNull(dir); |
| |
| Module[] modules = ModuleManager.getInstance(getProject()).getModules(); |
| assertEquals(1, modules.length); |
| |
| String projectName = getProject().getName(); |
| String expected = |
| projectName + "\n" + |
| " Gradle Scripts\n" + |
| " build.gradle (Project: " + modules[0].getName() + ")\n" + |
| " gradle-wrapper.properties (Gradle Version)\n" + |
| " " + modules[0].getName() + "\n" + |
| " .idea\n" + |
| " AndroidManifest.xml\n" + |
| " build.gradle\n" + |
| " gradle\n" + |
| " wrapper\n" + |
| " gradle-wrapper.jar\n" + |
| " gradle-wrapper.properties\n" + |
| " gradlew\n" + |
| " gradlew.bat\n"; |
| int numLines = expected.split("\n").length; |
| |
| Object rootNode = structure.getRootElement(); |
| ProjectViewTestUtil.checkGetParentConsistency(structure, rootNode); |
| Comparator<AbstractTreeNode> comparator = PlatformTestUtil.createComparator(printInfo); |
| |
| // Android Studio now bundles a local maven repo. Our gradle builds pass this via an init script. It turns out that this drops |
| // in an additional gradle file into the project view. This gradle file is named asLocalRepo???.gradle. Since we can't predict |
| // the exact name, we just trim it out from the actual output. |
| String actual = PlatformTestUtil.print(structure, rootNode, 0, comparator, numLines + 1, ' ', printInfo).toString(); |
| List<String> filtered = Lists.newArrayList(); |
| for (String s : Splitter.on('\n').split(actual)) { |
| if (!s.contains("asLocalRepo")) { |
| filtered.add(s); |
| } |
| } |
| |
| actual = Joiner.on('\n').join(filtered); |
| |
| assertEquals(expected, actual); |
| } |
| |
| @Nullable |
| private PsiDirectory getBaseFolder() throws Exception { |
| VirtualFile folder = getProject().getBaseDir(); |
| assertNotNull("project basedir is null!", folder); |
| return PsiManager.getInstance(getProject()).findDirectory(folder); |
| } |
| |
| private class TestAndroidTreeStructure extends TestProjectTreeStructure { |
| public TestAndroidTreeStructure(Project project, Disposable parentDisposable) { |
| super(project, parentDisposable); |
| } |
| |
| @Override |
| protected AbstractTreeNode createRoot(Project project, ViewSettings settings) { |
| return new AndroidViewProjectNode(project, settings, myPane); |
| } |
| |
| @Override |
| public boolean isShowLibraryContents() { |
| return false; |
| } |
| |
| @Override |
| public boolean isHideEmptyMiddlePackages() { |
| return true; |
| } |
| } |
| |
| private AndroidProjectViewPane createPane() { |
| final AndroidProjectViewPane pane = new AndroidProjectViewPane(getProject()); |
| pane.createComponent(); |
| Disposer.register(getProject(), pane); |
| return pane; |
| } |
| } |