[K/N] Migrate LLDB test to blackboxtest
This change only introduces a single sample test together with the necessary plumbing for basic LLDB testing. Migrating the rest of the tests over and introducing more complex interop setups will be tackled as a follow-up.
This commit is contained in:
@@ -49,6 +49,7 @@ val kotlinTestLibraryTest = nativeTest("kotlinTestLibraryTest", "kotlin-test")
|
||||
val klibAbiTest = nativeTest("klibAbiTest", "klib-abi")
|
||||
val klibBinaryCompatibilityTest = nativeTest("klibBinaryCompatibilityTest", "klib-binary-compatibility")
|
||||
val cinteropTest = nativeTest("cinteropTest", "cinterop")
|
||||
val debuggerTest = nativeTest("debuggerTest", "debugger")
|
||||
|
||||
// "test" task is created by convention. We can't just remove it. Let's enable it in developer's environment, so it can be used
|
||||
// to run any test from IDE or from console, but disable it at TeamCity where it is not supposed to be ever used.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: canInspectArrayChildren.txt
|
||||
fun main(args: Array<String>) {
|
||||
val xs = intArrayOf(3, 5, 8)
|
||||
return
|
||||
}
|
||||
|
||||
data class Point(val x: Int, val y: Int)
|
||||
@@ -0,0 +1,5 @@
|
||||
> b main.kt:5
|
||||
> r
|
||||
> fr var xs
|
||||
(ObjHeader *) xs = [..., ..., ...]
|
||||
> q
|
||||
@@ -0,0 +1,13 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: canInspectArrays.txt
|
||||
fun main(args: Array<String>) {
|
||||
val xs = IntArray(3)
|
||||
xs[0] = 1
|
||||
xs[1] = 2
|
||||
xs[2] = 3
|
||||
val ys: Array<Any?> = arrayOfNulls(2)
|
||||
ys[0] = Point(1, 2)
|
||||
return
|
||||
}
|
||||
|
||||
data class Point(val x: Int, val y: Int)
|
||||
@@ -0,0 +1,7 @@
|
||||
> b main.kt:10
|
||||
> r
|
||||
> fr var
|
||||
(ObjHeader *) args = []
|
||||
(ObjHeader *) xs = [..., ..., ...]
|
||||
(ObjHeader *) ys = [..., ...]
|
||||
> q
|
||||
@@ -0,0 +1,19 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: canInspectCatchParameter.txt
|
||||
fun main() {
|
||||
try {
|
||||
throw Exception("message 1")
|
||||
} catch (e1: Throwable) {
|
||||
println(e1.message)
|
||||
}
|
||||
|
||||
try {
|
||||
throwError()
|
||||
} catch (e2: Throwable) {
|
||||
println(e2.message)
|
||||
}
|
||||
}
|
||||
|
||||
fun throwError() {
|
||||
throw Error("message 2")
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
> b main.kt:7
|
||||
> r
|
||||
> fr var
|
||||
(ObjHeader *) e1 = [..]
|
||||
> b main.kt:11
|
||||
> c
|
||||
> fr var
|
||||
(ObjHeader *) e2 = [..]
|
||||
> q
|
||||
@@ -0,0 +1,12 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: canInspectClasses.txt
|
||||
fun main(args: Array<String>) {
|
||||
val point = Point(1, 2)
|
||||
val person = Person()
|
||||
return
|
||||
}
|
||||
|
||||
data class Point(val x: Int, val y: Int)
|
||||
class Person {
|
||||
override fun toString() = "John Doe"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
> b main.kt:6
|
||||
> r
|
||||
> fr var
|
||||
(ObjHeader *) args = []
|
||||
(ObjHeader *) point = [x: ..., y: ...]
|
||||
(ObjHeader *) person = []
|
||||
> q
|
||||
@@ -0,0 +1,10 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: canInspectValuesOfPrimitiveTypes.txt
|
||||
fun main(args: Array<String>) {
|
||||
var a: Byte = 1
|
||||
var b: Int = 2
|
||||
var c: Long = -3
|
||||
var d: Char = 'c'
|
||||
var e: Boolean = true
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
> b main.kt:9
|
||||
> r
|
||||
> fr var
|
||||
(char) a = '\x01'
|
||||
(int) b = 2
|
||||
(long) c = -3
|
||||
(short) d = 99
|
||||
(bool) e = true
|
||||
> q
|
||||
@@ -0,0 +1,8 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: canStepThroughCode.txt
|
||||
fun main(args: Array<String>) {
|
||||
var x = 1
|
||||
var y = 2
|
||||
var z = x + y
|
||||
println(z)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
> b main.kt:4
|
||||
Breakpoint 1: [..]
|
||||
|
||||
> r
|
||||
Process [..] stopped
|
||||
[..] stop reason = breakpoint 1.1
|
||||
[..] at main.kt:4[..]
|
||||
|
||||
> n
|
||||
Process [..] stopped
|
||||
[..] stop reason = step over
|
||||
[..] at main.kt:5[..]
|
||||
|
||||
> n
|
||||
Process [..] stopped
|
||||
[..] stop reason = step over
|
||||
[..] at main.kt:6[..]
|
||||
|
||||
> n
|
||||
Process [..] stopped
|
||||
[..] stop reason = step over
|
||||
[..] at main.kt:7[..]
|
||||
> q
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// FREE_COMPILER_ARGS: -Xg-generate-debug-trampoline=enable
|
||||
// LLDB_TRACE: kt33055.txt
|
||||
// FILE: kt33055.kt
|
||||
fun question(subject: String, names: Array<String> = emptyArray()): String {
|
||||
return buildString { // breakpoint here
|
||||
append("$subject?") // actual stop
|
||||
for (name in names)
|
||||
append(" $name?")
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(question("Subject", args))
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
> b 6
|
||||
Breakpoint 1: where = [..]`kfun:#question(kotlin.String;kotlin.Array<kotlin.String>){}kotlin.String [..] at kt33055.kt:6:12, [..]
|
||||
> q
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// FREE_COMPILER_ARGS: -Xg-generate-debug-trampoline=enable
|
||||
// LLDB_TRACE: kt33364.txt
|
||||
// FILE: kt33364.kt
|
||||
fun main() {
|
||||
val param = 3
|
||||
|
||||
//breakpoint here (line: 8, breakpoint is set to 9th line)
|
||||
when(param) {
|
||||
1 -> print("A")
|
||||
2 -> print("B")
|
||||
else -> print("C")
|
||||
}
|
||||
|
||||
// breakpoint here (line: 15, breakpoint is set to 16th line)
|
||||
when {
|
||||
param == 1 -> print("A")
|
||||
param == 2 -> print("B")
|
||||
else -> print("C")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
> b 8
|
||||
Breakpoint 1: where = [..]kfun:#main(){} [..] at kt33364.kt:9:[..]
|
||||
> b 15
|
||||
Breakpoint 2: where = [..]kfun:#main(){} [..] at kt33364.kt:16:[..]
|
||||
> q
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: kt42208.txt
|
||||
// FILE: kt42208-1.kt
|
||||
fun main() {
|
||||
foo()()
|
||||
}
|
||||
// FILE: kt42208-2.kt
|
||||
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
inline fun foo() = {
|
||||
throw Error()
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
> b ThrowException
|
||||
> r
|
||||
> bt
|
||||
* thread #1, [..] stop reason = breakpoint 1.1
|
||||
* frame #0: [..]`ThrowException
|
||||
frame #1: [..]`kfun:main$lambda$0#internal at kt42208-2.kt:10:18
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.invoke#internal(_this=[..]) at kt42208-1.kt:5:5
|
||||
frame #3: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.$<bridge-UNN>invoke(_this=[..]){}kotlin.Nothing#internal at kt42208-1.kt:5:5
|
||||
frame #4: [..]`kfun:#main(){} at kt42208-1.kt:5:5
|
||||
frame #5: [..]`Konan_start(args=[..]) at kt42208-1.kt:4:1
|
||||
frame #6: [..]
|
||||
frame #7: [..]
|
||||
> q
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// FREE_COMPILER_ARGS: -XXLanguage:+UnitConversionsOnArbitraryExpressions
|
||||
// LLDB_TRACE: kt42208WithPassingLambdaToAnotherFunction.txt
|
||||
// FILE: kt42208-1.kt
|
||||
fun main() {
|
||||
val a = foo()
|
||||
bar(a)
|
||||
}
|
||||
// FILE: kt42208-2.kt
|
||||
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
class A
|
||||
val list = mutableListOf<A>()
|
||||
inline fun foo() = { ->
|
||||
list.add(A())
|
||||
}
|
||||
// FILE: kt42208-3.kt
|
||||
fun bar(v:(()->Unit)) {
|
||||
v()
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
> b kt42208-2.kt:14
|
||||
> r
|
||||
> bt
|
||||
* thread #1, [..] stop reason = breakpoint 1.1
|
||||
* frame #0: [..]`kfun:main$lambda$0#internal at kt42208-2.kt:14:5
|
||||
frame #1: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.invoke#internal(_this=[..]) at kt42208-1.kt:6:5
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.$<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean#internal at kt42208-1.kt:6:5
|
||||
frame #3: [..]`kfun:#bar(v=[..]){} at kt42208-3.kt:18:5
|
||||
frame #4: [..]`kfun:#main(){} at kt42208-1.kt:7:5
|
||||
frame #5: [..]`Konan_start(args=[..]) at kt42208-1.kt:5:1
|
||||
> c
|
||||
> q
|
||||
@@ -0,0 +1,16 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: kt42208WithVariable.txt
|
||||
// FILE: kt42208-1.kt
|
||||
fun main() {
|
||||
val a = foo()
|
||||
a()
|
||||
a()
|
||||
a()
|
||||
}
|
||||
// FILE: kt42208-2.kt
|
||||
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
class A
|
||||
val list = mutableListOf<A>()
|
||||
inline fun foo() = { ->
|
||||
list.add(A())
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
> b kt42208-2.kt:15
|
||||
> r
|
||||
> bt
|
||||
* thread #1, [..] stop reason = breakpoint 1.1
|
||||
* frame #0: [..]`kfun:main$lambda$0#internal at kt42208-2.kt:15:5
|
||||
frame #1: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.invoke#internal(_this=[..]) at kt42208-1.kt:5:5
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.$<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean#internal at kt42208-1.kt:5:5
|
||||
frame #3: [..]`kfun:#main(){} at kt42208-1.kt:6:5
|
||||
frame #4: [..]`Konan_start(args=[..]) at kt42208-1.kt:4:1
|
||||
frame #5: [..]
|
||||
> c
|
||||
> bt
|
||||
* thread #1, [..] stop reason = breakpoint 1.1
|
||||
* frame #0: [..]`kfun:main$lambda$0#internal at kt42208-2.kt:15:5
|
||||
frame #1: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.invoke#internal(_this=[..]) at kt42208-1.kt:5:5
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.$<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean#internal at kt42208-1.kt:5:5
|
||||
frame #3: [..]`kfun:#main(){} at kt42208-1.kt:7:5
|
||||
frame #4: [..]`Konan_start(args=[..]) at kt42208-1.kt:4:1
|
||||
> c
|
||||
> bt
|
||||
* thread #1, [..] stop reason = breakpoint 1.1
|
||||
* frame #0: [..]`kfun:main$lambda$0#internal at kt42208-2.kt:15:5
|
||||
frame #1: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.invoke#internal(_this=[..]) at kt42208-1.kt:5:5
|
||||
frame #2: [..]`kfun:$main$lambda$0$FUNCTION_REFERENCE$0.$<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean#internal at kt42208-1.kt:5:5
|
||||
frame #3: [..]`kfun:#main(){} at kt42208-1.kt:8:5
|
||||
frame #4: [..]`Konan_start(args=[..]) at kt42208-1.kt:4:1
|
||||
> q
|
||||
@@ -0,0 +1,8 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: kt47198.txt
|
||||
// FILE: kt47198.kt
|
||||
fun foo(a:Int) = print("a: $a")
|
||||
|
||||
fun main() {
|
||||
foo(33)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
> b 4
|
||||
Breakpoint 1: where = [..]`kfun:#foo(kotlin.Int){} [..] at kt47198.kt:4:1, [..]
|
||||
> r
|
||||
> fr v
|
||||
(int) a = 33
|
||||
> q
|
||||
@@ -0,0 +1,10 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: kt47198WithBody.txt
|
||||
// FILE: kt47198.kt
|
||||
fun foo(a:Int){
|
||||
print("a: ${'$'}a")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
foo(33)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
> b 4
|
||||
Breakpoint 1: where = [..]`kfun:#foo(kotlin.Int){} [..] at kt47198.kt:4:[..]
|
||||
> r
|
||||
> fr v
|
||||
(int) a = 33
|
||||
> q
|
||||
@@ -0,0 +1,10 @@
|
||||
// KIND: STANDALONE_LLDB
|
||||
// LLDB_TRACE: standalone_lldb_stepping.txt
|
||||
import kotlin.test.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
var x = 1
|
||||
var y = 2
|
||||
var z = x + y
|
||||
println(z)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
> b main.kt:6
|
||||
Breakpoint 1: [..]
|
||||
> r
|
||||
Process [..] stopped
|
||||
[..] stop reason = breakpoint 1.1
|
||||
[..] at main.kt:6[..]
|
||||
3 import kotlin.test.*
|
||||
4
|
||||
5 fun main(args: Array<String>) {
|
||||
-> 6 var x = 1
|
||||
7 var y = 2
|
||||
8 var z = x + y
|
||||
9 println(z)
|
||||
> n
|
||||
Process [..] stopped
|
||||
[..] stop reason = step over
|
||||
[..] at main.kt:7[..]
|
||||
> n
|
||||
Process [..] stopped
|
||||
[..] stop reason = step over
|
||||
[..] at main.kt:8[..]
|
||||
> n
|
||||
Process [..] stopped
|
||||
[..] stop reason = step over
|
||||
[..] at main.kt:9[..]
|
||||
> q
|
||||
|
||||
|
||||
+6
@@ -120,6 +120,12 @@ public class InfrastructureTestGenerated extends AbstractNativeBlackBoxTest {
|
||||
runTest("native/native.tests/testData/samples/regular_simple_worker_tr.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("standalone_lldb_stepping.kt")
|
||||
public void testStandalone_lldb_stepping() throws Exception {
|
||||
runTest("native/native.tests/testData/samples/standalone_lldb_stepping.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("standalone_multifile.kt")
|
||||
public void testStandalone_multifile() throws Exception {
|
||||
|
||||
Generated
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.konan.blackboxtest;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.group.UseStandardTestCaseGroupProvider;
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.EnforcedProperty;
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.ClassLevelProperty;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateNativeTestsKt}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("native/native.tests/testData/lldb")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@Tag("debugger")
|
||||
@UseStandardTestCaseGroupProvider()
|
||||
@EnforcedProperty(property = ClassLevelProperty.OPTIMIZATION_MODE, propertyValue = "DEBUG")
|
||||
public class LldbTestGenerated extends AbstractNativeBlackBoxTest {
|
||||
@Test
|
||||
public void testAllFilesPresentInLldb() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("native/native.tests/testData/lldb"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("canInspectArrayChildren.kt")
|
||||
public void testCanInspectArrayChildren() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/canInspectArrayChildren.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("canInspectArrays.kt")
|
||||
public void testCanInspectArrays() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/canInspectArrays.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("canInspectCatchParameter.kt")
|
||||
public void testCanInspectCatchParameter() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/canInspectCatchParameter.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("canInspectClasses.kt")
|
||||
public void testCanInspectClasses() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/canInspectClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("canInspectValuesOfPrimitiveTypes.kt")
|
||||
public void testCanInspectValuesOfPrimitiveTypes() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/canInspectValuesOfPrimitiveTypes.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("canStepThroughCode.kt")
|
||||
public void testCanStepThroughCode() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/canStepThroughCode.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt33055.kt")
|
||||
public void testKt33055() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/kt33055.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt33364.kt")
|
||||
public void testKt33364() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/kt33364.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt42208.kt")
|
||||
public void testKt42208() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/kt42208.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt42208WithPassingLambdaToAnotherFunction.kt")
|
||||
public void testKt42208WithPassingLambdaToAnotherFunction() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/kt42208WithPassingLambdaToAnotherFunction.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt42208WithVariable.kt")
|
||||
public void testKt42208WithVariable() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/kt42208WithVariable.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47198.kt")
|
||||
public void testKt47198() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/kt47198.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt47198WithBody.kt")
|
||||
public void testKt47198WithBody() throws Exception {
|
||||
runTest("native/native.tests/testData/lldb/kt47198WithBody.kt");
|
||||
}
|
||||
}
|
||||
+14
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.generators.tests
|
||||
import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
|
||||
import org.jetbrains.kotlin.generators.model.annotation
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.ClassLevelProperty
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.EnforcedProperty
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.group.UseExtTestCaseGroupProvider
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.group.UseStandardTestCaseGroupProvider
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
@@ -81,10 +83,22 @@ fun main() {
|
||||
model("CInterop/KT-39120/defs", pattern = "^([^_](.+))$", recursive = false)
|
||||
}
|
||||
}
|
||||
|
||||
// LLDB integration tests.
|
||||
testGroup("native/native.tests/tests-gen", "native/native.tests/testData") {
|
||||
testClass<AbstractNativeBlackBoxTest>(
|
||||
suiteTestClassName = "LldbTestGenerated",
|
||||
annotations = listOf(debugger(), provider<UseStandardTestCaseGroupProvider>(), debugOnly())
|
||||
) {
|
||||
model("lldb")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T : Annotation> provider() = annotation(T::class.java)
|
||||
|
||||
private fun debugOnly() = annotation(EnforcedProperty::class.java, Pair("property", ClassLevelProperty.OPTIMIZATION_MODE), Pair("propertyValue", "DEBUG"))
|
||||
private fun codegen() = annotation(Tag::class.java, "codegen")
|
||||
private fun debugger() = annotation(Tag::class.java, "debugger")
|
||||
private fun infrastructure() = annotation(Tag::class.java, "infrastructure")
|
||||
|
||||
+2
-1
@@ -79,7 +79,8 @@ private object NativeTestSupport {
|
||||
TestProcessSettings(
|
||||
nativeHome,
|
||||
computeNativeClassLoader(),
|
||||
computeBaseDirs()
|
||||
computeBaseDirs(),
|
||||
DebugUtils()
|
||||
)
|
||||
} as TestProcessSettings
|
||||
|
||||
|
||||
+2
-2
@@ -179,12 +179,12 @@ internal class TestCase(
|
||||
val extras: Extras
|
||||
) {
|
||||
sealed interface Extras
|
||||
class NoTestRunnerExtras(val entryPoint: String, val inputDataFile: File?) : Extras
|
||||
class NoTestRunnerExtras(val entryPoint: String, val inputDataFile: File? = null, val arguments: List<String> = emptyList()) : Extras
|
||||
class WithTestRunnerExtras(val runnerType: TestRunnerType, val ignoredTests: Set<String> = emptySet()) : Extras
|
||||
|
||||
init {
|
||||
when (kind) {
|
||||
TestKind.STANDALONE_NO_TR -> assertTrue(extras is NoTestRunnerExtras)
|
||||
TestKind.STANDALONE_NO_TR, TestKind.STANDALONE_LLDB -> assertTrue(extras is NoTestRunnerExtras)
|
||||
TestKind.REGULAR, TestKind.STANDALONE -> assertTrue(extras is WithTestRunnerExtras)
|
||||
}
|
||||
}
|
||||
|
||||
+20
-3
@@ -12,9 +12,11 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.INPUT_DATA
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.KIND
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.OUTPUT_DATA_FILE
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.EXPECTED_TIMEOUT_FAILURE
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.LLDB_TRACE
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestDirectives.TEST_RUNNER
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.TestRunCheck
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.TestRunCheck.OutputDataFile
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.LldbSessionSpecification
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.StringDirective
|
||||
@@ -26,7 +28,7 @@ import java.io.File
|
||||
internal object TestDirectives : SimpleDirectivesContainer() {
|
||||
val KIND by enumDirective<TestKind>(
|
||||
description = """
|
||||
Usage: // KIND: [REGULAR, STANDALONE, STANDALONE_NO_TR]
|
||||
Usage: // KIND: [REGULAR, STANDALONE, STANDALONE_NO_TR, STANDALONE_LLDB]
|
||||
Declares the kind of the test:
|
||||
|
||||
- REGULAR (the default) - include this test into the shared test binary.
|
||||
@@ -37,6 +39,8 @@ internal object TestDirectives : SimpleDirectivesContainer() {
|
||||
|
||||
- STANDALONE_NO_TR - compile the test to a separate binary that is supposed to have main entry point.
|
||||
The entry point can be customized Note that @kotlin.Test annotations are ignored.
|
||||
|
||||
- STANDALONE_LLDB - compile the test to a separate binary and debug with LLDB.
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
@@ -51,7 +55,7 @@ internal object TestDirectives : SimpleDirectivesContainer() {
|
||||
val ENTRY_POINT by stringDirective(
|
||||
description = """
|
||||
Specify custom program entry point. The default entry point is `main` function in the root package.
|
||||
Note that this directive makes sense only in combination with // KIND: STANDALONE_NO_TR
|
||||
Note that this directive makes sense only in combination with // KIND: STANDALONE_NO_TR or // KIND: STANDALONE_LLDB
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
@@ -122,12 +126,19 @@ internal object TestDirectives : SimpleDirectivesContainer() {
|
||||
val FREE_COMPILER_ARGS by stringDirective(
|
||||
description = "Specify free compiler arguments for Kotlin/Native compiler"
|
||||
)
|
||||
|
||||
val LLDB_TRACE by stringDirective(
|
||||
description = """
|
||||
Specify a filename containing the LLDB commands and the patterns that
|
||||
the output should match""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
internal enum class TestKind {
|
||||
REGULAR,
|
||||
STANDALONE,
|
||||
STANDALONE_NO_TR;
|
||||
STANDALONE_NO_TR,
|
||||
STANDALONE_LLDB;
|
||||
}
|
||||
|
||||
internal enum class TestRunnerType {
|
||||
@@ -203,6 +214,12 @@ internal fun parseEntryPoint(registeredDirectives: RegisteredDirectives, locatio
|
||||
return entryPoint
|
||||
}
|
||||
|
||||
internal fun parseLLDBSpec(baseDir:File, registeredDirectives: RegisteredDirectives, location: Location): LldbSessionSpecification {
|
||||
val specFile = parseFileBasedDirective(baseDir, LLDB_TRACE, registeredDirectives, location)
|
||||
?: fail { "$location: A lldb session specification must be provided" }
|
||||
return LldbSessionSpecification.parse(specFile.readText())
|
||||
}
|
||||
|
||||
internal fun parseModule(parsedDirective: RegisteredDirectivesParser.ParsedDirective, location: Location): TestModule.Exclusive {
|
||||
val module = parsedDirective.values.singleOrNull()?.toString()?.let(TEST_MODULE_REGEX::matchEntire)?.let { match ->
|
||||
val name = match.groupValues[1]
|
||||
|
||||
+30
-8
@@ -48,6 +48,11 @@ internal class StandardTestCaseGroupProvider : TestCaseGroupProvider {
|
||||
|
||||
val testCases = includedTestDataFiles.map { testDataFile -> createTestCase(testDataFile, settings) }
|
||||
|
||||
if (!settings.get<DebugUtils>().lldbIsAvailable) {
|
||||
val lldbTests = testCases.filter { it.kind == TestKind.STANDALONE_LLDB }
|
||||
lldbTests.mapTo(disabledTestCaseIds) { it.id }
|
||||
}
|
||||
|
||||
TestCaseGroup.Default(disabledTestCaseIds, testCases)
|
||||
}
|
||||
}
|
||||
@@ -200,6 +205,12 @@ internal class StandardTestCaseGroupProvider : TestCaseGroupProvider {
|
||||
fixPackageNames(testModules.values, nominalPackageName, testDataFile)
|
||||
}
|
||||
|
||||
val lldbSpec = if (testKind == TestKind.STANDALONE_LLDB) parseLLDBSpec(
|
||||
baseDir = testDataFile.parentFile,
|
||||
registeredDirectives,
|
||||
location
|
||||
) else null
|
||||
|
||||
val testCase = TestCase(
|
||||
id = TestCaseId.TestDataFile(testDataFile),
|
||||
kind = testKind,
|
||||
@@ -209,15 +220,26 @@ internal class StandardTestCaseGroupProvider : TestCaseGroupProvider {
|
||||
checks = TestRunChecks(
|
||||
computeExecutionTimeoutCheck(settings, expectedTimeoutFailure),
|
||||
computeExitCodeCheck(testKind, registeredDirectives, location),
|
||||
computeOutputDataFileCheck(testDataFile, registeredDirectives, location)
|
||||
computeOutputDataFileCheck(testDataFile, registeredDirectives, location),
|
||||
lldbSpec?.let { OutputMatcher(it::matchOutput) }
|
||||
),
|
||||
extras = if (testKind == TestKind.STANDALONE_NO_TR)
|
||||
NoTestRunnerExtras(
|
||||
entryPoint = parseEntryPoint(registeredDirectives, location),
|
||||
inputDataFile = parseInputDataFile(baseDir = testDataFile.parentFile, registeredDirectives, location)
|
||||
)
|
||||
else
|
||||
WithTestRunnerExtras(runnerType = parseTestRunner(registeredDirectives, location))
|
||||
extras = when (testKind) {
|
||||
TestKind.STANDALONE_NO_TR -> {
|
||||
NoTestRunnerExtras(
|
||||
entryPoint = parseEntryPoint(registeredDirectives, location),
|
||||
inputDataFile = parseInputDataFile(baseDir = testDataFile.parentFile, registeredDirectives, location)
|
||||
)
|
||||
}
|
||||
TestKind.REGULAR, TestKind.STANDALONE -> {
|
||||
WithTestRunnerExtras(runnerType = parseTestRunner(registeredDirectives, location))
|
||||
}
|
||||
TestKind.STANDALONE_LLDB -> {
|
||||
NoTestRunnerExtras(
|
||||
entryPoint = parseEntryPoint(registeredDirectives, location),
|
||||
arguments = lldbSpec!!.generateCLIArguments(settings.get<KotlinNativeHome>().lldbPrettyPrinters)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
testCase.initialize(
|
||||
givenModules = settings.get<CustomKlibs>().klibs.mapToSet(TestModule::Given),
|
||||
|
||||
+17
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.runner.TestRunCheck.Execu
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.TestRunCheck.ExitCode
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.runner.UnfilteredProcessOutput.Companion.launchReader
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TestOutputFilter
|
||||
import org.junit.jupiter.api.Assertions.fail
|
||||
import java.io.ByteArrayOutputStream
|
||||
import kotlin.time.*
|
||||
|
||||
@@ -105,6 +106,22 @@ internal abstract class AbstractLocalProcessRunner<R>(private val checks: TestRu
|
||||
"Tested process output mismatch. See \"TEST STDOUT\" and \"EXPECTED OUTPUT DATA FILE\" below."
|
||||
}
|
||||
}
|
||||
is TestRunCheck.OutputMatcher -> {
|
||||
try {
|
||||
verifyExpectation(check.match(runResult.processOutput.stdOut.filteredOutput)) {
|
||||
"Tested process output has not passed validation."
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
if (t is Exception || t is AssertionError) {
|
||||
fail<Nothing>(
|
||||
getLoggedRun().withErrorMessage("Tested process output has not passed validation:\n\n" + t.message),
|
||||
t
|
||||
)
|
||||
} else {
|
||||
throw t
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -29,6 +29,13 @@ internal open class BaseTestRunProvider {
|
||||
addIfNotNull(testCase.checks.outputDataFile?.file?.let(TestRunParameter::WithExpectedOutputData))
|
||||
|
||||
when (testCase.kind) {
|
||||
TestKind.STANDALONE_LLDB -> {
|
||||
assertTrue(testName == null)
|
||||
// Note: TestRunParameter.WithLLDB adds program arguments and would therefore conflict
|
||||
// with other TestRunParameters that do the same (such as WithTCTestLogger).
|
||||
add(TestRunParameter.WithLLDB(testCase.extras<NoTestRunnerExtras>().arguments))
|
||||
addIfNotNull(testCase.extras<NoTestRunnerExtras>().inputDataFile?.let(TestRunParameter::WithInputData))
|
||||
}
|
||||
TestKind.STANDALONE_NO_TR -> {
|
||||
assertTrue(testName == null)
|
||||
addIfNotNull(testCase.extras<NoTestRunnerExtras>().inputDataFile?.let(TestRunParameter::WithInputData))
|
||||
|
||||
+10
@@ -8,7 +8,10 @@ package org.jetbrains.kotlin.konan.blackboxtest.support.runner
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.Executable
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Success
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.KotlinNativeHome
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.DumpedTestListing
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.LldbSessionSpecification
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.startsWith
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertFalse
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
@@ -96,6 +99,13 @@ internal sealed interface TestRunParameter {
|
||||
override fun applyTo(programArgs: MutableList<String>) = Unit
|
||||
}
|
||||
|
||||
class WithLLDB(val commands: List<String>) : TestRunParameter {
|
||||
override fun applyTo(programArgs: MutableList<String>) {
|
||||
programArgs.add(0, "lldb")
|
||||
programArgs.addAll(commands)
|
||||
}
|
||||
}
|
||||
|
||||
// Currently, used only for logging the data.
|
||||
class WithExpectedOutputData(val expectedOutputDataFile: File) : TestRunParameter {
|
||||
override fun applyTo(programArgs: MutableList<String>) = Unit
|
||||
|
||||
+7
-2
@@ -22,18 +22,22 @@ internal sealed interface TestRunCheck {
|
||||
}
|
||||
|
||||
class OutputDataFile(val file: File) : TestRunCheck
|
||||
|
||||
class OutputMatcher(val match: (String) -> Boolean): TestRunCheck
|
||||
}
|
||||
|
||||
internal class TestRunChecks(
|
||||
val executionTimeoutCheck: ExecutionTimeout,
|
||||
private val exitCodeCheck: ExitCode,
|
||||
val outputDataFile: OutputDataFile?
|
||||
val outputDataFile: OutputDataFile?,
|
||||
val outputMatcher: OutputMatcher?
|
||||
) : Iterable<TestRunCheck> {
|
||||
|
||||
override fun iterator() = iterator {
|
||||
yield(executionTimeoutCheck)
|
||||
yield(exitCodeCheck)
|
||||
yieldIfNotNull(outputDataFile)
|
||||
yieldIfNotNull(outputMatcher)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -42,7 +46,8 @@ internal class TestRunChecks(
|
||||
fun Default(timeout: Duration) = TestRunChecks(
|
||||
executionTimeoutCheck = ExecutionTimeout.ShouldNotExceed(timeout),
|
||||
exitCodeCheck = ExitCode.Expected(0),
|
||||
outputDataFile = null
|
||||
outputDataFile = null,
|
||||
outputMatcher = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilati
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationFactory
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.group.TestCaseGroupProvider
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.DebugUtils
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.Settings
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.ThreadSafeCache
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.util.TreeNode
|
||||
@@ -103,6 +104,12 @@ internal class TestRunProvider(
|
||||
fun createTestRun(testRunName: String, testName: TestName?) = createTestRun(testCase, executable, testRunName, testName)
|
||||
|
||||
when (testCase.kind) {
|
||||
TestKind.STANDALONE_LLDB -> {
|
||||
if (!settings.get<DebugUtils>().lldbIsAvailable) TreeNode.oneLevel<TestRun>()
|
||||
val testRunName = testCase.extras<NoTestRunnerExtras>().entryPoint.substringAfterLast('.')
|
||||
val testRun = createTestRun(testRunName, testName = null)
|
||||
TreeNode.oneLevel(testRun)
|
||||
}
|
||||
TestKind.STANDALONE_NO_TR -> {
|
||||
val testRunName = testCase.extras<NoTestRunnerExtras>().entryPoint.substringAfterLast('.')
|
||||
val testRun = createTestRun(testRunName, testName = null)
|
||||
@@ -130,7 +137,7 @@ internal class TestRunProvider(
|
||||
val testCase = testCaseGroup.getByName(testCaseId) ?: fail { "No test case for $testCaseId" }
|
||||
|
||||
val testCompilation = when (testCase.kind) {
|
||||
TestKind.STANDALONE, TestKind.STANDALONE_NO_TR -> {
|
||||
TestKind.STANDALONE, TestKind.STANDALONE_NO_TR, TestKind.STANDALONE_LLDB -> {
|
||||
// Create a separate compilation for each standalone test case.
|
||||
cachedCompilations.computeIfAbsent(
|
||||
TestCompilationCacheKey.Standalone(testCaseId)
|
||||
|
||||
+13
-1
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.konan.target.Distribution
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
@@ -29,12 +30,23 @@ internal class KotlinNativeTargets(val testTarget: KonanTarget, val hostTarget:
|
||||
internal class KotlinNativeHome(val dir: File) {
|
||||
val librariesDir: File = dir.resolve("klib")
|
||||
val stdlibFile: File = librariesDir.resolve("common/stdlib")
|
||||
|
||||
val lldbPrettyPrinters: File = dir.resolve("tools/konan_lldb.py")
|
||||
val properties: Properties by lazy {
|
||||
dir.resolve("konan/konan.properties").inputStream().use { Properties().apply { load(it) } }
|
||||
}
|
||||
}
|
||||
|
||||
internal class DebugUtils {
|
||||
val lldbIsAvailable: Boolean by lazy {
|
||||
try {
|
||||
val exitCode = ProcessBuilder("lldb", "-version").start().waitFor()
|
||||
exitCode == 0
|
||||
} catch (e: IOException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy-initialized class loader with the Kotlin/Native embedded compiler.
|
||||
*/
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.konan.blackboxtest.support.util
|
||||
|
||||
import org.jetbrains.kotlin.test.services.JUnit5Assertions.fail
|
||||
import java.io.File
|
||||
|
||||
private class Step(val command: String, val body: List<String>)
|
||||
|
||||
class LldbSessionSpecification private constructor(
|
||||
private val steps: List<Step>,
|
||||
) {
|
||||
|
||||
|
||||
fun generateCLIArguments(pp: File): List<String> {
|
||||
val args = mutableListOf("-b", "-o", "command script import ${pp.absolutePath}")
|
||||
args.addAll(steps.flatMap { listOf("-o", it.command) })
|
||||
return args
|
||||
}
|
||||
|
||||
// TODO: Re-introduce this when we add support for iOS simulator based testing.
|
||||
fun targetIsHost() = true
|
||||
|
||||
fun matchOutput(output: String): Boolean {
|
||||
val blocks = output.split(lldbOutputCommand).filterNot(String::isEmpty)
|
||||
if (targetIsHost()) {
|
||||
check(blocks[0].startsWith("(lldb) target create")) { "Missing block \"target create\". Got: ${blocks[0]}" }
|
||||
check(blocks[1].startsWith("(lldb) command script import")) {
|
||||
"Missing block \"command script import\". Got: ${blocks[1]}"
|
||||
}
|
||||
}
|
||||
val responses = if (targetIsHost())
|
||||
blocks.drop(2)
|
||||
else
|
||||
blocks.drop(2).dropLast(1)
|
||||
|
||||
val recordedSteps = responses.map { Step(it.lines().first(), it.lines().drop(1)) }
|
||||
|
||||
if (recordedSteps.size != steps.size) {
|
||||
val message = """
|
||||
|Responses do not match commands.
|
||||
|
|
||||
|COMMANDS: |${steps.map { it.command }} (${steps.size})
|
||||
|RESPONSES: |${recordedSteps.map { it.command }} (${recordedSteps.size})
|
||||
""".trimMargin()
|
||||
fail { message }
|
||||
}
|
||||
|
||||
steps.zip(recordedSteps).all { (step, recordedStep) -> recordedStep.command == "(lldb) ${step.command}" }
|
||||
|
||||
for ((step, recordedStep) in steps.zip(recordedSteps)) {
|
||||
check(recordedStep.command == "(lldb) ${step.command}") {
|
||||
"Wrong command in response. Expected: (lldb) ${step.command}. Got: ${recordedStep.command}"
|
||||
}
|
||||
val mismatch = findMismatch(step.body, recordedStep.body)
|
||||
if (mismatch != null) {
|
||||
val message = """
|
||||
|Wrong LLDB output.
|
||||
|
|
||||
|COMMAND: ${step.command}
|
||||
|PATTERN: $mismatch
|
||||
|OUTPUT:
|
||||
|${recordedStep.body.joinToString("\n")}
|
||||
""".trimMargin()
|
||||
fail { message }
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun findMismatch(patterns: List<String>, actualLines: List<String>): String? {
|
||||
val indices = mutableListOf<Int>()
|
||||
for (pattern in patterns) {
|
||||
val idx = actualLines.indexOfFirst { match(pattern, it) }
|
||||
if (idx == -1) {
|
||||
return pattern
|
||||
}
|
||||
indices += idx
|
||||
}
|
||||
check(indices == indices.sorted())
|
||||
return null
|
||||
}
|
||||
|
||||
private fun match(pattern: String, line: String): Boolean {
|
||||
val chunks = pattern.split(wildcard)
|
||||
.filter { it.isNotBlank() }
|
||||
.map { it.trim() }
|
||||
check(chunks.isNotEmpty())
|
||||
val trimmedLine = line.trim()
|
||||
|
||||
val indices = chunks.map { trimmedLine.indexOf(it) }
|
||||
if (indices.any { it == -1 } || indices != indices.sorted()) return false
|
||||
if (!(trimmedLine.startsWith(chunks.first()) || pattern.startsWith("[..]"))) return false
|
||||
if (!(trimmedLine.endsWith(chunks.last()) || pattern.endsWith("[..]"))) return false
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
val lldbOutputCommand = """(?=\(lldb\))""".toRegex()
|
||||
val wildcard = """\s*\[\.\.]\s*""".toRegex()
|
||||
val separator = "(?=^>)".toRegex(RegexOption.MULTILINE)
|
||||
|
||||
fun parse(spec: String): LldbSessionSpecification {
|
||||
val blocks = spec.trimIndent()
|
||||
.split(separator)
|
||||
.filterNot(String::isEmpty)
|
||||
for (cmd in blocks) {
|
||||
check(cmd.startsWith(">")) { "Invalid lldb session specification: $cmd" }
|
||||
}
|
||||
val steps = blocks.map {
|
||||
Step(it.lines().first().substring(1).trim(), it.lines().drop(1).filter { it.isNotBlank() })
|
||||
}
|
||||
return LldbSessionSpecification(steps)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user