diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt index 866eb8d4..b1f3dcd9 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt @@ -15,9 +15,13 @@ abstract class GenericTestRunner: ITestRunnerContributor { abstract val dependencyName : String abstract val mainClass: String abstract val annotationPackage: String + abstract val runnerName: String + abstract fun args(project: Project, context: KobaltContext, classpath: List, testConfig: TestConfig) : List + open val extraClasspath: List = emptyList() + open fun filterTestClasses(classes: List) : List = classes override fun run(project: Project, context: KobaltContext, configName: String, @@ -98,7 +102,7 @@ abstract class GenericTestRunner: ITestRunnerContributor { configName: String) : Boolean { var result = false - context.logger.log(project.name, 1, "Running default TestNG runner") + context.logger.log(project.name, 1, "Running tests with " + runnerName) val testConfig = project.testConfigs.firstOrNull { it.name == configName } @@ -144,13 +148,14 @@ abstract class GenericTestRunner: ITestRunnerContributor { */ @VisibleForTesting fun calculateAllJvmArgs(project: Project, context: KobaltContext, - testConfig: TestConfig, classpath: List, pluginInfo: IPluginInfo) : List { + testConfig: TestConfig, classpath: List, pluginInfo: IPluginInfo) : List { + val fullClasspath = classpath.map { it.jarFile.get().absolutePath } + extraClasspath // Default JVM args val jvmFlags = arrayListOf().apply { addAll(testConfig.jvmArgs) add("-ea") add("-classpath") - add(classpath.map { it.jarFile.get().absolutePath }.joinToString(File.pathSeparator)) + add(fullClasspath.joinToString(File.pathSeparator)) } // JVM flags from the contributors diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnit5Runner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnit5Runner.kt new file mode 100644 index 00000000..7dd72df7 --- /dev/null +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnit5Runner.kt @@ -0,0 +1,151 @@ +package com.beust.kobalt.internal + +import com.beust.jcommander.JCommander +import com.beust.jcommander.Parameter +import com.beust.kobalt.TestConfig +import com.beust.kobalt.api.IAffinity +import com.beust.kobalt.api.IClasspathDependency +import com.beust.kobalt.api.KobaltContext +import com.beust.kobalt.api.Project +import com.beust.kobalt.misc.KFiles +import com.beust.kobalt.misc.KobaltLogger +import com.google.inject.Inject +import org.junit.platform.engine.TestExecutionResult +import org.junit.platform.engine.discovery.DiscoverySelectors +import org.junit.platform.engine.reporting.ReportEntry +import org.junit.platform.engine.support.descriptor.MethodSource +import org.junit.platform.launcher.LauncherDiscoveryRequest +import org.junit.platform.launcher.TestExecutionListener +import org.junit.platform.launcher.TestIdentifier +import org.junit.platform.launcher.TestPlan +import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder +import org.junit.platform.launcher.core.LauncherFactory +import java.io.File +import java.nio.file.Paths + +/** + * Runner for JUnit 5 tests. This class also contains a main() entry point since JUnit 5 no longer supplies one. + */ +class JUnit5Runner @Inject constructor(kFiles: KFiles) : GenericTestRunner() { + + override val dependencyName = "jupiter" + override val annotationPackage = "org.junit.jupiter.api" + override val mainClass = "com.beust.kobalt.internal.JUnit5RunnerKt" + override val runnerName = "JUnit 5" + + override fun affinity(project: Project, context: KobaltContext) : Int { + val result = + if (project.testDependencies.any { it.id.contains("jupiter") }) IAffinity.DEFAULT_POSITIVE_AFFINITY + 100 + else 0 + return result + + } + + override fun args(project: Project, context: KobaltContext, classpath: List, testConfig: TestConfig): List { + val testClassDir = KFiles.joinDir(project.buildDirectory, KFiles.TEST_CLASSES_DIR) + val classDir = KFiles.joinDir(project.buildDirectory, KFiles.CLASSES_DIR) + val args = listOf("--testClassDir", testClassDir, + "--classDir", classDir, + "--log", KobaltLogger.LOG_LEVEL.toString()) + return args + } + + override val extraClasspath = kFiles.kobaltJar +} + +private class Args { + @Parameter(names = arrayOf("--log")) + var log: Int = 1 + + @Parameter(names = arrayOf("--testClassDir")) + var testClassDir: String = "kobaltBuild/test-classes" + + @Parameter(names = arrayOf("--classDir")) + var classDir: String = "kobaltBuild/classes" +} + +fun main(argv: Array) { + val args = Args() + val jc = JCommander(args) + jc.parse(*argv) + + val testClassDir = File(args.testClassDir).absolutePath + val classDir = File(args.classDir).absolutePath + val request : LauncherDiscoveryRequest = LauncherDiscoveryRequestBuilder() + .selectors(DiscoverySelectors.selectClasspathRoots(setOf( + Paths.get(testClassDir), + Paths.get(classDir) + ))) + .selectors(DiscoverySelectors.selectDirectory(testClassDir)) + .build() + + fun testName(id: TestIdentifier) : String? { + val result = + if (id.source.isPresent) { + val source = id.source.get() + if (source is MethodSource) { + source.className + "." + source.methodName + } else { + null + } + } else { + null + } + return result + } + + var passed = 0 + var failed = 0 + var skipped = 0 + var aborted = 0 + + fun log(level: Int, s: String) { + if (level <= args.log) println(s) + } + + val listener = object: TestExecutionListener { + override fun executionFinished(testIdentifier: TestIdentifier, testExecutionResult: TestExecutionResult) { + val testName = testName(testIdentifier) + if (testName != null) { + when(testExecutionResult.status) { + TestExecutionResult.Status.FAILED -> { + log(1, "FAILED: $testName, reason: " + testExecutionResult.throwable.get().toString()) + failed++ + } + TestExecutionResult.Status.ABORTED -> { + log(1, "ABORTED: $testName, reason: " + testExecutionResult.throwable.get().toString()) + aborted++ + } + TestExecutionResult.Status.SUCCESSFUL -> { + log(2, "PASSED: $testName") + passed++ + } else -> { + + } + } + } + } + + override fun executionSkipped(testIdentifier: TestIdentifier, reason: String) { + testName(testIdentifier)?.let { + log(1, "Skipping $it because $reason") + skipped++ + } + } + + override fun executionStarted(testIdentifier: TestIdentifier) { + testName(testIdentifier)?.let { + log(2, "Starting $it") + } + } + + override fun testPlanExecutionStarted(testPlan: TestPlan?) {} + override fun dynamicTestRegistered(testIdentifier: TestIdentifier?) {} + override fun reportingEntryPublished(testIdentifier: TestIdentifier?, entry: ReportEntry?) {} + override fun testPlanExecutionFinished(testPlan: TestPlan?) {} + } + + LauncherFactory.create().execute(request, listener) + + log(1, "TEST RESULTS: $passed PASSED, $failed FAILED, $skipped SKIPPED, $aborted ABORTED") +} \ No newline at end of file diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt index eec22afd..bfe6d800 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt @@ -8,10 +8,9 @@ import com.beust.kobalt.api.Project open class JUnitRunner() : GenericTestRunner() { override val mainClass = "org.junit.runner.JUnitCore" - override val annotationPackage = "org.junit" - override val dependencyName = "junit" + override val runnerName = "JUnit 4" override fun args(project: Project, context: KobaltContext, classpath: List, testConfig: TestConfig) = findTestClasses(project, context, testConfig) diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/KotlinTestRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/KotlinTestRunner.kt index 90c5d354..b78da1db 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/KotlinTestRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/KotlinTestRunner.kt @@ -6,6 +6,7 @@ package com.beust.kobalt.internal */ class KotlinTestRunner : JUnitRunner() { override val dependencyName = "io.kotlintest" + override val runnerName = "Kotlin Test" /** * KotlinTestRunner runs tests in the init{} initializer, so ignore all the extra diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/SpekRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/SpekRunner.kt index 2129a315..0c1ddad6 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/SpekRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/SpekRunner.kt @@ -6,5 +6,6 @@ package com.beust.kobalt.internal */ class SpekRunner : JUnitRunner() { override val dependencyName = "org.jetbrains.spek" + override val runnerName = "Spek" } diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt index c0d2b122..735b26e1 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt @@ -2,21 +2,25 @@ package com.beust.kobalt.internal import com.beust.kobalt.AsciiArt import com.beust.kobalt.TestConfig -import com.beust.kobalt.api.* +import com.beust.kobalt.api.IClasspathDependency +import com.beust.kobalt.api.KobaltContext +import com.beust.kobalt.api.Project import com.beust.kobalt.maven.aether.AetherDependency import com.beust.kobalt.misc.* import org.testng.remote.RemoteArgs -import org.testng.remote.strprotocol.* +import org.testng.remote.strprotocol.JsonMessageSender +import org.testng.remote.strprotocol.MessageHelper +import org.testng.remote.strprotocol.MessageHub +import org.testng.remote.strprotocol.TestResultMessage import java.io.File import java.io.IOException class TestNgRunner : GenericTestRunner() { override val mainClass = "org.testng.TestNG" - override val dependencyName = "testng" - override val annotationPackage = "org.testng" + override val runnerName = "TestNG" fun defaultOutput(project: Project) = KFiles.joinDir(project.buildDirectory, "test-output") diff --git a/src/main/resources/META-INF/kobalt-core-plugin.xml b/src/main/resources/META-INF/kobalt-core-plugin.xml index 4a2b359b..fbcca6c6 100644 --- a/src/main/resources/META-INF/kobalt-core-plugin.xml +++ b/src/main/resources/META-INF/kobalt-core-plugin.xml @@ -24,6 +24,7 @@ com.beust.kobalt.internal.TestNgRunner com.beust.kobalt.internal.SpekRunner com.beust.kobalt.internal.KotlinTestRunner + com.beust.kobalt.internal.JUnit5Runner com.beust.kobalt.app.KobaltPluginTemplate