blob: f2a37d35b716085fc457091d0f61cd2a71d88346 [file] [log] [blame]
/*
* 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);
}
}