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:
@@ -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
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
|
|||||||
jvmArgs "-Xmx4G"
|
jvmArgs "-Xmx4G"
|
||||||
jvmArgs "-Dkonan.home=${UtilsKt.getKotlinNativeDist(project)}"
|
jvmArgs "-Dkonan.home=${UtilsKt.getKotlinNativeDist(project)}"
|
||||||
enableAssertions = true
|
enableAssertions = true
|
||||||
def sources = File.createTempFile(name,".lst")
|
def sources = File.createTempFile(name, ".lst")
|
||||||
sources.deleteOnExit()
|
sources.deleteOnExit()
|
||||||
def sourcesWriter = sources.newWriter()
|
def sourcesWriter = sources.newWriter()
|
||||||
filesToCompile.each { f ->
|
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
|
def vars = new HashSet<String>() // variables that has the same name as a package
|
||||||
TestModule mainModule = null
|
TestModule mainModule = null
|
||||||
def testFiles = TestDirectivesKt.buildCompileList(project.rootProject.file(src).toPath(), "$outputDirectory/${project.rootProject.file(src).name}")
|
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 text = testFile.text
|
||||||
def filePath = testFile.path
|
def filePath = testFile.path
|
||||||
if (text.contains('COROUTINES_PACKAGE')) {
|
if (text.contains('COROUTINES_PACKAGE')) {
|
||||||
@@ -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]
|
||||||
packages.add(pkg)
|
if (!pkg.startsWith("kotlin")) {
|
||||||
pkg = "$sourceName.$pkg"
|
packages.add(pkg)
|
||||||
text = text.replaceFirst(packagePattern, "package $pkg")
|
pkg = "$sourceName.$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:")
|
||||||
@@ -308,7 +310,7 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
|
|||||||
}
|
}
|
||||||
testFile.text = text
|
testFile.text = text
|
||||||
}
|
}
|
||||||
for (TestFile testFile: testFiles) {
|
for (TestFile testFile : testFiles) {
|
||||||
def text = testFile.text
|
def text = testFile.text
|
||||||
// Find if there are any imports in the file
|
// Find if there are any imports in the file
|
||||||
def matcher = (text =~ ~/${importRegex}(${fullQualified}*)/)
|
def matcher = (text =~ ~/${importRegex}(${fullQualified}*)/)
|
||||||
@@ -373,14 +375,21 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
String normalize(String name) {
|
String normalize(String name) {
|
||||||
return name.replace('.kt', '')
|
return name.replace('.kt', '')
|
||||||
.replace('-','_')
|
.replace('-', '_')
|
||||||
.replace('.', '_')
|
.replace('.', '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
@@ -524,7 +535,7 @@ fun runTest() {
|
|||||||
Map<String, TestModule> modules = compileList.stream()
|
Map<String, TestModule> modules = compileList.stream()
|
||||||
.map { it.module }
|
.map { it.module }
|
||||||
.distinct()
|
.distinct()
|
||||||
.collect(Collectors.toMap({ it.name }, UnaryOperator.identity() ))
|
.collect(Collectors.toMap({ it.name }, UnaryOperator.identity()))
|
||||||
|
|
||||||
List<TestModule> orderedModules = DFS.INSTANCE.topologicalOrder(modules.values()) { module ->
|
List<TestModule> orderedModules = DFS.INSTANCE.topologicalOrder(modules.values()) { module ->
|
||||||
module.dependencies.collect { modules[it] }.findAll { it != null }
|
module.dependencies.collect { modules[it] }.findAll { it != null }
|
||||||
@@ -561,8 +572,8 @@ fun runTest() {
|
|||||||
}
|
}
|
||||||
if (isEnabledForNativeBackend(src)) {
|
if (isEnabledForNativeBackend(src)) {
|
||||||
suite.executeTest(file.name) {
|
suite.executeTest(file.name) {
|
||||||
project.logger.quiet(src)
|
project.logger.quiet(src)
|
||||||
runExecutable()
|
runExecutable()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
suite.skipTest(file.name)
|
suite.skipTest(file.name)
|
||||||
|
|||||||
@@ -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,42 +32,66 @@ 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) {
|
|
||||||
var moduleName = matcher.group(1)
|
|
||||||
val moduleDependencies = matcher.group(2)
|
|
||||||
val moduleFriends = matcher.group(3)
|
|
||||||
|
|
||||||
if (moduleName != null) {
|
while (nextModuleExists || nextFileExists) {
|
||||||
moduleName = moduleName.trim { it <= ' ' }
|
if (nextModuleExists) {
|
||||||
module = TestModule("${srcFile.name}.$moduleName",
|
var moduleName = moduleMatcher.group(1)
|
||||||
moduleDependencies.parseModuleList().map {
|
val moduleDependencies = moduleMatcher.group(2)
|
||||||
if (it != "support") "${srcFile.name}.$it" else it
|
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" })
|
moduleFriends.parseModuleList().map { "${srcFile.name}.$it" })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val fileName = matcher.group(4)
|
nextModuleExists = moduleMatcher.find()
|
||||||
val filePath = "$outputDirectory/$fileName"
|
while (nextFileExists) {
|
||||||
val start = processedChars
|
val fileName = fileMatcher.group(1)
|
||||||
nextFileExists = matcher.find()
|
val filePath = "$outputDirectory/$fileName"
|
||||||
val end = if (nextFileExists) matcher.start() else srcText.length
|
val start = processedChars
|
||||||
val fileText = srcText.substring(start, end)
|
nextFileExists = fileMatcher.find()
|
||||||
processedChars = end
|
val end = when {
|
||||||
if (fileName.endsWith(".kt")) {
|
nextFileExists && nextModuleExists -> Math.min(fileMatcher.start(), moduleMatcher.start())
|
||||||
result.add(TestFile(fileName, filePath, fileText, module))
|
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()
|
?: 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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user