add android completion tests

- add android plugin test classes as test library
- add base android test cases for kotlin, setting up android facet and sdk
This commit is contained in:
Mikhail Mutcianko
2014-08-29 20:51:44 +04:00
committed by Yan Zhulanow
parent 08245ffe1c
commit adbe9afd4a
23 changed files with 908 additions and 54 deletions
+9
View File
@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="android_tests">
<CLASSES>
<root url="jar://$PROJECT_DIR$/dependencies/android_tests.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
+2 -1
View File
@@ -8,6 +8,7 @@
<excludeFolder url="file://$MODULE_DIR$/android.tests.dependencies" />
<excludeFolder url="file://$MODULE_DIR$/dependencies" />
<excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/ideaSDK/androidSDK" />
<excludeFolder url="file://$MODULE_DIR$/ideaSDK/config" />
<excludeFolder url="file://$MODULE_DIR$/ideaSDK/config-idea" />
<excludeFolder url="file://$MODULE_DIR$/ideaSDK/system" />
@@ -45,4 +46,4 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
</module>
@@ -18,6 +18,10 @@ package org.jetbrains.jet.lang.resolve.android
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiElement
import com.intellij.openapi.project.Project
import org.jetbrains.jet.lang.psi.JetFile
import com.intellij.openapi.components.ServiceManager
import java.util.ArrayList
trait AndroidResource
@@ -44,4 +48,11 @@ fun isAndroidSyntheticFile(f: PsiFile?): Boolean {
public fun isAndroidSyntheticElement(element: PsiElement?): Boolean {
return isAndroidSyntheticFile(element?.getContainingFile())
}
}
public fun searchAndAddAndroidDeclarations(project: Project, originalFiles: Collection<JetFile>): Collection<JetFile> {
val parser = ServiceManager.getService<AndroidUIXmlProcessor>(project, javaClass<AndroidUIXmlProcessor>())
val file = parser?.parseToPsi(project)
val files = ArrayList(originalFiles)
if (file != null) files.add(file)
return files
}
@@ -34,6 +34,11 @@ import org.jetbrains.kotlin.analyzer.ModuleContent
import org.jetbrains.kotlin.di.InjectorForLazyResolveWithJava
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.jet.lang.psi.JetFile
import com.intellij.openapi.components.ServiceManager
import org.jetbrains.jet.lang.resolve.android.AndroidUIXmlProcessor
import java.util.ArrayList
import org.jetbrains.jet.lang.resolve.android.searchAndAddAndroidDeclarations
public class JvmResolverForModule(
override val lazyResolveSession: ResolveSession,
@@ -56,8 +61,10 @@ public object JvmAnalyzerFacade : AnalyzerFacade<JvmResolverForModule, JvmPlatfo
resolverForProject: ResolverForProject<M, JvmResolverForModule>
): JvmResolverForModule {
val (syntheticFiles, moduleContentScope) = moduleContent
val filesToAnalyze = searchAndAddAndroidDeclarations(project, syntheticFiles)
val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
project, globalContext.storageManager, syntheticFiles, if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope
project, globalContext.storageManager, filesToAnalyze,
if (moduleInfo.isLibrary) GlobalSearchScope.EMPTY_SCOPE else moduleContentScope
)
val moduleClassResolver = ModuleClassResolverImpl { javaClass ->
@@ -17,13 +17,11 @@
package org.jetbrains.kotlin.resolve.jvm;
import com.google.common.base.Predicate;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.android.AndroidUIXmlProcessor;
import org.jetbrains.kotlin.analyzer.AnalysisResult;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.context.GlobalContext;
@@ -174,11 +172,4 @@ public enum TopDownAnalyzerFacadeForJVM {
module.seal();
return module;
}
private static Collection<JetFile> searchAndAddAndroidDeclarations(Project project, Collection<JetFile> files) {
AndroidUIXmlProcessor parser = ServiceManager.getService(project, AndroidUIXmlProcessor.class);
JetFile file = parser.parseToPsi(project);
if (file != null) files.add(file);
return files;
}
}
@@ -145,6 +145,7 @@ import org.jetbrains.kotlin.idea.highlighter.*
import org.jetbrains.jet.lang.resolve.android.AbstractAndroidXml2KConversionTest
import org.jetbrains.jet.android.AbstractCrossParserTest
import org.jetbrains.jet.lang.resolve.android.AbstractAndroidBoxTest
import org.jetbrains.jet.android.AbstractAndroidCompletionTest
fun main(args: Array<String>) {
System.setProperty("java.awt.headless", "true")
@@ -710,6 +711,10 @@ fun main(args: Array<String>) {
testClass<AbstractJvmBasicCompletionTest>("org.jetbrains.kotlin.idea.kdoc.KDocCompletionTestGenerated") {
model("kdoc/completion")
}
testClass(javaClass<AbstractAndroidCompletionTest>()) {
model("android/completion", recursive = false, extension = null)
}
}
testGroup("idea/tests", "compiler/testData") {
+2 -1
View File
@@ -39,11 +39,12 @@
<orderEntry type="library" scope="PROVIDED" name="maven" level="project" />
<orderEntry type="module" module-name="eval4j" />
<orderEntry type="module" module-name="frontend.android" />
<orderEntry type="library" name="android" level="project" />
<orderEntry type="library" scope="PROVIDED" name="android" level="project" />
<orderEntry type="module" module-name="idea-analysis" exported="" />
<orderEntry type="module" module-name="kotlin-android-plugin" />
<orderEntry type="module" module-name="js.frontend" />
<orderEntry type="library" scope="PROVIDED" name="coverage-plugin" level="project" />
<orderEntry type="module" module-name="util" />
<orderEntry type="library" name="android_tests" level="project" />
</component>
</module>
@@ -27,14 +27,11 @@ import org.jetbrains.jet.lang.resolve.android.CliAndroidResourceManager
import org.jetbrains.jet.lang.resolve.android.AndroidResourceManager
class IDEAndroidUIXmlProcessor(project: Project) : AndroidUIXmlProcessor(project) {
override val searchPath: String? = if (ApplicationManager.getApplication()!!.isUnitTestMode()) project.getUserData(TestConst.TESTDATA_PATH) + "layout/"
else project.getBasePath() + "/res/layout/"
override val searchPath: String? = project.getBasePath() + "/res/layout/"
override var androidAppPackage: String = ""
get() = resourceManager.readManifest()._package
override val resourceManager: AndroidResourceManager = if (ApplicationManager.getApplication()!!.isUnitTestMode())
CliAndroidResourceManager(project, searchPath, project.getUserData(TestConst.TESTDATA_PATH) + "AndroidManifest.xml")
else IDEAndroidResourceManager(project, searchPath)
override val resourceManager: AndroidResourceManager = IDEAndroidResourceManager(project, searchPath)
override fun parseSingleFileImpl(file: PsiFile): String {
val ids: MutableCollection<AndroidWidget> = ArrayList()
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.myapp"
android:versionCode="1"
android:versionName="1.0" >
</manifest>
@@ -1,33 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.myapp"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-sdk
android:minSdkVersion="18"
android:targetSdkVersion="18" />
<permission android:name="android"></permission>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Sample" >
<activity
android:name="com.example.android.basiccontactables.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop">
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,10 @@
package com.myapp
import android.app.Activity
class MyActivity: Activity() {
val button = this.MyBu<caret>
}
// EXIST: MyButton
@@ -0,0 +1,15 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<view
class="org.my.cool.Button"
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,10 @@
package com.myapp
import android.app.Activity
class MyActivity: Activity() {
val button = this.MyBu<caret>
}
// EXIST: MyButton
@@ -0,0 +1,14 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<org.my.cool.Button
android:id="@+id/MyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,10 @@
package com.myapp
import android.app.Activity
class MyActivity: Activity() {
val button = this.log<caret>
}
// EXIST: login, loginButton
@@ -0,0 +1,29 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -0,0 +1,29 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ItemDetailActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:id="@+id/passwordField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter your password" />
<EditText
android:id="@+id/passwordCaption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword" />
<Button
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in" />
</FrameLayout>
@@ -1,10 +1,9 @@
package com.myapp
trait Activity
trait Button
import android.app.Activity
class MyActivity: Activity {
class MyActivity: Activity() {
val button = this.log<caret>
}
@@ -0,0 +1,107 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* 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.jet.android
import org.jetbrains.jet.plugin.PluginTestCaseBase
import com.intellij.openapi.application.PathManager
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.openapi.util.io.FileUtil
import java.io.File
import org.jetbrains.jet.completion.util.testCompletion
import org.jetbrains.jet.plugin.project.TargetPlatform
import com.intellij.ide.startup.impl.StartupManagerImpl
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
import com.intellij.openapi.startup.StartupManager
import org.jetbrains.jet.JetTestCaseBuilder
import org.jetbrains.jet.plugin.actions.internal.KotlinInternalMode
import com.intellij.psi.impl.PsiManagerEx
import com.intellij.psi.PsiManager
import org.jetbrains.jet.plugin.references.BuiltInsReferenceResolver
import com.android.SdkConstants
import com.intellij.codeInsight.CodeInsightSettings
public abstract class AbstractAndroidCompletionTest : KotlinAndroidTestCase() {
private var kotlinInternalModeOriginalValue: Boolean = false
override fun setUp() {
System.setProperty(KotlinAndroidTestCaseBase.SDK_PATH_PROPERTY, PathManager.getHomePath() + "/androidSDK/")
System.setProperty(KotlinAndroidTestCaseBase.PLATFORM_DIR_PROPERTY, "android-17")
super.setUp()
myFixture!!.setTestDataPath(getTestDataPath())
(StartupManager.getInstance(getProject()) as StartupManagerImpl).runPostStartupActivities()
VfsRootAccess.allowRootAccess(JetTestCaseBuilder.getHomeDirectory())
kotlinInternalModeOriginalValue = KotlinInternalMode.enabled
KotlinInternalMode.enabled = true
setAutoCompleteSetting(false)
}
override fun createManifest() {
myFixture!!.copyFileToProject("idea/testData/android/AndroidManifest.xml", SdkConstants.FN_ANDROID_MANIFEST_XML)
}
override fun tearDown() {
KotlinInternalMode.enabled = kotlinInternalModeOriginalValue
VfsRootAccess.disallowRootAccess(JetTestCaseBuilder.getHomeDirectory())
val builtInsSources = getProject()!!.getComponent<BuiltInsReferenceResolver>(javaClass<BuiltInsReferenceResolver>())!!.getBuiltInsSources()!!
val fileManager = (PsiManager.getInstance(getProject()!!) as PsiManagerEx).getFileManager()
super.tearDown()
// Restore mapping between PsiFiles and VirtualFiles dropped in FileManager.cleanupForNextTest(),
// otherwise built-ins psi elements will become invalid in next test.
for (source in builtInsSources) {
val provider = source.getViewProvider()
fileManager.setViewProvider(provider.getVirtualFile(), provider)
}
}
private fun setAutoCompleteSetting(value: Boolean): Boolean {
val settings = CodeInsightSettings.getInstance()
val oldValue: Boolean
if (completionType() == CompletionType.SMART) {
oldValue = settings.AUTOCOMPLETE_ON_SMART_TYPE_COMPLETION
settings.AUTOCOMPLETE_ON_SMART_TYPE_COMPLETION = value
}
else {
oldValue = settings.AUTOCOMPLETE_COMMON_PREFIX
settings.AUTOCOMPLETE_ON_CODE_COMPLETION = value
}
return oldValue
}
private fun completionType() = CompletionType.BASIC
fun doTest(testPath: String?) {
myFixture!!.copyDirectoryToProject("res/", "res")
myFixture!!.configureByFile(testPath!! + getTestName(true) + ".kt");
val fileText = FileUtil.loadFile(File(testPath + getTestName(true) + ".kt"), true)
testCompletion(fileText, TargetPlatform.JVM, {
count -> myFixture!!.complete(completionType())
})
}
override fun getTestDataPath(): String {
return PluginTestCaseBase.getTestDataPathBase() + "/android/completion/" + getTestName(true) + "/"
}
override fun requireRecentSdk() = true
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* 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.jet.android;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.TestMetadata;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/android/completion")
public class AndroidCompletionTestGenerated extends AbstractAndroidCompletionTest {
public void SKIPtestAllFilesPresentInCompletion() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/android/completion"), Pattern.compile("^([^\\.]+)$"), false);
}
@TestMetadata("fqNameInAttr")
public void testFqNameInAttr() throws Exception {
doTest("idea/testData/android/completion/fqNameInAttr/");
}
@TestMetadata("fqNameInTag")
public void testFqNameInTag() throws Exception {
doTest("idea/testData/android/completion/fqNameInTag/");
}
@TestMetadata("multiFile")
public void testMultiFile() throws Exception {
doTest("idea/testData/android/completion/multiFile/");
}
@TestMetadata("propertiesSimple")
public void testPropertiesSimple() throws Exception {
doTest("idea/testData/android/completion/propertiesSimple/");
}
}
@@ -0,0 +1,270 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.jet.android;
import com.android.SdkConstants;
import com.android.ide.common.rendering.RenderSecurityManager;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInspection.GlobalInspectionTool;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.ex.GlobalInspectionContextImpl;
import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper;
import com.intellij.codeInspection.ex.InspectionManagerEx;
import com.intellij.facet.FacetManager;
import com.intellij.facet.ModifiableFacetModel;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.InspectionTestUtil;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
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 com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"})
public abstract class KotlinAndroidTestCase extends KotlinAndroidTestCaseBase {
protected Module myModule;
protected List<Module> myAdditionalModules;
private boolean myCreateManifest;
protected AndroidFacet myFacet;
public KotlinAndroidTestCase(boolean createManifest) {
this.myCreateManifest = createManifest;
}
public KotlinAndroidTestCase() {
this(true);
}
public static void SHIT() {}
@Override
public void setUp() throws Exception {
super.setUp();
// this will throw an exception if we don't have a full Android SDK, so we need to do this first thing before any other setup
String sdkPath = getTestSdkPath();
final TestFixtureBuilder<IdeaProjectTestFixture> projectBuilder =
IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getName());
myFixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(projectBuilder.getFixture());
final JavaModuleFixtureBuilder moduleFixtureBuilder = projectBuilder.addModule(JavaModuleFixtureBuilder.class);
final String dirPath = myFixture.getTempDirPath() + getContentRootPath();
final File dir = new File(dirPath);
if (!dir.exists()) {
assertTrue(dir.mkdirs());
}
tuneModule(moduleFixtureBuilder, dirPath);
final ArrayList<MyAdditionalModuleData> modules = new ArrayList<MyAdditionalModuleData>();
configureAdditionalModules(projectBuilder, modules);
myFixture.setUp();
myFixture.setTestDataPath(getTestDataPath());
myModule = moduleFixtureBuilder.getFixture().getModule();
// Must be done before addAndroidFacet, and must always be done, even if !myCreateManifest.
// We will delete it at the end of setUp; this is needed when unit tests want to rewrite
// the manifest on their own.
createManifest();
myFacet = addAndroidFacet(myModule, sdkPath, getPlatformDir(), isToAddSdk());
myFixture.copyDirectoryToProject(getResDir(), "res");
myAdditionalModules = new ArrayList<Module>();
for (MyAdditionalModuleData data : modules) {
final Module additionalModule = data.myModuleFixtureBuilder.getFixture().getModule();
myAdditionalModules.add(additionalModule);
final AndroidFacet facet = addAndroidFacet(additionalModule, sdkPath, getPlatformDir());
facet.setLibraryProject(data.myLibrary);
final String rootPath = getContentRootPath(data.myDirName);
myFixture.copyDirectoryToProject("res", rootPath + "/res");
myFixture.copyFileToProject(SdkConstants.FN_ANDROID_MANIFEST_XML,
rootPath + '/' + SdkConstants.FN_ANDROID_MANIFEST_XML);
ModuleRootModificationUtil.addDependency(myModule, additionalModule);
}
if (!myCreateManifest) {
deleteManifest();
}
if (RenderSecurityManager.RESTRICT_READS) {
// Unit test class loader includes disk directories which security manager does not allow access to
RenderSecurityManager.sEnabled = false;
}
}
protected boolean isToAddSdk() {
return true;
}
protected String getContentRootPath() {
return "";
}
protected void configureAdditionalModules(@NotNull TestFixtureBuilder<IdeaProjectTestFixture> projectBuilder,
@NotNull List<MyAdditionalModuleData> modules) {
}
protected void addModuleWithAndroidFacet(@NotNull TestFixtureBuilder<IdeaProjectTestFixture> projectBuilder,
@NotNull List<MyAdditionalModuleData> modules,
@NotNull String dirName,
boolean library) {
final JavaModuleFixtureBuilder moduleFixtureBuilder = projectBuilder.addModule(JavaModuleFixtureBuilder.class);
final String moduleDirPath = myFixture.getTempDirPath() + getContentRootPath(dirName);
//noinspection ResultOfMethodCallIgnored
new File(moduleDirPath).mkdirs();
tuneModule(moduleFixtureBuilder, moduleDirPath);
modules.add(new MyAdditionalModuleData(moduleFixtureBuilder, dirName, library));
}
protected static String getContentRootPath(@NotNull String moduleName) {
return "/additionalModules/" + moduleName;
}
protected String getResDir() {
return "res";
}
public static void tuneModule(JavaModuleFixtureBuilder moduleBuilder, String moduleDirPath) {
moduleBuilder.addContentRoot(moduleDirPath);
//noinspection ResultOfMethodCallIgnored
new File(moduleDirPath + "/src/").mkdir();
moduleBuilder.addSourceRoot("src");
//noinspection ResultOfMethodCallIgnored
new File(moduleDirPath + "/gen/").mkdir();
moduleBuilder.addSourceRoot("gen");
}
protected void createManifest() throws IOException {
myFixture.copyFileToProject(SdkConstants.FN_ANDROID_MANIFEST_XML, SdkConstants.FN_ANDROID_MANIFEST_XML);
}
protected void createProjectProperties() throws IOException {
myFixture.copyFileToProject(SdkConstants.FN_PROJECT_PROPERTIES, SdkConstants.FN_PROJECT_PROPERTIES);
}
protected void deleteManifest() throws IOException {
deleteManifest(myModule);
}
protected void deleteManifest(final Module module) throws IOException {
final AndroidFacet facet = AndroidFacet.getInstance(module);
assertNotNull(facet);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
String manifestRelativePath = facet.getProperties().MANIFEST_FILE_RELATIVE_PATH;
VirtualFile manifest = AndroidRootUtil.getFileByRelativeModulePath(module, manifestRelativePath, true);
if (manifest != null) {
try {
manifest.delete(this);
}
catch (IOException e) {
fail("Could not delete default manifest");
}
}
}
});
}
@Override
public void tearDown() throws Exception {
myModule = null;
myAdditionalModules = null;
myFixture.tearDown();
myFixture = null;
myFacet = null;
if (RenderSecurityManager.RESTRICT_READS) {
RenderSecurityManager.sEnabled = true;
}
super.tearDown();
}
public AndroidFacet addAndroidFacet(Module module, String sdkPath, String platformDir) {
return addAndroidFacet(module, sdkPath, platformDir, true);
}
public AndroidFacet addAndroidFacet(Module module, String sdkPath, String platformDir, boolean addSdk) {
FacetManager facetManager = FacetManager.getInstance(module);
AndroidFacet facet = facetManager.createFacet(AndroidFacet.getFacetType(), "Android", null);
if (addSdk) {
addAndroidSdk(module, sdkPath, platformDir);
}
final ModifiableFacetModel facetModel = facetManager.createModifiableModel();
facetModel.addFacet(facet);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
facetModel.commit();
}
});
return facet;
}
protected void doGlobalInspectionTest(@NotNull GlobalInspectionTool inspection,
@NotNull String globalTestDir,
@NotNull AnalysisScope scope) {
doGlobalInspectionTest(new GlobalInspectionToolWrapper(inspection), globalTestDir, scope);
}
protected void doGlobalInspectionTest(@NotNull GlobalInspectionToolWrapper wrapper,
@NotNull String globalTestDir,
@NotNull AnalysisScope scope) {
myFixture.enableInspections(wrapper.getTool());
scope.invalidate();
final InspectionManagerEx inspectionManager = (InspectionManagerEx)InspectionManager.getInstance(getProject());
final GlobalInspectionContextImpl globalContext =
CodeInsightTestFixtureImpl.createGlobalContextForTool(scope, getProject(), inspectionManager, wrapper);
InspectionTestUtil.runTool(wrapper, scope, globalContext, inspectionManager);
InspectionTestUtil.compareToolResults(globalContext, wrapper, false, getTestDataPath() + globalTestDir);
}
protected static class MyAdditionalModuleData {
final JavaModuleFixtureBuilder myModuleFixtureBuilder;
final String myDirName;
final boolean myLibrary;
private MyAdditionalModuleData(@NotNull JavaModuleFixtureBuilder moduleFixtureBuilder,
@NotNull String dirName,
boolean library) {
myModuleFixtureBuilder = moduleFixtureBuilder;
myDirName = dirName;
myLibrary = library;
}
}
}
@@ -0,0 +1,312 @@
/*
* 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.jet.android;
import com.android.sdklib.IAndroidTarget;
import com.android.tools.idea.rendering.ResourceHelper;
import com.intellij.openapi.application.PathManager;
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.vfs.JarFileSystem;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
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 org.jetbrains.jet.plugin.PluginTestCaseBase;
import java.io.File;
@SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"})
public abstract class KotlinAndroidTestCaseBase 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" */
public static final String PLATFORM_DIR_PROPERTY = "ADT_TEST_PLATFORM";
protected JavaCodeInsightTestFixture myFixture;
protected KotlinAndroidTestCaseBase() {
IdeaTestCase.initPlatformPrefix();
}
public 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 String getTestDataPath() {
return PluginTestCaseBase.getTestDataPathBase();
}
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 new File(PathManager.getHomePath(), "android/android").getPath();
}
public String getDefaultTestSdkPath() {
return getTestDataPath() + "/sdk1.5";
}
public String getDefaultPlatformDir() {
return "android-1.5";
}
protected String getTestSdkPath() {
if (requireRecentSdk()) {
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;
}
fail("This unit test requires " + SDK_PATH_PROPERTY + " and " + PLATFORM_DIR_PROPERTY + " to be defined.");
}
return getDefaultTestSdkPath();
}
protected String getPlatformDir() {
if (requireRecentSdk()) {
String override = System.getProperty(PLATFORM_DIR_PROPERTY);
if (override != null) {
return override;
}
override = System.getenv(PLATFORM_DIR_PROPERTY);
if (override != null) {
return override;
}
fail("This unit test requires " + SDK_PATH_PROPERTY + " and " + PLATFORM_DIR_PROPERTY + " to be defined.");
}
return getDefaultPlatformDir();
}
/** Is the bundled (incomplete) SDK install adequate or do we need to find a valid install? */
protected boolean requireRecentSdk() {
return false;
}
protected void addAndroidSdk(Module module, String sdkPath, String platformDir) {
Sdk androidSdk = createAndroidSdk(sdkPath, platformDir);
ModuleRootModificationUtil.setModuleSdk(module, androidSdk);
}
public 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 = (AndroidSdkAdditionalData)androidSdk.getSdkAdditionalData();
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');
}
}