[analysis api] allow generating test with for with more possible parameters

This commit is contained in:
Ilya Kirillov
2022-03-24 20:55:19 +01:00
parent 12e0254918
commit 443ee4c860
24 changed files with 495 additions and 309 deletions
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2022 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.analysis.api.fe10
import org.jetbrains.kotlin.analysis.test.framework.*
object AnalysisApiFe10TestConfiguratorFactory : AnalysisApiTestConfiguratorFactory() {
override fun createConfigurator(data: AnalysisApiTestConfiguratorFactoryData): AnalysisApiTestConfiguratorService {
require(supportMode(data))
return when (data.moduleKind) {
TestModuleKind.Source -> when (data.analysisSessionMode) {
AnalysisSessionMode.Normal -> KtFe10AnalysisApiTestConfiguratorService
AnalysisSessionMode.Dependent -> error("Unsupported AnalysisSessionMode.Dependent for fe10")
}
else -> {
error("Unsupported non-source module for fe10")
}
}
}
override fun supportMode(data: AnalysisApiTestConfiguratorFactoryData): Boolean {
return when {
data.frontend != FrontendKind.Fe10 -> false
data.analysisSessionMode != AnalysisSessionMode.Normal -> false
data.analysisApiMode != AnalysisApiMode.Ide -> false
else -> when (data.moduleKind) {
TestModuleKind.Source -> true
TestModuleKind.LibraryBinary,
TestModuleKind.LibrarySource -> false
}
}
}
}
@@ -3,7 +3,7 @@
* 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.analysis.api.descriptors.test
package org.jetbrains.kotlin.analysis.api.fe10
import com.intellij.mock.MockApplication
import com.intellij.mock.MockProject
@@ -3,7 +3,7 @@
* 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.analysis.api.descriptors.test
package org.jetbrains.kotlin.analysis.api.fe10
import com.intellij.mock.MockProject
import com.intellij.openapi.Disposable
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2022 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.analysis.api.fir
import org.jetbrains.kotlin.analysis.api.fir.utils.libraries.binary.LibraryAnalysisApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.fir.utils.libraries.source.LibrarySourceAnalysisApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.test.framework.*
object AnalysisApiFirTestConfiguratorFactory : AnalysisApiTestConfiguratorFactory() {
override fun createConfigurator(data: AnalysisApiTestConfiguratorFactoryData): AnalysisApiTestConfiguratorService {
require(supportMode(data))
return when (data.moduleKind) {
TestModuleKind.Source -> when (data.analysisSessionMode) {
AnalysisSessionMode.Normal -> FirAnalysisApiNormalModeTestConfiguratorService
AnalysisSessionMode.Dependent -> FirAnalysisApiDependentModeTestConfiguratorService
}
TestModuleKind.LibraryBinary -> {
require(data.analysisSessionMode == AnalysisSessionMode.Normal)
LibraryAnalysisApiTestConfiguratorService
}
TestModuleKind.LibrarySource -> {
require(data.analysisSessionMode == AnalysisSessionMode.Normal)
LibrarySourceAnalysisApiTestConfiguratorService
}
}
}
override fun supportMode(data: AnalysisApiTestConfiguratorFactoryData): Boolean {
return when {
data.frontend != FrontendKind.Fir -> false
data.analysisApiMode != AnalysisApiMode.Ide -> false
else -> when (data.moduleKind) {
TestModuleKind.Source -> true
TestModuleKind.LibraryBinary,
TestModuleKind.LibrarySource ->
data.analysisSessionMode == AnalysisSessionMode.Normal
}
}
}
}
@@ -29,14 +29,6 @@ open class FirAnalysisApiTestConfiguratorService(override val analyseInDependent
configureOptionalTestCompilerPlugin()
}
override fun processTestFiles(files: List<KtFile>): List<KtFile> {
return FirLowLevelAnalysisApiTestConfiguratorService.processTestFiles(files)
}
override fun getOriginalFile(file: KtFile): KtFile {
return FirLowLevelAnalysisApiTestConfiguratorService.getOriginalFile(file)
}
override fun registerProjectServices(
project: MockProject,
compilerConfig: CompilerConfiguration,
@@ -44,9 +44,7 @@ abstract class AbstractReferenceResolveTest : AbstractAnalysisApiBasedSingleModu
}
val resolvedTo =
analyseForTest(
PsiTreeUtil.findElementOfClassAtOffset(mainKtFile, caretPosition, KtDeclaration::class.java, false) ?: mainKtFile
) {
analyseForTest(ktReferences.first().element) {
val symbols = ktReferences.flatMap { it.resolveToSymbols() }
checkReferenceResultForValidity(ktReferences, module, testServices, symbols)
renderResolvedTo(symbols, renderingOptions)
@@ -19,7 +19,7 @@ abstract class AbstractAnalysisApiAnnotationsOnDeclarationsTest : AbstractAnalys
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val ktDeclaration = testServices.expressionMarkerProvider
.getElementOfTypAtCaret<KtDeclaration>(ktFile)
val actual = analyseForTest(ktFile) {
val actual = analyseForTest(ktDeclaration) {
val declarationSymbol = ktDeclaration.getSymbol() as KtAnnotatedSymbol
buildString {
appendLine("KtDeclaration: ${ktDeclaration::class.simpleName} ${ktDeclaration.name}")
@@ -21,7 +21,7 @@ abstract class AbstractAnalysisApiAnnotationsOnTypesTest : AbstractAnalysisApiSi
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val ktTypeReference = testServices.expressionMarkerProvider
.getElementOfTypAtCaret<KtTypeReference>(ktFile)
val actual = analyseForTest(ktFile) {
val actual = analyseForTest(ktTypeReference) {
val ktType = ktTypeReference.getKtType()
val annotations = ktType.annotations
buildString {
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2022 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.analysis.api.standalone
import org.jetbrains.kotlin.analysis.test.framework.*
object AnalysisApiFirStandaloneModeTestConfiguratorFactory : AnalysisApiTestConfiguratorFactory() {
override fun createConfigurator(data: AnalysisApiTestConfiguratorFactoryData): AnalysisApiTestConfiguratorService {
requireSupported(data)
return when (data.moduleKind) {
TestModuleKind.Source -> when (data.analysisSessionMode) {
AnalysisSessionMode.Normal -> StandaloneModeConfiguratorService
AnalysisSessionMode.Dependent -> unsupportedModeError(data)
}
else -> {
unsupportedModeError(data)
}
}
}
override fun supportMode(data: AnalysisApiTestConfiguratorFactoryData): Boolean {
return when {
data.frontend != FrontendKind.Fir -> false
data.analysisSessionMode != AnalysisSessionMode.Normal -> false
data.analysisApiMode != AnalysisApiMode.Standalone -> false
else -> when (data.moduleKind) {
TestModuleKind.Source -> true
TestModuleKind.LibraryBinary,
TestModuleKind.LibrarySource -> false
}
}
}
}
@@ -1,3 +1,5 @@
// DISABLE_DEPENDED_MODE
class Generic<T> {
inner class Nested
}
@@ -8,3 +10,4 @@ class C {
class Foo
@@ -24,9 +24,7 @@ interface AnalysisApiTestConfiguratorService {
val analyseInDependentSession: Boolean
fun TestConfigurationBuilder.configureTest(disposable: Disposable)
fun processTestFiles(files: List<KtFile>): List<KtFile> = files
fun getOriginalFile(file: KtFile): KtFile = file
fun registerProjectServices(
project: MockProject,
compilerConfig: CompilerConfiguration,
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2022 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.analysis.test.framework
abstract class AnalysisApiTestConfiguratorFactory {
abstract fun createConfigurator(data: AnalysisApiTestConfiguratorFactoryData): AnalysisApiTestConfiguratorService
abstract fun supportMode(data: AnalysisApiTestConfiguratorFactoryData): Boolean
protected fun requireSupported(data: AnalysisApiTestConfiguratorFactoryData) {
if (!supportMode(data)) {
unsupportedModeError(data)
}
}
protected fun unsupportedModeError(data: AnalysisApiTestConfiguratorFactoryData): Nothing {
error("${this::class} is does not support $data")
}
}
data class AnalysisApiTestConfiguratorFactoryData(
val frontend: FrontendKind,
val moduleKind: TestModuleKind,
val analysisSessionMode: AnalysisSessionMode,
val analysisApiMode: AnalysisApiMode,
)
enum class AnalysisSessionMode(val suffix: String) {
Normal("Normal"),
Dependent("Dependent");
}
enum class AnalysisApiMode(val suffix: String) {
Ide("Ide"),
Standalone("Standalone");
}
enum class FrontendKind(val suffix: String) {
Fir("Fir"),
Fe10("Fe10"),
}
enum class TestModuleKind(val suffix: String) {
Source("Source"),
LibraryBinary("LibraryBinary"),
LibrarySource("LibrarySource");
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2022 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.analysis.test.framework
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
object AnalysisApiTestDirectives : SimpleDirectivesContainer() {
val DISABLE_DEPENDED_MODE by directive("Analysis in dependent mode should not be run in this test")
}
@@ -9,12 +9,14 @@ import com.intellij.mock.MockApplication
import com.intellij.mock.MockProject
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Disposer
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.testFramework.TestDataFile
import junit.framework.ComparisonFailure
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.analyse
import org.jetbrains.kotlin.analysis.api.analyseInDependedAnalysisSession
import org.jetbrains.kotlin.analysis.test.framework.AnalysisApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.test.framework.AnalysisApiTestDirectives
import org.jetbrains.kotlin.analysis.test.framework.TestWithDisposable
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KotlinProjectStructureProviderTestImpl
import org.jetbrains.kotlin.analysis.test.framework.project.structure.TestKtModuleProvider
@@ -58,6 +60,9 @@ abstract class AbstractAnalysisApiBasedTest : TestWithDisposable() {
protected lateinit var testDataPath: Path
private set
private lateinit var moduleStructure: TestModuleStructure
private set
protected open fun configureTest(builder: TestConfigurationBuilder) {
with(configurator) {
builder.configureTest(disposable)
@@ -122,6 +127,7 @@ abstract class AbstractAnalysisApiBasedTest : TestWithDisposable() {
useAdditionalService(::TestKtModuleProvider)
useAdditionalService<ApplicationDisposableProvider> { ExecutionListenerBasedDisposableProvider() }
useAdditionalService<KotlinStandardLibrariesPathProvider> { StandardLibrariesPathProviderForKotlinProject }
useDirectives(AnalysisApiTestDirectives)
configureTest(this)
startingArtifactFactory = { ResultingArtifact.Source() }
@@ -144,6 +150,7 @@ abstract class AbstractAnalysisApiBasedTest : TestWithDisposable() {
path,
testConfiguration.directives,
)
this.moduleStructure = moduleStructure
val singleModule = moduleStructure.modules.single()
val project = try {
testServices.compilerConfigurationProvider.getProject(singleModule)
@@ -193,10 +200,12 @@ abstract class AbstractAnalysisApiBasedTest : TestWithDisposable() {
}
protected fun <R> analyseForTest(contextElement: KtElement, action: KtAnalysisSession.() -> R): R {
return if (configurator.analyseInDependentSession) {
require(!contextElement.isPhysical)
val originalFile = configurator.getOriginalFile(contextElement.containingKtFile)
analyseInDependedAnalysisSession(originalFile, contextElement, action)
return if (configurator.analyseInDependentSession
&& AnalysisApiTestDirectives.DISABLE_DEPENDED_MODE !in this.moduleStructure.allDirectives
) {
val originalContainingFile = contextElement.containingKtFile
val fileCopy = originalContainingFile.copy() as KtFile
analyseInDependedAnalysisSession(originalContainingFile, PsiTreeUtil.findSameElementInCopy(contextElement, fileCopy), action)
} else {
analyse(contextElement, action)
}
@@ -217,8 +226,4 @@ abstract class AbstractAnalysisApiBasedTest : TestWithDisposable() {
tags = testInfo.tags
)
}
companion object {
val DISABLE_DEPENDED_MODE_DIRECTIVE = "DISABLE_DEPENDED_MODE"
}
}
@@ -30,18 +30,6 @@ object FirLowLevelAnalysisApiTestConfiguratorService : AnalysisApiTestConfigurat
usePreAnalysisHandlers(::ModuleRegistrarPreAnalysisHandler.bind(disposable, false))
}
override fun processTestFiles(files: List<KtFile>): List<KtFile> {
return files.map {
val fakeFile = it.copy() as KtFile
fakeFile.originalKtFile = it
fakeFile
}
}
override fun getOriginalFile(file: KtFile): KtFile {
return file.originalKtFile!!
}
override fun registerProjectServices(
project: MockProject,
compilerConfig: CompilerConfiguration,
@@ -5,21 +5,21 @@
package org.jetbrains.kotlin.generators.tests.analysis.api
import org.jetbrains.kotlin.generators.TestGroupSuite
import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.FrontendConfiguratorTestGenerator
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.AnalysisApiTestGroup
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.generate
fun main(args: Array<String>) {
System.setProperty("java.awt.headless", "true")
generateTestGroupSuiteWithJUnit5(args, additionalMethodGenerators = listOf(FrontendConfiguratorTestGenerator)) {
generate(args) {
generateTests()
}
}
private fun TestGroupSuite.generateTests() {
private fun AnalysisApiTestGroup.generateTests() {
generateAnalysisApiTests()
generateFirLowLevelApiTests()
generateDecompiledTests()
generateSymbolLightClassesTests()
generateStandaloneModeTests()
suiteBasedTests {
generateFirLowLevelApiTests()
generateDecompiledTests()
generateSymbolLightClassesTests()
}
}
@@ -37,65 +37,72 @@ import org.jetbrains.kotlin.analysis.api.impl.base.test.scopes.AbstractSubstitut
import org.jetbrains.kotlin.analysis.api.impl.base.test.symbols.AbstractSymbolByFqNameTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.symbols.AbstractSymbolByPsiTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.symbols.AbstractSymbolByReferenceTest
import org.jetbrains.kotlin.generators.TestGroupSuite
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.TestModuleKind
import org.jetbrains.kotlin.analysis.test.framework.AnalysisSessionMode
import org.jetbrains.kotlin.analysis.test.framework.FrontendKind
import org.jetbrains.kotlin.analysis.test.framework.TestModuleKind
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.*
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.AnalysisApiTestGroup
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.component
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.group
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.test
import org.jetbrains.kotlin.generators.util.TestGeneratorUtil
fun TestGroupSuite.generateAnalysisApiTests() {
generateAnalysisApiComponentsTests()
generateAnalysisApiNonComponentsTests()
}
private fun TestGroupSuite.generateAnalysisApiNonComponentsTests() {
internal fun AnalysisApiTestGroup.generateAnalysisApiTests() {
test(
AbstractReferenceResolveTest::class,
testModuleKinds = TestModuleKind.SOURCE_AND_LIBRARY_SOURCE,
addFe10 = false,
) { moduleKind ->
when (moduleKind) {
TestModuleKind.LIBRARY_SOURCE -> {
model("referenceResolve", pattern = TestGeneratorUtil.KT_WITHOUT_DOTS_IN_NAME, excludeDirsRecursively = listOf("withErrors"))
filter = frontendIs(FrontendKind.Fir) and testModuleKindIs(TestModuleKind.Source, TestModuleKind.LibrarySource),
) { data ->
when (data.moduleKind) {
TestModuleKind.LibrarySource -> {
model(
"referenceResolve",
pattern = TestGeneratorUtil.KT_WITHOUT_DOTS_IN_NAME,
excludeDirsRecursively = listOf("withErrors")
)
}
else -> {
model("referenceResolve", pattern = TestGeneratorUtil.KT_WITHOUT_DOTS_IN_NAME)
}
}
}
group("scopes") {
group(filter = testModuleKindIs(TestModuleKind.Source)) {
generateAnalysisApiComponentsTests()
generateAnalysisApiNonComponentsTests()
}
}
private fun AnalysisApiTestGroup.generateAnalysisApiNonComponentsTests() {
group("scopes", filter = analysisSessionModeIs(AnalysisSessionMode.Normal)) {
test(
AbstractSubstitutionOverridesUnwrappingTest::class,
generateFe10 = false,
filter = frontendIs(FrontendKind.Fir),
) {
model("substitutionOverridesUnwrapping")
}
test(
AbstractMemberScopeByFqNameTest::class,
generateFe10 = false,
filter = frontendIs(FrontendKind.Fir),
) {
model("memberScopeByFqName")
}
test(
AbstractFileScopeTest::class,
generateFe10 = false,
filter = frontendIs(FrontendKind.Fir),
) {
model("fileScopeTest", extension = "kt")
}
test(
AbstractDelegateMemberScopeTest::class,
generateFe10 = false,
filter = frontendIs(FrontendKind.Fir),
) {
model("delegatedMemberScope")
}
}
group("symbols") {
group("symbols", filter = analysisSessionModeIs(AnalysisSessionMode.Normal)) {
test(AbstractSymbolByPsiTest::class) {
model("symbolByPsi")
}
@@ -122,7 +129,7 @@ private fun TestGroupSuite.generateAnalysisApiNonComponentsTests() {
test(
AbstractAnalysisApiAnnotationsOnFilesTest::class,
generateFe10 = false // TODO "fails with Rewrite at slice ANNOTATION key"
filter = frontendIs(FrontendKind.Fir) and analysisSessionModeIs(AnalysisSessionMode.Normal), // TODO "fe10 fails with Rewrite at slice ANNOTATION key"
) {
model("annotationsOnFiles")
}
@@ -130,8 +137,8 @@ private fun TestGroupSuite.generateAnalysisApiNonComponentsTests() {
}
private fun TestGroupSuite.generateAnalysisApiComponentsTests() {
component("callResolver") {
private fun AnalysisApiTestGroup.generateAnalysisApiComponentsTests() {
component("callResolver", filter = analysisSessionModeIs(AnalysisSessionMode.Normal)) {
test(AbstractResolveCallTest::class) {
model("resolveCall")
}
@@ -176,7 +183,7 @@ private fun TestGroupSuite.generateAnalysisApiComponentsTests() {
}
}
component("diagnosticsProvider") {
component("diagnosticsProvider", filter = analysisSessionModeIs(AnalysisSessionMode.Normal)) {
test(AbstractCollectDiagnosticsTest::class) {
model("diagnostics")
}
@@ -185,18 +192,18 @@ private fun TestGroupSuite.generateAnalysisApiComponentsTests() {
component("importOptimizer") {
test(
AbstractHLImportOptimizerTest::class,
generateFe10 = false,
filter = frontendIs(FrontendKind.Fir) and analysisSessionModeIs(AnalysisSessionMode.Normal),
) {
model("analyseImports", pattern = TestGeneratorUtil.KT_WITHOUT_DOTS_IN_NAME)
}
}
component("psiTypeProvider") {
test(AbstractPsiTypeProviderTest::class, generateFe10 = false) {
test(AbstractPsiTypeProviderTest::class, filter = frontendIs(FrontendKind.Fir)) {
model("psiType/forDeclaration")
}
test(AbstractExpressionPsiTypeProviderTest::class, generateFe10 = false) {
test(AbstractExpressionPsiTypeProviderTest::class, filter = frontendIs(FrontendKind.Fir)) {
model("psiType/forExpression")
}
}
@@ -230,10 +237,10 @@ private fun TestGroupSuite.generateAnalysisApiComponentsTests() {
}
component("typeInfoProvider") {
test(AbstractFunctionClassKindTest::class, generateFe10 = false) {
test(AbstractFunctionClassKindTest::class, filter = frontendIs(FrontendKind.Fir)) {
model("functionClassKind")
}
test(AbstractFirGetSuperTypesTest::class, generateFe10 = false) {
test(AbstractFirGetSuperTypesTest::class, filter = frontendIs(FrontendKind.Fir)) {
model("superTypes")
}
test(AbstractIsDenotableTest::class) {
@@ -242,8 +249,10 @@ private fun TestGroupSuite.generateAnalysisApiComponentsTests() {
}
component("typeProvider") {
test(AbstractHasCommonSubtypeTest::class) {
model("haveCommonSubtype")
group(filter = analysisSessionModeIs(AnalysisSessionMode.Normal)) {
test(AbstractHasCommonSubtypeTest::class) {
model("haveCommonSubtype")
}
}
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2022 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.generators.tests.analysis.api.dsl
import org.jetbrains.kotlin.analysis.api.fe10.AnalysisApiFe10TestConfiguratorFactory
import org.jetbrains.kotlin.analysis.api.fir.AnalysisApiFirTestConfiguratorFactory
import org.jetbrains.kotlin.analysis.api.standalone.AnalysisApiFirStandaloneModeTestConfiguratorFactory
import org.jetbrains.kotlin.analysis.test.framework.*
object AnalysisApiConfiguratorFactoryProvider {
private val allFactories = listOf(
AnalysisApiFirTestConfiguratorFactory,
AnalysisApiFe10TestConfiguratorFactory,
)
fun getFactory(data: AnalysisApiTestConfiguratorFactoryData): AnalysisApiTestConfiguratorFactory? {
val supportedFactories = allFactories.filter { it.supportMode(data) }
check(supportedFactories.size <= 1) {
buildString {
append("For $data")
append(" expected no more than 1 supported ")
append(AnalysisApiTestConfiguratorFactory::class.simpleName)
append(" but ${supportedFactories.size} found ")
append(supportedFactories.joinToString(prefix = "[", postfix = "]") { it::class.simpleName!! })
}
}
return supportedFactories.singleOrNull()
}
fun getTestPath(data: AnalysisApiTestConfiguratorFactoryData): String? = when {
data.frontend == FrontendKind.Fir && data.analysisApiMode == AnalysisApiMode.Ide -> "analysis/analysis-api-fir/tests"
data.frontend == FrontendKind.Fe10 && data.analysisApiMode == AnalysisApiMode.Ide -> "analysis/analysis-api-fe10/tests"
data.frontend == FrontendKind.Fir && data.analysisApiMode == AnalysisApiMode.Standalone -> "analysis/analysis-api-standalone/tests"
else -> null
}
val allPossibleFactoryDataList: List<AnalysisApiTestConfiguratorFactoryData> = buildList {
FrontendKind.values().forEach { frontend ->
TestModuleKind.values().forEach { moduleKind ->
AnalysisSessionMode.values().forEach { analysisSessionMode ->
AnalysisApiMode.values().forEach { analysisApiMode ->
add(AnalysisApiTestConfiguratorFactoryData(frontend, moduleKind, analysisSessionMode, analysisApiMode))
}
}
}
}
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2022 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.generators.tests.analysis.api.dsl
import org.jetbrains.kotlin.generators.TestGroupSuite
import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
internal class AnalysisApiTestGenerator(val suite: TestGroupSuite) {
fun group(init: AnalysisApiTestGroup.() -> Unit) {
AnalysisApiTestGroup(this, { true }, null).init()
}
}
internal fun generate(args: Array<String>, init: AnalysisApiTestGroup.() -> Unit) {
generateTestGroupSuiteWithJUnit5(args, additionalMethodGenerators = listOf(FrontendConfiguratorTestGenerator)) {
AnalysisApiTestGenerator(this).group(init)
}
}
@@ -0,0 +1,99 @@
/*
* Copyright 2010-2022 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.generators.tests.analysis.api.dsl
import org.jetbrains.kotlin.analysis.test.framework.AnalysisApiTestConfiguratorFactoryData
import org.jetbrains.kotlin.generators.TestGroup
import org.jetbrains.kotlin.generators.TestGroupSuite
import org.jetbrains.kotlin.generators.getDefaultSuiteTestClassName
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import kotlin.reflect.KClass
internal class AnalysisApiTestGroup(
private val generator: AnalysisApiTestGenerator,
private val groupFilter: TestFilter,
private val directory: String?,
) {
fun group(directory: String? = null, filter: TestFilter = { true }, init: AnalysisApiTestGroup.() -> Unit) {
AnalysisApiTestGroup(
generator,
groupFilter and filter,
listOfNotNull(this.directory, directory).joinToString(separator = "/")
).init()
}
fun suiteBasedTests(init: TestGroupSuite.() -> Unit) {
generator.suite.init()
}
fun test(
testClass: KClass<*>,
filter: TestFilter = { true },
init: TestGroup.TestClass.(data: AnalysisApiTestConfiguratorFactoryData) -> Unit,
) {
with(generator.suite) {
val fullTestPath = "analysis/analysis-api/testData" + directory?.let { "/$it" }.orEmpty()
AnalysisApiConfiguratorFactoryProvider.allPossibleFactoryDataList.filter(groupFilter).filter(filter)
.groupBy { AnalysisApiConfiguratorFactoryProvider.getTestPath(it) }
.forEach { (testRoot, datas) ->
if (testRoot == null) return@forEach
testGroup(testRoot, fullTestPath) {
datas.forEach { data ->
analysisApiTestClass(data, testClass, init)
}
}
}
}
}
}
private fun TestGroup.analysisApiTestClass(
data: AnalysisApiTestConfiguratorFactoryData,
testClass: KClass<*>,
init: TestGroup.TestClass.(data: AnalysisApiTestConfiguratorFactoryData) -> Unit
) {
val factory = AnalysisApiConfiguratorFactoryProvider.getFactory(data) ?: return
val fullPackage = getPackageName(data.frontend.suffix, testClass)
val suiteTestClassName = buildString {
append(fullPackage)
append(getTestNameSuffix(data))
append(getDefaultSuiteTestClassName(testClass.java.simpleName))
}
getDefaultSuiteTestClassName(testClass.java.simpleName)
testClass(
testClass,
suiteTestClassName = suiteTestClassName,
useJunit4 = false
) {
method(FrontendConfiguratorTestModel(factory::class, data))
init(data)
}
}
private fun getTestNameSuffix(data: AnalysisApiTestConfiguratorFactoryData): String {
return buildString {
append(data.frontend.suffix.capitalizeAsciiOnly())
append(data.analysisApiMode.suffix.capitalizeAsciiOnly());
append(data.analysisSessionMode.suffix.capitalizeAsciiOnly()); append("Analysis")
append(data.moduleKind.suffix.capitalizeAsciiOnly()); append("Module")
}
}
private fun getPackageName(prefix: String, testClass: KClass<*>): String {
val basePrefix = "org.jetbrains.kotlin.analysis.api.${prefix.lowercase()}.test.cases.generated"
val packagePrefix = testClass.java.name
.substringAfter("org.jetbrains.kotlin.analysis.api.impl.base.test.")
.substringBeforeLast('.', "")
return if (packagePrefix.isEmpty()) "$basePrefix." else "$basePrefix.$packagePrefix."
}
@@ -6,7 +6,10 @@
package org.jetbrains.kotlin.generators.tests.analysis.api.dsl
import org.jetbrains.annotations.NotNull
import org.jetbrains.kotlin.analysis.test.framework.AnalysisApiTestConfiguratorFactory
import org.jetbrains.kotlin.analysis.test.framework.AnalysisApiTestConfiguratorFactoryData
import org.jetbrains.kotlin.analysis.test.framework.AnalysisApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.test.framework.FrontendKind
import org.jetbrains.kotlin.generators.MethodGenerator
import org.jetbrains.kotlin.generators.model.MethodModel
import org.jetbrains.kotlin.utils.Printer
@@ -18,20 +21,36 @@ object FrontendConfiguratorTestGenerator : MethodGenerator<FrontendConfiguratorT
override fun generateSignature(method: FrontendConfiguratorTestModel, p: Printer): Unit = with(p) {
println("@NotNull")
println("@Override")
print("public FrontendApiTestConfiguratorService getConfigurator()")
print("public AnalysisApiTestConfiguratorService getConfigurator()")
}
override fun generateBody(method: FrontendConfiguratorTestModel, p: Printer): Unit = with(p) {
print("return ")
printWithNoIndent(method.frontendConfiguratorClass.simpleName)
printWithNoIndent(".INSTANCE")
printlnWithNoIndent(";")
printWithNoIndent(method.frontendConfiguratorFactoryClass.simpleName)
printlnWithNoIndent(".INSTANCE.createConfigurator(")
pushIndent()
println("new ", AnalysisApiTestConfiguratorFactoryData::class.simpleName, "(")
pushIndent()
println(method.data.frontend.asJavaCode(), ",")
println(method.data.moduleKind.asJavaCode(), ",")
println(method.data.analysisSessionMode.asJavaCode(), ",")
println(method.data.analysisApiMode.asJavaCode())
popIndent()
println(")")
popIndent()
println(");")
}
private fun Enum<*>.asJavaCode(): String = "${this::class.simpleName}.${this.name}"
}
object FrontendConfiguratorTestModelKind : MethodModel.Kind()
class FrontendConfiguratorTestModel(val frontendConfiguratorClass: KClass<out AnalysisApiTestConfiguratorService>) : MethodModel {
class FrontendConfiguratorTestModel(
val frontendConfiguratorFactoryClass: KClass<out AnalysisApiTestConfiguratorFactory>,
val data: AnalysisApiTestConfiguratorFactoryData
) : MethodModel {
override val kind: MethodModel.Kind get() = FrontendConfiguratorTestModelKind
override val name: String get() = "getConfigurator"
override val dataString: String? get() = null
@@ -43,8 +62,14 @@ class FrontendConfiguratorTestModel(val frontendConfiguratorClass: KClass<out An
override fun imports(): Collection<Class<*>> {
return buildList {
add(NotNull::class.java)
add(frontendConfiguratorFactoryClass.java)
add(AnalysisApiTestConfiguratorFactoryData::class.java)
add(AnalysisApiTestConfiguratorService::class.java)
add(frontendConfiguratorClass.java)
add(data.moduleKind::class.java)
add(data.frontend::class.java)
add(data.analysisSessionMode::class.java)
add(data.analysisApiMode::class.java)
}
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2022 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.generators.tests.analysis.api.dsl
import org.jetbrains.kotlin.analysis.test.framework.*
typealias TestFilter = (AnalysisApiTestConfiguratorFactoryData) -> Boolean
infix fun TestFilter.and(other: TestFilter): TestFilter =
{ data -> this(data) && other(data) }
infix fun TestFilter.or(other: TestFilter): TestFilter =
{ data -> this(data) || other(data) }
fun frontendIs(vararg frontends: FrontendKind): TestFilter =
{ it.frontend in frontends }
fun testModuleKindIs(vararg moduleKinds: TestModuleKind): TestFilter =
{ it.moduleKind in moduleKinds }
fun analysisSessionModeIs(vararg modes: AnalysisSessionMode): TestFilter =
{ it.analysisSessionMode in modes }
fun analysisApiModeIs(vararg modes: AnalysisApiMode): TestFilter =
{ it.analysisApiMode in modes }
@@ -5,206 +5,14 @@
package org.jetbrains.kotlin.generators.tests.analysis.api.dsl
import org.jetbrains.kotlin.analysis.api.descriptors.test.KtFe10AnalysisApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.fir.FirAnalysisApiDependentModeTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.fir.FirAnalysisApiNormalModeTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.fir.utils.libraries.binary.LibraryAnalysisApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.fir.utils.libraries.source.LibrarySourceAnalysisApiTestConfiguratorService
import org.jetbrains.kotlin.analysis.api.standalone.StandaloneModeConfiguratorService
import org.jetbrains.kotlin.analysis.test.framework.AnalysisApiTestConfiguratorService
import org.jetbrains.kotlin.generators.TestGroup
import org.jetbrains.kotlin.generators.TestGroupSuite
import org.jetbrains.kotlin.generators.getDefaultSuiteTestClassName
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import kotlin.reflect.KClass
import org.jetbrains.kotlin.analysis.test.framework.AnalysisApiTestConfiguratorFactoryData
internal data class FirAndFe10TestGroup(
val suite: TestGroupSuite,
val directory: String?,
val testModuleKinds: List<TestModuleKind>,
)
internal sealed class TestModuleKind(val componentName: String) {
object SOURCE : TestModuleKind("source")
object LIBRARY : TestModuleKind("library")
object LIBRARY_SOURCE : TestModuleKind("librarySource")
object STANDALONE_MODE : TestModuleKind("StandaloneMode")
companion object {
val SOURCE_ONLY by lazy(LazyThreadSafetyMode.NONE) { listOf(SOURCE) }
val SOURCE_AND_LIBRARY_SOURCE by lazy(LazyThreadSafetyMode.NONE) { listOf(SOURCE, LIBRARY_SOURCE) }
}
}
internal enum class AnalysisMode(val suffix: String) {
NORMAL("NormalMode"),
DEPENDENT("DependentMode"),
}
internal enum class Frontend(val prefix: String) {
FIR("Fir"),
FE10("Fe10"),
}
internal fun FirAndFe10TestGroup.test(
baseClass: KClass<*>,
generateFe10: Boolean = true,
analysisModesForFir: List<AnalysisMode> = listOf(AnalysisMode.NORMAL, AnalysisMode.DEPENDENT),
init: TestGroup.TestClass.(module: TestModuleKind) -> Unit,
) {
analysisApiTest(
"analysis/analysis-api-fir/tests",
Frontend.FIR,
baseClass,
analysisModesForFir,
init
)
if (generateFe10) {
analysisApiTest(
"analysis/analysis-api-fe10/tests",
Frontend.FE10,
baseClass,
listOf(AnalysisMode.NORMAL),
init
)
}
}
internal fun TestGroupSuite.test(
baseClass: KClass<*>,
addFe10: Boolean = true,
testModuleKinds: List<TestModuleKind> = TestModuleKind.SOURCE_ONLY,
analysisModesForFir: List<AnalysisMode> = listOf(AnalysisMode.NORMAL, AnalysisMode.DEPENDENT),
init: TestGroup.TestClass.(module: TestModuleKind) -> Unit,
) {
FirAndFe10TestGroup(this, directory = null, testModuleKinds).test(baseClass, addFe10, analysisModesForFir, init)
}
internal fun TestGroupSuite.group(
internal fun AnalysisApiTestGroup.component(
directory: String,
testModuleKinds: List<TestModuleKind> = TestModuleKind.SOURCE_ONLY,
init: FirAndFe10TestGroup.() -> Unit,
filter: (AnalysisApiTestConfiguratorFactoryData) -> Boolean = { true },
init: AnalysisApiTestGroup.() -> Unit,
) {
FirAndFe10TestGroup(this, directory, testModuleKinds).init()
}
internal fun TestGroupSuite.component(
directory: String,
init: FirAndFe10TestGroup.() -> Unit,
) {
group("components/$directory", init = init)
}
private fun FirAndFe10TestGroup.analysisApiTest(
testRoot: String,
frontend: Frontend,
testClass: KClass<*>,
analysisModes: List<AnalysisMode>,
init: TestGroup.TestClass.(module: TestModuleKind) -> Unit,
) {
with(suite) {
val fullTestPath = "analysis/analysis-api/testData" + directory?.let { "/$it" }.orEmpty()
testGroup(testRoot, fullTestPath) {
for (testModuleKind in testModuleKinds) {
for (analysisMode in analysisModes) {
analysisApiTestClass(frontend, testModuleKind, analysisMode, testClass, init)
}
}
}
}
}
private fun TestGroup.analysisApiTestClass(
frontend: Frontend,
moduleKind: TestModuleKind,
analysisMode: AnalysisMode,
testClass: KClass<*>,
init: TestGroup.TestClass.(module: TestModuleKind) -> Unit
) {
val fullPackage = getPackageName(frontend.prefix, testClass)
val suiteTestClassName = buildString {
append(fullPackage)
append(frontend.prefix)
append(moduleKind.componentName.capitalizeAsciiOnly())
append(analysisMode.suffix)
append(getDefaultSuiteTestClassName(testClass.java.simpleName))
}
getDefaultSuiteTestClassName(testClass.java.simpleName)
val configurator = createConfigurator(frontend, moduleKind, analysisMode)
testClass(
testClass,
suiteTestClassName = suiteTestClassName,
useJunit4 = false
) {
method(FrontendConfiguratorTestModel(configurator))
init(moduleKind)
}
}
internal fun createConfigurator(
frontend: Frontend,
moduleKind: TestModuleKind,
analysisMode: AnalysisMode,
): KClass<out AnalysisApiTestConfiguratorService> = when (frontend) {
Frontend.FIR -> when (moduleKind) {
TestModuleKind.SOURCE -> when (analysisMode) {
AnalysisMode.NORMAL -> FirAnalysisApiNormalModeTestConfiguratorService::class
AnalysisMode.DEPENDENT -> FirAnalysisApiDependentModeTestConfiguratorService::class
}
TestModuleKind.LIBRARY -> {
checkNormalMode(frontend, moduleKind, analysisMode)
LibraryAnalysisApiTestConfiguratorService::class
}
TestModuleKind.LIBRARY_SOURCE -> {
checkNormalMode(frontend, moduleKind, analysisMode)
LibrarySourceAnalysisApiTestConfiguratorService::class
}
TestModuleKind.STANDALONE_MODE -> {
checkNormalMode(frontend, moduleKind, analysisMode)
StandaloneModeConfiguratorService::class
}
}
Frontend.FE10 -> when (moduleKind) {
TestModuleKind.SOURCE -> {
checkNormalMode(frontend, moduleKind, analysisMode)
KtFe10AnalysisApiTestConfiguratorService::class
}
TestModuleKind.LIBRARY -> TODO("TestModuleKind.LIBRARY is unsupported for fe10")
TestModuleKind.LIBRARY_SOURCE -> TODO("TestModuleKind.LIBRARY_SOURCE is unsupported for fe10")
TestModuleKind.STANDALONE_MODE -> TODO("TestModuleKind.STANDALONE_MODE is unsupported for fe10")
}
}
private fun checkNormalMode(
frontend: Frontend,
moduleKind: TestModuleKind,
analysisMode: AnalysisMode
) {
if (analysisMode == AnalysisMode.DEPENDENT) {
TODO("${AnalysisMode.DEPENDENT} is not supported for $frontend with $moduleKind")
}
group("components/$directory", filter, init)
}
private fun getPackageName(prefix: String, testClass: KClass<*>): String {
val basePrefix = "org.jetbrains.kotlin.analysis.api.${prefix.lowercase()}.test.cases"
val packagePrefix = testClass.java.name
.substringAfter("org.jetbrains.kotlin.analysis.api.impl.base.test.")
.substringBeforeLast('.', "")
return if (packagePrefix.isEmpty()) "$basePrefix." else "$basePrefix.$packagePrefix."
}
@@ -1,27 +0,0 @@
/*
* Copyright 2010-2022 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.generators.tests.analysis.api
import org.jetbrains.kotlin.generators.TestGroupSuite
import org.jetbrains.kotlin.analysis.api.standalone.AbstractStandaloneModeSingleModuleTest
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.*
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.AnalysisMode
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.Frontend
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.TestModuleKind
import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.createConfigurator
internal fun TestGroupSuite.generateStandaloneModeTests() {
testGroup(
"analysis/analysis-api-standalone/tests",
"analysis/analysis-api/testData"
) {
val configurator = createConfigurator(Frontend.FIR, TestModuleKind.STANDALONE_MODE, analysisMode = AnalysisMode.NORMAL)
testClass<AbstractStandaloneModeSingleModuleTest> {
method(FrontendConfiguratorTestModel(configurator))
model("standalone/singleModule")
}
}
}