Fixes to Kotlin/Native test directives infrastructure.

* parse files and modules separately
* fix default and support modules usage
* add another way to ignore backend (new directive)
* add new test to exclude list
This commit is contained in:
Pavel Punegov
2021-03-30 10:34:07 +03:00
committed by Space
parent 7d13aaad24
commit 6aa7628808
4 changed files with 90 additions and 55 deletions
@@ -326,8 +326,7 @@ task run_external () {
rootProject.files( rootProject.files(
'compiler/testData/codegen/box', 'compiler/testData/codegen/box',
'compiler/codegen/boxInline', 'compiler/testData/codegen/boxInline'
'compiler/compileKotlinAgainstKotlin'
).asFileTree.visit { file -> ).asFileTree.visit { file ->
if (!file.isDirectory() || !file.file.list().any{ it.endsWith(".kt")}) if (!file.isDirectory() || !file.file.list().any{ it.endsWith(".kt")})
return return
@@ -290,9 +290,11 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
def pkg def pkg
if (text =~ packagePattern) { if (text =~ packagePattern) {
pkg = (text =~ packagePattern)[0][1] pkg = (text =~ packagePattern)[0][1]
if (!pkg.startsWith("kotlin")) {
packages.add(pkg) packages.add(pkg)
pkg = "$sourceName.$pkg" pkg = "$sourceName.$pkg"
text = text.replaceFirst(packagePattern, "package $pkg") text = text.replaceFirst(packagePattern, "package $pkg")
}
} else { } else {
pkg = sourceName pkg = sourceName
text = insertInTextAfter(text, "\npackage $pkg\n", "@file:") text = insertInTextAfter(text, "\npackage $pkg\n", "@file:")
@@ -373,8 +375,15 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
testFile.text = res testFile.text = res
} }
def launcherText = createLauncherFileText(src, imports) def launcherText = createLauncherFileText(src, imports)
testFiles.add(new TestFile("_launcher.kt", "$outputDirectory/${project.rootProject.file(src).name}/_launcher.kt".toString(), testFiles.add(
launcherText, mainModule != null ? mainModule : TestModule.default)) new TestFile(
"_launcher.kt",
"$outputDirectory/$src/_launcher.kt".toString(),
launcherText,
mainModule ?: testFiles.collect { it.module }.find { it.isDefaultModule() }
?: TestModule.default()
)
)
return testFiles return testFiles
} }
@@ -420,13 +429,14 @@ fun runTest() {
static def excludeList = [ static def excludeList = [
"compiler/testData/codegen/boxInline/multiplatform/defaultArguments/receiversAndParametersInLambda.kt", // KT-36880 "compiler/testData/codegen/boxInline/multiplatform/defaultArguments/receiversAndParametersInLambda.kt", // KT-36880
"compiler/testData/compileKotlinAgainstKotlin/specialBridgesInDependencies.kt", // KT-42723 "compiler/testData/codegen/box/compileKotlinAgainstKotlin/specialBridgesInDependencies.kt", // KT-42723
"compiler/testData/codegen/box/collections/kt41123.kt", // KT-42723 "compiler/testData/codegen/box/collections/kt41123.kt", // KT-42723
"compiler/testData/codegen/box/multiplatform/multiModule/expectActualTypealiasLink.kt", // KT-40137 "compiler/testData/codegen/box/multiplatform/multiModule/expectActualTypealiasLink.kt", // KT-40137
"compiler/testData/codegen/box/multiplatform/multiModule/expectActualMemberLink.kt", // KT-33091 "compiler/testData/codegen/box/multiplatform/multiModule/expectActualMemberLink.kt", // KT-33091
"compiler/testData/codegen/box/multiplatform/multiModule/expectActualLink.kt", // KT-41901 "compiler/testData/codegen/box/multiplatform/multiModule/expectActualLink.kt", // KT-41901
"compiler/testData/codegen/box/coroutines/multiModule/", // KT-40121 "compiler/testData/codegen/box/coroutines/multiModule/", // KT-40121
"compiler/testData/codegen/box/defaultArguments/recursiveDefaultArguments.kt" // KT-42684 "compiler/testData/codegen/box/defaultArguments/recursiveDefaultArguments.kt", // KT-42684
"compiler/testData/codegen/box/inlineClasses/nestedInlineClass.kt" // KT-45139
] ]
boolean isEnabledForNativeBackend(String fileName) { boolean isEnabledForNativeBackend(String fileName) {
@@ -469,7 +479,8 @@ fun runTest() {
return false return false
} else { } else {
// No target backend. Check if NATIVE backend is ignored. // No target backend. Check if NATIVE backend is ignored.
def ignoredBackends = findLinesWithPrefixesRemoved(text, "// IGNORE_BACKEND: ") def ignoredBackends = findLinesWithPrefixesRemoved(text, "// IGNORE_BACKEND: ") +
findLinesWithPrefixesRemoved(text, "// DONT_TARGET_EXACT_BACKEND: ")
for (String s : ignoredBackends) { for (String s : ignoredBackends) {
if (s.contains("NATIVE")) { return false } if (s.contains("NATIVE")) { return false }
} }
@@ -61,7 +61,7 @@ class MultiModuleCompilerInvocations(
fun produceProgram(compileList: List<TestFile>) { fun produceProgram(compileList: List<TestFile>) {
val compileMain = compileList.filter { val compileMain = compileList.filter {
it.module.isDefaultModule() || it.module === TestModule.support it.module.isDefaultModule() || it.module.isSupportModule()
} }
compileMain.forEach { f -> compileMain.forEach { f ->
libs.addAll(f.module.dependencies) libs.addAll(f.module.dependencies)
@@ -5,20 +5,20 @@
package org.jetbrains.kotlin package org.jetbrains.kotlin
import org.jetbrains.kotlin.TestModule.Companion.default
import org.jetbrains.kotlin.TestModule.Companion.support
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.util.regex.Matcher import java.util.regex.Matcher
import java.util.regex.Pattern import java.util.regex.Pattern
import java.io.File
private const val MODULE_DELIMITER = ",\\s*" private const val MODULE_DELIMITER = ",\\s*"
// These patterns are copies from // These patterns are copies from
// kotlin/compiler/tests-common/tests/org/jetbrains/kotlin/test/TestFiles.java // kotlin/compiler/tests-common/tests/org/jetbrains/kotlin/test/TestFiles.java
// kotlin/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java // kotlin/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinTestUtils.java
private val FILE_OR_MODULE_PATTERN: Pattern = Pattern.compile("(?://\\s*MODULE:\\s*([^()\\n]+)(?:\\(([^()]+(?:" + private val MODULE_PATTERN: Pattern = Pattern.compile("//\\s*MODULE:\\s*([^()\\n]+)(?:\\(([^()]+(?:" +
"$MODULE_DELIMITER[^()]+)*)\\))?\\s*(?:\\(([^()]+(?:$MODULE_DELIMITER[^()]+)*)\\))?\\s*)?//\\s*FILE:\\s*(.*)$", MODULE_DELIMITER + "[^()]+)*)\\))?\\s*(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\n")
Pattern.MULTILINE) private val FILE_PATTERN = Pattern.compile("//\\s*FILE:\\s*(.*)\n")
private val DIRECTIVE_PATTERN = Pattern.compile("^//\\s*[!]?([A-Z_]+)(:[ \\t]*(.*))?$", Pattern.MULTILINE) private val DIRECTIVE_PATTERN = Pattern.compile("^//\\s*[!]?([A-Z_]+)(:[ \\t]*(.*))?$", Pattern.MULTILINE)
/** /**
@@ -32,43 +32,67 @@ fun buildCompileList(source: Path, outputDirectory: String): List<TestFile> {
// Remove diagnostic parameters in external tests. // Remove diagnostic parameters in external tests.
val srcText = srcFile.readText().replace(Regex("<!.*?!>(.*?)<!>")) { match -> match.groupValues[1] } val srcText = srcFile.readText().replace(Regex("<!.*?!>(.*?)<!>")) { match -> match.groupValues[1] }
var supportModule: TestModule? = null
if (srcText.contains("// WITH_COROUTINES")) { if (srcText.contains("// WITH_COROUTINES")) {
result.add(TestFile("helpers.kt", "$outputDirectory/helpers.kt", createTextForHelpers(), TestModule.support)) supportModule = TestModule.support()
result.add(TestFile("helpers.kt", "$outputDirectory/helpers.kt",
createTextForHelpers(), supportModule))
} }
val matcher = FILE_OR_MODULE_PATTERN.matcher(srcText) val defaultModule = TestModule.default()
if (!matcher.find()) { val moduleMatcher = MODULE_PATTERN.matcher(srcText)
val fileMatcher = FILE_PATTERN.matcher(srcText)
var nextModuleExists = moduleMatcher.find()
var nextFileExists = fileMatcher.find()
if (!nextModuleExists && !nextFileExists) {
// There is only one file in the input // There is only one file in the input
result.add(TestFile(srcFile.name, "$outputDirectory/${srcFile.name}", srcText)) result.add(TestFile(srcFile.name, "$outputDirectory/${srcFile.name}", srcText, defaultModule))
} else { } else {
// There are several files // There are several files
var processedChars = 0 var processedChars = 0
var module: TestModule = TestModule.default var module: TestModule = defaultModule
var nextFileExists = true
while (nextFileExists) { while (nextModuleExists || nextFileExists) {
var moduleName = matcher.group(1) if (nextModuleExists) {
val moduleDependencies = matcher.group(2) var moduleName = moduleMatcher.group(1)
val moduleFriends = matcher.group(3) val moduleDependencies = moduleMatcher.group(2)
val moduleFriends = moduleMatcher.group(3)
if (moduleName != null) { if (moduleName != null) {
moduleName = moduleName.trim { it <= ' ' } moduleName = moduleName.trim { it <= ' ' }
module = TestModule("${srcFile.name}.$moduleName", val dependencies = mutableListOf<String>().apply {
moduleDependencies.parseModuleList().map { addAll(moduleDependencies.parseModuleList())
if (supportModule != null && !contains("support")) {
add("support")
}
}.map {
if (it != "support") "${srcFile.name}.$it" else it if (it != "support") "${srcFile.name}.$it" else it
}, }
module = TestModule("${srcFile.name}.$moduleName",
dependencies,
moduleFriends.parseModuleList().map { "${srcFile.name}.$it" }) moduleFriends.parseModuleList().map { "${srcFile.name}.$it" })
} }
}
val fileName = matcher.group(4) nextModuleExists = moduleMatcher.find()
while (nextFileExists) {
val fileName = fileMatcher.group(1)
val filePath = "$outputDirectory/$fileName" val filePath = "$outputDirectory/$fileName"
val start = processedChars val start = processedChars
nextFileExists = matcher.find() nextFileExists = fileMatcher.find()
val end = if (nextFileExists) matcher.start() else srcText.length val end = when {
nextFileExists && nextModuleExists -> Math.min(fileMatcher.start(), moduleMatcher.start())
nextFileExists -> fileMatcher.start()
else -> srcText.length
}
val fileText = srcText.substring(start, end) val fileText = srcText.substring(start, end)
processedChars = end processedChars = end
if (fileName.endsWith(".kt")) { if (fileName.endsWith(".kt")) {
result.add(TestFile(fileName, filePath, fileText, module)) result.add(TestFile(fileName, filePath, fileText, module))
} }
if (nextModuleExists && nextFileExists && fileMatcher.start() > moduleMatcher.start()) break
}
} }
} }
return result return result
@@ -79,7 +103,7 @@ private fun String?.parseModuleList() = this
?: emptyList() ?: emptyList()
/** /**
* Test module from the test source declared by the [FILE_OR_MODULE_PATTERN]. * Test module from the test source declared by the [MODULE_PATTERN].
* Module should have a [name] and could have [dependencies] on other modules and [friends]. * Module should have a [name] and could have [dependencies] on other modules and [friends].
* *
* There are 2 predefined modules: * There are 2 predefined modules:
@@ -92,14 +116,15 @@ data class TestModule(
val friends: List<String> val friends: List<String>
) { ) {
val files = mutableListOf<TestFile>() val files = mutableListOf<TestFile>()
fun isDefaultModule() = this == default || name.endsWith(".main") fun isDefaultModule() = this.name == "default" || name.endsWith(".main")
fun isSupportModule() = this.name == "support"
val hasVersions get() = this.files.any { it.version != null } val hasVersions get() = this.files.any { it.version != null }
fun versionFiles(version: Int) = this.files.filter { it.version == null || it.version == version } fun versionFiles(version: Int) = this.files.filter { it.version == null || it.version == version }
companion object { companion object {
val default = TestModule("default", emptyList(), emptyList()) @JvmStatic fun default() = TestModule("default", emptyList(), emptyList())
val support = TestModule("support", emptyList(), emptyList()) @JvmStatic fun support() = TestModule("support", emptyList(), emptyList())
} }
} }
@@ -110,7 +135,7 @@ data class TestFile(
val name: String, val name: String,
val path: String, val path: String,
var text: String = "", var text: String = "",
val module: TestModule = TestModule.default val module: TestModule
) { ) {
init { init {
this.module.files.add(this) this.module.files.add(this)
@@ -127,8 +152,8 @@ data class TestFile(
val directiveMatcher: Matcher = DIRECTIVE_PATTERN.matcher(text) val directiveMatcher: Matcher = DIRECTIVE_PATTERN.matcher(text)
while (directiveMatcher.find()) { while (directiveMatcher.find()) {
val name = directiveMatcher.group(1) val name = directiveMatcher.group(1)
val value = directiveMatcher.group(3) val value = directiveMatcher.group(3) ?: ""
newDirectives.put(name, value) newDirectives[name] = value
} }
return newDirectives return newDirectives
} }