[Gradle] Migrate MppHighlightingTestDataWithGradleIT to new test DSL
^KT-65528 In Progress
This commit is contained in:
committed by
Space Team
parent
a40459c520
commit
0f8a416496
+140
-129
@@ -5,146 +5,107 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle
|
package org.jetbrains.kotlin.gradle
|
||||||
|
|
||||||
import org.jetbrains.kotlin.gradle.testbase.enableCacheRedirector
|
import org.gradle.util.GradleVersion
|
||||||
|
import org.jetbrains.kotlin.gradle.testbase.*
|
||||||
import org.jetbrains.kotlin.gradle.util.modify
|
import org.jetbrains.kotlin.gradle.util.modify
|
||||||
import org.junit.Before
|
import org.jetbrains.kotlin.gradle.util.capitalize
|
||||||
import org.junit.Test
|
import org.junit.jupiter.api.DisplayName
|
||||||
import org.junit.runner.RunWith
|
import org.junit.jupiter.api.extension.ExtensionContext
|
||||||
import org.junit.runners.Parameterized
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.Arguments
|
||||||
|
import org.junit.jupiter.params.provider.ArgumentsSource
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.nio.file.Files
|
||||||
|
import java.util.stream.Stream
|
||||||
|
import kotlin.io.path.appendText
|
||||||
|
import kotlin.streams.asStream
|
||||||
|
import kotlin.streams.toList
|
||||||
|
|
||||||
@RunWith(Parameterized::class)
|
@DisplayName("Check Multiplatform IDE highlighting projects")
|
||||||
class MppHighlightingTestDataWithGradleIT : BaseGradleIT() {
|
@MppGradlePluginTests
|
||||||
override val defaultGradleVersion: GradleVersionRequired = GradleVersionRequired.FOR_MPP_SUPPORT
|
internal class MppHighlightingTestDataWithGradleIT : KGPBaseTest() {
|
||||||
|
|
||||||
@Test
|
@GradleWithMppHighlightingTest
|
||||||
fun runTestK2NativeCli() = doTest(CliCompiler.NATIVE)
|
fun runTest(
|
||||||
|
gradleVersion: GradleVersion,
|
||||||
@Test
|
cliCompiler: CliCompiler,
|
||||||
fun runTestK2MetadataCli() = doTest(CliCompiler.K2METADATA)
|
testDataDir: File,
|
||||||
|
sourceRoots: List<TestCaseSourceRoot>
|
||||||
@Before
|
) {
|
||||||
fun cleanup() {
|
project("mpp-source-set-hierarchy-analysis", gradleVersion) {
|
||||||
project.setupWorkingDir(false)
|
val expectedErrorsPerSourceSetName = sourceRoots.associate { sourceRoot ->
|
||||||
project.gradleSettingsScript().modify { it.lines().filter { !it.startsWith("include") }.joinToString("\n") }
|
sourceRoot.kotlinSourceSetName to testDataDir.resolve(sourceRoot.directoryName).walkTopDown()
|
||||||
project.projectDir.resolve("src").deleteRecursively()
|
.filter { it.extension == "kt" }
|
||||||
project.gradleBuildScript().modify { line ->
|
.map { CodeWithErrorInfo.parse(it.readText()) }.toList()
|
||||||
line.lines().dropLastWhile { it != buildScriptCustomizationMarker }.joinToString("\n")
|
.flatMap { it.errorInfo }
|
||||||
}
|
|
||||||
|
|
||||||
project.projectDir.toPath().enableCacheRedirector()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val project by lazy { Project("mpp-source-set-hierarchy-analysis") }
|
|
||||||
|
|
||||||
private fun doTest(cliCompiler: CliCompiler) = with(project) {
|
|
||||||
val expectedErrorsPerSourceSetName = sourceRoots.associate { sourceRoot ->
|
|
||||||
sourceRoot.kotlinSourceSetName to testDataDir.resolve(sourceRoot.directoryName).walkTopDown()
|
|
||||||
.filter { it.extension == "kt" }
|
|
||||||
.map { CodeWithErrorInfo.parse(it.readText()) }.toList()
|
|
||||||
.flatMap { it.errorInfo }
|
|
||||||
}
|
|
||||||
|
|
||||||
// put sources into project dir:
|
|
||||||
sourceRoots.forEach { sourceRoot ->
|
|
||||||
val sourceSetDir = projectDir.resolve(sourceRoot.gradleSrcDir)
|
|
||||||
testDataDir.resolve(sourceRoot.directoryName).copyRecursively(sourceSetDir)
|
|
||||||
sourceSetDir.walkTopDown().filter { it.isFile }.forEach { file ->
|
|
||||||
file.modify { CodeWithErrorInfo.parse(file.readText()).code }
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// create Gradle Kotlin source sets for project roots:
|
// put sources into project dir:
|
||||||
val scriptCustomization = buildString {
|
|
||||||
appendLine()
|
|
||||||
appendLine("kotlin {\n sourceSets {")
|
|
||||||
sourceRoots.forEach { sourceRoot ->
|
sourceRoots.forEach { sourceRoot ->
|
||||||
if (sourceRoot.kotlinSourceSetName != "commonMain") {
|
val sourceSetDir = projectPath.resolve(sourceRoot.gradleSrcDir).toFile()
|
||||||
appendLine(
|
testDataDir.resolve(sourceRoot.directoryName).copyRecursively(sourceSetDir)
|
||||||
""" create("${sourceRoot.kotlinSourceSetName}") {
|
sourceSetDir.walkTopDown().filter { it.isFile }.forEach { file ->
|
||||||
| dependsOn(getByName("commonMain"))
|
file.modify { CodeWithErrorInfo.parse(file.readText()).code }
|
||||||
| listOf(${cliCompiler.targets.joinToString { "$it()" }}).forEach {
|
|
||||||
| it.compilations["main"].defaultSourceSet.dependsOn(this@create)
|
|
||||||
| }
|
|
||||||
| }
|
|
||||||
|
|
|
||||||
""".trimMargin()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
appendLine(" // commonMain source set used for common module")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add dependencies using dependsOn:
|
// create Gradle Kotlin source sets for project roots:
|
||||||
sourceRoots.forEach { sourceRoot ->
|
val scriptCustomization = buildString {
|
||||||
sourceRoot.dependencies.forEach { dependency ->
|
appendLine()
|
||||||
sourceRoots.find { it.qualifiedName == dependency }?.let { depSourceRoot ->
|
appendLine("kotlin {\n sourceSets {")
|
||||||
val depSourceSet = depSourceRoot.kotlinSourceSetName
|
sourceRoots.forEach { sourceRoot ->
|
||||||
appendLine(""" getByName("${sourceRoot.kotlinSourceSetName}").dependsOn(getByName("$depSourceSet"))""")
|
if (sourceRoot.kotlinSourceSetName != "commonMain") {
|
||||||
|
appendLine(
|
||||||
|
"""
|
||||||
|
| create("${sourceRoot.kotlinSourceSetName}") {
|
||||||
|
| dependsOn(getByName("commonMain"))
|
||||||
|
| listOf(${cliCompiler.targets.joinToString { "$it()" }}).forEach {
|
||||||
|
| it.compilations["main"].defaultSourceSet.dependsOn(this@create)
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
|
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
appendLine(" // commonMain source set used for common module")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add dependencies using dependsOn:
|
||||||
|
sourceRoots.forEach { sourceRoot ->
|
||||||
|
sourceRoot.dependencies.forEach { dependency ->
|
||||||
|
sourceRoots.find { it.qualifiedName == dependency }?.let { depSourceRoot ->
|
||||||
|
val depSourceSet = depSourceRoot.kotlinSourceSetName
|
||||||
|
appendLine(""" getByName("${sourceRoot.kotlinSourceSetName}").dependsOn(getByName("$depSourceSet"))""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appendLine(" }\n}")
|
||||||
}
|
}
|
||||||
appendLine(" }\n}")
|
|
||||||
}
|
|
||||||
|
|
||||||
gradleBuildScript().appendText("\n" + scriptCustomization)
|
buildGradleKts.appendText("\n" + scriptCustomization)
|
||||||
|
|
||||||
val tasks = sourceRoots.map { "compile" + it.kotlinSourceSetName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } + "KotlinMetadata" }
|
val tasks = sourceRoots.map {
|
||||||
|
"compile" + it.kotlinSourceSetName.capitalize() + "KotlinMetadata"
|
||||||
|
}
|
||||||
|
|
||||||
build(*tasks.toTypedArray()) {
|
|
||||||
if (expectedErrorsPerSourceSetName.values.all { it.all(ErrorInfo::isAllowedInCli) }) {
|
if (expectedErrorsPerSourceSetName.values.all { it.all(ErrorInfo::isAllowedInCli) }) {
|
||||||
assertSuccessful()
|
build(*tasks.toTypedArray())
|
||||||
} else {
|
} else {
|
||||||
assertFailed() // TODO: check the exact error message in the output, not just that the build failed
|
buildAndFail(*tasks.toTypedArray())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val testDataRoot =
|
|
||||||
File("../../../idea/testData/multiModuleHighlighting/multiplatform")
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@Parameterized.Parameters(name = "{0}")
|
|
||||||
fun testData() = testDataRoot.listFiles()!!.filter { it.isDirectory }.mapNotNull { testDataDir ->
|
|
||||||
val testDataSourceRoots = checkNotNull(testDataDir.listFiles())
|
|
||||||
val sourceRoots = testDataSourceRoots.map { TestCaseSourceRoot.parse(it.name) }
|
|
||||||
|
|
||||||
arrayOf(testDataDir.name, testDataDir, sourceRoots).takeIf { isTestSuiteValidForCommonCode(testDataDir, sourceRoots) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val bannedDependencies = setOf("fulljdk", "stdlib", "coroutines")
|
|
||||||
|
|
||||||
const val testSourceRootSuffix = "tests"
|
|
||||||
|
|
||||||
private const val buildScriptCustomizationMarker = "// customized content below"
|
|
||||||
|
|
||||||
private fun isTestSuiteValidForCommonCode(testDataDir: File, sourceRoots: List<TestCaseSourceRoot>): Boolean {
|
|
||||||
sourceRoots.forEach {
|
|
||||||
val bannedDepsFound = bannedDependencies.intersect(it.dependencies)
|
|
||||||
if (bannedDepsFound.isNotEmpty())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Java sources can't be used in intermediate source sets
|
|
||||||
if (testDataDir.walkTopDown().any { it.extension == "java" })
|
|
||||||
return false
|
|
||||||
|
|
||||||
// Cannot test !CHECK_HIGHLIGHTING in CLI
|
|
||||||
if (testDataDir.walkTopDown().filter { it.isFile }.any { "!CHECK_HIGHLIGHTING" in it.readText() })
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class TestCaseSourceRoot(
|
data class TestCaseSourceRoot(
|
||||||
val directoryName: String,
|
val directoryName: String,
|
||||||
val qualifiedNameParts: Iterable<String>,
|
val qualifiedNameParts: Iterable<String>,
|
||||||
val dependencies: Iterable<String>,
|
val dependencies: Iterable<String>,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val TEST_SOURCE_ROOT_SUFFIX = "tests"
|
||||||
|
private const val COMMON_SOURCE_ROOT_NAME = "common"
|
||||||
|
|
||||||
fun parse(directoryName: String): TestCaseSourceRoot {
|
fun parse(directoryName: String): TestCaseSourceRoot {
|
||||||
val parts = directoryName.split("_")
|
val parts = directoryName.split("_")
|
||||||
|
|
||||||
@@ -156,21 +117,19 @@ class MppHighlightingTestDataWithGradleIT : BaseGradleIT() {
|
|||||||
|
|
||||||
val platformIndex = when (nameParts.size) {
|
val platformIndex = when (nameParts.size) {
|
||||||
1 -> 0
|
1 -> 0
|
||||||
else -> if (nameParts.last() == testSourceRootSuffix) 0 else 1
|
else -> if (nameParts.last() == TEST_SOURCE_ROOT_SUFFIX) 0 else 1
|
||||||
}
|
}
|
||||||
|
|
||||||
val additionalDependencies = mutableListOf<String>().apply {
|
val additionalDependencies = mutableListOf<String>().apply {
|
||||||
if (nameParts[platformIndex] != commonSourceRootName)
|
if (nameParts[platformIndex] != COMMON_SOURCE_ROOT_NAME)
|
||||||
add(partsToQualifiedName(nameParts.take(platformIndex) + commonSourceRootName + nameParts.drop(platformIndex + 1)))
|
add(partsToQualifiedName(nameParts.take(platformIndex) + COMMON_SOURCE_ROOT_NAME + nameParts.drop(platformIndex + 1)))
|
||||||
if (nameParts.last() == testSourceRootSuffix)
|
if (nameParts.last() == TEST_SOURCE_ROOT_SUFFIX)
|
||||||
add(partsToQualifiedName(nameParts.dropLast(1)))
|
add(partsToQualifiedName(nameParts.dropLast(1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return TestCaseSourceRoot(directoryName, nameParts, deps + additionalDependencies)
|
return TestCaseSourceRoot(directoryName, nameParts, deps + additionalDependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val commonSourceRootName = "common"
|
|
||||||
|
|
||||||
private fun partsToQualifiedName(parts: Iterable<String>) = parts.joinToString("")
|
private fun partsToQualifiedName(parts: Iterable<String>) = parts.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +137,7 @@ class MppHighlightingTestDataWithGradleIT : BaseGradleIT() {
|
|||||||
get() = partsToQualifiedName(qualifiedNameParts)
|
get() = partsToQualifiedName(qualifiedNameParts)
|
||||||
|
|
||||||
val kotlinSourceSetName
|
val kotlinSourceSetName
|
||||||
get() = "intermediate${qualifiedName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}"
|
get() = "intermediate${qualifiedName.capitalize()}"
|
||||||
|
|
||||||
val gradleSrcDir
|
val gradleSrcDir
|
||||||
get() = "src/$kotlinSourceSetName/kotlin"
|
get() = "src/$kotlinSourceSetName/kotlin"
|
||||||
@@ -210,22 +169,74 @@ class MppHighlightingTestDataWithGradleIT : BaseGradleIT() {
|
|||||||
) {
|
) {
|
||||||
val isAllowedInCli
|
val isAllowedInCli
|
||||||
get() = when (expectedErrorKind) {
|
get() = when (expectedErrorKind) {
|
||||||
"NO_ACTUAL_FOR_EXPECT", null /*TODO are some nulls better than others?*/ -> true
|
"NO_ACTUAL_FOR_EXPECT", null -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class CliCompiler(val targets: List<String>) {
|
internal enum class CliCompiler(
|
||||||
K2METADATA(listOf("jvm", "js")), NATIVE(listOf("linuxX64", "linuxArm64"))
|
val targets: List<String>
|
||||||
|
) {
|
||||||
|
K2METADATA(listOf("jvm", "js")),
|
||||||
|
NATIVE(listOf("linuxX64", "linuxArm64"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused") // used for parameter string representation in test output
|
class GradleAndMppHighlightingProvider : GradleArgumentsProvider() {
|
||||||
@Parameterized.Parameter(0)
|
private val testDataRoot = File("../../../idea/testData/multiModuleHighlighting/multiplatform")
|
||||||
lateinit var testCaseName: String
|
|
||||||
|
|
||||||
@Parameterized.Parameter(1)
|
private val bannedDependencies = setOf("fulljdk", "stdlib", "coroutines")
|
||||||
lateinit var testDataDir: File
|
|
||||||
|
|
||||||
@Parameterized.Parameter(2)
|
private fun isTestSuiteValidForCommonCode(
|
||||||
lateinit var sourceRoots: List<TestCaseSourceRoot>
|
testDataDir: File,
|
||||||
|
sourceRoots: List<TestCaseSourceRoot>
|
||||||
|
): Boolean = when {
|
||||||
|
sourceRoots.any { bannedDependencies.intersect(it.dependencies.toSet()).isNotEmpty() } -> false
|
||||||
|
// Java sources can't be used in intermediate source sets
|
||||||
|
testDataDir.walkTopDown().any { it.extension == "java" } -> false
|
||||||
|
// Cannot test !CHECK_HIGHLIGHTING in CLI
|
||||||
|
testDataDir.walkTopDown().filter { it.isFile }.any { "!CHECK_HIGHLIGHTING" in it.readText() } -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val testData = testDataRoot
|
||||||
|
.walkTopDown()
|
||||||
|
.maxDepth(1)
|
||||||
|
.filter { it.isDirectory && Files.newDirectoryStream(it.toPath()).use { stream -> stream.toList().isNotEmpty() } }
|
||||||
|
.map { testDataDir ->
|
||||||
|
Pair(
|
||||||
|
testDataDir,
|
||||||
|
Files.newDirectoryStream(testDataDir.toPath()).use { stream ->
|
||||||
|
stream.map { TestCaseSourceRoot.parse(it.fileName.toString()) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.filter {
|
||||||
|
isTestSuiteValidForCommonCode(it.first, it.second)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
override fun provideArguments(
|
||||||
|
context: ExtensionContext
|
||||||
|
): Stream<out Arguments> {
|
||||||
|
val gradleVersions = super.provideArguments(context).map { it.get().first() as GradleVersion }.toList()
|
||||||
|
|
||||||
|
return gradleVersions
|
||||||
|
.flatMap { gradleVersion ->
|
||||||
|
CliCompiler.entries.flatMap { cliCompiler ->
|
||||||
|
testData.map {
|
||||||
|
Arguments.of(gradleVersion, cliCompiler, it.first, it.second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.asSequence()
|
||||||
|
.asStream()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
@GradleTestVersions
|
||||||
|
@ParameterizedTest(name = "{0} - {1} - {2} - {3}: {displayName}")
|
||||||
|
@ArgumentsSource(GradleAndMppHighlightingProvider::class)
|
||||||
|
annotation class GradleWithMppHighlightingTest
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user