/* * 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 file. */ import org.codehaus.groovy.runtime.IOGroovyMethods import org.jetbrains.kotlin.* import org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompileNativeBinary import org.jetbrains.kotlin.konan.target.Architecture import org.jetbrains.kotlin.konan.target.Family buildscript { repositories { mavenCentral() maven { url project.bootstrapKotlinRepo } } } apply plugin: 'konan' apply plugin: 'kotlin' apply plugin: 'kotlin.native.build-tools-conventions' apply plugin: 'native-dependencies' configurations { update_tests nopPluginApi api.extendsFrom nopPluginApi } ext.testOutputRoot = rootProject.file("test.output").absolutePath ext.platformManager = project.project(":kotlin-native").platformManager ext.target = platformManager.targetManager(project.testTarget).target ext.buildNumber = rootProject.property("kotlinVersion") // Add executor to run tests depending on a target // NOTE: If this persists in a gradle daemon, environment update (e.g. an Xcode update) may lead to execution failures. project.extensions.executor = ExecutorServiceKt.create(project) ext.useCustomDist = UtilsKt.getUseCustomDist(project) ext.kotlinNativeDist = UtilsKt.getKotlinNativeDist(project) if (!useCustomDist) { ext.setProperty("kotlin.native.home", kotlinNativeDist.absolutePath) ext.setProperty("org.jetbrains.kotlin.native.home", kotlinNativeDist.absolutePath) ext.setProperty("konan.home", kotlinNativeDist.absolutePath) } compileTestKotlin { compilerOptions.freeCompilerArgs.add("-Xskip-prerelease-check") } // Do not generate run tasks for konan built artifacts ext.konanNoRun = true final CacheTesting cacheTesting = CacheTestingKt.configureCacheTesting(project) if (cacheTesting != null) { // Check for debug build and set the -g option. if (project.globalTestArgs.contains("-opt")) { throw new IllegalArgumentException("Cache testing should be run with debug build. " + "Remove -opt options from the test args") } if (!project.globalTestArgs.contains("-g")) { project.globalTestArgs.add("-g") } // Note: can't do this in [CacheTesting.configure] since task classes aren't accessible there. tasks.withType(KonanCompileNativeBinary.class).configureEach { dependsOn cacheTesting.buildCacheTask extraOpts cacheTesting.compilerArgs } } // Enable two-stage test compilation if the test_two_stage property is set. ext.twoStageEnabled = project.hasProperty("test_two_stage") tasks.withType(KonanCompileNativeBinary.class).configureEach { enableTwoStageCompilation = twoStageEnabled } ext.isNoopGC = project.globalTestArgs.contains("-Xbinary=gc=noop") || project.globalTestArgs.contains("-Xgc=noop") ext.isSTWMSGC = project.globalTestArgs.contains("-Xbinary=gc=stwms") || project.globalTestArgs.contains("-Xgc=stms") ext.isAggressiveGC = project.globalTestArgs.contains("-Xbinary=gcSchedulerType=aggressive") ext.isWithStateChecker = project.globalTestArgs.contains("-Xbinary=checkStateAtExternalCalls=true") || project.globalTestArgs.contains("-Xcheck-state-at-external-calls") ext.runtimeAssertionsPanic = false // TODO: It also makes sense to test -g without asserts, and also to test -opt with asserts. if (project.globalTestArgs.contains("-g") && (cacheTesting == null)) { tasks.withType(KonanCompileNativeBinary.class).configureEach { extraOpts "-Xbinary=runtimeAssertionsMode=panic" } ext.runtimeAssertionsPanic = true } tasks.withType(KonanCompileNativeBinary).configureEach { extraOpts "-XXLanguage:+ImplicitSignedToUnsignedIntegerConversion" extraOpts "-opt-in=kotlinx.cinterop.ExperimentalForeignApi" } allprojects { // Root directories for test output (logs, compiled files, statistics etc). Only single path must be in each set. // backend.native/tests ext.testOutputLocal = rootProject.file("$testOutputRoot/local") // backend.native/tests/external ext.testOutputExternal = rootProject.file("$testOutputRoot/external") // backend.native/tests/framework ext.testOutputFramework = rootProject.file("$testOutputRoot/framework") } testOutputExternal.mkdirs() konanArtifacts { library('baseTestClass', targets: [target]) { srcFiles 'testing/library.kt' } UtilsKt.dependsOnDist(UtilsKt.findKonanBuildTask(project, "baseTestClass", target)) } void konanc(String[] args) { String kotlincNative = PlatformInfo.isWindows() ? "kotlinc-native.bat" : "kotlinc-native" String allArgs = args.join(" ") String commandWithArguments = "$kotlinNativeDist/bin/$kotlincNative $allArgs" println(commandWithArguments) Process process = commandWithArguments.execute() int exitCode = process.waitFor() if (exitCode != 0) { String stdOut = IOGroovyMethods.getText(process.inputStream) String stdErr = IOGroovyMethods.getText(process.errorStream) StringBuilder builder = new StringBuilder() builder.append("Error during execution of the command: $commandWithArguments\n") builder.append("exitCode = $exitCode\n") builder.append("=== STDOUT: BEGIN ===\n") if (!stdOut.isEmpty()) { builder.append(stdOut) if (!stdOut.endsWith("\n")) builder.append("\n") } builder.append("=== STDOUT: END ===\n") builder.append("=== STDERR: BEGIN ===\n") if (!stdErr.isEmpty()) { builder.append(stdErr) if (!stdErr.endsWith("\n")) builder.append("\n") } builder.append("=== STDERR: END ===\n") throw new GradleException(builder.toString()) } } tasks.named("clean", Delete.class) { doLast { delete(project(":kotlin-native").file(testOutputRoot)) } } tasks.named("run") { dependsOn(tasks.withType(KonanTest).matching { it.enabled }) // Add regular gradle test tasks dependsOn(tasks.withType(Test).matching { it.enabled }) } // Was a subset of tests. tasks.register("sanity") { dependsOn("run") } boolean isExcluded(String dir) { // List of tests that fail due to unresolved compiler bugs def excluded = [ ] boolean result = false excluded.forEach { if (dir.endsWith(it.replace("/", File.separator))) { result = true } } return result } /** * Creates a task for a standalone test. Configures runner and adds building task. */ Task standaloneTest(String name, Closure configureClosure) { return KotlinNativeTestKt.createTest(project, name, KonanStandaloneTest) { task -> task.configure(configureClosure) if (task.enabled) { konanArtifacts { program(name, targets: [target.name]) { baseDir "$testOutputLocal/$name" srcFiles task.getSources() extraOpts task.flags extraOpts project.globalTestArgs } } } } } /** * Creates a task for a dynamic test. Configures runner and adds library and test build tasks. */ Task dynamicTest(String name, Closure configureClosure) { return KotlinNativeTestKt.createTest(project, name, KonanDynamicTest) { KonanDynamicTest task -> task.configure(configureClosure) if (task.enabled) { konanArtifacts { def targetName = target.name def lib = task.interop if (lib != null) { UtilsKt.dependsOnKonanBuildingTask(task, lib, target) } dynamic(name, targets: [targetName]) { if (lib != null) { libraries { artifact lib } } srcFiles task.getSources() baseDir "$testOutputLocal/$name" extraOpts task.flags extraOpts project.globalTestArgs if (targetName == "mingw_x64" || targetName == "mingw_x86") { extraOpts "-linker-option", "-Wl,--out-implib,$testOutputLocal/$name/$targetName/${name}.dll.a" } } } def buildTask = UtilsKt.findKonanBuildTask(project, name, target) UtilsKt.dependsOnDist(buildTask) task.dependsOn(nativeDependencies.llvmDependency) task.dependsOn(nativeDependencies.targetDependency(target)) } } } // This test checks object layout can't be done in // KonanLocalTest paradigm //tasks.register("constructor", UnitKonanTest) { // source = "codegen/object/constructor.kt" //} standaloneTest("runtime_basic_init") { source = "runtime/basic/init.kt" expectedExitStatus = 0 } standaloneTest("cleaner_basic") { enabled = !isNoopGC source = "runtime/basic/cleaner_basic.kt" flags = ['-tr', '-opt-in=kotlin.native.internal.InternalForKotlinNative'] } standaloneTest("cleaner_workers") { enabled = !isNoopGC source = "runtime/basic/cleaner_workers.kt" flags = ['-tr', '-opt-in=kotlin.native.internal.InternalForKotlinNative'] } standaloneTest("cleaner_in_main_with_checker") { enabled = !isNoopGC source = "runtime/basic/cleaner_in_main_with_checker.kt" useGoldenData = true } standaloneTest("cleaner_in_main_without_checker") { source = "runtime/basic/cleaner_in_main_without_checker.kt" useGoldenData = true } standaloneTest("cleaner_leak_without_checker") { source = "runtime/basic/cleaner_leak_without_checker.kt" useGoldenData = true } standaloneTest("cleaner_leak_with_checker") { enabled = !isNoopGC source = "runtime/basic/cleaner_leak_with_checker.kt" expectedExitStatusChecker = { it != 0 } outputChecker = { s -> (s =~ /Cleaner (0x)?[0-9a-fA-F]+ was disposed during program exit/).find() } } standaloneTest("cleaner_in_tls_main_without_checker") { source = "runtime/basic/cleaner_in_tls_main_without_checker.kt" } standaloneTest("cleaner_in_tls_main_with_checker") { enabled = !isNoopGC source = "runtime/basic/cleaner_in_tls_main_with_checker.kt" expectedExitStatusChecker = { it != 0 } outputChecker = { s -> (s =~ /Cleaner (0x)?[0-9a-fA-F]+ was disposed during program exit/).find() } } standaloneTest("cleaner_in_tls_worker") { enabled = !isNoopGC source = "runtime/basic/cleaner_in_tls_worker.kt" flags = ['-opt-in=kotlin.native.internal.InternalForKotlinNative'] } standaloneTest('logging') { outputChecker = { out -> out.toLowerCase().contains("[logging]") && // loging reports configured log levels on info level out.toLowerCase().contains("logging = debug") && out.toLowerCase().contains("gc = info") && out.toLowerCase().contains("mm = warning") && out.toLowerCase().contains("tls = error") && out.toLowerCase().contains("[gc]") // gc reports initialization on info level } source = "runtime/basic/logging.kt" flags = ['-Xruntime-logs=gc=info,mm=warning,tls=error,logging=debug'] } standaloneTest('logging_invalid') { outputChecker = { it.isEmpty() } source = "runtime/basic/logging.kt" flags = ['-Xruntime-logs=invalid=unknown,logging=debug'] } standaloneTest('logging_override') { outputChecker = { it.isEmpty() } source = "runtime/basic/logging.kt" flags = ['-Xruntime-logs=logging=info,logging=debug,logging=none'] } standaloneTest("worker_bound_reference0") { source = "runtime/concurrent/worker_bound_reference0.kt" flags = ['-tr'] if (isNoopGC) { def exclude = [ "*.testCollect", "*.testCollectFrozen", "*.testCollectInWorkerFrozen", "*.collectCyclicGarbage", "*.collectCyclicGarbageWithAtomicsFrozen", "*.collectCrossThreadCyclicGarbageWithAtomicsFrozen" ] arguments += ["--ktest_filter=*-${exclude.join(":")}"] } } // This tests changes main thread worker queue state, so better be executed alone. standaloneTest("worker5") { useGoldenData = true source = "runtime/workers/worker5.kt" } tasks.register("worker10", KonanLocalTest) { enabled = !isNoopGC useGoldenData = true source = "runtime/workers/worker10.kt" } standaloneTest("worker_exceptions") { flags = ["-tr"] outputChecker = { !it.contains("testExecuteAfterStartQuiet error") && it.contains("testExecuteStart error") && !it.contains("testExecuteStartQuiet error") } source = "runtime/workers/worker_exceptions.kt" } standaloneTest("worker_exceptions_terminate") { expectedExitStatusChecker = { it != 0 } outputChecker = { it.contains("some error") && !it.contains("Will not happen") } source = "runtime/workers/worker_exceptions_terminate.kt" } standaloneTest("worker_exceptions_terminate_hook") { outputChecker = { it.contains("hook called") && !it.contains("some error") && it.contains("Will happen") } source = "runtime/workers/worker_exceptions_terminate_hook.kt" } standaloneTest("worker_exceptions_terminate_current") { expectedExitStatusChecker = { it != 0 } outputChecker = { it.contains("some error") && !it.contains("Will not happen") } source = "runtime/workers/worker_exceptions_terminate_current.kt" } standaloneTest("worker_exceptions_terminate_hook_current") { outputChecker = { it.contains("hook called") && !it.contains("some error") && it.contains("Will happen") } source = "runtime/workers/worker_exceptions_terminate_hook_current.kt" } standaloneTest("worker_threadlocal_no_leak") { source = "runtime/workers/worker_threadlocal_no_leak.kt" } standaloneTest("freeze_disabled") { enabled = !isNoopGC flags = ["-tr"] source = "runtime/workers/freeze_disabled.kt" testLogger = KonanTest.Logger.SILENT } standaloneTest("lazy2") { useGoldenData = true source = "runtime/workers/lazy2.kt" } standaloneTest("lazy3") { enabled = !isNoopGC source = "runtime/workers/lazy3.kt" } tasks.register("mutableData1", KonanLocalTest) { source = "runtime/workers/mutableData1.kt" } tasks.register("enumIdentity", KonanLocalTest) { useGoldenData = true source = "runtime/workers/enum_identity.kt" } standaloneTest("leakWorker") { source = "runtime/workers/leak_worker.kt" expectedExitStatusChecker = { it != 0 } outputChecker = { s -> s.contains("Unfinished workers detected, 1 workers leaked!") } } standaloneTest("leakMemoryWithWorkerTermination") { source = "runtime/workers/leak_memory_with_worker_termination.kt" } standaloneTest("initializers_testInfrastructure") { source = "codegen/initializers/testInfrastructure.kt" flags = ["-tr"] } tasks.register("sanity_assertions_enabled_for_local_tests", KonanLocalTest) { source = "sanity/assertions_enabled_for_local_tests.kt" } tasks.register("immutable_binary_blob_in_lambda", KonanLocalTest) { source = "lower/immutable_blob_in_lambda.kt" useGoldenData = true } tasks.register("abstract_super", KonanLocalTest) { source = "datagen/rtti/abstract_super.kt" } tasks.register("vtable1", KonanLocalTest) { source = "datagen/rtti/vtable1.kt" } tasks.register("vtable_any", KonanLocalTest) { source = "datagen/rtti/vtable_any.kt" useGoldenData = true } tasks.register("empty_string", KonanLocalTest) { useGoldenData = true source = "datagen/literals/empty_string.kt" } standaloneTest("link_default_libs") { useGoldenData = true source = "link/default/default.kt" UtilsKt.dependsOnPlatformLibs(it) } standaloneTest("check_stacktrace_format_coresymbolication") { disabled = !PlatformInfo.supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt') flags = ['-g', '-Xbinary=sourceInfoType=coresymbolication'] arguments = ['coresymbolication'] source = "runtime/exceptions/check_stacktrace_format.kt" } standaloneTest("stack_trace_inline") { disabled = !PlatformInfo.supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt') flags = ['-g', '-Xg-generate-debug-trampoline=enable', '-Xbinary=sourceInfoType=coresymbolication'] source = "runtime/exceptions/stack_trace_inline.kt" arguments = ['coresymbolication'] } standaloneTest("kt-49240-stack-trace-completeness") { disabled = !PlatformInfo.supportsExceptions(project) || project.globalTestArgs.contains('-opt') source = "runtime/exceptions/kt-49240-stack-trace-completeness.kt" } standaloneTest("kt-37572") { disabled = !PlatformInfo.supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt') flags = ['-g', '-Xbinary=sourceInfoType=coresymbolication'] arguments = ['coresymbolication'] source = "runtime/exceptions/kt-37572.kt" } standaloneTest("check_stacktrace_format_libbacktrace") { disabled = !PlatformInfo.supportsLibBacktrace(project)|| project.globalTestArgs.contains('-opt') flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace'] arguments = ['libbacktrace'] source = "runtime/exceptions/check_stacktrace_format.kt" } standaloneTest("stack_trace_inline_libbacktrace") { disabled = !PlatformInfo.supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt') flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace'] arguments = ['libbacktrace'] source = "runtime/exceptions/stack_trace_inline.kt" } standaloneTest("kt-37572-libbacktrace") { disabled = !PlatformInfo.supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt') flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace'] arguments = ['libbacktrace'] source = "runtime/exceptions/kt-37572.kt" } standaloneTest("except_constr_w_default") { disabled = !PlatformInfo.supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt') flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace'] source = "runtime/exceptions/except_constr_w_default.kt" } standaloneTest("throw_from_except_constr") { disabled = !PlatformInfo.supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt') flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace'] source = "runtime/exceptions/throw_from_except_constr.kt" } tasks.register("initializers6", KonanLocalTest) { source = "runtime/basic/initializers6.kt" } tasks.register("memory_var1", KonanLocalTest) { source = "runtime/memory/var1.kt" } tasks.register("memory_var2", KonanLocalTest) { source = "runtime/memory/var2.kt" } tasks.register("memory_var3", KonanLocalTest) { source = "runtime/memory/var3.kt" } tasks.register("memory_var4", KonanLocalTest) { source = "runtime/memory/var4.kt" } tasks.register("memory_throw_cleanup", KonanLocalTest) { useGoldenData = true source = "runtime/memory/throw_cleanup.kt" } tasks.register("memory_escape0", KonanLocalTest) { source = "runtime/memory/escape0.kt" } tasks.register("memory_escape1", KonanLocalTest) { useGoldenData = true source = "runtime/memory/escape1.kt" } tasks.register("memory_cycles0", KonanLocalTest) { useGoldenData = true source = "runtime/memory/cycles0.kt" } tasks.register("memory_cycles1", KonanLocalTest) { enabled = !isNoopGC source = "runtime/memory/cycles1.kt" } tasks.register("memory_basic0", KonanLocalTest) { source = "runtime/memory/basic0.kt" } tasks.register("memory_escape2", KonanLocalTest) { useGoldenData = true source = "runtime/memory/escape2.kt" } tasks.register("memory_weak0", KonanLocalTest) { enabled = !isNoopGC useGoldenData = true source = "runtime/memory/weak0.kt" } tasks.register("memory_weak1", KonanLocalTest) { useGoldenData = true source = "runtime/memory/weak1.kt" } standaloneTest("memory_only_gc") { source = "runtime/memory/only_gc.kt" } standaloneTest("leakMemory") { source = "runtime/memory/leak_memory.kt" } standaloneTest("leakMemoryWithTestRunner") { source = "runtime/memory/leak_memory_test_runner.kt" flags = ['-tr'] } standaloneTest("gcStats") { source = "runtime/memory/gcStats.kt" flags = ['-tr', "-Xbinary=gcSchedulerType=disabled"] enabled = !isNoopGC && !isAggressiveGC } standaloneTest("stress_gc_allocations") { // TODO: Support obtaining peak RSS on more platforms. enabled = (project.testTarget != "watchos_arm32") && (project.testTarget != "watchos_arm64") && (project.testTarget != "watchos_x86") && (project.testTarget != "watchos_x64") && (project.testTarget != "watchos_simulator_arm64") && !isNoopGC && // Requires some GC. !isAggressiveGC && // No need to check with aggressive GC at all !runtimeAssertionsPanic // New allocator with assertions makes this test very slow. source = "runtime/memory/stress_gc_allocations.kt" flags = [ '-opt-in=kotlin.native.internal.InternalForKotlinNative', // MemoryUsageInfo is internal '-Xdisable-phases=EscapeAnalysis', // The test checks GC, we need to allocate everything on the heap. ] } standaloneTest("array_out_of_memory") { // This test allocs a large array that may make Linux // kill any process inlcuding Gradle with OOM-killer enabled = project.target.family != Family.LINUX source = "runtime/memory/array_out_of_memory.kt" flags = ['-tr'] switch(project.target.architecture) { case Architecture.X64: case Architecture.ARM64: break; case Architecture.X86: case Architecture.ARM32: case Architecture.MIPS32: case Architecture.MIPSEL32: case Architecture.WASM32: expectedExitStatusChecker = { it != 0 } outputChecker = { s -> s.contains("Out of memory trying to allocate") } break; } } tasks.register("vararg0", KonanLocalTest) { source = "lower/vararg.kt" } tasks.register("vararg_of_literals", KonanLocalTest) { enabled = false useGoldenData = true source = "lower/vararg_of_literals.kt" } standaloneTest('tailrec') { disabled = isAggressiveGC // TODO: Investigate why too slow useGoldenData = true source = "lower/tailrec.kt" flags = ['-XXLanguage:-ProhibitTailrecOnVirtualMember', '-e', 'lower.tailrec.main'] } tasks.register("deserialized_inline0", KonanLocalTest) { source = "serialization/deserialized_inline0.kt" } tasks.register("deserialized_listof0", KonanLocalTest) { source = "serialization/deserialized_listof0.kt" } tasks.register("deserialized_fields", KonanLocalTest) { source = "serialization/deserialized_fields.kt" } KotlinNativeTestKt.createTest(project, "kt39548", KonanStandaloneTest) { task -> // Test infrastructure doesn't support generated source; // workaround by specifying dummy source: task.source = "does/not/exist/kt39548.dummy.kt" task.enabled = PlatformInfo.isWindowsTarget(project) if (task.enabled) { def ktFile = project.layout.buildDirectory.file("kt39548/kt39548.kt").get().asFile konanArtifacts { program(name, targets: [target.name]) { baseDir "$testOutputLocal/$name" srcFiles ktFile.toString() // Generated by doBeforeBuild task. extraOpts task.flags extraOpts project.globalTestArgs } } doBeforeBuild { GenTestKT39548Kt.genTestKT39548(ktFile) } } } standaloneTest("serialized_no_typemap") { source = "serialization/regression/no_type_map.kt" useGoldenData = true } standaloneTest("testing_annotations") { source = "testing/annotations.kt" flags = ['-tr'] arguments = ['--ktest_logger=SIMPLE'] useGoldenData = true } standaloneTest("testing_assertions") { source = "testing/assertions.kt" flags = ['-tr'] expectedExitStatus = 0 } standaloneTest("testing_custom_main") { source = "testing/custom_main.kt" flags = ['-tr', '-e', 'kotlin.test.tests.main'] arguments = ['--ktest_logger=SIMPLE', '--ktest_repeat=2'] useGoldenData = true } standaloneTest("testing_library_usage") { def target = project.testTarget ?: 'host' dependsOn konanArtifacts.baseTestClass.getByTarget(target) def klib = konanArtifacts.baseTestClass.getArtifactByTarget(target) source = 'testing/library_user.kt' flags = ['-tr', '-e', 'main', '-l', klib.absolutePath] arguments = ['--ktest_logger=SILENT'] } class Filter { List positive List negative List expectedTests Filter(List positive, List negative, List expectedTests) { this.positive = positive this.negative = negative this.expectedTests = expectedTests } List args() { List result = [] if (positive != null && !positive.isEmpty()) { result += "--ktest_gradle_filter=${asListOfPatterns(positive)}" } if (negative != null && !negative.isEmpty()) { result += "--ktest_negative_gradle_filter=${asListOfPatterns(negative)}" } return result } static private String asListOfPatterns(List input) { return input.collect { "kotlin.test.tests.$it" }.join(",") } } standaloneTest("testing_filters") { source = "testing/filters.kt" flags = ['-tr', '-ea'] def filters = [ new Filter(["A.foo1", "B", "FiltersKt.foo1"], [], ["A.foo1", "B.foo1", "B.foo2", "B.bar", "FiltersKt.foo1"]), new Filter([], ["A.foo1", "B", "FiltersKt.foo1"], ["A.foo2", "A.bar", "FiltersKt.foo2", "FiltersKt.bar"]), new Filter(["A", "FiltersKt"], ["A.foo1", "FiltersKt.foo1"], ["A.foo2", "A.bar", "FiltersKt.foo2", "FiltersKt.bar"]), new Filter(["A.foo*", "B.*"], [], ["A.foo1", "A.foo2", "B.foo1", "B.foo2", "B.bar"]), new Filter([], ["A.foo*", "B.*"], ["A.bar", "FiltersKt.foo1", "FiltersKt.foo2", "FiltersKt.bar"]), new Filter(["*.foo*"], ["B"], ["A.foo1", "A.foo2", "FiltersKt.foo1", "FiltersKt.foo2"]), new Filter(["A"], ["*.foo*"], ["A.bar"]) ] multiRuns = true multiArguments = filters.collect { it.args() + '--ktest_logger=SIMPLE' } outputChecker = { String output -> // The first chunk is empty - drop it. def outputs = output.split("Starting testing\n").drop(1) if (outputs.size() != filters.size()) { println("Incorrect number of test runs. Expected: ${filters.size()}, actual: ${outputs.size()}") return false } // Check the correct set of tests was executed in each run. for (int i = 0; i < outputs.size(); i++) { def actualMessages = outputs[i].split('\n').findAll { it.startsWith("Passed: ") } def expectedMessages = filters[i].expectedTests.collect { String test -> def method = test.substring(test.lastIndexOf('.') + 1) def suite = test.substring(0, test.lastIndexOf('.')) "Passed: $method (kotlin.test.tests.$suite)".toString() } if (actualMessages.size() != expectedMessages.size()) { println("Incorrect number of tests executed for filters[$i]. Expected: ${expectedMessages.size()}. Actual: ${actualMessages.size()}") return false } for (message in expectedMessages) { if (!actualMessages.contains(message)) { println("Test run output for filters[$i] doesn't contain the expected message '$message'") return false } } } return true } } standaloneTest("testing_filtered_suites") { source = "testing/filtered_suites.kt" flags = ["-tr", "-ea"] def filters = [ ["Filtered_suitesKt.*"], // filter out a class. ["A.*"], // filter out a top-level suite. ["*.common"], // run a test from all suites -> all hooks executed. ["Ignored.*"], // an ignored suite -> no hooks executed. ["A.ignored"] // a suite with only ignored tests -> no hooks executed. ] def expectedHooks = [ ["Filtered_suitesKt.before", "Filtered_suitesKt.after"], ["A.before", "A.after"], ["A.before", "A.after", "Filtered_suitesKt.before", "Filtered_suitesKt.after"], [], [] ] multiRuns = true multiArguments = filters.collect { def filter = it.collect { "kotlin.test.tests.$it" }.join(",") ["--ktest_gradle_filter=$filter", "--ktest_logger=SIMPLE"] } outputChecker = { String output -> // The first chunk is empty - drop it. def outputs = output.split("Starting testing\n").drop(1) if (outputs.size() != expectedHooks.size()) { println("Incorrect number of test runs. Expected: ${expectedHooks.size()}, actual: ${outputs.size()}") return false } // Check the correct set of hooks was executed on each run. for (int i = 0; i < outputs.size(); i++) { def actual = outputs[i].split('\n') .findAll { it.startsWith("Hook:") } .collect { it.replace("Hook: ", "") } def expected = expectedHooks[i] if (actual.size() != expected.size()) { println("Incorrect number of executed hooks for run #$i. Expected: ${expected.size()}. Actual: ${actual.size()}") println("Expected hooks: $expected") println("Actual hooks: $actual") return false } for (expectedHook in expected) { if (!actual.contains(expectedHook)) { println("Expected hook wasn't executed for run #$i: $expectedHook") println("Expected hooks: $expected") println("Actual hooks: $actual") return false } } } return true } } // Check that stacktraces and ignored suite are correctly reported in the TC logger. standaloneTest("testing_stacktrace") { source = "testing/stacktrace.kt" flags = ['-tr', '-ea'] arguments = ["--ktest_logger=TEAMCITY", "--ktest_no_exit_code"] // This test prints TeamCity service messages about failed test causing false negative failure at CI. // So we need to suppress printing these messages to stdout. // printOutput = false outputChecker = { String output -> def ignoredOutput = """\ |##teamcity[testSuiteStarted name='kotlin.test.tests.Ignored' locationHint='ktest:suite://kotlin.test.tests.Ignored'] |##teamcity[testIgnored name='foo'] |##teamcity[testSuiteFinished name='kotlin.test.tests.Ignored']""".stripMargin() def failedOutput = """\ |##teamcity[testSuiteStarted name='kotlin.test.tests.Failed' locationHint='ktest:suite://kotlin.test.tests.Failed'] |##teamcity[testStarted name='bar' locationHint='ktest:test://kotlin.test.tests.Failed.bar'] |##teamcity[testFailed name='bar' message='Bar' details='kotlin.Exception: Bar|n""".stripMargin() def shownInnerException = output.readLines() .find { it.startsWith("##teamcity[testFailed name='bar'") } ?.contains("Caused by: kotlin.Exception: Baz") ?: false return output.contains(ignoredOutput) && output.contains(failedOutput) && shownInnerException } } // Just check that the driver is able to produce runnable binaries. tasks.register("driver0", KonanDriverTest) { disabled = isAggressiveGC // No need to test with different GC schedulers useGoldenData = true source = "runtime/basic/driver0.kt" } tasks.register("driver_opt", KonanDriverTest) { disabled = (cacheTesting != null) // Cache is not compatible with -opt. || isAggressiveGC // No need to test with different GC schedulers useGoldenData = true source = "runtime/basic/driver_opt.kt" flags = ["-opt"] } // A helper method to create interop artifacts void createInterop(String name, Closure conf) { konanArtifacts { interop(name, targets: [target.name]) { dependsOn(nativeDependencies.llvmDependency) dependsOn(nativeDependencies.targetDependency(target)) conf(it) noDefaultLibs(true) noEndorsedLibs(true) baseDir "$testOutputLocal/$name" } } } createInterop("sockets") { it.defFile 'interop/basics/sockets.def' } createInterop("kt43502") { it.defFile 'interop/kt43502/kt43502.def' it.headers "$projectDir/interop/kt43502/kt43502.h" // Note: also hardcoded in def file. final File libDir = project.layout.buildDirectory.dir("kt43502").get().asFile // Construct library that contains actual symbol definition. it.getByTarget(target.name).configure { doFirst { UtilsKt.buildStaticLibrary( project, [file("$projectDir/interop/kt43502/kt43502.c")], file("$libDir/kt43502.a"), file("$libDir/kt43502.objs"), ) } } } createInterop("leakMemoryWithRunningThread") { it.defFile 'interop/leakMemoryWithRunningThread/leakMemory.def' it.headers "$projectDir/interop/leakMemoryWithRunningThread/leakMemory.h" it.extraOpts "-Xcompile-source", "$projectDir/interop/leakMemoryWithRunningThread/leakMemory.cpp" } createInterop("workerSignals") { it.defFile "interop/workerSignals/workerSignals.def" it.headers "$projectDir/interop/workerSignals/workerSignals.h" it.extraOpts "-Xcompile-source", "$projectDir/interop/workerSignals/workerSignals.cpp" } createInterop("exceptions_throwThroughBridge") { it.defFile "interop/exceptions/throwThroughBridge.def" it.extraOpts "-Xcompile-source", "$projectDir/interop/exceptions/throwThroughBridgeInterop.cpp" } /** * Creates a task for the interop test. Configures runner and adds library and test build tasks. */ Task interopTestBase(String name, boolean multiFile, boolean interop2ForMainOnly, Closure configureClosure) { return KotlinNativeTestKt.createTest(project, name, KonanInteropTest) { KonanInteropTest task -> task.configure(configureClosure) if (task.enabled) { konanArtifacts { def targetName = target.name def interopLib = task.interop def interopLib2 = task.interop2 UtilsKt.dependsOnKonanBuildingTask(task, interopLib, target) if (interopLib2 != null) UtilsKt.dependsOnKonanBuildingTask(task, interopLib2, target) def lib = "lib$name" def hasIntermediateLib = task.lib != null if (hasIntermediateLib) { // build a library from KonanInteropTest::lib property library(lib, targets: [targetName]) { libraries { artifact interopLib if (interopLib2 != null && !interop2ForMainOnly) artifact interopLib2 } srcFiles task.lib baseDir "$testOutputLocal/$name" extraOpts task.flags extraOpts project.globalTestArgs.findAll { !it.contains("-Xpartial-linkage") } } UtilsKt.dependsOnKonanBuildingTask(task, lib, target) } program(name, targets: [targetName]) { libraries { artifact interopLib if (interopLib2 != null) artifact interopLib2 if (hasIntermediateLib) klibFile file("$testOutputLocal/$name/$targetName/${lib}.klib") } srcFiles multiFile ? fileTree(task.source) { include '**/*.kt' } : task.getSources() baseDir "$testOutputLocal/$name" linkerOpts "-L${project.layout.buildDirectory.get().asFile}" extraOpts task.flags extraOpts project.globalTestArgs } } task.dependsOn(nativeDependencies.llvmDependency) task.dependsOn(nativeDependencies.targetDependency(target)) } } } Task interopTest(String name, Closure configureClosure) { return interopTestBase(name, false, false, configureClosure) } Task interopTestMultifile(String name, Closure configureClosure) { return interopTestBase(name, true, false, configureClosure) } standaloneTest("interop_objc_allocException") { disabled = !PlatformInfo.isAppleTarget(project) expectedExitStatus = 0 source = "interop/objc/allocException.kt" UtilsKt.dependsOnPlatformLibs(it) } dynamicTest("interop_kt43502") { interop = "kt43502" source = "interop/kt43502/main.kt" cSource = "$projectDir/interop/kt43502/main.c" useGoldenData = true } interopTest("interop_leakMemoryWithRunningThreadChecked") { interop = 'leakMemoryWithRunningThread' source = "interop/leakMemoryWithRunningThread/checked.kt" expectedExitStatusChecker = { it != 0 } outputChecker = { s -> s.contains("Cannot run checkers when there are 1 alive runtimes at the shutdown") } } interopTest("interop_workerSignals") { disabled = project.testTarget == 'mingw_x64' // cross-thread signalling does not work on Windows source = "interop/workerSignals/workerSignals.kt" interop = "workerSignals" } /* TODO: This test isn't run automatically tasks.register("interop_echo_server", RunInteropKonanTest) { disabled = PlatformInfo.isWasmTarget(project) || PlatformInfo.isWindowsTarget(project) run = false source = "interop/basics/echo_server.kt" interop = 'sockets' } */ /* TODO: This test isn't run automatically standaloneTest("interop_opengl_teapot") { enabled = PlatformInfo.isAppleTarget(project) && project.testTarget != 'ios_x64' // No GLUT in iOS dependsOnPlatformLibs(it as Task) run = false source = "interop/basics/opengl_teapot.kt" } */ if (PlatformInfo.isAppleTarget(project)) { standaloneTest("interop_objc_kt61441") { source = "interop/objc/kt61441.kt" UtilsKt.dependsOnPlatformLibs(it) } standaloneTest("objc_arc_contract") { def bcFile = project.layout.buildDirectory.file("objc_arc_contract.bc").get().asFile doBeforeBuild { mkdir(bcFile.parentFile) ExecLlvmKt.execLlvmUtility(project, "llvm-as") { args "$projectDir/interop/objc_arc_contract/main.ll" args "-o", bcFile.toString() } } source = "interop/objc_arc_contract/main.kt" useGoldenData = true flags = ["-native-library", bcFile.toString()] } standaloneTest("interop_kt55653") { // Test depends on macOS-specific AppKit enabled = (project.testTarget == 'macos_x64' || project.testTarget == 'macos_arm64' || project.testTarget == null) source = "interop/objc/kt55653/main.kt" useGoldenData = true UtilsKt.dependsOnPlatformLibs(it) } } tasks.register("KT-50983", KonanDriverTest) { // The test is not working on Windows Server 2019-based TeamCity agents for the unknown reason. // TODO: Re-enable it after LLVM update where llvm-windres will be added. enabled = false def resFile = project.layout.buildDirectory.file("File.res").get().asFile doBeforeBuild { mkdir(resFile.parentFile) exec { commandLine UtilsKt.binaryFromToolchain(project, "windres") args "$projectDir/windows/KT-50983/File.rc", "-O", "coff", "-o", resFile.toString() } } source = "windows/KT-50983/main.kt" flags = ['-linker-option', resFile.toString()] } standaloneTest("interop_libiconv") { enabled = (project.testTarget == null) source = "interop/libiconv.kt" useGoldenData = true UtilsKt.dependsOnPlatformLibs(it) } standaloneTest("interop_objc_illegal_sharing") { disabled = !PlatformInfo.isAppleTarget(project) source = "interop/objc/illegal_sharing.kt" UtilsKt.dependsOnPlatformLibs(it) outputChecker = { it.startsWith("Before") && it.contains("After") } } dynamicTest("produce_dynamic") { source = "produce_dynamic/simple/hello.kt" cSource = "$projectDir/produce_dynamic/simple/main.c" useGoldenData = true } dynamicTest("kt36878") { source = "produce_dynamic/kt-36878/hello.kt" cSource = "$projectDir/produce_dynamic/kt-36878/main.c" useGoldenData = true } dynamicTest("kt39015") { source = "produce_dynamic/kt-39015/hello.kt" cSource = "$projectDir/produce_dynamic/kt-39015/main.c" useGoldenData = true } dynamicTest("kt39496") { source = "produce_dynamic/kt-39496/hello.kt" cSource = "$projectDir/produce_dynamic/kt-39496/main.c" useGoldenData = true } dynamicTest("kt41904") { source = "produce_dynamic/kt-41904/hello.kt" cSource = "$projectDir/produce_dynamic/kt-41904/main.c" useGoldenData = true } for (i in 0..2) { dynamicTest("kt42796_$i") { clangTool = "clang++" source = "produce_dynamic/kt-42796/main-${i}.kt" cSource = "$projectDir/produce_dynamic/kt-42796/main.cpp" useGoldenData = true clangFlags = ["-DTEST=$i", "-Werror"] } } dynamicTest("kt42830") { source = "produce_dynamic/kt-42830/box_unbox.kt" cSource = "$projectDir/produce_dynamic/kt-42830/main.c" useGoldenData = true } dynamicTest("kt64508") { source = "produce_dynamic/kt-64508/hello.kt" cSource = "$projectDir/produce_dynamic/kt-64508/main.c" useGoldenData = true } dynamicTest("produce_dynamic_unhandledException") { disabled = (cacheTesting != null) // Disabled due to KT-47828. source = "produce_dynamic/unhandledException/unhandled.kt" cSource = "$projectDir/produce_dynamic/unhandledException/main.cpp" clangTool = "clang++" expectedExitStatusChecker = { it != 0 } outputChecker = { str -> str.startsWith("Kotlin hook: Exception. Runnable state: true") } flags = ['-opt-in=kotlin.native.internal.InternalForKotlinNative'] } dynamicTest("interop_concurrentRuntime") { source = "interop/concurrentTerminate/reverseInterop.kt" cSource = "$projectDir/interop/concurrentTerminate/main.cpp" clangTool = "clang++" if (project.testTarget == 'mingw_x64') { // Our sysroot for mingw_x64 still requires -femulated-tls. For C++ code as well. See KT-46612. clangFlags += "-femulated-tls" } outputChecker = { str -> str.startsWith("Uncaught Kotlin exception: kotlin.RuntimeException: Example") } expectedExitStatus = 99 } for (i in ["kt42397", "kt56182_root", "kt56182_package1lvl", "kt56182_subpackage2lvl", "kt56182_root_package1lvl", "kt56182_root_subpackage2lvl"]) { // note: kt56182_package1lvl is same as kt42397 // note: kt56182_root_package1lvl is similar as kt41904 // note: kt56182_subpackage2lvl is similar as kt42796_1 dynamicTest("interop_$i") { source = "interop/$i/knlibrary.kt" cSource = "$projectDir/interop/$i/test.cpp" clangTool = "clang++" } } dynamicTest("interop_cleaners_main_thread") { disabled = isNoopGC source = "interop/cleaners/cleaners.kt" cSource = "$projectDir/interop/cleaners/main_thread.cpp" clangTool = "clang++" useGoldenData = true flags = ['-opt-in=kotlin.native.internal.InternalForKotlinNative'] } dynamicTest("interop_cleaners_second_thread") { disabled = isNoopGC source = "interop/cleaners/cleaners.kt" cSource = "$projectDir/interop/cleaners/second_thread.cpp" clangTool = "clang++" useGoldenData = true flags = ['-opt-in=kotlin.native.internal.InternalForKotlinNative'] } dynamicTest("interop_cleaners_leak") { source = "interop/cleaners/cleaners.kt" cSource = "$projectDir/interop/cleaners/leak.cpp" clangTool = "clang++" useGoldenData = true flags = ['-opt-in=kotlin.native.internal.InternalForKotlinNative'] } dynamicTest("interop_migrating_main_thread") { source = "interop/migrating_main_thread/lib.kt" clangFlags = ['-DEXPERIMENTAL_MM'] cSource = "$projectDir/interop/migrating_main_thread/main.cpp" clangTool = "clang++" } dynamicTest("interop_exceptions_throwThroughBridge") { source = "interop/exceptions/throwThroughBridge.kt" cSource = "$projectDir/interop/exceptions/throwThroughBridge.cpp" clangTool = "clang++" expectedExitStatusChecker = { it != 0 } outputChecker = { s -> !s.contains("Should not happen") } interop = "exceptions_throwThroughBridge" } dynamicTest("interop_kotlin_exception_hook") { source = "interop/exceptions/exceptionHook.kt" cSource = "$projectDir/interop/exceptions/exceptionHook.cpp" clangTool = "clang++" expectedExitStatusChecker = { it != 0 } outputChecker = { s -> s.contains("OK. Kotlin unhandled exception hook") } } standaloneTest("kt51302") { enabled = project.target.name == project.hostName source = "serialization/KT-51302/main.kt" flags = ["-l", "$projectDir/serialization/KT-51302/kt51302_dependency"] } standaloneTest("fake_override_0") { def sources = "$projectDir/link/fake_overrides" doBeforeBuild { konanc("$sources/base.kt -p library -target ${target.name} -o $outputDirectory/base.klib") konanc("$sources/move.kt -p library -target ${target.name} -o $outputDirectory/move.klib -l $outputDirectory/base.klib") konanc("$sources/use.kt -p library -target ${target.name} -o $outputDirectory/use.klib -l $outputDirectory/move.klib -l $outputDirectory/base.klib") konanc("$sources/move2.kt -p library -target ${target.name} -o $outputDirectory/move.klib -l $outputDirectory/base.klib") } source = "$sources/main.kt" flags = ["-l", "$outputDirectory/use.klib", "-l", "$outputDirectory/move.klib", "-l", "$outputDirectory/base.klib"] useGoldenData = true } standaloneTest("split_compilation_pipeline") { // Test infrastructure does not support passing flags only to the second stage, // and we can't pass -Xcompile-from-bitcode when producing library. Thus, this test // does not support 2-stage compilation for now. // Also, it is failing for some reason on Windows CI, but since MinGW targets are not inteneded // to use this mode, we can disable it for these targets. enabled = !twoStageEnabled && project.target.name != "mingw_x64" && project.target.name != "mingw_x86" def dir = project.layout.buildDirectory.get().asFile.absolutePath source = "link/private_fake_overrides/override_main.kt" doBeforeBuild { konanc("$projectDir/link/private_fake_overrides/override_lib.kt -p library -target ${target.name} -o $dir/lib.klib") konanc("$projectDir/$source -target ${target.name} -o $dir/out.klib -l $dir/lib.klib " + "-Xtemporary-files-dir=$dir/tmp/split " + "-Xwrite-dependencies-to=${dir}/split_compilation_pipeline.deps") } flags = ["-Xread-dependencies-from=${dir}/split_compilation_pipeline.deps", "-Xcompile-from-bitcode=${dir}/tmp/split/out.bc"] useGoldenData = true } tasks.register("override_konan_properties0", KonanDriverTest) { disabled = isAggressiveGC // No need to test with different GC schedulers def overrides = PlatformInfo.isWindows() ? '-Xoverride-konan-properties="llvmInlineThreshold=76"' : '-Xoverride-konan-properties=llvmInlineThreshold=76' flags = [ "-opt", "-Xverbose-phases=MandatoryBitcodeLLVMPostprocessingPhase", overrides ] compilerMessages = true source = "link/ir_providers/hello.kt" def messages = "inline_threshold: 76\nhello\n" outputChecker = { out -> messages.split("\n") .collect { line -> out.contains(line) } .every { it == true } } } tasks.register("llvm_variant_dev", KonanDriverTest) { // We use clang from Xcode on macOS for apple targets, // so it is hard to reliably detect LLVM variant for these targets. disabled = PlatformInfo.isAppleTarget(project) || isAggressiveGC // No need to test with different GC schedulers flags = [ "-Xllvm-variant=dev", "-Xverbose-phases=ObjectFiles" ] compilerMessages = true source = "link/ir_providers/hello.kt" // User LLVM variant has "essentials" suffix. def message = "-essentials" outputChecker = { out -> !out.contains(message) } } /** * Builds tests into a single binary with TestRunner */ task buildKonanTests { t -> def compileList = [ "codegen", "datagen", "lower", "runtime", "serialization", "sanity" ] // These tests should not be built into the TestRunner's test executable def excludeList = [ "codegen/inline/returnLocalClassFromBlock.kt" ] project.tasks .withType(KonanStandaloneTest.class) .each { // add library and source files if (it.source != null) { excludeList += it.source } } def sources = UtilsKt.getFilesToCompile(project, compileList, excludeList) konanArtifacts { program('localTest', targets: [target.name]) { srcFiles sources baseDir testOutputLocal extraOpts '-ea' // Without this option assertions would remain inactive. extraOpts '-tr' extraOpts '-Xverify-ir=error' extraOpts project.globalTestArgs } } // Set build dependencies. dependsOn compileKonanLocalTest def buildTask = UtilsKt.findKonanBuildTask(project, "localTest", target) UtilsKt.dependsOnDist(buildTask) // Local tests build into a single binary should depend on this task project.tasks .withType(KonanLocalTest.class) .matching { !(it instanceof KonanStandaloneTest) } .configureEach { it.dependsOn(t) } } sourceSets { nopPlugin { kotlin { srcDir 'extensions/nop/src/main/kotlin' } } } compileNopPluginKotlin { compilerOptions.freeCompilerArgs.add('-Xskip-prerelease-check') } Task pluginTest(String name, String pluginName, Closure configureClosure) { def jarTask = project.tasks.register("jar-$pluginName", Jar) { it.dependsOn("compile${pluginName.capitalize()}Kotlin") from { sourceSets[pluginName].output } archiveBaseName = pluginName destinationDirectory = project.layout.buildDirectory.get().asFile } def taskName = "$name-with-$pluginName" return KotlinNativeTestKt.createTest(project, taskName, KonanStandaloneTest) { task -> task.configure(configureClosure) task.dependsOn(jarTask) if (task.enabled) { konanArtifacts { program(taskName, targets: [target.name]) { baseDir "$testOutputLocal/$taskName" srcFiles task.getSources() extraOpts task.flags + "-Xplugin=${project.layout.buildDirectory.file("nop-plugin.jar").get().asFile}" extraOpts project.globalTestArgs } } } } } pluginTest("runtime_basic_init", "nopPlugin") { source = "runtime/basic/runtime_basic_init.kt" flags = ["-tr"] } dependencies { nopPluginApi project(":native:kotlin-native-utils") nopPluginApi project(":core:descriptors") nopPluginApi project(":compiler:ir.tree") nopPluginApi project(":compiler:ir.backend.common") nopPluginApi project(":compiler:util") nopPluginApi project(":native:frontend.native") nopPluginApi project(":compiler:cli-common") nopPluginApi project(":compiler:cli-base") nopPluginApi project(":compiler:cli") nopPluginApi project(":kotlin-util-klib") nopPluginApi project(":kotlin-util-klib-metadata") nopPluginApi project(":compiler:ir.serialization.common") api project(':native:cli-native') api libs.junit4 } project.afterEvaluate { // Don't treat any task as up-to-date, no matter what. // Note: this project should contain only test tasks, including ones that build binaries, // and ones that run binaries. // So the configuration below mostly means "tests aren't up-to-date". tasks.configureEach { outputs.upToDateWhen { false } } }