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(
'compiler/testData/codegen/box',
'compiler/codegen/boxInline',
'compiler/compileKotlinAgainstKotlin'
'compiler/testData/codegen/boxInline'
).asFileTree.visit { file ->
if (!file.isDirectory() || !file.file.list().any{ it.endsWith(".kt")})
return
@@ -86,7 +86,7 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
jvmArgs "-Xmx4G"
jvmArgs "-Dkonan.home=${UtilsKt.getKotlinNativeDist(project)}"
enableAssertions = true
def sources = File.createTempFile(name,".lst")
def sources = File.createTempFile(name, ".lst")
sources.deleteOnExit()
def sourcesWriter = sources.newWriter()
filesToCompile.each { f ->
@@ -281,7 +281,7 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
def vars = new HashSet<String>() // variables that has the same name as a package
TestModule mainModule = null
def testFiles = TestDirectivesKt.buildCompileList(project.rootProject.file(src).toPath(), "$outputDirectory/${project.rootProject.file(src).name}")
for (TestFile testFile: testFiles) {
for (TestFile testFile : testFiles) {
def text = testFile.text
def filePath = testFile.path
if (text.contains('COROUTINES_PACKAGE')) {
@@ -290,9 +290,11 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
def pkg
if (text =~ packagePattern) {
pkg = (text =~ packagePattern)[0][1]
packages.add(pkg)
pkg = "$sourceName.$pkg"
text = text.replaceFirst(packagePattern, "package $pkg")
if (!pkg.startsWith("kotlin")) {
packages.add(pkg)
pkg = "$sourceName.$pkg"
text = text.replaceFirst(packagePattern, "package $pkg")
}
} else {
pkg = sourceName
text = insertInTextAfter(text, "\npackage $pkg\n", "@file:")
@@ -308,7 +310,7 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
}
testFile.text = text
}
for (TestFile testFile: testFiles) {
for (TestFile testFile : testFiles) {
def text = testFile.text
// Find if there are any imports in the file
def matcher = (text =~ ~/${importRegex}(${fullQualified}*)/)
@@ -373,14 +375,21 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
testFile.text = res
}
def launcherText = createLauncherFileText(src, imports)
testFiles.add(new TestFile("_launcher.kt", "$outputDirectory/${project.rootProject.file(src).name}/_launcher.kt".toString(),
launcherText, mainModule != null ? mainModule : TestModule.default))
testFiles.add(
new TestFile(
"_launcher.kt",
"$outputDirectory/$src/_launcher.kt".toString(),
launcherText,
mainModule ?: testFiles.collect { it.module }.find { it.isDefaultModule() }
?: TestModule.default()
)
)
return testFiles
}
String normalize(String name) {
return name.replace('.kt', '')
.replace('-','_')
.replace('-', '_')
.replace('.', '_')
}
@@ -420,13 +429,14 @@ fun runTest() {
static def excludeList = [
"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/multiplatform/multiModule/expectActualTypealiasLink.kt", // KT-40137
"compiler/testData/codegen/box/multiplatform/multiModule/expectActualMemberLink.kt", // KT-33091
"compiler/testData/codegen/box/multiplatform/multiModule/expectActualLink.kt", // KT-41901
"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) {
@@ -469,7 +479,8 @@ fun runTest() {
return false
} else {
// 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) {
if (s.contains("NATIVE")) { return false }
}
@@ -524,7 +535,7 @@ fun runTest() {
Map<String, TestModule> modules = compileList.stream()
.map { it.module }
.distinct()
.collect(Collectors.toMap({ it.name }, UnaryOperator.identity() ))
.collect(Collectors.toMap({ it.name }, UnaryOperator.identity()))
List<TestModule> orderedModules = DFS.INSTANCE.topologicalOrder(modules.values()) { module ->
module.dependencies.collect { modules[it] }.findAll { it != null }
@@ -561,8 +572,8 @@ fun runTest() {
}
if (isEnabledForNativeBackend(src)) {
suite.executeTest(file.name) {
project.logger.quiet(src)
runExecutable()
project.logger.quiet(src)
runExecutable()
}
} else {
suite.skipTest(file.name)
@@ -61,7 +61,7 @@ class MultiModuleCompilerInvocations(
fun produceProgram(compileList: List<TestFile>) {
val compileMain = compileList.filter {
it.module.isDefaultModule() || it.module === TestModule.support
it.module.isDefaultModule() || it.module.isSupportModule()
}
compileMain.forEach { f ->
libs.addAll(f.module.dependencies)
@@ -5,20 +5,20 @@
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.Paths
import java.util.regex.Matcher
import java.util.regex.Pattern
import java.io.File
private const val MODULE_DELIMITER = ",\\s*"
// 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/KotlinTestUtils.java
private val FILE_OR_MODULE_PATTERN: Pattern = Pattern.compile("(?://\\s*MODULE:\\s*([^()\\n]+)(?:\\(([^()]+(?:" +
"$MODULE_DELIMITER[^()]+)*)\\))?\\s*(?:\\(([^()]+(?:$MODULE_DELIMITER[^()]+)*)\\))?\\s*)?//\\s*FILE:\\s*(.*)$",
Pattern.MULTILINE)
private val MODULE_PATTERN: Pattern = Pattern.compile("//\\s*MODULE:\\s*([^()\\n]+)(?:\\(([^()]+(?:" +
MODULE_DELIMITER + "[^()]+)*)\\))?\\s*(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\n")
private val FILE_PATTERN = Pattern.compile("//\\s*FILE:\\s*(.*)\n")
private val DIRECTIVE_PATTERN = Pattern.compile("^//\\s*[!]?([A-Z_]+)(:[ \\t]*(.*))?$", Pattern.MULTILINE)
/**
@@ -32,42 +32,66 @@ fun buildCompileList(source: Path, outputDirectory: String): List<TestFile> {
// Remove diagnostic parameters in external tests.
val srcText = srcFile.readText().replace(Regex("<!.*?!>(.*?)<!>")) { match -> match.groupValues[1] }
var supportModule: TestModule? = null
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)
if (!matcher.find()) {
val defaultModule = TestModule.default()
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
result.add(TestFile(srcFile.name, "$outputDirectory/${srcFile.name}", srcText))
result.add(TestFile(srcFile.name, "$outputDirectory/${srcFile.name}", srcText, defaultModule))
} else {
// There are several files
var processedChars = 0
var module: TestModule = TestModule.default
var nextFileExists = true
while (nextFileExists) {
var moduleName = matcher.group(1)
val moduleDependencies = matcher.group(2)
val moduleFriends = matcher.group(3)
var module: TestModule = defaultModule
if (moduleName != null) {
moduleName = moduleName.trim { it <= ' ' }
module = TestModule("${srcFile.name}.$moduleName",
moduleDependencies.parseModuleList().map {
if (it != "support") "${srcFile.name}.$it" else it
},
while (nextModuleExists || nextFileExists) {
if (nextModuleExists) {
var moduleName = moduleMatcher.group(1)
val moduleDependencies = moduleMatcher.group(2)
val moduleFriends = moduleMatcher.group(3)
if (moduleName != null) {
moduleName = moduleName.trim { it <= ' ' }
val dependencies = mutableListOf<String>().apply {
addAll(moduleDependencies.parseModuleList())
if (supportModule != null && !contains("support")) {
add("support")
}
}.map {
if (it != "support") "${srcFile.name}.$it" else it
}
module = TestModule("${srcFile.name}.$moduleName",
dependencies,
moduleFriends.parseModuleList().map { "${srcFile.name}.$it" })
}
}
val fileName = matcher.group(4)
val filePath = "$outputDirectory/$fileName"
val start = processedChars
nextFileExists = matcher.find()
val end = if (nextFileExists) matcher.start() else srcText.length
val fileText = srcText.substring(start, end)
processedChars = end
if (fileName.endsWith(".kt")) {
result.add(TestFile(fileName, filePath, fileText, module))
nextModuleExists = moduleMatcher.find()
while (nextFileExists) {
val fileName = fileMatcher.group(1)
val filePath = "$outputDirectory/$fileName"
val start = processedChars
nextFileExists = fileMatcher.find()
val end = when {
nextFileExists && nextModuleExists -> Math.min(fileMatcher.start(), moduleMatcher.start())
nextFileExists -> fileMatcher.start()
else -> srcText.length
}
val fileText = srcText.substring(start, end)
processedChars = end
if (fileName.endsWith(".kt")) {
result.add(TestFile(fileName, filePath, fileText, module))
}
if (nextModuleExists && nextFileExists && fileMatcher.start() > moduleMatcher.start()) break
}
}
}
@@ -79,7 +103,7 @@ private fun String?.parseModuleList() = this
?: 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].
*
* There are 2 predefined modules:
@@ -92,14 +116,15 @@ data class TestModule(
val friends: List<String>
) {
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 }
fun versionFiles(version: Int) = this.files.filter { it.version == null || it.version == version }
companion object {
val default = TestModule("default", emptyList(), emptyList())
val support = TestModule("support", emptyList(), emptyList())
@JvmStatic fun default() = TestModule("default", emptyList(), emptyList())
@JvmStatic fun support() = TestModule("support", emptyList(), emptyList())
}
}
@@ -110,7 +135,7 @@ data class TestFile(
val name: String,
val path: String,
var text: String = "",
val module: TestModule = TestModule.default
val module: TestModule
) {
init {
this.module.files.add(this)
@@ -127,8 +152,8 @@ data class TestFile(
val directiveMatcher: Matcher = DIRECTIVE_PATTERN.matcher(text)
while (directiveMatcher.find()) {
val name = directiveMatcher.group(1)
val value = directiveMatcher.group(3)
newDirectives.put(name, value)
val value = directiveMatcher.group(3) ?: ""
newDirectives[name] = value
}
return newDirectives
}