| /* |
| * 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.gradle.service.resolve; |
| |
| import com.android.tools.idea.templates.AndroidGradleTestCase; |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Sets; |
| import com.intellij.codeInsight.completion.CompletionType; |
| import com.intellij.codeInsight.lookup.LookupElement; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.*; |
| 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.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder; |
| |
| import java.util.Set; |
| |
| /** |
| * {@link AndroidDslContributorTest} tests that various elements in the Gradle build script are |
| * being resolved to their appropriate elements in the gradle model. We have this as a |
| * {@link AndroidGradleTestCase} instead of just a simple PSI test case since resolving these |
| * requires that the Android Gradle plugin is actually in the classpath, and that happens |
| * only if a Gradle project is actually imported by the IDE. |
| */ |
| public class AndroidDslContributorTest extends AndroidGradleTestCase { |
| // This test fails on the build server with the error: "Assertion failed: Already disposed", but works fine locally |
| // Temporarily disable this until we find the root cause. |
| public void disabled_testResolutions() throws Exception { |
| loadProject("projects/resolve/simple"); |
| PsiFile psiFile = getPsiFile("build.gradle"); |
| assertNotNull(psiFile); |
| |
| // symbols inside android block |
| validateResolution(psiFile, "compileSdkVersion", "com.android.build.gradle.BaseExtension", "compileSdkVersion"); |
| validateResolution(psiFile, "buildToolsVersion", "com.android.build.gradle.BaseExtension", "buildToolsVersion"); |
| validateResolution(psiFile, "defaultConfig", "com.android.build.gradle.BaseExtension", "defaultConfig"); |
| |
| // symbols inside defaultConfig |
| validateResolution(psiFile, "minSdkVersion", "com.android.builder.DefaultProductFlavor", "setMinSdkVersion"); |
| |
| // symbols inside a build type |
| validateResolution(psiFile, "runProguard", "com.android.builder.DefaultBuildType", "setRunProguard"); |
| validateResolution(psiFile, "proguardFiles", "com.android.build.gradle.internal.dsl.BuildTypeDsl", "proguardFiles"); |
| |
| // symbols inside a product flavor |
| validateResolution(psiFile, "packageName", "com.android.builder.DefaultProductFlavor", "setPackageName"); |
| |
| // symbols inside lintOptions |
| validateResolution(psiFile, "quiet", "com.android.build.gradle.internal.dsl.LintOptionsImpl", "setQuiet"); |
| |
| // symbols inside signingConfigs |
| validateResolution(psiFile, "storeFile", "com.android.builder.signing.DefaultSigningConfig", "setStoreFile"); |
| |
| // symbols inside sourceSets |
| validateResolution(psiFile, "aidl", "com.android.build.gradle.api.AndroidSourceSet", "getAidl"); |
| validateResolution(psiFile, "setRoot", "com.android.build.gradle.api.AndroidSourceSet", "setRoot"); |
| |
| validateNoResolution(psiFile, "publishNonDefault"); |
| } |
| |
| // This test fails on the build server with the error: "Assertion failed: Already disposed", but works fine locally |
| // Temporarily disable this until we find the root cause. |
| public void disabled_testResolutionsInLibrary() throws Exception { |
| if (isRunningOnHudson()) { |
| logTestDisabled(); |
| return; |
| } |
| |
| loadProject("projects/resolve/simple"); |
| PsiFile psiFile = getPsiFile("lib.gradle"); |
| assertNotNull(psiFile); |
| |
| validateResolution(psiFile, "publishNonDefault", "com.android.build.gradle.LibraryExtension", "publishNonDefault"); |
| } |
| |
| private void logTestDisabled() { |
| System.out.println("The test: " + getTestName(false) + " is temporarily disabled on the build server."); |
| } |
| |
| private static boolean isRunningOnHudson() { |
| return System.getenv("HUDSON_HOME") != null; |
| } |
| |
| private static void validateNoResolution(PsiFile psiFile, String symbol) { |
| PsiReference ref = getPsiReference(psiFile, symbol); |
| assert ref instanceof GrReferenceExpression : symbol; |
| |
| GrReferenceExpression referenceExpression = (GrReferenceExpression)ref; |
| PsiElement element = referenceExpression.advancedResolve().getElement(); |
| assertNull(element); |
| } |
| |
| // tests that the given symbol in the given psi file resolves to the given method and class |
| // Note: This method just does a substring match within the given file to locate the symbol, |
| // so make sure that symbols that are being searched for aren't duplicated (in which it'll always use the first one) |
| private static void validateResolution(PsiFile psiFile, String symbol, String fqcn, String methodName) { |
| PsiReference ref = getPsiReference(psiFile, symbol); |
| assert ref instanceof GrReferenceExpression : symbol; |
| |
| GrReferenceExpression referenceExpression = (GrReferenceExpression)ref; |
| PsiElement element = referenceExpression.advancedResolve().getElement(); |
| |
| if (element instanceof GrLightMethodBuilder) { |
| element = element.getNavigationElement(); |
| } |
| |
| assert element instanceof PsiMethod; |
| PsiMethod psiMethod = (PsiMethod)element; |
| PsiClass cls = psiMethod.getContainingClass(); |
| |
| assertNotNull("Unable to find containing class for " + symbol, cls); |
| assertEquals("Class names don't match while resolving " + symbol, fqcn, cls.getQualifiedName()); |
| assertEquals("Method names don't match while resolving " + symbol, methodName, psiMethod.getName()); |
| } |
| |
| // This test fails on the build server with the error: "Assertion failed: Already disposed", but works fine locally |
| // Temporarily disable this until we find the root cause. |
| public void disabled_testCompletions() throws Exception { |
| loadProject("projects/resolve/simple"); |
| |
| assertHasCompletions("completion/comp.gradle", "compileSdkVersion", "compileOptions"); |
| assertHasCompletions("completion/suffix.gradle", "packageNameSuffix", "versionNameSuffix"); |
| assertHasCompletions("completion/ndk.gradle", "ndk", "ndkConfig", "renderscriptNdkMode"); |
| } |
| |
| private void assertHasCompletions(String path, String... expectedCompletions) throws Exception { |
| VirtualFile file = getProject().getBaseDir().findFileByRelativePath(path); |
| assertNotNull(file); |
| |
| myFixture.configureFromExistingVirtualFile(file); |
| |
| LookupElement[] elements = myFixture.complete(CompletionType.BASIC); |
| assertNotNull(elements); |
| |
| Set<String> suggestions = Sets.newHashSetWithExpectedSize(elements.length); |
| for (LookupElement element : elements) { |
| suggestions.add(element.getLookupString()); |
| } |
| |
| for (String expected : expectedCompletions) { |
| String msg = String.format("%1$s not in available completions: {%2$s}", expected, Joiner.on(',').join(suggestions)); |
| assertTrue(msg, suggestions.contains(expected)); |
| } |
| } |
| |
| @Nullable |
| private GroovyFile getPsiFile(String path) throws Exception { |
| VirtualFile buildFile = getProject().getBaseDir().findChild(path); |
| assertNotNull(buildFile); |
| |
| PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(buildFile); |
| assertTrue(psiFile instanceof GroovyFile); |
| |
| return (GroovyFile)psiFile; |
| } |
| |
| @Nullable |
| private static PsiReference getPsiReference(PsiFile psiFile, String element) { |
| int offset = psiFile.getText().indexOf(element); |
| if (offset < 0) { |
| fail("Symbol " + element + " not found in file."); |
| } |
| return psiFile.findReferenceAt(offset); |
| } |
| } |