blob: 250bdace82a660636dceca81cebc396fb8cf2e42 [file] [log] [blame]
/*
* Copyright (C) 2013 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 org.jetbrains.android;
import com.android.sdklib.IAndroidTarget;
import com.android.tools.idea.rendering.ResourceHelper;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.roots.JavadocOrderRootType;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.testFramework.IdeaTestCase;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture;
import org.jetbrains.android.dom.wrappers.LazyValueResourceElementWrapper;
import org.jetbrains.android.sdk.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import static org.jetbrains.android.sdk.AndroidSdkUtils.getAndroidSdkAdditionalData;
@SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"})
public abstract class AndroidTestBase extends UsefulTestCase {
/** Environment variable or system property containing the full path to an SDK install */
public static final String SDK_PATH_PROPERTY = "ADT_TEST_SDK_PATH";
/** Environment variable or system property pointing to the directory name of the platform inside $sdk/platforms, e.g. "android-17" */
private static final String PLATFORM_DIR_PROPERTY = "ADT_TEST_PLATFORM";
protected JavaCodeInsightTestFixture myFixture;
protected AndroidTestBase() {
IdeaTestCase.initPlatformPrefix();
// IDEA14 seems to be stricter regarding validating accesses against known roots. By default, it contains the entire idea folder,
// but it doesn't seem to include our custom structure tools/idea/../adt/idea where the android plugin is placed.
// The following line explicitly adds that folder as an allowed root.
VfsRootAccess.allowRootAccess(FileUtil.toCanonicalPath(getAndroidPluginHome()));
}
public static String getAbsoluteTestDataPath() {
// The following code doesn't work right now that the Android
// plugin lives in a separate place:
//String androidHomePath = System.getProperty("android.home.path");
//if (androidHomePath == null) {
// androidHomePath = new File(PathManager.getHomePath(), "android/android").getPath();
//}
//return PathUtil.getCanonicalPath(androidHomePath + "/testData");
// getTestDataPath already gives the absolute path anyway:
String path = getTestDataPath();
assertTrue(new File(path).isAbsolute());
return path;
}
public static String getTestDataPath() {
return getAndroidPluginHome() + "/testData";
}
public static String getAndroidPluginHome() {
// Now that the Android plugin is kept in a separate place, we need to look in
// a relative position instead
String adtPath = PathManager.getHomePath() + "/../adt/idea/android";
if (new File(adtPath).exists()) {
return adtPath;
}
return PathManagerEx.findFileUnderCommunityHome("android/android").getPath();
}
public static String getDefaultTestSdkPath() {
return getTestDataPath() + "/sdk1.5";
}
public static String getDefaultPlatformDir() {
return "android-1.5";
}
protected String getTestSdkPath() {
if (requireRecentSdk()) {
String override = getRecentSdkPath();
if (override != null) {
return override;
}
fail("This unit test requires " + SDK_PATH_PROPERTY + " and " + PLATFORM_DIR_PROPERTY + " to be defined.");
}
return getDefaultTestSdkPath();
}
@Nullable
public static String getRecentSdkPath() {
String override = System.getProperty(SDK_PATH_PROPERTY);
if (override != null) {
assertTrue("Must also define " + PLATFORM_DIR_PROPERTY, System.getProperty(PLATFORM_DIR_PROPERTY) != null);
assertTrue(override, new File(override).exists());
return override;
}
override = System.getenv(SDK_PATH_PROPERTY);
if (override != null) {
assertTrue("Must also define " + PLATFORM_DIR_PROPERTY, System.getenv(PLATFORM_DIR_PROPERTY) != null);
return override;
}
return null;
}
protected String getPlatformDir() {
if (requireRecentSdk()) {
String override = getRecentPlatformDir();
if (override != null) {
return override;
}
fail("This unit test requires " + SDK_PATH_PROPERTY + " and " + PLATFORM_DIR_PROPERTY + " to be defined.");
}
return getDefaultPlatformDir();
}
@Nullable
public static String getRecentPlatformDir() {
String override = System.getProperty(PLATFORM_DIR_PROPERTY);
if (override != null) {
return override;
}
override = System.getenv(PLATFORM_DIR_PROPERTY);
if (override != null) {
return override;
}
return null;
}
/** Is the bundled (incomplete) SDK install adequate or do we need to find a valid install? */
protected boolean requireRecentSdk() {
return false;
}
protected static void addAndroidSdk(Module module, String sdkPath, String platformDir) {
Sdk androidSdk = createAndroidSdk(sdkPath, platformDir);
ModuleRootModificationUtil.setModuleSdk(module, androidSdk);
}
public static Sdk createAndroidSdk(String sdkPath, String platformDir) {
Sdk sdk = ProjectJdkTable.getInstance().createSdk("android_test_sdk", AndroidSdkType.getInstance());
SdkModificator sdkModificator = sdk.getSdkModificator();
sdkModificator.setHomePath(sdkPath);
VirtualFile androidJar;
if (platformDir.equals(getDefaultPlatformDir())) {
// Compatibility: the unit tests were using android.jar outside the sdk1.5 install;
// we need to use that one, rather than the real one in sdk1.5, in order for the
// tests to pass. Longer term, we should switch the unit tests over to all using
// a valid SDK.
String androidJarPath = sdkPath + "/../android.jar!/";
androidJar = JarFileSystem.getInstance().findFileByPath(androidJarPath);
} else {
androidJar = LocalFileSystem.getInstance().findFileByPath(sdkPath + "/platforms/" + platformDir + "/android.jar");
}
sdkModificator.addRoot(androidJar, OrderRootType.CLASSES);
VirtualFile resFolder = LocalFileSystem.getInstance().findFileByPath(sdkPath + "/platforms/" + platformDir + "/data/res");
sdkModificator.addRoot(resFolder, OrderRootType.CLASSES);
VirtualFile docsFolder = LocalFileSystem.getInstance().findFileByPath(sdkPath + "/docs/reference");
if (docsFolder != null) {
sdkModificator.addRoot(docsFolder, JavadocOrderRootType.getInstance());
}
AndroidSdkAdditionalData data = new AndroidSdkAdditionalData(sdk);
AndroidSdkData sdkData = AndroidSdkData.getSdkData(sdkPath);
assertNotNull(sdkData);
IAndroidTarget target = sdkData.findTargetByName("Android 4.2"); // TODO: Get rid of this hardcoded version number
if (target == null) {
IAndroidTarget[] targets = sdkData.getTargets();
for (IAndroidTarget t : targets) {
if (t.getLocation().contains(platformDir)) {
target = t;
break;
}
}
if (target == null && targets.length > 0) {
target = targets[targets.length - 1];
}
}
assertNotNull(target);
data.setBuildTarget(target);
sdkModificator.setSdkAdditionalData(data);
sdkModificator.commitChanges();
return sdk;
}
protected Project getProject() {
return myFixture.getProject();
}
protected void ensureSdkManagerAvailable() {
AndroidSdkData sdkData = AndroidSdkUtils.tryToChooseAndroidSdk();
if (sdkData == null) {
sdkData = createTestSdkManager();
if (sdkData != null) {
AndroidSdkUtils.setSdkData(sdkData);
}
}
assertNotNull(sdkData);
}
@Nullable
protected AndroidSdkData createTestSdkManager() {
Sdk androidSdk = createAndroidSdk(getTestSdkPath(), getPlatformDir());
AndroidSdkAdditionalData data = getAndroidSdkAdditionalData(androidSdk);
if (data != null) {
AndroidPlatform androidPlatform = data.getAndroidPlatform();
if (androidPlatform != null) {
// Put default platforms in the list before non-default ones so they'll be looked at first.
return androidPlatform.getSdkData();
} else {
fail("No getAndroidPlatform() associated with the AndroidSdkAdditionalData: " + data);
}
} else {
fail("Could not find data associated with the SDK: " + androidSdk.getName());
}
return null;
}
/** Returns a description of the given elements, suitable as unit test golden file output */
public static String describeElements(@Nullable PsiElement[] elements) {
if (elements == null) {
return "Empty";
}
StringBuilder sb = new StringBuilder();
for (PsiElement target : elements) {
appendElementDescription(sb, target);
}
return sb.toString();
}
/** Appends a description of the given element, suitable as unit test golden file output */
public static void appendElementDescription(@NotNull StringBuilder sb, @NotNull PsiElement element) {
if (element instanceof LazyValueResourceElementWrapper) {
LazyValueResourceElementWrapper wrapper = (LazyValueResourceElementWrapper)element;
XmlAttributeValue value = wrapper.computeElement();
if (value != null) {
element = value;
}
}
PsiFile file = element.getContainingFile();
int offset = element.getTextOffset();
TextRange segment = element.getTextRange();
appendSourceDescription(sb, file, offset, segment);
}
/** Appends a description of the given elements, suitable as unit test golden file output */
public static void appendSourceDescription(@NotNull StringBuilder sb, @Nullable PsiFile file, int offset, @Nullable Segment segment) {
if (file != null && segment != null) {
if (ResourceHelper.getFolderType(file) != null) {
assertNotNull(file.getParent());
sb.append(file.getParent().getName());
sb.append("/");
}
sb.append(file.getName());
sb.append(':');
String text = file.getText();
int lineNumber = 1;
for (int i = 0; i < offset; i++) {
if (text.charAt(i) == '\n') {
lineNumber++;
}
}
sb.append(lineNumber);
sb.append(":");
sb.append('\n');
int startOffset = segment.getStartOffset();
int endOffset = segment.getEndOffset();
assertTrue(offset == -1 || offset >= startOffset);
assertTrue(offset == -1 || offset <= endOffset);
int lineStart = startOffset;
while (lineStart > 0 && text.charAt(lineStart - 1) != '\n') {
lineStart--;
}
// Skip over leading whitespace
while (lineStart < startOffset && Character.isWhitespace(text.charAt(lineStart))) {
lineStart++;
}
int lineEnd = startOffset;
while (lineEnd < text.length() && text.charAt(lineEnd) != '\n') {
lineEnd++;
}
String indent = " ";
sb.append(indent);
sb.append(text.substring(lineStart, lineEnd));
sb.append('\n');
sb.append(indent);
for (int i = lineStart; i < lineEnd; i++) {
if (i == offset) {
sb.append('|');
} else if (i >= startOffset && i <= endOffset) {
sb.append('~');
} else {
sb.append(' ');
}
}
} else {
sb.append(offset);
sb.append(":?");
}
sb.append('\n');
}
}