blob: d9ed60cb9089f3dbe4e4b7485657ba68b7daff35 [file] [log] [blame]
/*
* Copyright (C) 2015 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.google.appindexing.api;
import com.android.SdkConstants;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.util.InheritanceUtil;
import org.jetbrains.android.AndroidTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ApiCreatorTest extends AndroidTestCase {
private static final String BASE_PATH = PathManager.getHomePath() + "/../studio/google/appindexing/testData/ApiCreator";
private static final String ANDROID_MANIFEST = "AndroidManifest.xml";
private static final String JAVA_FILE_NOT_ACTIVITY = "src/NotActivity.java";
private static final String JAVA_FILE_HAS_APP_INDEXING_START_AND_END = "src/HasAppIndexingStartAndEnd.java";
private static final String JAVA_FILE_SPECIAL_STATEMENT = "src/SpecialStatement.java";
private static final String JAVA_FILE_EMPTY_ACTIVITY = "src/EmptyActivity.java";
private static final String JAVA_FILE_EMPTY_ACTIVITY_AFTER = "src/EmptyActivity_after.java";
private static final String JAVA_FILE_CONFLICT_IMPORTS = "src/ConflictImports.java";
private static final String JAVA_FILE_CONFLICT_IMPORTS_AFTER = "src/ConflictImports_after.java";
private static final String JAVA_FILE_WILD_CARD_IMPORTS = "src/WildCardImports.java";
private static final String JAVA_FILE_WILD_CARD_IMPORTS_AFTER = "src/WildCardImports_after.java";
private static final String JAVA_FILE_BOTH_CALL_ON_START = "src/BothCallsInOnStart.java";
private static final String JAVA_FILE_BOTH_CALL_ON_START_AFTER = "src/BothCallsInOnStart_after.java";
private static final String JAVA_FILE_ONLY_UPDATE_CALL = "src/OnlyStartCall.java";
private static final String JAVA_FILE_ONLY_UPDATE_CALL_AFTER = "src/OnlyStartCall.java";
private static final String JAVA_FILE_ONLY_START_CALL = "src/OnlyStartCall.java";
private static final String JAVA_FILE_ONLY_START_CALL_AFTER = "src/OnlyStartCall_after.java";
private static final String JAVA_FILE_ONLY_END_CALL = "src/OnlyEndCall.java";
private static final String JAVA_FILE_ONLY_END_CALL_AFTER = "src/OnlyEndCall_after.java";
private static final String JAVA_FILE_OVERRIDE_METHOD = "src/OverrideMethod.java";
private static final String JAVA_FILE_OVERRIDE_METHOD_AFTER = "src/OverrideMethod_after.java";
private static final String JAVA_FILE_NO_KEYWORD_SUPER = "src/NoKeywordSuper.java";
private static final String JAVA_FILE_NO_KEYWORD_SUPER_AFTER = "src/NoKeywordSuper_after.java";
private static final String JAVA_FILE_CONFLICT_METHOD_NAME = "src/ConflictGetActionMethodName.java";
private static final String JAVA_FILE_CONFLICT_METHOD_NAME_AFTER = "src/ConflictGetActionMethodName_after.java";
@Override
protected boolean providesCustomManifest() {
return true;
}
@Override
public void setUp() throws Exception {
super.setUp();
myFixture.setTestDataPath(BASE_PATH);
TemplateManagerImpl.setTemplateTesting(getProject(), getTestRootDisposable());
// Set an empty temporary code style. Otherwise, the variable name will follow users' custom setting.
CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(myFixture.getProject());
codeStyleSettings.clearCodeStyleSettings();
CodeStyleSettingsManager.getInstance().setTemporarySettings(codeStyleSettings);
}
@Override
public void tearDown() throws Exception {
try {
CodeStyleSettingsManager.getInstance().dropTemporarySettings();
}
finally {
super.tearDown();
}
}
public void testClassNotInheritActivity() throws Exception {
doEnableTest(JAVA_FILE_NOT_ACTIVITY, ApiCreator.InsertStatusCode.JAVA_FILE_WITHOUT_ACTIVITY);
}
public void testHasAppIndexingApiStartAndEnd() throws Exception {
doEnableTest(JAVA_FILE_HAS_APP_INDEXING_START_AND_END, ApiCreator.InsertStatusCode.DEPENDENCY_MISSING);
}
/**
* The app indexing APIs are used in special statements,
* such as block statements, declaration statements and loop statement.
*/
public void testSpecialStatement() throws Exception {
doEnableTest(JAVA_FILE_SPECIAL_STATEMENT, ApiCreator.InsertStatusCode.DEPENDENCY_MISSING);
}
public void testEmptyActivity() throws Exception {
doJavaFileTest(JAVA_FILE_EMPTY_ACTIVITY, JAVA_FILE_EMPTY_ACTIVITY_AFTER);
}
public void testConflictImports() throws Exception {
doJavaFileTest(JAVA_FILE_CONFLICT_IMPORTS, JAVA_FILE_CONFLICT_IMPORTS_AFTER);
}
public void testWildCardImports() throws Exception {
doJavaFileTest(JAVA_FILE_WILD_CARD_IMPORTS, JAVA_FILE_WILD_CARD_IMPORTS_AFTER);
}
public void testOnlyAppIndexingStart() throws Exception {
doJavaFileTest(JAVA_FILE_ONLY_START_CALL, JAVA_FILE_ONLY_START_CALL_AFTER);
}
public void testOnlyAppIndexingUpdate() throws Exception {
doJavaFileTest(JAVA_FILE_ONLY_UPDATE_CALL, JAVA_FILE_ONLY_UPDATE_CALL_AFTER);
}
public void testBothCallsInOnStart() throws Exception {
doJavaFileTest(JAVA_FILE_BOTH_CALL_ON_START, JAVA_FILE_BOTH_CALL_ON_START_AFTER);
}
public void testOnlyAppIndexingEnd() throws Exception {
doJavaFileTest(JAVA_FILE_ONLY_END_CALL, JAVA_FILE_ONLY_END_CALL_AFTER);
}
public void testWithOverridingMethod() throws Exception {
doJavaFileTest(JAVA_FILE_OVERRIDE_METHOD, JAVA_FILE_OVERRIDE_METHOD_AFTER);
}
/**
* Tests class with overriding method but not invoking overridden method through keyword super
*/
public void testNoKeywordSuper() throws Exception {
doJavaFileTest(JAVA_FILE_NO_KEYWORD_SUPER, JAVA_FILE_NO_KEYWORD_SUPER_AFTER);
}
/**
* Tests class with conflicted method name.
*/
public void testConflictMethodName() throws Exception {
doJavaFileTest(JAVA_FILE_CONFLICT_METHOD_NAME, JAVA_FILE_CONFLICT_METHOD_NAME_AFTER);
}
/**
* Tests whether the project is eligible for App Indexing API code inserting.
*
* @param filePath The path of the Java source file.
* @param enable If the creator (generator / intention) should be enabled.
*/
private void doEnableTest(@NotNull String filePath, ApiCreator.InsertStatusCode statusCode) {
myFixture.configureByFiles(filePath);
myFixture.copyFileToProject(ANDROID_MANIFEST);
PsiClass activity = getActivityClass(myFixture.getFile());
if (activity == null) {
assertEquals(statusCode, ApiCreator.InsertStatusCode.JAVA_FILE_WITHOUT_ACTIVITY);
return;
}
final ApiCreator creator = new ApiCreator(myFixture.getProject(), myFixture.getFile(), activity);
assertEquals(statusCode, creator.eligibleForInsertingAppIndexingApiCode());
}
/**
* Test for creation in Java source file.
*/
private void doJavaFileTest(@NotNull String testFilePath, @NotNull String expectedFilePath) {
myFixture.configureByFiles(testFilePath);
myFixture.copyFileToProject(ANDROID_MANIFEST);
final ApiCreator creator = new ApiCreator(myFixture.getProject(), myFixture.getFile(), getActivityClass(myFixture.getFile()));
CommandProcessor.getInstance().executeCommand(null, new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
creator.insertAppIndexingApiCodeInJavaFile();
}
});
}
}, null, null);
VirtualFile expectedFile = myFixture.copyFileToProject(expectedFilePath);
PsiFile expectedPsiFile = PsiManager.getInstance(myFixture.getProject()).findFile(expectedFile);
assertNotNull(expectedPsiFile);
String normalizedTestFile = getNormalizedContent(myFixture.getFile()).replace("<caret>", "");
assertEquals(getNormalizedContent(expectedPsiFile), normalizedTestFile);
}
/**
* Normalizes file content: remove java.lang imports, comments, white space and line terminators.
*
* @param file The file to normalize.
* @return The normalized content.
*/
protected static String getNormalizedContent(@NotNull final PsiFile file) {
StringBuilder stringBuilder = new StringBuilder();
// Removes line terminators.
Iterable<String> lines = Splitter.onPattern("\r?\n").omitEmptyStrings().split(file.getText());
for (String line : lines) {
line = CharMatcher.WHITESPACE.removeFrom(line);
// Removes import statements of classes from java.lang package if it is a Java file.
// Because Android Studio will automatically import them.
if (line.startsWith("importjava.lang.")) {
continue;
}
stringBuilder.append(line);
}
return stringBuilder.toString();
}
@NotNull
protected String getGradleRelativePath() {
StringBuilder builder = new StringBuilder();
String projectPath = myFixture.getProject().getBasePath();
while (projectPath != null && projectPath.contains("/")) {
projectPath = projectPath.substring(0, projectPath.lastIndexOf("/"));
builder.append("../");
}
builder.deleteCharAt(builder.length() - 1);
String moduleFilePath = myModule.getModuleFilePath();
builder.append(moduleFilePath.substring(0, moduleFilePath.lastIndexOf("/")));
builder.append("/build.gradle");
return builder.toString();
}
/**
* Gets the activity class contained by the psi file.
*
* @param file psi file which may contain activity class
* @return the first class which is inherited from CLASS_ACTIVITY
*/
@Nullable
static PsiClass getActivityClass(@NotNull PsiFile file) {
if (!(file instanceof PsiJavaFile)) return null;
PsiClass[] classes = ((PsiJavaFile)file).getClasses();
for (PsiClass candidate : classes) {
if (InheritanceUtil.isInheritor(candidate, SdkConstants.CLASS_ACTIVITY)) {
return candidate;
}
}
return null;
}
}