FIR IDE: introduce multi module lazy resolve tests

This commit is contained in:
Ilya Kirillov
2020-08-31 18:02:27 +03:00
parent 8714ad575f
commit 87898021a1
8 changed files with 231 additions and 0 deletions
@@ -88,6 +88,7 @@ import org.jetbrains.kotlin.idea.filters.AbstractKotlinExceptionFilterTest
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyResolveTest
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleResolveTest
import org.jetbrains.kotlin.idea.fir.AbstractKtDeclarationAndFirDeclarationEqualityChecker
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleLazyResolveTest
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
import org.jetbrains.kotlin.idea.frontend.api.scopes.AbstractMemberScopeByFqNameTest
@@ -1015,6 +1016,12 @@ fun main(args: Array<String>) {
}
}
testGroup("idea/idea-frontend-fir/idea-fir-low-level-api/tests", "idea/idea-frontend-fir/idea-fir-low-level-api/testdata") {
testClass<AbstractFirMultiModuleLazyResolveTest> {
model("multiModuleLazyResolve", recursive = false, extension = null)
}
}
testGroup("idea/idea-fir/tests", "idea") {
testClass<AbstractFirHighlightingTest> {
model("testData/highlighter")
@@ -0,0 +1,8 @@
interface A
interface X : A
interface Y : A
interface F : Y, X
interface G : Y, X
@@ -0,0 +1,26 @@
FILE: main.kt
public abstract interface A : R|kotlin/Any| {
}
public abstract interface X : R|A| {
}
public abstract interface Y : R|A| {
}
public abstract interface F : R|Y|, R|X| {
}
public abstract interface G : R|Y|, R|X| {
}
public final fun R|Y|.test(): R|kotlin/String?| {
lval a: R|kotlin/Int| = when (this@R|/test|) {
($subj$ is R|F|) -> {
Int(1)
}
($subj$ is R|G|) -> {
Int(2)
}
else -> {
^test Null(null)
}
}
^test Null(null)
}
@@ -0,0 +1,8 @@
fun Y.test(): String? {
val a = when (this) {
is F -> 1
is G -> 2
else -> return null
}
return null
}
@@ -0,0 +1,8 @@
{
"modules" : [
{ "name": "main", "dependsOn": ["dependency"] },
{ "name": "dependency" }
],
"fileToResolve": { "module": "main", "file": "main.kt" },
"fails": true
}
@@ -0,0 +1,57 @@
/*
* 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.fir.low.level.api
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.idea.stubs.AbstractMultiModuleTest
import org.jetbrains.kotlin.idea.util.sourceRoots
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
import java.nio.file.Paths
abstract class AbstractFirMultiModuleLazyResolveTest : AbstractMultiModuleTest() {
override fun getTestDataPath(): String =
"${KotlinTestUtils.getHomeDirectory()}/idea/idea-frontend-fir/idea-fir-low-level-api/testdata/multiModuleLazyResolve/"
fun doTest(path: String) {
val testStructure = TestProjectStructureReader.read(Paths.get(path))
val modulesByNames = testStructure.modules.associate { moduleData ->
moduleData.name to module(moduleData.name)
}
testStructure.modules.forEach { moduleData ->
val module = modulesByNames.getValue(moduleData.name)
moduleData.dependsOnModules.forEach { dependencyName ->
module.addDependency(modulesByNames.getValue(dependencyName))
}
}
val moduleToResolve = modulesByNames.getValue(testStructure.fileToResolve.moduleName)
val fileToAnalysePath = moduleToResolve.sourceRoots.first().url + "/" + testStructure.fileToResolve.relativeFilePath
val virtualFileToAnalyse = VirtualFileManager.getInstance().findFileByUrl(fileToAnalysePath)
?: error("File ${testStructure.fileToResolve.filePath} not found")
val ktFileToAnalyse = PsiManager.getInstance(project).findFile(virtualFileToAnalyse) as KtFile
val resolveState = LowLevelFirApiFacade.getResolveStateFor(ktFileToAnalyse)
val fails = testStructure.fails
try {
val fir = LowLevelFirApiFacade.getOrBuildFirFor(ktFileToAnalyse, resolveState, FirResolvePhase.BODY_RESOLVE)
KotlinTestUtils.assertEqualsToFile(File("$path/expected.txt"), fir.render())
} catch (e: Exception) {
if (!fails) throw e
return
}
if (fails) {
throw AssertionError("Looks like test is passing, please remove // FAILS form the file")
}
}
}
@@ -0,0 +1,35 @@
/*
* 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.fir.low.level.api;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/multiModuleLazyResolve")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class FirMultiModuleLazyResolveTestGenerated extends AbstractFirMultiModuleLazyResolveTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInMultiModuleLazyResolve() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/multiModuleLazyResolve"), Pattern.compile("^([^\\.]+)$"), null, false);
}
@TestMetadata("whenWithHeiarchy")
public void testWhenWithHeiarchy() throws Exception {
runTest("idea/idea-frontend-fir/idea-fir-low-level-api/testdata/multiModuleLazyResolve/whenWithHeiarchy/");
}
}
@@ -0,0 +1,82 @@
/*
* 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.fir.low.level.api
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.google.gson.JsonPrimitive
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.idea.jsonUtils.getString
import java.nio.file.Path
sealed class TestProjectStructure {
abstract val modules: List<TestProjectModule>
abstract val fileToResolve: FileToResolve
abstract val fails: Boolean
companion object {
fun parse(json: JsonElement): TestProjectStructure {
require(json is JsonObject)
val fails = if (json.has(FAILS_FIELD)) json.get(FAILS_FIELD).asBoolean else false
return MultiModuleTestProjectStructure(
json.getAsJsonArray("modules").map { TestProjectModule.parse(it) },
FileToResolve.parse(json.getAsJsonObject("fileToResolve")),
fails
)
}
private const val FAILS_FIELD = "fails"
}
}
data class FileToResolve(val moduleName: String, val relativeFilePath: String) {
val filePath get() = "$moduleName/$relativeFilePath"
companion object {
fun parse(json: JsonElement): FileToResolve {
require(json is JsonObject)
return FileToResolve(
moduleName = json.getString("module"),
relativeFilePath = json.getString("file")
)
}
}
}
data class MultiModuleTestProjectStructure(
override val modules: List<TestProjectModule>,
override val fileToResolve: FileToResolve,
override val fails: Boolean
) : TestProjectStructure()
data class TestProjectModule(val name: String, val dependsOnModules: List<String>) {
companion object {
fun parse(json: JsonElement): TestProjectModule {
require(json is JsonObject)
val dependencies = if (json.has(DEPENDS_ON_FIELD)) {
json.getAsJsonArray(DEPENDS_ON_FIELD).map { (it as JsonPrimitive).asString }
} else emptyList()
return TestProjectModule(
json.getString("name"),
dependencies
)
}
private const val DEPENDS_ON_FIELD = "dependsOn"
}
}
object TestProjectStructureReader {
fun read(testDirectory: Path, jsonFileName: String = "structure.json"): TestProjectStructure {
val jsonFile = testDirectory.resolve(jsonFileName)
@Suppress("DEPRECATION") // AS 4.0
val json = JsonParser().parse(FileUtil.loadFile(jsonFile.toFile(), /*convertLineSeparators=*/true))
return TestProjectStructure.parse(json)
}
}