diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 568b3ece109..2253068bcdc 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -51,6 +51,7 @@ repositories { } dependencies { + compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' cli_bc project(path: ':backend.native', configuration: 'cli_bc') def updateTestData = true if (project.hasProperty("kotlinProjectPath")) { diff --git a/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/DistProperties.kt b/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/DistProperties.kt index 03e54c67c84..cfcd7e24034 100644 --- a/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/DistProperties.kt +++ b/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/DistProperties.kt @@ -15,6 +15,7 @@ object DistProperties { private val konancDriver = if (HostManager.host.family == Family.MINGW) "konanc.bat" else "konanc" val konanc: Path = dist.resolve("bin/$konancDriver") val lldb: Path = Paths.get("lldb") + val devToolsSecurity: Path = Paths.get("DevToolsSecurity") val lldbPrettyPrinters: Path = dist.resolve("tools/konan_lldb.py") private fun requireProp(name: String): String diff --git a/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Driver.kt b/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Driver.kt index c53ebcf45f0..0ad7dadc801 100644 --- a/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Driver.kt +++ b/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Driver.kt @@ -5,9 +5,18 @@ package org.jetbrains.kotlin.native.test.debugger +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import kotlinx.coroutines.runBlocking import org.jetbrains.kotlin.cli.bc.K2Native +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.StringReader +import java.lang.StringBuilder import java.nio.file.Files import java.nio.file.Path +import java.time.temporal.ChronoUnit import java.util.concurrent.TimeUnit class ToolDriver( @@ -31,7 +40,7 @@ class ToolDriver( } fun runLldb(program: Path, commands: List): String { - val args = listOf("-o", "command script import \"$lldbPrettyPrinters\"") + + val args = listOf("-b", "-o", "command script import \"$lldbPrettyPrinters\"") + commands.flatMap { listOf("-o", it) } return subprocess(lldb, program.toString(), "-b", *args.toTypedArray()) .thrownIfFailed() @@ -63,15 +72,41 @@ data class ProcessOutput( fun subprocess(program: Path, vararg args: String): ProcessOutput { val start = System.currentTimeMillis() val process = ProcessBuilder(program.toString(), *args).start() - val outReader = process.inputStream.bufferedReader() - val errReader = process.errorStream.bufferedReader() - - val timeout = 5L - if (!process.waitFor(timeout, TimeUnit.MINUTES)) { - process.destroy() - error("$program is running for more then $timeout minutes") + val out = GlobalScope.async(Dispatchers.IO) { + readStream(process, process.inputStream.buffered()) } - val stdout = outReader.readText() - val stderr = errReader.readText() - return ProcessOutput(program, process, stdout, stderr, System.currentTimeMillis() - start) -} \ No newline at end of file + + val err = GlobalScope.async(Dispatchers.IO) { + readStream(process, process.errorStream.buffered()) + } + + return runBlocking { + try { + val status = process.waitFor(5L, TimeUnit.MINUTES) + if (!status) { + out.cancel() + err.cancel() + error("$program timeouted") + } + }catch (e:Exception) { + out.cancel() + err.cancel() + error(e) + } + ProcessOutput(program, process, out.await(), err.await(), System.currentTimeMillis() - start) + } +} + +private fun readStream(process: Process, stream: InputStream): String { + var size = 4096 + val buffer = ByteArray(size) { 0 } + val sunk = ByteArrayOutputStream() + while (true) { + size = stream.read(buffer, 0, buffer.size) + if (size < 0 && !process.isAlive) + break + if (size > 0) + sunk.write(buffer, 0, size) + } + return String(sunk.toByteArray()) +} diff --git a/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Matchers.kt b/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Matchers.kt index f46052c84b0..edbf6b7dc49 100644 --- a/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Matchers.kt +++ b/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Matchers.kt @@ -62,6 +62,15 @@ fun lldbTest(@Language("kotlin") programText: String, lldbSession: String) { return } + if (!isOsxDevToolsEnabled) { + println("""Development tools aren't available. + |Please consider to execute: + | ${DistProperties.devToolsSecurity} -enable + |or + | csrutil disable + |to run lldb tests""".trimMargin()) + return + } val lldbSessionSpec = LldbSessionSpecification.parse(lldbSession) val tmpdir = Files.createTempDirectory("debugger_test") @@ -76,6 +85,15 @@ fun lldbTest(@Language("kotlin") programText: String, lldbSession: String) { lldbSessionSpec.match(result) } +private val isOsxDevToolsEnabled: Boolean by lazy { + //TODO: add OSX checks. + val rawStatus = subprocess(DistProperties.devToolsSecurity, "-status") + println("> status: $rawStatus") + + val r = Regex("^.*\\ (enabled|disabled).$") + r.find(rawStatus.stdout)?.destructured?.component1() == "enabled" +} + private val haveLldb: Boolean by lazy { val lldbVersion = try { subprocess(DistProperties.lldb, "-version")