From 65afe37704cafc80de601dc246482987a57d07fe Mon Sep 17 00:00:00 2001 From: Rick Busarow Date: Fri, 5 Jan 2024 18:41:00 -0600 Subject: [PATCH] adding `KaseTestFactoryNodeBuilder` --- .gitignore | 2 +- .idea/ktlint-plugin.xml | 6 + .idea/ktlint.xml | 7 - build.gradle.kts | 5 +- detekt/detekt-config.yml | 2 + kase-gradle/api/kase-gradle.api | 2 + kase/api/kase.api | 71 +++++++--- .../com/rickbusarow/kase/KaseTestFactory.kt | 34 +++-- .../kase/KaseTestFactoryNodeBuilder.kt | 63 +++++++++ .../com/rickbusarow/kase/TestEnvironment.kt | 8 +- .../kase/TestEnvironmentFactory.kt | 19 ++- .../com/rickbusarow/kase/TestNodeBuilder.kt | 118 +++++++---------- .../kase/internal/DefaultTestNodeBuilder.kt | 122 ++++++++++++++++++ .../rickbusarow/kase/KaseTestFactoryTest.kt | 44 ++++++- 14 files changed, 383 insertions(+), 120 deletions(-) create mode 100644 .idea/ktlint-plugin.xml delete mode 100644 .idea/ktlint.xml create mode 100644 kase/src/main/kotlin/com/rickbusarow/kase/KaseTestFactoryNodeBuilder.kt create mode 100644 kase/src/main/kotlin/com/rickbusarow/kase/internal/DefaultTestNodeBuilder.kt diff --git a/.gitignore b/.gitignore index 309db6a..898c3b3 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,7 @@ captures/ !/.idea/runConfigurations/ !/.idea/inspectionProfiles/ !.idea/detekt.xml -!.idea/ktlint.xml +!.idea/ktlint-plugin.xml # Keystore files # Uncomment the following lines if you do not want to check your keystore files in. diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml new file mode 100644 index 0000000..bee5678 --- /dev/null +++ b/.idea/ktlint-plugin.xml @@ -0,0 +1,6 @@ + + + + DISTRACT_FREE + + \ No newline at end of file diff --git a/.idea/ktlint.xml b/.idea/ktlint.xml deleted file mode 100644 index 7efbd1d..0000000 --- a/.idea/ktlint.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - false - true - - \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index c1e3646..fd4fe02 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Rick Busarow + * Copyright (C) 2024 Rick Busarow * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -17,7 +17,6 @@ import builds.GROUP import com.rickbusarow.doks.DoksTask import com.rickbusarow.kgx.mustRunAfter import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import kotlin.text.Regex.Companion.escapeReplacement buildscript { @@ -37,7 +36,7 @@ doks { rule("maven-with-version") { regex = maven(GROUP) - replacement = "$1:$2:${libs.versions.rickBusarow.kase.get() .escapeReplacement()}" + replacement = "$1:$2:${libs.versions.rickBusarow.kase.get().escapeReplacement()}" } rule("kgx-group") { regex = "com\\.rickbusarow\\.kase" diff --git a/detekt/detekt-config.yml b/detekt/detekt-config.yml index 00cbc61..dbc77d2 100644 --- a/detekt/detekt-config.yml +++ b/detekt/detekt-config.yml @@ -594,6 +594,8 @@ potential-bugs: - "kotlinx.coroutines.flow.*Flow" - "java.util.stream.*Stream" ignoreFunctionCall: [] + excludes: + - "**/test/**" ImplicitDefaultLocale: active: true ImplicitUnitReturnType: diff --git a/kase-gradle/api/kase-gradle.api b/kase-gradle/api/kase-gradle.api index dae05f1..28d6e2a 100644 --- a/kase-gradle/api/kase-gradle.api +++ b/kase-gradle/api/kase-gradle.api @@ -440,6 +440,7 @@ public final class com/rickbusarow/kase/gradle/GradleTestEnvironmentFactory$Defa public static fun getDslLanguage (Lcom/rickbusarow/kase/gradle/GradleTestEnvironmentFactory;)Lcom/rickbusarow/kase/gradle/DslLanguage; public static fun getLocalM2Path (Lcom/rickbusarow/kase/gradle/GradleTestEnvironmentFactory;)Ljava/io/File; public static fun newTestEnvironment (Lcom/rickbusarow/kase/gradle/GradleTestEnvironmentFactory;Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/gradle/GradleTestEnvironment; + public static fun newTestEnvironment (Lcom/rickbusarow/kase/gradle/GradleTestEnvironmentFactory;Ljava/util/List;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/gradle/GradleTestEnvironment; public static fun settingsFileDefault (Lcom/rickbusarow/kase/gradle/GradleTestEnvironmentFactory;Lcom/rickbusarow/kase/Kase;)Lcom/rickbusarow/kase/gradle/DslStringFactory; } @@ -599,6 +600,7 @@ public final class com/rickbusarow/kase/gradle/KaseGradleTest$DefaultImpls { public static fun getLocalM2Path (Lcom/rickbusarow/kase/gradle/KaseGradleTest;)Ljava/io/File; public static fun getVersionMatrix (Lcom/rickbusarow/kase/gradle/KaseGradleTest;)Lcom/rickbusarow/kase/KaseMatrix; public static fun newTestEnvironment (Lcom/rickbusarow/kase/gradle/KaseGradleTest;Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/gradle/GradleTestEnvironment; + public static fun newTestEnvironment (Lcom/rickbusarow/kase/gradle/KaseGradleTest;Ljava/util/List;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/gradle/GradleTestEnvironment; public static fun settingsFileDefault (Lcom/rickbusarow/kase/gradle/KaseGradleTest;Lcom/rickbusarow/kase/Kase;)Lcom/rickbusarow/kase/gradle/DslStringFactory; public static fun test (Lcom/rickbusarow/kase/gradle/KaseGradleTest;Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;Lkotlin/jvm/functions/Function2;)V public static fun testFactory (Lcom/rickbusarow/kase/gradle/KaseGradleTest;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; diff --git a/kase/api/kase.api b/kase/api/kase.api index 1435bfe..5d349bf 100644 --- a/kase/api/kase.api +++ b/kase/api/kase.api @@ -2096,6 +2096,7 @@ public final class com/rickbusarow/kase/KaseTestFactory$DefaultImpls { public static fun asTests (Lcom/rickbusarow/kase/KaseTestFactory;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; public static fun asTests (Lcom/rickbusarow/kase/KaseTestFactory;Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; public static fun newTestEnvironment (Lcom/rickbusarow/kase/KaseTestFactory;Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; + public static fun newTestEnvironment (Lcom/rickbusarow/kase/KaseTestFactory;Ljava/util/List;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; public static fun test (Lcom/rickbusarow/kase/KaseTestFactory;Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;Lkotlin/jvm/functions/Function2;)V public static synthetic fun test$default (Lcom/rickbusarow/kase/KaseTestFactory;Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V public static fun testFactory (Lcom/rickbusarow/kase/KaseTestFactory;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; @@ -2103,16 +2104,45 @@ public final class com/rickbusarow/kase/KaseTestFactory$DefaultImpls { public static fun testFactory (Lcom/rickbusarow/kase/KaseTestFactory;[Lcom/rickbusarow/kase/Kase;Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; } +public final class com/rickbusarow/kase/KaseTestFactoryNodeBuilder : com/rickbusarow/kase/KaseTestFactory, com/rickbusarow/kase/TestNodeBuilder { + public fun (Lcom/rickbusarow/kase/KaseTestFactory;Lcom/rickbusarow/kase/TestNodeBuilder;)V + public fun asContainers (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; + public fun asContainers (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/util/stream/Stream; + public fun asContainers (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; + public fun asContainers (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/util/stream/Stream; + public fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lcom/rickbusarow/kase/TestNodeBuilder; + public fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; + public fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; + public final fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; + public fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; + public fun asTests (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lcom/rickbusarow/kase/TestNodeBuilder; + public fun asTests (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; + public fun build ()Lorg/junit/jupiter/api/DynamicNode; + public fun container (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V + public fun getDisplayName ()Ljava/lang/String; + public fun getKases ()Ljava/util/List; + public fun getParent ()Lcom/rickbusarow/kase/TestNodeBuilder; + public fun getTestFunctionCoordinates ()Lcom/rickbusarow/kase/files/TestFunctionCoordinates; + public fun newTestEnvironment (Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; + public fun newTestEnvironment (Ljava/util/List;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; + public fun nodeSequence ()Lkotlin/sequences/Sequence; + public fun test (Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;Lkotlin/jvm/functions/Function2;)V + public fun test (Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V + public fun testFactory (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; + public fun testFactory (Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; + public fun testFactory ([Lcom/rickbusarow/kase/Kase;Lkotlin/jvm/functions/Function3;)Ljava/util/stream/Stream; +} + public abstract interface class com/rickbusarow/kase/TestEnvironment : com/rickbusarow/kase/files/HasWorkingDir { public static final field Companion Lcom/rickbusarow/kase/TestEnvironment$Companion; public abstract fun tearDown ()V } public final class com/rickbusarow/kase/TestEnvironment$Companion { + public final fun invoke (Ljava/lang/String;[Ljava/lang/String;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; public final fun invoke (Ljava/util/List;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; - public final fun invoke ([Ljava/lang/String;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; + public static synthetic fun invoke$default (Lcom/rickbusarow/kase/TestEnvironment$Companion;Ljava/lang/String;[Ljava/lang/String;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestEnvironment; public static synthetic fun invoke$default (Lcom/rickbusarow/kase/TestEnvironment$Companion;Ljava/util/List;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestEnvironment; - public static synthetic fun invoke$default (Lcom/rickbusarow/kase/TestEnvironment$Companion;[Ljava/lang/String;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestEnvironment; } public final class com/rickbusarow/kase/TestEnvironment$DefaultImpls { @@ -2122,11 +2152,14 @@ public final class com/rickbusarow/kase/TestEnvironment$DefaultImpls { public abstract interface class com/rickbusarow/kase/TestEnvironmentFactory { public abstract fun newTestEnvironment (Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; + public abstract fun newTestEnvironment (Ljava/util/List;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; } public final class com/rickbusarow/kase/TestEnvironmentFactory$DefaultImpls { public static fun newTestEnvironment (Lcom/rickbusarow/kase/TestEnvironmentFactory;Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; + public static fun newTestEnvironment (Lcom/rickbusarow/kase/TestEnvironmentFactory;Ljava/util/List;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;)Lcom/rickbusarow/kase/TestEnvironment; public static synthetic fun newTestEnvironment$default (Lcom/rickbusarow/kase/TestEnvironmentFactory;Lcom/rickbusarow/kase/Kase;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestEnvironment; + public static synthetic fun newTestEnvironment$default (Lcom/rickbusarow/kase/TestEnvironmentFactory;Ljava/util/List;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestEnvironment; } public final class com/rickbusarow/kase/TestEnvironmentFactoryKt { @@ -2134,28 +2167,26 @@ public final class com/rickbusarow/kase/TestEnvironmentFactoryKt { public static synthetic fun test$default (Lcom/rickbusarow/kase/TestEnvironmentFactory;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V } -public final class com/rickbusarow/kase/TestNodeBuilder { - public fun (Ljava/lang/String;Lcom/rickbusarow/kase/files/TestFunctionCoordinates;Lcom/rickbusarow/kase/TestNodeBuilder;)V - public final fun asContainers (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; - public final fun asContainers (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; +public abstract interface class com/rickbusarow/kase/TestNodeBuilder : com/rickbusarow/kase/HasDisplayName { + public abstract fun asContainers (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; + public abstract fun asContainers (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; + public abstract fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lcom/rickbusarow/kase/TestNodeBuilder; + public abstract fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; + public abstract fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; + public abstract fun asTests (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lcom/rickbusarow/kase/TestNodeBuilder; + public abstract fun build ()Lorg/junit/jupiter/api/DynamicNode; + public abstract fun container (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V + public abstract fun getParent ()Lcom/rickbusarow/kase/TestNodeBuilder; + public abstract fun getTestFunctionCoordinates ()Lcom/rickbusarow/kase/files/TestFunctionCoordinates; + public abstract fun nodeSequence ()Lkotlin/sequences/Sequence; + public abstract fun test (Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V +} + +public final class com/rickbusarow/kase/TestNodeBuilder$DefaultImpls { public static synthetic fun asContainers$default (Lcom/rickbusarow/kase/TestNodeBuilder;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestNodeBuilder; public static synthetic fun asContainers$default (Lcom/rickbusarow/kase/TestNodeBuilder;Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestNodeBuilder; - public final fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lcom/rickbusarow/kase/TestNodeBuilder; - public final fun asTests (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lcom/rickbusarow/kase/TestNodeBuilder; - public final fun asTests (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lcom/rickbusarow/kase/TestNodeBuilder; public static synthetic fun asTests$default (Lcom/rickbusarow/kase/TestNodeBuilder;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestNodeBuilder; - public static synthetic fun asTests$default (Lcom/rickbusarow/kase/TestNodeBuilder;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestNodeBuilder; public static synthetic fun asTests$default (Lcom/rickbusarow/kase/TestNodeBuilder;Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/rickbusarow/kase/TestNodeBuilder; - public final fun container (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V - public fun equals (Ljava/lang/Object;)Z - public final fun getName ()Ljava/lang/String; - public final fun getNodes ()Ljava/util/List; - public final fun getParent ()Lcom/rickbusarow/kase/TestNodeBuilder; - public final fun getTestFunctionCoordinates ()Lcom/rickbusarow/kase/files/TestFunctionCoordinates; - public fun hashCode ()I - public final fun nodeSequence ()Lkotlin/sequences/Sequence; - public final fun test (Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V - public fun toString ()Ljava/lang/String; } public final class com/rickbusarow/kase/TestNodeBuilderKt { diff --git a/kase/src/main/kotlin/com/rickbusarow/kase/KaseTestFactory.kt b/kase/src/main/kotlin/com/rickbusarow/kase/KaseTestFactory.kt index e381c98..8e7f578 100644 --- a/kase/src/main/kotlin/com/rickbusarow/kase/KaseTestFactory.kt +++ b/kase/src/main/kotlin/com/rickbusarow/kase/KaseTestFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Rick Busarow + * Copyright (C) 2024 Rick Busarow * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,10 +16,12 @@ package com.rickbusarow.kase import com.rickbusarow.kase.files.TestFunctionCoordinates +import com.rickbusarow.kase.internal.DefaultTestNodeBuilder import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.DynamicNode import org.junit.jupiter.api.DynamicTest import java.util.stream.Stream +import kotlin.streams.asStream /** * Common interface for creating dynamic tests with predefined @@ -137,9 +139,9 @@ public interface KaseTestFactory : * @return a stream of dynamic nodes representing the containers. * @since 0.1.0 */ - public fun Iterable.asContainers( - testName: (K) -> String = { it.displayName }, - testAction: TestNodeBuilder.(K) -> Unit + public fun Iterable.asContainers( + testName: (K2) -> String = { it.displayName }, + testAction: KaseTestFactoryNodeBuilder.(K2) -> Unit ): Stream = asSequence().asContainers(testName, testAction) /** @@ -152,10 +154,22 @@ public interface KaseTestFactory : * @return a stream of dynamic nodes representing the containers. * @since 0.1.0 */ - public fun Sequence.asContainers( - testName: (K) -> String = { it.displayName }, - testAction: TestNodeBuilder.(K) -> Unit - ): Stream = com.rickbusarow.kase.testFactory { - asContainers(testName, testAction) - } + public fun Sequence.asContainers( + testName: (K2) -> String = { it.displayName }, + testAction: KaseTestFactoryNodeBuilder.(K2) -> Unit + ): Stream = KaseTestFactoryNodeBuilder( + delegateFactory = this@KaseTestFactory, + delegateNodeBuilder = DefaultTestNodeBuilder( + displayName = "root", + testFunctionCoordinates = TestFunctionCoordinates.get(), + parent = null + ) + ) + .also { + for (k in this@asContainers) { + it.wrapContainer(testName(k)) { testAction(k) } + } + } + .nodeSequence() + .asStream() } diff --git a/kase/src/main/kotlin/com/rickbusarow/kase/KaseTestFactoryNodeBuilder.kt b/kase/src/main/kotlin/com/rickbusarow/kase/KaseTestFactoryNodeBuilder.kt new file mode 100644 index 0000000..d869230 --- /dev/null +++ b/kase/src/main/kotlin/com/rickbusarow/kase/KaseTestFactoryNodeBuilder.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 Rick Busarow + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.rickbusarow.kase + +/** + * Enables the creation of multiple layers of nodes without + * losing the scope of the original TestEnvironment factory. + */ +public class KaseTestFactoryNodeBuilder( + delegateFactory: KaseTestFactory, + delegateNodeBuilder: TestNodeBuilder +) : TestNodeBuilder by delegateNodeBuilder, + KaseTestFactory by delegateFactory { + + /** + * Enables the creation of multiple layers of nodes without + * losing the scope of the original TestEnvironment factory. + */ + public fun Iterable.asTests(testAction: T.(K) -> Unit): TestNodeBuilder { + return this@KaseTestFactoryNodeBuilder.also { builder -> + for (kase in this@asTests) { + test(kase.displayName) { + val parents = generateSequence(builder) { it.parent } + val names = parents.toList() + .dropLast(1) + .asReversed() + .map { it.displayName } + .plus(kase.displayName) + val environment = newTestEnvironment(names, testFunctionCoordinates) + environment.testAction(kase) + } + } + } + } + + internal fun wrapContainer( + name: String, + init: KaseTestFactoryNodeBuilder.() -> Unit + ) { + container(name) del@{ + val delegate: TestNodeBuilder = this@del + + val child = KaseTestFactoryNodeBuilder( + delegateFactory = this@KaseTestFactoryNodeBuilder, + delegateNodeBuilder = delegate + ) + child.init() + } + } +} diff --git a/kase/src/main/kotlin/com/rickbusarow/kase/TestEnvironment.kt b/kase/src/main/kotlin/com/rickbusarow/kase/TestEnvironment.kt index 3277923..d7af961 100644 --- a/kase/src/main/kotlin/com/rickbusarow/kase/TestEnvironment.kt +++ b/kase/src/main/kotlin/com/rickbusarow/kase/TestEnvironment.kt @@ -56,7 +56,8 @@ public interface TestEnvironment : HasWorkingDir { /** * Creates a new [TestEnvironment] instance. * - * @param testParameterDisplayNames The display names of the test parameters, if any. + * @param testParameterDisplayName The display name of the first test parameter + * @param additionalNames optional additional names * @param testFunctionCoordinates The [TestFunctionCoordinates] * from which the test is being run. * @return A new [TestEnvironment] instance. @@ -65,10 +66,11 @@ public interface TestEnvironment : HasWorkingDir { * @since 0.1.0 */ public operator fun invoke( - vararg testParameterDisplayNames: String, + testParameterDisplayName: String, + vararg additionalNames: String, testFunctionCoordinates: TestFunctionCoordinates = TestFunctionCoordinates.get() ): TestEnvironment = DefaultTestEnvironment( - testParameterDisplayNames = testParameterDisplayNames.toList(), + testParameterDisplayNames = listOf(testParameterDisplayName) + additionalNames, testFunctionCoordinates = testFunctionCoordinates ) } diff --git a/kase/src/main/kotlin/com/rickbusarow/kase/TestEnvironmentFactory.kt b/kase/src/main/kotlin/com/rickbusarow/kase/TestEnvironmentFactory.kt index b11e933..04aaa8d 100644 --- a/kase/src/main/kotlin/com/rickbusarow/kase/TestEnvironmentFactory.kt +++ b/kase/src/main/kotlin/com/rickbusarow/kase/TestEnvironmentFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Rick Busarow + * Copyright (C) 2024 Rick Busarow * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -41,6 +41,23 @@ public interface TestEnvironmentFactory { ) as? T ?: error("Override `newTestEnvironment` in order to create this TestEnvironment type.") } + + /** + * Creates a new [TestEnvironment]. + * + * @return A new [TestEnvironment] of type [T]. + */ + public fun newTestEnvironment( + testParameterDisplayNames: List, + testFunctionCoordinates: TestFunctionCoordinates = TestFunctionCoordinates.get() + ): T { + @Suppress("UNCHECKED_CAST") + return TestEnvironment( + testParameterDisplayNames, + testFunctionCoordinates = testFunctionCoordinates + ) as? T + ?: error("Override `newTestEnvironment` in order to create this TestEnvironment type.") + } } /** diff --git a/kase/src/main/kotlin/com/rickbusarow/kase/TestNodeBuilder.kt b/kase/src/main/kotlin/com/rickbusarow/kase/TestNodeBuilder.kt index 767d5f6..6e748d0 100644 --- a/kase/src/main/kotlin/com/rickbusarow/kase/TestNodeBuilder.kt +++ b/kase/src/main/kotlin/com/rickbusarow/kase/TestNodeBuilder.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Rick Busarow + * Copyright (C) 2024 Rick Busarow * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,10 +16,8 @@ package com.rickbusarow.kase import com.rickbusarow.kase.files.TestFunctionCoordinates -import dev.drewhamilton.poko.Poko -import org.junit.jupiter.api.DynamicContainer +import com.rickbusarow.kase.internal.DefaultTestNodeBuilder import org.junit.jupiter.api.DynamicNode -import org.junit.jupiter.api.DynamicTest import java.util.stream.Stream import kotlin.streams.asStream @@ -41,8 +39,8 @@ import kotlin.streams.asStream * @since 0.1.0 */ public fun testFactory(init: TestNodeBuilder.() -> Unit): Stream { - return TestNodeBuilder( - name = "root", + return DefaultTestNodeBuilder( + displayName = "root", testFunctionCoordinates = TestFunctionCoordinates.get(), parent = null ) @@ -67,28 +65,23 @@ public fun testFactory(init: TestNodeBuilder.() -> Unit): Stream DynamicNode> = mutableListOf() + /** + * Captured before executing any tests, meaning that it's the frame that called `asTests { ... }` + */ + public val testFunctionCoordinates: TestFunctionCoordinates + + /** the parent node, or `null` if this is the root container */ + public val parent: TestNodeBuilder? - @PublishedApi - internal fun nodeSequence(): Sequence = nodes.asSequence().map { it() } + /** Converts this builder to a [DynamicNode] */ + public fun build(): DynamicNode - private fun build(): DynamicNode { - return DynamicContainer.dynamicContainer(name, nodeSequence().asStream()) - } + /** */ + public fun nodeSequence(): Sequence /** * Creates a dynamic test with the provided name and test logic, adds it to the list of nodes. @@ -97,11 +90,7 @@ public class TestNodeBuilder @PublishedApi internal constructor( * @param testAction a function containing the test logic. * @since 0.1.0 */ - public fun test(name: String, testAction: () -> Unit) { - nodes.add { - DynamicTest.dynamicTest(name, testFunctionCoordinates.testUriOrNull) { testAction() } - } - } + public fun test(name: String, testAction: () -> Unit) /** * Creates a dynamic container with the provided name @@ -111,17 +100,7 @@ public class TestNodeBuilder @PublishedApi internal constructor( * @param init a lambda with receiver that initializes the [TestNodeBuilder]. * @since 0.1.0 */ - public fun container(name: String, init: TestNodeBuilder.() -> Unit) { - nodes.add { - TestNodeBuilder( - name = name, - testFunctionCoordinates = testFunctionCoordinates, - parent = this - ) - .apply(init) - .build() - } - } + public fun container(name: String, init: TestNodeBuilder.() -> Unit) /** * Adds tests to the invoking [TestNodeBuilder] for each kaseParam of the @@ -135,13 +114,9 @@ public class TestNodeBuilder @PublishedApi internal constructor( * @since 0.1.0 */ public fun Iterable.asTests( - testName: (E) -> String = { (it as? HasDisplayName)?.displayName ?: it.toString() }, + testName: (E) -> String = maybeDisplayNameOrToString(), testAction: (E) -> Unit - ): TestNodeBuilder = this@TestNodeBuilder.apply { - for (element in this@asTests) { - test(testName(element)) { testAction(element) } - } - } + ): TestNodeBuilder /** * Adds tests to the invoking [TestNodeBuilder] for each kaseParam of the @@ -156,17 +131,25 @@ public class TestNodeBuilder @PublishedApi internal constructor( * @since 0.1.0 */ public fun Iterable.asTests( - testName: (K) -> String = { (it as? HasDisplayName)?.displayName ?: it.toString() }, + testName: (K) -> String, testEnvironmentFactory: (kase: K) -> T, testAction: T.(K) -> Unit - ): TestNodeBuilder = this@TestNodeBuilder.apply { - for (kase in this@asTests) { - test(testName(kase)) { - val environment = testEnvironmentFactory.invoke(kase) - environment.testAction(kase) - } - } - } + ): TestNodeBuilder + + /** + * Adds tests to the invoking [TestNodeBuilder] for each kaseParam of the + * iterable. The tests themselves are defined by the [testAction] function. + * + * @param testEnvironmentFactory creates a new [TestEnvironment] for each test. + * @param testAction a function to define each test. + * @receiver the [TestNodeBuilder] to which tests will be added. + * @return the invoking [TestNodeBuilder], after adding the new tests. + * @since 0.1.0 + */ + public fun Iterable.asTests( + testEnvironmentFactory: (kase: K) -> T, + testAction: T.(K) -> Unit + ): TestNodeBuilder /** * Adds tests to the invoking [TestNodeBuilder] for each kaseParam of the @@ -180,13 +163,9 @@ public class TestNodeBuilder @PublishedApi internal constructor( * @since 0.1.0 */ public fun Sequence.asTests( - testName: (E) -> String = { (it as? HasDisplayName)?.displayName ?: it.toString() }, + testName: (E) -> String = maybeDisplayNameOrToString(), testAction: (E) -> Unit - ): TestNodeBuilder = this@TestNodeBuilder.apply { - for (element in this@asTests) { - test(testName(element)) { testAction(element) } - } - } + ): TestNodeBuilder /** * Adds containers to the invoking [TestNodeBuilder] for each kaseParam of the @@ -200,13 +179,9 @@ public class TestNodeBuilder @PublishedApi internal constructor( * @since 0.1.0 */ public fun Iterable.asContainers( - testName: (E) -> String = { (it as? HasDisplayName)?.displayName ?: it.toString() }, + testName: (E) -> String = maybeDisplayNameOrToString(), testAction: TestNodeBuilder.(E) -> Unit - ): TestNodeBuilder = this@TestNodeBuilder.apply { - for (element in this@asContainers) { - container(testName(element)) { testAction(element) } - } - } + ): TestNodeBuilder /** * Adds containers to the invoking [TestNodeBuilder] for each kaseParam of the @@ -221,13 +196,9 @@ public class TestNodeBuilder @PublishedApi internal constructor( * @since 0.1.0 */ public fun Sequence.asContainers( - testName: (E) -> String = { (it as? HasDisplayName)?.displayName ?: it.toString() }, + testName: (E) -> String = maybeDisplayNameOrToString(), testAction: TestNodeBuilder.(E) -> Unit - ): TestNodeBuilder = this@TestNodeBuilder.apply { - for (element in this@asContainers) { - container(testName(element)) { testAction(element) } - } - } + ): TestNodeBuilder } /** @@ -244,3 +215,6 @@ public fun Iterable.asContainers( testName: (K) -> String = { it.displayName }, testAction: TestNodeBuilder.(K) -> Unit ): Stream = testFactory { asContainers(testName, testAction) } + +internal fun maybeDisplayNameOrToString(): (E) -> String = + { (it as? HasDisplayName)?.displayName ?: it.toString() } diff --git a/kase/src/main/kotlin/com/rickbusarow/kase/internal/DefaultTestNodeBuilder.kt b/kase/src/main/kotlin/com/rickbusarow/kase/internal/DefaultTestNodeBuilder.kt new file mode 100644 index 0000000..22daeef --- /dev/null +++ b/kase/src/main/kotlin/com/rickbusarow/kase/internal/DefaultTestNodeBuilder.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 Rick Busarow + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.rickbusarow.kase.internal + +import com.rickbusarow.kase.Kase +import com.rickbusarow.kase.TestEnvironment +import com.rickbusarow.kase.TestNodeBuilder +import com.rickbusarow.kase.files.TestFunctionCoordinates +import dev.drewhamilton.poko.Poko +import org.junit.jupiter.api.DynamicContainer +import org.junit.jupiter.api.DynamicNode +import org.junit.jupiter.api.DynamicTest +import kotlin.streams.asStream + +@Poko +internal class DefaultTestNodeBuilder @PublishedApi internal constructor( + override val displayName: String, + override val testFunctionCoordinates: TestFunctionCoordinates, + override val parent: TestNodeBuilder? +) : TestNodeBuilder { + + @PublishedApi + internal val nodes: MutableList<() -> DynamicNode> = mutableListOf() + + override fun nodeSequence(): Sequence = nodes.asSequence().map { it() } + + override fun build(): DynamicNode { + return DynamicContainer.dynamicContainer(displayName, nodeSequence().asStream()) + } + + override fun test(name: String, testAction: () -> Unit) { + nodes.add { + DynamicTest.dynamicTest(name, testFunctionCoordinates.testUriOrNull) { testAction() } + } + } + + override fun container(name: String, init: TestNodeBuilder.() -> Unit) { + nodes.add { + DefaultTestNodeBuilder( + displayName = name, + testFunctionCoordinates = testFunctionCoordinates, + parent = this + ) + .apply(init) + .build() + } + } + + override fun Iterable.asTests( + testName: (E) -> String, + testAction: (E) -> Unit + ): TestNodeBuilder = this@DefaultTestNodeBuilder.apply { + for (element in this@asTests) { + test(testName(element)) { testAction(element) } + } + } + + override fun Iterable.asTests( + testName: (K) -> String, + testEnvironmentFactory: (kase: K) -> T, + testAction: T.(K) -> Unit + ): TestNodeBuilder = this@DefaultTestNodeBuilder.apply { + for (kase in this@asTests) { + test(testName(kase)) { + val environment = testEnvironmentFactory.invoke(kase) + environment.testAction(kase) + } + } + } + + override fun Iterable.asTests( + testEnvironmentFactory: (kase: K) -> T, + testAction: T.(K) -> Unit + ): TestNodeBuilder = this@DefaultTestNodeBuilder.apply { + for (kase in this@asTests) { + test(kase.displayName) { + val environment = testEnvironmentFactory.invoke(kase) + environment.testAction(kase) + } + } + } + + override fun Sequence.asTests( + testName: (E) -> String, + testAction: (E) -> Unit + ): TestNodeBuilder = this@DefaultTestNodeBuilder.apply { + for (element in this@asTests) { + test(testName(element)) { testAction(element) } + } + } + + override fun Iterable.asContainers( + testName: (E) -> String, + testAction: TestNodeBuilder.(E) -> Unit + ): TestNodeBuilder = this@DefaultTestNodeBuilder.apply { + for (element in this@asContainers) { + container(testName(element)) { testAction(element) } + } + } + + override fun Sequence.asContainers( + testName: (E) -> String, + testAction: TestNodeBuilder.(E) -> Unit + ): TestNodeBuilder = this@DefaultTestNodeBuilder.apply { + for (element in this@asContainers) { + container(testName(element)) { testAction(element) } + } + } +} diff --git a/kase/src/test/kotlin/com/rickbusarow/kase/KaseTestFactoryTest.kt b/kase/src/test/kotlin/com/rickbusarow/kase/KaseTestFactoryTest.kt index d1af386..f7f9ceb 100644 --- a/kase/src/test/kotlin/com/rickbusarow/kase/KaseTestFactoryTest.kt +++ b/kase/src/test/kotlin/com/rickbusarow/kase/KaseTestFactoryTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Rick Busarow + * Copyright (C) 2024 Rick Busarow * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -20,8 +20,6 @@ import com.rickbusarow.kase.ParamTypes.B import com.rickbusarow.kase.ParamTypes.C import com.rickbusarow.kase.files.HasWorkingDir.Companion.baseWorkingDir import com.rickbusarow.kase.files.HasWorkingDir.Companion.cleanStringForFileSystem -import com.rickbusarow.kase.files.HasWorkingDir.Companion.div -import com.rickbusarow.kase.stdlib.div import io.kotest.matchers.shouldBe import org.junit.jupiter.api.DynamicNode import org.junit.jupiter.api.TestFactory @@ -68,6 +66,46 @@ class KaseTestFactoryTest : KaseTestFactory> { } } } + + @TestFactory + fun `scoping nested asTests`(): Stream { + val base = baseWorkingDir() + + val functionDir = File("KaseTestFactoryTest") + .resolve(cleanStringForFileSystem("scoping nested asTests")) + + return cs.asContainers { k1 -> + + kases.asTests { k2 -> + + val path = functionDir + .resolve(cleanStringForFileSystem(k1.displayName)) + .resolve(cleanStringForFileSystem(k2.displayName)) + + workingDir.relativeTo(base) shouldBe path + } + } + } + + @TestFactory + fun `scoping nested testFactory`(): Stream { + val base = baseWorkingDir() + + val functionDir = File("KaseTestFactoryTest") + .resolve(cleanStringForFileSystem("scoping nested asTests")) + + return cs.asContainers { k1 -> + + testFactory { k2 -> + + val path = functionDir + .resolve(cleanStringForFileSystem(k1.displayName)) + .resolve(cleanStringForFileSystem(k2.displayName)) + + workingDir.relativeTo(base) shouldBe path + } + } + } } sealed class ParamTypes {