Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for running private classes #3915

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions documentation/docs/framework/config_props.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ object KotestEngineProperties {
*/
const val filterSpecs = "kotest.filter.specs"

/**
* If set to "true" then private classes (specs) will be included in the test suite.
*/
const val includePrivateClasses = "kotest.include.private.classes"

const val propertiesFilename = "kotest.properties.filename"

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,7 @@ public abstract class io/kotest/core/config/AbstractProjectConfig {
public fun getFailOnEmptyTestSuite ()Ljava/lang/Boolean;
public fun getFailOnIgnoredTests ()Ljava/lang/Boolean;
public fun getGlobalAssertSoftly ()Ljava/lang/Boolean;
public fun getIncludePrivateClasses ()Ljava/lang/Boolean;
public fun getIncludeTestScopePrefixes ()Ljava/lang/Boolean;
public fun getInvocationTimeout ()Ljava/lang/Long;
public fun getIsolationMode ()Lio/kotest/core/spec/IsolationMode;
Expand All @@ -926,6 +927,7 @@ public abstract class io/kotest/core/config/AbstractProjectConfig {
public fun setDisableTestNestedJarScanning (Ljava/lang/Boolean;)V
public fun setDispatcherAffinity (Ljava/lang/Boolean;)V
public fun setDisplayFullTestPath (Ljava/lang/Boolean;)V
public fun setIncludePrivateClasses (Ljava/lang/Boolean;)V
public fun setProjectWideFailFast (Ljava/lang/Boolean;)V
public fun setRandomOrderSeed (Ljava/lang/Long;)V
public fun setTestCoroutineDispatcher (Z)V
Expand Down Expand Up @@ -978,6 +980,7 @@ public final class io/kotest/core/config/Defaults {
public static final field failOnIgnoredTests Z
public static final field failfast Z
public static final field globalAssertSoftly Z
public static final field includePrivateClasses Z
public static final field parallelism I
public static final field projectWideFailFast Z
public static final field specFailureFilePath Ljava/lang/String;
Expand Down Expand Up @@ -1083,6 +1086,7 @@ public final class io/kotest/core/config/ProjectConfiguration {
public final fun getFailOnIgnoredTests ()Z
public final fun getFailfast ()Z
public final fun getGlobalAssertSoftly ()Z
public final fun getIncludePrivateClasses ()Z
public final fun getIncludeTestScopeAffixes ()Ljava/lang/Boolean;
public final fun getInvocationTimeout ()Ljava/lang/Long;
public final fun getIsolationMode ()Lio/kotest/core/spec/IsolationMode;
Expand Down Expand Up @@ -1121,6 +1125,7 @@ public final class io/kotest/core/config/ProjectConfiguration {
public final fun setFailOnIgnoredTests (Z)V
public final fun setFailfast (Z)V
public final fun setGlobalAssertSoftly (Z)V
public final fun setIncludePrivateClasses (Z)V
public final fun setIncludeTestScopeAffixes (Ljava/lang/Boolean;)V
public final fun setInvocationTimeout (Ljava/lang/Long;)V
public final fun setIsolationMode (Lio/kotest/core/spec/IsolationMode;)V
Expand Down Expand Up @@ -1577,6 +1582,7 @@ public final class io/kotest/core/internal/KotestEngineProperties {
public static final field filterSpecs Ljava/lang/String;
public static final field filterTests Ljava/lang/String;
public static final field globalAssertSoftly Ljava/lang/String;
public static final field includePrivateClasses Ljava/lang/String;
public static final field includeTags Ljava/lang/String;
public static final field invocationTimeout Ljava/lang/String;
public static final field isolationMode Ljava/lang/String;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ abstract class AbstractProjectConfig {
*/
open var disableTestNestedJarScanning: Boolean? = null

open var includePrivateClasses: Boolean? = null

/**
* If set to true then the test engine will install a
* [`TestDispatcher`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-dispatcher/).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,6 @@ object Defaults {
const val dispatcherAffinity = true

const val allowOutOfOrderCallbacks = false

const val includePrivateClasses: Boolean = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,11 @@ class ProjectConfiguration {

var allowOutOfOrderCallbacks: Boolean = Defaults.allowOutOfOrderCallbacks

/**
* Set to true to include private classes when executing specs.
*/
var includePrivateClasses: Boolean = Defaults.includePrivateClasses

/**
* Returns all globally registered [Listener]s.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ object KotestEngineProperties {
*/
const val filterSpecs = "kotest.filter.specs"

/**
* If set to "true" then private classes (specs) will be included in the test suite.
*/
const val includePrivateClasses = "kotest.include.private.classes"

const val propertiesFilename = "kotest.properties.filename"

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.kotest.datatest

import io.kotest.core.config.ProjectConfiguration
import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.core.spec.style.FunSpec
import io.kotest.core.spec.style.WordSpec
import io.kotest.engine.TestEngineLauncher
import io.kotest.engine.listener.CollectingTestEngineListener
import io.kotest.matchers.shouldBe
import kotlin.random.Random

class DataTestingRepeatedTestNameTest : FunSpec() {
init {
Expand All @@ -17,6 +20,7 @@ class DataTestingRepeatedTestNameTest : FunSpec() {
val collector = CollectingTestEngineListener()
TestEngineLauncher(collector)
.withClasses(RepeatedNamesDescribeSpec::class)
.withConfiguration(ProjectConfiguration().also { it.includePrivateClasses = true })
.launch()

collector.names shouldBe listOf(
Expand All @@ -36,6 +40,7 @@ class DataTestingRepeatedTestNameTest : FunSpec() {
val collector = CollectingTestEngineListener()
TestEngineLauncher(collector)
.withClasses(RepeatedNamesDescribeSpecRoot::class)
.withConfiguration(ProjectConfiguration().also { it.includePrivateClasses = true })
.launch()

collector.names shouldBe listOf(
Expand All @@ -54,6 +59,7 @@ class DataTestingRepeatedTestNameTest : FunSpec() {
val collector = CollectingTestEngineListener()
TestEngineLauncher(collector)
.withClasses(RepeatedNamesFunSpec::class)
.withConfiguration(ProjectConfiguration().also { it.includePrivateClasses = true })
.launch()

collector.names shouldBe listOf(
Expand All @@ -73,6 +79,7 @@ class DataTestingRepeatedTestNameTest : FunSpec() {
val collector = CollectingTestEngineListener()
TestEngineLauncher(collector)
.withClasses(RepeatedNamesRootFunSpec::class)
.withConfiguration(ProjectConfiguration().also { it.includePrivateClasses = true })
.launch()

collector.names shouldBe listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.kotest.datatest

import io.kotest.common.ExperimentalKotest
import io.kotest.core.annotation.Isolate
import io.kotest.core.config.ProjectConfiguration
import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.core.spec.style.FunSpec
Expand Down Expand Up @@ -35,6 +36,7 @@ class UnstableTestNameWithLeafIsolationTest : FunSpec() {
TestEngineLauncher()
.withListener(listener)
.withClasses(RegularClassAndLeafIsolation::class)
.withConfiguration(ProjectConfiguration().also { it.includePrivateClasses = true })
.launch()

results shouldBe listOf(
Expand All @@ -53,6 +55,7 @@ class UnstableTestNameWithLeafIsolationTest : FunSpec() {
TestEngineLauncher()
.withListener(listener)
.withClasses(DataClassWithNonDataParameterAndLeafIsolation::class)
.withConfiguration(ProjectConfiguration().also { it.includePrivateClasses = true })
.launch()

results shouldBe listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,6 @@ public abstract class io/kotest/framework/discovery/DiscoveryFilter {
public abstract fun test (Lkotlin/reflect/KClass;)Z
}

public final class io/kotest/framework/discovery/DiscoveryFilter$ClassModifierDiscoveryFilter : io/kotest/framework/discovery/DiscoveryFilter {
public fun <init> (Ljava/util/Set;)V
public final fun component1 ()Ljava/util/Set;
public final fun copy (Ljava/util/Set;)Lio/kotest/framework/discovery/DiscoveryFilter$ClassModifierDiscoveryFilter;
public static synthetic fun copy$default (Lio/kotest/framework/discovery/DiscoveryFilter$ClassModifierDiscoveryFilter;Ljava/util/Set;ILjava/lang/Object;)Lio/kotest/framework/discovery/DiscoveryFilter$ClassModifierDiscoveryFilter;
public fun equals (Ljava/lang/Object;)Z
public final fun getModifiers ()Ljava/util/Set;
public fun hashCode ()I
public fun test (Lkotlin/reflect/KClass;)Z
public fun toString ()Ljava/lang/String;
}

public final class io/kotest/framework/discovery/DiscoveryFilter$ClassNameDiscoveryFilter : io/kotest/framework/discovery/DiscoveryFilter {
public fun <init> (Lkotlin/jvm/functions/Function1;)V
public final fun component1 ()Lkotlin/jvm/functions/Function1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package io.kotest.framework.discovery

import io.kotest.core.spec.Spec
import kotlin.reflect.KClass
import kotlin.reflect.KVisibility

sealed class DiscoveryFilter {

Expand All @@ -25,20 +24,4 @@ sealed class DiscoveryFilter {
return f(PackageName(kclass.java.`package`.name))
}
}

/**
* Filters specs based on their [Modifier] values (public, internal, etc).
* A Spec is included if it has a modifier that is included in the given set.
*/
data class ClassModifierDiscoveryFilter(val modifiers: Set<Modifier>) : DiscoveryFilter() {
override fun test(kclass: KClass<out Spec>): Boolean {
if (kclass.visibility == KVisibility.INTERNAL)
return modifiers.contains(Modifier.Internal)
if (kclass.visibility == KVisibility.PUBLIC || java.lang.reflect.Modifier.isPublic(kclass.java.modifiers))
return modifiers.contains(Modifier.Public)
if (kclass.visibility == KVisibility.PRIVATE || java.lang.reflect.Modifier.isPrivate(kclass.java.modifiers))
return modifiers.contains(Modifier.Private)
return false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public final class io/kotest/engine/TestEngineLauncher {
public final fun withListener (Lio/kotest/engine/listener/TestEngineListener;)Lio/kotest/engine/TestEngineLauncher;
public final fun withNative ()Lio/kotest/engine/TestEngineLauncher;
public final fun withPlatform (Lio/kotest/common/Platform;)Lio/kotest/engine/TestEngineLauncher;
public final fun withPrivate (Z)Lio/kotest/engine/TestEngineLauncher;
public final fun withProjectConfig ([Lio/kotest/core/config/AbstractProjectConfig;)Lio/kotest/engine/TestEngineLauncher;
public final fun withSpecs ([Lio/kotest/core/spec/Spec;)Lio/kotest/engine/TestEngineLauncher;
public final fun withTagExpression (Lio/kotest/core/TagExpression;)Lio/kotest/engine/TestEngineLauncher;
Expand Down Expand Up @@ -262,18 +263,20 @@ public final class io/kotest/engine/interceptors/ProjectTimeoutException : java/
}

public final class io/kotest/engine/launcher/LauncherArgs {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/String;
public final fun component4 ()Ljava/lang/String;
public final fun component5 ()Ljava/lang/String;
public final fun component6 ()Ljava/lang/String;
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lio/kotest/engine/launcher/LauncherArgs;
public static synthetic fun copy$default (Lio/kotest/engine/launcher/LauncherArgs;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/kotest/engine/launcher/LauncherArgs;
public final fun component7 ()Z
public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Lio/kotest/engine/launcher/LauncherArgs;
public static synthetic fun copy$default (Lio/kotest/engine/launcher/LauncherArgs;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Lio/kotest/engine/launcher/LauncherArgs;
public fun equals (Ljava/lang/Object;)Z
public final fun getListener ()Ljava/lang/String;
public final fun getPackageName ()Ljava/lang/String;
public final fun getPrivate ()Z
public final fun getSpec ()Ljava/lang/String;
public final fun getTagExpression ()Ljava/lang/String;
public final fun getTermcolor ()Ljava/lang/String;
Expand Down Expand Up @@ -516,6 +519,11 @@ public final class io/kotest/engine/spec/TempfileKt {
public static synthetic fun tempfile$default (Lio/kotest/core/TestConfiguration;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Ljava/io/File;
}

public final class io/kotest/engine/spec/interceptors/ClassVisibilitySpecRefInterceptor : io/kotest/engine/spec/interceptor/SpecRefInterceptor {
public fun <init> (Lio/kotest/engine/interceptors/EngineContext;)V
public fun intercept-0E7RQCE (Lio/kotest/core/spec/SpecRef;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class io/kotest/engine/tags/ActiveKt {
public static final fun isActive (Lio/kotest/engine/tags/Expression;Lio/kotest/core/Tag;)Z
public static final fun isActive (Lio/kotest/engine/tags/Expression;Ljava/util/Set;)Z
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ class TestEngineLauncher(
return this
}

/**
* Note: If after invoking this method, the [withConfiguration] is invoked, then any changes
* here will be lost.
*/
fun withPrivate(private: Boolean): TestEngineLauncher {
projectConfiguration.includePrivateClasses = private
return this
}

fun withConfiguration(configuration: ProjectConfiguration): TestEngineLauncher {
return TestEngineLauncher(
platform = platform,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ internal fun applyConfigFromProjectConfig(config: AbstractProjectConfig, configu

// discovery
config.disableTestNestedJarScanning?.let { configuration.disableTestNestedJarScanning = it }
config.includePrivateClasses?.let { configuration.includePrivateClasses = it }

// test names
config.includeTestScopePrefixes?.let { configuration.includeTestScopeAffixes = it }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ internal class SpecRefInterceptorPipeline(
ref: SpecRef,
inner: suspend (SpecRef) -> Result<Map<TestCase, TestResult>>
): Result<Map<TestCase, TestResult>> {
val interceptors = createPipeline()
val interceptors = platformInterceptors(context) + createCommonInterceptors()
logger.log { Pair(ref.kclass.bestName(), "Executing ${interceptors.size} reference interceptors") }
return interceptors.foldRight(inner) { interceptor, fn: suspend (SpecRef) -> Result<Map<TestCase, TestResult>> ->
{ ref -> interceptor.intercept(ref, fn) }
}.invoke(ref)
}

private fun createPipeline(): List<SpecRefInterceptor> {
private fun createCommonInterceptors(): List<SpecRefInterceptor> {
return listOfNotNull(
RequiresPlatformInterceptor(listener, context, configuration.registry),
if (platform == Platform.JVM) EnabledIfInterceptor(listener, configuration.registry) else null,
Expand All @@ -73,3 +73,5 @@ internal class SpecRefInterceptorPipeline(
)
}
}

internal expect fun platformInterceptors(context: EngineContext): List<SpecRefInterceptor>
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import kotlin.time.Duration
* https://github.com/JetBrains/intellij-community/blob/master/plugins/testng/testSources/com/theoryinpractice/testng/configuration/TestNGTreeHierarchyTest.java
* https://github.com/JetBrains/intellij-community/blob/master/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java
*
* https://github.com/JetBrains/intellij-community/blob/23bb68de04cfd849d615dac6ceaa2738e5bd431d/platform/testFramework/core/src/com/intellij/testFramework/TeamCityLogger.java#L15
*
* @param prefix Is the opening string used to signal that the line is a team city line.
* If unspecified this defaults to the team city format '##teamcity'.
* Can be overriden to help with testing.
*
* @param escapeColons team city uses colons in its format, and does not support colons inside messages properly,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.kotest.engine.spec.interceptor

import io.kotest.engine.interceptors.EngineContext

internal actual fun platformInterceptors(context: EngineContext): List<SpecRefInterceptor> = emptyList()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.kotest.engine.spec.interceptor

import io.kotest.engine.interceptors.EngineContext

internal actual fun platformInterceptors(context: EngineContext): List<SpecRefInterceptor> = emptyList()
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ internal fun setupLauncher(
.withExtensions(listOfNotNull(filter))
.withTagExpression(args.tagExpression?.let { TagExpression(it) })
.withClasses(specs)
.withPrivate(args.private)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ data class LauncherArgs(
val listener: String?,
// Tag expression to control which tests are executed
val tagExpression: String?,
val private: Boolean,
)

fun parseLauncherArgs(args: List<String>): LauncherArgs {
Expand All @@ -26,5 +27,6 @@ fun parseLauncherArgs(args: List<String>): LauncherArgs {
termcolor = a["termcolor"],
listener = a["listener"] ?: a["writer"] ?: a["reporter"],
tagExpression = a["tags"],
private = a["private"] == "true",
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.kotest.engine.spec.interceptor

import io.kotest.engine.interceptors.EngineContext
import io.kotest.engine.spec.interceptors.ClassVisibilitySpecRefInterceptor

internal actual fun platformInterceptors(context: EngineContext): List<SpecRefInterceptor> {
return listOf(ClassVisibilitySpecRefInterceptor(context))
}