203: Fix compilation
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
|
||||
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
|
||||
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
|
||||
abstract public class KotlinDaemonAnalyzerTestCase extends DaemonAnalyzerTestCase {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
VfsRootAccess.allowRootAccess(getTestRootDisposable(), KotlinTestUtils.getHomeDirectory());
|
||||
super.setUp();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.caches.resolve
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.StdModuleTypes
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.testFramework.ModuleTestCase
|
||||
import com.intellij.testFramework.PsiTestUtil
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.idea.caches.project.*
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.ModuleTestSourceInfo
|
||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||
import org.jetbrains.kotlin.idea.framework.platform
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase.*
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.util.addDependency
|
||||
import org.jetbrains.kotlin.test.util.jarRoot
|
||||
import org.jetbrains.kotlin.test.util.projectLibrary
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import org.junit.Assert
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class IdeaModuleInfoTest : ModuleTestCase() {
|
||||
fun testSimpleModuleDependency() {
|
||||
val (a, b) = modules()
|
||||
b.addDependency(a)
|
||||
|
||||
b.production.assertDependenciesEqual(b.production, a.production)
|
||||
UsefulTestCase.assertDoesntContain(a.production.dependencies(), b.production)
|
||||
}
|
||||
|
||||
fun testCircularDependency() {
|
||||
val (a, b) = modules()
|
||||
|
||||
b.addDependency(a)
|
||||
a.addDependency(b)
|
||||
|
||||
a.production.assertDependenciesEqual(a.production, b.production)
|
||||
b.production.assertDependenciesEqual(b.production, a.production)
|
||||
}
|
||||
|
||||
fun testExportedDependency() {
|
||||
val (a, b, c) = modules()
|
||||
|
||||
b.addDependency(a, exported = true)
|
||||
c.addDependency(b)
|
||||
|
||||
a.production.assertDependenciesEqual(a.production)
|
||||
b.production.assertDependenciesEqual(b.production, a.production)
|
||||
c.production.assertDependenciesEqual(c.production, b.production, a.production)
|
||||
}
|
||||
|
||||
fun testRedundantExportedDependency() {
|
||||
val (a, b, c) = modules()
|
||||
|
||||
b.addDependency(a, exported = true)
|
||||
c.addDependency(a)
|
||||
c.addDependency(b)
|
||||
|
||||
a.production.assertDependenciesEqual(a.production)
|
||||
b.production.assertDependenciesEqual(b.production, a.production)
|
||||
c.production.assertDependenciesEqual(c.production, a.production, b.production)
|
||||
}
|
||||
|
||||
fun testCircularExportedDependency() {
|
||||
val (a, b, c) = modules()
|
||||
|
||||
b.addDependency(a, exported = true)
|
||||
c.addDependency(b, exported = true)
|
||||
a.addDependency(c, exported = true)
|
||||
|
||||
a.production.assertDependenciesEqual(a.production, c.production, b.production)
|
||||
b.production.assertDependenciesEqual(b.production, a.production, c.production)
|
||||
c.production.assertDependenciesEqual(c.production, b.production, a.production)
|
||||
}
|
||||
|
||||
fun testSimpleLibDependency() {
|
||||
val a = module("a")
|
||||
val lib = projectLibrary()
|
||||
a.addDependency(lib)
|
||||
|
||||
a.production.assertDependenciesEqual(a.production, lib.classes)
|
||||
}
|
||||
|
||||
fun testCircularExportedDependencyWithLib() {
|
||||
val (a, b, c) = modules()
|
||||
|
||||
val lib = projectLibrary()
|
||||
|
||||
a.addDependency(lib)
|
||||
|
||||
b.addDependency(a, exported = true)
|
||||
c.addDependency(b, exported = true)
|
||||
a.addDependency(c, exported = true)
|
||||
|
||||
b.addDependency(lib)
|
||||
c.addDependency(lib)
|
||||
|
||||
a.production.assertDependenciesEqual(a.production, lib.classes, c.production, b.production)
|
||||
b.production.assertDependenciesEqual(b.production, a.production, c.production, lib.classes)
|
||||
c.production.assertDependenciesEqual(c.production, b.production, a.production, lib.classes)
|
||||
}
|
||||
|
||||
fun testSeveralModulesExportLibs() {
|
||||
val (a, b, c) = modules()
|
||||
|
||||
val lib1 = projectLibrary("lib1")
|
||||
val lib2 = projectLibrary("lib2")
|
||||
|
||||
a.addDependency(lib1, exported = true)
|
||||
b.addDependency(lib2, exported = true)
|
||||
c.addDependency(a)
|
||||
c.addDependency(b)
|
||||
|
||||
c.production.assertDependenciesEqual(c.production, a.production, lib1.classes, b.production, lib2.classes)
|
||||
}
|
||||
|
||||
fun testSeveralModulesExportSameLib() {
|
||||
val (a, b, c) = modules()
|
||||
|
||||
val lib = projectLibrary()
|
||||
|
||||
a.addDependency(lib, exported = true)
|
||||
b.addDependency(lib, exported = true)
|
||||
c.addDependency(a)
|
||||
c.addDependency(b)
|
||||
|
||||
c.production.assertDependenciesEqual(c.production, a.production, lib.classes, b.production)
|
||||
}
|
||||
|
||||
fun testRuntimeDependency() {
|
||||
val (a, b) = modules()
|
||||
|
||||
b.addDependency(a, dependencyScope = DependencyScope.RUNTIME)
|
||||
b.addDependency(projectLibrary(), dependencyScope = DependencyScope.RUNTIME)
|
||||
|
||||
b.production.assertDependenciesEqual(b.production)
|
||||
}
|
||||
|
||||
fun testProvidedDependency() {
|
||||
val (a, b) = modules()
|
||||
val lib = projectLibrary()
|
||||
|
||||
b.addDependency(a, dependencyScope = DependencyScope.PROVIDED)
|
||||
b.addDependency(lib, dependencyScope = DependencyScope.PROVIDED)
|
||||
|
||||
b.production.assertDependenciesEqual(b.production, a.production, lib.classes)
|
||||
}
|
||||
|
||||
fun testSimpleTestDependency() {
|
||||
val (a, b) = modules()
|
||||
b.addDependency(a, dependencyScope = DependencyScope.TEST)
|
||||
|
||||
a.production.assertDependenciesEqual(a.production)
|
||||
a.test.assertDependenciesEqual(a.test, a.production)
|
||||
b.production.assertDependenciesEqual(b.production)
|
||||
b.test.assertDependenciesEqual(b.test, b.production, a.test, a.production)
|
||||
}
|
||||
|
||||
fun testLibTestDependency() {
|
||||
val a = module("a")
|
||||
val lib = projectLibrary()
|
||||
a.addDependency(lib, dependencyScope = DependencyScope.TEST)
|
||||
|
||||
a.production.assertDependenciesEqual(a.production)
|
||||
a.test.assertDependenciesEqual(a.test, a.production, lib.classes)
|
||||
}
|
||||
|
||||
fun testExportedTestDependency() {
|
||||
val (a, b, c) = modules()
|
||||
b.addDependency(a, exported = true)
|
||||
c.addDependency(b, dependencyScope = DependencyScope.TEST)
|
||||
|
||||
c.production.assertDependenciesEqual(c.production)
|
||||
c.test.assertDependenciesEqual(c.test, c.production, b.test, b.production, a.test, a.production)
|
||||
}
|
||||
|
||||
fun testDependents() {
|
||||
//NOTE: we do not differ between dependency kinds
|
||||
val (a, b, c) = modules(name1 = "a", name2 = "b", name3 = "c")
|
||||
val (d, e, f) = modules(name1 = "d", name2 = "e", name3 = "f")
|
||||
|
||||
b.addDependency(a, exported = true)
|
||||
|
||||
c.addDependency(a)
|
||||
|
||||
d.addDependency(c, exported = true)
|
||||
|
||||
e.addDependency(b)
|
||||
|
||||
f.addDependency(d)
|
||||
f.addDependency(e)
|
||||
|
||||
|
||||
a.test.assertDependentsEqual(a.test, b.test, c.test, e.test)
|
||||
a.production.assertDependentsEqual(a.production, a.test, b.production, b.test, c.production, c.test, e.production, e.test)
|
||||
|
||||
b.test.assertDependentsEqual(b.test, e.test)
|
||||
b.production.assertDependentsEqual(b.production, b.test, e.production, e.test)
|
||||
|
||||
|
||||
c.test.assertDependentsEqual(c.test, d.test, f.test)
|
||||
c.production.assertDependentsEqual(c.production, c.test, d.production, d.test, f.production, f.test)
|
||||
|
||||
d.test.assertDependentsEqual(d.test, f.test)
|
||||
d.production.assertDependentsEqual(d.production, d.test, f.production, f.test)
|
||||
|
||||
e.test.assertDependentsEqual(e.test, f.test)
|
||||
e.production.assertDependentsEqual(e.production, e.test, f.production, f.test)
|
||||
|
||||
f.test.assertDependentsEqual(f.test)
|
||||
f.production.assertDependentsEqual(f.production, f.test)
|
||||
}
|
||||
|
||||
fun testLibraryDependency1() {
|
||||
val lib1 = projectLibrary("lib1")
|
||||
val lib2 = projectLibrary("lib2")
|
||||
|
||||
val module = module("module")
|
||||
module.addDependency(lib1)
|
||||
module.addDependency(lib2)
|
||||
|
||||
lib1.classes.assertAdditionalLibraryDependencies(lib2.classes)
|
||||
lib2.classes.assertAdditionalLibraryDependencies(lib1.classes)
|
||||
}
|
||||
|
||||
fun testLibraryDependency2() {
|
||||
val lib1 = projectLibrary("lib1")
|
||||
val lib2 = projectLibrary("lib2")
|
||||
val lib3 = projectLibrary("lib3")
|
||||
|
||||
val (a, b, c) = modules()
|
||||
a.addDependency(lib1)
|
||||
b.addDependency(lib2)
|
||||
c.addDependency(lib3)
|
||||
|
||||
c.addDependency(a)
|
||||
c.addDependency(b)
|
||||
|
||||
lib1.classes.assertAdditionalLibraryDependencies()
|
||||
lib2.classes.assertAdditionalLibraryDependencies()
|
||||
lib3.classes.assertAdditionalLibraryDependencies(lib1.classes, lib2.classes)
|
||||
}
|
||||
|
||||
fun testLibraryDependency3() {
|
||||
val lib1 = projectLibrary("lib1")
|
||||
val lib2 = projectLibrary("lib2")
|
||||
val lib3 = projectLibrary("lib3")
|
||||
|
||||
val (a, b) = modules()
|
||||
a.addDependency(lib1)
|
||||
b.addDependency(lib2)
|
||||
|
||||
a.addDependency(lib3)
|
||||
b.addDependency(lib3)
|
||||
|
||||
lib1.classes.assertAdditionalLibraryDependencies(lib3.classes)
|
||||
lib2.classes.assertAdditionalLibraryDependencies(lib3.classes)
|
||||
lib3.classes.assertAdditionalLibraryDependencies(lib1.classes, lib2.classes)
|
||||
}
|
||||
|
||||
fun testRoots() {
|
||||
val a = module("a", hasProductionRoot = true, hasTestRoot = false)
|
||||
|
||||
val empty = module("empty", hasProductionRoot = false, hasTestRoot = false)
|
||||
a.addDependency(empty)
|
||||
|
||||
val b = module("b", hasProductionRoot = false, hasTestRoot = true)
|
||||
b.addDependency(a)
|
||||
|
||||
val c = module("c")
|
||||
c.addDependency(b)
|
||||
c.addDependency(a)
|
||||
|
||||
assertNotNull(a.productionSourceInfo())
|
||||
assertNull(a.testSourceInfo())
|
||||
|
||||
assertNull(empty.productionSourceInfo())
|
||||
assertNull(empty.testSourceInfo())
|
||||
|
||||
assertNull(b.productionSourceInfo())
|
||||
assertNotNull(b.testSourceInfo())
|
||||
|
||||
b.test.assertDependenciesEqual(b.test, a.production)
|
||||
c.test.assertDependenciesEqual(c.test, c.production, b.test, a.production)
|
||||
c.production.assertDependenciesEqual(c.production, a.production)
|
||||
}
|
||||
|
||||
fun testCommonLibraryDoesNotDependOnPlatform() {
|
||||
val stdlibCommon = stdlibCommon()
|
||||
val stdlibJvm = stdlibJvm()
|
||||
val stdlibJs = stdlibJs()
|
||||
|
||||
val a = module("a")
|
||||
a.addDependency(stdlibCommon)
|
||||
a.addDependency(stdlibJvm)
|
||||
|
||||
val b = module("b")
|
||||
b.addDependency(stdlibCommon)
|
||||
b.addDependency(stdlibJs)
|
||||
|
||||
stdlibCommon.classes.assertAdditionalLibraryDependencies()
|
||||
stdlibJvm.classes.assertAdditionalLibraryDependencies(stdlibCommon.classes)
|
||||
stdlibJs.classes.assertAdditionalLibraryDependencies(stdlibCommon.classes)
|
||||
}
|
||||
|
||||
fun testScriptDependenciesForModule() {
|
||||
val a = module("a")
|
||||
val b = module("b")
|
||||
|
||||
with(createFileInModule(a, "script.kts").moduleInfo) {
|
||||
dependencies().contains(a.production)
|
||||
dependencies().contains(a.test)
|
||||
!dependencies().contains(b.production)
|
||||
}
|
||||
}
|
||||
|
||||
fun testScriptDependenciesForProject() {
|
||||
val a = module("a")
|
||||
|
||||
val script = createFileInProject("script.kts").moduleInfo
|
||||
|
||||
!script.dependencies().contains(a.production)
|
||||
!script.dependencies().contains(a.test)
|
||||
|
||||
script.dependencies().firstIsInstance<ScriptDependenciesInfo.ForFile>()
|
||||
}
|
||||
|
||||
fun testSdkForScript() {
|
||||
// The first known jdk will be used for scripting if there is no jdk in the project
|
||||
runWriteAction {
|
||||
addJdk(testRootDisposable, ::mockJdk6)
|
||||
addJdk(testRootDisposable, ::mockJdk9)
|
||||
|
||||
ProjectRootManager.getInstance(project).projectSdk = null
|
||||
}
|
||||
|
||||
val firstSDK = getProjectJdkTableSafe().allJdks.first()
|
||||
|
||||
with(createFileInProject("script.kts").moduleInfo) {
|
||||
dependencies().filterIsInstance<SdkInfo>().single { it.sdk == firstSDK }
|
||||
}
|
||||
}
|
||||
|
||||
fun testSdkForScriptProjectSdk() {
|
||||
runWriteAction {
|
||||
addJdk(testRootDisposable, ::mockJdk6)
|
||||
addJdk(testRootDisposable, ::mockJdk9)
|
||||
|
||||
ProjectRootManager.getInstance(project).projectSdk = mockJdk9()
|
||||
}
|
||||
|
||||
with(createFileInProject("script.kts").moduleInfo) {
|
||||
dependencies().filterIsInstance<SdkInfo>().single { it.sdk == mockJdk9() }
|
||||
}
|
||||
}
|
||||
|
||||
fun testSdkForScriptModuleSdk() {
|
||||
val a = module("a")
|
||||
|
||||
runWriteAction {
|
||||
addJdk(testRootDisposable, ::mockJdk6)
|
||||
addJdk(testRootDisposable, ::mockJdk9)
|
||||
|
||||
ProjectRootManager.getInstance(project).projectSdk = mockJdk6()
|
||||
with(ModuleRootManager.getInstance(a).modifiableModel) {
|
||||
sdk = mockJdk9()
|
||||
commit()
|
||||
}
|
||||
}
|
||||
|
||||
with(createFileInModule(a, "script.kts").moduleInfo) {
|
||||
dependencies().filterIsInstance<SdkInfo>().first { it.sdk == mockJdk9() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFileInModule(module: Module, fileName: String, inTests: Boolean = false): VirtualFile {
|
||||
val fileToCopyIO = createTempFile(fileName, "")
|
||||
|
||||
for (contentEntry in ModuleRootManager.getInstance(module).contentEntries) {
|
||||
for (sourceFolder in contentEntry.sourceFolders) {
|
||||
if (((!inTests && !sourceFolder.isTestSource) || (inTests && sourceFolder.isTestSource)) && sourceFolder.file != null) {
|
||||
return runWriteAction {
|
||||
getVirtualFile(fileToCopyIO).copy(this, sourceFolder.file!!, fileName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error("Couldn't find source folder in ${module.name}")
|
||||
}
|
||||
|
||||
private fun createFileInProject(fileName: String): VirtualFile {
|
||||
return runWriteAction {
|
||||
getVirtualFile(createTempFile(fileName, "")).copy(this, project.baseDir, fileName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Module.addDependency(
|
||||
other: Module,
|
||||
dependencyScope: DependencyScope = DependencyScope.COMPILE,
|
||||
exported: Boolean = false
|
||||
) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported)
|
||||
|
||||
private val VirtualFile.moduleInfo: IdeaModuleInfo
|
||||
get() {
|
||||
return PsiManager.getInstance(project).findFile(this)!!.getModuleInfo()
|
||||
}
|
||||
|
||||
private val Module.production: ModuleProductionSourceInfo
|
||||
get() = productionSourceInfo()!!
|
||||
|
||||
private val Module.test: ModuleTestSourceInfo
|
||||
get() = testSourceInfo()!!
|
||||
|
||||
private val LibraryEx.classes: LibraryInfo
|
||||
get() = object : LibraryInfo(project!!, this) {
|
||||
override val platform: TargetPlatform
|
||||
get() = kind.platform
|
||||
}
|
||||
|
||||
private fun module(name: String, hasProductionRoot: Boolean = true, hasTestRoot: Boolean = true): Module {
|
||||
return createModuleFromTestData(createTempDirectory().absolutePath, name, StdModuleTypes.JAVA, false).apply {
|
||||
if (hasProductionRoot)
|
||||
PsiTestUtil.addSourceContentToRoots(this, dir(), false)
|
||||
if (hasTestRoot)
|
||||
PsiTestUtil.addSourceContentToRoots(this, dir(), true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dir() = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(createTempDirectory())!!
|
||||
|
||||
private fun modules(name1: String = "a", name2: String = "b", name3: String = "c") = Triple(module(name1), module(name2), module(name3))
|
||||
|
||||
private fun IdeaModuleInfo.assertDependenciesEqual(vararg expected: IdeaModuleInfo) {
|
||||
Assert.assertEquals(expected.toList(), this.dependencies())
|
||||
}
|
||||
|
||||
private fun LibraryInfo.assertAdditionalLibraryDependencies(vararg expected: IdeaModuleInfo) {
|
||||
Assert.assertEquals(this, dependencies().first())
|
||||
val dependenciesWithoutSelf = this.dependencies().drop(1)
|
||||
UsefulTestCase.assertSameElements(dependenciesWithoutSelf, expected.toList())
|
||||
}
|
||||
|
||||
private fun ModuleSourceInfo.assertDependentsEqual(vararg expected: ModuleSourceInfo) {
|
||||
UsefulTestCase.assertSameElements(this.getDependentModules(), expected.toList())
|
||||
}
|
||||
|
||||
private fun stdlibCommon(): LibraryEx = projectLibrary(
|
||||
"kotlin-stdlib-common",
|
||||
ForTestCompileRuntime.stdlibCommonForTests().jarRoot,
|
||||
kind = CommonLibraryKind
|
||||
)
|
||||
|
||||
private fun stdlibJvm(): LibraryEx = projectLibrary("kotlin-stdlib", ForTestCompileRuntime.runtimeJarForTests().jarRoot)
|
||||
|
||||
private fun stdlibJs(): LibraryEx = projectLibrary(
|
||||
"kotlin-stdlib-js",
|
||||
ForTestCompileRuntime.runtimeJarForTests().jarRoot,
|
||||
kind = JSLibraryKind
|
||||
)
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
VfsRootAccess.allowRootAccess(testRootDisposable, KotlinTestUtils.getHomeDirectory())
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -36,12 +36,11 @@ abstract class AbstractConfigureKotlinTest : PlatformTestCase() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
VfsRootAccess.allowRootAccess(KotlinTestUtils.getHomeDirectory())
|
||||
VfsRootAccess.allowRootAccess(testRootDisposable, KotlinTestUtils.getHomeDirectory())
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun tearDown() {
|
||||
VfsRootAccess.disallowRootAccess(KotlinTestUtils.getHomeDirectory())
|
||||
PathMacros.getInstance().removeMacro(TEMP_DIR_MACRO_KEY)
|
||||
|
||||
super.tearDown()
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.repl
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleRunner
|
||||
import org.jetbrains.kotlin.idea.completion.test.KotlinFixtureCompletionBaseTestCase
|
||||
import org.jetbrains.kotlin.idea.completion.test.testCompletion
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractIdeReplCompletionTest : KotlinFixtureCompletionBaseTestCase() {
|
||||
private var consoleRunner: KotlinConsoleRunner? = null
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
consoleRunner = KotlinConsoleKeeper.getInstance(project).run(module)!!
|
||||
ScriptConfigurationManager.updateScriptDependenciesSynchronously(consoleRunner!!.consoleFile)
|
||||
VfsRootAccess.allowRootAccess(testRootDisposable, KotlinTestUtils.getHomeDirectory())
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
consoleRunner?.dispose()
|
||||
consoleRunner = null
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
override fun getPlatform() = JvmPlatforms.unspecifiedJvmPlatform
|
||||
override fun defaultCompletionType() = CompletionType.BASIC
|
||||
|
||||
override fun doTest(testPath: String) {
|
||||
val runner = consoleRunner!!
|
||||
val file = File(testPath)
|
||||
val lines = file.readLines()
|
||||
lines.prefixedWith(">> ").forEach { runner.successfulLine(it) } // not actually executing anything, only simulating
|
||||
val codeSample = lines.prefixedWith("-- ").joinToString("\n")
|
||||
|
||||
runWriteAction {
|
||||
val editor = runner.consoleView.editorDocument
|
||||
editor.setText(codeSample)
|
||||
FileDocumentManager.getInstance().saveDocument(runner.consoleView.editorDocument)
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
}
|
||||
|
||||
myFixture.configureFromExistingVirtualFile(runner.consoleFile.virtualFile)
|
||||
myFixture.editor.caretModel.moveToOffset(myFixture.editor.document.getLineEndOffset(0))
|
||||
|
||||
testCompletion(file.readText(), getPlatform(), { completionType, count -> myFixture.complete(completionType, count) })
|
||||
}
|
||||
|
||||
private fun List<String>.prefixedWith(prefix: String) = filter { it.startsWith(prefix) }.map { it.removePrefix(prefix) }
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor = FullJdkProjectDescriptor
|
||||
}
|
||||
|
||||
private object FullJdkProjectDescriptor : KotlinWithJdkAndRuntimeLightProjectDescriptor() {
|
||||
override fun getSdk() = PluginTestCaseBase.fullJdk()
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.repl
|
||||
|
||||
import com.intellij.execution.console.LanguageConsoleImpl
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.testFramework.PlatformTestCase
|
||||
import com.intellij.util.ThrowableRunnable
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleRunner
|
||||
import org.jetbrains.kotlin.idea.run.createLibraryWithLongPaths
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.jetbrains.kotlin.test.WithMutedInDatabaseRunTest
|
||||
import org.jetbrains.kotlin.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
@WithMutedInDatabaseRunTest
|
||||
class IdeReplExecutionTest : PlatformTestCase() {
|
||||
private lateinit var consoleRunner: KotlinConsoleRunner
|
||||
private var commandsSent = 0
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
consoleRunner = KotlinConsoleKeeper.getInstance(project).run(module)!!
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
consoleRunner.dispose()
|
||||
(this::consoleRunner as KMutableProperty0<KotlinConsoleRunner?>).set(null)
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
override fun runTestRunnable(testRunnable: ThrowableRunnable<Throwable>) {
|
||||
runTest {
|
||||
super.runTestRunnable(testRunnable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendCommand(command: String) {
|
||||
runWriteAction { consoleRunner.consoleView.editorDocument.setText(command) }
|
||||
consoleRunner.executor.executeCommand()
|
||||
commandsSent++
|
||||
}
|
||||
|
||||
private fun checkOutput(expectedOutput: String) {
|
||||
val output = getReplOutput(textOnTimeOut = {
|
||||
"Only ${consoleRunner.commandHistory.processedEntriesCount} commands were processed"
|
||||
}) { consoleRunner.commandHistory.processedEntriesCount >= commandsSent }
|
||||
assertTrue(output.trim().endsWith(expectedOutput), "'$expectedOutput' should be printed but document text is:\n$output")
|
||||
}
|
||||
|
||||
private fun getReplOutput(
|
||||
maxIterations: Int = 50,
|
||||
sleepTime: Long = 500,
|
||||
textOnTimeOut: () -> String,
|
||||
predicate: () -> Boolean
|
||||
): String {
|
||||
for (i in 1..maxIterations) {
|
||||
UIUtil.dispatchAllInvocationEvents()
|
||||
if (predicate()) {
|
||||
return refreshAndGetHistoryEditorText()
|
||||
}
|
||||
Thread.sleep(sleepTime)
|
||||
}
|
||||
|
||||
return textOnTimeOut()
|
||||
}
|
||||
|
||||
private fun refreshAndGetHistoryEditorText(): String {
|
||||
val consoleView = consoleRunner.consoleView as LanguageConsoleImpl
|
||||
consoleView.flushDeferredText()
|
||||
|
||||
return consoleView.historyViewer.document.text
|
||||
}
|
||||
|
||||
private fun testSimpleCommand(command: String, expectedOutput: String) {
|
||||
sendCommand(command)
|
||||
checkOutput(expectedOutput)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRunPossibility() {
|
||||
val allOk = { x: String -> x.contains(":help for help") }
|
||||
val hasErrors = { x: String ->
|
||||
x.contains("Process finished with exit code 1") || x.contains("Exception in") || x.contains("Error")
|
||||
}
|
||||
|
||||
val output = getReplOutput(textOnTimeOut = { "Repl startup timed out" }) {
|
||||
val editorText = refreshAndGetHistoryEditorText()
|
||||
hasErrors(editorText) || allOk(editorText)
|
||||
}
|
||||
|
||||
assertFalse(hasErrors(output), "Cannot run kotlin repl")
|
||||
assertTrue(allOk(output), "Successful run should contain text: ':help for help'")
|
||||
assertFalse(consoleRunner.processHandler.isProcessTerminated, "Process accidentally terminated")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLongCommandLine() {
|
||||
ModuleRootModificationUtil.addDependency(module, createLibraryWithLongPaths(project))
|
||||
|
||||
consoleRunner.dispose()
|
||||
consoleRunner = KotlinConsoleKeeper.getInstance(project).run(module)!!
|
||||
|
||||
testRunPossibility()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnePlusOne() = testSimpleCommand("1 + 1", "2")
|
||||
|
||||
@Test
|
||||
fun testPrintlnText() = "Hello, console world!".let { testSimpleCommand("println(\"$it\")", it) }
|
||||
|
||||
@Test
|
||||
fun testDivisionByZeroException() = testSimpleCommand("1 / 0", "java.lang.ArithmeticException: / by zero")
|
||||
|
||||
@Test
|
||||
fun testMultilineSupport() {
|
||||
val printText = "Print in multiline!"
|
||||
|
||||
sendCommand(
|
||||
"fun f() {\n" +
|
||||
" println(\"$printText\")\n" +
|
||||
"}\n"
|
||||
)
|
||||
sendCommand("f()")
|
||||
|
||||
checkOutput(printText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReadLineSingle() {
|
||||
val readLineText = "ReadMe!"
|
||||
|
||||
sendCommand("val a = readLine()")
|
||||
sendCommand(readLineText)
|
||||
sendCommand("a")
|
||||
checkOutput(readLineText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReadLineMultiple() {
|
||||
val readLineTextA = "ReadMe A!"
|
||||
val readLineTextB = "ReadMe B!"
|
||||
|
||||
sendCommand(
|
||||
"val a = readLine()\n" +
|
||||
"val b = readLine()"
|
||||
)
|
||||
sendCommand(readLineTextA)
|
||||
sendCommand(readLineTextB)
|
||||
|
||||
sendCommand("a")
|
||||
checkOutput(readLineTextA)
|
||||
sendCommand("b")
|
||||
checkOutput(readLineTextB)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCorrectAfterError() {
|
||||
val message = "MyMessage"
|
||||
sendCommand("fun f() { println(x)\n println(y) ")
|
||||
sendCommand("println(\"$message\")")
|
||||
checkOutput(message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMultipleErrorsHandling() {
|
||||
val veryLongTextWithErrors = "println($);".repeat(30)
|
||||
sendCommand(veryLongTextWithErrors)
|
||||
sendCommand(veryLongTextWithErrors)
|
||||
sendCommand(veryLongTextWithErrors)
|
||||
sendCommand("println(\"OK\")")
|
||||
checkOutput("OK")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.stubs
|
||||
|
||||
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase
|
||||
import com.intellij.openapi.application.WriteAction
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleType
|
||||
import com.intellij.openapi.module.StdModuleTypes
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
||||
import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testFramework.PsiTestUtil
|
||||
import com.intellij.util.ThrowableRunnable
|
||||
import org.jetbrains.kotlin.config.CompilerSettings
|
||||
import org.jetbrains.kotlin.config.KotlinFacetSettings
|
||||
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
|
||||
import org.jetbrains.kotlin.idea.facet.initializeIfNeeded
|
||||
import org.jetbrains.kotlin.idea.test.*
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import org.jetbrains.kotlin.test.WithMutedInDatabaseRunTest
|
||||
import org.jetbrains.kotlin.test.runTest
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
|
||||
@WithMutedInDatabaseRunTest
|
||||
abstract class AbstractMultiModuleTest : DaemonAnalyzerTestCase() {
|
||||
|
||||
abstract override fun getTestDataPath(): String
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
enableKotlinOfficialCodeStyle(project)
|
||||
VfsRootAccess.allowRootAccess(testRootDisposable, KotlinTestUtils.getHomeDirectory())
|
||||
}
|
||||
|
||||
fun module(name: String, jdk: TestJdkKind = TestJdkKind.MOCK_JDK, hasTestRoot: Boolean = false): Module {
|
||||
val srcDir = testDataPath + "${getTestName(true)}/$name"
|
||||
val moduleWithSrcRootSet = createModuleFromTestData(srcDir, name, StdModuleTypes.JAVA, true)
|
||||
if (hasTestRoot) {
|
||||
addRoot(
|
||||
moduleWithSrcRootSet,
|
||||
File(testDataPath + "${getTestName(true)}/${name}Test"),
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
ConfigLibraryUtil.configureSdk(moduleWithSrcRootSet, PluginTestCaseBase.addJdk(testRootDisposable) { PluginTestCaseBase.jdk(jdk) })
|
||||
|
||||
return moduleWithSrcRootSet
|
||||
}
|
||||
|
||||
override fun tearDown() = runAll(
|
||||
ThrowableRunnable { disableKotlinOfficialCodeStyle(project) },
|
||||
ThrowableRunnable { super.tearDown() },
|
||||
)
|
||||
|
||||
public override fun createModule(path: String, moduleType: ModuleType<*>): Module {
|
||||
return super.createModule(path, moduleType)
|
||||
}
|
||||
|
||||
override fun runTestRunnable(testRunnable: ThrowableRunnable<Throwable>) {
|
||||
runTest { super.runTestRunnable(testRunnable) }
|
||||
}
|
||||
|
||||
fun addRoot(module: Module, sourceDirInTestData: File, isTestRoot: Boolean, transformContainedFiles: ((File) -> Unit)? = null) {
|
||||
val tmpDir = createTempDirectory()
|
||||
|
||||
// Preserve original root name. This might be useful for later matching of copied files to original ones
|
||||
val tmpRootDir = File(tmpDir, sourceDirInTestData.name).also { it.mkdir() }
|
||||
|
||||
FileUtil.copyDir(sourceDirInTestData, tmpRootDir)
|
||||
|
||||
if (transformContainedFiles != null) {
|
||||
tmpRootDir.listFiles().forEach(transformContainedFiles)
|
||||
}
|
||||
|
||||
val virtualTempDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tmpRootDir)!!
|
||||
object : WriteCommandAction.Simple<Unit>(project) {
|
||||
override fun run() {
|
||||
virtualTempDir.refresh(false, isTestRoot)
|
||||
}
|
||||
}.execute().throwException()
|
||||
PsiTestUtil.addSourceRoot(module, virtualTempDir, isTestRoot)
|
||||
}
|
||||
|
||||
fun Module.addDependency(
|
||||
other: Module,
|
||||
dependencyScope: DependencyScope = DependencyScope.COMPILE,
|
||||
exported: Boolean = false
|
||||
): Module = this.apply { ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported) }
|
||||
|
||||
fun Module.addLibrary(
|
||||
jar: File,
|
||||
name: String = KotlinJdkAndLibraryProjectDescriptor.LIBRARY_NAME,
|
||||
kind: PersistentLibraryKind<*>? = null
|
||||
) {
|
||||
ConfigLibraryUtil.addLibrary(NewLibraryEditor().apply {
|
||||
this.name = name
|
||||
addRoot(VfsUtil.getUrlForLibraryRoot(jar), OrderRootType.CLASSES)
|
||||
}, this, kind)
|
||||
}
|
||||
|
||||
fun Module.enableMultiPlatform(additionalCompilerArguments: String = "") {
|
||||
createFacet()
|
||||
val facetSettings = KotlinFacetSettingsProvider.getInstance(project)?.getInitializedSettings(this)
|
||||
?: error("Facet settings are not found")
|
||||
|
||||
facetSettings.useProjectSettings = false
|
||||
facetSettings.compilerSettings = CompilerSettings().apply {
|
||||
additionalArguments += " -Xmulti-platform $additionalCompilerArguments"
|
||||
}
|
||||
}
|
||||
|
||||
fun Module.enableCoroutines() {
|
||||
createFacet()
|
||||
val facetSettings = KotlinFacetSettingsProvider.getInstance(project)?.getInitializedSettings(this)
|
||||
?: error("Facet settings are not found")
|
||||
|
||||
facetSettings.useProjectSettings = false
|
||||
facetSettings.coroutineSupport = LanguageFeature.State.ENABLED
|
||||
}
|
||||
|
||||
protected fun checkFiles(
|
||||
findFiles: () -> List<PsiFile>,
|
||||
check: () -> Unit
|
||||
) {
|
||||
var atLeastOneFile = false
|
||||
findFiles().forEach { file ->
|
||||
configureByExistingFile(file.virtualFile!!)
|
||||
atLeastOneFile = true
|
||||
check()
|
||||
}
|
||||
Assert.assertTrue(atLeastOneFile)
|
||||
}
|
||||
}
|
||||
|
||||
fun Module.createFacet(
|
||||
platformKind: TargetPlatform? = null,
|
||||
useProjectSettings: Boolean = true
|
||||
) {
|
||||
createFacetWithAdditionalSetup(platformKind, useProjectSettings) { }
|
||||
}
|
||||
|
||||
fun Module.createMultiplatformFacetM1(
|
||||
platformKind: TargetPlatform? = null,
|
||||
useProjectSettings: Boolean = true,
|
||||
implementedModuleNames: List<String>,
|
||||
pureKotlinSourceFolders: List<String>
|
||||
) {
|
||||
createFacetWithAdditionalSetup(platformKind, useProjectSettings) {
|
||||
this.implementedModuleNames = implementedModuleNames
|
||||
this.pureKotlinSourceFolders = pureKotlinSourceFolders
|
||||
}
|
||||
}
|
||||
|
||||
fun Module.createMultiplatformFacetM3(
|
||||
platformKind: TargetPlatform? = null,
|
||||
useProjectSettings: Boolean = true,
|
||||
dependsOnModuleNames: List<String>,
|
||||
pureKotlinSourceFolders: List<String>
|
||||
) {
|
||||
createFacetWithAdditionalSetup(platformKind, useProjectSettings) {
|
||||
this.dependsOnModuleNames = dependsOnModuleNames
|
||||
this.isHmppEnabled = true
|
||||
this.pureKotlinSourceFolders = pureKotlinSourceFolders
|
||||
}
|
||||
}
|
||||
|
||||
private fun Module.createFacetWithAdditionalSetup(
|
||||
platformKind: TargetPlatform?,
|
||||
useProjectSettings: Boolean,
|
||||
additionalSetup: KotlinFacetSettings.() -> Unit
|
||||
) {
|
||||
WriteAction.run<Throwable> {
|
||||
val modelsProvider = IdeModifiableModelsProviderImpl(project)
|
||||
with(getOrCreateFacet(modelsProvider, useProjectSettings).configuration.settings) {
|
||||
initializeIfNeeded(
|
||||
this@createFacetWithAdditionalSetup,
|
||||
modelsProvider.getModifiableRootModel(this@createFacetWithAdditionalSetup),
|
||||
platformKind
|
||||
)
|
||||
additionalSetup()
|
||||
}
|
||||
modelsProvider.commit()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user