[JS IR] Test the incremental rebuild after an error from JS IR BE
Move JS IR BE IC integration tests in a separate file Test case for KT-56282
This commit is contained in:
committed by
Space Team
parent
8cfe234140
commit
3a42b9846e
+4
-131
@@ -9,11 +9,9 @@ import com.google.gson.Gson
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.api.logging.configuration.WarningMode
|
||||
import org.gradle.testkit.runner.BuildResult
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KLIB_TYPE
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectModules
|
||||
@@ -128,132 +126,6 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Check IR incremental cache invalidation by compiler args")
|
||||
@GradleTest
|
||||
fun testJsIrIncrementalCacheInvalidationByArgs(gradleVersion: GradleVersion) {
|
||||
project("kotlin2JsIrICProject", gradleVersion) {
|
||||
val buildConfig = buildGradleKts.readText()
|
||||
|
||||
fun setLazyInitializationArg(value: Boolean) {
|
||||
buildGradleKts.writeText(buildConfig)
|
||||
buildGradleKts.appendText(
|
||||
"""
|
||||
|
|
||||
|tasks.named<org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrLink>("compileDevelopmentExecutableKotlinJs") {
|
||||
| kotlinOptions {
|
||||
| freeCompilerArgs += "-Xir-property-lazy-initialization=$value"
|
||||
| }
|
||||
|}
|
||||
""".trimMargin()
|
||||
)
|
||||
}
|
||||
|
||||
fun String.testScriptOutLines() = this.lines().mapNotNull {
|
||||
val trimmed = it.removePrefix(">>> TEST OUT: ")
|
||||
if (trimmed == it) null else trimmed
|
||||
}
|
||||
|
||||
// -Xir-property-lazy-initialization default is true
|
||||
build("nodeRun") {
|
||||
assertTasksExecuted(":compileDevelopmentExecutableKotlinJs")
|
||||
assertEquals(listOf("Hello, Gradle."), output.testScriptOutLines())
|
||||
}
|
||||
|
||||
setLazyInitializationArg(false)
|
||||
build("nodeRun") {
|
||||
assertTasksExecuted(":compileDevelopmentExecutableKotlinJs")
|
||||
assertEquals(listOf("TOP LEVEL!", "Hello, Gradle."), output.testScriptOutLines())
|
||||
}
|
||||
|
||||
setLazyInitializationArg(true)
|
||||
build("nodeRun") {
|
||||
assertTasksExecuted(":compileDevelopmentExecutableKotlinJs")
|
||||
assertEquals(listOf("Hello, Gradle."), output.testScriptOutLines())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("incremental compilation for JS IR consider multiple artifacts in one project")
|
||||
@GradleTest
|
||||
fun testJsIrIncrementalMultipleArtifacts(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-ir-ic-multiple-artifacts", gradleVersion) {
|
||||
build("compileDevelopmentExecutableKotlinJs") {
|
||||
val cacheDir = projectPath.resolve("app/build/klib/cache/").toFile()
|
||||
val cacheRootDirName = cacheDir.list()?.singleOrNull()
|
||||
assertTrue("Lib cache root dir should contain 1 element 'version.hash'") {
|
||||
cacheRootDirName?.startsWith("version.") ?: false
|
||||
}
|
||||
val cacheRootDir = cacheDir.resolve(cacheRootDirName!!)
|
||||
val klibCacheDirs = cacheRootDir.list()
|
||||
// 2 for lib.klib + 1 for stdlib + 1 for main
|
||||
assertEquals(4, klibCacheDirs?.size, "cache should contain 4 dirs")
|
||||
|
||||
val libKlibCacheDirs = klibCacheDirs?.filter { dir -> dir.startsWith("lib.klib.") }
|
||||
assertEquals(2, libKlibCacheDirs?.size, "cache should contain 2 dirs for lib.klib")
|
||||
|
||||
var lib = false
|
||||
var libOther = false
|
||||
|
||||
cacheRootDir.listFiles()!!
|
||||
.forEach {
|
||||
it.listFiles()!!
|
||||
.filter { it.isFile }
|
||||
.forEach {
|
||||
val text = it.readText()
|
||||
// cache keeps the js code of compiled module, this substring from that js code
|
||||
if (text.contains("root['kotlin-js-ir-ic-multiple-artifacts-lib']")) {
|
||||
if (lib) {
|
||||
error("lib should be only once in cache")
|
||||
}
|
||||
lib = true
|
||||
}
|
||||
// cache keeps the js code of compiled module, this substring from that js code
|
||||
if (text.contains("root['kotlin-js-ir-ic-multiple-artifacts-lib-other']")) {
|
||||
if (libOther) {
|
||||
error("libOther should be only once in cache")
|
||||
}
|
||||
libOther = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
assertTrue("lib and libOther should be once in cache") {
|
||||
lib && libOther
|
||||
}
|
||||
assertTasksExecuted(":app:compileDevelopmentExecutableKotlinJs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Remove unused dependency from klib")
|
||||
@GradleTest
|
||||
fun testJsIrIncrementalKlibRemoveUnusedDependency(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-ir-ic-remove-unused-dep", gradleVersion) {
|
||||
val appBuildGradleKts = subProject("app").buildGradleKts
|
||||
|
||||
val buildGradleKtsWithoutDependency = appBuildGradleKts.readText()
|
||||
appBuildGradleKts.appendText(
|
||||
"""
|
||||
|
|
||||
|dependencies {
|
||||
| implementation(project(":lib"))
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
)
|
||||
|
||||
build("compileDevelopmentExecutableKotlinJs") {
|
||||
assertTasksExecuted(":app:compileDevelopmentExecutableKotlinJs")
|
||||
}
|
||||
|
||||
appBuildGradleKts.writeText(buildGradleKtsWithoutDependency)
|
||||
build("compileDevelopmentExecutableKotlinJs") {
|
||||
assertTasksExecuted(":app:compileDevelopmentExecutableKotlinJs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("falsify kotlin js compiler args")
|
||||
@GradleTest
|
||||
fun testFalsifyKotlinJsCompilerArgs(gradleVersion: GradleVersion) {
|
||||
@@ -1545,10 +1417,11 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean)
|
||||
Gson().fromJson(it.readText(), PackageJson::class.java)
|
||||
}
|
||||
|
||||
@DisplayName("incremental compilation with multiple js modules after compilation error works")
|
||||
@DisplayName("incremental compilation with multiple js modules after frontend compilation error works")
|
||||
@GradleTest
|
||||
fun testIncrementalCompilationWithMultipleModulesAfterCompilationError(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-ir-ic-multiple-artifacts", gradleVersion) {
|
||||
val buildOptions = defaultBuildOptions.copy(jsOptions = defaultJsOptions.copy(incrementalJsKlib = true))
|
||||
project("kotlin-js-ir-ic-multiple-artifacts", gradleVersion, buildOptions = buildOptions) {
|
||||
build("compileKotlinJs")
|
||||
|
||||
val libKt = subProject("lib").kotlinSourcesDir().resolve("Lib.kt") ?: error("No Lib.kt file in test project")
|
||||
@@ -1558,7 +1431,7 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean)
|
||||
buildAndFail("compileKotlinJs")
|
||||
libKt.modify { it.replace("func answe", "fun answer") } // revert compilation error
|
||||
appKt.modify { it.replace("Sheldon:", "Sheldon :") } // some change for incremental compilation
|
||||
build("compileKotlinJs", buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) {
|
||||
build("compileKotlinJs", buildOptions = buildOptions.copy(logLevel = LogLevel.DEBUG)) {
|
||||
assertOutputContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE)
|
||||
assertTasksUpToDate(":lib:compileKotlinJs")
|
||||
assertTasksExecuted(":app:compileKotlinJs")
|
||||
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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
|
||||
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import kotlin.io.path.appendText
|
||||
import kotlin.io.path.readText
|
||||
import kotlin.io.path.writeText
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@DisplayName("Incremental compilation tests for Kotlin JS IR backend")
|
||||
@JsGradlePluginTests
|
||||
class Kotlin2JsIrBeIncrementalCompilationIT : KGPBaseTest() {
|
||||
override val defaultBuildOptions = BuildOptions(
|
||||
jsOptions = BuildOptions.JsOptions(
|
||||
jsCompilerType = KotlinJsCompilerType.IR,
|
||||
incrementalJsKlib = true,
|
||||
incrementalJsIr = true
|
||||
)
|
||||
)
|
||||
|
||||
@DisplayName("Test rebuild after backend error")
|
||||
@GradleTest
|
||||
fun testRebuildAfterError(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-ir-ic-rebuild-after-error", gradleVersion) {
|
||||
fun readCacheFiles(): Map<String, Int> {
|
||||
val cacheFiles = mutableMapOf<String, Int>()
|
||||
projectPath.resolve("app/build/klib/cache/").toFile().walk().forEach { cachedFile ->
|
||||
if (cachedFile.isFile) {
|
||||
cacheFiles[cachedFile.absolutePath] = cachedFile.readBytes().contentHashCode()
|
||||
}
|
||||
}
|
||||
return cacheFiles
|
||||
}
|
||||
|
||||
val srcFile = projectPath.resolve("app/src/main/kotlin/App.kt").toFile()
|
||||
val badCode = srcFile.readText()
|
||||
|
||||
var successfulBuildCacheFiles = emptyMap<String, Int>()
|
||||
srcFile.appendText("\nfun unknownFunction() = 1\n")
|
||||
build("nodeDevelopmentRun") {
|
||||
assertTasksExecuted(":app:compileDevelopmentExecutableKotlinJs")
|
||||
assertOutputContains("Hello, World!")
|
||||
successfulBuildCacheFiles = readCacheFiles()
|
||||
assertTrue("Cache should not be empty after successful build") { successfulBuildCacheFiles.isNotEmpty() }
|
||||
}
|
||||
|
||||
srcFile.writeText(badCode)
|
||||
for (i in 0..1) {
|
||||
buildAndFail("nodeDevelopmentRun") {
|
||||
assertTasksFailed(":app:compileDevelopmentExecutableKotlinJs")
|
||||
val failedBuildCacheFiles = readCacheFiles()
|
||||
assertEquals(successfulBuildCacheFiles, failedBuildCacheFiles, "The cache files should not be modified")
|
||||
}
|
||||
}
|
||||
|
||||
srcFile.writeText(badCode.replace("Hello, World!", "Hello, Kotlin!") + "\nfun unknownFunction() = 2\n")
|
||||
build("nodeDevelopmentRun") {
|
||||
assertTasksExecuted(":app:compileDevelopmentExecutableKotlinJs")
|
||||
assertOutputContains("Hello, Kotlin!")
|
||||
val successfulRebuildCacheFiles = readCacheFiles()
|
||||
assertEquals(successfulBuildCacheFiles.size, successfulRebuildCacheFiles.size, "The number of files must be the same")
|
||||
assertNotEquals(successfulBuildCacheFiles, successfulRebuildCacheFiles, "The cache files should be modified")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Test cache invalidation after modifying compiler args")
|
||||
@GradleTest
|
||||
fun testCacheInvalidationAfterCompilerArgModifying(gradleVersion: GradleVersion) {
|
||||
project("kotlin2JsIrICProject", gradleVersion) {
|
||||
val buildConfig = buildGradleKts.readText()
|
||||
|
||||
fun setLazyInitializationArg(value: Boolean) {
|
||||
buildGradleKts.writeText(buildConfig)
|
||||
buildGradleKts.appendText(
|
||||
"""
|
||||
|
|
||||
|tasks.named<org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrLink>("compileDevelopmentExecutableKotlinJs") {
|
||||
| kotlinOptions {
|
||||
| freeCompilerArgs += "-Xir-property-lazy-initialization=$value"
|
||||
| }
|
||||
|}
|
||||
""".trimMargin()
|
||||
)
|
||||
}
|
||||
|
||||
fun String.testScriptOutLines() = this.lines().mapNotNull {
|
||||
val trimmed = it.removePrefix(">>> TEST OUT: ")
|
||||
if (trimmed == it) null else trimmed
|
||||
}
|
||||
|
||||
// -Xir-property-lazy-initialization default is true
|
||||
build("nodeRun") {
|
||||
assertTasksExecuted(":compileDevelopmentExecutableKotlinJs")
|
||||
assertEquals(listOf("Hello, Gradle."), output.testScriptOutLines())
|
||||
}
|
||||
|
||||
setLazyInitializationArg(false)
|
||||
build("nodeRun") {
|
||||
assertTasksExecuted(":compileDevelopmentExecutableKotlinJs")
|
||||
assertEquals(listOf("TOP LEVEL!", "Hello, Gradle."), output.testScriptOutLines())
|
||||
}
|
||||
|
||||
setLazyInitializationArg(true)
|
||||
build("nodeRun") {
|
||||
assertTasksExecuted(":compileDevelopmentExecutableKotlinJs")
|
||||
assertEquals(listOf("Hello, Gradle."), output.testScriptOutLines())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Test multiple artifacts in one project")
|
||||
@GradleTest
|
||||
fun testMultipleArtifacts(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-ir-ic-multiple-artifacts", gradleVersion) {
|
||||
build("compileDevelopmentExecutableKotlinJs") {
|
||||
val cacheDir = projectPath.resolve("app/build/klib/cache/").toFile()
|
||||
val cacheRootDirName = cacheDir.list()?.singleOrNull()
|
||||
assertTrue("Lib cache root dir should contain 1 element 'version.hash'") {
|
||||
cacheRootDirName?.startsWith("version.") ?: false
|
||||
}
|
||||
val cacheRootDir = cacheDir.resolve(cacheRootDirName!!)
|
||||
val klibCacheDirs = cacheRootDir.list()
|
||||
// 2 for lib.klib + 1 for stdlib + 1 for main
|
||||
assertEquals(4, klibCacheDirs?.size, "cache should contain 4 dirs")
|
||||
|
||||
val libKlibCacheDirs = klibCacheDirs?.filter { dir -> dir.startsWith("lib.klib.") }
|
||||
assertEquals(2, libKlibCacheDirs?.size, "cache should contain 2 dirs for lib.klib")
|
||||
|
||||
var lib = false
|
||||
var libOther = false
|
||||
|
||||
cacheRootDir.listFiles()!!
|
||||
.forEach {
|
||||
it.listFiles()!!
|
||||
.filter { it.isFile }
|
||||
.forEach {
|
||||
val text = it.readText()
|
||||
// cache keeps the js code of compiled module, this substring from that js code
|
||||
if (text.contains("root['kotlin-js-ir-ic-multiple-artifacts-lib']")) {
|
||||
if (lib) {
|
||||
error("lib should be only once in cache")
|
||||
}
|
||||
lib = true
|
||||
}
|
||||
// cache keeps the js code of compiled module, this substring from that js code
|
||||
if (text.contains("root['kotlin-js-ir-ic-multiple-artifacts-lib-other']")) {
|
||||
if (libOther) {
|
||||
error("libOther should be only once in cache")
|
||||
}
|
||||
libOther = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
assertTrue("lib and libOther should be once in cache") {
|
||||
lib && libOther
|
||||
}
|
||||
assertTasksExecuted(":app:compileDevelopmentExecutableKotlinJs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Test removing unused dependency from klib")
|
||||
@GradleTest
|
||||
fun testRemoveUnusedDependency(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-ir-ic-remove-unused-dep", gradleVersion) {
|
||||
val appBuildGradleKts = subProject("app").buildGradleKts
|
||||
|
||||
val buildGradleKtsWithoutDependency = appBuildGradleKts.readText()
|
||||
appBuildGradleKts.appendText(
|
||||
"""
|
||||
|
|
||||
|dependencies {
|
||||
| implementation(project(":lib"))
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
)
|
||||
|
||||
build("compileDevelopmentExecutableKotlinJs") {
|
||||
assertTasksExecuted(":app:compileDevelopmentExecutableKotlinJs")
|
||||
}
|
||||
|
||||
appBuildGradleKts.writeText(buildGradleKtsWithoutDependency)
|
||||
build("compileDevelopmentExecutableKotlinJs") {
|
||||
assertTasksExecuted(":app:compileDevelopmentExecutableKotlinJs")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -57,6 +57,7 @@ data class BuildOptions(
|
||||
val jsCompilerType: KotlinJsCompilerType? = null,
|
||||
val incrementalJs: Boolean? = null,
|
||||
val incrementalJsKlib: Boolean? = null,
|
||||
val incrementalJsIr: Boolean? = null,
|
||||
val compileNoWarn: Boolean = true
|
||||
)
|
||||
|
||||
@@ -119,6 +120,7 @@ data class BuildOptions(
|
||||
if (jsOptions != null) {
|
||||
jsOptions.incrementalJs?.let { arguments.add("-Pkotlin.incremental.js=$it") }
|
||||
jsOptions.incrementalJsKlib?.let { arguments.add("-Pkotlin.incremental.js.klib=$it") }
|
||||
jsOptions.incrementalJsIr?.let { arguments.add("-Pkotlin.incremental.js.ir=$it") }
|
||||
jsOptions.useIrBackend?.let { arguments.add("-Pkotlin.js.useIrBackend=$it") }
|
||||
jsOptions.jsCompilerType?.let { arguments.add("-Pkotlin.js.compiler=$it") }
|
||||
// because we have legacy compiler tests, we need nowarn for compiler testing
|
||||
|
||||
-2
@@ -1,3 +1 @@
|
||||
kotlin.incremental.js.klib=true
|
||||
kotlin.incremental.js.ir=true
|
||||
kotlin.daemon.jvmargs=-Dkotlin.js.compiler.legacy.force_enabled=true
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
kotlin("js")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js {
|
||||
nodejs {
|
||||
}
|
||||
binaries.executable()
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package com.example
|
||||
|
||||
private fun someFunction(x: Any? = null) {}
|
||||
|
||||
fun getSomething(x: dynamic) = with(x) {
|
||||
someFunction(::unknownFunction)
|
||||
"Hello, World!"
|
||||
}
|
||||
|
||||
fun main() {
|
||||
println(getSomething(null))
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
plugins {
|
||||
kotlin("js").apply(false)
|
||||
}
|
||||
|
||||
group = "com.example"
|
||||
version = "1.0"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
rootProject.name = "kotlin-js-ir-ic-rebuild-after-error"
|
||||
|
||||
include("app")
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
kotlin.incremental.js.klib=true
|
||||
kotlin.incremental.js.ir=true
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
kotlin.code.style=official
|
||||
kotlin.incremental.js.ir=true
|
||||
kotlin.incremental.js.klib=true
|
||||
Reference in New Issue
Block a user