Gradle, Kotlin/JS: enable source maps by default, get it working on tests.
Publish kotlin-test-nodejs-runner separately from kotlin-gradle-plugin.
This commit is contained in:
@@ -68,7 +68,8 @@ projectTest {
|
||||
":examples:annotation-processor-example:install",
|
||||
":kotlin-scripting-common:install",
|
||||
":kotlin-scripting-jvm:install",
|
||||
":kotlin-scripting-compiler-embeddable:install"
|
||||
":kotlin-scripting-compiler-embeddable:install",
|
||||
":kotlin-test-nodejs-runner:install"
|
||||
)
|
||||
exclude(jpsIncrementalTestsClass)
|
||||
}
|
||||
|
||||
+18
@@ -380,6 +380,24 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() {
|
||||
|
||||
assertFileExists(moduleDir + "kotlin-js-plugin-1.0-sources.jar")
|
||||
|
||||
assertFileExists("build/test_node_modules/kotlin.js")
|
||||
assertFileExists("build/test_node_modules/kotlin.js.map")
|
||||
assertFileExists("build/test_node_modules/kotlin-test.js")
|
||||
assertFileExists("build/test_node_modules/kotlin-test.js.map")
|
||||
assertFileExists("build/test_node_modules/kotlin-test-nodejs-runner.js")
|
||||
assertFileExists("build/test_node_modules/kotlin-test-nodejs-runner.js.map")
|
||||
assertFileExists("build/test_node_modules/kotlin-nodejs-source-map-support.js")
|
||||
assertFileExists("build/test_node_modules/kotlin-nodejs-source-map-support.js.map")
|
||||
assertFileExists("build/test_node_modules/kotlin-js-plugin.js")
|
||||
assertFileExists("build/test_node_modules/kotlin-js-plugin.js.map")
|
||||
assertFileExists("build/test_node_modules/kotlin-js-plugin_test.js")
|
||||
assertFileExists("build/test_node_modules/kotlin-js-plugin_test.js.map")
|
||||
|
||||
assertFileContains(
|
||||
"build/test_node_modules/kotlin-js-plugin_test.js.map",
|
||||
"\"sources\":[\"../../src/test/kotlin/MainTest.kt\"]"
|
||||
)
|
||||
|
||||
assertTestResults("testProject/kotlin-js-plugin-project/tests.xml", "test")
|
||||
}
|
||||
}
|
||||
|
||||
+18
-10
@@ -311,8 +311,6 @@ class MultiplatformGradleIT : BaseGradleIT() {
|
||||
assertSuccessful()
|
||||
|
||||
assertTasksRegisteredAndNotRealized(
|
||||
":kotlinNodeJsTestRuntimeExtract",
|
||||
|
||||
":clientTestNodeModules",
|
||||
":clientTest",
|
||||
|
||||
@@ -325,8 +323,6 @@ class MultiplatformGradleIT : BaseGradleIT() {
|
||||
assertSuccessful()
|
||||
|
||||
assertTasksExecuted(
|
||||
":kotlinNodeJsTestRuntimeExtract",
|
||||
|
||||
":clientTestNodeModules",
|
||||
":clientTest",
|
||||
|
||||
@@ -334,6 +330,24 @@ class MultiplatformGradleIT : BaseGradleIT() {
|
||||
":serverTest"
|
||||
)
|
||||
|
||||
assertFileExists("build/client_test_node_modules/kotlin.js")
|
||||
assertFileExists("build/client_test_node_modules/kotlin.js.map")
|
||||
assertFileExists("build/client_test_node_modules/kotlin-test.js")
|
||||
assertFileExists("build/client_test_node_modules/kotlin-test.js.map")
|
||||
assertFileExists("build/client_test_node_modules/kotlin-test-nodejs-runner.js")
|
||||
assertFileExists("build/client_test_node_modules/kotlin-test-nodejs-runner.js.map")
|
||||
assertFileExists("build/client_test_node_modules/kotlin-nodejs-source-map-support.js")
|
||||
assertFileExists("build/client_test_node_modules/kotlin-nodejs-source-map-support.js.map")
|
||||
assertFileExists("build/client_test_node_modules/new-mpp-js-tests.js")
|
||||
assertFileExists("build/client_test_node_modules/new-mpp-js-tests.js.map")
|
||||
assertFileExists("build/client_test_node_modules/new-mpp-js-tests_test.js")
|
||||
assertFileExists("build/client_test_node_modules/new-mpp-js-tests_test.js.map")
|
||||
|
||||
assertFileContains(
|
||||
"build/client_test_node_modules/new-mpp-js-tests_test.js.map",
|
||||
"\"sources\":[\"../../src/commonTest/kotlin/CommonTest.kt\"]"
|
||||
)
|
||||
|
||||
assertTestResults("testProject/new-mpp-js-tests/TEST-CommonTest.xml", "clientTest")
|
||||
assertTestResults("testProject/new-mpp-js-tests/TEST-CommonTest.xml", "serverTest")
|
||||
}
|
||||
@@ -343,8 +357,6 @@ class MultiplatformGradleIT : BaseGradleIT() {
|
||||
assertSuccessful()
|
||||
|
||||
assertTasksUpToDate(
|
||||
":kotlinNodeJsTestRuntimeExtract",
|
||||
|
||||
":clientTestNodeModules",
|
||||
":clientTest",
|
||||
|
||||
@@ -359,10 +371,6 @@ class MultiplatformGradleIT : BaseGradleIT() {
|
||||
build("check") {
|
||||
assertSuccessful()
|
||||
|
||||
assertTasksUpToDate(
|
||||
":kotlinNodeJsTestRuntimeExtract"
|
||||
)
|
||||
|
||||
assertTasksExecuted(
|
||||
":clientTestNodeModules",
|
||||
":clientTest",
|
||||
|
||||
@@ -46,6 +46,8 @@ dependencies {
|
||||
compileOnly(project(":kotlin-annotation-processing-gradle"))
|
||||
compileOnly(project(":kotlin-scripting-compiler"))
|
||||
|
||||
compile("com.google.code.gson:gson:2.8.5")
|
||||
|
||||
compileOnly("com.android.tools.build:gradle:2.0.0")
|
||||
compileOnly("com.android.tools.build:gradle-core:2.0.0")
|
||||
compileOnly("com.android.tools.build:builder:2.0.0")
|
||||
@@ -62,8 +64,7 @@ dependencies {
|
||||
runtime(projectRuntimeJar(":kotlin-scripting-compiler-embeddable"))
|
||||
runtime(project(":kotlin-reflect"))
|
||||
|
||||
jarContents(compileOnly(intellijDep()) { includeJars("serviceMessages") })
|
||||
jarContents(projectArchives(":kotlin-test-nodejs-runner"))
|
||||
jarContents(compileOnly(intellijDep()) { includeJars("serviceMessages", "gson-2.8.5") })
|
||||
|
||||
// com.android.tools.build:gradle has ~50 unneeded transitive dependencies
|
||||
compileOnly("com.android.tools.build:gradle:3.0.0") { isTransitive = false }
|
||||
|
||||
+9
-9
@@ -63,6 +63,10 @@ internal class KotlinJsCompilationTestsConfigurator(
|
||||
get() = classpath.plus(project.files(destinationDir))
|
||||
|
||||
fun configure() {
|
||||
compilation.dependencies {
|
||||
implementation(kotlin("test-nodejs-runner"))
|
||||
}
|
||||
|
||||
val nodeModulesTask = registerTask(
|
||||
project,
|
||||
disambiguateCamelCased("nodeModules", true),
|
||||
@@ -79,20 +83,13 @@ internal class KotlinJsCompilationTestsConfigurator(
|
||||
}
|
||||
|
||||
val projectWithNodeJsPlugin = NodeJsPlugin.ensureAppliedInHierarchy(target.project)
|
||||
val kotlinJsExtractTestRunnerTask = project.kotlinNodeJsTestRuntimeExtractTask
|
||||
|
||||
val testTask = registerTask(project, testTaskName, KotlinNodeJsTestTask::class.java) { testJs ->
|
||||
testJs.group = "verification"
|
||||
|
||||
testJs.dependsOn(
|
||||
nodeModulesTask.getTaskOrProvider(),
|
||||
kotlinJsExtractTestRunnerTask.getTaskOrProvider()
|
||||
)
|
||||
testJs.dependsOn(nodeModulesTask.getTaskOrProvider())
|
||||
|
||||
testJs.onlyIf {
|
||||
// run only if there is org.jetbrains.kotlin:kotlin-test-js in classpath
|
||||
// (kotlin-test-nodejs-runner.js requires kotlin-test.js)
|
||||
nodeModulesDir.resolve("kotlin-test.js").exists()
|
||||
compileTestKotlin2Js.outputFile.exists()
|
||||
}
|
||||
|
||||
@@ -103,7 +100,10 @@ internal class KotlinJsCompilationTestsConfigurator(
|
||||
testJs.nodeJsProcessOptions.workingDir = project.projectDir
|
||||
|
||||
testJs.nodeModulesDir = nodeModulesDir
|
||||
testJs.testRuntimeNodeModule = project.extractedKotlinTestNodeJsRunner
|
||||
testJs.testRuntimeNodeModules = listOf(
|
||||
"kotlin-test-nodejs-runner.js",
|
||||
"kotlin-nodejs-source-map-support.js"
|
||||
)
|
||||
testJs.nodeModulesToLoad = setOf(compileTestKotlin2Js.outputFile.name)
|
||||
|
||||
val htmlReport = DslObject(testJs.reports.html)
|
||||
|
||||
+5
-1
@@ -22,7 +22,11 @@ class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
|
||||
super.configureCompilations(platformTarget)
|
||||
|
||||
platformTarget.compilations.all {
|
||||
it.compileKotlinTask.kotlinOptions.moduleKind = "umd"
|
||||
it.compileKotlinTask.kotlinOptions {
|
||||
moduleKind = "umd"
|
||||
sourceMap = true
|
||||
sourceMapEmbedSources = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
package org.jetbrains.kotlin.gradle.targets.js.internal
|
||||
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import com.google.gson.stream.MalformedJsonException
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.*
|
||||
|
||||
open class RewriteSourceMapFilterReader(
|
||||
val input: Reader
|
||||
) : FilterReader(input) {
|
||||
// This implementation works only when source map contents starts
|
||||
// with prolog `{"version":3,"file":"...","sources":[...],"sourcesContent":...`
|
||||
// This is always true for Kotlin/JS and many other tools that produces JS source maps.
|
||||
//
|
||||
// Full implementation (without loading file contents entirely into memory) requires
|
||||
// streaming read of string literals inside "mappings", which is not supported by any
|
||||
// known JSON parsers. We cannot store "mappings" into memory since it can reach tens
|
||||
// of megabytes for large projects.
|
||||
//
|
||||
// Implementation: read contents before [PROLOG_END], parse it as JSON and transform source paths.
|
||||
// All rest contents will be passed directly from [input].
|
||||
|
||||
companion object {
|
||||
internal const val PROLOG_END = "],\"sourcesContent\":"
|
||||
const val UNSUPPORTED_FORMAT_MESSAGE =
|
||||
"Unsupported format. Contents should starts with `{\"version\":3,\"file\":\"...\",\"sources\":[...],\"sourcesContent\":...`"
|
||||
|
||||
private val log = LoggerFactory.getLogger("kotlin")
|
||||
}
|
||||
|
||||
private var wasReadFirst = false
|
||||
private val prologLimit = 0xfffff
|
||||
|
||||
// buffer with transformed prolog, that wil be emitted first
|
||||
private lateinit var bufferWriter: StringWriter
|
||||
private lateinit var bufferJsonWriter: JsonWriter
|
||||
private val buffer: StringBuffer get() = bufferWriter.buffer
|
||||
private var bufferReadPos = 0
|
||||
private val bufferAvailable get() = buffer.length - bufferReadPos
|
||||
|
||||
private var inputEof = false
|
||||
|
||||
lateinit var srcSourceRoot: String
|
||||
lateinit var targetSourceRoot: String
|
||||
|
||||
private fun maybeReadFirst() {
|
||||
if (!wasReadFirst) {
|
||||
wasReadFirst = true
|
||||
readFirst()
|
||||
}
|
||||
}
|
||||
|
||||
private fun readFirst() {
|
||||
// read 1Kb chunks from [input] until [PROLOG_END] will be found
|
||||
val jsonString = StringBuilder()
|
||||
val readBuffer = CharArray(1024)
|
||||
var lastRead: Int
|
||||
var jsonPrologPos: Int
|
||||
while (true) {
|
||||
lastRead = input.read(readBuffer)
|
||||
if (lastRead == -1) {
|
||||
inputEof = true
|
||||
writeBackUnsupported(jsonString, "$UNSUPPORTED_FORMAT_MESSAGE. \"sourcesContent\" or \"sources\" not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Try find PROLOG_END. Note the case when prolog contents is splitted between reads.
|
||||
// Like, one buffer ends with `],"sourc`, and the other starts with `esContent"`
|
||||
val prevEnd = jsonString.length
|
||||
jsonString.append(readBuffer, 0, lastRead)
|
||||
jsonPrologPos = jsonString.indexOf(PROLOG_END, prevEnd - PROLOG_END.length)
|
||||
if (jsonPrologPos == -1) {
|
||||
if (jsonString.length + lastRead > prologLimit) {
|
||||
writeBackUnsupported(jsonString, "Too many sources or format is not supported")
|
||||
return
|
||||
}
|
||||
} else break
|
||||
}
|
||||
|
||||
// create StringWriter to write transformed prolog and contents that was read after PROLOG_END
|
||||
bufferWriter = StringWriter(jsonString.length)
|
||||
bufferJsonWriter = JsonWriter(bufferWriter)
|
||||
|
||||
// parse json in prolog and write it back to bufferJsonWriter with transformed source paths
|
||||
val json = JsonReader(StringReader(jsonString.toString()))
|
||||
try {
|
||||
json.beginObject()
|
||||
bufferJsonWriter.beginObject()
|
||||
|
||||
reading@ while (true) {
|
||||
val token = json.peek()
|
||||
check(token == JsonToken.NAME) { "JSON key expected, but $token found" }
|
||||
val key = json.nextName()
|
||||
when (key) {
|
||||
"sources" -> {
|
||||
json.beginArray()
|
||||
bufferJsonWriter.name("sources").beginArray()
|
||||
while (json.peek() != JsonToken.END_ARRAY) {
|
||||
val path = json.nextString()
|
||||
bufferJsonWriter.value(transformString(path))
|
||||
}
|
||||
json.endArray()
|
||||
}
|
||||
"version" -> bufferJsonWriter.name(key).value(json.nextInt())
|
||||
"file" -> bufferJsonWriter.name(key).value(json.nextString())
|
||||
"sourcesContent" -> break@reading
|
||||
else -> throw IllegalStateException("Unknown key \"$key\"")
|
||||
}
|
||||
}
|
||||
|
||||
// leave bufferJsonWriter unclosed
|
||||
|
||||
// push back contents that was read after PROLOG_END
|
||||
bufferWriter.append(jsonString.substring(jsonPrologPos))
|
||||
} catch (e: IllegalStateException) {
|
||||
writeBackUnsupported(jsonString, json, e.message!!)
|
||||
} catch (e: MalformedJsonException) {
|
||||
writeBackUnsupported(jsonString, json, "Malformed JSON")
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeBackUnsupported(jsonString: StringBuilder, reader: JsonReader, message: String) =
|
||||
writeBackUnsupported(
|
||||
jsonString,
|
||||
"$UNSUPPORTED_FORMAT_MESSAGE. $message at ${reader.toString().replace("JsonReader at ", "")} in `$jsonString"
|
||||
)
|
||||
|
||||
private fun writeBackUnsupported(jsonString: StringBuilder, reason: String) =
|
||||
writeBackUnsupported(jsonString.toString(), reason)
|
||||
|
||||
private fun writeBackUnsupported(jsonString: String, reason: String) {
|
||||
warnUnsupported(reason)
|
||||
bufferWriter = StringWriter(jsonString.length)
|
||||
bufferWriter.append(jsonString)
|
||||
}
|
||||
|
||||
protected open fun warnUnsupported(reason: String) {
|
||||
log.warn("Cannot rewrite paths in JavaScript source maps: $reason")
|
||||
}
|
||||
|
||||
private fun transformString(value: String): String =
|
||||
File(srcSourceRoot).resolve(value).canonicalFile.relativeToOrSelf(File(targetSourceRoot)).path
|
||||
|
||||
override fun read(): Int {
|
||||
maybeReadFirst()
|
||||
if (bufferAvailable == 0) {
|
||||
if (inputEof) return -1
|
||||
val read = input.read()
|
||||
if (read == -1) {
|
||||
inputEof = true
|
||||
return -1
|
||||
} else {
|
||||
return read
|
||||
}
|
||||
}
|
||||
return buffer[bufferReadPos++].toInt()
|
||||
}
|
||||
|
||||
override fun read(dest: CharArray, initialDestOffset: Int, n: Int): Int {
|
||||
maybeReadFirst()
|
||||
|
||||
var todo = n
|
||||
var destOffset = initialDestOffset
|
||||
while (todo > 0) {
|
||||
if (bufferAvailable == 0) {
|
||||
if (inputEof) return if (n == todo) -1 else n - todo
|
||||
val read = input.read(dest, destOffset, todo)
|
||||
if (read == -1) {
|
||||
inputEof = true
|
||||
return n - todo
|
||||
} else {
|
||||
return read + (n - todo)
|
||||
}
|
||||
}
|
||||
|
||||
val toRead = Math.min(todo, bufferAvailable)
|
||||
buffer.getChars(bufferReadPos, bufferReadPos + toRead, dest, destOffset)
|
||||
bufferReadPos += toRead
|
||||
destOffset += toRead
|
||||
todo -= toRead
|
||||
}
|
||||
|
||||
return n - todo
|
||||
}
|
||||
|
||||
override fun skip(n: Long): Long {
|
||||
maybeReadFirst()
|
||||
|
||||
var todo = n.toInt()
|
||||
while (todo > 0) {
|
||||
if (bufferAvailable == 0) {
|
||||
if (inputEof) return n - todo
|
||||
val read = input.skip(todo.toLong())
|
||||
if (read == 0L && todo != 0) {
|
||||
inputEof = true
|
||||
return n - todo
|
||||
} else {
|
||||
return read + (n - todo)
|
||||
}
|
||||
}
|
||||
|
||||
val toRead = Math.min(todo, bufferAvailable)
|
||||
bufferReadPos += toRead
|
||||
todo -= toRead
|
||||
}
|
||||
return n - todo
|
||||
}
|
||||
}
|
||||
+15
-2
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.targets.js.tasks
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.targets.js.internal.RewriteSourceMapFilterReader
|
||||
import java.io.File
|
||||
|
||||
open class KotlinJsNodeModulesTask : DefaultTask() {
|
||||
@@ -34,17 +35,29 @@ open class KotlinJsNodeModulesTask : DefaultTask() {
|
||||
isKotlinJsRuntimeFile(fileTreeElement.file)
|
||||
}
|
||||
|
||||
sync.eachFile {
|
||||
if (it.name.endsWith(".js.map")) {
|
||||
it.filter(
|
||||
mapOf(
|
||||
"srcSourceRoot" to it.file.parentFile,
|
||||
"targetSourceRoot" to nodeModulesDir
|
||||
),
|
||||
RewriteSourceMapFilterReader::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sync.into(nodeModulesDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val File.isZip
|
||||
get() = isFile && name.endsWith(".jar")
|
||||
get() = isFile && (name.endsWith(".jar") || name.endsWith(".zip"))
|
||||
|
||||
internal fun isKotlinJsRuntimeFile(file: File): Boolean {
|
||||
if (!file.isFile) return false
|
||||
val name = file.name
|
||||
return (name.endsWith(".js") && !name.endsWith(".meta.js"))
|
||||
|| name.endsWith(".js.map")
|
||||
|| name.endsWith(".js.map")
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.SkipWhenEmpty
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
const val kotlinTestNodeJsRunnerJs = "kotlin-test-nodejs-runner.js"
|
||||
|
||||
internal val Project.extractedKotlinTestNodeJsRunner: File
|
||||
get() = project.buildDir.resolve("kotlin/$kotlinTestNodeJsRunnerJs")
|
||||
|
||||
internal val Project.kotlinNodeJsTestRuntimeExtractTask
|
||||
get() = project.locateOrRegisterTask<KotlinNodeJsTestRuntimeExtractTask>(
|
||||
"kotlinNodeJsTestRuntimeExtract"
|
||||
) { task ->
|
||||
task.destination = extractedKotlinTestNodeJsRunner
|
||||
}
|
||||
|
||||
open class KotlinNodeJsTestRuntimeExtractTask : DefaultTask() {
|
||||
@Input
|
||||
public var resourceName: String = "/$kotlinTestNodeJsRunnerJs"
|
||||
|
||||
@OutputFile
|
||||
@SkipWhenEmpty
|
||||
public lateinit var destination: File
|
||||
|
||||
@TaskAction
|
||||
fun copyRuntime() {
|
||||
val testsRuntime = javaClass.getResourceAsStream(resourceName)
|
||||
?: error("Cannot find `$resourceName` in resources")
|
||||
|
||||
check(destination.parentFile.exists() || destination.parentFile.mkdirs()) {
|
||||
"Cannot create directory ${destination.parentFile}"
|
||||
}
|
||||
|
||||
Files.copy(
|
||||
testsRuntime,
|
||||
destination.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+19
-5
@@ -38,14 +38,14 @@ open class KotlinNodeJsTestTask : AbstractTestTask() {
|
||||
var excludes = mutableSetOf<String>()
|
||||
|
||||
@InputDirectory
|
||||
var nodeModulesDir: File? = null
|
||||
lateinit var nodeModulesDir: File
|
||||
|
||||
@Input
|
||||
@SkipWhenEmpty
|
||||
var nodeModulesToLoad: Set<String> = setOf()
|
||||
|
||||
@InputFile
|
||||
lateinit var testRuntimeNodeModule: File
|
||||
@Input
|
||||
lateinit var testRuntimeNodeModules: Collection<String>
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
private val filterExt: DefaultTestFilter
|
||||
@@ -69,6 +69,9 @@ open class KotlinNodeJsTestTask : AbstractTestTask() {
|
||||
val nodeJsWorkingDirCanonicalPath: String
|
||||
@Input get() = nodeJsProcessOptions.workingDir.canonicalPath
|
||||
|
||||
@Input
|
||||
var debug: Boolean = false
|
||||
|
||||
fun nodeJs(options: ProcessForkOptions.() -> Unit) {
|
||||
options(nodeJsProcessOptions)
|
||||
}
|
||||
@@ -77,7 +80,13 @@ open class KotlinNodeJsTestTask : AbstractTestTask() {
|
||||
val extendedForkOptions = DefaultProcessForkOptions(fileResolver)
|
||||
nodeJsProcessOptions.copyTo(extendedForkOptions)
|
||||
|
||||
extendedForkOptions.environment.addPath("NODE_PATH", nodeModulesDir!!.canonicalPath)
|
||||
extendedForkOptions.environment.addPath("NODE_PATH", nodeModulesDir.canonicalPath)
|
||||
|
||||
val nodeJsArgs = mutableListOf<String>()
|
||||
|
||||
if (debug) {
|
||||
nodeJsArgs.add("--inspect-brk")
|
||||
}
|
||||
|
||||
val cliArgs = KotlinNodeJsTestRunnerCliArgs(
|
||||
nodeModulesToLoad.toList(),
|
||||
@@ -98,7 +107,12 @@ open class KotlinNodeJsTestTask : AbstractTestTask() {
|
||||
|
||||
return TCServiceMessagesTestExecutionSpec(
|
||||
extendedForkOptions,
|
||||
listOf(testRuntimeNodeModule.absolutePath) + cliArgs.toList(),
|
||||
nodeJsArgs +
|
||||
testRuntimeNodeModules
|
||||
.map { nodeModulesDir.resolve(it) }
|
||||
.filter { it.exists() }
|
||||
.map { it.absolutePath } +
|
||||
cliArgs.toList(),
|
||||
clientSettings
|
||||
)
|
||||
}
|
||||
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
package org.jetbrains.kotlin.gradle.org.jetbrains.kotlin.gradle.targets.js.internal
|
||||
|
||||
import org.jetbrains.kotlin.gradle.targets.js.internal.RewriteSourceMapFilterReader
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import java.io.Reader
|
||||
import java.io.StringReader
|
||||
|
||||
class RewriteSourceMapFilterReaderTest {
|
||||
@Test
|
||||
fun testShort() {
|
||||
// input json should fit in buffer (1024)
|
||||
doTest(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLong() {
|
||||
doTest(100)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testVeryLong() {
|
||||
doTest(1000)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPrologSplitByReads() {
|
||||
// Test the case when prolog contents is splitted between reads.
|
||||
// Like, one buffer ends with `],"sourc`, and the other starts with `esContent"`
|
||||
val prolog = sample("", "").substringBefore(RewriteSourceMapFilterReader.PROLOG_END)
|
||||
doTest(1, addToProlog = "-".repeat(1024 - prolog.length - "],\"sourc".length))
|
||||
}
|
||||
|
||||
private fun sample(mappings: String, addToProlog: String): String {
|
||||
//language=JSON
|
||||
return """{"version":3,"file":"single-platform${addToProlog}.js","sources":["../../../../src/main/kotlin/main.kt"],"sourcesContent":[null],"names":[],"mappings":"$mappings"}"""
|
||||
}
|
||||
|
||||
private fun doTest(repeatMappings: Int, addToProlog: String = "") {
|
||||
val mappings0 =
|
||||
";;;;;;;;;;;;;;;;;;;;;;;;;IAQc,c;EAAA,C;;IAQc,O;IACJ,W;EAAA,C;;IAFA,aAAE,wBAAF,kBAA4B,gCAA5B," +
|
||||
"C;IAGJ,W;EAAA,C;;IAJA,uBAAI,yBAAJ,C;IAKJ,W;EAAA,C;;IANA,wBAAK,kBAAL,C;IAOJ,W;EAAA,C;;IATR," +
|
||||
"QACqC,KAAb,WAAhB,oBAAgB,CAAa,UAAK,WAAL,CADrC,C;EAWJ,C;;;;;;;;;"
|
||||
val mappings = mappings0.repeat(repeatMappings)
|
||||
|
||||
val filter = RewriteSourceMapFilterReader(
|
||||
StringReader(sample(mappings, addToProlog)),
|
||||
"/root/build/classes/kotlin/test/",
|
||||
"/root/build/test_node_modules/"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
//language=JSON
|
||||
"""{"version":3,"file":"single-platform${addToProlog}.js","sources":["../../src/main/kotlin/main.kt"],"sourcesContent":[null],"names":[],"mappings":"$mappings"}""",
|
||||
filter.readText()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnsupportedUnderfindProlog() {
|
||||
val filter =
|
||||
RewriteSourceMapFilterReaderMock(
|
||||
StringReader(
|
||||
//language=JSON
|
||||
"""{"version":3,"file":"single-platform.js","sources":["../../../../src/main/kotlin/main.kt"],"names":[],"mappings":""}"""
|
||||
),
|
||||
"/root/build/classes/kotlin/test/",
|
||||
"/root/build/test_node_modules/"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
//language=JSON
|
||||
"""{"version":3,"file":"single-platform.js","sources":["../../../../src/main/kotlin/main.kt"],"names":[],"mappings":""}""",
|
||||
filter.readText()
|
||||
)
|
||||
assertEquals(
|
||||
"Unsupported format. Contents should starts with `{\"version\":3,\"file\":\"...\",\"sources\":[...],\"sourcesContent\":...`. \"sourcesContent\" or \"sources\" not found",
|
||||
filter.warning
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnsupportedPrologSize() {
|
||||
val repeat = "\"../../../../src/main/kotlin/main.kt\",".repeat(0xfffff / 35)
|
||||
val filter =
|
||||
RewriteSourceMapFilterReaderMock(
|
||||
StringReader(
|
||||
//language=JSON
|
||||
"""{"version":3,"file":"single-platform.js","sources":[$repeat],"sourcesContent":[null],"names":[],"mappings":""}"""
|
||||
),
|
||||
"/root/build/classes/kotlin/test/",
|
||||
"/root/build/test_node_modules/"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
//language=JSON
|
||||
"""{"version":3,"file":"single-platform.js","sources":[$repeat],"sourcesContent":[null],"names":[],"mappings":""}""",
|
||||
filter.readText()
|
||||
)
|
||||
assertEquals("Too many sources or format is not supported", filter.warning)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testUnsupportedFormat() {
|
||||
val filter =
|
||||
RewriteSourceMapFilterReaderMock(
|
||||
StringReader(
|
||||
//language=JSON
|
||||
"""{"file":"single-platform.js","version1":3,"sources":["../../../../src/main/kotlin/main.kt"],"sourcesContent":[null],"names":[],"mappings":""}"""
|
||||
),
|
||||
"/root/build/classes/kotlin/test/",
|
||||
"/root/build/test_node_modules/"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
//language=JSON
|
||||
"""{"file":"single-platform.js","version1":3,"sources":["../../../../src/main/kotlin/main.kt"],"sourcesContent":[null],"names":[],"mappings":""}""",
|
||||
filter.readText()
|
||||
)
|
||||
assertEquals(
|
||||
"Unsupported format. Contents should starts with `{\"version\":3,\"file\":\"...\",\"sources\":[...],\"sourcesContent\":...`. Unknown key \"version1\" at line 1 column 40 path \$.version1 in `{\"file\":\"single-platform.js\",\"version1\":3,\"sources\":[\"../../../../src/main/kotlin/main.kt\"],\"sourcesContent\":[null],\"names\":[],\"mappings\":\"\"}",
|
||||
filter.warning
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInvalidJson() {
|
||||
val filter =
|
||||
RewriteSourceMapFilterReaderMock(
|
||||
StringReader("{:)],\"sourcesContent\":[null]}"),
|
||||
"/root/build/classes/kotlin/test/",
|
||||
"/root/build/test_node_modules/"
|
||||
)
|
||||
|
||||
assertEquals("{:)],\"sourcesContent\":[null]}", filter.readText())
|
||||
assertEquals(
|
||||
"Unsupported format. Contents should starts with `{\"version\":3,\"file\":\"...\",\"sources\":[...],\"sourcesContent\":...`. Malformed JSON at line 1 column 3 path \$. in `{:)],\"sourcesContent\":[null]}",
|
||||
filter.warning
|
||||
)
|
||||
}
|
||||
|
||||
class RewriteSourceMapFilterReaderMock(input: Reader, srcSourceRoot: String, targetSourceRoot: String) :
|
||||
RewriteSourceMapFilterReader(input) {
|
||||
init {
|
||||
setProperties(this, srcSourceRoot, targetSourceRoot)
|
||||
}
|
||||
|
||||
var warning: String? = null
|
||||
|
||||
override fun warnUnsupported(reason: String) {
|
||||
warning = reason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun RewriteSourceMapFilterReader(
|
||||
input: Reader,
|
||||
srcSourceRoot: String,
|
||||
targetSourceRoot: String
|
||||
) = RewriteSourceMapFilterReader(input).also {
|
||||
setProperties(it, srcSourceRoot, targetSourceRoot)
|
||||
}
|
||||
|
||||
private fun setProperties(
|
||||
it: RewriteSourceMapFilterReader,
|
||||
srcSourceRoot: String,
|
||||
targetSourceRoot: String
|
||||
) {
|
||||
it.srcSourceRoot = srcSourceRoot
|
||||
it.targetSourceRoot = targetSourceRoot
|
||||
}
|
||||
@@ -1,10 +1,27 @@
|
||||
import com.moowork.gradle.node.yarn.YarnTask
|
||||
|
||||
description = "Simple Kotlin/JS tests runner with TeamCity reporter"
|
||||
|
||||
plugins {
|
||||
id("base")
|
||||
id("com.moowork.node") version "1.2.0"
|
||||
}
|
||||
|
||||
val default = configurations.getByName(Dependency.DEFAULT_CONFIGURATION)
|
||||
val archives = configurations.getByName(Dependency.ARCHIVES_CONFIGURATION)
|
||||
|
||||
default.extendsFrom(archives)
|
||||
|
||||
plugins.apply("maven")
|
||||
|
||||
convention.getPlugin(MavenPluginConvention::class.java).also {
|
||||
it.conf2ScopeMappings.addMapping(MavenPlugin.RUNTIME_PRIORITY, archives, Conf2ScopeMappingContainer.RUNTIME)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
archives(project(":kotlin-test:kotlin-test-js"))
|
||||
}
|
||||
|
||||
node {
|
||||
version = "11.9.0"
|
||||
download = true
|
||||
@@ -25,25 +42,43 @@ tasks {
|
||||
setWorkingDir(projectDir)
|
||||
args = listOf("build")
|
||||
|
||||
inputs.dir(projectDir.resolve("src"))
|
||||
outputs.file(projectDir.resolve("lib/kotlin-test-nodejs-runner.js"))
|
||||
inputs.dir("src")
|
||||
inputs.files(
|
||||
"cli.ts",
|
||||
"nodejs-source-map-support.js",
|
||||
"package.json",
|
||||
"rollup.config.js",
|
||||
"tsconfig.json",
|
||||
"yarn.lock"
|
||||
)
|
||||
outputs.dir("lib")
|
||||
}
|
||||
|
||||
create<Delete>("cleanYarn") {
|
||||
group = "build"
|
||||
|
||||
delete = setOf(
|
||||
projectDir.resolve("node_modules"),
|
||||
projectDir.resolve("lib"),
|
||||
projectDir.resolve(".rpt2_cache")
|
||||
"node_modules",
|
||||
"lib",
|
||||
".rpt2_cache"
|
||||
)
|
||||
}
|
||||
|
||||
getByName("clean").dependsOn("cleanYarn")
|
||||
}
|
||||
|
||||
val jar = tasks.create<Jar>("jar") {
|
||||
dependsOn("yarnBuild")
|
||||
from(projectDir.resolve("lib"))
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add("archives", projectDir.resolve("lib/kotlin-test-nodejs-runner.js")) {
|
||||
builtBy("yarnBuild")
|
||||
add(
|
||||
"archives",
|
||||
jar.archiveFile.get().asFile
|
||||
) {
|
||||
builtBy(jar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publish()
|
||||
@@ -0,0 +1 @@
|
||||
require('source-map-support').install();
|
||||
@@ -14,7 +14,8 @@
|
||||
"build": "rimraf lib/* && rollup -c rollup.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^10.12.21"
|
||||
"@types/node": "^10.12.21",
|
||||
"source-map-support": "^0.5.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"copyfiles": "^2.1.0",
|
||||
|
||||
@@ -11,49 +11,66 @@ export default [
|
||||
output: {
|
||||
file: 'lib/kotlin-test-nodejs-runner.js',
|
||||
format: 'cjs',
|
||||
banner: '#!/usr/bin/env node'
|
||||
banner: '#!/usr/bin/env node',
|
||||
sourcemap: true
|
||||
},
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
jsnext: true,
|
||||
main: true
|
||||
}),
|
||||
jsnext: true,
|
||||
main: true
|
||||
}),
|
||||
commonjs(),
|
||||
typescript({
|
||||
tsconfig: "tsconfig.json"
|
||||
}),
|
||||
tsconfig: "tsconfig.json"
|
||||
}),
|
||||
uglify({
|
||||
sourcemap: true,
|
||||
compress: {
|
||||
// hoist_funs: true,
|
||||
// hoist_vars: true,
|
||||
toplevel: true,
|
||||
unsafe: true,
|
||||
dead_code: true,
|
||||
global_defs: {
|
||||
DEBUG: false,
|
||||
VERSION: pckg.version,
|
||||
BIN: Object.keys(pckg.bin)[0],
|
||||
DESCRIPTION: pckg.description
|
||||
}
|
||||
},
|
||||
mangle: {
|
||||
properties: {
|
||||
keep_quoted: true,
|
||||
reserved: [
|
||||
"argv", "hrtime",
|
||||
"kotlin_test", "kotlin", "setAdapter", "setAssertHook_4duqou$",
|
||||
"suite", "test",
|
||||
"stack"
|
||||
]
|
||||
},
|
||||
toplevel: true,
|
||||
},
|
||||
// output: {
|
||||
// beautify: true
|
||||
// }
|
||||
}),
|
||||
// sourceMaps()
|
||||
sourcemap: true,
|
||||
compress: {
|
||||
// hoist_funs: true,
|
||||
// hoist_vars: true,
|
||||
toplevel: true,
|
||||
unsafe: true,
|
||||
dead_code: true,
|
||||
global_defs: {
|
||||
DEBUG: false,
|
||||
VERSION: pckg.version,
|
||||
BIN: Object.keys(pckg.bin)[0],
|
||||
DESCRIPTION: pckg.description
|
||||
}
|
||||
},
|
||||
mangle: {
|
||||
properties: {
|
||||
keep_quoted: true,
|
||||
reserved: [
|
||||
"argv", "hrtime",
|
||||
"kotlin_test", "kotlin", "setAdapter", "setAssertHook_4duqou$",
|
||||
"suite", "test",
|
||||
"stack"
|
||||
]
|
||||
},
|
||||
toplevel: true,
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
input: './nodejs-source-map-support.js',
|
||||
external: ['path', 'fs', 'module'],
|
||||
output: {
|
||||
file: 'lib/kotlin-nodejs-source-map-support.js',
|
||||
format: 'cjs',
|
||||
sourcemap: true
|
||||
},
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
jsnext: true,
|
||||
main: true
|
||||
}),
|
||||
commonjs(),
|
||||
uglify({
|
||||
compress: true,
|
||||
sourcemap: true
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -2,10 +2,8 @@
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "ESNext",
|
||||
// "declaration": true,
|
||||
// "outDir": "./lib",
|
||||
"strict": true,
|
||||
// "sourceMap": true,
|
||||
"sourceMap": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"inlineSourceMap": true
|
||||
},
|
||||
|
||||
@@ -94,6 +94,11 @@ braces@^1.8.2:
|
||||
preserve "^0.2.0"
|
||||
repeat-element "^1.1.2"
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
builtin-modules@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.0.0.tgz#1e587d44b006620d90286cc7a9238bbc6129cab1"
|
||||
@@ -889,12 +894,20 @@ source-map-resolve@^0.5.0:
|
||||
source-map-url "^0.4.0"
|
||||
urix "^0.1.0"
|
||||
|
||||
source-map-support@^0.5.11:
|
||||
version "0.5.11"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.11.tgz#efac2ce0800355d026326a0ca23e162aeac9a4e2"
|
||||
integrity sha512-//sajEx/fGL3iw6fltKMdPvy8kL3kJ2O3iuYlRoT3k9Kb4BjOoZ+BZzaNHeuaruSt+Kf3Zk9tnfAQg9/AJqUVQ==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map-url@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
|
||||
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
|
||||
|
||||
source-map@~0.6.1:
|
||||
source-map@^0.6.0, source-map@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
Reference in New Issue
Block a user