[Native][tests] Remove external tests from old test infra
This commit is contained in:
@@ -39,7 +39,6 @@ apply plugin: 'kotlin'
|
|||||||
configurations {
|
configurations {
|
||||||
cli_bc
|
cli_bc
|
||||||
update_tests
|
update_tests
|
||||||
update_stdlib_tests
|
|
||||||
|
|
||||||
nopPluginApi
|
nopPluginApi
|
||||||
api.extendsFrom nopPluginApi
|
api.extendsFrom nopPluginApi
|
||||||
@@ -50,10 +49,6 @@ dependencies {
|
|||||||
cli_bc project(path: ':kotlin-native:backend.native', configuration: 'cli_bcApiElements')
|
cli_bc project(path: ':kotlin-native:backend.native', configuration: 'cli_bcApiElements')
|
||||||
}
|
}
|
||||||
ext.testOutputRoot = rootProject.file("test.output").absolutePath
|
ext.testOutputRoot = rootProject.file("test.output").absolutePath
|
||||||
ext.externalTestsDir = project.file("build/external")
|
|
||||||
ext.externalStdlibTestsDir = project.file("build/stdlib_external/stdlib")
|
|
||||||
externalTestsDir.mkdirs()
|
|
||||||
externalStdlibTestsDir.mkdirs()
|
|
||||||
|
|
||||||
ext.platformManager = project.project(":kotlin-native").platformManager
|
ext.platformManager = project.project(":kotlin-native").platformManager
|
||||||
ext.target = platformManager.targetManager(project.testTarget).target
|
ext.target = platformManager.targetManager(project.testTarget).target
|
||||||
@@ -89,11 +84,6 @@ if (cacheTesting != null) {
|
|||||||
dependsOn cacheTesting.buildCacheTask
|
dependsOn cacheTesting.buildCacheTask
|
||||||
extraOpts cacheTesting.compilerArgs
|
extraOpts cacheTesting.compilerArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(RunExternalTestGroup.class).configureEach {
|
|
||||||
dependsOn cacheTesting.buildCacheTask
|
|
||||||
flags = (flags ?: []) + cacheTesting.compilerArgs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable two-stage test compilation if the test_two_stage property is set.
|
// Enable two-stage test compilation if the test_two_stage property is set.
|
||||||
@@ -103,10 +93,6 @@ tasks.withType(KonanCompileNativeBinary.class).configureEach {
|
|||||||
enableTwoStageCompilation = twoStageEnabled
|
enableTwoStageCompilation = twoStageEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(RunExternalTestGroup.class).configureEach {
|
|
||||||
enableTwoStageCompilation = twoStageEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
ext.isExperimentalMM = project.globalTestArgs.contains("-memory-model") && project.globalTestArgs.contains("experimental")
|
ext.isExperimentalMM = project.globalTestArgs.contains("-memory-model") && project.globalTestArgs.contains("experimental")
|
||||||
ext.isNoopGC = project.globalTestArgs.contains("-Xgc=noop")
|
ext.isNoopGC = project.globalTestArgs.contains("-Xgc=noop")
|
||||||
|
|
||||||
@@ -115,10 +101,6 @@ if (project.globalTestArgs.contains("-g")) {
|
|||||||
tasks.withType(KonanCompileNativeBinary.class).configureEach {
|
tasks.withType(KonanCompileNativeBinary.class).configureEach {
|
||||||
extraOpts "-Xbinary=runtimeAssertionsMode=panic"
|
extraOpts "-Xbinary=runtimeAssertionsMode=panic"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(RunExternalTestGroup.class).configureEach {
|
|
||||||
flags = (flags ?: []) + "-Xbinary=runtimeAssertionsMode=panic"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@@ -221,18 +203,13 @@ task sanity {
|
|||||||
// Collect reports in one json.
|
// Collect reports in one json.
|
||||||
task resultsTask() {
|
task resultsTask() {
|
||||||
doLast {
|
doLast {
|
||||||
List<KonanTestGroupReport> reports = []
|
|
||||||
def statistics = new Statistics()
|
def statistics = new Statistics()
|
||||||
tasks.withType(RunExternalTestGroup).matching { it.state.executed }.each {
|
|
||||||
reports += new KonanTestGroupReport(it.name, it.testGroupReporter.suiteReports)
|
|
||||||
statistics.add(it.testGroupReporter.statistics)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(KonanGTest).matching { it.state.executed }.each {
|
tasks.withType(KonanGTest).matching { it.state.executed }.each {
|
||||||
statistics.add(it.statistics)
|
statistics.add(it.statistics)
|
||||||
}
|
}
|
||||||
|
|
||||||
ExternalReportUtilsKt.saveReport("$testOutputExternal/reports.json", statistics, reports)
|
ExternalReportUtilsKt.saveReport("$testOutputExternal/reports.json", statistics)
|
||||||
use(KonanTestSuiteReportKt) {
|
use(KonanTestSuiteReportKt) {
|
||||||
project.logger.quiet("DONE.\n\n" +
|
project.logger.quiet("DONE.\n\n" +
|
||||||
"TOTAL: $statistics.total\n" +
|
"TOTAL: $statistics.total\n" +
|
||||||
@@ -344,38 +321,6 @@ Task dynamicTest(String name, Closure<KonanDynamicTest> configureClosure) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, List<String>> ignoredTests = [:]
|
|
||||||
|
|
||||||
task run_external () {
|
|
||||||
outputs.upToDateWhen{ false }
|
|
||||||
|
|
||||||
rootProject.files(
|
|
||||||
'compiler/testData/codegen/box',
|
|
||||||
'compiler/testData/codegen/boxInline'
|
|
||||||
).asFileTree.visit { file ->
|
|
||||||
if (!file.isDirectory() || !file.file.list().any{ it.endsWith(".kt")})
|
|
||||||
return
|
|
||||||
def taskPath = rootProject.relativePath(file.file)
|
|
||||||
def fileSeparator = System.properties["file.separator"]
|
|
||||||
def taskName = taskPath.split("\\$fileSeparator").join('_')
|
|
||||||
project.tasks.register(taskName, RunExternalTestGroup) {
|
|
||||||
it.groupDirectory = taskPath
|
|
||||||
it.finalizedBy resultsTask
|
|
||||||
it.ignoredTests = ignoredTests[taskName]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up dependencies.
|
|
||||||
dependsOn(tasksOf(RunExternalTestGroup))
|
|
||||||
dependsOn("stdlibTest")
|
|
||||||
dependsOn("stdlibTestInWorker")
|
|
||||||
}
|
|
||||||
|
|
||||||
task daily() {
|
|
||||||
dependsOn(run_external)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
task slackReport(type:Reporter) {
|
task slackReport(type:Reporter) {
|
||||||
reportHome = rootProject.file("test.output").absoluteFile
|
reportHome = rootProject.file("test.output").absoluteFile
|
||||||
}
|
}
|
||||||
@@ -5850,51 +5795,6 @@ tasks.register("llvm_variant_dev", KonanDriverTest) {
|
|||||||
outputChecker = { out -> !out.contains(message) }
|
outputChecker = { out -> !out.contains(message) }
|
||||||
}
|
}
|
||||||
|
|
||||||
createStdlibTest('stdlibTest', /* inWorker = */ false)
|
|
||||||
createStdlibTest('stdlibTestInWorker', /*inWorker = */ true)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates tasks to build and execute stdlib tests.
|
|
||||||
*/
|
|
||||||
private void createStdlibTest(String name, boolean inWorker) {
|
|
||||||
KotlinNativeTestKt.createTest(project, name, KonanGTest) { task ->
|
|
||||||
configureStdlibTest(task, inWorker)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures tasks to build and execute stdlib tests.
|
|
||||||
*/
|
|
||||||
private void configureStdlibTest(KonanGTest task, boolean inWorker) {
|
|
||||||
def sources = UtilsKt.getFilesToCompile(project,
|
|
||||||
[ '../../../libraries/stdlib/common/test',
|
|
||||||
'../../../libraries/stdlib/test',
|
|
||||||
'../../../libraries/kotlin.test/common/src/test/kotlin',
|
|
||||||
'stdlib_external/utils.kt',
|
|
||||||
'stdlib_external/jsCollectionFactoriesActuals.kt',
|
|
||||||
'stdlib_external/text'],
|
|
||||||
[ /*'build/stdlib_external/stdlib/test/internalAnnotations.kt'*/ ])
|
|
||||||
|
|
||||||
konanArtifacts {
|
|
||||||
program(task.name, targets: [target.name]) {
|
|
||||||
srcFiles sources
|
|
||||||
baseDir "$testOutputStdlib/$task.name"
|
|
||||||
enableMultiplatform true
|
|
||||||
extraOpts inWorker ? '-trw' : '-tr',
|
|
||||||
'-Xverify-ir',
|
|
||||||
'-opt-in=kotlin.RequiresOptIn,kotlin.ExperimentalStdlibApi',
|
|
||||||
"-friend-modules", project.rootProject.file("${UtilsKt.getKotlinNativeDist(project)}/klib/common/stdlib").absolutePath
|
|
||||||
extraOpts project.globalTestArgs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Exclude test providing args to the test executable
|
|
||||||
task.arguments = [ "--ktest_negative_regex_filter=test.collections.CollectionTest.abstractCollectionToArray" ]
|
|
||||||
task.useFilter = false
|
|
||||||
task.testLogger = KonanTest.Logger.GTEST
|
|
||||||
task.finalizedBy("resultsTask")
|
|
||||||
task.enabled = (project.testTarget != 'wasm32') // Uses exceptions
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds tests into a single binary with TestRunner
|
* Builds tests into a single binary with TestRunner
|
||||||
*/
|
*/
|
||||||
@@ -5951,13 +5851,6 @@ sourceSets {
|
|||||||
srcDir 'debugger/src/test/kotlin'
|
srcDir 'debugger/src/test/kotlin'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update_stdlib_tests{
|
|
||||||
kotlin {
|
|
||||||
srcDir '../../../libraries/stdlib/common/test'
|
|
||||||
srcDir '../../../libraries/stdlib/test'
|
|
||||||
srcDir '../../../libraries/kotlin.test/common/src/test/kotlin'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileNopPluginKotlin {
|
compileNopPluginKotlin {
|
||||||
|
|||||||
@@ -1,646 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2017 JetBrains s.r.o.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin
|
|
||||||
|
|
||||||
import org.gradle.api.tasks.Input
|
|
||||||
import org.gradle.api.tasks.Optional
|
|
||||||
import org.gradle.api.tasks.Internal
|
|
||||||
import org.gradle.api.tasks.JavaExec
|
|
||||||
import org.gradle.api.tasks.TaskAction
|
|
||||||
import org.gradle.process.ExecResult
|
|
||||||
import org.jetbrains.kotlin.utils.DFS
|
|
||||||
import org.jetbrains.kotlin.*
|
|
||||||
|
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.util.function.Function
|
|
||||||
import java.util.function.UnaryOperator
|
|
||||||
import java.util.regex.Pattern
|
|
||||||
import java.util.stream.Collectors
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class RunExternalTestGroup extends JavaExec implements CompilerRunner {
|
|
||||||
private def platformManager = project.project(":kotlin-native").platformManager
|
|
||||||
private def target = platformManager.targetManager(project.testTarget).target
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
def dist = UtilsKt.getKotlinNativeDist(project)
|
|
||||||
|
|
||||||
@Input
|
|
||||||
def enableKonanAssertions = true
|
|
||||||
|
|
||||||
@Input
|
|
||||||
def verifyIr = true
|
|
||||||
|
|
||||||
@Input
|
|
||||||
@Optional
|
|
||||||
String outputDirectory = null
|
|
||||||
@Input
|
|
||||||
@Optional
|
|
||||||
String goldValue = null
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
// Checks test's output against gold value and returns true if the output matches the expectation
|
|
||||||
Function<String, Boolean> outputChecker = { str -> (goldValue == null || goldValue == str) }
|
|
||||||
|
|
||||||
@Input
|
|
||||||
boolean printOutput = true
|
|
||||||
|
|
||||||
@Input
|
|
||||||
@Optional
|
|
||||||
String testData = null
|
|
||||||
|
|
||||||
@Input
|
|
||||||
int expectedExitStatus = 0
|
|
||||||
|
|
||||||
@Input
|
|
||||||
@Optional
|
|
||||||
List<String> arguments = null
|
|
||||||
|
|
||||||
@Input
|
|
||||||
@Optional
|
|
||||||
List<String> flags = null
|
|
||||||
|
|
||||||
@Input
|
|
||||||
boolean multiRuns = false
|
|
||||||
@Input
|
|
||||||
@Optional
|
|
||||||
List<List<String>> multiArguments = null
|
|
||||||
|
|
||||||
@Input
|
|
||||||
boolean expectedFail = false
|
|
||||||
|
|
||||||
@Input
|
|
||||||
boolean compilerMessages = false
|
|
||||||
|
|
||||||
// Uses directory defined in $outputSourceSetName source set
|
|
||||||
void createOutputDirectory() {
|
|
||||||
if (outputDirectory != null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
def outputSourceSet = UtilsKt.getTestOutputExternal(project)
|
|
||||||
outputDirectory = Paths.get(outputSourceSet, name).toString()
|
|
||||||
project.file(outputDirectory).mkdirs()
|
|
||||||
}
|
|
||||||
|
|
||||||
RunExternalTestGroup() {
|
|
||||||
// We don't build the compiler if a custom dist path is specified.
|
|
||||||
UtilsKt.dependsOnDist(this)
|
|
||||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void exec() {
|
|
||||||
// Perhaps later we will return this exec() back but for now rest of infrastructure expects
|
|
||||||
// compilation begins on runCompiler call, to emulate this behaviour we call super.exec() after
|
|
||||||
// configuration part at runCompiler.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void runCompiler(List<String> filesToCompile, String output, List<String> moreArgs) {
|
|
||||||
def log = new ByteArrayOutputStream()
|
|
||||||
try {
|
|
||||||
classpath = project.fileTree("$dist.canonicalPath/konan/lib/") {
|
|
||||||
include '*.jar'
|
|
||||||
}
|
|
||||||
jvmArgs "-Xmx4G"
|
|
||||||
jvmArgs "-Dkonan.home=${UtilsKt.getKotlinNativeDist(project)}"
|
|
||||||
enableAssertions = true
|
|
||||||
def sources = File.createTempFile(name, ".lst")
|
|
||||||
sources.deleteOnExit()
|
|
||||||
def sourcesWriter = sources.newWriter()
|
|
||||||
filesToCompile.each { f ->
|
|
||||||
sourcesWriter.write(f.chars().any { Character.isWhitespace(it) }
|
|
||||||
? "\"${f.replace("\\", "\\\\")}\"\n" // escape file name
|
|
||||||
: "$f\n")
|
|
||||||
}
|
|
||||||
sourcesWriter.close()
|
|
||||||
args = ["-output", output,
|
|
||||||
"@${sources.absolutePath}",
|
|
||||||
*moreArgs,
|
|
||||||
*project.globalTestArgs]
|
|
||||||
if (project.testTarget) {
|
|
||||||
args "-target", target.visibleName
|
|
||||||
}
|
|
||||||
if (enableKonanAssertions) {
|
|
||||||
args "-ea"
|
|
||||||
}
|
|
||||||
if (verifyIr) {
|
|
||||||
args "-Xverify-ir"
|
|
||||||
}
|
|
||||||
if (project.hasProperty("test_verbose")) {
|
|
||||||
println("Files to compile: $filesToCompile")
|
|
||||||
println(args)
|
|
||||||
}
|
|
||||||
standardOutput = log
|
|
||||||
errorOutput = log
|
|
||||||
super.exec()
|
|
||||||
} finally {
|
|
||||||
def logString = log.toString("UTF-8")
|
|
||||||
project.file("${output}.compilation.log").write(logString)
|
|
||||||
println(logString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: output directory here changes and hence this is not a property
|
|
||||||
String executablePath() { return "$outputDirectory/program.tr" }
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
OutputStream out
|
|
||||||
|
|
||||||
void runExecutable() {
|
|
||||||
if (!enabled) {
|
|
||||||
println "Test is disabled: $name"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
def program = executablePath()
|
|
||||||
def suffix = target.family.exeSuffix
|
|
||||||
def exe = "$program.$suffix"
|
|
||||||
|
|
||||||
println "execution: $exe"
|
|
||||||
|
|
||||||
def compilerMessagesText = compilerMessages ? project.file("${program}.compilation.log").getText('UTF-8') : ""
|
|
||||||
|
|
||||||
out = new ByteArrayOutputStream()
|
|
||||||
//TODO Add test timeout
|
|
||||||
|
|
||||||
def times = multiRuns ? multiArguments.size() : 1
|
|
||||||
|
|
||||||
def exitCodeMismatch = false
|
|
||||||
for (int i = 0; i < times; i++) {
|
|
||||||
ExecResult execResult = project.extensions.executor.execute {
|
|
||||||
|
|
||||||
commandLine exe
|
|
||||||
|
|
||||||
if (arguments != null) {
|
|
||||||
args arguments
|
|
||||||
}
|
|
||||||
if (multiRuns && multiArguments[i] != null) {
|
|
||||||
args multiArguments[i]
|
|
||||||
}
|
|
||||||
if (testData != null) {
|
|
||||||
standardInput = new ByteArrayInputStream(testData.bytes)
|
|
||||||
}
|
|
||||||
standardOutput = out
|
|
||||||
|
|
||||||
ignoreExitValue = true
|
|
||||||
}
|
|
||||||
|
|
||||||
exitCodeMismatch |= execResult.exitValue != expectedExitStatus
|
|
||||||
if (exitCodeMismatch) {
|
|
||||||
def message = "Expected exit status: $expectedExitStatus, actual: ${execResult.exitValue}"
|
|
||||||
if (this.expectedFail) {
|
|
||||||
println("Expected failure. $message")
|
|
||||||
} else {
|
|
||||||
throw new TestFailedException("Test failed on iteration $i. $message\n ${out.toString("UTF-8")}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def result = compilerMessagesText + out.toString("UTF-8")
|
|
||||||
if (printOutput) {
|
|
||||||
println(result)
|
|
||||||
}
|
|
||||||
result = result.replace(System.lineSeparator(), "\n")
|
|
||||||
def goldValueMismatch = !outputChecker.apply(result)
|
|
||||||
if (goldValueMismatch) {
|
|
||||||
def message
|
|
||||||
if (goldValue != null) {
|
|
||||||
message = "Expected output: $goldValue, actual output: $result"
|
|
||||||
} else {
|
|
||||||
message = "Actual output doesn't match output checker: $result"
|
|
||||||
}
|
|
||||||
if (this.expectedFail) {
|
|
||||||
println("Expected failure. $message")
|
|
||||||
} else {
|
|
||||||
throw new TestFailedException("Test failed. $message")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!exitCodeMismatch && !goldValueMismatch && this.expectedFail) println("Unexpected pass")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, the test executable will be built in two stages:
|
|
||||||
* 1. Build a klibrary from sources.
|
|
||||||
* 2. Build a final executable from this klibrary.
|
|
||||||
*/
|
|
||||||
@Input
|
|
||||||
def enableTwoStageCompilation = false
|
|
||||||
|
|
||||||
@Input
|
|
||||||
def groupDirectory = "."
|
|
||||||
|
|
||||||
@Input
|
|
||||||
@Optional
|
|
||||||
List<String> ignoredTests = null
|
|
||||||
|
|
||||||
@Input
|
|
||||||
@Optional
|
|
||||||
String filter = project.findProperty("filter")
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
def testGroupReporter = new KonanTestGroupReportEnvironment(project)
|
|
||||||
|
|
||||||
void parseLanguageFlags(String src) {
|
|
||||||
def text = project.rootProject.file(src).text
|
|
||||||
def languageSettings = findLinesWithPrefixesRemoved(text, "// !LANGUAGE: ") +
|
|
||||||
findLinesWithPrefixesRemoved(text, '// LANGUAGE: ')
|
|
||||||
if (languageSettings.size() != 0) {
|
|
||||||
languageSettings.forEach { line ->
|
|
||||||
line.split(" ").toList().forEach {
|
|
||||||
if (it != "+NewInference") // It is on already by default, but passing it explicitly turns on a special "compatibility mode" in FE which is not desirable.
|
|
||||||
flags.add("-XXLanguage:$it")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def experimentalSettings = findLinesWithPrefixesRemoved(text, "// !OPT_IN: ")
|
|
||||||
if (experimentalSettings.size() != 0) {
|
|
||||||
experimentalSettings.forEach { line ->
|
|
||||||
line.split(" ").toList().forEach { flags.add("-opt-in=$it") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def expectActualLinker = findLinesWithPrefixesRemoved(text, "// EXPECT_ACTUAL_LINKER")
|
|
||||||
if (expectActualLinker.size() != 0) {
|
|
||||||
flags.add("-Xexpect-actual-linker")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String markMutableObjects(String text) {
|
|
||||||
def lines = text.readLines()
|
|
||||||
def result = new ArrayList<String>(lines.size())
|
|
||||||
lines.forEach { line ->
|
|
||||||
// FIXME: find only those who has vars inside
|
|
||||||
// Find object declarations and companion objects
|
|
||||||
if (line.matches("\\s*(private|public|internal)?\\s*object [a-zA-Z_][a-zA-Z0-9_]*\\s*.*")
|
|
||||||
|| line.matches("\\s*(private|public|internal)?\\s*companion object.*")) {
|
|
||||||
result += "@kotlin.native.ThreadLocal"
|
|
||||||
}
|
|
||||||
result += line
|
|
||||||
}
|
|
||||||
return result.join(System.lineSeparator())
|
|
||||||
}
|
|
||||||
|
|
||||||
static String insertInTextAfter(String text, String insert, String after) {
|
|
||||||
def begin = text.indexOf(after)
|
|
||||||
if (begin != -1) {
|
|
||||||
def end = text.indexOf("\n", begin)
|
|
||||||
text = text.substring(0, end) + insert + text.substring(end)
|
|
||||||
} else {
|
|
||||||
text = insert + text
|
|
||||||
}
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TestFile> createTestFiles(String src, TestModule defaultModule) {
|
|
||||||
def identifier = /[a-zA-Z_][a-zA-Z0-9_]/
|
|
||||||
def fullQualified = /[a-zA-Z_][a-zA-Z0-9_.]/
|
|
||||||
def importRegex = /(?m)^\s*import\s+/
|
|
||||||
|
|
||||||
def packagePattern = ~/(?m)^\s*package\s+(${fullQualified}*)/
|
|
||||||
def boxPattern = ~/(?m)fun\s+box\s*\(\s*\)/
|
|
||||||
def classPattern = ~/.*(class|object|enum|interface)\s+(${identifier}*).*/
|
|
||||||
|
|
||||||
def sourceName = "_" + normalize(project.rootProject.file(src).name)
|
|
||||||
def packages = new LinkedHashSet<String>()
|
|
||||||
def imports = []
|
|
||||||
def classes = []
|
|
||||||
def vars = new HashSet<String>() // variables that has the same name as a package
|
|
||||||
TestModule mainModule = defaultModule
|
|
||||||
def testFiles = TestDirectivesKt.buildCompileList(
|
|
||||||
project.rootProject.file(src).toPath(),
|
|
||||||
"$outputDirectory/${project.rootProject.file(src).name}",
|
|
||||||
mainModule
|
|
||||||
)
|
|
||||||
for (TestFile testFile : testFiles) {
|
|
||||||
def text = testFile.text
|
|
||||||
def filePath = testFile.path
|
|
||||||
if (text.contains('COROUTINES_PACKAGE')) {
|
|
||||||
text = text.replace('COROUTINES_PACKAGE', 'kotlin.coroutines')
|
|
||||||
}
|
|
||||||
def pkg
|
|
||||||
if (text =~ packagePattern) {
|
|
||||||
pkg = (text =~ packagePattern)[0][1]
|
|
||||||
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:")
|
|
||||||
}
|
|
||||||
if (text =~ boxPattern) {
|
|
||||||
imports.add("${pkg}.*")
|
|
||||||
mainModule = testFile.module
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find mutable objects that should be marked as ThreadLocal
|
|
||||||
if (filePath != "$outputDirectory/${project.rootProject.file(src).name}/helpers.kt") {
|
|
||||||
text = markMutableObjects(text)
|
|
||||||
}
|
|
||||||
testFile.text = text
|
|
||||||
}
|
|
||||||
for (TestFile testFile : testFiles) {
|
|
||||||
def text = testFile.text
|
|
||||||
// Find if there are any imports in the file
|
|
||||||
def matcher = (text =~ ~/${importRegex}(${fullQualified}*)/)
|
|
||||||
if (matcher) {
|
|
||||||
// Prepend package name to found imports
|
|
||||||
for (int i = 0; i < matcher.count; i++) {
|
|
||||||
String importStatement = matcher[i][1]
|
|
||||||
def subImport = importStatement.with {
|
|
||||||
int dotIdx = indexOf('.')
|
|
||||||
dotIdx > 0 ? substring(0, dotIdx) : it
|
|
||||||
}
|
|
||||||
if (packages.contains(subImport) || classes.contains(subImport)) {
|
|
||||||
// add only to those who import packages or import classes from the test files
|
|
||||||
text = text.replaceFirst(~/${importRegex}${Pattern.quote(importStatement)}/,
|
|
||||||
"import $sourceName.$importStatement")
|
|
||||||
} else if (text =~ classPattern) {
|
|
||||||
// special case for import from the local class
|
|
||||||
def clsMatcher = (text =~ classPattern)
|
|
||||||
for (int j = 0; j < clsMatcher.count; j++) {
|
|
||||||
def cl = (text =~ classPattern)[j][2]
|
|
||||||
classes.add(cl)
|
|
||||||
if (subImport == cl) {
|
|
||||||
text = text.replaceFirst(~/${importRegex}${Pattern.quote(importStatement)}/,
|
|
||||||
"import $sourceName.$importStatement")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (packages.empty) {
|
|
||||||
// Add import statement after package
|
|
||||||
def pkg = null
|
|
||||||
if (text =~ packagePattern) {
|
|
||||||
pkg = 'package ' + (text =~ packagePattern)[0][1]
|
|
||||||
text = text.replaceFirst(packagePattern, '')
|
|
||||||
}
|
|
||||||
text = insertInTextAfter(text, (pkg ? "\n$pkg\n" : "") + "import $sourceName.*\n", "@file:")
|
|
||||||
}
|
|
||||||
// now replace all package usages in full qualified names
|
|
||||||
def res = "" // filesToCompile
|
|
||||||
text.eachLine { line ->
|
|
||||||
packages.each { pkg ->
|
|
||||||
// line contains val or var declaration or function parameter declaration
|
|
||||||
if ((line =~ ~/va(l|r) *$pkg*( *: *$fullQualified*)?( *get\(\))? *\=.*/) ||
|
|
||||||
(line =~ ~/fun .*\(\n?\s*$pkg:.*/)) {
|
|
||||||
vars.add(pkg)
|
|
||||||
}
|
|
||||||
if (line.contains("$pkg.") && !(line =~ packagePattern || line =~ importRegex)
|
|
||||||
&& !vars.contains(pkg)) {
|
|
||||||
def idx = 0
|
|
||||||
while ((idx = line.indexOf(pkg, idx)) >= 0) {
|
|
||||||
if (!Character.isJavaIdentifierPart(line.charAt(idx - 1))) {
|
|
||||||
line = line.substring(0, idx) + "$sourceName.$pkg" + line.substring(idx + pkg.length())
|
|
||||||
idx += sourceName.length() + pkg.length() + 1
|
|
||||||
} else {
|
|
||||||
idx += pkg.length()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res += "$line\n"
|
|
||||||
}
|
|
||||||
testFile.text = res
|
|
||||||
}
|
|
||||||
def launcherText = createLauncherFileText(src, imports)
|
|
||||||
testFiles.add(
|
|
||||||
new TestFile(
|
|
||||||
"_launcher.kt",
|
|
||||||
"$outputDirectory/$src/_launcher.kt".toString(),
|
|
||||||
launcherText,
|
|
||||||
mainModule
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return testFiles
|
|
||||||
}
|
|
||||||
|
|
||||||
String normalize(String name) {
|
|
||||||
return name.replace('.kt', '')
|
|
||||||
.replace('-', '_')
|
|
||||||
.replace('.', '_')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* There are tests that require non-trivial 'package foo' in test launcher.
|
|
||||||
*/
|
|
||||||
String createLauncherFileText(String src, List<String> imports) {
|
|
||||||
StringBuilder text = new StringBuilder()
|
|
||||||
def pack = normalize(project.file(src).name)
|
|
||||||
text.append("package _$pack\n")
|
|
||||||
for (v in imports) {
|
|
||||||
text.append("import $v\n")
|
|
||||||
}
|
|
||||||
text.append(
|
|
||||||
"""
|
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun runTest() {
|
|
||||||
@Suppress("UNUSED_VARIABLE")
|
|
||||||
val result = box()
|
|
||||||
if (result != "OK") throw AssertionError("Test failed with: " + result)
|
|
||||||
}
|
|
||||||
""" )
|
|
||||||
return text.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> findLinesWithPrefixesRemoved(String text, String prefix) {
|
|
||||||
def result = []
|
|
||||||
text.eachLine {
|
|
||||||
if (it.startsWith(prefix)) {
|
|
||||||
result.add(it - prefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
static def excludeList = [
|
|
||||||
"compiler/testData/codegen/boxInline/multiplatform/defaultArguments/receiversAndParametersInLambda.kt", // KT-36880
|
|
||||||
"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/compileKotlinAgainstKotlin/clashingFakeOverrideSignatures.kt" // KT-42020
|
|
||||||
]
|
|
||||||
|
|
||||||
boolean isEnabledForNativeBackend(String fileName) {
|
|
||||||
def testFile = project.rootProject.file(fileName)
|
|
||||||
def text = testFile.text
|
|
||||||
|
|
||||||
if (testFile.name in ignoredTests)
|
|
||||||
return false
|
|
||||||
|
|
||||||
if (excludeList.any { fileName.replace(File.separator, "/").contains(it) }) return false
|
|
||||||
|
|
||||||
def languageSettings = findLinesWithPrefixesRemoved(text, '// !LANGUAGE: ') +
|
|
||||||
findLinesWithPrefixesRemoved(text, '// LANGUAGE: ')
|
|
||||||
if (!languageSettings.empty) {
|
|
||||||
def settings = languageSettings.first()
|
|
||||||
if (settings.contains('-ProperIeee754Comparisons') || // K/N supports only proper IEEE754 comparisons
|
|
||||||
settings.contains('-ReleaseCoroutines') || // only release coroutines
|
|
||||||
settings.contains('-DataClassInheritance') || // old behavior is not supported
|
|
||||||
settings.contains('-ProhibitAssigningSingleElementsToVarargsInNamedForm') || // Prohibit these assignments
|
|
||||||
settings.contains('-ProhibitDataClassesOverridingCopy') || // Prohibit as no longer supported
|
|
||||||
settings.contains('-ProhibitOperatorMod') || // Prohibit as no longer supported
|
|
||||||
settings.contains('-UseBuilderInferenceOnlyIfNeeded') || // Run only default one
|
|
||||||
settings.contains('-UseCorrectExecutionOrderForVarargArguments')) { // Run only correct one
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def diagnostics = findLinesWithPrefixesRemoved(text, '// !DIAGNOSTICS')
|
|
||||||
if (!diagnostics.empty) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
def version = findLinesWithPrefixesRemoved(text, '// LANGUAGE_VERSION: ')
|
|
||||||
if (version.size() != 0 && (!version.contains("1.3") || !version.contains("1.4"))) {
|
|
||||||
// Support tests for 1.3 and exclude 1.2
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
def apiVersion = findLinesWithPrefixesRemoved(text, '// !API_VERSION: ')
|
|
||||||
if (apiVersion.size() != 0 && !apiVersion.contains("1.4")) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
def targetBackend = findLinesWithPrefixesRemoved(text, "// TARGET_BACKEND")
|
|
||||||
if (targetBackend.size() != 0) {
|
|
||||||
// There is some target backend. Check if it is NATIVE or not.
|
|
||||||
for (String s : targetBackend) {
|
|
||||||
if (s.contains("NATIVE")){ return true }
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
// No target backend. Check if NATIVE backend is ignored.
|
|
||||||
def ignoredBackends = findLinesWithPrefixesRemoved(text, "// IGNORE_BACKEND: ") +
|
|
||||||
findLinesWithPrefixesRemoved(text, "// DONT_TARGET_EXACT_BACKEND: ")
|
|
||||||
for (String s : ignoredBackends) {
|
|
||||||
if (s.contains("NATIVE")) { return false }
|
|
||||||
}
|
|
||||||
// No ignored backends. Check if test is targeted to FULL_JDK or has JVM_TARGET set
|
|
||||||
if (!findLinesWithPrefixesRemoved(text, "// FULL_JDK").isEmpty()) { return false }
|
|
||||||
if (!findLinesWithPrefixesRemoved(text, "// JVM_TARGET:").isEmpty()) { return false }
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
void executeTest() {
|
|
||||||
createOutputDirectory()
|
|
||||||
// Form the test list.
|
|
||||||
def ktFiles = project.rootProject.projectDir.toPath().resolve(groupDirectory).toFile()
|
|
||||||
.listFiles({
|
|
||||||
it.isFile() && it.name.endsWith(".kt")
|
|
||||||
} as FileFilter)
|
|
||||||
if (filter != null) {
|
|
||||||
def pattern = ~filter
|
|
||||||
ktFiles = ktFiles.findAll {
|
|
||||||
it.name =~ pattern
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def defaultModule = TestModule.default()
|
|
||||||
|
|
||||||
testGroupReporter.suite(name) { suite ->
|
|
||||||
// Build tests in the group
|
|
||||||
flags = (flags ?: []) + "-tr"
|
|
||||||
List<TestFile> compileList = []
|
|
||||||
ktFiles.each {
|
|
||||||
def src = project.rootProject.relativePath(it)
|
|
||||||
if (isEnabledForNativeBackend(src)) {
|
|
||||||
// Create separate output directory for each test in the group.
|
|
||||||
parseLanguageFlags(src)
|
|
||||||
compileList.addAll(createTestFiles(src, defaultModule))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (compileList.any { it.module.dependencies.contains("support") }) {
|
|
||||||
def supportModule = TestModule.support()
|
|
||||||
compileList.add(new TestFile("helpers.kt", "$outputDirectory/helpers.kt",
|
|
||||||
CoroutineTestUtilKt.createTextForHelpers(), supportModule))
|
|
||||||
}
|
|
||||||
compileList*.writeTextToFile()
|
|
||||||
try {
|
|
||||||
if (enableTwoStageCompilation) {
|
|
||||||
// Two-stage compilation.
|
|
||||||
def klibPath = "${executablePath()}.klib"
|
|
||||||
def files = compileList.stream()
|
|
||||||
.map { it.path }
|
|
||||||
.collect(Collectors.toList())
|
|
||||||
if (!files.empty) {
|
|
||||||
runCompiler(files, klibPath, flags + ["-p", "library"])
|
|
||||||
runCompiler([], executablePath(), flags + ["-Xinclude=$klibPath"])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Regular compilation with modules.
|
|
||||||
Map<String, TestModule> modules = compileList.stream()
|
|
||||||
.map {
|
|
||||||
println(it.module)
|
|
||||||
it.module }
|
|
||||||
.distinct()
|
|
||||||
.collect(Collectors.toMap({ it.name }, UnaryOperator.identity()))
|
|
||||||
|
|
||||||
List<TestModule> orderedModules = DFS.INSTANCE.topologicalOrder(modules.values()) { module ->
|
|
||||||
module.dependencies.collect { modules[it] }.findAll { it != null }
|
|
||||||
}
|
|
||||||
def compiler = new MultiModuleCompilerInvocations(this, outputDirectory, executablePath(), modules, flags)
|
|
||||||
|
|
||||||
orderedModules.reverse().each { module ->
|
|
||||||
println("${module.name} ${module.dependencies}")
|
|
||||||
if (!module.isDefaultModule()) {
|
|
||||||
compiler.produceLibrary(module)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compiler.produceProgram(compileList)
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
project.logger.quiet("ERROR: Compilation failed for test suite: $name with exception", ex)
|
|
||||||
project.logger.quiet("The following files were unable to compile:")
|
|
||||||
ktFiles.each { project.logger.quiet(it.name) }
|
|
||||||
suite.abort("Compilation failed for test suite: $name", ex, ktFiles.collect { it.name })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the tests.
|
|
||||||
arguments = (arguments ?: []) + "--ktest_logger=SILENT"
|
|
||||||
ktFiles.each { file ->
|
|
||||||
def src = project.rootProject.relativePath(file)
|
|
||||||
def savedArgs = arguments
|
|
||||||
arguments += "--ktest_filter=_${normalize(file.name)}.*"
|
|
||||||
use(KonanTestSuiteReportKt) {
|
|
||||||
project.logger.quiet("TEST: $file.name " +
|
|
||||||
"(done: $testGroupReporter.statistics.total/${ktFiles.size()}, " +
|
|
||||||
"passed: $testGroupReporter.statistics.passed, " +
|
|
||||||
"skipped: $testGroupReporter.statistics.skipped)")
|
|
||||||
}
|
|
||||||
if (isEnabledForNativeBackend(src)) {
|
|
||||||
suite.executeTest(file.name) {
|
|
||||||
project.logger.quiet(src)
|
|
||||||
runExecutable()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
suite.skipTest(file.name)
|
|
||||||
}
|
|
||||||
arguments = savedArgs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-2
@@ -11,11 +11,11 @@ import java.io.PrintWriter
|
|||||||
|
|
||||||
data class ExternalTestReport(@Expose val statistics: Statistics, @Expose val groups: List<KonanTestGroupReport>)
|
data class ExternalTestReport(@Expose val statistics: Statistics, @Expose val groups: List<KonanTestGroupReport>)
|
||||||
|
|
||||||
fun saveReport(reportFileName: String, statistics: Statistics, groups:List<KonanTestGroupReport>){
|
fun saveReport(reportFileName: String, statistics: Statistics){
|
||||||
File(reportFileName).apply {
|
File(reportFileName).apply {
|
||||||
parentFile.mkdirs()
|
parentFile.mkdirs()
|
||||||
PrintWriter(this).use {
|
PrintWriter(this).use {
|
||||||
it.append(gson.toJson(ExternalTestReport(statistics, groups)))
|
it.append(gson.toJson(ExternalTestReport(statistics, emptyList())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
package org.jetbrains.kotlin
|
|
||||||
|
|
||||||
interface CompilerRunner {
|
|
||||||
fun runCompiler(filesToCompile: List<String>, output: String, moreArgs: List<String>)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This class knows how to run the compiler for multi-module external tests.
|
|
||||||
//
|
|
||||||
// In addition it knows how to deal with multi-versioned modules.
|
|
||||||
// The versioned modules are used for binary compatibility testing.
|
|
||||||
// For such modules we produce two klibs:
|
|
||||||
// `version1/foo.klib` and `version2/foo.klib`.
|
|
||||||
// For un-versioned modules we still produce just `bar.klib`
|
|
||||||
// We link all klibs against `version1/foo.klib` and `bar.klib`
|
|
||||||
// We link the final executable against `version2/foo.klib` and `bar.klib`.
|
|
||||||
|
|
||||||
class MultiModuleCompilerInvocations(
|
|
||||||
val konanTest: CompilerRunner,
|
|
||||||
val outputDirectory: String,
|
|
||||||
val executablePath: String,
|
|
||||||
val modules: Map<String, TestModule>,
|
|
||||||
val flags: List<String>
|
|
||||||
) {
|
|
||||||
val libs = HashSet<String>()
|
|
||||||
|
|
||||||
fun moduleToKlibName(moduleName: String, version: Int): String {
|
|
||||||
val module = modules[moduleName] ?: error("Could not find module $moduleName")
|
|
||||||
|
|
||||||
return if (module.hasVersions) {
|
|
||||||
val versionSuffix = "_version$version"
|
|
||||||
"$executablePath$versionSuffix/$moduleName.klib"
|
|
||||||
} else {
|
|
||||||
"$executablePath.$moduleName.klib"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun produceLibrary(module: TestModule, moduleVersion: Int) {
|
|
||||||
val klibModulePath = moduleToKlibName(module.name, moduleVersion)
|
|
||||||
libs.addAll(module.dependencies)
|
|
||||||
|
|
||||||
val dependencyVersion = moduleVersion
|
|
||||||
|
|
||||||
val klibs = module.dependencies.flatMap { listOf("-l", moduleToKlibName(it, dependencyVersion)) }
|
|
||||||
val repos = listOf("-r", "${executablePath}_version$dependencyVersion", "-r", outputDirectory)
|
|
||||||
|
|
||||||
val friends = module.friends.flatMap {
|
|
||||||
listOf("-friend-modules", "$executablePath.$it.klib")
|
|
||||||
}
|
|
||||||
konanTest.runCompiler(module.versionFiles(moduleVersion).map { it.path },
|
|
||||||
klibModulePath, flags + listOf("-p", "library") + repos + klibs + friends)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun produceLibrary(module: TestModule) {
|
|
||||||
if (!module.hasVersions) {
|
|
||||||
produceLibrary(module, 1)
|
|
||||||
} else {
|
|
||||||
produceLibrary(module, 1)
|
|
||||||
produceLibrary(module, 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun produceProgram(compileList: List<TestFile>) {
|
|
||||||
val compileMain = compileList.filter {
|
|
||||||
it.module.isDefaultModule() && !it.module.isSupportModule()
|
|
||||||
}
|
|
||||||
compileMain.forEach { f ->
|
|
||||||
libs.addAll(f.module.dependencies)
|
|
||||||
}
|
|
||||||
|
|
||||||
// When producing an executable link them against version 2 of multiversioned klibs.
|
|
||||||
val dependencyVersion = 2
|
|
||||||
|
|
||||||
val friends = compileMain.flatMap { it.module.friends }.toSet()
|
|
||||||
val repos = listOf("-r", "${executablePath}_version$dependencyVersion", "-r", outputDirectory)
|
|
||||||
|
|
||||||
if (compileMain.isNotEmpty()) {
|
|
||||||
konanTest.runCompiler(compileMain.map { it.path }, executablePath, flags + repos +
|
|
||||||
libs.flatMap { listOf("-l", moduleToKlibName(it, dependencyVersion)) } +
|
|
||||||
friends.flatMap { listOf("-friend-modules", "${executablePath}.${it}.klib") }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. 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
|
|
||||||
|
|
||||||
// This is almost a full copy of kotlin/compiler/tests-common/tests/org/jetbrains/kotlin/coroutineTestUtil.kt
|
|
||||||
// TODO: get it automatically as a dependency
|
|
||||||
fun createTextForHelpers(): String = """
|
|
||||||
package helpers
|
|
||||||
import kotlin.coroutines.*
|
|
||||||
|
|
||||||
fun <T> handleResultContinuation(x: (T) -> Unit): Continuation<T> = object: Continuation<T> {
|
|
||||||
override val context = EmptyCoroutineContext
|
|
||||||
|
|
||||||
override fun resumeWith(result: Result<T>) {
|
|
||||||
x(result.getOrThrow())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun handleExceptionContinuation(x: (Throwable) -> Unit): Continuation<Any?> = object: Continuation<Any?> {
|
|
||||||
override val context = EmptyCoroutineContext
|
|
||||||
|
|
||||||
override fun resumeWith(result: Result<Any?>) {
|
|
||||||
result.exceptionOrNull()?.let(x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class EmptyContinuation(override val context: CoroutineContext = EmptyCoroutineContext) : Continuation<Any?> {
|
|
||||||
companion object : EmptyContinuation()
|
|
||||||
|
|
||||||
override fun resumeWith(result: Result<Any?>) {
|
|
||||||
result.getOrThrow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class ContinuationAdapter<in T> : Continuation<T> {
|
|
||||||
override val context: CoroutineContext = EmptyCoroutineContext
|
|
||||||
|
|
||||||
override fun resumeWith(result: Result<T>) {
|
|
||||||
if (result.isSuccess) {
|
|
||||||
resume(result.getOrThrow())
|
|
||||||
} else {
|
|
||||||
resumeWithException(result.exceptionOrNull()!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun resumeWithException(exception: Throwable)
|
|
||||||
abstract fun resume(value: T)
|
|
||||||
}
|
|
||||||
|
|
||||||
class StateMachineCheckerClass {
|
|
||||||
private var counter = 0
|
|
||||||
var finished = false
|
|
||||||
|
|
||||||
var proceed: () -> Unit = {}
|
|
||||||
|
|
||||||
fun reset() {
|
|
||||||
counter = 0
|
|
||||||
finished = false
|
|
||||||
proceed = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun suspendHere() = suspendCoroutine<Unit> { c ->
|
|
||||||
counter++
|
|
||||||
proceed = { c.resume(Unit) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun check(numberOfSuspensions: Int, checkFinished: Boolean = true) {
|
|
||||||
for (i in 1..numberOfSuspensions) {
|
|
||||||
if (counter != i) error("Wrong state-machine generated: suspendHere called should be called exactly once in one state. Expected " + i + ", got " + counter)
|
|
||||||
proceed()
|
|
||||||
}
|
|
||||||
if (counter != numberOfSuspensions)
|
|
||||||
error("Wrong state-machine generated: suspendHere called should be called exactly once in one state. Expected " + numberOfSuspensions + ", got " + counter)
|
|
||||||
if (finished) error("Wrong state-machine generated: it is finished early")
|
|
||||||
proceed()
|
|
||||||
if (checkFinished && !finished) error("Wrong state-machine generated: it is not finished yet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val StateMachineChecker = StateMachineCheckerClass()
|
|
||||||
object CheckStateMachineContinuation: Continuation<Unit> {
|
|
||||||
override val context: CoroutineContext
|
|
||||||
get() = EmptyCoroutineContext
|
|
||||||
|
|
||||||
override fun resumeWith(result: Result<Unit>) {
|
|
||||||
result.getOrThrow()
|
|
||||||
StateMachineChecker.proceed = {
|
|
||||||
StateMachineChecker.finished = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
Reference in New Issue
Block a user