Gradle, js: mocha

#KT-31010 Fixed
This commit is contained in:
Sergey Rostov
2019-04-11 07:58:14 +03:00
parent 4c2c8a1788
commit 92910ec3e9
17 changed files with 150 additions and 174 deletions
@@ -44,8 +44,8 @@ interface KotlinDependencyHandler {
fun project(notation: Map<String, Any?>): ProjectDependency
fun npm(packageName: String, version: String = "*"): Dependency?
fun npm(org: String, packageName: String, version: String = "*"): Dependency?
fun npm(packageName: String, version: String = "*"): Dependency
fun npm(org: String, packageName: String, version: String = "*"): Dependency
}
interface HasKotlinDependencies {
@@ -24,7 +24,8 @@ data class TCServiceMessagesClientSettings(
val testNameSuffix: String? = null,
val prepandSuiteName: Boolean = false,
val treatFailedTestOutputAsStacktrace: Boolean = false,
val stackTraceParser: (String) -> ParsedStackTrace? = { null }
val stackTraceParser: (String) -> ParsedStackTrace? = { null },
val ignoreOutOfRootNodes: Boolean = false
)
internal class TCServiceMessagesClient(
@@ -190,8 +191,13 @@ internal class TCServiceMessagesClient(
private fun close(ts: Long, assertLocalId: String?) = pop().also {
if (assertLocalId != null) {
if (it.localId != assertLocalId && settings.ignoreOutOfRootNodes && it.parent == null) {
push(it)
return it
}
check(it.localId == assertLocalId) {
"Bad TCSM: unexpected node to close: ${it.localId}, stack: ${
"Bad TCSM: unexpected node to close `$assertLocalId`, expected `${it.localId}`, stack: ${
leaf.collectParents().joinToString("") { item -> "\n - ${item.localId}" }
}\n"
}
@@ -1,11 +1,10 @@
package org.jetbrains.kotlin.gradle.targets.js
import org.gradle.api.file.FileCollection
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestTask
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
@@ -35,58 +34,28 @@ internal class KotlinJsCompilationTestsConfigurator(
return components.first() + components.drop(1).joinToString("") { it.capitalize() }
}
@Suppress("SameParameterValue")
private fun disambiguateUnderscored(name: String, includeCompilation: Boolean) =
disambiguate(name, includeCompilation).joinToString("_")
private val testTaskName: String
get() = disambiguateCamelCased("test", false)
private val Kotlin2JsCompile.jsRuntimeClasspath: FileCollection
get() = classpath.plus(project.files(destinationDir))
fun configure() {
compilation.dependencies {
implementation(kotlin("test-nodejs-runner"))
}
// apply plugin (cannot do it lazy)
val nodeJs = NodeJsPlugin[target.project]
val testTask = registerTask(project, testTaskName, KotlinNodeJsTestTask::class.java) { testJs ->
registerTask(project, testTaskName, KotlinNodeJsTestTask::class.java) { testJs ->
testJs.group = LifecycleBasePlugin.VERIFICATION_GROUP
testJs.dependsOn(compileTestKotlin2Js)
testJs.dependsOn(compileTestKotlin2Js, nodeJs.nodeJsSetupTask)
testJs.onlyIf {
compileTestKotlin2Js.outputFile.exists()
}
if (disambiguationClassifier != null) {
testJs.targetName = disambiguationClassifier
testJs.showTestTargetName = true
}
testJs.nodeJsProcessOptions.workingDir = project.projectDir
testJs.testRuntimeNodeModules = listOf(
"kotlin-test-nodejs-runner.js",
"kotlin-nodejs-source-map-support.js"
)
testJs.nodeModulesToLoad = setOf(compileTestKotlin2Js.outputFile.name)
testJs.runtimeDependencyHandler = compilation
testJs.targetName = disambiguationClassifier
testJs.nodeModulesToLoad.add(compileTestKotlin2Js.outputFile.name)
testJs.configureConventions()
registerTestTask(testJs)
}
project.afterEvaluate {
// defer nodeJs executable setup, as nodejs project settings may change during configuration
testTask.configure {
val nodeJsSetupTask = nodeJs.nodeJsSetupTask
it.dependsOn(nodeJsSetupTask)
if (it.nodeJsProcessOptions.executable == null) {
it.nodeJsProcessOptions.executable = nodeJs.buildEnv().nodeExec
}
}
}
}
}
@@ -6,7 +6,7 @@ internal data class NodeJsEnv(
val nodeDir: File,
val nodeBinDir: File,
val nodeExecutable: String,
val npmExecutable: String,
val platformName: String,
val architectureName: String,
@@ -49,8 +49,8 @@ open class NodeJsRootExtension(project: Project) : NodeJsExtension(project) {
return NodeJsEnv(
nodeDir = nodeDir,
nodeBinDir = nodeBinDir,
nodeExec = getExecutable("node", nodeCommand, "exe"),
npmExec = getExecutable("npm", npmCommand, "cmd"),
nodeExecutable = getExecutable("node", nodeCommand, "exe"),
npmExecutable = getExecutable("npm", npmCommand, "cmd"),
platformName = platform,
architectureName = architecture,
ivyDependency = getIvyDependency()
@@ -66,7 +66,7 @@ open class NodeJsSetupTask : DefaultTask() {
unpackNodeArchive(result, destination.parentFile) // parent because archive contains name already
if (!env.isWindows) {
File(env.nodeExec).setExecutable(true)
File(env.nodeExecutable).setExecutable(true)
}
}
@@ -177,18 +177,23 @@ internal class GradleNodeModulesSync(val project: Project) {
val kotlin2JsCompile = compilation.compileKotlinTask as Kotlin2JsCompile
// classpath
kotlin2JsCompile.classpath.forEach { srcFile ->
when {
srcFile.name == PACKAGE_JSON -> visitPackageJson(srcFile, transitiveDependencies)
isKotlinJsRuntimeFile(srcFile) -> getOrCompute(srcFile) {
listOf(srcFile)
}
srcFile.isZip -> getOrCompute(srcFile) {
mutableListOf<File>().also { files ->
this.project.zipTree(srcFile).forEach { innerFile ->
when {
innerFile.name == PACKAGE_JSON -> visitPackageJson(innerFile, transitiveDependencies)
isKotlinJsRuntimeFile(innerFile) -> files.add(innerFile)
compilation.relatedConfigurationNames.forEach {
val configuration = project.configurations.getByName(it)
if (configuration.isCanBeResolved) {
configuration.resolve().forEach { srcFile ->
when {
srcFile.name == PACKAGE_JSON -> visitPackageJson(srcFile, transitiveDependencies)
isKotlinJsRuntimeFile(srcFile) -> getOrCompute(srcFile) {
listOf(srcFile)
}
srcFile.isZip -> getOrCompute(srcFile) {
mutableListOf<File>().also { files ->
this.project.zipTree(srcFile).forEach { innerFile ->
when {
innerFile.name == PACKAGE_JSON -> visitPackageJson(innerFile, transitiveDependencies)
isKotlinJsRuntimeFile(innerFile) -> files.add(innerFile)
}
}
}
}
}
@@ -6,50 +6,42 @@
package org.jetbrains.kotlin.gradle.targets.js.tasks
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.process.ProcessForkOptions
import org.gradle.process.internal.DefaultProcessForkOptions
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTrace
import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolver
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTestFramework
import org.jetbrains.kotlin.gradle.targets.js.testing.nodejs.KotlinNodeJsTestRunner
import org.jetbrains.kotlin.gradle.targets.js.testing.mocha.KotlinMocha
import org.jetbrains.kotlin.gradle.tasks.KotlinTestTask
import org.jetbrains.kotlin.gradle.testing.IgnoredTestSuites
import java.io.File
open class KotlinNodeJsTestTask : KotlinTestTask() {
@Input
var ignoredTestSuites: IgnoredTestSuites =
IgnoredTestSuites.showWithContents
@Input
@SkipWhenEmpty
var nodeModulesToLoad: Set<String> = setOf()
@Input
lateinit var testRuntimeNodeModules: Collection<String>
@Suppress("LeakingThis")
@Internal
val nodeJsProcessOptions: ProcessForkOptions = DefaultProcessForkOptions(fileResolver)
@Suppress("unused")
val nodeJsExecutable: String
@Input get() = nodeJsProcessOptions.executable
@Suppress("unused")
val nodeJsWorkingDirCanonicalPath: String
@Input get() = nodeJsProcessOptions.workingDir.canonicalPath
private var testFramework: KotlinJsTestFramework = KotlinNodeJsTestRunner()
@Input
var debug: Boolean = false
fun nodeJs(options: ProcessForkOptions.() -> Unit) {
options(nodeJsProcessOptions)
@Internal
var runtimeDependencyHandler: HasKotlinDependencies? = null
@Input
var nodeModulesToLoad: MutableList<String> = mutableListOf()
fun useNodeJs(body: KotlinNodeJsTestRunner.() -> Unit) = use(KotlinNodeJsTestRunner(), body)
fun useMocha(body: KotlinMocha.() -> Unit) = use(KotlinMocha(), body)
private inline fun <T : KotlinJsTestFramework> use(runner: T, body: T.() -> Unit) {
testFramework = runner.also(body)
val dependenciesHolder = runtimeDependencyHandler
if (dependenciesHolder != null) {
testFramework.configure(dependenciesHolder)
}
}
override fun executeTests() {
@@ -58,13 +50,12 @@ open class KotlinNodeJsTestTask : KotlinTestTask() {
}
override fun createTestExecutionSpec(): TCServiceMessagesTestExecutionSpec {
val extendedForkOptions = DefaultProcessForkOptions(fileResolver)
nodeJsProcessOptions.copyTo(extendedForkOptions)
val forkOptions = DefaultProcessForkOptions(fileResolver)
NpmResolver.resolve(project)
val npmProjectLayout = NpmProjectLayout[project]
extendedForkOptions.workingDir = npmProjectLayout.nodeWorkDir
forkOptions.workingDir = NpmProjectLayout[project].nodeWorkDir
forkOptions.executable = NodeJsPlugin[project].buildEnv().nodeExecutable
val nodeJsArgs = mutableListOf<String>()
@@ -72,61 +63,6 @@ open class KotlinNodeJsTestTask : KotlinTestTask() {
nodeJsArgs.add("--inspect-brk")
}
val cliArgs = KotlinNodeJsTestRunnerCliArgs(
nodeModulesToLoad.toList(),
filterExt.includePatterns + filterExt.commandLineIncludePatterns,
excludes,
ignoredTestSuites.cli
)
val clientSettings = TCServiceMessagesClientSettings(
name,
testNameSuffix = if (showTestTargetName) targetName else null,
prepandSuiteName = true,
stackTraceParser = ::parseNodeJsStackTraceAsJvm
)
return TCServiceMessagesTestExecutionSpec(
extendedForkOptions,
nodeJsArgs +
testRuntimeNodeModules
.map { npmProjectLayout.nodeModulesDir.resolve(it) }
.filter { it.exists() }
.map { it.absolutePath } +
cliArgs.toList(),
true,
clientSettings
)
}
}
data class KotlinNodeJsTestRunnerCliArgs(
val moduleNames: List<String>,
val include: Collection<String> = listOf(),
val exclude: Collection<String> = listOf(),
val ignoredTestSuites: IgnoredTestSuitesReporting = IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored
) {
fun toList(): List<String> = mutableListOf<String>().also { args ->
if (include.isNotEmpty()) {
args.add("--include")
args.add(include.joinToString(","))
}
if (exclude.isNotEmpty()) {
args.add("--exclude")
args.add(exclude.joinToString(","))
}
if (ignoredTestSuites !== IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored) {
args.add("--ignoredTestSuites")
args.add(ignoredTestSuites.name)
}
args.addAll(moduleNames)
}
@Suppress("EnumEntryName")
enum class IgnoredTestSuitesReporting {
skip, reportAsIgnoredTest, reportAllInnerTestsAsIgnored
return testFramework.createTestExecutionSpec(this, forkOptions, nodeJsArgs)
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.gradle.targets.js.testing
import org.gradle.process.ProcessForkOptions
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestTask
interface KotlinJsTestFramework {
fun createTestExecutionSpec(
task: KotlinNodeJsTestTask,
forkOptions: ProcessForkOptions,
nodeJsArgs: MutableList<String>
): TCServiceMessagesTestExecutionSpec
fun configure(dependenciesHolder: HasKotlinDependencies) {
}
}
@@ -10,8 +10,8 @@ import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSetti
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestTask
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTestFramework
class KotlinMocha : KotlinJsTestFramework {
@@ -24,14 +24,14 @@ class KotlinMocha : KotlinJsTestFramework {
}
override fun createTestExecutionSpec(
task: KotlinJsTest,
task: KotlinNodeJsTestTask,
forkOptions: ProcessForkOptions,
nodeJsArgs: MutableList<String>
): TCServiceMessagesTestExecutionSpec {
val clientSettings = TCServiceMessagesClientSettings(
task.name,
testNameSuffix = task.targetName,
prependSuiteName = true,
prepandSuiteName = true,
stackTraceParser = ::parseNodeJsStackTraceAsJvm,
ignoreOutOfRootNodes = true
)
@@ -41,12 +41,12 @@ class KotlinMocha : KotlinJsTestFramework {
task.nodeModulesToLoad.single()
)
val npmProjectLayout = NpmProject[task.project]
val npmProjectLayout = NpmProjectLayout[task.project]
val args = nodeJsArgs +
nodeModules.map {
npmProjectLayout.nodeModulesDir.resolve(it).also { file ->
check(file.isFile) { "Cannot find ${file.canonicalPath}" }
check(file.isFile) { "Cannot find ${file.canonicalPath}"}
}.canonicalPath
} +
listOf(
@@ -11,9 +11,9 @@ import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSetti
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.plugin.HasKotlinDependencies
import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestTask
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTestFramework
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.testing.IgnoredTestSuites
class KotlinNodeJsTestRunner : KotlinJsTestFramework {
@@ -27,7 +27,7 @@ class KotlinNodeJsTestRunner : KotlinJsTestFramework {
}
override fun createTestExecutionSpec(
task: KotlinJsTest,
task: KotlinNodeJsTestTask,
forkOptions: ProcessForkOptions,
nodeJsArgs: MutableList<String>
): TCServiceMessagesTestExecutionSpec {
@@ -41,20 +41,24 @@ class KotlinNodeJsTestRunner : KotlinJsTestFramework {
val clientSettings = TCServiceMessagesClientSettings(
task.name,
testNameSuffix = task.targetName,
prependSuiteName = true,
prepandSuiteName = true,
stackTraceParser = ::parseNodeJsStackTraceAsJvm
)
val testRuntimeNodeModules = listOf(
"kotlin-test-nodejs-runner/kotlin-test-nodejs-runner.js",
"kotlin-test-nodejs-runner/kotlin-nodejs-source-map-support.js"
"kotlin-test-nodejs-runner.js",
"kotlin-nodejs-source-map-support.js"
)
val npmProjectLayout = NpmProject[task.project]
val npmProjectLayout = NpmProjectLayout[task.project]
val args = nodeJsArgs +
testRuntimeNodeModules.map {
npmProjectLayout.getModuleEntryPath(it)
npmProjectLayout.nodeModulesDir.resolve(it).also { file ->
check(file.isFile) {
"Cannot find ${file.canonicalPath}"
}
}.canonicalPath
} +
cliArgs.toList()
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.gradle.targets.js.testing.nodejs
data class KotlinNodeJsTestRunnerCliArgs(
val moduleNames: List<String>,
val include: Collection<String> = listOf(),
val exclude: Collection<String> = listOf(),
val ignoredTestSuites: IgnoredTestSuitesReporting = IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored
) {
fun toList(): List<String> = mutableListOf<String>().also { args ->
if (include.isNotEmpty()) {
args.add("--include")
args.add(include.joinToString(","))
}
if (exclude.isNotEmpty()) {
args.add("--exclude")
args.add(exclude.joinToString(","))
}
if (ignoredTestSuites !== IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored) {
args.add("--ignoredTestSuites")
args.add(ignoredTestSuites.name)
}
args.addAll(moduleNames)
}
@Suppress("EnumEntryName")
enum class IgnoredTestSuitesReporting {
skip, reportAsIgnoredTest, reportAllInnerTestsAsIgnored
}
}
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.internal.execWithProgress
import org.jetbrains.kotlin.gradle.internal.operation
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
@@ -32,7 +31,7 @@ object Yarn : NpmApi {
val yarnEnv = YarnPlugin[project].buildEnv()
project.execWithProgress(description) { exec ->
exec.executable = nodeJsEnv.nodeExec
exec.executable = nodeJsEnv.nodeExecutable
exec.args = listOf(yarnEnv.home.resolve("bin/yarn.js").absolutePath) + args
exec.workingDir = npmProjectLayout.nodeWorkDir
}
@@ -135,7 +135,6 @@ open class KotlinNativeTargetConfigurator(
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = "Executes Kotlin/Native unit tests for target ${binary.target.name}."
targetName = binary.compilation.target.targetName
showTestTargetName = true
enabled = binary.target.konanTarget.isCurrentHost
@@ -48,8 +48,8 @@ open class KotlinNativeTestTask : KotlinTestTask() {
val clientSettings = TCServiceMessagesClientSettings(
name,
testNameSuffix = if (showTestTargetName) targetName else null,
prepandSuiteName = showTestTargetName,
testNameSuffix = targetName,
prepandSuiteName = targetName != null,
treatFailedTestOutputAsStacktrace = true,
stackTraceParser = ::parseKotlinNativeStackTraceAsJvm
)
@@ -17,9 +17,6 @@ import org.jetbrains.kotlin.gradle.utils.injected
import javax.inject.Inject
abstract class KotlinTestTask : AbstractTestTask() {
@Input
var showTestTargetName: Boolean = false
@Input
@Optional
var targetName: String? = null
@@ -35,10 +32,10 @@ abstract class KotlinTestTask : AbstractTestTask() {
filterExt.isFailOnNoMatchingTests = false
}
protected val includePatterns: Set<String>
val includePatterns: Set<String>
@Internal get() = filterExt.includePatterns + filterExt.commandLineIncludePatterns
protected val excludePatterns: Set<String>
val excludePatterns: Set<String>
@Internal get() = excludes
@get:Inject
@@ -1,6 +1,6 @@
package org.jetbrains.kotlin.gradle.testing
import org.jetbrains.kotlin.gradle.targets.js.tasks.KotlinNodeJsTestRunnerCliArgs
import org.jetbrains.kotlin.gradle.targets.js.testing.nodejs.KotlinNodeJsTestRunnerCliArgs
@Suppress("EnumEntryName")
enum class IgnoredTestSuites(val cli: KotlinNodeJsTestRunnerCliArgs.IgnoredTestSuitesReporting) {