Skip to content

Commit 89859ea

Browse files
authored
Some launcher cleanup (#4832)
<!-- If this PR updates documentation, please update all relevant versions of the docs, see: https://github.com/kotest/kotest/tree/master/documentation/versioned_docs The documentation at https://github.com/kotest/kotest/tree/master/documentation/docs is the documentation for the next minor or major version _TO BE RELEASED_ -->
1 parent 1c1ec4e commit 89859ea

File tree

8 files changed

+141
-84
lines changed

8 files changed

+141
-84
lines changed

kotest-framework/kotest-framework-engine/api/kotest-framework-engine.api

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3523,13 +3523,13 @@ public final class io/kotest/engine/interceptors/ProjectTimeoutException : java/
35233523
}
35243524

35253525
public final class io/kotest/engine/launcher/LauncherArgs {
3526-
public static final field CANDIDATES Ljava/lang/String;
3526+
public static final field ARG_CANDIDATES Ljava/lang/String;
3527+
public static final field ARG_LISTENER Ljava/lang/String;
3528+
public static final field ARG_TERMCOLOR Ljava/lang/String;
35273529
public static final field DESCRIPTOR Ljava/lang/String;
35283530
public static final field INSTANCE Lio/kotest/engine/launcher/LauncherArgs;
3529-
public static final field LISTENER Ljava/lang/String;
35303531
public static final field REPORTER Ljava/lang/String;
35313532
public static final field SPEC Ljava/lang/String;
3532-
public static final field TERMCOLORS Ljava/lang/String;
35333533
public static final field TESTPATH Ljava/lang/String;
35343534
public static final field WRITER Ljava/lang/String;
35353535
}
@@ -3539,13 +3539,11 @@ public final class io/kotest/engine/launcher/MainKt {
35393539
}
35403540

35413541
public final class io/kotest/engine/launcher/TestEngineListenerBuilder {
3542-
public static final field ANSI16 Ljava/lang/String;
3543-
public static final field ANSI256 Ljava/lang/String;
3542+
public static final field COLORS_PLAIN Ljava/lang/String;
3543+
public static final field COLORS_TRUE Ljava/lang/String;
35443544
public static final field Companion Lio/kotest/engine/launcher/TestEngineListenerBuilder$Companion;
3545-
public static final field DEFAULT Ljava/lang/String;
3546-
public static final field ENHANCED Ljava/lang/String;
3547-
public static final field TEAMCITY Ljava/lang/String;
3548-
public static final field TRUECOLOR Ljava/lang/String;
3545+
public static final field LISTENER_CONSOLE Ljava/lang/String;
3546+
public static final field LISTENER_TC Ljava/lang/String;
35493547
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
35503548
public final fun build ()Lio/kotest/engine/listener/TestEngineListener;
35513549
public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lio/kotest/engine/launcher/TestEngineListenerBuilder;

kotest-framework/kotest-framework-engine/src/commonMain/kotlin/io/kotest/engine/extensions/DescriptorFilter.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import io.kotest.core.extensions.Extension
55

66
/**
77
* A [DescriptorFilter] can be used to filter specs and tests before they are executed.
8+
*
89
* A given [Descriptor] must be included by all filters for it to be considered enabled at runtime.
9-
* If no filters are registered, then no specs and tests will be excluded.
10+
*
11+
* If no filters are registered, then all specs and tests will be executed.
1012
*/
1113
interface DescriptorFilter : Extension {
1214

kotest-framework/kotest-framework-engine/src/jvmMain/kotlin/io/kotest/engine/launcher/TestEngineListenerBuilder.kt

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,27 @@ import io.kotest.engine.listener.EnhancedConsoleTestEngineListener
55
import io.kotest.engine.listener.TeamCityTestEngineListener
66
import io.kotest.engine.listener.TestEngineListener
77

8+
/**
9+
* Builds a [TestEngineListener] based on the type and termcolors specified which is suitable
10+
* for test engines launched externally, by gradle or from intellij for example.
11+
*/
812
data class TestEngineListenerBuilder(
913
private val type: String?,
1014
private val termcolors: String?,
1115
) {
1216

1317
companion object {
1418

15-
const val TEAMCITY = "teamcity"
16-
const val ENHANCED = "enhanced"
17-
const val DEFAULT = "default"
18-
const val TRUECOLOR = "true"
19-
const val ANSI256 = "ansi256"
20-
const val ANSI16 = "ansi16"
19+
// the value used to specify the team city format
20+
const val LISTENER_TC = "teamcity"
21+
22+
// the value used to specify a console format
23+
const val LISTENER_CONSOLE = "enhanced"
24+
25+
const val COLORS_PLAIN = "ansi16"
26+
const val COLORS_TRUE = "true"
27+
28+
internal const val IDEA_PROP = "idea.active"
2129

2230
fun builder(): TestEngineListenerBuilder = TestEngineListenerBuilder(null, null)
2331
}
@@ -27,21 +35,21 @@ data class TestEngineListenerBuilder(
2735

2836
fun build(): TestEngineListener {
2937
return when (type) {
30-
TEAMCITY -> TeamCityTestEngineListener()
31-
ENHANCED -> EnhancedConsoleTestEngineListener(TermColors())
38+
LISTENER_TC -> TeamCityTestEngineListener()
39+
LISTENER_CONSOLE -> EnhancedConsoleTestEngineListener(colours())
40+
// if not speciifed, we'll try to detect instead
3241
else if isIntellij() -> TeamCityTestEngineListener()
3342
else -> EnhancedConsoleTestEngineListener(colours())
3443
}
3544
}
3645

3746
// this system property is added by intellij itself when running tasks
38-
private fun isIntellij() = System.getProperty("idea.active") != null
47+
private fun isIntellij() = System.getProperty(IDEA_PROP) != null
3948

4049
internal fun colours(): TermColors {
4150
return when (termcolors) {
42-
TRUECOLOR -> TermColors(TermColors.Level.TRUECOLOR)
43-
ANSI256 -> TermColors(TermColors.Level.ANSI256)
44-
ANSI16 -> TermColors(TermColors.Level.ANSI16)
51+
COLORS_TRUE -> TermColors(TermColors.Level.TRUECOLOR)
52+
COLORS_PLAIN -> TermColors(TermColors.Level.ANSI16)
4553
else -> TermColors()
4654
}
4755
}

kotest-framework/kotest-framework-engine/src/jvmMain/kotlin/io/kotest/engine/launcher/main.kt

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,38 @@ import io.kotest.core.spec.Spec
55
import io.kotest.engine.TestEngineLauncher
66
import io.kotest.engine.cli.parseArgs
77
import io.kotest.engine.extensions.ProvidedDescriptorFilter
8-
import io.kotest.engine.launcher.LauncherArgs.CANDIDATES
8+
import io.kotest.engine.launcher.LauncherArgs.ARG_CANDIDATES
9+
import io.kotest.engine.launcher.LauncherArgs.ARG_LISTENER
10+
import io.kotest.engine.launcher.LauncherArgs.ARG_TERMCOLOR
911
import io.kotest.engine.launcher.LauncherArgs.DESCRIPTOR
10-
import io.kotest.engine.launcher.LauncherArgs.LISTENER
1112
import io.kotest.engine.launcher.LauncherArgs.REPORTER
1213
import io.kotest.engine.launcher.LauncherArgs.SPEC
13-
import io.kotest.engine.launcher.LauncherArgs.TERMCOLORS
1414
import io.kotest.engine.launcher.LauncherArgs.TESTPATH
1515
import io.kotest.engine.launcher.LauncherArgs.WRITER
1616
import io.kotest.engine.listener.CollectingTestEngineListener
1717
import io.kotest.engine.listener.CompositeTestEngineListener
1818
import io.kotest.engine.listener.LoggingTestEngineListener
1919
import io.kotest.engine.listener.PinnedSpecTestEngineListener
20+
import io.kotest.engine.listener.TestEngineListener
2021
import io.kotest.engine.listener.ThreadSafeTestEngineListener
2122
import io.kotest.engine.runBlocking
2223
import kotlin.reflect.KClass
2324
import kotlin.system.exitProcess
2425

2526
object LauncherArgs {
2627

27-
const val CANDIDATES = "candidates"
28+
// required to pass the candidates to the engine
29+
const val ARG_CANDIDATES = "candidates"
2830

2931
// these are optional
30-
const val LISTENER = "listener"
31-
const val TERMCOLORS = "termcolors"
32+
33+
// used to specify if we want team city or console output
34+
const val ARG_LISTENER = "--listener"
35+
36+
// used to specify the color of the output
37+
const val ARG_TERMCOLOR = "--termcolor"
38+
39+
// used to filter to a single spec or test within a spec
3240
const val DESCRIPTOR = "descriptor"
3341

3442
// these are deprecated kotest 5 flags kept for backwards compatibility
@@ -41,11 +49,12 @@ object LauncherArgs {
4149
/**
4250
* The entry point for the launcher.
4351
*
44-
* Parses the cli args, creates the listeners and creates a test launcher using a [TestEngineLauncherBuilder].
52+
* Parses the cli args, creates [io.kotest.engine.listener.TestEngineListener]s and invokes
53+
* the test engine using a [TestEngineLauncher].
4554
*
4655
* --- IMPORTANT NOTE ---
4756
* This is used by the Gradle and Intellij plugins (and other third party clients).
48-
* Therefore, the package name and contract for this main method *MUST* remain backwards compatible.
57+
* Therefore, the package name and contract for this main method **MUST** remain backwards compatible.
4958
*/
5059
fun main(args: Array<String>) {
5160

@@ -54,37 +63,28 @@ fun main(args: Array<String>) {
5463
val launcherArgs = parseArgs(args.toList())
5564
// println("Parsed args: $launcherArgs")
5665

57-
// The launcher *must* be told what classes are available on the classpath, the engine will not perform scanning.
66+
// The enigne *must* be given the classes to execute - in Kotest 6 the engine does not perform scanning
5867
// It is the responsibility of the caller to pass this information.
59-
// In Kotest 5 the argument was called --spec but was changed to --candidates in Kotest 6,
68+
// In Kotest 5 a similar argument was called --spec to specify a single class but kotest 6 uses --candidates
6069
// we must support both for backwards compatibility
61-
val candidatesArg = launcherArgs[CANDIDATES]
70+
// todo do we need to do this? if people are upgrading to kotest 6 they can update the plugin too?
71+
val candidatesArg = launcherArgs[ARG_CANDIDATES]
6272
?: launcherArgs[SPEC]
63-
?: error("The $CANDIDATES arg must be provided")
73+
?: error("The $ARG_CANDIDATES arg must be provided")
6474

6575
@Suppress("UNCHECKED_CAST")
6676
val classes = candidatesArg.split(';').map { Class.forName(it).kotlin as KClass<out Spec> }
6777

6878
// we support --descriptor to support an exact descriptor path as a way to run a single test
69-
val descriptorFilter = launcherArgs[DESCRIPTOR]?.let { descriptor ->
70-
// println("Making a filter from input $descriptor")
71-
ProvidedDescriptorFilter(DescriptorPaths.parse(descriptor))
72-
}
79+
val descriptorFilter = buildDescriptorFilter(launcherArgs)
7380

7481
// Kotest 5 supported --testpath and didn't support the a descriptor selector, only the test name
7582
// but we can combine that with the --spec arg which we know must be present in kotest 5 if testpath is
76-
val descriptorFilterKotest5 = launcherArgs[TESTPATH]?.let { test ->
77-
launcherArgs[SPEC]?.let { spec ->
78-
ProvidedDescriptorFilter(DescriptorPaths.parse("$spec/$test"))
79-
}
80-
}
83+
// this exists so people can upgrade to kotest 6 but keep the old plugin
84+
// todo do we need to do this? if people are upgrading to kotest 6 they can update the plugin too?
85+
val descriptorFilterKotest5 = buildKotest5DescriptorFilter(launcherArgs)
8186

82-
val console = TestEngineListenerBuilder.builder()
83-
.withType(
84-
launcherArgs[LISTENER] ?: launcherArgs[REPORTER] ?: launcherArgs[WRITER]
85-
) // sets the output type, will be detected if not specified
86-
.withTermColors(launcherArgs[TERMCOLORS]) // if using the console, determines the prettiness of the output
87-
.build()
87+
val outputListener = buildOutputTestEngineListener(launcherArgs)
8888

8989
// we want to collect the results, so we can check if we need exit with an error
9090
val collector = CollectingTestEngineListener()
@@ -93,7 +93,7 @@ fun main(args: Array<String>) {
9393
CompositeTestEngineListener(
9494
collector,
9595
LoggingTestEngineListener,// we use this to write to the kotest log file
96-
ThreadSafeTestEngineListener(PinnedSpecTestEngineListener(console))
96+
ThreadSafeTestEngineListener(PinnedSpecTestEngineListener(outputListener))
9797
)
9898
).withClasses(classes)
9999
.addExtensions(listOfNotNull(descriptorFilter, descriptorFilterKotest5))
@@ -107,3 +107,27 @@ fun main(args: Array<String>) {
107107
// so we must force the exit
108108
if (collector.errors) exitProcess(-1) else exitProcess(0)
109109
}
110+
111+
private fun buildOutputTestEngineListener(launcherArgs: Map<String, String>): TestEngineListener {
112+
return TestEngineListenerBuilder.builder()
113+
.withType(
114+
launcherArgs[ARG_LISTENER] ?: launcherArgs[REPORTER] ?: launcherArgs[WRITER]
115+
) // sets the output type, will be detected if not specified
116+
.withTermColors(launcherArgs[ARG_TERMCOLOR]) // if using the console, determines the prettiness of the output
117+
.build()
118+
}
119+
120+
private fun buildDescriptorFilter(launcherArgs: Map<String, String>): ProvidedDescriptorFilter? {
121+
return launcherArgs[DESCRIPTOR]?.let { descriptor ->
122+
// println("Making a filter from input $descriptor")
123+
ProvidedDescriptorFilter(DescriptorPaths.parse(descriptor))
124+
}
125+
}
126+
127+
private fun buildKotest5DescriptorFilter(launcherArgs: Map<String, String>): ProvidedDescriptorFilter? {
128+
return launcherArgs[TESTPATH]?.let { test ->
129+
launcherArgs[SPEC]?.let { spec ->
130+
ProvidedDescriptorFilter(DescriptorPaths.parse("$spec/$test"))
131+
}
132+
}
133+
}

kotest-framework/kotest-framework-engine/src/jvmTest/kotlin/io/kotest/engine/launcher/TestEngineListenerBuilderTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ class TestEngineListenerBuilderTest : FunSpec() {
1414
test("specifying teamcity should return TeamCityTestEngineListener") {
1515
TestEngineListenerBuilder
1616
.builder()
17-
.withType(TestEngineListenerBuilder.TEAMCITY)
17+
.withType(TestEngineListenerBuilder.LISTENER_TC)
1818
.build()
1919
.shouldBeInstanceOf<TeamCityTestEngineListener>()
2020
}
2121

2222
test("specifying enchanced should return EnhancedConsoleTestEngineListener") {
2323
TestEngineListenerBuilder
2424
.builder()
25-
.withType(TestEngineListenerBuilder.ENHANCED)
25+
.withType(TestEngineListenerBuilder.LISTENER_CONSOLE)
2626
.build()
2727
.shouldBeInstanceOf<EnhancedConsoleTestEngineListener>()
2828
}

kotest-framework/kotest-framework-plugin-gradle/src/main/kotlin/io/kotest/framework/gradle/TestClassDetector.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ internal class TestClassDetector {
3131
fun detect(inputs: FileTree): Set<TestClass> {
3232
parents.clear()
3333
inputs.filter { it.name.endsWith(".class") }.asFileTree.visit(visitor)
34-
return candidates.filter { isSpecClass(parents[it] ?: "") }.map { toTestClass(it) }.toSet()
34+
return candidates.filter { isSpecClass(it) }.map { toTestClass(it) }.toSet()
3535
}
3636

3737
private fun toTestClass(className: String): TestClass {
@@ -50,13 +50,13 @@ internal class TestClassDetector {
5050
}
5151

5252
/**
53-
* Returns true if this class extends directly a spec class,
53+
* Returns true if this class directly extends a spec class,
5454
* or indirectly via a chain of super classes.
5555
*/
56-
private fun isSpecClass(superName: String): Boolean {
56+
private fun isSpecClass(className: String): Boolean {
57+
val superName = parents[className] ?: return false
5758
if (specClasses.contains(superName)) return true
58-
val superSuperName = parents[superName] ?: return false
59-
return isSpecClass(superSuperName)
59+
return isSpecClass(superName)
6060
}
6161

6262
// basic visitor that just adds every class and its parent to the mutable map

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy